[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = tab\nindent_size = 4\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.{yml,yaml}]\nindent_style = space\nindent_size = 2\n\n[docker-compose.yml]\nindent_size = 4\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contribution\n\nBefore you start working on a PR, contact us via [Discord](https://discord.froxlor.org) or the forum at [https://forum.froxlor.org](https://forum.froxlor.org) to get a clue whether someone else isn't already working on it or if we don not want/need this certain change. Of course, bugfixes are always welcome.\n\nPlease always focus on the **main** branch of our [Github repository](https://github.com/Froxlor/Froxlor).\n\n## Checklist\n\nGeneral rules for PRs are:\n\n* Please save us all some trouble and unnecessary round-trips by _testing_ your changes.\n* Re-write your commit history to provide a CLEAN history!\n    * i.e. do not provide PRs which contain a commit that changes something, the next changes it back, a third one changes it again, only a little differently...\n\nThanks!\n\n### Service changes\n\nIf you make changes to the functionality of service configurations, please make sure your implementation covers all supported services and distributions.\n\n### l10n\n\nIf you add new language strings, please make sure you add the english fallback strings in `lng/en.php`.\n\n### New settings and database-layout changes\n\nIf you add new settings or implement database-changes, please make sure you add these to\n\n* `install/froxlor.sql.php`\n* handle the update (see [`install/updates/froxlor/update_2.x.inc.php`](https://github.com/Froxlor/Froxlor/blob/main/install/updates/froxlor/update_2.x.inc.php))\n* if you have any question on how update-process works, please contact us\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: d00p\ncustom: ['https://paypal.me/Froxlor']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**As a rule of thumb: before reporting an issue**\n* see if it hasn't been [reported](https://github.com/Froxlor/froxlor/issues) (and possibly already been [fixed](https://github.com/Froxlor/froxlor/issues?utf8=✓&q=is:issue%20is:closed)) first\n* try with the git master\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**System information**\n* Froxlor version: \\$version/\\$gitSHA1\n* PHP sapi & version: php-fpm 8.3 / fcgid 8.0 / etc.\n* Web server: apache2/nginx\n* DNS server: Bind/PowerDNS (standalone)/PowerDNS (Bind-backend)\n* POP/IMAP server: Courier/Dovecot\n* SMTP server: postfix/exim\n* FTP server: proftpd/pureftpd\n* OS/Version: ...\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Logfiles**\nIf applicable, add log-entries to help explain your problem.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "# Bug report vs. support request\n\nIf you're unsure of whether your problem is a bug or a configuration error\n* contact us via IRC in #froxlor on irc.libera.chat\n* or post a thread in our forum at https://forum.froxlor.org\n\nAs a rule of thumb: before reporting an issue\n\n* see if it hasn't been [reported](https://github.com/Froxlor/froxlor/issues) (and possibly already been [fixed](https://github.com/Froxlor/froxlor/issues?utf8=✓&q=is:issue%20is:closed)) first\n* try with the git master\n\n# Summary\n\nPlease provide a concise summary of the problem you're experiencing...\n\n# System information\n\n* Froxlor version: $version/$gitSHA1\n* PHP sapi & version: php-fpm 8.3 / fcgid 8.0 / etc.\n* Web server: apache2/nginx\n* DNS server: Bind/PowerDNS (standalone)/PowerDNS (Bind-backend)\n* POP/IMAP server: Courier/Dovecot\n* SMTP server: postfix/exim\n* FTP server: proftpd/pureftpd\n* OS/Version: ...\n\n# Steps to reproduce\n\n1.\n2.\n3.\n\n# Expected behavior\n\n1.\n2.\n3.\n\n# Actual behavior\n\n1.\n2.\n3.\n\n# Log files/log entries\n\nsyslog:\n<pre>\nexample\n</pre>\n"
  },
  {
    "path": ".github/LICENSE_HEADER",
    "content": "/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "# Description\n\nPlease include a summary of the change and which issue is fixed if any. Please also include relevant motivation and context. List any dependencies that are required for this change.\n\nFixes # (issue)\n\n## Type of change\n\nPlease delete options that are not relevant.\n\n- [ ] Bug fix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)\n- [ ] This change requires a documentation update\n\n# How Has This Been Tested?\n\nPlease describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration\n\n- [ ] Test A\n- [ ] Test B\n\n**Test Configuration**:\n\n* Distribution:\n* Webserver:\n* PHP:\n* etc.etc.:\n\n# Checklist:\n\n- [ ] I have performed a self-review of my own code\n- [ ] I have commented my code, particularly in hard-to-understand areas\n- [ ] I have made corresponding changes to the documentation\n- [ ] My changes generate no new warnings\n- [ ] I have added tests that prove my fix is effective or that my feature works\n- [ ] New and existing unit tests pass locally with my changes\n"
  },
  {
    "path": ".github/workflows/build-docs.yml",
    "content": "name: build-documentation\n\non:\n  release:\n    # only run for stable releases\n    types: [released]\n\njobs:\n  build_docs:\n    runs-on: ubuntu-latest\n    steps:\n      - env:\n          GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }}\n        run: |\n          gh workflow run --repo Froxlor/Documentation build-and-deploy.yml -f type=tags -f ref=${{github.ref_name}}\n"
  },
  {
    "path": ".github/workflows/build-mariadb.yml",
    "content": "name: Froxlor-CI-MariaDB\non: [ 'push', 'pull_request', 'create' ]\n\njobs:\n  froxlor:\n    name: Froxlor (PHP ${{ matrix.php-versions }}, MariaDB ${{ matrix.mariadb-version }})\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        php-versions: [ '7.4', '8.4' ]\n        mariadb-version: [ 10.11, 11.7 ]\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup PHP, with composer and extensions\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php-versions }}\n          tools: composer:v2\n          extensions: mbstring, xml, ctype, pdo_mysql, mysql, curl, json, zip, session, filter, posix, openssl, fileinfo, bcmath, gmp\n\n      - name: Install tools\n        run: sudo apt-get install -y ant\n\n      - name: Adjust firewall\n        run: |\n          sudo ufw allow out 3306/tcp\n          sudo ufw allow in 3306/tcp\n\n      - name: Setup MariaDB\n        uses: shogo82148/actions-setup-mysql@v1.47.0\n        with:\n          distribution: \"mariadb\"\n          mysql-version: ${{ matrix.mariadb-version }}\n          root-password: 'fr0xl0r.TravisCI'\n          user: 'froxlor010'\n          password: 'fr0xl0r.TravisCI'\n\n      - name: Wait for database\n        run: sleep 15\n\n      - name: Setup databases\n        run: |\n          mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI -e \"CREATE DATABASE froxlor010;\"\n          php -r \"echo include('install/froxlor.sql.php');\" > /tmp/froxlor.sql\n          mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI froxlor010 < /tmp/froxlor.sql\n\n      - name: Run testing\n        run: ant quick-build\n\n  nightly:\n    name: Create nightly/testing tarball\n    runs-on: ubuntu-latest\n    needs: froxlor\n    if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup PHP with PECL extension\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: '7.4'\n          tools: composer:v2\n          extensions: mbstring, xml, ctype, pdo_mysql, mysql, curl, json, zip, session, filter, posix, openssl, fileinfo, bcmath, gmp, gnupg\n\n      - name: Install composer dependencies\n        run: composer install --no-dev\n\n      - name: Install Node.js\n        uses: actions/setup-node@v5\n        with:\n          node-version: '22.x'\n\n      - name: Install npm dependencies\n        run: npm install\n\n      - name: Build assets\n        run: npm run build\n        working-directory: .\n\n      - name: Setting file/directory permissions\n        run: |\n          find -exec chmod ugo+r,u+w,go-w {} \\;\n          find -type f -exec chmod ugo-x {} \\;\n          find -type d -exec chmod ugo+x {} \\;\n          chmod 0755 bin/froxlor-cli\n\n      - name: Remove vcs and unneeded files\n        run: |\n          rm .gitignore\n          rm .editorconfig\n          rm -rf node_modules\n          rm composer.json\n          rm composer.lock\n          rm package.json\n          rm package-lock.json\n          rm *.xml\n          rm vite.config.js\n\n      - name: Create empty index.html in built assets directory\n        run: |\n          touch templates/Froxlor/build/index.html\n          touch templates/Froxlor/build/assets/index.html\n\n      - name: Set outputs\n        id: vars\n        run: echo \"sha_short=$(git rev-parse --short HEAD)\" >> $GITHUB_OUTPUT\n\n      - name: Set nightly branding\n        run: |\n          sed -i \"s/const BRANDING = '';/const BRANDING = '+nightly.${{steps.vars.outputs.sha_short}}';/\" lib/Froxlor/Froxlor.php\n          zip -r froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip . -x \"*.git*\"\n          sha256sum froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip > froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip.sha256\n          mkdir dist\n          mv froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip dist/\n          mv froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip.sha256 dist/\n\n      - name: Deploy nightly to server\n        uses: easingthemes/ssh-deploy@main\n        with:\n          ARGS: \"-rltDzvO --chown=${{ secrets.WEB_USER }}:${{ secrets.WEB_USER }}\"\n          SOURCE: \"dist/\"\n          SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}\n          REMOTE_HOST: ${{ secrets.REMOTE_HOST }}\n          REMOTE_USER: ${{ secrets.REMOTE_USER }}\n          TARGET: \"${{ secrets.REMOTE_TARGET }}\"\n"
  },
  {
    "path": ".github/workflows/build-mysql.yml",
    "content": "name: Froxlor-CI-MySQL\non: ['push', 'pull_request', 'create']\n\njobs:\n  froxlor:\n    name: Froxlor (PHP ${{ matrix.php-versions }}, MySQL ${{ matrix.mysql-version }})\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        php-versions: ['7.4', '8.4']\n        mysql-version: [8.4, 5.7]\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup PHP, with composer and extensions\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php-versions }}\n          tools: composer:v2\n          extensions: mbstring, xml, ctype, pdo_mysql, mysql, curl, json, zip, session, filter, posix, openssl, fileinfo, bcmath, gmp\n\n      - name: Install tools\n        run: sudo apt-get install -y ant\n\n      - name: Adjust firewall\n        run: |\n          sudo ufw allow out 3306/tcp\n          sudo ufw allow in 3306/tcp\n\n      - name: Setup MySQL\n        uses: shogo82148/actions-setup-mysql@v1.47.0\n        with:\n          mysql-version: ${{ matrix.mysql-version }}\n          root-password: 'fr0xl0r.TravisCI'\n          user: 'froxlor010'\n          password: 'fr0xl0r.TravisCI'\n\n      - name: Wait for database\n        run: sleep 15\n\n      - name: Setup database\n        run: |\n          mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI -e \"CREATE DATABASE froxlor010;\"\n          php -r \"echo include('install/froxlor.sql.php');\" > /tmp/froxlor.sql\n          mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI froxlor010 < /tmp/froxlor.sql\n\n      - name: Run testing\n        run: ant quick-build\n"
  },
  {
    "path": ".gitignore",
    "content": "install/update.log\ninstall/*.json\nlib/userdata.inc.php\nlib/userdata.inc.php.bak\nlib/config.inc.php\nlogs/*\n!logs/index.html\n.buildpath\n.project\n.settings/\n.test/\n*.diff\n*.patch\n*~\n.well-known\n.idea\n.DS_Store\n*.iml\nimg/\nvendor/\nnode_modules/\nfonts/\ntemplates/*\n!templates/index.html\n!templates/Froxlor/\ntemplates/Froxlor/build/\ntemplates/Froxlor/hot\n!templates/misc/\n"
  },
  {
    "path": "2fa.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nif (!defined('AREA')) {\n\theader(\"Location: index.php\");\n\texit();\n}\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\FroxlorTwoFactorAuth;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\User;\n\nif (Settings::Get('2fa.enabled') != '1') {\n\tResponse::dynamicError('2fa.2fa_not_activated');\n}\n\n// This file is being included in admin_index and customer_index\n// and therefore does not need to require lib/init.php\nif (AREA == 'admin') {\n\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `type_2fa` = :t2fa, `data_2fa` = :d2fa WHERE adminid = :id\");\n\t$uid = $userinfo['adminid'];\n} elseif (AREA == 'customer') {\n\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `type_2fa` = :t2fa, `data_2fa` = :d2fa WHERE customerid = :id\");\n\t$uid = $userinfo['customerid'];\n}\n$success_message = \"\";\n\n$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));\n\n// do the delete and then just show a success-message\nif ($action == 'delete') {\n\tDatabase::pexecute($upd_stmt, [\n\t\t't2fa' => 0,\n\t\t'd2fa' => \"\",\n\t\t'id' => $uid\n\t]);\n\tResponse::standardSuccess('2fa.2fa_removed');\n} elseif ($action == 'preadd') {\n\t$type = Request::post('type_2fa', '0');\n\n\t$data = \"\";\n\tif ($type > 0) {\n\t\t// generate secret for TOTP\n\t\t$data = $tfa->createSecret();\n\n\t\t$userinfo['type_2fa'] = $type;\n\t\t$userinfo['data_2fa'] = $data;\n\t\t$userinfo['2fa_unsaved'] = true;\n\n\t\t// if type = email, send a code there for confirmation\n\t\tif ($type == 1) {\n\t\t\t$code = $tfa->getCode($data);\n\t\t\t$_mailerror = false;\n\t\t\t$mailerr_msg = \"\";\n\t\t\t$replace_arr = [\n\t\t\t\t'CODE' => $code\n\t\t\t];\n\t\t\t$mail_body = html_entity_decode(PhpHelper::replaceVariables(lng('mails.2fa.mailbody'), $replace_arr));\n\n\t\t\ttry {\n\t\t\t\t$mail->Subject = lng('mails.2fa.subject');\n\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t$mail->MsgHTML(str_replace(\"\\n\", \"<br />\", $mail_body));\n\t\t\t\t$mail->AddAddress($userinfo['email'], User::getCorrectUserSalutation($userinfo));\n\t\t\t\t$mail->Send();\n\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t$_mailerror = true;\n\t\t\t} catch (Exception $e) {\n\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t$_mailerror = true;\n\t\t\t}\n\n\t\t\tif ($_mailerror) {\n\t\t\t\tResponse::dynamicError($mailerr_msg);\n\t\t\t}\n\t\t}\n\t\tUI::twig()->addGlobal('userinfo', $userinfo);\n\t} else {\n\t\tResponse::dynamicError('Select one of the possible values for 2FA');\n\t}\n} elseif ($action == 'add') {\n\t$type = Request::post('type_2fa', '0');\n\t$data = Request::post('data_2fa', '');\n\t$code = Request::post('codevalidation', '');\n\n\t// validate\n\t$result = $tfa->verifyCode($data, $code, 3);\n\n\tif ($result) {\n\t\tif ($type == 0 || $type == 1) {\n\t\t\t// no fixed secret for email validation, the validation code will be set on the fly\n\t\t\t$data = \"\";\n\t\t}\n\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t't2fa' => $type,\n\t\t\t'd2fa' => $data,\n\t\t\t'id' => $uid\n\t\t]);\n\t\tResponse::standardSuccess('2fa.2fa_added', $filename);\n\t}\n\tResponse::dynamicError('Invalid/wrong code');\n}\n\n$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"viewed 2fa::overview\");\n\n$type_select_values = [];\n$ga_qrcode = '';\nif ($userinfo['type_2fa'] == '0') {\n\t// available types\n\t$type_select_values = [\n\t\t0 => '-',\n\t\t1 => 'E-Mail',\n\t\t2 => 'Authenticator'\n\t];\n\tasort($type_select_values);\n} elseif ($userinfo['type_2fa'] == '1') {\n\t// email 2fa enabled\n} elseif ($userinfo['type_2fa'] == '2') {\n\t// authenticator 2fa enabled\n\t$ga_qrcode = $tfa->getQRCodeImageAsDataUri($userinfo['loginname'], $userinfo['data_2fa']);\n}\n\nUI::view('user/2fa.html.twig', [\n\t'type_select_values' => $type_select_values,\n\t'ga_qrcode' => $ga_qrcode\n]);\n"
  },
  {
    "path": "COPYING",
    "content": "\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\f\n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\f\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\f\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\f\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "README.md",
    "content": "[![Froxlor-CI](https://github.com/Froxlor/Froxlor/actions/workflows/build-mariadb.yml/badge.svg?branch=main)](https://github.com/Froxlor/Froxlor/actions/workflows/build-mariadb.yml)\n[![Froxlor-CI](https://github.com/Froxlor/Froxlor/actions/workflows/build-mysql.yml/badge.svg?branch=main)](https://github.com/Froxlor/Froxlor/actions/workflows/build-mysql.yml)\n[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.froxlor.org)\n\n# Froxlor\n\nThe server administration software for your needs.\nDeveloped by experienced server administrators, this panel simplifies the effort of managing your hosting platform.\n\n## Installation\n\n### Fast install\n\n1. Ensure that your webserver serves /var/www/html\n2. Extract froxlor into /var/www/html\n3. Point your browser to http://[ip-of-webserver]/froxlor\n4. Follow the installer\n5. Login as administrator\n6. Have fun!\n\nIf you have chosen to do the configuration by hand during the installation, you have to complete some more steps:\n\n1. Adjust \"System > Settings\" according to your needs\n2. Choose your distribution under \"System > Configuration\"\n3. Follow the steps for your services\n\n### Detailed installation\n\nhttps://docs.froxlor.org/latest/general/installation/\n\n## Help\n\nYou may find help in the following places:\n\n### Discord\n\nThe froxlor community discord server can be found here: https://discord.froxlor.org\n\n### Forum\n\nThe community is located on https://forum.froxlor.org/\n\n### Documentation\n\nThe documentation may be found at https://docs.froxlor.org/\n\n## License\n\nMay be found in [COPYING](COPYING)\n\n## Downloads\n\n### Tarball\n\nhttps://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](https://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1)\n\n### Debian / Ubuntu repository\n\n[HowTo](https://docs.froxlor.org/latest/general/installation/apt-package.html)\n\n#### Debian\n\n```\napt -y install apt-transport-https lsb-release ca-certificates curl gnupg\ncurl -sSLo /usr/share/keyrings/deb.froxlor.org-froxlor.gpg https://deb.froxlor.org/froxlor.gpg\nsh -c 'echo \"deb [signed-by=/usr/share/keyrings/deb.froxlor.org-froxlor.gpg] https://deb.froxlor.org/debian $(lsb_release -sc) main\" > /etc/apt/sources.list.d/froxlor.list'\n```\n\n#### Ubuntu\n\n```\napt -y install apt-transport-https lsb-release ca-certificates curl gnupg\ncurl -sSLo /usr/share/keyrings/deb.froxlor.org-froxlor.gpg https://deb.froxlor.org/froxlor.gpg\nsh -c 'echo \"deb [signed-by=/usr/share/keyrings/deb.froxlor.org-froxlor.gpg] https://deb.froxlor.org/ubuntu $(lsb_release -sc) main\" > /etc/apt/sources.list.d/froxlor.list'\n```\n\n## Contributing\n\n[see here](.github/CONTRIBUTING.md)\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# froxlor's Security Policy\n\nWelcome and thanks for taking interest in [froxlor](https://www.froxlor.org)!\n\nWe are mostly interested in reports by actual froxlor users but all high quality contributions are welcome.\n\nPlease try your best to describe a clear and realistic impact for your report and please don't open any public issues on GitHub or social media, we're doing our best to respond through huntr as quickly as we can.\n\nWith that, good luck hacking us ;)\n\n## Supported versions\n\n- ️✅ **2.3.x**  (`main` and `v2.3` git-branch)\n- ️❌ <=2.2.x\n- ❌ other git-branches\n\n## Qualifying Vulnerabilities\n\n### Vulnerabilities we really care about\n- SQL injection bugs\n- server-side code execution bugs\n- cross-site scripting vulnerabilities\n- cross-site request forgery vulnerabilities\n- authentication and authorization flaws\n- sensitive information disclosure\n\n### Vulnerabilities we accept\n\nOnly reproducible issues on a default/clean setup from the latest stable release of a supported version will be accepted.\n\n## Non-Qualifying Vulnerabilities\n\n- Reports from automated tools or scanners\n- Theoretical attacks without proof of exploitability\n- Attacks that are the result of a third party library should be reported to the library maintainers\n- Social engineering\n- Attacks that require disabling security features or reducing the security level of the environment\n- Exploits by an admin user itself (privileged user and implicitly trusted)\n- Reflected file download\n- Physical attacks\n- Weak SSL/TLS/SSH algorithms or protocols\n- Attacks involving physical access to a user’s device, or involving a device or network that’s already seriously compromised (eg man-in-the-middle).\n- The user attacks themselves\n- anything in `/doc`\n- anything in `/tests`\n\n## Reporting a Vulnerability\n\nIf you think you have found a vulnerability in froxlor, please head over to [https://github.com/Froxlor/Froxlor/security/advisories](https://github.com/Froxlor/Froxlor/security/advisories/new) and use the reporting possibilities there. Also, please give us appropriate time to fix the issue and build update-packages before publishing anything into the wild. Alternatively you can email us to [team@froxlor.org](team@froxlor.org).\n"
  },
  {
    "path": "actions/admin/index.html",
    "content": ""
  },
  {
    "path": "actions/admin/settings/100.panel.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'panel' => [\n\t\t\t'title' => lng('admin.panelsettings'),\n\t\t\t'icon' => 'fa-solid fa-chalkboard-user',\n\t\t\t'fields' => [\n\t\t\t\t'panel_standardlanguage' => [\n\t\t\t\t\t'label' => [\n\t\t\t\t\t\t'title' => lng('login.language'),\n\t\t\t\t\t\t'description' => lng('serversettings.language.description')\n\t\t\t\t\t],\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'standardlanguage',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'en',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Language',\n\t\t\t\t\t\t'getLanguages'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_default_theme' => [\n\t\t\t\t\t'label' => [\n\t\t\t\t\t\t'title' => lng('panel.theme'),\n\t\t\t\t\t\t'description' => lng('serversettings.default_theme')\n\t\t\t\t\t],\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'default_theme',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'Froxlor',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\UI\\\\Panel\\\\UI',\n\t\t\t\t\t\t'getThemes'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingDefaultTheme'\n\t\t\t\t],\n\t\t\t\t'panel_allow_theme_change_customer' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_allow_theme_change_customer'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'allow_theme_change_customer',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_allow_theme_change_admin' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_allow_theme_change_admin'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'allow_theme_change_admin',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_natsorting' => [\n\t\t\t\t\t'label' => lng('serversettings.natsorting'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'natsorting',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_paging' => [\n\t\t\t\t\t'label' => lng('serversettings.paging'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'paging',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 0,\n\t\t\t\t\t'default' => 0,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_pathedit' => [\n\t\t\t\t\t'label' => lng('serversettings.pathedit'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'pathedit',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'Manual',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'Manual' => lng('serversettings.manual'),\n\t\t\t\t\t\t'Dropdown' => lng('serversettings.dropdown')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_adminmail' => [\n\t\t\t\t\t'label' => lng('serversettings.adminmail'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'adminmail',\n\t\t\t\t\t'type' => 'email',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_adminmail_defname' => [\n\t\t\t\t\t'label' => lng('serversettings.adminmail_defname'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'adminmail_defname',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'Froxlor Administrator',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_adminmail_return' => [\n\t\t\t\t\t'label' => lng('serversettings.adminmail_return'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'adminmail_return',\n\t\t\t\t\t'type' => 'email',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_decimal_places' => [\n\t\t\t\t\t'label' => lng('serversettings.decimal_places'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'decimal_places',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 0,\n\t\t\t\t\t'max' => 15,\n\t\t\t\t\t'default' => 4,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_phpmyadmin_url' => [\n\t\t\t\t\t'label' => lng('serversettings.phpmyadmin_url'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'phpmyadmin_url',\n\t\t\t\t\t'type' => 'url',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_webmail_url' => [\n\t\t\t\t\t'label' => lng('serversettings.webmail_url'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'webmail_url',\n\t\t\t\t\t'type' => 'url',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_webftp_url' => [\n\t\t\t\t\t'label' => lng('serversettings.webftp_url'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'webftp_url',\n\t\t\t\t\t'type' => 'url',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'admin_show_version_login' => [\n\t\t\t\t\t'label' => lng('admin.show_version_login'),\n\t\t\t\t\t'settinggroup' => 'admin',\n\t\t\t\t\t'varname' => 'show_version_login',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'admin_show_version_footer' => [\n\t\t\t\t\t'label' => lng('admin.show_version_footer'),\n\t\t\t\t\t'settinggroup' => 'admin',\n\t\t\t\t\t'varname' => 'show_version_footer',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'admin_show_news_feed' => [\n\t\t\t\t\t'label' => lng('admin.show_news_feed'),\n\t\t\t\t\t'settinggroup' => 'admin',\n\t\t\t\t\t'varname' => 'show_news_feed',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'customer_show_news_feed' => [\n\t\t\t\t\t'label' => lng('admin.customer_show_news_feed'),\n\t\t\t\t\t'settinggroup' => 'customer',\n\t\t\t\t\t'varname' => 'show_news_feed',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'customer_news_feed_url' => [\n\t\t\t\t\t'label' => lng('admin.customer_news_feed_url'),\n\t\t\t\t\t'settinggroup' => 'customer',\n\t\t\t\t\t'varname' => 'news_feed_url',\n\t\t\t\t\t'type' => 'url',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_allow_domain_change_admin' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_allow_domain_change_admin'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'allow_domain_change_admin',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_allow_domain_change_customer' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_allow_domain_change_customer'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'allow_domain_change_customer',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_phpconfigs_hidesubdomains'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'phpconfigs_hidesubdomains',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_phpconfigs_hidestdsubdomain'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'phpconfigs_hidestdsubdomain',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_customer_hide_options' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_customer_hide_options'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'customer_hide_options',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'select_mode' => 'multiple',\n\t\t\t\t\t'option_emptyallowed' => true,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'email' => lng('menue.email.email'),\n\t\t\t\t\t\t'mysql' => lng('menue.mysql.mysql'),\n\t\t\t\t\t\t'domains' => lng('menue.domains.domains'),\n\t\t\t\t\t\t'ftp' => lng('menue.ftp.ftp'),\n\t\t\t\t\t\t'extras' => lng('menue.extras.extras'),\n\t\t\t\t\t\t'extras.directoryprotection' => lng('menue.extras.extras') . \" / \" . lng('menue.extras.directoryprotection'),\n\t\t\t\t\t\t'extras.pathoptions' => lng('menue.extras.extras') . \" / \" . lng('menue.extras.pathoptions'),\n\t\t\t\t\t\t'extras.logger' => lng('menue.extras.extras') . \" / \" . lng('menue.logger.logger'),\n\t\t\t\t\t\t'extras.export' => lng('menue.extras.extras') . \" / \" . lng('menue.extras.export'),\n\t\t\t\t\t\t'traffic' => lng('menue.traffic.traffic'),\n\t\t\t\t\t\t'traffic.http' => lng('menue.traffic.traffic') . \" / HTTP\",\n\t\t\t\t\t\t'traffic.ftp' => lng('menue.traffic.traffic') . \" / FTP\",\n\t\t\t\t\t\t'traffic.mail' => lng('menue.traffic.traffic') . \" / Mail\",\n\t\t\t\t\t\t'misc.documentation' => lng('admin.documentation'),\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_imprint_url' => [\n\t\t\t\t\t'label' => lng('serversettings.imprint_url'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'imprint_url',\n\t\t\t\t\t'type' => 'url',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_terms_url' => [\n\t\t\t\t\t'label' => lng('serversettings.terms_url'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'terms_url',\n\t\t\t\t\t'type' => 'url',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_privacy_url' => [\n\t\t\t\t\t'label' => lng('serversettings.privacy_url'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'privacy_url',\n\t\t\t\t\t'type' => 'url',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_logo_overridetheme' => [\n\t\t\t\t\t'label' => lng('serversettings.logo_overridetheme'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'logo_overridetheme',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_logo_overridecustom' => [\n\t\t\t\t\t'label' => lng('serversettings.logo_overridecustom'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'logo_overridecustom',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_logo_image_header' => [\n\t\t\t\t\t'label' => lng('serversettings.logo_image_header'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'logo_image_header',\n\t\t\t\t\t'type' => 'image',\n\t\t\t\t\t'accept' => 'image/jpeg, image/jpg, image/png, image/gif',\n\t\t\t\t\t'image_name' => 'logo_header',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingImage'\n\t\t\t\t],\n\t\t\t\t'panel_logo_image_login' => [\n\t\t\t\t\t'label' => lng('serversettings.logo_image_login'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'logo_image_login',\n\t\t\t\t\t'type' => 'image',\n\t\t\t\t\t'accept' => 'image/jpeg, image/jpg, image/png, image/gif',\n\t\t\t\t\t'image_name' => 'logo_login',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingImage'\n\t\t\t\t],\n\t\t\t\t'panel_menu_collapsed' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_menu_collapsed'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'menu_collapsed',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/110.accounts.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'accounts' => [\n\t\t\t'title' => lng('admin.accountsettings'),\n\t\t\t'icon' => 'fa-solid fa-users-gear',\n\t\t\t'fields' => [\n\t\t\t\t'session_sessiontimeout' => [\n\t\t\t\t\t'label' => lng('serversettings.session_timeout'),\n\t\t\t\t\t'settinggroup' => 'session',\n\t\t\t\t\t'varname' => 'sessiontimeout',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 60,\n\t\t\t\t\t'max' => 31536000,\n\t\t\t\t\t'default' => 600,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'session_allow_multiple_login' => [\n\t\t\t\t\t'label' => lng('serversettings.session_allow_multiple_login'),\n\t\t\t\t\t'settinggroup' => 'session',\n\t\t\t\t\t'varname' => 'allow_multiple_login',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'login_domain_login' => [\n\t\t\t\t\t'label' => lng('serversettings.login_domain_login'),\n\t\t\t\t\t'settinggroup' => 'login',\n\t\t\t\t\t'varname' => 'domain_login',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'login_maxloginattempts' => [\n\t\t\t\t\t'label' => lng('serversettings.maxloginattempts'),\n\t\t\t\t\t'settinggroup' => 'login',\n\t\t\t\t\t'varname' => 'maxloginattempts',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 1,\n\t\t\t\t\t'default' => 3,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'login_deactivatetime' => [\n\t\t\t\t\t'label' => lng('serversettings.deactivatetime'),\n\t\t\t\t\t'settinggroup' => 'login',\n\t\t\t\t\t'varname' => 'deactivatetime',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 0,\n\t\t\t\t\t'default' => 900,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'2fa_enabled' => [\n\t\t\t\t\t'label' => lng('2fa.2fa_enabled'),\n\t\t\t\t\t'settinggroup' => '2fa',\n\t\t\t\t\t'varname' => 'enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_password_min_length' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_password_min_length'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'password_min_length',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 0,\n\t\t\t\t\t'default' => 0,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_password_alpha_lower' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_password_alpha_lower'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'password_alpha_lower',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_password_alpha_upper' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_password_alpha_upper'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'password_alpha_upper',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_password_numeric' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_password_numeric'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'password_numeric',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_password_special_char_required' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_password_special_char_required'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'password_special_char_required',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_password_special_char' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_password_special_char'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'password_special_char',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '!?<>§$%+#=@',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_password_regex' => [\n\t\t\t\t\t'label' => lng('serversettings.panel_password_regex'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'password_regex',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_req_limit_per_interval' => [\n\t\t\t\t\t'label' => lng('serversettings.req_limit_per_interval'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'req_limit_per_interval',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 30,\n\t\t\t\t\t'default' => 60,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_req_limit_interval' => [\n\t\t\t\t\t'label' => lng('serversettings.req_limit_interval'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'req_limit_interval',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 5,\n\t\t\t\t\t'default' => 60,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'customer_accountprefix' => [\n\t\t\t\t\t'label' => lng('serversettings.accountprefix'),\n\t\t\t\t\t'settinggroup' => 'customer',\n\t\t\t\t\t'varname' => 'accountprefix',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkUsername'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'customer_mysqlprefix' => [\n\t\t\t\t\t'label' => lng('serversettings.mysqlprefix'),\n\t\t\t\t\t'settinggroup' => 'customer',\n\t\t\t\t\t'varname' => 'mysqlprefix',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkUsername'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'customer_ftpprefix' => [\n\t\t\t\t\t'label' => lng('serversettings.ftpprefix'),\n\t\t\t\t\t'settinggroup' => 'customer',\n\t\t\t\t\t'varname' => 'ftpprefix',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'customer_ftpatdomain' => [\n\t\t\t\t\t'label' => lng('serversettings.ftpdomain'),\n\t\t\t\t\t'settinggroup' => 'customer',\n\t\t\t\t\t'varname' => 'ftpatdomain',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'panel_allow_preset' => [\n\t\t\t\t\t'label' => lng('serversettings.allow_password_reset'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'allow_preset',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'dependency' => [\n\t\t\t\t\t\t'fieldname' => 'panel_allow_preset_admin',\n\t\t\t\t\t\t'fielddata' => [\n\t\t\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t\t\t'varname' => 'allow_preset_admin'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'onlyif' => 0\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'panel_allow_preset_admin' => [\n\t\t\t\t\t'label' => lng('serversettings.allow_password_reset_admin'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'allow_preset_admin',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'dependency' => [\n\t\t\t\t\t\t'fieldname' => 'panel_allow_preset',\n\t\t\t\t\t\t'fielddata' => [\n\t\t\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t\t\t'varname' => 'allow_preset'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'onlyif' => 1\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'system_exportenabled' => [\n\t\t\t\t\t'label' => lng('serversettings.exportenabled'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'exportenabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'cronmodule' => 'froxlor/export',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_createstdsubdom_default' => [\n\t\t\t\t\t'label' => lng('serversettings.createstdsubdom_default'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'createstdsubdom_default',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '1',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'0' => lng('panel.no'),\n\t\t\t\t\t\t'1' => lng('panel.yes')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/120.system.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'system' => [\n\t\t\t'title' => lng('admin.systemsettings'),\n\t\t\t'icon' => 'fa-solid fa-gears',\n\t\t\t'fields' => [\n\t\t\t\t'system_documentroot_prefix' => [\n\t\t\t\t\t'label' => lng('serversettings.documentroot_prefix'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'documentroot_prefix',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/var/customers/webs/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkPathConflicts'\n\t\t\t\t\t],\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_documentroot_use_default_value' => [\n\t\t\t\t\t'label' => lng('serversettings.documentroot_use_default_value'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'documentroot_use_default_value',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_ipaddress' => [\n\t\t\t\t\t'label' => lng('serversettings.ipaddress'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'ipaddress',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Domain\\\\IpAddr',\n\t\t\t\t\t\t'getIpAddresses'\n\t\t\t\t\t],\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingIpAddress'\n\t\t\t\t],\n\t\t\t\t'system_defaultip' => [\n\t\t\t\t\t'label' => lng('serversettings.defaultip'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'defaultip',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'select_mode' => 'multiple',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Domain\\\\IpAddr',\n\t\t\t\t\t\t'getIpPortCombinations'\n\t\t\t\t\t],\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingDefaultIp'\n\t\t\t\t],\n\t\t\t\t'system_defaultsslip' => [\n\t\t\t\t\t'label' => lng('serversettings.defaultsslip'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'defaultsslip',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'select_mode' => 'multiple',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Domain\\\\IpAddr',\n\t\t\t\t\t\t'getSslIpPortCombinations'\n\t\t\t\t\t],\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingDefaultSslIp'\n\t\t\t\t],\n\t\t\t\t'system_hostname' => [\n\t\t\t\t\t'label' => lng('serversettings.hostname'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'hostname',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingHostname',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkHostname'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'api_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.enable_api'),\n\t\t\t\t\t'settinggroup' => 'api',\n\t\t\t\t\t'varname' => 'enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'api_customer_default' => [\n\t\t\t\t\t'label' => lng('serversettings.api_customer_default'),\n\t\t\t\t\t'settinggroup' => 'api',\n\t\t\t\t\t'varname' => 'customer_default',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 1,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t1 => lng('panel.yes'),\n\t\t\t\t\t\t0 => lng('panel.no')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_update_channel' => [\n\t\t\t\t\t'label' => lng('serversettings.update_channel'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'update_channel',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'stable',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'stable' => lng('serversettings.uc_stable'),\n\t\t\t\t\t\t'testing' => lng('serversettings.uc_testing'),\n\t\t\t\t\t\t'nightly' => lng('serversettings.uc_nightly')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_validate_domain' => [\n\t\t\t\t\t'label' => lng('serversettings.validate_domain'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'validate_domain',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_stdsubdomain' => [\n\t\t\t\t\t'label' => lng('serversettings.stdsubdomainhost'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'stdsubdomain',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingHostname'\n\t\t\t\t],\n\t\t\t\t'system_mysql_access_host' => [\n\t\t\t\t\t'label' => lng('serversettings.mysql_access_host'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mysql_access_host',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '127.0.0.1,localhost',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkMysqlAccessHost'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingMysqlAccessHost'\n\t\t\t\t],\n\t\t\t\t'system_nssextrausers' => [\n\t\t\t\t\t'label' => lng('serversettings.nssextrausers'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'nssextrausers',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_store_index_file_subs' => [\n\t\t\t\t\t'label' => lng('serversettings.system_store_index_file_subs'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'store_index_file_subs',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_report_enable' => [\n\t\t\t\t\t'label' => lng('serversettings.report.report'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'report_enable',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'cronmodule' => 'froxlor/reports',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_report_webmax' => [\n\t\t\t\t\t'label' => lng('serversettings.report.webmax'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'report_webmax',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 0,\n\t\t\t\t\t'max' => 150,\n\t\t\t\t\t'default' => 90,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_report_trafficmax' => [\n\t\t\t\t\t'label' => lng('serversettings.report.trafficmax'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'report_trafficmax',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 0,\n\t\t\t\t\t'max' => 150,\n\t\t\t\t\t'default' => 90,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_report_web_bccadmin' => [\n\t\t\t\t\t'label' => lng('serversettings.report.report_web_bccadmin'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'report_web_bccadmin',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mail_use_smtp' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_use_smtp'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_use_smtp',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mail_smtp_host' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_smtp_host'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_smtp_host',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'localhost',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mail_smtp_port' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_smtp_port'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_smtp_port',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 1,\n\t\t\t\t\t'max' => 65535,\n\t\t\t\t\t'default' => 25,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mail_smtp_usetls' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_smtp_usetls'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_smtp_usetls',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mail_smtp_auth' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_smtp_auth'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_smtp_auth',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mail_smtp_user' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_smtp_user'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_smtp_user',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'autocomplete' => 'off'\n\t\t\t\t],\n\t\t\t\t'system_mail_smtp_passwd' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_smtp_passwd'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_smtp_passwd',\n\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'autocomplete' => 'new-password'\n\t\t\t\t],\n\t\t\t\t'system_apply_specialsettings_default' => [\n\t\t\t\t\t'label' => lng('serversettings.apply_specialsettings_default'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apply_specialsettings_default',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_apply_phpconfigs_default' => [\n\t\t\t\t\t'label' => lng('serversettings.apply_phpconfigs_default'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apply_phpconfigs_default',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_domaindefaultalias' => [\n\t\t\t\t\t'label' => lng('admin.domaindefaultalias'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'domaindefaultalias',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '0',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'0' => lng('domains.serveraliasoption_wildcard'),\n\t\t\t\t\t\t'1' => lng('domains.serveraliasoption_www'),\n\t\t\t\t\t\t'2' => lng('domains.serveraliasoption_none')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_hide_incompatible_settings' => [\n\t\t\t\t\t'label' => lng('serversettings.hide_incompatible_settings'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'hide_incompatible_settings',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/122.froxlorvhost.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'groups' => [\n\t\t'froxlorvhost' => [\n\t\t\t'title' => lng('admin.froxlorvhost') . (call_user_func([\n\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t]) == false ? lng('admin.novhostcontainer') : ''),\n\t\t\t'icon' => 'fa-solid fa-wrench',\n\t\t\t'fields' => [\n\t\t\t\t/**\n\t\t\t\t * Webserver-Vhost\n\t\t\t\t */\n\t\t\t\t'system_froxlordirectlyviahostname' => [\n\t\t\t\t\t'label' => lng('serversettings.froxlordirectlyviahostname'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'froxlordirectlyviahostname',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_froxloraliases' => [\n\t\t\t\t\t'label' => lng('serversettings.froxloraliases'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'froxloraliases',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^(([a-z0-9\\-\\._]+, ?)*[a-z0-9\\-\\._]+)?$/i',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingClearCertificates',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t/**\n\t\t\t\t * SSL / Let's Encrypt\n\t\t\t\t */\n\t\t\t\t'system_le_froxlor_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.le_froxlor_enabled'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'le_froxlor_enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingClearCertificates',\n\t\t\t\t\t'visible' => Settings::Get('system.leenabled') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t], true),\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_le_froxlor_redirect' => [\n\t\t\t\t\t'label' => lng('serversettings.le_froxlor_redirect'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'le_froxlor_redirect',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t], true)\n\t\t\t\t],\n\t\t\t\t'system_hsts_maxage' => [\n\t\t\t\t\t'label' => lng('admin.domain_hsts_maxage'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'hsts_maxage',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 0,\n\t\t\t\t\t'max' => 94608000, // 3-years\n\t\t\t\t\t'default' => 10368000,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t], true),\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_hsts_incsub' => [\n\t\t\t\t\t'label' => lng('admin.domain_hsts_incsub'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'hsts_incsub',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t], true),\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_hsts_preload' => [\n\t\t\t\t\t'label' => lng('admin.domain_hsts_preload'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'hsts_preload',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t], true),\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_honorcipherorder' => [\n\t\t\t\t\t'label' => lng('admin.domain_honorcipherorder'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'honorcipherorder',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t], true),\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_sessiontickets' => [\n\t\t\t\t\t'label' => lng('admin.domain_sessiontickets'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'sessiontickets',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t], true),\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t/**\n\t\t\t\t * FCGID\n\t\t\t\t */\n\t\t\t\t'system_mod_fcgid_ownvhost' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid_ownvhost'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_ownvhost',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t]),\n\t\t\t\t\t'requires_reconf' => ['system:fcgid']\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_httpuser' => [\n\t\t\t\t\t'label' => lng('admin.mod_fcgid_user'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_httpuser',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'froxlorlocal',\n\t\t\t\t\t'string_emptyallowed' => false,\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkSystemUsername'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingWebserverFcgidFpmUser',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t]),\n\t\t\t\t\t'requires_reconf' => ['system:fcgid']\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_httpgroup' => [\n\t\t\t\t\t'label' => lng('admin.mod_fcgid_group'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_httpgroup',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'froxlorlocal',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'string_emptyallowed' => false,\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t]),\n\t\t\t\t\t'requires_reconf' => ['system:fcgid']\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_defaultini_ownvhost' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.defaultini_ownvhost'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_defaultini_ownvhost',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '2',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Http\\\\PhpConfig',\n\t\t\t\t\t\t'getPhpConfigs'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t])\n\t\t\t\t],\n\t\t\t\t/**\n\t\t\t\t * php-fpm\n\t\t\t\t */\n\t\t\t\t'phpfpm_enabled_ownvhost' => [\n\t\t\t\t\t'label' => lng('phpfpm.ownvhost'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'enabled_ownvhost',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t]),\n\t\t\t\t\t'requires_reconf' => ['system:php-fpm']\n\t\t\t\t],\n\t\t\t\t'phpfpm_vhost_httpuser' => [\n\t\t\t\t\t'label' => lng('phpfpm.vhost_httpuser'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'vhost_httpuser',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'froxlorlocal',\n\t\t\t\t\t'string_emptyallowed' => false,\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkSystemUsername'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingWebserverFcgidFpmUser',\n\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t]),\n\t\t\t\t\t'requires_reconf' => ['system:php-fpm']\n\t\t\t\t],\n\t\t\t\t'phpfpm_vhost_httpgroup' => [\n\t\t\t\t\t'label' => lng('phpfpm.vhost_httpgroup'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'vhost_httpgroup',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'froxlorlocal',\n\t\t\t\t\t'string_emptyallowed' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t]),\n\t\t\t\t\t'requires_reconf' => ['system:php-fpm']\n\t\t\t\t],\n\t\t\t\t'phpfpm_vhost_defaultini' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.defaultini_ownvhost'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'vhost_defaultini',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '2',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Http\\\\PhpConfig',\n\t\t\t\t\t\t'getPhpConfigs'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') && call_user_func([\n\t\t\t\t\t\t'\\Froxlor\\Settings\\FroxlorVhostSettings',\n\t\t\t\t\t\t'hasVhostContainerEnabled'\n\t\t\t\t\t])\n\t\t\t\t],\n\t\t\t\t/**\n\t\t\t\t * DNS\n\t\t\t\t */\n\t\t\t\t'system_dns_createhostnameentry' => [\n\t\t\t\t\t'label' => lng('serversettings.dns_createhostnameentry'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'dns_createhostnameentry',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.bind_enable')\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/125.cronjob.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'crond' => [\n\t\t\t'title' => lng('admin.cronsettings'),\n\t\t\t'icon' => 'fa-solid fa-clock-rotate-left',\n\t\t\t'advanced_mode' => true,\n\t\t\t'fields' => [\n\t\t\t\t'system_cronconfig' => [\n\t\t\t\t\t'label' => lng('serversettings.system_cronconfig'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'cronconfig',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/etc/cron.d/froxlor',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_croncmdline' => [\n\t\t\t\t\t'label' => lng('serversettings.system_croncmdline'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'croncmdline',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^[a-z0-9\\/\\._\\- ]+$/i',\n\t\t\t\t\t'default' => '/usr/bin/nice -n 5 /usr/bin/php -q',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_crondreload' => [\n\t\t\t\t\t'label' => lng('serversettings.system_crondreload'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'crondreload',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^[a-z0-9\\/\\._\\- ]+$/i',\n\t\t\t\t\t'default' => '/etc/init.d/cron reload',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_cron_allowautoupdate' => [\n\t\t\t\t\t'label' => lng('serversettings.system_cron_allowautoupdate'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'cron_allowautoupdate',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/130.webserver.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'groups' => [\n\t\t'webserver' => [\n\t\t\t'title' => lng('admin.webserversettings'),\n\t\t\t'icon' => 'fa-solid fa-server',\n\t\t\t'fields' => [\n\t\t\t\t'system_webserver' => [\n\t\t\t\t\t'label' => lng('admin.webserver'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'webserver',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'apache2',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'apache2' => 'Apache 2',\n\t\t\t\t\t\t'nginx' => 'Nginx'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkPhpInterfaceSetting'\n\t\t\t\t\t],\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_apache24' => [\n\t\t\t\t\t'label' => lng('serversettings.apache_24'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apache24',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'system_apacheitksupport' => [\n\t\t\t\t\t'label' => lng('serversettings.apache_itksupport'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apacheitksupport',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0),\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_http2_support' => [\n\t\t\t\t\t'label' => lng('serversettings.http2_support'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'http2_support',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2',\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t],\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl')\n\t\t\t\t],\n\t\t\t\t'system_http3_support' => [\n\t\t\t\t\t'label' => lng('serversettings.http3_support'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'http3_support',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t],\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl')\n\t\t\t\t],\n\t\t\t\t'system_dhparams_file' => [\n\t\t\t\t\t'label' => lng('serversettings.dhparams_file'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'dhparams_file',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl'),\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_httpuser' => [\n\t\t\t\t\t'label' => lng('admin.webserver_user'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'httpuser',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'www-data',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkSystemUsername'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingWebserverFcgidFpmUser'\n\t\t\t\t],\n\t\t\t\t'system_httpgroup' => [\n\t\t\t\t\t'label' => lng('admin.webserver_group'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'httpgroup',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'www-data',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_apacheconf_vhost' => [\n\t\t\t\t\t'label' => lng('serversettings.apacheconf_vhost'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apacheconf_vhost',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'filedir',\n\t\t\t\t\t'default' => '/etc/apache2/sites-enabled/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_apacheconf_diroptions' => [\n\t\t\t\t\t'label' => lng('serversettings.apacheconf_diroptions'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apacheconf_diroptions',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'filedir',\n\t\t\t\t\t'default' => '/etc/apache2/sites-enabled/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_apacheconf_htpasswddir' => [\n\t\t\t\t\t'label' => lng('serversettings.apacheconf_htpasswddir'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apacheconf_htpasswddir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'confdir',\n\t\t\t\t\t'default' => '/etc/apache2/htpasswd/',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_logfiles_directory' => [\n\t\t\t\t\t'label' => lng('serversettings.logfiles_directory'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'logfiles_directory',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/var/customers/logs/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_logfiles_script' => [\n\t\t\t\t\t'label' => lng('serversettings.logfiles_script'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'logfiles_script',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_logfiles_piped' => [\n\t\t\t\t\t'label' => lng('serversettings.logfiles_piped'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'logfiles_piped',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_logfiles_format' => [\n\t\t\t\t\t'label' => lng('serversettings.logfiles_format'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'logfiles_format',\n\t\t\t\t\t'type' => (strpos(Settings::Get('system.logfiles_format'), '\"') !== false ? 'textarea' : 'text'),\n\t\t\t\t\t'string_regexp' => '/^[^\\0\\r\\n<>]*$/i',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2',\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t],\n\t\t\t\t\t'visible' => Settings::Get('system.traffictool') != 'webalizer',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_logfiles_type' => [\n\t\t\t\t\t'label' => lng('serversettings.logfiles_type'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'logfiles_type',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '1',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'1' => 'combined',\n\t\t\t\t\t\t'2' => 'vhost_combined'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'system_errorlog_level' => [\n\t\t\t\t\t'label' => lng('serversettings.errorlog_level'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'errorlog_level',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => (Settings::Get('system.webserver') == 'nginx' ? 'error' : 'warn'),\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'emerg' => 'emerg',\n\t\t\t\t\t\t'alert' => 'alert',\n\t\t\t\t\t\t'crit' => 'crit',\n\t\t\t\t\t\t'error' => 'error',\n\t\t\t\t\t\t'warn' => 'warn',\n\t\t\t\t\t\t'notice' => 'notice',\n\t\t\t\t\t\t'info' => 'info',\n\t\t\t\t\t\t'debug' => 'debug'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2',\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'system_customer_ssl_path' => [\n\t\t\t\t\t'label' => lng('serversettings.customerssl_directory'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'customer_ssl_path',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'confdir',\n\t\t\t\t\t'default' => '/etc/ssl/froxlor-custom/',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_phpappendopenbasedir' => [\n\t\t\t\t\t'label' => lng('serversettings.phpappendopenbasedir'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'phpappendopenbasedir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_deactivateddocroot' => [\n\t\t\t\t\t'label' => lng('serversettings.deactivateddocroot'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'deactivateddocroot',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_default_vhostconf' => [\n\t\t\t\t\t'label' => lng('serversettings.default_vhostconf'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'default_vhostconf',\n\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_default_sslvhostconf' => [\n\t\t\t\t\t'label' => lng('serversettings.default_sslvhostconf'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'default_sslvhostconf',\n\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') == 1,\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_include_default_vhostconf' => [\n\t\t\t\t\t'label' => lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'include_default_vhostconf',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_apacheglobaldiropt' => [\n\t\t\t\t\t'label' => lng('serversettings.apache_globaldiropt'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apacheglobaldiropt',\n\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0),\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_apachereload_command' => [\n\t\t\t\t\t'label' => lng('serversettings.apachereload_command'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apachereload_command',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^[a-z0-9\\/\\._\\- ]+$/i',\n\t\t\t\t\t'default' => '/etc/init.d/apache2 reload',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_phpreload_command' => [\n\t\t\t\t\t'label' => lng('serversettings.phpreload_command'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'phpreload_command',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^[a-z0-9\\/\\._\\- ]+$/i',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t],\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_nginx_php_backend' => [\n\t\t\t\t\t'label' => lng('serversettings.nginx_php_backend'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'nginx_php_backend',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '127.0.0.1:8888',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'nginx_fastcgiparams' => [\n\t\t\t\t\t'label' => lng('serversettings.nginx_fastcgiparams'),\n\t\t\t\t\t'settinggroup' => 'nginx',\n\t\t\t\t\t'varname' => 'fastcgiparams',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/etc/nginx/fastcgi_params',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'defaultwebsrverrhandler_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.defaultwebsrverrhandler_enabled'),\n\t\t\t\t\t'settinggroup' => 'defaultwebsrverrhandler',\n\t\t\t\t\t'varname' => 'enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t\t\t'label' => lng('serversettings.defaultwebsrverrhandler_err401'),\n\t\t\t\t\t'settinggroup' => 'defaultwebsrverrhandler',\n\t\t\t\t\t'varname' => 'err401',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2',\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t],\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t\t\t'label' => lng('serversettings.defaultwebsrverrhandler_err403'),\n\t\t\t\t\t'settinggroup' => 'defaultwebsrverrhandler',\n\t\t\t\t\t'varname' => 'err403',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2',\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t],\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'defaultwebsrverrhandler_err404' => [\n\t\t\t\t\t'label' => lng('serversettings.defaultwebsrverrhandler_err404'),\n\t\t\t\t\t'settinggroup' => 'defaultwebsrverrhandler',\n\t\t\t\t\t'varname' => 'err404',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t\t\t'label' => lng('serversettings.defaultwebsrverrhandler_err500'),\n\t\t\t\t\t'settinggroup' => 'defaultwebsrverrhandler',\n\t\t\t\t\t'varname' => 'err500',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2',\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t],\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'customredirect_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.customredirect_enabled'),\n\t\t\t\t\t'settinggroup' => 'customredirect',\n\t\t\t\t\t'varname' => 'enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'customredirect_default' => [\n\t\t\t\t\t'label' => lng('serversettings.customredirect_default'),\n\t\t\t\t\t'settinggroup' => 'customredirect',\n\t\t\t\t\t'varname' => 'default',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '1',\n\t\t\t\t\t'option_options_method' => ['\\\\Froxlor\\\\Domain\\\\Domain', 'getRedirectCodes'],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_webserver_serveradmin' => [\n\t\t\t\t\t'label' => lng('admin.webserver_serveradmin.setting'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'webserver_serveradmin',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'customer',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'customer' => lng('admin.webserver_serveradmin.customer'),\n\t\t\t\t\t\t'admin' => lng('admin.webserver_serveradmin.admin'),\n\t\t\t\t\t\t'global' => lng('admin.webserver_serveradmin.global'),\n\t\t\t\t\t\t'none' => lng('admin.webserver_serveradmin.none')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/131.ssl.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\n\nreturn [\n\t'groups' => [\n\t\t'ssl' => [\n\t\t\t'title' => lng('admin.sslsettings'),\n\t\t\t'icon' => 'fa-solid fa-shield',\n\t\t\t'fields' => [\n\t\t\t\t'system_use_ssl' => [\n\t\t\t\t\t'label' => lng('serversettings.ssl.use_ssl'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'use_ssl',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'overview_option' => true,\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_ssl_protocols' => [\n\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_protocols'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'ssl_protocols',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'TLSv1.2',\n\t\t\t\t\t'select_mode' => 'multiple',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'TLSv1' => 'TLSv1',\n\t\t\t\t\t\t'TLSv1.1' => 'TLSv1.1',\n\t\t\t\t\t\t'TLSv1.2' => 'TLSv1.2',\n\t\t\t\t\t\t'TLSv1.3' => 'TLSv1.3'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_ssl_cipher_list' => [\n\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_cipher_list'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'ssl_cipher_list',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_emptyallowed' => false,\n\t\t\t\t\t'default' => 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_tlsv13_cipher_list' => [\n\t\t\t\t\t'label' => lng('serversettings.ssl.tlsv13_cipher_list'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'tlsv13_cipher_list',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'visible' => Settings::Get('system.webserver') == \"apache2\" && Settings::Get('system.apache24') == 1,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_ssl_cert_file' => [\n\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_cert_file'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'ssl_cert_file',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '/etc/ssl/froxlor_selfsigned.pem',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_ssl_key_file' => [\n\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_key_file'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'ssl_key_file',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '/etc/ssl/froxlor_selfsigned.key',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_ssl_cert_chainfile' => [\n\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_chainfile'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'ssl_cert_chainfile',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_ssl_ca_file' => [\n\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_ca_file'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'ssl_ca_file',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_apache24_ocsp_cache_path' => [\n\t\t\t\t\t'label' => lng('serversettings.ssl.apache24_ocsp_cache_path'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'apache24_ocsp_cache_path',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_emptyallowed' => false,\n\t\t\t\t\t'default' => 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)',\n\t\t\t\t\t'visible' => Settings::Get('system.webserver') == \"apache2\" && Settings::Get('system.apache24') == 1,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_sessionticketsenabled' => [\n\t\t\t\t\t'label' => lng('admin.domain_sessionticketsenabled'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'sessionticketsenabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') && (Settings::Get('system.webserver') == \"nginx\" || (Settings::Get('system.webserver') == \"apache2\" && Settings::Get('system.apache24') == 1)),\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_leenabled' => [\n\t\t\t\t\t'label' => lng('serversettings.leenabled'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'leenabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'cronmodule' => 'froxlor/letsencrypt',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_acmeshpath' => [\n\t\t\t\t\t'label' => lng('serversettings.acmeshpath'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'acmeshpath',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/root/.acme.sh/acme.sh',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_letsencryptacmeconf' => [\n\t\t\t\t\t'label' => lng('serversettings.letsencryptacmeconf'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'letsencryptacmeconf',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/etc/apache2/conf-enabled/acme.conf',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_letsencryptca' => [\n\t\t\t\t\t'label' => lng('serversettings.letsencryptca'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'letsencryptca',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'letsencrypt',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'letsencrypt_test' => 'Let\\'s Encrypt (Test / Staging)',\n\t\t\t\t\t\t'letsencrypt' => 'Let\\'s Encrypt (Live)',\n\t\t\t\t\t\t'buypass_test' => 'Buypass (Test / Staging)',\n\t\t\t\t\t\t'buypass' => 'Buypass (Live)',\n\t\t\t\t\t\t'zerossl' => 'ZeroSSL (Live)',\n\t\t\t\t\t\t'google' => 'Google (Live)',\n\t\t\t\t\t\t'google_test' => 'Google (Test / Staging)',\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_letsencryptchallengepath' => [\n\t\t\t\t\t'label' => lng('serversettings.letsencryptchallengepath'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'letsencryptchallengepath',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_emptyallowed' => false,\n\t\t\t\t\t'default' => Froxlor::getInstallDir(),\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_letsencryptkeysize' => [\n\t\t\t\t\t'label' => lng('serversettings.letsencryptkeysize'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'letsencryptkeysize',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '2048',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'2048' => '2048',\n\t\t\t\t\t\t'3072' => '3072',\n\t\t\t\t\t\t'4096' => '4096',\n\t\t\t\t\t\t'8192' => '8192'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_leecc' => [\n\t\t\t\t\t'label' => lng('serversettings.letsencryptecc'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'leecc',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '0',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'0' => '-',\n\t\t\t\t\t\t'256' => 'ec-256',\n\t\t\t\t\t\t'384' => 'ec-384'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_letsencryptreuseold' => [\n\t\t\t\t\t'label' => lng('serversettings.letsencryptreuseold'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'letsencryptreuseold',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_le_domain_dnscheck' => [\n\t\t\t\t\t'label' => lng('serversettings.le_domain_dnscheck'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'le_domain_dnscheck',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_le_domain_dnscheck_resolver' => [\n\t\t\t\t\t'label' => lng('serversettings.le_domain_dnscheck_resolver'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'le_domain_dnscheck_resolver',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'validate_ip',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_le_renew_services' => [\n\t\t\t\t\t'label' => lng('serversettings.le_renew_services'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'le_renew_services',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'select_mode' => 'multiple',\n\t\t\t\t\t'option_emptyallowed' => true,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'' => lng('panel.none_value'),\n\t\t\t\t\t\t'postfix' => 'postfix (smtp)',\n\t\t\t\t\t\t'dovecot' => 'dovecot <2.4 (imap/pop3)',\n\t\t\t\t\t\t'dovecot24' => 'dovecot >=2.4 (imap/pop3)',\n\t\t\t\t\t\t'proftpd' => 'proftpd (ftp)',\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingFieldInsertUpdateServicesTask',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_le_renew_hook' => [\n\t\t\t\t\t'label' => lng('serversettings.le_renew_hook'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'le_renew_hook',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^[a-z0-9\\/\\._\\- ]+$/i',\n\t\t\t\t\t'default' => 'systemctl restart postfix dovecot proftpd',\n\t\t\t\t\t'save_method' => 'storeSettingFieldInsertUpdateServicesTask',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/135.fcgid.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'fcgid' => [\n\t\t\t'title' => lng('admin.fcgid_settings'),\n\t\t\t'icon' => 'fa-brands fa-php',\n\t\t\t'websrv_avail' => [\n\t\t\t\t'apache2',\n\t\t\t],\n\t\t\t'fields' => [\n\t\t\t\t'system_mod_fcgid' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkFcgidPhpFpm'\n\t\t\t\t\t],\n\t\t\t\t\t'overview_option' => true,\n\t\t\t\t\t'requires_reconf' => ['http', 'system:fcgid']\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_configdir' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.configdir'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_configdir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'confdir',\n\t\t\t\t\t'default' => '/var/www/php-fcgi-scripts/',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkPathConflicts'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['system:fcgid']\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_tmpdir' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.tmpdir'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_tmpdir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/var/customers/tmp/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['http']\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_peardir' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.peardir'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_peardir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'string_delimiter' => ':',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '/usr/share/php/:/usr/share/php5/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_wrapper' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.wrapper'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_wrapper',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t0 => 'ScriptAlias',\n\t\t\t\t\t\t1 => 'FcgidWrapper'\n\t\t\t\t\t],\n\t\t\t\t\t'default' => 1,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t],\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_starter' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.starter'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_starter',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'min' => 0,\n\t\t\t\t\t'default' => 0,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_maxrequests' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.maxrequests'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_maxrequests',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'default' => 250,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_defaultini' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.defaultini'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_defaultini',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '1',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Http\\\\PhpConfig',\n\t\t\t\t\t\t'getPhpConfigs'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mod_fcgid_idle_timeout' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.idle_timeout'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mod_fcgid_idle_timeout',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'default' => 30,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/136.phpfpm.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'groups' => [\n\t\t'phpfpm' => [\n\t\t\t'title' => lng('admin.phpfpm_settings'),\n\t\t\t'icon' => 'fa-brands fa-php',\n\t\t\t'fields' => [\n\t\t\t\t'phpfpm_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.phpfpm'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkFcgidPhpFpm'\n\t\t\t\t\t],\n\t\t\t\t\t'overview_option' => true,\n\t\t\t\t\t'requires_reconf' => ['http', 'system:php-fpm']\n\t\t\t\t],\n\t\t\t\t'phpfpm_defaultini' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.defaultini'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'defaultini',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '1',\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Http\\\\PhpConfig',\n\t\t\t\t\t\t'getPhpConfigs'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'phpfpm_aliasconfigdir' => [\n\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.aliasconfigdir'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'aliasconfigdir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'confdir',\n\t\t\t\t\t'default' => '/var/www/php-fpm/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'phpfpm_tmpdir' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.tmpdir'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'tmpdir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/var/customers/tmp/',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'phpfpm_peardir' => [\n\t\t\t\t\t'label' => lng('serversettings.mod_fcgid.peardir'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'peardir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'string_delimiter' => ':',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '/usr/share/php/:/usr/share/php5/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'phpfpm_envpath' => [\n\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.envpath'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'envpath',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'string_delimiter' => ':',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '/usr/local/bin:/usr/bin:/bin',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'phpfpm_fastcgi_ipcdir' => [\n\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.ipcdir'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'fastcgi_ipcdir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/var/lib/apache2/fastcgi/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'phpfpm_use_mod_proxy' => [\n\t\t\t\t\t'label' => lng('phpfpm.use_mod_proxy'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'use_mod_proxy',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'visible' => Settings::Get('system.apache24'),\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'phpfpm_ini_flags' => [\n\t\t\t\t\t'label' => lng('phpfpm.ini_flags'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'ini_flags',\n\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'phpfpm_ini_values' => [\n\t\t\t\t\t'label' => lng('phpfpm.ini_values'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'ini_values',\n\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'phpfpm_ini_admin_flags' => [\n\t\t\t\t\t'label' => lng('phpfpm.ini_admin_flags'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'ini_admin_flags',\n\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'phpfpm_ini_admin_values' => [\n\t\t\t\t\t'label' => lng('phpfpm.ini_admin_values'),\n\t\t\t\t\t'settinggroup' => 'phpfpm',\n\t\t\t\t\t'varname' => 'ini_admin_values',\n\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/137.perl.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'perl' => [\n\t\t\t'title' => lng('admin.perl_settings'),\n\t\t\t'icon' => 'fa-solid fa-code',\n\t\t\t'fields' => [\n\t\t\t\t'perl_suexecworkaround' => [\n\t\t\t\t\t'label' => lng('serversettings.perl.suexecworkaround'),\n\t\t\t\t\t'settinggroup' => 'perl',\n\t\t\t\t\t'varname' => 'suexecworkaround',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'perl_suexecpath' => [\n\t\t\t\t\t'label' => lng('serversettings.perl.suexeccgipath'),\n\t\t\t\t\t'settinggroup' => 'perl',\n\t\t\t\t\t'varname' => 'suexecpath',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/var/www/cgi-bin/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'apache2'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'serversettings_perl_server' => [\n\t\t\t\t\t'label' => lng('serversettings.perl_server'),\n\t\t\t\t\t'settinggroup' => 'serversettings',\n\t\t\t\t\t'varname' => 'perl_server',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => 'unix:/var/run/nginx/cgiwrap-dispatch.sock',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'websrv_avail' => [\n\t\t\t\t\t\t'nginx'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/140.statistics.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'groups' => [\n\t\t'statistics' => [\n\t\t\t'title' => lng('admin.statisticsettings'),\n\t\t\t'icon' => 'fa-solid fa-chart-area',\n\t\t\t'fields' => [\n\t\t\t\t'system_traffictool' => [\n\t\t\t\t\t'label' => lng('serversettings.traffictool.toolselect'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'traffictool',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'goaccess',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'webalizer' => lng('serversettings.traffictool.webalizer'),\n\t\t\t\t\t\t'awstats' => lng('serversettings.traffictool.awstats'),\n\t\t\t\t\t\t'goaccess' => lng('serversettings.traffictool.goaccess')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingUpdateTrafficTool',\n\t\t\t\t\t'requires_reconf' => ['system']\n\t\t\t\t],\n\t\t\t\t'system_webalizer_quiet' => [\n\t\t\t\t\t'label' => lng('serversettings.webalizer_quiet'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'webalizer_quiet',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 2,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t0 => lng('admin.webalizer.normal'),\n\t\t\t\t\t\t1 => lng('admin.webalizer.quiet'),\n\t\t\t\t\t\t2 => lng('admin.webalizer.veryquiet')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.traffictool') == 'webalizer'\n\t\t\t\t],\n\t\t\t\t'system_awstats_path' => [\n\t\t\t\t\t'label' => lng('serversettings.awstats_path'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'awstats_path',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/usr/share/awstats/tools/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.traffictool') == 'awstats'\n\t\t\t\t],\n\t\t\t\t'system_awstats_awstatspath' => [\n\t\t\t\t\t'label' => lng('serversettings.awstats_awstatspath'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'awstats_awstatspath',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/usr/lib/cgi-bin/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.traffictool') == 'awstats'\n\t\t\t\t],\n\t\t\t\t'system_awstats_conf' => [\n\t\t\t\t\t'label' => lng('serversettings.awstats_conf'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'awstats_conf',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/etc/awstats/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.traffictool') == 'awstats',\n\t\t\t\t\t'requires_reconf' => ['system:awstats']\n\t\t\t\t],\n\t\t\t\t'system_awstats_icons' => [\n\t\t\t\t\t'label' => lng('serversettings.awstats_icons'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'awstats_icons',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/usr/share/awstats/icon/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.traffictool') == 'awstats'\n\t\t\t\t],\n\t\t\t\t'system_awstats_logformat' => [\n\t\t\t\t\t'label' => lng('serversettings.awstats.logformat'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'awstats_logformat',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '1',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.traffictool') == 'awstats',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/150.mail.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'mail' => [\n\t\t\t'title' => lng('admin.mailserversettings'),\n\t\t\t'icon' => 'fa-solid fa-envelope',\n\t\t\t'fields' => [\n\t\t\t\t'system_vmail_uid' => [\n\t\t\t\t\t'label' => lng('serversettings.vmail_uid'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'vmail_uid',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'default' => 2000,\n\t\t\t\t\t'min' => 2,\n\t\t\t\t\t'max' => 65535,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'requires_reconf' => ['smtp']\n\t\t\t\t],\n\t\t\t\t'system_vmail_gid' => [\n\t\t\t\t\t'label' => lng('serversettings.vmail_gid'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'vmail_gid',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'default' => 2000,\n\t\t\t\t\t'min' => 2,\n\t\t\t\t\t'max' => 65535,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'requires_reconf' => ['smtp']\n\t\t\t\t],\n\t\t\t\t'system_vmail_homedir' => [\n\t\t\t\t\t'label' => lng('serversettings.vmail_homedir'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'vmail_homedir',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/var/customers/mail/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['smtp']\n\t\t\t\t],\n\t\t\t\t'system_vmail_maildirname' => [\n\t\t\t\t\t'label' => lng('serversettings.vmail_maildirname'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'vmail_maildirname',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => 'Maildir',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'panel_sendalternativemail' => [\n\t\t\t\t\t'label' => lng('serversettings.sendalternativemail'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'sendalternativemail',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mail_quota_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_quota_enabled'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_quota_enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_mail_quota' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_quota'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mail_quota',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'default' => 100,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'catchall_catchall_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.catchall_enabled'),\n\t\t\t\t\t'settinggroup' => 'catchall',\n\t\t\t\t\t'varname' => 'catchall_enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingResetCatchall'\n\t\t\t\t],\n\t\t\t\t'system_mailtraffic_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.mailtraffic_enabled'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mailtraffic_enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_mdaserver' => [\n\t\t\t\t\t'label' => lng('serversettings.mdaserver'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mdaserver',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'dovecot',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'courier' => 'Courier',\n\t\t\t\t\t\t'dovecot' => 'Dovecot'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_mdalog' => [\n\t\t\t\t\t'label' => lng('serversettings.mdalog'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mdalog',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/var/log/mail.log',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_mtaserver' => [\n\t\t\t\t\t'label' => lng('serversettings.mtaserver'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mtaserver',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'postfix',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'exim4' => 'Exim4',\n\t\t\t\t\t\t'postfix' => 'Postfix'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_mtalog' => [\n\t\t\t\t\t'label' => lng('serversettings.mtalog'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mtalog',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/var/log/mail.log',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'mail_enable_allow_sender' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_enable_allow_sender'),\n\t\t\t\t\t'settinggroup' => 'mail',\n\t\t\t\t\t'varname' => 'enable_allow_sender',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'mail_allow_external_domains' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_allow_external_domains'),\n\t\t\t\t\t'settinggroup' => 'mail',\n\t\t\t\t\t'varname' => 'allow_external_domains',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/155.ftpserver.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'ftpserver' => [\n\t\t\t'title' => lng('admin.ftpserversettings'),\n\t\t\t'icon' => 'fa-solid fa-arrow-right-arrow-left',\n\t\t\t'fields' => [\n\t\t\t\t'system_ftpserver' => [\n\t\t\t\t\t'label' => lng('admin.ftpserver'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'ftpserver',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'proftpd',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'proftpd' => 'Proftpd',\n\t\t\t\t\t\t'pureftpd' => 'Pureftpd'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/160.nameserver.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'groups' => [\n\t\t'nameserver' => [\n\t\t\t'title' => lng('admin.nameserversettings'),\n\t\t\t'icon' => 'fa-solid fa-globe',\n\t\t\t'fields' => [\n\t\t\t\t'system_bind_enable' => [\n\t\t\t\t\t'label' => lng('serversettings.bindenable'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'bind_enable',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'overview_option' => true,\n\t\t\t\t\t'requires_reconf' => ['dns']\n\t\t\t\t],\n\t\t\t\t'system_dnsenabled' => [\n\t\t\t\t\t'label' => lng('serversettings.dnseditorenable'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'dnsenabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_dns_server' => [\n\t\t\t\t\t'label' => lng('serversettings.dns_server'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'dns_server',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'Bind',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'Bind' => 'Bind9',\n\t\t\t\t\t\t'PowerDNS' => 'PowerDNS'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'requires_reconf' => ['dns']\n\t\t\t\t],\n\t\t\t\t'system_bindconf_directory' => [\n\t\t\t\t\t'label' => lng('serversettings.bindconf_directory'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'bindconf_directory',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'dir',\n\t\t\t\t\t'default' => '/etc/bind/',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'visible' => Settings::Get('system.dns_server') == 'Bind',\n\t\t\t\t\t'requires_reconf' => ['dns:bind']\n\t\t\t\t],\n\t\t\t\t'system_bindreload_command' => [\n\t\t\t\t\t'label' => lng('serversettings.bindreload_command'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'bindreload_command',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^[a-z0-9\\/\\._\\- ]+$/i',\n\t\t\t\t\t'default' => '/etc/init.d/bind9 reload',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_nameservers' => [\n\t\t\t\t\t'label' => lng('serversettings.nameservers'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'nameservers',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^(([a-z0-9\\-\\._]+, ?)*[a-z0-9\\-\\._]+)?$/i',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingFieldInsertBindTask'\n\t\t\t\t],\n\t\t\t\t'system_mxservers' => [\n\t\t\t\t\t'label' => lng('serversettings.mxservers'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mxservers',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^(([0-9]+ [a-z0-9\\-\\._]+, ?)*[0-9]+ [a-z0-9\\-\\._]+)?$/i',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_axfrservers' => [\n\t\t\t\t\t'label' => lng('serversettings.axfrservers'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'axfrservers',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'validate_ip_incl_private',\n\t\t\t\t\t'string_delimiter' => ',',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_powerdns_mode' => [\n\t\t\t\t\t'label' => lng('serversettings.powerdns_mode'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'powerdns_mode',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'Native',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'Native' => 'Native',\n\t\t\t\t\t\t'Master' => 'Master'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'visible' => Settings::Get('system.dns_server') == 'PowerDNS',\n\t\t\t\t],\n\t\t\t\t'system_dns_createmailentry' => [\n\t\t\t\t\t'label' => lng('serversettings.mail_also_with_mxservers'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'dns_createmailentry',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_dns_createcaaentry' => [\n\t\t\t\t\t'label' => lng('serversettings.caa_entry'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'dns_createcaaentry',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'caa_caa_entry' => [\n\t\t\t\t\t'label' => lng('serversettings.caa_entry_custom'),\n\t\t\t\t\t'settinggroup' => 'caa',\n\t\t\t\t\t'varname' => 'caa_entry',\n\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'system_defaultttl' => [\n\t\t\t\t\t'label' => lng('serversettings.defaultttl'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'defaultttl',\n\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t'default' => 604800, /* 1 week */\n\t\t\t\t\t'min' => 3600, /* 1 hour */\n\t\t\t\t\t'max' => 2147483647, /* integer max */\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'system_soaemail' => [\n\t\t\t\t\t'label' => lng('serversettings.soaemail'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'soaemail',\n\t\t\t\t\t'type' => 'email',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/170.logger.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'logging' => [\n\t\t\t'title' => lng('admin.loggersettings'),\n\t\t\t'icon' => 'fa-solid fa-file-lines',\n\t\t\t'fields' => [\n\t\t\t\t'logger_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.logger.enable'),\n\t\t\t\t\t'settinggroup' => 'logger',\n\t\t\t\t\t'varname' => 'enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'overview_option' => true\n\t\t\t\t],\n\t\t\t\t'logger_severity' => [\n\t\t\t\t\t'label' => lng('serversettings.logger.severity'),\n\t\t\t\t\t'settinggroup' => 'logger',\n\t\t\t\t\t'varname' => 'severity',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 1,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t1 => lng('admin.logger.normal'),\n\t\t\t\t\t\t2 => lng('admin.logger.paranoid')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'logger_logtypes' => [\n\t\t\t\t\t'label' => lng('serversettings.logger.types'),\n\t\t\t\t\t'settinggroup' => 'logger',\n\t\t\t\t\t'varname' => 'logtypes',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 'syslog,mysql',\n\t\t\t\t\t'select_mode' => 'multiple',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'syslog' => 'syslog',\n\t\t\t\t\t\t'file' => 'file',\n\t\t\t\t\t\t'mysql' => 'mysql'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'logger_logfile' => [\n\t\t\t\t\t'label' => lng('serversettings.logger.logfile'),\n\t\t\t\t\t'settinggroup' => 'logger',\n\t\t\t\t\t'varname' => 'logfile',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'logger_log_cron' => [\n\t\t\t\t\t'label' => lng('serversettings.logger.logcron'),\n\t\t\t\t\t'settinggroup' => 'logger',\n\t\t\t\t\t'varname' => 'log_cron',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 0,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t0 => lng('serversettings.logger.logcronoption.never'),\n\t\t\t\t\t\t1 => lng('serversettings.logger.logcronoption.once'),\n\t\t\t\t\t\t2 => lng('serversettings.logger.logcronoption.always')\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/180.antispam.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'antispam' => [\n\t\t\t'title' => lng('admin.antispam_settings'),\n\t\t\t'icon' => 'fa-solid fa-clipboard-check',\n\t\t\t'fields' => [\n\t\t\t\t'antispam_activated' => [\n\t\t\t\t\t'label' => lng('antispam.activated'),\n\t\t\t\t\t'settinggroup' => 'antispam',\n\t\t\t\t\t'varname' => 'activated',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'overview_option' => true,\n\t\t\t\t\t'save_method' => 'storeSettingFieldInsertAntispamTask',\n\t\t\t\t],\n\t\t\t\t'antispam_config_file' => [\n\t\t\t\t\t'label' => lng('antispam.config_file'),\n\t\t\t\t\t'settinggroup' => 'antispam',\n\t\t\t\t\t'varname' => 'config_file',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/etc/rspamd/local.d/froxlor_settings.conf',\n\t\t\t\t\t'save_method' => 'storeSettingFieldInsertAntispamTask',\n\t\t\t\t\t'requires_reconf' => ['antispam']\n\t\t\t\t],\n\t\t\t\t'antispam_reload_command' => [\n\t\t\t\t\t'label' => lng('antispam.reload_command'),\n\t\t\t\t\t'settinggroup' => 'antispam',\n\t\t\t\t\t'varname' => 'reload_command',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^[a-z0-9\\/\\._\\- ]+$/i',\n\t\t\t\t\t'default' => 'service rspamd restart',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'antispam_default_bypass_spam' => [\n\t\t\t\t\t'label' => lng('antispam.default_bypass_spam'),\n\t\t\t\t\t'settinggroup' => 'antispam',\n\t\t\t\t\t'varname' => 'default_bypass_spam',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 2,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t1 => lng('antispam.default_select.on_changeable'),\n\t\t\t\t\t\t2 => lng('antispam.default_select.off_changeable'),\n\t\t\t\t\t\t3 => lng('antispam.default_select.on_unchangeable'),\n\t\t\t\t\t\t4 => lng('antispam.default_select.off_unchangeable'),\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'antispam_default_spam_rewrite_subject' => [\n\t\t\t\t\t'label' => lng('antispam.default_spam_rewrite_subject'),\n\t\t\t\t\t'settinggroup' => 'antispam',\n\t\t\t\t\t'varname' => 'default_spam_rewrite_subject',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 1,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t1 => lng('antispam.default_select.on_changeable'),\n\t\t\t\t\t\t2 => lng('antispam.default_select.off_changeable'),\n\t\t\t\t\t\t3 => lng('antispam.default_select.on_unchangeable'),\n\t\t\t\t\t\t4 => lng('antispam.default_select.off_unchangeable'),\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'antispam_default_policy_greylist' => [\n\t\t\t\t\t'label' => lng('antispam.default_policy_greylist'),\n\t\t\t\t\t'settinggroup' => 'antispam',\n\t\t\t\t\t'varname' => 'default_policy_greylist',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => 1,\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t1 => lng('antispam.default_select.on_changeable'),\n\t\t\t\t\t\t2 => lng('antispam.default_select.off_changeable'),\n\t\t\t\t\t\t3 => lng('antispam.default_select.on_unchangeable'),\n\t\t\t\t\t\t4 => lng('antispam.default_select.off_unchangeable'),\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true\n\t\t\t\t],\n\t\t\t\t'antispam_dkim_keylength' => [\n\t\t\t\t\t'label' => lng('antispam.dkim_keylength'),\n\t\t\t\t\t'settinggroup' => 'antispam',\n\t\t\t\t\t'varname' => 'dkim_keylength',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => '1024',\n\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t'1024' => '1024 Bit',\n\t\t\t\t\t\t'2048' => '2048 Bit'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingFieldInsertBindTask',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t],\n\t\t\t\t'spf_use_spf' => [\n\t\t\t\t\t'label' => lng('spf.use_spf'),\n\t\t\t\t\t'settinggroup' => 'spf',\n\t\t\t\t\t'varname' => 'use_spf',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t],\n\t\t\t\t'spf_spf_entry' => [\n\t\t\t\t\t'label' => lng('spf.spf_entry'),\n\t\t\t\t\t'settinggroup' => 'spf',\n\t\t\t\t\t'varname' => 'spf_entry',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^v=spf[a-z0-9:~?\\s\\.\\-\\/]+$/i',\n\t\t\t\t\t'default' => 'v=spf1 a mx -all',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t],\n\t\t\t\t'dmarc_use_dmarc' => [\n\t\t\t\t\t'label' => lng('dmarc.use_dmarc'),\n\t\t\t\t\t'settinggroup' => 'dmarc',\n\t\t\t\t\t'varname' => 'use_dmarc',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t],\n\t\t\t\t'dmarc_dmarc_entry' => [\n\t\t\t\t\t'label' => lng('dmarc.dmarc_entry'),\n\t\t\t\t\t'settinggroup' => 'dmarc',\n\t\t\t\t\t'varname' => 'dmarc_entry',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_regexp' => '/^v=dmarc1(.+)$/i',\n\t\t\t\t\t'default' => 'v=DMARC1; p=none;',\n\t\t\t\t\t'save_method' => 'storeSettingField'\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/210.security.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'groups' => [\n\t\t'security' => [\n\t\t\t'title' => lng('admin.security_settings'),\n\t\t\t'icon' => 'fa-solid fa-user-lock',\n\t\t\t'fields' => [\n\t\t\t\t'panel_unix_names' => [\n\t\t\t\t\t'label' => lng('serversettings.unix_names'),\n\t\t\t\t\t'settinggroup' => 'panel',\n\t\t\t\t\t'varname' => 'unix_names',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => true,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_mailpwcleartext' => [\n\t\t\t\t\t'label' => lng('serversettings.mailpwcleartext'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'mailpwcleartext',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_passwordcryptfunc' => [\n\t\t\t\t\t'label' => lng('serversettings.passwordcryptfunc'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'passwordcryptfunc',\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'default' => PASSWORD_DEFAULT,\n\t\t\t\t\t'option_options_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\System\\\\Crypt',\n\t\t\t\t\t\t'getAvailablePasswordHashes'\n\t\t\t\t\t],\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_allow_error_report_admin' => [\n\t\t\t\t\t'label' => lng('serversettings.allow_error_report_admin'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'allow_error_report_admin',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_allow_error_report_customer' => [\n\t\t\t\t\t'label' => lng('serversettings.allow_error_report_customer'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'allow_error_report_customer',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_allow_customer_shell' => [\n\t\t\t\t\t'label' => lng('serversettings.allow_allow_customer_shell'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'allow_customer_shell',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_available_shells' => [\n\t\t\t\t\t'label' => lng('serversettings.available_shells'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'available_shells',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_emptyallowed' => true,\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_froxlorusergroup' => [\n\t\t\t\t\t'label' => lng('serversettings.froxlorusergroup'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'froxlorusergroup',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'default' => '',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'plausibility_check_method' => [\n\t\t\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Check',\n\t\t\t\t\t\t'checkLocalGroup'\n\t\t\t\t\t],\n\t\t\t\t\t'visible' => Settings::Get('system.nssextrausers'),\n\t\t\t\t\t'advanced_mode' => true,\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/220.quota.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'groups' => [\n\t\t'diskquota' => [\n\t\t\t'title' => lng('diskquota'),\n\t\t\t'icon' => 'fa-solid fa-sliders',\n\t\t\t'advanced_mode' => true,\n\t\t\t'fields' => [\n\t\t\t\t'system_diskquota_enabled' => [\n\t\t\t\t\t'label' => lng('serversettings.diskquota_enabled'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'diskquota_enabled',\n\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t'default' => false,\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'overview_option' => true\n\t\t\t\t],\n\t\t\t\t'system_diskquota_repquota_path' => [\n\t\t\t\t\t'label' => lng('serversettings.diskquota_repquota_path.description'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'diskquota_repquota_path',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/usr/sbin/repquota',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_diskquota_quotatool_path' => [\n\t\t\t\t\t'label' => lng('serversettings.diskquota_quotatool_path.description'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'diskquota_quotatool_path',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/usr/bin/quotatool',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t],\n\t\t\t\t'system_diskquota_customer_partition' => [\n\t\t\t\t\t'label' => lng('serversettings.diskquota_customer_partition.description'),\n\t\t\t\t\t'settinggroup' => 'system',\n\t\t\t\t\t'varname' => 'diskquota_customer_partition',\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t'string_type' => 'file',\n\t\t\t\t\t'default' => '/dev/root',\n\t\t\t\t\t'save_method' => 'storeSettingField',\n\t\t\t\t\t'required_otp' => true\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "actions/admin/settings/index.html",
    "content": ""
  },
  {
    "path": "actions/index.html",
    "content": ""
  },
  {
    "path": "admin_admins.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n$id = (int)Request::any('id');\n\nif (($page == 'admins' || $page == 'overview') && $userinfo['change_serversettings'] == '1') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_admins\");\n\n\t\ttry {\n\t\t\t$admin_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.admins.php';\n\t\t\t$collection = (new Collection(Admins::class, $userinfo))\n\t\t\t\t->withPagination($admin_list_data['admin_list']['columns'], $admin_list_data['admin_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $admin_list_data, 'admin_list'),\n\t\t\t'actions_links' => [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'admins', 'page' => $page, 'action' => 'add']),\n\t\t\t\t\t'label' => lng('admin.admin_add')\n\t\t\t\t]\n\t\t\t]\n\t\t]);\n\t} elseif ($action == 'su') {\n\t\ttry {\n\t\t\t$json_result = Admins::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$destination_admin = $result['loginname'];\n\n\t\tif ($destination_admin != '' && $result['adminid'] != $userinfo['userid']) {\n\t\t\t$result['switched_user'] = CurrentUser::getData();\n\t\t\t$result['adminsession'] = 1;\n\t\t\t$result['userid'] = $result['adminid'];\n\t\t\tsession_regenerate_id(true);\n\t\t\tCurrentUser::setData($result);\n\n\t\t\t$log->logAction(\n                FroxlorLogger::ADM_ACTION,\n                LOG_INFO,\n                \"switched adminuser and is now '\" . $destination_admin . \"'\"\n            );\n\t\t\tResponse::redirectTo('admin_index.php');\n\t\t} else {\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t'action' => 'login'\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Admins::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['loginname'] != '') {\n\t\t\tif ($result['adminid'] == $userinfo['userid']) {\n\t\t\t\tResponse::standardError('youcantdeleteyourself');\n\t\t\t}\n\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\tAdmins::getLocal($userinfo, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t])->delete();\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('admin_admin_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['loginname']);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tAdmins::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$ipaddress = [];\n\t\t\t$ipaddress[-1] = lng('admin.allips');\n\t\t\t$ipsandports_stmt = Database::query(\"\n\t\t\t\tSELECT `id`, `ip` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` GROUP BY `ip` ORDER BY `ip` ASC\n\t\t\t\");\n\t\t\twhile ($row = $ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$ipaddress[$row['id']] = $row['ip'];\n\t\t\t}\n\n\t\t\t$admin_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/admin/formfield.admin_add.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'admins']),\n\t\t\t\t'formdata' => $admin_add_data['admin_add']\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Admins::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['loginname'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tAdmins::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$dec_places = Settings::Get('panel.decimal_places');\n\t\t\t\t$result['traffic'] = round($result['traffic'] / (1024 * 1024), $dec_places);\n\t\t\t\t$result['diskspace'] = round($result['diskspace'] / 1024, $dec_places);\n\t\t\t\t$result['email'] = $idna_convert->decode($result['email']);\n\n\t\t\t\t$ipaddress = [];\n\t\t\t\t$ipaddress[-1] = lng('admin.allips');\n\t\t\t\t$ipsandports_stmt = Database::query(\"\n\t\t\t\t\tSELECT `id`, `ip` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` GROUP BY `ip` ORDER BY `ip` ASC\n\t\t\t\t\");\n\t\t\t\twhile ($row = $ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$ipaddress[$row['id']] = $row['ip'];\n\t\t\t\t}\n\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t$admin_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/admin/formfield.admin_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'admins', 'id' => $id]),\n\t\t\t\t\t'formdata' => $admin_edit_data['admin_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "admin_apcuinfo.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @author     Janos Muzsi <muzsij@hypernics.hu>\n * @author     Ralf Becker <beckerr@php.net>\n * @author     Rasmus Lerdorf <rasmus@php.net>\n * @author     Ilia Alshanetsky <ilia@prohost.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n *\n * Based on https://github.com/krakjoe/apcu/blob/master/apc.php, which is\n * licensed under the PHP licence (version 3.01), which can be viewed\n * online at https://www.php.net/license/3_01.txt\n */\n\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\UI\\HTML;\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\n$horizontal_bar_size = 950; // 1280px window width\n\nif ($action == 'delete' && function_exists('apcu_clear_cache') && $userinfo['change_serversettings'] == '1') {\n\tif (Request::post('send') == 'send') {\n\t\tapcu_clear_cache();\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"cleared APCu cache\");\n\t\theader('Location: ' . $linker->getLink([\n\t\t\t\t'section' => 'apcuinfo',\n\t\t\t\t'page' => 'showinfo'\n\t\t\t]));\n\t\texit();\n\t} else {\n\t\tHTML::askYesNo('cache_reallydelete', $filename, [\n\t\t\t'page' => $page,\n\t\t\t'action' => 'delete',\n\t\t], '', [\n\t\t\t'section' => 'apcuinfo',\n\t\t\t'page' => 'showinfo'\n\t\t]);\n\t}\n}\n\nif (!function_exists('apcu_cache_info') || !function_exists('apcu_sma_info')) {\n\tResponse::standardError('no_apcuinfo');\n}\n\nif ($page == 'showinfo' && $userinfo['change_serversettings'] == '1') {\n\t$cache = apcu_cache_info();\n\t$mem = apcu_sma_info();\n\t$time = time();\n\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_apcuinfo\");\n\n\t// check for possible empty values that are used in the templates\n\tif (!isset($cache['file_upload_progress'])) {\n\t\t$cache['file_upload_progress'] = lng('logger.unknown');\n\t}\n\n\tif (!isset($cache['num_expunges'])) {\n\t\t$cache['num_expunges'] = lng('logger.unknown');\n\t}\n\n\t$overview = [\n\t\t'mem_size' => $mem['num_seg'] * $mem['seg_size'],\n\t\t'mem_avail' => $mem['avail_mem'],\n\t\t'mem_used' => ($mem['num_seg'] * $mem['seg_size']) - $mem['avail_mem'],\n\t\t'seg_size' => bsize($mem['seg_size']),\n\t\t'num_hits' => $cache['num_hits'],\n\t\t'num_misses' => $cache['num_misses'],\n\t\t'num_inserts' => $cache['num_inserts'],\n\t\t'req_rate_user' => sprintf(\"%.2f\",\n\t\t\t$cache['num_hits'] ? (($cache['num_hits'] + $cache['num_misses']) / ($time - $cache['start_time'])) : 0),\n\t\t'hit_rate_user' => sprintf(\"%.2f\",\n\t\t\t$cache['num_hits'] ? (($cache['num_hits']) / ($time - $cache['start_time'])) : 0),\n\t\t'miss_rate_user' => sprintf(\"%.2f\",\n\t\t\t$cache['num_misses'] ? (($cache['num_misses']) / ($time - $cache['start_time'])) : 0),\n\t\t'insert_rate_user' => sprintf(\"%.2f\",\n\t\t\t$cache['num_inserts'] ? (($cache['num_inserts']) / ($time - $cache['start_time'])) : 0),\n\t\t'apcversion' => phpversion('apcu'),\n\t\t'phpversion' => phpversion(),\n\t\t'number_vars' => $cache['num_entries'],\n\t\t'size_vars' => bsize($cache['mem_size']),\n\t\t'num_hits_and_misses' => 0 >= ($cache['num_hits'] + $cache['num_misses']) ? 1 : ($cache['num_hits'] + $cache['num_misses']),\n\t\t'file_upload_progress' => $cache['file_upload_progress'],\n\t\t'num_expunges' => $cache['num_expunges'],\n\t\t'host' => (function_exists('gethostname')\n\t\t\t? gethostname()\n\t\t\t: (php_uname('n')\n\t\t\t\t?: (empty($_SERVER['SERVER_NAME'])\n\t\t\t\t\t? $_SERVER['HOST_NAME']\n\t\t\t\t\t: $_SERVER['SERVER_NAME']\n\t\t\t\t)\n\t\t\t)\n\t\t),\n\t\t'server' => $_SERVER['SERVER_SOFTWARE'] ?: '',\n\t\t'start_time' => $cache['start_time'],\n\t\t'uptime' => duration($cache['start_time'])\n\t];\n\n\t$overview['mem_used_percentage'] = number_format(($overview['mem_used'] / $overview['mem_size']) * 100, 1);\n\t$overview['num_hits_percentage'] = number_format(($overview['num_hits'] / $overview['num_hits_and_misses']) * 100,\n\t\t1);\n\t$overview['num_misses_percentage'] = number_format(($overview['num_misses'] / $overview['num_hits_and_misses']) * 100,\n\t\t1);\n\t$overview['readable'] = [\n\t\t'mem_size' => bsize($overview['mem_size']),\n\t\t'mem_avail' => bsize($overview['mem_avail']),\n\t\t'mem_used' => bsize($overview['mem_used']),\n\t\t'num_hits' => number_format($overview['num_hits']),\n\t\t'num_misses' => number_format($overview['num_misses']),\n\t\t'number_vars' => number_format($overview['number_vars']),\n\t];\n\n\t$overview['runtimelines'] = [];\n\tforeach (ini_get_all('apcu') as $name => $v) {\n\t\t$value = $v['local_value'];\n\t\t$overview['runtimelines'][$name] = $value;\n\t}\n\n\t// Fragementation: (freeseg - 1) / total_seg\n\t$nseg = $freeseg = $fragsize = $freetotal = 0;\n\tfor ($i = 0; $i < $mem['num_seg']; $i++) {\n\t\t$ptr = 0;\n\t\tforeach ($mem['block_lists'][$i] as $block) {\n\t\t\tif ($block['offset'] != $ptr) {\n\t\t\t\t++$nseg;\n\t\t\t}\n\t\t\t$ptr = $block['offset'] + $block['size'];\n\t\t\t/* Only consider blocks <5M for the fragmentation % */\n\t\t\tif ($block['size'] < (5 * 1024 * 1024)) {\n\t\t\t\t$fragsize += $block['size'];\n\t\t\t}\n\t\t\t$freetotal += $block['size'];\n\t\t}\n\t\t$freeseg += count($mem['block_lists'][$i]);\n\t}\n\n\t$overview['fragmentation'] = [];\n\tif ($freeseg > 1) {\n\t\t$overview['fragmentation']['used_percentage'] = number_format(($fragsize / $freetotal) * 100, 1);\n\t\t$overview['fragmentation']['used_bytes'] = $fragsize;\n\t\t$overview['fragmentation']['total_bytes'] = $freetotal;\n\t\t$overview['fragmentation']['num_frags'] = $freeseg;\n\t\t$overview['fragmentation']['readable'] = [\n\t\t\t'used_bytes' => bsize($fragsize),\n\t\t\t'total_bytes' => bsize($freetotal),\n\t\t\t'num_frags' => number_format($freeseg)\n\t\t];\n\t} else {\n\t\t$overview['fragmentation'] = 0;\n\t}\n\n\tUI::view('settings/apcuinfo.html.twig', [\n\t\t'apcuinfo' => $overview\n\t]);\n}\n// pretty printer for byte values\nfunction bsize($size)\n{\n\t$i = 0;\n\t$val = ['b', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];\n\twhile (($size / 1024) > 1) {\n\t\t$size /= 1024;\n\t\t++$i;\n\t}\n\treturn sprintf(\n\t\t'%.2f%s%s',\n\t\t$size,\n\t\t'',\n\t\t$val[$i]\n\t);\n}\n\nfunction duration($ts)\n{\n\tglobal $time;\n\t$years = (int)((($time - $ts) / (7 * 86400)) / 52.177457);\n\t$rem = (int)(($time - $ts) - ($years * 52.177457 * 7 * 86400));\n\t$weeks = (int)(($rem) / (7 * 86400));\n\t$days = (int)(($rem) / 86400) - $weeks * 7;\n\t$hours = (int)(($rem) / 3600) - $days * 24 - $weeks * 7 * 24;\n\t$mins = (int)(($rem) / 60) - $hours * 60 - $days * 24 * 60 - $weeks * 7 * 24 * 60;\n\t$str = '';\n\tif ($years == 1) {\n\t\t$str .= \"$years year, \";\n\t}\n\tif ($years > 1) {\n\t\t$str .= \"$years years, \";\n\t}\n\tif ($weeks == 1) {\n\t\t$str .= \"$weeks week, \";\n\t}\n\tif ($weeks > 1) {\n\t\t$str .= \"$weeks weeks, \";\n\t}\n\tif ($days == 1) {\n\t\t$str .= \"$days day,\";\n\t}\n\tif ($days > 1) {\n\t\t$str .= \"$days days,\";\n\t}\n\tif ($hours == 1) {\n\t\t$str .= \" $hours hour and\";\n\t}\n\tif ($hours > 1) {\n\t\t$str .= \" $hours hours and\";\n\t}\n\tif ($mins == 1) {\n\t\t$str .= \" 1 minute\";\n\t} else {\n\t\t$str .= \" $mins minutes\";\n\t}\n\treturn $str;\n}\n"
  },
  {
    "path": "admin_autoupdate.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\FileDir;\nuse Froxlor\\Install\\AutoUpdate;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\nif ($page != 'error') {\n\t// check for webupdate to be enabled\n\tif (Settings::Config('enable_webupdate') != true) {\n\t\tResponse::redirectTo($filename, [\n\t\t\t'page' => 'error',\n\t\t\t'errno' => 11\n\t\t]);\n\t}\n}\n\n// display initial version check\nif ($page == 'overview') {\n\t// log our actions\n\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"checking auto-update\");\n\n\t// check for new version\n\ttry {\n\t\t$result = AutoUpdate::checkVersion();\n\t} catch (Exception $e) {\n\t\tResponse::dynamicError($e->getMessage());\n\t}\n\n\tif ($result == 1) {\n\n\t\t// anzeige über version-status mit ggfls. formular\n\t\t// zum update schritt #1 -> download\n\t\t$text = lng('admin.newerversionavailable') . ' ' . lng('admin.newerversiondetails', [AutoUpdate::getFromResult('version'), Froxlor::VERSION]);\n\n\t\t$upd_formfield = [\n\t\t\t'updates' => [\n\t\t\t\t'title' => lng('update.update'),\n\t\t\t\t'image' => 'fa-solid fa-download',\n\t\t\t\t'sections' => [\n\t\t\t\t\t'section_autoupd' => [\n\t\t\t\t\t\t'fields' => [\n\t\t\t\t\t\t\t'newversion' => ['type' => 'hidden', 'value' => AutoUpdate::getFromResult('version')]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'buttons' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'class' => 'btn-outline-secondary',\n\t\t\t\t\t\t'label' => lng('panel.cancel'),\n\t\t\t\t\t\t'type' => 'reset'\n\t\t\t\t\t],\n\t\t\t\t\t[\n\t\t\t\t\t\t'label' => lng('update.proceed')\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\n\t\tUI::view('user/form-note.html.twig', [\n\t\t\t'formaction' => $linker->getLink(['section' => 'autoupdate', 'page' => 'getdownload']),\n\t\t\t'formdata' => $upd_formfield['updates'],\n\t\t\t// alert\n\t\t\t'type' => 'warning',\n\t\t\t'alert_msg' => $text\n\t\t]);\n\t} else if ($result < 0 || $result > 1) {\n\t\t// remote errors\n\t\tif ($result < 0) {\n\t\t\tResponse::dynamicError(AutoUpdate::getLastError());\n\t\t} else {\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => 'error',\n\t\t\t\t'errno' => $result\n\t\t\t]);\n\t\t}\n\t} else {\n\t\t// no new version\n\t\tResponse::standardSuccess('update.noupdatesavail', (Settings::Get('system.update_channel') == 'testing' ? lng('serversettings.uc_testing') . ' ' : ''));\n\t}\n} // download the new archive\nelseif ($page == 'getdownload') {\n\t// retrieve the new version from the form\n\t$newversion = Request::post('newversion');\n\n\t$result = 6;\n\t// valid?\n\tif ($newversion !== null) {\n\t\t$result = AutoUpdate::downloadZip($newversion);\n\t\tif (!is_numeric($result)) {\n\t\t\t// to the next step\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => 'extract',\n\t\t\t\t'archive' => $result\n\t\t\t]);\n\t\t}\n\t}\n\tResponse::redirectTo($filename, [\n\t\t'page' => 'error',\n\t\t'errno' => $result\n\t]);\n} // extract and install new version\nelseif ($page == 'extract') {\n\tif (Request::post('send') == 'send') {\n\t\t$toExtract = Request::post('archive');\n\t\t$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"Extracting \" . $localArchive . \" to \" . Froxlor::getInstallDir());\n\t\t$result = AutoUpdate::extractZip($localArchive);\n\t\tif ($result > 0) {\n\t\t\t// error\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => 'error',\n\t\t\t\t'errno' => $result\n\t\t\t]);\n\t\t}\n\t\tif (function_exists('opcache_reset')) {\n\t\t\t@opcache_reset();\n\t\t}\n\t\t// redirect to update-page\n\t\tResponse::redirectTo('admin_updates.php');\n\t} else {\n\t\t$toExtract = Request::get('archive');\n\t\t$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);\n\t}\n\n\tif (!file_exists($localArchive)) {\n\t\tResponse::redirectTo($filename, [\n\t\t\t'page' => 'error',\n\t\t\t'errno' => 7\n\t\t]);\n\t}\n\n\t$text = lng('admin.extractdownloadedzip', [$toExtract]);\n\n\t$upd_formfield = [\n\t\t'updates' => [\n\t\t\t'title' => lng('update.update'),\n\t\t\t'image' => 'fa-solid fa-download',\n\t\t\t'sections' => [\n\t\t\t\t'section_autoupd' => [\n\t\t\t\t\t'fields' => [\n\t\t\t\t\t\t'archive' => ['type' => 'hidden', 'value' => $toExtract]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'buttons' => [\n\t\t\t\t[\n\t\t\t\t\t'class' => 'btn-outline-secondary',\n\t\t\t\t\t'label' => lng('panel.cancel'),\n\t\t\t\t\t'type' => 'reset'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'label' => lng('update.proceed')\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t];\n\n\tUI::view('user/form-note.html.twig', [\n\t\t'formaction' => $linker->getLink(['section' => 'autoupdate', 'page' => 'extract']),\n\t\t'formdata' => $upd_formfield['updates'],\n\t\t// alert\n\t\t'type' => 'warning',\n\t\t'alert_msg' => $text\n\t]);\n} // display error\nelseif ($page == 'error') {\n\t// retrieve error-number via url-parameter\n\t$errno = Request::get('errno', 0);\n\n\t// 2 = no Zlib\n\t// 3 = custom version detected\n\t// 4 = could not store archive to local hdd\n\t// 5 = some weird value came from version.froxlor.org\n\t// 6 = download without valid version\n\t// 7 = local archive does not exist\n\t// 8 = could not extract archive\n\t// 9 = checksum mismatch\n\t// 10 = <php-7.4\n\t// 11 = enable_webupdate = false\n\t$errmsg = 'autoupdate_' . $errno;\n\tif ($errno == 3) {\n\t\t$errmsg = 'customized_version';\n\t}\n\tResponse::standardError($errmsg);\n}\n"
  },
  {
    "path": "admin_configfiles.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Config\\ConfigParser;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\n\nif ($userinfo['change_serversettings'] == '1') {\n\tif ($action == 'setconfigured') {\n\t\tSettings::Set('panel.is_configured', '1', true);\n\t\tResponse::redirectTo('admin_configfiles.php');\n\t}\n\n\t// get distro from URL param\n\t$distribution = Request::any('distribution');\n\t$reselect = Request::any('reselect', 0);\n\n\t// check for possible setting\n\tif (empty($distribution)) {\n\t\t$distribution = Settings::Get('system.distribution') ?? \"\";\n\t}\n\tif ($reselect == 2) {\n\t\tSettings::Set('system.distro_mismatch', '2');\n\t\t$reselect = 1;\n\t}\n\tif ($reselect == 1) {\n\t\t$distribution = '';\n\t}\n\n\t$distributions_select = [];\n\n\t$services = [];\n\t$config_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/');\n\n\tif (!empty($distribution)) {\n\t\tif (!file_exists($config_dir . '/' . $distribution . \".xml\")) {\n\t\t\t// unknown distribution -> redirect to select a valid distribution for config-templates\n\t\t\tSettings::Set('system.distribution', '');\n\t\t\tResponse::redirectTo('admin_configfiles.php', ['reselect' => 1]);\n\t\t}\n\n\t\t// update setting if different\n\t\tif ($distribution != Settings::Get('system.distribution')) {\n\t\t\tSettings::Set('system.distribution', $distribution);\n\t\t\tSettings::Set('system.distro_mismatch', '0');\n\t\t}\n\n\t\t// create configparser object\n\t\t$configfiles = new ConfigParser($config_dir . '/' . $distribution . \".xml\");\n\n\t\t// get distro-info\n\t\t$dist_display = $configfiles->getCompleteDistroName();\n\n\t\t// get all the services from the distro\n\t\t$services = $configfiles->getServices();\n\t} else {\n\t\t// show list of available distro's\n\t\t$distros = glob($config_dir . '*.xml');\n\t\t// read in all the distros\n\t\tforeach ($distros as $_distribution) {\n\t\t\t// get configparser object\n\t\t\t$dist = new ConfigParser($_distribution);\n\t\t\t// store in tmp array\n\t\t\t$distributions_select[str_replace(\".xml\", \"\", strtolower(basename($_distribution)))] = $dist->getCompleteDistroName();\n\t\t}\n\n\t\t// sort by distribution name\n\t\tasort($distributions_select);\n\t}\n\n\tif ($distribution != \"\" && !empty(Request::post('finish'))) {\n\t\t$valid_keys = ['http', 'dns', 'smtp', 'mail', 'antispam', 'ftp', 'system', 'distro'];\n\t\tunset($_POST['finish']);\n\t\tunset($_POST['csrf_token']);\n\t\t$params = Request::postAll();\n\t\t$params['distro'] = $distribution;\n\t\t$params['system'] = [];\n\t\tforeach (Request::post('system', []) as $sysdaemon) {\n\t\t\t$params['system'][] = $sysdaemon;\n\t\t}\n\t\t// validate params\n\t\tforeach ($params as $key => $value) {\n\t\t\tif (!in_array($key, $valid_keys)) {\n\t\t\t\tunset($params[$key]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!is_array($value)) {\n\t\t\t\t$params[$key] = Validate::validate($value, $key);\n\t\t\t} else {\n\t\t\t\tforeach ($value as $subkey => $subvalue) {\n\t\t\t\t\t$params[$key][$subkey] = Validate::validate($subvalue, $key.'.'.$subkey);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t$params_content = json_encode($params);\n\t\t$params_filename = FileDir::makeCorrectFile(Froxlor::getInstallDir() . 'install/' . Froxlor::genSessionId() . '.json');\n\t\tfile_put_contents($params_filename, $params_content);\n\n\t\tUI::twigBuffer('settings/configuration-final.html.twig', [\n\t\t\t'distribution' => $distribution,\n\t\t\t// alert\n\t\t\t'type' => 'info',\n\t\t\t'alert_msg' => lng('admin.configfiles.finishnote'),\n\t\t\t'basedir' => Froxlor::getInstallDir(),\n\t\t\t'params_filename' => $params_filename\n\t\t]);\n\t} else {\n\t\tif (!empty($distribution)) {\n\t\t\t// show available services to configure\n\t\t\t$fields = $services;\n\t\t\t$link_params = ['section' => 'configfiles', 'distribution' => $distribution];\n\t\t\tUI::twigBuffer('settings/configuration.html.twig', [\n\t\t\t\t'action' => $linker->getLink($link_params),\n\t\t\t\t'fields' => $fields,\n\t\t\t\t'distribution' => $distribution\n\t\t\t]);\n\t\t} else {\n\t\t\t$cfg_formfield = [\n\t\t\t\t'config' => [\n\t\t\t\t\t'title' => lng('admin.configfiles.serverconfiguration'),\n\t\t\t\t\t'image' => 'fa-solid fa-wrench',\n\t\t\t\t\t'description' => lng('admin.configfiles.description'),\n\t\t\t\t\t'sections' => [\n\t\t\t\t\t\t'section_config' => [\n\t\t\t\t\t\t\t'fields' => [\n\t\t\t\t\t\t\t\t'distribution' => [\n\t\t\t\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t\t\t\t'select_var' => $distributions_select,\n\t\t\t\t\t\t\t\t\t'label' => lng('admin.configfiles.distribution'),\n\t\t\t\t\t\t\t\t\t'selected' => Settings::Get('system.distribution') ?? ''\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'buttons' => [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-outline-secondary',\n\t\t\t\t\t\t\t'label' => lng('panel.cancel'),\n\t\t\t\t\t\t\t'type' => 'reset'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'label' => lng('update.proceed')\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t];\n\n\t\t\tUI::twigBuffer('user/form-note.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'configfiles']),\n\t\t\t\t'formdata' => $cfg_formfield['config'],\n\t\t\t\t'actions_links' => (int)Settings::Get('panel.is_configured') == 0 ? [\n\t\t\t\t\t[\n\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t'section' => 'configfiles',\n\t\t\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t\t\t'action' => 'setconfigured'\n\t\t\t\t\t\t]),\n\t\t\t\t\t\t'label' => lng('panel.ihave_configured'),\n\t\t\t\t\t\t'class' => 'btn-outline-warning',\n\t\t\t\t\t\t'icon' => 'fa-solid fa-circle-check'\n\t\t\t\t\t]\n\t\t\t\t] : [],\n\t\t\t\t// alert\n\t\t\t\t'type' => 'warning',\n\t\t\t\t'alert_msg' => lng('panel.settings_before_configuration') . ((int)Settings::Get('panel.is_configured') == 1 ? '<br><br>' . lng('panel.system_is_configured') : '')\n\t\t\t]);\n\t\t}\n\t}\n\n\tUI::twigOutputBuffer();\n} else {\n\tResponse::redirectTo('admin_index.php');\n}\n"
  },
  {
    "path": "admin_cronjobs.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\Cronjobs;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n$id = (int)Request::any('id');\n\nif (($page == 'cronjobs' || $page == 'overview') && $userinfo['change_serversettings'] == '1') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'viewed admin_cronjobs');\n\n\t\ttry {\n\t\t\t$cron_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.cronjobs.php';\n\t\t\t$collection = (new Collection(Cronjobs::class, $userinfo))\n\t\t\t\t->withPagination($cron_list_data['cron_list']['columns'], $cron_list_data['cron_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\tUI::view('user/table-note.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $cron_list_data, 'cron_list'),\n\t\t\t// alert-box\n\t\t\t'type' => 'warning',\n\t\t\t'alert_msg' => lng('cron.changewarning')\n\t\t]);\n\t} elseif ($action == 'new') {\n\t\t/*\n\t\t * @TODO later\n\t\t */\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Cronjobs::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\t\tif ($result['cronfile'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tCronjobs::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$cronjobs_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/cronjobs/formfield.cronjobs_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'cronjobs', 'id' => $id]),\n\t\t\t\t\t'formdata' => $cronjobs_edit_data['cronjobs_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\t/*\n\t\t * @TODO later\n\t\t */\n\t}\n}\n"
  },
  {
    "path": "admin_customers.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\MysqlServer;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n$id = (int)Request::any('id');\n\nif (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '0') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_customers\");\n\n\t\ttry {\n\t\t\t$customer_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.customers.php';\n\t\t\t$collection = (new Collection(Customers::class, $userinfo, ['show_usages' => true]))\n\t\t\t\t->withPagination($customer_list_data['customer_list']['columns'], $customer_list_data['customer_list']['default_sorting']);\n\t\t\tif ($userinfo['change_serversettings']) {\n\t\t\t\t$collection->has('admin', Admins::class, 'adminid', 'adminid');\n\t\t\t}\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = false;\n\t\tif (CurrentUser::canAddResource('customers')) {\n\t\t\t$actions_links = [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'customers', 'page' => $page, 'action' => 'add']),\n\t\t\t\t\t'label' => lng('admin.customer_add')\n\t\t\t\t]\n\t\t\t];\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $customer_list_data, 'customer_list'),\n\t\t\t'actions_links' => $actions_links\n\t\t]);\n\t} elseif ($action == 'su' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Customers::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$destination_user = $result['loginname'];\n\n\t\tif ($destination_user != '') {\n\t\t\tif ($result['deactivated'] == '1') {\n\t\t\t\tResponse::standardError(\"usercurrentlydeactivated\", $destination_user);\n\t\t\t}\n\n\t\t\t$result['switched_user'] = CurrentUser::getData();\n\t\t\t$result['adminsession'] = 0;\n\t\t\t$result['userid'] = $result['customerid'];\n\t\t\tsession_regenerate_id(true);\n\t\t\tCurrentUser::setData($result);\n\n\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"switched user and is now '\" . $destination_user . \"'\");\n\n\t\t\t$target = Request::get('target', 'index');\n\t\t\t$redirect = \"customer_\" . $target . \".php\";\n\t\t\tif (!file_exists(Froxlor::getInstallDir() . \"/\" . $redirect)) {\n\t\t\t\t$redirect = \"customer_index.php\";\n\t\t\t}\n\t\t\tResponse::redirectTo($redirect, null, true);\n\t\t} else {\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t'action' => 'login'\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'unlock' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Customers::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\t$json_result = Customers::getLocal($userinfo, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t])->unlock();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\tHTML::askYesNo('customer_reallyunlock', $filename, [\n\t\t\t\t'id' => $id,\n\t\t\t\t'page' => $page,\n\t\t\t\t'action' => $action\n\t\t\t], $result['loginname']);\n\t\t}\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Customers::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\t$json_result = Customers::getLocal($userinfo, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'delete_userfiles' => Request::post('delete_userfiles', 0)\n\t\t\t\t])->delete();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\tHTML::askYesNoWithCheckbox('admin_customer_reallydelete', 'admin_customer_alsoremovefiles', $filename, [\n\t\t\t\t'id' => $id,\n\t\t\t\t'page' => $page,\n\t\t\t\t'action' => $action\n\t\t\t], $result['loginname']);\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tCustomers::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$mysql_servers = [];\n\t\t\ttry {\n\t\t\t\t$result_json = MysqlServer::getLocal($userinfo)->listing();\n\t\t\t\t$result_decoded = json_decode($result_json, true)['data']['list'];\n\t\t\t\tforeach ($result_decoded as $dbserver => $dbdata) {\n\t\t\t\t\t$mysql_servers[] = [\n\t\t\t\t\t\t'label' => $dbdata['caption'],\n\t\t\t\t\t\t'value' => $dbserver\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t} catch (Exception $e) {\n\t\t\t\t/* just none */\n\t\t\t}\n\n\t\t\t$phpconfigs = [];\n\t\t\t$configs = Database::query(\"\n\t\t\t\tSELECT c.*, fc.description as interpreter\n\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fc ON fc.id = c.fpmsettingid\n\t\t\t\");\n\t\t\twhile ($row = $configs->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t$phpconfigs[] = [\n\t\t\t\t\t\t'label' => $row['description'] . \" [\" . $row['interpreter'] . \"]\",\n\t\t\t\t\t\t'value' => $row['id']\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\t$phpconfigs[] = [\n\t\t\t\t\t\t'label' => $row['description'],\n\t\t\t\t\t\t'value' => $row['id']\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// hosting plans\n\t\t\t$hosting_plans = [];\n\t\t\t$plans = Database::query(\"\n\t\t\t\tSELECT *\n\t\t\t\tFROM `\" . TABLE_PANEL_PLANS . \"`\n\t\t\t\tORDER BY name ASC\n\t\t\t\");\n\t\t\t$hosting_plans = [\n\t\t\t\t0 => \"---\"\n\t\t\t];\n\t\t\twhile ($row = $plans->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$hosting_plans[$row['id']] = $row['name'];\n\t\t\t}\n\n\t\t\t$customer_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/customer/formfield.customer_add.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'customers']),\n\t\t\t\t'formdata' => $customer_add_data['customer_add']\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Customers::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['loginname'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tCustomers::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$dec_places = Settings::Get('panel.decimal_places');\n\t\t\t\t$result['traffic'] = round($result['traffic'] / (1024 * 1024), $dec_places);\n\t\t\t\t$result['diskspace'] = round($result['diskspace'] / 1024, $dec_places);\n\t\t\t\t$result['email'] = $idna_convert->decode($result['email']);\n\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t$mysql_servers = [];\n\t\t\t\ttry {\n\t\t\t\t\t$result_json = MysqlServer::getLocal($userinfo)->listing();\n\t\t\t\t\t$result_decoded = json_decode($result_json, true)['data']['list'];\n\t\t\t\t\tforeach ($result_decoded as $dbserver => $dbdata) {\n\t\t\t\t\t\t$mysql_servers[] = [\n\t\t\t\t\t\t\t'label' => $dbdata['caption'],\n\t\t\t\t\t\t\t'value' => $dbserver\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t/* just none */\n\t\t\t\t}\n\n\t\t\t\t$phpconfigs = [];\n\t\t\t\t$configs = Database::query(\"\n\t\t\t\t\tSELECT c.*, fc.description as interpreter\n\t\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fc ON fc.id = c.fpmsettingid\n\t\t\t\t\");\n\t\t\t\twhile ($row = $configs->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\t$phpconfigs[] = [\n\t\t\t\t\t\t\t'label' => $row['description'] . \" [\" . $row['interpreter'] . \"]\",\n\t\t\t\t\t\t\t'value' => $row['id']\n\t\t\t\t\t\t];\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$phpconfigs[] = [\n\t\t\t\t\t\t\t'label' => $row['description'],\n\t\t\t\t\t\t\t'value' => $row['id']\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// hosting plans\n\t\t\t\t$plans = Database::query(\"\n\t\t\t\t\tSELECT *\n\t\t\t\t\tFROM `\" . TABLE_PANEL_PLANS . \"`\n\t\t\t\t\tORDER BY name ASC\n\t\t\t\t\");\n\t\t\t\t$hosting_plans = [\n\t\t\t\t\t0 => \"---\"\n\t\t\t\t];\n\t\t\t\twhile ($row = $plans->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$hosting_plans[$row['id']] = $row['name'];\n\t\t\t\t}\n\n\t\t\t\t$admin_select = [];\n\t\t\t\tif ($userinfo['customers_see_all'] == '1') {\n\t\t\t\t\t$available_admins_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\t\t\tWHERE (`customers` = '-1' OR `customers` > `customers_used`)\n\t\t\t\t\t\tAND adminid <> :currentadmin\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($available_admins_stmt, ['currentadmin' => $result['adminid']]);\n\t\t\t\t\t$admin_select = [\n\t\t\t\t\t\t0 => \"---\"\n\t\t\t\t\t];\n\t\t\t\t\twhile ($available_admin = $available_admins_stmt->fetch()) {\n\t\t\t\t\t\t$admin_select[$available_admin['adminid']] = $available_admin['name'] . \" (\" . $available_admin['loginname'] . \")\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$customer_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/customer/formfield.customer_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'customers', 'id' => $id]),\n\t\t\t\t\t'formdata' => $customer_edit_data['customer_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "admin_domains.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\Customers as Customers;\nuse Froxlor\\Api\\Commands\\Domains as Domains;\nuse Froxlor\\Bulk\\DomainBulkAction;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse Froxlor\\Validate\\Validate;\n\n$id = (int)Request::any('id');\n\nif ($page == 'domains' || $page == 'overview') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_domains\");\n\n\t\ttry {\n\t\t\t$customerCollection = (new Collection(Customers::class, $userinfo));\n\t\t\t$domain_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.domains.php';\n\t\t\t$collection = (new Collection(Domains::class, $userinfo))\n\t\t\t\t->has('customer', Customers::class, 'customerid', 'customerid')\n\t\t\t\t->withPagination($domain_list_data['domain_list']['columns'], $domain_list_data['domain_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = false;\n\t\tif (CurrentUser::canAddResource('domains')) {\n\t\t\t$actions_links = [];\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'domains', 'page' => $page, 'action' => 'add']),\n\t\t\t\t'label' => lng('admin.domain_add')\n\t\t\t];\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'domains', 'page' => $page, 'action' => 'import']),\n\t\t\t\t'label' => lng('domains.domain_import'),\n\t\t\t\t'icon' => 'fa-solid fa-file-import',\n\t\t\t\t'class' => 'btn-outline-secondary'\n\t\t\t];\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $domain_list_data, 'domain_list'),\n\t\t\t'actions_links' => $actions_links\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Domains::getLocal($userinfo, [\n\t\t\t\t'id' => $id,\n\t\t\t\t'no_std_subdomain' => true\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$alias_check_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(`id`) AS `count` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\tWHERE `aliasdomain`= :id\");\n\t\t$alias_check = Database::pexecute_first($alias_check_stmt, [\n\t\t\t'id' => $id\n\t\t]);\n\n\t\tif ($result['domain'] != '') {\n\t\t\tif (Request::post('send') == 'send' && $alias_check['count'] == 0) {\n\t\t\t\ttry {\n\t\t\t\t\tDomains::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} elseif ($alias_check['count'] > 0) {\n\t\t\t\tResponse::standardError('domains_cantdeletedomainwithaliases');\n\t\t\t} else {\n\t\t\t\tHTML::askYesNoWithCheckbox('admin_domain_reallydelete', 'admin_customer_alsoremovemail', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $idna_convert->decode($result['domain']));\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tDomains::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$customers = [\n\t\t\t\t0 => lng('panel.please_choose')\n\t\t\t];\n\t\t\t$result_customers_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `customerid`, `loginname`, `name`, `firstname`, `company`\n\t\t\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` \" . ($userinfo['customers_see_all'] ? '' : \" WHERE `adminid` = :adminid \") . \" ORDER BY COALESCE(NULLIF(`name`,''), `company`) ASC\");\n\t\t\t$params = [];\n\t\t\tif ($userinfo['customers_see_all'] == '0') {\n\t\t\t\t$params['adminid'] = $userinfo['adminid'];\n\t\t\t}\n\t\t\tDatabase::pexecute($result_customers_stmt, $params);\n\n\t\t\twhile ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$customers[$row_customer['customerid']] = User::getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')';\n\t\t\t}\n\n\t\t\t$admins = [];\n\t\t\tif ($userinfo['customers_see_all'] == '1') {\n\t\t\t\t$result_admins_stmt = Database::query(\"\n\t\t\t\t\t\tSELECT `adminid`, `loginname`, `name`\n\t\t\t\t\t\tFROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\t\t\tWHERE `domains_used` < `domains` OR `domains` = '-1' ORDER BY `name` ASC\");\n\n\t\t\t\twhile ($row_admin = $result_admins_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$admins[$row_admin['adminid']] = User::getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')';\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($userinfo['ip'] == \"-1\") {\n\t\t\t\t$result_ipsandports_stmt = Database::query(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='0' ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\t$result_ssl_ipsandports_stmt = Database::query(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='1' ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t} else {\n\t\t\t\t$admin_ip_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `id` = :ipid ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\t$admin_ip = Database::pexecute_first($admin_ip_stmt, [\n\t\t\t\t\t'ipid' => $userinfo['ip']\n\t\t\t\t]);\n\n\t\t\t\t$result_ipsandports_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='0' AND `ip` = :ipid ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($result_ipsandports_stmt, [\n\t\t\t\t\t'ipid' => $admin_ip['ip']\n\t\t\t\t]);\n\n\t\t\t\t$result_ssl_ipsandports_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='1' AND `ip` = :ipid ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($result_ssl_ipsandports_stmt, [\n\t\t\t\t\t'ipid' => $admin_ip['ip']\n\t\t\t\t]);\n\t\t\t}\n\n\t\t\t// Build array holding all IPs and Ports available to this admin\n\t\t\t$ipsandports = [];\n\t\t\twhile ($row_ipandport = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif (filter_var($row_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t\t$row_ipandport['ip'] = '[' . $row_ipandport['ip'] . ']';\n\t\t\t\t}\n\n\t\t\t\t$ipsandports[] = [\n\t\t\t\t\t'label' => $row_ipandport['ip'] . ':' . $row_ipandport['port'],\n\t\t\t\t\t'value' => $row_ipandport['id']\n\t\t\t\t];\n\t\t\t}\n\n\t\t\t$ssl_ipsandports = [];\n\t\t\twhile ($row_ssl_ipandport = $result_ssl_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif (filter_var($row_ssl_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t\t$row_ssl_ipandport['ip'] = '[' . $row_ssl_ipandport['ip'] . ']';\n\t\t\t\t}\n\n\t\t\t\t$ssl_ipsandports[] = [\n\t\t\t\t\t'label' => $row_ssl_ipandport['ip'] . ':' . $row_ssl_ipandport['port'],\n\t\t\t\t\t'value' => $row_ssl_ipandport['id']\n\t\t\t\t];\n\t\t\t}\n\n\t\t\t$standardsubdomains = [];\n\t\t\t$result_standardsubdomains_stmt = Database::query(\"\n\t\t\t\t\tSELECT `id` FROM `\" . TABLE_PANEL_DOMAINS . \"` `d`, `\" . TABLE_PANEL_CUSTOMERS . \"` `c` WHERE `d`.`id` = `c`.`standardsubdomain`\n\t\t\t\t\");\n\n\t\t\twhile ($row_standardsubdomain = $result_standardsubdomains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$standardsubdomains[$row_standardsubdomain['id']] = $row_standardsubdomain['id'];\n\t\t\t}\n\n\t\t\tif (count($standardsubdomains) > 0) {\n\t\t\t\t$standardsubdomains = \" AND `d`.`id` NOT IN (\" . join(',', $standardsubdomains) . \") \";\n\t\t\t} else {\n\t\t\t\t$standardsubdomains = '';\n\t\t\t}\n\n\t\t\t$domains = [\n\t\t\t\t0 => lng('domains.noaliasdomain')\n\t\t\t];\n\t\t\t$result_domains_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `d`.`id`, `d`.`domain`, `c`.`loginname` FROM `\" . TABLE_PANEL_DOMAINS . \"` `d`, `\" . TABLE_PANEL_CUSTOMERS . \"` `c`\n\t\t\t\t\tWHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = 0\" . $standardsubdomains . ($userinfo['customers_see_all'] ? '' : \" AND `d`.`adminid` = :adminid\") . \"\n\t\t\t\t\tAND `d`.`customerid`=`c`.`customerid` ORDER BY `loginname`, `domain` ASC\n\t\t\t\t\");\n\t\t\t$params = [];\n\t\t\tif ($userinfo['customers_see_all'] == '0') {\n\t\t\t\t$params['adminid'] = $userinfo['adminid'];\n\t\t\t}\n\t\t\tDatabase::pexecute($result_domains_stmt, $params);\n\n\t\t\twhile ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$domains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')';\n\t\t\t}\n\n\t\t\t$phpconfigs = [];\n\t\t\t$configs = Database::query(\"\n\t\t\t\t\tSELECT c.*, fc.description as interpreter\n\t\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fc ON fc.id = c.fpmsettingid\n\t\t\t\t\");\n\n\t\t\twhile ($row = $configs->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t$phpconfigs[$row['id']] = $row['description'] . \" [\" . $row['interpreter'] . \"]\";\n\t\t\t\t} else {\n\t\t\t\t\t$phpconfigs[$row['id']] = $row['description'];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$openbasedir = [\n\t\t\t\t0 => lng('domain.docroot'),\n\t\t\t\t1 => lng('domain.homedir'),\n\t\t\t\t2 => lng('domain.docparent')\n\t\t\t];\n\n\t\t\t// create serveralias options\n\t\t\t$serveraliasoptions = [\n\t\t\t\t0 => lng('domains.serveraliasoption_wildcard'),\n\t\t\t\t1 => lng('domains.serveraliasoption_www'),\n\t\t\t\t2 => lng('domains.serveraliasoption_none')\n\t\t\t];\n\n\t\t\t$subcanemaildomain = [\n\t\t\t\t0 => lng('admin.subcanemaildomain.never'),\n\t\t\t\t1 => lng('admin.subcanemaildomain.choosableno'),\n\t\t\t\t2 => lng('admin.subcanemaildomain.choosableyes'),\n\t\t\t\t3 => lng('admin.subcanemaildomain.always')\n\t\t\t];\n\n\t\t\t$domain_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_add.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'domains']),\n\t\t\t\t'formdata' => $domain_add_data['domain_add']\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Domains::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['domain'] != '') {\n\t\t\t$subdomains_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(`id`) AS count FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE\n\t\t\t\t`parentdomainid` = :resultid\n\t\t\t\");\n\t\t\t$subdomains = Database::pexecute_first($subdomains_stmt, [\n\t\t\t\t'resultid' => $result['id']\n\t\t\t]);\n\t\t\t$subdomains = $subdomains['count'];\n\n\t\t\t$alias_check_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(`id`) AS count FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE\n\t\t\t\t`aliasdomain` = :resultid\n\t\t\t\");\n\t\t\t$alias_check = Database::pexecute_first($alias_check_stmt, [\n\t\t\t\t'resultid' => $result['id']\n\t\t\t]);\n\t\t\t$alias_check = $alias_check['count'];\n\n\t\t\t$domain_emails_result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `email`, `email_full`, `destination`, `popaccountid`\n\t\t\t\tFROM `\" . TABLE_MAIL_VIRTUAL . \"` WHERE `customerid` = :customerid AND `domainid` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($domain_emails_result_stmt, [\n\t\t\t\t'customerid' => $result['customerid'],\n\t\t\t\t'id' => $result['id']\n\t\t\t]);\n\n\t\t\t$emails = Database::num_rows();\n\t\t\t$email_forwarders = 0;\n\t\t\t$email_accounts = 0;\n\n\t\t\twhile ($domain_emails_row = $domain_emails_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ($domain_emails_row['destination'] != '') {\n\t\t\t\t\t$domain_emails_row['destination'] = explode(' ', FileDir::makeCorrectDestination($domain_emails_row['destination']));\n\t\t\t\t\t$email_forwarders += count($domain_emails_row['destination']);\n\n\t\t\t\t\tif (in_array($domain_emails_row['email_full'], $domain_emails_row['destination'])) {\n\t\t\t\t\t\t$email_forwarders -= 1;\n\t\t\t\t\t\t$email_accounts++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$ipsresult_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id_ipandports` FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($ipsresult_stmt, [\n\t\t\t\t'id' => $result['id']\n\t\t\t]);\n\n\t\t\t$usedips = [];\n\t\t\twhile ($ipsresultrow = $ipsresult_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$usedips[] = $ipsresultrow['id_ipandports'];\n\t\t\t}\n\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\t// remove ssl ip/ports if set is empty\n\t\t\t\t\tif (empty(Request::post('ssl_ipandport'))) {\n\t\t\t\t\t\t$_POST['remove_ssl_ipandport'] = true;\n\t\t\t\t\t}\n\t\t\t\t\tDomains::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tif (Settings::Get('panel.allow_domain_change_customer') == '1') {\n\t\t\t\t\t$customers = [];\n\t\t\t\t\t$result_customers_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `customerid`, `loginname`, `name`, `firstname`, `company` FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\t\t\tWHERE ( (`subdomains_used` + :subdomains <= `subdomains` OR `subdomains` = '-1' )\n\t\t\t\t\t\tAND (`emails_used` + :emails <= `emails` OR `emails` = '-1' )\n\t\t\t\t\t\tAND (`email_forwarders_used` + :forwarders <= `email_forwarders` OR `email_forwarders` = '-1' )\n\t\t\t\t\t\tAND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) \" . ($userinfo['customers_see_all'] ? '' : \" AND `adminid` = :adminid \") . \")\n\t\t\t\t\t\tOR `customerid` = :customerid ORDER BY `name` ASC\n\t\t\t\t\t\");\n\t\t\t\t\t$params = [\n\t\t\t\t\t\t'subdomains' => $subdomains,\n\t\t\t\t\t\t'emails' => $emails,\n\t\t\t\t\t\t'forwarders' => $email_forwarders,\n\t\t\t\t\t\t'accounts' => $email_accounts,\n\t\t\t\t\t\t'customerid' => $result['customerid']\n\t\t\t\t\t];\n\t\t\t\t\tif ($userinfo['customers_see_all'] == '0') {\n\t\t\t\t\t\t$params['adminid'] = $userinfo['adminid'];\n\t\t\t\t\t}\n\t\t\t\t\tDatabase::pexecute($result_customers_stmt, $params);\n\n\t\t\t\t\twhile ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\t$customers[$row_customer['customerid']] = User::getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')';\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$customer_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `customerid`, `loginname`, `name`, `firstname`, `company` FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\t\");\n\t\t\t\t\t$customer = Database::pexecute_first($customer_stmt, [\n\t\t\t\t\t\t'customerid' => $result['customerid']\n\t\t\t\t\t]);\n\t\t\t\t\t$result['customername'] = User::getCorrectFullUserDetails($customer);\n\t\t\t\t}\n\n\t\t\t\tif ($userinfo['customers_see_all'] == '1') {\n\t\t\t\t\tif (Settings::Get('panel.allow_domain_change_admin') == '1') {\n\t\t\t\t\t\t$admins = [];\n\t\t\t\t\t\t$result_admins_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tSELECT `adminid`, `loginname`, `name` FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\t\t\t\tWHERE (`domains_used` < `domains` OR `domains` = '-1') OR `adminid` = :adminid ORDER BY `name` ASC\n\t\t\t\t\t\t\");\n\t\t\t\t\t\tDatabase::pexecute($result_admins_stmt, [\n\t\t\t\t\t\t\t'adminid' => $result['adminid']\n\t\t\t\t\t\t]);\n\n\t\t\t\t\t\twhile ($row_admin = $result_admins_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\t\t$admins[$row_admin['adminid']] = User::getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')';\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$admin_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tSELECT `adminid`, `loginname`, `name` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `adminid` = :adminid\n\t\t\t\t\t\t\");\n\t\t\t\t\t\t$admin = Database::pexecute_first($admin_stmt, [\n\t\t\t\t\t\t\t'adminid' => $result['adminid']\n\t\t\t\t\t\t]);\n\t\t\t\t\t\t$result['adminname'] = User::getCorrectFullUserDetails($admin) . ' (' . $admin['loginname'] . ')';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$domains = [\n\t\t\t\t\t0 => lng('domains.noaliasdomain')\n\t\t\t\t];\n\n\t\t\t\t$result_domains_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `d`.`id`, `d`.`domain`  FROM `\" . TABLE_PANEL_DOMAINS . \"` `d`, `\" . TABLE_PANEL_CUSTOMERS . \"` `c`\n\t\t\t\t\tWHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = '0' AND `d`.`id` <> :id\n\t\t\t\t\tAND `c`.`standardsubdomain`<>`d`.`id` AND `d`.`customerid` = :customerid AND `c`.`customerid`=`d`.`customerid`\n\t\t\t\t\tORDER BY `d`.`domain` ASC\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($result_domains_stmt, [\n\t\t\t\t\t'id' => $result['id'],\n\t\t\t\t\t'customerid' => $result['customerid']\n\t\t\t\t]);\n\n\t\t\t\twhile ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$domains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']);\n\t\t\t\t}\n\n\t\t\t\tif ($userinfo['ip'] == \"-1\") {\n\t\t\t\t\t$result_ipsandports_stmt = Database::query(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='0' ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\t\t$result_ssl_ipsandports_stmt = Database::query(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='1' ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\t} else {\n\t\t\t\t\t$admin_ip_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `id` = :ipid ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\t\t$admin_ip = Database::pexecute_first($admin_ip_stmt, [\n\t\t\t\t\t\t'ipid' => $userinfo['ip']\n\t\t\t\t\t]);\n\n\t\t\t\t\t$result_ipsandports_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='0' AND `ip` = :ipid ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($result_ipsandports_stmt, [\n\t\t\t\t\t\t'ipid' => $admin_ip['ip']\n\t\t\t\t\t]);\n\n\t\t\t\t\t$result_ssl_ipsandports_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='1' AND `ip` = :ipid ORDER BY `ip`, `port` ASC\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($result_ssl_ipsandports_stmt, [\n\t\t\t\t\t\t'ipid' => $admin_ip['ip']\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\t$ipsandports = [];\n\t\t\t\twhile ($row_ipandport = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\tif (filter_var($row_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t\t\t$row_ipandport['ip'] = '[' . $row_ipandport['ip'] . ']';\n\t\t\t\t\t}\n\t\t\t\t\t$ipsandports[] = [\n\t\t\t\t\t\t'label' => $row_ipandport['ip'] . ':' . $row_ipandport['port'],\n\t\t\t\t\t\t'value' => $row_ipandport['id']\n\t\t\t\t\t];\n\t\t\t\t}\n\n\t\t\t\t$ssl_ipsandports = [];\n\t\t\t\twhile ($row_ssl_ipandport = $result_ssl_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\tif (filter_var($row_ssl_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t\t\t$row_ssl_ipandport['ip'] = '[' . $row_ssl_ipandport['ip'] . ']';\n\t\t\t\t\t}\n\t\t\t\t\t$ssl_ipsandports[] = [\n\t\t\t\t\t\t'label' => $row_ssl_ipandport['ip'] . ':' . $row_ssl_ipandport['port'],\n\t\t\t\t\t\t'value' => $row_ssl_ipandport['id']\n\t\t\t\t\t];\n\t\t\t\t}\n\n\t\t\t\t// check that letsencrypt is not activated for wildcard domain\n\t\t\t\tif ($result['iswildcarddomain'] == '1') {\n\t\t\t\t\t$letsencrypt = 0;\n\t\t\t\t}\n\n\t\t\t\t// Fudge the result for ssl_redirect to hide the Let's Encrypt steps\n\t\t\t\t$result['temporary_ssl_redirect'] = $result['ssl_redirect'];\n\t\t\t\t$result['ssl_redirect'] = ($result['ssl_redirect'] == 0 ? 0 : 1);\n\n\t\t\t\t$openbasedir = [\n\t\t\t\t\t0 => lng('domain.docroot'),\n\t\t\t\t\t1 => lng('domain.homedir'),\n\t\t\t\t\t2 => lng('domain.docparent')\n\t\t\t\t];\n\n\t\t\t\t$serveraliasoptions = [\n\t\t\t\t\t0 => lng('domains.serveraliasoption_wildcard'),\n\t\t\t\t\t1 => lng('domains.serveraliasoption_www'),\n\t\t\t\t\t2 => lng('domains.serveraliasoption_none')\n\t\t\t\t];\n\n\t\t\t\t$subcanemaildomain = [\n\t\t\t\t\t0 => lng('admin.subcanemaildomain.never'),\n\t\t\t\t\t1 => lng('admin.subcanemaildomain.choosableno'),\n\t\t\t\t\t2 => lng('admin.subcanemaildomain.choosableyes'),\n\t\t\t\t\t3 => lng('admin.subcanemaildomain.always')\n\t\t\t\t];\n\n\t\t\t\t$phpconfigs = [];\n\t\t\t\t$phpconfigs_result_stmt = Database::query(\"\n\t\t\t\t\tSELECT c.*, fc.description as interpreter\n\t\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fc ON fc.id = c.fpmsettingid\n\t\t\t\t\");\n\t\t\t\t$c_allowed_configs = Customer::getCustomerDetail($result['customerid'], 'allowed_phpconfigs');\n\t\t\t\tif (!empty($c_allowed_configs)) {\n\t\t\t\t\t$c_allowed_configs = json_decode($c_allowed_configs, true);\n\t\t\t\t} else {\n\t\t\t\t\t$c_allowed_configs = [];\n\t\t\t\t}\n\n\t\t\t\twhile ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$disabled = !empty($c_allowed_configs) && !in_array($phpconfigs_row['id'], $c_allowed_configs);\n\t\t\t\t\tif (!$disabled) {\n\t\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\t\t$phpconfigs[$phpconfigs_row['id']] = $phpconfigs_row['description'] . \" [\" . $phpconfigs_row['interpreter'] . \"]\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$phpconfigs[$phpconfigs_row['id']] = $phpconfigs_row['description'];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (Settings::Get('panel.allow_domain_change_customer') != '1') {\n\t\t\t\t\t$result['customername'] .= ' (<a href=\"' . $linker->getLink([\n\t\t\t\t\t\t\t'section' => 'customers',\n\t\t\t\t\t\t\t'page' => 'customers',\n\t\t\t\t\t\t\t'action' => 'su',\n\t\t\t\t\t\t\t'id' => $customer['customerid']\n\t\t\t\t\t\t]) . '\" rel=\"external\">' . $customer['loginname'] . '</a>)';\n\t\t\t\t}\n\n\t\t\t\t$domain_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'domains', 'id' => $id]),\n\t\t\t\t\t'formdata' => $domain_edit_data['domain_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'jqGetCustomerPHPConfigs') {\n\t\t$customerid = intval(Request::post('customerid'));\n\t\t$allowed_phpconfigs = Customer::getCustomerDetail($customerid, 'allowed_phpconfigs');\n\t\techo !empty($allowed_phpconfigs) ? $allowed_phpconfigs : json_encode([]);\n\t\texit();\n\t} elseif ($action == 'jqSpeciallogfileNote') {\n\t\t$domainid = intval(Request::post('id'));\n\t\t$newval = intval(Request::post('newval'));\n\t\ttry {\n\t\t\t$json_result = Domains::getLocal($userinfo, [\n\t\t\t\t'id' => $domainid\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\t\tif ($newval != $result['speciallogfile']) {\n\t\t\techo json_encode(['changed' => true, 'info' => lng('admin.speciallogwarning')]);\n\t\t\texit();\n\t\t}\n\t\techo 0;\n\t\texit();\n\t} elseif ($action == 'jqEmaildomainNote') {\n\t\t$domainid = intval(Request::post('id'));\n\t\t$newval = intval(Request::post('newval'));\n\t\ttry {\n\t\t\t$json_result = Domains::getLocal($userinfo, [\n\t\t\t\t'id' => $domainid\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\t\tif ((int)$newval == 0 && $newval != $result['isemaildomain']) {\n\t\t\techo json_encode(['changed' => true, 'info' => lng('admin.emaildomainwarning')]);\n\t\t\texit();\n\t\t}\n\t\techo 0;\n\t\texit();\n\t} elseif ($action == 'import') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\t$separator = Validate::validate(Request::post('separator'), 'separator');\n\t\t\t$offset = (int)Validate::validate(Request::post('offset'), 'offset', \"/[0-9]/i\");\n\n\t\t\t$file_name = $_FILES['file']['tmp_name'];\n\n\t\t\t$result = [];\n\n\t\t\ttry {\n\t\t\t\t$bulk = new DomainBulkAction($file_name, $userinfo);\n\t\t\t\t$result = $bulk->doImport($separator, $offset);\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::standardError('domain_import_error', $e->getMessage());\n\t\t\t}\n\n\t\t\tif (!empty($bulk->getErrors())) {\n\t\t\t\tResponse::dynamicError(implode(\"<br>\", $bulk->getErrors()));\n\t\t\t}\n\n\t\t\t// update customer/admin counters\n\t\t\tUser::updateCounters(false);\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\n\t\t\t$result_str = $result['imported'] . ' / ' . $result['all'] . (!empty($result['note']) ? ' (' . $result['note'] . ')' : '');\n\t\t\tResponse::standardSuccess('domain_import_successfully', $result_str, [\n\t\t\t\t'filename' => $filename,\n\t\t\t\t'action' => '',\n\t\t\t\t'page' => 'domains'\n\t\t\t]);\n\t\t} else {\n\t\t\t$domain_import_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_import.php';\n\n\t\t\tUI::view('user/form-note.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'domains', 'page' => $page]),\n\t\t\t\t'formdata' => $domain_import_data['domain_import'],\n\t\t\t\t// alert-box\n\t\t\t\t'type' => 'info',\n\t\t\t\t'alert_msg' => lng('domains.import_description')\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'duplicate') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tDomains::getLocal($userinfo, Request::postAll())->duplicate();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page,\n\t\t\t\t'searchfield' => 'd.domain_ace',\n\t\t\t\t'searchtext' => Request::post('domain', \"\")\n\t\t\t]);\n\t\t} else {\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => 'overview'\n\t\t\t]);\n\t\t}\n\t}\n} elseif ($page == 'domainssleditor') {\n\trequire_once __DIR__ . '/ssl_editor.php';\n} elseif ($page == 'domaindnseditor' && Settings::Get('system.dnsenabled') == '1') {\n\trequire_once __DIR__ . '/dns_editor.php';\n} elseif ($page == 'sslcertificates') {\n\trequire_once __DIR__ . '/ssl_certificates.php';\n} elseif ($page == 'logfiles') {\n\trequire_once __DIR__ . '/logfiles_viewer.php';\n}\n"
  },
  {
    "path": "admin_index.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\Admins as Admins;\nuse Froxlor\\Api\\Commands\\Froxlor as Froxlor;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Language;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\n\n$id = (int)Request::any('id');\n\nif ($action == 'logout') {\n\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"logged out\");\n\tunset($_SESSION['userinfo']);\n\tCurrentUser::setData();\n\tsession_destroy();\n\n\tResponse::redirectTo('index.php');\n} elseif ($action == 'suback') {\n\tif (is_array(CurrentUser::getField('switched_user'))) {\n\t\t$result = CurrentUser::getData();\n\t\t$result = $result['switched_user'];\n\t\tsession_regenerate_id(true);\n\t\tCurrentUser::setData($result);\n\t\t$target = Request::get('target', 'index');\n\t\t$redirect = \"admin_\" . $target . \".php\";\n\t\tif (!file_exists(\\Froxlor\\Froxlor::getInstallDir() . \"/\" . $redirect)) {\n\t\t\t$redirect = \"admin_index.php\";\n\t\t}\n\t\tResponse::redirectTo($redirect, null, true);\n\t} else {\n\t\tResponse::dynamicError(\"Cannot change back - You've never switched to another user :-)\");\n\t}\n}\n\nif ($page == 'overview') {\n\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_index\");\n\t$params = [];\n\tif ($userinfo['customers_see_all'] == '0') {\n\t\t$params = [\n\t\t\t'adminid' => $userinfo['adminid']\n\t\t];\n\t}\n\t$overview_stmt = Database::prepare(\"SELECT COUNT(*) AS `number_customers`,\n\t\t\t\tSUM(case when `diskspace` > 0 then `diskspace` else 0 end) AS `diskspace_assigned`,\n\t\t\t\tSUM(`diskspace_used`) AS `diskspace_used`,\n\t\t\t\tSUM(case when `mysqls` > 0 then `mysqls` else 0 end) AS `mysqls_assigned`,\n\t\t\t\tSUM(`mysqls_used`) AS `mysqls_used`,\n\t\t\t\tSUM(case when `emails` > 0 then `emails` else 0 end) AS `emails_assigned`,\n\t\t\t\tSUM(`emails_used`) AS `emails_used`,\n\t\t\t\tSUM(case when `email_accounts` > 0 then `email_accounts` else 0 end) AS `email_accounts_assigned`,\n\t\t\t\tSUM(`email_accounts_used`) AS `email_accounts_used`,\n\t\t\t\tSUM(case when `email_forwarders` > 0 then `email_forwarders` else 0 end) AS `email_forwarders_assigned`,\n\t\t\t\tSUM(`email_forwarders_used`) AS `email_forwarders_used`,\n\t\t\t\tSUM(case when `email_quota` > 0 then `email_quota` else 0 end) AS `email_quota_assigned`,\n\t\t\t\tSUM(`email_quota_used`) AS `email_quota_used`,\n\t\t\t\tSUM(case when `ftps` > 0 then `ftps` else 0 end) AS `ftps_assigned`,\n\t\t\t\tSUM(`ftps_used`) AS `ftps_used`,\n\t\t\t\tSUM(case when `subdomains` > 0 then `subdomains` else 0 end) AS `subdomains_assigned`,\n\t\t\t\tSUM(`subdomains_used`) AS `subdomains_used`,\n\t\t\t\tSUM(case when `traffic` > 0 then `traffic` else 0 end) AS `traffic_assigned`,\n\t\t\t\tSUM(`traffic_used`) AS `traffic_used`\n\t\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"`\" . ($userinfo['customers_see_all'] ? '' : \" WHERE `adminid` = :adminid \"));\n\t$overview = Database::pexecute_first($overview_stmt, $params);\n\n\t$userinfo['diskspace_bytes'] = ($userinfo['diskspace'] > -1) ? $userinfo['diskspace'] * 1024 : -1;\n\t$overview['diskspace_bytes'] = $overview['diskspace_assigned'] * 1024;\n\t$overview['diskspace_bytes_used'] = $overview['diskspace_used'] * 1024;\n\n\t$userinfo['traffic_bytes'] = ($userinfo['traffic'] > -1) ? $userinfo['traffic'] * 1024 : -1;\n\t$overview['traffic_bytes'] = $overview['traffic_assigned'] * 1024;\n\t$overview['traffic_bytes_used'] = $overview['traffic_used'] * 1024;\n\n\t$number_domains_stmt = Database::prepare(\"\n\t\tSELECT COUNT(*) AS `number_domains` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\tWHERE `parentdomainid`='0'\" . ($userinfo['customers_see_all'] ? '' : \" AND `adminid` = :adminid\"));\n\t$number_domains = Database::pexecute_first($number_domains_stmt, $params);\n\n\t$overview['number_domains'] = $number_domains['number_domains'];\n\n\tif (Request::get('lookfornewversion') == 'yes' || (isset($lookfornewversion) && $lookfornewversion == 'yes')) {\n\t\ttry {\n\t\t\t$json_result = Froxlor::getLocal($userinfo)->checkUpdate();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$lookfornewversion_lable = $result['version'];\n\t\t$lookfornewversion_link = $result['link'];\n\t\t$lookfornewversion_message = $result['message'];\n\t\t$lookfornewversion_addinfo = $result['additional_info'];\n\t\t$isnewerversion = $result['isnewerversion'];\n\t} else {\n\t\t$lookfornewversion_lable = lng('admin.lookfornewversion.clickhere');\n\t\t$lookfornewversion_link = htmlspecialchars($filename . '?page=' . urlencode($page) . '&lookfornewversion=yes');\n\t\t$lookfornewversion_message = '';\n\t\t$lookfornewversion_addinfo = '';\n\t\t$isnewerversion = 0;\n\t}\n\n\t$cron_last_runs = Cronjob::getCronjobsLastRun();\n\t$outstanding_tasks = Cronjob::getOutstandingTasks();\n\n\t// additional sys-infos\n\t$meminfo = explode(\"\\n\", @file_get_contents(\"/proc/meminfo\"));\n\t$memory = \"\";\n\tfor ($i = 0; $i < count($meminfo); ++$i) {\n\t\tif (substr($meminfo[$i], 0, 3) === \"Mem\") {\n\t\t\t$memory .= $meminfo[$i] . PHP_EOL;\n\t\t}\n\t}\n\n\tif (function_exists('sys_getloadavg')) {\n\t\t$loadArray = sys_getloadavg();\n\t\t$load = number_format($loadArray[0], 2, '.', '') . \" / \" . number_format($loadArray[1], 2, '.', '') . \" / \" . number_format($loadArray[2], 2, '.', '');\n\t} else {\n\t\t$load = @file_get_contents('/proc/loadavg');\n\t\tif (!$load) {\n\t\t\t$load = lng('admin.noloadavailable');\n\t\t}\n\t}\n\n\t$kernel = '';\n\tif (function_exists('posix_uname')) {\n\t\t$kernel_nfo = posix_uname();\n\t\t$kernel = $kernel_nfo['release'] . ' (' . $kernel_nfo['machine'] . ')';\n\t}\n\n\t// Try to get the uptime\n\t// First: With exec (let's hope it's enabled for the Froxlor - vHost)\n\t$uptime_array = explode(\" \", @file_get_contents(\"/proc/uptime\"));\n\t$uptime = '';\n\tif (is_array($uptime_array) && isset($uptime_array[0]) && is_numeric($uptime_array[0])) {\n\t\t// Some calculatioon to get a nicly formatted display\n\t\t$seconds = round($uptime_array[0], 0);\n\t\t$minutes = $seconds / 60;\n\t\t$hours = $minutes / 60;\n\t\t$days = floor($hours / 24);\n\t\t$hours = floor($hours - ($days * 24));\n\t\t$minutes = floor($minutes - ($days * 24 * 60) - ($hours * 60));\n\t\t$seconds = floor($seconds - ($days * 24 * 60 * 60) - ($hours * 60 * 60) - ($minutes * 60));\n\t\t$uptime = \"{$days}d, {$hours}h, {$minutes}m, {$seconds}s\";\n\t\t// Just cleanup\n\t\tunset($uptime_array, $seconds, $minutes, $hours, $days);\n\t}\n\n\t$sysinfo = [\n\t\t'webserver' => $_SERVER['SERVER_SOFTWARE'] ?? 'unknown',\n\t\t'phpversion' => phpversion(),\n\t\t'mysqlserverversion' => Database::getAttribute(PDO::ATTR_SERVER_VERSION),\n\t\t'phpsapi' => strtoupper(@php_sapi_name()),\n\t\t'hostname' => gethostname(),\n\t\t'memory' => $memory,\n\t\t'load' => $load,\n\t\t'kernel' => $kernel,\n\t\t'uptime' => $uptime\n\t];\n\n\tUI::twig()->addGlobal('userinfo', $userinfo);\n\tUI::view('user/index.html.twig', [\n\t\t'sysinfo' => $sysinfo,\n\t\t'overview' => $overview,\n\t\t'outstanding_tasks' => $outstanding_tasks,\n\t\t'cron_last_runs' => $cron_last_runs\n\t]);\n} elseif ($page == 'profile') {\n\t$languages = Language::getLanguages();\n\n\tif (!empty($_POST)) {\n\t\tif (Request::post('send') == 'changepassword') {\n\t\t\t$old_password = Validate::validate(Request::post('old_password'), 'old password');\n\n\t\t\tif (!Crypt::validatePasswordLogin($userinfo, $old_password, TABLE_PANEL_ADMINS, 'adminid')) {\n\t\t\t\tResponse::standardError('oldpasswordnotcorrect');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t$new_password = Crypt::validatePassword(Request::post('new_password'), 'new password');\n\t\t\t\t$new_password_confirm = Crypt::validatePassword(Request::post('new_password_confirm'), 'new password confirm');\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\n\t\t\tif ($old_password == '') {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'stringisempty',\n\t\t\t\t\t'changepassword.old_password'\n\t\t\t\t]);\n\t\t\t} elseif ($new_password == '') {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'stringisempty',\n\t\t\t\t\t'changepassword.new_password'\n\t\t\t\t]);\n\t\t\t} elseif ($new_password_confirm == '') {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'stringisempty',\n\t\t\t\t\t'changepassword.new_password_confirm'\n\t\t\t\t]);\n\t\t\t} elseif ($new_password != $new_password_confirm) {\n\t\t\t\tResponse::standardError('newpasswordconfirmerror');\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tAdmins::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $userinfo['adminid'],\n\t\t\t\t\t\t'admin_password' => $new_password\n\t\t\t\t\t])->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'changed password');\n\t\t\t\tResponse::redirectTo($filename);\n\t\t\t}\n\t\t} elseif (Request::post('send') == 'changetheme') {\n\t\t\tif (Settings::Get('panel.allow_theme_change_admin') == 1) {\n\t\t\t\t$theme = Validate::validate(Request::post('theme'), 'theme');\n\t\t\t\ttry {\n\t\t\t\t\tAdmins::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $userinfo['adminid'],\n\t\t\t\t\t\t'theme' => $theme\n\t\t\t\t\t])->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\n\t\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"changed his/her theme to '\" . $theme . \"'\");\n\t\t\t}\n\t\t\tResponse::redirectTo($filename);\n\t\t} elseif (Request::post('send') == 'changelanguage') {\n\t\t\t$def_language = Validate::validate(Request::post('def_language'), 'default language');\n\n\t\t\tif (isset($languages[$def_language])) {\n\t\t\t\ttry {\n\t\t\t\t\tAdmins::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $userinfo['adminid'],\n\t\t\t\t\t\t'def_language' => $def_language\n\t\t\t\t\t])->update();\n\t\t\t\t\tCurrentUser::setField('language', $def_language);\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"changed his/her default language to '\" . $def_language . \"'\");\n\t\t\tResponse::redirectTo($filename);\n\t\t}\n\t} else {\n\t\t// change theme\n\t\t$default_theme = Settings::Get('panel.default_theme');\n\t\tif ($userinfo['theme'] != '') {\n\t\t\t$default_theme = $userinfo['theme'];\n\t\t}\n\t\t$themes_avail = UI::getThemes();\n\n\t\t// change language\n\t\t$default_lang = Settings::Get('panel.standardlanguage');\n\t\tif ($userinfo['def_language'] != '') {\n\t\t\t$default_lang = $userinfo['def_language'];\n\t\t}\n\n\t\tUI::view('user/profile.html.twig', [\n\t\t\t'themes' => $themes_avail,\n\t\t\t'default_theme' => $default_theme,\n\t\t\t'languages' => $languages,\n\t\t\t'default_lang' => $default_lang,\n\t\t]);\n\t}\n} elseif ($page == 'send_error_report' && Settings::Get('system.allow_error_report_admin') == '1') {\n\trequire_once __DIR__ . '/error_report.php';\n} elseif ($page == 'apikeys' && Settings::Get('api.enabled') == 1) {\n\trequire_once __DIR__ . '/api_keys.php';\n} elseif ($page == '2fa' && Settings::Get('2fa.enabled') == 1) {\n\trequire_once __DIR__ . '/2fa.php';\n}\n"
  },
  {
    "path": "admin_ipsandports.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\IpsAndPorts;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n$id = (int)Request::any('id');\n\nif (($page == 'ipsandports' || $page == 'overview') && $userinfo['change_serversettings'] == '1') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_ipsandports\");\n\n\t\ttry {\n\t\t\t$ipsandports_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.ipsandports.php';\n\t\t\t$collection = (new Collection(IpsAndPorts::class, $userinfo))\n\t\t\t\t->withPagination($ipsandports_list_data['ipsandports_list']['columns'], $ipsandports_list_data['ipsandports_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $ipsandports_list_data, 'ipsandports_list'),\n\t\t\t'actions_links' => [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'ipsandports', 'page' => $page, 'action' => 'add']),\n\t\t\t\t\t'label' => lng('admin.ipsandports.add')\n\t\t\t\t]\n\t\t\t]\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = IpsAndPorts::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['id']) && $result['id'] == $id) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tIpsAndPorts::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t])->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('admin_ip_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['ip'] . ':' . $result['port']);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tIpsAndPorts::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$ipsandports_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/ipsandports/formfield.ipsandports_add.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'ipsandports']),\n\t\t\t\t'formdata' => $ipsandports_add_data['ipsandports_add']\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = IpsAndPorts::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['ip'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tIpsAndPorts::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t$ipsandports_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'ipsandports', 'id' => $id]),\n\t\t\t\t\t'formdata' => $ipsandports_edit_data['ipsandports_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'jqCheckIP') {\n\t\t$ip = Request::post('ip', '');\n\t\tif (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {\n\t\t\techo json_encode('<div id=\"ipnote\" class=\"invalid-feedback\">'.lng('error.invalidip', [$ip]).'</div>');\n\t\t} elseif (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE)) {\n\t\t\t// returns notice if private network detected, so we can display it\n\t\t\techo json_encode(lng('admin.ipsandports.ipnote'));\n\t\t} else {\n\t\t\techo 0;\n\t\t}\n\t\texit();\n\t}\n}\n"
  },
  {
    "path": "admin_logger.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\SysLog;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\nif ($page == 'log') {\n\tif ($action == '') {\n\t\ttry {\n\t\t\t$syslog_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/tablelisting.syslog.php';\n\t\t\t$collection = (new Collection(SysLog::class, $userinfo))\n\t\t\t\t->withPagination($syslog_list_data['syslog_list']['columns'], $syslog_list_data['syslog_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $syslog_list_data, 'syslog_list'),\n\t\t\t'actions_links' => ($userinfo['change_serversettings'] == '1' ? [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'logger', 'page' => 'log', 'action' => 'truncate']),\n\t\t\t\t\t'label' => lng('logger.truncate'),\n\t\t\t\t\t'icon' => 'fa-solid fa-recycle',\n\t\t\t\t\t'class' => 'btn-warning'\n\t\t\t\t]\n\t\t\t] : [])\n\t\t]);\n\t} elseif ($action == 'truncate' && $userinfo['change_serversettings'] == '1') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tSysLog::getLocal($userinfo, [\n\t\t\t\t\t'min_to_keep' => 10\n\t\t\t\t])->delete();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\tHTML::askYesNo('logger_reallytruncate', $filename, [\n\t\t\t\t'page' => $page,\n\t\t\t\t'action' => $action\n\t\t\t], TABLE_PANEL_LOG);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "admin_message.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\n\n$id = (int)Request::any('id');\n\n$note_type = null;\n$note_msg = null;\n\nif ($page == 'message') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'viewed panel_message');\n\n\t\tif (Request::post('send') == 'send') {\n\t\t\tif (Request::post('recipient', -1) == 0 && $userinfo['customers_see_all'] == '1') {\n\t\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'sending messages to admins');\n\t\t\t\t$result = Database::query('SELECT `name`, `email`  FROM `' . TABLE_PANEL_ADMINS . \"`\");\n\t\t\t} elseif (Request::post('recipient', -1) == 1) {\n\t\t\t\tif ($userinfo['customers_see_all'] == '1') {\n\t\t\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'sending messages to ALL customers');\n\t\t\t\t\t$result = Database::query('SELECT `firstname`, `name`, `company`, `email`  FROM `' . TABLE_PANEL_CUSTOMERS . \"`\");\n\t\t\t\t} else {\n\t\t\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'sending messages to customers');\n\t\t\t\t\t$result = Database::prepare('\n\t\t\t\t\t\tSELECT `firstname`, `name`, `company`, `email`  FROM `' . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\t\t\tWHERE `adminid` = :adminid\");\n\t\t\t\t\tDatabase::pexecute($result, [\n\t\t\t\t\t\t'adminid' => $userinfo['adminid']\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tResponse::standardError('norecipientsgiven');\n\t\t\t}\n\n\t\t\t$subject = Request::post('subject');\n\t\t\t$message = wordwrap(Request::post('message'), 70);\n\n\t\t\tif (!empty($message)) {\n\t\t\t\t$mailcounter = 0;\n\t\t\t\t$mail->Body = $message;\n\t\t\t\t$mail->Subject = $subject;\n\n\t\t\t\twhile ($row = $result->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$row['firstname'] = isset($row['firstname']) ? $row['firstname'] : '';\n\t\t\t\t\t$row['company'] = isset($row['company']) ? $row['company'] : '';\n\t\t\t\t\t$mail->AddAddress($row['email'], User::getCorrectUserSalutation([\n\t\t\t\t\t\t'firstname' => $row['firstname'],\n\t\t\t\t\t\t'name' => $row['name'],\n\t\t\t\t\t\t'company' => $row['company']\n\t\t\t\t\t]));\n\t\t\t\t\t$mail->From = $userinfo['email'];\n\t\t\t\t\t$mail->FromName = (isset($userinfo['firstname']) ? $userinfo['firstname'] . ' ' : '') . $userinfo['name'];\n\n\t\t\t\t\tif (!$mail->Send()) {\n\t\t\t\t\t\tif ($mail->ErrorInfo != '') {\n\t\t\t\t\t\t\t$mailerr_msg = $mail->ErrorInfo;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$mailerr_msg = $row['email'];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, 'Error sending mail: ' . $mailerr_msg);\n\t\t\t\t\t\tResponse::standardError('errorsendingmail', $row['email']);\n\t\t\t\t\t}\n\n\t\t\t\t\t$mailcounter++;\n\t\t\t\t\t$mail->ClearAddresses();\n\t\t\t\t}\n\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => 'showsuccess',\n\t\t\t\t\t'sentitems' => $mailcounter\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tResponse::standardError('nomessagetosend');\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'showsuccess') {\n\t\t$sentitems = Request::get('sentitems', 0);\n\n\t\tif ($sentitems == 0) {\n\t\t\t$note_type = 'info';\n\t\t\t$note_msg = lng('message.norecipients');\n\t\t} else {\n\t\t\t$note_type = 'success';\n\t\t\t$note_msg = lng('message.success', [$sentitems]);\n\t\t}\n\t}\n\n\t$recipients = [];\n\n\tif ($userinfo['customers_see_all'] == '1') {\n\t\t$recipients[0] = lng('panel.reseller');\n\t}\n\t$recipients[1] = lng('panel.customer');\n\n\t$messages_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/messages/formfield.messages_add.php';\n\n\tUI::view('user/form-note.html.twig', [\n\t\t'formaction' => $linker->getLink(['section' => 'message', 'action' => '']),\n\t\t'formdata' => $messages_add_data['messages_add'],\n\t\t'actions_links' => ($userinfo['change_serversettings'] == '1' ? [\n\t\t\t[\n\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t'section' => 'settings',\n\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t'part' => 'system',\n\t\t\t\t\t'em' => 'system_mail_use_smtp'\n\t\t\t\t]),\n\t\t\t\t'label' => lng('admin.smtpsettings'),\n\t\t\t\t'icon' => 'fa-solid fa-gears',\n\t\t\t\t'class' => 'btn-outline-secondary'\n\t\t\t]\n\t\t] : []),\n\t\t// alert-box\n\t\t'type' => $note_type,\n\t\t'alert_msg' => $note_msg\n\t]);\n}\n"
  },
  {
    "path": "admin_mysqlserver.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\MysqlServer;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n$id = (int)Request::any('id');\n\nif (($page == 'mysqlserver' || $page == 'overview') && $userinfo['change_serversettings'] == '1') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_mysqlserver\");\n\n\t\ttry {\n\t\t\t$mysqlserver_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.mysqlserver.php';\n\t\t\t$collection = (new Collection(MysqlServer::class, $userinfo))\n\t\t\t\t->withPagination($mysqlserver_list_data['mysqlserver_list']['columns'], $mysqlserver_list_data['mysqlserver_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $mysqlserver_list_data, 'mysqlserver_list'),\n\t\t\t'actions_links' => [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'mysqlserver', 'page' => $page, 'action' => 'add']),\n\t\t\t\t\t'label' => lng('admin.mysqlserver.add')\n\t\t\t\t]\n\t\t\t]\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = MysqlServer::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['id']) && $result['id'] == $id) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tMysqlServer::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t])->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('admin_mysqlserver_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['caption'] . ' (' . $result['host'] . ')');\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tMysqlServer::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$mysqlserver_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/mysqlserver/formfield.mysqlserver_add.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'mysqlserver']),\n\t\t\t\t'formdata' => $mysqlserver_add_data['mysqlserver_add']\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'edit' && $id >= 0) {\n\t\ttry {\n\t\t\t$json_result = MysqlServer::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['id']) && $result['id'] == $id) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tMysqlServer::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t$mysqlserver_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/mysqlserver/formfield.mysqlserver_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'mysqlserver', 'id' => $id]),\n\t\t\t\t\t'formdata' => $mysqlserver_edit_data['mysqlserver_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "admin_opcacheinfo.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @author     Janos Muzsi <muzsij@hypernics.hu>\n * @author     Andrew Collington <andy@amnuts.com>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n *\n * Based on https://github.com/amnuts/opcache-gui, which is\n * licensed under the MIT licence, which can be viewed\n * online at https://acollington.mit-license.org/\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\nif ($action == 'reset' && function_exists('opcache_reset') && $userinfo['change_serversettings'] == '1') {\n\tif (Request::post('send') == 'send') {\n\t\topcache_reset();\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"reset OPcache\");\n\t\theader('Location: ' . $linker->getLink([\n\t\t\t\t'section' => 'opcacheinfo',\n\t\t\t\t'page' => 'showinfo'\n\t\t\t]));\n\t\texit();\n\t} else {\n\t\tHTML::askYesNo('cache_reallydelete', $filename, [\n\t\t\t'page' => $page,\n\t\t\t'action' => 'reset',\n\t\t], '', [\n\t\t\t'section' => 'opcacheinfo',\n\t\t\t'page' => 'showinfo'\n\t\t]);\n\t}\n}\n\nif (!extension_loaded('Zend OPcache')) {\n\tResponse::standardError('no_opcacheinfo');\n}\n\n$ocEnabled = ini_get('opcache.enable');\nif (empty($ocEnabled)) {\n\tResponse::standardError('inactive_opcacheinfo');\n}\n\nif ($page == 'showinfo' && $userinfo['change_serversettings'] == '1') {\n\t$time = time();\n\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed OPcache info\");\n\n\t$opcache = (new \\Amnuts\\Opcache\\Service())->getData();\n\n\tUI::view('settings/opcacheinfo.html.twig', [\n\t\t'opcacheinfo' => [\n\t\t\t'version' => $opcache['version'],\n\t\t\t'overview' => $opcache['overview'],\n\t\t\t'files' => $opcache['files'],\n\t\t\t'preload' => $opcache['preload'],\n\t\t\t'directives' => $opcache['directives'],\n\t\t\t'blacklist' => $opcache['blacklist'],\n\t\t\t'functions' => $opcache['functions'],\n\t\t]\n\t]);\n}\n"
  },
  {
    "path": "admin_phpsettings.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\FpmDaemons;\nuse Froxlor\\Api\\Commands\\PhpSettings;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n$id = (int)Request::any('id');\n\nif ($page == 'overview') {\n\tif ($action == '') {\n\t\ttry {\n\t\t\t$phpconf_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.phpconfigs.php';\n\t\t\t$collection = (new Collection(PhpSettings::class, $userinfo, ['with_subdomains' => true]))\n\t\t\t\t->withPagination($phpconf_list_data['phpconf_list']['columns'], $phpconf_list_data['phpconf_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $phpconf_list_data, 'phpconf_list'),\n\t\t\t'actions_links' => (bool)$userinfo['change_serversettings'] ? [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'phpsettings', 'page' => $page, 'action' => 'add']),\n\t\t\t\t\t'label' => lng('admin.phpsettings.addnew')\n\t\t\t\t]\n\t\t\t] : []\n\t\t]);\n\t}\n\n\tif ($action == 'add') {\n\t\tif ((int)$userinfo['change_serversettings'] == 1) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tPhpSettings::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tif (file_exists(Froxlor::getInstallDir() . '/templates/misc/php/default.ini.php')) {\n\t\t\t\t\tinclude Froxlor::getInstallDir() . '/templates/misc/php/default.ini.php';\n\t\t\t\t\t$result = [\n\t\t\t\t\t\t'phpsettings' => $phpini\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\t// use first php-config as fallback\n\t\t\t\t\t$result_stmt = Database::query(\"SELECT * FROM `\" . TABLE_PANEL_PHPCONFIGS . \"` WHERE `id` = 1\");\n\t\t\t\t\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\t}\n\n\t\t\t\t$fpmconfigs = [];\n\t\t\t\t$configs = Database::query(\"SELECT * FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` ORDER BY `description` ASC\");\n\t\t\t\twhile ($row = $configs->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$fpmconfigs[$row['id']] = $row['description'];\n\t\t\t\t}\n\n\t\t\t\t$phpconfig_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php';\n\n\t\t\t\tUI::view('user/form-replacers.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'phpsettings']),\n\t\t\t\t\t'formdata' => $phpconfig_add_data['phpconfig_add'],\n\t\t\t\t\t'replacers' => $phpconfig_add_data['phpconfig_replacers']\n\t\t\t\t]);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('nopermissionsorinvalidid');\n\t\t}\n\t}\n\n\tif ($action == 'delete') {\n\t\ttry {\n\t\t\t$json_result = PhpSettings::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config\n\t\t{\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tPhpSettings::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t])->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('phpsetting_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['description']);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('nopermissionsorinvalidid');\n\t\t}\n\t}\n\n\tif ($action == 'edit') {\n\t\ttry {\n\t\t\t$json_result = PhpSettings::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tPhpSettings::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$fpmconfigs = [];\n\t\t\t\t$configs = Database::query(\"SELECT * FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` ORDER BY `description` ASC\");\n\t\t\t\twhile ($row = $configs->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$fpmconfigs[$row['id']] = $row['description'];\n\t\t\t\t}\n\n\t\t\t\t$phpconfig_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php';\n\n\t\t\t\tUI::view('user/form-replacers.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'phpsettings', 'id' => $id]),\n\t\t\t\t\t'formdata' => $phpconfig_edit_data['phpconfig_edit'],\n\t\t\t\t\t'replacers' => $phpconfig_edit_data['phpconfig_replacers'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('nopermissionsorinvalidid');\n\t\t}\n\t}\n} elseif ($page == 'fpmdaemons') {\n\tif ($action == '') {\n\t\ttry {\n\t\t\t$fpmconf_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.fpmconfigs.php';\n\t\t\t$collection = (new Collection(FpmDaemons::class, $userinfo))\n\t\t\t\t->withPagination($fpmconf_list_data['fpmconf_list']['columns'], $fpmconf_list_data['fpmconf_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $fpmconf_list_data, 'fpmconf_list'),\n\t\t\t'actions_links' => (bool)$userinfo['change_serversettings'] ? [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'phpsettings', 'page' => $page, 'action' => 'add']),\n\t\t\t\t\t'label' => lng('admin.fpmsettings.addnew')\n\t\t\t\t]\n\t\t\t] : []\n\t\t]);\n\t}\n\n\tif ($action == 'add') {\n\t\tif ((int)$userinfo['change_serversettings'] == 1) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tFpmDaemons::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$fpmconfig_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php';\n\n\t\t\t\tUI::view('user/form-replacers.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'phpsettings', 'page' => 'fpmdaemons']),\n\t\t\t\t\t'formdata' => $fpmconfig_add_data['fpmconfig_add'],\n\t\t\t\t\t'replacers' => $fpmconfig_add_data['fpmconfig_replacers']\n\t\t\t\t]);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('nopermissionsorinvalidid');\n\t\t}\n\t}\n\n\tif ($action == 'delete') {\n\t\ttry {\n\t\t\t$json_result = FpmDaemons::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($id == 1) {\n\t\t\tResponse::standardError('cannotdeletedefaultphpconfig');\n\t\t}\n\n\t\tif ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config\n\t\t{\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tFpmDaemons::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('fpmsetting_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['description']);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('nopermissionsorinvalidid');\n\t\t}\n\t}\n\n\tif ($action == 'edit') {\n\t\ttry {\n\t\t\t$json_result = FpmDaemons::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tFpmDaemons::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$fpmconfig_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php';\n\n\t\t\t\tUI::view('user/form-replacers.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'phpsettings', 'page' => 'fpmdaemons', 'id' => $id]),\n\t\t\t\t\t'formdata' => $fpmconfig_edit_data['fpmconfig_edit'],\n\t\t\t\t\t'replacers' => $fpmconfig_edit_data['fpmconfig_replacers'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('nopermissionsorinvalidid');\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "admin_plans.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\HostingPlans;\nuse Froxlor\\Api\\Commands\\MysqlServer;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n$id = (int)Request::any('id');\n\nif ($page == '' || $page == 'overview') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_plans\");\n\n\t\ttry {\n\t\t\t$plan_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.plans.php';\n\t\t\t$collection = (new Collection(HostingPlans::class, $userinfo))\n\t\t\t\t->withPagination($plan_list_data['plan_list']['columns'], $plan_list_data['plan_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $plan_list_data, 'plan_list'),\n\t\t\t'actions_links' => [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'plans', 'page' => $page, 'action' => 'add']),\n\t\t\t\t\t'label' => lng('admin.plans.add')\n\t\t\t\t]\n\t\t\t]\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = HostingPlans::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['adminid'] == $result['adminid']) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tHostingPlans::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t])->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('plan_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['name']);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('nopermissionsorinvalidid');\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tHostingPlans::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$mysql_servers = [];\n\t\t\ttry {\n\t\t\t\t$result_json = MysqlServer::getLocal($userinfo)->listing();\n\t\t\t\t$result_decoded = json_decode($result_json, true)['data']['list'];\n\t\t\t\tforeach ($result_decoded as $dbserver => $dbdata) {\n\t\t\t\t\t$mysql_servers[] = [\n\t\t\t\t\t\t'label' => $dbdata['caption'],\n\t\t\t\t\t\t'value' => $dbserver\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t} catch (Exception $e) {\n\t\t\t\t/* just none */\n\t\t\t}\n\n\t\t\t$phpconfigs = [];\n\t\t\t$configs = Database::query(\"\n\t\t\t\t\tSELECT c.*, fc.description as interpreter\n\t\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fc ON fc.id = c.fpmsettingid\n\t\t\t\t\");\n\t\t\twhile ($row = $configs->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t$phpconfigs[] = [\n\t\t\t\t\t\t'label' => $row['description'] . \" [\" . $row['interpreter'] . \"]\",\n\t\t\t\t\t\t'value' => $row['id']\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\t$phpconfigs[] = [\n\t\t\t\t\t\t'label' => $row['description'],\n\t\t\t\t\t\t'value' => $row['id']\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// dummy to avoid unknown variables\n\t\t\t$hosting_plans = null;\n\n\t\t\t$plans_add_data = include_once __DIR__ . '/lib/formfields/admin/plans/formfield.plans_add.php';\n\t\t\t$cust_add_data = include_once __DIR__ . '/lib/formfields/admin/customer/formfield.customer_add.php';\n\t\t\t// unset unneeded stuff\n\t\t\tunset($cust_add_data['customer_add']['sections']['section_a']);\n\t\t\tunset($cust_add_data['customer_add']['sections']['section_b']);\n\t\t\tunset($cust_add_data['customer_add']['sections']['section_cpre']);\n\t\t\t// merge\n\t\t\t$plans_add_data['plans_add']['sections'] = array_merge($plans_add_data['plans_add']['sections'], $cust_add_data['customer_add']['sections']);\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'plans']),\n\t\t\t\t'formdata' => $plans_add_data['plans_add']\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = HostingPlans::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ($result['name'] != '') {\n\t\t\t$result['value'] = json_decode($result['value'], true);\n\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\tforeach ($result['value'] as $index => $value) {\n\t\t\t\t$result[$index] = $value;\n\t\t\t}\n\t\t\t$result['allowed_phpconfigs'] = json_encode($result['allowed_phpconfigs']);\n\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tHostingPlans::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$mysql_servers = [];\n\t\t\t\ttry {\n\t\t\t\t\t$result_json = MysqlServer::getLocal($userinfo)->listing();\n\t\t\t\t\t$result_decoded = json_decode($result_json, true)['data']['list'];\n\t\t\t\t\tforeach ($result_decoded as $dbserver => $dbdata) {\n\t\t\t\t\t\t$mysql_servers[] = [\n\t\t\t\t\t\t\t'label' => $dbdata['caption'],\n\t\t\t\t\t\t\t'value' => $dbserver\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t/* just none */\n\t\t\t\t}\n\n\t\t\t\t$phpconfigs = [];\n\t\t\t\t$configs = Database::query(\"\n\t\t\t\t\tSELECT c.*, fc.description as interpreter\n\t\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fc ON fc.id = c.fpmsettingid\n\t\t\t\t\");\n\t\t\t\twhile ($row = $configs->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\t$phpconfigs[] = [\n\t\t\t\t\t\t\t'label' => $row['description'] . \" [\" . $row['interpreter'] . \"]\",\n\t\t\t\t\t\t\t'value' => $row['id']\n\t\t\t\t\t\t];\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$phpconfigs[] = [\n\t\t\t\t\t\t\t'label' => $row['description'],\n\t\t\t\t\t\t\t'value' => $row['id']\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$result['imap'] = $result['email_imap'];\n\t\t\t\t$result['pop3'] = $result['email_pop3'];\n\n\t\t\t\t// dummy to avoid unknown variables\n\t\t\t\t$result['loginname'] = null;\n\t\t\t\t$result['documentroot'] = null;\n\t\t\t\t$result['standardsubdomain'] = null;\n\t\t\t\t$result['deactivated'] = null;\n\t\t\t\t$result['def_language'] = null;\n\t\t\t\t$result['firstname'] = null;\n\t\t\t\t$result['gender'] = null;\n\t\t\t\t$result['company'] = null;\n\t\t\t\t$result['street'] = null;\n\t\t\t\t$result['zipcode'] = null;\n\t\t\t\t$result['city'] = null;\n\t\t\t\t$result['phone'] = null;\n\t\t\t\t$result['fax'] = null;\n\t\t\t\t$result['email'] = null;\n\t\t\t\t$result['customernumber'] = null;\n\t\t\t\t$result['custom_notes'] = null;\n\t\t\t\t$result['custom_notes_show'] = null;\n\t\t\t\t$result['api_allowed'] = null;\n\t\t\t\t$hosting_plans = null;\n\t\t\t\t$admin_select = [];\n\n\t\t\t\t$plans_edit_data = include_once __DIR__ . '/lib/formfields/admin/plans/formfield.plans_edit.php';\n\t\t\t\t$cust_edit_data = include_once __DIR__ . '/lib/formfields/admin/customer/formfield.customer_edit.php';\n\t\t\t\t// unset unneeded stuff\n\t\t\t\tunset($cust_edit_data['customer_edit']['sections']['section_a']);\n\t\t\t\tunset($cust_edit_data['customer_edit']['sections']['section_b']);\n\t\t\t\tunset($cust_edit_data['customer_edit']['sections']['section_cpre']);\n\t\t\t\tunset($cust_edit_data['customer_edit']['sections']['section_d']);\n\t\t\t\t// merge\n\t\t\t\t$plans_edit_data['plans_edit']['sections'] = array_merge($plans_edit_data['plans_edit']['sections'], $cust_edit_data['customer_edit']['sections']);\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'plans', 'id' => $id]),\n\t\t\t\t\t'formdata' => $plans_edit_data['plans_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'jqGetPlanValues') {\n\t\t$planid = (int)Request::any('planid', 0);\n\t\ttry {\n\t\t\t$json_result = HostingPlans::getLocal($userinfo, [\n\t\t\t\t'id' => $planid\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\t\techo $result['value'];\n\t\texit();\n\t}\n}\n"
  },
  {
    "path": "admin_settings.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Api\\Commands\\Froxlor;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Database\\IntegrityCheck;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Form;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse PHPMailer\\PHPMailer\\PHPMailer;\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nif ($page == 'overview' && $userinfo['change_serversettings'] == '1') {\n\t$settings_data = PhpHelper::loadConfigArrayDir('./actions/admin/settings/');\n\tSettings::loadSettingsInto($settings_data);\n\n\tif (Request::post('send') == 'send') {\n\t\t$_part = Request::get('part', '');\n\t\tif ($_part == '') {\n\t\t\t$_part = Request::post('part', '');\n\t\t}\n\n\t\tif ($_part != '') {\n\t\t\tif ($_part == 'all') {\n\t\t\t\t$settings_all = true;\n\t\t\t\t$settings_part = false;\n\t\t\t} else {\n\t\t\t\t$settings_all = false;\n\t\t\t\t$settings_part = true;\n\t\t\t}\n\t\t\t$only_enabledisable = false;\n\t\t} else {\n\t\t\t$settings_all = false;\n\t\t\t$settings_part = false;\n\t\t\t$only_enabledisable = true;\n\t\t}\n\n\t\t// check if the session timeout is too low #815\n\t\tif (!empty(Request::post('session_sessiontimeout')) &&  intval(Request::post('session_sessiontimeout', 0)) < 60) {\n\t\t\tResponse::standardError(['session_timeout', 'session_timeout_desc']);\n\t\t}\n\n\t\ttry {\n\t\t\tif (Form::processForm($settings_data, Request::postAll(), [\n\t\t\t\t'filename' => $filename,\n\t\t\t\t'action' => $action,\n\t\t\t\t'page' => $page,\n\t\t\t\t'part' => $_part,\n\t\t\t], $_part, $settings_all, $settings_part, $only_enabledisable)) {\n\t\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"rebuild configfiles due to changed setting\");\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t\t\t// cron.d file\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_CRON);\n\n\t\t\t\tResponse::standardSuccess('settingssaved', '', [\n\t\t\t\t\t'filename' => $filename,\n\t\t\t\t\t'action' => $action,\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t}\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage(), $e->getCode());\n\t\t}\n\t} else {\n\t\t$_part = Request::get('part', '');\n\t\tif ($_part == '') {\n\t\t\t$_part = Request::post('part', '');\n\t\t}\n\n\t\t$fields = Form::buildForm($settings_data, $_part);\n\n\t\tif ($_part == '' || $_part == 'all') {\n\t\t\tUI::view('settings/index.html.twig', ['fields' => $fields]);\n\t\t} else {\n\t\t\t$em = Request::any('em', '');\n\t\t\tUI::view('settings/detailpart.html.twig', [\n\t\t\t\t'fields' => $fields,\n\t\t\t\t'em' => $em,\n\t\t\t\t'part' => $_part,\n\t\t\t\t// alert-box\n\t\t\t\t'type' => 'warning',\n\t\t\t\t'heading' => lng('dns.nis2note.title'),\n\t\t\t\t'alert_msg' => lng('dns.nis2note.content')\n\t\t\t]);\n\t\t}\n\t}\n} elseif ($page == 'phpinfo' && $userinfo['change_serversettings'] == '1') {\n\tob_start();\n\tphpinfo();\n\t$phpinfo = [\n\t\t'phpinfo' => []\n\t];\n\tif (preg_match_all('#(?:<h2>(?:<a name=\".*?\">)?(.*?)(?:</a>)?</h2>)|(?:<tr(?: class=\".*?\")?><t[hd](?: class=\".*?\")?>(.*?)\\s*</t[hd]>(?:<t[hd](?: class=\".*?\")?>(.*?)\\s*</t[hd]>(?:<t[hd](?: class=\".*?\")?>(.*?)\\s*</t[hd]>)?)?</tr>)#s', ob_get_clean(), $matches, PREG_SET_ORDER)) {\n\t\tforeach ($matches as $match) {\n\t\t\t$end = array_keys($phpinfo);\n\t\t\t$end = end($end);\n\t\t\tif (strlen($match[1])) {\n\t\t\t\t$phpinfo[$match[1]] = [];\n\t\t\t} elseif (isset($match[3])) {\n\t\t\t\t$phpinfo[$end][$match[2]] = isset($match[4]) ? [\n\t\t\t\t\t$match[3],\n\t\t\t\t\t$match[4]\n\t\t\t\t] : $match[3];\n\t\t\t} else {\n\t\t\t\t$phpinfo[$end][] = $match[2];\n\t\t\t}\n\t\t}\n\t} else {\n\t\tResponse::standardError('error.no_phpinfo');\n\t}\n\tUI::view('settings/phpinfo.html.twig', [\n\t\t'phpversion' => PHP_VERSION,\n\t\t'phpinfo' => $phpinfo\n\t]);\n} elseif ($page == 'rebuildconfigs' && $userinfo['change_serversettings'] == '1') {\n\tif (Request::post('send') == 'send') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"rebuild configfiles\");\n\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\tCronjob::inserttask(TaskId::CREATE_QUOTA);\n\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t// cron.d file\n\t\tCronjob::inserttask(TaskId::REBUILD_CRON);\n\n\t\tResponse::standardSuccess('rebuildingconfigs', '', [\n\t\t\t'filename' => 'admin_index.php'\n\t\t]);\n\t} else {\n\t\tHTML::askYesNo('admin_configs_reallyrebuild', $filename, [\n\t\t\t'page' => $page\n\t\t]);\n\t}\n} elseif ($page == 'updatecounters' && $userinfo['change_serversettings'] == '1') {\n\tif (Request::post('send') == 'send') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"updated resource-counters\");\n\t\t$updatecounters = User::updateCounters(true);\n\t\tUI::view('user/resource-counter.html.twig', [\n\t\t\t'counters' => $updatecounters\n\t\t]);\n\t} else {\n\t\tHTML::askYesNo('admin_counters_reallyupdate', $filename, [\n\t\t\t'page' => $page\n\t\t]);\n\t}\n} elseif ($page == 'wipecleartextmailpws' && $userinfo['change_serversettings'] == '1') {\n\tif (Request::post('send') == 'send') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"wiped all cleartext mail passwords\");\n\t\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password` = '';\");\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `value` = '0' WHERE `settinggroup` = 'system' AND `varname` = 'mailpwcleartext'\");\n\t\tResponse::redirectTo($filename);\n\t} else {\n\t\tHTML::askYesNo('admin_cleartextmailpws_reallywipe', $filename, [\n\t\t\t'page' => $page\n\t\t]);\n\t}\n} elseif ($page == 'wipequotas' && $userinfo['change_serversettings'] == '1') {\n\tif (Request::post('send') == 'send') {\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"wiped all mailquotas\");\n\n\t\t// Set the quota to 0 which means unlimited\n\t\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `quota` = '0';\");\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `email_quota_used` = '0'\");\n\t\tResponse::redirectTo($filename);\n\t} else {\n\t\tHTML::askYesNo('admin_quotas_reallywipe', $filename, [\n\t\t\t'page' => $page\n\t\t]);\n\t}\n} elseif ($page == 'enforcequotas' && $userinfo['change_serversettings'] == '1') {\n\tif (Request::post('send') == 'send') {\n\t\t// Fetch all accounts\n\t\t$result_stmt = Database::query(\"SELECT `quota`, `customerid` FROM `\" . TABLE_MAIL_USERS . \"`\");\n\n\t\tif (Database::num_rows() > 0) {\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t`email_quota_used` = `email_quota_used` + :diff\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\");\n\n\t\t\twhile ($array = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$difference = Settings::Get('system.mail_quota') - $array['quota'];\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'diff' => $difference,\n\t\t\t\t\t'customerid' => $customerid\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\n\t\t// Set the new quota\n\t\t$upd_stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_MAIL_USERS . \"` SET `quota` = :quota\n\t\t\");\n\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t'quota' => Settings::Get('system.mail_quota')\n\t\t]);\n\n\t\t// Update the Customer, if the used quota is bigger than the allowed quota\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `email_quota` = `email_quota_used` WHERE `email_quota` < `email_quota_used`\");\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, 'enforcing mailquota to all customers: ' . Settings::Get('system.mail_quota') . ' MB');\n\t\tResponse::redirectTo($filename);\n\t} else {\n\t\tHTML::askYesNo('admin_quotas_reallyenforce', $filename, [\n\t\t\t'page' => $page\n\t\t]);\n\t}\n} elseif ($page == 'integritycheck' && $userinfo['change_serversettings'] == '1') {\n\t$integrity = new IntegrityCheck();\n\tif (Request::post('send') == 'send') {\n\t\t$integrity->fixAll();\n\t} elseif (Request::get('action') == \"fix\") {\n\t\tHTML::askYesNo('admin_integritycheck_reallyfix', $filename, [\n\t\t\t'page' => $page\n\t\t]);\n\t}\n\n\t$integritycheck = [];\n\tforeach ($integrity->available as $id => $check) {\n\t\t$integritycheck[] = [\n\t\t\t'displayid' => $id + 1,\n\t\t\t'result' => $integrity->$check(),\n\t\t\t'checkdesc' => lng('integrity_check.' . $check)\n\t\t];\n\t}\n\n\t$integrity_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.integrity.php';\n\t$collection = [\n\t\t'data' => $integritycheck,\n\t\t'pagination' => []\n\t];\n\n\tUI::view('user/table.html.twig', [\n\t\t'listing' => Listing::formatFromArray($collection, $integrity_list_data['integrity_list'], 'integrity_list'),\n\t\t'actions_links' => [\n\t\t\t[\n\t\t\t\t'href' => $linker->getLink(['section' => 'settings', 'page' => $page, 'action' => 'fix']),\n\t\t\t\t'label' => lng('admin.integrityfix'),\n\t\t\t\t'icon' => 'fa-solid fa-screwdriver-wrench',\n\t\t\t\t'class' => 'btn-warning'\n\t\t\t]\n\t\t]\n\t]);\n} elseif ($page == 'importexport' && $userinfo['change_serversettings'] == '1') {\n\t// check for json-stuff\n\tif (!extension_loaded('json')) {\n\t\tResponse::standardError('jsonextensionnotfound');\n\t}\n\n\tif (Request::get('action') == \"export\") {\n\t\t// export\n\t\ttry {\n\t\t\t$json_result = Froxlor::getLocal($userinfo)->exportSettings();\n\t\t\t$json_export = json_decode($json_result, true)['data'];\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\theader('Content-disposition: attachment; filename=Froxlor_settings-' . \\Froxlor\\Froxlor::VERSION . '-' . \\Froxlor\\Froxlor::DBVERSION . '_' . date('d.m.Y') . '.json');\n\t\theader('Content-type: application/json');\n\t\techo $json_export;\n\t\texit();\n\t} elseif (Request::get('action') == \"import\") {\n\t\t// import\n\t\tif (Request::post('send') == 'send') {\n\t\t\t// get uploaded file\n\t\t\tif (isset($_FILES[\"import_file\"][\"tmp_name\"])) {\n\t\t\t\t$imp_content = file_get_contents($_FILES[\"import_file\"][\"tmp_name\"]);\n\t\t\t\ttry {\n\t\t\t\t\tFroxlor::getLocal($userinfo, [\n\t\t\t\t\t\t'json_str' => $imp_content\n\t\t\t\t\t])->importSettings();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::standardSuccess('settingsimported', '', [\n\t\t\t\t\t'filename' => 'admin_settings.php'\n\t\t\t\t]);\n\t\t\t}\n\t\t\tResponse::dynamicError(\"Upload failed\");\n\t\t}\n\t} else {\n\t\t$settings_data = include_once dirname(__FILE__) . '/lib/formfields/admin/settings/formfield.settings_import.php';\n\n\t\tUI::view('user/form.html.twig', [\n\t\t\t'formaction' => $linker->getLink(['section' => 'settings', 'page' => $page, 'action' => 'import']),\n\t\t\t'formdata' => $settings_data['settings_import'],\n\t\t\t'actions_links' => [\n\t\t\t\t[\n\t\t\t\t\t'class' => 'btn-outline-primary',\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'settings', 'page' => 'overview']),\n\t\t\t\t\t'label' => lng('admin.configfiles.overview'),\n\t\t\t\t\t'icon' => 'fa-solid fa-grip'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'class' => 'btn-outline-secondary',\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'settings', 'page' => $page, 'action' => 'export']),\n\t\t\t\t\t'label' => 'Download/export ' . lng('admin.serversettings'),\n\t\t\t\t\t'icon' => 'fa-solid fa-file-import'\n\t\t\t\t]\n\t\t\t]\n\t\t]);\n\t}\n} elseif ($page == 'testmail') {\n\t$note_type = 'info';\n\t$note_msg = lng('admin.smtptestnote');\n\n\tif (Request::post('send') == 'send') {\n\t\t$test_addr = Request::post('test_addr');\n\n\t\t// Initialize the mailingsystem\n\t\t$testmail = new PHPMailer(true);\n\t\t$testmail->CharSet = \"UTF-8\";\n\n\t\tif (Settings::Get('system.mail_use_smtp')) {\n\t\t\t$testmail->isSMTP();\n\t\t\t$testmail->Host = Settings::Get('system.mail_smtp_host');\n\t\t\t$testmail->SMTPAuth = Settings::Get('system.mail_smtp_auth') == '1';\n\t\t\t$testmail->Username = Settings::Get('system.mail_smtp_user');\n\t\t\t$testmail->Password = Settings::Get('system.mail_smtp_passwd');\n\t\t\tif (Settings::Get('system.mail_smtp_usetls')) {\n\t\t\t\t$testmail->SMTPSecure = 'tls';\n\t\t\t} else {\n\t\t\t\t$testmail->SMTPAutoTLS = false;\n\t\t\t}\n\t\t\t$testmail->Port = Settings::Get('system.mail_smtp_port');\n\t\t}\n\n\t\t$_mailerror = false;\n\t\tif (PHPMailer::ValidateAddress(Settings::Get('panel.adminmail')) !== false) {\n\t\t\t// set return-to address and custom sender-name, see #76\n\t\t\t$testmail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));\n\t\t\tif (Settings::Get('panel.adminmail_return') != '') {\n\t\t\t\t$testmail->AddReplyTo(Settings::Get('panel.adminmail_return'), Settings::Get('panel.adminmail_defname'));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t$testmail->Subject = \"Froxlor Test-Mail\";\n\t\t\t\t$mail_body = \"Yay, this worked :)\";\n\t\t\t\t$testmail->AltBody = $mail_body;\n\t\t\t\t$testmail->MsgHTML(str_replace(\"\\n\", \"<br />\", $mail_body));\n\t\t\t\t$testmail->AddAddress($test_addr);\n\t\t\t\t$testmail->Send();\n\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t$note_type = 'danger';\n\t\t\t\t$note_msg = $e->getMessage();\n\t\t\t\t$_mailerror = true;\n\t\t\t} catch (Exception $e) {\n\t\t\t\t$note_type = 'danger';\n\t\t\t\t$note_msg = $e->getMessage();\n\t\t\t\t$_mailerror = true;\n\t\t\t}\n\n\t\t\tif (!$_mailerror) {\n\t\t\t\t// success\n\t\t\t\t$mail->ClearAddresses();\n\t\t\t\tResponse::standardSuccess('testmailsent', '', [\n\t\t\t\t\t'filename' => 'admin_settings.php',\n\t\t\t\t\t'page' => 'testmail'\n\t\t\t\t]);\n\t\t\t}\n\t\t} else {\n\t\t\t// invalid sender e-mail\n\t\t\t$note_type = 'warning';\n\t\t\t$note_msg = \"Invalid sender e-mail address: \" . Settings::Get('panel.adminmail');\n\t\t}\n\t}\n\n\t$mailtest_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/settings/formfield.settings_mailtest.php';\n\n\tUI::view('user/form-note.html.twig', [\n\t\t'formaction' => $linker->getLink(['section' => 'settings']),\n\t\t'formdata' => $mailtest_add_data['mailtest'],\n\t\t'actions_links' => ($userinfo['change_serversettings'] == '1' ? [\n\t\t\t[\n\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t'section' => 'settings',\n\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t'part' => 'system',\n\t\t\t\t\t'em' => 'system_mail_use_smtp'\n\t\t\t\t]),\n\t\t\t\t'label' => lng('admin.smtpsettings'),\n\t\t\t\t'icon' => 'fa-solid fa-gears',\n\t\t\t\t'class' => 'btn-outline-secondary'\n\t\t\t]\n\t\t] : []),\n\t\t// alert-box\n\t\t'type' => $note_type,\n\t\t'alert_msg' => $note_msg\n\t]);\n} elseif ($page == 'toggleSettingsMode') {\n\tif ($userinfo['change_serversettings'] == '1') {\n\t\t$cmode = Settings::Get('panel.settings_mode');\n\t\tSettings::Set('panel.settings_mode', (int)(!(bool)$cmode));\n\t}\n\tResponse::redirectTo($filename);\n}\n"
  },
  {
    "path": "admin_templates.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Language;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse Froxlor\\CurrentUser;\n\n$id = (int)Request::any('id');\n$subjectid = intval(Request::any('subjectid'));\n$mailbodyid = intval(Request::any('mailbodyid'));\n\n$available_templates = [\n\t'createcustomer',\n\t'pop_success',\n\t'new_database_by_customer',\n\t'new_ftpaccount_by_customer',\n\t'password_reset'\n];\n\n// only show templates of features that are enabled #1191\nif ((int)Settings::Get('system.report_enable') == 1) {\n\tarray_push($available_templates, 'trafficmaxpercent', 'diskmaxpercent');\n}\nif (Settings::Get('panel.sendalternativemail') == 1) {\n\tarray_push($available_templates, 'pop_success_alternative');\n}\n\n$file_templates = [\n\t'index_html',\n\t'unconfigured_html'\n];\n\n$languages = Language::getLanguages();\n\nif ($action == '') {\n\t// email templates\n\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_templates\");\n\n\t$templates_array = [];\n\t$result_stmt = Database::prepare(\"\n\t\tSELECT `id`, `language`, `varname` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\tWHERE `adminid` = :adminid AND `templategroup`='mails'\n\t\tORDER BY `language`, `varname`\n\t\");\n\tDatabase::pexecute($result_stmt, [\n\t\t'adminid' => $userinfo['adminid']\n\t]);\n\n\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t$parts = [];\n\t\tpreg_match('/^([a-z]([a-z_]+[a-z])*)_(mailbody|subject)$/', $row['varname'], $parts);\n\t\t$templates_array[$row['language']][$parts[1]][$parts[3]] = $row['id'];\n\t}\n\n\t$templates = [];\n\tforeach ($templates_array as $language => $template_defs) {\n\t\tforeach ($template_defs as $action => $email) {\n\t\t\t$templates[] = [\n\t\t\t\t'subjectid' => $email['subject'],\n\t\t\t\t'mailbodyid' => $email['mailbody'],\n\t\t\t\t'template' => lng('admin.templates.' . $action),\n\t\t\t\t'language' => $language\n\t\t\t];\n\t\t}\n\t}\n\n\t$mail_actions_links = false;\n\tforeach ($languages as $language_file => $language_name) {\n\t\t$templates_done = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT `varname` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\tWHERE `adminid` = :adminid AND `language`= :lang\n\t\t\tAND `templategroup` = 'mails' AND `varname` LIKE '%_subject'\n\t\t\");\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t'lang' => $language_name\n\t\t]);\n\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$templates_done[] = str_replace('_subject', '', $row['varname']);\n\t\t}\n\n\t\tif (count(array_diff($available_templates, $templates_done)) > 0) {\n\t\t\t$mail_actions_links = [\n\t\t\t\t[\n\t\t\t\t\t'href' => $linker->getLink(['section' => 'templates', 'page' => $page, 'action' => 'add']),\n\t\t\t\t\t'label' => lng('admin.templates.template_add')\n\t\t\t\t]\n\t\t\t];\n\t\t}\n\t}\n\n\t$mailtpl_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.mailtemplates.php';\n\t$collection_mail = [\n\t\t'data' => $templates,\n\t\t'pagination' => []\n\t];\n\n\t// filetemplates\n\t$result_stmt = Database::prepare(\"\n\t\tSELECT `id`, `varname` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\tWHERE `adminid` = :adminid AND `templategroup`='files'\");\n\tDatabase::pexecute($result_stmt, [\n\t\t'adminid' => $userinfo['adminid']\n\t]);\n\n\t$filetemplates = [];\n\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t$filetemplates[] = [\n\t\t\t'id' => $row['id'],\n\t\t\t'template' => lng('admin.templates.' . $row['varname'])\n\t\t];\n\t}\n\n\t$file_actions_links = false;\n\tif (Database::num_rows() != count($file_templates)) {\n\t\t$file_actions_links = [\n\t\t\t[\n\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t'section' => 'templates',\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => 'add',\n\t\t\t\t\t'files' => 'files'\n\t\t\t\t]),\n\t\t\t\t'label' => lng('admin.templates.template_fileadd')\n\t\t\t]\n\t\t];\n\t}\n\n\t$filetpl_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.filetemplates.php';\n\t$collection_file = [\n\t\t'data' => $filetemplates,\n\t\t'pagination' => []\n\t];\n\n\tif ($mail_actions_links === false) {\n\t\t$mail_actions_links = [];\n\t}\n\tif ($file_actions_links === false) {\n\t\t$file_actions_links = [];\n\t}\n\n\tUI::view('user/table-tpl.html.twig', [\n\t\t'maillisting' => Listing::formatFromArray($collection_mail, $mailtpl_list_data['mailtpl_list'], 'mailtpl_list'),\n\t\t'filelisting' => Listing::formatFromArray($collection_file, $filetpl_list_data['filetpl_list'], 'filetpl_list'),\n\t\t'actions_links' => array_merge($mail_actions_links, $file_actions_links)\n\t]);\n} elseif ($action == 'delete' && $subjectid != 0 && $mailbodyid != 0) {\n\t// email templates\n\t$result_stmt = Database::prepare(\"\n\t\tSELECT `language`, `varname` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\tWHERE `adminid` = :adminid AND `id` = :id\");\n\tDatabase::pexecute($result_stmt, [\n\t\t'adminid' => $userinfo['adminid'],\n\t\t'id' => $subjectid\n\t]);\n\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\n\tif ($result['varname'] != '') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\tAND (`id` = :ida OR `id` = :idb)\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t\t'ida' => $subjectid,\n\t\t\t\t'idb' => $mailbodyid\n\t\t\t]);\n\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"deleted template '\" . $result['language'] . ' - ' . lng('admin.templates.' . str_replace('_subject', '', $result['varname'])) . \"'\");\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\tHTML::askYesNo('admin_template_reallydelete', $filename, [\n\t\t\t\t'subjectid' => $subjectid,\n\t\t\t\t'mailbodyid' => $mailbodyid,\n\t\t\t\t'page' => $page,\n\t\t\t\t'action' => $action\n\t\t\t], $result['language'] . ' - ' . lng('admin.templates.' . str_replace('_subject', '', $result['varname'])));\n\t\t}\n\t}\n} elseif ($action == 'deletef' && $id != 0) {\n\t// file templates\n\t$result_stmt = Database::prepare(\"\n\t\tSELECT * FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\tWHERE `adminid` = :adminid AND `id` = :id\");\n\tDatabase::pexecute($result_stmt, [\n\t\t'adminid' => $userinfo['adminid'],\n\t\t'id' => $id\n\t]);\n\n\tif (Database::num_rows() > 0) {\n\t\t$row = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\tif (Request::post('send') == 'send') {\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\t\tWHERE `adminid` = :adminid AND `id` = :id\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t\t'id' => $id\n\t\t\t]);\n\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"deleted template '\" . lng('admin.templates.' . $row['varname']) . \"'\");\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\tHTML::askYesNo('admin_template_reallydelete', $filename, [\n\t\t\t\t'id' => $id,\n\t\t\t\t'page' => $page,\n\t\t\t\t'action' => $action\n\t\t\t], lng('admin.templates.' . $row['varname']));\n\t\t}\n\t} else {\n\t\tResponse::standardError('templatenotfound');\n\t}\n} elseif ($action == 'add') {\n\tif (Request::post('prepare') == 'prepare') {\n\t\t// email templates\n\t\t$language = htmlentities(Validate::validate(Request::post('language'), 'language', '/^[^\\r\\n\\0\"\\']+$/', 'nolanguageselect'));\n\t\tif (!array_key_exists($language, $languages)) {\n\t\t\tResponse::standardError('templatelanguageinvalid');\n\t\t}\n\t\t$template = Validate::validate(Request::post('template'), 'template');\n\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(*) as def FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\tWHERE `adminid` = :adminid AND `language` = :lang\n\t\t\tAND `templategroup` = 'mails' AND `varname` LIKE :template\n\t\t\");\n\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t'lang' => $language,\n\t\t\t'template' => $template . '%'\n\t\t]);\n\t\tif ($result && $result['def'] > 0) {\n\t\t\tResponse::standardError('templatelanguagecombodefined');\n\t\t}\n\n\t\t// set target language\n\t\tLanguage::setLanguage($language);\n\n\t\t$subject = lng('mails.' . $template . '.subject');\n\t\t$body = str_replace('\\n', \"\\n\", lng('mails.' . $template . '.mailbody'));\n\n\t\t// re set language to user\n\t\tLanguage::setLanguage(CurrentUser::getField('def_language'));\n\n\t\t$template_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/templates/formfield.template_add.php';\n\n\t\tUI::view('user/form-replacers.html.twig', [\n\t\t\t'formaction' => $linker->getLink(['section' => 'templates']),\n\t\t\t'formdata' => $template_add_data['template_add'],\n\t\t\t'replacers' => $template_add_data['template_replacers']\n\t\t]);\n\t} elseif (Request::post('send') == 'send' && empty(Request::post('filesend'))) {\n\t\t// email templates\n\t\t$language = htmlentities(Validate::validate(Request::post('language'), 'language', '/^[^\\r\\n\\0\"\\']+$/', 'nolanguageselect'));\n\t\tif (!array_key_exists($language, $languages)) {\n\t\t\tResponse::standardError('templatelanguageinvalid');\n\t\t}\n\t\t$template = Validate::validate(Request::post('template'), 'template');\n\t\t$subject = Validate::validate(Request::post('subject'), 'subject', '/^[^\\r\\n\\0]+$/', 'nosubjectcreate');\n\t\t$mailbody = Validate::validate(Request::post('mailbody'), 'mailbody', '/^[^\\0]+$/', 'nomailbodycreate');\n\t\t$templates = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT `varname` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\tWHERE `adminid` = :adminid AND `language` = :lang\n\t\t\tAND `templategroup` = 'mails' AND `varname` LIKE '%_subject'\");\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t'lang' => $language\n\t\t]);\n\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$templates[] = str_replace('_subject', '', $row['varname']);\n\t\t}\n\n\t\t$templates = array_diff($available_templates, $templates);\n\t\tif (!in_array($template, $templates)) {\n\t\t\tResponse::standardError('templatenotfound');\n\t\t} else {\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_TEMPLATES . \"` SET\n\t\t\t\t\t`adminid` = :adminid,\n\t\t\t\t\t`language` = :lang,\n\t\t\t\t\t`templategroup` = 'mails',\n\t\t\t\t\t`varname` = :var,\n\t\t\t\t\t`value` = :value\");\n\n\t\t\t// mail-subject\n\t\t\t$ins_data = [\n\t\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t\t'lang' => $language,\n\t\t\t\t'var' => $template . '_subject',\n\t\t\t\t'value' => $subject\n\t\t\t];\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\n\t\t\t// mail-body\n\t\t\t$ins_data = [\n\t\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t\t'lang' => $language,\n\t\t\t\t'var' => $template . '_mailbody',\n\t\t\t\t'value' => $mailbody\n\t\t\t];\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\n\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"added template '\" . $language . ' - ' . $template . \"'\");\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t}\n\t} elseif (Request::post('filesend') == 'filesend') {\n\t\t// file templates\n\t\t$template = Validate::validate(Request::post('template'), 'template');\n\t\t$filecontent = Validate::validate(Request::post('filecontent'), 'filecontent', '/^[^\\0]+$/', 'filecontentnotset');\n\n\t\t$ins_stmt = Database::prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_TEMPLATES . \"` SET\n\t\t\t\t`adminid` = :adminid,\n\t\t\t\t`language` = '',\n\t\t\t\t`templategroup` = 'files',\n\t\t\t\t`varname` = :var,\n\t\t\t\t`value` = :value\");\n\n\t\t$ins_data = [\n\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t'var' => $template,\n\t\t\t'value' => $filecontent\n\t\t];\n\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\n\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"added template '\" . $template . \"'\");\n\t\tResponse::redirectTo($filename, [\n\t\t\t'page' => $page\n\t\t]);\n\t} elseif (empty(Request::get('files'))) {\n\t\t// email templates\n\t\t$add = false;\n\t\t$language_options = [];\n\t\t$template_options = [];\n\n\t\tforeach ($languages as $language_file => $language_name) {\n\t\t\t$templates = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `varname` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\t\tWHERE `adminid` = :adminid AND `language` = :lang\n\t\t\t\tAND `templategroup` = 'mails' AND `varname` LIKE '%_subject'\");\n\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t\t'lang' => $language_name\n\t\t\t]);\n\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$templates[] = str_replace('_subject', '', $row['varname']);\n\t\t\t}\n\n\t\t\tif (count(array_diff($available_templates, $templates)) > 0) {\n\t\t\t\t$add = true;\n\t\t\t\t$language_options[$language_file] = $language_name;\n\n\t\t\t\t$templates = array_diff($available_templates, $templates);\n\n\t\t\t\tforeach ($templates as $template) {\n\t\t\t\t\t$template_options[$template] = lng('admin.templates.' . $template);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($add) {\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'templates']),\n\t\t\t\t'formdata' => [\n\t\t\t\t\t'title' => lng('admin.templates.template_add'),\n\t\t\t\t\t'image' => 'fa-solid fa-plus',\n\t\t\t\t\t'self_overview' => ['section' => 'templates', 'page' => 'email'],\n\t\t\t\t\t'sections' => [\n\t\t\t\t\t\t'section_a' => [\n\t\t\t\t\t\t\t'title' => lng('admin.templates.template_add'),\n\t\t\t\t\t\t\t'fields' => [\n\t\t\t\t\t\t\t\t'language' => [\n\t\t\t\t\t\t\t\t\t'label' => lng('login.language'),\n\t\t\t\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t\t\t\t'select_var' => $language_options,\n\t\t\t\t\t\t\t\t\t'selected' => $userinfo['language']\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t'template' => [\n\t\t\t\t\t\t\t\t\t'label' => lng('admin.templates.action'),\n\t\t\t\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t\t\t\t'select_var' => $template_options\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t'prepare' => [\n\t\t\t\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t\t\t\t'value' => 'prepare'\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'editid' => $id\n\t\t\t]);\n\t\t} else {\n\t\t\tResponse::standardError('alltemplatesdefined');\n\t\t}\n\t} else {\n\t\t// filetemplates\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT `id`, `varname` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\tWHERE `adminid` = :adminid AND `templategroup`='files'\");\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'adminid' => $userinfo['adminid']\n\t\t]);\n\n\t\tif (Database::num_rows() == count($file_templates)) {\n\t\t\tResponse::standardError('alltemplatesdefined');\n\t\t} else {\n\t\t\t$templatesdefined = [];\n\t\t\t$free_templates = [];\n\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$templatesdefined[] = $row['varname'];\n\t\t\t}\n\n\t\t\tforeach (array_diff($file_templates, $templatesdefined) as $template) {\n\t\t\t\t$free_templates[$template] = lng('admin.templates.' . $template);\n\t\t\t}\n\n\t\t\t$filetemplate_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/templates/formfield.filetemplate_add.php';\n\n\t\t\tUI::view('user/form-replacers.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'templates']),\n\t\t\t\t'formdata' => $filetemplate_add_data['filetemplate_add'],\n\t\t\t\t'replacers' => $filetemplate_add_data['filetemplate_replacers']\n\t\t\t]);\n\t\t}\n\t}\n} elseif ($action == 'edit' && $subjectid != 0 && $mailbodyid != 0) {\n\t// email templates\n\t$result_stmt = Database::prepare(\"\n\t\tSELECT `language`, `varname`, `value` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\tWHERE `adminid` = :adminid AND `id` = :subjectid\");\n\tDatabase::pexecute($result_stmt, [\n\t\t'adminid' => $userinfo['adminid'],\n\t\t'subjectid' => $subjectid\n\t]);\n\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\n\tif ($result['varname'] != '') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\t$subject = Validate::validate(Request::post('subject'), 'subject', '/^[^\\r\\n\\0]+$/', 'nosubjectcreate');\n\t\t\t$mailbody = Validate::validate(Request::post('mailbody'), 'mailbody', '/^[^\\0]+$/', 'nomailbodycreate');\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_TEMPLATES . \"` SET\n\t\t\t\t\t`value` = :value\n\t\t\t\tWHERE `adminid` = :adminid AND `id` = :id\");\n\t\t\t// subject\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'value' => $subject,\n\t\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t\t'id' => $subjectid\n\t\t\t]);\n\t\t\t// same query but mailbody\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'value' => $mailbody,\n\t\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t\t'id' => $mailbodyid\n\t\t\t]);\n\n\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"edited template '\" . $result['varname'] . \"'\");\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\t\t\t$template_name = lng('admin.templates.' . str_replace('_subject', '', $result['varname']));\n\t\t\t$subject = $result['value'];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `language`, `varname`, `value`\n\t\t\t\tFROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\t\tWHERE `id` = :id\");\n\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t'id' => $mailbodyid\n\t\t\t]);\n\t\t\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t\t$template = str_replace('_mailbody', '', $result['varname']);\n\n\t\t\t// don't escape the already escaped language-string so save up before htmlentities()\n\t\t\t$language = $result['language'];\n\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\t\t\t$mailbody = $result['value'];\n\n\t\t\t$template_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/templates/formfield.template_edit.php';\n\n\t\t\tUI::view('user/form-replacers.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'templates']),\n\t\t\t\t'formdata' => $template_edit_data['template_edit'],\n\t\t\t\t'replacers' => $template_edit_data['template_replacers']\n\t\t\t]);\n\t\t}\n\t}\n} elseif ($action == 'editf' && $id != 0) {\n\t// file templates\n\t$result_stmt = Database::prepare(\"\n\t\tSELECT * FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\tWHERE `adminid` = :adminid AND `id` = :id\");\n\tDatabase::pexecute($result_stmt, [\n\t\t'adminid' => $userinfo['adminid'],\n\t\t'id' => $id\n\t]);\n\n\tif (Database::num_rows() > 0) {\n\t\t$row = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t// filetemplates\n\t\tif (Request::post('filesend') == 'filesend') {\n\t\t\t$filecontent = Validate::validate(Request::post('filecontent'), 'filecontent', '/^[^\\0]+$/', 'filecontentnotset');\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_TEMPLATES . \"` SET\n\t\t\t\t\t`value` = :value\n\t\t\t\tWHERE `adminid` = :adminid AND `id` = :id\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'value' => $filecontent,\n\t\t\t\t'adminid' => $userinfo['adminid'],\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"edited template '\" . $row['varname'] . \"'\");\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$row = PhpHelper::htmlentitiesArray($row);\n\n\t\t\t$filetemplate_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/templates/formfield.filetemplate_edit.php';\n\n\t\t\tUI::view('user/form-replacers.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'templates']),\n\t\t\t\t'formdata' => $filetemplate_edit_data['filetemplate_edit'],\n\t\t\t\t'replacers' => $filetemplate_edit_data['filetemplate_replacers'],\n\t\t\t\t'editid' => $id\n\t\t\t]);\n\t\t}\n\t} else {\n\t\tResponse::standardError('templatenotfound');\n\t}\n}\n"
  },
  {
    "path": "admin_traffic.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Traffic\\Traffic;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n$range = Request::any('range', 'currentmonth');\n\nif ($page == 'overview' || $page == 'customers') {\n\ttry {\n\t\t$context = Traffic::getCustomerStats($userinfo, $range);\n\t} catch (Exception $e) {\n\t\tif ($e->getCode() === 405) {\n\t\t\tResponse::dynamicError(lng('traffic.nocustomers'));\n\t\t}\n\t\tResponse::dynamicError($e->getMessage());\n\t}\n\n\t// pass metrics to the view\n\tUI::view('user/traffic.html.twig', $context);\n}\n"
  },
  {
    "path": "admin_updates.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'admin';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Install\\Preconfig;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\n\nif ($page == 'overview') {\n\t$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"viewed admin_updates\");\n\n\tif (!Froxlor::isFroxlor()) {\n\t\tthrow new Exception('SysCP/customized upgrades are not supported');\n\t}\n\n\tif (Froxlor::hasDbUpdates() || Froxlor::hasUpdates()) {\n\t\t$successful_update = false;\n\t\t$message = '';\n\n\t\tif (Request::post('send') == 'send') {\n\t\t\tif ((!empty(Request::post('update_preconfig')) && intval(Request::post('update_changesagreed', 0)) != 0) || empty(Request::post('update_preconfig'))) {\n\t\t\t\tinclude_once Froxlor::getInstallDir() . 'install/updatesql.php';\n\n\t\t\t\tUser::updateCounters();\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t@chmod(Froxlor::getInstallDir() . '/lib/userdata.inc.php', 0400);\n\n\t\t\t\tUI::view('install/update.html.twig', [\n\t\t\t\t\t'checks' => Update::getUpdateTasks()\n\t\t\t\t]);\n\t\t\t\texit;\n\t\t\t} else {\n\t\t\t\t$message = '<br><br><strong>You have to agree that you have read the update notifications.</strong>';\n\t\t\t}\n\t\t}\n\n\t\t$current_version = Settings::Get('panel.version');\n\t\t$current_db_version = Settings::Get('panel.db_version');\n\t\tif (empty($current_db_version)) {\n\t\t\t$current_db_version = \"0\";\n\t\t}\n\t\t$new_version = Froxlor::VERSION;\n\t\t$new_db_version = Froxlor::DBVERSION;\n\n\t\tif (Froxlor::VERSION != $current_version) {\n\t\t\t$replacer_currentversion = $current_version;\n\t\t\t$replacer_newversion = $new_version;\n\t\t} else {\n\t\t\t// show db version\n\t\t\t$replacer_currentversion = $current_db_version;\n\t\t\t$replacer_newversion = $new_db_version;\n\t\t}\n\t\t$ui_text = lng('update.update_information.part_a', [$replacer_newversion, $replacer_currentversion]);\n\t\t$ui_text .= lng('update.update_information.part_b');\n\n\t\t$upd_formfield = [\n\t\t\t'updates' => [\n\t\t\t\t'title' => lng('update.update'),\n\t\t\t\t'image' => 'fa-solid fa-download',\n\t\t\t\t'description' => lng('update.description'),\n\t\t\t\t'sections' => [],\n\t\t\t\t'buttons' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'label' => lng('update.proceed')\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\n\t\t$preconfig = Preconfig::getPreConfig();\n\t\tif (!empty($preconfig)) {\n\t\t\t$upd_formfield['updates']['sections'] = $preconfig;\n\t\t}\n\n\t\tUI::view('user/form-note.html.twig', [\n\t\t\t'formaction' => $linker->getLink(['section' => 'updates']),\n\t\t\t'formdata' => $upd_formfield['updates'],\n\t\t\t// alert\n\t\t\t'type' => !empty($message) ? 'danger' : 'info',\n\t\t\t'alert_msg' => $ui_text . $message\n\t\t]);\n\t} else {\n\t\tResponse::standardSuccess('update.noupdatesavail', Settings::Get('system.update_channel') == 'testing' ? lng('serversettings.uc_testing') . ' ' : '');\n\t}\n}\n"
  },
  {
    "path": "api.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Api\\Api;\nuse Froxlor\\Api\\Response;\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/lib/functions.php';\nrequire __DIR__ . '/lib/tables.inc.php';\n\n// set error-handler\n@set_error_handler([\n\t'\\\\Froxlor\\\\Api\\\\Api',\n\t'phpErrHandler'\n]);\n\n// Return response\ntry {\n\techo (new Api)->formatMiddleware(@file_get_contents('php://input'))->handle();\n} catch (Exception $e) {\n\techo Response::jsonErrorResponse($e->getMessage(), $e->getCode());\n}\n"
  },
  {
    "path": "api_keys.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nif (!defined('AREA')) {\n\theader(\"Location: index.php\");\n\texit();\n}\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// redirect if this customer has no permission for API usage\nif ($userinfo['adminsession'] == 0 && $userinfo['api_allowed'] == 0) {\n\tResponse::redirectTo('customer_index.php');\n}\n// redirect if this admin has no permission for API usage\nif ($userinfo['adminsession'] == 1 && $userinfo['api_allowed'] == 0) {\n\tResponse::redirectTo('admin_index.php');\n}\n\n// This file is being included in admin_index and customer_index\n// and therefore does not need to require lib/init.php\n\n$del_stmt = Database::prepare(\"DELETE FROM `\" . TABLE_API_KEYS . \"` WHERE id = :id\");\n$id = (int)Request::any('id');\n\n// do the delete and then just show a success-message and the apikeys list again\nif ($action == 'delete' && $id > 0) {\n\tHTML::askYesNo('apikey_reallydelete', $filename, [\n\t\t'id' => $id,\n\t\t'page' => $page,\n\t\t'action' => 'deletesure'\n\t], '', [\n\t\t'section' => 'index',\n\t\t'page' => $page\n\t]);\n} elseif (Request::post('send') == 'send' && $action == 'deletesure' && $id > 0) {\n\t$chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false;\n\tif (AREA == 'customer') {\n\t\t$chk_stmt = Database::prepare(\"\n\t\t\t\tSELECT c.customerid FROM `\" . TABLE_PANEL_CUSTOMERS . \"` c\n\t\t\t\tLEFT JOIN `\" . TABLE_API_KEYS . \"` ak ON ak.customerid = c.customerid\n\t\t\t\tWHERE ak.`id` = :id AND c.`customerid` = :cid\n\t\t\t\");\n\t\t$chk = Database::pexecute_first($chk_stmt, [\n\t\t\t'id' => $id,\n\t\t\t'cid' => $userinfo['customerid']\n\t\t]);\n\t} elseif (AREA == 'admin' && $userinfo['customers_see_all'] == '0') {\n\t\t$chk_stmt = Database::prepare(\"\n\t\t\t\tSELECT a.adminid FROM `\" . TABLE_PANEL_ADMINS . \"` a\n\t\t\t\tLEFT JOIN `\" . TABLE_API_KEYS . \"` ak ON ak.adminid = a.adminid\n\t\t\t\tWHERE ak.`id` = :id AND a.`adminid` = :aid\n\t\t\t\");\n\t\t$chk = Database::pexecute_first($chk_stmt, [\n\t\t\t'id' => $id,\n\t\t\t'aid' => $userinfo['adminid']\n\t\t]);\n\t}\n\tif ($chk !== false) {\n\t\tDatabase::pexecute($del_stmt, [\n\t\t\t'id' => $id\n\t\t]);\n\t\tResponse::standardSuccess('apikeys.apikey_removed', $id, [\n\t\t\t'filename' => $filename,\n\t\t\t'page' => $page\n\t\t]);\n\t}\n} elseif ($action == 'add') {\n\tif (Request::post('send') == 'send') {\n\t\t$user_passwd = Request::post('user_password');\n\t\tif (empty($user_passwd)) {\n\t\t\tResponse::dynamicError(lng('panel.noauthentication'));\n\t\t}\n\t\tif ($userinfo['adminsession']) {\n\t\t\t$table = \"`\" . TABLE_PANEL_ADMINS . \"`\";\n\t\t\t$uid = 'adminid';\n\t\t} else {\n\t\t\t$table = \"`\" . TABLE_PANEL_CUSTOMERS . \"`\";\n\t\t\t$uid = 'customerid';\n\t\t}\n\t\tif (\\Froxlor\\System\\Crypt::validatePasswordLogin($userinfo, $user_passwd, $table, $uid)) {\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_API_KEYS . \"` SET\n\t\t\t\t`apikey` = :key, `secret` = :secret, `adminid` = :aid, `customerid` = :cid, `valid_until` = '-1', `allowed_from` = ''\n\t\t\t\");\n\t\t\t// customer generates for himself, admins will see a customer-select-box later\n\t\t\tif (AREA == 'admin') {\n\t\t\t\t$cid = 0;\n\t\t\t} elseif (AREA == 'customer') {\n\t\t\t\t$cid = $userinfo['customerid'];\n\t\t\t}\n\t\t\t$key = hash('sha256', openssl_random_pseudo_bytes(64 * 64));\n\t\t\t$secret = hash('sha512', openssl_random_pseudo_bytes(64 * 64 * 4));\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'key' => $key,\n\t\t\t\t'secret' => $secret,\n\t\t\t\t'aid' => $userinfo['adminid'],\n\t\t\t\t'cid' => $cid\n\t\t\t]);\n\t\t\tResponse::standardSuccess('apikeys.apikey_added', '', [\n\t\t\t\t'filename' => $filename,\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\tResponse::dynamicError(lng('panel.authenticationfailed'));\n\t\t}\n\t}\n\tHTML::askUserPasswd('apikey_reallyadd', $filename, [\n\t\t'id' => $id,\n\t\t'page' => $page,\n\t\t'action' => $action\n\t], '', [\n\t\t'section' => 'index',\n\t\t'page' => $page\n\t]);\n\texit;\n}\n\n$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"viewed api::api_keys\");\n\n// select all my (accessible) api-keys\n$keys_stmt_query = \"SELECT ak.*, c.loginname, a.loginname as adminname\n\tFROM `\" . TABLE_API_KEYS . \"` ak\n\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` c ON `c`.`customerid` = `ak`.`customerid`\n\tLEFT JOIN `\" . TABLE_PANEL_ADMINS . \"` a ON `a`.`adminid` = `ak`.`adminid`\n\tWHERE \";\n\n$qry_params = [];\nif (AREA == 'admin' && $userinfo['customers_see_all'] == '0') {\n\t// admin with only customer-specific permissions\n\t$keys_stmt_query .= \"ak.adminid = :adminid \";\n\t$qry_params['adminid'] = $userinfo['adminid'];\n\t$fields = [\n\t\t'a.loginname' => lng('login.username')\n\t];\n} elseif (AREA == 'customer') {\n\t// customer-area\n\t$keys_stmt_query .= \"ak.customerid = :cid \";\n\t$qry_params['cid'] = $userinfo['customerid'];\n\t$fields = [\n\t\t'c.loginname' => lng('login.username')\n\t];\n} else {\n\t// admin who can see all customers / reseller / admins\n\t$keys_stmt_query .= \"1 \";\n\t$fields = [\n\t\t'a.loginname' => lng('login.username')\n\t];\n}\n\n//$keys_stmt_query .= $paging->getSqlWhere(true) . \" \" . $paging->getSqlOrderBy() . \" \" . $paging->getSqlLimit();\n\n$keys_stmt = Database::prepare($keys_stmt_query);\nDatabase::pexecute($keys_stmt, $qry_params);\n$all_keys = $keys_stmt->fetchAll(PDO::FETCH_ASSOC);\n\n$apikeys_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/tablelisting.apikeys.php';\n$collection = [\n\t'data' => $all_keys,\n\t'pagination' => []\n];\n\n$tpl = 'user/table.html.twig';\n\nUI::view($tpl, [\n\t'listing' => Listing::formatFromArray($collection, $apikeys_list_data['apikeys_list'], 'apikeys_list'),\n\t'actions_links' => (int)$userinfo['api_allowed'] == 1 ? [\n\t\t[\n\t\t\t'href' => $linker->getLink(['section' => 'index', 'page' => $page, 'action' => 'add']),\n\t\t\t'label' => lng('apikeys.key_add')\n\t\t]\n\t] : null,\n]);\n"
  },
  {
    "path": "bin/froxlor-cli",
    "content": "#!/usr/bin/env php\n<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Froxlor;\nuse Symfony\\Component\\Console\\Application;\n\n// validate correct php version\nif (version_compare(\"7.4.0\", PHP_VERSION, \">=\")) {\n\tdie('Froxlor requires at least php-7.4. Please validate that your php-cli version is suitable.');\n}\n\n// ensure that default timezone is set\nif (function_exists(\"date_default_timezone_set\") && function_exists(\"date_default_timezone_get\")) {\n\t@date_default_timezone_set(@date_default_timezone_get());\n}\n\nrequire dirname(__DIR__) . '/vendor/autoload.php';\nrequire dirname(__DIR__) . '/lib/tables.inc.php';\n\n$application = new Application('froxlor-cli', Froxlor::getFullVersion());\n\n// files that are no commands\n$fileIgnoreList = [\n\t// Current non-command files\n\t'CliCommand.php',\n\t'index.html',\n\t'install.functions.php',\n];\n// directory of commands to include\n$cmd_files = glob(Froxlor::getInstallDir() . '/lib/Froxlor/Cli/*.php');\n\n// include and add commands\nforeach ($cmd_files as $cmdFile) {\n\t// check ignore-list\n\tif (!in_array(basename($cmdFile), $fileIgnoreList)) {\n\t\t// include class-file\n\t\trequire $cmdFile;\n\t\t// create class-name including namespace\n\t\t$cmdClass = \"\\\\Froxlor\\\\Cli\\\\\" . substr(basename($cmdFile), 0, -4);\n\t\t// check whether it exists\n\t\tif (class_exists($cmdClass) && is_subclass_of($cmdClass, '\\Symfony\\Component\\Console\\Command\\Command')) {\n\t\t\t// add to cli application\n\t\t\t$application->add(new $cmdClass());\n\t\t}\n\t}\n}\n\n$application->run();\n"
  },
  {
    "path": "build.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<project name=\"froxlor\" default=\"build\">\n\n\t<!-- Use this when the tools are managed by Composer in ${basedir}/vendor/bin -->\n\t<property name=\"pdepend\" value=\"${basedir}/vendor/bin/pdepend\" />\n\t<property name=\"phpcpd\" value=\"${basedir}/vendor/bin/phpcpd\" />\n\t<property name=\"phpcs\" value=\"${basedir}/vendor/bin/phpcs\" />\n\t<property name=\"phploc\" value=\"${basedir}/vendor/bin/phploc\" />\n\t<property name=\"phpmd\" value=\"${basedir}/vendor/bin/phpmd\" />\n\t<property name=\"phpunit\" value=\"${basedir}/vendor/bin/phpunit\" />\n\n\t<target name=\"full-build\"\n\t\tdepends=\"prepare,composer,static-analysis,phpunit,-check-failure\"\n\t\tdescription=\"Performs static analysis, runs the tests, and generates project documentation\" />\n\n\t<target name=\"full-build-parallel\"\n\t\tdepends=\"prepare,composer,static-analysis-parallel,phpunit,-check-failure\"\n\t\tdescription=\"Performs static analysis (executing the tools in parallel), runs the tests, and generates project documentation\" />\n\n\t<target name=\"quick-build\"\n\t\tdepends=\"prepare,composer,lint,phpunit-no-coverage,-check-failure\"\n\t\tdescription=\"Performs a lint check and runs the tests (without generating code coverage reports)\" />\n\n\t<target name=\"static-analysis\"\n\t\tdepends=\"composer,lint,phploc-ci,pdepend,phpmd-ci,phpcs-ci,phpcompat-ci,phpcpd-ci\"\n\t\tdescription=\"Performs static analysis\" />\n\n\t<!-- Adjust the threadCount attribute's value to the number of CPUs -->\n\t<target name=\"static-analysis-parallel\"\n\t\tdescription=\"Performs static analysis (executing the tools in parallel)\">\n\t\t<parallel threadCount=\"2\">\n\t\t\t<sequential>\n\t\t\t\t<antcall target=\"pdepend\" />\n\t\t\t\t<antcall target=\"phpmd-ci\" />\n\t\t\t</sequential>\n\t\t\t<antcall target=\"lint\" />\n\t\t\t<antcall target=\"phpcpd-ci\" />\n\t\t\t<antcall target=\"phpcs-ci\" />\n\t\t\t<antcall target=\"phpcompat-ci\" />\n\t\t\t<antcall target=\"phploc-ci\" />\n\t\t</parallel>\n\t</target>\n\n\t<target name=\"clean\" unless=\"clean.done\"\n\t\tdescription=\"Cleanup build artifacts\">\n\t\t<delete dir=\"${basedir}/build/api\" />\n\t\t<delete dir=\"${basedir}/build/coverage\" />\n\t\t<delete dir=\"${basedir}/build/logs\" />\n\t\t<delete dir=\"${basedir}/build/pdepend\" />\n\t\t<property name=\"clean.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"prepare\" unless=\"prepare.done\" depends=\"clean\"\n\t\tdescription=\"Prepare for build\">\n\t\t<mkdir dir=\"${basedir}/build/api\" />\n\t\t<mkdir dir=\"${basedir}/build/coverage\" />\n\t\t<mkdir dir=\"${basedir}/build/logs\" />\n\t\t<mkdir dir=\"${basedir}/build/pdepend\" />\n\n\t\t<property name=\"prepare.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"composer\"\n\t\tdescription=\"Installing composer dependencies\" depends=\"prepare\">\n\t\t<exec executable=\"composer\" failonerror=\"true\">\n\t\t\t<arg value=\"install\" />\n\t\t\t<arg value=\"--prefer-dist\" />\n\t\t\t<arg value=\"--no-progress\" />\n\t\t</exec>\n\t</target>\n\n\t<target name=\"lint\" unless=\"lint.done\"\n\t\tdescription=\"Perform syntax check of sourcecode files\">\n\t\t<apply executable=\"php\" taskname=\"lint\">\n\t\t\t<arg value=\"-l\" />\n\n\t\t\t<fileset dir=\"${basedir}/lib/Froxlor\">\n\t\t\t\t<include name=\"**/*.php\" />\n\t\t\t\t<modified />\n\t\t\t</fileset>\n\n\t\t\t<fileset dir=\"${basedir}/tests\">\n\t\t\t\t<include name=\"**/*.php\" />\n\t\t\t\t<modified />\n\t\t\t</fileset>\n\t\t</apply>\n\n\t\t<property name=\"lint.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phploc\" unless=\"phploc.done\"\n\t\tdescription=\"Measure project size using PHPLOC and print human readable output. Intended for usage on the command line.\">\n\t\t<exec executable=\"${phploc}\" taskname=\"phploc\">\n\t\t\t<arg value=\"--count-tests\" />\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t\t<arg path=\"${basedir}/tests\" />\n\t\t</exec>\n\n\t\t<property name=\"phploc.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phploc-ci\" unless=\"phploc.done\" depends=\"prepare\"\n\t\tdescription=\"Measure project size using PHPLOC and log result in CSV and XML format. Intended for usage within a continuous integration environment.\">\n\t\t<exec executable=\"${phploc}\" taskname=\"phploc\">\n\t\t\t<arg value=\"--count-tests\" />\n\t\t\t<arg value=\"--log-csv\" />\n\t\t\t<arg path=\"${basedir}/build/logs/phploc.csv\" />\n\t\t\t<arg value=\"--log-xml\" />\n\t\t\t<arg path=\"${basedir}/build/logs/phploc.xml\" />\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t\t<arg path=\"${basedir}/tests\" />\n\t\t</exec>\n\n\t\t<property name=\"phploc.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"pdepend\" unless=\"pdepend.done\" depends=\"prepare\"\n\t\tdescription=\"Calculate software metrics using PHP_Depend and log result in XML format. Intended for usage within a continuous integration environment.\">\n\t\t<exec executable=\"${pdepend}\" taskname=\"pdepend\">\n\t\t\t<arg value=\"--jdepend-xml=${basedir}/build/logs/jdepend.xml\" />\n\t\t\t<arg\n\t\t\t\tvalue=\"--jdepend-chart=${basedir}/build/pdepend/dependencies.svg\" />\n\t\t\t<arg\n\t\t\t\tvalue=\"--overview-pyramid=${basedir}/build/pdepend/overview-pyramid.svg\" />\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t</exec>\n\n\t\t<property name=\"pdepend.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpmd\" unless=\"phpmd.done\"\n\t\tdescription=\"Perform project mess detection using PHPMD and print human readable output. Intended for usage on the command line before committing.\">\n\t\t<exec executable=\"${phpmd}\" taskname=\"phpmd\">\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t\t<arg value=\"text\" />\n\t\t\t<arg path=\"${basedir}/phpmd.xml\" />\n\t\t</exec>\n\n\t\t<property name=\"phpmd.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpmd-ci\" unless=\"phpmd.done\" depends=\"prepare\"\n\t\tdescription=\"Perform project mess detection using PHPMD and log result in XML format. Intended for usage within a continuous integration environment.\">\n\t\t<exec executable=\"${phpmd}\" taskname=\"phpmd\">\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t\t<arg value=\"xml\" />\n\t\t\t<arg path=\"${basedir}/phpmd.xml\" />\n\t\t\t<arg value=\"--reportfile\" />\n\t\t\t<arg path=\"${basedir}/build/logs/pmd.xml\" />\n\t\t</exec>\n\n\t\t<property name=\"phpmd.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpcs\" unless=\"phpcs.done\"\n\t\tdescription=\"Find coding standard violations using PHP_CodeSniffer and print human readable output. Intended for usage on the command line before committing.\">\n\t\t<exec executable=\"${phpcs}\" taskname=\"phpcs\">\n\t\t\t<arg value=\"--standard=${basedir}/phpcs.xml\" />\n\t\t\t<arg value=\"--extensions=php\" />\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t\t<arg path=\"${basedir}/tests\" />\n\t\t</exec>\n\n\t\t<property name=\"phpcs.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpcs-ci\" unless=\"phpcs.done\" depends=\"prepare\"\n\t\tdescription=\"Find coding standard violations using PHP_CodeSniffer and log result in XML format. Intended for usage within a continuous integration environment.\">\n\t\t<exec executable=\"${phpcs}\" output=\"/dev/null\" taskname=\"phpcs\">\n\t\t\t<arg value=\"--report=checkstyle\" />\n\t\t\t<arg\n\t\t\t\tvalue=\"--report-file=${basedir}/build/logs/checkstyle-standard.xml\" />\n\t\t\t<arg value=\"--standard=${basedir}/phpcs.xml\" />\n\t\t\t<arg value=\"--extensions=php\" />\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t\t<arg path=\"${basedir}/tests\" />\n\t\t</exec>\n\n\t\t<property name=\"phpcs.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpcompat\" unless=\"phpcompat.done\"\n\t\tdepends=\"composer\"\n\t\tdescription=\"Find php violations using PHP_CodeSniffer and print human readable output. Intended for usage on the command line before committing.\">\n\t\t<exec executable=\"${phpcs}\" taskname=\"phpcompat\">\n\t\t\t<arg\n\t\t\t\tline=\"--standard=PHPCompatibility --runtime-set testVersion 5.6 ${basedir}/lib/Froxlor ${basedir}/tests\" />\n\t\t</exec>\n\n\t\t<property name=\"phpcompat.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpcompat-ci\" unless=\"phpcompat.done\"\n\t\tdepends=\"composer\"\n\t\tdescription=\"Find php violations using PHP_CodeSniffer and log result in XML format. Intended for usage within a continuous integration environment.\">\n\t\t<exec executable=\"${phpcs}\" output=\"/dev/null\"\n\t\t\ttaskname=\"phpcompat\">\n\t\t\t<arg\n\t\t\t\tline=\"--standard=PHPCompatibility --runtime-set testVersion 5.6 --report=checkstyle --report-file=${basedir}/build/logs/checkstyle-compat.xml ${basedir}/lib/Froxlor ${basedir}/tests\" />\n\t\t</exec>\n\n\t\t<property name=\"phpcompat.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpcpd\" unless=\"phpcpd.done\"\n\t\tdescription=\"Find duplicate code using PHPCPD and print human readable output. Intended for usage on the command line before committing.\">\n\t\t<exec executable=\"${phpcpd}\" taskname=\"phpcpd\">\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t</exec>\n\n\t\t<property name=\"phpcpd.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpcpd-ci\" unless=\"phpcpd.done\" depends=\"prepare\"\n\t\tdescription=\"Find duplicate code using PHPCPD and log result in XML format. Intended for usage within a continuous integration environment.\">\n\t\t<exec executable=\"${phpcpd}\" taskname=\"phpcpd\">\n\t\t\t<arg value=\"--log-pmd\" />\n\t\t\t<arg path=\"${basedir}/build/logs/pmd-cpd.xml\" />\n\t\t\t<arg path=\"${basedir}/lib/Froxlor\" />\n\t\t</exec>\n\n\t\t<property name=\"phpcpd.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpunit-prepare\" unless=\"phpunit-prepare.done\" depends=\"composer\"\n\t\tdescription=\"prepare xdebug unit tests\">\n\t\t<exec executable=\"${phpunit}\" resultproperty=\"result.phpunit-prepare\"\n\t\t\ttaskname=\"phpunit\">\n\t\t\t<arg value=\"--configuration\" />\n\t\t\t<arg path=\"${basedir}/phpunit.xml\" />\n\t\t\t<arg value=\"--dump-xdebug-filter\" />\n\t\t\t<arg path=\"${basedir}/tests/xdebug-filter.php\" />\n\t\t</exec>\n\n\t\t<property name=\"phpunit-prepare.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpunit\" unless=\"phpunit.done\" depends=\"phpunit-prepare\"\n\t\tdescription=\"Run unit tests with PHPUnit\">\n\t\t<exec executable=\"${phpunit}\" failonerror=\"true\" resultproperty=\"result.phpunit\"\n\t\t\ttaskname=\"phpunit\">\n\t\t\t<arg value=\"--configuration\" />\n\t\t\t<arg path=\"${basedir}/phpunit.xml\" />\n\t\t\t<arg value=\"--testsuite\" />\n\t\t\t<arg value=\"froxlor\" />\n\t\t\t<arg value=\"--prepend\" />\n\t\t\t<arg path=\"${basedir}/tests/xdebug-filter.php\" />\n\t\t</exec>\n\n\t\t<property name=\"phpunit.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"phpunit-no-coverage\" unless=\"phpunit.done\"\n\t\tdepends=\"composer\"\n\t\tdescription=\"Run unit tests with PHPUnit (without generating code coverage reports)\">\n\t\t<exec executable=\"${phpunit}\" failonerror=\"true\" resultproperty=\"result.phpunit\"\n\t\t\ttaskname=\"phpunit\">\n\t\t\t<arg value=\"--configuration\" />\n\t\t\t<arg path=\"${basedir}/phpunit.xml\" />\n\t\t\t<arg value=\"--testsuite\" />\n\t\t\t<arg value=\"froxlor\" />\n\t\t\t<arg value=\"--no-coverage\" />\n\t\t</exec>\n\n\t\t<property name=\"phpunit.done\" value=\"true\" />\n\t</target>\n\n\t<target name=\"-check-failure\">\n\t\t<fail message=\"PHPUnit did not finish successfully\">\n\t\t\t<condition>\n\t\t\t\t<not>\n\t\t\t\t\t<equals arg1=\"${result.phpunit}\" arg2=\"0\" />\n\t\t\t\t</not>\n\t\t\t</condition>\n\t\t</fail>\n\t</target>\n</project>\n"
  },
  {
    "path": "composer.json",
    "content": "{\n\t\"name\": \"froxlor/froxlor\",\n\t\"description\": \"The server administration software for your needs. Developed by experienced server administrators, this panel simplifies the effort of managing your hosting platform.\",\n\t\"keywords\": [\n\t\t\"server\",\n\t\t\"administration\",\n\t\t\"php\"\n\t],\n\t\"homepage\": \"https://www.froxlor.org\",\n\t\"license\": \"GPL-2.0-or-later\",\n\t\"authors\": [\n\t\t{\n\t\t\t\"name\": \"Michael Kaufmann\",\n\t\t\t\"email\": \"team@froxlor.org\",\n\t\t\t\"role\": \"Lead Developer\"\n\t\t}\n\t],\n\t\"support\": {\n\t\t\"email\": \"team@froxlor.org\",\n\t\t\"issues\": \"https://github.com/Froxlor/Froxlor/issues\",\n\t\t\"forum\": \"https://forum.froxlor.org/\",\n\t\t\"source\": \"https://github.com/Froxlor/Froxlor\",\n\t\t\"docs\": \"https://docs.froxlor.org/\",\n\t\t\"chat\": \"https://discord.froxlor.org/\"\n\t},\n\t\"funding\": [\n\t\t{\n\t\t\t\"type\": \"github\",\n\t\t\t\"url\": \"https://github.com/sponsors/d00p\"\n\t\t}\n\t],\n\t\"require\": {\n\t\t\"php\": \"^7.4 || ^8.0\",\n\t\t\"ext-session\": \"*\",\n\t\t\"ext-ctype\": \"*\",\n\t\t\"ext-pdo\": \"*\",\n\t\t\"ext-pdo_mysql\": \"*\",\n\t\t\"ext-simplexml\": \"*\",\n\t\t\"ext-xml\": \"*\",\n\t\t\"ext-filter\": \"*\",\n\t\t\"ext-posix\": \"*\",\n\t\t\"ext-mbstring\": \"*\",\n\t\t\"ext-curl\": \"*\",\n\t\t\"ext-json\": \"*\",\n\t\t\"ext-openssl\": \"*\",\n\t\t\"ext-fileinfo\": \"*\",\n\t\t\"ext-gmp\": \"*\",\n\t\t\"ext-gd\": \"*\",\n\t\t\"phpmailer/phpmailer\": \"~6.0\",\n\t\t\"monolog/monolog\": \"^1.24\",\n\t\t\"robthree/twofactorauth\": \"^1.6\",\n\t\t\"froxlor/idna-convert-legacy\": \"^2.1\",\n\t\t\"voku/anti-xss\": \"^4.1\",\n\t\t\"twig/twig\": \"^3.3\",\n\t\t\"symfony/console\": \"^5.4\",\n\t\t\"pear/net_dns2\": \"^1.5\",\n\t\t\"amnuts/opcache-gui\": \"^3.4\",\n\t\t\"league/commonmark\": \"^2.4\",\n\t\t\"phpseclib/phpseclib\": \"~3.0\"\n\t},\n\t\"require-dev\": {\n\t\t\"phpunit/phpunit\": \"^9\",\n\t\t\"ext-pcntl\": \"*\",\n\t\t\"phpcompatibility/php-compatibility\": \"*\",\n\t\t\"squizlabs/php_codesniffer\": \"*\",\n\t\t\"pdepend/pdepend\": \"^2.9\",\n\t\t\"sebastian/phpcpd\": \"^6.0\",\n\t\t\"phploc/phploc\": \"^7.0\",\n\t\t\"phpmd/phpmd\": \"^2.10\",\n\t\t\"phpunit/php-timer\" : \"^5\",\n\t\t\"phpstan/phpstan\": \"^1.8\"\n\t},\n\t\"suggest\": {\n\t\t\"ext-bcmath\": \"*\",\n\t\t\"ext-zip\": \"*\",\n\t\t\"ext-gnupg\": \"*\",\n\t\t\"ext-apcu\": \"*\",\n\t\t\"ext-readline\": \"*\"\n\t},\n\t\"config\": {\n\t\t\"platform\": {\n\t\t\t\"php\": \"7.4\"\n\t\t}\n\t},\n\t\"autoload\": {\n\t\t\"psr-4\": {\n\t\t\t\"Froxlor\\\\\": [\n\t\t\t\t\"lib/Froxlor\"\n\t\t\t]\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"dev\": [\n\t\t\t\"Composer\\\\Config::disableProcessTimeout\",\n\t\t\t\"npx concurrently -c \\\"#93c5fd,#fdba74\\\" \\\"php -S 127.0.0.1:8000\\\" \\\"npm run dev\\\" --names=server,vite\"\n\t\t],\n\t\t\"post-install-cmd\": \"if [ -f ./vendor/bin/phpcs ]; then \\\"vendor/bin/phpcs\\\" --config-set installed_paths vendor/phpcompatibility/php-compatibility ; fi\",\n\t\t\"post-update-cmd\" : \"if [ -f ./vendor/bin/phpcs ]; then \\\"vendor/bin/phpcs\\\" --config-set installed_paths vendor/phpcompatibility/php-compatibility ; fi\"\n\t}\n}\n"
  },
  {
    "path": "customer_domains.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'customer';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\SubDomains;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\n\n// redirect if this customer page is hidden via settings\nif (Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\tResponse::redirectTo('customer_index.php');\n}\n\n$id = (int)Request::any('id');\n\nif ($page == 'overview' || $page == 'domains') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, \"viewed customer_domains::domains\");\n\n\t\t$parentdomain_id = (int)Request::any('pid', '0');\n\n\t\ttry {\n\t\t\t$domain_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.domains.php';\n\t\t\t$collection = (new Collection(SubDomains::class, $userinfo))\n\t\t\t\t->withPagination($domain_list_data['domain_list']['columns'], $domain_list_data['domain_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = [];\n\t\tif (CurrentUser::canAddResource('subdomains')) {\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'domains', 'page' => 'domains', 'action' => 'add']),\n\t\t\t\t'label' => lng('domains.subdomain_add')\n\t\t\t];\n\t\t}\n\n\t\t$actions_links[] = [\n\t\t\t'href' => \\Froxlor\\Froxlor::getDocsUrl() . 'user-guide/domains/',\n\t\t\t'target' => '_blank',\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'class' => 'btn-outline-secondary'\n\t\t];\n\n\t\t$table_tpl = 'table.html.twig';\n\t\tif ($collection->count() == 0) {\n\t\t\t$table_tpl = 'table-note.html.twig';\n\t\t}\n\t\tUI::view('user/' . $table_tpl, [\n\t\t\t'listing' => Listing::format($collection, $domain_list_data, 'domain_list'),\n\t\t\t'actions_links' => $actions_links,\n\t\t\t'entity_info' => lng('domains.description'),\n\t\t\t// alert-box\n\t\t\t'type' => 'warning',\n\t\t\t'alert_msg' => lng('domains.nodomainsassignedbyadmin')\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = SubDomains::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$alias_stmt = Database::prepare(\"SELECT COUNT(`id`) AS `count` FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `aliasdomain` = :aliasdomain\");\n\t\t$alias_check = Database::pexecute_first($alias_stmt, [\n\t\t\t\"aliasdomain\" => $id\n\t\t]);\n\n\t\tif (isset($result['parentdomainid']) && $result['parentdomainid'] != '0' && $alias_check['count'] == 0) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tSubDomains::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('domains_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $idna_convert->decode($result['domain']));\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('domains_cantdeletemaindomain');\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif ($userinfo['subdomains_used'] < $userinfo['subdomains'] || $userinfo['subdomains'] == '-1') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tSubDomains::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$stmt = Database::prepare(\"SELECT `id`, `domain`, `documentroot`, `ssl_redirect`,`isemaildomain`,`letsencrypt` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\tAND `parentdomainid` = '0'\n\t\t\t\t\tAND `email_only` = '0'\n\t\t\t\t\tAND `deactivated` = '0'\n\t\t\t\t\tORDER BY `domain` ASC\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\"customerid\" => $userinfo['customerid']\n\t\t\t\t]);\n\t\t\t\t$domains = [];\n\t\t\t\twhile ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$domains[$row['domain']] = $idna_convert->decode($row['domain']);\n\t\t\t\t}\n\n\t\t\t\t// check of there are any domains to be used\n\t\t\t\tif (count($domains) <= 0) {\n\t\t\t\t\t// no, possible direct URL access, redirect to overview\n\t\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t\t'page' => $page\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\t$aliasdomains[0] = lng('domains.noaliasdomain');\n\t\t\t\t$domains_stmt = Database::prepare(\"SELECT `d`.`id`, `d`.`domain` FROM `\" . TABLE_PANEL_DOMAINS . \"` `d`, `\" . TABLE_PANEL_CUSTOMERS . \"` `c`\n\t\t\t\t\tWHERE `d`.`aliasdomain` IS NULL\n\t\t\t\t\tAND `d`.`id` <> `c`.`standardsubdomain`\n\t\t\t\t\tAND `d`.`parentdomainid` = '0'\n\t\t\t\t\tAND `d`.`customerid`=`c`.`customerid`\n\t\t\t\t\tAND `d`.`email_only`='0'\n\t\t\t\t\tAND `d`.`customerid`= :customerid\n\t\t\t\t\tORDER BY `d`.`domain` ASC\");\n\t\t\t\tDatabase::pexecute($domains_stmt, [\n\t\t\t\t\t\"customerid\" => $userinfo['customerid']\n\t\t\t\t]);\n\n\t\t\t\twhile ($row_domain = $domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$aliasdomains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']);\n\t\t\t\t}\n\n\t\t\t\t$redirectcode = [];\n\t\t\t\tif (Settings::Get('customredirect.enabled') == '1') {\n\t\t\t\t\t$codes = Domain::getRedirectCodesArray();\n\t\t\t\t\tforeach ($codes as $rc) {\n\t\t\t\t\t\t$redirectcode[$rc['id']] = $rc['code'] . ' (' . lng('redirect_desc.' . $rc['desc']) . ')';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// check if we at least have one ssl-ip/port, #1179\n\t\t\t\t$ssl_ipsandports = false;\n\t\t\t\t$ssl_ip_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT COUNT(*) as countSSL\n\t\t\t\t\tFROM `\" . TABLE_PANEL_IPSANDPORTS . \"` pip\n\t\t\t\t\tLEFT JOIN `\" . TABLE_DOMAINTOIP . \"` dti ON dti.id_ipandports = pip.id\n\t\t\t\t\tWHERE pip.`ssl`='1'\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($ssl_ip_stmt);\n\t\t\t\t$resultX = $ssl_ip_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\tif (isset($resultX['countSSL']) && (int)$resultX['countSSL'] > 0) {\n\t\t\t\t\t$ssl_ipsandports = true;\n\t\t\t\t}\n\n\t\t\t\t$openbasedir = [\n\t\t\t\t\t0 => lng('domain.docroot'),\n\t\t\t\t\t1 => lng('domain.homedir'),\n\t\t\t\t\t2 => lng('domain.docparent')\n\t\t\t\t];\n\t\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']);\n\n\t\t\t\t$phpconfigs = [];\n\t\t\t\tif (isset($userinfo['allowed_phpconfigs']) && !empty($userinfo['allowed_phpconfigs'])) {\n\t\t\t\t\t$allowed_cfg = json_decode($userinfo['allowed_phpconfigs'], JSON_OBJECT_AS_ARRAY);\n\t\t\t\t\t$phpconfigs_result_stmt = Database::query(\"\n\t\t\t\t\t\tSELECT c.*, fc.description as interpreter\n\t\t\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fc ON fc.id = c.fpmsettingid\n\t\t\t\t\t\tWHERE c.id IN (\" . implode(\", \", $allowed_cfg) . \")\n\t\t\t\t\t\");\n\t\t\t\t\twhile ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\t\t$phpconfigs[$phpconfigs_row['id']] = $phpconfigs_row['description'] . \" [\" . $phpconfigs_row['interpreter'] . \"]\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$phpconfigs[$phpconfigs_row['id']] = $phpconfigs_row['description'];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$subdomain_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/domains/formfield.domains_add.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'domains']),\n\t\t\t\t\t'formdata' => $subdomain_add_data['domain_add']\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = SubDomains::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['customerid']) && $result['customerid'] == $userinfo['customerid']) {\n\n\t\t\tif ((int)$result['caneditdomain'] == 0) {\n\t\t\t\tResponse::standardError('domaincannotbeedited', $result['domain']);\n\t\t\t}\n\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tSubDomains::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$result['domain'] = $idna_convert->decode($result['domain']);\n\n\t\t\t\t$domains[0] = lng('domains.noaliasdomain');\n\t\t\t\t// also check ip/port combination to be the same, #176\n\t\t\t\t$domains_stmt = Database::prepare(\"SELECT `d`.`id`, `d`.`domain` FROM `\" . TABLE_PANEL_DOMAINS . \"` `d` , `\" . TABLE_PANEL_CUSTOMERS . \"` `c` , `\" . TABLE_DOMAINTOIP . \"` `dip`\n\t\t\t\t\tWHERE `d`.`aliasdomain` IS NULL\n\t\t\t\t\tAND `d`.`id` <> :id\n\t\t\t\t\tAND `c`.`standardsubdomain` <> `d`.`id`\n\t\t\t\t\tAND `d`.`parentdomainid` = '0'\n\t\t\t\t\tAND `d`.`customerid` = :customerid\n\t\t\t\t\tAND `c`.`customerid` = `d`.`customerid`\n\t\t\t\t\tAND `d`.`id` = `dip`.`id_domain`\n\t\t\t\t\tAND `dip`.`id_ipandports`\n\t\t\t\t\tIN (SELECT `id_ipandports` FROM `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\t\t\tWHERE `id_domain` = :id)\n\t\t\t\t\tGROUP BY `d`.`id`, `d`.`domain`\n\t\t\t\t\tORDER BY `d`.`domain` ASC\");\n\t\t\t\tDatabase::pexecute($domains_stmt, [\n\t\t\t\t\t\"id\" => $result['id'],\n\t\t\t\t\t\"customerid\" => $userinfo['customerid']\n\t\t\t\t]);\n\n\t\t\t\twhile ($row_domain = $domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$domains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']);\n\t\t\t\t}\n\n\t\t\t\tif (preg_match('/^https?\\:\\/\\//', $result['documentroot']) && Validate::validateUrl($result['documentroot'])) {\n\t\t\t\t\tif (Settings::Get('panel.pathedit') == 'Dropdown') {\n\t\t\t\t\t\t$urlvalue = $result['documentroot'];\n\t\t\t\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$urlvalue = '';\n\t\t\t\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid'], $result['documentroot'], true);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$urlvalue = '';\n\t\t\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid'], $result['documentroot']);\n\t\t\t\t}\n\n\t\t\t\t$redirectcode = [];\n\t\t\t\tif (Settings::Get('customredirect.enabled') == '1') {\n\t\t\t\t\t$def_code = Domain::getDomainRedirectId($id);\n\t\t\t\t\t$codes = Domain::getRedirectCodesArray();\n\t\t\t\t\tforeach ($codes as $rc) {\n\t\t\t\t\t\t$redirectcode[$rc['id']] = $rc['code'] . ' (' . lng('redirect_desc.' . $rc['desc']) . ')';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// check if we at least have one ssl-ip/port, #1179\n\t\t\t\t$ssl_ipsandports = false;\n\t\t\t\t$ssl_ip_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT COUNT(*) as countSSL\n\t\t\t\t\tFROM `\" . TABLE_PANEL_IPSANDPORTS . \"` pip\n\t\t\t\t\tLEFT JOIN `\" . TABLE_DOMAINTOIP . \"` dti ON dti.id_ipandports = pip.id\n\t\t\t\t\tWHERE `dti`.`id_domain` = :id_domain AND pip.`ssl`='1'\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($ssl_ip_stmt, [\n\t\t\t\t\t\"id_domain\" => $result['id']\n\t\t\t\t]);\n\t\t\t\t$resultX = $ssl_ip_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\tif (isset($resultX['countSSL']) && (int)$resultX['countSSL'] > 0) {\n\t\t\t\t\t$ssl_ipsandports = true;\n\t\t\t\t}\n\n\t\t\t\t// Fudge the result for ssl_redirect to hide the Let's Encrypt steps\n\t\t\t\t$result['temporary_ssl_redirect'] = $result['ssl_redirect'];\n\t\t\t\t$result['ssl_redirect'] = ($result['ssl_redirect'] == 0 ? 0 : 1);\n\n\t\t\t\t$openbasedir = [\n\t\t\t\t\t0 => lng('domain.docroot'),\n\t\t\t\t\t1 => lng('domain.homedir'),\n\t\t\t\t\t2 => lng('domain.docparent')\n\t\t\t\t];\n\n\t\t\t\t// create serveralias options\n\t\t\t\t$serveraliasoptions = [];\n\t\t\t\t$serveraliasoptions_selected = '2';\n\t\t\t\tif ($result['iswildcarddomain'] == '1') {\n\t\t\t\t\t$serveraliasoptions_selected = '0';\n\t\t\t\t} elseif ($result['wwwserveralias'] == '1') {\n\t\t\t\t\t$serveraliasoptions_selected = '1';\n\t\t\t\t}\n\t\t\t\t$serveraliasoptions[0] = lng('domains.serveraliasoption_wildcard');\n\t\t\t\t$serveraliasoptions[1] = lng('domains.serveraliasoption_www');\n\t\t\t\t$serveraliasoptions[2] = lng('domains.serveraliasoption_none');\n\n\t\t\t\t$ips_stmt = Database::prepare(\"SELECT `p`.`ip` AS `ip` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` `p`\n\t\t\t\t\tLEFT JOIN `\" . TABLE_DOMAINTOIP . \"` `dip`\n\t\t\t\t\tON ( `dip`.`id_ipandports` = `p`.`id` )\n\t\t\t\t\tWHERE `dip`.`id_domain` = :id_domain\n\t\t\t\t\tGROUP BY `p`.`ip`\");\n\t\t\t\tDatabase::pexecute($ips_stmt, [\n\t\t\t\t\t\"id_domain\" => $result['id']\n\t\t\t\t]);\n\t\t\t\t$domainips = [];\n\t\t\t\twhile ($rowip = $ips_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$domainips[] = ['item' => $rowip['ip']];\n\t\t\t\t}\n\n\t\t\t\t$phpconfigs = [];\n\t\t\t\tif (isset($userinfo['allowed_phpconfigs']) && !empty($userinfo['allowed_phpconfigs'])) {\n\t\t\t\t\t$allowed_cfg = json_decode($userinfo['allowed_phpconfigs'], JSON_OBJECT_AS_ARRAY);\n\t\t\t\t\t$phpconfigs_result_stmt = Database::query(\"\n\t\t\t\t\t\tSELECT c.*, fc.description as interpreter\n\t\t\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fc ON fc.id = c.fpmsettingid\n\t\t\t\t\t\tWHERE c.id IN (\" . implode(\", \", $allowed_cfg) . \")\n\t\t\t\t\t\");\n\t\t\t\t\twhile ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\t\t$phpconfigs[$phpconfigs_row['id']] = $phpconfigs_row['description'] . \" [\" . $phpconfigs_row['interpreter'] . \"]\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$phpconfigs[$phpconfigs_row['id']] = $phpconfigs_row['description'];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$alias_stmt = Database::prepare(\"SELECT COUNT(`id`) AS count FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `aliasdomain`= :aliasdomain\");\n\t\t\t\t$alias_check = Database::pexecute_first($alias_stmt, [\n\t\t\t\t\t\"aliasdomain\" => $result['id']\n\t\t\t\t]);\n\t\t\t\t$alias_check = $alias_check['count'];\n\n\t\t\t\t$subdomain_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/domains/formfield.domains_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'domains', 'id' => $id]),\n\t\t\t\t\t'formdata' => $subdomain_edit_data['domain_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('domains_canteditdomain');\n\t\t}\n\t} elseif ($action == 'jqSpeciallogfileNote') {\n\t\t$domainid = intval(Request::post('id'));\n\t\t$newval = intval(Request::post('newval'));\n\t\ttry {\n\t\t\t$json_result = SubDomains::getLocal($userinfo, [\n\t\t\t\t'id' => $domainid\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\t\tif ($newval != $result['speciallogfile']) {\n\t\t\techo json_encode(['changed' => true, 'info' => lng('admin.speciallogwarning')]);\n\t\t\texit();\n\t\t}\n\t\techo 0;\n\t\texit();\n\t}\n} elseif ($page == 'domainssleditor') {\n\trequire_once __DIR__ . '/ssl_editor.php';\n} elseif ($page == 'domaindnseditor' && $userinfo['dnsenabled'] == '1' && Settings::Get('system.dnsenabled') == '1') {\n\trequire_once __DIR__ . '/dns_editor.php';\n} elseif ($page == 'sslcertificates') {\n\trequire_once __DIR__ . '/ssl_certificates.php';\n} elseif ($page == 'logfiles') {\n\trequire_once __DIR__ . '/logfiles_viewer.php';\n}\n"
  },
  {
    "path": "customer_email.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'customer';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\EmailAccounts;\nuse Froxlor\\Api\\Commands\\EmailDomains;\nuse Froxlor\\Api\\Commands\\EmailForwarders;\nuse Froxlor\\Api\\Commands\\Emails;\nuse Froxlor\\Api\\Commands\\EmailSender;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Check;\n\n// redirect if this customer page is hidden via settings\nif (Settings::IsInList('panel.customer_hide_options', 'email') || $userinfo['emails'] == 0) {\n\tResponse::redirectTo('customer_index.php');\n}\n\n$id = (int)Request::any('id');\n\nif ($page == 'overview' || $page == 'emails') {\n\t$result_stmt = Database::prepare(\"\n\t\tSELECT COUNT(DISTINCT `domainid`) as maildomains FROM `\" . TABLE_MAIL_VIRTUAL . \"` WHERE `customerid`= :cid\n\t\");\n\t$domain_count = Database::pexecute_first($result_stmt, [\n\t\t\"cid\" => $userinfo['customerid']\n\t]);\n\tif ($domain_count['maildomains'] && $domain_count['maildomains'] > 1) {\n\t\ttry {\n\t\t\t$emaildomain_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails_overview.php';\n\t\t\t$collection = (new Collection(EmailDomains::class, $userinfo))\n\t\t\t\t->withPagination($emaildomain_list_data['emaildomain_list']['columns'],\n\t\t\t\t\t$emaildomain_list_data['emaildomain_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = [];\n\t\tif (CurrentUser::canAddResource('emails')) {\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'email', 'page' => 'email_domain', 'action' => 'add']),\n\t\t\t\t'label' => lng('emails.emails_add')\n\t\t\t];\n\t\t}\n\n\t\t$actions_links[] = [\n\t\t\t'href' => Froxlor::getDocsUrl() . 'user-guide/emails/',\n\t\t\t'target' => '_blank',\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'class' => 'btn-outline-secondary'\n\t\t];\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $emaildomain_list_data, 'emaildomain_list'),\n\t\t\t'actions_links' => $actions_links,\n\t\t]);\n\t} else {\n\t\t// only emails for one domain -> show email address listing directly\n\t\t$page = 'email_domain';\n\t}\n}\nif ($page == 'email_domain') {\n\t$email_domainid = Request::any('domainid', 0);\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, \"viewed customer_email::emails\");\n\n\t\t$sql_search = [];\n\t\tif ($email_domainid > 0) {\n\t\t\t$sql_search = ['sql_search' => ['m.domainid' => ['op' => '=', 'value' => $email_domainid]]];\n\t\t}\n\t\ttry {\n\t\t\t$email_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails.php';\n\t\t\t$collection = (new Collection(Emails::class, $userinfo, $sql_search))\n\t\t\t\t->withPagination($email_list_data['email_list']['columns'],\n\t\t\t\t\t$email_list_data['email_list']['default_sorting'], ['domainid=' . $email_domainid]);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(`id`) as emaildomains\n\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\tWHERE `customerid`= :cid AND `isemaildomain` = '1'\n\t\t\");\n\t\t$result2 = Database::pexecute_first($result_stmt, [\n\t\t\t\"cid\" => $userinfo['customerid']\n\t\t]);\n\t\t$emaildomains_count = $result2['emaildomains'];\n\n\t\t$actions_links = [];\n\t\tif ($email_domainid > 0) {\n\t\t\t$actions_links[] = [\n\t\t\t\t'class' => 'btn-outline-primary',\n\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t'page' => 'emails',\n\t\t\t\t]),\n\t\t\t\t'label' => lng('emails.back_to_overview'),\n\t\t\t\t'icon' => 'fa-solid fa-reply'\n\t\t\t];\n\t\t}\n\t\tif (CurrentUser::canAddResource('emails')) {\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'email', 'page' => 'email_domain', 'action' => 'add', 'domainid' => $email_domainid]),\n\t\t\t\t'label' => lng('emails.emails_add')\n\t\t\t];\n\t\t}\n\t\t$actions_links[] = [\n\t\t\t'href' => Froxlor::getDocsUrl() . 'user-guide/emails/',\n\t\t\t'target' => '_blank',\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'class' => 'btn-outline-secondary'\n\t\t];\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $email_list_data, 'email_list'),\n\t\t\t'actions_links' => $actions_links,\n\t\t\t'entity_info' => lng('emails.description')\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['email']) && $result['email'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tEmails::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $id,\n\t\t\t\t\t\t'delete_userfiles' => Request::post('delete_userfiles', 0)\n\t\t\t\t\t])->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tif ($result['popaccountid'] != '0') {\n\t\t\t\t\t$show_checkbox = true;\n\t\t\t\t} else {\n\t\t\t\t\t$show_checkbox = false;\n\t\t\t\t}\n\t\t\t\tHTML::askYesNoWithCheckbox('email_reallydelete', 'admin_customer_alsoremovemail', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $idna_convert->decode($result['email_full']), $show_checkbox);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif ($userinfo['emails_used'] < $userinfo['emails'] || $userinfo['emails'] == '-1') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\t$json_result = Emails::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\t$result = json_decode($json_result, true)['data'];\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $result['id']\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"SELECT `id`, `domain`, `customerid` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `customerid`= :cid\n\t\t\t\t\tAND `isemaildomain`='1'\n\t\t\t\t\tORDER BY `domain_ace` ASC\");\n\t\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t\t\"cid\" => $userinfo['customerid']\n\t\t\t\t]);\n\t\t\t\t$domains = [];\n\t\t\t\t$selected_domain = \"\";\n\t\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\tif ($email_domainid == $row['id']) {\n\t\t\t\t\t\t$selected_domain = $row['domain'];\n\t\t\t\t\t}\n\t\t\t\t\t$domains[$row['domain']] = $idna_convert->decode($row['domain']);\n\t\t\t\t}\n\n\t\t\t\tif (count($domains) > 0) {\n\t\t\t\t\t$email_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_add.php';\n\n\t\t\t\t\tif (Settings::Get('catchall.catchall_enabled') != '1') {\n\t\t\t\t\t\tunset($email_add_data['emails_add']['sections']['section_a']['fields']['iscatchall']);\n\t\t\t\t\t}\n\t\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'email']),\n\t\t\t\t\t\t'formdata' => $email_add_data['emails_add']\n\t\t\t\t\t]);\n\t\t\t\t} else {\n\t\t\t\t\tResponse::standardError('emails.noemaildomainaddedyet');\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('allresourcesused');\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['email']) && $result['email'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tEmails::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t}\n\t\t\t$result['email'] = $idna_convert->decode($result['email']);\n\t\t\t$result['email_full'] = $idna_convert->decode($result['email_full']);\n\t\t\t$result['destination'] = explode(' ', $result['destination']);\n\t\t\tuasort($result['destination'], 'strcasecmp');\n\t\t\t$forwarders = [];\n\t\t\t$forwarders_count = 0;\n\n\t\t\tforeach ($result['destination'] as $dest_id => $destination) {\n\t\t\t\t$destination = $idna_convert->decode($destination);\n\t\t\t\tif ($destination != $result['email_full'] && $destination != '') {\n\t\t\t\t\t$forwarders[] = [\n\t\t\t\t\t\t'item' => $destination,\n\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t'page' => 'forwarders',\n\t\t\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t\t\t'id' => $id,\n\t\t\t\t\t\t\t'forwarderid' => $dest_id\n\t\t\t\t\t\t]),\n\t\t\t\t\t\t'label' => lng('panel.delete'),\n\t\t\t\t\t\t'classes' => 'btn btn-sm btn-danger'\n\t\t\t\t\t];\n\t\t\t\t\t$forwarders_count++;\n\t\t\t\t}\n\t\t\t\t$result['destination'][$dest_id] = $destination;\n\t\t\t}\n\t\t\t$destinations_count = count($result['destination']);\n\n\t\t\t// allowed senders listing\n\t\t\t$senders = [];\n\t\t\t$senders_count = 0;\n\t\t\tif (Settings::Get('mail.enable_allow_sender') == '1') {\n\t\t\t\ttry {\n\t\t\t\t\t$json_result = EmailSender::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t])->listing();\n\t\t\t\t\t$sender_listing = json_decode($json_result, true)['data'];\n\t\t\t\t\tif ($sender_listing['count'] > 0) {\n\t\t\t\t\t\tforeach ($sender_listing['list'] as $sender) {\n\t\t\t\t\t\t\t$senders[] = [\n\t\t\t\t\t\t\t\t'item' => $sender['allowed_sender'],\n\t\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t\t'page' => 'senders',\n\t\t\t\t\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t\t\t\t\t'id' => $id,\n\t\t\t\t\t\t\t\t\t'senderid' => $sender['id']\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t'label' => lng('panel.delete'),\n\t\t\t\t\t\t\t\t'classes' => 'btn btn-sm btn-danger'\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t$senders_count++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t// nothing\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t$email_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_edit.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'email']),\n\t\t\t\t'formdata' => $email_edit_data['emails_edit'],\n\t\t\t\t'editid' => $id\n\t\t\t]);\n\t\t}\n\t}\n} elseif ($page == 'accounts') {\n\t$email_domainid = Request::any('domainid', 0);\n\tif ($action == 'add' && $id != 0) {\n\t\tif ($userinfo['email_accounts'] == '-1' || ($userinfo['email_accounts_used'] < $userinfo['email_accounts'])) {\n\t\t\ttry {\n\t\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t])->get();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tEmailAccounts::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $id\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tif (Check::checkMailAccDeletionState($result['email_full'])) {\n\t\t\t\t\tResponse::standardError([\n\t\t\t\t\t\t'mailaccistobedeleted'\n\t\t\t\t\t], $result['email_full']);\n\t\t\t\t}\n\n\t\t\t\t$result['email_full'] = $idna_convert->decode($result['email_full']);\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\t\t\t\t$quota = Settings::Get('system.mail_quota');\n\n\t\t\t\t$account_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_addaccount.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'email', 'id' => $id]),\n\t\t\t\t\t'formdata' => $account_add_data['emails_addaccount'],\n\t\t\t\t\t'actions_links' => [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'label' => lng('emails.emails_edit'),\n\t\t\t\t\t\t\t'icon' => 'fa-solid fa-pen'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t'domainid' => $email_domainid\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'label' => lng('menue.email.emails'),\n\t\t\t\t\t\t\t'icon' => 'fa-solid fa-envelope'\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t]);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError([\n\t\t\t\t'allresourcesused',\n\t\t\t\t'allocatetoomuchquota'\n\t\t\t], $quota);\n\t\t}\n\t} elseif ($action == 'changepw' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['popaccountid']) && $result['popaccountid'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tEmailAccounts::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $id\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$result['email_full'] = $idna_convert->decode($result['email_full']);\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t$account_changepw_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_accountchangepasswd.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'email', 'id' => $id]),\n\t\t\t\t\t'formdata' => $account_changepw_data['emails_accountchangepasswd'],\n\t\t\t\t\t'actions_links' => [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'label' => lng('emails.emails_edit'),\n\t\t\t\t\t\t\t'icon' => 'fa-solid fa-pen'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t'domainid' => $email_domainid\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'label' => lng('menue.email.emails'),\n\t\t\t\t\t\t\t'icon' => 'fa-solid fa-envelope'\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'changequota' && Settings::Get('system.mail_quota_enabled') == '1' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['popaccountid']) && $result['popaccountid'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tEmailAccounts::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $id\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$result['email_full'] = $idna_convert->decode($result['email_full']);\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t$quota_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_accountchangequota.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'email', 'id' => $id]),\n\t\t\t\t\t'formdata' => $quota_edit_data['emails_accountchangequota'],\n\t\t\t\t\t'actions_links' => [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'label' => lng('emails.emails_edit'),\n\t\t\t\t\t\t\t'icon' => 'fa-solid fa-pen'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t'domainid' => $email_domainid\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'label' => lng('menue.email.emails'),\n\t\t\t\t\t\t\t'icon' => 'fa-solid fa-envelope'\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['popaccountid']) && $result['popaccountid'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tEmailAccounts::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $id\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNoWithCheckbox('email_reallydelete_account', 'admin_customer_alsoremovemail', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $idna_convert->decode($result['email_full']));\n\t\t\t}\n\t\t}\n\t}\n} elseif ($page == 'forwarders') {\n\t$email_domainid = Request::any('domainid', 0);\n\tif ($action == 'add' && $id != 0) {\n\t\tif ($userinfo['email_forwarders_used'] < $userinfo['email_forwarders'] || $userinfo['email_forwarders'] == '-1') {\n\t\t\ttry {\n\t\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t])->get();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t\tif (isset($result['email']) && $result['email'] != '') {\n\t\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tEmailForwarders::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t\t}\n\t\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t]);\n\t\t\t\t} else {\n\t\t\t\t\t$result['email_full'] = $idna_convert->decode($result['email_full']);\n\t\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t\t$forwarder_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_addforwarder.php';\n\n\t\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'email', 'id' => $id]),\n\t\t\t\t\t\t'formdata' => $forwarder_add_data['emails_addforwarder'],\n\t\t\t\t\t\t'actions_links' => [\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t'label' => lng('emails.emails_edit'),\n\t\t\t\t\t\t\t\t'icon' => 'fa-solid fa-pen'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t\t'domainid' => $email_domainid\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\t'label' => lng('menue.email.emails'),\n\t\t\t\t\t\t\t\t'icon' => 'fa-solid fa-envelope'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('allresourcesused');\n\t\t}\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['destination']) && $result['destination'] != '') {\n\t\t\t$forwarderid = Request::any('forwarderid', 0);\n\t\t\t$result['destination'] = explode(' ', $result['destination']);\n\n\t\t\tif (isset($result['destination'][$forwarderid]) && $result['email'] != $result['destination'][$forwarderid]) {\n\t\t\t\t$forwarder = $result['destination'][$forwarderid];\n\n\t\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tEmailForwarders::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t\t}\n\t\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t]);\n\t\t\t\t} else {\n\t\t\t\t\tHTML::askYesNo('email_reallydelete_forwarder', $filename, [\n\t\t\t\t\t\t'id' => $id,\n\t\t\t\t\t\t'forwarderid' => $forwarderid,\n\t\t\t\t\t\t'page' => $page,\n\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t'action' => $action\n\t\t\t\t\t], $idna_convert->decode($result['email_full']) . ' -> ' . $idna_convert->decode($forwarder));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n} elseif ($page == 'senders' && Settings::Get('mail.enable_allow_sender') == '1') {\n\t$email_domainid = Request::any('domainid', 0);\n\tif ($action == 'add' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['email']) && $result['email'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\t// build target-sender\n\t\t\t\t\t$allowed_sender = Request::post('allowed_sender', '');\n\t\t\t\t\t$allowed_sender .= '@' . Request::post('allowed_domain', '');\n\t\t\t\t\t$postdata = [\n\t\t\t\t\t\t'id' => $id,\n\t\t\t\t\t\t'allowed_sender' => $allowed_sender\n\t\t\t\t\t];\n\t\t\t\t\tEmailSender::getLocal($userinfo, $postdata)->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $id\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$result['email_full'] = $idna_convert->decode($result['email_full']);\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\tif (Settings::Get('mail.allow_external_domains') == '0') {\n\t\t\t\t\t$result_stmt = Database::prepare(\"SELECT `id`, `domain`, `customerid` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\t\tWHERE `customerid`= :cid\n\t\t\t\t\t\tAND `isemaildomain`='1'\n\t\t\t\t\t\tORDER BY `domain_ace` ASC\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t\t\t\"cid\" => $userinfo['customerid']\n\t\t\t\t\t]);\n\t\t\t\t\t$domains = [];\n\t\t\t\t\t$selected_domain = \"\";\n\t\t\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\tif ($email_domainid == $row['id']) {\n\t\t\t\t\t\t\t$selected_domain = $row['domain'];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$domains[$row['domain']] = $idna_convert->decode($row['domain']);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (count($domains) == 0) {\n\t\t\t\t\t\tResponse::standardError('emails.noemaildomainaddedyet');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$sender_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_addsender.php';\n\n\t\t\t\tUI::view('user/form-note.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'email', 'id' => $id]),\n\t\t\t\t\t'formdata' => $sender_add_data['emails_addsender'],\n\t\t\t\t\t'actions_links' => [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'label' => lng('emails.emails_edit'),\n\t\t\t\t\t\t\t'icon' => 'fa-solid fa-pen'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'class' => 'btn-secondary',\n\t\t\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t\t\t\t'domainid' => $email_domainid\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'label' => lng('menue.email.emails'),\n\t\t\t\t\t\t\t'icon' => 'fa-solid fa-envelope'\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t// alert-box\n\t\t\t\t\t'type' => 'info',\n\t\t\t\t\t'alert_msg' => lng('emails.allowed_sender_info')\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Emails::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (!empty($result['popaccountid'])) {\n\t\t\t$senderid = Request::any('senderid', 0);\n\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tEmailSender::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $id\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$sel_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT s.`allowed_sender`\n\t\t\t\t\tFROM `\" . TABLE_MAIL_SENDER_ALIAS . \"` s\n\t\t\t\t\tLEFT JOIN `\" . TABLE_MAIL_USERS . \"` u ON u.username = s.email\n\t\t\t\t\tWHERE u.id = :popaccountid AND u.customerid = :cid AND s.id = :senderid\n\t\t\t\t\");\n\t\t\t\t$sender_data = Database::pexecute_first($sel_stmt, ['popaccountid' => $result['popaccountid'], 'cid' => $userinfo['customerid'], 'senderid' => (int)$senderid]);\n\n\t\t\t\tif ($sender_data) {\n\t\t\t\t\tHTML::askYesNo('email_reallydelete_sender', $filename, [\n\t\t\t\t\t\t'id' => $id,\n\t\t\t\t\t\t'senderid' => $senderid,\n\t\t\t\t\t\t'page' => $page,\n\t\t\t\t\t\t'domainid' => $email_domainid,\n\t\t\t\t\t\t'action' => $action\n\t\t\t\t\t], $idna_convert->decode($result['email_full']) . ' -> ' . $sender_data['allowed_sender']);\n\t\t\t\t}\n\t\t\t\tResponse::dynamicError('No such entity');\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "customer_extras.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'customer';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\DataDump as DataDump;\nuse Froxlor\\Api\\Commands\\DirOptions as DirOptions;\nuse Froxlor\\Api\\Commands\\DirProtections as DirProtections;\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// redirect if this customer page is hidden via settings\nif (Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\tResponse::redirectTo('customer_index.php');\n}\n\n$id = (int)Request::any('id');\n\nif ($page == 'overview' || $page == 'htpasswds') {\n\t// redirect if this customer sub-page is hidden via settings\n\tif (Settings::IsInList('panel.customer_hide_options', 'extras.directoryprotection')) {\n\t\tResponse::redirectTo('customer_index.php');\n\t}\n\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"viewed customer_extras::htpasswds\");\n\t\t$fields = [\n\t\t\t'username' => lng('login.username'),\n\t\t\t'path' => lng('panel.path')\n\t\t];\n\t\ttry {\n\t\t\t$htpasswd_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.htpasswd.php';\n\t\t\t$collection = (new Collection(DirProtections::class, $userinfo))\n\t\t\t\t->withPagination($htpasswd_list_data['htpasswd_list']['columns'], $htpasswd_list_data['htpasswd_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = [];\n\t\t$actions_links[] = [\n\t\t\t'href' => $linker->getLink(['section' => 'extras', 'page' => 'htpasswds', 'action' => 'add']),\n\t\t\t'label' => lng('extras.directoryprotection_add')\n\t\t];\n\n\t\t$actions_links[] = [\n\t\t\t'href' => \\Froxlor\\Froxlor::getDocsUrl() . 'user-guide/extras/',\n\t\t\t'target' => '_blank',\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'class' => 'btn-outline-secondary'\n\t\t];\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $htpasswd_list_data, 'htpasswd_list'),\n\t\t\t'actions_links' => $actions_links,\n\t\t\t'entity_info' => lng('extras.description')\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = DirProtections::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['username']) && $result['username'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tDirProtections::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tif (strpos($result['path'], $userinfo['documentroot']) === 0) {\n\t\t\t\t\t$result['path'] = str_replace($userinfo['documentroot'], \"/\", $result['path']);\n\t\t\t\t}\n\n\t\t\t\tHTML::askYesNo('extras_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['username'] . ' (' . $result['path'] . ')');\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tDirProtections::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']);\n\n\t\t\t$htpasswd_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.htpasswd_add.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'extras']),\n\t\t\t\t'formdata' => $htpasswd_add_data['htpasswd_add']\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = DirProtections::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['username']) && $result['username'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tDirProtections::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tif (strpos($result['path'], $userinfo['documentroot']) === 0) {\n\t\t\t\t\t$result['path'] = str_replace($userinfo['documentroot'], \"/\", $result['path']);\n\t\t\t\t}\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t$htpasswd_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.htpasswd_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'extras', 'id' => $id]),\n\t\t\t\t\t'formdata' => $htpasswd_edit_data['htpasswd_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n} elseif ($page == 'htaccess') {\n\t// redirect if this customer sub-page is hidden via settings\n\tif (Settings::IsInList('panel.customer_hide_options', 'extras.pathoptions')) {\n\t\tResponse::redirectTo('customer_index.php');\n\t}\n\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"viewed customer_extras::htaccess\");\n\n\t\t$cperlenabled = Customer::customerHasPerlEnabled($userinfo['customerid']);\n\n\t\ttry {\n\t\t\t$htaccess_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.htaccess.php';\n\t\t\t$collection = (new Collection(DirOptions::class, $userinfo))\n\t\t\t\t->withPagination($htaccess_list_data['htaccess_list']['columns'], $htaccess_list_data['htaccess_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = [];\n\t\t$actions_links[] = [\n\t\t\t'href' => $linker->getLink(['section' => 'extras', 'page' => 'htaccess', 'action' => 'add']),\n\t\t\t'label' => lng('extras.pathoptions_add')\n\t\t];\n\n\t\t$actions_links[] = [\n\t\t\t'href' => \\Froxlor\\Froxlor::getDocsUrl() . 'user-guide/extras/',\n\t\t\t'target' => '_blank',\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'class' => 'btn-outline-secondary'\n\t\t];\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $htaccess_list_data, 'htaccess_list'),\n\t\t\t'actions_links' => $actions_links,\n\t\t\t'entity_info' => lng('extras.description')\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = DirOptions::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['customerid']) && $result['customerid'] != '' && $result['customerid'] == $userinfo['customerid']) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tDirOptions::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('extras_reallydelete_pathoptions', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], str_replace($userinfo['documentroot'], '/', $result['path']));\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tDirOptions::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']);\n\t\t\t$cperlenabled = Customer::customerHasPerlEnabled($userinfo['customerid']);\n\n\t\t\t$htaccess_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.htaccess_add.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'extras']),\n\t\t\t\t'formdata' => $htaccess_add_data['htaccess_add']\n\t\t\t]);\n\t\t}\n\t} elseif (($action == 'edit') && ($id != 0)) {\n\t\ttry {\n\t\t\t$json_result = DirOptions::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif ((isset($result['customerid'])) && ($result['customerid'] != '') && ($result['customerid'] == $userinfo['customerid'])) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tDirOptions::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tif (strpos($result['path'], $userinfo['documentroot']) === 0) {\n\t\t\t\t\t$result['path'] = str_replace($userinfo['documentroot'], \"/\", $result['path']);\n\t\t\t\t}\n\t\t\t\t$cperlenabled = Customer::customerHasPerlEnabled($userinfo['customerid']);\n\n\t\t\t\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t\t\t\t$htaccess_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.htaccess_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'extras', 'id' => $id]),\n\t\t\t\t\t'formdata' => $htaccess_edit_data['htaccess_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n} elseif ($page == 'export') {\n\t// redirect if this customer sub-page is hidden via settings\n\tif (Settings::IsInList('panel.customer_hide_options', 'extras.export')) {\n\t\tResponse::redirectTo('customer_index.php');\n\t}\n\n\tif (Settings::Get('system.exportenabled') == 1) {\n\t\tif ($action == 'abort') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"customer_extras::export - aborted scheduled data export job\");\n\t\t\t\ttry {\n\t\t\t\t\tDataDump::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => ''\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('extras_reallydelete_export', $filename, [\n\t\t\t\t\t'job_entry' => $id,\n\t\t\t\t\t'section' => 'extras',\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t]);\n\t\t\t}\n\t\t} elseif ($action == '') {\n\t\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, \"viewed customer_extras::export\");\n\n\t\t\t// check whether there is a backup-job for this customer\n\t\t\ttry {\n\t\t\t\t$export_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.export.php';\n\t\t\t\t$collection = (new Collection(DataDump::class, $userinfo));\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tDataDump::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::standardSuccess('exportscheduled');\n\t\t\t} else {\n\t\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']);\n\t\t\t\t$export_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.export.php';\n\n\t\t\t\t$actions_links = [\n\t\t\t\t\t[\n\t\t\t\t\t\t'href' => \\Froxlor\\Froxlor::getDocsUrl() . 'user-guide/extras/',\n\t\t\t\t\t\t'target' => '_blank',\n\t\t\t\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t\t\t\t'class' => 'btn-outline-secondary'\n\t\t\t\t\t]\n\t\t\t\t];\n\n\t\t\t\tUI::view('user/form-datatable.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'extras']),\n\t\t\t\t\t'formdata' => $export_data['export'],\n\t\t\t\t\t'actions_links' => $actions_links,\n\t\t\t\t\t'tabledata' => Listing::format($collection, $export_list_data, 'export_list'),\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tResponse::standardError('exportfunctionnotenabled');\n\t}\n}\n"
  },
  {
    "path": "customer_ftp.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'customer';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\Ftps;\nuse Froxlor\\Api\\Commands\\SshKeys;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// redirect if this customer page is hidden via settings\nif (Settings::IsInList('panel.customer_hide_options', 'ftp')) {\n\tResponse::redirectTo('customer_index.php');\n}\n\n$id = (int)Request::any('id', 0);\n\nif ($page == 'overview' || $page == 'accounts') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"viewed customer_ftp::accounts\");\n\t\ttry {\n\t\t\t$ftp_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.ftps.php';\n\t\t\t$collection = (new Collection(Ftps::class, $userinfo))\n\t\t\t\t->withPagination($ftp_list_data['ftp_list']['columns'], $ftp_list_data['ftp_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = [];\n\t\tif (CurrentUser::canAddResource('ftps')) {\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'ftp', 'page' => 'accounts', 'action' => 'add']),\n\t\t\t\t'label' => lng('ftp.account_add')\n\t\t\t];\n\t\t}\n\t\t$actions_links[] = [\n\t\t\t'href' => Froxlor::getDocsUrl() . 'user-guide/ftp-accounts/',\n\t\t\t'target' => '_blank',\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'class' => 'btn-outline-secondary'\n\t\t];\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $ftp_list_data, 'ftp_list'),\n\t\t\t'actions_links' => $actions_links,\n\t\t\t'entity_info' => lng('ftp.description')\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Ftps::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['username']) && $result['username'] != $userinfo['loginname']) {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tFtps::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNoWithCheckbox('ftp_reallydelete', 'admin_customer_alsoremoveftphomedir', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['username']);\n\t\t\t}\n\t\t} else {\n\t\t\tResponse::standardError('ftp_cantdeletemainaccount');\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif ($userinfo['ftps_used'] < $userinfo['ftps'] || $userinfo['ftps'] == '-1') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tFtps::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid'], '/');\n\n\t\t\t\tif (Settings::Get('customer.ftpatdomain') == '1') {\n\t\t\t\t\t$domainlist = [];\n\t\t\t\t\t$result_domains_stmt = Database::prepare(\"SELECT `domain` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\t\tWHERE `customerid`= :customerid ORDER BY `domain` ASC\");\n\t\t\t\t\tDatabase::pexecute($result_domains_stmt, [\n\t\t\t\t\t\t\"customerid\" => $userinfo['customerid']\n\t\t\t\t\t]);\n\n\t\t\t\t\twhile ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\t$domainlist[$row_domain['domain']] = $idna_convert->decode($row_domain['domain']);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$user_shell_allowed = intval($userinfo['shell_allowed']) == 1;\n\t\t\t\tif (Settings::Get('system.allow_customer_shell') == '1') {\n\t\t\t\t\t$shells['/bin/false'] = \"/bin/false\";\n\t\t\t\t\t$shells_avail = Settings::Get('system.available_shells');\n\t\t\t\t\tif (!empty($shells_avail)) {\n\t\t\t\t\t\t$shells_avail_arr = explode(\",\", $shells_avail);\n\t\t\t\t\t\t$shells_avail_arr = array_map(\"trim\", $shells_avail_arr);\n\t\t\t\t\t\tforeach ($shells_avail_arr as $shell) {\n\t\t\t\t\t\t\t$shells[$shell] = $shell;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$ftp_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/ftp/formfield.ftp_add.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'ftp']),\n\t\t\t\t\t'formdata' => $ftp_add_data['ftp_add']\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Ftps::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['username']) && $result['username'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tFtps::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tif (strpos($result['homedir'], $userinfo['documentroot']) === 0) {\n\t\t\t\t\t$homedir = str_replace($userinfo['documentroot'], \"/\", $result['homedir']);\n\t\t\t\t} else {\n\t\t\t\t\t$homedir = $result['homedir'];\n\t\t\t\t}\n\t\t\t\t$homedir = FileDir::makeCorrectDir($homedir);\n\n\t\t\t\t$pathSelect = FileDir::makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid'], $homedir);\n\n\t\t\t\t$user_shell_allowed = intval($userinfo['shell_allowed']) == 1;\n\t\t\t\tif (Settings::Get('system.allow_customer_shell') == '1') {\n\t\t\t\t\t$shells['/bin/false'] = \"/bin/false\";\n\t\t\t\t\t$shells_avail = Settings::Get('system.available_shells');\n\t\t\t\t\tif (!empty($shells_avail)) {\n\t\t\t\t\t\t$shells_avail_arr = explode(\",\", $shells_avail);\n\t\t\t\t\t\t$shells_avail_arr = array_map(\"trim\", $shells_avail_arr);\n\t\t\t\t\t\tforeach ($shells_avail_arr as $shell) {\n\t\t\t\t\t\t\t$shells[$shell] = $shell;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$ftp_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/ftp/formfield.ftp_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'ftp', 'id' => $id]),\n\t\t\t\t\t'formdata' => $ftp_edit_data['ftp_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n} elseif ($page == 'sshkeys') {\n\n\t// redirect if this customer has no permission for API usage\n\tif ($userinfo['adminsession'] == 0 && (intval(Settings::Get('system.allow_customer_shell')) == 0 || $userinfo['shell_allowed'] == 0)) {\n\t\tResponse::redirectTo('customer_index.php');\n\t}\n\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"viewed customer_ftp::sshkeys\");\n\t\ttry {\n\t\t\t$sshkeys_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.sshkeys.php';\n\t\t\t$collection = (new Collection(SshKeys::class, $userinfo))\n\t\t\t\t->withPagination($sshkeys_list_data['sshkeys_list']['columns'], $sshkeys_list_data['sshkeys_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = [];\n\t\tif (/* User-has-ftp-users-with-active-shell */\n\t\ttrue) {\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'ftp', 'page' => 'sshkeys', 'action' => 'add']),\n\t\t\t\t'label' => lng('ftp.sshkey_add')\n\t\t\t];\n\t\t}\n\t\t$actions_links[] = [\n\t\t\t'href' => Froxlor::getDocsUrl() . 'user-guide/ssh-keys/',\n\t\t\t'target' => '_blank',\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'class' => 'btn-outline-secondary'\n\t\t];\n\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $sshkeys_list_data, 'sshkeys_list'),\n\t\t\t'actions_links' => $actions_links,\n\t\t\t'entity_info' => lng('sshkeys.description')\n\t\t]);\n\t} elseif ($action == 'add') {\n\n\t\tif (Request::post('send') == 'send') {\n\t\t\ttry {\n\t\t\t\tSshKeys::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => $page\n\t\t\t]);\n\t\t} else {\n\n\t\t\t$userList = [];\n\t\t\t$result_ftpusers_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id`, `username`, `shell`\n\t\t\t\tFROM `\" . TABLE_FTP_USERS . \"`\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\tORDER BY `username` ASC\n\t\t\t\");\n\t\t\tDatabase::pexecute($result_ftpusers_stmt, [\n\t\t\t\t\"customerid\" => $userinfo['customerid']\n\t\t\t]);\n\n\t\t\twhile ($row_ftpusers = $result_ftpusers_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$userList[$row_ftpusers['id']] = $row_ftpusers['username'] . ' (Shell: ' . $row_ftpusers['shell'] . ')';\n\t\t\t}\n\n\t\t\t$sshkey_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/ftp/formfield.ftp_ssh_add.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'ftp', 'page' => 'sshkeys']),\n\t\t\t\t'formdata' => $sshkey_add_data['sshkey_add']\n\t\t\t]);\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = SshKeys::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['ssh_pubkey']) && $result['ssh_pubkey'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tSshKeys::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\n\t\t\t\t$sshkey_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/ftp/formfield.ftp_ssh_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'ftp', 'page' => 'sshkeys', 'id' => $id]),\n\t\t\t\t\t'formdata' => $sshkey_edit_data['sshkey_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = SshKeys::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['ssh_pubkey']) && $result['ssh_pubkey'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tSshKeys::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tHTML::askYesNo('sshkey_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $result['fingerprint']);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "customer_index.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'customer';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\Customers as Customers;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Database\\DbManager;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Language;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\n\nif ($action == 'logout') {\n\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, 'logged out');\n\n\tunset($_SESSION['userinfo']);\n\tCurrentUser::setData();\n\tsession_destroy();\n\n\tResponse::redirectTo('index.php');\n} elseif ($action == 'suback') {\n\tif (is_array(CurrentUser::getField('switched_user'))) {\n\t\t$result = CurrentUser::getData();\n\t\t$result = $result['switched_user'];\n\t\tsession_regenerate_id(true);\n\t\tCurrentUser::setData($result);\n\t\t$target = Request::get('target', 'index');\n\t\t$redirect = \"admin_\" . $target . \".php\";\n\t\tif (!file_exists(Froxlor::getInstallDir() . \"/\" . $redirect)) {\n\t\t\t$redirect = \"admin_index.php\";\n\t\t}\n\t\tResponse::redirectTo($redirect, null, true);\n\t} else {\n\t\tResponse::dynamicError(\"Cannot change back - You've never switched to another user :-)\");\n\t}\n}\n\nif ($page == 'overview') {\n\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, \"viewed customer_index\");\n\n\t$domain_stmt = Database::prepare(\"SELECT `domain` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\tWHERE `customerid` = :customerid\n\t\tAND `parentdomainid` = '0'\n\t\tAND `id` <> :standardsubdomain\n\t\");\n\tDatabase::pexecute($domain_stmt, [\n\t\t\"customerid\" => $userinfo['customerid'],\n\t\t\"standardsubdomain\" => $userinfo['standardsubdomain']\n\t]);\n\n\t$domainArray = [];\n\twhile ($row = $domain_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t$domainArray[] = $idna_convert->decode($row['domain']);\n\t}\n\tnatsort($domainArray);\n\n\t// standard-subdomain\n\t$stdsubdomain = '';\n\tif ($userinfo['standardsubdomain'] != '0') {\n\t\t$std_domain_stmt = Database::prepare(\"\n\t\t\tSELECT `domain` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\tWHERE `customerid` = :customerid\n\t\t\tAND `id` = :standardsubdomain\n\t\t\");\n\t\t$std_domain = Database::pexecute_first($std_domain_stmt, [\n\t\t\t\"customerid\" => $userinfo['customerid'],\n\t\t\t\"standardsubdomain\" => $userinfo['standardsubdomain']\n\t\t]);\n\t\t$stdsubdomain = $std_domain['domain'];\n\t}\n\n\t$userinfo['email'] = $idna_convert->decode($userinfo['email']);\n\t$yesterday = time() - (60 * 60 * 24);\n\t$month = date('M Y', $yesterday);\n\n\t// get disk-space usages for web, mysql and mail\n\t$usages_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DISKSPACE . \"` WHERE `customerid` = :cid ORDER BY `stamp` DESC LIMIT 1\");\n\t$usages = Database::pexecute_first($usages_stmt, [\n\t\t'cid' => $userinfo['customerid']\n\t]);\n\n\t// get everything in bytes for the percentage calculation on the dashboard\n\t$userinfo['diskspace_bytes'] = ($userinfo['diskspace'] > -1) ? $userinfo['diskspace'] * 1024 : -1;\n\t$userinfo['traffic_bytes'] = ($userinfo['traffic'] > -1) ? $userinfo['traffic'] * 1024 : -1;\n\t$userinfo['traffic_bytes_used'] = $userinfo['traffic_used'] * 1024;\n\n\tif (Settings::Get('system.mail_quota_enabled')) {\n\t\t$userinfo['email_quota_bytes'] = ($userinfo['email_quota'] > -1) ? $userinfo['email_quota'] * 1024 * 1024 : -1;\n\t\t$userinfo['email_quota_bytes_used'] = $userinfo['email_quota_used'] * 1024 * 1024;\n\t}\n\n\tif ($usages) {\n\t\t$userinfo['diskspace_bytes_used'] = $usages['webspace'] * 1024;\n\t\t$userinfo['mailspace_used'] = $usages['mail'] * 1024;\n\t\t$userinfo['dbspace_used'] = $usages['mysql'] * 1024;\n\t\t$userinfo['total_bytes_used'] = ($usages['webspace'] + $usages['mail'] + $usages['mysql']) * 1024;\n\t} else {\n\t\t$userinfo['diskspace_bytes_used'] = 0;\n\t\t$userinfo['total_bytes_used'] = 0;\n\t\t$userinfo['mailspace_used'] = 0;\n\t\t$userinfo['dbspace_used'] = 0;\n\t}\n\n\tUI::twig()->addGlobal('userinfo', $userinfo);\n\tUI::view('user/index.html.twig', [\n\t\t'domains' => $domainArray,\n\t\t'stdsubdomain' => $stdsubdomain\n\t]);\n} elseif ($page == 'profile') {\n\t$languages = Language::getLanguages();\n\n\tif (!empty($_POST)) {\n\t\tif (Request::post('send') == 'changepassword') {\n\t\t\t$old_password = Validate::validate(Request::post('old_password'), 'old password');\n\n\t\t\tif (!Crypt::validatePasswordLogin($userinfo, $old_password, TABLE_PANEL_CUSTOMERS, 'customerid')) {\n\t\t\t\tResponse::standardError('oldpasswordnotcorrect');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t$new_password = Crypt::validatePassword(Request::post('new_password'), 'new password');\n\t\t\t\t$new_password_confirm = Crypt::validatePassword(Request::post('new_password_confirm'), 'new password confirm');\n\t\t\t} catch (Exception $e) {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\n\t\t\tif ($old_password == '') {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'stringisempty',\n\t\t\t\t\t'changepassword.old_password'\n\t\t\t\t]);\n\t\t\t} elseif ($new_password == '') {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'stringisempty',\n\t\t\t\t\t'changepassword.new_password'\n\t\t\t\t]);\n\t\t\t} elseif ($new_password_confirm == '') {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'stringisempty',\n\t\t\t\t\t'changepassword.new_password_confirm'\n\t\t\t\t]);\n\t\t\t} elseif ($new_password != $new_password_confirm) {\n\t\t\t\tResponse::standardError('newpasswordconfirmerror');\n\t\t\t} else {\n\t\t\t\t// Update user password\n\t\t\t\ttry {\n\t\t\t\t\tCustomers::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $userinfo['customerid'],\n\t\t\t\t\t\t'new_customer_password' => $new_password\n\t\t\t\t\t])->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, 'changed password');\n\n\t\t\t\t// Update ftp password\n\t\t\t\tif (Request::post('change_main_ftp') == 'true') {\n\t\t\t\t\t$cryptPassword = Crypt::makeCryptPassword($new_password);\n\t\t\t\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_FTP_USERS . \"`\n\t\t\t\t\tSET `password` = :password\n\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\tAND `username` = :username\");\n\t\t\t\t\t$params = [\n\t\t\t\t\t\t\"password\" => $cryptPassword,\n\t\t\t\t\t\t\"customerid\" => $userinfo['customerid'],\n\t\t\t\t\t\t\"username\" => $userinfo['loginname']\n\t\t\t\t\t];\n\t\t\t\t\tDatabase::pexecute($stmt, $params);\n\t\t\t\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, 'changed main ftp password');\n\t\t\t\t}\n\n\t\t\t\t// Update statistics password\n\t\t\t\tif (Request::post('change_stats') == 'true') {\n\t\t\t\t\t$new_stats_password = Crypt::makeCryptPassword($new_password, true);\n\n\t\t\t\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_HTPASSWDS . \"`\n\t\t\t\t\tSET `password` = :password\n\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\tAND `username` = :username\");\n\t\t\t\t\t$params = [\n\t\t\t\t\t\t\"password\" => $new_stats_password,\n\t\t\t\t\t\t\"customerid\" => $userinfo['customerid'],\n\t\t\t\t\t\t\"username\" => $userinfo['loginname']\n\t\t\t\t\t];\n\t\t\t\t\tDatabase::pexecute($stmt, $params);\n\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t}\n\n\t\t\t\t// Update global myqsl user password\n\t\t\t\tif ($userinfo['mysqls'] != 0 && Request::post('change_global_mysql') == 'true') {\n\t\t\t\t\t$allowed_mysqlservers = json_decode($userinfo['allowed_mysqlserver'] ?? '[]', true);\n\t\t\t\t\tforeach ($allowed_mysqlservers as $dbserver) {\n\t\t\t\t\t\t// require privileged access for target db-server\n\t\t\t\t\t\tDatabase::needRoot(true, $dbserver, false);\n\t\t\t\t\t\t// get DbManager\n\t\t\t\t\t\t$dbm = new DbManager($log);\n\t\t\t\t\t\t// give permission to the user on every access-host we have\n\t\t\t\t\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t\t\t\t\tif ($dbm->getManager()->userExistsOnHost($userinfo['loginname'], $mysql_access_host)) {\n\t\t\t\t\t\t\t\t$dbm->getManager()->grantPrivilegesTo($userinfo['loginname'], $new_password, $mysql_access_host, false, true);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// create global mysql user if not exists\n\t\t\t\t\t\t\t\t$dbm->getManager()->grantPrivilegesTo($userinfo['loginname'], $new_password, $mysql_access_host, false, false, true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tResponse::redirectTo($filename);\n\t\t\t}\n\t\t} elseif (Request::post('send') == 'changetheme') {\n\t\t\tif (Settings::Get('panel.allow_theme_change_customer') == 1) {\n\t\t\t\t$theme = Validate::validate(Request::post('theme'), 'theme');\n\t\t\t\ttry {\n\t\t\t\t\tCustomers::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $userinfo['customerid'],\n\t\t\t\t\t\t'theme' => $theme\n\t\t\t\t\t])->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\n\t\t\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"changed default theme to '\" . $theme . \"'\");\n\t\t\t}\n\t\t\tResponse::redirectTo($filename);\n\t\t} elseif (Request::post('send') == 'changelanguage') {\n\t\t\t$def_language = Validate::validate(Request::post('def_language'), 'default language');\n\t\t\tif (isset($languages[$def_language])) {\n\t\t\t\ttry {\n\t\t\t\t\tCustomers::getLocal($userinfo, [\n\t\t\t\t\t\t'id' => $userinfo['customerid'],\n\t\t\t\t\t\t'def_language' => $def_language\n\t\t\t\t\t])->update();\n\t\t\t\t\tCurrentUser::setField('language', $def_language);\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"changed default language to '\" . $def_language . \"'\");\n\t\t\tResponse::redirectTo($filename);\n\t\t}\n\t} else {\n\t\t// change theme\n\t\t$default_theme = Settings::Get('panel.default_theme');\n\t\tif ($userinfo['theme'] != '') {\n\t\t\t$default_theme = $userinfo['theme'];\n\t\t}\n\t\t$themes_avail = UI::getThemes();\n\n\t\t// change language\n\t\t$default_lang = Settings::Get('panel.standardlanguage');\n\t\tif ($userinfo['def_language'] != '') {\n\t\t\t$default_lang = $userinfo['def_language'];\n\t\t}\n\n\t\tUI::view('user/profile.html.twig', [\n\t\t\t'themes' => $themes_avail,\n\t\t\t'default_theme' => $default_theme,\n\t\t\t'languages' => $languages,\n\t\t\t'default_lang' => $default_lang,\n\t\t]);\n\t}\n} elseif ($page == 'send_error_report' && Settings::Get('system.allow_error_report_customer') == '1') {\n\trequire_once __DIR__ . '/error_report.php';\n} elseif ($page == 'apikeys' && Settings::Get('api.enabled') == 1) {\n\trequire_once __DIR__ . '/api_keys.php';\n} elseif ($page == '2fa' && Settings::Get('2fa.enabled') == 1) {\n\trequire_once __DIR__ . '/2fa.php';\n}\n"
  },
  {
    "path": "customer_logger.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'customer';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\SysLog;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Response;\n\n// redirect if this customer page is hidden via settings\nif (Settings::IsInList('panel.customer_hide_options', 'extras.logger')) {\n\tResponse::redirectTo('customer_index.php');\n}\n\nif ($page == 'log') {\n\tif ($action == '') {\n\t\ttry {\n\t\t\t$syslog_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/tablelisting.syslog.php';\n\t\t\t$collection = (new Collection(SysLog::class, $userinfo))\n\t\t\t\t->withPagination($syslog_list_data['syslog_list']['columns'], $syslog_list_data['syslog_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\tUI::view('user/table.html.twig', [\n\t\t\t'listing' => Listing::format($collection, $syslog_list_data, 'syslog_list')\n\t\t]);\n\t}\n}\n"
  },
  {
    "path": "customer_mysql.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'customer';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\Commands\\Mysqls;\nuse Froxlor\\Api\\Commands\\MysqlServer;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Database\\DbManager;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// redirect if this customer page is hidden via settings or no resources given\nif (Settings::IsInList('panel.customer_hide_options', 'mysql') || $userinfo['mysqls'] == 0) {\n\tResponse::redirectTo('customer_index.php');\n}\n\n// get sql-root access data\nDatabase::needRoot(true);\nDatabase::needSqlData();\n$sql_root = Database::getSqlData();\nDatabase::needRoot(false);\n\n$id = (int)Request::any('id');\n\nif ($page == 'overview' || $page == 'mysqls') {\n\tif ($action == '') {\n\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"viewed customer_mysql::mysqls\");\n\n\t\t$multiple_mysqlservers = count(json_decode($userinfo['allowed_mysqlserver'] ?? '[]', true)) > 1;\n\n\t\ttry {\n\t\t\t$mysql_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.mysqls.php';\n\t\t\t$collection = (new Collection(Mysqls::class, $userinfo))\n\t\t\t\t->withPagination($mysql_list_data['mysql_list']['columns'], $mysql_list_data['mysql_list']['default_sorting']);\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\n\t\t$actions_links = [];\n\t\tif (CurrentUser::canAddResource('mysqls')) {\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'mysql', 'page' => 'mysqls', 'action' => 'add']),\n\t\t\t\t'label' => lng('mysql.database_create')\n\t\t\t];\n\t\t}\n\n\t\t$view = 'user/table.html.twig';\n\t\tif ($collection->count() > 0) {\n\t\t\t$view = 'user/table-note.html.twig';\n\n\t\t\t$actions_links[] = [\n\t\t\t\t'href' => $linker->getLink(['section' => 'mysql', 'page' => 'mysqls', 'action' => 'global_user']),\n\t\t\t\t'label' => lng('mysql.edit_global_user'),\n\t\t\t\t'icon' => 'fa-solid fa-user-tie',\n\t\t\t\t'class' => 'btn-outline-secondary'\n\t\t\t];\n\t\t}\n\n\t\t$actions_links[] = [\n\t\t\t'href' => \\Froxlor\\Froxlor::getDocsUrl() . 'user-guide/databases/',\n\t\t\t'target' => '_blank',\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'class' => 'btn-outline-secondary'\n\t\t];\n\n\t\tUI::view($view, [\n\t\t\t'listing' => Listing::format($collection, $mysql_list_data, 'mysql_list'),\n\t\t\t'actions_links' => $actions_links,\n\t\t\t'entity_info' => lng('mysql.description'),\n\t\t\t// alert-box\n\t\t\t'type' => 'info',\n\t\t\t'alert_msg' => lng('mysql.globaluserinfo', [$userinfo['loginname']]),\n\t\t]);\n\t} elseif ($action == 'delete' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Mysqls::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['databasename']) && $result['databasename'] != '') {\n\t\t\tDatabase::needRoot(true, $result['dbserver'], false);\n\t\t\tDatabase::needSqlData();\n\t\t\t$sql_root = Database::getSqlData();\n\t\t\tDatabase::needRoot(false);\n\n\t\t\tif (!isset($sql_root[$result['dbserver']]) || !is_array($sql_root[$result['dbserver']])) {\n\t\t\t\t$result['dbserver'] = 0;\n\t\t\t}\n\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tMysqls::getLocal($userinfo, Request::postAll())->delete();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$dbnamedesc = $result['databasename'];\n\t\t\t\tif (isset($result['description']) && $result['description'] != '') {\n\t\t\t\t\t$dbnamedesc .= ' (' . $result['description'] . ')';\n\t\t\t\t}\n\t\t\t\tHTML::askYesNo('mysql_reallydelete', $filename, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => $action\n\t\t\t\t], $dbnamedesc);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'add') {\n\t\tif ($userinfo['mysqls_used'] < $userinfo['mysqls'] || $userinfo['mysqls'] == '-1') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\tMysqls::getLocal($userinfo, Request::postAll())->add();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$mysql_servers = [];\n\t\t\t\ttry {\n\t\t\t\t\t$result_json = MysqlServer::getLocal($userinfo)->listing();\n\t\t\t\t\t$result_decoded = json_decode($result_json, true)['data']['list'];\n\t\t\t\t\tforeach ($result_decoded as $dbserver => $dbdata) {\n\t\t\t\t\t\t$mysql_servers[$dbserver] = $dbdata['caption'];\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t/* just none */\n\t\t\t\t}\n\n\t\t\t\t$mysql_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/mysql/formfield.mysql_add.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'mysql']),\n\t\t\t\t\t'formdata' => $mysql_add_data['mysql_add']\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'edit' && $id != 0) {\n\t\ttry {\n\t\t\t$json_result = Mysqls::getLocal($userinfo, [\n\t\t\t\t'id' => $id\n\t\t\t])->get();\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\tif (isset($result['databasename']) && $result['databasename'] != '') {\n\t\t\tif (Request::post('send') == 'send') {\n\t\t\t\ttry {\n\t\t\t\t\t$json_result = Mysqls::getLocal($userinfo, Request::postAll())->update();\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t\t}\n\t\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t\t'page' => $page\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$mysql_servers = [];\n\t\t\t\ttry {\n\t\t\t\t\t$result_json = MysqlServer::getLocal($userinfo)->listing();\n\t\t\t\t\t$result_decoded = json_decode($result_json, true)['data']['list'];\n\t\t\t\t\tforeach ($result_decoded as $dbserver => $dbdata) {\n\t\t\t\t\t\t$mysql_servers[$dbserver] = $dbdata['caption'] . ' (' . $dbdata['host'] . (isset($dbdata['port']) && !empty($dbdata['port']) ? ':' . $dbdata['port'] : '') . ')';\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t/* just none */\n\t\t\t\t}\n\n\t\t\t\t$mysql_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/mysql/formfield.mysql_edit.php';\n\n\t\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t\t'formaction' => $linker->getLink(['section' => 'mysql', 'id' => $id]),\n\t\t\t\t\t'formdata' => $mysql_edit_data['mysql_edit'],\n\t\t\t\t\t'editid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t} elseif ($action == 'global_user') {\n\n\t\t$allowed_mysqlservers = json_decode($userinfo['allowed_mysqlserver'] ?? '[]', true);\n\t\tif ($userinfo['mysqls'] == 0 || empty($allowed_mysqlservers)) {\n\t\t\tResponse::dynamicError('No permission');\n\t\t}\n\n\t\tif (Request::post('send') == 'send') {\n\n\t\t\t$new_password = Crypt::validatePassword(Request::post('mysql_password'));\n\t\t\tforeach ($allowed_mysqlservers as $dbserver) {\n\t\t\t\t// require privileged access for target db-server\n\t\t\t\tDatabase::needRoot(true, $dbserver, true);\n\t\t\t\t// get DbManager\n\t\t\t\t$dbm = new DbManager($log);\n\t\t\t\t// give permission to the user on every access-host we have\n\t\t\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t\t\tif ($dbm->getManager()->userExistsOnHost($userinfo['loginname'], $mysql_access_host)) {\n\t\t\t\t\t\t// update password\n\t\t\t\t\t\t$dbm->getManager()->grantPrivilegesTo($userinfo['loginname'], $new_password, $mysql_access_host, false, true, true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// create missing user\n\t\t\t\t\t\t$dbm->getManager()->grantPrivilegesTo($userinfo['loginname'], $new_password, $mysql_access_host, false, false, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t}\n\n\t\t\tResponse::redirectTo($filename, [\n\t\t\t\t'page' => 'overview'\n\t\t\t]);\n\t\t} else {\n\t\t\t$mysql_global_user_data = include_once dirname(__FILE__) . '/lib/formfields/customer/mysql/formfield.mysql_global_user.php';\n\n\t\t\tUI::view('user/form.html.twig', [\n\t\t\t\t'formaction' => $linker->getLink(['section' => 'mysql', 'page' => 'mysqls', 'action' => 'global_user']),\n\t\t\t\t'formdata' => $mysql_global_user_data['mysql_global_user'],\n\t\t\t\t'editid' => $id\n\t\t\t]);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "customer_traffic.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'customer';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Traffic\\Traffic;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// redirect if this customer page is hidden via settings\nif (Settings::IsInList('panel.customer_hide_options', 'traffic')) {\n\tResponse::redirectTo('customer_index.php');\n}\n\n$range = Request::any('range', 'currentyear');\n\nif ($page == 'current') {\n\t$range = 'currentmonth';\n}\n\ntry {\n\t$context = Traffic::getCustomerStats($userinfo, $range);\n} catch (Exception $e) {\n\tResponse::dynamicError($e->getMessage());\n}\n\n// pass metrics to the view\nUI::view('user/traffic.html.twig', $context);\n"
  },
  {
    "path": "dns_editor.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nif (!defined('AREA')) {\n\theader(\"Location: index.php\");\n\texit();\n}\n\nuse Froxlor\\Api\\Commands\\DomainZones;\nuse Froxlor\\Dns\\Dns;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// This file is being included in admin_domains and customer_domains\n// and therefore does not need to require lib/init.php\n\n$domain_id = (int)Request::any('domain_id');\n\n$record = Request::post('dns_record');\n$type = Request::post('dns_type', 'A');\n$prio = Request::post('dns_mxp');\n$content = Request::post('dns_content');\n$ttl = (int)Request::post('dns_ttl', Settings::get('system.defaultttl'));\n\n// get domain-name\n$domain = Dns::getAllowedDomainEntry($domain_id, AREA, $userinfo);\n\n$errors = \"\";\n$success_message = \"\";\n\n// action for adding a new entry\nif ($action == 'add_record' && !empty($_POST)) {\n\ttry {\n\t\tDomainZones::getLocal($userinfo, [\n\t\t\t'id' => $domain_id,\n\t\t\t'record' => $record,\n\t\t\t'type' => $type,\n\t\t\t'prio' => $prio,\n\t\t\t'content' => $content,\n\t\t\t'ttl' => $ttl\n\t\t])->add();\n\t\t$success_message = lng('success.dns_record_added');\n\t\t$record = $prio = $content = \"\";\n\t} catch (Exception $e) {\n\t\t$errors = str_replace(\"\\n\", \"<br>\", $e->getMessage());\n\t}\n} elseif ($action == 'delete') {\n\t$entry_id = (int)Request::get('id', 0);\n\tHTML::askYesNo('dnsentry_reallydelete', $filename, [\n\t\t'id' => $entry_id,\n\t\t'domain_id' => $domain_id,\n\t\t'page' => $page,\n\t\t'action' => 'deletesure'\n\t], '', [\n\t\t'section' => 'domains',\n\t\t'page' => $page,\n\t\t'domain_id' => $domain_id\n\t]);\n} elseif (Request::post('send') == 'send' && $action == 'deletesure' && !empty($_POST)) {\n\t$entry_id = (int)Request::post('id', 0);\n\t$domain_id = (int)Request::post('domain_id', 0);\n\t// remove entry\n\tif ($entry_id > 0 && $domain_id > 0) {\n\t\ttry {\n\t\t\tDomainZones::getLocal($userinfo, [\n\t\t\t\t'entry_id' => $entry_id,\n\t\t\t\t'id' => $domain_id\n\t\t\t])->delete();\n\t\t\t// success message (inline)\n\t\t\t$success_message = lng('success.dns_record_deleted');\n\t\t} catch (Exception $e) {\n\t\t\t$errors = str_replace(\"\\n\", \"<br>\", $e->getMessage());\n\t\t}\n\t}\n}\n\n// select all entries\ntry {\n\t$dns_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/tablelisting.dns.php';\n\t$collection = (new Collection(DomainZones::class, $userinfo, ['id' => $domain_id]))\n\t\t->withPagination($dns_list_data['dns_list']['columns'], $dns_list_data['dns_list']['default_sorting'], ['domain_id='.$domain_id]);\n} catch (Exception $e) {\n\tResponse::dynamicError($e->getMessage());\n}\n\ntry {\n\t$json_result = DomainZones::getLocal($userinfo, [\n\t\t'id' => $domain_id\n\t])->get();\n} catch (Exception $e) {\n\tResponse::dynamicError($e->getMessage());\n}\n$result = json_decode($json_result, true)['data'];\n$zonefile = implode(\"\\n\", $result);\n\n$dns_add_data = include_once dirname(__FILE__) . '/lib/formfields/formfield.dns_add.php';\n\nUI::view('user/dns-editor.html.twig', [\n\t'listing' => Listing::format($collection, $dns_list_data, 'dns_list', ['domain_id' => $domain_id]),\n\t'actions_links' => [\n\t\t[\n\t\t\t'href' => $linker->getLink([\n\t\t\t\t'section' => 'domains',\n\t\t\t\t'page' => 'domains',\n\t\t\t\t'action' => 'edit',\n\t\t\t\t'id' => $domain_id\n\t\t\t]),\n\t\t\t'label' => lng('admin.domain_edit'),\n\t\t\t'icon' => 'fa-solid fa-pen'\n\t\t],\n\t\t[\n\t\t\t'href' => $linker->getLink(['section' => 'domains', 'page' => 'domains']),\n\t\t\t'label' => lng('panel.backtooverview'),\n\t\t\t'icon' => 'fa-solid fa-reply'\n\t\t]\n\t],\n\t'formaction' => $linker->getLink(['section' => 'domains', 'action' => 'add_record', 'domain_id' => $domain_id]),\n\t'formdata' => $dns_add_data['dns_add'],\n\t// alert-box\n\t'type' => (!empty($errors) ? 'danger' : (!empty($success_message) ? 'success' : 'warning')),\n\t'alert_msg' => (!empty($errors) ? $errors : (!empty($success_message) ? $success_message : lng('dns.howitworks'))),\n\t'zonefile' => $zonefile,\n]);\n"
  },
  {
    "path": "doc/example/FroxlorAPI.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nclass FroxlorAPI\n{\n    private string $url;\n    private string $key;\n    private string $secret;\n    private ?array $lastError = null;\n    private ?string $lastStatusCode = null;\n\n    public function __construct($url, $key, $secret)\n    {\n        $this->url = $url;\n        $this->key = $key;\n        $this->secret = $secret;\n    }\n\n    public function request($command, array $data = [])\n    {\n        $payload = [\n            'command' => $command,\n            'params' => $data\n        ];\n\n        $ch = curl_init($this->url);\n        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n        curl_setopt($ch, CURLOPT_USERPWD, $this->key . \":\" . $this->secret);\n        curl_setopt($ch, CURLOPT_TIMEOUT, 30);\n        curl_setopt($ch, CURLOPT_POST, 1);\n        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n        $result = curl_exec($ch);\n\n        $this->lastStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n\n        return json_decode($result ?? curl_error($ch), true);\n    }\n\n    public function getLastStatusCode(): ?string\n    {\n        return $this->lastStatusCode;\n    }\n}\n"
  },
  {
    "path": "doc/example/create_customer.php",
    "content": "<?php\n\n// include FroxlorAPI helper class\nrequire __DIR__ . '/FroxlorAPI.php';\n\n// create object of FroxlorAPI with URL, apikey and apisecret\n$fapi = new FroxlorAPI('http://127.0.0.1/api.php', 'your-api-key', 'your-api-secret');\n\n// customer data\n$data = [\n\t'new_loginname' => 'test',\n\t'email' => 'test@froxlor.org',\n\t'firstname' => 'Test',\n\t'name' => 'Testman',\n\t'customernumber' => 1337,\n\t'new_customer_password' => 's0mEcRypt1cpassword' . uniqid()\n];\n// send request\n$response = $fapi->request('Customers.add', $data);\n\n// check for error\nif ($fapi->getLastStatusCode() != 200) {\n\techo \"HTTP-STATUS: \" . $fapi->getLastStatusCode() . PHP_EOL;\n    echo \"Description: \"  . $response['message'] . PHP_EOL;\n    exit();\n}\n\n// view response data\nvar_dump($response);\n\n/*\narray(60) {\n  [\"customerid\"]=>\n  string(1) \"1\"\n  [\"loginname\"]=>\n  string(4) \"test\"\n  [\"password\"]=>\n  string(63) \"$5$asdasdasd.asdasd\"\n  [\"adminid\"]=>\n  string(1) \"1\"\n  [\"name\"]=>\n  string(7) \"Testman\"\n  [\"firstname\"]=>\n  string(4) \"Test\"\n  [...]\n*/"
  },
  {
    "path": "doc/example/index.html",
    "content": ""
  },
  {
    "path": "doc/example/list_functions.php",
    "content": "<?php\n\n// include FroxlorAPI helper class\nrequire __DIR__ . '/FroxlorAPI.php';\n\n// create object of FroxlorAPI with URL, apikey and apisecret\n$fapi = new FroxlorAPI('http://localhost/api.php', 'your-api-key', 'your-api-secret');\n\n// send request\n$response = $fapi->request('Froxlor.listFunctions');\n\n// check for error\nif ($fapi->getLastStatusCode() != 200) {\n    echo \"HTTP-STATUS: \" . $fapi->getLastStatusCode() . PHP_EOL;\n    echo \"Description: \"  . $response['message'] . PHP_EOL;\n    exit();\n}\n\n// view response data\nvar_dump($response);\n"
  },
  {
    "path": "doc/index.html",
    "content": ""
  },
  {
    "path": "error_report.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nif (!defined('AREA')) {\n\theader(\"Location: index.php\");\n\texit();\n}\n\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Database\\Database;\n\n// This file is being included in admin_domains and customer_domains\n// and therefore does not need to require lib/init.php\n\n$errid = Request::any('errorid');\n\nif (!empty($errid)) {\n\t// read error file\n\t$err_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . \"/logs/\");\n\t$err_file = FileDir::makeCorrectFile($err_dir . \"/\" . $errid . \"_sql-error.log\");\n\n\tif (file_exists($err_file)) {\n\t\t$error_content = file_get_contents($err_file);\n\t\t$error = explode(\"|\", $error_content);\n\n\t\t$_error = [\n\t\t\t'code' => str_replace(\"\\n\", \"\", substr($error[1], 5)),\n\t\t\t'message' => str_replace(\"\\n\", \"\", substr($error[2], 4)),\n\t\t\t'file' => str_replace(\"\\n\", \"\", substr($error[3], 5 + strlen(Froxlor::getInstallDir()))),\n\t\t\t'line' => str_replace(\"\\n\", \"\", substr($error[4], 5)),\n\t\t\t'trace' => str_replace(Froxlor::getInstallDir(), \"\", substr($error[5], 6))\n\t\t];\n\n\t\t// build mail-content\n\t\t$mail_body = \"Dear froxlor-team,\\n\\n\";\n\t\t$mail_body .= \"the following error has been reported by a user:\\n\\n\";\n\t\t$mail_body .= \"-------------------------------------------------------------\\n\";\n\t\t$mail_body .= $_error['code'] . ' ' . $_error['message'] . \"\\n\\n\";\n\t\t$mail_body .= \"File: \" . $_error['file'] . ':' . $_error['line'] . \"\\n\\n\";\n\t\t$mail_body .= \"Trace:\\n\" . trim($_error['trace']) . \"\\n\\n\";\n\t\t$mail_body .= \"-------------------------------------------------------------\\n\\n\";\n\t\t$mail_body .= \"User-Area: \" . AREA . \"\\n\";\n\t\t$mail_body .= \"Froxlor-version: \" . Froxlor::VERSION . \"\\n\";\n\t\t$mail_body .= \"DB-version: \" . Froxlor::DBVERSION . \"\\n\\n\";\n\t\ttry {\n\t\t\t$mail_body .= \"Database: \" . Database::getAttribute(PDO::ATTR_SERVER_VERSION);\n\t\t} catch (\\Exception $e) {\n\t\t\t/* ignore */\n\t\t}\n\t\t$mail_body .= \"End of report\";\n\t\t$mail_html = nl2br($mail_body);\n\n\t\t// send actual report to dev-team\n\t\tif (Request::post('send') == 'send') {\n\t\t\t// send mail and say thanks\n\t\t\t$_mailerror = false;\n\t\t\ttry {\n\t\t\t\t$mail->Subject = '[Froxlor] Error report by user';\n\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t$mail->MsgHTML($mail_html);\n\t\t\t\t$mail->AddAddress('error-reports@froxlor.org', 'Froxlor Developer Team');\n\t\t\t\t$mail->Send();\n\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t$_mailerror = true;\n\t\t\t} catch (Exception $e) {\n\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t$_mailerror = true;\n\t\t\t}\n\n\t\t\tif ($_mailerror) {\n\t\t\t\t// error when reporting an error...LOLFUQ\n\t\t\t\tResponse::standardError('send_report_error', $mailerr_msg);\n\t\t\t}\n\n\t\t\t// finally remove error from fs\n\t\t\t@unlink($err_file);\n\t\t\tResponse::standardSuccess('sent_error_report', '', ['filename' => 'index.php']);\n\t\t}\n\t\t// show a nice summary of the error-report\n\t\t// before actually sending anything\n\t\tUI::view('user/error_report.html.twig', [\n\t\t\t'mail_html' => $mail_body,\n\t\t\t'errorid' => $errid\n\t\t]);\n\t} else {\n\t\tResponse::redirectTo($filename);\n\t}\n} else {\n\tResponse::redirectTo($filename);\n}\n"
  },
  {
    "path": "index.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst AREA = 'login';\nrequire __DIR__ . '/lib/init.php';\n\nuse Froxlor\\Api\\FroxlorRPC;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\FroxlorTwoFactorAuth;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse Froxlor\\Validate\\Validate;\n\nif ($action == '') {\n\t$action = 'login';\n}\n\nif ($action == '2fa_entercode') {\n\t// page for entering the 2FA code after successful login\n\tif (!isset($_SESSION) || !isset($_SESSION['secret_2fa'])) {\n\t\t// no session - redirect to index\n\t\tResponse::redirectTo('index.php');\n\t\texit();\n\t}\n\t$smessage = (int)Request::get('showmessage', 0);\n\t$message = \"\";\n\tif ($smessage > 0) {\n\t\t$message = lng('error.2fa_wrongcode');\n\t}\n\t// show template to enter code\n\tUI::view('login/enter2fa.html.twig', [\n\t\t'pagetitle' => lng('login.2fa'),\n\t\t'remember_me' => (Settings::Get('panel.db_version') >= 202407200) ? true : false,\n\t\t'message' => $message\n\t]);\n} elseif ($action == '2fa_verify') {\n\t// verify code from 2fa code-enter form\n\tif (!isset($_SESSION) || !isset($_SESSION['secret_2fa'])) {\n\t\t// no session - redirect to index\n\t\tResponse::redirectTo('index.php');\n\t\texit();\n\t}\n\t$code = Request::post('2fa_code');\n\t$remember = Request::post('2fa_remember');\n\t// verify entered code\n\t$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));\n\t// get user-data\n\t$table = $_SESSION['uidtable_2fa'];\n\t$field = $_SESSION['uidfield_2fa'];\n\t$uid = $_SESSION['uid_2fa'];\n\t$isadmin = $_SESSION['unfo_2fa'];\n\tif ($_SESSION['secret_2fa'] == 'email') {\n\t\t// verify code set to user's data_2fa field\n\t\t$sel_stmt = Database::prepare(\"SELECT `data_2fa` FROM \" . $table . \" WHERE `\" . $field . \"` = :uid\");\n\t\t$userinfo_code = Database::pexecute_first($sel_stmt, ['uid' => $uid]);\n\t\t// 60sec discrepancy (possible slow email delivery)\n\t\t$result = $tfa->verifyCode($userinfo_code['data_2fa'], $code, 60);\n\t} else {\n\t\t$result = $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3);\n\t}\n\t// either the code is valid when using authenticator-app, or we will select userdata by id and entered code\n\t// which is temporarily stored for the customer when using email-2fa\n\tif ($result) {\n\t\t$sel_param = [\n\t\t\t'uid' => $uid\n\t\t];\n\t\t$sel_stmt = Database::prepare(\"SELECT * FROM \" . $table . \" WHERE `\" . $field . \"` = :uid\");\n\t\t$userinfo = Database::pexecute_first($sel_stmt, $sel_param);\n\t\t// whoops, no (valid) user? Start again\n\t\tif (empty($userinfo)) {\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t'showmessage' => '2'\n\t\t\t]);\n\t\t}\n\t\t// set fields in $userinfo required for finishLogin()\n\t\t$userinfo['adminsession'] = $isadmin;\n\t\t$userinfo['userid'] = $uid;\n\n\t\t// when using email-2fa, remove the one-time-code\n\t\tif ($userinfo['type_2fa'] == '1') {\n\t\t\t$del_stmt = Database::prepare(\"UPDATE \" . $table . \" SET `data_2fa` = '' WHERE `\" . $field . \"` = :uid\");\n\t\t\tDatabase::pexecute_first($del_stmt, [\n\t\t\t\t'uid' => $uid\n\t\t\t]);\n\t\t}\n\n\t\t// when remember is activated, set the cookie\n\t\tif ($remember) {\n\t\t\t$selector = base64_encode(Froxlor::genSessionId(9));\n\t\t\t$authenticator = Froxlor::genSessionId(33);\n\t\t\t$valid_until = time()+60*60*24*30;\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\".TABLE_PANEL_2FA_TOKENS.\"` SET\n\t\t\t\t`selector` = :selector,\n\t\t\t\t`token` = :authenticator,\n\t\t\t\t`userid` = :userid,\n\t\t\t\t`valid_until` = :valid_until\n\t\t\t\");\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'selector' => $selector,\n\t\t\t\t'authenticator' => hash('sha256', $authenticator),\n\t\t\t\t'userid' => $uid,\n\t\t\t\t'valid_until' => $valid_until\n\t\t\t]);\n\t\t\t$cookie_params = [\n\t\t\t\t'expires' => $valid_until, // 30 days\n\t\t\t\t'path' => '/',\n\t\t\t\t'domain' => UI::getCookieHost(),\n\t\t\t\t'secure' => UI::requestIsHttps(),\n\t\t\t\t'httponly' => true,\n\t\t\t\t'samesite' => 'Strict'\n\t\t\t];\n\t\t\tsetcookie('frx_2fa_remember', $selector.':'.base64_encode($authenticator), $cookie_params);\n\t\t}\n\n\t\t// if not successful somehow - start again\n\t\tif (!finishLogin($userinfo)) {\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t'showmessage' => '2'\n\t\t\t]);\n\t\t}\n\t\texit();\n\t}\n\t// wrong 2fa code - treat like \"wrong password\"\n\t$stmt = Database::prepare(\"\n\t\tUPDATE \" . $table . \"\n\t\tSET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1\n\t\tWHERE `\" . $field . \"`= :uid\n\t\");\n\tDatabase::pexecute($stmt, [\n\t\t\"lastlogin_fail\" => time(),\n\t\t\"uid\" => $uid\n\t]);\n\n\t// get data for processing further\n\t$stmt = Database::prepare(\"\n\t\tSELECT `loginname`, `loginfail_count`, `lastlogin_fail` FROM \" . $table . \"\n\t\tWHERE `\" . $field . \"`= :uid\n\t\");\n\t$fail_user = Database::pexecute_first($stmt, [\n\t\t\"uid\" => $uid\n\t]);\n\n\tif ($fail_user['loginfail_count'] >= Settings::Get('login.maxloginattempts') && $fail_user['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))) {\n\t\t// Log failed login\n\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t'loginname' => $_SERVER['REMOTE_ADDR']\n\t\t]);\n\t\t$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, \"User '\" . $fail_user['loginname'] . \"' entered wrong 2fa code too often.\");\n\t\tunset($fail_user);\n\t\tResponse::redirectTo('index.php', [\n\t\t\t'showmessage' => '3'\n\t\t]);\n\t\texit();\n\t}\n\tunset($fail_user);\n\t// back to form\n\tResponse::redirectTo('index.php', [\n\t\t'action' => '2fa_entercode',\n\t\t'showmessage' => '1'\n\t]);\n\texit();\n} elseif ($action == 'login') {\n\tif (!empty($_POST)) {\n\t\t$loginname = Validate::validate(Request::post('loginname'), 'loginname');\n\t\t$password = Validate::validate(Request::post('password'), 'password');\n\n\t\t$select_additional = '';\n\t\tif (Settings::Get('panel.db_version') >= 202312230) {\n\t\t\t$select_additional = ' AND `gui_access` = 1';\n\t\t}\n\t\t$stmt = Database::prepare(\"\n\t\t\tSELECT `loginname` AS `customer`\n\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\tWHERE `loginname`= :loginname\" .\n\t\t\t$select_additional\n\t\t);\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"loginname\" => $loginname\n\t\t]);\n\t\t$row = $stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t$is_admin = false;\n\t\t$table = \"\";\n\t\tif ($row && $row['customer'] == $loginname) {\n\t\t\t$table = \"`\" . TABLE_PANEL_CUSTOMERS . \"`\";\n\t\t\t$uid = 'customerid';\n\t\t\t$adminsession = '0';\n\t\t} else {\n\t\t\tif ((int)Settings::Get('login.domain_login') == 1) {\n\t\t\t\t$domainname = $idna_convert->encode(preg_replace([\n\t\t\t\t\t'/\\:(\\d)+$/',\n\t\t\t\t\t'/^https?\\:\\/\\//'\n\t\t\t\t], '', $loginname));\n\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `customerid`\n\t\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `domain` = :domain\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\"domain\" => $domainname\n\t\t\t\t]);\n\t\t\t\t$row2 = $stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t\t\tif (isset($row2['customerid']) && $row2['customerid'] > 0) {\n\t\t\t\t\t$loginname = Customer::getCustomerDetail($row2['customerid'], 'loginname');\n\t\t\t\t\tif ($loginname !== false) {\n\t\t\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\t\t\tSELECT `loginname` AS `customer`\n\t\t\t\t\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\t\t\t\tWHERE `loginname`= :loginname\n\t\t\t\t\t\t\");\n\t\t\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\t\t\"loginname\" => $loginname\n\t\t\t\t\t\t]);\n\t\t\t\t\t\t$row3 = $stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\t\t\tif ($row3 && $row3['customer'] == $loginname) {\n\t\t\t\t\t\t\t$table = \"`\" . TABLE_PANEL_CUSTOMERS . \"`\";\n\t\t\t\t\t\t\t$uid = 'customerid';\n\t\t\t\t\t\t\t$adminsession = '0';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (empty($table)) {\n\t\t\t// try login as admin of no customer-login method worked\n\t\t\t$is_admin = true;\n\t\t}\n\n\t\tif ((Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) && $is_admin == false) {\n\t\t\tResponse::redirectTo('index.php');\n\t\t\texit();\n\t\t}\n\n\t\tif ($is_admin) {\n\t\t\tif (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {\n\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `loginname` AS `admin` FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\t\tWHERE `loginname`= :loginname\n\t\t\t\t\tAND `change_serversettings` = '1'\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\"loginname\" => $loginname\n\t\t\t\t]);\n\t\t\t\t$row = $stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\tif (!isset($row['admin'])) {\n\t\t\t\t\t// not an admin who can see updates\n\t\t\t\t\tResponse::redirectTo('index.php');\n\t\t\t\t\texit();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$select_additional = '';\n\t\t\t\tif (Settings::Get('panel.db_version') >= 202312230) {\n\t\t\t\t\t$select_additional = ' AND `gui_access` = 1';\n\t\t\t\t}\n\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `loginname` AS `admin`\n\t\t\t\t\tFROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\t\tWHERE `loginname`= :loginname\" .\n\t\t\t\t\t$select_additional\n\t\t\t\t);\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\"loginname\" => $loginname\n\t\t\t\t]);\n\t\t\t\t$row = $stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t}\n\n\t\t\tif ($row && $row['admin'] == $loginname) {\n\t\t\t\t$table = \"`\" . TABLE_PANEL_ADMINS . \"`\";\n\t\t\t\t$uid = 'adminid';\n\t\t\t\t$adminsession = '1';\n\t\t\t} else {\n\t\t\t\t// Log failed login\n\t\t\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t\t'loginname' => $_SERVER['REMOTE_ADDR']\n\t\t\t\t]);\n\t\t\t\t$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, \"Unknown user tried to login.\");\n\n\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t'showmessage' => '2'\n\t\t\t\t]);\n\t\t\t\texit();\n\t\t\t}\n\t\t}\n\n\t\t$userinfo_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM $table WHERE `loginname`= :loginname\n\t\t\");\n\t\tDatabase::pexecute($userinfo_stmt, [\n\t\t\t\"loginname\" => $loginname\n\t\t]);\n\t\t$userinfo = $userinfo_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\tif ($userinfo['loginfail_count'] >= Settings::Get('login.maxloginattempts') && $userinfo['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))) {\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t'showmessage' => '3'\n\t\t\t]);\n\t\t\texit();\n\t\t} elseif (Crypt::validatePasswordLogin($userinfo, $password, $table, $uid)) {\n\t\t\t// only show \"you're banned\" if the login was successful\n\t\t\t// because we don't want to publish that the user does exist\n\t\t\tif ($userinfo['deactivated']) {\n\t\t\t\tunset($userinfo);\n\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t'showmessage' => '5'\n\t\t\t\t]);\n\t\t\t\texit();\n\t\t\t} else {\n\t\t\t\t// login correct\n\t\t\t\t// reset loginfail_counter, set lastlogin_succ\n\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE $table\n\t\t\t\t\tSET `lastlogin_succ`= :lastlogin_succ, `loginfail_count`='0'\n\t\t\t\t\tWHERE `$uid`= :uid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\"lastlogin_succ\" => time(),\n\t\t\t\t\t\"uid\" => $userinfo[$uid]\n\t\t\t\t]);\n\t\t\t\t$userinfo['userid'] = $userinfo[$uid];\n\t\t\t\t$userinfo['adminsession'] = $adminsession;\n\t\t\t}\n\t\t} else {\n\t\t\t// login incorrect\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tUPDATE $table\n\t\t\t\tSET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1\n\t\t\t\tWHERE `$uid`= :uid\n\t\t\t\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\"lastlogin_fail\" => time(),\n\t\t\t\t\"uid\" => $userinfo[$uid]\n\t\t\t]);\n\n\t\t\t// Log failed login\n\t\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t'loginname' => $_SERVER['REMOTE_ADDR']\n\t\t\t]);\n\t\t\t$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, \"User tried to login with wrong password.\");\n\n\t\t\tunset($userinfo);\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t'showmessage' => '2'\n\t\t\t]);\n\t\t\texit();\n\t\t}\n\n\t\t// 2FA activated\n\t\tif (Settings::Get('2fa.enabled') == '1' && $userinfo['type_2fa'] > 0) {\n\n\t\t\t// check for remember cookie\n\t\t\tif (!empty($_COOKIE['frx_2fa_remember'])) {\n\t\t\t\tlist($selector, $authenticator) = explode(':', $_COOKIE['frx_2fa_remember']);\n\t\t\t\t$sel_stmt = Database::prepare(\"SELECT `token` FROM `\".TABLE_PANEL_2FA_TOKENS.\"` WHERE `selector` = :selector AND `userid` = :uid AND `valid_until` >= UNIX_TIMESTAMP()\");\n\t\t\t\t$token_check = Database::pexecute_first($sel_stmt, ['selector' => $selector, 'uid' => $userinfo[$uid]]);\n\t\t\t\tif ($token_check && hash_equals($token_check['token'], hash('sha256', base64_decode($authenticator)))) {\n\t\t\t\t\tif (!finishLogin($userinfo)) {\n\t\t\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t\t\t'showmessage' => '2'\n\t\t\t\t\t\t]);\n\t\t\t\t\t}\n\t\t\t\t\texit();\n\t\t\t\t}\n\t\t\t\t// not found or invalid, this cookie is useless, get rid of it\n\t\t\t\tunset($_COOKIE['frx_2fa_remember']);\n\t\t\t\tsetcookie('frx_2fa_remember', \"\", time()-3600);\n\t\t\t}\n\n\t\t\t// redirect to code-enter-page\n\t\t\t$_SESSION['secret_2fa'] = ($userinfo['type_2fa'] == 2 ? $userinfo['data_2fa'] : 'email');\n\t\t\t$_SESSION['uid_2fa'] = $userinfo[$uid];\n\t\t\t$_SESSION['uidfield_2fa'] = $uid;\n\t\t\t$_SESSION['uidtable_2fa'] = $table;\n\t\t\t$_SESSION['unfo_2fa'] = $is_admin;\n\t\t\t// send mail if type_2fa = 1 (email)\n\t\t\tif ($userinfo['type_2fa'] == 1) {\n\t\t\t\t// generate code\n\t\t\t\t$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));\n\t\t\t\t$secret = $tfa->createSecret();\n\t\t\t\t$code = $tfa->getCode($secret);\n\t\t\t\t// set code for user\n\t\t\t\t$stmt = Database::prepare(\"UPDATE $table SET `data_2fa` = :d2fa WHERE `$uid` = :uid\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\"d2fa\" => $secret,\n\t\t\t\t\t\"uid\" => $userinfo[$uid]\n\t\t\t\t]);\n\t\t\t\t// build up & send email\n\t\t\t\t$_mailerror = false;\n\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t$replace_arr = [\n\t\t\t\t\t'CODE' => $code\n\t\t\t\t];\n\t\t\t\t$mail_body = html_entity_decode(PhpHelper::replaceVariables(lng('mails.2fa.mailbody'), $replace_arr));\n\n\t\t\t\ttry {\n\t\t\t\t\t$mail->Subject = lng('mails.2fa.subject');\n\t\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t\t$mail->MsgHTML(str_replace(\"\\n\", \"<br />\", $mail_body));\n\t\t\t\t\t$mail->AddAddress($userinfo['email'], User::getCorrectUserSalutation($userinfo));\n\t\t\t\t\t$mail->Send();\n\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t$_mailerror = true;\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t$_mailerror = true;\n\t\t\t\t}\n\n\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t\t\t'loginname' => '2fa code-sending'\n\t\t\t\t\t]);\n\t\t\t\t\t$rstlog->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, \"Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t\t'showmessage' => '4',\n\t\t\t\t\t\t'customermail' => $userinfo['email']\n\t\t\t\t\t]);\n\t\t\t\t\texit();\n\t\t\t\t}\n\n\t\t\t\t$mail->ClearAddresses();\n\t\t\t}\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t'action' => '2fa_entercode'\n\t\t\t]);\n\t\t\texit();\n\t\t}\n\n\t\tif (!finishLogin($userinfo)) {\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t'showmessage' => '2'\n\t\t\t]);\n\t\t}\n\t\texit();\n\t} else {\n\t\t$smessage = (int)Request::get('showmessage', 0);\n\t\t$message = '';\n\t\t$successmessage = '';\n\n\t\tswitch ($smessage) {\n\t\t\tcase 1:\n\t\t\t\t$successmessage = lng('pwdreminder.success');\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\t$message = lng('error.login');\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\t$message = lng('error.login_blocked', [Settings::Get('login.deactivatetime')]);\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\t$message = lng('error.errorsendingmailpub');\n\t\t\t\tbreak;\n\t\t\tcase 5:\n\t\t\t\t$message = lng('error.user_banned');\n\t\t\t\tbreak;\n\t\t\tcase 6:\n\t\t\t\t$successmessage = lng('pwdreminder.changed');\n\t\t\t\tbreak;\n\t\t\tcase 7:\n\t\t\t\t$message = lng('pwdreminder.wrongcode');\n\t\t\t\tbreak;\n\t\t\tcase 8:\n\t\t\t\t$message = lng('pwdreminder.notallowed');\n\t\t\t\tbreak;\n\t\t}\n\n\t\t$update_in_progress = false;\n\t\tif (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {\n\t\t\t$update_in_progress = true;\n\t\t}\n\n\t\t// Pass the last used page if needed\n\t\t$lastscript = Request::any('script', '');\n\t\tif (!empty($lastscript)) {\n\t\t\t$lastscript = str_replace(\"..\", \"\", $lastscript);\n\t\t\t$lastscript = htmlspecialchars($lastscript, ENT_QUOTES);\n\n\t\t\tif (file_exists(__DIR__ . \"/\" . $lastscript)) {\n\t\t\t\t$_SESSION['lastscript'] = $lastscript;\n\t\t\t} else {\n\t\t\t\t$lastscript = \"\";\n\t\t\t}\n\t\t}\n\t\t$lastqrystr = Request::any('qrystr', '');\n\t\tif (!empty($lastqrystr)) {\n\t\t\t$lastqrystr = urlencode($lastqrystr);\n\t\t\t$_SESSION['lastqrystr'] = $lastqrystr;\n\t\t}\n\n\t\tUI::view('login/login.html.twig', [\n\t\t\t'pagetitle' => 'Login',\n\t\t\t'upd_in_progress' => $update_in_progress,\n\t\t\t'message' => $message,\n\t\t\t'successmsg' => $successmessage\n\t\t]);\n\t}\n}\n\nif ($action == 'forgotpwd') {\n\t$adminchecked = false;\n\t$message = '';\n\n\tif (!empty($_POST)) {\n\t\t$loginname = Validate::validate(Request::post('loginname'), 'loginname');\n\t\t$email = Validate::validateEmail(Request::post('loginemail'));\n\t\t$result_stmt = Database::prepare(\"SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\tWHERE `loginname`= :loginname\n\t\t\tAND `email`= :email\");\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\"loginname\" => $loginname,\n\t\t\t\"email\" => $email\n\t\t]);\n\n\t\tif (Database::num_rows() == 0) {\n\t\t\t$result_stmt = Database::prepare(\"SELECT `adminid`, `name`, `email`, `loginname`, `def_language`, `deactivated` FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\tWHERE `loginname`= :loginname\n\t\t\t\tAND `email`= :email\");\n\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t\"loginname\" => $loginname,\n\t\t\t\t\"email\" => $email\n\t\t\t]);\n\n\t\t\tif (Database::num_rows() > 0) {\n\t\t\t\t$adminchecked = true;\n\t\t\t} else {\n\t\t\t\t$result_stmt = null;\n\t\t\t}\n\t\t}\n\n\t\tif ($adminchecked) {\n\t\t\tif (Settings::Get('panel.allow_preset_admin') != '1') {\n\t\t\t\t$message = lng('pwdreminder.notallowed');\n\t\t\t\tunset($adminchecked);\n\t\t\t}\n\t\t} else {\n\t\t\tif (Settings::Get('panel.allow_preset') != '1') {\n\t\t\t\t$message = lng('pwdreminder.notallowed');\n\t\t\t}\n\t\t}\n\n\t\tif (empty($message)) {\n\t\t\tif ($result_stmt !== null) {\n\t\t\t\t$user = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t\t\t/* Check whether user is banned */\n\t\t\t\tif ($user['deactivated']) {\n\t\t\t\t\t$message = lng('pwdreminder.notallowed');\n\t\t\t\t} else {\n\t\t\t\t\tif (($adminchecked && Settings::Get('panel.allow_preset_admin') == '1') || $adminchecked == false) {\n\t\t\t\t\t\tif ($user !== false) {\n\t\t\t\t\t\t\t// build a activation code\n\t\t\t\t\t\t\t$timestamp = time();\n\t\t\t\t\t\t\t$first = substr(md5($user['loginname'] . $timestamp . PhpHelper::randomStr(16)), 0, 15);\n\t\t\t\t\t\t\t$third = substr(md5($user['email'] . $timestamp . PhpHelper::randomStr(16)), -15);\n\t\t\t\t\t\t\t$activationcode = $first . $timestamp . $third . substr(md5($third . $timestamp), 0, 10);\n\n\t\t\t\t\t\t\t// Drop all existing activation codes for this user\n\t\t\t\t\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_ACTIVATION . \"`\n\t\t\t\t\t\t\t\tWHERE `userid` = :userid\n\t\t\t\t\t\t\t\tAND `admin` = :admin\");\n\t\t\t\t\t\t\t$params = [\n\t\t\t\t\t\t\t\t\"userid\" => $adminchecked ? $user['adminid'] : $user['customerid'],\n\t\t\t\t\t\t\t\t\"admin\" => $adminchecked ? 1 : 0\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\tDatabase::pexecute($stmt, $params);\n\n\t\t\t\t\t\t\t// Add new activation code to database\n\t\t\t\t\t\t\t$stmt = Database::prepare(\"INSERT INTO `\" . TABLE_PANEL_ACTIVATION . \"`\n\t\t\t\t\t\t\t\t(userid, admin, creation, activationcode)\n\t\t\t\t\t\t\t\tVALUES (:userid, :admin, :creation, :activationcode)\");\n\t\t\t\t\t\t\t$params = [\n\t\t\t\t\t\t\t\t\"userid\" => $adminchecked ? $user['adminid'] : $user['customerid'],\n\t\t\t\t\t\t\t\t\"admin\" => $adminchecked ? 1 : 0,\n\t\t\t\t\t\t\t\t\"creation\" => $timestamp,\n\t\t\t\t\t\t\t\t\"activationcode\" => $activationcode\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\tDatabase::pexecute($stmt, $params);\n\n\t\t\t\t\t\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t\t\t\t\t'loginname' => 'password_reset'\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t$rstlog->logAction(FroxlorLogger::USR_ACTION, LOG_WARNING, \"User '\" . $user['loginname'] . \"' requested a link for setting a new password.\");\n\n\t\t\t\t\t\t\t// Set together our activation link\n\t\t\t\t\t\t\t$protocol = empty($_SERVER['HTTPS']) ? 'http' : 'https';\n\t\t\t\t\t\t\t// this can be a fixed value to avoid potential exploiting by modifying headers\n\t\t\t\t\t\t\t$host = Settings::Get('system.hostname'); // $_SERVER['HTTP_HOST'];\n\t\t\t\t\t\t\t$port = $_SERVER['SERVER_PORT'] != 80 ? ':' . $_SERVER['SERVER_PORT'] : '';\n\t\t\t\t\t\t\t// don't add :443 when https is used, as it is default (and just looks weird!)\n\t\t\t\t\t\t\tif ($protocol == 'https' && $_SERVER['SERVER_PORT'] == '443') {\n\t\t\t\t\t\t\t\t$port = '';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// there can be only one script to handle this so we can use a fixed value here\n\t\t\t\t\t\t\t$script = \"/index.php\"; // $_SERVER['SCRIPT_NAME'];\n\t\t\t\t\t\t\tif (Settings::Get('system.froxlordirectlyviahostname') == 0) {\n\t\t\t\t\t\t\t\t$script = FileDir::makeCorrectFile(\"/\" . basename(__DIR__) . \"/\" . $script);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$activationlink = $protocol . '://' . $host . $port . $script . '?action=resetpwd&resetcode=' . $activationcode;\n\n\t\t\t\t\t\t\t$replace_arr = [\n\t\t\t\t\t\t\t\t'SALUTATION' => User::getCorrectUserSalutation($user),\n\t\t\t\t\t\t\t\t'NAME' => $user['name'],\n\t\t\t\t\t\t\t\t'FIRSTNAME' => $user['firstname'] ?? \"\",\n\t\t\t\t\t\t\t\t'COMPANY' => $user['company'] ?? \"\",\n\t\t\t\t\t\t\t\t'CUSTOMER_NO' => $user['customernumber'] ?? 0,\n\t\t\t\t\t\t\t\t'USERNAME' => $loginname,\n\t\t\t\t\t\t\t\t'LINK' => $activationlink\n\t\t\t\t\t\t\t];\n\n\t\t\t\t\t\t\t$def_language = ($user['def_language'] != '') ? $user['def_language'] : Settings::Get('panel.standardlanguage');\n\t\t\t\t\t\t\t$result_stmt = Database::prepare('SELECT `value` FROM `' . TABLE_PANEL_TEMPLATES . '`\n\t\t\t\t\t\t\t\tWHERE `adminid`= :adminid\n\t\t\t\t\t\t\t\tAND `language`= :lang\n\t\t\t\t\t\t\t\tAND `templategroup`=\\'mails\\'\n\t\t\t\t\t\t\t\tAND `varname`=\\'password_reset_subject\\'');\n\t\t\t\t\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t\t\t\t\t\"adminid\" => $user['adminid'],\n\t\t\t\t\t\t\t\t\"lang\" => $def_language\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\t\t\t\t$mail_subject = html_entity_decode(PhpHelper::replaceVariables((($result['value'] != '') ? $result['value'] : lng('mails.password_reset.subject')), $replace_arr));\n\n\t\t\t\t\t\t\t$result_stmt = Database::prepare('SELECT `value` FROM `' . TABLE_PANEL_TEMPLATES . '`\n\t\t\t\t\t\t\t\tWHERE `adminid`= :adminid\n\t\t\t\t\t\t\t\tAND `language`= :lang\n\t\t\t\t\t\t\t\tAND `templategroup`=\\'mails\\'\n\t\t\t\t\t\t\t\tAND `varname`=\\'password_reset_mailbody\\'');\n\t\t\t\t\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t\t\t\t\t\"adminid\" => $user['adminid'],\n\t\t\t\t\t\t\t\t\"lang\" => $def_language\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\t\t\t\t$mail_body = html_entity_decode(PhpHelper::replaceVariables((($result['value'] != '') ? $result['value'] : lng('mails.password_reset.mailbody')), $replace_arr));\n\n\t\t\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t$mail->Subject = $mail_subject;\n\t\t\t\t\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t\t\t\t\t$mail->MsgHTML(str_replace(\"\\n\", \"<br />\", $mail_body));\n\t\t\t\t\t\t\t\t$mail->AddAddress($user['email'], User::getCorrectUserSalutation($user));\n\t\t\t\t\t\t\t\t$mail->Send();\n\t\t\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\t\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t\t\t\t\t\t'loginname' => 'password_reset'\n\t\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t\t$rstlog->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, \"Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t\t\t\t\t'showmessage' => '4',\n\t\t\t\t\t\t\t\t\t'customermail' => $user['email']\n\t\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t\texit();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t$mail->ClearAddresses();\n\t\t\t\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t\t\t\t'showmessage' => '1'\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\texit();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t\t\t\t\t'loginname' => 'password_reset'\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t$rstlog->logAction(FroxlorLogger::USR_ACTION, LOG_WARNING, \"Unknown user requested to set a new password, but was not found in database!\");\n\t\t\t\t\t\t\t$message = lng('login.usernotfound');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tunset($user);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$message = lng('pwdreminder.notallowed');\n\t\t\t}\n\t\t}\n\t}\n\n\tUI::view('login/fpwd.html.twig', [\n\t\t'pagetitle' => lng('login.presend'),\n\t\t'formaction' => 'index.php?action=' . $action,\n\t\t'message' => $message,\n\t]);\n}\n\nif ($action == 'resetpwd') {\n\t$message = '';\n\n\t// Remove old activation codes\n\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_ACTIVATION . \"`\n\t\tWHERE creation < :oldest\");\n\tDatabase::pexecute($stmt, [\n\t\t\"oldest\" => time() - 86400\n\t]);\n\n\t$activationcode = Request::get('resetcode');\n\tif (!empty($activationcode) && strlen($activationcode) == 50) {\n\t\t// Check if activation code is valid\n\t\t$timestamp = substr($activationcode, 15, 10);\n\t\t$third = substr($activationcode, 25, 15);\n\t\t$check = substr($activationcode, 40, 10);\n\n\t\tif (substr(md5($third . $timestamp), 0, 10) == $check && $timestamp >= time() - 86400) {\n\t\t\tif (!empty($_POST)) {\n\t\t\t\t$stmt = Database::prepare(\"SELECT `userid`, `admin` FROM `\" . TABLE_PANEL_ACTIVATION . \"`\n\t\t\t\t\tWHERE `activationcode` = :activationcode\");\n\t\t\t\t$result = Database::pexecute_first($stmt, [\n\t\t\t\t\t\"activationcode\" => $activationcode\n\t\t\t\t]);\n\n\t\t\t\tif ($result !== false) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$new_password = Crypt::validatePassword(Request::post('new_password'), true);\n\t\t\t\t\t\t$new_password_confirm = Crypt::validatePassword(Request::post('new_password_confirm'), true);\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t$message = $e->getMessage();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (empty($message) && (empty($new_password) || $new_password != $new_password_confirm)) {\n\t\t\t\t\t\t$message = lng('error.newpasswordconfirmerror');\n\t\t\t\t\t}\n\n\t\t\t\t\tif (empty($message)) {\n\t\t\t\t\t\t// Update user password\n\t\t\t\t\t\tif ($result['admin'] == 1) {\n\t\t\t\t\t\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\t\t\t\t\tSET `password` = :newpassword\n\t\t\t\t\t\t\t\tWHERE `adminid` = :userid\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\t\t\t\t\tSET `password` = :newpassword\n\t\t\t\t\t\t\t\tWHERE `customerid` = :userid\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\t\t\"newpassword\" => Crypt::makeCryptPassword($new_password),\n\t\t\t\t\t\t\t\"userid\" => $result['userid']\n\t\t\t\t\t\t]);\n\n\t\t\t\t\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t\t\t\t'loginname' => 'password_reset'\n\t\t\t\t\t\t]);\n\t\t\t\t\t\t$rstlog->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"changed password using password reset.\");\n\n\t\t\t\t\t\t// Remove activation code from DB\n\t\t\t\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_ACTIVATION . \"`\n\t\t\t\t\t\t\tWHERE `activationcode` = :activationcode\n\t\t\t\t\t\t\tAND `userid` = :userid\");\n\t\t\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\t\t\"activationcode\" => $activationcode,\n\t\t\t\t\t\t\t\"userid\" => $result['userid']\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t\t\t\"showmessage\" => '6'\n\t\t\t\t\t\t]);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t\t\"showmessage\" => '7'\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tUI::view('login/rpwd.html.twig', [\n\t\t\t\t'pagetitle' => lng('pwdreminder.choosenew'),\n\t\t\t\t'formaction' => 'index.php?action=resetpwd&resetcode=' . $activationcode,\n\t\t\t\t'message' => $message,\n\t\t\t]);\n\t\t} else {\n\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\"showmessage\" => '7'\n\t\t\t]);\n\t\t}\n\t} else {\n\t\tResponse::redirectTo('index.php');\n\t}\n}\n\n// one-time link login\nif ($action == 'll') {\n\tif (!Froxlor::hasUpdates() && !Froxlor::hasDbUpdates()) {\n\t\t$loginname = Request::get('ln');\n\t\t$hash = Request::get('h');\n\t\tif ($loginname && $hash) {\n\t\t\t$sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_LOGINLINKS . \"`\n\t\t\t\tWHERE `loginname` = :loginname AND `hash` = :hash\n\t\t\t\");\n\t\t\ttry {\n\t\t\t\t$entry = Database::pexecute_first($sel_stmt, ['loginname' => $loginname, 'hash' => $hash]);\n\t\t\t} catch (Exception $e) {\n\t\t\t\t$entry = false;\n\t\t\t}\n\t\t\tif ($entry) {\n\t\t\t\t// delete entry\n\t\t\t\t$del_stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_LOGINLINKS . \"` WHERE `loginname` = :loginname AND `hash` = :hash\");\n\t\t\t\tDatabase::pexecute($del_stmt, ['loginname' => $loginname, 'hash' => $hash]);\n\t\t\t\tif (time() <= $entry['valid_until']) {\n\t\t\t\t\t$valid = true;\n\t\t\t\t\t// validate source ip if specified\n\t\t\t\t\tif (!empty($entry['allowed_from'])) {\n\t\t\t\t\t\t$valid = false;\n\t\t\t\t\t\t$ip_list = explode(\",\", $entry['allowed_from']);\n\t\t\t\t\t\tif (FroxlorRPC::validateAllowedFrom($ip_list, $_SERVER['REMOTE_ADDR'])) {\n\t\t\t\t\t\t\t$valid = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ($valid) {\n\t\t\t\t\t\t// login user / select only non-deactivated (in case the user got deactivated after generating the link)\n\t\t\t\t\t\t$userinfo_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `loginname`= :loginname AND `deactivated` = 0\");\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t$userinfo = Database::pexecute_first($userinfo_stmt, [\n\t\t\t\t\t\t\t\t\"loginname\" => $loginname\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t\t$userinfo = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ($userinfo) {\n\t\t\t\t\t\t\t$userinfo['userid'] = $userinfo['customerid'];\n\t\t\t\t\t\t\t$userinfo['adminsession'] = 0;\n\t\t\t\t\t\t\tfinishLogin($userinfo);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tResponse::redirectTo('index.php');\n}\n\nfunction finishLogin($userinfo)\n{\n\tif (isset($userinfo['userid']) && $userinfo['userid'] != '') {\n\t\tsession_regenerate_id(true);\n\t\tCurrentUser::setData($userinfo);\n\n\t\t$language = $userinfo['def_language'] ?? Settings::Get('panel.standardlanguage');\n\t\tCurrentUser::setField('language', $language);\n\n\t\tif (isset($userinfo['theme']) && $userinfo['theme'] != '') {\n\t\t\t$theme = $userinfo['theme'];\n\t\t} else {\n\t\t\t$theme = Settings::Get('panel.default_theme');\n\t\t}\n\t\tCurrentUser::setField('theme', $theme);\n\n\t\t$qryparams = [];\n\t\tif (!empty($_SESSION['lastqrystr'])) {\n\t\t\tparse_str(urldecode($_SESSION['lastqrystr']), $qryparams);\n\t\t\tunset($_SESSION['lastqrystr']);\n\t\t}\n\n\t\tif ($userinfo['adminsession'] == '1') {\n\t\t\tif (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {\n\t\t\t\tResponse::redirectTo('admin_updates.php?page=overview');\n\t\t\t} else {\n\t\t\t\tif (!empty($_SESSION['lastscript'])) {\n\t\t\t\t\t$lastscript = $_SESSION['lastscript'];\n\t\t\t\t\tunset($_SESSION['lastscript']);\n\t\t\t\t\tif (preg_match(\"/customer\\_/\", $lastscript) === 1) {\n\t\t\t\t\t\tResponse::redirectTo('admin_customers.php', [\n\t\t\t\t\t\t\t\"page\" => \"customers\"\n\t\t\t\t\t\t]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tResponse::redirectTo($lastscript, $qryparams);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tResponse::redirectTo('admin_index.php', $qryparams);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (!empty($_SESSION['lastscript'])) {\n\t\t\t\t$lastscript = $_SESSION['lastscript'];\n\t\t\t\tunset($_SESSION['lastscript']);\n\t\t\t\tResponse::redirectTo($lastscript, $qryparams);\n\t\t\t} else {\n\t\t\t\tResponse::redirectTo('customer_index.php', $qryparams);\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "install/froxlor.sql.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn <<<'FROXLORSQL'\nDROP TABLE IF EXISTS `ftp_groups`;\nCREATE TABLE `ftp_groups` (\n  `id` int(20) NOT NULL auto_increment,\n  `groupname` varchar(60) NOT NULL default '',\n  `gid` int(5) NOT NULL default '0',\n  `members` longtext NOT NULL,\n  `customerid` int(11) NOT NULL default '0',\n  PRIMARY KEY  (`id`),\n  UNIQUE KEY `groupname` (`groupname`),\n  KEY `customerid` (`customerid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\nDROP TABLE IF EXISTS `ftp_users`;\nCREATE TABLE `ftp_users` (\n  `id` int(20) NOT NULL auto_increment,\n  `username` varchar(255) NOT NULL,\n  `uid` int(5) NOT NULL default '0',\n  `gid` int(5) NOT NULL default '0',\n  `password` varchar(255) NOT NULL,\n  `homedir` varchar(255) NOT NULL default '',\n  `shell` varchar(255) NOT NULL default '/bin/false',\n  `login_enabled` enum('N','Y') NOT NULL default 'N',\n  `login_count` int(15) NOT NULL default '0',\n  `last_login` datetime default NULL,\n  `up_count` int(15) NOT NULL default '0',\n  `up_bytes` bigint(30) NOT NULL default '0',\n  `down_count` int(15) NOT NULL default '0',\n  `down_bytes` bigint(30) NOT NULL default '0',\n  `customerid` int(11) NOT NULL default '0',\n  `description` varchar(255) NOT NULL DEFAULT '',\n  PRIMARY KEY  (`id`),\n  UNIQUE KEY `username` (`username`),\n  KEY `customerid` (`customerid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `mail_users`;\nCREATE TABLE `mail_users` (\n  `id` int(11) NOT NULL auto_increment,\n  `email` varchar(255) NOT NULL default '',\n  `username` varchar(255) NOT NULL default '',\n  `password` varchar(255) NOT NULL default '',\n  `password_enc` varchar(255) NOT NULL default '',\n  `uid` int(11) NOT NULL default '0',\n  `gid` int(11) NOT NULL default '0',\n  `homedir` varchar(255) NOT NULL default '',\n  `maildir` varchar(255) NOT NULL default '',\n  `postfix` enum('Y','N') NOT NULL default 'Y',\n  `domainid` int(11) NOT NULL default '0',\n  `customerid` int(11) NOT NULL default '0',\n  `quota` bigint(13) NOT NULL default '0',\n  `pop3` tinyint(1) NOT NULL default '1',\n  `imap` tinyint(1) NOT NULL default '1',\n  `mboxsize` bigint(30) NOT NULL default '0',\n  PRIMARY KEY  (`id`),\n  UNIQUE KEY `email` (`email`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `mail_virtual`;\nCREATE TABLE `mail_virtual` (\n  `id` int(11) NOT NULL auto_increment,\n  `email` varchar(255) NOT NULL default '',\n  `email_full` varchar(255) NOT NULL default '',\n  `destination` text,\n  `domainid` int(11) NOT NULL default '0',\n  `customerid` int(11) NOT NULL default '0',\n  `popaccountid` int(11) NOT NULL default '0',\n  `iscatchall` tinyint(1) unsigned NOT NULL default '0',\n  `description` varchar(255) NOT NULL DEFAULT '',\n  `spam_tag_level` float(4,1) NOT NULL DEFAULT 7.0,\n  `rewrite_subject` tinyint(1) NOT NULL default '1',\n  `spam_kill_level` float(4,1) NOT NULL DEFAULT 14.0,\n  `bypass_spam` tinyint(1) NOT NULL default '0',\n  `policy_greylist` tinyint(1) NOT NULL default '1',\n  PRIMARY KEY  (`id`),\n  KEY `email` (`email`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\nDROP TABLE IF EXISTS `mail_sender_aliases`;\nCREATE TABLE `mail_sender_aliases` (\n  `id` int(11) NOT NULL auto_increment,\n  `email` varchar(255) NOT NULL,\n  `allowed_sender` varchar(255) NOT NULL,\n  PRIMARY KEY  (`id`),\n  UNIQUE KEY `email_sender` (`email`, `allowed_sender`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\nDROP TABLE IF EXISTS `panel_activation`;\nCREATE TABLE `panel_activation` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `userid` int(11) unsigned NOT NULL default '0',\n  `admin` tinyint(1) unsigned NOT NULL default '0',\n  `creation` int(11) unsigned NOT NULL default '0',\n  `activationcode` varchar(50) default NULL,\n  PRIMARY KEY (id)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_admins`;\nCREATE TABLE `panel_admins` (\n  `adminid` int(11) unsigned NOT NULL auto_increment,\n  `loginname` varchar(50) NOT NULL,\n  `password` varchar(255) NOT NULL,\n  `name` varchar(255) NOT NULL default '',\n  `email` varchar(255) NOT NULL default '',\n  `def_language` varchar(100) NOT NULL default '',\n  `ip` varchar(500) NOT NULL default '-1',\n  `customers` int(15) NOT NULL default '0',\n  `customers_used` int(15) NOT NULL default '0',\n  `customers_see_all` tinyint(1) NOT NULL default '0',\n  `domains` int(15) NOT NULL default '0',\n  `domains_used` int(15) NOT NULL default '0',\n  `caneditphpsettings` tinyint(1) NOT NULL default '0',\n  `change_serversettings` tinyint(1) NOT NULL default '0',\n  `diskspace` int(15) NOT NULL default '0',\n  `diskspace_used` int(15) NOT NULL default '0',\n  `mysqls` int(15) NOT NULL default '0',\n  `mysqls_used` int(15) NOT NULL default '0',\n  `emails` int(15) NOT NULL default '0',\n  `emails_used` int(15) NOT NULL default '0',\n  `email_accounts` int(15) NOT NULL default '0',\n  `email_accounts_used` int(15) NOT NULL default '0',\n  `email_forwarders` int(15) NOT NULL default '0',\n  `email_forwarders_used` int(15) NOT NULL default '0',\n  `email_quota` bigint(13) NOT NULL default '0',\n  `email_quota_used` bigint(13) NOT NULL default '0',\n  `ftps` int(15) NOT NULL default '0',\n  `ftps_used` int(15) NOT NULL default '0',\n  `subdomains` int(15) NOT NULL default '0',\n  `subdomains_used` int(15) NOT NULL default '0',\n  `traffic` bigint(30) NOT NULL default '0',\n  `traffic_used` bigint(30) NOT NULL default '0',\n  `deactivated` tinyint(1) NOT NULL default '0',\n  `lastlogin_succ` int(11) unsigned NOT NULL default '0',\n  `lastlogin_fail` int(11) unsigned NOT NULL default '0',\n  `loginfail_count` int(11) unsigned NOT NULL default '0',\n  `reportsent` tinyint(4) unsigned NOT NULL default '0',\n  `theme` varchar(50) NOT NULL default 'Froxlor',\n  `custom_notes` text,\n  `custom_notes_show` tinyint(1) NOT NULL default '0',\n  `type_2fa` tinyint(1) NOT NULL default '0',\n  `data_2fa` varchar(25) NOT NULL default '',\n  `api_allowed` tinyint(1) NOT NULL default '1',\n  `gui_access` tinyint(1) NOT NULL default '1',\n   PRIMARY KEY  (`adminid`),\n   UNIQUE KEY `loginname` (`loginname`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;\n\n\nDROP TABLE IF EXISTS `panel_customers`;\nCREATE TABLE `panel_customers` (\n  `customerid` int(11) unsigned NOT NULL auto_increment,\n  `loginname` varchar(50) NOT NULL,\n  `password` varchar(255) NOT NULL default '',\n  `adminid` int(11) unsigned NOT NULL default '0',\n  `name` varchar(255) NOT NULL default '',\n  `firstname` varchar(255) NOT NULL default '',\n  `gender` int(1) NOT NULL DEFAULT '0',\n  `company` varchar(255) NOT NULL default '',\n  `street` varchar(255) NOT NULL default '',\n  `zipcode` varchar(25) NOT NULL default '',\n  `city` varchar(255) NOT NULL default '',\n  `phone` varchar(50) NOT NULL default '',\n  `fax` varchar(50) NOT NULL default '',\n  `email` varchar(255) NOT NULL default '',\n  `customernumber` varchar(100) NOT NULL default '',\n  `def_language` varchar(100) NOT NULL default '',\n  `diskspace` bigint(30) NOT NULL default '0',\n  `diskspace_used` bigint(30) NOT NULL default '0',\n  `mysqls` int(15) NOT NULL default '0',\n  `mysqls_used` int(15) NOT NULL default '0',\n  `emails` int(15) NOT NULL default '0',\n  `emails_used` int(15) NOT NULL default '0',\n  `email_accounts` int(15) NOT NULL default '0',\n  `email_accounts_used` int(15) NOT NULL default '0',\n  `email_forwarders` int(15) NOT NULL default '0',\n  `email_forwarders_used` int(15) NOT NULL default '0',\n  `email_quota` bigint(13) NOT NULL default '0',\n  `email_quota_used` bigint(13) NOT NULL default '0',\n  `ftps` int(15) NOT NULL default '0',\n  `ftps_used` int(15) NOT NULL default '0',\n  `subdomains` int(15) NOT NULL default '0',\n  `subdomains_used` int(15) NOT NULL default '0',\n  `traffic` bigint(30) NOT NULL default '0',\n  `traffic_used` bigint(30) NOT NULL default '0',\n  `documentroot` varchar(255) NOT NULL default '',\n  `standardsubdomain` int(11) NOT NULL default '0',\n  `guid` int(5) NOT NULL default '0',\n  `ftp_lastaccountnumber` int(11) NOT NULL default '0',\n  `mysql_lastaccountnumber` int(11) NOT NULL default '0',\n  `deactivated` tinyint(1) NOT NULL default '0',\n  `phpenabled` tinyint(1) NOT NULL default '1',\n  `lastlogin_succ` int(11) unsigned NOT NULL default '0',\n  `lastlogin_fail` int(11) unsigned NOT NULL default '0',\n  `loginfail_count` int(11) unsigned NOT NULL default '0',\n  `reportsent` tinyint(4) unsigned NOT NULL default '0',\n  `pop3` tinyint(1) NOT NULL default '1',\n  `imap` tinyint(1) NOT NULL default '1',\n  `perlenabled` tinyint(1) NOT NULL default '0',\n  `dnsenabled` tinyint(1) NOT NULL default '0',\n  `theme` varchar(50) NOT NULL default 'Froxlor',\n  `custom_notes` text,\n  `custom_notes_show` tinyint(1) NOT NULL default '0',\n  `lepublickey` mediumtext default NULL,\n  `leprivatekey` mediumtext default NULL,\n  `leregistered` tinyint(1) NOT NULL default '0',\n  `allowed_phpconfigs` text NOT NULL,\n  `type_2fa` tinyint(1) NOT NULL default '0',\n  `data_2fa` varchar(25) NOT NULL default '',\n  `api_allowed` tinyint(1) NOT NULL default '1',\n  `shell_allowed` tinyint(1) NOT NULL default '0',\n  `logviewenabled` tinyint(1) NOT NULL default '0',\n  `allowed_mysqlserver` text NOT NULL,\n  `gui_access` tinyint(1) NOT NULL default '1',\n   PRIMARY KEY  (`customerid`),\n   UNIQUE KEY `loginname` (`loginname`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;\n\n\nDROP TABLE IF EXISTS `panel_databases`;\nCREATE TABLE `panel_databases` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `customerid` int(11) NOT NULL default '0',\n  `databasename` varchar(255) NOT NULL default '',\n  `description` varchar(255) NOT NULL default '',\n  `dbserver` int(11) unsigned NOT NULL default '0',\n  PRIMARY KEY  (`id`),\n  KEY `customerid` (`customerid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_domains`;\nCREATE TABLE `panel_domains` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `domain` varchar(255) NOT NULL,\n  `domain_ace` varchar(255) NOT NULL default '',\n  `adminid` int(11) unsigned NOT NULL default '0',\n  `customerid` int(11) unsigned NOT NULL default '0',\n  `aliasdomain` int(11) unsigned NULL,\n  `documentroot` varchar(255) NOT NULL default '',\n  `isbinddomain` tinyint(1) NOT NULL default '0',\n  `isemaildomain` tinyint(1) NOT NULL default '0',\n  `email_only` tinyint(1) NOT NULL default '0',\n  `iswildcarddomain` tinyint(1) NOT NULL default '1',\n  `subcanemaildomain` tinyint(1) NOT NULL default '0',\n  `caneditdomain` tinyint(1) NOT NULL default '1',\n  `zonefile` varchar(255) NOT NULL default '',\n  `dkim` tinyint(1) NOT NULL default '0',\n  `dkim_id` int(11) unsigned NOT NULL default '0',\n  `dkim_privkey` text,\n  `dkim_pubkey` text,\n  `wwwserveralias` tinyint(1) NOT NULL default '1',\n  `parentdomainid` int(11) NOT NULL default '0',\n  `phpenabled` tinyint(1) NOT NULL default '0',\n  `openbasedir` tinyint(1) NOT NULL default '0',\n  `openbasedir_path` tinyint(1) NOT NULL default '0',\n  `speciallogfile` tinyint(1) NOT NULL default '0',\n  `ssl_redirect` tinyint(4) NOT NULL default '0',\n  `specialsettings` text,\n  `ssl_specialsettings` text,\n  `include_specialsettings` tinyint(1) NOT NULL default '0',\n  `deactivated` tinyint(1) NOT NULL default '0',\n  `bindserial` varchar(10) NOT NULL default '2000010100',\n  `add_date` int( 11 ) NOT NULL default '0',\n  `registration_date` date DEFAULT NULL,\n  `termination_date` date DEFAULT NULL,\n  `phpsettingid` INT( 11 ) UNSIGNED NOT NULL DEFAULT '1',\n  `mod_fcgid_starter` int(4) default '-1',\n  `mod_fcgid_maxrequests` int(4) default '-1',\n  `letsencrypt` tinyint(1) NOT NULL default '0',\n  `hsts` varchar(10) NOT NULL default '0',\n  `hsts_sub` tinyint(1) NOT NULL default '0',\n  `hsts_preload` tinyint(1) NOT NULL default '0',\n  `ocsp_stapling` tinyint(1) DEFAULT '0',\n  `http2` tinyint(1) DEFAULT '0',\n  `http3` tinyint(1) DEFAULT '0',\n  `notryfiles` tinyint(1) DEFAULT '0',\n  `writeaccesslog` tinyint(1) DEFAULT '1',\n  `writeerrorlog` tinyint(1) DEFAULT '1',\n  `override_tls` tinyint(1) DEFAULT '0',\n  `ssl_protocols` varchar(255) NOT NULL DEFAULT '',\n  `ssl_cipher_list` varchar(500) NOT NULL DEFAULT '',\n  `tlsv13_cipher_list` varchar(500) NOT NULL DEFAULT '',\n  `ssl_enabled` tinyint(1) DEFAULT '1',\n  `ssl_honorcipherorder` tinyint(1) DEFAULT '0',\n  `ssl_sessiontickets` tinyint(1) DEFAULT '1',\n  `description` varchar(255) NOT NULL DEFAULT '',\n  PRIMARY KEY  (`id`),\n  KEY `customerid` (`customerid`),\n  KEY `parentdomain` (`parentdomainid`),\n  KEY `domain` (`domain`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;\n\n\nDROP TABLE IF EXISTS `panel_ipsandports`;\nCREATE TABLE `panel_ipsandports` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `ip` varchar(39) NOT NULL,\n  `port` int(5) NOT NULL default '80',\n  `listen_statement` tinyint(1) NOT NULL default '0',\n  `namevirtualhost_statement` tinyint(1) NOT NULL default '0',\n  `vhostcontainer` tinyint(1) NOT NULL default '0',\n  `vhostcontainer_servername_statement` tinyint(1) NOT NULL default '0',\n  `specialsettings` text,\n  `ssl` tinyint(4) NOT NULL default '0',\n  `ssl_cert_file` varchar(255) NOT NULL default '',\n  `ssl_key_file` varchar(255) NOT NULL default '',\n  `ssl_ca_file` varchar(255) NOT NULL default '',\n  `default_vhostconf_domain` text,\n  `ssl_cert_chainfile` varchar(255) NOT NULL default '',\n  `docroot` varchar(255) NOT NULL default '',\n  `ssl_specialsettings` text,\n  `include_specialsettings` tinyint(1) NOT NULL default '0',\n  `ssl_default_vhostconf_domain` text,\n  `include_default_vhostconf_domain` tinyint(1) NOT NULL default '0',\n  PRIMARY KEY  (`id`),\n  UNIQUE KEY `ip_port` (`ip`,`port`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_htaccess`;\nCREATE TABLE `panel_htaccess` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `customerid` int(11) unsigned NOT NULL default '0',\n  `path` varchar(255) NOT NULL default '',\n  `options_indexes` tinyint(1) NOT NULL default '0',\n  `error404path` varchar(255) NOT NULL default '',\n  `error403path` varchar(255) NOT NULL default '',\n  `error500path` varchar(255) NOT NULL default '',\n  `error401path` varchar(255) NOT NULL default '',\n  `options_cgi` tinyint(1) NOT NULL default '0',\n  PRIMARY KEY  (`id`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_htpasswds`;\nCREATE TABLE `panel_htpasswds` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `customerid` int(11) unsigned NOT NULL default '0',\n  `path` varchar(255) NOT NULL default '',\n  `username` varchar(255) NOT NULL default '',\n  `password` varchar(255) NOT NULL default '',\n  `authname` varchar(255) NOT NULL default 'Restricted Area',\n  PRIMARY KEY  (`id`),\n  KEY `customerid` (`customerid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_settings`;\nCREATE TABLE `panel_settings` (\n  `settingid` int(11) unsigned NOT NULL auto_increment,\n  `settinggroup` varchar(255) NOT NULL default '',\n  `varname` varchar(255) NOT NULL default '',\n  `value` text NOT NULL,\n  PRIMARY KEY  (`settingid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\nINSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES\n\t('catchall', 'catchall_enabled', '1'),\n\t('session', 'allow_multiple_login', '0'),\n\t('session', 'sessiontimeout', '600'),\n\t('customer', 'accountprefix', 'web'),\n\t('customer', 'ftpprefix', 'ftp'),\n\t('customer', 'mysqlprefix', 'sql'),\n\t('customer', 'ftpatdomain', '0'),\n\t('customer', 'show_news_feed', '0'),\n\t('customer', 'news_feed_url', ''),\n\t('logger', 'enabled', '1'),\n\t('logger', 'log_cron', '0'),\n\t('logger', 'logfile', ''),\n\t('logger', 'logtypes', 'syslog,mysql'),\n\t('logger', 'severity', '1'),\n\t('antispam', 'activated', '0'),\n\t('antispam', 'config_file', '/etc/rspamd/local.d/froxlor_settings.conf'),\n\t('antispam', 'reload_command', 'service rspamd restart'),\n\t('antispam', 'dkim_keylength', '1024'),\n\t('antispam', 'default_bypass_spam', '2'),\n\t('antispam', 'default_spam_rewrite_subject', '1'),\n\t('antispam', 'default_policy_greylist', '1'),\n\t('admin', 'show_news_feed', '0'),\n\t('admin', 'show_version_login', '0'),\n\t('admin', 'show_version_footer', '0'),\n\t('caa', 'caa_entry', ''),\n\t('spf', 'use_spf', '0'),\n\t('spf', 'spf_entry', 'v=spf1 a mx -all'),\n\t('dmarc', 'use_dmarc', '0'),\n\t('dmarc', 'dmarc_entry', 'v=DMARC1; p=none;'),\n\t('defaultwebsrverrhandler', 'enabled', '0'),\n\t('defaultwebsrverrhandler', 'err401', ''),\n\t('defaultwebsrverrhandler', 'err403', ''),\n\t('defaultwebsrverrhandler', 'err404', ''),\n\t('defaultwebsrverrhandler', 'err500', ''),\n\t('customredirect', 'enabled', '1'),\n\t('customredirect', 'default', '1'),\n\t('perl', 'suexecworkaround', '0'),\n\t('perl', 'suexecpath', '/var/www/cgi-bin/'),\n\t('login', 'domain_login', '0'),\n\t('login', 'maxloginattempts', '3'),\n\t('login', 'deactivatetime', '900'),\n\t('phpfpm', 'enabled', '0'),\n\t('phpfpm', 'tmpdir', '/var/customers/tmp/'),\n\t('phpfpm', 'peardir', '/usr/share/php/:/usr/share/php5/'),\n\t('phpfpm', 'envpath', '/usr/local/bin:/usr/bin:/bin'),\n\t('phpfpm', 'enabled_ownvhost', '0'),\n\t('phpfpm', 'vhost_httpuser', 'froxlorlocal'),\n\t('phpfpm', 'vhost_httpgroup', 'froxlorlocal'),\n\t('phpfpm', 'aliasconfigdir', '/var/www/php-fpm/'),\n\t('phpfpm', 'defaultini', '1'),\n\t('phpfpm', 'vhost_defaultini', '2'),\n\t('phpfpm', 'fastcgi_ipcdir', '/var/lib/apache2/fastcgi/'),\n\t('phpfpm', 'use_mod_proxy', '1'),\n\t('phpfpm', 'ini_flags', 'asp_tags\ndisplay_errors\ndisplay_startup_errors\nhtml_errors\nlog_errors\nmagic_quotes_gpc\nmagic_quotes_runtime\nmagic_quotes_sybase\nmail.add_x_header\nsession.cookie_secure\nsession.use_cookies\nshort_open_tag\ntrack_errors\nxmlrpc_errors\nsuhosin.simulation\nsuhosin.session.encrypt\nsuhosin.session.cryptua\nsuhosin.session.cryptdocroot\nsuhosin.cookie.encrypt\nsuhosin.cookie.cryptua\nsuhosin.cookie.cryptdocroot\nsuhosin.executor.disable_eval\nmbstring.func_overload'),\n\t('phpfpm', 'ini_values', 'auto_append_file\nauto_prepend_file\ndate.timezone\ndefault_charset\nerror_reporting\ninclude_path\nlog_errors_max_len\nmail.log\nmax_execution_time\nsession.cookie_domain\nsession.cookie_lifetime\nsession.cookie_path\nsession.name\nsession.serialize_handler\nupload_max_filesize\nxmlrpc_error_number\nsession.auto_start\nalways_populate_raw_post_data\nsuhosin.session.cryptkey\nsuhosin.session.cryptraddr\nsuhosin.session.checkraddr\nsuhosin.cookie.cryptkey\nsuhosin.cookie.plainlist\nsuhosin.cookie.cryptraddr\nsuhosin.cookie.checkraddr\nsuhosin.executor.func.blacklist\nsuhosin.executor.eval.whitelist'),\n\t('phpfpm', 'ini_admin_flags', 'allow_call_time_pass_reference\nallow_url_fopen\nallow_url_include\nauto_detect_line_endings\ncgi.fix_pathinfo\ncgi.force_redirect\nenable_dl\nexpose_php\nfile_uploads\nignore_repeated_errors\nignore_repeated_source\nlog_errors\nregister_argc_argv\nreport_memleaks\nopcache.enable\nopcache.consistency_checks\nopcache.dups_fix\nopcache.load_comments\nopcache.revalidate_path\nopcache.save_comments\nopcache.use_cwd\nopcache.fast_shutdown'),\n\t('phpfpm', 'ini_admin_values', 'cgi.redirect_status_env\ndisable_classes\ndisable_functions\nerror_log\ngpc_order\nmax_input_time\nmax_input_vars\nmemory_limit\nopen_basedir\noutput_buffering\npost_max_size\nprecision\nsendmail_path\nsession.gc_divisor\nsession.gc_probability\nvariables_order\nopcache.log_verbosity_level\nopcache.restrict_api\nopcache.revalidate_freq\nopcache.max_accelerated_files\nopcache.memory_consumption\nopcache.interned_strings_buffer\nopcache.validate_timestamps'),\n\t('nginx', 'fastcgiparams', '/etc/nginx/fastcgi_params'),\n\t('system', 'lastaccountnumber', '0'),\n\t('system', 'lastguid', '9999'),\n\t('system', 'documentroot_prefix', '/var/customers/webs/'),\n\t('system', 'logfiles_directory', '/var/customers/logs/'),\n\t('system', 'ipaddress', 'SERVERIP'),\n\t('system', 'apachereload_command', 'service apache2 reload'),\n\t('system', 'last_traffic_run', '000000'),\n\t('system', 'vmail_uid', '2000'),\n\t('system', 'vmail_gid', '2000'),\n\t('system', 'vmail_homedir', '/var/customers/mail/'),\n\t('system', 'vmail_maildirname', 'Maildir'),\n\t('system', 'bind_enable', '0'),\n\t('system', 'bindconf_directory', '/etc/bind/'),\n\t('system', 'bindreload_command', 'service bind9 reload'),\n\t('system', 'hostname', 'SERVERNAME'),\n\t('system', 'mysql_access_host', 'localhost'),\n\t('system', 'lastcronrun', ''),\n\t('system', 'defaultip', '1'),\n\t('system', 'defaultsslip', ''),\n\t('system', 'phpappendopenbasedir', '/tmp/'),\n\t('system', 'deactivateddocroot', '/var/www/html/froxlor/templates/misc/deactivated/'),\n\t('system', 'mailpwcleartext', '0'),\n\t('system', 'last_tasks_run', '000000'),\n\t('system', 'nameservers', ''),\n\t('system', 'mxservers', ''),\n\t('system', 'mod_fcgid', '0'),\n\t('system', 'apacheconf_vhost', '/etc/apache2/sites-enabled/'),\n\t('system', 'apacheconf_diroptions', '/etc/apache2/sites-enabled/'),\n\t('system', 'apacheconf_htpasswddir', '/etc/apache2/froxlor-htpasswd/'),\n\t('system', 'webalizer_quiet', '2'),\n\t('system', 'last_archive_run', '000000'),\n\t('system', 'mod_fcgid_configdir', '/var/www/php-fcgi-scripts'),\n\t('system', 'mod_fcgid_tmpdir', '/var/customers/tmp'),\n\t('system', 'ssl_cert_file', '/etc/ssl/froxlor_selfsigned.pem'),\n\t('system', 'use_ssl', '0'),\n\t('system', 'default_vhostconf', ''),\n\t('system', 'default_sslvhostconf', ''),\n\t('system', 'mail_quota_enabled', '0'),\n\t('system', 'mail_quota', '100'),\n\t('system', 'httpuser', 'www-data'),\n\t('system', 'httpgroup', 'www-data'),\n\t('system', 'webserver', 'apache2'),\n\t('system', 'mod_fcgid_wrapper', '1'),\n\t('system', 'mod_fcgid_starter', '0'),\n\t('system', 'mod_fcgid_peardir', '/usr/share/php/:/usr/share/php5/'),\n\t('system', 'mod_fcgid_maxrequests', '250'),\n\t('system', 'ssl_key_file','/etc/ssl/froxlor_selfsigned.key'),\n\t('system', 'ssl_ca_file', ''),\n\t('system', 'debug_cron', '0'),\n\t('system', 'store_index_file_subs', '1'),\n\t('system', 'stdsubdomain', ''),\n\t('system', 'awstats_path', '/usr/share/awstats/tools/'),\n\t('system', 'awstats_conf', '/etc/awstats/'),\n\t('system', 'awstats_logformat', '1'),\n\t('system', 'defaultttl', '604800'),\n\t('system', 'mod_fcgid_defaultini', '1'),\n\t('system', 'ftpserver', 'proftpd'),\n\t('system', 'dns_createmailentry', '0'),\n\t('system', 'dns_createcaaentry', '1'),\n\t('system', 'froxlordirectlyviahostname', '1'),\n\t('system', 'report_enable', '1'),\n\t('system', 'report_webmax', '90'),\n\t('system', 'report_trafficmax', '90'),\n\t('system', 'validate_domain', '1'),\n\t('system', 'diskquota_enabled', '0'),\n\t('system', 'diskquota_repquota_path', '/usr/sbin/repquota'),\n\t('system', 'diskquota_quotatool_path', '/usr/bin/quotatool'),\n\t('system', 'diskquota_customer_partition', '/dev/root'),\n\t('system', 'mod_fcgid_idle_timeout', '30'),\n\t('system', 'mod_fcgid_ownvhost', '0'),\n\t('system', 'mod_fcgid_httpuser', 'froxlorlocal'),\n\t('system', 'mod_fcgid_httpgroup', 'froxlorlocal'),\n\t('system', 'awstats_awstatspath', '/usr/lib/cgi-bin/'),\n\t('system', 'mod_fcgid_defaultini_ownvhost', '2'),\n\t('system', 'awstats_icons', '/usr/share/awstats/icon/'),\n\t('system', 'ssl_cert_chainfile', ''),\n\t('system', 'ssl_cipher_list', 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305'),\n\t('system', 'nginx_php_backend', '127.0.0.1:8888'),\n\t('system', 'http2_support', '0'),\n\t('system', 'http3_support', '0'),\n\t('system', 'perl_server', 'unix:/var/run/nginx/cgiwrap-dispatch.sock'),\n\t('system', 'phpreload_command', ''),\n\t('system', 'apache24', '1'),\n\t('system', 'apache24_ocsp_cache_path', 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)'),\n\t('system', 'documentroot_use_default_value', '0'),\n\t('system', 'passwordcryptfunc', '2y'),\n\t('system', 'axfrservers', ''),\n\t('system', 'powerdns_mode', 'Native'),\n\t('system', 'customer_ssl_path', '/etc/ssl/froxlor-custom/'),\n\t('system', 'allow_error_report_admin', '1'),\n\t('system', 'allow_error_report_customer', '0'),\n\t('system', 'mdalog', '/var/log/mail.log'),\n\t('system', 'mtalog', '/var/log/mail.log'),\n\t('system', 'mdaserver', 'dovecot'),\n\t('system', 'mtaserver', 'postfix'),\n\t('system', 'mailtraffic_enabled', '1'),\n\t('system', 'cronconfig', '/etc/cron.d/froxlor'),\n\t('system', 'crondreload', 'service cron reload'),\n\t('system', 'croncmdline', '/usr/bin/nice -n 5 /usr/bin/php -q'),\n\t('system', 'cron_allowautoupdate', '0'),\n\t('system', 'dns_createhostnameentry', '0'),\n\t('system', 'send_cron_errors', '0'),\n\t('system', 'apacheitksupport', '0'),\n\t('system', 'leprivatekey', 'unset'),\n\t('system', 'lepublickey', 'unset'),\n\t('system', 'letsencryptca', 'letsencrypt'),\n\t('system', 'letsencryptchallengepath', '/var/www/html/froxlor'),\n\t('system', 'letsencryptkeysize', '4096'),\n\t('system', 'letsencryptreuseold', 0),\n\t('system', 'leenabled', '0'),\n\t('system', 'leapiversion', '2'),\n\t('system', 'exportenabled', '0'),\n\t('system', 'dnsenabled', '0'),\n\t('system', 'dns_server', 'Bind'),\n\t('system', 'apacheglobaldiropt', ''),\n\t('system', 'allow_customer_shell', '0'),\n\t('system', 'available_shells', ''),\n\t('system', 'le_froxlor_enabled', '0'),\n\t('system', 'le_froxlor_redirect', '0'),\n\t('system', 'le_renew_hook', 'systemctl restart postfix dovecot proftpd'),\n\t('system', 'le_renew_services', ''),\n\t('system', 'letsencryptacmeconf', '/etc/apache2/conf-enabled/acme.conf'),\n\t('system', 'mail_use_smtp', '0'),\n\t('system', 'mail_smtp_host', 'localhost'),\n\t('system', 'mail_smtp_port', '25'),\n\t('system', 'mail_smtp_usetls', '1'),\n\t('system', 'mail_smtp_auth', '1'),\n\t('system', 'mail_smtp_user', ''),\n\t('system', 'mail_smtp_passwd', ''),\n\t('system', 'hsts_maxage', '10368000'),\n\t('system', 'hsts_incsub', '0'),\n\t('system', 'hsts_preload', '0'),\n\t('system', 'leregistered', '0'),\n\t('system', 'leaccount', ''),\n\t('system', 'nssextrausers', '1'),\n\t('system', 'le_domain_dnscheck', '1'),\n\t('system', 'le_domain_dnscheck_resolver', '1.1.1.1'),\n\t('system', 'ssl_protocols', 'TLSv1.2'),\n\t('system', 'tlsv13_cipher_list', ''),\n\t('system', 'honorcipherorder', '0'),\n\t('system', 'sessiontickets', '1'),\n\t('system', 'sessionticketsenabled', '1'),\n\t('system', 'logfiles_format', ''),\n\t('system', 'logfiles_type', '1'),\n\t('system', 'logfiles_piped', '0'),\n\t('system', 'logfiles_script', ''),\n\t('system', 'dhparams_file', ''),\n\t('system', 'errorlog_level', 'warn'),\n\t('system', 'leecc', '0'),\n\t('system', 'froxloraliases', ''),\n\t('system', 'apply_specialsettings_default', '1'),\n\t('system', 'apply_phpconfigs_default', '1'),\n\t('system', 'hide_incompatible_settings', '1'),\n\t('system', 'include_default_vhostconf', '0'),\n\t('system', 'soaemail', ''),\n\t('system', 'domaindefaultalias', '0'),\n\t('system', 'createstdsubdom_default', '1'),\n\t('system', 'froxlorusergroup', ''),\n\t('system', 'froxlorusergroup_gid', ''),\n\t('system', 'acmeshpath', '/root/.acme.sh/acme.sh'),\n\t('system', 'distribution', ''),\n\t('system', 'distro_mismatch', '0'),\n\t('system', 'update_channel', 'stable'),\n\t('system', 'updatecheck_data', ''),\n\t('system', 'update_notify_last', ''),\n\t('system', 'traffictool', 'goaccess'),\n\t('system', 'req_limit_per_interval', 60),\n\t('system', 'req_limit_interval', 60),\n\t('system', 'report_web_bccadmin', '0'),\n\t('system', 'webserver_serveradmin', 'customer'),\n\t('api', 'enabled', '0'),\n\t('api', 'customer_default', '1'),\n\t('2fa', 'enabled', '1'),\n\t('mail', 'enable_allow_sender', '0'),\n\t('mail', 'allow_external_domains', '0'),\n\t('panel', 'decimal_places', '4'),\n\t('panel', 'adminmail', 'ADMIN_MAIL'),\n\t('panel', 'phpmyadmin_url', ''),\n\t('panel', 'webmail_url', ''),\n\t('panel', 'webftp_url', ''),\n\t('panel', 'standardlanguage', 'en'),\n\t('panel', 'pathedit', 'Manual'),\n\t('panel', 'paging', '20'),\n\t('panel', 'natsorting', '1'),\n\t('panel', 'sendalternativemail', '0'),\n\t('panel', 'allow_domain_change_admin', '0'),\n\t('panel', 'allow_domain_change_customer', '0'),\n\t('panel', 'frontend', 'froxlor'),\n\t('panel', 'default_theme', 'Froxlor'),\n\t('panel', 'password_min_length', '0'),\n\t('panel', 'adminmail_defname', 'Froxlor Administrator'),\n\t('panel', 'adminmail_return', ''),\n\t('panel', 'unix_names', '1'),\n\t('panel', 'allow_preset', '1'),\n\t('panel', 'allow_preset_admin', '0'),\n\t('panel', 'password_regex', ''),\n\t('panel', 'phpconfigs_hidestdsubdomain', '0'),\n\t('panel', 'phpconfigs_hidesubdomains', '1'),\n\t('panel', 'allow_theme_change_admin', '1'),\n\t('panel', 'allow_theme_change_customer', '1'),\n\t('panel', 'password_alpha_lower', '1'),\n\t('panel', 'password_alpha_upper', '1'),\n\t('panel', 'password_numeric', '0'),\n\t('panel', 'password_special_char_required', '0'),\n\t('panel', 'password_special_char', '!?<>§$%+#=@'),\n\t('panel', 'customer_hide_options', ''),\n\t('panel', 'is_configured', '0'),\n\t('panel', 'imprint_url', ''),\n\t('panel', 'terms_url', ''),\n\t('panel', 'privacy_url', ''),\n\t('panel', 'logo_image_header', ''),\n\t('panel', 'logo_image_login', ''),\n\t('panel', 'logo_overridetheme', '0'),\n\t('panel', 'logo_overridecustom', '0'),\n\t('panel', 'settings_mode', '0'),\n\t('panel', 'menu_collapsed', '1'),\n\t('panel', 'version', '2.3.7'),\n\t('panel', 'db_version', '202603100');\n\n\nDROP TABLE IF EXISTS `panel_tasks`;\nCREATE TABLE `panel_tasks` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `type` int(11) NOT NULL default '0',\n  `data` text,\n  PRIMARY KEY  (`id`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nINSERT INTO `panel_tasks` (`type`) VALUES ('99');\n\n\nDROP TABLE IF EXISTS `panel_templates`;\nCREATE TABLE `panel_templates` (\n  `id` int(11) NOT NULL auto_increment,\n  `adminid` int(11) NOT NULL default '0',\n  `language` varchar(255) NOT NULL default '',\n  `templategroup` varchar(255) NOT NULL default '',\n  `varname` varchar(255) NOT NULL default '',\n  `value` longtext NOT NULL,\n  `file_extension` varchar(50) NOT NULL default 'html',\n  PRIMARY KEY  (id),\n  KEY adminid (adminid)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_traffic`;\nCREATE TABLE `panel_traffic` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `customerid` int(11) unsigned NOT NULL default '0',\n  `year` int(4) unsigned zerofill NOT NULL default '0000',\n  `month` int(2) unsigned zerofill NOT NULL default '00',\n  `day` int(2) unsigned zerofill NOT NULL default '00',\n  `stamp` int(11) unsigned NOT NULL default '0',\n  `http` bigint(30) unsigned NOT NULL default '0',\n  `ftp_up` bigint(30) unsigned NOT NULL default '0',\n  `ftp_down` bigint(30) unsigned NOT NULL default '0',\n  `mail` bigint(30) unsigned NOT NULL default '0',\n  PRIMARY KEY  (`id`),\n  KEY `customerid` (`customerid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_traffic_admins`;\nCREATE TABLE `panel_traffic_admins` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `adminid` int(11) unsigned NOT NULL default '0',\n  `year` int(4) unsigned zerofill NOT NULL default '0000',\n  `month` int(2) unsigned zerofill NOT NULL default '00',\n  `day` int(2) unsigned zerofill NOT NULL default '00',\n  `stamp` int(11) unsigned NOT NULL default '0',\n  `http` bigint(30) unsigned NOT NULL default '0',\n  `ftp_up` bigint(30) unsigned NOT NULL default '0',\n  `ftp_down` bigint(30) unsigned NOT NULL default '0',\n  `mail` bigint(30) unsigned NOT NULL default '0',\n  PRIMARY KEY  (`id`),\n  KEY `adminid` (`adminid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_diskspace`;\nCREATE TABLE `panel_diskspace` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `customerid` int(11) unsigned NOT NULL default '0',\n  `year` int(4) unsigned zerofill NOT NULL default '0000',\n  `month` int(2) unsigned zerofill NOT NULL default '00',\n  `day` int(2) unsigned zerofill NOT NULL default '00',\n  `stamp` int(11) unsigned NOT NULL default '0',\n  `webspace` bigint(30) unsigned NOT NULL default '0',\n  `mail` bigint(30) unsigned NOT NULL default '0',\n  `mysql` bigint(30) unsigned NOT NULL default '0',\n  PRIMARY KEY  (`id`),\n  KEY `customerid` (`customerid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_syslog`;\nCREATE TABLE IF NOT EXISTS `panel_syslog` (\n  `logid` bigint(20) NOT NULL auto_increment,\n  `action` int(5) NOT NULL default '10',\n  `type` int(5) NOT NULL default '0',\n  `date` int(15) NOT NULL,\n  `user` varchar(50) NOT NULL,\n  `text` text NOT NULL,\n  PRIMARY KEY  (`logid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_fpmdaemons`;\nCREATE TABLE `panel_fpmdaemons` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `description` varchar(50) NOT NULL,\n  `reload_cmd` varchar(255) NOT NULL,\n  `config_dir` varchar(255) NOT NULL,\n  `pm` varchar(15) NOT NULL DEFAULT 'dynamic',\n  `max_children` int(4) NOT NULL DEFAULT '5',\n  `start_servers` int(4) NOT NULL DEFAULT '2',\n  `min_spare_servers` int(4) NOT NULL DEFAULT '1',\n  `max_spare_servers` int(4) NOT NULL DEFAULT '3',\n  `max_requests` int(4) NOT NULL DEFAULT '0',\n  `idle_timeout` int(4) NOT NULL DEFAULT '10',\n  `limit_extensions` varchar(255) NOT NULL default '.php',\n  `custom_config` text,\n  PRIMARY KEY  (`id`),\n  UNIQUE KEY `reload` (`reload_cmd`),\n  UNIQUE KEY `config` (`config_dir`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nINSERT INTO `panel_fpmdaemons` (`id`, `description`, `reload_cmd`, `config_dir`) VALUES\n(1, 'System default', 'service php7.4-fpm restart', '/etc/php/7.4/fpm/pool.d/');\n\n\nDROP TABLE IF EXISTS `panel_phpconfigs`;\nCREATE TABLE `panel_phpconfigs` (\n  `id` int(11) unsigned NOT NULL auto_increment,\n  `description` varchar(50) NOT NULL,\n  `binary` varchar(255) NOT NULL,\n  `file_extensions` varchar(255) NOT NULL,\n  `mod_fcgid_starter` int(4) NOT NULL DEFAULT '-1',\n  `mod_fcgid_maxrequests` int(4) NOT NULL DEFAULT '-1',\n  `mod_fcgid_umask` varchar(15) NOT NULL DEFAULT '022',\n  `fpm_slowlog` tinyint(1) NOT NULL default '0',\n  `fpm_reqterm` varchar(15) NOT NULL default '60s',\n  `fpm_reqslow` varchar(15) NOT NULL default '5s',\n  `phpsettings` text NOT NULL,\n  `fpmsettingid` int(11) NOT NULL DEFAULT '1',\n  `pass_authorizationheader` tinyint(1) NOT NULL default '0',\n  `override_fpmconfig` tinyint(1) NOT NULL DEFAULT '0',\n  `pm` varchar(15) NOT NULL DEFAULT 'dynamic',\n  `max_children` int(4) NOT NULL DEFAULT '5',\n  `start_servers` int(4) NOT NULL DEFAULT '2',\n  `min_spare_servers` int(4) NOT NULL DEFAULT '1',\n  `max_spare_servers` int(4) NOT NULL DEFAULT '3',\n  `max_requests` int(4) NOT NULL DEFAULT '0',\n  `idle_timeout` int(4) NOT NULL DEFAULT '10',\n  `limit_extensions` varchar(255) NOT NULL default '.php',\n  PRIMARY KEY  (`id`),\n  KEY `fpmsettingid` (`fpmsettingid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nINSERT INTO `panel_phpconfigs` (`id`, `description`, `binary`, `file_extensions`, `mod_fcgid_starter`, `mod_fcgid_maxrequests`, `pass_authorizationheader`, `phpsettings`) VALUES\n(1, 'Default Config', '/usr/bin/php-cgi', 'php', '-1', '-1', '1', 'allow_url_fopen = Off\\r\\nallow_url_include = Off\\r\\nauto_append_file =\\r\\nauto_globals_jit = On\\r\\nauto_prepend_file =\\r\\nbcmath.scale = 0\\r\\ncli_server.color = On\\r\\ndefault_charset = \"UTF-8\"\\r\\ndefault_mimetype = \"text/html\"\\r\\ndefault_socket_timeout = 60\\r\\nasp_tags = Off\\r\\ndisable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,curl_exec,curl_multi_exec,exec,parse_ini_file,passthru,popen,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,show_source,system\\r\\ndisplay_errors = Off\\r\\ndisplay_startup_errors = Off\\r\\ndoc_root =\\r\\nenable_dl = Off\\r\\nerror_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE\\r\\nexpose_php = Off\\r\\nfile_uploads = On\\r\\nhtml_errors = On\\r\\nignore_repeated_errors = Off\\r\\nignore_repeated_source = Off\\r\\ninclude_path = \".:{PEAR_DIR}\"\\r\\nimplicit_flush = Off\\r\\nldap.max_links = -1\\r\\nlog_errors = On\\r\\nlog_errors_max_len = 1024\\r\\nmail.add_x_header = Off\\r\\nmax_execution_time = 30\\r\\nmax_file_uploads = 20\\r\\nmax_input_time = 60\\r\\nmemory_limit = 128M\\r\\n{OPEN_BASEDIR_C}open_basedir = \"{OPEN_BASEDIR}\"\\r\\noutput_buffering = 4096\\r\\npost_max_size = 16M\\r\\nprecision = 14\\r\\nregister_argc_argv = Off\\r\\nreport_memleaks = On\\r\\nrequest_order = \"GP\"\\r\\nsendmail_path = \"/usr/sbin/sendmail -t -i -f postmaster@{DOMAIN}\"\\r\\nserialize_precision = -1\\r\\nsession.auto_start = 0\\r\\nsession.cache_expire = 180\\r\\nsession.cache_limiter = nocache\\r\\nsession.cookie_domain =\\r\\nsession.cookie_httponly =\\r\\nsession.cookie_lifetime = 0\\r\\nsession.cookie_path = /\\r\\nsession.cookie_samesite =\\r\\nsession.gc_divisor = 1000\\r\\nsession.gc_maxlifetime = 1440\\r\\nsession.gc_probability = 0\\r\\nsession.name = PHPSESSID\\r\\nsession.referer_check =\\r\\nsession.save_handler = files\\r\\nsession.save_path = \"{TMP_DIR}\"\\r\\nsession.serialize_handler = php\\r\\nsession.sid_bits_per_character = 5\\r\\nsession.sid_length = 26\\r\\nsession.trans_sid_tags = \"a=href,area=href,frame=src,form=\"\\r\\nsession.use_cookies = 1\\r\\nsession.use_only_cookies = 1\\r\\nsession.use_strict_mode = 0\\r\\nsession.use_trans_sid = 0\\r\\nshort_open_tag = On\\r\\nupload_max_filesize = 32M\\r\\nupload_tmp_dir = \"{TMP_DIR}\"\\r\\nvariables_order = \"GPCS\"\\r\\nopcache.restrict_api = \"{DOCUMENT_ROOT}\"\\r\\n'),\n(2, 'Froxlor Vhost Config', '/usr/bin/php-cgi', 'php', '-1', '-1', '1', 'allow_url_fopen = On\\r\\nallow_url_include = Off\\r\\nauto_append_file =\\r\\nauto_globals_jit = On\\r\\nauto_prepend_file =\\r\\nbcmath.scale = 0\\r\\ncli_server.color = On\\r\\ndefault_charset = \"UTF-8\"\\r\\ndefault_mimetype = \"text/html\"\\r\\ndefault_socket_timeout = 60\\r\\nasp_tags = Off\\r\\ndisable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,curl_multi_exec,parse_ini_file,passthru,popen,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,show_source,system\\r\\ndisplay_errors = Off\\r\\ndisplay_startup_errors = Off\\r\\ndoc_root =\\r\\nenable_dl = Off\\r\\nerror_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE\\r\\nexpose_php = Off\\r\\nfile_uploads = On\\r\\nhtml_errors = On\\r\\nignore_repeated_errors = Off\\r\\nignore_repeated_source = Off\\r\\ninclude_path = \".:{PEAR_DIR}\"\\r\\nimplicit_flush = Off\\r\\nldap.max_links = -1\\r\\nlog_errors = On\\r\\nlog_errors_max_len = 1024\\r\\nmail.add_x_header = Off\\r\\nmax_execution_time = 60\\r\\nmax_file_uploads = 20\\r\\nmax_input_time = 60\\r\\nmemory_limit = 128M\\r\\noutput_buffering = 4096\\r\\npost_max_size = 16M\\r\\nprecision = 14\\r\\nregister_argc_argv = Off\\r\\nreport_memleaks = On\\r\\nrequest_order = \"GP\"\\r\\nsendmail_path = \"/usr/sbin/sendmail -t -i -f postmaster@{DOMAIN}\"\\r\\nserialize_precision = -1\\r\\nsession.auto_start = 0\\r\\nsession.cache_expire = 180\\r\\nsession.cache_limiter = nocache\\r\\nsession.cookie_domain =\\r\\nsession.cookie_httponly =\\r\\nsession.cookie_lifetime = 0\\r\\nsession.cookie_path = /\\r\\nsession.cookie_samesite =\\r\\nsession.gc_divisor = 1000\\r\\nsession.gc_maxlifetime = 1440\\r\\nsession.gc_probability = 0\\r\\nsession.name = PHPSESSID\\r\\nsession.referer_check =\\r\\nsession.save_handler = files\\r\\nsession.save_path = \"{TMP_DIR}\"\\r\\nsession.serialize_handler = php\\r\\nsession.sid_bits_per_character = 5\\r\\nsession.sid_length = 26\\r\\nsession.trans_sid_tags = \"a=href,area=href,frame=src,form=\"\\r\\nsession.use_cookies = 1\\r\\nsession.use_only_cookies = 1\\r\\nsession.use_strict_mode = 0\\r\\nsession.use_trans_sid = 0\\r\\nshort_open_tag = On\\r\\nupload_max_filesize = 32M\\r\\nupload_tmp_dir = \"{TMP_DIR}\"\\r\\nvariables_order = \"GPCS\"\\r\\nopcache.restrict_api = \"\"\\r\\n');\n\n\nDROP TABLE IF EXISTS `cronjobs_run`;\nCREATE TABLE IF NOT EXISTS `cronjobs_run` (\n  `id` bigint(20) NOT NULL auto_increment,\n  `module` varchar(250) NOT NULL,\n  `cronfile` varchar(250) NOT NULL,\n  `cronclass` varchar(500) NOT NULL,\n  `lastrun` int(15) NOT NULL DEFAULT '0',\n  `interval` varchar(100) NOT NULL DEFAULT '5 MINUTE',\n  `isactive` tinyint(1) DEFAULT '1',\n  `desc_lng_key` varchar(100) NOT NULL DEFAULT 'cron_unknown_desc',\n  PRIMARY KEY  (`id`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nINSERT INTO `cronjobs_run` (`id`, `module`, `cronfile`, `cronclass`, `interval`, `isactive`, `desc_lng_key`) VALUES\n\t(1, 'froxlor/core', 'tasks', '\\\\Froxlor\\\\Cron\\\\System\\\\TasksCron', '5 MINUTE', '1', 'cron_tasks'),\n\t(2, 'froxlor/core', 'traffic', '\\\\Froxlor\\\\Cron\\\\Traffic\\\\TrafficCron', '1 DAY', '1', 'cron_traffic'),\n\t(3, 'froxlor/reports', 'usage_report', '\\\\Froxlor\\\\Cron\\\\Traffic\\\\ReportsCron', '1 DAY', '1', 'cron_usage_report'),\n\t(4, 'froxlor/core', 'mailboxsize', '\\\\Froxlor\\\\Cron\\\\System\\\\MailboxsizeCron', '6 HOUR', '1', 'cron_mailboxsize'),\n\t(5, 'froxlor/letsencrypt', 'letsencrypt', '\\\\Froxlor\\\\Cron\\\\Http\\\\LetsEncrypt\\\\AcmeSh', '5 MINUTE', '0', 'cron_letsencrypt'),\n\t(6, 'froxlor/export', 'export', '\\\\Froxlor\\\\Cron\\\\System\\\\ExportCron', '1 HOUR', '0', 'cron_export');\n\n\nDROP TABLE IF EXISTS `ftp_quotalimits`;\nCREATE TABLE IF NOT EXISTS `ftp_quotalimits` (\n  `name` varchar(255) default NULL,\n  `quota_type` enum('user','group','class','all') NOT NULL default 'user',\n  `per_session` enum('false','true') NOT NULL default 'false',\n  `limit_type` enum('soft','hard') NOT NULL default 'hard',\n  `bytes_in_avail` float NOT NULL,\n  `bytes_out_avail` float NOT NULL,\n  `bytes_xfer_avail` float NOT NULL,\n  `files_in_avail` int(10) unsigned NOT NULL,\n  `files_out_avail` int(10) unsigned NOT NULL,\n  `files_xfer_avail` int(10) unsigned NOT NULL\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nINSERT INTO `ftp_quotalimits` (`name`, `quota_type`, `per_session`, `limit_type`, `bytes_in_avail`, `bytes_out_avail`, `bytes_xfer_avail`, `files_in_avail`, `files_out_avail`, `files_xfer_avail`) VALUES\n\t('froxlor', 'user', 'false', 'hard', 0, 0, 0, 0, 0, 0);\n\n\nDROP TABLE IF EXISTS `ftp_quotatallies`;\nCREATE TABLE IF NOT EXISTS `ftp_quotatallies` (\n  `name` varchar(255) NOT NULL,\n  `quota_type` enum('user','group','class','all') NOT NULL,\n  `bytes_in_used` float NOT NULL,\n  `bytes_out_used` float NOT NULL,\n  `bytes_xfer_used` float NOT NULL,\n  `files_in_used` int(10) unsigned NOT NULL,\n  `files_out_used` int(10) unsigned NOT NULL,\n  `files_xfer_used` int(10) unsigned NOT NULL\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `redirect_codes`;\nCREATE TABLE IF NOT EXISTS `redirect_codes` (\n  `id` int(5) NOT NULL auto_increment,\n  `code` varchar(3) NOT NULL,\n  `desc` varchar(200) NOT NULL,\n  `enabled` tinyint(1) DEFAULT '1',\n  PRIMARY KEY  (`id`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nINSERT INTO `redirect_codes` (`id`, `code`, `desc`, `enabled`) VALUES\n\t(1, '---', 'rc_default', 1),\n\t(2, '301', 'rc_movedperm', 1),\n\t(3, '302', 'rc_found', 1),\n\t(4, '303', 'rc_seeother', 1),\n\t(5, '307', 'rc_tempred', 1);\n\n\nDROP TABLE IF EXISTS `domain_redirect_codes`;\nCREATE TABLE IF NOT EXISTS `domain_redirect_codes` (\n  `rid` int(5) NOT NULL,\n  `did` int(11) unsigned NOT NULL,\n  UNIQUE KEY `rc` (`rid`, `did`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `domain_ssl_settings`;\nCREATE TABLE IF NOT EXISTS `domain_ssl_settings` (\n  `id` int(5) NOT NULL auto_increment,\n  `domainid` int(11) NOT NULL,\n  `ssl_cert_file` mediumtext,\n  `ssl_key_file` mediumtext,\n  `ssl_ca_file` mediumtext,\n  `ssl_cert_chainfile` mediumtext,\n  `ssl_csr_file` mediumtext,\n  `ssl_fullchain_file` mediumtext,\n  `validfromdate` datetime DEFAULT NULL,\n  `validtodate` datetime DEFAULT NULL,\n  `issuer` varchar(255) NOT NULL default '',\n  PRIMARY KEY  (`id`),\n  UNIQUE KEY (`domainid`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_domaintoip`;\nCREATE TABLE IF NOT EXISTS `panel_domaintoip` (\n  `id_domain` int(11) unsigned NOT NULL,\n  `id_ipandports` int(11) unsigned NOT NULL,\n  PRIMARY KEY (`id_domain`,`id_ipandports`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `domain_dns_entries`;\nCREATE TABLE `domain_dns_entries` (\n  `id` int(20) NOT NULL auto_increment,\n  `domain_id` int(15) NOT NULL,\n  `record` varchar(255) NOT NULL,\n  `type` varchar(10) NOT NULL DEFAULT 'A',\n  `content` text NOT NULL,\n  `ttl` int(11) NOT NULL DEFAULT '18000',\n  `prio` int(11) DEFAULT NULL,\n  PRIMARY KEY  (`id`)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_plans`;\nCREATE TABLE `panel_plans` (\n  `id` int(11) NOT NULL auto_increment,\n  `adminid` int(11) NOT NULL default '0',\n  `name` varchar(255) NOT NULL default '',\n  `description` text NOT NULL,\n  `value` longtext NOT NULL,\n  `ts` int(15) NOT NULL default '0',\n  PRIMARY KEY  (id),\n  KEY adminid (adminid)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `api_keys`;\nCREATE TABLE `api_keys` (\n  `id` int(11) NOT NULL auto_increment,\n  `adminid` int(11) NOT NULL default '0',\n  `customerid` int(11) NOT NULL default '0',\n  `apikey` varchar(500) NOT NULL default '',\n  `secret` varchar(500) NOT NULL default '',\n  `allowed_from` text NOT NULL,\n  `valid_until` int(15) NOT NULL default '0',\n  PRIMARY KEY  (id),\n  KEY adminid (adminid),\n  KEY customerid (customerid)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_usercolumns`;\nCREATE TABLE `panel_usercolumns` (\n  `adminid` int(11) NOT NULL default '0',\n  `customerid` int(11) NOT NULL default '0',\n  `section` varchar(500) NOT NULL default '',\n  `columns` text NOT NULL,\n  UNIQUE KEY `user_section` (`adminid`, `customerid`, `section`),\n  KEY adminid (adminid),\n  KEY customerid (customerid)\n) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_loginlinks`;\nCREATE TABLE `panel_loginlinks` (\n  `hash` varchar(500) NOT NULL,\n  `loginname` varchar(50) NOT NULL,\n  `valid_until` int(15) NOT NULL,\n  `allowed_from` text NOT NULL,\n  UNIQUE KEY `loginname` (`loginname`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;\n\n\nDROP TABLE IF EXISTS `panel_2fa_tokens`;\nCREATE TABLE `panel_2fa_tokens` (\n  `id` int(11) NOT NULL auto_increment,\n  `selector` varchar(200) NOT NULL,\n  `token` varchar(200) NOT NULL,\n  `userid` int(11) NOT NULL default '0',\n  `valid_until` int(15) NOT NULL,\n  PRIMARY KEY  (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;\n\nDROP TABLE IF EXISTS `panel_sshkeys`;\nCREATE TABLE `panel_sshkeys` (\n  `id` int(11) NOT NULL auto_increment,\n  `customerid` int(11) NOT NULL,\n  `ftp_user_id` int(20) NOT NULL,\n  `ssh_pubkey` text NOT NULL,\n  `description` varchar(255) NOT NULL DEFAULT '',\n  PRIMARY KEY  (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;\nFROXLORSQL;\n"
  },
  {
    "path": "install/index.html",
    "content": ""
  },
  {
    "path": "install/install.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Http\\RateLimiter;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\Install\\Install;\n\nrequire dirname(__DIR__) . '/lib/functions.php';\n\n// define default theme for configurehint, etc.\n$_deftheme = 'Froxlor';\n\n// validate correct php version\nif (version_compare(\"7.4.0\", PHP_VERSION, \">=\")) {\n\tdie(view($_deftheme . '/misc/phprequirementfailed.html.twig', [\n\t\t'{{ basehref }}' => '../',\n\t\t'{{ froxlor_min_version }}' => '7.4.0',\n\t\t'{{ current_version }}' => PHP_VERSION,\n\t\t'{{ current_year }}' => date('Y', time()),\n\t]));\n}\n\n// validate vendor autoloader\nif (!file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {\n\tdie(view($_deftheme . '/misc/vendormissinghint.html.twig', [\n\t\t'{{ basehref }}' => '../',\n\t\t'{{ froxlor_install_dir }}' => dirname(__DIR__),\n\t\t'{{ current_year }}' => date('Y', time()),\n\t]));\n}\n\n// check installation status\nif (file_exists(dirname(__DIR__) . '/lib/userdata.inc.php')) {\n\theader(\"Location: ../\");\n\texit;\n}\n\nrequire dirname(__DIR__) . '/vendor/autoload.php';\nrequire dirname(__DIR__) . '/lib/tables.inc.php';\n\n// init twig\nUI::initTwig(true);\nUI::sendHeaders();\nRateLimiter::run(true);\n\n$installer = new Install();\n$installer->handle();\n"
  },
  {
    "path": "install/updates/froxlor/index.html",
    "content": ""
  },
  {
    "path": "install/updates/froxlor/update_2.0.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\n\nif (!defined('_CRON_UPDATE')) {\n\tif (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) {\n\t\theader('Location: ../../../../index.php');\n\t\texit();\n\t}\n}\n\n// last 0.10.x release\nif (Froxlor::isFroxlorVersion('0.10.38.3')) {\n\t$update_to = '2.0.0-beta1';\n\n\tUpdate::showUpdateStep(\"Updating from 0.10.38.3 to \" . $update_to, false);\n\n\tUpdate::showUpdateStep(\"Removing unused table\");\n\tDatabase::query(\"DROP TABLE IF EXISTS `panel_sessions`;\");\n\tDatabase::query(\"DROP TABLE IF EXISTS `panel_languages`;\");\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Updating froxlor - theme\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `theme` = 'Froxlor' WHERE `theme` <> 'Froxlor';\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `theme` = 'Froxlor' WHERE `theme` <> 'Froxlor';\");\n\tSettings::Set('panel.default_theme', 'Froxlor');\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Creating new tables and fields\");\n\tDatabase::query(\"DROP TABLE IF EXISTS `panel_usercolumns`;\");\n\t$sql = \"CREATE TABLE `panel_usercolumns` (\n\t`adminid` int(11) NOT NULL default '0',\n\t`customerid` int(11) NOT NULL default '0',\n\t`section` varchar(500) NOT NULL default '',\n\t`columns` text NOT NULL,\n\tUNIQUE KEY `user_section` (`adminid`, `customerid`, `section`),\n\tKEY adminid (adminid),\n\tKEY customerid (customerid)\n\t) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;\";\n\tDatabase::query($sql);\n\t// new customer allowed_mysqlserver field\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` ROW_FORMAT=DYNAMIC;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` CHANGE COLUMN `customernumber` `customernumber` varchar(100) NOT NULL default '';\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` CHANGE COLUMN `allowed_phpconfigs` `allowed_phpconfigs` text NOT NULL;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` ADD `allowed_mysqlserver` text NOT NULL;\");\n\t$has_customer_table_update_200 = true;\n\t// ftp_users adjustments\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_FTP_USERS . \"` CHANGE COLUMN `password` `password` varchar(255) NOT NULL default '';\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_FTP_QUOTALIMITS . \"` CHANGE COLUMN `name` `name` varchar(255) default NULL;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_FTP_QUOTATALLIES . \"` CHANGE COLUMN `name` `name` varchar(255) default NULL;\");\n\t// mail_users adjustments\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_MAIL_USERS . \"` CHANGE COLUMN `password` `password` varchar(255) NOT NULL default '';\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_MAIL_USERS . \"` CHANGE COLUMN `password_enc` `password_enc` varchar(255) NOT NULL default '';\");\n\t// drop domains_see_all field from panel_admins\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_ADMINS . \"` DROP COLUMN `domains_see_all`;\");\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Checking for multiple mysql-servers to allow access to customers for existing databases\");\n\t$dbservers_stmt = Database::query(\"\n\t\tSELECT `customerid`,\n\t\tGROUP_CONCAT(DISTINCT `dbserver` SEPARATOR ',') as allowed_mysqlserver\n\t\tFROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\tGROUP BY `customerid`;\n\t\");\n\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `allowed_mysqlserver` = :allowed_mysqlserver WHERE `customerid` = :customerid\");\n\twhile ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\tif (isset($dbserver['allowed_mysqlserver']) && !empty($dbserver['allowed_mysqlserver'])) {\n\t\t\t$allowed_mysqlserver = json_encode(explode(\",\", $dbserver['allowed_mysqlserver']));\n\t\t\tDatabase::pexecute($upd_stmt,\n\t\t\t\t['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]);\n\t\t}\n\t}\n\tUpdate::lastStepStatus(0);\n\n\t$to_clean = array(\n\t\t\"install/lib\",\n\t\t\"install/lng\",\n\t\t\"install/updates/froxlor/0.9\",\n\t\t\"install/updates/froxlor/0.10\",\n\t\t\"install/updates/preconfig/0.9\",\n\t\t\"install/updates/preconfig/0.10\",\n\t\t\"install/updates/preconfig.php\",\n\t\t\"templates/Sparkle\",\n\t\t\"lib/version.inc.php\",\n\t\t\"lng/czech.lng.php\",\n\t\t\"lng/dutch.lng.php\",\n\t\t\"lng/english.lng.php\",\n\t\t\"lng/french.lng.php\",\n\t\t\"lng/german.lng.php\",\n\t\t\"lng/italian.lng.php\",\n\t\t\"lng/lng_references.php\",\n\t\t\"lng/portugues.lng.php\",\n\t\t\"lng/swedish.lng.php\",\n\t\t\"scripts\",\n\t);\n\tUpdate::cleanOldFiles($to_clean);\n\n\tUpdate::showUpdateStep(\"Adding new settings\");\n\t$panel_settings_mode = isset($_POST['panel_settings_mode']) ? (int)$_POST['panel_settings_mode'] : 0;\n\tSettings::AddNew(\"panel.settings_mode\", $panel_settings_mode);\n\t$system_distribution = isset($_POST['system_distribution']) ? $_POST['system_distribution'] : 'bullseye';\n\tSettings::AddNew(\"system.distribution\", $system_distribution);\n\tSettings::AddNew(\"system.update_channel\", 'stable');\n\tSettings::AddNew(\"system.updatecheck_data\", '');\n\tSettings::AddNew(\"system.update_notify_last\", $update_to);\n\tSettings::AddNew(\"panel.phpconfigs_hidesubdomains\", '1');\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Adjusting existing settings\");\n\tSettings::Set('system.passwordcryptfunc', PASSWORD_DEFAULT);\n\t// remap default-language\n\t$lang_map = [\n\t\t'Deutsch' => 'de',\n\t\t'English' => 'en',\n\t\t'Fran&ccedil;ais' => 'fr',\n\t\t'Portugu&ecirc;s' => 'pt',\n\t\t'Italiano' => 'it',\n\t\t'Nederlands' => 'nl',\n\t\t'Svenska' => 'se',\n\t\t'&#268;esk&aacute; republika' => 'cz'\n\t];\n\t// update user default languages\n\t$upd_adm_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `def_language` = :nv WHERE `def_language` = :ov\");\n\t$upd_cus_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `def_language` = :nv WHERE `def_language` = :ov\");\n\tforeach ($lang_map as $old_val => $new_val) {\n\t\tDatabase::pexecute($upd_adm_stmt, ['nv' => $new_val, 'ov' => $old_val]);\n\t\tDatabase::pexecute($upd_cus_stmt, ['nv' => $new_val, 'ov' => $old_val]);\n\t}\n\tSettings::Set('panel.standardlanguage', $lang_map[Settings::Get('panel_standardlanguage')] ?? 'en');\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'system' AND `varname` = 'debug_cron'\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'system' AND `varname` = 'letsencryptcountrycode'\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'system' AND `varname` = 'letsencryptstate'\");\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Updating email account password-hashes\");\n\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password_enc` = REPLACE(`password_enc`, '$1$', '{MD5-CRYPT}$1$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$1$'\");\n\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password_enc` = REPLACE(`password_enc`, '$5$', '{SHA256-CRYPT}$5$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$5$'\");\n\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password_enc` = REPLACE(`password_enc`, '$6$', '{SHA512-CRYPT}$6$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$6$'\");\n\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password_enc` = REPLACE(`password_enc`, '$2y$', '{BLF-CRYPT}$2y$') WHERE SUBSTRING(`password_enc`, 1, 4) = '$2y$'\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToVersion($update_to);\n}\n\nif (Froxlor::isDatabaseVersion('202112310')) {\n\tUpdate::showUpdateStep(\"Adjusting traffic tool settings\");\n\t$traffic_tool = Settings::Get('system.awstats_enabled') == 1 ? 'awstats' : 'webalizer';\n\tSettings::AddNew(\"system.traffictool\", $traffic_tool);\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'system' AND `varname` = 'awstats_enabled'\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202211030');\n}\n\nif (Froxlor::isDatabaseVersion('202211030')) {\n\tUpdate::showUpdateStep(\"Creating backward compatibility for cronjob\");\n\t$disabled = explode(',', ini_get('disable_functions'));\n\t$exec_allowed = !in_array('exec', $disabled);\n\t// check whether old files could be deleted in previous updates and if not,\n\t// user should run cron to regenerate cron.d-file manually as he will run\n\t// the other commands manually only after the update so this file would be deleted too\n\tif ($exec_allowed) {\n\t\t$complete_filedir = Froxlor::getInstallDir() . '/scripts';\n\t\tmkdir($complete_filedir, 0750, true);\n\t\t$newCronBin = Froxlor::getInstallDir() . '/bin/froxlor-cli';\n\t\t$compCron = <<<EOF\n<?php\nchmod('$newCronBin', 0755);\n// re-create cron.d configuration file\nexec('$newCronBin froxlor:cron -r 99');\nexit;\nEOF;\n\t\tfile_put_contents($complete_filedir . '/froxlor_master_cronjob.php', $compCron);\n\t\tUpdate::lastStepStatus(0);\n\t} else {\n\t\t$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL;\n\t\t$cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99';\n\t\tUpdate::lastStepStatus(1, 'manual commands needed',\n\t\t\t'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');\n\t}\n\n\tFroxlor::updateToDbVersion('202212060');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.0-beta1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.0-beta1 to 2.0.0\", false);\n\tFroxlor::updateToVersion('2.0.0');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.0')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.0 to 2.0.1\", false);\n\n\tif (!isset($has_customer_table_update_200)) {\n\t\tUpdate::showUpdateStep(\"Creating new tables and fields\");\n\t\t// new customer allowed_mysqlserver field\n\t\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` CHANGE COLUMN `allowed_mysqlserver` `allowed_mysqlserver` text NOT NULL;\");\n\t\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` CHANGE COLUMN `allowed_phpconfigs` `allowed_phpconfigs` text NOT NULL;\");\n\t\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` CHANGE COLUMN `customernumber` `customernumber` varchar(100) NOT NULL default '';\");\n\t\tUpdate::lastStepStatus(0);\n\t}\n\n\tFroxlor::updateToVersion('2.0.1');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.1 to 2.0.2\", false);\n\tFroxlor::updateToVersion('2.0.2');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.2')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.2 to 2.0.3\", false);\n\tFroxlor::updateToVersion('2.0.3');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.3')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.3 to 2.0.4\", false);\n\n\t$complete_filedir = Froxlor::getInstallDir() . '/scripts';\n\t// check if compat. cronjob still exists (most likely didn't run successfully b/c of error from former 2.0 release)\n\tif (@file_exists($complete_filedir . '/froxlor_master_cronjob.php')) {\n\t\tUpdate::showUpdateStep(\"Adjusting backward compatibility for cronjob\");\n\t\t$disabled = explode(',', ini_get('disable_functions'));\n\t\t$exec_allowed = !in_array('exec', $disabled);\n\t\tif ($exec_allowed) {\n\t\t\t$newCronBin = Froxlor::getInstallDir() . '/bin/froxlor-cli';\n\t\t\t$compCron = <<<EOF\n<?php\nchmod('$newCronBin', 0755);\n// re-create cron.d configuration file\nexec('$newCronBin froxlor:cron -r 99');\nexit;\nEOF;\n\t\t\tfile_put_contents($complete_filedir . '/froxlor_master_cronjob.php', $compCron);\n\t\t\tUpdate::lastStepStatus(0);\n\t\t} else {\n\t\t\t$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL;\n\t\t\t$cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99';\n\t\t\tUpdate::lastStepStatus(1, 'manual commands needed',\n\t\t\t\t'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');\n\t\t}\n\t}\n\tFroxlor::updateToVersion('2.0.4');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.4')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.4 to 2.0.5\", false);\n\tFroxlor::updateToVersion('2.0.5');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.5')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.5 to 2.0.6\", false);\n\n\tUpdate::showUpdateStep(\"Updating possible missing email account password-hashes\");\n\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password_enc` = REPLACE(`password_enc`, '$1$', '{MD5-CRYPT}$1$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$1$'\");\n\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password_enc` = REPLACE(`password_enc`, '$5$', '{SHA256-CRYPT}$5$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$5$'\");\n\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password_enc` = REPLACE(`password_enc`, '$6$', '{SHA512-CRYPT}$6$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$6$'\");\n\tDatabase::query(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `password_enc` = REPLACE(`password_enc`, '$2y$', '{BLF-CRYPT}$2y$') WHERE SUBSTRING(`password_enc`, 1, 4) = '$2y$'\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToVersion('2.0.6');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.6')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.6 to 2.0.7\", false);\n\n\tUpdate::showUpdateStep(\"Correcting allowed_mysqlserver for customers\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `allowed_mysqlserver` = '[0]' WHERE `allowed_mysqlserver` = ''\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToVersion('2.0.7');\n}\n\nif (Froxlor::isDatabaseVersion('202212060')) {\n\tUpdate::showUpdateStep(\"Validating acme.sh challenge path\");\n\t$acmesh_challenge_dir = Settings::Get('system.letsencryptchallengepath');\n\t$system_letsencryptchallengepath_upd = isset($_POST['system_letsencryptchallengepath_upd']) ? $_POST['system_letsencryptchallengepath_upd'] : $acmesh_challenge_dir;\n\tif ($acmesh_challenge_dir != $system_letsencryptchallengepath_upd) {\n\t\tSettings::Set('system.letsencryptchallengepath', $system_letsencryptchallengepath_upd);\n\t\tif ((int)Settings::Get('system.leenabled') == 1) {\n\t\t\t// create JSON string for --apply\n\t\t\t$dist = Settings::Get('system.distribution');\n\t\t\t$webserver = Settings::Get('system.webserver');\n\t\t\tif ($webserver == 'apache2') {\n\t\t\t\t$webserver = 'apache22';\n\t\t\t\tif (Settings::Get('system.apache24')) {\n\t\t\t\t\t$webserver = 'apache24';\n\t\t\t\t}\n\t\t\t}\n\t\t\t$apply_json = '{\"http\":\"' . $webserver . '\",\"dns\":\"x\",\"smtp\":\"x\",\"mail\":\"x\",\"ftp\":\"x\",\"distro\":\"' . $dist . '\",\"system\":[]}';\n\t\t\tUpdate::lastStepStatus(1, 'manual commands needed',\n\t\t\t\t\"Please reconfigure webserver service using <pre>bin/froxlor-cli froxlor:config-services --apply='\" . $apply_json . \"'</pre>\" .\n\t\t\t\t'<br>or adjust the path manually in <pre>' . Settings::Get('system.letsencryptacmeconf') . '</pre>' .\n\t\t\t\t'<br><br>In case you already have certificates issued, run the following command to validate and correct the webroot used for renewal:<br>' .\n\t\t\t\t'<pre>bin/froxlor-cli froxlor:validate-acme-webroot</pre><br>'\n\t\t\t);\n\t\t} else {\n\t\t\tUpdate::lastStepStatus(0);\n\t\t}\n\t} else {\n\t\tUpdate::lastStepStatus(0);\n\t}\n\n\tFroxlor::updateToDbVersion('202301120');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.7')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.7 to 2.0.8\", false);\n\n\t// adjust file-logging to be set to froxlor/logs/\n\t$logtypes = explode(',', Settings::Get('logger.logtypes'));\n\tif (in_array('file', $logtypes)) {\n\t\tUpdate::showUpdateStep(\"Adjusting froxlor logfile for system-logging to be stored in logs/froxlor.log\");\n\t\tSettings::Set('logger.logfile', 'froxlor.log');\n\t\tUpdate::lastStepStatus(0);\n\t}\n\n\tFroxlor::updateToVersion('2.0.8');\n}\n\nif (Froxlor::isDatabaseVersion('202301120')) {\n\tUpdate::showUpdateStep(\"Adding new setting for DNS resolver when using Let's Encrypt\");\n\t$system_le_domain_dnscheck_resolver = isset($_POST['system_le_domain_dnscheck_resolver']) ? $_POST['system_le_domain_dnscheck_resolver'] : '1.1.1.1';\n\tSettings::AddNew(\"system.le_domain_dnscheck_resolver\", $system_le_domain_dnscheck_resolver);\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202301180');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.8')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.8 to 2.0.9\", false);\n\tFroxlor::updateToVersion('2.0.9');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.9')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.9 to 2.0.10\", false);\n\tFroxlor::updateToVersion('2.0.10');\n}\n\nif (Froxlor::isDatabaseVersion('202301180')) {\n\tUpdate::showUpdateStep(\"Adding new setting for 'Allow API access' default value for new customers\");\n\tSettings::AddNew(\"api.customer_default\", \"1\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202302030');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.10')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.10 to 2.0.11\", false);\n\tFroxlor::updateToVersion('2.0.11');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.11')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.11 to 2.0.12\", false);\n\tFroxlor::updateToVersion('2.0.12');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.12')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.12 to 2.0.13\", false);\n\tFroxlor::updateToVersion('2.0.13');\n}\n\nif (Froxlor::isDatabaseVersion('202302030')) {\n\tUpdate::showUpdateStep(\"Correcting language mapping of templates created pre 2.0.x\");\n\t// languages from 0.10.x\n\t$language_mapping_comp = [\n\t\t'de' => 'Deutsch',\n\t\t'en' => 'English',\n\t\t'fr' => 'Fran&ccedil;ais',\n\t\t'pt' => 'Portugu&ecirc;s',\n\t\t'it' => 'Italiano',\n\t\t'nl' => 'Nederlands',\n\t\t'se' => 'Svenska',\n\t\t'cz' => '&#268;esk&aacute; republika'\n\t];\n\t$upd_tpl_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_TEMPLATES . \"` SET `language` = :iso WHERE `language` = :lng\");\n\tforeach ($language_mapping_comp as $iso => $lang) {\n\t\tDatabase::pexecute($upd_tpl_stmt, ['iso' => $iso, 'lng' => $lang]);\n\t}\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Enhancing ssl data table\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` CHANGE `expirationdate` `validtodate` datetime DEFAULT NULL;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` ADD `validfromdate` datetime DEFAULT NULL AFTER `ssl_fullchain_file`;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` ADD `issuer` varchar(255) NOT NULL default '' AFTER `validtodate`;\");\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Filling new ssl data fields with existing certificate data\");\n\t$crt_upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` SET `validfromdate` = :validfromdate, `issuer` = :issuer WHERE `id` = :id\");\n\t$crt_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\");\n\tDatabase::pexecute($crt_stmt);\n\twhile ($cert = $crt_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t$cert_content = openssl_x509_parse($cert['ssl_cert_file']);\n\t\tif (is_array($cert_content)) {\n\t\t\t$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date(\"Y-m-d H:i:s\", $cert_content['validFrom_time_t']);\n\t\t\t$issuer = $cert_content['issuer']['O'] ?? \"\";\n\t\t\tDatabase::pexecute($crt_upd_stmt, ['validfromdate' => $validfromdate, 'issuer' => $issuer, 'id' => $cert['id']]);\n\t\t}\n\t}\n\t// clear possible user customized columns\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_USERCOLUMNS . \"` WHERE `section` = 'sslcertificates_list'\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202303150');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.13')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.13 to 2.0.14\", false);\n\tFroxlor::updateToVersion('2.0.14');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.14')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.14 to 2.0.15\", false);\n\tFroxlor::updateToVersion('2.0.15');\n}\n\nif (Froxlor::isDatabaseVersion('202303150')) {\n\tUpdate::showUpdateStep(\"Adding new request rate limit settings\");\n\tSettings::AddNew(\"system.req_limit_per_interval\", \"60\");\n\tSettings::AddNew(\"system.req_limit_interval\", \"60\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202304260');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.15')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.15 to 2.0.16\", false);\n\tFroxlor::updateToVersion('2.0.16');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.16')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.16 to 2.0.17\", false);\n\tFroxlor::updateToVersion('2.0.17');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.17')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.17 to 2.0.18\", false);\n\tFroxlor::updateToVersion('2.0.18');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.18')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.18 to 2.0.19\", false);\n\tFroxlor::updateToVersion('2.0.19');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.19')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.19 to 2.0.20\", false);\n\tFroxlor::updateToVersion('2.0.20');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.20')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.20 to 2.0.21\", false);\n\tFroxlor::updateToVersion('2.0.21');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.21')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.21 to 2.0.22\", false);\n\tFroxlor::updateToVersion('2.0.22');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.22')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.22 to 2.0.23\", false);\n\tFroxlor::updateToVersion('2.0.23');\n}\n\nif (Froxlor::isFroxlorVersion('2.0.23')) {\n\tUpdate::showUpdateStep(\"Updating from 2.0.23 to 2.0.24\", false);\n\tFroxlor::updateToVersion('2.0.24');\n}\n"
  },
  {
    "path": "install/updates/froxlor/update_2.1.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\n\nif (!defined('_CRON_UPDATE')) {\n\tif (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) {\n\t\theader('Location: ../../../../index.php');\n\t\texit();\n\t}\n}\n\nif (Froxlor::isFroxlorVersion('2.0.24')) {\n\tUpdate::showUpdateStep(\"Cleaning domains table\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_DOMAINS . \"` ROW_FORMAT=DYNAMIC;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_DOMAINS . \"` DROP COLUMN `ismainbutsubto`;\");\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Creating new tables and fields\");\n\tDatabase::query(\"DROP TABLE IF EXISTS `panel_loginlinks`;\");\n\t$sql = \"CREATE TABLE `panel_loginlinks` (\n\t  `hash` varchar(500) NOT NULL,\n\t  `loginname` varchar(50) NOT NULL,\n\t  `valid_until` int(15) NOT NULL,\n\t  `allowed_from` text NOT NULL,\n\t  UNIQUE KEY `loginname` (`loginname`)\n\t) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;\";\n\tDatabase::query($sql);\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Adding new settings\");\n\tSettings::AddNew('panel.menu_collapsed', 1);\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Adjusting setting for deactivated webroot\");\n\t$current_deactivated_webroot = Settings::Get('system.deactivateddocroot');\n\tif (empty($current_deactivated_webroot)) {\n\t\tSettings::Set('system.deactivateddocroot', FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/templates/misc/deactivated/'));\n\t\tUpdate::lastStepStatus(0);\n\t} else {\n\t\tUpdate::lastStepStatus(1, 'Customized setting, not changing');\n\t}\n\n\tUpdate::showUpdateStep(\"Adjusting cronjobs\");\n\t$cfupd_stmt = Database::prepare(\"\n        UPDATE `\" . TABLE_PANEL_CRONRUNS . \"` SET\n        `module`= 'froxlor/export',\n        `cronfile` = 'export',\n        `cronclass` = :cc,\n        `interval` = '1 HOUR',\n        `desc_lng_key` = 'cron_export'\n        WHERE `module` = 'froxlor/backup'\n    \");\n\tDatabase::pexecute($cfupd_stmt, [\n\t\t'cc' => '\\\\Froxlor\\\\Cron\\\\System\\\\ExportCron'\n\t]);\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Adjusting system for data-export function\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"`SET `varname` = 'exportenabled' WHERE `settinggroup`= 'system' AND `varname`= 'backupenabled'\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"`SET `value` = REPLACE(`value`, 'extras.backup', 'extras.export') WHERE `settinggroup` = 'panel' AND `varname` = 'customer_hide_options'\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_USERCOLUMNS . \"` WHERE `section` = 'backup_list'\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` = '20'\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202305240');\n\tFroxlor::updateToVersion('2.1.0-dev1');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.0-dev1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.0-dev1 to 2.1.0-beta1\", false);\n\tFroxlor::updateToVersion('2.1.0-beta1');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.0-beta1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.0-beta1 to 2.1.0-beta2\", false);\n\n\tUpdate::showUpdateStep(\"Removing unused table\");\n\tDatabase::query(\"DROP TABLE IF EXISTS `panel_sessions`;\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToVersion('2.1.0-beta2');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.0-beta2')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.0-beta2 to 2.1.0-rc1\", false);\n\tFroxlor::updateToVersion('2.1.0-rc1');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.0-rc1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.0-rc1 to 2.1.0-rc2\", false);\n\n\tUpdate::showUpdateStep(\"Adjusting setting spf_entry\");\n\t$spf_entry = Settings::Get('spf.spf_entry');\n\tif (!preg_match('/^v=spf[a-z0-9:~?\\s.-]+$/i', $spf_entry)) {\n\t\tSettings::Set('spf.spf_entry', 'v=spf1 a mx -all');\n\t\tUpdate::lastStepStatus(1, 'corrected');\n\t} else {\n\t\tUpdate::lastStepStatus(0);\n\t}\n\n\tFroxlor::updateToVersion('2.1.0-rc2');\n}\n\nif (Froxlor::isDatabaseVersion('202305240')) {\n\n\tUpdate::showUpdateStep(\"Adjusting file-template file extension setttings\");\n\t$current_fileextension = Settings::Get('system.index_file_extension');\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup`= 'system' AND `varname`= 'index_file_extension'\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_TEMPLATES . \"` ADD `file_extension` varchar(50) NOT NULL default 'html';\");\n\tif (!empty(trim($current_fileextension)) && strtolower(trim($current_fileextension)) != 'html') {\n\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_TEMPLATES . \"` SET `file_extension` = :ext WHERE `templategroup` = 'files'\");\n\t\tDatabase::pexecute($stmt, ['ext' => strtolower(trim($current_fileextension))]);\n\t}\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202311260');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.0-rc2')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.0-rc2 to 2.1.0-rc3\", false);\n\tFroxlor::updateToVersion('2.1.0-rc3');\n}\n\nif (Froxlor::isDatabaseVersion('202311260')) {\n\t$to_clean = array(\n\t\t\"install/updates/froxlor/update_2.x.inc.php\",\n\t\t\"install/updates/preconfig/preconfig_2.x.inc.php\",\n\t\t\"lib/Froxlor/Api/Commands/CustomerBackups.php\",\n\t\t\"lib/Froxlor/Cli/Action\",\n\t\t\"lib/Froxlor/Cli/Action.php\",\n\t\t\"lib/Froxlor/Cli/CmdLineHandler.php\",\n\t\t\"lib/Froxlor/Cli/ConfigServicesCmd.php\",\n\t\t\"lib/Froxlor/Cli/PhpSessioncleanCmd.php\",\n\t\t\"lib/Froxlor/Cli/SwitchServerIpCmd.php\",\n\t\t\"lib/Froxlor/Cli/UpdateCliCmd.php\",\n\t\t\"lib/Froxlor/Cron/System/BackupCron.php\",\n\t\t\"lib/formfields/customer/extras/formfield.backup.php\",\n\t\t\"lib/tablelisting/customer/tablelisting.backups.php\",\n\t\t\"templates/Froxlor/assets/mix-manifest.json\",\n\t\t\"templates/Froxlor/assets/css\",\n\t\t\"templates/Froxlor/assets/webfonts\",\n\t\t\"templates/Froxlor/assets/js/main.js\",\n\t\t\"templates/Froxlor/assets/js/main.js.LICENSE.txt\",\n\t\t\"templates/Froxlor/src\",\n\t\t\"templates/Froxlor/user/change_language.html.twig\",\n\t\t\"templates/Froxlor/user/change_password.html.twig\",\n\t\t\"templates/Froxlor/user/change_theme.html.twig\",\n\t\t\"tests/Backup/CustomerBackupsTest.php\"\n\t);\n\tUpdate::cleanOldFiles($to_clean);\n\n\tFroxlor::updateToDbVersion('202312050');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.0-rc3')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.0-rc3 to 2.1.0 stable\", false);\n\tFroxlor::updateToVersion('2.1.0');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.0')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.0 to 2.1.1\", false);\n\tFroxlor::updateToVersion('2.1.1');\n}\n\nif (Froxlor::isDatabaseVersion('202312050')) {\n\t$to_clean = array(\n\t\t\"lib/configfiles/centos7.xml\",\n\t\t\"lib/configfiles/centos8.xml\",\n\t\t\"lib/configfiles/stretch.xml\",\n\t\t\"lib/configfiles/xenial.xml\",\n\t\t\"lib/configfiles/buster.xml\",\n\t\t\"lib/configfiles/bionic.xml\",\n\t);\n\tUpdate::cleanOldFiles($to_clean);\n\n\tFroxlor::updateToDbVersion('202312100');\n}\n\nif (Froxlor::isDatabaseVersion('202312100')) {\n\n\tUpdate::showUpdateStep(\"Adjusting table row format of larger tables\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_ADMINS . \"` ROW_FORMAT=DYNAMIC;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_DOMAINS . \"` ROW_FORMAT=DYNAMIC;\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202312120');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.1 to 2.1.2\", false);\n\tFroxlor::updateToVersion('2.1.2');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.2')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.2 to 2.1.3\", false);\n\tFroxlor::updateToVersion('2.1.3');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.3')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.3 to 2.1.4\", false);\n\tFroxlor::updateToVersion('2.1.4');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.4')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.4 to 2.1.5\", false);\n\tFroxlor::updateToVersion('2.1.5');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.5')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.5 to 2.1.6\", false);\n\tFroxlor::updateToVersion('2.1.6');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.6')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.6 to 2.1.7\", false);\n\tFroxlor::updateToVersion('2.1.7');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.7')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.7 to 2.1.8\", false);\n\tFroxlor::updateToVersion('2.1.8');\n}\n\nif (Froxlor::isFroxlorVersion('2.1.8')) {\n\tUpdate::showUpdateStep(\"Updating from 2.1.8 to 2.1.9\", false);\n\tFroxlor::updateToVersion('2.1.9');\n}\n"
  },
  {
    "path": "install/updates/froxlor/update_2.2.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Database\\DbManager;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\n\nif (!defined('_CRON_UPDATE')) {\n\tif (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) {\n\t\theader('Location: ../../../../index.php');\n\t\texit();\n\t}\n}\n\nif (Froxlor::isFroxlorVersion('2.1.9')) {\n\tUpdate::showUpdateStep(\"Enhancing virtual email table\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_MAIL_VIRTUAL . \"` ADD `spam_tag_level` float(4,1) NOT NULL DEFAULT 7.0;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_MAIL_VIRTUAL . \"` ADD `spam_kill_level` float(4,1) NOT NULL DEFAULT 14.0;\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_MAIL_VIRTUAL . \"` ADD `bypass_spam` tinyint(1) NOT NULL default '0';\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_MAIL_VIRTUAL . \"` ADD `policy_greylist` tinyint(1) NOT NULL default '1';\");\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Adjusting settings\");\n\t$antispam_activated = $_POST['antispam_activated'] ?? 0;\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `settinggroup` = 'antispam', `varname` = 'activated', `value` = '\" . (int)$antispam_activated . \"' WHERE `settinggroup` = 'dkim' AND `varname` = 'use_dkim';\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `settinggroup` = 'antispam', `varname` = 'reload_command', `value` = 'service rspamd restart' WHERE `settinggroup` = 'dkim' AND `varname` = 'dkimrestart_command';\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `settinggroup` = 'antispam', `varname` = 'config_file', `value` = '/etc/rspamd/local.d/froxlor_settings.conf' WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_prefix';\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `settinggroup` = 'antispam' WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_keylength';\");\n\tSettings::AddNew(\"dmarc.use_dmarc\", \"0\");\n\tSettings::AddNew(\"dmarc.dmarc_entry\", \"v=DMARC1; p=none;\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'dkim' AND `varname` = 'privkeysuffix';\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_domains';\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_algorithm';\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_notes';\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_add_adsp';\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_dkimkeys';\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_servicetype';\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_add_adsppolicy';\");\n\tUpdate::lastStepStatus(0);\n\n\tif ($antispam_activated) {\n\t\tUpdate::showUpdateStep(\"Converting existing domainkeys\");\n\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `dkim` = '1' AND `dkim_pubkey` <> ''\");\n\t\tDatabase::pexecute($sel_stmt);\n\t\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `dkim_pubkey` = :pkey WHERE `id` = :did\");\n\t\twhile ($domain = $sel_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t$pubkey = trim(preg_replace(\n\t\t\t\t'/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s',\n\t\t\t\t'$1',\n\t\t\t\tstr_replace(\"\\n\", '', $domain['dkim_pubkey'])\n\t\t\t));\n\t\t\tDatabase::pexecute($upd_stmt, ['pkey' => $pubkey, 'did' => $domain['id']]);\n\t\t}\n\t\tUpdate::lastStepStatus(0);\n\n\t\tUpdate::showUpdateStep(\"Configure antispam services\");\n\t\t$froxlorCliBin = Froxlor::getInstallDir() . '/bin/froxlor-cli';\n\t\t$currentDistro = Settings::Get('system.distribution');\n\t\t$manual_command = <<<EOC\n{$froxlorCliBin} froxlor:config-services -a '{\"http\":\"x\",\"dns\":\"x\",\"smtp\":\"x\",\"mail\":\"x\",\"antispam\":\"rspamd\",\"ftp\":\"x\",\"distro\":\"{$currentDistro}\",\"system\":[]}'\nEOC;\n\t\tUpdate::lastStepStatus(\n\t\t\t1,\n\t\t\t'manual action needed',\n\t\t\t\"Please run the following command manually as root:<br><pre>\" . $manual_command . \"</pre>\"\n\t\t);\n\t} else {\n\t\tUpdate::showUpdateStep(\"Removing existing domainkeys because antispam is disabled\");\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `dkim` = '0', `dkim_id` = '0', `dkim_privkey` = '', `dkim_pubkey` = '' WHERE `dkim` = '1';\");\n\t\tUpdate::lastStepStatus(1, '!!!');\n\t}\n\n\tUpdate::showUpdateStep(\"Enhancing admin and user table\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_ADMINS . \"` ADD `gui_access` tinyint(1) NOT NULL default '1';\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` ADD `gui_access` tinyint(1) NOT NULL default '1';\");\n\tUpdate::lastStepStatus(0);\n\n\t$to_clean = [\n\t\t'actions/admin/settings/180.dkim.php',\n\t\t'actions/admin/settings/185.spf.php',\n\t];\n\tUpdate::cleanOldFiles($to_clean);\n\n\tFroxlor::updateToDbVersion('202312230');\n\tFroxlor::updateToVersion('2.2.0-dev1');\n}\n\nif (Froxlor::isDatabaseVersion('202312230')) {\n\n\tUpdate::showUpdateStep(\"Adding new settings\");\n\tSettings::AddNew(\"system.le_renew_services\", \"\");\n\tSettings::AddNew(\"system.le_renew_hook\", \"systemctl restart postfix dovecot proftpd\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202401090');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.0-dev1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.0-dev1 to 2.2.0-rc1\", false);\n\tFroxlor::updateToVersion('2.2.0-rc1');\n}\n\nif (Froxlor::isDatabaseVersion('202401090')) {\n\n\tUpdate::showUpdateStep(\"Adding new table for 2fa tokens\");\n\tDatabase::query(\"DROP TABLE IF EXISTS `panel_2fa_tokens`;\");\n\t$sql = \"CREATE TABLE `panel_2fa_tokens` (\n\t  `id` int(11) NOT NULL auto_increment,\n\t  `selector` varchar(20) NOT NULL,\n\t  `token` varchar(200) NOT NULL,\n\t  `userid` int(11) NOT NULL default '0',\n\t  `valid_until` int(15) NOT NULL,\n\t  PRIMARY KEY  (id)\n\t) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;\";\n\tDatabase::query($sql);\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202407200');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.0-rc1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.0-rc1 to 2.2.0-rc2\", false);\n\tFroxlor::updateToVersion('2.2.0-rc2');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.0-rc2')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.0-rc2 to 2.2.0-rc3\", false);\n\tFroxlor::updateToVersion('2.2.0-rc3');\n}\n\nif (Froxlor::isDatabaseVersion('202407200')) {\n\n\tUpdate::showUpdateStep(\"Adjusting field in 2fa-token table\");\n\tDatabase::query(\"ALTER TABLE `panel_2fa_tokens` CHANGE COLUMN `selector` `selector` varchar(200) NOT NULL;\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202408140');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.0-rc3')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.0-rc3 to 2.2.0 stable\", false);\n\tFroxlor::updateToVersion('2.2.0');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.0')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.0 to 2.2.1\", false);\n\tFroxlor::updateToVersion('2.2.1');\n}\n\nif (Froxlor::isDatabaseVersion('202408140')) {\n\n\tUpdate::showUpdateStep(\"Adding new rewrite-subject field to email table\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_MAIL_VIRTUAL . \"` ADD `rewrite_subject` tinyint(1) NOT NULL default '1' AFTER `spam_tag_level`;\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202409280');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.1 to 2.2.2\", false);\n\tFroxlor::updateToVersion('2.2.2');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.2')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.2 to 2.2.3\", false);\n\tFroxlor::updateToVersion('2.2.3');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.3')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.3 to 2.2.4\", false);\n\tFroxlor::updateToVersion('2.2.4');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.4')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.4 to 2.2.5\", false);\n\tFroxlor::updateToVersion('2.2.5');\n}\n\nif (Froxlor::isDatabaseVersion('202409280')) {\n\n\tUpdate::showUpdateStep(\"Adding new antispam settings\");\n\tSettings::AddNew(\"antispam.default_bypass_spam\", \"2\");\n\tSettings::AddNew(\"antispam.default_spam_rewrite_subject\", \"1\");\n\tSettings::AddNew(\"antispam.default_policy_greylist\", \"1\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202411200');\n}\n\nif (Froxlor::isDatabaseVersion('202411200')) {\n\n\tUpdate::showUpdateStep(\"Adjusting customer mysql global user\");\n\t// get all customers that are not deactivated and that have at least one database (hence a global database-user)\n\t$customers = Database::query(\"\n\t\tSELECT DISTINCT c.loginname, c.allowed_mysqlserver\n\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` c\n\t\tLEFT JOIN `\" . TABLE_PANEL_DATABASES . \"` d ON c.customerid = d.customerid\n\t\tWHERE c.deactivated = '0' AND d.id IS NOT NULL\n\t\");\n\twhile ($customer = $customers->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t$current_allowed_mysqlserver = !empty($customer['allowed_mysqlserver']) ? json_decode($customer['allowed_mysqlserver'], true) : [];\n\t\tforeach ($current_allowed_mysqlserver as $dbserver) {\n\t\t\t// require privileged access for target db-server\n\t\t\tDatabase::needRoot(true, $dbserver, false);\n\t\t\t// get DbManager\n\t\t\t$dbm = new DbManager(FroxlorLogger::getInstanceOf());\n\t\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t\ttry {\n\t\t\t\t\tif ($dbm->getManager()->userExistsOnHost($customer['loginname'], $mysql_access_host)) {\n\t\t\t\t\t\t// deactivate temporarily\n\t\t\t\t\t\t$dbm->getManager()->disableUser($customer['loginname'], $mysql_access_host);\n\t\t\t\t\t\t// re-enable\n\t\t\t\t\t\t$dbm->getManager()->enableUser($customer['loginname'], $mysql_access_host, true);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t// continue\n\t\t\t\t}\n\t\t\t}\n\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\tDatabase::needRoot();\n\t\t}\n\t}\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202412030');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.5')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.5 to 2.2.6\", false);\n\tFroxlor::updateToVersion('2.2.6');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.6')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.6 to 2.2.7\", false);\n\tFroxlor::updateToVersion('2.2.7');\n}\n\nif (Froxlor::isFroxlorVersion('2.2.7')) {\n\tUpdate::showUpdateStep(\"Updating from 2.2.7 to 2.2.8\", false);\n\tFroxlor::updateToVersion('2.2.8');\n}\n\n"
  },
  {
    "path": "install/updates/froxlor/update_2.3.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\n\nif (!defined('_CRON_UPDATE')) {\n\tif (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) {\n\t\theader('Location: ../../../../index.php');\n\t\texit();\n\t}\n}\n\nif (Froxlor::isDatabaseVersion('202412030')) {\n\tUpdate::showUpdateStep(\"Enhancing customer table\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_CUSTOMERS . \"` ADD `shell_allowed` tinyint(1) NOT NULL DEFAULT 0;\");\n\tUpdate::lastStepStatus(0);\n\n\tif (Settings::Get('system.allow_customer_shell') == '1') {\n\t\tUpdate::showUpdateStep(\"Allowing shell-usage to current customers as setting is globally enabled\");\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `shell_allowed` = '1';\");\n\t\tUpdate::lastStepStatus(0);\n\t}\n\n\tFroxlor::updateToDbVersion('202508310');\n\n\tUpdate::showUpdateStep(\"Updating from 2.2.8 to 2.3.0-dev1\", false);\n\tFroxlor::updateToVersion('2.3.0-dev1');\n}\n\nif (Froxlor::isDatabaseVersion('202508310')) {\n\tUpdate::showUpdateStep(\"Remove old settings\");\n\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_SETTINGS . \"` WHERE `settinggroup` = 'system' AND `varname` = 'perl_path'\");\n\tUpdate::lastStepStatus(0);\n\n\tif (Settings::Get('system.webserver') == 'lighttpd') {\n\t\t$system_alt_webserver = $_POST['system_alt_webserver'] ?? 'apache2';\n\t\tUpdate::showUpdateStep(\"Switching from lighttpd to \" . $system_alt_webserver);\n\t\tSettings::Set('system.webserver', $system_alt_webserver);\n\t\tSettings::Set('system.apache24', 1);\n\t\tUpdate::lastStepStatus(0);\n\t}\n\n\tFroxlor::updateToDbVersion('202509010');\n}\n\nif (Froxlor::isDatabaseVersion('202509010')) {\n\tUpdate::showUpdateStep(\"Adding new table for user ssh-keys\");\n\tDatabase::query(\"DROP TABLE IF EXISTS `panel_sshkeys`;\");\n\t$sql = \"CREATE TABLE `panel_sshkeys` (\n\t  `id` int(11) NOT NULL auto_increment,\n\t  `customerid` int(11) NOT NULL,\n\t  `ftp_user_id` int(20) NOT NULL,\n\t  `ssh_pubkey` text NOT NULL,\n\t  `description` varchar(255) NOT NULL DEFAULT '',\n\t  PRIMARY KEY  (id)\n\t) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;\";\n\tDatabase::query($sql);\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202509060');\n}\n\nif (Froxlor::isDatabaseVersion('202509060')) {\n\tUpdate::showUpdateStep(\"Disabling OCSP for Let's Encrypt enabled domains, as service is EOL\");\n\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `ocsp_stapling` = '0' WHERE `letsencrypt` = '1';\");\n\tUpdate::lastStepStatus(0);\n\n\t// clear templates cache\n\tUpdate::cleanOldFiles([\n\t\t'cache/*'\n\t]);\n\n\tFroxlor::updateToDbVersion('202509120');\n}\n\nif (Froxlor::isDatabaseVersion('202509120')) {\n\tUpdate::showUpdateStep(\"Adding new settings\");\n\tSettings::AddNew(\"system.http3_support\", \"0\");\n\tUpdate::lastStepStatus(0);\n\n\tUpdate::showUpdateStep(\"Adding http3 field to domain table\");\n\tDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_DOMAINS . \"` ADD `http3` tinyint(1) NOT NULL default '0' AFTER `http2`;\");\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202509210');\n}\n\nif (Froxlor::isDatabaseVersion('202509210')) {\n\tUpdate::showUpdateStep(\"Adding new table for email sender aliases\");\n\tDatabase::query(\"DROP TABLE IF EXISTS `mail_sender_aliases`;\");\n\t$sql = \"CREATE TABLE `mail_sender_aliases` (\n\t  `id` int(11) NOT NULL auto_increment,\n\t  `email` varchar(255) NOT NULL,\n\t  `allowed_sender` varchar(255) NOT NULL,\n\t  PRIMARY KEY  (`id`),\n\t  UNIQUE KEY `email_sender` (`email`, `allowed_sender`)\n\t) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;\";\n\tDatabase::query($sql);\n\t$mail_enable_allow_sender = $_POST['mail_enable_allow_sender'] ?? 0;\n\tSettings::AddNew('mail.enable_allow_sender', $mail_enable_allow_sender);\n\t$mail_allow_external_domains = $_POST['mail_allow_external_domains'] ?? 0;\n\tSettings::AddNew('mail.allow_external_domains', $mail_allow_external_domains);\n\tUpdate::lastStepStatus(0);\n\n\t$to_clean = [\n\t\t'lib/configfiles/gentoo.xml',\n\t];\n\tUpdate::cleanOldFiles($to_clean);\n\n\tFroxlor::updateToDbVersion('202509270');\n}\n\nif (Froxlor::isDatabaseVersion('202509270')) {\n\n\tSettings::AddNew('system.distro_mismatch', '0');\n\tFroxlor::updateToDbVersion('202511020');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.0-dev1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.0-dev1 to 2.3.0-rc1\", false);\n\tFroxlor::updateToVersion('2.3.0-rc1');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.0-rc1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.0-rc1 to 2.3.0\", false);\n\tFroxlor::updateToVersion('2.3.0');\n}\n\nif (Froxlor::isDatabaseVersion('202511020')) {\n\n\tSettings::AddNew('system.report_web_bccadmin', '0');\n\tFroxlor::updateToDbVersion('202512090');\n}\n\nif (Froxlor::isDatabaseVersion('202512090')) {\n\n\t$to_clean = [\n\t\t'install/updates/froxlor/update_0.10.inc.php',\n\t\t'install/updates/preconfig/preconfig_0.10.inc.php',\n\t\t'lib/Froxlor/Cron/Http/Lighttpd.php',\n\t\t'lib/Froxlor/Cron/Http/LighttpdFcgi.php',\n\t];\n\tUpdate::cleanOldFiles($to_clean);\n\n\tFroxlor::updateToDbVersion('202512280');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.0')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.0 to 2.3.1\", false);\n\tFroxlor::updateToVersion('2.3.1');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.1')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.1 to 2.3.2\", false);\n\tFroxlor::updateToVersion('2.3.2');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.2')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.2 to 2.3.3\", false);\n\tFroxlor::updateToVersion('2.3.3');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.3')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.3 to 2.3.4\", false);\n\tFroxlor::updateToVersion('2.3.4');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.4')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.4 to 2.3.5\", false);\n\tFroxlor::updateToVersion('2.3.5');\n}\n\nif (Froxlor::isDatabaseVersion('202512280')) {\n\n\tUpdate::showUpdateStep(\"Adding new settings\");\n\t$system_webserver_serveradmin = $_POST['system_webserver_serveradmin'] ?? 'customer';\n\tif (!in_array($system_webserver_serveradmin, ['customer', 'admin', 'global', 'none'])) {\n\t\t$system_webserver_serveradmin = 'customer';\n\t}\n\tSettings::AddNew(\"system.webserver_serveradmin\", $system_webserver_serveradmin);\n\tUpdate::lastStepStatus(0);\n\n\tFroxlor::updateToDbVersion('202603100');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.5')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.5 to 2.3.6\", false);\n\tFroxlor::updateToVersion('2.3.6');\n}\n\nif (Froxlor::isFroxlorVersion('2.3.6')) {\n\tUpdate::showUpdateStep(\"Updating from 2.3.6 to 2.3.7\", false);\n\tFroxlor::updateToVersion('2.3.7');\n}\n"
  },
  {
    "path": "install/updates/index.html",
    "content": ""
  },
  {
    "path": "install/updates/preconfig/index.html",
    "content": ""
  },
  {
    "path": "install/updates/preconfig/preconfig_2.0.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Froxlor;\nuse Froxlor\\FileDir;\nuse Froxlor\\Config\\ConfigParser;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\n\n$preconfig = [\n\t'title' => '2.0.x updates',\n\t'fields' => []\n];\n$return = [];\n\nif (Update::versionInUpdate($current_version, '2.0.0-beta1')) {\n\t$description = 'We have rearranged the settings and split them into basic and advanced categories. This makes it easier for users who do not need all the detailed or very specific settings and options and gives a better overview of the basic/mostly used settings.';\n\t$question = '<strong>Chose settings mode (you can change that at any time)</strong>';\n\t$return['panel_settings_mode'] = [\n\t\t'type' => 'select',\n\t\t'select_var' => [\n\t\t\t0 => 'Basic',\n\t\t\t1 => 'Advanced'\n\t\t],\n\t\t'selected' => 1,\n\t\t'label' => $question,\n\t\t'prior_infotext' => $description\n\t];\n\n\t$description = 'The configuration page now can preselect a distribution, please select your current distribution';\n\t$question = '<strong>Select distribution</strong>';\n\t$config_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/');\n\t// show list of available distro's\n\t$distros = glob($config_dir . '*.xml');\n\t// selection is required $distributions_select[''] = '-';\n\t// read in all the distros\n\tforeach ($distros as $_distribution) {\n\t\t// get configparser object\n\t\t$dist = new ConfigParser($_distribution);\n\t\t// store in tmp array\n\t\t$distributions_select[str_replace(\".xml\", \"\", strtolower(basename($_distribution)))] = $dist->getCompleteDistroName();\n\t}\n\t// sort by distribution name\n\tasort($distributions_select);\n\t$return['system_distribution'] = [\n\t\t'type' => 'select',\n\t\t'select_var' => $distributions_select,\n\t\t'selected' => '',\n\t\t'label' => $question,\n\t\t'prior_infotext' => $description\n\t];\n}\n\nif (Update::versionInUpdate($current_db_version, '202301120')) {\n\t$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), \"/\");\n\t$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), \"/\");\n\tif ((int) Settings::Get('system.leenabled') == 1 && $acmesh_challenge_dir != $recommended) {\n\t\t$has_preconfig = true;\n\t\t$description = 'ACME challenge docroot from settings differs from the current installation directory.';\n\t\t$question = '<strong>Validate Let\\'s Encrypt challenge path (recommended value: ' . $recommended . ')</strong>';\n\t\t$return['system_letsencryptchallengepath_upd'] = [\n\t\t\t'type' => 'text',\n\t\t\t'value' => $recommended,\n\t\t\t'placeholder' => $acmesh_challenge_dir,\n\t\t\t'label' => $question,\n\t\t\t'prior_infotext' => $description,\n\t\t\t'mandatory' => true,\n\t\t];\n\t}\n}\n\nif (Update::versionInUpdate($current_db_version, '202301180')) {\n\tif ((int) Settings::Get('system.leenabled') == 1) {\n\t\t$has_preconfig = true;\n\t\t$description = 'Froxlor now supports to set an external DNS resolver for the Let\\'s Encrypt pre-check.';\n\t\t$question = '<strong>Specify a DNS resolver IP (recommended value: 1.1.1.1 or similar)</strong>';\n\t\t$return['system_le_domain_dnscheck_resolver'] = [\n\t\t\t'type' => 'text',\n\t\t\t'pattern' => '^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$|^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$|^\\s*$',\n\t\t\t'value' => '1.1.1.1',\n\t\t\t'placeholder' => '1.1.1.1',\n\t\t\t'label' => $question,\n\t\t\t'prior_infotext' => $description,\n\t\t];\n\t}\n}\n\n$preconfig['fields'] = $return;\nreturn $preconfig;\n"
  },
  {
    "path": "install/updates/preconfig/preconfig_2.1.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Install\\Update;\n\n$preconfig = [\n\t'title' => '2.1.x updates',\n\t'fields' => []\n];\n$return = [];\n\nif (Update::versionInUpdate($current_version, '2.1.0-dev1')) {\n\n}\n\n$preconfig['fields'] = $return;\nreturn $preconfig;\n"
  },
  {
    "path": "install/updates/preconfig/preconfig_2.2.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Install\\Update;\n\n$preconfig = [\n\t'title' => '2.2.x updates',\n\t'fields' => []\n];\n$return = [];\n\nif (Update::versionInUpdate($current_version, '2.2.0-dev1')) {\n\t$has_preconfig = true;\n\t$description = 'Froxlor now features antispam configurations using rspamd. Would you like to enable the antispam feature (required re-configuration of services)?<br><strong>ATTENTION:</strong> When not enabled and the former DomainKey feature was used, keep in mind that all existing domainkeys for all domain are being removed and the dkim-flag disabled for the domains.';\n\t$question = '<strong>Enable antispam (recommended)</strong>&nbsp;';\n\t$return['antispam_activated'] = [\n\t\t'type' => 'checkbox',\n\t\t'value' => 1,\n\t\t'checked' => 0,\n\t\t'label' => $question,\n\t\t'prior_infotext' => $description\n\t];\n}\n\n$preconfig['fields'] = $return;\nreturn $preconfig;\n"
  },
  {
    "path": "install/updates/preconfig/preconfig_2.3.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\n\n$preconfig = [\n\t'title' => '2.3.x updates',\n\t'fields' => []\n];\n$return = [];\n\nif (Update::versionInUpdate($current_db_version, '202509010')) {\n\tif (Settings::Get('system.webserver') == 'lighttpd') {\n\t\t$has_preconfig = true;\n\t\t$description = 'You seem to be using \"lighttpd\" as webserver, froxlor 2.3 no longer supports this webserver. Please select an alternative one. Remember to configure the service after the update!';\n\t\t$question = '<strong>Switch webserver to:</strong>&nbsp;';\n\t\t$return['system_alt_webserver'] = [\n\t\t\t'type' => 'select',\n\t\t\t'select_var' => [\n\t\t\t\t'apache2' => 'Apache 2.4',\n\t\t\t\t'nginx' => 'Nginx'\n\t\t\t],\n\t\t\t'selected' => 'apache2',\n\t\t\t'label' => $question,\n\t\t\t'prior_infotext' => $description\n\t\t];\n\t}\n}\n\nif (Update::versionInUpdate($current_db_version, '202509270')) {\n\n\t$has_preconfig = true;\n\t$description = 'It is now possible for customers to add \"allowed sender\" addresses for email-accounts to send from if enabled.';\n\t$question = '<strong>Enable \"allowed sender\" for customers?</strong>&nbsp;';\n\t$return['mail_enable_allow_sender'] = [\n\t\t'type' => 'checkbox',\n\t\t'value' => 1,\n\t\t'checked' => 0,\n\t\t'label' => $question,\n\t\t'prior_infotext' => $description\n\t];\n\t$description = 'By default, a customer cannot use domains that are not added to the account for the \"allowed sender\" feature. You can specify if you would like to allow adding addresses from external domains not managed by your installation.';\n\t$question = '<strong>Allow external domains for \"allowed sender\"?</strong>&nbsp;';\n\t$return['mail_allow_external_domains'] = [\n\t\t'type' => 'checkbox',\n\t\t'value' => 1,\n\t\t'checked' => 0,\n\t\t'label' => $question,\n\t\t'prior_infotext' => $description\n\t];\n}\n\nif (Update::versionInUpdate($current_db_version, '202603100')) {\n\n\tif (Settings::Get('system.webserver') == 'apache2') {\n\t\t$has_preconfig = true;\n\t\t$description = 'Select default value for the \"ServerAdmin\" value which is shown on webserver error pages (depending on ServerSignature setting).';\n\t\t$question = '<strong>Which email address should be shown in ServerAdmin directive?</strong>&nbsp;';\n\t\t$return['system_webserver_serveradmin'] = [\n\t\t\t'type' => 'select',\n\t\t\t'select_var' => [\n\t\t\t\t'customer' => 'Customer email address (default)',\n\t\t\t\t'admin' => 'Admin email address',\n\t\t\t\t'global' => 'Panel admin email address',\n\t\t\t\t'none' => 'No ServerAdmin'\n\t\t\t],\n\t\t\t'selected' => 'customer',\n\t\t\t'label' => $question,\n\t\t\t'prior_infotext' => $description\n\t\t];\n\t}\n}\n\n$preconfig['fields'] = $return;\nreturn $preconfig;\n"
  },
  {
    "path": "install/updatesql.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Froxlor;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Database\\IntegrityCheck;\nuse Froxlor\\Install\\Update;\n\nif (!defined('_CRON_UPDATE')) {\n\tif (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) {\n\t\theader('Location: ../index.php');\n\t\texit();\n\t}\n}\n\n$filelog = FroxlorLogger::getInstanceOf(array(\n\t'loginname' => 'updater'\n));\n\n// if first writing does not work we'll stop, tell the user to fix it\n// and then let him try again.\ntry {\n\t$filelog->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, '-------------- START LOG --------------');\n} catch (Exception $e) {\n\tResponse::standardError('exception', $e->getMessage());\n}\n\nif (Froxlor::isFroxlor()) {\n\n\tinclude_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.0.inc.php'));\n\tinclude_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.1.inc.php'));\n\tinclude_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.2.inc.php'));\n\tinclude_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.3.inc.php'));\n\n\t// Check Froxlor - database integrity (only happens after all updates are done, so we know the db-layout is okay)\n\tUpdate::showUpdateStep(\"Checking database integrity\");\n\n\t$integrity = new IntegrityCheck();\n\tif (!$integrity->checkAll()) {\n\t\tUpdate::lastStepStatus(1, 'Integrity could not be validated');\n\t\tUpdate::showUpdateStep(\"Trying to automatically restore integrity\");\n\t\tif (!$integrity->fixAll()) {\n\t\t\tUpdate::lastStepStatus(2, 'failed', 'Check \"database validation\" as admin on the left-side menu to see where the problem is');\n\t\t} else {\n\t\t\tUpdate::lastStepStatus(0, 'Integrity restored');\n\t\t}\n\t} else {\n\t\tUpdate::lastStepStatus(0);\n\t}\n\n\t// reset cached version check\n\tSettings::Set('system.updatecheck_data', '');\n\n\t$filelog->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, '--------------- END LOG ---------------');\n\tunset($filelog);\n}\n"
  },
  {
    "path": "lib/Froxlor/Ajax/Ajax.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Ajax;\n\nuse Exception;\nuse DateTime;\nuse Froxlor\\Config\\ConfigDisplay;\nuse Froxlor\\Config\\ConfigParser;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Http\\HttpClient;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\n\nclass Ajax\n{\n\tprotected string $action;\n\tprotected string $theme;\n\tprotected array $userinfo;\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic function __construct()\n\t{\n\t\t$this->action = Request::any('action');\n\t\t$this->theme = Request::any('theme', 'Froxlor');\n\n\t\tUI::sendHeaders();\n\t\tUI::sendSslHeaders();\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic function handle()\n\t{\n\t\t$this->userinfo = $this->getValidatedSession();\n\n\t\tswitch ($this->action) {\n\t\t\tcase 'newsfeed':\n\t\t\t\treturn $this->getNewsfeed();\n\t\t\tcase 'updatecheck':\n\t\t\t\treturn $this->getUpdateCheck();\n\t\t\tcase 'searchglobal':\n\t\t\t\treturn $this->searchGlobal();\n\t\t\tcase 'updatetablelisting':\n\t\t\t\treturn $this->updateTablelisting();\n\t\t\tcase 'resettablelisting':\n\t\t\t\treturn $this->resetTablelisting();\n\t\t\tcase 'editapikey':\n\t\t\t\treturn $this->editApiKey();\n\t\t\tcase 'getConfigDetails':\n\t\t\t\treturn $this->getConfigDetails();\n\t\t\tcase 'getConfigJsonExport':\n\t\t\t\treturn $this->getConfigJsonExport();\n\t\t\tcase 'loadLanguageString':\n\t\t\t\treturn $this->loadLanguageString();\n\t\t\tdefault:\n\t\t\t\treturn $this->errorResponse('Action not found!');\n\t\t}\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function getValidatedSession(): array\n\t{\n\t\tif (CurrentUser::hasSession() == false) {\n\t\t\tthrow new Exception(\"No valid session\");\n\t\t}\n\t\treturn CurrentUser::getData();\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function getNewsfeed()\n\t{\n\t\tUI::initTwig();\n\n\t\t$feed = \"https://inside.froxlor.org/news/\";\n\n\t\t// Set custom feed if provided\n\t\t$role = Request::get('role');\n\t\tif ($role == \"customer\") {\n\t\t\t$custom_feed = Settings::Get(\"customer.news_feed_url\");\n\t\t\tif (!empty(trim($custom_feed))) {\n\t\t\t\t$feed = $custom_feed;\n\t\t\t}\n\t\t}\n\n\t\t// Check for simplexml_load_file\n\t\tif (!function_exists(\"simplexml_load_file\")) {\n\t\t\treturn $this->errorResponse([\n\t\t\t\t\"Newsfeed not available due to missing php-simplexml extension\",\n\t\t\t\t\"Please install the php-simplexml extension in order to view our newsfeed.\"\n\t\t\t]);\n\t\t}\n\n\t\t// Check for curl_version\n\t\tif (!function_exists('curl_version')) {\n\t\t\treturn $this->errorResponse([\n\t\t\t\t\"Newsfeed not available due to missing php-curl extension\",\n\t\t\t\t\"Please install the php-curl extension in order to view our newsfeed.\"\n\t\t\t]);\n\t\t}\n\n\t\t$output = HttpClient::urlGet($feed);\n\t\t$news = simplexml_load_string(trim($output));\n\n\t\tif ($news === false) {\n\t\t\t$err = [];\n\t\t\tforeach (libxml_get_errors() as $error) {\n\t\t\t\t$err[] = $error->message;\n\t\t\t}\n\t\t\treturn $this->errorResponse(\n\t\t\t\t$err\n\t\t\t);\n\t\t}\n\n\t\t// Handle items\n\t\tif ($news) {\n\t\t\t$items = null;\n\n\t\t\tfor ($i = 0; $i < 3; $i++) {\n\t\t\t\t$item = $news->channel->item[$i];\n\n\t\t\t\t$title = (string)$item->title;\n\t\t\t\t$link = (string)$item->link;\n\t\t\t\t$date = date(\"d.m.Y\", strtotime($item->pubDate));\n\t\t\t\t$content = preg_replace(\"/[\\r\\n]+/\", \" \", strip_tags($item->description));\n\t\t\t\t$content = substr($content, 0, 150) . \"...\";\n\n\t\t\t\t$items .= UI::twig()->render(UI::validateThemeTemplate('/user/newsfeeditem.html.twig', $this->theme), [\n\t\t\t\t\t'link' => $link,\n\t\t\t\t\t'title' => $title,\n\t\t\t\t\t'date' => $date,\n\t\t\t\t\t'content' => $content\n\t\t\t\t]);\n\t\t\t}\n\n\t\t\treturn $this->jsonResponse($items);\n\t\t} else {\n\t\t\treturn $this->errorResponse('No Newsfeeds available at the moment.');\n\t\t}\n\t}\n\n\tpublic function errorResponse($message, int $response_code = 500)\n\t{\n\t\theader(\"Content-Type: application/json\");\n\t\treturn \\Froxlor\\Api\\Response::jsonErrorResponse($message, $response_code);\n\t}\n\n\tpublic function jsonResponse($value, int $response_code = 200)\n\t{\n\t\theader(\"Content-Type: application/json\");\n\t\treturn \\Froxlor\\Api\\Response::jsonResponse($value, $response_code);\n\t}\n\n\tprivate function getUpdateCheck()\n\t{\n\t\tUI::initTwig();\n\n\t\ttry {\n\t\t\t$force = Request::get('force', 0);\n\t\t\t$json_result = \\Froxlor\\Api\\Commands\\Froxlor::getLocal($this->userinfo, ['force' => $force])->checkUpdate();\n\t\t\t$result = json_decode($json_result, true)['data'];\n\t\t\t$result['full_version'] = Froxlor::getFullVersion();\n\t\t\t$result['dbversion'] = Froxlor::DBVERSION;\n\t\t\t$uc_data = Update::getUpdateCheckData();\n\t\t\t$result['last_update_check'] = $uc_data['ts'];\n\t\t\t$result['channel'] = Settings::Get('system.update_channel');\n\n\t\t\t$result_rendered = UI::twig()->render(UI::validateThemeTemplate('/misc/version_top.html.twig', $this->theme), $result);\n\t\t\treturn $this->jsonResponse($result_rendered);\n\t\t} catch (Exception $e) {\n\t\t\t// don't display anything if just not allowed due to permissions\n\t\t\tif ($e->getCode() != 403) {\n\t\t\t\treturn $this->errorResponse($e->getMessage(), $e->getCode());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * search globally in various resources\n\t */\n\tprivate function searchGlobal()\n\t{\n\t\t$searchtext = Request::any('searchtext');\n\n\t\t$result = [];\n\n\t\t// settings\n\t\t$result_settings = [];\n\t\tif (isset($this->userinfo['adminsession']) && $this->userinfo['adminsession'] == 1 && $this->userinfo['change_serversettings'] == 1) {\n\t\t\t$result_settings = GlobalSearch::searchSettings($searchtext, $this->userinfo);\n\t\t}\n\n\t\t// all searchable entities\n\t\t$result_entities = GlobalSearch::searchGlobal($searchtext, $this->userinfo);\n\n\t\t$result = array_merge($result_settings, $result_entities);\n\n\t\treturn $this->jsonResponse($result);\n\t}\n\n\tprivate function updateTablelisting()\n\t{\n\t\t$columns = [];\n\t\tforeach ((Request::post('columns') ?? []) as $value) {\n\t\t\t$columns[] = $value;\n\t\t}\n\t\tif (!empty($columns)) {\n\t\t\t$columns = Listing::storeColumnListingForUser([Request::get('listing') => $columns]);\n\t\t\treturn $this->jsonResponse($columns);\n\t\t}\n\t\treturn $this->errorResponse('At least one column must be selected', 406);\n\t}\n\n\tprivate function resetTablelisting()\n\t{\n\t\tListing::deleteColumnListingForUser([Request::get('listing') => []]);\n\t\treturn $this->jsonResponse([]);\n\t}\n\n\tprivate function editApiKey()\n\t{\n\t\t$keyid = Request::post('id', 0);\n\t\t$allowed_from = Request::post('allowed_from', \"\");\n\t\t$valid_until = Request::post('valid_until', \"\");\n\n\t\tif (empty($keyid)) {\n\t\t\treturn $this->errorResponse('Invalid call', 406);\n\t\t}\n\n\t\t// validate allowed_from\n\t\tif (!empty($allowed_from)) {\n\t\t\t$ip_list = array_map('trim', explode(\",\", $allowed_from));\n\t\t\t$_check_list = $ip_list;\n\t\t\tforeach ($_check_list as $idx => $ip) {\n\t\t\t\tif (Validate::validate_ip2($ip, true, 'invalidip', true, true, true) == false) {\n\t\t\t\t\treturn $this->errorResponse('Invalid ip address', 406);\n\t\t\t\t}\n\t\t\t\t// check for cidr\n\t\t\t\tif (strpos($ip, '/') !== false) {\n\t\t\t\t\t$ipparts = explode(\"/\", $ip);\n\t\t\t\t\t// shorten IP\n\t\t\t\t\t$ip = inet_ntop(inet_pton($ipparts[0]));\n\t\t\t\t\t// re-add cidr\n\t\t\t\t\t$ip .= '/' . $ipparts[1];\n\t\t\t\t} else {\n\t\t\t\t\t// shorten IP\n\t\t\t\t\t$ip = inet_ntop(inet_pton($ip));\n\t\t\t\t}\n\t\t\t\t$ip_list[$idx] = $ip;\n\t\t\t}\n\t\t\t$allowed_from = implode(\",\", array_unique($ip_list));\n\t\t}\n\n\t\tif (!empty($valid_until)) {\n\t\t\t$valid_until_db = DateTime::createFromFormat('Y-m-d\\TH:i', $valid_until)->format('U');\n\t\t} else {\n\t\t\t$valid_until_db = -1;\n\t\t}\n\n\t\t$upd_stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_API_KEYS . \"` SET\n\t\t\t`valid_until` = :vu, `allowed_from` = :af\n\t\t\tWHERE `id` = :keyid AND `adminid` = :aid AND `customerid` = :cid\n\t\t\");\n\t\tif ((int)$this->userinfo['adminsession'] == 1) {\n\t\t\t$cid = 0;\n\t\t} else {\n\t\t\t$cid = $this->userinfo['customerid'];\n\t\t}\n\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t'keyid' => $keyid,\n\t\t\t'af' => $allowed_from,\n\t\t\t'vu' => $valid_until_db,\n\t\t\t'aid' => $this->userinfo['adminid'],\n\t\t\t'cid' => $cid\n\t\t]);\n\t\treturn $this->jsonResponse(['allowed_from' => $allowed_from, 'valid_until' => $valid_until]);\n\t}\n\n\t/**\n\t * return parsed commands/files of configuration templates\n\t */\n\tprivate function getConfigDetails()\n\t{\n\t\tif (isset($this->userinfo['adminsession']) && $this->userinfo['adminsession'] == 1 && $this->userinfo['change_serversettings'] == 1) {\n\t\t\t$distribution = Request::post('distro', \"\");\n\t\t\t$section = Request::post('section', \"\");\n\t\t\t$daemon = Request::post('daemon', \"\");\n\n\t\t\t// validate distribution config-xml exists\n\t\t\t$config_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/');\n\t\t\tif (!file_exists($config_dir . \"/\" . $distribution . \".xml\")) {\n\t\t\t\treturn $this->errorResponse(\"Unknown distribution. The configuration could not be found.\");\n\t\t\t}\n\t\t\t// read in all configurations\n\t\t\t$configfiles = new ConfigParser($config_dir . \"/\" . $distribution . \".xml\");\n\t\t\t// get the services\n\t\t\t$services = $configfiles->getServices();\n\t\t\t// validate selected service exists for this distribution\n\t\t\tif (!isset($services[$section])) {\n\t\t\t\treturn $this->errorResponse(\"Unknown category for selected distribution\");\n\t\t\t}\n\t\t\t// get the daemons\n\t\t\t$daemons = $services[$section]->getDaemons();\n\t\t\t// validate selected daemon exists for this section\n\t\t\tif (!isset($daemons[$daemon])) {\n\t\t\t\treturn $this->errorResponse(\"Unknown service for selected category\");\n\t\t\t}\n\t\t\t// finally the config-steps\n\t\t\t$confarr = $daemons[$daemon]->getConfig();\n\t\t\t// get parsed content\n\t\t\tUI::initTwig();\n\t\t\t$content = ConfigDisplay::fromConfigArr($confarr, $configfiles->distributionEditor, $this->theme);\n\n\t\t\treturn $this->jsonResponse([\n\t\t\t\t'title' => $configfiles->getCompleteDistroName() . '&nbsp;&raquo;&nbsp' . $services[$section]->title . '&nbsp;&raquo;&nbsp' . $daemons[$daemon]->title,\n\t\t\t\t'content' => $content\n\t\t\t]);\n\t\t}\n\t\treturn $this->errorResponse('Not allowed', 403);\n\t}\n\n\t/**\n\t * download JSON export of config-selection\n\t */\n\tprivate function getConfigJsonExport()\n\t{\n\t\tif (isset($this->userinfo['adminsession']) && $this->userinfo['adminsession'] == 1 && $this->userinfo['change_serversettings'] == 1) {\n\t\t\t$params = $_GET;\n\t\t\tunset($params['action']);\n\t\t\tunset($params['finish']);\n\t\t\tunset($params['csrf_token']);\n\t\t\theader('Content-disposition: attachment; filename=froxlor-config-' . time() . '.json');\n\t\t\treturn $this->jsonResponse($params);\n\t\t}\n\t\treturn $this->errorResponse('Not allowed', 403);\n\t}\n\n\t/**\n\t * loads a given language string by its identifier\n\t */\n\tprivate function loadLanguageString()\n\t{\n\t\t$langid = Request::post('langid', \"\");\n\t\tif (preg_match('/^([a-zA-Z\\.]+)$/', $langid)) {\n\t\t\treturn $this->jsonResponse(lng($langid));\n\t\t}\n\t\treturn $this->errorResponse('Invalid identifier: ' . $langid, 406);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Ajax/GlobalSearch.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Ajax;\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Domains;\nuse Froxlor\\Api\\Commands\\EmailDomains;\nuse Froxlor\\Api\\Commands\\Emails;\nuse Froxlor\\Api\\Commands\\FpmDaemons;\nuse Froxlor\\Api\\Commands\\Ftps;\nuse Froxlor\\Api\\Commands\\HostingPlans;\nuse Froxlor\\Api\\Commands\\IpsAndPorts;\nuse Froxlor\\Api\\Commands\\Mysqls;\nuse Froxlor\\Api\\Commands\\PhpSettings;\nuse Froxlor\\Api\\Commands\\SubDomains;\nuse Froxlor\\Froxlor;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Collection;\n\nclass GlobalSearch\n{\n\tprotected array $userinfo;\n\n\tpublic static function searchSettings(string $searchtext, array $userinfo): array\n\t{\n\t\t$result = [];\n\t\tif ($searchtext && strlen(trim($searchtext)) > 2) {\n\t\t\t$processed = [];\n\t\t\t$stparts = explode(\" \", $searchtext);\n\t\t\tforeach ($stparts as $searchtext) {\n\t\t\t\t$searchtext = trim($searchtext);\n\t\t\t\tif (preg_match('/^([a-z]+):$/', $searchtext, $matches)) {\n\t\t\t\t\t// only search settings if specific search is 'settings', else skip\n\t\t\t\t\tif ($matches[1] == 'settings') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$settings_data = PhpHelper::loadConfigArrayDir(Froxlor::getInstallDir() . '/actions/admin/settings/');\n\t\t\t\t$results = [];\n\t\t\t\tif (!isset($processed['settings'])) {\n\t\t\t\t\t$processed['settings'] = [];\n\t\t\t\t}\n\t\t\t\tPhpHelper::recursive_array_search($searchtext, $settings_data, $results);\n\t\t\t\tforeach ($results as $pathkey) {\n\t\t\t\t\t$pk = explode(\".\", $pathkey);\n\t\t\t\t\tif (count($pk) > 4) {\n\t\t\t\t\t\t$settingkey = $pk[0] . '.' . $pk[1] . '.' . $pk[2] . '.' . $pk[3];\n\t\t\t\t\t\tif (isset($settings_data[$pk[0]][$pk[1]]['advanced_mode']) && $settings_data[$pk[0]][$pk[1]]['advanced_mode'] && (int)Settings::Get('panel.settings_mode') == 0) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (is_array($processed['settings']) && !array_key_exists($settingkey, $processed['settings'])) {\n\t\t\t\t\t\t\t$processed['settings'][$settingkey] = true;\n\t\t\t\t\t\t\t$sresult = $settings_data[$pk[0]][$pk[1]][$pk[2]][$pk[3]];\n\t\t\t\t\t\t\tif (isset($sresult['advanced_mode']) && $sresult['advanced_mode'] && (int)Settings::Get('panel.settings_mode') == 0) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif ($sresult['type'] != 'hidden') {\n\t\t\t\t\t\t\t\tif (!isset($result['settings'])) {\n\t\t\t\t\t\t\t\t\t$result['settings'] = [];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t$result['settings'][] = [\n\t\t\t\t\t\t\t\t\t'title' => (is_array($sresult['label']) ? $sresult['label']['title'] : $sresult['label']),\n\t\t\t\t\t\t\t\t\t'href' => 'admin_settings.php?page=overview&part=' . $pk[1] . '&em=' . $pk[3]\n\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t} // not hidden\n\t\t\t\t\t\t} // if not processed\n\t\t\t\t\t} // correct settingkey\n\t\t\t\t} // foreach\n\t\t\t} // foreach\n\t\t} // searchtext min 3 chars\n\t\treturn $result;\n\t}\n\n\t/**\n\t *\n\t */\n\tpublic static function searchGlobal(string $searchtext, array $userinfo): array\n\t{\n\t\t$result = [];\n\t\tif ($searchtext && strlen(trim($searchtext)) > 2) {\n\t\t\t$processed = [];\n\n\t\t\t$stparts = explode(\" \", $searchtext);\n\t\t\t$module = \"\";\n\n\t\t\tforeach ($stparts as $searchtext) {\n\t\t\t\t$searchtext = trim($searchtext);\n\n\t\t\t\tif (preg_match('/^([a-z]+):$/', $searchtext, $matches)) {\n\t\t\t\t\t$module = $matches[1];\n\t\t\t\t\tif ($matches[1] == 'settings') {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// admin\n\t\t\t\tif (isset($userinfo['adminsession']) && $userinfo['adminsession'] == 1) {\n\t\t\t\t\t$toSearch = [\n\t\t\t\t\t\t// customers\n\t\t\t\t\t\t'customer' => [\n\t\t\t\t\t\t\t'class' => Customers::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'c.loginname',\n\t\t\t\t\t\t\t\t'c.name',\n\t\t\t\t\t\t\t\t'c.firstname',\n\t\t\t\t\t\t\t\t'c.company',\n\t\t\t\t\t\t\t\t'c.street',\n\t\t\t\t\t\t\t\t'c.zipcode',\n\t\t\t\t\t\t\t\t'c.city',\n\t\t\t\t\t\t\t\t'c.email',\n\t\t\t\t\t\t\t\t'c.customernumber',\n\t\t\t\t\t\t\t\t'c.custom_notes'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'loginname',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\User', 'getCorrectFullUserDetails'],\n\t\t\t\t\t\t\t\t'href' => 'admin_customers.php?page=customers&searchfield=c.loginname&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// domains\n\t\t\t\t\t\t'domains' => [\n\t\t\t\t\t\t\t'class' => Domains::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'd.domain',\n\t\t\t\t\t\t\t\t'd.domain_ace',\n\t\t\t\t\t\t\t\t'd.documentroot'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'domain_ace',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'domain_ace',\n\t\t\t\t\t\t\t\t'href' => 'admin_domains.php?page=domains&searchfield=d.domain_ace&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// ips and ports\n\t\t\t\t\t\t'ipsandports' => [\n\t\t\t\t\t\t\t'class' => IpsAndPorts::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'ip',\n\t\t\t\t\t\t\t\t'vhostcontainer',\n\t\t\t\t\t\t\t\t'specialsettings'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'ip',\n\t\t\t\t\t\t\t'result_groupkey' => 'ip',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'ip',\n\t\t\t\t\t\t\t\t'href' => 'admin_ipsandports.php?page=ipsandports&searchfield=ip&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// hosting-plans\n\t\t\t\t\t\t'hostingplans' => [\n\t\t\t\t\t\t\t'class' => HostingPlans::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'p.name',\n\t\t\t\t\t\t\t\t'p.description'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'id',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'name',\n\t\t\t\t\t\t\t\t'href' => 'admin_plans.php?page=overview&searchfield=id&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// PHP configs\n\t\t\t\t\t\t'phpconfigs' => [\n\t\t\t\t\t\t\t'class' => PhpSettings::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'c.description',\n\t\t\t\t\t\t\t\t'fd.description',\n\t\t\t\t\t\t\t\t'c.binary'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'id',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'description',\n\t\t\t\t\t\t\t\t'href' => 'admin_phpsettings.php?page=overview&searchfield=id&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// FPM daemons\n\t\t\t\t\t\t'fpmconfigs' => [\n\t\t\t\t\t\t\t'class' => FpmDaemons::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'description',\n\t\t\t\t\t\t\t\t'reload_cmd'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'id',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'description',\n\t\t\t\t\t\t\t\t'href' => 'admin_phpsettings.php?page=fpmdaemons&searchfield=id&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t];\n\n\t\t\t\t\tif ((bool)$userinfo['change_serversettings']) {\n\t\t\t\t\t\t// admins\n\t\t\t\t\t\t$toSearch['admins'] = [\n\t\t\t\t\t\t\t'class' => Admins::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'loginname',\n\t\t\t\t\t\t\t\t'name',\n\t\t\t\t\t\t\t\t'email',\n\t\t\t\t\t\t\t\t'custom_notes'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'loginname',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'name',\n\t\t\t\t\t\t\t\t'href' => 'admin_admins.php?page=admins&searchfield=loginname&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$toSearch = [\n\t\t\t\t\t\t// (sub)domains\n\t\t\t\t\t\t'domains' => [\n\t\t\t\t\t\t\t'class' => SubDomains::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'd.domain',\n\t\t\t\t\t\t\t\t'd.domain_ace',\n\t\t\t\t\t\t\t\t'd.documentroot'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'domain_ace',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'domain_ace',\n\t\t\t\t\t\t\t\t'href' => 'customer_domains.php?page=domains&searchfield=d.domain_ace&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// email addresses\n\t\t\t\t\t\t'emails' => [\n\t\t\t\t\t\t\t'class' => Emails::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'm.email',\n\t\t\t\t\t\t\t\t'm.email_full'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'email',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'email',\n\t\t\t\t\t\t\t\t'href' => 'customer_email.php?page=email_domain&domainid={domainid}&searchfield=m.email&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// email-domains\n\t\t\t\t\t\t'email_domains' => [\n\t\t\t\t\t\t\t'class' => EmailDomains::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'd.domain',\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'domain',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'domain',\n\t\t\t\t\t\t\t\t'href' => 'customer_email.php?page=emails&searchfield=d.domain&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// databases\n\t\t\t\t\t\t'databases' => [\n\t\t\t\t\t\t\t'class' => Mysqls::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'databasename',\n\t\t\t\t\t\t\t\t'description'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'databasename',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'databasename',\n\t\t\t\t\t\t\t\t'href' => 'customer_mysql.php?page=mysqls&searchfield=databasename&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t// ftp user\n\t\t\t\t\t\t'ftpuser' => [\n\t\t\t\t\t\t\t'class' => Ftps::class,\n\t\t\t\t\t\t\t'searchfields' => [\n\t\t\t\t\t\t\t\t'username',\n\t\t\t\t\t\t\t\t'description'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'result_key' => 'username',\n\t\t\t\t\t\t\t'result_format' => [\n\t\t\t\t\t\t\t\t'title' => ['\\\\Froxlor\\\\Ajax\\\\GlobalSearch', 'getFieldFromResult'],\n\t\t\t\t\t\t\t\t'title_args' => 'username',\n\t\t\t\t\t\t\t\t'href' => 'customer_ftp.php?page=accounts&searchfield=username&searchtext='\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t];\n\t\t\t\t}\n\n\t\t\t\t// module specific search\n\t\t\t\tif (!empty($module)) {\n\t\t\t\t\t$modSearch = $toSearch[$module] ?? [];\n\t\t\t\t\t$toSearch = [$module => $modSearch];\n\t\t\t\t}\n\n\t\t\t\tforeach ($toSearch as $entity => $edata) {\n\t\t\t\t\t$collection = (new Collection($edata['class'], $userinfo))\n\t\t\t\t\t\t->setInternal(true)\n\t\t\t\t\t\t->addParam([\n\t\t\t\t\t\t\t'sql_search' => [\n\t\t\t\t\t\t\t\t'_plainsql' => self::searchStringSql($edata['searchfields'], $searchtext)\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]);\n\t\t\t\t\tif ($collection->count() > 0) {\n\t\t\t\t\t\tif (!isset($processed[$entity])) {\n\t\t\t\t\t\t\t$processed[$entity] = [];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$group_key = $edata['result_groupkey'] ?? $edata['result_key'];\n\t\t\t\t\t\tforeach ($collection->getList() as $cresult) {\n\t\t\t\t\t\t\tif (is_array($processed[$entity]) && !array_key_exists($cresult[$group_key], $processed[$entity])) {\n\t\t\t\t\t\t\t\t$processed[$entity][$cresult[$group_key]] = true;\n\t\t\t\t\t\t\t\tif (!isset($result[$entity])) {\n\t\t\t\t\t\t\t\t\t$result[$entity] = [];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// replacer from result in href\n\t\t\t\t\t\t\t\t$href_replacer = [];\n\t\t\t\t\t\t\t\tif (preg_match_all('/\\{([a-z]+)\\}/', $edata['result_format']['href'], $href_replacer) !== false) {\n\t\t\t\t\t\t\t\t\tforeach ($href_replacer[1] as $href_field) {\n\t\t\t\t\t\t\t\t\t\t$href_field_value = self::getFieldFromResult($cresult, $href_field);\n\t\t\t\t\t\t\t\t\t\t$edata['result_format']['href'] = str_replace('{' . $href_field . '}', $href_field_value, $edata['result_format']['href']);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t$result[$entity][] = [\n\t\t\t\t\t\t\t\t\t'title' => call_user_func($edata['result_format']['title'], $cresult, ($edata['result_format']['title_args'] ?? null)),\n\t\t\t\t\t\t\t\t\t'href' => $edata['result_format']['href'] . $cresult[$edata['result_key']]\n\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} // foreach entity\n\n\t\t\t} // foreach split search-term\n\t\t}\n\t\treturn $result;\n\t}\n\n\tprivate static function searchStringSql(array $searchfields, $searchtext)\n\t{\n\t\t$result = ['sql' => [], 'values' => []];\n\t\t$result['sql'] = \"(\";\n\t\tforeach ($searchfields as $sf) {\n\t\t\t$result['sql'] .= $sf . \" LIKE :searchtext OR \";\n\t\t}\n\t\t$result['sql'] = substr($result['sql'], 0, -3) . \")\";\n\t\t$result['values'] = ['searchtext' => '%' . $searchtext . '%'];\n\t\treturn $result;\n\t}\n\n\tprivate static function getFieldFromResult(array $resultset, string $field = null)\n\t{\n\t\treturn $resultset[$field] ?? '';\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Ajax/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Api/Api.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api;\n\nuse Exception;\nuse Froxlor\\Http\\RateLimiter;\nuse Froxlor\\Settings;\nuse voku\\helper\\AntiXSS;\n\nclass Api\n{\n\tprotected array $headers;\n\n\tprotected $request = null;\n\n\t/**\n\t * Api constructor.\n\t *\n\t * @throws Exception\n\t */\n\tpublic function __construct()\n\t{\n\t\t$this->headers = getallheaders();\n\n\t\t// set header for the response\n\t\theader(\"Accept: application/json\");\n\t\theader(\"Content-Type: application/json\");\n\n\t\t// check whether API interface is enabled after all\n\t\tif (Settings::Get('api.enabled') != 1) {\n\t\t\tthrow new Exception('API is not enabled. Please contact the administrator if you think this is wrong.', 400);\n\t\t}\n\n\t\tRateLimiter::run();\n\t}\n\n\t/**\n\t * @param mixed $request\n\t *\n\t * @return Api\n\t */\n\tpublic function formatMiddleware($request): Api\n\t{\n\t\t// check auf RESTful api call\n\t\t$this->request = $request;\n\n\t\t$uri = parse_url($_SERVER[\"REQUEST_URI\"], PHP_URL_QUERY);\n\t\t// map /module/command to internal request array if match\n\t\tif (!empty($uri) && preg_match(\"/^\\/([a-z]+)\\/([a-z]+)\\/?/\", $uri, $matches)) {\n\t\t\t$request = [];\n\t\t\t$request['command'] = ucfirst($matches[1]) . '.' . $matches[2];\n\t\t\t$request['params'] = !empty($this->request) ? json_decode($this->request, true) : null;\n\t\t\t$this->request = json_encode($request);\n\t\t}\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Handle incoming api request to our backend.\n\t *\n\t * @throws Exception\n\t */\n\tpublic function handle()\n\t{\n\t\t$request = $this->request;\n\t\t// validate content\n\t\t$request = FroxlorRPC::validateRequest($request);\n\t\t$request = (new AntiXSS())->xss_clean(\n\t\t\t$this->stripcslashesDeep($request)\n\t\t);\n\n\t\t// now actually do it\n\t\t$cls = \"\\\\Froxlor\\\\Api\\\\Commands\\\\\" . $request['command']['class'];\n\t\t$method = $request['command']['method'];\n\t\t$apiObj = new $cls([\n\t\t\t'apikey' => $_SERVER['PHP_AUTH_USER'],\n\t\t\t'secret' => $_SERVER['PHP_AUTH_PW']\n\t\t], $request['params']);\n\n\t\t// call the method with the params if any\n\t\treturn $apiObj->$method();\n\t}\n\n\t/**\n\t * API PHP error handler to always return a valid JSON response\n\t *\n\t * @param mixed $errno\n\t * @param mixed $errstr\n\t * @param mixed $errfile\n\t * @param mixed $errline\n\t * @return never\n\t */\n\tpublic static function phpErrHandler($errno, $errstr, $errfile, $errline)\n\t{\n\t\tthrow new Exception('Internal PHP error: #' . $errno . ' ' . $errstr /* . ' in ' . $errfile . ':' . $errline */, 500);\n\t}\n\n\tprivate function stripcslashesDeep($value)\n\t{\n\t\treturn is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : (!empty($value) ? stripcslashes($value) : $value);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/ApiCommand.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Language;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Mailer;\n\nabstract class ApiCommand extends ApiParameter\n{\n\n\t/**\n\t * froxlor version\n\t *\n\t * @var string\n\t */\n\tprotected $version = null;\n\t/**\n\t * froxlor dbversion\n\t *\n\t * @var int\n\t */\n\tprotected $dbversion = null;\n\t/**\n\t * froxlor version-branding\n\t *\n\t * @var string\n\t */\n\tprotected $branding = null;\n\t/**\n\t * debug flag\n\t *\n\t * @var boolean\n\t */\n\tprivate $debug = false;\n\t/**\n\t * is admin flag\n\t *\n\t * @var boolean\n\t */\n\tprivate $is_admin = false;\n\t/**\n\t * internal user data array\n\t *\n\t * @var array\n\t */\n\tprivate $user_data = null;\n\t/**\n\t * logger interface\n\t *\n\t * @var FroxlorLogger\n\t */\n\tprivate $logger = null;\n\t/**\n\t * mail interface\n\t *\n\t * @var Mailer\n\t */\n\tprivate $mail = null;\n\t/**\n\t * whether the call is an internal one or not\n\t *\n\t * @var boolean\n\t */\n\tprivate $internal_call = false;\n\n\t/**\n\t *\n\t * @param array $header\n\t *            optional, passed via API\n\t * @param array $params\n\t *            optional, array of parameters (var=>value) for the command\n\t * @param array $userinfo\n\t *            optional, passed via WebInterface (instead of $header)\n\t * @param boolean $internal\n\t *            optional whether called internally, default false\n\t *\n\t * @throws Exception\n\t */\n\tfinal public function __construct($header = null, $params = null, $userinfo = null, $internal = false)\n\t{\n\t\tparent::__construct($params);\n\n\t\t$this->version = Froxlor::VERSION;\n\t\t$this->dbversion = Froxlor::DBVERSION;\n\t\t$this->branding = Froxlor::BRANDING;\n\n\t\tif (!empty($header)) {\n\t\t\t$this->readUserData($header);\n\t\t} elseif (!empty($userinfo)) {\n\t\t\t$this->user_data = $userinfo;\n\t\t\t$this->is_admin = (isset($userinfo['adminsession']) && $userinfo['adminsession'] == 1 && $userinfo['adminid'] > 0) ? true : false;\n\t\t} else {\n\t\t\tthrow new Exception(\"Invalid user data\", 500);\n\t\t}\n\t\t$this->logger = FroxlorLogger::getInstanceOf($this->user_data);\n\n\t\t// check whether the user is deactivated\n\t\tif ($this->getUserDetail('deactivated') == 1) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::LOG_ERROR, LOG_INFO, \"[API] User '\" . $this->getUserDetail('loginnname') . \"' tried to use API but is deactivated\");\n\t\t\tthrow new Exception(\"Account suspended\", 406);\n\t\t}\n\n\t\t$this->initLang();\n\n\t\t/**\n\t\t * Initialize the mailingsystem\n\t\t */\n\t\t$this->mail = new Mailer(true);\n\n\t\tif ($this->debug) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::LOG_ERROR, LOG_DEBUG, \"[API] \" . get_called_class() . \": \" . json_encode($params, JSON_UNESCAPED_SLASHES));\n\t\t}\n\n\t\t// set internal call flag\n\t\t$this->internal_call = $internal;\n\t}\n\n\t/**\n\t * read user data from database by api-request-header fields\n\t *\n\t * @param array $header\n\t *            api-request header\n\t *\n\t * @return boolean\n\t * @throws Exception\n\t */\n\tprivate function readUserData($header = null)\n\t{\n\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `api_keys` WHERE `apikey` = :ak AND `secret` = :as\");\n\t\t$result = Database::pexecute_first($sel_stmt, [\n\t\t\t'ak' => $header['apikey'],\n\t\t\t'as' => $header['secret']\n\t\t], true, true);\n\t\tif ($result) {\n\t\t\t// admin or customer?\n\t\t\tif ($result['customerid'] == 0 && $result['adminid'] > 0) {\n\t\t\t\t$this->is_admin = true;\n\t\t\t\t$table = 'panel_admins';\n\t\t\t\t$key = \"adminid\";\n\t\t\t} elseif ($result['customerid'] > 0 && $result['adminid'] > 0) {\n\t\t\t\t$this->is_admin = false;\n\t\t\t\t$table = 'panel_customers';\n\t\t\t\t$key = \"customerid\";\n\t\t\t} else {\n\t\t\t\t// neither adminid is > 0 nor customerid is > 0 - sorry man, no way\n\t\t\t\tthrow new Exception(\"Invalid API credentials\", 400);\n\t\t\t}\n\t\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `\" . $table . \"` WHERE `\" . $key . \"` = :id\");\n\t\t\t$this->user_data = Database::pexecute_first($sel_stmt, [\n\t\t\t\t'id' => ($this->is_admin ? $result['adminid'] : $result['customerid'])\n\t\t\t], true, true);\n\t\t\tif ($this->is_admin) {\n\t\t\t\t$this->user_data['adminsession'] = 1;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tthrow new Exception(\"Invalid API credentials\", 400);\n\t}\n\n\t/**\n\t * return field from user-table\n\t *\n\t * @param string $detail\n\t *\n\t * @return string|null\n\t */\n\tprotected function getUserDetail($detail = null)\n\t{\n\t\treturn ($this->user_data[$detail] ?? null);\n\t}\n\n\t/**\n\t * return logger instance\n\t *\n\t * @return FroxlorLogger\n\t */\n\tprotected function logger()\n\t{\n\t\treturn $this->logger;\n\t}\n\n\t/**\n\t * initialize language to have localized strings available for the ApiCommands\n\t */\n\tprivate function initLang()\n\t{\n\t\tLanguage::setLanguage(Settings::Get('panel.standardlanguage'));\n\n\t\tif ($this->getUserDetail('language') !== null && isset(Language::getLanguages()[$this->getUserDetail('language')])) {\n\t\t\tLanguage::setLanguage($this->getUserDetail('language'));\n\t\t} elseif ($this->getUserDetail('def_language') !== null) {\n\t\t\tLanguage::setLanguage($this->getUserDetail('def_language'));\n\t\t}\n\t}\n\n\t/**\n\t * increase/decrease a resource field for customers/admins\n\t *\n\t * @param string $table\n\t * @param string $keyfield\n\t * @param int $key\n\t * @param string $operator\n\t * @param string $resource\n\t * @param string $extra\n\t * @param int $step\n\t */\n\tprotected static function updateResourceUsage($table = null, $keyfield = null, $key = null, $operator = '+', $resource = null, $extra = null, $step = 1)\n\t{\n\t\t$stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . $table . \"`\n\t\t\tSET `\" . $resource . \"` = `\" . $resource . \"` \" . $operator . \" \" . (int)$step . \" \" . $extra . \"\n\t\t\tWHERE `\" . $keyfield . \"` = :key\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t'key' => $key\n\t\t], true, true);\n\t}\n\n\t/**\n\t * return SQL when parameter $sql_search is given via API\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param array $query_fields\n\t *            optional array of placeholders mapped to the actual value which is used in the API commands when\n\t *            executing the statement [internal]\n\t * @param boolean $append\n\t *            optional append to WHERE clause rather then create new one, default false [internal]\n\t *\n\t * @return string\n\t */\n\tprotected function getSearchWhere(&$query_fields = [], $append = false)\n\t{\n\t\t$search = $this->getParam('sql_search', true, []);\n\t\t$condition = '';\n\t\tif (!empty($search)) {\n\t\t\tif ($append == true) {\n\t\t\t\t$condition = ' AND ';\n\t\t\t} else {\n\t\t\t\t$condition = ' WHERE ';\n\t\t\t}\n\t\t\t$ops = [\n\t\t\t\t'<',\n\t\t\t\t'>',\n\t\t\t\t'=',\n\t\t\t\t'<>'\n\t\t\t];\n\t\t\t$first = true;\n\t\t\tforeach ($search as $field => $valoper) {\n\t\t\t\tif ($field == '_plainsql' && $this->internal_call) {\n\t\t\t\t\tif (isset($valoper['sql']) && isset($valoper['values']) && is_array($valoper['values'])) {\n\t\t\t\t\t\tif (preg_match('/^([a-z0-9\\-\\.,=\\+_`\\(\\)\\:\\'\\\"\\!\\<\\>\\ ]+)$/i', $valoper['sql']) == false) {\n\t\t\t\t\t\t\t// skip\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$condition .= $valoper['sql'];\n\t\t\t\t\t\tforeach ($valoper['values'] as $var => $value) {\n\t\t\t\t\t\t\t$query_fields[':' . $var] = $value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$cleanfield = str_replace(\".\", \"\", $field);\n\t\t\t\t\t$sortfield = explode('.', $field);\n\t\t\t\t\tforeach ($sortfield as $id => $sfield) {\n\t\t\t\t\t\tif (substr($sfield, -1, 1) != '`') {\n\t\t\t\t\t\t\t$sfield .= '`';\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ($sfield[0] != '`') {\n\t\t\t\t\t\t\t$sfield = '`' . $sfield;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$sortfield[$id] = $sfield;\n\t\t\t\t\t}\n\t\t\t\t\t$field = implode('.', $sortfield);\n\t\t\t\t\tif (preg_match('/^([a-z0-9\\-\\._`]+)$/i', $field) == false) {\n\t\t\t\t\t\t// skip\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!$first) {\n\t\t\t\t\t\t$condition .= ' AND ';\n\t\t\t\t\t}\n\t\t\t\t\tif (!is_array($valoper) || !isset($valoper['op']) || empty($valoper['op'])) {\n\t\t\t\t\t\t$condition .= $field . ' LIKE :' . $cleanfield;\n\t\t\t\t\t\tif (!is_array($valoper)) {\n\t\t\t\t\t\t\t$query_fields[':' . $cleanfield] = '%' . $valoper . '%';\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$query_fields[':' . $cleanfield] = '%' . $valoper['value'] . '%';\n\t\t\t\t\t\t}\n\t\t\t\t\t} elseif (in_array($valoper['op'], $ops)) {\n\t\t\t\t\t\t$condition .= $field . ' ' . $valoper['op'] . ':' . $cleanfield;\n\t\t\t\t\t\t$query_fields[':' . $cleanfield] = $valoper['value'] ?? '';\n\t\t\t\t\t} elseif (strtolower($valoper['op']) == 'in' && is_array($valoper['value']) && count($valoper['value']) > 0) {\n\t\t\t\t\t\t$condition .= $field . ' ' . $valoper['op'] . ' (';\n\t\t\t\t\t\tforeach ($valoper['value'] as $incnt => $invalue) {\n\t\t\t\t\t\t\tif (!is_numeric($incnt)) {\n\t\t\t\t\t\t\t\t// skip\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!empty($invalue) && preg_match('/^([a-z0-9\\-\\._`]+)$/i', $invalue) == false) {\n\t\t\t\t\t\t\t\t// skip\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$condition .= \":\" . $cleanfield . $incnt . \", \";\n\t\t\t\t\t\t\t$query_fields[':' . $cleanfield . $incnt] = $invalue ?? '';\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$condition = substr($condition, 0, -2) . ')';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif ($first) {\n\t\t\t\t\t\t$first = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $condition;\n\t}\n\n\t/**\n\t * return LIMIT clause when at least $sql_limit parameter is given via API\n\t *\n\t * @param int $sql_limit\n\t *            optional, limit resultset, default 0\n\t * @param int $sql_offset\n\t *            optional, offset for limitation, default 0\n\t *\n\t * @return string\n\t */\n\tprotected function getLimit()\n\t{\n\t\t$limit = $this->getParam('sql_limit', true, 0);\n\t\t$offset = $this->getParam('sql_offset', true, 0);\n\n\t\tif (!is_numeric($limit)) {\n\t\t\t$limit = 0;\n\t\t}\n\t\tif (!is_numeric($offset)) {\n\t\t\t$offset = 0;\n\t\t}\n\n\t\tif ($limit > 0) {\n\t\t\treturn ' LIMIT ' . $offset . ',' . $limit;\n\t\t}\n\n\t\treturn '';\n\t}\n\n\t/**\n\t * return ORDER BY clause if parameter $sql_orderby parameter is given via API\n\t *\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC\n\t * @param boolean $append\n\t *            optional append to ORDER BY clause rather then create new one, default false [internal]\n\t *\n\t * @return string\n\t */\n\tprotected function getOrderBy($append = false)\n\t{\n\t\t$orderby = $this->getParam('sql_orderby', true, []);\n\t\t$order = \"\";\n\t\tif (!empty($orderby)) {\n\t\t\tif ($append) {\n\t\t\t\t$order .= \", \";\n\t\t\t} else {\n\t\t\t\t$order .= \" ORDER BY \";\n\t\t\t}\n\n\t\t\t$nat_fields = [\n\t\t\t\t'`c`.`loginname`',\n\t\t\t\t'`c`.`name`',\n\t\t\t\t'`a`.`loginname`',\n\t\t\t\t'`adminname`',\n\t\t\t\t'`databasename`',\n\t\t\t\t'`username`'\n\t\t\t];\n\n\t\t\tforeach ($orderby as $field => $by) {\n\t\t\t\t$sortfield = explode('.', $field);\n\t\t\t\tforeach ($sortfield as $id => $sfield) {\n\t\t\t\t\tif (substr($sfield, -1, 1) != '`') {\n\t\t\t\t\t\t$sfield .= '`';\n\t\t\t\t\t}\n\t\t\t\t\tif ($sfield[0] != '`') {\n\t\t\t\t\t\t$sfield = '`' . $sfield;\n\t\t\t\t\t}\n\t\t\t\t\t$sortfield[$id] = $sfield;\n\t\t\t\t}\n\t\t\t\t$field = implode('.', $sortfield);\n\t\t\t\tif (preg_match('/^([a-z0-9\\-\\._`]+)$/i', $field) == false) {\n\t\t\t\t\t// skip\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$by = strtoupper($by);\n\t\t\t\tif (!in_array($by, [\n\t\t\t\t\t'ASC',\n\t\t\t\t\t'DESC'\n\t\t\t\t])) {\n\t\t\t\t\t$by = 'ASC';\n\t\t\t\t}\n\t\t\t\tif (Settings::Get('panel.natsorting') == 1 && in_array($field, $nat_fields)) {\n\t\t\t\t\t// Acts similar to php's natsort(), found in one comment at http://my.opera.com/cpr/blog/show.dml/160556\n\t\t\t\t\t$order .= \"CONCAT( IF( ASCII( LEFT( \" . $field . \", 5 ) ) > 57,\n\t\t\t\t\tLEFT( \" . $field . \", 1 ), 0 ),\n\t\t\t\t\tIF( ASCII( RIGHT( \" . $field . \", 1 ) ) > 57,\n\t\t\t\t\t\tLPAD( \" . $field . \", 255, '0' ),\n\t\t\t\t\t\tLPAD( CONCAT( \" . $field . \", '-' ), 255, '0' )\n\t\t\t\t\t)) \" . $by . \", \";\n\t\t\t\t} else {\n\t\t\t\t\t$order .= $field . \" \" . $by . \", \";\n\t\t\t\t}\n\t\t\t}\n\t\t\t$order = substr($order, 0, -2);\n\t\t}\n\n\t\treturn $order;\n\t}\n\n\t/**\n\t * return mailer instance\n\t *\n\t * @return Mailer\n\t */\n\tprotected function mailer()\n\t{\n\t\treturn $this->mail;\n\t}\n\n\t/**\n\t * return api-compatible response in JSON format and send corresponding http-header\n\t *\n\t * @param mixed $data\n\t * @param int $response_code\n\t * @return string json-encoded response message\n\t */\n\tprotected function response($data = null, int $response_code = 200)\n\t{\n\t\treturn Response::jsonDataResponse($data, $response_code);\n\t}\n\n\t/**\n\t * returns an array of customers the current user can access\n\t *\n\t * @param string $customer_hide_option\n\t *            optional, when called as customer, some options might be hidden due to the\n\t *            panel.customer_hide_options settings\n\t *\n\t * @return array\n\t * @throws Exception\n\t */\n\tprotected function getAllowedCustomerIds($customer_hide_option = '')\n\t{\n\t\t$customer_ids = [];\n\t\tif ($this->isAdmin()) {\n\t\t\t// if we're an admin, list all of the admins customers\n\t\t\t// or optionally for one specific customer identified by id or loginname\n\t\t\t$customerid = $this->getParam('customerid', true, 0);\n\t\t\t$loginname = $this->getParam('loginname', true, '');\n\n\t\t\tif (!empty($customerid) || !empty($loginname)) {\n\t\t\t\t$_result = $this->apiCall('Customers.get', [\n\t\t\t\t\t'id' => $customerid,\n\t\t\t\t\t'loginname' => $loginname\n\t\t\t\t]);\n\t\t\t\t$custom_list_result = [\n\t\t\t\t\t$_result\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t}\n\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t}\n\t\t} else {\n\t\t\tif (!$this->isInternal() && !empty($customer_hide_option) && Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$customer_ids = [\n\t\t\t\t$this->getUserDetail('customerid')\n\t\t\t];\n\t\t}\n\t\tif (empty($customer_ids)) {\n\t\t\tthrow new Exception(\"Required resource unsatisfied.\", 405);\n\t\t}\n\t\treturn $customer_ids;\n\t}\n\n\t/**\n\t * admin flag\n\t *\n\t * @return boolean\n\t */\n\tprotected function isAdmin()\n\t{\n\t\treturn $this->is_admin;\n\t}\n\n\t/**\n\t * call an api-command internally\n\t *\n\t * @param string $command\n\t * @param array|null $params\n\t * @param boolean $internal\n\t *            optional whether called internally, default false\n\t *\n\t *\n\t * @return array\n\t */\n\tprotected function apiCall($command = null, $params = null, $internal = false)\n\t{\n\t\t$_command = explode(\".\", $command);\n\t\t$module = __NAMESPACE__ . \"\\Commands\\\\\" . $_command[0];\n\t\t$function = $_command[1];\n\t\t$json_result = $module::getLocal($this->getUserData(), $params, $internal)->{$function}();\n\t\treturn json_decode($json_result, true)['data'];\n\t}\n\n\t/**\n\t * returns an instance of the wanted ApiCommand (e.g.\n\t * Customers, Domains, etc);\n\t * this is used widely in the WebInterface\n\t *\n\t * @param array $userinfo\n\t *            array of user-data\n\t * @param array $params\n\t *            array of parameters for the command\n\t * @param boolean $internal\n\t *            optional whether called internally, default false\n\t *\n\t * @return static\n\t * @throws Exception\n\t */\n\tpublic static function getLocal($userinfo = null, $params = null, $internal = false)\n\t{\n\t\treturn new static(null, $params, $userinfo, $internal);\n\t}\n\n\t/**\n\t * return user-data array\n\t *\n\t * @return array\n\t */\n\tprotected function getUserData()\n\t{\n\t\treturn $this->user_data;\n\t}\n\n\t/**\n\t * internal call flag\n\t *\n\t * @return boolean\n\t */\n\tprotected function isInternal()\n\t{\n\t\treturn $this->internal_call;\n\t}\n\n\t/**\n\t * returns an array of customer data for customer, or by customer-id/loginname for admin/reseller\n\t *\n\t * @param int $customerid\n\t *            optional, required if loginname is empty\n\t * @param string $loginname\n\t *            optional, required of customerid is empty\n\t * @param string $customer_resource_check\n\t *            optional, when called as admin, check the resources of the target customer\n\t *\n\t * @return array\n\t * @throws Exception\n\t */\n\tprotected function getCustomerData($customer_resource_check = '')\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$customerid = $this->getParam('customerid', true, 0);\n\t\t\t$loginname = $this->getParam('loginname', true, '');\n\t\t\t$customer = $this->apiCall('Customers.get', [\n\t\t\t\t'id' => $customerid,\n\t\t\t\t'loginname' => $loginname\n\t\t\t]);\n\t\t\t// check whether the customer has enough resources\n\t\t\tif (!empty($customer_resource_check) && $customer[$customer_resource_check . '_used'] >= $customer[$customer_resource_check] && $customer[$customer_resource_check] != '-1') {\n\t\t\t\tthrow new Exception(\"Customer has no more resources available\", 406);\n\t\t\t}\n\t\t} else {\n\t\t\t$customer = $this->getUserData();\n\t\t}\n\t\treturn $customer;\n\t}\n\n\t/**\n\t * return email template content from database or global language file if not found in DB\n\t *\n\t * @param array $customerdata\n\t * @param string $group\n\t * @param string $varname\n\t * @param array $replace_arr\n\t * @param string $default\n\t *\n\t * @return string\n\t */\n\tprotected function getMailTemplate($customerdata = null, $group = null, $varname = null, $replace_arr = [], $default = \"\")\n\t{\n\t\t// get template\n\t\t$stmt = Database::prepare(\"\n\t\t\tSELECT `value` FROM `\" . TABLE_PANEL_TEMPLATES . \"` WHERE `adminid`= :adminid\n\t\t\tAND `language`= :lang AND `templategroup`= :group AND `varname`= :var\n\t\t\");\n\t\t$result = Database::pexecute_first($stmt, [\n\t\t\t\"adminid\" => $customerdata['adminid'],\n\t\t\t\"lang\" => $customerdata['def_language'],\n\t\t\t\"group\" => $group,\n\t\t\t\"var\" => $varname\n\t\t], true, true);\n\t\t$content = $default;\n\t\tif ($result) {\n\t\t\t$content = $result['value'] ?? $default;\n\t\t}\n\t\t// @fixme html_entity_decode\n\t\t$content = html_entity_decode(PhpHelper::replaceVariables($content, $replace_arr));\n\t\treturn $content;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/ApiParameter.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api;\n\nuse Exception;\n\nabstract class ApiParameter\n{\n\n\t/**\n\t * array of parameters passed to the command\n\t *\n\t * @var array\n\t */\n\tprivate $cmd_params = null;\n\n\t/**\n\t *\n\t * @param array|null $params\n\t *            optional, array of parameters (var=>value) for the command\n\t *\n\t * @throws Exception\n\t */\n\tpublic function __construct(?array $params = null)\n\t{\n\t\tif (!is_null($params)) {\n\t\t\t$params = $this->trimArray($params);\n\t\t}\n\t\t$this->cmd_params = $params;\n\t}\n\n\t/**\n\t * run 'trim' function on an array recursively\n\t *\n\t * @param array $input\n\t *\n\t * @return string|array\n\t */\n\tprivate function trimArray($input)\n\t{\n\t\tif ($input === '') {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (is_numeric($input) || is_null($input)) {\n\t\t\treturn $input;\n\t\t}\n\t\tif (!is_array($input)) {\n\t\t\treturn trim($input);\n\t\t}\n\t\treturn array_map([\n\t\t\t$this,\n\t\t\t'trimArray'\n\t\t], $input);\n\t}\n\n\t/**\n\t * get specific parameter which also has and unlimited-field\n\t *\n\t * @param string|null $param\n\t *            parameter to get out of the request-parameter list\n\t * @param string|null $ul_field\n\t *            parameter to get out of the request-parameter list\n\t * @param bool $optional\n\t *            default: false\n\t * @param mixed $default\n\t *            value which is returned if optional=true and param is not set\n\t *\n\t * @return mixed\n\t * @throws Exception\n\t */\n\tprotected function getUlParam(?string $param = null, ?string $ul_field = null, bool $optional = false, $default = 0)\n\t{\n\t\t$param_value = (int)$this->getParam($param, $optional, $default);\n\t\t$ul_field_value = $this->getBoolParam($ul_field, true, 0);\n\t\tif ($ul_field_value != '0') {\n\t\t\t$param_value = -1;\n\t\t}\n\t\treturn $param_value;\n\t}\n\n\t/**\n\t * get specific parameter from the parameter list;\n\t * check for existence and != empty if needed.\n\t * Maybe more in the future\n\t *\n\t * @param string|null $param\n\t *            parameter to get out of the request-parameter list\n\t * @param bool $optional\n\t *            default: false\n\t * @param mixed $default\n\t *            value which is returned if optional=true and param is not set\n\t *\n\t * @return mixed\n\t * @throws Exception\n\t */\n\tprotected function getParam(?string $param = null, bool $optional = false, $default = '')\n\t{\n\t\t// does it exist?\n\t\tif (!isset($this->cmd_params[$param])) {\n\t\t\tif ($optional === false) {\n\t\t\t\t// get module + function for better error-messages\n\t\t\t\t$inmod = $this->getModFunctionString();\n\t\t\t\tthrow new Exception('Requested parameter \"' . $param . '\" could not be found for \"' . $inmod . '\"', 404);\n\t\t\t}\n\t\t\treturn $default;\n\t\t}\n\t\t// is it empty? - test really on string, as value 0 is being seen as empty by php\n\t\tif (!is_array($this->cmd_params[$param]) && trim($this->cmd_params[$param]) === \"\") {\n\t\t\tif ($optional === false) {\n\t\t\t\t// get module + function for better error-messages\n\t\t\t\t$inmod = $this->getModFunctionString();\n\t\t\t\tthrow new Exception('Requested parameter \"' . $param . '\" is empty where it should not be for \"' . $inmod . '\"', 406);\n\t\t\t}\n\t\t\treturn '';\n\t\t}\n\t\t// everything else is fine\n\t\treturn $this->cmd_params[$param];\n\t}\n\n\t/**\n\t * returns \"module::function()\" for better error-messages (missing parameter etc.)\n\t * makes debugging a lot more comfortable\n\t *\n\t * @param int $level\n\t *            depth of backtrace, default 2\n\t *\n\t * @param int $max_level\n\t * @param array|null $trace\n\t *\n\t * @return string\n\t */\n\tprivate function getModFunctionString(int $level = 1, int $max_level = 5, $trace = null)\n\t{\n\t\t// which class called us\n\t\t$_class = get_called_class();\n\t\tif (empty($trace)) {\n\t\t\t// get backtrace\n\t\t\t$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n\t\t}\n\t\t// check class and function\n\t\t$class = $trace[$level]['class'];\n\t\t$func = $trace[$level]['function'];\n\t\t// is it the one we are looking for?\n\t\tif ($class != $_class && $level <= $max_level) {\n\t\t\t// check one level deeper\n\t\t\treturn $this->getModFunctionString(++$level, $max_level, $trace);\n\t\t}\n\t\treturn str_replace(\"Froxlor\\\\Api\\\\Commands\\\\\", \"\", $class) . ':' . $func;\n\t}\n\n\t/**\n\t * getParam wrapper for boolean parameter\n\t *\n\t * @param string|null $param\n\t *            parameter to get out of the request-parameter list\n\t * @param bool $optional\n\t *            default: false\n\t * @param mixed $default\n\t *            value which is returned if optional=true and param is not set\n\t *\n\t * @return string\n\t */\n\tprotected function getBoolParam(?string $param = null, bool $optional = false, $default = false)\n\t{\n\t\t$_default = '0';\n\t\tif ($default) {\n\t\t\t$_default = '1';\n\t\t}\n\t\t$param_value = $this->getParam($param, $optional, $_default);\n\t\tif ($param_value && intval($param_value) != 0) {\n\t\t\treturn '1';\n\t\t}\n\t\treturn '0';\n\t}\n\n\t/**\n\t * return list of all parameters\n\t *\n\t * @return array\n\t */\n\tprotected function getParamList()\n\t{\n\t\treturn $this->cmd_params;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Admins.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Language;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Admins extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * increase resource-usage\n\t *\n\t * @param int $adminid\n\t * @param string $resource\n\t * @param string $extra\n\t *            optional, default empty\n\t * @param int $increase_by\n\t *            optional, default 1\n\t */\n\tpublic static function increaseUsage($adminid = 0, $resource = null, $extra = '', $increase_by = 1)\n\t{\n\t\tself::updateResourceUsage(TABLE_PANEL_ADMINS, 'adminid', $adminid, '+', $resource, $extra, $increase_by);\n\t}\n\n\t/**\n\t * decrease resource-usage\n\t *\n\t * @param int $adminid\n\t * @param string $resource\n\t * @param string $extra\n\t *            optional, default empty\n\t * @param int $decrease_by\n\t *            optional, default 1\n\t */\n\tpublic static function decreaseUsage($adminid = 0, $resource = null, $extra = '', $decrease_by = 1)\n\t{\n\t\tself::updateResourceUsage(TABLE_PANEL_ADMINS, 'adminid', $adminid, '-', $resource, $extra, $decrease_by);\n\t}\n\n\t/**\n\t * lists all admin entries\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] list admins\");\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT *\n\t\t\t\tFROM `\" . TABLE_PANEL_ADMINS . \"`\" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());\n\t\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\t\t$result = [];\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$result[] = $row;\n\t\t\t}\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($result),\n\t\t\t\t'list' => $result\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns the total number of admins for the given admin\n\t *\n\t * @access admin\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_admins\n\t\t\t\tFROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\" . $this->getSearchWhere($query_fields));\n\t\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_admins']);\n\t\t\t}\n\t\t\t$this->response(0);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * create a new admin user\n\t *\n\t * @param string $name\n\t *            required, name of the adminstrator\n\t * @param string $email\n\t *            required, email address of the administrator\n\t * @param string $new_loginname\n\t *            required, loginname/username of the administrator\n\t * @param string $admin_password\n\t *            optional, default auto-generated\n\t * @param string $def_language\n\t *            optional, ISO 639-1 language code (e.g. 'en', 'de', see lng-folder for supported languages),\n\t *            default is system-default language\n\t * @param bool $gui_access\n\t *            optional, allow login via webui, if false ONLY the login via webui is disallowed; default true\n\t * @param bool $api_allowed\n\t *            optional, default is true if system setting api.enabled is true, else false\n\t * @param string $custom_notes\n\t *            optional, default empty\n\t * @param bool $custom_notes_show\n\t *            optional, default false\n\t * @param int $diskspace\n\t *            optional, default 0\n\t * @param bool $diskspace_ul\n\t *            optional, default false\n\t * @param int $traffic\n\t *            optional, default 0\n\t * @param bool $traffic_ul\n\t *            optional, default false\n\t * @param int $customers\n\t *            optional, default 0\n\t * @param bool $customers_ul\n\t *            optional, default false\n\t * @param int $domains\n\t *            optional, default 0\n\t * @param bool $domains_ul\n\t *            optional, default false\n\t * @param int $subdomains\n\t *            optional, default 0\n\t * @param bool $subdomains_ul\n\t *            optional, default false\n\t * @param int $emails\n\t *            optional, default 0\n\t * @param bool $emails_ul\n\t *            optional, default false\n\t * @param int $email_accounts\n\t *            optional, default 0\n\t * @param bool $email_accounts_ul\n\t *            optional, default false\n\t * @param int $email_forwarders\n\t *            optional, default 0\n\t * @param bool $email_forwarders_ul\n\t *            optional, default false\n\t * @param int $email_quota\n\t *            optional, default 0\n\t * @param bool $email_quota_ul\n\t *            optional, default false\n\t * @param int $ftps\n\t *            optional, default 0\n\t * @param bool $ftps_ul\n\t *            optional, default false\n\t * @param int $mysqls\n\t *            optional, default 0\n\t * @param bool $mysqls_ul\n\t *            optional, default false\n\t * @param bool $customers_see_all\n\t *            optional, default false\n\t * @param bool $caneditphpsettings\n\t *            optional, default false\n\t * @param bool $change_serversettings\n\t *            optional, default false\n\t * @param array $ipaddress\n\t *            optional, list of ip-address id's; default -1 (all IP's)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t// required parameters\n\t\t\t$name = $this->getParam('name');\n\t\t\t$email = $this->getParam('email');\n\t\t\t$loginname = $this->getParam('new_loginname');\n\n\t\t\t// parameters\n\t\t\t$def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage'));\n\t\t\t$gui_access = $this->getBoolParam('gui_access', true, true);\n\t\t\t$api_allowed = $this->getBoolParam('api_allowed', true, Settings::Get('api.enabled'));\n\t\t\t$custom_notes = $this->getParam('custom_notes', true, '');\n\t\t\t$custom_notes_show = $this->getBoolParam('custom_notes_show', true, 0);\n\t\t\t$password = $this->getParam('admin_password', true, '');\n\n\t\t\t$diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, 0);\n\t\t\t$traffic = $this->getUlParam('traffic', 'traffic_ul', true, 0);\n\t\t\t$customers = $this->getUlParam('customers', 'customers_ul', true, 0);\n\t\t\t$domains = $this->getUlParam('domains', 'domains_ul', true, 0);\n\t\t\t$subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, 0);\n\t\t\t$emails = $this->getUlParam('emails', 'emails_ul', true, 0);\n\t\t\t$email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, 0);\n\t\t\t$email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, 0);\n\t\t\t$email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, 0);\n\t\t\t$ftps = $this->getUlParam('ftps', 'ftps_ul', true, 0);\n\t\t\t$mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, 0);\n\n\t\t\t$customers_see_all = $this->getBoolParam('customers_see_all', true, 0);\n\t\t\t$caneditphpsettings = $this->getBoolParam('caneditphpsettings', true, 0);\n\t\t\t$change_serversettings = $this->getBoolParam('change_serversettings', true, 0);\n\t\t\t$ipaddress = $this->getParam('ipaddress', true, -1);\n\n\t\t\t// validation\n\t\t\t$name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$email = $idna_convert->encode(Validate::validate($email, 'email', '', '', [], true));\n\t\t\t$def_language = Validate::validate($def_language, 'default language', '', '', [], true);\n\t\t\tif (!empty($def_language) && !isset(Language::getLanguages()[$def_language])) {\n\t\t\t\t$def_language = Settings::Get('panel.standardlanguage');\n\t\t\t}\n\t\t\t$custom_notes = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $custom_notes), 'custom_notes', Validate::REGEX_CONF_TEXT, '', [], true);\n\n\t\t\tif (Settings::Get('system.mail_quota_enabled') != '1') {\n\t\t\t\t$email_quota = -1;\n\t\t\t}\n\n\t\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\t\t\t// only check if not empty,\n\t\t\t// cause empty == generate password automatically\n\t\t\tif ($password != '') {\n\t\t\t\t$password = Crypt::validatePassword($password, true);\n\t\t\t}\n\n\t\t\t$diskspace *= 1024;\n\t\t\t$traffic *= 1024 * 1024;\n\n\t\t\t// Check if the account already exists\n\t\t\t// do not check via api as we skip any permission checks for this task\n\t\t\t$loginname_check_stmt = Database::prepare(\"\n\t\t\t\tSELECT `loginname` FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `loginname` = :login\n\t\t\t\");\n\t\t\t$loginname_check = Database::pexecute_first($loginname_check_stmt, [\n\t\t\t\t'login' => $loginname\n\t\t\t], true, true);\n\n\t\t\t// Check if an admin with the loginname already exists\n\t\t\t// do not check via api as we skip any permission checks for this task\n\t\t\t$loginname_check_admin_stmt = Database::prepare(\"\n\t\t\t\tSELECT `loginname` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `loginname` = :login\n\t\t\t\");\n\t\t\t$loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, [\n\t\t\t\t'login' => $loginname\n\t\t\t], true, true);\n\n\t\t\t// Check for existing email address\n\t\t\t// do not check via api as we skip any permission checks for this task\n\t\t\t$email_check_admin_stmt = Database::prepare(\"\n\t\t\t\tSELECT `email` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `email` = :email\n\t\t\t\");\n\t\t\t$email_check_admin = Database::pexecute_first($email_check_admin_stmt, [\n\t\t\t\t'email' => $email\n\t\t\t], true, true);\n\n\t\t\tif (($loginname_check && strtolower($loginname_check['loginname']) == strtolower($loginname)) || ($loginname_check_admin && strtolower($loginname_check_admin['loginname']) == strtolower($loginname))) {\n\t\t\t\tResponse::standardError('loginnameexists', $loginname, true);\n\t\t\t} elseif (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) {\n\t\t\t\t// Accounts which match systemaccounts are not allowed, filtering them\n\t\t\t\tResponse::standardError('loginnameisusingprefix', Settings::Get('customer.accountprefix'), true);\n\t\t\t} elseif (function_exists('posix_getpwnam') && !in_array(\"posix_getpwnam\", explode(\",\", ini_get('disable_functions'))) && posix_getpwnam($loginname)) {\n\t\t\t\tResponse::standardError('loginnameissystemaccount', $loginname, true);\n\t\t\t} elseif (!Validate::validateUsername($loginname)) {\n\t\t\t\tResponse::standardError('loginnameiswrong', $loginname, true);\n\t\t\t} elseif (!Validate::validateEmail($email)) {\n\t\t\t\tResponse::standardError('emailiswrong', $email, true);\n\t\t\t} elseif ($email_check_admin && strtolower($email_check_admin['email']) == strtolower($email)) {\n\t\t\t\tResponse::standardError('emailexists', $email, true);\n\t\t\t} else {\n\t\t\t\tif ($customers_see_all != '1') {\n\t\t\t\t\t$customers_see_all = '0';\n\t\t\t\t}\n\n\t\t\t\tif ($caneditphpsettings != '1') {\n\t\t\t\t\t$caneditphpsettings = '0';\n\t\t\t\t}\n\n\t\t\t\tif ($change_serversettings != '1') {\n\t\t\t\t\t$change_serversettings = '0';\n\t\t\t\t}\n\n\t\t\t\tif ($password == '') {\n\t\t\t\t\t$password = Crypt::generatePassword();\n\t\t\t\t}\n\n\t\t\t\t$_theme = Settings::Get('panel.default_theme');\n\n\t\t\t\t$ins_data = [\n\t\t\t\t\t'loginname' => $loginname,\n\t\t\t\t\t'password' => Crypt::makeCryptPassword($password),\n\t\t\t\t\t'name' => $name,\n\t\t\t\t\t'email' => $email,\n\t\t\t\t\t'lang' => $def_language,\n\t\t\t\t\t'gui_access' => $gui_access,\n\t\t\t\t\t'api_allowed' => $api_allowed,\n\t\t\t\t\t'change_serversettings' => $change_serversettings,\n\t\t\t\t\t'customers' => $customers,\n\t\t\t\t\t'customers_see_all' => $customers_see_all,\n\t\t\t\t\t'domains' => $domains,\n\t\t\t\t\t'caneditphpsettings' => $caneditphpsettings,\n\t\t\t\t\t'diskspace' => $diskspace,\n\t\t\t\t\t'traffic' => $traffic,\n\t\t\t\t\t'subdomains' => $subdomains,\n\t\t\t\t\t'emails' => $emails,\n\t\t\t\t\t'accounts' => $email_accounts,\n\t\t\t\t\t'forwarders' => $email_forwarders,\n\t\t\t\t\t'quota' => $email_quota,\n\t\t\t\t\t'ftps' => $ftps,\n\t\t\t\t\t'mysqls' => $mysqls,\n\t\t\t\t\t'ip' => empty($ipaddress) ? \"\" : (is_array($ipaddress) && $ipaddress > 0 ? json_encode($ipaddress) : -1),\n\t\t\t\t\t'theme' => $_theme,\n\t\t\t\t\t'custom_notes' => $custom_notes,\n\t\t\t\t\t'custom_notes_show' => $custom_notes_show\n\t\t\t\t];\n\n\t\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\tINSERT INTO `\" . TABLE_PANEL_ADMINS . \"` SET\n\t\t\t\t\t`loginname` = :loginname,\n\t\t\t\t\t`password` = :password,\n\t\t\t\t\t`name` = :name,\n\t\t\t\t\t`email` = :email,\n\t\t\t\t\t`def_language` = :lang,\n\t\t\t\t\t`gui_access` = :gui_access,\n\t\t\t\t\t`api_allowed` = :api_allowed,\n\t\t\t\t\t`change_serversettings` = :change_serversettings,\n\t\t\t\t\t`customers` = :customers,\n\t\t\t\t\t`customers_see_all` = :customers_see_all,\n\t\t\t\t\t`domains` = :domains,\n\t\t\t\t\t`caneditphpsettings` = :caneditphpsettings,\n\t\t\t\t\t`diskspace` = :diskspace,\n\t\t\t\t\t`traffic` = :traffic,\n\t\t\t\t\t`subdomains` = :subdomains,\n\t\t\t\t\t`emails` = :emails,\n\t\t\t\t\t`email_accounts` = :accounts,\n\t\t\t\t\t`email_forwarders` = :forwarders,\n\t\t\t\t\t`email_quota` = :quota,\n\t\t\t\t\t`ftps` = :ftps,\n\t\t\t\t\t`mysqls` = :mysqls,\n\t\t\t\t\t`ip` = :ip,\n\t\t\t\t\t`theme` = :theme,\n\t\t\t\t\t`custom_notes` = :custom_notes,\n\t\t\t\t\t`custom_notes_show` = :custom_notes_show\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($ins_stmt, $ins_data, true, true);\n\n\t\t\t\t$adminid = Database::lastInsertId();\n\t\t\t\t$ins_data['adminid'] = $adminid;\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] added admin '\" . $loginname . \"'\");\n\n\t\t\t\t// get all admin-data for return-array\n\t\t\t\t$result = $this->apiCall('Admins.get', [\n\t\t\t\t\t'id' => $adminid\n\t\t\t\t]);\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return an admin entry by either id or loginname\n\t *\n\t * @param int $id\n\t *            optional, the admin-id\n\t * @param string $loginname\n\t *            optional, the loginname\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ln_optional = $id > 0;\n\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\n\t\tif ($this->isAdmin() && ($this->getUserDetail('change_serversettings') == 1 || ($this->getUserDetail('adminid') == $id || $this->getUserDetail('loginname') == $loginname))) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\tWHERE \" . ($id > 0 ? \"`adminid` = :idln\" : \"`loginname` = :idln\"));\n\t\t\t$params = [\n\t\t\t\t'idln' => ($id <= 0 ? $loginname : $id)\n\t\t\t];\n\t\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\t\tif ($result) {\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] get admin '\" . $result['loginname'] . \"'\");\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t\t$key = ($id > 0 ? \"id #\" . $id : \"loginname '\" . $loginname . \"'\");\n\t\t\tthrow new Exception(\"Admin with \" . $key . \" could not be found\", 404);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * update an admin user by given id or loginname\n\t *\n\t * @param int $id\n\t *            optional, the admin-id\n\t * @param string $loginname\n\t *            optional, the loginname\n\t * @param string $name\n\t *            optional\n\t * @param string $email\n\t *            optional\n\t * @param string $admin_password\n\t *            optional, default auto-generated\n\t * @param string $def_language\n\t *            optional, ISO 639-1 language code (e.g. 'en', 'de', see lng-folder for supported languages),\n\t *            default is system-default language\n\t * @param bool $gui_access\n\t *            optional, allow login via webui, if false ONLY the login via webui is disallowed; default true\n\t * @param bool $api_allowed\n\t *            optional, default is true if system setting api.enabled is true, else false\n\t * @param string $custom_notes\n\t *            optional, default empty\n\t * @param string $theme\n\t *            optional\n\t * @param bool $deactivated\n\t *            optional, default false\n\t * @param bool $custom_notes_show\n\t *            optional, default false\n\t * @param int $diskspace\n\t *            optional, default 0\n\t * @param bool $diskspace_ul\n\t *            optional, default false\n\t * @param int $traffic\n\t *            optional, default 0\n\t * @param bool $traffic_ul\n\t *            optional, default false\n\t * @param int $customers\n\t *            optional, default 0\n\t * @param bool $customers_ul\n\t *            optional, default false\n\t * @param int $domains\n\t *            optional, default 0\n\t * @param bool $domains_ul\n\t *            optional, default false\n\t * @param int $subdomains\n\t *            optional, default 0\n\t * @param bool $subdomains_ul\n\t *            optional, default false\n\t * @param int $emails\n\t *            optional, default 0\n\t * @param bool $emails_ul\n\t *            optional, default false\n\t * @param int $email_accounts\n\t *            optional, default 0\n\t * @param bool $email_accounts_ul\n\t *            optional, default false\n\t * @param int $email_forwarders\n\t *            optional, default 0\n\t * @param bool $email_forwarders_ul\n\t *            optional, default false\n\t * @param int $email_quota\n\t *            optional, default 0\n\t * @param bool $email_quota_ul\n\t *            optional, default false\n\t * @param int $ftps\n\t *            optional, default 0\n\t * @param bool $ftps_ul\n\t *            optional, default false\n\t * @param int $mysqls\n\t *            optional, default 0\n\t * @param bool $mysqls_ul\n\t *            optional, default false\n\t * @param bool $customers_see_all\n\t *            optional, default false\n\t * @param bool $caneditphpsettings\n\t *            optional, default false\n\t * @param bool $change_serversettings\n\t *            optional, default false\n\t * @param array $ipaddress\n\t *            optional, list of ip-address id's; default -1 (all IP's)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$ln_optional = $id > 0;\n\t\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\n\t\t\t$result = $this->apiCall('Admins.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'loginname' => $loginname\n\t\t\t]);\n\t\t\t$id = $result['adminid'];\n\n\t\t\tif ($this->getUserDetail('change_serversettings') == 1 || $result['adminid'] == $this->getUserDetail('adminid')) {\n\t\t\t\t// parameters\n\t\t\t\t$name = $this->getParam('name', true, $result['name']);\n\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\t$email = $this->getParam('email', true, $idna_convert->decode($result['email']));\n\t\t\t\t$password = $this->getParam('admin_password', true, '');\n\t\t\t\t$def_language = $this->getParam('def_language', true, $result['def_language']);\n\t\t\t\t$custom_notes = $this->getParam('custom_notes', true, ($result['custom_notes'] ?? \"\"));\n\t\t\t\t$custom_notes_show = $this->getBoolParam('custom_notes_show', true, $result['custom_notes_show']);\n\t\t\t\t$theme = $this->getParam('theme', true, $result['theme']);\n\n\t\t\t\t// you cannot edit some of the details of yourself\n\t\t\t\tif ($result['adminid'] == $this->getUserDetail('adminid')) {\n\t\t\t\t\t$gui_access = $result['gui_access'];\n\t\t\t\t\t$api_allowed = $result['api_allowed'];\n\t\t\t\t\t$deactivated = $result['deactivated'];\n\t\t\t\t\t$customers = $result['customers'];\n\t\t\t\t\t$domains = $result['domains'];\n\t\t\t\t\t$subdomains = $result['subdomains'];\n\t\t\t\t\t$emails = $result['emails'];\n\t\t\t\t\t$email_accounts = $result['email_accounts'];\n\t\t\t\t\t$email_forwarders = $result['email_forwarders'];\n\t\t\t\t\t$email_quota = $result['email_quota'];\n\t\t\t\t\t$ftps = $result['ftps'];\n\t\t\t\t\t$mysqls = $result['mysqls'];\n\t\t\t\t\t$customers_see_all = $result['customers_see_all'];\n\t\t\t\t\t$caneditphpsettings = $result['caneditphpsettings'];\n\t\t\t\t\t$change_serversettings = $result['change_serversettings'];\n\t\t\t\t\t$diskspace = $result['diskspace'];\n\t\t\t\t\t$traffic = $result['traffic'];\n\t\t\t\t\t$ipaddress = ($result['ip'] != -1 ? json_decode($result['ip'], true) : -1);\n\t\t\t\t} else {\n\t\t\t\t\t$gui_access = $this->getBoolParam('gui_access', true, $result['gui_access']);\n\t\t\t\t\t$api_allowed = $this->getBoolParam('api_allowed', true, $result['api_allowed']);\n\t\t\t\t\t$deactivated = $this->getBoolParam('deactivated', true, $result['deactivated']);\n\n\t\t\t\t\t$dec_places = Settings::Get('panel.decimal_places');\n\t\t\t\t\t$diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places));\n\t\t\t\t\t$traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places));\n\t\t\t\t\t$customers = $this->getUlParam('customers', 'customers_ul', true, $result['customers']);\n\t\t\t\t\t$domains = $this->getUlParam('domains', 'domains_ul', true, $result['domains']);\n\t\t\t\t\t$subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']);\n\t\t\t\t\t$emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']);\n\t\t\t\t\t$email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']);\n\t\t\t\t\t$email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']);\n\t\t\t\t\t$email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']);\n\t\t\t\t\t$ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']);\n\t\t\t\t\t$mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']);\n\n\t\t\t\t\t$customers_see_all = $this->getBoolParam('customers_see_all', true, $result['customers_see_all']);\n\t\t\t\t\t$caneditphpsettings = $this->getBoolParam('caneditphpsettings', true, $result['caneditphpsettings']);\n\t\t\t\t\t$change_serversettings = $this->getBoolParam('change_serversettings', true, $result['change_serversettings']);\n\t\t\t\t\t$ipaddress = $this->getParam('ipaddress', true, ($result['ip'] != -1 ? json_decode($result['ip'], true) : -1));\n\n\t\t\t\t\t$diskspace *= 1024;\n\t\t\t\t\t$traffic *= 1024 * 1024;\n\t\t\t\t}\n\n\t\t\t\t// validation\n\t\t\t\t$name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\t$email = $idna_convert->encode(Validate::validate($email, 'email', '', '', [], true));\n\t\t\t\t$def_language = Validate::validate($def_language, 'default language', '', '', [], true);\n\t\t\t\tif (!empty($def_language) && !isset(Language::getLanguages()[$def_language])) {\n\t\t\t\t\t$def_language = Settings::Get('panel.standardlanguage');\n\t\t\t\t}\n\t\t\t\t$custom_notes = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $custom_notes ?? \"\"), 'custom_notes', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t\t$theme = Validate::validate($theme, 'theme', '', '', [], true);\n\t\t\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\n\t\t\t\tif (Settings::Get('system.mail_quota_enabled') != '1') {\n\t\t\t\t\t$email_quota = -1;\n\t\t\t\t}\n\n\t\t\t\tif (empty($theme)) {\n\t\t\t\t\t$theme = Settings::Get('panel.default_theme');\n\t\t\t\t}\n\n\t\t\t\tif (empty(trim($name))) {\n\t\t\t\t\tResponse::standardError([\n\t\t\t\t\t\t'stringisempty',\n\t\t\t\t\t\t'admin.name'\n\t\t\t\t\t], '', true);\n\t\t\t\t}\n\t\t\t\tif (empty(trim($email))) {\n\t\t\t\t\tResponse::standardError([\n\t\t\t\t\t\t'stringisempty',\n\t\t\t\t\t\t'admin.email'\n\t\t\t\t\t], '', true);\n\t\t\t\t}\n\t\t\t\t// Check for existing email address\n\t\t\t\t// do not check via api as we skip any permission checks for this task\n\t\t\t\t$email_check_admin_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `email` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `email` = :email and `adminid` <> :adminid\n\t\t\t\t\");\n\t\t\t\t$email_check_admin = Database::pexecute_first($email_check_admin_stmt, [\n\t\t\t\t\t'email' => $email,\n\t\t\t\t\t'adminid' => $id,\n\t\t\t\t], true, true);\n\n\t\t\t\tif (!Validate::validateEmail($email)) {\n\t\t\t\t\tResponse::standardError('emailiswrong', $email, true);\n\t\t\t\t} elseif ($email_check_admin && strtolower($email_check_admin['email']) == strtolower($email)) {\n\t\t\t\t\tResponse::standardError('emailexists', $email, true);\n\t\t\t\t} else {\n\t\t\t\t\tif ($deactivated != '1') {\n\t\t\t\t\t\t$deactivated = '0';\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($customers_see_all != '1') {\n\t\t\t\t\t\t$customers_see_all = '0';\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($caneditphpsettings != '1') {\n\t\t\t\t\t\t$caneditphpsettings = '0';\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($change_serversettings != '1') {\n\t\t\t\t\t\t$change_serversettings = '0';\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($password != '') {\n\t\t\t\t\t\t$password = Crypt::validatePassword($password, true);\n\t\t\t\t\t\t$password = Crypt::makeCryptPassword($password);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$password = $result['password'];\n\t\t\t\t\t}\n\n\t\t\t\t\t// check if a resource was set to something lower\n\t\t\t\t\t// than actually used by the admin/reseller\n\t\t\t\t\t$res_warning = \"\";\n\t\t\t\t\tif ($customers != $result['customers'] && $customers != -1 && $customers < $result['customers_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['customers']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($domains != $result['domains'] && $domains != -1 && $domains < $result['domains_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['domains']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($diskspace != $result['diskspace'] && ($diskspace / 1024) != -1 && $diskspace < $result['diskspace_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['diskspace']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($traffic != $result['traffic'] && ($traffic / 1024 / 1024) != -1 && $traffic < $result['traffic_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['traffic']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($emails != $result['emails'] && $emails != -1 && $emails < $result['emails_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['emails']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($email_accounts != $result['email_accounts'] && $email_accounts != -1 && $email_accounts < $result['email_accounts_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['email accounts']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($email_forwarders != $result['email_forwarders'] && $email_forwarders != -1 && $email_forwarders < $result['email_forwarders_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['email forwarders']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($email_quota != $result['email_quota'] && $email_quota != -1 && $email_quota < $result['email_quota_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['email quota']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($ftps != $result['ftps'] && $ftps != -1 && $ftps < $result['ftps_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['ftps']);\n\t\t\t\t\t}\n\t\t\t\t\tif ($mysqls != $result['mysqls'] && $mysqls != -1 && $mysqls < $result['mysqls_used']) {\n\t\t\t\t\t\t$res_warning .= lng('error.setlessthanalreadyused', ['mysqls']);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!empty($res_warning)) {\n\t\t\t\t\t\tthrow new Exception($res_warning, 406);\n\t\t\t\t\t}\n\n\t\t\t\t\t$upd_data = [\n\t\t\t\t\t\t'password' => $password,\n\t\t\t\t\t\t'name' => $name,\n\t\t\t\t\t\t'email' => $email,\n\t\t\t\t\t\t'lang' => $def_language,\n\t\t\t\t\t\t'gui_access' => $gui_access,\n\t\t\t\t\t\t'api_allowed' => $api_allowed,\n\t\t\t\t\t\t'change_serversettings' => $change_serversettings,\n\t\t\t\t\t\t'customers' => $customers,\n\t\t\t\t\t\t'customers_see_all' => $customers_see_all,\n\t\t\t\t\t\t'domains' => $domains,\n\t\t\t\t\t\t'caneditphpsettings' => $caneditphpsettings,\n\t\t\t\t\t\t'diskspace' => $diskspace,\n\t\t\t\t\t\t'traffic' => $traffic,\n\t\t\t\t\t\t'subdomains' => $subdomains,\n\t\t\t\t\t\t'emails' => $emails,\n\t\t\t\t\t\t'accounts' => $email_accounts,\n\t\t\t\t\t\t'forwarders' => $email_forwarders,\n\t\t\t\t\t\t'quota' => $email_quota,\n\t\t\t\t\t\t'ftps' => $ftps,\n\t\t\t\t\t\t'mysqls' => $mysqls,\n\t\t\t\t\t\t'ip' => empty($ipaddress) ? \"\" : (is_array($ipaddress) && $ipaddress > 0 ? json_encode($ipaddress) : -1),\n\t\t\t\t\t\t'deactivated' => $deactivated,\n\t\t\t\t\t\t'custom_notes' => $custom_notes,\n\t\t\t\t\t\t'custom_notes_show' => $custom_notes_show,\n\t\t\t\t\t\t'theme' => $theme,\n\t\t\t\t\t\t'adminid' => $id\n\t\t\t\t\t];\n\n\t\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET\n\t\t\t\t\t\t`password` = :password,\n\t\t\t\t\t\t`name` = :name,\n\t\t\t\t\t\t`email` = :email,\n\t\t\t\t\t\t`def_language` = :lang,\n\t\t\t\t\t\t`gui_access` = :gui_access,\n\t\t\t\t\t\t`api_allowed` = :api_allowed,\n\t\t\t\t\t\t`change_serversettings` = :change_serversettings,\n\t\t\t\t\t\t`customers` = :customers,\n\t\t\t\t\t\t`customers_see_all` = :customers_see_all,\n\t\t\t\t\t\t`domains` = :domains,\n\t\t\t\t\t\t`caneditphpsettings` = :caneditphpsettings,\n\t\t\t\t\t\t`diskspace` = :diskspace,\n\t\t\t\t\t\t`traffic` = :traffic,\n\t\t\t\t\t\t`subdomains` = :subdomains,\n\t\t\t\t\t\t`emails` = :emails,\n\t\t\t\t\t\t`email_accounts` = :accounts,\n\t\t\t\t\t\t`email_forwarders` = :forwarders,\n\t\t\t\t\t\t`email_quota` = :quota,\n\t\t\t\t\t\t`ftps` = :ftps,\n\t\t\t\t\t\t`mysqls` = :mysqls,\n\t\t\t\t\t\t`ip` = :ip,\n\t\t\t\t\t\t`deactivated` = :deactivated,\n\t\t\t\t\t\t`custom_notes` = :custom_notes,\n\t\t\t\t\t\t`custom_notes_show` = :custom_notes_show,\n\t\t\t\t\t\t`theme` = :theme\n\t\t\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data, true, true);\n\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] edited admin '\" . $result['loginname'] . \"'\");\n\n\t\t\t\t\t// get all admin-data for return-array\n\t\t\t\t\t$result = $this->apiCall('Admins.get', [\n\t\t\t\t\t\t'id' => $result['adminid']\n\t\t\t\t\t]);\n\t\t\t\t\treturn $this->response($result);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * delete a admin entry by either id or loginname\n\t *\n\t * @param int $id\n\t *            optional, the admin-id\n\t * @param string $loginname\n\t *            optional, the loginname\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$ln_optional = $id > 0;\n\t\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\n\t\t\t$result = $this->apiCall('Admins.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'loginname' => $loginname\n\t\t\t]);\n\t\t\t$id = $result['adminid'];\n\n\t\t\t// don't be stupid\n\t\t\tif ($id == $this->getUserDetail('adminid')) {\n\t\t\t\tResponse::standardError('youcantdeleteyourself', '', true);\n\t\t\t}\n\t\t\t// can't delete the first superadmin\n\t\t\tif ($id == 1) {\n\t\t\t\tResponse::standardError('cannotdeletesuperadmin', '', true);\n\t\t\t}\n\n\t\t\t// delete admin\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `adminid` = :adminid\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'adminid' => $id\n\t\t\t], true, true);\n\n\t\t\t// delete the traffic-usage\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_TRAFFIC_ADMINS . \"` WHERE `adminid` = :adminid\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'adminid' => $id\n\t\t\t], true, true);\n\n\t\t\t// set admin-id of the old admin's customer to current admins\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t`adminid` = :userid WHERE `adminid` = :adminid\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'userid' => $this->getUserDetail('adminid'),\n\t\t\t\t'adminid' => $id\n\t\t\t], true, true);\n\n\t\t\t// set admin-id of the old admin's domains to current admins\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t`adminid` = :userid WHERE `adminid` = :adminid\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'userid' => $this->getUserDetail('adminid'),\n\t\t\t\t'adminid' => $id\n\t\t\t], true, true);\n\n\t\t\t// delete old admin's api keys if exists (no customer keys)\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_API_KEYS . \"` WHERE\n\t\t\t\t`adminid` = :adminid AND `customerid` = '0'\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'adminid' => $id\n\t\t\t], true, true);\n\n\t\t\t// set admin-id of the old admin's api-keys to current admins\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_API_KEYS . \"` SET\n\t\t\t\t`adminid` = :userid WHERE `adminid` = :adminid\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'userid' => $this->getUserDetail('adminid'),\n\t\t\t\t'adminid' => $id\n\t\t\t], true, true);\n\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] deleted admin '\" . $result['loginname'] . \"'\");\n\t\t\tUser::updateCounters();\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * unlock a locked admin by either id or loginname\n\t *\n\t * @param int $id\n\t *            optional, the admin-id\n\t * @param string $loginname\n\t *            optional, the loginname\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function unlock()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$ln_optional = $id > 0;\n\t\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\n\t\t\t$result = $this->apiCall('Admins.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'loginname' => $loginname\n\t\t\t]);\n\t\t\t$id = $result['adminid'];\n\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET\n\t\t\t\t`loginfail_count` = '0'\n\t\t\t\tWHERE `adminid`= :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\t// set the new value for result-array\n\t\t\t$result['loginfail_count'] = 0;\n\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] unlocked admin '\" . $result['loginname'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Certificates.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Certificates extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add new ssl-certificate entry for given domain by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param string $ssl_cert_file\n\t * @param string $ssl_key_file\n\t * @param string $ssl_ca_file\n\t *            optional\n\t * @param string $ssl_cert_chainfile\n\t *            optional\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\t$domainid = $this->getParam('domainid', true, 0);\n\t\t$dn_optional = $domainid > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$domain = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $domainid,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$domainid = $domain['id'];\n\n\t\t// parameters\n\t\t$ssl_cert_file = $this->getParam('ssl_cert_file');\n\t\t$ssl_key_file = $this->getParam('ssl_key_file');\n\t\t$ssl_ca_file = $this->getParam('ssl_ca_file', true, '');\n\t\t$ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, '');\n\n\t\t// validate whether the domain does not already have an entry\n\t\t$has_cert = true;\n\t\ttry {\n\t\t\t$this->apiCall('Certificates.get', [\n\t\t\t\t'id' => $domainid\n\t\t\t]);\n\t\t} catch (Exception $e) {\n\t\t\tif ($e->getCode() == 412) {\n\t\t\t\t$has_cert = false;\n\t\t\t} else {\n\t\t\t\tthrow $e;\n\t\t\t}\n\t\t}\n\t\tif (!$has_cert) {\n\t\t\t$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true);\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added ssl-certificate for '\" . $domain['domain'] . \"'\");\n\t\t\t$result = $this->apiCall('Certificates.get', [\n\t\t\t\t'id' => $domain['id']\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Domain '\" . $domain['domain'] . \"' already has a certificate. Did you mean to call update?\", 406);\n\t}\n\n\t/**\n\t * insert or update certificates entry\n\t *\n\t * @param int $domainid\n\t * @param string $ssl_cert_file\n\t * @param string $ssl_key_file\n\t * @param string $ssl_ca_file\n\t * @param string $ssl_cert_chainfile\n\t * @param boolean $do_insert\n\t *            optional default false\n\t *\n\t * @return boolean\n\t * @throws Exception\n\t */\n\tprivate function addOrUpdateCertificate($domainid = 0, $ssl_cert_file = '', $ssl_key_file = '', $ssl_ca_file = '', $ssl_cert_chainfile = '', $do_insert = false)\n\t{\n\t\tif ($ssl_cert_file != '' && $ssl_key_file == '') {\n\t\t\tResponse::standardError('sslcertificateismissingprivatekey', '', true);\n\t\t}\n\n\t\t$do_verify = true;\n\t\t$validtodate = null;\n\t\t$validtodate = null;\n\t\t$issuer = \"\";\n\t\t// no cert-file given -> forget everything\n\t\tif ($ssl_cert_file == '') {\n\t\t\t$ssl_key_file = '';\n\t\t\t$ssl_ca_file = '';\n\t\t\t$ssl_cert_chainfile = '';\n\t\t\t$do_verify = false;\n\t\t}\n\n\t\t// verify certificate content\n\t\tif ($do_verify) {\n\t\t\t// array openssl_x509_parse ( mixed $x509cert [, bool $shortnames = true ] )\n\t\t\t// openssl_x509_parse() returns information about the supplied x509cert, including fields such as\n\t\t\t// subject name, issuer name, purposes, valid from and valid to dates etc.\n\t\t\t$cert_content = openssl_x509_parse($ssl_cert_file);\n\n\t\t\tif (is_array($cert_content) && isset($cert_content['subject']) && isset($cert_content['subject']['CN'])) {\n\t\t\t\t// bool openssl_x509_check_private_key ( mixed $cert , mixed $key )\n\t\t\t\t// Checks whether the given key is the private key that corresponds to cert.\n\t\t\t\tif (openssl_x509_check_private_key($ssl_cert_file, $ssl_key_file) === false) {\n\t\t\t\t\tResponse::standardError('sslcertificateinvalidcertkeypair', '', true);\n\t\t\t\t}\n\n\t\t\t\t// check optional stuff\n\t\t\t\tif ($ssl_ca_file != '') {\n\t\t\t\t\t$ca_content = openssl_x509_parse($ssl_ca_file);\n\t\t\t\t\tif (!is_array($ca_content)) {\n\t\t\t\t\t\t// invalid\n\t\t\t\t\t\tResponse::standardError('sslcertificateinvalidca', '', true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ($ssl_cert_chainfile != '') {\n\t\t\t\t\t$chain_content = openssl_x509_parse($ssl_cert_chainfile);\n\t\t\t\t\tif (!is_array($chain_content)) {\n\t\t\t\t\t\t// invalid\n\t\t\t\t\t\tResponse::standardError('sslcertificateinvalidchain', '', true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tResponse::standardError('sslcertificateinvalidcert', '', true);\n\t\t\t}\n\t\t\t// get data from certificate to store in the table\n\t\t\t$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date(\"Y-m-d H:i:s\", $cert_content['validFrom_time_t']);\n\t\t\t$validtodate = empty($cert_content['validTo_time_t']) ? null : date(\"Y-m-d H:i:s\", $cert_content['validTo_time_t']);\n\t\t\t$issuer = $cert_content['issuer']['O'] ?? \"\";\n\t\t}\n\n\t\t// Add/Update database entry\n\t\t$qrystart = \"UPDATE \";\n\t\t$qrywhere = \"WHERE \";\n\t\tif ($do_insert) {\n\t\t\t$qrystart = \"INSERT INTO \";\n\t\t\t$qrywhere = \", \";\n\t\t}\n\t\t$stmt = Database::prepare($qrystart . \" `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` SET\n\t\t\t`ssl_cert_file` = :ssl_cert_file,\n\t\t\t`ssl_key_file` = :ssl_key_file,\n\t\t\t`ssl_ca_file` = :ssl_ca_file,\n\t\t\t`ssl_cert_chainfile` = :ssl_cert_chainfile,\n\t\t\t`validfromdate` = :validfromdate,\n\t\t\t`validtodate` = :validtodate,\n\t\t\t`issuer` = :issuer\n\t\t\t\" . $qrywhere . \" `domainid`= :domainid\n\t\t\");\n\t\t$params = [\n\t\t\t\"ssl_cert_file\" => $ssl_cert_file,\n\t\t\t\"ssl_key_file\" => $ssl_key_file,\n\t\t\t\"ssl_ca_file\" => $ssl_ca_file,\n\t\t\t\"ssl_cert_chainfile\" => $ssl_cert_chainfile,\n\t\t\t\"validfromdate\" => $validfromdate,\n\t\t\t\"validtodate\" => $validtodate,\n\t\t\t\"issuer\" => $issuer,\n\t\t\t\"domainid\" => $domainid\n\t\t];\n\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t// insert task to re-generate webserver-configs (#1260)\n\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\treturn true;\n\t}\n\n\t/**\n\t * update ssl-certificate entry for given domain by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param string $ssl_cert_file\n\t * @param string $ssl_key_file\n\t * @param string $ssl_ca_file\n\t *            optional\n\t * @param string $ssl_cert_chainfile\n\t *            optional\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$domain = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\n\t\t// parameters\n\t\t$ssl_cert_file = $this->getParam('ssl_cert_file');\n\t\t$ssl_key_file = $this->getParam('ssl_key_file');\n\t\t$ssl_ca_file = $this->getParam('ssl_ca_file', true, '');\n\t\t$ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, '');\n\t\t$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false);\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] updated ssl-certificate for '\" . $domain['domain'] . \"'\");\n\t\t$result = $this->apiCall('Certificates.get', [\n\t\t\t'id' => $domain['id']\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * lists all certificate entries\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t// select all my (accessible) certificates\n\t\t$certs_stmt_query = \"SELECT s.*, d.domain, d.letsencrypt, c.customerid, c.loginname\n\t\t\tFROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` s\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` d ON `d`.`id` = `s`.`domainid`\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` c ON `c`.`customerid` = `d`.`customerid`\n\t\t\tWHERE \";\n\n\t\t$qry_params = [];\n\t\t$query_fields = [];\n\t\tif ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '0') {\n\t\t\t// admin with only customer-specific permissions\n\t\t\t$certs_stmt_query .= \"d.adminid = :adminid \";\n\t\t\t$qry_params['adminid'] = $this->getUserDetail('adminid');\n\t\t} elseif ($this->isAdmin() == false) {\n\t\t\t// customer-area\n\t\t\t$certs_stmt_query .= \"d.customerid = :cid \";\n\t\t\t$qry_params['cid'] = $this->getUserDetail('customerid');\n\t\t} else {\n\t\t\t$certs_stmt_query .= \"1 \";\n\t\t}\n\t\t$certs_stmt = Database::prepare($certs_stmt_query . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\t$qry_params = array_merge($qry_params, $query_fields);\n\t\tDatabase::pexecute($certs_stmt, $qry_params, true, true);\n\t\t$result = [];\n\t\twhile ($cert = $certs_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t// respect froxlor-hostname\n\t\t\tif ($cert['domainid'] == 0) {\n\t\t\t\t$cert['domain'] = Settings::Get('system.hostname');\n\t\t\t\t$cert['letsencrypt'] = Settings::Get('system.le_froxlor_enabled');\n\t\t\t\t$cert['loginname'] = 'froxlor.panel';\n\t\t\t}\n\n\t\t\t// Set data from certificate\n\t\t\t$cert['isvalid'] = false;\n\t\t\t$cert['san'] = null;\n\t\t\t$cert_data = openssl_x509_parse($cert['ssl_cert_file']);\n\t\t\tif ($cert_data) {\n\t\t\t\t$cert['isvalid'] = (bool)$cert_data['validTo_time_t'] > time();\n\t\t\t\t// Set subject alt names from certificate\n\t\t\t\tif (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {\n\t\t\t\t\t$SANs = explode(\",\", $cert_data['extensions']['subjectAltName']);\n\t\t\t\t\t$SANs = array_map('trim', $SANs);\n\t\t\t\t\tforeach ($SANs as $san) {\n\t\t\t\t\t\t$san = str_replace(\"DNS:\", \"\", $san);\n\t\t\t\t\t\tif ($san != $cert_data['subject']['CN'] && strpos($san, \"othername:\") === false) {\n\t\t\t\t\t\t\t$cert['san'][] = $san;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t$result[] = $cert;\n\t\t}\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * return ssl-certificate entry for given domain by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$domain = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$domainid = $domain['id'];\n\n\t\t$stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid`= :domainid\");\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get ssl-certificate for '\" . $domain['domain'] . \"'\");\n\t\t$result = Database::pexecute_first($stmt, [\n\t\t\t\"domainid\" => $domainid\n\t\t]);\n\t\tif (!$result) {\n\t\t\tthrow new Exception(\"Domain '\" . $domain['domain'] . \"' does not have a certificate.\", 412);\n\t\t}\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * returns the total number of certificates for the given user\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\t// select all my (accessible) certificates\n\t\t$certs_stmt_query = \"SELECT COUNT(*) as num_certs\n\t\t\tFROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` s\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` d ON `d`.`id` = `s`.`domainid`\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` c ON `c`.`customerid` = `d`.`customerid`\n\t\t\tWHERE \";\n\t\t$qry_params = [];\n\t\t$query_fields = [];\n\t\tif ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '0') {\n\t\t\t// admin with only customer-specific permissions\n\t\t\t$certs_stmt_query .= \"d.adminid = :adminid \";\n\t\t\t$qry_params['adminid'] = $this->getUserDetail('adminid');\n\t\t} elseif ($this->isAdmin() == false) {\n\t\t\t// customer-area\n\t\t\t$certs_stmt_query .= \"d.customerid = :cid \";\n\t\t\t$qry_params['cid'] = $this->getUserDetail('customerid');\n\t\t} else {\n\t\t\t$certs_stmt_query .= \"1 \";\n\t\t}\n\t\t$certs_stmt = Database::prepare($certs_stmt_query . $this->getSearchWhere($query_fields, true));\n\t\t$qry_params = array_merge($qry_params, $query_fields);\n\t\t$result = Database::pexecute_first($certs_stmt, $qry_params, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_certs']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * delete certificates entry by id\n\t *\n\t * @param int $id\n\t *\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\t$id = $this->getParam('id');\n\n\t\tif ($this->isAdmin() == false) {\n\t\t\t$chk_stmt = Database::prepare(\"\n\t\t\t\tSELECT d.domain, d.letsencrypt FROM `\" . TABLE_PANEL_DOMAINS . \"` d\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` s ON s.domainid = d.id\n\t\t\t\tWHERE s.`id` = :id AND d.`customerid` = :cid\n\t\t\t\");\n\t\t\t$chk = Database::pexecute_first($chk_stmt, [\n\t\t\t\t'id' => $id,\n\t\t\t\t'cid' => $this->getUserDetail('customerid')\n\t\t\t]);\n\t\t} elseif ($this->isAdmin()) {\n\t\t\t$chk_stmt = Database::prepare(\"\n\t\t\t\tSELECT d.domain, d.letsencrypt FROM `\" . TABLE_PANEL_DOMAINS . \"` d\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` s ON s.domainid = d.id\n\t\t\t\tWHERE s.`id` = :id\" . ($this->getUserDetail('customers_see_all') == '0' ? \" AND d.`adminid` = :aid\" : \"\"));\n\t\t\t$params = [\n\t\t\t\t'id' => $id\n\t\t\t];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params['aid'] = $this->getUserDetail('adminid');\n\t\t\t}\n\t\t\t$chk = Database::pexecute_first($chk_stmt, $params);\n\t\t\tif ($chk == false && $this->getUserDetail('change_serversettings')) {\n\t\t\t\t// check whether it might be the froxlor-vhost certificate\n\t\t\t\t$chk_stmt = Database::prepare(\"\n\t\t\t\tSELECT \\\"\" . Settings::Get('system.hostname') . \"\\\" as domain, \\\"\" . Settings::Get('system.le_froxlor_enabled') . \"\\\" as letsencrypt FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\t\t\tWHERE `id` = :id AND `domainid` = '0'\");\n\t\t\t\t$params = [\n\t\t\t\t\t'id' => $id\n\t\t\t\t];\n\t\t\t\t$chk = Database::pexecute_first($chk_stmt, $params);\n\t\t\t\t$chk['isFroxlorVhost'] = true;\n\t\t\t}\n\t\t}\n\t\tif ($chk !== false) {\n\t\t\t// additional access check by trying to get the certificate\n\t\t\tif (isset($chk['isFroxlorVhost']) && $chk['isFroxlorVhost'] == true) {\n\t\t\t\t$result = $chk;\n\t\t\t} else {\n\t\t\t\t$result = $this->apiCall('Certificates.get', [\n\t\t\t\t\t'domainname' => $chk['domain']\n\t\t\t\t]);\n\t\t\t}\n\t\t\t$del_stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE id = :id\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\t\t\t// trigger removing of certificate from acme.sh if let's encrypt\n\t\t\tif ($chk['letsencrypt'] == '1') {\n\t\t\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $chk['domain']);\n\t\t\t}\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] removed ssl-certificate for '\" . $chk['domain'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Unable to determine SSL certificate. Maybe no access?\", 406);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Cronjobs.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Cronjobs extends ApiCommand implements ResourceEntity\n{\n\n\tprivate array $allowed_intervals = [\n\t\t'MINUTE',\n\t\t'HOUR',\n\t\t'DAY',\n\t\t'WEEK',\n\t\t'MONTH'\n\t];\n\n\t/**\n\t * You cannot add new cronjobs yet.\n\t */\n\tpublic function add()\n\t{\n\t\tthrow new Exception('You cannot add new cronjobs yet.', 303);\n\t}\n\n\t/**\n\t * return a cronjob entry by id\n\t *\n\t * @param int $id\n\t *            cronjob-id\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_CRONRUNS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t\tthrow new Exception(\"cronjob with id #\" . $id . \" could not be found\", 404);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * update a cronjob entry by given id\n\t *\n\t * @param int $id\n\t * @param bool $isactive\n\t *            optional whether the cronjob is active or not\n\t * @param int $interval_value\n\t *            optional number of seconds/minutes/hours/etc. for the interval\n\t * @param string $interval_interval\n\t *            optional interval for the cronjob (MINUTE, HOUR, DAY, WEEK or MONTH)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t// required parameter\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result = $this->apiCall('Cronjobs.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\t// split interval\n\t\t\t$cur_int = explode(\" \", $result['interval']);\n\n\t\t\t// parameter\n\t\t\t$isactive = $this->getBoolParam('isactive', true, $result['isactive']);\n\t\t\t$interval_value = $this->getParam('interval_value', true, $cur_int[0]);\n\t\t\t$interval_interval = $this->getParam('interval_interval', true, $cur_int[1]);\n\n\t\t\t// validation\n\t\t\tif ($isactive != 1) {\n\t\t\t\t$isactive = 0;\n\t\t\t}\n\t\t\t$interval_value = Validate::validate($interval_value, 'interval_value', '/^([0-9]+)$/Di', 'stringisempty', [], true);\n\t\t\t$interval_interval = Validate::validate($interval_interval, 'interval_interval', '', '', [], true);\n\n\t\t\tif (!in_array(strtoupper($interval_interval), $this->allowed_intervals)) {\n\t\t\t\tResponse::standardError('invalidcronjobintervalvalue', implode(\", \", $this->allowed_intervals), true);\n\t\t\t}\n\n\t\t\t// put together interval value\n\t\t\t$interval = $interval_value . ' ' . strtoupper($interval_interval);\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_CRONRUNS . \"`\n\t\t\t\tSET `isactive` = :isactive, `interval` = :int\n\t\t\t\tWHERE `id` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'isactive' => $isactive,\n\t\t\t\t'int' => $interval,\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// insert task to re-generate the cron.d-file\n\t\t\tCronjob::inserttask(TaskId::REBUILD_CRON);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] cronjob with description '\" . $result['module'] . '/' . $result['cronfile'] . \"' has been updated by '\" . $this->getUserDetail('loginname') . \"'\");\n\t\t\t$result = $this->apiCall('Cronjobs.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * lists all cronjob entries\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] list cronjobs\");\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `c`.* FROM `\" . TABLE_PANEL_CRONRUNS . \"` `c` \" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());\n\t\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\t\t$result = [];\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$result[] = $row;\n\t\t\t}\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($result),\n\t\t\t\t'list' => $result\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns the total number of cronjobs\n\t *\n\t * @access admin\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_crons FROM `\" . TABLE_PANEL_CRONRUNS . \"` `c`\n\t\t\t\" . $this->getSearchWhere($query_fields));\n\t\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_crons']);\n\t\t\t}\n\t\t\treturn $this->response(0);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * You cannot delete system cronjobs.\n\t */\n\tpublic function delete()\n\t{\n\t\tthrow new Exception('You cannot delete system cronjobs.', 303);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Customers.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Database\\DbManager;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Language;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Customers extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * lists all customer entries\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t * @param bool $show_usages\n\t *            optional, default false\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$show_usages = $this->getBoolParam('show_usages', true, false);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] list customers\");\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `c`.*, `a`.`loginname` AS `adminname`\n\t\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` `c`, `\" . TABLE_PANEL_ADMINS . \"` `a`\n\t\t\t\tWHERE \" . ($this->getUserDetail('customers_see_all') ? '' : \" `c`.`adminid` = :adminid AND \") . \"\n\t\t\t\t`c`.`adminid` = `a`.`adminid`\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\t\t$params = [];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params = [\n\t\t\t\t\t'adminid' => $this->getUserDetail('adminid')\n\t\t\t\t];\n\t\t\t}\n\t\t\t$params = array_merge($params, $query_fields);\n\t\t\tDatabase::pexecute($result_stmt, $params, true, true);\n\t\t\t$result = [];\n\n\t\t\t$domains_stmt = null;\n\t\t\t$usages_stmt = null;\n\t\t\tif ($show_usages) {\n\t\t\t\t$domains_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT COUNT(`id`) AS `domains`\n\t\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `customerid` = :cid\n\t\t\t\t\tAND `parentdomainid` = '0'\n\t\t\t\t\tAND `id`<> :stdd\n\t\t\t\t\");\n\t\t\t\t$usages_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT webspace, mail, mysql FROM `\" . TABLE_PANEL_DISKSPACE . \"`\n\t\t\t\t\tWHERE `customerid` = :cid\n\t\t\t\t\tORDER BY `stamp` DESC LIMIT 1\n\t\t\t\t\");\n\t\t\t}\n\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ($show_usages) {\n\t\t\t\t\t// get number of domains\n\t\t\t\t\t$domains = Database::pexecute_first($domains_stmt, [\n\t\t\t\t\t\t'cid' => $row['customerid'],\n\t\t\t\t\t\t'stdd' => $row['standardsubdomain']\n\t\t\t\t\t]);\n\t\t\t\t\t$row['domains'] = intval($domains['domains']);\n\t\t\t\t\t// get disk-space usages for web, mysql and mail\n\t\t\t\t\t$usages = Database::pexecute_first($usages_stmt, [\n\t\t\t\t\t\t'cid' => $row['customerid']\n\t\t\t\t\t]);\n\t\t\t\t\tif ($usages) {\n\t\t\t\t\t\t$row['webspace_used'] = $usages['webspace'];\n\t\t\t\t\t\t$row['mailspace_used'] = $usages['mail'];\n\t\t\t\t\t\t$row['dbspace_used'] = $usages['mysql'];\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$row['webspace_used'] = 0;\n\t\t\t\t\t\t$row['mailspace_used'] = 0;\n\t\t\t\t\t\t$row['dbspace_used'] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$result[] = $row;\n\t\t\t}\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($result),\n\t\t\t\t'list' => $result\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns the total number of customers for the given admin\n\t *\n\t * @access admin\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_customers\n\t\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` `c`, `\" . TABLE_PANEL_ADMINS . \"` `a`\n\t\t\t\tWHERE `c`.`adminid` = `a`.`adminid` AND \" . ($this->getUserDetail('customers_see_all') ? \"1\" : \" `c`.`adminid` = :adminid \") . $this->getSearchWhere($query_fields, true));\n\t\t\t$params = [];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params = [\n\t\t\t\t\t'adminid' => $this->getUserDetail('adminid')\n\t\t\t\t];\n\t\t\t}\n\t\t\t$params = array_merge($params, $query_fields);\n\t\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_customers']);\n\t\t\t}\n\t\t\treturn $this->response(0);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * create a new customer with default ftp-user and standard-subdomain (if wanted)\n\t *\n\t * @param string $email\n\t *                             required, email address of new customer\n\t * @param string $name\n\t *                             optional if company is set, else required\n\t * @param string $firstname\n\t *                             optional if company is set, else required\n\t * @param string $company\n\t *                             optional but required if name/firstname empty\n\t * @param string $street\n\t *                             optional\n\t * @param string $zipcode\n\t *                             optional\n\t * @param string $city\n\t *                             optional\n\t * @param string $phone\n\t *                             optional\n\t * @param string $fax\n\t *                             optional\n\t * @param int $customernumber\n\t *                             optional\n\t * @param string $def_language\n\t *                             optional, ISO 639-1 language code (e.g. 'en', 'de', see lng-folder for supported languages),\n\t *                             default is system-default language\n\t * @param bool $gui_access\n\t *                             optional, allow login via webui, if false ONLY the login via webui is disallowed; default true\n\t * @param bool $api_allowed\n\t *                             optional, default is true if system setting api.enabled is true, else false\n\t * @param bool $shell_allowed\n\t *                             optional, default is true if system setting system.allow_customer_shell is true, else false\n\t * @param int $gender\n\t *                             optional, 0 = no-gender, 1 = male, 2 = female\n\t * @param string $custom_notes\n\t *                             optional notes\n\t * @param bool $custom_notes_show\n\t *                             optional, whether to show the content of custom_notes to the customer, default 0\n\t *                             (false)\n\t * @param string $new_loginname\n\t *                             optional, if empty generated automatically using customer-prefix and increasing\n\t *                             number\n\t * @param string $new_customer_password\n\t *                             optional, if empty generated automatically and send to the customer's email if\n\t *                             $sendpassword is 1\n\t * @param bool $sendpassword\n\t *                             optional, whether to send the password to the customer after creation, default 0\n\t *                             (false)\n\t * @param int $diskspace\n\t *                             optional disk-space available for customer in MB, default 0\n\t * @param bool $diskspace_ul\n\t *                             optional, whether customer should have unlimited diskspace, default 0 (false)\n\t * @param int $traffic\n\t *                             optional traffic available for customer in GB, default 0\n\t * @param bool $traffic_ul\n\t *                             optional, whether customer should have unlimited traffic, default 0 (false)\n\t * @param int $subdomains\n\t *                             optional amount of subdomains available for customer, default 0\n\t * @param bool $subdomains_ul\n\t *                             optional, whether customer should have unlimited subdomains, default 0 (false)\n\t * @param int $emails\n\t *                             optional amount of emails available for customer, default 0\n\t * @param bool $emails_ul\n\t *                             optional, whether customer should have unlimited emails, default 0 (false)\n\t * @param int $email_accounts\n\t *                             optional amount of email-accounts available for customer, default 0\n\t * @param bool $email_accounts_ul\n\t *                             optional, whether customer should have unlimited email-accounts, default 0 (false)\n\t * @param int $email_forwarders\n\t *                             optional amount of email-forwarders available for customer, default 0\n\t * @param bool $email_forwarders_ul\n\t *                             optional, whether customer should have unlimited email-forwarders, default 0 (false)\n\t * @param int $email_quota\n\t *                             optional size of email-quota available for customer in MB, default is system-setting\n\t *                             mail_quota\n\t * @param bool $email_quota_ul\n\t *                             optional, whether customer should have unlimited email-quota, default 0 (false)\n\t * @param bool $email_imap\n\t *                             optional, whether to allow IMAP access, default 0 (false)\n\t * @param bool $email_pop3\n\t *                             optional, whether to allow POP3 access, default 0 (false)\n\t * @param int $ftps\n\t *                             optional amount of ftp-accounts available for customer, default 0\n\t * @param bool $ftps_ul\n\t *                             optional, whether customer should have unlimited ftp-accounts, default 0 (false)\n\t * @param int $mysqls\n\t *                             optional amount of mysql-databases available for customer, default 0\n\t * @param bool $mysqls_ul\n\t *                             optional, whether customer should have unlimited mysql-databases, default 0 (false)\n\t * @param bool $createstdsubdomain\n\t *                             optional, whether to create a standard-subdomain ([loginname].froxlor-hostname.tld),\n\t *                             default [system.createstdsubdom_default]\n\t * @param bool $phpenabled\n\t *                             optional, whether to allow usage of PHP, default 0 (false)\n\t * @param array $allowed_phpconfigs\n\t *                             optional, array of IDs of php-config that the customer is allowed to use, default\n\t *                             empty (none)\n\t * @param bool $perlenabled\n\t *                             optional, whether to allow usage of Perl/CGI, default 0 (false)\n\t * @param bool $dnsenabled\n\t *                             optional, whether to allow usage of the DNS editor (requires activated nameserver in\n\t *                             settings), default 0 (false)\n\t * @param bool $logviewenabled\n\t *                             optional, whether to allow access to webserver access/error-logs, default 0 (false)\n\t * @param bool $store_defaultindex\n\t *                             optional, whether to store the default index file to customers homedir\n\t * @param int $hosting_plan_id\n\t *                             optional, specify a hosting-plan to set certain resource-values from the plan\n\t *                             instead of specifying them\n\t * @param array $allowed_mysqlserver\n\t *                             optional, array of IDs of defined mysql-servers the customer is allowed to use,\n\t *                             default is to allow the default dbserver (id=0)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($this->getUserDetail('customers_used') < $this->getUserDetail('customers') || $this->getUserDetail('customers') == '-1') {\n\t\t\t\t// required parameters\n\t\t\t\t$email = $this->getParam('email');\n\n\t\t\t\t// parameters\n\t\t\t\t$name = $this->getParam('name', true, '');\n\t\t\t\t$firstname = $this->getParam('firstname', true, '');\n\t\t\t\t$company_required = (!empty($name) && empty($firstname)) || (empty($name) && !empty($firstname)) || (empty($name) && empty($firstname));\n\t\t\t\t$company = $this->getParam('company', !$company_required, '');\n\t\t\t\t$street = $this->getParam('street', true, '');\n\t\t\t\t$zipcode = $this->getParam('zipcode', true, '');\n\t\t\t\t$city = $this->getParam('city', true, '');\n\t\t\t\t$phone = $this->getParam('phone', true, '');\n\t\t\t\t$fax = $this->getParam('fax', true, '');\n\t\t\t\t$customernumber = $this->getParam('customernumber', true, '');\n\t\t\t\t$def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage'));\n\t\t\t\t$gui_access = $this->getBoolParam('gui_access', true, 1);\n\t\t\t\t$api_allowed = $this->getBoolParam('api_allowed', true, (Settings::Get('api.enabled') && Settings::Get('api.customer_default')));\n\t\t\t\t$shell_allowed = $this->getBoolParam('shell_allowed', true, intval(Settings::Get('system.allow_customer_shell')));\n\t\t\t\t$gender = (int)$this->getParam('gender', true, 0);\n\t\t\t\t$custom_notes = $this->getParam('custom_notes', true, '');\n\t\t\t\t$custom_notes_show = $this->getBoolParam('custom_notes_show', true, 0);\n\t\t\t\t$createstdsubdomain = $this->getBoolParam('createstdsubdomain', true, Settings::Get('system.createstdsubdom_default'));\n\t\t\t\t$password = $this->getParam('new_customer_password', true, '');\n\t\t\t\t$sendpassword = $this->getBoolParam('sendpassword', true, 0);\n\t\t\t\t$store_defaultindex = $this->getBoolParam('store_defaultindex', true, 0);\n\t\t\t\t$loginname = $this->getParam('new_loginname', true, '');\n\n\t\t\t\t// hosting-plan values\n\t\t\t\t$hosting_plan_id = $this->getParam('hosting_plan_id', true, 0);\n\t\t\t\tif ($hosting_plan_id > 0) {\n\t\t\t\t\t$hp_result = $this->apiCall('HostingPlans.get', [\n\t\t\t\t\t\t'id' => $hosting_plan_id\n\t\t\t\t\t]);\n\t\t\t\t\t$hp_result['value'] = json_decode($hp_result['value'], true);\n\t\t\t\t\tforeach ($hp_result['value'] as $index => $value) {\n\t\t\t\t\t\t$hp_result[$index] = $value;\n\t\t\t\t\t}\n\t\t\t\t\t$diskspace = $hp_result['diskspace'] ?? 0;\n\t\t\t\t\t$traffic = $hp_result['traffic'] ?? 0;\n\t\t\t\t\t$subdomains = $hp_result['subdomains'] ?? 0;\n\t\t\t\t\t$emails = $hp_result['emails'] ?? 0;\n\t\t\t\t\t$email_accounts = $hp_result['email_accounts'] ?? 0;\n\t\t\t\t\t$email_forwarders = $hp_result['email_forwarders'] ?? 0;\n\t\t\t\t\t$email_quota = $hp_result['email_quota'] ?? Settings::Get('system.mail_quota');\n\t\t\t\t\t$email_imap = $hp_result['email_imap'] ?? 0;\n\t\t\t\t\t$email_pop3 = $hp_result['email_pop3'] ?? 0;\n\t\t\t\t\t$ftps = $hp_result['ftps'] ?? 0;\n\t\t\t\t\t$mysqls = $hp_result['mysqls'] ?? 0;\n\t\t\t\t\t$phpenabled = $hp_result['phpenabled'] ?? 0;\n\t\t\t\t\t$p_allowed_phpconfigs = $hp_result['allowed_phpconfigs'] ?? 0;\n\t\t\t\t\t$perlenabled = $hp_result['perlenabled'] ?? 0;\n\t\t\t\t\t$dnsenabled = $hp_result['dnsenabled'] ?? 0;\n\t\t\t\t\t$logviewenabled = $hp_result['logviewenabled'] ?? 0;\n\t\t\t\t} else {\n\t\t\t\t\t$diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, 0);\n\t\t\t\t\t$traffic = $this->getUlParam('traffic', 'traffic_ul', true, 0);\n\t\t\t\t\t$subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, 0);\n\t\t\t\t\t$emails = $this->getUlParam('emails', 'emails_ul', true, 0);\n\t\t\t\t\t$email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, 0);\n\t\t\t\t\t$email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, 0);\n\t\t\t\t\t$email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, Settings::Get('system.mail_quota'));\n\t\t\t\t\t$email_imap = $this->getBoolParam('email_imap', true, 0);\n\t\t\t\t\t$email_pop3 = $this->getBoolParam('email_pop3', true, 0);\n\t\t\t\t\t$ftps = $this->getUlParam('ftps', 'ftps_ul', true, 0);\n\t\t\t\t\t$mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, 0);\n\t\t\t\t\t$phpenabled = $this->getBoolParam('phpenabled', true, 0);\n\t\t\t\t\t$p_allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, []);\n\t\t\t\t\t$perlenabled = $this->getBoolParam('perlenabled', true, 0);\n\t\t\t\t\t$dnsenabled = $this->getBoolParam('dnsenabled', true, 0);\n\t\t\t\t\t$logviewenabled = $this->getBoolParam('logviewenabled', true, 0);\n\t\t\t\t}\n\n\t\t\t\tif ($mysqls == -1 || $mysqls > 0) {\n\t\t\t\t\t$p_allowed_mysqlserver = $this->getParam('allowed_mysqlserver', true, [0]);\n\t\t\t\t} else {\n\t\t\t\t\t// mysql not allowed, so no mysql available for customer\n\t\t\t\t\t$p_allowed_mysqlserver = [];\n\t\t\t\t}\n\n\t\t\t\t// validation\n\t\t\t\t$name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t\t$firstname = Validate::validate($firstname, 'first name', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t\t$company = Validate::validate($company, 'company', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t\t$street = Validate::validate($street, 'street', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t\t$zipcode = Validate::validate($zipcode, 'zipcode', '/^[0-9 \\-A-Z]*$/', '', [], true);\n\t\t\t\t$city = Validate::validate($city, 'city', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t\t$phone = Validate::validate($phone, 'phone', '/^[0-9\\- \\+\\(\\)\\/]*$/', '', [], true);\n\t\t\t\t$fax = Validate::validate($fax, 'fax', '/^[0-9\\- \\+\\(\\)\\/]*$/', '', [], true);\n\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\t$email = $idna_convert->encode(Validate::validate($email, 'email', '', '', [], true));\n\t\t\t\t$customernumber = Validate::validate($customernumber, 'customer number', '/^[A-Za-z0-9 \\-]*$/Di', '', [], true);\n\t\t\t\t$def_language = Validate::validate($def_language, 'default language', '', '', [], true);\n\t\t\t\tif (!empty($def_language) && !isset(Language::getLanguages()[$def_language])) {\n\t\t\t\t\t$def_language = Settings::Get('panel.standardlanguage');\n\t\t\t\t}\n\t\t\t\t$custom_notes = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $custom_notes), 'custom_notes', Validate::REGEX_CONF_TEXT, '', [], true);\n\n\t\t\t\tif (Settings::Get('system.mail_quota_enabled') != '1') {\n\t\t\t\t\t$email_quota = -1;\n\t\t\t\t}\n\n\t\t\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\t\t\t\t// only check if not empty,\n\t\t\t\t// cause empty == generate password automatically\n\t\t\t\tif ($password != '') {\n\t\t\t\t\t$password = Crypt::validatePassword($password, true);\n\t\t\t\t}\n\n\t\t\t\t// gender out of range? [0,2]\n\t\t\t\tif ($gender < 0 || $gender > 2) {\n\t\t\t\t\t$gender = 0;\n\t\t\t\t}\n\n\t\t\t\t$allowed_phpconfigs = [];\n\t\t\t\tif (!empty($p_allowed_phpconfigs) && is_array($p_allowed_phpconfigs)) {\n\t\t\t\t\tforeach ($p_allowed_phpconfigs as $allowed_phpconfig) {\n\t\t\t\t\t\t$allowed_phpconfig = intval($allowed_phpconfig);\n\t\t\t\t\t\t$allowed_phpconfigs[] = $allowed_phpconfig;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$allowed_phpconfigs = array_map('intval', $allowed_phpconfigs);\n\n\t\t\t\tif (empty($allowed_phpconfigs) && $phpenabled == 1) {\n\t\t\t\t\t// only required if not using mod_php\n\t\t\t\t\tif ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\tResponse::standardError('customerphpenabledbutnoconfig', '', true);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$allowed_mysqlserver = array();\n\t\t\t\tif (!empty($p_allowed_mysqlserver) && is_array($p_allowed_mysqlserver)) {\n\t\t\t\t\tforeach ($p_allowed_mysqlserver as $allowed_ms) {\n\t\t\t\t\t\t$allowed_ms = intval($allowed_ms);\n\t\t\t\t\t\t$allowed_mysqlserver[] = $allowed_ms;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$allowed_mysqlserver = array_map('intval', $allowed_mysqlserver);\n\n\t\t\t\t$diskspace *= 1024;\n\t\t\t\t$traffic *= 1024 * 1024;\n\n\t\t\t\tif (\n\t\t\t\t\t($diskspace != 0 && (($this->getUserDetail('diskspace_used') + $diskspace) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1')\n\t\t\t\t\t|| ($mysqls != 0 && (($this->getUserDetail('mysqls_used') + $mysqls) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1')\n\t\t\t\t\t|| ($emails != 0 && (($this->getUserDetail('emails_used') + $emails) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1')\n\t\t\t\t\t|| ($email_accounts != 0 && (($this->getUserDetail('email_accounts_used') + $email_accounts) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1')\n\t\t\t\t\t|| ($email_forwarders != 0 && (($this->getUserDetail('email_forwarders_used') + $email_forwarders) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1')\n\t\t\t\t\t|| ($email_quota != 0 && (($this->getUserDetail('email_quota_used') + $email_quota) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1')\n\t\t\t\t\t|| ($ftps != 0 && (($this->getUserDetail('ftps_used') + $ftps) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1')\n\t\t\t\t\t|| ($subdomains != 0 && (($this->getUserDetail('subdomains_used') + $subdomains) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1')\n\t\t\t\t\t|| (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1')\n\t\t\t\t\t|| ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1')\n\t\t\t\t\t|| ($emails == '-1' && $this->getUserDetail('emails') != '-1')\n\t\t\t\t\t|| ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1')\n\t\t\t\t\t|| ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1')\n\t\t\t\t\t|| ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1')\n\t\t\t\t\t|| ($ftps == '-1' && $this->getUserDetail('ftps') != '-1')\n\t\t\t\t\t|| ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')\n\t\t\t\t) {\n\t\t\t\t\tResponse::standardError('youcantallocatemorethanyouhave', '', true);\n\t\t\t\t}\n\n\t\t\t\tif (!Validate::validateEmail($email)) {\n\t\t\t\t\tResponse::standardError('emailiswrong', $email, true);\n\t\t\t\t} else {\n\t\t\t\t\tif ($loginname != '') {\n\t\t\t\t\t\t$accountnumber = intval(Settings::Get('system.lastaccountnumber'));\n\t\t\t\t\t\t$loginname = Validate::validate($loginname, 'loginname', '/^[a-z][a-z0-9\\-_]+$/i', '', [], true);\n\n\t\t\t\t\t\t// Accounts which match systemaccounts are not allowed, filtering them\n\t\t\t\t\t\tif (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) {\n\t\t\t\t\t\t\tResponse::standardError('loginnameisusingprefix', Settings::Get('customer.accountprefix'), true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Additional filtering for Bug #962\n\t\t\t\t\t\tif (function_exists('posix_getpwnam') && !in_array(\"posix_getpwnam\", explode(\",\", ini_get('disable_functions'))) && posix_getpwnam($loginname)) {\n\t\t\t\t\t\t\tResponse::standardError('loginnameissystemaccount', $loginname, true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// blacklist some system-internal names that might lead to issues\n\t\t\t\t\t\tDatabase::needSqlData();\n\t\t\t\t\t\t$sqldata = Database::getSqlData();\n\t\t\t\t\t\tDatabase::needRoot(true);\n\t\t\t\t\t\tDatabase::needSqlData();\n\t\t\t\t\t\t$sqlrdata = Database::getSqlData();\n\t\t\t\t\t\t$login_blacklist = [\n\t\t\t\t\t\t\t'root',\n\t\t\t\t\t\t\t'admin',\n\t\t\t\t\t\t\t'froxroot',\n\t\t\t\t\t\t\t'froxlor',\n\t\t\t\t\t\t\t$sqldata['user'],\n\t\t\t\t\t\t\t$sqldata['db'],\n\t\t\t\t\t\t\t$sqlrdata['user'],\n\t\t\t\t\t\t];\n\t\t\t\t\t\tunset($sqldata);\n\t\t\t\t\t\tunset($sqlrdata);\n\t\t\t\t\t\t$login_blacklist = array_unique($login_blacklist);\n\t\t\t\t\t\tif (in_array($loginname, $login_blacklist)) {\n\t\t\t\t\t\t\tResponse::standardError('loginnameisreservedname', $loginname, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$accountnumber = intval(Settings::Get('system.lastaccountnumber')) + 1;\n\t\t\t\t\t\t$loginname = Settings::Get('customer.accountprefix') . $accountnumber;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if the account already exists\n\t\t\t\t\t// do not check via api as we skip any permission checks for this task\n\t\t\t\t\t$loginname_check_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `loginname` FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `loginname` = :login\n\t\t\t\t\t\");\n\t\t\t\t\t$loginname_check = Database::pexecute_first($loginname_check_stmt, [\n\t\t\t\t\t\t'login' => $loginname\n\t\t\t\t\t], true, true);\n\n\t\t\t\t\t// Check if an admin with the loginname already exists\n\t\t\t\t\t// do not check via api as we skip any permission checks for this task\n\t\t\t\t\t$loginname_check_admin_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `loginname` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `loginname` = :login\n\t\t\t\t\t\");\n\t\t\t\t\t$loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, [\n\t\t\t\t\t\t'login' => $loginname\n\t\t\t\t\t], true, true);\n\n\t\t\t\t\t// Check for existing email address\n\t\t\t\t\t// do not check via api as we skip any permission checks for this task\n\t\t\t\t\t$email_check_admin_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `email` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `email` = :email\n\t\t\t\t\t\");\n\t\t\t\t\t$email_check_admin = Database::pexecute_first($email_check_admin_stmt, [\n\t\t\t\t\t\t'email' => $email\n\t\t\t\t\t], true, true);\n\n\t\t\t\t\t$mysql_maxlen = Database::getSqlUsernameLength() - strlen(Settings::Get('customer.mysqlprefix'));\n\t\t\t\t\tif (($loginname_check && strtolower($loginname_check['loginname']) == strtolower($loginname)) || ($loginname_check_admin && strtolower($loginname_check_admin['loginname']) == strtolower($loginname))) {\n\t\t\t\t\t\tResponse::standardError('loginnameexists', $loginname, true);\n\t\t\t\t\t} elseif (!Validate::validateUsername($loginname, Settings::Get('panel.unix_names'), $mysql_maxlen)) {\n\t\t\t\t\t\tif (strlen($loginname) > $mysql_maxlen) {\n\t\t\t\t\t\t\tResponse::standardError('loginnameiswrong2', $mysql_maxlen, true);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tResponse::standardError('loginnameiswrong', $loginname, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t} elseif ($email_check_admin && strtolower($email_check_admin['email']) == strtolower($email)) {\n\t\t\t\t\t\tResponse::standardError('emailexistsanon', $email, true);\n\t\t\t\t\t}\n\n\t\t\t\t\t$guid = intval(Settings::Get('system.lastguid')) + 1;\n\t\t\t\t\t$documentroot = FileDir::makeCorrectDir(Settings::Get('system.documentroot_prefix') . '/' . $loginname);\n\n\t\t\t\t\tif (file_exists($documentroot)) {\n\t\t\t\t\t\tResponse::standardError('documentrootexists', $documentroot, true);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($password == '') {\n\t\t\t\t\t\t$password = Crypt::generatePassword();\n\t\t\t\t\t}\n\n\t\t\t\t\t$_theme = Settings::Get('panel.default_theme');\n\n\t\t\t\t\t$ins_data = [\n\t\t\t\t\t\t'adminid' => $this->getUserDetail('adminid'),\n\t\t\t\t\t\t'loginname' => $loginname,\n\t\t\t\t\t\t'passwd' => Crypt::makeCryptPassword($password),\n\t\t\t\t\t\t'name' => $name,\n\t\t\t\t\t\t'firstname' => $firstname,\n\t\t\t\t\t\t'gender' => $gender,\n\t\t\t\t\t\t'company' => $company,\n\t\t\t\t\t\t'street' => $street,\n\t\t\t\t\t\t'zipcode' => $zipcode,\n\t\t\t\t\t\t'city' => $city,\n\t\t\t\t\t\t'phone' => $phone,\n\t\t\t\t\t\t'fax' => $fax,\n\t\t\t\t\t\t'email' => $email,\n\t\t\t\t\t\t'customerno' => $customernumber,\n\t\t\t\t\t\t'lang' => $def_language,\n\t\t\t\t\t\t'gui_access' => $gui_access,\n\t\t\t\t\t\t'api_allowed' => $api_allowed,\n\t\t\t\t\t\t'shell_allowed' => $shell_allowed,\n\t\t\t\t\t\t'docroot' => $documentroot,\n\t\t\t\t\t\t'guid' => $guid,\n\t\t\t\t\t\t'diskspace' => $diskspace,\n\t\t\t\t\t\t'traffic' => $traffic,\n\t\t\t\t\t\t'subdomains' => $subdomains,\n\t\t\t\t\t\t'emails' => $emails,\n\t\t\t\t\t\t'email_accounts' => $email_accounts,\n\t\t\t\t\t\t'email_forwarders' => $email_forwarders,\n\t\t\t\t\t\t'email_quota' => $email_quota,\n\t\t\t\t\t\t'ftps' => $ftps,\n\t\t\t\t\t\t'mysqls' => $mysqls,\n\t\t\t\t\t\t'phpenabled' => $phpenabled,\n\t\t\t\t\t\t'allowed_phpconfigs' => empty($allowed_phpconfigs) ? \"\" : json_encode($allowed_phpconfigs),\n\t\t\t\t\t\t'imap' => $email_imap,\n\t\t\t\t\t\t'pop3' => $email_pop3,\n\t\t\t\t\t\t'perlenabled' => $perlenabled,\n\t\t\t\t\t\t'dnsenabled' => $dnsenabled,\n\t\t\t\t\t\t'logviewenabled' => $logviewenabled,\n\t\t\t\t\t\t'theme' => $_theme,\n\t\t\t\t\t\t'custom_notes' => $custom_notes,\n\t\t\t\t\t\t'custom_notes_show' => $custom_notes_show,\n\t\t\t\t\t\t'allowed_mysqlserver' => empty($allowed_mysqlserver) ? \"\" : json_encode($allowed_mysqlserver)\n\t\t\t\t\t];\n\n\t\t\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\t\tINSERT INTO `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t\t\t`adminid` = :adminid,\n\t\t\t\t\t\t`loginname` = :loginname,\n\t\t\t\t\t\t`password` = :passwd,\n\t\t\t\t\t\t`name` = :name,\n\t\t\t\t\t\t`firstname` = :firstname,\n\t\t\t\t\t\t`gender` = :gender,\n\t\t\t\t\t\t`company` = :company,\n\t\t\t\t\t\t`street` = :street,\n\t\t\t\t\t\t`zipcode` = :zipcode,\n\t\t\t\t\t\t`city` = :city,\n\t\t\t\t\t\t`phone` = :phone,\n\t\t\t\t\t\t`fax` = :fax,\n\t\t\t\t\t\t`email` = :email,\n\t\t\t\t\t\t`customernumber` = :customerno,\n\t\t\t\t\t\t`def_language` = :lang,\n\t\t\t\t\t\t`gui_access` = :gui_access,\n\t\t\t\t\t\t`api_allowed` = :api_allowed,\n\t\t\t\t\t\t`shell_allowed` = :shell_allowed,\n\t\t\t\t\t\t`documentroot` = :docroot,\n\t\t\t\t\t\t`guid` = :guid,\n\t\t\t\t\t\t`diskspace` = :diskspace,\n\t\t\t\t\t\t`traffic` = :traffic,\n\t\t\t\t\t\t`subdomains` = :subdomains,\n\t\t\t\t\t\t`emails` = :emails,\n\t\t\t\t\t\t`email_accounts` = :email_accounts,\n\t\t\t\t\t\t`email_forwarders` = :email_forwarders,\n\t\t\t\t\t\t`email_quota` = :email_quota,\n\t\t\t\t\t\t`ftps` = :ftps,\n\t\t\t\t\t\t`mysqls` = :mysqls,\n\t\t\t\t\t\t`standardsubdomain` = '0',\n\t\t\t\t\t\t`phpenabled` = :phpenabled,\n\t\t\t\t\t\t`allowed_phpconfigs` = :allowed_phpconfigs,\n\t\t\t\t\t\t`imap` = :imap,\n\t\t\t\t\t\t`pop3` = :pop3,\n\t\t\t\t\t\t`perlenabled` = :perlenabled,\n\t\t\t\t\t\t`dnsenabled` = :dnsenabled,\n\t\t\t\t\t\t`logviewenabled` = :logviewenabled,\n\t\t\t\t\t\t`theme` = :theme,\n\t\t\t\t\t\t`custom_notes` = :custom_notes,\n\t\t\t\t\t\t`custom_notes_show` = :custom_notes_show,\n\t\t\t\t\t\t`allowed_mysqlserver`= :allowed_mysqlserver\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($ins_stmt, $ins_data, true, true);\n\n\t\t\t\t\t$customerid = Database::lastInsertId();\n\t\t\t\t\t$ins_data['customerid'] = $customerid;\n\n\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'customers_used');\n\n\t\t\t\t\t// update admin resource-usage\n\t\t\t\t\tif ($mysqls != '-1') {\n\t\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'mysqls_used', '', (int)$mysqls);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($emails != '-1') {\n\t\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'emails_used', '', (int)$emails);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($email_accounts != '-1') {\n\t\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'email_accounts_used', '', (int)$email_accounts);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($email_forwarders != '-1') {\n\t\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'email_forwarders_used', '', (int)$email_forwarders);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($email_quota != '-1') {\n\t\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'email_quota_used', '', (int)$email_quota);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($subdomains != '-1') {\n\t\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'subdomains_used', '', (int)$subdomains);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($ftps != '-1') {\n\t\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'ftps_used', '', (int)$ftps);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (($diskspace / 1024) != '-1') {\n\t\t\t\t\t\tAdmins::increaseUsage($this->getUserDetail('adminid'), 'diskspace_used', '', (int)$diskspace);\n\t\t\t\t\t}\n\n\t\t\t\t\t// update last guid\n\t\t\t\t\tSettings::Set('system.lastguid', $guid, true);\n\n\t\t\t\t\tif ($accountnumber != intval(Settings::Get('system.lastaccountnumber'))) {\n\t\t\t\t\t\t// update last account number\n\t\t\t\t\t\tSettings::Set('system.lastaccountnumber', $accountnumber, true);\n\t\t\t\t\t}\n\n\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] added customer '\" . $loginname . \"'\");\n\t\t\t\t\tunset($ins_data);\n\n\t\t\t\t\t// insert task to create homedir etc.\n\t\t\t\t\tCronjob::inserttask(TaskId::CREATE_HOME, $loginname, $guid, $guid, $store_defaultindex);\n\n\t\t\t\t\t// Using filesystem - quota, insert a task which cleans the filesystem - quota\n\t\t\t\t\tCronjob::inserttask(TaskId::CREATE_QUOTA);\n\n\t\t\t\t\t// Add htpasswd for the stats-pages\n\t\t\t\t\t$htpasswdPassword = Crypt::makeCryptPassword($password, true);\n\n\t\t\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\t\tINSERT INTO `\" . TABLE_PANEL_HTPASSWDS . \"` SET\n\t\t\t\t\t\t`customerid` = :customerid,\n\t\t\t\t\t\t`username` = :username,\n\t\t\t\t\t\t`password` = :passwd,\n\t\t\t\t\t\t`path` = :path\n\t\t\t\t\t\");\n\t\t\t\t\t$ins_data = [\n\t\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t\t'username' => $loginname,\n\t\t\t\t\t\t'passwd' => $htpasswdPassword\n\t\t\t\t\t];\n\n\t\t\t\t\t$stats_folder = Settings::Get('system.traffictool');\n\t\t\t\t\t$ins_data['path'] = FileDir::makeCorrectDir($documentroot . '/' . $stats_folder . '/');\n\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] automatically added \" . $stats_folder . \" htpasswd for user '\" . $loginname . \"'\");\n\t\t\t\t\tDatabase::pexecute($ins_stmt, $ins_data, true, true);\n\n\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\n\t\t\t\t\t// add default FTP-User\n\t\t\t\t\t// also, add froxlor-local user to ftp-group (if exists!) to\n\t\t\t\t\t// allow access to customer-directories from within the panel, which\n\t\t\t\t\t// is necessary when pathedit = Dropdown\n\t\t\t\t\t$local_users = [\n\t\t\t\t\t\tSettings::Get('system.httpuser')\n\t\t\t\t\t];\n\t\t\t\t\tif ((int)Settings::Get('system.mod_fcgid_ownvhost') == 1 || (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) {\n\t\t\t\t\t\tif ((int)Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t\t\t\t\t$local_user = Settings::Get('system.mod_fcgid_httpuser');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$local_user = Settings::Get('phpfpm.vhost_httpuser');\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// check froxlor-local user membership in ftp-group\n\t\t\t\t\t\t// without this check addition may duplicate user in list if httpuser == local_user\n\t\t\t\t\t\tif (in_array($local_user, $local_users) == false) {\n\t\t\t\t\t\t\t$local_users[] = $local_user;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$this->apiCall('Ftps.add', [\n\t\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t\t'path' => '/',\n\t\t\t\t\t\t'ftp_password' => $password,\n\t\t\t\t\t\t'ftp_description' => \"Default\",\n\t\t\t\t\t\t'sendinfomail' => 0,\n\t\t\t\t\t\t'ftp_username' => $loginname,\n\t\t\t\t\t\t'additional_members' => $local_users,\n\t\t\t\t\t\t'is_defaultuser' => 1\n\t\t\t\t\t]);\n\n\t\t\t\t\t$_stdsubdomain = '';\n\t\t\t\t\tif ($createstdsubdomain == '1') {\n\t\t\t\t\t\tif (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') {\n\t\t\t\t\t\t\t$_stdsubdomain = $loginname . '.' . Settings::Get('system.stdsubdomain');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$_stdsubdomain = $loginname . '.' . Settings::Get('system.hostname');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$ins_data = [\n\t\t\t\t\t\t\t'domain' => $_stdsubdomain,\n\t\t\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t\t\t'adminid' => $this->getUserDetail('adminid'),\n\t\t\t\t\t\t\t'docroot' => $documentroot,\n\t\t\t\t\t\t\t'phpenabled' => $phpenabled,\n\t\t\t\t\t\t\t'openbasedir' => '1',\n\t\t\t\t\t\t\t'is_stdsubdomain' => 1\n\t\t\t\t\t\t];\n\t\t\t\t\t\t$domainid = -1;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t$std_domain = $this->apiCall('Domains.add', $ins_data, true);\n\t\t\t\t\t\t\t$domainid = $std_domain['id'];\n\t\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, \"[API] Unable to add standard-subdomain: \" . $e->getMessage());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ($domainid > 0) {\n\t\t\t\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid\n\t\t\t\t\t\t\t\");\n\t\t\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t\t\t'domainid' => $domainid,\n\t\t\t\t\t\t\t\t'customerid' => $customerid\n\t\t\t\t\t\t\t], true, true);\n\t\t\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] automatically added standardsubdomain for user '\" . $loginname . \"'\");\n\t\t\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create default mysql-user if enabled\n\t\t\t\t\tif ($mysqls != 0) {\n\t\t\t\t\t\tforeach ($allowed_mysqlserver as $dbserver) {\n\t\t\t\t\t\t\t// require privileged access for target db-server\n\t\t\t\t\t\t\tDatabase::needRoot(true, $dbserver, false);\n\t\t\t\t\t\t\t// get DbManager\n\t\t\t\t\t\t\t$dbm = new DbManager($this->logger());\n\t\t\t\t\t\t\t// give permission to the user on every access-host we have\n\t\t\t\t\t\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t\t\t\t\t\t$dbm->getManager()->grantPrivilegesTo($loginname, $password, $mysql_access_host, false, false, true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t\t\t\t\tDatabase::needRoot(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($sendpassword == '1') {\n\t\t\t\t\t\t$srv_hostname = Settings::Get('system.hostname');\n\t\t\t\t\t\tif (Settings::Get('system.froxlordirectlyviahostname') == '0') {\n\t\t\t\t\t\t\t$srv_hostname .= '/' . basename(\\Froxlor\\Froxlor::getInstallDir());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$srv_ip_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tSELECT ip, port FROM `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\t\t\t\tWHERE `id` = :defaultip\n\t\t\t\t\t\t\");\n\t\t\t\t\t\t$default_ips = Settings::Get('system.defaultip');\n\t\t\t\t\t\t$default_ips = explode(',', $default_ips);\n\t\t\t\t\t\t$srv_ip = Database::pexecute_first($srv_ip_stmt, [\n\t\t\t\t\t\t\t'defaultip' => reset($default_ips)\n\t\t\t\t\t\t], true, true);\n\n\t\t\t\t\t\t$replace_arr = [\n\t\t\t\t\t\t\t'FIRSTNAME' => $firstname,\n\t\t\t\t\t\t\t'NAME' => $name,\n\t\t\t\t\t\t\t'COMPANY' => $company,\n\t\t\t\t\t\t\t'SALUTATION' => User::getCorrectUserSalutation([\n\t\t\t\t\t\t\t\t'firstname' => $firstname,\n\t\t\t\t\t\t\t\t'name' => $name,\n\t\t\t\t\t\t\t\t'company' => $company\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t'CUSTOMER_NO' => $customernumber,\n\t\t\t\t\t\t\t'USERNAME' => $loginname,\n\t\t\t\t\t\t\t'PASSWORD' => $password,\n\t\t\t\t\t\t\t'SERVER_HOSTNAME' => $srv_hostname,\n\t\t\t\t\t\t\t'SERVER_IP' => $srv_ip['ip'] ?? '',\n\t\t\t\t\t\t\t'SERVER_PORT' => $srv_ip['port'] ?? '',\n\t\t\t\t\t\t\t'DOMAINNAME' => $_stdsubdomain\n\t\t\t\t\t\t];\n\n\t\t\t\t\t\t// get template for mail subject\n\t\t\t\t\t\t$mail_subject = $this->getMailTemplate([\n\t\t\t\t\t\t\t'adminid' => $this->getUserDetail('adminid'),\n\t\t\t\t\t\t\t'def_language' => $def_language\n\t\t\t\t\t\t], 'mails', 'createcustomer_subject', $replace_arr, lng('mails.createcustomer.subject'));\n\t\t\t\t\t\t// get template for mail body\n\t\t\t\t\t\t$mail_body = $this->getMailTemplate([\n\t\t\t\t\t\t\t'adminid' => $this->getUserDetail('adminid'),\n\t\t\t\t\t\t\t'def_language' => $def_language\n\t\t\t\t\t\t], 'mails', 'createcustomer_mailbody', $replace_arr, lng('mails.createcustomer.mailbody'));\n\n\t\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t$this->mailer()->Subject = $mail_subject;\n\t\t\t\t\t\t\t$this->mailer()->AltBody = $mail_body;\n\t\t\t\t\t\t\t$this->mailer()->Body = str_replace(\"\\n\", \"<br />\", $mail_body);\n\t\t\t\t\t\t\t$this->mailer()->addAddress($email, User::getCorrectUserSalutation([\n\t\t\t\t\t\t\t\t'firstname' => $firstname,\n\t\t\t\t\t\t\t\t'name' => $name,\n\t\t\t\t\t\t\t\t'company' => $company\n\t\t\t\t\t\t\t]));\n\t\t\t\t\t\t\t$this->mailer()->send();\n\t\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, \"[API] Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\t\t\tResponse::standardError('errorsendingmail', $email, true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$this->mailer()->clearAddresses();\n\t\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] automatically sent password to user '\" . $loginname . \"'\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] added customer '\" . $loginname . \"'\");\n\n\t\t\t\t$result = $this->apiCall('Customers.get', [\n\t\t\t\t\t'loginname' => $loginname\n\t\t\t\t]);\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t\tthrow new Exception(\"No more resources available\", 406);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return a customer entry by either id or loginname\n\t *\n\t * @param int $id\n\t *            optional, the customer-id\n\t * @param string $loginname\n\t *            optional, the loginname\n\t * @param bool $show_usages\n\t *            optional, default false\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ln_optional = $id > 0;\n\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\t\t$show_usages = $this->getBoolParam('show_usages', true, false);\n\n\t\tif ($this->isAdmin()) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT `c`.*, `a`.`loginname` AS `adminname`\n\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` `c`, `\" . TABLE_PANEL_ADMINS . \"` `a`\n\t\t\tWHERE \" . ($id > 0 ? \"`c`.`customerid` = :idln\" : \"`c`.`loginname` = :idln\") . ($this->getUserDetail('customers_see_all') ? '' : \" AND `c`.`adminid` = :adminid\") . \" AND `c`.`adminid` = `a`.`adminid`\");\n\t\t\t$params = [\n\t\t\t\t'idln' => ($id <= 0 ? $loginname : $id)\n\t\t\t];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t}\n\t\t} else {\n\t\t\tif (($id > 0 && $id != $this->getUserDetail('customerid')) || !empty($loginname) && $loginname != $this->getUserDetail('loginname')) {\n\t\t\t\tthrow new Exception(\"You cannot access data of other customers\", 401);\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\tWHERE \" . ($id > 0 ? \"`customerid` = :idln\" : \"`loginname` = :idln\"));\n\t\t\t$params = [\n\t\t\t\t'idln' => ($id <= 0 ? $loginname : $id)\n\t\t\t];\n\t\t}\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\t// check whether the admin does not want the customer to see the notes\n\t\t\tif (!$this->isAdmin() && $result['custom_notes_show'] != 1) {\n\t\t\t\t$result['custom_notes'] = \"\";\n\t\t\t}\n\t\t\tif ($show_usages) {\n\t\t\t\t// get number of domains\n\t\t\t\t$domains_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT COUNT(`id`) AS `domains`\n\t\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `customerid` = :cid\n\t\t\t\t\tAND `parentdomainid` = '0'\n\t\t\t\t\tAND `id`<> :stdd\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($domains_stmt, [\n\t\t\t\t\t'cid' => $result['customerid'],\n\t\t\t\t\t'stdd' => $result['standardsubdomain']\n\t\t\t\t]);\n\t\t\t\t$domains = $domains_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\t$result['domains'] = intval($domains['domains']);\n\t\t\t\t// get disk-space usages for web, mysql and mail\n\t\t\t\t$usages_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DISKSPACE . \"`\n\t\t\t\t\tWHERE `customerid` = :cid\n\t\t\t\t\tORDER BY `stamp` DESC LIMIT 1\n\t\t\t\t\");\n\t\t\t\t$usages = Database::pexecute_first($usages_stmt, [\n\t\t\t\t\t'cid' => $result['customerid']\n\t\t\t\t]);\n\t\t\t\tif ($usages) {\n\t\t\t\t\t$result['webspace_used'] = $usages['webspace'];\n\t\t\t\t\t$result['mailspace_used'] = $usages['mail'];\n\t\t\t\t\t$result['dbspace_used'] = $usages['mysql'];\n\t\t\t\t} else {\n\t\t\t\t\t$result['webspace_used'] = 0;\n\t\t\t\t\t$result['mailspace_used'] = 0;\n\t\t\t\t\t$result['dbspace_used'] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get customer '\" . $result['loginname'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\t$key = ($id > 0 ? \"id #\" . $id : \"loginname '\" . $loginname . \"'\");\n\t\tthrow new Exception(\"Customer with \" . $key . \" could not be found\", 404);\n\t}\n\n\t/**\n\t * increase resource-usage\n\t *\n\t * @param int $customerid\n\t * @param string $resource\n\t * @param string $extra\n\t *            optional, default empty\n\t * @param int $increase_by\n\t *            optional, default 1\n\t */\n\tpublic static function increaseUsage($customerid = 0, $resource = null, $extra = '', $increase_by = 1)\n\t{\n\t\tself::updateResourceUsage(TABLE_PANEL_CUSTOMERS, 'customerid', $customerid, '+', $resource, $extra, $increase_by);\n\t}\n\n\t/**\n\t * update customer entry by either id or loginname, customer can only change language, password and theme\n\t *\n\t * @param int $id\n\t *                             optional, the customer-id\n\t * @param string $loginname\n\t *                             optional, the loginname\n\t * @param string $email\n\t *                             optional\n\t * @param string $name\n\t *                             optional if company is set, else required\n\t * @param string $firstname\n\t *                             optional if company is set, else required\n\t * @param string $company\n\t *                             optional but required if name/firstname empty\n\t * @param string $street\n\t *                             optional\n\t * @param string $zipcode\n\t *                             optional\n\t * @param string $city\n\t *                             optional\n\t * @param string $phone\n\t *                             optional\n\t * @param string $fax\n\t *                             optional\n\t * @param int $customernumber\n\t *                             optional\n\t * @param string $def_language\n\t *                             optional, ISO 639-1 language code (e.g. 'en', 'de', see lng-folder for supported languages),\n\t *                             default is system-default language\n\t * @param bool $gui_access\n\t *                             optional, allow login via webui, if false ONLY the login via webui is disallowed; default true\n\t * @param bool $api_allowed\n\t *                             optional, default is true if system setting api.enabled is true, else false\n\t * @param bool $shell_allowed\n\t *                             optional, default is true if system setting system.allow_customer_shell is true, else false\n\t * @param int $gender\n\t *                             optional, 0 = no-gender, 1 = male, 2 = female\n\t * @param string $custom_notes\n\t *                             optional notes\n\t * @param bool $custom_notes_show\n\t *                             optional, whether to show the content of custom_notes to the customer, default 0\n\t *                             (false)\n\t * @param string $new_customer_password\n\t *                             optional, set new password\n\t * @param bool $sendpassword\n\t *                             optional, whether to send the password to the customer after creation, default 0\n\t *                             (false)\n\t * @param int $move_to_admin\n\t *                             optional, if valid admin-id is given here, the customer's admin/reseller can be\n\t *                             changed\n\t * @param bool $deactivated\n\t *                             optional, if 1 (true) the customer can be deactivated/suspended\n\t * @param int $diskspace\n\t *                             optional disk-space available for customer in MB, default 0\n\t * @param bool $diskspace_ul\n\t *                             optional, whether customer should have unlimited diskspace, default 0 (false)\n\t * @param int $traffic\n\t *                             optional traffic available for customer in GB, default 0\n\t * @param bool $traffic_ul\n\t *                             optional, whether customer should have unlimited traffic, default 0 (false)\n\t * @param int $subdomains\n\t *                             optional amount of subdomains available for customer, default 0\n\t * @param bool $subdomains_ul\n\t *                             optional, whether customer should have unlimited subdomains, default 0 (false)\n\t * @param int $emails\n\t *                             optional amount of emails available for customer, default 0\n\t * @param bool $emails_ul\n\t *                             optional, whether customer should have unlimited emails, default 0 (false)\n\t * @param int $email_accounts\n\t *                             optional amount of email-accounts available for customer, default 0\n\t * @param bool $email_accounts_ul\n\t *                             optional, whether customer should have unlimited email-accounts, default 0 (false)\n\t * @param int $email_forwarders\n\t *                             optional amount of email-forwarders available for customer, default 0\n\t * @param bool $email_forwarders_ul\n\t *                             optional, whether customer should have unlimited email-forwarders, default 0 (false)\n\t * @param int $email_quota\n\t *                             optional size of email-quota available for customer in MB, default is system-setting\n\t *                             mail_quota\n\t * @param bool $email_quota_ul\n\t *                             optional, whether customer should have unlimited email-quota, default 0 (false)\n\t * @param bool $email_imap\n\t *                             optional, whether to allow IMAP access, default 0 (false)\n\t * @param bool $email_pop3\n\t *                             optional, whether to allow POP3 access, default 0 (false)\n\t * @param int $ftps\n\t *                             optional amount of ftp-accounts available for customer, default 0\n\t * @param bool $ftps_ul\n\t *                             optional, whether customer should have unlimited ftp-accounts, default 0 (false)\n\t * @param int $mysqls\n\t *                             optional amount of mysql-databases available for customer, default 0\n\t * @param bool $mysqls_ul\n\t *                             optional, whether customer should have unlimited mysql-databases, default 0 (false)\n\t * @param bool $createstdsubdomain\n\t *                             optional, whether to create a standard-subdomain ([loginname].froxlor-hostname.tld),\n\t *                             default 1 (if customer has std-subdomain) else 0 (false)\n\t * @param bool $phpenabled\n\t *                             optional, whether to allow usage of PHP, default 0 (false)\n\t * @param array $allowed_phpconfigs\n\t *                             optional, array of IDs of php-config that the customer is allowed to use, default\n\t *                             empty (none)\n\t * @param bool $perlenabled\n\t *                             optional, whether to allow usage of Perl/CGI, default 0 (false)\n\t * @param bool $dnsenabled\n\t *                             optional, whether to allow usage of the DNS editor (requires activated nameserver in\n\t *                             settings), default 0 (false)\n\t * @param bool $logviewenabled\n\t *                             optional, whether to allow access to webserver access/error-logs, default 0 (false)\n\t * @param string $theme\n\t *                             optional, change theme\n\t * @param array $allowed_mysqlserver\n\t *                             optional, array of IDs of defined mysql-servers the customer is allowed to use,\n\t *                             default is to allow the default dbserver (id=0)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ln_optional = $id > 0;\n\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\n\t\t$result = $this->apiCall('Customers.get', [\n\t\t\t'id' => $id,\n\t\t\t'loginname' => $loginname\n\t\t]);\n\t\t$id = $result['customerid'];\n\n\t\tif ($this->isAdmin()) {\n\t\t\t// parameters\n\t\t\t$move_to_admin = (int)($this->getParam('move_to_admin', true, 0));\n\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$email = $this->getParam('email', true, $idna_convert->decode($result['email']));\n\t\t\t$name = $this->getParam('name', true, $result['name']);\n\t\t\t$firstname = $this->getParam('firstname', true, $result['firstname']);\n\t\t\t$company_required = ((!empty($name) && empty($firstname)) || (empty($name) && !empty($firstname)) || (empty($name) && empty($firstname))) && empty($result['company']);\n\t\t\t$company = $this->getParam('company', !$company_required, $result['company']);\n\t\t\t$street = $this->getParam('street', true, $result['street']);\n\t\t\t$zipcode = $this->getParam('zipcode', true, $result['zipcode']);\n\t\t\t$city = $this->getParam('city', true, $result['city']);\n\t\t\t$phone = $this->getParam('phone', true, $result['phone']);\n\t\t\t$fax = $this->getParam('fax', true, $result['fax']);\n\t\t\t$customernumber = $this->getParam('customernumber', true, $result['customernumber']);\n\t\t\t$def_language = $this->getParam('def_language', true, $result['def_language']);\n\t\t\t$gui_access = $this->getBoolParam('gui_access', true, $result['gui_access']);\n\t\t\t$api_allowed = $this->getBoolParam('api_allowed', true, $result['api_allowed']);\n\t\t\t$shell_allowed = $this->getBoolParam('shell_allowed', true, $result['shell_allowed']);\n\t\t\t$gender = (int)$this->getParam('gender', true, $result['gender']);\n\t\t\t$custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']);\n\t\t\t$custom_notes_show = $this->getBoolParam('custom_notes_show', true, $result['custom_notes_show']);\n\n\t\t\t$dec_places = Settings::Get('panel.decimal_places');\n\t\t\t$diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places));\n\t\t\t$traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places));\n\t\t\t$subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']);\n\t\t\t$emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']);\n\t\t\t$email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']);\n\t\t\t$email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']);\n\t\t\t$email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']);\n\t\t\t$email_imap = $this->getParam('email_imap', true, $result['imap']);\n\t\t\t$email_pop3 = $this->getParam('email_pop3', true, $result['pop3']);\n\t\t\t$ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']);\n\t\t\t$mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']);\n\t\t\t$createstdsubdomain = $this->getBoolParam('createstdsubdomain', true, ($result['standardsubdomain'] != 0 ? 1 : 0));\n\t\t\t$password = $this->getParam('new_customer_password', true, '');\n\t\t\t$phpenabled = $this->getBoolParam('phpenabled', true, $result['phpenabled']);\n\t\t\t$allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, json_decode($result['allowed_phpconfigs'], true));\n\t\t\t$perlenabled = $this->getBoolParam('perlenabled', true, $result['perlenabled']);\n\t\t\t$dnsenabled = $this->getBoolParam('dnsenabled', true, $result['dnsenabled']);\n\t\t\t$logviewenabled = $this->getBoolParam('logviewenabled', true, $result['logviewenabled']);\n\t\t\t$deactivated = $this->getBoolParam('deactivated', true, $result['deactivated']);\n\t\t\t$theme = $this->getParam('theme', true, $result['theme']);\n\t\t\t$allowed_mysqlserver = $this->getParam('allowed_mysqlserver', true, json_decode($result['allowed_mysqlserver'], true));\n\t\t} else {\n\t\t\t// allowed parameters\n\t\t\t$def_language = $this->getParam('def_language', true, $result['def_language']);\n\t\t\t$password = $this->getParam('new_customer_password', true, '');\n\t\t\t$theme = $this->getParam('theme', true, $result['theme']);\n\t\t}\n\n\t\t// validation\n\t\tif ($this->isAdmin()) {\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$firstname = Validate::validate($firstname, 'first name', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$company = Validate::validate($company, 'company', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$street = Validate::validate($street, 'street', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$zipcode = Validate::validate($zipcode, 'zipcode', '/^[0-9 \\-A-Z]*$/', '', [], true);\n\t\t\t$city = Validate::validate($city, 'city', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$phone = Validate::validate($phone, 'phone', '/^[0-9\\- \\+\\(\\)\\/]*$/', '', [], true);\n\t\t\t$fax = Validate::validate($fax, 'fax', '/^[0-9\\- \\+\\(\\)\\/]*$/', '', [], true);\n\t\t\t$email = $idna_convert->encode(Validate::validate($email, 'email', '', '', [], true));\n\t\t\t$customernumber = Validate::validate($customernumber, 'customer number', '/^[A-Za-z0-9 \\-]*$/Di', '', [], true);\n\t\t\t$custom_notes = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $custom_notes), 'custom_notes', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\tif (!empty($allowed_phpconfigs)) {\n\t\t\t\t$allowed_phpconfigs = array_map('intval', $allowed_phpconfigs);\n\t\t\t}\n\t\t\tif (empty($allowed_phpconfigs) && $phpenabled == 1) {\n\t\t\t\t// only required if not using mod_php\n\t\t\t\tif ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\tResponse::standardError('customerphpenabledbutnoconfig', '', true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// add permission for allowed mysql usage if customer was not allowed to use mysql prior\n\t\t\tif ($result['mysqls'] == 0 && ($mysqls == -1 || $mysqls > 0)) {\n\t\t\t\t$allowed_mysqlserver = $this->getParam('allowed_mysqlserver', true, [0]);\n\t\t\t}\n\t\t\tif (!empty($allowed_mysqlserver)) {\n\t\t\t\t$allowed_mysqlserver = array_map('intval', $allowed_mysqlserver);\n\t\t\t}\n\n\t\t}\n\t\t$def_language = Validate::validate($def_language, 'default language', '', '', [], true);\n\t\tif (!empty($def_language) && !isset(Language::getLanguages()[$def_language])) {\n\t\t\t$def_language = Settings::Get('panel.standardlanguage');\n\t\t}\n\n\t\t$theme = Validate::validate($theme, 'theme', '', '', [], true);\n\n\t\tif (Settings::Get('system.mail_quota_enabled') != '1') {\n\t\t\t$email_quota = -1;\n\t\t}\n\n\t\tif (empty($theme)) {\n\t\t\t$theme = Settings::Get('panel.default_theme');\n\t\t}\n\n\t\tif ($this->isAdmin()) {\n\t\t\t$diskspace *= 1024;\n\t\t\t$traffic *= 1024 * 1024;\n\n\t\t\tif (\n\t\t\t\t($diskspace != 0 && (($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1')\n\t\t\t\t|| ($mysqls != 0 && (($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1')\n\t\t\t\t|| ($emails != 0 && (($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1')\n\t\t\t\t|| ($email_accounts != 0 && (($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1')\n\t\t\t\t|| ($email_forwarders != 0 && (($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1')\n\t\t\t\t|| ($email_quota != 0 && (($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1')\n\t\t\t\t|| ($ftps != 0 && (($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1')\n\t\t\t\t|| ($subdomains != 0 && (($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1')\n\t\t\t\t|| (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1')\n\t\t\t\t|| ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1')\n\t\t\t\t|| ($emails == '-1' && $this->getUserDetail('emails') != '-1')\n\t\t\t\t|| ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1')\n\t\t\t\t|| ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1')\n\t\t\t\t|| ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1')\n\t\t\t\t|| ($ftps == '-1' && $this->getUserDetail('ftps') != '-1')\n\t\t\t\t|| ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')\n\t\t\t) {\n\t\t\t\tResponse::standardError('youcantallocatemorethanyouhave', '', true);\n\t\t\t}\n\n\t\t\t// validate allowed_mysqls whether the customer has databases on a removed, now disallowed db-server and abort if true\n\t\t\t$former_allowed_mysqlserver = json_decode($result['allowed_mysqlserver'], true);\n\t\t\tif ($allowed_mysqlserver != $former_allowed_mysqlserver && !empty($former_allowed_mysqlserver)) {\n\t\t\t\t$to_remove_mysqlserver = array_diff($former_allowed_mysqlserver, $allowed_mysqlserver);\n\t\t\t\tif (count($to_remove_mysqlserver) > 0) {\n\t\t\t\t\tforeach ($to_remove_mysqlserver as $mysqlserver_check) {\n\t\t\t\t\t\t$result_ms = $this->apiCall('MysqlServer.databasesOnServer', [\n\t\t\t\t\t\t\t'mysql_server' => $mysqlserver_check,\n\t\t\t\t\t\t\t'customerid' => $id\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tif ($result_ms['count'] > 0) {\n\t\t\t\t\t\t\tResponse::standardError('mysqlserverstillhasdbs', '', true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($email == '') {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'stringisempty',\n\t\t\t\t\t'customer.email'\n\t\t\t\t], '', true);\n\t\t\t} elseif (!Validate::validateEmail($email)) {\n\t\t\t\tResponse::standardError('emailiswrong', $email, true);\n\t\t\t} else {\n\t\t\t\t// Check for existing email address\n\t\t\t\t// do not check via api as we skip any permission checks for this task\n\t\t\t\t$email_check_admin_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `email` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `email` = :email\n\t\t\t\t\t\");\n\t\t\t\t$email_check_admin = Database::pexecute_first($email_check_admin_stmt, [\n\t\t\t\t\t'email' => $email\n\t\t\t\t], true, true);\n\t\t\t\tif ($email_check_admin && strtolower($email_check_admin['email']) == strtolower($email)) {\n\t\t\t\t\tResponse::standardError('emailexistsanon', $email, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($password != '') {\n\t\t\t$password = Crypt::validatePassword($password, true);\n\t\t\t$password = Crypt::makeCryptPassword($password);\n\t\t} else {\n\t\t\t$password = $result['password'];\n\t\t}\n\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($createstdsubdomain != '1' || $deactivated) {\n\t\t\t\t$createstdsubdomain = '0';\n\t\t\t}\n\n\t\t\tif ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') {\n\t\t\t\tif (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') {\n\t\t\t\t\t$_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain');\n\t\t\t\t} else {\n\t\t\t\t\t$_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname');\n\t\t\t\t}\n\n\t\t\t\t$ins_data = [\n\t\t\t\t\t'domain' => $_stdsubdomain,\n\t\t\t\t\t'customerid' => $result['customerid'],\n\t\t\t\t\t'adminid' => $this->getUserDetail('adminid'),\n\t\t\t\t\t'docroot' => $result['documentroot'],\n\t\t\t\t\t'phpenabled' => $phpenabled,\n\t\t\t\t\t'openbasedir' => '1'\n\t\t\t\t];\n\t\t\t\t$domainid = -1;\n\t\t\t\ttry {\n\t\t\t\t\t$std_domain = $this->apiCall('Domains.add', $ins_data);\n\t\t\t\t\t$domainid = $std_domain['id'];\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, \"[API] Unable to add standard-subdomain: \" . $e->getMessage());\n\t\t\t\t}\n\n\t\t\t\tif ($domainid > 0) {\n\t\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid\n\t\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t'domainid' => $domainid,\n\t\t\t\t\t\t'customerid' => $result['customerid']\n\t\t\t\t\t], true, true);\n\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] automatically added standardsubdomain for user '\" . $result['loginname'] . \"'\");\n\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') {\n\t\t\t\ttry {\n\t\t\t\t\t$std_domain = $this->apiCall('Domains.delete', [\n\t\t\t\t\t\t'id' => $result['standardsubdomain'],\n\t\t\t\t\t\t'is_stdsubdomain' => 1\n\t\t\t\t\t]);\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, \"[API] Unable to delete standard-subdomain: \" . $e->getMessage());\n\t\t\t\t}\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] automatically deleted standardsubdomain for user '\" . $result['loginname'] . \"'\");\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t}\n\n\t\t\tif ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled'] || $email != $result['email']) {\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t}\n\n\t\t\t// activate/deactivate customer services\n\t\t\tif ($deactivated != $result['deactivated']) {\n\t\t\t\t$yesno = ($deactivated ? 'N' : 'Y');\n\t\t\t\t$pop3 = ($deactivated ? '0' : (int)$result['pop3']);\n\t\t\t\t$imap = ($deactivated ? '0' : (int)$result['imap']);\n\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_MAIL_USERS . \"` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'yesno' => $yesno,\n\t\t\t\t\t'pop3' => $pop3,\n\t\t\t\t\t'imap' => $imap,\n\t\t\t\t\t'customerid' => $id\n\t\t\t\t]);\n\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_FTP_USERS . \"` SET `login_enabled` = :yesno WHERE `customerid` = :customerid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'yesno' => $yesno,\n\t\t\t\t\t'customerid' => $id\n\t\t\t\t]);\n\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `deactivated`= :deactivated WHERE `customerid` = :customerid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'deactivated' => $deactivated,\n\t\t\t\t\t'customerid' => $id\n\t\t\t\t]);\n\n\t\t\t\t// enable/disable global mysql-user (loginname)\n\t\t\t\t$current_allowed_mysqlserver = isset($result['allowed_mysqlserver']) && !empty($result['allowed_mysqlserver']) ? json_decode($result['allowed_mysqlserver'], true) : [];\n\t\t\t\tforeach ($current_allowed_mysqlserver as $dbserver) {\n\t\t\t\t\t// require privileged access for target db-server\n\t\t\t\t\tDatabase::needRoot(true, $dbserver, true);\n\t\t\t\t\t// get DbManager\n\t\t\t\t\t$dbm = new DbManager($this->logger());\n\t\t\t\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t\t\t\t// Prevent access, if deactivated\n\t\t\t\t\t\tif ($deactivated) {\n\t\t\t\t\t\t\t// failsafe if user has been deleted manually (requires MySQL 4.1.2+)\n\t\t\t\t\t\t\t$dbm->getManager()->disableUser($result['loginname'], $mysql_access_host);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Otherwise grant access\n\t\t\t\t\t\t\t$dbm->getManager()->enableUser($result['loginname'], $mysql_access_host, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t\t\tDatabase::needRoot(false);\n\t\t\t\t}\n\n\t\t\t\t// Retrieve customer's databases\n\t\t\t\t$databases_stmt = Database::prepare(\"SELECT * FROM \" . TABLE_PANEL_DATABASES . \" WHERE customerid = :customerid ORDER BY `dbserver`\");\n\t\t\t\tDatabase::pexecute($databases_stmt, [\n\t\t\t\t\t'customerid' => $id\n\t\t\t\t]);\n\n\t\t\t\tDatabase::needRoot(true);\n\t\t\t\t$last_dbserver = 0;\n\n\t\t\t\t$dbm = new DbManager($this->logger());\n\n\t\t\t\t// For each of them\n\t\t\t\t$priv_changed = false;\n\t\t\t\twhile ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\tif ($last_dbserver != $row_database['dbserver']) {\n\t\t\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t\t\t\tDatabase::needRoot(true, $row_database['dbserver']);\n\t\t\t\t\t\t$last_dbserver = $row_database['dbserver'];\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t\t\t\t// Prevent access, if deactivated\n\t\t\t\t\t\tif ($deactivated) {\n\t\t\t\t\t\t\t// failsafe if user has been deleted manually (requires MySQL 4.1.2+)\n\t\t\t\t\t\t\t$dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Otherwise grant access\n\t\t\t\t\t\t\t$dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$priv_changed = true;\n\t\t\t\t}\n\n\t\t\t\t// At last flush the new privileges\n\t\t\t\tif ($priv_changed) {\n\t\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t\t}\n\t\t\t\tDatabase::needRoot(false);\n\n\t\t\t\t// reactivate/deactivate api-keys\n\t\t\t\t$valid_until = $deactivated ? 0 : -1;\n\t\t\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_API_KEYS . \"` SET `valid_until` = :vu WHERE `customerid` = :id\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t'id' => $id,\n\t\t\t\t\t'vu' => $valid_until\n\t\t\t\t], true, true);\n\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] \" . ($deactivated ? 'deactivated' : 'reactivated') . \" user '\" . $result['loginname'] . \"'\");\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t}\n\n\t\t\t// Disable or enable POP3 Login for customers Mail Accounts\n\t\t\tif ($email_pop3 != $result['pop3']) {\n\t\t\t\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `pop3` = :pop3 WHERE `customerid` = :customerid\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'pop3' => $email_pop3,\n\t\t\t\t\t'customerid' => $id\n\t\t\t\t]);\n\t\t\t}\n\n\t\t\t// Disable or enable IMAP Login for customers Mail Accounts\n\t\t\tif ($email_imap != $result['imap']) {\n\t\t\t\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_MAIL_USERS . \"` SET `imap` = :imap WHERE `customerid` = :customerid\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'imap' => $email_imap,\n\t\t\t\t\t'customerid' => $id\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\n\t\t$upd_data = [\n\t\t\t'customerid' => $id,\n\t\t\t'passwd' => $password,\n\t\t\t'lang' => $def_language,\n\t\t\t'theme' => $theme\n\t\t];\n\n\t\tif ($this->isAdmin()) {\n\t\t\t$admin_upd_data = [\n\t\t\t\t'name' => $name,\n\t\t\t\t'firstname' => $firstname,\n\t\t\t\t'gender' => $gender,\n\t\t\t\t'company' => $company,\n\t\t\t\t'street' => $street,\n\t\t\t\t'zipcode' => $zipcode,\n\t\t\t\t'city' => $city,\n\t\t\t\t'phone' => $phone,\n\t\t\t\t'fax' => $fax,\n\t\t\t\t'email' => $email,\n\t\t\t\t'customerno' => $customernumber,\n\t\t\t\t'diskspace' => $diskspace,\n\t\t\t\t'traffic' => $traffic,\n\t\t\t\t'subdomains' => $subdomains,\n\t\t\t\t'emails' => $emails,\n\t\t\t\t'email_accounts' => $email_accounts,\n\t\t\t\t'email_forwarders' => $email_forwarders,\n\t\t\t\t'email_quota' => $email_quota,\n\t\t\t\t'ftps' => $ftps,\n\t\t\t\t'mysqls' => $mysqls,\n\t\t\t\t'deactivated' => $deactivated,\n\t\t\t\t'phpenabled' => $phpenabled,\n\t\t\t\t'allowed_phpconfigs' => empty($allowed_phpconfigs) ? \"\" : json_encode($allowed_phpconfigs),\n\t\t\t\t'imap' => $email_imap,\n\t\t\t\t'pop3' => $email_pop3,\n\t\t\t\t'perlenabled' => $perlenabled,\n\t\t\t\t'dnsenabled' => $dnsenabled,\n\t\t\t\t'logviewenabled' => $logviewenabled,\n\t\t\t\t'custom_notes' => $custom_notes,\n\t\t\t\t'custom_notes_show' => $custom_notes_show,\n\t\t\t\t'gui_access' => $gui_access,\n\t\t\t\t'api_allowed' => $api_allowed,\n\t\t\t\t'shell_allowed' => $shell_allowed,\n\t\t\t\t'allowed_mysqlserver' => empty($allowed_mysqlserver) ? \"\" : json_encode($allowed_mysqlserver)\n\t\t\t];\n\t\t\t$upd_data += $admin_upd_data;\n\t\t}\n\n\t\t$upd_query = \"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t`def_language` = :lang,\n\t\t\t\t`password` = :passwd,\n\t\t\t\t`theme` = :theme\";\n\n\t\tif ($this->isAdmin()) {\n\t\t\t$admin_upd_query = \",\n\t\t\t\t`name` = :name,\n\t\t\t\t`firstname` = :firstname,\n\t\t\t\t`gender` = :gender,\n\t\t\t\t`company` = :company,\n\t\t\t\t`street` = :street,\n\t\t\t\t`zipcode` = :zipcode,\n\t\t\t\t`city` = :city,\n\t\t\t\t`phone` = :phone,\n\t\t\t\t`fax` = :fax,\n\t\t\t\t`email` = :email,\n\t\t\t\t`customernumber` = :customerno,\n\t\t\t\t`diskspace` = :diskspace,\n\t\t\t\t`traffic` = :traffic,\n\t\t\t\t`subdomains` = :subdomains,\n\t\t\t\t`emails` = :emails,\n\t\t\t\t`email_accounts` = :email_accounts,\n\t\t\t\t`email_forwarders` = :email_forwarders,\n\t\t\t\t`ftps` = :ftps,\n\t\t\t\t`mysqls` = :mysqls,\n\t\t\t\t`deactivated` = :deactivated,\n\t\t\t\t`phpenabled` = :phpenabled,\n\t\t\t\t`allowed_phpconfigs` = :allowed_phpconfigs,\n\t\t\t\t`email_quota` = :email_quota,\n\t\t\t\t`imap` = :imap,\n\t\t\t\t`pop3` = :pop3,\n\t\t\t\t`perlenabled` = :perlenabled,\n\t\t\t\t`dnsenabled` = :dnsenabled,\n\t\t\t\t`logviewenabled` = :logviewenabled,\n\t\t\t\t`custom_notes` = :custom_notes,\n\t\t\t\t`custom_notes_show` = :custom_notes_show,\n\t\t\t\t`gui_access` = :gui_access,\n\t\t\t\t`api_allowed` = :api_allowed,\n\t\t\t\t`shell_allowed` = :shell_allowed,\n\t\t\t\t`allowed_mysqlserver` = :allowed_mysqlserver\";\n\t\t\t$upd_query .= $admin_upd_query;\n\t\t}\n\t\t$upd_query .= \" WHERE `customerid` = :customerid\";\n\t\t$upd_stmt = Database::prepare($upd_query);\n\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\n\t\tif ($this->isAdmin()) {\n\t\t\t// Using filesystem - quota, insert a task which cleans the filesystem - quota\n\t\t\tCronjob::inserttask(TaskId::CREATE_QUOTA);\n\n\t\t\t$admin_update_query = \"UPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `customers_used` = `customers_used` \";\n\n\t\t\tif ($mysqls != '-1' || $result['mysqls'] != '-1') {\n\t\t\t\t$admin_update_query .= \", `mysqls_used` = `mysqls_used` \";\n\n\t\t\t\tif ($mysqls != '-1') {\n\t\t\t\t\t$admin_update_query .= \" + 0\" . (int)$mysqls . \" \";\n\t\t\t\t}\n\t\t\t\tif ($result['mysqls'] != '-1') {\n\t\t\t\t\t$admin_update_query .= \" - 0\" . (int)$result['mysqls'] . \" \";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($emails != '-1' || $result['emails'] != '-1') {\n\t\t\t\t$admin_update_query .= \", `emails_used` = `emails_used` \";\n\n\t\t\t\tif ($emails != '-1') {\n\t\t\t\t\t$admin_update_query .= \" + 0\" . (int)$emails . \" \";\n\t\t\t\t}\n\t\t\t\tif ($result['emails'] != '-1') {\n\t\t\t\t\t$admin_update_query .= \" - 0\" . (int)$result['emails'] . \" \";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($email_accounts != '-1' || $result['email_accounts'] != '-1') {\n\t\t\t\t$admin_update_query .= \", `email_accounts_used` = `email_accounts_used` \";\n\n\t\t\t\tif ($email_accounts != '-1') {\n\t\t\t\t\t$admin_update_query .= \" + 0\" . (int)$email_accounts . \" \";\n\t\t\t\t}\n\t\t\t\tif ($result['email_accounts'] != '-1') {\n\t\t\t\t\t$admin_update_query .= \" - 0\" . (int)$result['email_accounts'] . \" \";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($email_forwarders != '-1' || $result['email_forwarders'] != '-1') {\n\t\t\t\t$admin_update_query .= \", `email_forwarders_used` = `email_forwarders_used` \";\n\n\t\t\t\tif ($email_forwarders != '-1') {\n\t\t\t\t\t$admin_update_query .= \" + 0\" . (int)$email_forwarders . \" \";\n\t\t\t\t}\n\t\t\t\tif ($result['email_forwarders'] != '-1') {\n\t\t\t\t\t$admin_update_query .= \" - 0\" . (int)$result['email_forwarders'] . \" \";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($email_quota != '-1' || $result['email_quota'] != '-1') {\n\t\t\t\t$admin_update_query .= \", `email_quota_used` = `email_quota_used` \";\n\n\t\t\t\tif ($email_quota != '-1') {\n\t\t\t\t\t$admin_update_query .= \" + 0\" . (int)$email_quota . \" \";\n\t\t\t\t}\n\t\t\t\tif ($result['email_quota'] != '-1') {\n\t\t\t\t\t$admin_update_query .= \" - 0\" . (int)$result['email_quota'] . \" \";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($subdomains != '-1' || $result['subdomains'] != '-1') {\n\t\t\t\t$admin_update_query .= \", `subdomains_used` = `subdomains_used` \";\n\n\t\t\t\tif ($subdomains != '-1') {\n\t\t\t\t\t$admin_update_query .= \" + 0\" . (int)$subdomains . \" \";\n\t\t\t\t}\n\t\t\t\tif ($result['subdomains'] != '-1') {\n\t\t\t\t\t$admin_update_query .= \" - 0\" . (int)$result['subdomains'] . \" \";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($ftps != '-1' || $result['ftps'] != '-1') {\n\t\t\t\t$admin_update_query .= \", `ftps_used` = `ftps_used` \";\n\n\t\t\t\tif ($ftps != '-1') {\n\t\t\t\t\t$admin_update_query .= \" + 0\" . (int)$ftps . \" \";\n\t\t\t\t}\n\t\t\t\tif ($result['ftps'] != '-1') {\n\t\t\t\t\t$admin_update_query .= \" - 0\" . (int)$result['ftps'] . \" \";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (($diskspace / 1024) != '-1' || ($result['diskspace'] / 1024) != '-1') {\n\t\t\t\t$admin_update_query .= \", `diskspace_used` = `diskspace_used` \";\n\n\t\t\t\tif (($diskspace / 1024) != '-1') {\n\t\t\t\t\t$admin_update_query .= \" + 0\" . (int)$diskspace . \" \";\n\t\t\t\t}\n\t\t\t\tif (($result['diskspace'] / 1024) != '-1') {\n\t\t\t\t\t$admin_update_query .= \" - 0\" . (int)$result['diskspace'] . \" \";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$admin_update_query .= \" WHERE `adminid` = '\" . (int)$result['adminid'] . \"'\";\n\t\t\tDatabase::query($admin_update_query);\n\t\t}\n\n\t\t// shell allowance has changed\n\t\tif ($result['shell_allowed'] == '1' && $shell_allowed == '0') {\n\t\t\t// update all users with a valid shell to have /bin/false (disable shell)\n\t\t\t$ftp_upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_FTP_USERS . \"` SET `shell` = '/bin/false' WHERE `customerid` = :cid\");\n\t\t\tDatabase::pexecute($ftp_upd_stmt, ['cid' => (int)$result['customerid']]);\n\t\t}\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] edited user '\" . $result['loginname'] . \"'\");\n\n\t\t/*\n\t\t * move customer to another admin/reseller; #1166\n\t\t */\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($move_to_admin > 0 && $move_to_admin != $result['adminid']) {\n\t\t\t\t$move_result = $this->apiCall('Customers.move', [\n\t\t\t\t\t'id' => $result['customerid'],\n\t\t\t\t\t'adminid' => $move_to_admin\n\t\t\t\t]);\n\t\t\t\tif ($move_result != true) {\n\t\t\t\t\tResponse::standardError('moveofcustomerfailed', $move_result, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$result = $this->apiCall('Customers.get', [\n\t\t\t'id' => $result['customerid']\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * delete a customer entry by either id or loginname\n\t *\n\t * @param int $id\n\t *            optional, the customer-id\n\t * @param string $loginname\n\t *            optional, the loginname\n\t * @param bool $delete_userfiles\n\t *            optional, default false\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$ln_optional = $id > 0;\n\t\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\t\t\t$delete_userfiles = $this->getParam('delete_userfiles', true, 0);\n\n\t\t\t$result = $this->apiCall('Customers.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'loginname' => $loginname\n\t\t\t]);\n\t\t\t$id = $result['customerid'];\n\n\t\t\t// remove global mysql-user (loginname)\n\t\t\t$current_allowed_mysqlserver = isset($result['allowed_mysqlserver']) && !empty($result['allowed_mysqlserver']) ? json_decode($result['allowed_mysqlserver'], true) : [];\n\t\t\tforeach ($current_allowed_mysqlserver as $dbserver) {\n\t\t\t\t// require privileged access for target db-server\n\t\t\t\tDatabase::needRoot(true, $dbserver, false);\n\t\t\t\t// get DbManager\n\t\t\t\t$dbm = new DbManager($this->logger());\n\t\t\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t\t\t$dbm->getManager()->deleteUser($result['loginname'], $mysql_access_host);\n\t\t\t\t}\n\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t\tDatabase::needRoot(false);\n\t\t\t}\n\n\t\t\t// remove all databases\n\t\t\t$databases_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\t\tWHERE `customerid` = :id ORDER BY `dbserver`\n\t\t\t\");\n\t\t\tDatabase::pexecute($databases_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\t\t\tDatabase::needRoot(true);\n\t\t\t$last_dbserver = 0;\n\n\t\t\t$dbm = new DbManager($this->logger());\n\n\t\t\t$priv_changed = false;\n\t\t\twhile ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ($last_dbserver != $row_database['dbserver']) {\n\t\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t\t\tDatabase::needRoot(true, $row_database['dbserver']);\n\t\t\t\t\t$last_dbserver = $row_database['dbserver'];\n\t\t\t\t}\n\t\t\t\t$dbm->getManager()->deleteDatabase($row_database['databasename']);\n\t\t\t\t$priv_changed = true;\n\t\t\t}\n\t\t\tif ($priv_changed) {\n\t\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\t}\n\t\t\tDatabase::needRoot(false);\n\n\t\t\t// delete customer itself\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// delete customer databases\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_DATABASES . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// first gather all domain-id's to clean up panel_domaintoip, dns-entries and certificates accordingly\n\t\t\t$did_stmt = Database::prepare(\"SELECT `id`, `domain` FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($did_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\twhile ($row = $did_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t// remove domain->ip connection\n\t\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :did\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t'did' => $row['id']\n\t\t\t\t], true, true);\n\t\t\t\t// remove domain->dns entries\n\t\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_DOMAIN_DNS . \"` WHERE `domain_id` = :did\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t'did' => $row['id']\n\t\t\t\t], true, true);\n\t\t\t\t// remove domain->certificates entries\n\t\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = :did\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t'did' => $row['id']\n\t\t\t\t], true, true);\n\t\t\t\t// remove domains DNS from powerDNS if used, #581\n\t\t\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_PDNS, $row['domain']);\n\t\t\t\t// remove domain from acme.sh / lets encrypt if used\n\t\t\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $row['domain']);\n\t\t\t}\n\t\t\t// remove customer domains\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\t$domains_deleted = $stmt->rowCount();\n\n\t\t\t// delete htpasswds\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_HTPASSWDS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// delete htaccess options\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_HTACCESS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// delete traffic information\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_TRAFFIC . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// remove diskspace analysis\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_DISKSPACE . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// delete mail-accounts\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_MAIL_USERS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// delete mail-addresses\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_MAIL_VIRTUAL . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// gather ftp-user names\n\t\t\t$result2_stmt = Database::prepare(\"SELECT `username` FROM `\" . TABLE_FTP_USERS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($result2_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\twhile ($row = $result2_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t// delete ftp-quotatallies by username\n\t\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_FTP_QUOTATALLIES . \"` WHERE `name` = :name\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t'name' => $row['username']\n\t\t\t\t], true, true);\n\t\t\t}\n\n\t\t\t// remove ftp-group\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_FTP_GROUPS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// remove ftp-users\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_FTP_USERS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// remove api-keys\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_API_KEYS . \"` WHERE `customerid` = :id\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t// Delete all waiting \"create user\" -tasks for this user, #276\n\t\t\t// Note: the WHERE selects part of a serialized array, but it should be safe this way\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_TASKS . \"`\n\t\t\t\tWHERE `type` = '2' AND `data` LIKE :loginname\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'loginname' => \"%:{$result['loginname']};%\"\n\t\t\t], true, true);\n\n\t\t\t// update admin-resource-usage\n\t\t\tAdmins::decreaseUsage($result['adminid'], 'customers_used');\n\t\t\tAdmins::decreaseUsage($result['adminid'], 'domains_used', '', (int)($domains_deleted - $result['subdomains_used']));\n\n\t\t\tif ($result['mysqls'] != '-1') {\n\t\t\t\tAdmins::decreaseUsage($result['adminid'], 'mysqls_used', '', (int)$result['mysqls']);\n\t\t\t}\n\n\t\t\tif ($result['emails'] != '-1') {\n\t\t\t\tAdmins::decreaseUsage($result['adminid'], 'emails_used', '', (int)$result['emails']);\n\t\t\t}\n\n\t\t\tif ($result['email_accounts'] != '-1') {\n\t\t\t\tAdmins::decreaseUsage($result['adminid'], 'email_accounts_used', '', (int)$result['email_accounts']);\n\t\t\t}\n\n\t\t\tif ($result['email_forwarders'] != '-1') {\n\t\t\t\tAdmins::decreaseUsage($result['adminid'], 'email_forwarders_used', '', (int)$result['email_forwarders']);\n\t\t\t}\n\n\t\t\tif ($result['email_quota'] != '-1') {\n\t\t\t\tAdmins::decreaseUsage($result['adminid'], 'email_quota_used', '', (int)$result['email_quota']);\n\t\t\t}\n\n\t\t\tif ($result['subdomains'] != '-1') {\n\t\t\t\tAdmins::decreaseUsage($result['adminid'], 'subdomains_used', '', (int)$result['subdomains']);\n\t\t\t}\n\n\t\t\tif ($result['ftps'] != '-1') {\n\t\t\t\tAdmins::decreaseUsage($result['adminid'], 'ftps_used', '', (int)$result['ftps']);\n\t\t\t}\n\n\t\t\tif (($result['diskspace'] / 1024) != '-1') {\n\t\t\t\tAdmins::decreaseUsage($result['adminid'], 'diskspace_used', '', (int)$result['diskspace']);\n\t\t\t}\n\n\t\t\t// rebuild configs\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\n\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\n\t\t\tif ($delete_userfiles == 1) {\n\t\t\t\t// insert task to remove the customers files from the filesystem\n\t\t\t\tCronjob::inserttask(TaskId::DELETE_CUSTOMER_FILES, $result['loginname']);\n\t\t\t}\n\n\t\t\t// Using filesystem - quota, insert a task which cleans the filesystem - quota\n\t\t\tCronjob::inserttask(TaskId::CREATE_QUOTA);\n\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] deleted customer '\" . $result['loginname'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * decrease resource-usage\n\t *\n\t * @param int $customerid\n\t * @param string $resource\n\t * @param string $extra\n\t *            optional, default empty\n\t * @param int $decrease_by\n\t *            optional, default 1\n\t */\n\tpublic static function decreaseUsage($customerid = 0, $resource = null, $extra = '', $decrease_by = 1)\n\t{\n\t\tself::updateResourceUsage(TABLE_PANEL_CUSTOMERS, 'customerid', $customerid, '-', $resource, $extra, $decrease_by);\n\t}\n\n\t/**\n\t * unlock a locked customer by either id or loginname\n\t *\n\t * @param int $id\n\t *            optional, the customer-id\n\t * @param string $loginname\n\t *            optional, the loginname\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function unlock()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$ln_optional = $id > 0;\n\t\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\n\t\t\t$result = $this->apiCall('Customers.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'loginname' => $loginname\n\t\t\t]);\n\t\t\t$id = $result['customerid'];\n\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t`loginfail_count` = '0'\n\t\t\t\tWHERE `customerid`= :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\t// set the new value for result-array\n\t\t\t$result['loginfail_count'] = 0;\n\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] unlocked customer '\" . $result['loginname'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * Function to move a given customer to a given admin/reseller\n\t * and update all its references accordingly\n\t *\n\t * @param int $id\n\t *            optional, the customer-id\n\t * @param string $loginname\n\t *            optional, the loginname\n\t * @param int $adminid\n\t *            target-admin-id\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function move()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t$adminid = $this->getParam('adminid');\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$ln_optional = $id > 0;\n\t\t\t$loginname = $this->getParam('loginname', $ln_optional, '');\n\n\t\t\t$c_result = $this->apiCall('Customers.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'loginname' => $loginname\n\t\t\t]);\n\t\t\t$id = $c_result['customerid'];\n\n\t\t\t// check if target-admin is the current admin\n\t\t\tif ($adminid == $c_result['adminid']) {\n\t\t\t\tthrow new Exception(\"Cannot move customer to the same admin/reseller as he currently is assigned to\", 406);\n\t\t\t}\n\n\t\t\t// get target admin\n\t\t\t$a_result = $this->apiCall('Admins.get', [\n\t\t\t\t'id' => $adminid\n\t\t\t]);\n\n\t\t\t// Update customer entry\n\t\t\t$updCustomer_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `adminid` = :adminid WHERE `customerid` = :cid\n\t\t\t\");\n\t\t\tDatabase::pexecute($updCustomer_stmt, [\n\t\t\t\t'adminid' => $adminid,\n\t\t\t\t'cid' => $id\n\t\t\t], true, true);\n\n\t\t\t// Update customer-domains\n\t\t\t$updDomains_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `adminid` = :adminid WHERE `customerid` = :cid\n\t\t\t\");\n\t\t\tDatabase::pexecute($updDomains_stmt, [\n\t\t\t\t'adminid' => $adminid,\n\t\t\t\t'cid' => $id\n\t\t\t], true, true);\n\n\t\t\t// now, recalculate the resource-usage for the old and the new admin\n\t\t\tUser::updateCounters(false);\n\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] moved user '\" . $c_result['loginname'] . \"' from admin/reseller '\" . $c_result['adminname'] . \" to admin/reseller '\" . $a_result['loginname'] . \"'\");\n\n\t\t\t$result = $this->apiCall('Customers.get', [\n\t\t\t\t'id' => $c_result['customerid']\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/DataDump.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass DataDump extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new data dump job\n\t *\n\t * @param string $path\n\t *            path to store the dumped data to\n\t * @param string $pgp_public_key\n\t *            optional pgp public key to encrypt the archive, default is empty\n\t * @param bool $dump_dbs\n\t *            optional whether to include databases, default is 0 (false)\n\t * @param bool $dump_mail\n\t *            optional whether to include mail-data, default is 0 (false)\n\t * @param bool $dump_web\n\t *            optional whether to incoude web-data, default is 0 (false)\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\t$this->validateAccess();\n\n\t\t// required parameter\n\t\t$path = $this->getParam('path');\n\n\t\t// parameter\n\t\t$pgp_public_key = $this->getParam('pgp_public_key', true, '');\n\t\t$dump_dbs = $this->getBoolParam('dump_dbs', true, 0);\n\t\t$dump_mail = $this->getBoolParam('dump_mail', true, 0);\n\t\t$dump_web = $this->getBoolParam('dump_web', true, 0);\n\n\t\t// get customer data\n\t\t$customer = $this->getCustomerData();\n\n\t\t// validation\n\t\t$path = FileDir::makeCorrectDir(Validate::validate($path, 'path', '', '', [], true));\n\t\t$userpath = $path;\n\t\t$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);\n\n\t\t// path cannot be the customers docroot\n\t\tif ($path == FileDir::makeCorrectDir($customer['documentroot'])) {\n\t\t\tResponse::standardError('dumpfoldercannotbedocroot', '', true);\n\t\t}\n\n\t\t// pgp public key validation\n\t\tif (!empty($pgp_public_key)) {\n\t\t\t// check if gnupg extension is loaded\n\t\t\tif (!extension_loaded('gnupg')) {\n\t\t\t\tResponse::standardError('gnupgextensionnotavailable', '', true);\n\t\t\t}\n\t\t\t// check if the pgp public key is a valid key\n\t\t\tputenv('GNUPGHOME='.sys_get_temp_dir());\n\t\t\tif (gnupg_import(gnupg_init(), $pgp_public_key) === false) {\n\t\t\t\tResponse::standardError('invalidpgppublickey', '', true);\n\t\t\t}\n\t\t}\n\n\t\tif ($dump_dbs != '1') {\n\t\t\t$dump_dbs = '0';\n\t\t}\n\n\t\tif ($dump_mail != '1') {\n\t\t\t$dump_mail = '0';\n\t\t}\n\n\t\tif ($dump_web != '1') {\n\t\t\t$dump_web = '0';\n\t\t}\n\n\t\t$task_data = [\n\t\t\t'customerid' => $customer['customerid'],\n\t\t\t'uid' => $customer['guid'],\n\t\t\t'gid' => $customer['guid'],\n\t\t\t'loginname' => $customer['loginname'],\n\t\t\t'destdir' => $path,\n\t\t\t'pgp_public_key' => $pgp_public_key,\n\t\t\t'dump_dbs' => $dump_dbs,\n\t\t\t'dump_mail' => $dump_mail,\n\t\t\t'dump_web' => $dump_web\n\t\t];\n\n\t\t// schedule export job\n\t\tCronjob::inserttask(TaskId::CREATE_CUSTOMER_DATADUMP, $task_data);\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added customer data export job for '\" . $customer['loginname'] . \"'. Target directory: \" . $userpath);\n\t\treturn $this->response($task_data);\n\t}\n\n\t/**\n\t * check whether data dump is enabled systemwide and if accessible for customer (hide_options)\n\t *\n\t * @throws Exception\n\t */\n\tprivate function validateAccess()\n\t{\n\t\tif (Settings::Get('system.exportenabled') != 1) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.export')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t}\n\n\t/**\n\t * You cannot get a planned data export.\n\t * Try DataDump.listing()\n\t */\n\tpublic function get()\n\t{\n\t\tthrow new Exception('You cannot get a planned data export. Try DataDump.listing()', 303);\n\t}\n\n\t/**\n\t * You cannot update a planned data export.\n\t * You need to delete it and re-add it.\n\t */\n\tpublic function update()\n\t{\n\t\tthrow new Exception('You cannot update a planned data export. You need to delete it and re-add it.', 303);\n\t}\n\n\t/**\n\t * list all planned data export jobs, if called from an admin, list all planned data export jobs of all customers you are\n\t * allowed to view, or specify id or loginname for one specific customer\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select data export jobs of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select data export jobs of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t$this->validateAccess();\n\n\t\t$customer_ids = $this->getAllowedCustomerIds('extras.export');\n\n\t\t// check whether there is a data export job for this customer\n\t\t$query_fields = [];\n\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` = '20'\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\tDatabase::pexecute($sel_stmt, $query_fields, true, true);\n\t\t$result = [];\n\t\twhile ($entry = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$entry['data'] = json_decode($entry['data'], true);\n\t\t\tif (in_array($entry['data']['customerid'], $customer_ids)) {\n\t\t\t\t$result[] = $entry;\n\t\t\t}\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list customer data dump jobs\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of planned data exports\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select data export jobs of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select data export jobs of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\t$this->validateAccess();\n\n\t\t$customer_ids = $this->getAllowedCustomerIds('extras.export');\n\n\t\t// check whether there is a data export job for this customer\n\t\t$result_count = 0;\n\t\t$query_fields = [];\n\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` = '20' \" . $this->getSearchWhere($query_fields, true));\n\t\tDatabase::pexecute($sel_stmt, $query_fields, true, true);\n\t\twhile ($entry = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$entry['data'] = json_decode($entry['data'], true);\n\t\t\tif (in_array($entry['data']['customerid'], $customer_ids)) {\n\t\t\t\t$result_count++;\n\t\t\t}\n\t\t}\n\t\treturn $this->response($result_count);\n\t}\n\n\t/**\n\t * delete a planned data export jobs by id, if called from an admin you need to specify the customerid/loginname\n\t *\n\t * @param int $job_entry\n\t *            id of data export job\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return bool\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\t// get planned exports\n\t\t$result = $this->apiCall('DataDump.listing', $this->getParamList());\n\n\t\t$entry = $this->getParam('job_entry');\n\t\t$customer_ids = $this->getAllowedCustomerIds('extras.export');\n\n\t\tif ($result['count'] > 0 && $entry > 0) {\n\t\t\t// prepare statement\n\t\t\t$del_stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `id` = :tid\");\n\t\t\t// check for the correct job\n\t\t\tforeach ($result['list'] as $exportjob) {\n\t\t\t\tif ($exportjob['id'] == $entry && in_array($exportjob['data']['customerid'], $customer_ids)) {\n\t\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t\t'tid' => $entry\n\t\t\t\t\t], true, true);\n\t\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] deleted planned customer data export job #\" . $entry);\n\t\t\t\t\treturn $this->response(true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow new Exception('Data export job with id #' . $entry . ' could not be found', 404);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/DirOptions.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass DirOptions extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add options for a given directory\n\t *\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param string $path\n\t *            path relative to the customer's home-Directory\n\t * @param bool $options_indexes\n\t *            optional, activate directory-listing for this path, default 0 (false)\n\t * @param bool $options_cgi\n\t *            optional, allow Perl/CGI execution, default 0 (false)\n\t * @param string $error404path\n\t *            optional, custom 404 error string/file\n\t * @param string $error403path\n\t *            optional, custom 403 error string/file\n\t * @param string $error500path\n\t *            optional, custom 500 error string/file\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.pathoptions')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// get needed customer info to reduce the email-address-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\t// required parameters\n\t\t$path = $this->getParam('path');\n\n\t\t// parameters\n\t\t$options_indexes = $this->getBoolParam('options_indexes', true, 0);\n\t\t$options_cgi = $this->getBoolParam('options_cgi', true, 0);\n\t\t$error404path = $this->getParam('error404path', true, '');\n\t\t$error403path = $this->getParam('error403path', true, '');\n\t\t$error500path = $this->getParam('error500path', true, '');\n\n\t\t// validation\n\t\t$path = FileDir::makeCorrectDir(Validate::validate($path, 'path', Validate::REGEX_DIR, '', [], true));\n\t\t$userpath = $path;\n\t\t$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);\n\n\t\tif (!empty($error404path)) {\n\t\t\t$error404path = $this->correctErrorDocument($error404path, true);\n\t\t}\n\n\t\tif (!empty($error403path)) {\n\t\t\t$error403path = $this->correctErrorDocument($error403path, true);\n\t\t}\n\n\t\tif (!empty($error500path)) {\n\t\t\t$error500path = $this->correctErrorDocument($error500path, true);\n\t\t}\n\n\t\t// check for duplicate path\n\t\t$path_dupe_check_stmt = Database::prepare(\"\n\t\t\tSELECT `id`, `path` FROM `\" . TABLE_PANEL_HTACCESS . \"`\n\t\t\tWHERE `path`= :path AND `customerid`= :customerid\n\t\t\");\n\t\t$path_dupe_check = Database::pexecute_first($path_dupe_check_stmt, [\n\t\t\t\"path\" => $path,\n\t\t\t\"customerid\" => $customer['customerid']\n\t\t], true, true);\n\n\t\t// duplicate check\n\t\tif ($path_dupe_check && $path_dupe_check['path'] == $path) {\n\t\t\tResponse::standardError('errordocpathdupe', $userpath, true);\n\t\t}\n\n\t\t// insert the entry\n\t\t$stmt = Database::prepare('\n\t\t\tINSERT INTO `' . TABLE_PANEL_HTACCESS . '` SET\n\t\t\t`customerid` = :customerid,\n\t\t\t`path` = :path,\n\t\t\t`options_indexes` = :options_indexes,\n\t\t\t`error404path` = :error404path,\n\t\t\t`error403path` = :error403path,\n\t\t\t`error500path` = :error500path,\n\t\t\t`options_cgi` = :options_cgi\n\t\t');\n\t\t$params = [\n\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\"path\" => $path,\n\t\t\t\"options_indexes\" => $options_indexes,\n\t\t\t\"error403path\" => $error403path,\n\t\t\t\"error404path\" => $error404path,\n\t\t\t\"error500path\" => $error500path,\n\t\t\t\"options_cgi\" => $options_cgi\n\t\t];\n\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t$id = Database::lastInsertId();\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added directory-option for '\" . $userpath . \"'\");\n\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\n\t\t$result = $this->apiCall('DirOptions.get', [\n\t\t\t'id' => $id\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * this functions validates a given value as ErrorDocument\n\t * refs #267\n\t *\n\t * @param string $errdoc\n\t * @param bool $throw_exception\n\t *\n\t * @return string error-document-string\n\t *\n\t */\n\tprivate function correctErrorDocument(string $errdoc, $throw_exception = false)\n\t{\n\t\tif (trim($errdoc) != '') {\n\t\t\t// not a URL\n\t\t\tif ((strtoupper(substr($errdoc, 0, 5)) != 'HTTP:' && strtoupper(substr($errdoc, 0, 6)) != 'HTTPS:') || !Validate::validateUrl($errdoc)) {\n\t\t\t\t// a file\n\t\t\t\tif (substr($errdoc, 0, 1) != '\"') {\n\t\t\t\t\t$errdoc = FileDir::makeCorrectFile($errdoc);\n\t\t\t\t\t// apache needs a starting-slash (starting at the domains-docroot)\n\t\t\t\t\tif (!substr($errdoc, 0, 1) == '/') {\n\t\t\t\t\t\t$errdoc = '/' . $errdoc;\n\t\t\t\t\t}\n\t\t\t\t} elseif (preg_match('/^\"([^\\r\\n\\t\\f\\0\"]+)\"$/', $errdoc)) {\n\t\t\t\t\t// a string (check for ending \")\n\t\t\t\t} else {\n\t\t\t\t\tResponse::standardError('invaliderrordocumentvalue', '', $throw_exception);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn trim($errdoc);\n\t}\n\n\t/**\n\t * return a directory-protection entry by id\n\t *\n\t * @param int $id\n\t *            id of dir-protection entry\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\n\t\t$params = [];\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($this->getUserDetail('customers_see_all') == false) {\n\t\t\t\t// if it's a reseller or an admin who cannot see all customers, we need to check\n\t\t\t\t// whether the database belongs to one of his customers\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t\t$customer_ids = [];\n\t\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t\t}\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_HTACCESS . \"`\n\t\t\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\t\t\tAND `id` = :id\n\t\t\t\t\");\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_HTACCESS . \"`\n\t\t\t\t\tWHERE `id` = :id\n\t\t\t\t\");\n\t\t\t}\n\t\t} else {\n\t\t\tif (Settings::IsInList('panel.customer_hide_options', 'extras.pathoptions')) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_HTACCESS . \"`\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\tAND `id` = :id\n\t\t\t\");\n\t\t\t$params['customerid'] = $this->getUserDetail('customerid');\n\t\t}\n\t\t$params['id'] = $id;\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get directory options for '\" . $result['path'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\t$key = \"id #\" . $id;\n\t\tthrow new Exception(\"Directory option with \" . $key . \" could not be found\", 404);\n\t}\n\n\t/**\n\t * update options for a given directory by id\n\t *\n\t * @param int $id\n\t *            id of dir-protection entry\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param bool $options_indexes\n\t *            optional, activate directory-listing for this path, default 0 (false)\n\t * @param bool $options_cgi\n\t *            optional, allow Perl/CGI execution, default 0 (false)\n\t * @param string $error404path\n\t *            optional, custom 404 error string/file\n\t * @param string $error403path\n\t *            optional, custom 403 error string/file\n\t * @param string $error500path\n\t *            optional, custom 500 error string/file\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\n\t\t// validation\n\t\t$result = $this->apiCall('DirOptions.get', [\n\t\t\t'id' => $id\n\t\t]);\n\n\t\t// get needed customer info to reduce the email-address-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\t// parameters\n\t\t$options_indexes = $this->getBoolParam('options_indexes', true, $result['options_indexes']);\n\t\t$options_cgi = $this->getBoolParam('options_cgi', true, $result['options_cgi']);\n\t\t$error404path = $this->getParam('error404path', true, $result['error404path']);\n\t\t$error403path = $this->getParam('error403path', true, $result['error403path']);\n\t\t$error500path = $this->getParam('error500path', true, $result['error500path']);\n\n\t\tif (!empty($error404path)) {\n\t\t\t$error404path = $this->correctErrorDocument($error404path, true);\n\t\t}\n\n\t\tif (!empty($error403path)) {\n\t\t\t$error403path = $this->correctErrorDocument($error403path, true);\n\t\t}\n\n\t\tif (!empty($error500path)) {\n\t\t\t$error500path = $this->correctErrorDocument($error500path, true);\n\t\t}\n\n\t\tif (($options_indexes != $result['options_indexes']) || ($error404path != $result['error404path']) || ($error403path != $result['error403path']) || ($error500path != $result['error500path']) || ($options_cgi != $result['options_cgi'])) {\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_HTACCESS . \"`\n\t\t\t\tSET `options_indexes` = :options_indexes,\n\t\t\t\t`error404path` = :error404path,\n\t\t\t\t`error403path` = :error403path,\n\t\t\t\t`error500path` = :error500path,\n\t\t\t\t`options_cgi` = :options_cgi\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\tAND `id` = :id\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\"options_indexes\" => $options_indexes,\n\t\t\t\t\"error403path\" => $error403path,\n\t\t\t\t\"error404path\" => $error404path,\n\t\t\t\t\"error500path\" => $error500path,\n\t\t\t\t\"options_cgi\" => $options_cgi,\n\t\t\t\t\"id\" => $id\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] edited directory options for '\" . str_replace($customer['documentroot'], '/', $result['path']) . \"'\");\n\t\t}\n\n\t\t$result = $this->apiCall('DirOptions.get', [\n\t\t\t'id' => $id\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * list all directory-options, if called from an admin, list all directory-options of all customers you are allowed\n\t * to view, or specify id or loginname for one specific customer\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select directory-protections of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select directory-protections of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\t$customer_ids = $this->getAllowedCustomerIds('extras.pathoptions');\n\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_HTACCESS . \"`\n\t\t\tWHERE `customerid` IN (\" . implode(', ', $customer_ids) . \")\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$result[] = $row;\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list directory-options\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of accessible directory options\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select directory-protections of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select directory-protections of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\t$customer_ids = $this->getAllowedCustomerIds('extras.pathoptions');\n\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(*) as num_htaccess FROM `\" . TABLE_PANEL_HTACCESS . \"`\n\t\t\tWHERE `customerid` IN (\" . implode(', ', $customer_ids) . \")\n\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_htaccess']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * delete a directory-options by id\n\t *\n\t * @param int $id\n\t *            id of dir-protection entry\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id');\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.pathoptions')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// get directory-option\n\t\t$result = $this->apiCall('DirOptions.get', [\n\t\t\t'id' => $id\n\t\t]);\n\n\t\tif ($this->isAdmin()) {\n\t\t\t// get customer-data\n\t\t\t$customer_data = $this->apiCall('Customers.get', [\n\t\t\t\t'id' => $result['customerid']\n\t\t\t]);\n\t\t} else {\n\t\t\t$customer_data = $this->getUserData();\n\t\t}\n\n\t\t// do we have to remove the symlink and folder in suexecpath?\n\t\tif ((int)Settings::Get('perl.suexecworkaround') == 1) {\n\t\t\t$loginname = $customer_data['loginname'];\n\t\t\t$suexecpath = FileDir::makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($result['path']) . '/');\n\t\t\t$perlsymlink = FileDir::makeCorrectFile($result['path'] . '/cgi-bin');\n\t\t\t// remove symlink\n\t\t\tif (file_exists($perlsymlink)) {\n\t\t\t\tFileDir::safe_exec('rm -f ' . escapeshellarg($perlsymlink));\n\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_DEBUG, \"[API] deleted suexecworkaround symlink '\" . $perlsymlink . \"'\");\n\t\t\t}\n\t\t\t// remove folder in suexec-path\n\t\t\tif (file_exists($suexecpath)) {\n\t\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($suexecpath));\n\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_DEBUG, \"[API] deleted suexecworkaround path '\" . $suexecpath . \"'\");\n\t\t\t}\n\t\t}\n\t\t$stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_PANEL_HTACCESS . \"`\n\t\t\tWHERE `customerid`= :customerid\n\t\t\tAND `id`= :id\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"customerid\" => $customer_data['customerid'],\n\t\t\t\"id\" => $id\n\t\t], true, true);\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] deleted directory-option for '\" . str_replace($customer_data['documentroot'], '/', $result['path']) . \"'\");\n\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\treturn $this->response($result);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/DirProtections.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass DirProtections extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add htaccess protection to a given directory\n\t *\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param string $path\n\t * @param string $username\n\t * @param string $directory_password\n\t * @param string $directory_authname\n\t *            optional name/description for the protection\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.directoryprotection')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// get needed customer info to reduce the email-address-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\t// required parameters\n\t\t$path = $this->getParam('path');\n\t\t$username = $this->getParam('username');\n\t\t$password = $this->getParam('directory_password');\n\n\t\t// parameters\n\t\t$authname = $this->getParam('directory_authname', true, '');\n\n\t\t// validation\n\t\t$path = FileDir::makeCorrectDir(Validate::validate($path, 'path', Validate::REGEX_DIR, '', [], true));\n\t\t$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);\n\t\t$username = Validate::validate($username, 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\\-_]+\\$?$/', '', [], true);\n\t\t$authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\\-_ ]+\\$?$/', '', [], true);\n\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\t\t$password = Crypt::validatePassword($password, true);\n\n\t\t// check for duplicate usernames for the path\n\t\t$username_path_check_stmt = Database::prepare(\"\n\t\t\tSELECT `id`, `username`, `path` FROM `\" . TABLE_PANEL_HTPASSWDS . \"`\n\t\t\tWHERE `username`= :username AND `path`= :path AND `customerid`= :customerid\n\t\t\");\n\t\t$params = [\n\t\t\t\"username\" => $username,\n\t\t\t\"path\" => $path,\n\t\t\t\"customerid\" => $customer['customerid']\n\t\t];\n\t\t$username_path_check = Database::pexecute_first($username_path_check_stmt, $params, true, true);\n\n\t\t$password_enc = Crypt::makeCryptPassword($password, true);\n\n\t\t// duplicate check\n\t\tif ($username_path_check && $username_path_check['username'] == $username && $username_path_check['path'] == $path) {\n\t\t\tResponse::standardError('userpathcombinationdupe', '', true);\n\t\t} elseif ($password == $username) {\n\t\t\tResponse::standardError('passwordshouldnotbeusername', '', true);\n\t\t}\n\n\t\t// insert the entry\n\t\t$stmt = Database::prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_HTPASSWDS . \"` SET\n\t\t\t`customerid` = :customerid,\n\t\t\t`username` = :username,\n\t\t\t`password` = :password,\n\t\t\t`path` = :path,\n\t\t\t`authname` = :authname\n\t\t\");\n\t\t$params = [\n\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\"username\" => $username,\n\t\t\t\"password\" => $password_enc,\n\t\t\t\"path\" => $path,\n\t\t\t\"authname\" => $authname\n\t\t];\n\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t$id = Database::lastInsertId();\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added directory-protection for '\" . $username . \" (\" . $path . \")'\");\n\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\n\t\t$result = $this->apiCall('DirProtections.get', [\n\t\t\t'id' => $id\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * return a directory-protection entry by either id or username\n\t *\n\t * @param int $id\n\t *            optional, the directory-protection-id\n\t * @param string $username\n\t *            optional, the username\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$un_optional = $id > 0;\n\t\t$username = $this->getParam('username', $un_optional, '');\n\n\t\t$params = [];\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($this->getUserDetail('customers_see_all') == false) {\n\t\t\t\t// if it's a reseller or an admin who cannot see all customers, we need to check\n\t\t\t\t// whether the database belongs to one of his customers\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t\t$customer_ids = [];\n\t\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t\t}\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_HTPASSWDS . \"`\n\t\t\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\t\t\tAND (`id` = :idun OR `username` = :idun)\n\t\t\t\t\");\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_HTPASSWDS . \"`\n\t\t\t\t\tWHERE (`id` = :idun OR `username` = :idun)\n\t\t\t\t\");\n\t\t\t}\n\t\t} else {\n\t\t\tif (Settings::IsInList('panel.customer_hide_options', 'extras.directoryprotection')) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_HTPASSWDS . \"`\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\tAND (`id` = :idun OR `username` = :idun)\n\t\t\t\");\n\t\t\t$params['customerid'] = $this->getUserDetail('customerid');\n\t\t}\n\t\t$params['idun'] = ($id <= 0 ? $username : $id);\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get directory protection for '\" . $result['path'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\t$key = ($id > 0 ? \"id #\" . $id : \"username '\" . $username . \"'\");\n\t\tthrow new Exception(\"Directory protection with \" . $key . \" could not be found\", 404);\n\t}\n\n\t/**\n\t * update htaccess protection of a given directory\n\t *\n\t * @param int $id\n\t *            optional the directory-protection-id\n\t * @param string $username\n\t *            optional, the username\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param string $directory_password\n\t *            optional, leave empty for no change\n\t * @param string $directory_authname\n\t *            optional name/description for the protection\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$un_optional = $id > 0;\n\t\t$username = $this->getParam('username', $un_optional, '');\n\n\t\t// validation\n\t\t$result = $this->apiCall('DirProtections.get', [\n\t\t\t'id' => $id,\n\t\t\t'username' => $username\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// parameters\n\t\t$password = $this->getParam('directory_password', true, '');\n\t\t$authname = $this->getParam('directory_authname', true, $result['authname']);\n\n\t\t// get needed customer info\n\t\t$customer = $this->getCustomerData();\n\n\t\t// validation\n\t\t$authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\\-_ ]+\\$?$/', '', [], true);\n\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\t\t$password = Crypt::validatePassword($password, true);\n\n\t\t$upd_query = \"\";\n\t\t$upd_params = [\n\t\t\t\"id\" => $result['id'],\n\t\t\t\"cid\" => $customer['customerid']\n\t\t];\n\t\tif (!empty($password)) {\n\t\t\tif ($password == $result['username']) {\n\t\t\t\tResponse::standardError('passwordshouldnotbeusername', '', true);\n\t\t\t}\n\t\t\t$password_enc = Crypt::makeCryptPassword($password, true);\n\n\t\t\t$upd_query .= \"`password`= :password_enc\";\n\t\t\t$upd_params['password_enc'] = $password_enc;\n\t\t}\n\t\tif ($authname != $result['authname']) {\n\t\t\tif (!empty($upd_query)) {\n\t\t\t\t$upd_query .= \", \";\n\t\t\t}\n\t\t\t$upd_query .= \"`authname` = :authname\";\n\t\t\t$upd_params['authname'] = $authname;\n\t\t}\n\n\t\t// build update query\n\t\tif (!empty($upd_query)) {\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_HTPASSWDS . \"` SET \" . $upd_query . \" WHERE `id` = :id AND `customerid`= :cid\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, $upd_params, true, true);\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t}\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] updated directory-protection '\" . $result['username'] . \" (\" . $result['path'] . \")'\");\n\t\t$result = $this->apiCall('DirProtections.get', [\n\t\t\t'id' => $result['id']\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * list all directory-protections, if called from an admin, list all directory-protections of all customers you are\n\t * allowed to view, or specify id or loginname for one specific customer\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select directory-protections of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select directory-protections of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\t$customer_ids = $this->getAllowedCustomerIds('extras.directoryprotection');\n\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_HTPASSWDS . \"`\n\t\t\tWHERE `customerid` IN (\" . implode(', ', $customer_ids) . \")\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$result[] = $row;\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list directory-protections\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of accessible directory protections\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select directory-protections of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select directory-protections of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\t$customer_ids = $this->getAllowedCustomerIds('extras.directoryprotection');\n\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(*) as num_htpasswd FROM `\" . TABLE_PANEL_HTPASSWDS . \"`\n\t\t\tWHERE `customerid` IN (\" . implode(', ', $customer_ids) . \")\n\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_htpasswd']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * delete a directory-protection by either id or username\n\t *\n\t * @param int $id\n\t *            optional, the directory-protection-id\n\t * @param string $username\n\t *            optional, the username\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$un_optional = $id > 0;\n\t\t$username = $this->getParam('username', $un_optional, '');\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.directoryprotection')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// get directory protection\n\t\t$result = $this->apiCall('DirProtections.get', [\n\t\t\t'id' => $id,\n\t\t\t'username' => $username\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif ($this->isAdmin()) {\n\t\t\t// get customer-data\n\t\t\t$customer_data = $this->apiCall('Customers.get', [\n\t\t\t\t'id' => $result['customerid']\n\t\t\t]);\n\t\t} else {\n\t\t\t$customer_data = $this->getUserData();\n\t\t}\n\n\t\t$stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_PANEL_HTPASSWDS . \"` WHERE `customerid`= :customerid\tAND `id`= :id\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"customerid\" => $customer_data['customerid'],\n\t\t\t\"id\" => $id\n\t\t]);\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, \"[API] deleted htpasswd for '\" . $result['username'] . \" (\" . $result['path'] . \")'\");\n\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\treturn $this->response($result);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/DomainZones.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Dns\\Dns;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass DomainZones extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new dns zone for a given domain by id or domainname\n\t *\n\t * @param int $id\n\t *            optional domain id\n\t * @param string $domainname\n\t *            optional domain name\n\t * @param string $record\n\t *            optional, default empty\n\t * @param string $type\n\t *            optional, zone-entry type (A, AAAA, TXT, etc.), default 'A'\n\t * @param int $prio\n\t *            optional, priority, default empty\n\t * @param string $content\n\t *            optional, default empty\n\t * @param int $ttl\n\t *            optional, default 18000\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif (Settings::Get('system.dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"DNS service not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && $this->getUserDetail('dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\t// get requested domain\n\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// parameters\n\t\t$record = $this->getParam('record', true, null);\n\t\t$type = $this->getParam('type', true, 'A');\n\t\t$prio = $this->getParam('prio', true, null);\n\t\t$content = $this->getParam('content', true, null);\n\t\t$ttl = $this->getParam('ttl', true, 18000);\n\n\t\tif ($result['parentdomainid'] != '0') {\n\t\t\tthrow new Exception(\"DNS zones can only be generated for the main domain, not for subdomains\", 406);\n\t\t}\n\n\t\tif ($result['subisbinddomain'] != '1') {\n\t\t\tResponse::standardError('dns_domain_nodns', '', true);\n\t\t}\n\n\t\t$idna_convert = new IdnaWrapper();\n\t\t$domain = $idna_convert->encode($result['domain']);\n\n\t\t// select all entries\n\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_DOMAIN_DNS . \"` WHERE domain_id = :did\");\n\t\tDatabase::pexecute($sel_stmt, [\n\t\t\t'did' => $id\n\t\t], true, true);\n\t\t$dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC);\n\n\t\t// validation\n\t\t$errors = [];\n\t\tif (empty(trim($record))) {\n\t\t\t$record = \"@\";\n\t\t}\n\n\t\t$record = trim(strtolower($record));\n\n\t\tif ($record != '@' && $record != '*') {\n\t\t\t// validate record\n\t\t\tif (strpos($record, '--') !== false) {\n\t\t\t\t$errors[] = lng('error.domain_nopunycode');\n\t\t\t} else {\n\t\t\t\t// check for wildcard-record\n\t\t\t\t$add_wildcard_again = false;\n\t\t\t\tif (substr($record, 0, 2) == '*.') {\n\t\t\t\t\t$record = substr($record, 2);\n\t\t\t\t\t$add_wildcard_again = true;\n\t\t\t\t}\n\t\t\t\t// convert entry\n\t\t\t\t$record = $idna_convert->encode($record);\n\n\t\t\t\tif ($add_wildcard_again) {\n\t\t\t\t\t$record = '*.' . $record;\n\t\t\t\t}\n\n\t\t\t\tif (strlen($record) > 63) {\n\t\t\t\t\t$errors[] = lng('error.dns_record_toolong');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($ttl <= 0) {\n\t\t\t$ttl = 18000;\n\t\t}\n\n\t\t$content = trim($content);\n\t\tif (empty($content)) {\n\t\t\t$errors[] = lng('error.dns_content_empty');\n\t\t}\n\n\t\t// remove invalid control characters (allow tab + printable ASCII)\n\t\t$content = preg_replace('/[^\\x09\\x20-\\x7E]/', '', $content);\n\t\t// collapse excessive whitespace\n\t\t$content = preg_replace('/\\s+/', ' ', $content);\n\n\t\tif ($type != 'CNAME') {\n\t\t\t// check whether there is a CNAME-record for the same resource\n\t\t\tforeach ($dom_entries as $existing_entries) {\n\t\t\t\tif ($existing_entries['type'] == 'CNAME' && $existing_entries['record'] == $record) {\n\t\t\t\t\t$errors[] = lng('error.dns_other_nomorerr');\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// types\n\t\tif ($type == 'A' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) {\n\t\t\t$errors[] = lng('error.dns_arec_noipv4');\n\t\t} elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {\n\t\t\t$errors[] = lng('error.dns_aaaarec_noipv6');\n\t\t} elseif ($type == 'CAA' && !empty($content)) {\n\t\t\t$re = '/(?\\'critical\\'\\d+)\\h*(?\\'type\\'iodef|issue|issuewild)\\h*(?\\'value\\'(?\\'issuevalue\\'\"(?\\'domain\\'(?=.{3,128}$)(?>(?>[a-zA-Z0-9]+[a-zA-Z0-9-]*[a-zA-Z0-9]+|[a-zA-Z0-9]+)\\.)*(?>[a-zA-Z]{2,}|[a-zA-Z0-9]{2,}\\.[a-zA-Z]{2,}))[;\\h]*(?\\'parameters\\'(?>[a-zA-Z0-9]{1,60}=[a-zA-Z0-9:\\.\\/\\-]{1,60}\\h*)+)?\")|(?\\'iodefvalue\\'\"(?\\'url\\'(mailto:.*|http:\\/\\/.*|https:\\/\\/.*))\"))/';\n\t\t\tpreg_match($re, $content, $matches);\n\n\t\t\tif (empty($matches)) {\n\t\t\t\t$errors[] = lng('error.dns_content_invalid');\n\t\t\t} elseif (($matches['type'] == 'issue' || $matches['type'] == 'issuewild') && !Validate::validateDomain($matches['domain'])) {\n\t\t\t\t$errors[] = lng('error.dns_content_invalid');\n\t\t\t} elseif ($matches['type'] == 'iodef' && !Validate::validateUrl($matches['url'])) {\n\t\t\t\t$errors[] = lng('error.dns_content_invalid');\n\t\t\t} else {\n\t\t\t\t$content = $matches[0];\n\t\t\t}\n\t\t} elseif ($type == 'CNAME' || $type == 'DNAME') {\n\t\t\t// check for trailing dot\n\t\t\tif (substr($content, -1) == '.') {\n\t\t\t\t// remove it for checks\n\t\t\t\t$content = substr($content, 0, -1);\n\t\t\t} else {\n\t\t\t\t// add domain name\n\t\t\t\t$content .= '.' . $domain;\n\t\t\t}\n\t\t\tif (!Validate::validateDomain($content, true)) {\n\t\t\t\t$errors[] = lng('error.dns_cname_invaliddom');\n\t\t\t} else {\n\t\t\t\t// check whether there are RR-records for the same resource\n\t\t\t\tforeach ($dom_entries as $existing_entries) {\n\t\t\t\t\tif ($existing_entries['record'] == $record) {\n\t\t\t\t\t\t$errors[] = lng('error.dns_cname_nomorerr');\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// check www-alias setting\n\t\t\t\tif ($result['wwwserveralias'] == '1' && $result['iswildcarddomain'] == '0' && $record == 'www') {\n\t\t\t\t\t$errors[] = lng('error.no_wwwcnamae_ifwwwalias');\n\t\t\t\t}\n\t\t\t}\n\t\t\t// append trailing dot (again)\n\t\t\t$content .= '.';\n\t\t} elseif ($type == 'LOC' && !empty($content)) {\n\t\t\tif (!Validate::validateDnsLoc($content)) {\n\t\t\t\t$errors[] = lng('error.dns_loc_invalid');\n\t\t\t}\n\t\t} elseif ($type == 'MX') {\n\t\t\tif ($prio === null || $prio < 0) {\n\t\t\t\t$errors[] = lng('error.dns_mx_prioempty');\n\t\t\t}\n\t\t\t// check for trailing dot\n\t\t\tif (substr($content, -1) == '.') {\n\t\t\t\t// remove it for checks\n\t\t\t\t$content = substr($content, 0, -1);\n\t\t\t}\n\t\t\tif (!empty($content) && !Validate::validateDomain($content)) {\n\t\t\t\t$errors[] = lng('error.dns_mx_needdom');\n\t\t\t} else {\n\t\t\t\t// check whether there is a CNAME-record for the same resource\n\t\t\t\tforeach ($dom_entries as $existing_entries) {\n\t\t\t\t\t$fqdn = $existing_entries['record'] . '.' . $domain;\n\t\t\t\t\tif ($existing_entries['type'] == 'CNAME' && $fqdn == $content) {\n\t\t\t\t\t\t$errors[] = lng('error.dns_mx_noalias');\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// append trailing dot (again)\n\t\t\t$content .= '.';\n\t\t\t// if content is only \".\", the prio needs to be 0 which results in a \"null mx\" entry\n\t\t\tif ($content == '.' && $prio != 0) {\n\t\t\t\t$prio = 0;\n\t\t\t}\n\t\t} elseif ($type == 'NAPTR' && !empty($content)) {\n\t\t\tif (!Validate::validateDnsNaptr($content)) {\n\t\t\t\t$errors[] = lng('error.dns_naptr_invalid');\n\t\t\t}\n\t\t} elseif ($type == 'NS') {\n\t\t\t// check for trailing dot\n\t\t\tif (substr($content, -1) == '.') {\n\t\t\t\t// remove it for checks\n\t\t\t\t$content = substr($content, 0, -1);\n\t\t\t}\n\t\t\tif (!Validate::validateDomain($content)) {\n\t\t\t\t$errors[] = lng('error.dns_ns_invaliddom');\n\t\t\t}\n\t\t\t// append trailing dot (again)\n\t\t\t$content .= '.';\n\t\t} elseif ($type == 'RP' && !empty($content)) {\n\t\t\tif (!Validate::validateDnsRp($content)) {\n\t\t\t\t$errors[] = lng('error.dns_rp_invalid');\n\t\t\t}\n\t\t} elseif ($type == 'SRV') {\n\t\t\tif ($prio === null || $prio < 0) {\n\t\t\t\t$errors[] = lng('error.dns_srv_prioempty');\n\t\t\t}\n\t\t\t// check only last part of content, as it can look like:\n\t\t\t// _service._proto.name. TTL class SRV priority weight port target.\n\t\t\t$_split_content = explode(\" \", $content);\n\t\t\t// SRV content must be [weight] [port] [target]\n\t\t\tif (count($_split_content) != 3) {\n\t\t\t\t$errors[] = lng('error.dns_srv_invalidcontent');\n\t\t\t}\n\t\t\t$target = trim($_split_content[count($_split_content) - 1]);\n\t\t\tif ($target != '.') {\n\t\t\t\t// check for trailing dot\n\t\t\t\tif (substr($target, -1) == '.') {\n\t\t\t\t\t// remove it for checks\n\t\t\t\t\t$target = substr($target, 0, -1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ($target != '.' && !Validate::validateDomain($target, true)) {\n\t\t\t\t$errors[] = lng('error.dns_srv_needdom');\n\t\t\t} else {\n\t\t\t\t// check whether there is a CNAME-record for the same resource\n\t\t\t\tforeach ($dom_entries as $existing_entries) {\n\t\t\t\t\t$fqdn = $existing_entries['record'] . '.' . $domain;\n\t\t\t\t\tif ($existing_entries['type'] == 'CNAME' && $fqdn == $target) {\n\t\t\t\t\t\t$errors[] = lng('error.dns_srv_noalias');\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// append trailing dot if there's none\n\t\t\tif (substr($content, -1) != '.') {\n\t\t\t\t$content .= '.';\n\t\t\t}\n\t\t} elseif ($type == 'SSHFP' && !empty($content)) {\n\t\t\tif (!Validate::validateDnsSshfp($content)) {\n\t\t\t\t$errors[] = lng('error.dns_sshfp_invalid');\n\t\t\t}\n\t\t} elseif ($type == 'TLSA' && !empty($content)) {\n\t\t\tif (!Validate::validateDnsTlsa($content)) {\n\t\t\t\t$errors[] = lng('error.dns_tlsa_invalid');\n\t\t\t}\n\t\t} elseif ($type == 'TXT' && !empty($content)) {\n\t\t\t// check that TXT content is enclosed in \" \"\n\t\t\t$content = Dns::encloseTXTContent($content);\n\t\t}\n\n\t\t$new_entry = [\n\t\t\t'record' => $record,\n\t\t\t'type' => $type,\n\t\t\t'prio' => (int)$prio,\n\t\t\t'content' => $content,\n\t\t\t'ttl' => (int)$ttl,\n\t\t\t'domain_id' => (int)$id\n\t\t];\n\t\tksort($new_entry);\n\n\t\t// check for duplicate\n\t\tforeach ($dom_entries as $existing_entry) {\n\t\t\t// compare json-encoded string of array\n\t\t\t$check_entry = $existing_entry;\n\t\t\t// new entry has no ID yet\n\t\t\tunset($check_entry['id']);\n\t\t\t// sort by key\n\t\t\tksort($check_entry);\n\t\t\t// format integer fields to real integer (as they are read as string from the DB)\n\t\t\t$check_entry['prio'] = (int)$check_entry['prio'];\n\t\t\t$check_entry['ttl'] = (int)$check_entry['ttl'];\n\t\t\t$check_entry['domain_id'] = (int)$check_entry['domain_id'];\n\t\t\t// encode both\n\t\t\t$check_entry = json_encode($check_entry);\n\t\t\t$new = json_encode($new_entry);\n\t\t\t// compare\n\t\t\tif ($check_entry === $new) {\n\t\t\t\t$errors[] = lng('error.dns_duplicate_entry');\n\t\t\t\tunset($check_entry);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (empty($errors)) {\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_DOMAIN_DNS . \"` SET\n\t\t\t\t`record` = :record,\n\t\t\t\t`type` = :type,\n\t\t\t\t`prio` = :prio,\n\t\t\t\t`content` = :content,\n\t\t\t\t`ttl` = :ttl,\n\t\t\t\t`domain_id` = :domain_id\n\t\t\t\");\n\t\t\tDatabase::pexecute($ins_stmt, $new_entry, true, true);\n\t\t\t$new_entry_id = Database::lastInsertId();\n\n\t\t\t// add temporary to the entries-array (no reread of DB necessary)\n\t\t\t$new_entry['id'] = $new_entry_id;\n\t\t\t$dom_entries[] = $new_entry;\n\n\t\t\t// re-generate bind configs\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\n\t\t\t$result = $this->apiCall('DomainZones.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\t// return $errors\n\t\tthrow new Exception(implode(\"\\n\", $errors), 406);\n\t}\n\n\t/**\n\t * return a domain-dns entry by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain id\n\t * @param string $domainname\n\t *            optional, the domain name\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif (Settings::Get('system.dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"DNS service not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && $this->getUserDetail('dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\t// get requested domain\n\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif ($result['parentdomainid'] != '0') {\n\t\t\tthrow new Exception(\"DNS zones can only be generated for the main domain, not for subdomains\", 406);\n\t\t}\n\n\t\tif ($result['subisbinddomain'] != '1') {\n\t\t\tResponse::standardError('dns_domain_nodns', '', true);\n\t\t}\n\n\t\t$zone = Dns::createDomainZone($id);\n\t\t$zonefile = (string)$zone;\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get dns-zone for '\" . $result['domain'] . \"'\");\n\t\treturn $this->response(explode(\"\\n\", $zonefile));\n\t}\n\n\t/**\n\t * You cannot update a dns zone entry.\n\t * You need to delete it and re-add it.\n\t */\n\tpublic function update()\n\t{\n\t\tthrow new Exception('You cannot update a dns zone entry. You need to delete it and re-add it.', 303);\n\t}\n\n\t/**\n\t * List all entry records of a given domain by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain id\n\t * @param string $domainname\n\t *            optional, the domain name\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return bool\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif (Settings::Get('system.dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"DNS service not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && $this->getUserDetail('dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\t// get requested domain\n\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$id = $result['id'];\n\t\t$query_fields = [];\n\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_DOMAIN_DNS . \"` WHERE `domain_id` = :did\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\t$query_fields['did'] = $id;\n\t\tDatabase::pexecute($sel_stmt, $query_fields, true, true);\n\t\t$result = [];\n\t\twhile ($row = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$result[] = $row;\n\t\t}\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of domainzone-entries for given domain\n\t *\n\t * @param int $id\n\t *            optional, the domain id\n\t * @param string $domainname\n\t *            optional, the domain name\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif (Settings::Get('system.dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"DNS service not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && $this->getUserDetail('dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\t// get requested domain\n\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t$query_fields = [];\n\t\t$sel_stmt = Database::prepare(\"SELECT COUNT(*) as num_dns FROM `\" . TABLE_DOMAIN_DNS . \"` WHERE `domain_id` = :did\" . $this->getSearchWhere($query_fields, true));\n\t\t$params = array_merge(['did' => $id], $query_fields);\n\t\t$result = Database::pexecute_first($sel_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_dns']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * deletes a domain-dns entry by id\n\t *\n\t * @param int $entry_id\n\t * @param int $id\n\t *            optional, the domain id\n\t * @param string $domainname\n\t *            optional, the domain name\n\t *\n\t * @access admin, customer\n\t * @return bool\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif (Settings::Get('system.dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"DNS service not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && $this->getUserDetail('dnsenabled') != '1') {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$entry_id = $this->getParam('entry_id');\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\t// get requested domain\n\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t$del_stmt = Database::prepare(\"DELETE FROM `\" . TABLE_DOMAIN_DNS . \"` WHERE `id` = :id AND `domain_id` = :did\");\n\t\tDatabase::pexecute($del_stmt, [\n\t\t\t'id' => $entry_id,\n\t\t\t'did' => $id\n\t\t], true, true);\n\t\tif ($del_stmt->rowCount() > 0) {\n\t\t\t// re-generate bind configs\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t\treturn $this->response(true);\n\t\t}\n\t\treturn $this->response(true, 304);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Domains.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Domains extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * lists all domain entries\n\t *\n\t * @param bool $with_ips\n\t *            optional, default true\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$with_ips = $this->getParam('with_ips', true, true);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] list domains\");\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT\n\t\t\t\t`d`.*, `c`.`loginname`, `c`.`deactivated` as `customer_deactivated`, `c`.`name`, `c`.`firstname`, `c`.`company`, `c`.`standardsubdomain`, `c`.`adminid` as customeradmin,\n\t\t\t\t`ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain`\n\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` `d`\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` `c` USING(`customerid`)\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` `ad` ON `d`.`aliasdomain`=`ad`.`id`\n\t\t\t\tWHERE `d`.`parentdomainid`='0' \" . ($this->getUserDetail('customers_see_all') ? '' : \" AND `d`.`adminid` = :adminid \") . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\t\t$params = [];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t}\n\t\t\t$params = array_merge($params, $query_fields);\n\t\t\tDatabase::pexecute($result_stmt, $params, true, true);\n\t\t\t$result = [];\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$row['ipsandports'] = [];\n\t\t\t\tif ($with_ips) {\n\t\t\t\t\t$row['ipsandports'] = $this->getIpsForDomain($row['id']);\n\t\t\t\t}\n\t\t\t\t$row['domain_hascert'] = $this->getHasCertValueForDomain((int)$row['id'], (int)$row['parentdomainid']);\n\t\t\t\t$result[] = $row;\n\t\t\t}\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($result),\n\t\t\t\t'list' => $result\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * get ips connected to given domain as array\n\t *\n\t * @param number $domain_id\n\t * @param bool $ssl_only\n\t *            optional, return only ssl enabled ips, default false\n\t * @return array\n\t */\n\tprivate function getIpsForDomain($domain_id = 0, $ssl_only = false)\n\t{\n\t\t$resultips_stmt = Database::prepare(\"\n\t\t\tSELECT `ips`.* FROM `\" . TABLE_DOMAINTOIP . \"` AS `dti`, `\" . TABLE_PANEL_IPSANDPORTS . \"` AS `ips`\n\t\t\tWHERE `dti`.`id_ipandports` = `ips`.`id` AND `dti`.`id_domain` = :domainid \" . ($ssl_only ? \" AND `ips`.`ssl` = '1'\" : \"\"));\n\n\t\tDatabase::pexecute($resultips_stmt, [\n\t\t\t'domainid' => $domain_id\n\t\t]);\n\n\t\t$ipandports = [];\n\t\twhile ($rowip = $resultips_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (filter_var($rowip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t$rowip['is_ipv6'] = true;\n\t\t\t}\n\t\t\t$ipandports[] = $rowip;\n\t\t}\n\n\t\treturn $ipandports;\n\t}\n\n\tprivate function getHasCertValueForDomain(int $domainid, int $parentdomainid): int\n\t{\n\t\t// nothing (ssl_global)\n\t\t$domain_hascert = 0;\n\t\t$ssl_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = :domainid\");\n\t\tDatabase::pexecute($ssl_stmt, array(\n\t\t\t\"domainid\" => $domainid\n\t\t));\n\t\t$ssl_result = $ssl_stmt->fetch(PDO::FETCH_ASSOC);\n\t\tif (is_array($ssl_result) && isset($ssl_result['ssl_cert_file']) && $ssl_result['ssl_cert_file'] != '') {\n\t\t\t// own certificate (ssl_customer_green)\n\t\t\t$domain_hascert = 1;\n\t\t} else {\n\t\t\t// check if it's parent has one set (shared)\n\t\t\tif ($parentdomainid != 0) {\n\t\t\t\t$ssl_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = :domainid\");\n\t\t\t\tDatabase::pexecute($ssl_stmt, array(\n\t\t\t\t\t\"domainid\" => $parentdomainid\n\t\t\t\t));\n\t\t\t\t$ssl_result = $ssl_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\tif (is_array($ssl_result) && isset($ssl_result['ssl_cert_file']) && $ssl_result['ssl_cert_file'] != '') {\n\t\t\t\t\t// parent has a certificate (ssl_shared)\n\t\t\t\t\t$domain_hascert = 2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $domain_hascert;\n\t}\n\n\t/**\n\t * returns the total number of accessible domains\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] list domains\");\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT\n\t\t\t\tCOUNT(*) as num_domains\n\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` `d`\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` `c` USING(`customerid`)\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` `ad` ON `d`.`aliasdomain`=`ad`.`id`\n\t\t\t\tWHERE `d`.`parentdomainid`='0' \" . ($this->getUserDetail('customers_see_all') ? '' : \" AND `d`.`adminid` = :adminid \") . $this->getSearchWhere($query_fields, true));\n\t\t\t$params = [];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t}\n\t\t\t$params = array_merge($params, $query_fields);\n\t\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_domains']);\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * add new domain entry\n\t *\n\t * @param string $domain\n\t *            domain-name\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param int $adminid\n\t *            optional, default is the calling admin's ID\n\t * @param array $ipandport\n\t *            optional list of ip/ports to assign to domain, default is system-default-ips\n\t * @param int $subcanemaildomain\n\t *            optional, allow subdomains of this domain as email domains, 1 = choosable (default no), 2 = choosable\n\t *            (default yes), 3 = always, default 0 (never)\n\t * @param bool $isemaildomain\n\t *            optional, allow email usage with this domain, default 0 (false)\n\t * @param bool $email_only\n\t *            optional, restrict domain to email usage, default 0 (false)\n\t * @param int $selectserveralias\n\t *            optional, 0 = wildcard, 1 = www-alias, 2 = none, default [system.domaindefaultalias]\n\t * @param bool $speciallogfile\n\t *            optional, whether to create an exclusive web-logfile for this domain, default 0 (false)\n\t * @param int $alias\n\t *            optional, domain-id of a domain that the new domain should be an alias of, default 0 (none)\n\t * @param string $registration_date\n\t *            optional, date of domain registration in form of YYYY-MM-DD, default empty (none)\n\t * @param string $termination_date\n\t *            optional, date of domain termination in form of YYYY-MM-DD, default empty (none)\n\t * @param bool $caneditdomain\n\t *            optional, whether to allow the customer to edit domain settings, default 0 (false)\n\t * @param bool $isbinddomain\n\t *            optional, whether to generate a dns-zone or not (only of nameserver is activated), default 0 (false)\n\t * @param string $zonefile\n\t *            optional, custom dns zone filename (only of nameserver is activated), default empty (auto-generated)\n\t * @param bool $dkim\n\t *            optional, whether this domain should use dkim if antispam is activated, default 0 (false)\n\t * @param string $specialsettings\n\t *            optional, custom webserver vhost-content which is added to the generated vhost, default empty\n\t * @param string $ssl_specialsettings\n\t *            optional, custom webserver vhost-content which is added to the generated ssl-vhost, default empty\n\t * @param bool $include_specialsettings\n\t *            optional, whether to include non-ssl specialsettings in the generated ssl-vhost, default false\n\t * @param bool $notryfiles\n\t *            optional, [nginx only] do not generate the default try-files directive, default 0 (false)\n\t * @param bool $writeaccesslog\n\t *            optional, Enable writing an access-log file for this domain, default 1 (true)\n\t * @param bool $writeerrorlog\n\t *            optional, Enable writing an error-log file for this domain, default 1 (true)\n\t * @param string $documentroot\n\t *            optional, specify homedir of domain by specifying a directory (relative to customer-docroot), be\n\t *            aware, if path starts with / it is considered a full path, not relative to customer-docroot. Also\n\t *            specifying a URL is possible here (redirect), default empty (autogenerated)\n\t * @param bool $phpenabled\n\t *            optional, whether php is enabled for this domain, default 0 (false)\n\t * @param bool $openbasedir\n\t *            optional, whether to activate openbasedir restriction for this domain, default 0 (false)\n\t * @param int $openbasedir_path\n\t *            optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot\n\t * @param int $phpsettingid\n\t *            optional, specify php-configuration that is being used by id, default 1 (system-default)\n\t * @param int $mod_fcgid_starter\n\t *            optional number of fcgid-starters if FCGID is used, default is -1\n\t * @param int $mod_fcgid_maxrequests\n\t *            optional number of fcgid-maxrequests if FCGID is used, default is -1\n\t * @param bool $ssl_redirect\n\t *            optional, whether to generate a https-redirect or not, default false; requires SSL to be enabled\n\t * @param bool $letsencrypt\n\t *            optional, whether to generate a Let's Encrypt certificate for this domain, default false; requires\n\t *            SSL to be enabled\n\t * @param array $ssl_ipandport\n\t *            optional, list of ssl-enabled ip/port id's to assign to this domain, default empty\n\t * @param bool $dont_use_default_ssl_ipandport_if_empty\n\t *            optional, do NOT set the systems default ssl ip addresses if none are given via $ssl_ipandport\n\t *            parameter\n\t * @param bool $sslenabled\n\t *            optional, whether SSL is enabled for this domain, regardless of the assigned ssl-ips, default\n\t *            1 (true)\n\t * @param bool $http2\n\t *            optional, whether to enable http/2 for this domain (requires to be enabled in the settings), default\n\t *            0 (false)\n\t * @param bool $http3\n\t *            optional, whether to enable http/3 for this domain (requires to be enabled in the settings), default\n\t *            0 (false)\n\t * @param int $hsts_maxage\n\t *            optional max-age value for HSTS header\n\t * @param bool $hsts_sub\n\t *            optional whether to add subdomains to the HSTS header\n\t * @param bool $hsts_preload\n\t *            optional whether to preload HSTS header value\n\t * @param bool $ocsp_stapling\n\t *            optional whether to enable ocsp-stapling for this domain. default 0 (false), requires SSL\n\t * @param bool $honorcipherorder\n\t *            optional whether to honor the (server) cipher order for this domain. default 0 (false), requires SSL\n\t * @param bool $sessiontickets\n\t *            optional whether to enable or disable TLS sessiontickets (RFC 5077) for this domain. default 1\n\t *            (true), requires SSL\n\t * @param bool $override_tls\n\t *            optional whether to override system-tls settings like protocol, ssl-ciphers and if applicable\n\t *            tls-1.3 ciphers, requires change_serversettings flag for the admin, default false\n\t * @param array $ssl_protocols\n\t *            optional list of allowed/used ssl/tls protocols, see system.ssl_protocols setting, only used/required\n\t *            if $override_tls is true, default empty or system.ssl_protocols setting if $override_tls is true\n\t * @param string $ssl_cipher_list\n\t *            optional list of allowed/used ssl/tls ciphers, see system.ssl_cipher_list setting, only used/required\n\t *            if $override_tls is true, default empty or system.ssl_cipher_list setting if $override_tls is true\n\t * @param string $tlsv13_cipher_list\n\t *            optional list of allowed/used tls-1.3 specific ciphers, see system.tlsv13_cipher_list setting, only\n\t *            used/required if $override_tls is true, default empty or system.tlsv13_cipher_list setting if\n\t *            $override_tls is true\n\t * @param string $description\n\t *            optional custom description (currently not used/shown in the frontend), default empty\n\t * @param bool $is_stdsubdomain\n\t *            (internally) optional whether this is a standard subdomain for a customer which is being added so no usage is decreased\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$is_stdsubdomain = $this->isInternal() ? $this->getBoolParam('is_stdsubdomain', true, 0) : false;\n\t\t\tif ($is_stdsubdomain || $this->getUserDetail('domains_used') < $this->getUserDetail('domains') || $this->getUserDetail('domains') == '-1') {\n\t\t\t\t// parameters\n\t\t\t\t$p_domain = $this->getParam('domain');\n\n\t\t\t\t// optional parameters\n\t\t\t\t$p_ipandports = $this->getParam('ipandport', true, explode(',', Settings::Get('system.defaultip')));\n\t\t\t\t$adminid = intval($this->getParam('adminid', true, $this->getUserDetail('adminid')));\n\t\t\t\t$subcanemaildomain = $this->getParam('subcanemaildomain', true, 0);\n\t\t\t\t$isemaildomain = $this->getBoolParam('isemaildomain', true, 0);\n\t\t\t\t$email_only = $this->getBoolParam('email_only', true, 0);\n\t\t\t\t$serveraliasoption = $this->getParam('selectserveralias', true, Settings::Get('system.domaindefaultalias'));\n\t\t\t\t$speciallogfile = $this->getBoolParam('speciallogfile', true, 0);\n\t\t\t\t$aliasdomain = intval($this->getParam('alias', true, 0));\n\t\t\t\t$registration_date = $this->getParam('registration_date', true, '');\n\t\t\t\t$termination_date = $this->getParam('termination_date', true, '');\n\t\t\t\t$caneditdomain = $this->getBoolParam('caneditdomain', true, 0);\n\t\t\t\t$isbinddomain = $this->getBoolParam('isbinddomain', true, 0);\n\t\t\t\t$zonefile = $this->getParam('zonefile', true, '');\n\t\t\t\t$dkim = $this->getBoolParam('dkim', true, 0);\n\t\t\t\t$specialsettings = $this->getParam('specialsettings', true, '');\n\t\t\t\t$ssl_specialsettings = $this->getParam('ssl_specialsettings', true, '');\n\t\t\t\t$include_specialsettings = $this->getBoolParam('include_specialsettings', true, 0);\n\t\t\t\t$notryfiles = $this->getBoolParam('notryfiles', true, 0);\n\t\t\t\t$writeaccesslog = $this->getBoolParam('writeaccesslog', true, 1);\n\t\t\t\t$writeerrorlog = $this->getBoolParam('writeerrorlog', true, 1);\n\t\t\t\t$documentroot = $this->getParam('documentroot', true, '');\n\t\t\t\t$phpenabled = $this->getBoolParam('phpenabled', true, 0);\n\t\t\t\t$openbasedir = $this->getBoolParam('openbasedir', true, 0);\n\t\t\t\t$openbasedir_path = $this->getParam('openbasedir_path', true, 0);\n\t\t\t\t$phpsettingid = $this->getParam('phpsettingid', true, 1);\n\t\t\t\t$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, -1);\n\t\t\t\t$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, -1);\n\t\t\t\t$ssl_redirect = $this->getBoolParam('ssl_redirect', true, 0);\n\t\t\t\t$letsencrypt = $this->getBoolParam('letsencrypt', true, 0);\n\t\t\t\t$sslenabled = $this->getBoolParam('sslenabled', true, 1);\n\t\t\t\t$dont_use_default_ssl_ipandport_if_empty = $this->getBoolParam('dont_use_default_ssl_ipandport_if_empty', true, 0);\n\t\t\t\t$p_ssl_ipandports = $this->getParam('ssl_ipandport', true, $dont_use_default_ssl_ipandport_if_empty ? [] : explode(',', Settings::Get('system.defaultsslip')));\n\t\t\t\t$http2 = $this->getBoolParam('http2', true, 0);\n\t\t\t\t$http3 = $this->getBoolParam('http3', true, 0);\n\t\t\t\t$hsts_maxage = $this->getParam('hsts_maxage', true, 0);\n\t\t\t\t$hsts_sub = $this->getBoolParam('hsts_sub', true, 0);\n\t\t\t\t$hsts_preload = $this->getBoolParam('hsts_preload', true, 0);\n\t\t\t\t$ocsp_stapling = $this->getBoolParam('ocsp_stapling', true, 0);\n\t\t\t\t$honorcipherorder = $this->getBoolParam('honorcipherorder', true, 0);\n\t\t\t\t$sessiontickets = $this->getBoolParam('sessiontickets', true, 1);\n\n\t\t\t\t$override_tls = $this->getBoolParam('override_tls', true, 0);\n\t\t\t\t$p_ssl_protocols = [];\n\t\t\t\t$ssl_cipher_list = \"\";\n\t\t\t\t$tlsv13_cipher_list = \"\";\n\n\t\t\t\tif ($this->getUserDetail('change_serversettings') == '1') {\n\t\t\t\t\tif ($override_tls) {\n\t\t\t\t\t\t$p_ssl_protocols = $this->getParam('ssl_protocols', true, explode(',', Settings::Get('system.ssl_protocols')));\n\t\t\t\t\t\t$ssl_cipher_list = $this->getParam('ssl_cipher_list', true, Settings::Get('system.ssl_cipher_list'));\n\t\t\t\t\t\t$tlsv13_cipher_list = $this->getParam('tlsv13_cipher_list', true, Settings::Get('system.tlsv13_cipher_list'));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$description = $this->getParam('description', true, '');\n\n\t\t\t\t// validation\n\t\t\t\t$p_domain = strtolower($p_domain);\n\t\t\t\tif ($p_domain == strtolower(Settings::Get('system.hostname'))) {\n\t\t\t\t\tResponse::standardError('admin_domain_emailsystemhostname', '', true);\n\t\t\t\t}\n\n\t\t\t\tif (substr($p_domain, 0, 4) == 'xn--') {\n\t\t\t\t\tResponse::standardError('domain_nopunycode', '', true);\n\t\t\t\t} elseif (Validate::validate_ip2($p_domain, true, '', true, true)) {\n\t\t\t\t\tResponse::standardError('domain_noipaddress', '', true);\n\t\t\t\t}\n\n\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\t$domain = $idna_convert->encode(preg_replace([\n\t\t\t\t\t'/\\:(\\d)+$/',\n\t\t\t\t\t'/^https?\\:\\/\\//'\n\t\t\t\t], '', Validate::validate($p_domain, 'domain')));\n\n\t\t\t\t// Check whether domain validation is enabled and if, validate the domain\n\t\t\t\tif (Settings::Get('system.validate_domain') && !Validate::validateDomain($domain)) {\n\t\t\t\t\tResponse::standardError([\n\t\t\t\t\t\t'stringiswrong',\n\t\t\t\t\t\t'mydomain'\n\t\t\t\t\t], '', true);\n\t\t\t\t}\n\n\t\t\t\t$customer = $this->getCustomerData();\n\t\t\t\t$customerid = $customer['customerid'];\n\n\t\t\t\tif ($this->getUserDetail('customers_see_all') == '1' && $adminid != $this->getUserDetail('adminid')) {\n\t\t\t\t\t$admin_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\t\t\tWHERE `adminid` = :adminid AND (`domains_used` < `domains` OR `domains` = '-1')\");\n\t\t\t\t\t$admin = Database::pexecute_first($admin_stmt, [\n\t\t\t\t\t\t'adminid' => $adminid\n\t\t\t\t\t], true, true);\n\t\t\t\t\tif (empty($admin)) {\n\t\t\t\t\t\tResponse::dynamicError(\"Selected admin cannot have any more domains or could not be found\");\n\t\t\t\t\t}\n\t\t\t\t\tunset($admin);\n\t\t\t\t} else {\n\t\t\t\t\t// Force adminid to the caller's own ID when they don't have customers_see_all\n\t\t\t\t\t$adminid = intval($this->getUserDetail('adminid'));\n\t\t\t\t}\n\n\t\t\t\t// set default path if admin/reseller has \"change_serversettings == false\" but we still\n\t\t\t\t// need to respect the documentroot_use_default_value - setting\n\t\t\t\t$path_suffix = '';\n\t\t\t\tif (Settings::Get('system.documentroot_use_default_value') == 1) {\n\t\t\t\t\t$path_suffix = '/' . $domain;\n\t\t\t\t}\n\t\t\t\t$_documentroot = FileDir::makeCorrectDir($customer['documentroot'] . $path_suffix);\n\n\t\t\t\t$documentroot = Validate::validate($documentroot, 'documentroot', Validate::REGEX_DIR, '', [], true);\n\n\t\t\t\t// If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings,\n\t\t\t\t// set default path to subdomain or domain name\n\t\t\t\tif (!empty($documentroot)) {\n\t\t\t\t\tif (substr($documentroot, 0, 1) != '/' && !preg_match('/^https?\\:\\/\\//', $documentroot)) {\n\t\t\t\t\t\t$documentroot = $_documentroot . '/' . $documentroot;\n\t\t\t\t\t} elseif (substr($documentroot, 0, 1) == '/' && $this->getUserDetail('change_serversettings') != '1') {\n\t\t\t\t\t\tResponse::standardError('pathmustberelative', '', true);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$documentroot = $_documentroot;\n\t\t\t\t}\n\n\t\t\t\tif (!is_null($registration_date)) {\n\t\t\t\t\t$registration_date = Validate::validate($registration_date, 'registration_date',\n\t\t\t\t\t\tValidate::REGEX_YYYY_MM_DD, '', [\n\t\t\t\t\t\t\t'0000-00-00',\n\t\t\t\t\t\t\t'0',\n\t\t\t\t\t\t\t''\n\t\t\t\t\t\t], true);\n\t\t\t\t}\n\t\t\t\tif ($registration_date == '0000-00-00' || empty($registration_date)) {\n\t\t\t\t\t$registration_date = null;\n\t\t\t\t}\n\t\t\t\tif (!is_null($termination_date)) {\n\t\t\t\t\t$termination_date = Validate::validate($termination_date, 'termination_date',\n\t\t\t\t\t\tValidate::REGEX_YYYY_MM_DD, '', [\n\t\t\t\t\t\t\t'0000-00-00',\n\t\t\t\t\t\t\t'0',\n\t\t\t\t\t\t\t''\n\t\t\t\t\t\t], true);\n\t\t\t\t}\n\t\t\t\tif ($termination_date == '0000-00-00' || empty($termination_date)) {\n\t\t\t\t\t$termination_date = null;\n\t\t\t\t}\n\n\t\t\t\tif ($this->getUserDetail('change_serversettings') == '1') {\n\t\t\t\t\tif (Settings::Get('system.bind_enable') == '1') {\n\t\t\t\t\t\t$zonefile = Validate::validate($zonefile, 'zonefile', '', '', [], true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$isbinddomain = 0;\n\t\t\t\t\t\t$zonefile = '';\n\t\t\t\t\t}\n\n\t\t\t\t\t$specialsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $specialsettings), 'specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);\n\n\t\t\t\t\t$ssl_protocols = [];\n\t\t\t\t\tif (!empty($p_ssl_protocols) && is_numeric($p_ssl_protocols)) {\n\t\t\t\t\t\t$p_ssl_protocols = [\n\t\t\t\t\t\t\t$p_ssl_protocols\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t\tif (!empty($p_ssl_protocols) && !is_array($p_ssl_protocols)) {\n\t\t\t\t\t\t$p_ssl_protocols = json_decode($p_ssl_protocols, true);\n\t\t\t\t\t}\n\t\t\t\t\tif (!empty($p_ssl_protocols) && is_array($p_ssl_protocols)) {\n\t\t\t\t\t\t$protocols_available = [\n\t\t\t\t\t\t\t'TLSv1',\n\t\t\t\t\t\t\t'TLSv1.1',\n\t\t\t\t\t\t\t'TLSv1.2',\n\t\t\t\t\t\t\t'TLSv1.3'\n\t\t\t\t\t\t];\n\t\t\t\t\t\tforeach ($p_ssl_protocols as $ssl_protocol) {\n\t\t\t\t\t\t\tif (!in_array(trim($ssl_protocol), $protocols_available)) {\n\t\t\t\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_DEBUG, \"[API] unknown SSL protocol '\" . trim($ssl_protocol) . \"'\");\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$ssl_protocols[] = $ssl_protocol;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (empty($ssl_protocols)) {\n\t\t\t\t\t\t$override_tls = '0';\n\t\t\t\t\t}\n\n\t\t\t\t\t// http/3 for nginx only works with TLSv1.3 enabled\n\t\t\t\t\tif ($http3 == '1') {\n\t\t\t\t\t\t// overwrite enabled?\n\t\t\t\t\t\tif (Settings::Get('system.webserver') != 'nginx') {\n\t\t\t\t\t\t\t$http3 = '0';\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (($override_tls == '1' && !in_array('TLSv1.3', $ssl_protocols)) ||\n\t\t\t\t\t\t\t\t($override_tls == '0' && !in_array('TLSv1.3', explode(\",\", Settings::Get('system.ssl_protocols'))))\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// no tlsv1.3 -> no http/3\n\t\t\t\t\t\t\t\tResponse::standardError('tls13requiredforhttp3', '', true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$isbinddomain = '0';\n\t\t\t\t\tif (Settings::Get('system.bind_enable') == '1') {\n\t\t\t\t\t\t$isbinddomain = '1';\n\t\t\t\t\t}\n\t\t\t\t\t$caneditdomain = '1';\n\t\t\t\t\t$zonefile = '';\n\t\t\t\t\t$specialsettings = '';\n\t\t\t\t\t$ssl_specialsettings = '';\n\t\t\t\t\t$include_specialsettings = 0;\n\t\t\t\t\t$notryfiles = '0';\n\t\t\t\t\t$writeaccesslog = '1';\n\t\t\t\t\t$writeerrorlog = '1';\n\t\t\t\t\t$override_tls = '0';\n\t\t\t\t\t$ssl_protocols = [];\n\t\t\t\t}\n\n\t\t\t\tif ($this->getUserDetail('caneditphpsettings') == '1' || $this->getUserDetail('change_serversettings') == '1') {\n\t\t\t\t\tif ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\t$phpsettingid_check_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_PHPCONFIGS . \"`\n\t\t\t\t\t\t\tWHERE `id` = :phpsettingid\");\n\t\t\t\t\t\t$phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, [\n\t\t\t\t\t\t\t'phpsettingid' => $phpsettingid\n\t\t\t\t\t\t], true, true);\n\n\t\t\t\t\t\tif (!isset($phpsettingid_check['id']) || $phpsettingid_check['id'] == '0' || $phpsettingid_check['id'] != $phpsettingid) {\n\t\t\t\t\t\t\tResponse::standardError('phpsettingidwrong', '', true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((int)Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t\t\t\t\t$mod_fcgid_starter = Validate::validate($mod_fcgid_starter, 'mod_fcgid_starter', '/^[0-9]*$/', '', [\n\t\t\t\t\t\t\t\t'-1',\n\t\t\t\t\t\t\t\t''\n\t\t\t\t\t\t\t], true);\n\t\t\t\t\t\t\t$mod_fcgid_maxrequests = Validate::validate($mod_fcgid_maxrequests, 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', [\n\t\t\t\t\t\t\t\t'-1',\n\t\t\t\t\t\t\t\t''\n\t\t\t\t\t\t\t], true);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$mod_fcgid_starter = '-1';\n\t\t\t\t\t\t\t$mod_fcgid_maxrequests = '-1';\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\t\t$phpsettingid = Settings::Get('phpfpm.defaultini');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$phpsettingid = Settings::Get('system.mod_fcgid_defaultini');\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$mod_fcgid_starter = '-1';\n\t\t\t\t\t\t$mod_fcgid_maxrequests = '-1';\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// set default to whether the customer has php enabled or not\n\t\t\t\t\t$phpenabled = $customer['phpenabled'];\n\t\t\t\t\t$openbasedir = '1';\n\n\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t\t$phpsettingid = Settings::Get('phpfpm.defaultini');\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$phpsettingid = Settings::Get('system.mod_fcgid_defaultini');\n\t\t\t\t\t}\n\t\t\t\t\t$mod_fcgid_starter = '-1';\n\t\t\t\t\t$mod_fcgid_maxrequests = '-1';\n\t\t\t\t}\n\n\t\t\t\tif ($openbasedir_path > 2 && $openbasedir_path < 0) {\n\t\t\t\t\t$openbasedir_path = 0;\n\t\t\t\t}\n\n\t\t\t\t// check non-ssl IP\n\t\t\t\t$ipandports = $this->validateIpAddresses($p_ipandports);\n\t\t\t\t// check ssl IP\n\t\t\t\t$ssl_ipandports = [];\n\t\t\t\tif (Settings::Get('system.use_ssl') == \"1\" && !empty($p_ssl_ipandports)) {\n\t\t\t\t\t$ssl_ipandports = $this->validateIpAddresses($p_ssl_ipandports, true);\n\n\t\t\t\t\tif ($this->getUserDetail('change_serversettings') == '1') {\n\t\t\t\t\t\t$ssl_specialsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $ssl_specialsettings), 'ssl_specialsettings', '/^[^\\0]*$/', '', [], true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Settings::Get('system.use_ssl') == \"1\" && $sslenabled == 1 && empty($ssl_ipandports)) {\n\t\t\t\t\t// if this is a customer standard-subdomain, we simply ignore this and disable ssl-related settings (see if-statement below)\n\t\t\t\t\tif (!$is_stdsubdomain) {\n\t\t\t\t\t\t// enabled ssl for the domain but no ssl ip/port is selected\n\t\t\t\t\t\tResponse::standardError('nosslippportgiven', '', true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (Settings::Get('system.use_ssl') == \"0\" || empty($ssl_ipandports)) {\n\t\t\t\t\t$ssl_redirect = 0;\n\t\t\t\t\t$letsencrypt = 0;\n\t\t\t\t\t$http2 = 0;\n\t\t\t\t\t$http3 = 0;\n\t\t\t\t\t// we need this for the json_encode\n\t\t\t\t\t// if ssl is disabled or no ssl-ip/port exists\n\t\t\t\t\t$ssl_ipandports[] = -1;\n\n\t\t\t\t\t// HSTS\n\t\t\t\t\t$hsts_maxage = 0;\n\t\t\t\t\t$hsts_sub = 0;\n\t\t\t\t\t$hsts_preload = 0;\n\n\t\t\t\t\t// OCSP stapling\n\t\t\t\t\t$ocsp_stapling = 0;\n\n\t\t\t\t\t// vhost container settings\n\t\t\t\t\t$ssl_specialsettings = '';\n\t\t\t\t\t$include_specialsettings = 0;\n\t\t\t\t}\n\n\t\t\t\t// validate dns if lets encrypt is enabled to check whether we can use it at all\n\t\t\t\tif ($letsencrypt == '1' && Settings::Get('system.le_domain_dnscheck') == '1') {\n\t\t\t\t\t$domain_ips = PhpHelper::gethostbynamel6($domain, true, Settings::Get('system.le_domain_dnscheck_resolver'));\n\t\t\t\t\t$selected_ips = $this->getIpsFromIdArray($ssl_ipandports);\n\t\t\t\t\tif ($domain_ips == false || count(array_intersect($selected_ips, $domain_ips)) <= 0) {\n\t\t\t\t\t\tResponse::standardError('invaliddnsforletsencrypt', '', true);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// We can't enable let's encrypt for wildcard-domains\n\t\t\t\tif ($serveraliasoption == '0' && $letsencrypt == '1') {\n\t\t\t\t\tResponse::standardError('nowildcardwithletsencrypt', '', true);\n\t\t\t\t}\n\n\t\t\t\t// Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated\n\t\t\t\tif ($ssl_redirect > 0 && $letsencrypt == 1) {\n\t\t\t\t\t$ssl_redirect = 2;\n\t\t\t\t}\n\n\t\t\t\t// Check if given documentroot is either a valid URL or a valid path\n\t\t\t\tif (preg_match('/^https?\\:\\/\\//', $documentroot)) {\n\t\t\t\t\t$encoded = $idna_convert->encode($documentroot);\n\t\t\t\t\tif (!Validate::validateUrl($encoded, true)) {\n\t\t\t\t\t\tResponse::standardError('invaliddocumentrooturl', '', true);\n\t\t\t\t\t}\n\t\t\t\t\t$documentroot = $encoded;\n\t\t\t\t} else {\n\t\t\t\t\tif (strpos($documentroot, ':') !== false) {\n\t\t\t\t\t\tResponse::standardError('pathmaynotcontaincolon', '', true);\n\t\t\t\t\t}\n\t\t\t\t\t$documentroot = FileDir::makeCorrectDir($documentroot);\n\t\t\t\t}\n\n\t\t\t\t$domain_check_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `id`, `domain` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `domain` = :domain\");\n\t\t\t\t$domain_check = Database::pexecute_first($domain_check_stmt, [\n\t\t\t\t\t'domain' => strtolower($domain)\n\t\t\t\t], true, true);\n\t\t\t\t$aliasdomain_check = [\n\t\t\t\t\t'id' => 0\n\t\t\t\t];\n\n\t\t\t\tif ($aliasdomain != 0) {\n\t\t\t\t\t// Overwrite given ipandports with these of the \"main\" domain\n\t\t\t\t\t$ipandports = [];\n\t\t\t\t\t$ssl_ipandports = [];\n\t\t\t\t\t$origipresult_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `id_ipandports` FROM `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\t\t\tWHERE `id_domain` = :id\");\n\t\t\t\t\tDatabase::pexecute($origipresult_stmt, [\n\t\t\t\t\t\t'id' => $aliasdomain\n\t\t\t\t\t], true, true);\n\t\t\t\t\t$ipdata_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `id` = :ipid\");\n\t\t\t\t\twhile ($origip = $origipresult_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\t$_origip_tmp = Database::pexecute_first($ipdata_stmt, [\n\t\t\t\t\t\t\t'ipid' => $origip['id_ipandports']\n\t\t\t\t\t\t], true, true);\n\t\t\t\t\t\tif ($_origip_tmp['ssl'] == 0) {\n\t\t\t\t\t\t\t$ipandports[] = $origip['id_ipandports'];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$ssl_ipandports[] = $origip['id_ipandports'];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (count($ssl_ipandports) == 0) {\n\t\t\t\t\t\t// we need this for the json_encode\n\t\t\t\t\t\t// if ssl is disabled or no ssl-ip/port exists\n\t\t\t\t\t\t$ssl_ipandports[] = -1;\n\t\t\t\t\t}\n\n\t\t\t\t\t$aliasdomain_check_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `d`.`id` FROM `\" . TABLE_PANEL_DOMAINS . \"` `d`, `\" . TABLE_PANEL_CUSTOMERS . \"` `c`\n\t\t\t\t\t\tWHERE `d`.`customerid` = :customerid\n\t\t\t\t\t\tAND `d`.`aliasdomain` IS NULL AND `d`.`id` <> `c`.`standardsubdomain`\n\t\t\t\t\t\tAND `c`.`customerid` = :customerid\n\t\t\t\t\t\tAND `d`.`id` = :aliasdomainid\");\n\t\t\t\t\t$alias_params = [\n\t\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t\t'aliasdomainid' => $aliasdomain\n\t\t\t\t\t];\n\t\t\t\t\t$aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, $alias_params, true, true);\n\t\t\t\t}\n\n\t\t\t\tif (count($ipandports) == 0) {\n\t\t\t\t\tResponse::standardError('noipportgiven', '', true);\n\t\t\t\t}\n\n\t\t\t\tif ($email_only == '1') {\n\t\t\t\t\t$isemaildomain = '1';\n\t\t\t\t} else {\n\t\t\t\t\t$email_only = '0';\n\t\t\t\t}\n\n\t\t\t\tif ($subcanemaildomain != '1' && $subcanemaildomain != '2' && $subcanemaildomain != '3') {\n\t\t\t\t\t$subcanemaildomain = '0';\n\t\t\t\t}\n\n\t\t\t\tif ($serveraliasoption != '1' && $serveraliasoption != '2') {\n\t\t\t\t\t$serveraliasoption = '0';\n\t\t\t\t}\n\n\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\tif ($domain == '') {\n\t\t\t\t\tResponse::standardError([\n\t\t\t\t\t\t'stringisempty',\n\t\t\t\t\t\t'mydomain'\n\t\t\t\t\t], '', true);\n\t\t\t\t} elseif ($documentroot == '') {\n\t\t\t\t\tResponse::standardError([\n\t\t\t\t\t\t'stringisempty',\n\t\t\t\t\t\t'mydocumentroot'\n\t\t\t\t\t], '', true);\n\t\t\t\t} elseif ($customerid == 0) {\n\t\t\t\t\tResponse::standardError('adduserfirst', '', true);\n\t\t\t\t} elseif ($domain_check && strtolower($domain_check['domain']) == strtolower($domain)) {\n\t\t\t\t\tResponse::standardError('domainalreadyexists', $idna_convert->decode($domain), true);\n\t\t\t\t} elseif ($aliasdomain_check && $aliasdomain_check['id'] != $aliasdomain) {\n\t\t\t\t\tResponse::standardError('domainisaliasorothercustomer', '', true);\n\t\t\t\t} else {\n\t\t\t\t\t$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';\n\t\t\t\t\t$iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0';\n\n\t\t\t\t\t$ins_data = [\n\t\t\t\t\t\t'domain' => $domain,\n\t\t\t\t\t\t'domain_ace' => $idna_convert->decode($domain),\n\t\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t\t'adminid' => $adminid,\n\t\t\t\t\t\t'documentroot' => $documentroot,\n\t\t\t\t\t\t'aliasdomain' => ($aliasdomain != 0 ? $aliasdomain : null),\n\t\t\t\t\t\t'zonefile' => $zonefile,\n\t\t\t\t\t\t'dkim' => $dkim,\n\t\t\t\t\t\t'wwwserveralias' => $wwwserveralias,\n\t\t\t\t\t\t'iswildcarddomain' => $iswildcarddomain,\n\t\t\t\t\t\t'isbinddomain' => $isbinddomain,\n\t\t\t\t\t\t'isemaildomain' => $isemaildomain,\n\t\t\t\t\t\t'email_only' => $email_only,\n\t\t\t\t\t\t'subcanemaildomain' => $subcanemaildomain,\n\t\t\t\t\t\t'caneditdomain' => $caneditdomain,\n\t\t\t\t\t\t'phpenabled' => $phpenabled,\n\t\t\t\t\t\t'openbasedir' => $openbasedir,\n\t\t\t\t\t\t'openbasedir_path' => $openbasedir_path,\n\t\t\t\t\t\t'speciallogfile' => $speciallogfile,\n\t\t\t\t\t\t'specialsettings' => $specialsettings,\n\t\t\t\t\t\t'ssl_specialsettings' => $ssl_specialsettings,\n\t\t\t\t\t\t'include_specialsettings' => $include_specialsettings,\n\t\t\t\t\t\t'notryfiles' => $notryfiles,\n\t\t\t\t\t\t'writeaccesslog' => $writeaccesslog,\n\t\t\t\t\t\t'writeerrorlog' => $writeerrorlog,\n\t\t\t\t\t\t'ssl_redirect' => $ssl_redirect,\n\t\t\t\t\t\t'add_date' => time(),\n\t\t\t\t\t\t'registration_date' => $registration_date,\n\t\t\t\t\t\t'termination_date' => $termination_date,\n\t\t\t\t\t\t'phpsettingid' => $phpsettingid,\n\t\t\t\t\t\t'mod_fcgid_starter' => $mod_fcgid_starter,\n\t\t\t\t\t\t'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests,\n\t\t\t\t\t\t'letsencrypt' => $letsencrypt,\n\t\t\t\t\t\t'http2' => $http2,\n\t\t\t\t\t\t'http3' => $http3,\n\t\t\t\t\t\t'hsts' => $hsts_maxage,\n\t\t\t\t\t\t'hsts_sub' => $hsts_sub,\n\t\t\t\t\t\t'hsts_preload' => $hsts_preload,\n\t\t\t\t\t\t'ocsp_stapling' => $ocsp_stapling,\n\t\t\t\t\t\t'override_tls' => $override_tls,\n\t\t\t\t\t\t'ssl_protocols' => implode(\",\", $ssl_protocols),\n\t\t\t\t\t\t'ssl_cipher_list' => $ssl_cipher_list,\n\t\t\t\t\t\t'tlsv13_cipher_list' => $tlsv13_cipher_list,\n\t\t\t\t\t\t'sslenabled' => $sslenabled,\n\t\t\t\t\t\t'honorcipherorder' => $honorcipherorder,\n\t\t\t\t\t\t'sessiontickets' => $sessiontickets,\n\t\t\t\t\t\t'description' => $description\n\t\t\t\t\t];\n\n\t\t\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\t\tINSERT INTO `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t\t\t`domain` = :domain,\n\t\t\t\t\t\t`domain_ace` = :domain_ace,\n\t\t\t\t\t\t`customerid` = :customerid,\n\t\t\t\t\t\t`adminid` = :adminid,\n\t\t\t\t\t\t`documentroot` = :documentroot,\n\t\t\t\t\t\t`aliasdomain` = :aliasdomain,\n\t\t\t\t\t\t`zonefile` = :zonefile,\n\t\t\t\t\t\t`dkim` = :dkim,\n\t\t\t\t\t\t`dkim_id` = '0',\n\t\t\t\t\t\t`dkim_privkey` = '',\n\t\t\t\t\t\t`dkim_pubkey` = '',\n\t\t\t\t\t\t`wwwserveralias` = :wwwserveralias,\n\t\t\t\t\t\t`iswildcarddomain` = :iswildcarddomain,\n\t\t\t\t\t\t`isbinddomain` = :isbinddomain,\n\t\t\t\t\t\t`isemaildomain` = :isemaildomain,\n\t\t\t\t\t\t`email_only` = :email_only,\n\t\t\t\t\t\t`subcanemaildomain` = :subcanemaildomain,\n\t\t\t\t\t\t`caneditdomain` = :caneditdomain,\n\t\t\t\t\t\t`phpenabled` = :phpenabled,\n\t\t\t\t\t\t`openbasedir` = :openbasedir,\n\t\t\t\t\t\t`openbasedir_path` = :openbasedir_path,\n\t\t\t\t\t\t`speciallogfile` = :speciallogfile,\n\t\t\t\t\t\t`specialsettings` = :specialsettings,\n\t\t\t\t\t\t`ssl_specialsettings` = :ssl_specialsettings,\n\t\t\t\t\t\t`include_specialsettings` = :include_specialsettings,\n\t\t\t\t\t\t`notryfiles` = :notryfiles,\n\t\t\t\t\t\t`writeaccesslog` = :writeaccesslog,\n\t\t\t\t\t\t`writeerrorlog` = :writeerrorlog,\n\t\t\t\t\t\t`ssl_redirect` = :ssl_redirect,\n\t\t\t\t\t\t`add_date` = :add_date,\n\t\t\t\t\t\t`registration_date` = :registration_date,\n\t\t\t\t\t\t`termination_date` = :termination_date,\n\t\t\t\t\t\t`phpsettingid` = :phpsettingid,\n\t\t\t\t\t\t`mod_fcgid_starter` = :mod_fcgid_starter,\n\t\t\t\t\t\t`mod_fcgid_maxrequests` = :mod_fcgid_maxrequests,\n\t\t\t\t\t\t`letsencrypt` = :letsencrypt,\n\t\t\t\t\t\t`http2` = :http2,\n\t\t\t\t\t\t`http3` = :http3,\n\t\t\t\t\t\t`hsts` = :hsts,\n\t\t\t\t\t\t`hsts_sub` = :hsts_sub,\n\t\t\t\t\t\t`hsts_preload` = :hsts_preload,\n\t\t\t\t\t\t`ocsp_stapling` = :ocsp_stapling,\n\t\t\t\t\t\t`override_tls` = :override_tls,\n\t\t\t\t\t\t`ssl_protocols` = :ssl_protocols,\n\t\t\t\t\t\t`ssl_cipher_list` = :ssl_cipher_list,\n\t\t\t\t\t\t`tlsv13_cipher_list` = :tlsv13_cipher_list,\n\t\t\t\t\t\t`ssl_enabled` = :sslenabled,\n\t\t\t\t\t\t`ssl_honorcipherorder` = :honorcipherorder,\n\t\t\t\t\t\t`ssl_sessiontickets` = :sessiontickets,\n\t\t\t\t\t\t`description` = :description\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($ins_stmt, $ins_data, true, true);\n\t\t\t\t\t$domainid = Database::lastInsertId();\n\t\t\t\t\t$ins_data['id'] = $domainid;\n\t\t\t\t\tunset($ins_data);\n\n\t\t\t\t\tif (!$is_stdsubdomain) {\n\t\t\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `domains_used` = `domains_used` + 1\n\t\t\t\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\t\t\t\");\n\t\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t\t'adminid' => $adminid\n\t\t\t\t\t\t], true, true);\n\t\t\t\t\t}\n\n\t\t\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\t\tINSERT INTO `\" . TABLE_DOMAINTOIP . \"` SET\n\t\t\t\t\t\t`id_domain` = :domainid,\n\t\t\t\t\t\t`id_ipandports` = :ipandportsid\n\t\t\t\t\t\");\n\n\t\t\t\t\tforeach ($ipandports as $ipportid) {\n\t\t\t\t\t\t$ins_data = [\n\t\t\t\t\t\t\t'domainid' => $domainid,\n\t\t\t\t\t\t\t'ipandportsid' => $ipportid\n\t\t\t\t\t\t];\n\t\t\t\t\t\tDatabase::pexecute($ins_stmt, $ins_data, true, true);\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach ($ssl_ipandports as $ssl_ipportid) {\n\t\t\t\t\t\tif ($ssl_ipportid > 0) {\n\t\t\t\t\t\t\t$ins_data = [\n\t\t\t\t\t\t\t\t'domainid' => $domainid,\n\t\t\t\t\t\t\t\t'ipandportsid' => $ssl_ipportid\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\tDatabase::pexecute($ins_stmt, $ins_data, true, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());\n\n\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t\t\t\tif ($dkim == '1') {\n\t\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_RSPAMD);\n\t\t\t\t\t}\n\n\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] added domain '\" . $domain . \"'\");\n\n\t\t\t\t\t$result = $this->apiCall('Domains.get', [\n\t\t\t\t\t\t'domainname' => $domain\n\t\t\t\t\t]);\n\t\t\t\t\treturn $this->response($result);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Exception(\"No more resources available\", 406);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return a domain entry by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param bool $with_ips\n\t *            optional, default true\n\t * @param bool $no_std_subdomain\n\t *            optional, default false\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$dn_optional = $id > 0;\n\t\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\t\t\t$with_ips = $this->getParam('with_ips', true, true);\n\t\t\t$no_std_subdomain = $this->getParam('no_std_subdomain', true, false);\n\n\t\t\t// convert possible idn domain to punycode\n\t\t\tif (substr($domainname, 0, 4) != 'xn--') {\n\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\t$domainname = $idna_convert->encode($domainname);\n\t\t\t}\n\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `d`.*, `c`.`customerid`\n\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` `d`\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` `c` USING(`customerid`)\n\t\t\t\tWHERE `d`.`parentdomainid` = '0'\n\t\t\t\tAND \" . ($id > 0 ? \"`d`.`id` = :iddn\" : \"`d`.`domain` = :iddn\") . ($no_std_subdomain ? ' AND `d`.`id` <> `c`.`standardsubdomain`' : '') . ($this->getUserDetail('customers_see_all') ? '' : \" AND `d`.`adminid` = :adminid\"));\n\t\t\t$params = [\n\t\t\t\t'iddn' => ($id <= 0 ? $domainname : $id)\n\t\t\t];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t}\n\t\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\t\tif ($result) {\n\t\t\t\t$result['ipsandports'] = [];\n\t\t\t\tif ($with_ips) {\n\t\t\t\t\t$result['ipsandports'] = $this->getIpsForDomain($result['id']);\n\t\t\t\t}\n\t\t\t\t$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] get domain '\" . $result['domain'] . \"'\");\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t\t$key = ($id > 0 ? \"id #\" . $id : \"domainname '\" . $domainname . \"'\");\n\t\t\tthrow new Exception(\"Domain with \" . $key . \" could not be found\", 404);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * validate given ips\n\t *\n\t * @param int|string|array $p_ipandports\n\t * @param boolean $ssl\n\t *            default false\n\t * @param int $edit_id\n\t *            default 0\n\t *\n\t * @return array\n\t * @throws Exception\n\t */\n\tprivate function validateIpAddresses($p_ipandports = null, $ssl = false, $edit_id = 0)\n\t{\n\t\t// when adding a new domain and no ip is given, we try to use the\n\t\t// system-default, check here if there is none\n\t\t// this is not required for ssl-enabled ip's\n\t\tif ($edit_id <= 0 && !$ssl && empty($p_ipandports)) {\n\t\t\tthrow new Exception(\"No IPs given, unable to add domain (no default IPs set?)\", 406);\n\t\t}\n\n\t\t// convert given value(s) correctly\n\t\t$ipandports = [];\n\t\tif (!empty($p_ipandports) && is_numeric($p_ipandports)) {\n\t\t\t$p_ipandports = [\n\t\t\t\t$p_ipandports\n\t\t\t];\n\t\t}\n\t\tif (!empty($p_ipandports) && !is_array($p_ipandports)) {\n\t\t\t$p_ipandports = json_decode($p_ipandports, true);\n\t\t}\n\n\t\t// check whether there are ip usage restrictions\n\t\t$additional_ip_condition = '';\n\t\t$aip_param = [];\n\t\tif ($this->getUserDetail('ip') != \"-1\") {\n\t\t\t// handle multiple-ip-array\n\t\t\t$additional_ip_condition = \" AND `ip` IN (\" . implode(\",\", json_decode($this->getUserDetail('ip'), true)) . \") \";\n\t\t}\n\n\t\tif (!empty($p_ipandports) && is_array($p_ipandports)) {\n\t\t\t$ipandport_check_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id`, `ip`, `port`\n\t\t\t\tFROM `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\tWHERE `id` = :ipandport \" . ($ssl ? \" AND `ssl` = '1'\" : \"\") . $additional_ip_condition);\n\t\t\tforeach ($p_ipandports as $ipandport) {\n\t\t\t\tif (trim($ipandport) == \"\") {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// fix if no ip/port is checked\n\t\t\t\tif (trim($ipandport) < 1) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$ipandport = intval($ipandport);\n\t\t\t\t$ip_params = array_merge([\n\t\t\t\t\t'ipandport' => $ipandport\n\t\t\t\t], $aip_param);\n\t\t\t\t$ipandport_check = Database::pexecute_first($ipandport_check_stmt, $ip_params, true, true);\n\t\t\t\tif (!isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) {\n\t\t\t\t\tResponse::standardError('ipportdoesntexist', '', true);\n\t\t\t\t} else {\n\t\t\t\t\t$ipandports[] = $ipandport;\n\t\t\t\t}\n\t\t\t}\n\t\t} elseif ($edit_id > 0) {\n\t\t\t// set currently used ip's\n\t\t\t$ipsresult_stmt = Database::prepare(\"\n\t\t\t\tSELECT d2i.`id_ipandports`\n\t\t\t\tFROM `\" . TABLE_DOMAINTOIP . \"` d2i\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_IPSANDPORTS . \"` i ON i.id = d2i.id_ipandports\n\t\t\t\tWHERE d2i.`id_domain` = :id AND i.`ssl` = \" . ($ssl ? \"'1'\" : \"'0'\"));\n\t\t\tDatabase::pexecute($ipsresult_stmt, [\n\t\t\t\t'id' => $edit_id\n\t\t\t], true, true);\n\t\t\twhile ($ipsresultrow = $ipsresult_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$ipandports[] = $ipsresultrow['id_ipandports'];\n\t\t\t}\n\t\t}\n\t\treturn $ipandports;\n\t}\n\n\t/**\n\t * get ips from array of id's\n\t *\n\t * @param array $ips\n\t * @return array\n\t */\n\tprivate function getIpsFromIdArray(array $ids)\n\t{\n\t\t$resultips_stmt = Database::prepare(\"\n\t\t\tSELECT `ip` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE id = :id\n\t\t\");\n\t\t$result = [];\n\t\tforeach ($ids as $id) {\n\t\t\t$entry = Database::pexecute_first($resultips_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\t\t\t$result[] = $entry['ip'];\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * update domain entry by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param int $customerid\n\t *            required (if $loginname is not specified)\n\t * @param string $loginname\n\t *            required (if $customerid is not specified)\n\t * @param int $adminid\n\t *            optional, default is the calling admin's ID\n\t * @param array $ipandport\n\t *            optional list of ip/ports to assign to domain, default is system-default-ips\n\t * @param int $subcanemaildomain\n\t *            optional, allow subdomains of this domain as email domains, 1 = choosable (default no), 2 = choosable\n\t *            (default yes), 3 = always, default 0 (never)\n\t * @param bool $isemaildomain\n\t *            optional, allow email usage with this domain, default 0 (false)\n\t * @param bool $emaildomainverified\n\t *            optional, when setting $isemaildomain to false, this needs to be set to true to confirm the action in case email addresses exist for this domain,\n\t *            default 0 (false)\n\t * @param bool $email_only\n\t *            optional, restrict domain to email usage, default 0 (false)\n\t * @param int $selectserveralias\n\t *            optional, 0 = wildcard, 1 = www-alias, 2 = none, default 0\n\t * @param bool $speciallogfile\n\t *            optional, whether to create an exclusive web-logfile for this domain, default 0 (false)\n\t * @param bool $speciallogverified\n\t *            optional, when setting $speciallogfile to false, this needs to be set to true to confirm the action,\n\t *            default 0 (false)\n\t * @param int $alias\n\t *            optional, domain-id of a domain that the new domain should be an alias of, default 0 (none)\n\t * @param string $registration_date\n\t *            optional, date of domain registration in form of YYYY-MM-DD, default empty (none)\n\t * @param string $termination_date\n\t *            optional, date of domain termination in form of YYYY-MM-DD, default empty (none)\n\t * @param bool $caneditdomain\n\t *            optional, whether to allow the customer to edit domain settings, default 0 (false)\n\t * @param bool $isbinddomain\n\t *            optional, whether to generate a dns-zone or not (only of nameserver is activated), default 0 (false)\n\t * @param string $zonefile\n\t *            optional, custom dns zone filename (only of nameserver is activated), default empty (auto-generated)\n\t * @param bool $dkim\n\t *            optional, whether this domain should use dkim if antispam is activated, default 0 (false)\n\t * @param string $specialsettings\n\t *            optional, custom webserver vhost-content which is added to the generated vhost, default empty\n\t * @param string $ssl_specialsettings\n\t *            optional, custom webserver vhost-content which is added to the generated ssl-vhost, default empty\n\t * @param bool $include_specialsettings\n\t *            optional, whether to include non-ssl specialsettings in the generated ssl-vhost, default false\n\t * @param bool $specialsettingsforsubdomains\n\t *            optional, whether to apply specialsettings to all subdomains of this domain, default is read from\n\t *            setting system.apply_specialsettings_default\n\t * @param bool $notryfiles\n\t *            optional, [nginx only] do not generate the default try-files directive, default 0 (false)\n\t * @param bool $writeaccesslog\n\t *            optional, Enable writing an access-log file for this domain, default 1 (true)\n\t * @param bool $writeerrorlog\n\t *            optional, Enable writing an error-log file for this domain, default 1 (true)\n\t * @param string $documentroot\n\t *            optional, specify homedir of domain by specifying a directory (relative to customer-docroot), be\n\t *            aware, if path starts with / it is considered a full path, not relative to customer-docroot. Also\n\t *            specifying a URL is possible here (redirect), default empty (autogenerated)\n\t * @param bool $phpenabled\n\t *            optional, whether php is enabled for this domain, default 0 (false)\n\t * @param bool $phpsettingsforsubdomains\n\t *            optional, whether to apply php-setting to apply to all subdomains of this domain, default is read\n\t *            from setting system.apply_phpconfigs_default\n\t * @param bool $openbasedir\n\t *            optional, whether to activate openbasedir restriction for this domain, default 0 (false)\n\t * @param int $openbasedir_path\n\t *            optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot\n\t * @param int $phpsettingid\n\t *            optional, specify php-configuration that is being used by id, default 1 (system-default)\n\t * @param int $mod_fcgid_starter\n\t *            optional number of fcgid-starters if FCGID is used, default is -1\n\t * @param int $mod_fcgid_maxrequests\n\t *            optional number of fcgid-maxrequests if FCGID is used, default is -1\n\t * @param bool $ssl_redirect\n\t *            optional, whether to generate a https-redirect or not, default false; requires SSL to be enabled\n\t * @param bool $letsencrypt\n\t *            optional, whether to generate a Let's Encrypt certificate for this domain, default false; requires\n\t *            SSL to be enabled\n\t * @param array $ssl_ipandport\n\t *            optional, list of ssl-enabled ip/port id's to assign to this domain, if left empty, the current set\n\t *            value is being used, to remove all ssl ips use $remove_ssl_ipandport\n\t * @param bool $remove_ssl_ipandport\n\t *            optional, if set to true and no $ssl_ipandport value is given, the ip's get removed, otherwise, the\n\t *            currently set value is used, default false\n\t * @param bool $sslenabled\n\t *            optional, whether SSL is enabled for this domain, regardless of the assigned ssl-ips, default\n\t *            1 (true)\n\t * @param bool $http2\n\t *            optional, whether to enable http/2 for this domain (requires to be enabled in the settings), default\n\t *            0 (false)\n\t * @param bool $http3\n\t *            optional, whether to enable http/3 for this domain (requires to be enabled in the settings), default\n\t *            0 (false)\n\t * @param int $hsts_maxage\n\t *            optional max-age value for HSTS header\n\t * @param bool $hsts_sub\n\t *            optional whether to add subdomains to the HSTS header\n\t * @param bool $hsts_preload\n\t *            optional whether to preload HSTS header value\n\t * @param bool $ocsp_stapling\n\t *            optional whether to enable ocsp-stapling for this domain. default 0 (false), requires SSL\n\t * @param bool $honorcipherorder\n\t *            optional whether to honor the (server) cipher order for this domain. default 0 (false), requires SSL\n\t * @param bool $sessiontickets\n\t *            optional whether to enable or disable TLS sessiontickets (RFC 5077) for this domain. default 1\n\t *            (true), requires SSL\n\t * @param string $description\n\t *            optional custom description (currently not used/shown in the frontend), default empty\n\t * @param bool $deactivated\n\t *            optional, if 1 (true) the domain can be deactivated/suspended\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t// parameters\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$dn_optional = $id > 0;\n\t\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\t\t// get requested domain\n\t\t\t$result = $this->apiCall('Domains.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'domainname' => $domainname\n\t\t\t]);\n\t\t\t$id = $result['id'];\n\n\t\t\t// optional parameters\n\t\t\t$p_ipandports = $this->getParam('ipandport', true, []);\n\t\t\t$adminid = intval($this->getParam('adminid', true, $result['adminid']));\n\n\t\t\tif ($this->getParam('customerid', true, 0) == 0 && $this->getParam('loginname', true, '') == '') {\n\t\t\t\t$customerid = $result['customerid'];\n\t\t\t\t$customer = $this->apiCall('Customers.get', [\n\t\t\t\t\t'id' => $customerid\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\t$customer = $this->getCustomerData();\n\t\t\t\t$customerid = $customer['customerid'];\n\t\t\t}\n\n\t\t\t$subcanemaildomain = $this->getParam('subcanemaildomain', true, $result['subcanemaildomain']);\n\t\t\t$isemaildomain = $this->getBoolParam('isemaildomain', true, $result['isemaildomain']);\n\t\t\t$emaildomainverified = $this->getBoolParam('emaildomainverified', true, 0);\n\t\t\t$email_only = $this->getBoolParam('email_only', true, $result['email_only']);\n\t\t\t$p_serveraliasoption = $this->getParam('selectserveralias', true, -1);\n\t\t\t$speciallogfile = $this->getBoolParam('speciallogfile', true, $result['speciallogfile']);\n\t\t\t$speciallogverified = $this->getBoolParam('speciallogverified', true, 0);\n\t\t\t$aliasdomain = intval($this->getParam('alias', true, $result['aliasdomain']));\n\t\t\t$registration_date = $this->getParam('registration_date', true, $result['registration_date']);\n\t\t\t$termination_date = $this->getParam('termination_date', true, $result['termination_date']);\n\t\t\t$caneditdomain = $this->getBoolParam('caneditdomain', true, $result['caneditdomain']);\n\t\t\t$isbinddomain = $this->getBoolParam('isbinddomain', true, $result['isbinddomain']);\n\t\t\t$zonefile = $this->getParam('zonefile', true, $result['zonefile']);\n\t\t\t$dkim = $this->getBoolParam('dkim', true, $result['dkim']);\n\t\t\t$specialsettings = $this->getParam('specialsettings', true, $result['specialsettings']);\n\t\t\t$ssl_specialsettings = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']);\n\t\t\t$include_specialsettings = $this->getBoolParam('include_specialsettings', true, $result['include_specialsettings']);\n\t\t\t$ssfs = $this->getBoolParam('specialsettingsforsubdomains', true, Settings::Get('system.apply_specialsettings_default'));\n\t\t\t$notryfiles = $this->getBoolParam('notryfiles', true, $result['notryfiles']);\n\t\t\t$writeaccesslog = $this->getBoolParam('writeaccesslog', true, $result['writeaccesslog']);\n\t\t\t$writeerrorlog = $this->getBoolParam('writeerrorlog', true, $result['writeerrorlog']);\n\t\t\t$documentroot = $this->getParam('documentroot', true, $result['documentroot']);\n\t\t\t$phpenabled = $this->getBoolParam('phpenabled', true, $result['phpenabled']);\n\t\t\t$phpfs = $this->getBoolParam('phpsettingsforsubdomains', true, Settings::Get('system.apply_phpconfigs_default'));\n\t\t\t$openbasedir = $this->getBoolParam('openbasedir', true, $result['openbasedir']);\n\t\t\t$openbasedir_path = $this->getParam('openbasedir_path', true, $result['openbasedir_path']);\n\t\t\t$phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']);\n\t\t\t$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']);\n\t\t\t$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, $result['mod_fcgid_maxrequests']);\n\t\t\t$ssl_redirect = $this->getBoolParam('ssl_redirect', true, $result['ssl_redirect']);\n\t\t\t$letsencrypt = $this->getBoolParam('letsencrypt', true, $result['letsencrypt']);\n\t\t\t$remove_ssl_ipandport = $this->getBoolParam('remove_ssl_ipandport', true, 0);\n\t\t\t$p_ssl_ipandports = $this->getParam('ssl_ipandport', true, $remove_ssl_ipandport ? [\n\t\t\t\t-1\n\t\t\t] : null);\n\t\t\t$sslenabled = $remove_ssl_ipandport ? false : $this->getBoolParam('sslenabled', true, $result['ssl_enabled']);\n\t\t\t$http2 = $this->getBoolParam('http2', true, $result['http2']);\n\t\t\t$http3 = $this->getBoolParam('http3', true, $result['http3']);\n\t\t\t$hsts_maxage = $this->getParam('hsts_maxage', true, $result['hsts']);\n\t\t\t$hsts_sub = $this->getBoolParam('hsts_sub', true, $result['hsts_sub']);\n\t\t\t$hsts_preload = $this->getBoolParam('hsts_preload', true, $result['hsts_preload']);\n\t\t\t$ocsp_stapling = $this->getBoolParam('ocsp_stapling', true, $result['ocsp_stapling']);\n\t\t\t$honorcipherorder = $this->getBoolParam('honorcipherorder', true, $result['ssl_honorcipherorder']);\n\t\t\t$sessiontickets = $this->getBoolParam('sessiontickets', true, $result['ssl_sessiontickets']);\n\n\t\t\t$override_tls = $this->getBoolParam('override_tls', true, $result['override_tls']);\n\n\t\t\tif ($this->getUserDetail('change_serversettings') == '1') {\n\t\t\t\tif ($override_tls) {\n\t\t\t\t\t$p_ssl_protocols = $this->getParam('ssl_protocols', true, explode(',', $result['ssl_protocols']));\n\t\t\t\t\t$ssl_cipher_list = $this->getParam('ssl_cipher_list', true, $result['ssl_cipher_list']);\n\t\t\t\t\t$tlsv13_cipher_list = $this->getParam('tlsv13_cipher_list', true, $result['tlsv13_cipher_list']);\n\t\t\t\t} else {\n\t\t\t\t\t$p_ssl_protocols = [];\n\t\t\t\t\t$ssl_cipher_list = \"\";\n\t\t\t\t\t$tlsv13_cipher_list = \"\";\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$p_ssl_protocols = explode(',', $result['ssl_protocols']);\n\t\t\t\t$ssl_cipher_list = $result['ssl_cipher_list'];\n\t\t\t\t$tlsv13_cipher_list = $result['tlsv13_cipher_list'];\n\t\t\t}\n\t\t\t$description = $this->getParam('description', true, $result['description']);\n\t\t\t$deactivated = $this->getBoolParam('deactivated', true, $result['deactivated']);\n\n\t\t\t// count subdomain usage of source-domain\n\t\t\t$subdomains_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(`id`) AS count FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE\n\t\t\t\t`parentdomainid` = :resultid\n\t\t\t\");\n\t\t\t$subdomains = Database::pexecute_first($subdomains_stmt, [\n\t\t\t\t'resultid' => $result['id']\n\t\t\t], true, true);\n\t\t\t$subdomains = $subdomains['count'];\n\n\t\t\t// count where this domain is alias domain\n\t\t\t$alias_check_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(`id`) AS count FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE\n\t\t\t\t`aliasdomain` = :resultid\n\t\t\t\");\n\t\t\t$alias_check = Database::pexecute_first($alias_check_stmt, [\n\t\t\t\t'resultid' => $result['id']\n\t\t\t], true, true);\n\t\t\t$alias_check = $alias_check['count'];\n\n\t\t\t// count where we are used in email-accounts\n\t\t\t$domain_emails_result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `email`, `email_full`, `destination`, `popaccountid`\n\t\t\t\tFROM `\" . TABLE_MAIL_VIRTUAL . \"` WHERE `customerid` = :customerid AND `domainid` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($domain_emails_result_stmt, [\n\t\t\t\t'customerid' => $result['customerid'],\n\t\t\t\t'id' => $result['id']\n\t\t\t], true, true);\n\n\t\t\t$emails = Database::num_rows();\n\t\t\t$email_forwarders = 0;\n\t\t\t$email_accounts = 0;\n\n\t\t\twhile ($domain_emails_row = $domain_emails_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ($domain_emails_row['destination'] != '') {\n\t\t\t\t\t$domain_emails_row['destination'] = explode(' ', FileDir::makeCorrectDestination($domain_emails_row['destination']));\n\t\t\t\t\t$email_forwarders += count($domain_emails_row['destination']);\n\t\t\t\t\tif (in_array($domain_emails_row['email_full'], $domain_emails_row['destination'])) {\n\t\t\t\t\t\t$email_forwarders -= 1;\n\t\t\t\t\t\t$email_accounts++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($emails > 0 && (int)$isemaildomain == 0 && (int)$result['isemaildomain'] == 1 && (int)$emaildomainverified == 0) {\n\t\t\t\tResponse::standardError('emaildomainstillhasaddresses', '', true);\n\t\t\t}\n\n\t\t\t// handle change of customer (move domain from customer to customer)\n\t\t\tif ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') {\n\t\t\t\t// check whether target customer has enough resources\n\t\t\t\t$customer_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\tAND (`subdomains_used` + :subdomains <= `subdomains` OR `subdomains` = '-1' )\n\t\t\t\t\tAND (`emails_used` + :emails <= `emails` OR `emails` = '-1' )\n\t\t\t\t\tAND (`email_forwarders_used` + :forwarders <= `email_forwarders` OR `email_forwarders` = '-1' )\n\t\t\t\t\tAND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) \" . ($this->getUserDetail('customers_see_all') ? '' : \" AND `adminid` = :adminid\"));\n\t\t\t\t$params = [\n\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t'subdomains' => $subdomains,\n\t\t\t\t\t'emails' => $emails,\n\t\t\t\t\t'forwarders' => $email_forwarders,\n\t\t\t\t\t'accounts' => $email_accounts\n\t\t\t\t];\n\t\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t\t}\n\t\t\t\t$customer = Database::pexecute_first($customer_stmt, $params, true, true);\n\t\t\t\tif (empty($customer) || $customer['customerid'] != $customerid) {\n\t\t\t\t\tResponse::standardError('customerdoesntexist', '', true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// handle change of admin (move domain from admin to admin)\n\t\t\tif ($this->getUserDetail('customers_see_all') == '1') {\n\t\t\t\tif ($adminid > 0 && $adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') {\n\t\t\t\t\t$admin_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\t\t\tWHERE `adminid` = :adminid AND ( `domains_used` < `domains` OR `domains` = '-1' )\n\t\t\t\t\t\");\n\t\t\t\t\t$admin = Database::pexecute_first($admin_stmt, [\n\t\t\t\t\t\t'adminid' => $adminid\n\t\t\t\t\t], true, true);\n\n\t\t\t\t\tif (empty($admin) || $admin['adminid'] != $adminid) {\n\t\t\t\t\t\tResponse::standardError('admindoesntexist', '', true);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$adminid = $result['adminid'];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$adminid = $result['adminid'];\n\t\t\t}\n\n\t\t\tif (!is_null($registration_date)) {\n\t\t\t\t$registration_date = Validate::validate($registration_date, 'registration_date',\n\t\t\t\t\tValidate::REGEX_YYYY_MM_DD, '', [\n\t\t\t\t\t\t'0000-00-00',\n\t\t\t\t\t\t'0',\n\t\t\t\t\t\t''\n\t\t\t\t\t], true);\n\t\t\t}\n\t\t\tif ($registration_date == '0000-00-00' || empty($registration_date)) {\n\t\t\t\t$registration_date = null;\n\t\t\t}\n\t\t\tif (!is_null($termination_date)) {\n\t\t\t\t$termination_date = Validate::validate($termination_date, 'termination_date',\n\t\t\t\t\tValidate::REGEX_YYYY_MM_DD, '', [\n\t\t\t\t\t\t'0000-00-00',\n\t\t\t\t\t\t'0',\n\t\t\t\t\t\t''\n\t\t\t\t\t], true);\n\t\t\t}\n\t\t\tif ($termination_date == '0000-00-00' || empty($termination_date)) {\n\t\t\t\t$termination_date = null;\n\t\t\t}\n\n\t\t\t$serveraliasoption = '2';\n\t\t\tif ($result['iswildcarddomain'] == '1') {\n\t\t\t\t$serveraliasoption = '0';\n\t\t\t} elseif ($result['wwwserveralias'] == '1') {\n\t\t\t\t$serveraliasoption = '1';\n\t\t\t}\n\t\t\tif ($p_serveraliasoption > -1) {\n\t\t\t\t$serveraliasoption = $p_serveraliasoption;\n\t\t\t}\n\n\t\t\t$documentroot = Validate::validate($documentroot, 'documentroot', Validate::REGEX_DIR, '', [], true);\n\n\t\t\tif (!empty($documentroot) && $documentroot != $result['documentroot'] && substr($documentroot, 0, 1) == '/' && substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && $this->getUserDetail('change_serversettings') != '1') {\n\t\t\t\tResponse::standardError('pathmustberelative', '', true);\n\t\t\t}\n\n\t\t\t// when moving customer and no path is specified, update would normally reuse the current document-root\n\t\t\t// which would point to the wrong customer, therefore we will re-create that directory\n\t\t\tif (!empty($documentroot) && $customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') {\n\t\t\t\tif (Settings::Get('system.documentroot_use_default_value') == 1) {\n\t\t\t\t\t$_documentroot = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $result['domain']);\n\t\t\t\t} else {\n\t\t\t\t\t$_documentroot = $customer['documentroot'];\n\t\t\t\t}\n\t\t\t\t// set the customers default docroot\n\t\t\t\t$documentroot = $_documentroot;\n\t\t\t}\n\n\t\t\tif ($documentroot == '') {\n\t\t\t\t// If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings,\n\t\t\t\t// set default path to subdomain or domain name\n\t\t\t\tif (Settings::Get('system.documentroot_use_default_value') == 1) {\n\t\t\t\t\t$documentroot = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $result['domain']);\n\t\t\t\t} else {\n\t\t\t\t\t$documentroot = $customer['documentroot'];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($this->getUserDetail('change_serversettings') == '1') {\n\t\t\t\tif (Settings::Get('system.bind_enable') == '1') {\n\t\t\t\t\t$zonefile = Validate::validate($zonefile, 'zonefile', '', '', [], true);\n\t\t\t\t} else {\n\t\t\t\t\t$isbinddomain = $result['isbinddomain'];\n\t\t\t\t\t$zonefile = $result['zonefile'];\n\t\t\t\t}\n\n\t\t\t\tif (Settings::Get('antispam.activated') != '1') {\n\t\t\t\t\t$dkim = $result['dkim'];\n\t\t\t\t}\n\n\t\t\t\t$specialsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $specialsettings), 'specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);\n\n\t\t\t\t$ssl_protocols = [];\n\t\t\t\tif (!empty($p_ssl_protocols) && is_numeric($p_ssl_protocols)) {\n\t\t\t\t\t$p_ssl_protocols = [\n\t\t\t\t\t\t$p_ssl_protocols\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tif (!empty($p_ssl_protocols) && !is_array($p_ssl_protocols)) {\n\t\t\t\t\t$p_ssl_protocols = json_decode($p_ssl_protocols, true);\n\t\t\t\t}\n\t\t\t\tif (!empty($p_ssl_protocols) && is_array($p_ssl_protocols)) {\n\t\t\t\t\t$protocols_available = [\n\t\t\t\t\t\t'TLSv1',\n\t\t\t\t\t\t'TLSv1.1',\n\t\t\t\t\t\t'TLSv1.2',\n\t\t\t\t\t\t'TLSv1.3'\n\t\t\t\t\t];\n\t\t\t\t\tforeach ($p_ssl_protocols as $ssl_protocol) {\n\t\t\t\t\t\tif (!in_array(trim($ssl_protocol), $protocols_available)) {\n\t\t\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_DEBUG, \"[API] unknown SSL protocol '\" . trim($ssl_protocol) . \"'\");\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$ssl_protocols[] = $ssl_protocol;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (empty($ssl_protocols)) {\n\t\t\t\t\t$override_tls = '0';\n\t\t\t\t}\n\n\t\t\t\t// http/3 for nginx only works with TLSv1.3 enabled\n\t\t\t\tif ($http3 == '1') {\n\t\t\t\t\t// overwrite enabled?\n\t\t\t\t\tif (Settings::Get('system.webserver') != 'nginx') {\n\t\t\t\t\t\t$http3 = '0';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (($override_tls == '1' && !in_array('TLSv1.3', $ssl_protocols)) ||\n\t\t\t\t\t\t\t($override_tls == '0' && !in_array('TLSv1.3', explode(\",\", Settings::Get('system.ssl_protocols'))))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// no tlsv1.3 -> no http/3\n\t\t\t\t\t\t\tResponse::standardError('tls13requiredforhttp3', '', true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$isbinddomain = $result['isbinddomain'];\n\t\t\t\t$zonefile = $result['zonefile'];\n\t\t\t\t$specialsettings = $result['specialsettings'];\n\t\t\t\t$ssl_specialsettings = $result['ssl_specialsettings'];\n\t\t\t\t$include_specialsettings = $result['include_specialsettings'];\n\t\t\t\t$ssfs = (empty($specialsettings) ? 0 : 1);\n\t\t\t\t$notryfiles = $result['notryfiles'];\n\t\t\t\t$writeaccesslog = $result['writeaccesslog'];\n\t\t\t\t$writeerrorlog = $result['writeerrorlog'];\n\t\t\t\t$ssl_protocols = $p_ssl_protocols;\n\t\t\t\t$override_tls = $result['override_tls'];\n\t\t\t}\n\n\t\t\tif ($this->getUserDetail('caneditphpsettings') == '1' || $this->getUserDetail('change_serversettings') == '1') {\n\t\t\t\tif ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\t$phpsettingid_check_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_PHPCONFIGS . \"` WHERE `id` = :phpid\n\t\t\t\t\t\");\n\t\t\t\t\t$phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, [\n\t\t\t\t\t\t'phpid' => $phpsettingid\n\t\t\t\t\t], true, true);\n\n\t\t\t\t\tif (!isset($phpsettingid_check['id']) || $phpsettingid_check['id'] == '0' || $phpsettingid_check['id'] != $phpsettingid) {\n\t\t\t\t\t\tResponse::standardError('phpsettingidwrong', '', true);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((int)Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t\t\t\t$mod_fcgid_starter = Validate::validate($mod_fcgid_starter, 'mod_fcgid_starter', '/^[0-9]*$/', '', [\n\t\t\t\t\t\t\t'-1',\n\t\t\t\t\t\t\t''\n\t\t\t\t\t\t], true);\n\t\t\t\t\t\t$mod_fcgid_maxrequests = Validate::validate($mod_fcgid_maxrequests, 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', [\n\t\t\t\t\t\t\t'-1',\n\t\t\t\t\t\t\t''\n\t\t\t\t\t\t], true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$mod_fcgid_starter = $result['mod_fcgid_starter'];\n\t\t\t\t\t\t$mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests'];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$phpsettingid = $result['phpsettingid'];\n\t\t\t\t\t$phpfs = 1;\n\t\t\t\t\t$mod_fcgid_starter = $result['mod_fcgid_starter'];\n\t\t\t\t\t$mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests'];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$phpenabled = $result['phpenabled'];\n\t\t\t\t$openbasedir = $result['openbasedir'];\n\t\t\t\t$phpsettingid = $result['phpsettingid'];\n\t\t\t\t$phpfs = 1;\n\t\t\t\t$mod_fcgid_starter = $result['mod_fcgid_starter'];\n\t\t\t\t$mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests'];\n\t\t\t}\n\n\t\t\t// check changes of openbasedir-path variable\n\t\t\tif ($openbasedir_path > 2 && $openbasedir_path < 0) {\n\t\t\t\t$openbasedir_path = 0;\n\t\t\t}\n\n\t\t\t// check non-ssl IP\n\t\t\t$ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']);\n\t\t\t// check ssl IP\n\t\t\tif (empty($p_ssl_ipandports) || (!is_array($p_ssl_ipandports) && is_null($p_ssl_ipandports))) {\n\t\t\t\t$p_ssl_ipandports = [];\n\t\t\t\tforeach ($result['ipsandports'] as $ip) {\n\t\t\t\t\tif ($ip['ssl'] == 1) {\n\t\t\t\t\t\t$p_ssl_ipandports[] = $ip['id'];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t$ssl_ipandports = [];\n\t\t\tif (Settings::Get('system.use_ssl') == \"1\" && !empty($p_ssl_ipandports) && $p_ssl_ipandports[0] != -1) {\n\t\t\t\t$ssl_ipandports = $this->validateIpAddresses($p_ssl_ipandports, true, $result['id']);\n\n\t\t\t\tif ($this->getUserDetail('change_serversettings') == '1') {\n\t\t\t\t\t$ssl_specialsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $ssl_specialsettings), 'ssl_specialsettings', '/^[^\\0]*$/', '', [], true);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ($remove_ssl_ipandport || (!empty($p_ssl_ipandports) && $p_ssl_ipandports[0] == -1)) {\n\t\t\t\t$ssl_ipandports = [];\n\t\t\t}\n\t\t\tif (Settings::Get('system.use_ssl') == \"1\" && $sslenabled && empty($ssl_ipandports)) {\n\t\t\t\t// enabled ssl for the domain but no ssl ip/port is selected\n\t\t\t\tResponse::standardError('nosslippportgiven', '', true);\n\t\t\t}\n\t\t\tif (Settings::Get('system.use_ssl') == \"0\" || empty($ssl_ipandports) || !$sslenabled) {\n\t\t\t\t$ssl_redirect = 0;\n\t\t\t\t$letsencrypt = 0;\n\t\t\t\t$http2 = 0;\n\t\t\t\t$http3 = 0;\n\t\t\t\t// act like $remove_ssl_ipandport\n\t\t\t\t$ssl_ipandports = [];\n\n\t\t\t\t// HSTS\n\t\t\t\t$hsts_maxage = 0;\n\t\t\t\t$hsts_sub = 0;\n\t\t\t\t$hsts_preload = 0;\n\n\t\t\t\t// OCSP stapling\n\t\t\t\t$ocsp_stapling = 0;\n\n\t\t\t\t// vhost container settings\n\t\t\t\t$ssl_specialsettings = '';\n\t\t\t\t$include_specialsettings = 0;\n\t\t\t}\n\n\t\t\t// validate dns if lets encrypt is enabled to check whether we can use it at all\n\t\t\tif ($letsencrypt == '1' && Settings::Get('system.le_domain_dnscheck') == '1') {\n\t\t\t\t$domain_ips = PhpHelper::gethostbynamel6($result['domain'], true, Settings::Get('system.le_domain_dnscheck_resolver'));\n\t\t\t\t$selected_ips = $this->getIpsFromIdArray($ssl_ipandports);\n\t\t\t\tif ($domain_ips == false || count(array_intersect($selected_ips, $domain_ips)) <= 0) {\n\t\t\t\t\tResponse::standardError('invaliddnsforletsencrypt', '', true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// We can't enable let's encrypt for wildcard-domains\n\t\t\tif ($serveraliasoption == '0' && $letsencrypt == '1') {\n\t\t\t\tResponse::standardError('nowildcardwithletsencrypt', '', true);\n\t\t\t}\n\n\t\t\t// Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated\n\t\t\tif ($result['letsencrypt'] != $letsencrypt && $ssl_redirect > 0 && $letsencrypt == 1) {\n\t\t\t\t$ssl_redirect = 2;\n\t\t\t}\n\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\tif ($documentroot != $result['documentroot']) {\n\t\t\t\tif (preg_match('/^https?\\:\\/\\//', $documentroot)) {\n\t\t\t\t\t$encoded = $idna_convert->encode($documentroot);\n\t\t\t\t\tif (!Validate::validateUrl($encoded, true)) {\n\t\t\t\t\t\tResponse::standardError('invaliddocumentrooturl', '', true);\n\t\t\t\t\t}\n\t\t\t\t\t$documentroot = $encoded;\n\t\t\t\t} else {\n\t\t\t\t\tif (substr($documentroot, 0, 1) != \"/\") {\n\t\t\t\t\t\t$documentroot = $customer['documentroot'] . '/' . $documentroot;\n\t\t\t\t\t}\n\t\t\t\t\tif (strpos($documentroot, ':') !== false) {\n\t\t\t\t\t\tResponse::standardError('pathmaynotcontaincolon', '', true);\n\t\t\t\t\t}\n\t\t\t\t\t$documentroot = FileDir::makeCorrectDir($documentroot);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($email_only == '1') {\n\t\t\t\t$isemaildomain = '1';\n\t\t\t} else {\n\t\t\t\t$email_only = '0';\n\t\t\t}\n\n\t\t\tif ($subcanemaildomain != '1' && $subcanemaildomain != '2' && $subcanemaildomain != '3') {\n\t\t\t\t$subcanemaildomain = '0';\n\t\t\t}\n\n\t\t\t$aliasdomain_check = [\n\t\t\t\t'id' => 0\n\t\t\t];\n\n\t\t\tif ($aliasdomain != 0) {\n\t\t\t\t// Overwrite given ipandports with these of the \"main\" domain\n\t\t\t\t$ipandports = [];\n\t\t\t\t$ssl_ipandports = [];\n\t\t\t\t$origipresult_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `id_ipandports` FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :aliasdomain\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($origipresult_stmt, [\n\t\t\t\t\t'aliasdomain' => $aliasdomain\n\t\t\t\t], true, true);\n\t\t\t\t$ipdata_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `id` = :ipid\");\n\t\t\t\twhile ($origip = $origipresult_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$_origip_tmp = Database::pexecute_first($ipdata_stmt, [\n\t\t\t\t\t\t'ipid' => $origip['id_ipandports']\n\t\t\t\t\t], true, true);\n\t\t\t\t\tif ($_origip_tmp['ssl'] == 0) {\n\t\t\t\t\t\t$ipandports[] = $origip['id_ipandports'];\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$ssl_ipandports[] = $origip['id_ipandports'];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (count($ssl_ipandports) == 0) {\n\t\t\t\t\t// we need this for the json_encode\n\t\t\t\t\t// if ssl is disabled or no ssl-ip/port exists\n\t\t\t\t\t$ssl_ipandports[] = -1;\n\t\t\t\t}\n\n\t\t\t\t$aliasdomain_check_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `d`.`id` FROM `\" . TABLE_PANEL_DOMAINS . \"` `d`, `\" . TABLE_PANEL_CUSTOMERS . \"` `c`\n\t\t\t\t\tWHERE `d`.`customerid` = :customerid\n\t\t\t\t\tAND `d`.`aliasdomain` IS NULL AND `d`.`id` <> `c`.`standardsubdomain`\n\t\t\t\t\tAND `c`.`customerid` = :customerid\n\t\t\t\t\tAND `d`.`id` = :aliasdomain\n\t\t\t\t\");\n\t\t\t\t$aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, [\n\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t'aliasdomain' => $aliasdomain\n\t\t\t\t], true, true);\n\t\t\t}\n\n\t\t\tif (count($ipandports) == 0) {\n\t\t\t\tResponse::standardError('noipportgiven', '', true);\n\t\t\t}\n\n\t\t\tif ($aliasdomain_check['id'] != $aliasdomain) {\n\t\t\t\tResponse::standardError('domainisaliasorothercustomer', '', true);\n\t\t\t}\n\n\t\t\tif ($serveraliasoption != '1' && $serveraliasoption != '2') {\n\t\t\t\t$serveraliasoption = '0';\n\t\t\t}\n\n\t\t\t$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';\n\t\t\t$iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0';\n\n\t\t\tif ($documentroot != $result['documentroot']\n\t\t\t\t|| $ssl_redirect != $result['ssl_redirect']\n\t\t\t\t|| $wwwserveralias != $result['wwwserveralias']\n\t\t\t\t|| $iswildcarddomain != $result['iswildcarddomain']\n\t\t\t\t|| $phpenabled != $result['phpenabled']\n\t\t\t\t|| $openbasedir != $result['openbasedir']\n\t\t\t\t|| $openbasedir_path != $result['openbasedir_path']\n\t\t\t\t|| $phpsettingid != $result['phpsettingid']\n\t\t\t\t|| $mod_fcgid_starter != $result['mod_fcgid_starter']\n\t\t\t\t|| $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests']\n\t\t\t\t|| $specialsettings != $result['specialsettings']\n\t\t\t\t|| $ssl_specialsettings != $result['ssl_specialsettings']\n\t\t\t\t|| $notryfiles != $result['notryfiles']\n\t\t\t\t|| $writeaccesslog != $result['writeaccesslog']\n\t\t\t\t|| $writeerrorlog != $result['writeerrorlog']\n\t\t\t\t|| $aliasdomain != $result['aliasdomain']\n\t\t\t\t|| $email_only != $result['email_only']\n\t\t\t\t|| ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1')\n\t\t\t\t|| $letsencrypt != $result['letsencrypt']\n\t\t\t\t|| $http2 != $result['http2']\n\t\t\t\t|| $http3 != $result['http3']\n\t\t\t\t|| $hsts_maxage != $result['hsts']\n\t\t\t\t|| $hsts_sub != $result['hsts_sub']\n\t\t\t\t|| $hsts_preload != $result['hsts_preload']\n\t\t\t\t|| $ocsp_stapling != $result['ocsp_stapling']\n\t\t\t\t|| $sslenabled != $result['ssl_enabled']\n\t\t\t\t|| $override_tls != $result['override_tls']\n\t\t\t\t|| implode(\",\", $ssl_protocols) != $result['ssl_protocols']\n\t\t\t) {\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t}\n\n\t\t\tif ($dkim != $result['dkim']) {\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_RSPAMD);\n\t\t\t}\n\n\t\t\tif ($speciallogfile != $result['speciallogfile'] && $speciallogverified != '1') {\n\t\t\t\t$speciallogfile = $result['speciallogfile'];\n\t\t\t}\n\n\t\t\tif ($isbinddomain != $result['isbinddomain'] || $zonefile != $result['zonefile'] || $dkim != $result['dkim'] || $isemaildomain != $result['isemaildomain']) {\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t\t}\n\t\t\t// check whether nameserver has been disabled, #581\n\t\t\tif ($isbinddomain != $result['isbinddomain'] && $isbinddomain == 0) {\n\t\t\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_PDNS, $result['domain']);\n\t\t\t}\n\n\t\t\tif ($isemaildomain == '0' && $result['isemaildomain'] == '1') {\n\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_MAIL_USERS . \"` WHERE `domainid` = :id\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t], true, true);\n\n\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_MAIL_VIRTUAL . \"` WHERE `domainid` = :id\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t], true, true);\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] deleted domain #\" . $id . \" from mail-tables as is-email-domain was set to 0\");\n\t\t\t}\n\n\t\t\t// check whether LE has been disabled, so we remove the certificate\n\t\t\tif ($letsencrypt == '0' && $result['letsencrypt'] == '1') {\n\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = :id\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t], true, true);\n\t\t\t\t// remove domain from acme.sh / lets encrypt if used\n\t\t\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']);\n\t\t\t}\n\n\t\t\t$updatechildren = '';\n\n\t\t\tif ($subcanemaildomain == '0' && $result['subcanemaildomain'] != '0') {\n\t\t\t\t$updatechildren = \", `isemaildomain` = '0' \";\n\t\t\t} elseif ($subcanemaildomain == '3' && $result['subcanemaildomain'] != '3') {\n\t\t\t\t$updatechildren = \", `isemaildomain` = '1' \";\n\t\t\t}\n\n\t\t\tif ($customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') {\n\t\t\t\t$upd_data = [\n\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t'domainid' => $result['id']\n\t\t\t\t];\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_MAIL_USERS . \"` SET `customerid` = :customerid WHERE `domainid` = :domainid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data, true, true);\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_MAIL_VIRTUAL . \"` SET `customerid` = :customerid WHERE `domainid` = :domainid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data, true, true);\n\t\t\t\t$upd_data = [\n\t\t\t\t\t'subdomains' => $subdomains,\n\t\t\t\t\t'emails' => $emails,\n\t\t\t\t\t'forwarders' => $email_forwarders,\n\t\t\t\t\t'accounts' => $email_accounts\n\t\t\t\t];\n\t\t\t\t$upd_data['customerid'] = $customerid;\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t\t`subdomains_used` = `subdomains_used` + :subdomains,\n\t\t\t\t\t`emails_used` = `emails_used` + :emails,\n\t\t\t\t\t`email_forwarders_used` = `email_forwarders_used` + :forwarders,\n\t\t\t\t\t`email_accounts_used` = `email_accounts_used` + :accounts\n\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data, true, true);\n\n\t\t\t\t$upd_data['customerid'] = $result['customerid'];\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t\t`subdomains_used` = `subdomains_used` - :subdomains,\n\t\t\t\t\t`emails_used` = `emails_used` - :emails,\n\t\t\t\t\t`email_forwarders_used` = `email_forwarders_used` - :forwarders,\n\t\t\t\t\t`email_accounts_used` = `email_accounts_used` - :accounts\n\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data, true, true);\n\t\t\t}\n\n\t\t\tif ($adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') {\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `domains_used` = `domains_used` + 1 WHERE `adminid` = :adminid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'adminid' => $adminid\n\t\t\t\t], true, true);\n\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `domains_used` = `domains_used` - 1 WHERE `adminid` = :adminid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'adminid' => $result['adminid']\n\t\t\t\t], true, true);\n\t\t\t}\n\n\t\t\t$_update_data = [];\n\n\t\t\tif ($ssfs == 1) {\n\t\t\t\t$_update_data['specialsettings'] = $specialsettings;\n\t\t\t\t$_update_data['ssl_specialsettings'] = $ssl_specialsettings;\n\t\t\t\t$_update_data['include_specialsettings'] = $include_specialsettings;\n\t\t\t\t$upd_specialsettings = \", `specialsettings` = :specialsettings, `ssl_specialsettings` = :ssl_specialsettings, `include_specialsettings` = :include_specialsettings \";\n\t\t\t} else {\n\t\t\t\t$upd_specialsettings = '';\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `specialsettings`='', `ssl_specialsettings`='', `include_specialsettings`='0' WHERE `parentdomainid` = :id\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t], true, true);\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] removed specialsettings on all subdomains of domain #\" . $id);\n\t\t\t}\n\n\t\t\t$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';\n\t\t\t$iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0';\n\n\t\t\t$update_data = [];\n\t\t\t$update_data['customerid'] = $customerid;\n\t\t\t$update_data['adminid'] = $adminid;\n\t\t\t$update_data['documentroot'] = $documentroot;\n\t\t\t$update_data['ssl_redirect'] = $ssl_redirect;\n\t\t\t$update_data['aliasdomain'] = ($aliasdomain != 0 && $alias_check == 0) ? $aliasdomain : null;\n\t\t\t$update_data['isbinddomain'] = $isbinddomain;\n\t\t\t$update_data['isemaildomain'] = $isemaildomain;\n\t\t\t$update_data['email_only'] = $email_only;\n\t\t\t$update_data['subcanemaildomain'] = $subcanemaildomain;\n\t\t\t$update_data['dkim'] = $dkim;\n\t\t\t$update_data['caneditdomain'] = $caneditdomain;\n\t\t\t$update_data['zonefile'] = $zonefile;\n\t\t\t$update_data['wwwserveralias'] = $wwwserveralias;\n\t\t\t$update_data['iswildcarddomain'] = $iswildcarddomain;\n\t\t\t$update_data['phpenabled'] = $phpenabled;\n\t\t\t$update_data['openbasedir'] = $openbasedir;\n\t\t\t$update_data['openbasedir_path'] = $openbasedir_path;\n\t\t\t$update_data['speciallogfile'] = $speciallogfile;\n\t\t\t$update_data['phpsettingid'] = $phpsettingid;\n\t\t\t$update_data['mod_fcgid_starter'] = $mod_fcgid_starter;\n\t\t\t$update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests;\n\t\t\t$update_data['specialsettings'] = $specialsettings;\n\t\t\t$update_data['ssl_specialsettings'] = $ssl_specialsettings;\n\t\t\t$update_data['include_specialsettings'] = $include_specialsettings;\n\t\t\t$update_data['notryfiles'] = $notryfiles;\n\t\t\t$update_data['writeaccesslog'] = $writeaccesslog;\n\t\t\t$update_data['writeerrorlog'] = $writeerrorlog;\n\t\t\t$update_data['registration_date'] = $registration_date;\n\t\t\t$update_data['termination_date'] = $termination_date;\n\t\t\t$update_data['letsencrypt'] = $letsencrypt;\n\t\t\t$update_data['http2'] = $http2;\n\t\t\t$update_data['http3'] = $http3;\n\t\t\t$update_data['hsts'] = $hsts_maxage;\n\t\t\t$update_data['hsts_sub'] = $hsts_sub;\n\t\t\t$update_data['hsts_preload'] = $hsts_preload;\n\t\t\t$update_data['ocsp_stapling'] = $ocsp_stapling;\n\t\t\t$update_data['override_tls'] = $override_tls;\n\t\t\t$update_data['ssl_protocols'] = implode(\",\", $ssl_protocols);\n\t\t\t$update_data['ssl_cipher_list'] = $ssl_cipher_list;\n\t\t\t$update_data['tlsv13_cipher_list'] = $tlsv13_cipher_list;\n\t\t\t$update_data['sslenabled'] = $sslenabled;\n\t\t\t$update_data['honorcipherorder'] = $honorcipherorder;\n\t\t\t$update_data['sessiontickets'] = $sessiontickets;\n\t\t\t$update_data['description'] = $description;\n\t\t\t$update_data['deactivated'] = $deactivated;\n\t\t\t$update_data['id'] = $id;\n\n\t\t\t$update_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t`customerid` = :customerid,\n\t\t\t\t`adminid` = :adminid,\n\t\t\t\t`documentroot` = :documentroot,\n\t\t\t\t`ssl_redirect` = :ssl_redirect,\n\t\t\t\t`aliasdomain` = :aliasdomain,\n\t\t\t\t`isbinddomain` = :isbinddomain,\n\t\t\t\t`isemaildomain` = :isemaildomain,\n\t\t\t\t`email_only` = :email_only,\n\t\t\t\t`subcanemaildomain` = :subcanemaildomain,\n\t\t\t\t`dkim` = :dkim,\n\t\t\t\t`caneditdomain` = :caneditdomain,\n\t\t\t\t`zonefile` = :zonefile,\n\t\t\t\t`wwwserveralias` = :wwwserveralias,\n\t\t\t\t`iswildcarddomain` = :iswildcarddomain,\n\t\t\t\t`phpenabled` = :phpenabled,\n\t\t\t\t`openbasedir` = :openbasedir,\n\t\t\t\t`openbasedir_path` = :openbasedir_path,\n\t\t\t\t`speciallogfile` = :speciallogfile,\n\t\t\t\t`phpsettingid` = :phpsettingid,\n\t\t\t\t`mod_fcgid_starter` = :mod_fcgid_starter,\n\t\t\t\t`mod_fcgid_maxrequests` = :mod_fcgid_maxrequests,\n\t\t\t\t`specialsettings` = :specialsettings,\n\t\t\t\t`ssl_specialsettings` = :ssl_specialsettings,\n\t\t\t\t`include_specialsettings` = :include_specialsettings,\n\t\t\t\t`notryfiles` = :notryfiles,\n\t\t\t\t`writeaccesslog` = :writeaccesslog,\n\t\t\t\t`writeerrorlog` = :writeerrorlog,\n\t\t\t\t`registration_date` = :registration_date,\n\t\t\t\t`termination_date` = :termination_date,\n\t\t\t\t`letsencrypt` = :letsencrypt,\n\t\t\t\t`http2` = :http2,\n\t\t\t\t`http3` = :http3,\n\t\t\t\t`hsts` = :hsts,\n\t\t\t\t`hsts_sub` = :hsts_sub,\n\t\t\t\t`hsts_preload` = :hsts_preload,\n\t\t\t\t`ocsp_stapling` = :ocsp_stapling,\n\t\t\t\t`override_tls` = :override_tls,\n\t\t\t\t`ssl_protocols` = :ssl_protocols,\n\t\t\t\t`ssl_cipher_list` = :ssl_cipher_list,\n\t\t\t\t`tlsv13_cipher_list` = :tlsv13_cipher_list,\n\t\t\t\t`ssl_enabled` = :sslenabled,\n\t\t\t\t`ssl_honorcipherorder` = :honorcipherorder,\n\t\t\t\t`ssl_sessiontickets` = :sessiontickets,\n\t\t\t\t`description` = :description,\n\t\t\t\t`deactivated` = :deactivated\n\t\t\t\tWHERE `id` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($update_stmt, $update_data, true, true);\n\n\t\t\t// activate/deactivate domain-based services\n\t\t\tif ($deactivated != $result['deactivated']) {\n\t\t\t\t// deactivate email accounts\n\t\t\t\t$yesno = ($deactivated ? 'N' : 'Y');\n\t\t\t\t$pop3 = ($deactivated ? '0' : (int)$customer['pop3']);\n\t\t\t\t$imap = ($deactivated ? '0' : (int)$customer['imap']);\n\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_MAIL_USERS . \"`\n\t\t\t\t\tSET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap\n\t\t\t\t\tWHERE `customerid` = :customerid AND `domainid` = :domainid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'yesno' => $yesno,\n\t\t\t\t\t'pop3' => $pop3,\n\t\t\t\t\t'imap' => $imap,\n\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t'domainid' => $id\n\t\t\t\t]);\n\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] \" . ($deactivated ? 'deactivated' : 'reactivated') . \" domain '\" . $result['domain'] . \"'\");\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t}\n\n\t\t\t$_update_data['customerid'] = $customerid;\n\t\t\t$_update_data['adminid'] = $adminid;\n\t\t\t$_update_data['phpenabled'] = $phpenabled;\n\t\t\t$_update_data['openbasedir'] = $openbasedir;\n\t\t\t$_update_data['openbasedir_path'] = $openbasedir_path;\n\t\t\t$_update_data['mod_fcgid_starter'] = $mod_fcgid_starter;\n\t\t\t$_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests;\n\t\t\t$_update_data['notryfiles'] = $notryfiles;\n\t\t\t$_update_data['writeaccesslog'] = $writeaccesslog;\n\t\t\t$_update_data['writeerrorlog'] = $writeerrorlog;\n\t\t\t$_update_data['override_tls'] = $override_tls;\n\t\t\t$_update_data['ssl_protocols'] = implode(\",\", $ssl_protocols);\n\t\t\t$_update_data['ssl_cipher_list'] = $ssl_cipher_list;\n\t\t\t$_update_data['tlsv13_cipher_list'] = $tlsv13_cipher_list;\n\t\t\t$_update_data['honorcipherorder'] = $honorcipherorder;\n\t\t\t$_update_data['sessiontickets'] = $sessiontickets;\n\t\t\t$_update_data['parentdomainid'] = $id;\n\t\t\t$_update_data['deactivated'] = $deactivated;\n\n\t\t\t// if php config is to be set for all subdomains, check here\n\t\t\t$update_phpconfig = '';\n\t\t\tif ($phpfs == 1) {\n\t\t\t\t$_update_data['phpsettingid'] = $phpsettingid;\n\t\t\t\t$update_phpconfig = \", `phpsettingid` = :phpsettingid\";\n\t\t\t}\n\t\t\t// if we have no more ssl-ip's for this domain,\n\t\t\t// all its subdomains must have \"ssl-redirect = 0\"\n\t\t\t// and disable let's encrypt\n\t\t\t$update_sslredirect = '';\n\t\t\tif (count($ssl_ipandports) == 1 && $ssl_ipandports[0] == -1) {\n\t\t\t\t$update_sslredirect = \", `ssl_redirect` = '0', `letsencrypt` = '0' \";\n\t\t\t}\n\n\t\t\t$_update_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t`customerid` = :customerid,\n\t\t\t\t`adminid` = :adminid,\n\t\t\t\t`phpenabled` = :phpenabled,\n\t\t\t\t`openbasedir` = :openbasedir,\n\t\t\t\t`openbasedir_path` = :openbasedir_path,\n\t\t\t\t`mod_fcgid_starter` = :mod_fcgid_starter,\n\t\t\t\t`mod_fcgid_maxrequests` = :mod_fcgid_maxrequests,\n\t\t\t\t`notryfiles` = :notryfiles,\n\t\t\t\t`writeaccesslog` = :writeaccesslog,\n\t\t\t\t`writeerrorlog` = :writeerrorlog,\n\t\t\t\t`override_tls` = :override_tls,\n\t\t\t\t`ssl_protocols` = :ssl_protocols,\n\t\t\t\t`ssl_cipher_list` = :ssl_cipher_list,\n\t\t\t\t`tlsv13_cipher_list` = :tlsv13_cipher_list,\n\t\t\t\t`ssl_honorcipherorder` = :honorcipherorder,\n\t\t\t\t`ssl_sessiontickets` = :sessiontickets,\n\t\t\t\t`deactivated` = :deactivated\n\t\t\t\t\" . $update_phpconfig . $upd_specialsettings . $updatechildren . $update_sslredirect . \"\n\t\t\t\tWHERE `parentdomainid` = :parentdomainid\n\t\t\t\");\n\t\t\tDatabase::pexecute($_update_stmt, $_update_data, true, true);\n\n\t\t\t// get current ip<>domain entries\n\t\t\t$ip_sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT id_ipandports FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($ip_sel_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\t$current_ips = [];\n\t\t\twhile ($cIP = $ip_sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$current_ips[] = $cIP['id_ipandports'];\n\t\t\t}\n\n\t\t\t// Cleanup domain <-> ip mapping\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_DOMAINTOIP . \"` SET `id_domain` = :domainid, `id_ipandports` = :ipportid\n\t\t\t\");\n\n\t\t\tforeach ($ipandports as $ipportid) {\n\t\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t\t'domainid' => $id,\n\t\t\t\t\t'ipportid' => $ipportid\n\t\t\t\t], true, true);\n\t\t\t}\n\t\t\tforeach ($ssl_ipandports as $ssl_ipportid) {\n\t\t\t\tif ($ssl_ipportid > 0) {\n\t\t\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t\t\t'domainid' => $id,\n\t\t\t\t\t\t'ipportid' => $ssl_ipportid\n\t\t\t\t\t], true, true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check ip changes\n\t\t\t$all_new_ips = array_merge($ipandports, $ssl_ipandports);\n\t\t\tif (count(array_diff($current_ips, $all_new_ips)) != 0 || count(array_diff($all_new_ips, $current_ips)) != 0) {\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t}\n\n\t\t\t// Cleanup domain <-> ip mapping for subdomains\n\t\t\t$domainidsresult_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id` FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `parentdomainid` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($domainidsresult_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\twhile ($row = $domainidsresult_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :rowid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t'rowid' => $row['id']\n\t\t\t\t], true, true);\n\n\t\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\tINSERT INTO `\" . TABLE_DOMAINTOIP . \"` SET\n\t\t\t\t\t`id_domain` = :rowid,\n\t\t\t\t\t`id_ipandports` = :ipportid\n\t\t\t\t\");\n\n\t\t\t\tforeach ($ipandports as $ipportid) {\n\t\t\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t\t\t'rowid' => $row['id'],\n\t\t\t\t\t\t'ipportid' => $ipportid\n\t\t\t\t\t], true, true);\n\t\t\t\t}\n\t\t\t\tforeach ($ssl_ipandports as $ssl_ipportid) {\n\t\t\t\t\tif ($ssl_ipportid > 0) {\n\t\t\t\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t\t\t\t'rowid' => $row['id'],\n\t\t\t\t\t\t\t'ipportid' => $ssl_ipportid\n\t\t\t\t\t\t], true, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ($result['aliasdomain'] != $aliasdomain && is_numeric($result['aliasdomain'])) {\n\t\t\t\t// trigger when domain id for alias destination has changed: both for old and new destination\n\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());\n\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());\n\t\t\t}\n\t\t\tif ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) {\n\t\t\t\t// or when wwwserveralias or letsencrypt was changed\n\t\t\t\tif ((int)$aliasdomain === 0) {\n\t\t\t\t\t// in case the wwwserveralias is set on a main domain, $aliasdomain is 0\n\t\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($id, $this->logger());\n\t\t\t\t} else {\n\t\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] updated domain '\" . $idna_convert->decode($result['domain']) . \"'\");\n\t\t\t$result = $this->apiCall('Domains.get', [\n\t\t\t\t'domainname' => $result['domain']\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * delete a domain entry by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param bool $is_stdsubdomain\n\t *            optional, default false, specify whether it's a std-subdomain you are deleting as it does not count\n\t *            as subdomain-resource\n\t * @param bool $delete_userfiles\n\t *            optional, delete email account files on filesystem (if any), default false\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$dn_optional = $id > 0;\n\t\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\t\t\t$is_stdsubdomain = $this->getBoolParam('is_stdsubdomain', true, 0);\n\t\t\t$delete_user_emailfiles = $this->getBoolParam('delete_userfiles', true, 0);\n\n\t\t\t$result = $this->apiCall('Domains.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'domainname' => $domainname\n\t\t\t]);\n\t\t\t$id = $result['id'];\n\n\t\t\t$subresult_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tWHERE (`id` = :id OR `parentdomainid` = :id)\n\t\t\t\");\n\t\t\tDatabase::pexecute($subresult_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\t$idString = [];\n\t\t\t$paramString = [];\n\t\t\twhile ($subRow = $subresult_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$idString[] = \"`domainid` = :domain_\" . (int)$subRow['id'];\n\t\t\t\t$paramString['domain_' . $subRow['id']] = $subRow['id'];\n\t\t\t}\n\t\t\t$idString = implode(' OR ', $idString);\n\n\t\t\tif ($idString != '') {\n\t\t\t\tif ($delete_user_emailfiles) {\n\t\t\t\t\t// determine all connected email-accounts\n\t\t\t\t\t$emailaccount_sel = Database::prepare(\"SELECT `email`, `homedir`, `maildir` FROM `\" . TABLE_MAIL_USERS . \"` WHERE \" . $idString);\n\t\t\t\t\tDatabase::pexecute($emailaccount_sel, $paramString, true, true);\n\t\t\t\t\twhile ($emailacc_row = $emailaccount_sel->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\tCronjob::inserttask(TaskId::DELETE_EMAIL_DATA, $emailacc_row['email'], FileDir::makeCorrectDir($emailacc_row['homedir'] . '/' . $emailacc_row['maildir']));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\t\tDELETE FROM `\" . TABLE_MAIL_USERS . \"` WHERE \" . $idString);\n\t\t\t\tDatabase::pexecute($del_stmt, $paramString, true, true);\n\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\t\tDELETE FROM `\" . TABLE_MAIL_VIRTUAL . \"` WHERE \" . $idString);\n\t\t\t\tDatabase::pexecute($del_stmt, $paramString, true, true);\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] deleted domain/s from mail-tables\");\n\t\t\t}\n\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tWHERE `id` = :id OR `parentdomainid` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t$deleted_domains = $del_stmt->rowCount();\n\n\t\t\tif ($is_stdsubdomain == 0) {\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t\t\t`subdomains_used` = `subdomains_used` - :domaincount\n\t\t\t\t\t\tWHERE `customerid` = :customerid\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'domaincount' => ($deleted_domains - 1),\n\t\t\t\t\t'customerid' => $result['customerid']\n\t\t\t\t], true, true);\n\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET\n\t\t\t\t\t\t`domains_used` = `domains_used` - 1\n\t\t\t\t\t\tWHERE `adminid` = :adminid\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'adminid' => $this->getUserDetail('adminid')\n\t\t\t\t], true, true);\n\t\t\t}\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t\t`standardsubdomain` = '0'\n\t\t\t\t\tWHERE `standardsubdomain` = :id AND `customerid` = :customerid\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'id' => $result['id'],\n\t\t\t\t'customerid' => $result['customerid']\n\t\t\t], true, true);\n\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\t\tWHERE `id_domain` = :domainid\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'domainid' => $id\n\t\t\t], true, true);\n\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAINREDIRECTS . \"`\n\t\t\t\t\tWHERE `did` = :domainid\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'domainid' => $id\n\t\t\t], true, true);\n\n\t\t\t// remove certificate from domain_ssl_settings, fixes #1596\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\t\t\t\tWHERE `domainid` = :domainid\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'domainid' => $id\n\t\t\t], true, true);\n\n\t\t\t// remove possible existing DNS entries\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_DOMAIN_DNS . \"`\n\t\t\t\t\tWHERE `domain_id` = :domainid\n\t\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'domainid' => $id\n\t\t\t], true, true);\n\n\t\t\tif ((int)$result['aliasdomain'] !== 0) {\n\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());\n\t\t\t}\n\n\t\t\t// remove domains DNS from powerDNS if used, #581\n\t\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_PDNS, $result['domain']);\n\n\t\t\t// remove domain from acme.sh / lets encrypt if used\n\t\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']);\n\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] deleted domain/subdomains (#\" . $result['id'] . \")\");\n\t\t\tUser::updateCounters();\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * duplicate domain entry by either id or domainname. All parameters from Domains.add() can be used\n\t * to overwrite source entity values if necessary.\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param string $domain\n\t *            required, name of the new domain to be added\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function duplicate()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t// parameters\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$dn_optional = $id > 0;\n\t\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\t\t\t$p_domain = $this->getParam('domain');\n\n\t\t\t// get requested domain\n\t\t\t$result = $this->apiCall('Domains.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'domainname' => $domainname,\n\t\t\t]);\n\n\t\t\t// clear some defaults\n\t\t\tunset($result['domain_ace']);\n\t\t\tunset($result['adminid']);\n\t\t\tunset($result['documentroot']);\n\t\t\tunset($result['registration_date']);\n\t\t\tunset($result['termination_date']);\n\t\t\tunset($result['zonefile']);\n\t\t\t// clear auto-generated values\n\t\t\tunset($result['bindserial']);\n\t\t\tunset($result['dkim_privkey']);\n\t\t\tunset($result['dkim_pubkey']);\n\t\t\t// clear api-call generated fields\n\t\t\tunset($result['domain_hascert']);\n\n\t\t\t// set correct ip/port information\n\t\t\t$domain_ips = $result['ipsandports'];\n\t\t\tunset($result['ipsandports']);\n\t\t\t$result['ipandport'] = [];\n\t\t\t$result['ssl_ipandport'] = [];\n\t\t\tforeach ($domain_ips as $dip) {\n\t\t\t\tif ($dip['ssl'] == 1) {\n\t\t\t\t\t$result['ssl_ipandport'][] = $dip['id'];\n\t\t\t\t} else {\n\t\t\t\t\t$result['ipandport'][] = $dip['id'];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check whether we are changing the customer/owner\n\t\t\tif ($this->getParam('customerid', true, 0) == 0 && $this->getParam('loginname', true, '') == '') {\n\t\t\t\t$customerid = $result['customerid'];\n\t\t\t} else {\n\t\t\t\t$customer = $this->getCustomerData();\n\t\t\t\t$customerid = $customer['customerid'];\n\t\t\t}\n\n\t\t\t// check for alias-domain and whether it belongs to the target user\n\t\t\tif (!empty($result['aliasdomain']) && $customerid == $result['customerid']) {\n\t\t\t\t// duplicate alias entry\n\t\t\t\t$result['alias'] = $result['aliasdomain'];\n\t\t\t}\n\t\t\tunset($result['aliasdomain']);\n\n\t\t\t// validate possible fpm configs and whether the customer is allowed to use them\n\t\t\tif ($customerid != $result['customerid']) {\n\t\t\t\t$allowed_phpconfigs = json_decode($customer['allowed_phpconfigs'] ?? '[]', true);\n\t\t\t\tif (empty($allowed_phpconfigs)) {\n\t\t\t\t\t// system defaults\n\t\t\t\t\tunset($result['phpsettingid']);\n\t\t\t\t} elseif (!in_array($result['phpsettingid'], $allowed_phpconfigs)) {\n\t\t\t\t\t// use the first customer allowed config\n\t\t\t\t\t$result['phpsettingid'] = array_shift($allowed_phpconfigs);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// translate serveralias values\n\t\t\t$result['selectserveralias'] = 2;\n\t\t\tif ((int)$result['wwwserveralias'] == 1) {\n\t\t\t\t$result['selectserveralias'] = 1;\n\t\t\t} elseif ((int)$result['iswildcarddomain'] == 1) {\n\t\t\t\t$result['selectserveralias'] = 0;\n\t\t\t}\n\t\t\tunset($result['wwwserveralias']);\n\t\t\tunset($result['iswildcarddomain']);\n\n\t\t\t// translate sslenabled flag\n\t\t\t$result['sslenabled'] = $result['ssl_enabled'];\n\t\t\tunset($result['ssl_enabled']);\n\n\t\t\t$additional_params = $this->getParamList();\n\t\t\t// unset unneeded params from this call\n\t\t\tunset($additional_params['id']);\n\t\t\tunset($additional_params['domainname']);\n\t\t\tunset($additional_params['domain']);\n\n\t\t\t// set new values and merge with optional add() parameters\n\t\t\t$new_domain = array_merge($result, $additional_params);\n\t\t\t$new_domain['domain'] = $p_domain;\n\n\t\t\t$result_new = $this->apiCall('Domains.add', $new_domain);\n\t\t\treturn $this->response($result_new);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/EmailAccounts.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse Froxlor\\Validate\\Check;\nuse Froxlor\\Validate\\Validate;\n\n/**\n * @since 0.10.0\n */\nclass EmailAccounts extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new email account for a given email-address either by id or emailaddr\n\t *\n\t * @param int $id\n\t *            optional email-address-id of email-address to add the account for\n\t * @param string $emailaddr\n\t *            optional email-address to add the account for\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param string $email_password\n\t *            password for the account\n\t * @param string $alternative_email\n\t *            optional email address to send account information to, default is the account that is being created\n\t * @param int $email_quota\n\t *            optional quota if enabled in MB, default setting: system.mail_quota\n\t * @param bool $sendinfomail\n\t *            optional, sends the welcome message to the new account (needed for creation, without the user won't\n\t *            be able to login before any mail is received), default 1 (true)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\tif ($this->getUserDetail('email_accounts_used') < $this->getUserDetail('email_accounts') || $this->getUserDetail('email_accounts') == '-1') {\n\t\t\t// parameter\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$ea_optional = $id > 0;\n\t\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\t\t\t$email_password = $this->getParam('email_password');\n\t\t\t$alternative_email = $this->getParam('alternative_email', true, '');\n\t\t\t$quota = $this->getParam('email_quota', true, Settings::Get('system.mail_quota') ?? 0);\n\t\t\t$sendinfomail = $this->getBoolParam('sendinfomail', true, 1);\n\n\t\t\t// validation\n\t\t\t$quota = Validate::validate($quota, 'email_quota', '/^\\d+$/', 'vmailquotawrong', [], true);\n\n\t\t\t// get needed customer info to reduce the email-account-counter by one\n\t\t\t$customer = $this->getCustomerData('email_accounts');\n\n\t\t\t// check for imap||pop3 == 1, see #1298\n\t\t\t// d00p, 6.5.2023 @revert this - if a customer has resources which allow email accounts\n\t\t\t// it implicitly allowed SMTP, e.g. sending of emails which also requires an account to exist\n\t\t\t/*\n\t\t\tif ($customer['imap'] != '1' && $customer['pop3'] != '1') {\n\t\t\t\tResponse::standardError('notallowedtouseaccounts', '', true);\n\t\t\t}\n\t\t\t*/\n\n\t\t\tif (!empty($emailaddr)) {\n\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\t$emailaddr = $idna_convert->encode($emailaddr);\n\t\t\t}\n\n\t\t\t// get email address\n\t\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'emailaddr' => $emailaddr\n\t\t\t]);\n\t\t\t$id = $result['id'];\n\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$email_full = $result['email_full'];\n\t\t\t$username = $email_full;\n\t\t\t$password = Validate::validate($email_password, 'password', '', '', [], true);\n\t\t\t$password = Crypt::validatePassword($password, true);\n\n\t\t\tif ($result['popaccountid'] != 0) {\n\t\t\t\tthrow new Exception(\"Email address '\" . $email_full . \"' has already an account assigned.\", 406);\n\t\t\t}\n\n\t\t\tif (Check::checkMailAccDeletionState($email_full)) {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'mailaccistobedeleted'\n\t\t\t\t], $email_full, true);\n\t\t\t}\n\n\t\t\t// alternative email address to send info to\n\t\t\tif (Settings::Get('panel.sendalternativemail') == 1) {\n\t\t\t\t$alternative_email = $idna_convert->encode(Validate::validate($alternative_email, 'alternative_email', '', '', [], true));\n\t\t\t\tif (!empty($alternative_email) && !Validate::validateEmail($alternative_email)) {\n\t\t\t\t\tResponse::standardError('alternativeemailiswrong', $alternative_email, true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$alternative_email = '';\n\t\t\t}\n\n\t\t\t// validate quota if enabled\n\t\t\tif (Settings::Get('system.mail_quota_enabled') == 1) {\n\t\t\t\tif ($customer['email_quota'] != '-1' && ($quota == 0 || ($quota + $customer['email_quota_used']) > $customer['email_quota'])) {\n\t\t\t\t\tResponse::standardError('allocatetoomuchquota', $quota, true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// disable\n\t\t\t\t$quota = 0;\n\t\t\t}\n\n\t\t\tif ($password == $email_full) {\n\t\t\t\tResponse::standardError('passwordshouldnotbeusername', '', true);\n\t\t\t}\n\n\t\t\t// prefix hash-algo\n\t\t\tswitch (Settings::Get('system.passwordcryptfunc')) {\n\t\t\t\tcase 'argon2i':\n\t\t\t\t\t$cpPrefix = '{ARGON2I}';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'argon2id':\n\t\t\t\t\t$cpPrefix = '{ARGON2ID}';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t$cpPrefix = '{BLF-CRYPT}';\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// encrypt the password\n\t\t\t$cryptPassword = $cpPrefix . Crypt::makeCryptPassword($password);\n\n\t\t\t$email_user = substr($email_full, 0, strrpos($email_full, \"@\"));\n\t\t\t$email_domain = substr($email_full, strrpos($email_full, \"@\") + 1);\n\t\t\t$maildirname = trim(Settings::Get('system.vmail_maildirname'));\n\t\t\t// Add trailing slash to Maildir if needed\n\t\t\t$maildirpath = $maildirname;\n\t\t\tif (!empty($maildirname) && substr($maildirname, -1) != \"/\") {\n\t\t\t\t$maildirpath .= \"/\";\n\t\t\t}\n\n\t\t\t// insert data\n\t\t\t$stmt = Database::prepare(\"INSERT INTO `\" . TABLE_MAIL_USERS . \"` SET\n\t\t\t\t`customerid` = :cid,\n\t\t\t\t`email` = :email,\n\t\t\t\t`username` = :username,\" . (Settings::Get('system.mailpwcleartext') == '1' ? '`password` = :password, ' : '') . \"\n\t\t\t\t`password_enc` = :password_enc,\n\t\t\t\t`homedir` = :homedir,\n\t\t\t\t`maildir` = :maildir,\n\t\t\t\t`uid` = :uid,\n\t\t\t\t`gid` = :gid,\n\t\t\t\t`domainid` = :domainid,\n\t\t\t\t`postfix` = 'y',\n\t\t\t\t`quota` = :quota,\n\t\t\t\t`imap` = :imap,\n\t\t\t\t`pop3` = :pop3\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\t\"email\" => $email_full,\n\t\t\t\t\"username\" => $username,\n\t\t\t\t\"password_enc\" => $cryptPassword,\n\t\t\t\t\"homedir\" => Settings::Get('system.vmail_homedir'),\n\t\t\t\t\"maildir\" => $customer['loginname'] . '/' . $email_domain . \"/\" . $email_user . \"/\" . $maildirpath,\n\t\t\t\t\"uid\" => Settings::Get('system.vmail_uid'),\n\t\t\t\t\"gid\" => Settings::Get('system.vmail_gid'),\n\t\t\t\t\"domainid\" => $result['domainid'],\n\t\t\t\t\"quota\" => $quota,\n\t\t\t\t\"imap\" => $customer['imap'],\n\t\t\t\t\"pop3\" => $customer['pop3']\n\t\t\t];\n\t\t\tif (Settings::Get('system.mailpwcleartext') == '1') {\n\t\t\t\t$params[\"password\"] = $password;\n\t\t\t}\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t\t$popaccountid = Database::lastInsertId();\n\n\t\t\t// add email address to its destination field\n\t\t\t$result['destination'] .= ' ' . $email_full;\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_MAIL_VIRTUAL . \"`\tSET `destination` = :destination, `popaccountid` = :popaccountid\n\t\t\t\tWHERE `customerid`= :cid AND `id`= :id\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"destination\" => FileDir::makeCorrectDestination($result['destination']),\n\t\t\t\t\"popaccountid\" => $popaccountid,\n\t\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\t\"id\" => $id\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\n\t\t\t// update customer usage\n\t\t\tCustomers::increaseUsage($customer['customerid'], 'email_accounts_used');\n\t\t\tCustomers::increaseUsage($customer['customerid'], 'email_quota_used', '', $quota);\n\n\t\t\tif ($sendinfomail) {\n\t\t\t\t// replacer array for mail to create account on server\n\t\t\t\t$replace_arr = [\n\t\t\t\t\t'EMAIL' => $email_full,\n\t\t\t\t\t'PASSWORD' => htmlentities(htmlentities($password)),\n\t\t\t\t\t'SALUTATION' => User::getCorrectUserSalutation($customer),\n\t\t\t\t\t'NAME' => $customer['name'],\n\t\t\t\t\t'FIRSTNAME' => $customer['firstname'],\n\t\t\t\t\t'COMPANY' => $customer['company'],\n\t\t\t\t\t'USERNAME' => $customer['loginname'],\n\t\t\t\t\t'CUSTOMER_NO' => $customer['customernumber']\n\t\t\t\t];\n\n\t\t\t\t// get the customers admin\n\t\t\t\t$stmt = Database::prepare(\"SELECT `name`, `email` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `adminid`= :adminid\");\n\t\t\t\t$admin = Database::pexecute_first($stmt, [\n\t\t\t\t\t\"adminid\" => $customer['adminid']\n\t\t\t\t]);\n\n\t\t\t\t// get template for mail subject\n\t\t\t\t$mail_subject = $this->getMailTemplate($customer, 'mails', 'pop_success_subject', $replace_arr, lng('mails.pop_success.subject'));\n\t\t\t\t// get template for mail body\n\t\t\t\t$mail_body = $this->getMailTemplate($customer, 'mails', 'pop_success_mailbody', $replace_arr, lng('mails.pop_success.mailbody'));\n\n\t\t\t\t$_mailerror = false;\n\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\ttry {\n\t\t\t\t\t$this->mailer()->setFrom(Settings::Get('panel.adminmail'), User::getCorrectUserSalutation($admin));\n\t\t\t\t\t$this->mailer()->clearReplyTos();\n\t\t\t\t\t$this->mailer()->addReplyTo($admin['email'], User::getCorrectUserSalutation($admin));\n\t\t\t\t\t$this->mailer()->Subject = $mail_subject;\n\t\t\t\t\t$this->mailer()->AltBody = $mail_body;\n\t\t\t\t\t$this->mailer()->Body = str_replace(\"\\n\", \"<br />\", $mail_body);\n\t\t\t\t\t$this->mailer()->addAddress($email_full);\n\t\t\t\t\t$this->mailer()->send();\n\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t$_mailerror = true;\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t$_mailerror = true;\n\t\t\t\t}\n\n\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_ERR, \"[API] Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\tResponse::standardError('errorsendingmail', $email_full, true);\n\t\t\t\t}\n\n\t\t\t\t$this->mailer()->clearAddresses();\n\n\t\t\t\t// customer wants to send the e-mail to an alternative email address too\n\t\t\t\tif (Settings::Get('panel.sendalternativemail') == 1 && !empty($alternative_email)) {\n\t\t\t\t\t// get template for mail subject\n\t\t\t\t\t$mail_subject = $this->getMailTemplate($customer, 'mails', 'pop_success_alternative_subject', $replace_arr, lng('mails.pop_success_alternative.subject'));\n\t\t\t\t\t// get template for mail body\n\t\t\t\t\t$mail_body = $this->getMailTemplate($customer, 'mails', 'pop_success_alternative_mailbody', $replace_arr, lng('mails.pop_success_alternative.mailbody'));\n\n\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$this->mailer()->setFrom(Settings::Get('panel.adminmail'), User::getCorrectUserSalutation($admin));\n\t\t\t\t\t\t$this->mailer()->clearReplyTos();\n\t\t\t\t\t\t$this->mailer()->addReplyTo($admin['email'], User::getCorrectUserSalutation($admin));\n\t\t\t\t\t\t$this->mailer()->Subject = $mail_subject;\n\t\t\t\t\t\t$this->mailer()->AltBody = $mail_body;\n\t\t\t\t\t\t$this->mailer()->msgHTML(str_replace(\"\\n\", \"<br />\", $mail_body));\n\t\t\t\t\t\t$this->mailer()->addAddress($idna_convert->encode($alternative_email), User::getCorrectUserSalutation($customer));\n\t\t\t\t\t\t$this->mailer()->send();\n\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_ERR, \"[API] Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\t\tResponse::standardError([\n\t\t\t\t\t\t\t'errorsendingmail'\n\t\t\t\t\t\t], $alternative_email, true);\n\t\t\t\t\t}\n\n\t\t\t\t\t$this->mailer()->clearAddresses();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added email account for '\" . $result['email_full'] . \"'\");\n\t\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t\t'emailaddr' => $result['email_full']\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"No more resources available\", 406);\n\t}\n\n\t/**\n\t * You cannot directly get an email account.\n\t * You need to call Emails.get()\n\t */\n\tpublic function get()\n\t{\n\t\tthrow new Exception('You cannot directly get an email account. You need to call Emails.get()', 303);\n\t}\n\n\t/**\n\t * update email-account entry for given email-address by either id or email-address\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address to update\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param int $email_quota\n\t *            optional, update quota\n\t * @param string $email_password\n\t *            optional, update password\n\t * @param bool $deactivated\n\t *            optional, admin-only\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\n\t\tif (!empty($emailaddr)) {\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$emailaddr = $idna_convert->encode($emailaddr);\n\t\t}\n\n\t\t// validation\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif (empty($result['popaccountid']) || $result['popaccountid'] == 0) {\n\t\t\tthrow new Exception(\"Email address '\" . $result['email_full'] . \"' has no account assigned.\", 406);\n\t\t}\n\n\t\t$password = $this->getParam('email_password', true, '');\n\t\t$quota = $this->getParam('email_quota', true, $result['quota']);\n\t\t$deactivated = $this->getBoolParam('deactivated', true, strtolower($result['postfix']) == 'n');\n\n\t\t// get needed customer info to reduce the email-account-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\t// validation\n\t\t$quota = Validate::validate($quota, 'email_quota', '/^\\d+$/', 'vmailquotawrong', [], true);\n\n\t\t$upd_query = \"\";\n\t\t$upd_params = [\n\t\t\t\"id\" => $result['popaccountid'],\n\t\t\t\"cid\" => $customer['customerid']\n\t\t];\n\t\tif (!empty($password)) {\n\t\t\tif ($password == $result['email_full']) {\n\t\t\t\tResponse::standardError('passwordshouldnotbeusername', '', true);\n\t\t\t}\n\t\t\t$password = Crypt::validatePassword($password, true);\n\t\t\t// prefix hash-algo\n\t\t\tswitch (Settings::Get('system.passwordcryptfunc')) {\n\t\t\t\tcase 'argon2i':\n\t\t\t\t\t$cpPrefix = '{ARGON2I}';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'argon2id':\n\t\t\t\t\t$cpPrefix = '{ARGON2ID}';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t$cpPrefix = '{BLF-CRYPT}';\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// encrypt the password\n\t\t\t$cryptPassword = $cpPrefix . Crypt::makeCryptPassword($password);\n\t\t\t$upd_query .= (Settings::Get('system.mailpwcleartext') == '1' ? \"`password` = :password, \" : '') . \"`password_enc`= :password_enc\";\n\t\t\t$upd_params['password_enc'] = $cryptPassword;\n\t\t\tif (Settings::Get('system.mailpwcleartext') == '1') {\n\t\t\t\t$upd_params['password'] = $password;\n\t\t\t}\n\t\t}\n\n\t\tif (Settings::Get('system.mail_quota_enabled') == 1) {\n\t\t\tif ($quota != $result['quota']) {\n\t\t\t\tif ($customer['email_quota'] != '-1' && ($quota == 0 || ($quota + $customer['email_quota_used'] - $result['quota']) > $customer['email_quota'])) {\n\t\t\t\t\tResponse::standardError('allocatetoomuchquota', $quota, true);\n\t\t\t\t}\n\t\t\t\tif (!empty($upd_query)) {\n\t\t\t\t\t$upd_query .= \", \";\n\t\t\t\t}\n\t\t\t\t$upd_query .= \"`quota` = :quota\";\n\t\t\t\t$upd_params['quota'] = $quota;\n\t\t\t}\n\t\t} else {\n\t\t\t// disable\n\t\t\t$quota = 0;\n\t\t}\n\n\t\tif ($this->isAdmin()) {\n\t\t\tif (($deactivated == true && strtolower($result['postfix']) == 'y') || ($deactivated == false && strtolower($result['postfix']) == 'n')) {\n\t\t\t\tif (!empty($upd_query)) {\n\t\t\t\t\t$upd_query .= \", \";\n\t\t\t\t}\n\t\t\t\t$upd_query .= \"`postfix` = :postfix, `imap` = :imap, `pop3` = :pop3\";\n\t\t\t\t$upd_params['postfix'] = $deactivated ? 'N' : 'Y';\n\t\t\t\t$upd_params['imap'] = $deactivated ? '0' : '1';\n\t\t\t\t$upd_params['pop3'] = $deactivated ? '0' : '1';\n\t\t\t}\n\t\t}\n\n\t\t// build update query\n\t\tif (!empty($upd_query)) {\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_MAIL_USERS . \"` SET \" . $upd_query . \" WHERE `id` = :id AND `customerid`= :cid\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, $upd_params, true, true);\n\t\t}\n\n\t\tif ($customer['email_quota'] != '-1') {\n\t\t\tCustomers::increaseUsage($customer['customerid'], 'email_quota_used', '', ($quota - $result['quota']));\n\t\t\tAdmins::increaseUsage($customer['adminid'], 'email_quota_used', '', ($quota - $result['quota']));\n\t\t}\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] updated email account '\" . $result['email_full'] . \"'\");\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'emailaddr' => $result['email_full']\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * You cannot directly list email accounts.\n\t * You need to call Emails.listing()\n\t */\n\tpublic function listing()\n\t{\n\t\tthrow new Exception('You cannot directly list email accounts. You need to call Emails.listing()', 303);\n\t}\n\n\t/**\n\t * You cannot directly count email accounts.\n\t * You need to call Emails.listingCount()\n\t */\n\tpublic function listingCount()\n\t{\n\t\tthrow new Exception('You cannot directly count email accounts. You need to call Emails.listingCount()', 303);\n\t}\n\n\t/**\n\t * delete email-account entry for given email-address by either id or email-address\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address to delete the account for\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param bool $delete_userfiles\n\t *            optional, default false\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\t\t$delete_userfiles = $this->getBoolParam('delete_userfiles', true, 0);\n\n\t\t// validation\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t], true);\n\t\t$id = $result['id'];\n\n\t\tif (empty($result['popaccountid']) || $result['popaccountid'] == 0) {\n\t\t\tthrow new Exception(\"Email address '\" . $result['email_full'] . \"' has no account assigned.\", 406);\n\t\t}\n\n\t\t// get needed customer info to reduce the email-account-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\t// delete entry\n\t\t$stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_MAIL_USERS . \"` WHERE `customerid`= :cid AND `id`= :id\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\"id\" => $result['popaccountid']\n\t\t], true, true);\n\n\t\t// update mail-virtual entry\n\t\t$result['destination'] = str_replace($result['email_full'], '', $result['destination']);\n\n\t\t$stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_MAIL_VIRTUAL . \"` SET `destination` = :dest, `popaccountid` = '0' WHERE `customerid`= :cid AND `id`= :id\n\t\t\");\n\t\t$params = [\n\t\t\t\"dest\" => FileDir::makeCorrectDestination($result['destination']),\n\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\"id\" => $id\n\t\t];\n\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t$result['popaccountid'] = 0;\n\n\t\tif (Settings::Get('system.mail_quota_enabled') == '1' && $customer['email_quota'] != '-1') {\n\t\t\t$quota = (int)$result['quota'];\n\t\t} else {\n\t\t\t$quota = 0;\n\t\t}\n\n\t\tif ($delete_userfiles) {\n\t\t\tCronjob::inserttask(TaskId::DELETE_EMAIL_DATA, $customer['loginname'], FileDir::makeCorrectDir($result['homedir'] . '/' . $result['maildir']));\n\t\t}\n\n\t\t// decrease usage for customer\n\t\tCustomers::decreaseUsage($customer['customerid'], 'email_accounts_used');\n\t\tCustomers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $quota);\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, \"[API] deleted email account for '\" . $result['email_full'] . \"'\");\n\t\treturn $this->response($result);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/EmailDomains.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse PDO;\n\n/**\n * @since 2.0\n */\nclass EmailDomains extends ApiCommand implements ResourceEntity\n{\n\t/**\n\t * list all domains with email addresses connected to it.\n\t * If called from an admin, list all domains with email addresses\n\t * connected to it from all customers you are allowed to view, or\n\t * specify id or loginname for one specific customer\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select email addresses of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select email addresses of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t$customer_ids = $this->getAllowedCustomerIds('email');\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\tSELECT DISTINCT d.domain, d.domain_ace, e.domainid,\n\t\tCOUNT(e.email) as addresses,\n\t\tIFNULL(SUM(CASE WHEN e.popaccountid > 0 THEN 1 ELSE 0 END), 0) as accounts,\n\t\tIFNULL(SUM(\n\t\t\tCASE\n\t\t\tWHEN LENGTH(REPLACE(e.destination, CONCAT(e.email_full, ' '), '')) - LENGTH(REPLACE(REPLACE(e.destination, CONCAT(e.email_full, ' '), ''), ' ', '')) > 0\n\t\t\tTHEN LENGTH(REPLACE(e.destination, CONCAT(e.email_full, ' '), '')) - LENGTH(REPLACE(REPLACE(e.destination, CONCAT(e.email_full, ' '), ''), ' ', ''))\n\t\t\tWHEN e.destination <> e.email_full THEN 1\n\t\t\tELSE 0\n\t\t\tEND\n\t\t), 0) as forwarder\n\t\tFROM `\" . TABLE_MAIL_VIRTUAL . \"` e\n\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` d ON d.id = e.domainid\n\t\tWHERE e.customerid IN (\" . implode(\", \", $customer_ids) . \") AND d.domain IS NOT NULL \" .\n\t\t\t$this->getSearchWhere($query_fields,\n\t\t\t\ttrue) . \" GROUP BY e.domainid  \" . $this->getOrderBy() . $this->getLimit());\n\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$result[] = $row;\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO,\n\t\t\t\"[API] list email-domains\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of accessible domains with email addresses connected to\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select email addresses of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select email addresses of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\t$customer_ids = $this->getAllowedCustomerIds('email');\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\tSELECT COUNT(DISTINCT d.domain) as num_emaildomains\n\t\tFROM `\" . TABLE_MAIL_VIRTUAL . \"` e\n\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` d ON d.id = e.domainid\n\t\tWHERE e.customerid IN (\" . implode(\", \", $customer_ids) . \") AND d.domain IS NOT NULL\n\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_emaildomains']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * You cannot directly access email-domains\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\tthrow new Exception('You cannot directly access this resource.', 303);\n\t}\n\n\t/**\n\t * You cannot directly add email-domains\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\tthrow new Exception('You cannot directly add this resource.', 303);\n\t}\n\n\t/**\n\t * toggle catchall flag of given email address either by id or email-address\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\tthrow new Exception('You cannot directly update this resource.', 303);\n\t}\n\n\t/**\n\t * You cannot directly delete email-domains\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t\tthrow new Exception('You cannot directly delete this resource.', 303);\n\t}\n\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/EmailForwarders.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\n\n/**\n * @since 0.10.0\n */\nclass EmailForwarders extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add new email-forwarder entry for given email-address by either id or email-address\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address to add the forwarder for\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param string $destination\n\t *            email-address to add as forwarder\n\t *\n\t * @access admin,customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\tif ($this->getUserDetail('email_forwarders_used') < $this->getUserDetail('email_forwarders') || $this->getUserDetail('email_forwarders') == '-1') {\n\t\t\t// parameter\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$ea_optional = $id > 0;\n\t\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\t\t\t$destination = $this->getParam('destination');\n\n\t\t\t// validation\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$destination = $idna_convert->encode($destination);\n\n\t\t\tif (!empty($emailaddr)) {\n\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\t$emailaddr = $idna_convert->encode($emailaddr);\n\t\t\t}\n\n\t\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'emailaddr' => $emailaddr\n\t\t\t]);\n\t\t\t$id = $result['id'];\n\n\t\t\t// current destination array\n\t\t\t$result['destination_array'] = explode(' ', ($result['destination'] ?? \"\"));\n\n\t\t\t// prepare destination\n\t\t\t$destination = trim($destination);\n\n\t\t\tif (!Validate::validateEmail($destination)) {\n\t\t\t\tResponse::standardError('destinationiswrong', $destination, true);\n\t\t\t} elseif ($destination == $result['email']) {\n\t\t\t\tResponse::standardError('destinationalreadyexistasmail', $destination, true);\n\t\t\t} elseif (in_array($destination, $result['destination_array'])) {\n\t\t\t\tResponse::standardError('destinationalreadyexist', $destination, true);\n\t\t\t}\n\n\t\t\t// get needed customer info to reduce the email-forwarder-counter by one\n\t\t\t$customer = $this->getCustomerData('email_forwarders');\n\n\t\t\t// add destination to address\n\t\t\t$result['destination'] .= ' ' . $destination;\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_MAIL_VIRTUAL . \"` SET `destination` = :dest\n\t\t\t\tWHERE `customerid`= :cid AND `id`= :id\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"dest\" => FileDir::makeCorrectDestination($result['destination']),\n\t\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\t\"id\" => $id\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\n\t\t\t// update customer usage\n\t\t\tCustomers::increaseUsage($customer['customerid'], 'email_forwarders_used');\n\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added email forwarder for '\" . $result['email_full'] . \"'\");\n\n\t\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t\t'emailaddr' => $result['email_full']\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"No more resources available\", 406);\n\t}\n\n\t/**\n\t * You cannot directly get an email forwarder.\n\t * Try EmailForwarders.listing()\n\t */\n\tpublic function get()\n\t{\n\t\tthrow new Exception('You cannot directly get an email forwarder. Try EmailForwarders.listing()', 303);\n\t}\n\n\t/**\n\t * You cannot update an email forwarder.\n\t * You need to delete the entry and create a new one.\n\t */\n\tpublic function update()\n\t{\n\t\tthrow new Exception('You cannot update an email forwarder. You need to delete the entry and create a new one.', 303);\n\t}\n\n\t/**\n\t * List email forwarders for a given email address\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address to delete the forwarder from\n\t * @param int $customerid\n\t *            optional, admin-only, the customer-id\n\t * @param string $loginname\n\t *            optional, admin-only, the loginname\n\t *\n\t * @access admin,customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\n\t\t// validation\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t$result['destination'] = explode(' ', $result['destination']);\n\t\t$destination = [];\n\t\tforeach ($result['destination'] as $index => $address) {\n\t\t\t$destination[] = [\n\t\t\t\t'id' => $index,\n\t\t\t\t'address' => $address\n\t\t\t];\n\t\t}\n\n\t\treturn $this->response([\n\t\t\t'count' => count($destination),\n\t\t\t'list' => $destination\n\t\t]);\n\t}\n\n\t/**\n\t * count email forwarders for a given email address\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address to delete the forwarder from\n\t * @param int $customerid\n\t *            optional, admin-only, the customer-id\n\t * @param string $loginname\n\t *            optional, admin-only, the loginname\n\t *\n\t * @access admin,customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\n\t\t// validation\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t$result['destination'] = explode(' ', $result['destination']);\n\n\t\treturn $this->response(count($result['destination']));\n\t}\n\n\t/**\n\t * delete email-forwarder entry for given email-address by either id or email-address and forwarder-id\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address to delete the forwarder from\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param int $forwarderid\n\t *            id of the forwarder to delete\n\t *\n\t * @access admin,customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\t\t$forwarderid = $this->getParam('forwarderid');\n\n\t\t// validation\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t$result['destination'] = explode(' ', $result['destination']);\n\t\tif (isset($result['destination'][$forwarderid]) && $result['email'] != $result['destination'][$forwarderid]) {\n\t\t\t// get needed customer info to reduce the email-forwarder-counter by one\n\t\t\t$customer = $this->getCustomerData();\n\n\t\t\t// unset it from array\n\t\t\tunset($result['destination'][$forwarderid]);\n\t\t\t// rebuild destination-string\n\t\t\t$result['destination'] = implode(' ', $result['destination']);\n\t\t\t// update in DB\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_MAIL_VIRTUAL . \"` SET `destination` = :dest\n\t\t\t\tWHERE `customerid`= :cid AND `id`= :id\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"dest\" => FileDir::makeCorrectDestination($result['destination']),\n\t\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\t\"id\" => $id\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\n\t\t\t// update customer usage\n\t\t\tCustomers::decreaseUsage($customer['customerid'], 'email_forwarders_used');\n\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] deleted email forwarder for '\" . $result['email_full'] . \"'\");\n\n\t\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t\t'emailaddr' => $result['email_full']\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Unknown forwarder id\", 404);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/EmailSender.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\n\n/**\n * @since 2.3.0\n */\nclass EmailSender extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new sender email address for a given email-address either by id or emailaddr\n\t *\n\t * @param int $id\n\t *            optional id of email-address to add the allowed sender for (must have an account)\n\t * @param string $emailaddr\n\t *            optional address of email-address to add the allowed sender for (must have an account)\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param string $allowed_sender\n\t *            required email-address or @domain.tld notation (wildcard) of allowed sender entry for the given account\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\n\t\tif (Settings::Get('mail.enable_allow_sender') != '1') {\n\t\t\tthrow new Exception(\"Allowed-sender not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\t\t$allowed_sender = strtolower($this->getParam('allowed_sender'));\n\n\t\t// validation\n\t\t$idna_convert = new IdnaWrapper();\n\t\tif (!empty($emailaddr)) {\n\t\t\t$emailaddr = $idna_convert->encode($emailaddr);\n\t\t}\n\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif (empty($result['popaccountid'])) {\n\t\t\tResponse::standardError('emailhasnoaccount', $result['email_full'], true);\n\t\t}\n\n\t\tif (substr($allowed_sender, 0, 1) != '@') {\n\t\t\tif (!Validate::validateEmail($idna_convert->encode($allowed_sender))) {\n\t\t\t\tResponse::standardError('emailiswrong', $allowed_sender, true);\n\t\t\t}\n\t\t\tself::validateLocalDomainOwnership(explode(\"@\", $allowed_sender)[1] ?? \"\");\n\t\t} else {\n\t\t\tif (!Validate::validateDomain($idna_convert->encode(substr($allowed_sender, 1)))) {\n\t\t\t\tResponse::standardError('wildcardemailiswrong', substr($allowed_sender, 1), true);\n\t\t\t}\n\t\t\tself::validateLocalDomainOwnership(substr($allowed_sender, 1));\n\t\t}\n\n\t\t// get needed customer info\n\t\t$customer = $this->getCustomerData();\n\n\t\t// check whether account exists and if it belongs to the customer\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT `username`\n\t\t\tFROM `\" . TABLE_MAIL_USERS . \"`\n\t\t\tWHERE `id` = :id\n\t\t\tAND `customerid` = :cid\n\t\t\");\n\t\t$emailaccount = Database::pexecute_first($sel_stmt, [\n\t\t\t'id' => (int)$result['popaccountid'],\n\t\t\t'cid' => (int)$customer['customerid']\n\t\t]);\n\t\tif ($emailaccount && !empty($emailaccount['username'])) {\n\t\t\t// insert email sender\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT IGNORE INTO `\" . TABLE_MAIL_SENDER_ALIAS . \"` SET\n\t\t\t\t`email` = :email,\n\t\t\t\t`allowed_sender` = :allowed_sender\n\t\t\t\");\n\t\t\t$result = [\n\t\t\t\t'email' => $emailaccount['username'],\n\t\t\t\t'allowed_sender' => $allowed_sender\n\t\t\t];\n\t\t\tDatabase::pexecute($ins_stmt, $result);\n\t\t\t$result['id'] = Database::lastInsertId();\n\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] added email-sender alias '\" . $result['email'] . \"' for account '\" . $result['allowed_sender'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Email account for email-address \" . $result['email_full'] . \" could not be found\", 404);\n\t}\n\n\t/**\n\t * You cannot update an email sender alias.\n\t * You need to delete the entry and create a new one.\n\t */\n\tpublic function update()\n\t{\n\t\tthrow new Exception('You cannot update an email sender alias. You need to delete the entry and create a new one.', 303);\n\t}\n\n\t/**\n\t * You cannot directly get an email sender alias.\n\t * Try EmailSender.listing()\n\t */\n\tpublic function get()\n\t{\n\t\tthrow new Exception('You cannot directly get an email sender alias. Try EmailSender.listing()', 303);\n\t}\n\n\t/**\n\t * List email senders for a given email address\n\t *\n\t * @param int $id\n\t *            optional, the id of the email-address to list allowed senders from\n\t * @param string $emailaddr\n\t *            optional, the email-address to list allowed senders from\n\t * @param int $customerid\n\t *            optional, admin-only, the customer-id\n\t * @param string $loginname\n\t *            optional, admin-only, the loginname\n\t *\n\t * @access admin,customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif (Settings::Get('mail.enable_allow_sender') != '1') {\n\t\t\tthrow new Exception(\"Allowed-sender not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\n\t\t// validation\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif (empty($result['popaccountid'])) {\n\t\t\treturn $this->response([\n\t\t\t\t'count' => 0,\n\t\t\t\t'list' => []\n\t\t\t]);\n\t\t} else {\n\t\t\t$sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT s.*\n\t\t\t\tFROM `\" . TABLE_MAIL_SENDER_ALIAS . \"` s\n\t\t\t\tLEFT JOIN `\" . TABLE_MAIL_USERS . \"` u ON u.username = s.email\n\t\t\t\tWHERE u.id = :popaccountid AND u.customerid = :cid\n\t\t\t\");\n\t\t\tDatabase::pexecute($sel_stmt, ['popaccountid' => (int)$result['popaccountid'], 'cid' => $result['customerid']]);\n\t\t\t$senders = [];\n\t\t\twhile ($row = $sel_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t\t$senders[] = $row;\n\t\t\t}\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list email-senders for '\" . $result['email_full'] . \"'\");\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($senders),\n\t\t\t\t'list' => $senders\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * returns the total number of allowed sender addresses for a given email address\n\t *\n\t * @param int $id\n\t * \t\t\toptional, the id of the email-address to list allowed senders from\n\t * @param string $emailaddr\n\t * \t\t\toptional, the email-address to list allowed senders from\n\t * @param int $customerid\n\t * \t\t\toptional, admin-only, the customer-id\n\t * @param string $loginname\n\t * \t\t\toptional, admin-only, the loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif (Settings::Get('mail.enable_allow_sender') != '1') {\n\t\t\tthrow new Exception(\"Allowed-sender not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\n\t\t// validation\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif (empty($result['popaccountid'])) {\n\t\t\treturn $this->response(0);\n\t\t} else {\n\t\t\t$sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as cnt\n\t\t\t\tFROM `\" . TABLE_MAIL_SENDER_ALIAS . \"` s\n\t\t\t\tLEFT JOIN `\" . TABLE_MAIL_USERS . \"` u ON u.username = s.email\n\t\t\t\tWHERE u.id = :popaccountid AND u.customerid = :cid\n\t\t\t\");\n\t\t\t$sender_cnt = Database::pexecute_first($sel_stmt, ['popaccountid' => (int)$result['popaccountid'], 'cid' => $result['customerid']]);\n\n\t\t\treturn $this->response($sender_cnt['cnt']);\n\t\t}\n\t}\n\n\t/**\n\t * delete email-sender entry for given email-address by either id or email-address and sender-id\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address to delete the forwarder from\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param int $senderid\n\t *            id of the sender to delete\n\t *\n\t * @access admin,customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif (Settings::Get('mail.enable_allow_sender') != '1') {\n\t\t\tthrow new Exception(\"Allowed-sender not enabled on this system\", 405);\n\t\t}\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// parameter\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\t\t$senderid = $this->getParam('senderid');\n\n\t\t// validation\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif (!empty($result['popaccountid'])) {\n\t\t\t// get needed customer info\n\t\t\t$customer = $this->getCustomerData();\n\n\t\t\t$sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT s.id\n\t\t\t\tFROM `\" . TABLE_MAIL_SENDER_ALIAS . \"` s\n\t\t\t\tLEFT JOIN `\" . TABLE_MAIL_USERS . \"` u ON u.username = s.email\n\t\t\t\tWHERE u.id = :popaccountid AND u.customerid = :cid AND s.id = :senderid\n\t\t\t\");\n\t\t\t$sender_result = Database::pexecute_first($sel_stmt, [\n\t\t\t\t'popaccountid' => (int)$result['popaccountid'],\n\t\t\t\t'cid' => $customer['customerid'],\n\t\t\t\t'senderid' => $senderid\n\t\t\t]);\n\t\t\tif ($sender_result && $sender_result['id'] == $senderid) {\n\t\t\t\t$del_stmt = Database::prepare(\"DELETE FROM `\" . TABLE_MAIL_SENDER_ALIAS . \"` WHERE `id` = :senderid\");\n\t\t\t\tDatabase::pexecute($del_stmt, ['senderid' => $senderid]);\n\n\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] deleted email sender for '\" . $result['email_full'] . \"'\");\n\t\t\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t\t\t'emailaddr' => $result['email_full']\n\t\t\t\t]);\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"Unknown sender id\", 404);\n\t}\n\n\tprivate static function validateLocalDomainOwnership(string $domain): void\n\t{\n\t\t// check whether the used domain belongs to the customer if it's a domain on this system\n\t\t$sel_stmt = Database::prepare(\"SELECT customerid FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `domain` = :domain\");\n\t\t$domain_result = Database::pexecute_first($sel_stmt, ['domain' => $domain]);\n\t\tif ($domain_result && $domain_result['customerid'] != CurrentUser::getField('customerid')) {\n\t\t\t// domain exists in our system but not owned by current user\n\t\t\tResponse::standardError('senderdomainnotowned', $domain, true);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Emails.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Emails extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new email address\n\t *\n\t * @param string $email_part\n\t *            name of the address before @\n\t * @param string $domain\n\t *            domain-name for the email-address\n\t * @param float $spam_tag_level\n\t *            optional, score which is required to tag emails as spam, default: 7.0\n\t * @param bool $rewrite_subject\n\t *            optional, whether to add ***SPAM*** to the email's subject if applicable, default: [antispam.default_spam_rewrite_subject]\n\t * @param float $spam_kill_level\n\t *            optional, score which is required to discard emails, default: 14.0\n\t * @param boolean $bypass_spam\n\t *            optional, disable spam-filter entirely, default: [antispam.default_bypass_spam]\n\t * @param boolean $policy_greylist\n\t *            optional, enable grey-listing, default: [antispam.default_policy_greylist]\n\t * @param boolean $iscatchall\n\t *            optional, make this address a catchall address, default: no\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param string $description\n\t *            optional custom description (currently not used/shown in the frontend), default empty\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\tif ($this->getUserDetail('emails_used') < $this->getUserDetail('emails') || $this->getUserDetail('emails') == '-1') {\n\t\t\t// required parameters\n\t\t\t$email_part = $this->getParam('email_part');\n\t\t\t$domain = $this->getParam('domain');\n\n\t\t\t// parameters\n\t\t\t$spam_tag_level = $this->getParam('spam_tag_level', true, '7.0');\n\t\t\t$spam_kill_level = $this->getUlParam('spam_kill_level', 'spam_kill_level_ul', true, '14.0');\n\t\t\t$iscatchall = $this->getBoolParam('iscatchall', true, 0);\n\t\t\t$description = $this->getParam('description', true, '');\n\n\t\t\tif ((int)Settings::Get('antispam.default_spam_rewrite_subject') <= 2) {\n\t\t\t\t$rewrite_subject = $this->getBoolParam('rewrite_subject', true, (int)Settings::Get('antispam.default_spam_rewrite_subject') == 1 ? 1 : 0);\n\t\t\t} else {\n\t\t\t\t$rewrite_subject = (int)Settings::Get('antispam.default_spam_rewrite_subject') == 3 ? 1 : 0;\n\t\t\t}\n\t\t\tif ((int)Settings::Get('antispam.default_bypass_spam') <= 2) {\n\t\t\t\t$bypass_spam = $this->getBoolParam('bypass_spam', true, (int)Settings::Get('antispam.default_bypass_spam') == 1 ? 1 : 0);\n\t\t\t} else {\n\t\t\t\t$bypass_spam = (int)Settings::Get('antispam.default_bypass_spam') == 3 ? 1 : 0;\n\t\t\t}\n\t\t\tif ((int)Settings::Get('antispam.default_policy_greylist') <= 2) {\n\t\t\t\t$policy_greylist = $this->getBoolParam('policy_greylist', true, (int)Settings::Get('antispam.default_policy_greylist') == 1 ? 1 : 0);\n\t\t\t} else {\n\t\t\t\t$policy_greylist = (int)Settings::Get('antispam.default_policy_greylist') == 3 ? 1 : 0;\n\t\t\t}\n\n\t\t\t// validation\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\tif (substr($domain, 0, 4) != 'xn--') {\n\t\t\t\t$domain = $idna_convert->encode(Validate::validate($domain, 'domain', '', '', [], true));\n\t\t\t}\n\t\t\t$email_part = $idna_convert->encode(strtolower($email_part));\n\n\t\t\t// check domain and whether it's an email-enabled domain\n\t\t\t// use internal call because the customer might have 'domains' in customer_hide_options\n\t\t\t$domain_check = $this->apiCall('SubDomains.get', [\n\t\t\t\t'domainname' => $domain\n\t\t\t], true);\n\t\t\tif ((int)$domain_check['isemaildomain'] == 0) {\n\t\t\t\tResponse::standardError('maindomainnonexist', $idna_convert->decode($domain), true);\n\t\t\t}\n\t\t\tif ((int)$domain_check['deactivated'] == 1) {\n\t\t\t\tResponse::standardError('maindomaindeactivated', $idna_convert->decode($domain), true);\n\t\t\t}\n\n\t\t\tif (Settings::Get('catchall.catchall_enabled') != '1') {\n\t\t\t\t$iscatchall = 0;\n\t\t\t}\n\n\t\t\t// check for catchall-flag\n\t\t\tif ($iscatchall) {\n\t\t\t\t$iscatchall = '1';\n\t\t\t\t$email = '@' . $domain;\n\t\t\t} else {\n\t\t\t\t$iscatchall = '0';\n\t\t\t\t$email = $email_part . '@' . $domain;\n\t\t\t}\n\n\t\t\t// full email value\n\t\t\t$email_full = $email_part . '@' . $domain;\n\n\t\t\t// validate it\n\t\t\tif (!Validate::validateEmail($email_full)) {\n\t\t\t\tResponse::standardError('emailiswrong', $idna_convert->decode($email_full), true);\n\t\t\t}\n\n\t\t\t// get needed customer info to reduce the email-address-counter by one\n\t\t\t$customer = $this->getCustomerData('emails');\n\n\t\t\t// duplicate check\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tSELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid` FROM `\" . TABLE_MAIL_VIRTUAL . \"`\n\t\t\t\tWHERE (`email` = :email OR `email_full` = :emailfull )\n\t\t\t\tAND `customerid`= :cid\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"email\" => $email,\n\t\t\t\t\"emailfull\" => $email_full,\n\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t];\n\t\t\t$email_check = Database::pexecute_first($stmt, $params, true, true);\n\n\t\t\tif ($email_check) {\n\t\t\t\tif (strtolower($email_check['email_full']) == strtolower($email_full)) {\n\t\t\t\t\tResponse::standardError('emailexistalready', $idna_convert->decode($email_full), true);\n\t\t\t\t} elseif ($email_check['email'] == $email) {\n\t\t\t\t\tResponse::standardError('youhavealreadyacatchallforthisdomain', '', true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\\d{1,}(\\.\\d{1})?$/', '', [7.0], true);\n\t\t\tif ($spam_kill_level > -1) {\n\t\t\t\t$spam_kill_level = Validate::validate($spam_kill_level, 'spam_kill_level', '/^\\d{1,}(\\.\\d{1})?$/', '', [14.0], true);\n\t\t\t}\n\t\t\t$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_MAIL_VIRTUAL . \"` SET\n\t\t\t\t`customerid` = :cid,\n\t\t\t\t`email` = :email,\n\t\t\t\t`email_full` = :email_full,\n\t\t\t\t`spam_tag_level` = :spam_tag_level,\n\t\t\t\t`rewrite_subject` = :rewrite_subject,\n\t\t\t\t`spam_kill_level` = :spam_kill_level,\n\t\t\t\t`bypass_spam` = :bypass_spam,\n\t\t\t\t`policy_greylist` = :policy_greylist,\n\t\t\t\t`iscatchall` = :iscatchall,\n\t\t\t\t`domainid` = :domainid,\n\t\t\t\t`description` = :description\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\t\"email\" => $email,\n\t\t\t\t\"email_full\" => $email_full,\n\t\t\t\t\"spam_tag_level\" => $spam_tag_level,\n\t\t\t\t\"rewrite_subject\" => $rewrite_subject,\n\t\t\t\t\"spam_kill_level\" => $spam_kill_level,\n\t\t\t\t\"bypass_spam\" => $bypass_spam,\n\t\t\t\t\"policy_greylist\" => $policy_greylist,\n\t\t\t\t\"iscatchall\" => $iscatchall,\n\t\t\t\t\"domainid\" => $domain_check['id'],\n\t\t\t\t\"description\" => $description\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\n\t\t\t// update customer usage\n\t\t\tCustomers::increaseUsage($customer['customerid'], 'emails_used');\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_RSPAMD);\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added email address '\" . $email_full . \"'\");\n\n\t\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t\t'emailaddr' => $email_full\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"No more resources available\", 406);\n\t}\n\n\t/**\n\t * return a email-address entry by either id or email-address\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\n\t\t$params = [];\n\t\t$customer_ids = $this->getAllowedCustomerIds('email');\n\t\t$params['idea'] = ($id <= 0 ? $emailaddr : $id);\n\n\t\t$result_stmt = Database::prepare(\"SELECT v.*, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize` \" . ($this->isInternal() ? \", `u`.`homedir`, `u`.`maildir`\" : \"\") . \"\n\t\t\tFROM `\" . TABLE_MAIL_VIRTUAL . \"` v\n\t\t\tLEFT JOIN `\" . TABLE_MAIL_USERS . \"` u ON v.`popaccountid` = u.`id`\n\t\t\tWHERE v.`customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\tAND \" . (is_numeric($params['idea']) ? \"v.`id`= :idea\" : \"(v.`email` = :idea OR v.`email_full` = :idea)\"\n\t\t\t));\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get email address '\" . $result['email_full'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\t$key = ($id > 0 ? \"id #\" . $id : \"emailaddr '\" . $emailaddr . \"'\");\n\t\tthrow new Exception(\"Email address with \" . $key . \" could not be found\", 404);\n\t}\n\n\t/**\n\t * toggle catchall flag of given email address either by id or email-address\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param float $spam_tag_level\n\t *            optional, score which is required to tag emails as spam, default: 7.0\n\t * @param bool $rewrite_subject\n\t *              optional, whether to add ***SPAM*** to the email's subject if applicable, default: [antispam.default_spam_rewrite_subject]\n\t * @param float $spam_kill_level\n\t *            optional, score which is required to discard emails, default: 14.0\n\t * @param boolean $bypass_spam\n\t *            optional, disable spam-filter entirely, default: [antispam.default_bypass_spam]\n\t * @param boolean $policy_greylist\n\t *            optional, enable grey-listing, default: [antispam.default_policy_greylist]\n\t * @param boolean $iscatchall\n\t *            optional\n\t * @param string $description\n\t *            optional custom description (currently not used/shown in the frontend), default empty\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// parameters\n\t\t$spam_tag_level = $this->getParam('spam_tag_level', true, $result['spam_tag_level']);\n\t\t$spam_kill_level = $this->getUlParam('spam_kill_level', 'spam_kill_level_ul', true, $result['spam_kill_level']);\n\t\t$iscatchall = $this->getBoolParam('iscatchall', true, $result['iscatchall']);\n\t\t$description = $this->getParam('description', true, $result['description']);\n\n\t\tif ((int)Settings::Get('antispam.default_spam_rewrite_subject') <= 2) {\n\t\t\t$rewrite_subject = $this->getBoolParam('rewrite_subject', true, $result['rewrite_subject']);\n\t\t} else {\n\t\t\t$rewrite_subject = (int)Settings::Get('antispam.default_spam_rewrite_subject') == 3 ? 1 : 0;\n\t\t}\n\t\tif ((int)Settings::Get('antispam.default_bypass_spam') <= 2) {\n\t\t\t$bypass_spam = $this->getBoolParam('bypass_spam', true, $result['bypass_spam']);\n\t\t} else {\n\t\t\t$bypass_spam = (int)Settings::Get('antispam.default_bypass_spam') == 3 ? 1 : 0;\n\t\t}\n\t\tif ((int)Settings::Get('antispam.default_policy_greylist') <= 2) {\n\t\t\t$policy_greylist = $this->getBoolParam('policy_greylist', true, $result['policy_greylist']);\n\t\t} else {\n\t\t\t$policy_greylist = (int)Settings::Get('antispam.default_policy_greylist') == 3 ? 1 : 0;\n\t\t}\n\n\t\t// if enabling catchall is not allowed by settings, we do not need\n\t\t// to run update()\n\t\tif ($iscatchall && $result['iscatchall'] == 0 && Settings::Get('catchall.catchall_enabled') != '1') {\n\t\t\tResponse::standardError([\n\t\t\t\t'operationnotpermitted',\n\t\t\t\t'featureisdisabled'\n\t\t\t], 'catchall', true);\n\t\t}\n\n\t\t// get needed customer info to reduce the email-address-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\t// check for catchall-flag\n\t\t$email = $result['email_full'];\n\t\tif ($iscatchall) {\n\t\t\t$iscatchall = '1';\n\t\t\t$email = $result['email'];\n\t\t\t// update only required if it was not a catchall before\n\t\t\tif ($result['iscatchall'] == 0) {\n\t\t\t\t$email_parts = explode('@', $result['email_full']);\n\t\t\t\t$email = '@' . $email_parts[1];\n\t\t\t\t// catchall check\n\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `email_full` FROM `\" . TABLE_MAIL_VIRTUAL . \"`\n\t\t\t\t\tWHERE `email` = :email AND `customerid` = :cid AND `iscatchall` = '1'\n\t\t\t\t\");\n\t\t\t\t$params = [\n\t\t\t\t\t\"email\" => $email,\n\t\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t\t];\n\t\t\t\t$email_check = Database::pexecute_first($stmt, $params, true, true);\n\t\t\t\tif ($email_check) {\n\t\t\t\t\tResponse::standardError('youhavealreadyacatchallforthisdomain', '', true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\\d{1,}(\\.\\d{1,2})?$/', '', [7.0], true);\n\t\tif ($spam_kill_level > -1) {\n\t\t\t$spam_kill_level = Validate::validate($spam_kill_level, 'spam_kill_level', '/^\\d{1,}(\\.\\d{1,2})?$/', '', [14.0], true);\n\t\t}\n\t\t$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\t$stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_MAIL_VIRTUAL . \"` SET\n\t\t\t`email` = :email ,\n\t\t\t`spam_tag_level` = :spam_tag_level,\n\t\t\t`rewrite_subject` = :rewrite_subject,\n\t\t\t`spam_kill_level` = :spam_kill_level,\n\t\t\t`bypass_spam` = :bypass_spam,\n\t\t\t`policy_greylist` = :policy_greylist,\n\t\t\t`iscatchall` = :caflag,\n\t\t\t`description` = :description\n\t\t\tWHERE `customerid`= :cid AND `id`= :id\n\t\t\");\n\t\t$params = [\n\t\t\t\"email\" => $email,\n\t\t\t\"spam_tag_level\" => $spam_tag_level,\n\t\t\t\"rewrite_subject\" => $rewrite_subject,\n\t\t\t\"spam_kill_level\" => $spam_kill_level,\n\t\t\t\"bypass_spam\" => $bypass_spam,\n\t\t\t\"policy_greylist\" => $policy_greylist,\n\t\t\t\"caflag\" => $iscatchall,\n\t\t\t\"description\" => $description,\n\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\"id\" => $id\n\t\t];\n\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\tCronjob::inserttask(TaskId::REBUILD_RSPAMD);\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] toggled catchall-flag for email address '\" . $result['email_full'] . \"'\");\n\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'emailaddr' => $result['email_full']\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * list all email addresses, if called from an admin, list all email addresses of all customers you are allowed to\n\t * view, or specify id or loginname for one specific customer\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select email addresses of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select email addresses of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t$customer_ids = $this->getAllowedCustomerIds('email');\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT m.*, d.`domain`, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize`\n\t\t\tFROM `\" . TABLE_MAIL_VIRTUAL . \"` m\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` d ON (m.`domainid` = d.`id`)\n\t\t\tLEFT JOIN `\" . TABLE_MAIL_USERS . \"` u ON (m.`popaccountid` = u.`id`)\n\t\t\tWHERE m.`customerid` IN (\" . implode(\", \", $customer_ids) . \")\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\t$idna_convert = new IdnaWrapper();\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$row['email'] = $idna_convert->decode($row['email']);\n\t\t\t$row['email_full'] = $idna_convert->decode($row['email_full']);\n\t\t\t$result[] = $row;\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list email-addresses\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of accessible email addresses\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select email addresses of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select email addresses of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\t$customer_ids = $this->getAllowedCustomerIds('email');\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(*) as num_emails\n\t\t\tFROM `\" . TABLE_MAIL_VIRTUAL . \"` m\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` d ON (m.`domainid` = d.`id`)\n\t\t\tLEFT JOIN `\" . TABLE_MAIL_USERS . \"` u ON (m.`popaccountid` = u.`id`)\n\t\t\tWHERE m.`customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_emails']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * delete an email address by either id or username\n\t *\n\t * @param int $id\n\t *            optional, the email-address-id\n\t * @param string $emailaddr\n\t *            optional, the email-address\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param boolean $delete_userfiles\n\t *            optional, delete email data from filesystem, default: 0 (false)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$emailaddr = $this->getParam('emailaddr', $ea_optional, '');\n\n\t\t$result = $this->apiCall('Emails.get', [\n\t\t\t'id' => $id,\n\t\t\t'emailaddr' => $emailaddr\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// parameters\n\t\t$delete_userfiles = $this->getBoolParam('delete_userfiles', true, 0);\n\n\t\t// get needed customer info to reduce the email-address-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\t// check for forwarders\n\t\t$number_forwarders = 0;\n\t\tif ($result['destination'] != '') {\n\t\t\t$result['destination'] = explode(' ', $result['destination']);\n\t\t\t$number_forwarders = count($result['destination']);\n\t\t}\n\t\t// check whether this address is an account\n\t\tif ($result['popaccountid'] != 0) {\n\t\t\t// use EmailAccounts.delete\n\t\t\t$this->apiCall('EmailAccounts.delete', [\n\t\t\t\t'id' => $result['id'],\n\t\t\t\t'customerid' => $customer['customerid'],\n\t\t\t\t'delete_userfiles' => $delete_userfiles\n\t\t\t]);\n\t\t\t$number_forwarders--;\n\t\t}\n\n\t\t// decrease forwarder counter\n\t\tCustomers::decreaseUsage($customer['customerid'], 'email_forwarders_used', '', $number_forwarders);\n\t\tAdmins::decreaseUsage($customer['customerid'], 'email_forwarders_used', '', $number_forwarders);\n\n\t\t// delete address\n\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_MAIL_VIRTUAL . \"` WHERE `customerid`= :customerid AND `id`= :id\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\"id\" => $id\n\t\t], true, true);\n\t\tCustomers::decreaseUsage($customer['customerid'], 'emails_used');\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, \"[API] deleted email address '\" . $result['email_full'] . \"'\");\n\t\treturn $this->response($result);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/FpmDaemons.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass FpmDaemons extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * lists all fpm-daemon entries\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] list fpm-daemons\");\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_FPMDAEMONS . \"`\" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());\n\t\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\t\t$fpmdaemons = [];\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$query_params = [\n\t\t\t\t\t'id' => $row['id']\n\t\t\t\t];\n\n\t\t\t\t$configresult_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_PHPCONFIGS . \"` WHERE `fpmsettingid` = :id\");\n\t\t\t\tDatabase::pexecute($configresult_stmt, $query_params, true, true);\n\n\t\t\t\t$configs = [];\n\t\t\t\tif (Database::num_rows() > 0) {\n\t\t\t\t\twhile ($row2 = $configresult_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\t$configs[] = $row2['description'];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (empty($configs)) {\n\t\t\t\t\t$configs[] = lng('admin.phpsettings.notused');\n\t\t\t\t}\n\n\t\t\t\t$row['configs'] = $configs;\n\t\t\t\t$fpmdaemons[] = $row;\n\t\t\t}\n\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($fpmdaemons),\n\t\t\t\t'list' => $fpmdaemons\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns the total number of accessible fpm daemons\n\t *\n\t * @access admin\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_fpms FROM `\" . TABLE_PANEL_FPMDAEMONS . \"`\n\t\t\t\"  . $this->getSearchWhere($query_fields));\n\t\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_fpms']);\n\t\t\t}\n\t\t\treturn $this->response(0);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return a fpm-daemon entry by id\n\t *\n\t * @param int $id\n\t *            fpm-daemon-id\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t\tthrow new Exception(\"fpm-daemon with id #\" . $id . \" could not be found\", 404);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * create a new fpm-daemon entry\n\t *\n\t * @param string $description\n\t * @param string $reload_cmd\n\t * @param string $config_dir\n\t * @param string $pm\n\t *            optional, process-manager, one of 'static', 'dynamic' or 'ondemand', default 'dynamic'\n\t * @param int $max_children\n\t *            optional, default 5\n\t * @param int $start_servers\n\t *            optional, default 2\n\t * @param int $min_spare_servers\n\t *            optional, default 1\n\t * @param int $max_spare_servers\n\t *            optional, default 3\n\t * @param int $max_requests\n\t *            optional, default 0\n\t * @param int $idle_timeout\n\t *            optional, default 10\n\t * @param string $limit_extensions\n\t *            optional, limit execution to the following extensions, default '.php'\n\t * @param string $custom_config\n\t *            optional, custom settings appended to phpfpm pool configuration\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t// required parameter\n\t\t\t$description = $this->getParam('description');\n\t\t\t$reload_cmd = $this->getParam('reload_cmd');\n\t\t\t$config_dir = $this->getParam('config_dir');\n\n\t\t\t// parameters\n\t\t\t$pmanager = $this->getParam('pm', true, 'dynamic');\n\t\t\t$max_children = $this->getParam('max_children', true, 5);\n\t\t\t$start_servers = $this->getParam('start_servers', true, 2);\n\t\t\t$min_spare_servers = $this->getParam('min_spare_servers', true, 1);\n\t\t\t$max_spare_servers = $this->getParam('max_spare_servers', true, 3);\n\t\t\t$max_requests = $this->getParam('max_requests', true, 0);\n\t\t\t$idle_timeout = $this->getParam('idle_timeout', true, 10);\n\t\t\t$limit_extensions = $this->getParam('limit_extensions', true, '.php');\n\t\t\t$custom_config = $this->getParam('custom_config', true, '');\n\n\t\t\t// validation\n\t\t\t$description = Validate::validate($description, 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$reload_cmd = Validate::validate($reload_cmd, 'reload_cmd', '/^[a-z0-9\\/\\._\\-@ ]+$/i', '', [], true);\n\t\t\t$sel_stmt = Database::prepare(\"SELECT `id` FROM `\".TABLE_PANEL_FPMDAEMONS.\"` WHERE `reload_cmd` = :rc\");\n\t\t\t$dupcheck = Database::pexecute_first($sel_stmt, ['rc' => $reload_cmd]);\n\t\t\tif ($dupcheck && $dupcheck['id']) {\n\t\t\t\tthrow new Exception(\"PHP-FPM version with the given restart command already exists\", 406);\n\t\t\t}\n\t\t\t$config_dir = Validate::validate($config_dir, 'config_dir', Validate::REGEX_DIR, '', [], true);\n\t\t\tif (!in_array($pmanager, [\n\t\t\t\t'static',\n\t\t\t\t'dynamic',\n\t\t\t\t'ondemand'\n\t\t\t])) {\n\t\t\t\tthrow new Exception(\"Unknown process manager\", 406);\n\t\t\t}\n\t\t\tif (empty($limit_extensions)) {\n\t\t\t\t$limit_extensions = '.php';\n\t\t\t}\n\t\t\t$limit_extensions = Validate::validate($limit_extensions, 'limit_extensions', '/^(\\.[a-z]([a-z0-9]+)\\ ?)+$/', '', [], true);\n\n\t\t\tif (strlen($description) == 0 || strlen($description) > 50) {\n\t\t\t\tResponse::standardError('descriptioninvalid', '', true);\n\t\t\t}\n\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_FPMDAEMONS . \"` SET\n\t\t\t\t`description` = :desc,\n\t\t\t\t`reload_cmd` = :reload_cmd,\n\t\t\t\t`config_dir` = :config_dir,\n\t\t\t\t`pm` = :pm,\n\t\t\t\t`max_children` = :max_children,\n\t\t\t\t`start_servers` = :start_servers,\n\t\t\t\t`min_spare_servers` = :min_spare_servers,\n\t\t\t\t`max_spare_servers` = :max_spare_servers,\n\t\t\t\t`max_requests` = :max_requests,\n\t\t\t\t`idle_timeout` = :idle_timeout,\n\t\t\t\t`limit_extensions` = :limit_extensions,\n\t\t\t\t`custom_config` = :custom_config\n\t\t\t\");\n\t\t\t$ins_data = [\n\t\t\t\t'desc' => $description,\n\t\t\t\t'reload_cmd' => $reload_cmd,\n\t\t\t\t'config_dir' => FileDir::makeCorrectDir($config_dir),\n\t\t\t\t'pm' => $pmanager,\n\t\t\t\t'max_children' => $max_children,\n\t\t\t\t'start_servers' => $start_servers,\n\t\t\t\t'min_spare_servers' => $min_spare_servers,\n\t\t\t\t'max_spare_servers' => $max_spare_servers,\n\t\t\t\t'max_requests' => $max_requests,\n\t\t\t\t'idle_timeout' => $idle_timeout,\n\t\t\t\t'limit_extensions' => $limit_extensions,\n\t\t\t\t'custom_config' => $custom_config\n\t\t\t];\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\t\t\t$id = Database::lastInsertId();\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] fpm-daemon with description '\" . $description . \"' has been created by '\" . $this->getUserDetail('loginname') . \"'\");\n\t\t\t$result = $this->apiCall('FpmDaemons.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * update a fpm-daemon entry by given id\n\t *\n\t * @param int $id\n\t *            fpm-daemon id\n\t * @param string $description\n\t *            optional\n\t * @param string $reload_cmd\n\t *            optional\n\t * @param string $config_dir\n\t *            optional\n\t * @param string $pm\n\t *            optional, process-manager, one of 'static', 'dynamic' or 'ondemand', default 'dynamic'\n\t * @param int $max_children\n\t *            optional, default 5\n\t * @param int $start_servers\n\t *            optional, default 2\n\t * @param int $min_spare_servers\n\t *            optional, default 1\n\t * @param int $max_spare_servers\n\t *            optional, default 3\n\t * @param int $max_requests\n\t *            optional, default 0\n\t * @param int $idle_timeout\n\t *            optional, default 10\n\t * @param string $limit_extensions\n\t *            optional, limit execution to the following extensions, default '.php'\n\t * @param string $custom_config\n\t *            optional, custom settings appended to phpfpm pool configuration\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t// required parameter\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result = $this->apiCall('FpmDaemons.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\t// parameters\n\t\t\t$description = $this->getParam('description', true, $result['description']);\n\t\t\t$reload_cmd = $this->getParam('reload_cmd', true, $result['reload_cmd']);\n\t\t\t$config_dir = $this->getParam('config_dir', true, $result['config_dir']);\n\t\t\t$pmanager = $this->getParam('pm', true, $result['pm']);\n\t\t\t$max_children = $this->getParam('max_children', true, $result['max_children']);\n\t\t\t$start_servers = $this->getParam('start_servers', true, $result['start_servers']);\n\t\t\t$min_spare_servers = $this->getParam('min_spare_servers', true, $result['min_spare_servers']);\n\t\t\t$max_spare_servers = $this->getParam('max_spare_servers', true, $result['max_spare_servers']);\n\t\t\t$max_requests = $this->getParam('max_requests', true, $result['max_requests']);\n\t\t\t$idle_timeout = $this->getParam('idle_timeout', true, $result['idle_timeout']);\n\t\t\t$limit_extensions = $this->getParam('limit_extensions', true, $result['limit_extensions']);\n\t\t\t$custom_config = $this->getParam('custom_config', true, $result['custom_config']);\n\n\t\t\t// validation\n\t\t\t$description = Validate::validate($description, 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$reload_cmd = Validate::validate($reload_cmd, 'reload_cmd', '/^[a-z0-9\\/\\._\\-@ ]+$/i', '', [], true);\n\t\t\t$sel_stmt = Database::prepare(\"SELECT `id` FROM `\".TABLE_PANEL_FPMDAEMONS.\"` WHERE `reload_cmd` = :rc\");\n\t\t\t$dupcheck = Database::pexecute_first($sel_stmt, ['rc' => $reload_cmd]);\n\t\t\tif ($dupcheck && $dupcheck['id'] != $id) {\n\t\t\t\tthrow new Exception(\"PHP-FPM version with the given restart command already exists\", 406);\n\t\t\t}\n\t\t\t$config_dir = Validate::validate($config_dir, 'config_dir', Validate::REGEX_DIR, '', [], true);\n\t\t\tif (!in_array($pmanager, [\n\t\t\t\t'static',\n\t\t\t\t'dynamic',\n\t\t\t\t'ondemand'\n\t\t\t])) {\n\t\t\t\tthrow new Exception(\"Unknown process manager\", 406);\n\t\t\t}\n\t\t\tif (empty($limit_extensions)) {\n\t\t\t\t$limit_extensions = '.php';\n\t\t\t}\n\t\t\t$limit_extensions = Validate::validate($limit_extensions, 'limit_extensions', '/^(\\.[a-z]([a-z0-9]+)\\ ?)+$/', '', [], true);\n\n\t\t\tif (strlen($description) == 0 || strlen($description) > 50) {\n\t\t\t\tResponse::standardError('descriptioninvalid', '', true);\n\t\t\t}\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_FPMDAEMONS . \"` SET\n\t\t\t\t`description` = :desc,\n\t\t\t\t`reload_cmd` = :reload_cmd,\n\t\t\t\t`config_dir` = :config_dir,\n\t\t\t\t`pm` = :pm,\n\t\t\t\t`max_children` = :max_children,\n\t\t\t\t`start_servers` = :start_servers,\n\t\t\t\t`min_spare_servers` = :min_spare_servers,\n\t\t\t\t`max_spare_servers` = :max_spare_servers,\n\t\t\t\t`max_requests` = :max_requests,\n\t\t\t\t`idle_timeout` = :idle_timeout,\n\t\t\t\t`limit_extensions` = :limit_extensions,\n\t\t\t\t`custom_config` = :custom_config\n\t\t\t\tWHERE `id` = :id\n\t\t\t\");\n\t\t\t$upd_data = [\n\t\t\t\t'desc' => $description,\n\t\t\t\t'reload_cmd' => $reload_cmd,\n\t\t\t\t'config_dir' => FileDir::makeCorrectDir($config_dir),\n\t\t\t\t'pm' => $pmanager,\n\t\t\t\t'max_children' => $max_children,\n\t\t\t\t'start_servers' => $start_servers,\n\t\t\t\t'min_spare_servers' => $min_spare_servers,\n\t\t\t\t'max_spare_servers' => $max_spare_servers,\n\t\t\t\t'max_requests' => $max_requests,\n\t\t\t\t'idle_timeout' => $idle_timeout,\n\t\t\t\t'limit_extensions' => $limit_extensions,\n\t\t\t\t'custom_config' => $custom_config,\n\t\t\t\t'id' => $id\n\t\t\t];\n\t\t\tDatabase::pexecute($upd_stmt, $upd_data, true, true);\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] fpm-daemon with description '\" . $description . \"' has been updated by '\" . $this->getUserDetail('loginname') . \"'\");\n\t\t\t$result = $this->apiCall('FpmDaemons.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * delete a fpm-daemon entry by id\n\t *\n\t * @param int $id\n\t *            fpm-daemon-id\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t$id = $this->getParam('id');\n\n\t\t\tif ($id == 1) {\n\t\t\t\tResponse::standardError('cannotdeletedefaultphpconfig', '', true);\n\t\t\t}\n\n\t\t\t$result = $this->apiCall('FpmDaemons.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\t// set default fpm daemon config for all php-config that use this config that is to be deleted\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_PHPCONFIGS . \"` SET\n\t\t\t\t`fpmsettingid` = '1' WHERE `fpmsettingid` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] fpm-daemon setting '\" . $result['description'] . \"' has been deleted by '\" . $this->getUserDetail('loginname') . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Froxlor.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Database\\IntegrityCheck;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Install\\AutoUpdate;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\nuse Froxlor\\SImExporter;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\nuse RecursiveDirectoryIterator;\nuse RecursiveIteratorIterator;\nuse ReflectionClass;\nuse ReflectionException;\nuse ReflectionMethod;\n\n/**\n * @since 0.10.0\n */\nclass Froxlor extends ApiCommand\n{\n\n\tconst UPDATE_CHECK_INTERVAL = 21600; // 6 hrs\n\n\t/**\n\t * checks whether there is a newer version of froxlor available\n\t *\n\t * @param bool $force optional, force live update-check\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function checkUpdate()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\n\t\t\t$uc_data = Update::getUpdateCheckData();\n\n\t\t\t$force_ucheck = $this->getBoolParam('force', true, 0);\n\t\t\t$response = $uc_data['data'] ?? [];\n\n\t\t\tif (empty($uc_data) || empty($response) || $uc_data['ts'] + self::UPDATE_CHECK_INTERVAL < time() || $uc_data['channel'] != Settings::Get('system.update_channel') || $force_ucheck) {\n\t\t\t\t// log our actions\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] checking for updates\");\n\n\t\t\t\t// check for new version\n\t\t\t\t$aucheck = AutoUpdate::checkVersion();\n\n\t\t\t\t$response = [];\n\t\t\t\tif ($aucheck == 1) {\n\t\t\t\t\t// anzeige über version-status mit ggfls. formular\n\t\t\t\t\t// zum update schritt #1 -> download\n\t\t\t\t\t$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel').' ' : ''), AutoUpdate::getFromResult('version'), $this->version]);\n\t\t\t\t\t$response = [\n\t\t\t\t\t\t'isnewerversion' => (int) !AutoUpdate::getFromResult('has_latest'),\n\t\t\t\t\t\t'version' => $this->version,\n\t\t\t\t\t\t'message' => $text,\n\t\t\t\t\t\t'link' => AutoUpdate::getFromResult('url'),\n\t\t\t\t\t\t'additional_info' => AutoUpdate::getFromResult('info'),\n\t\t\t\t\t\t'aucheck' => $aucheck\n\t\t\t\t\t];\n\t\t\t\t} elseif ($aucheck < 0 || $aucheck > 1) {\n\t\t\t\t\t// errors\n\t\t\t\t\tif ($aucheck < 0) {\n\t\t\t\t\t\t$errmsg = AutoUpdate::getLastError();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif ($aucheck == 3) {\n\t\t\t\t\t\t\t$errmsg = lng('error.customized_version');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$errmsg = lng('error.autoupdate_' . $aucheck);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$response = [\n\t\t\t\t\t\t'isnewerversion' => 0,\n\t\t\t\t\t\t'version' => $this->version,\n\t\t\t\t\t\t'message' => '',\n\t\t\t\t\t\t'link' => '',\n\t\t\t\t\t\t'additional_info' => $errmsg,\n\t\t\t\t\t\t'aucheck' => $aucheck\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\t$response = [\n\t\t\t\t\t\t'isnewerversion' => 0,\n\t\t\t\t\t\t'version' => $this->version,\n\t\t\t\t\t\t'message' => '',\n\t\t\t\t\t\t'link' => '',\n\t\t\t\t\t\t'additional_info' => AutoUpdate::getFromResult('info'),\n\t\t\t\t\t\t'aucheck' => $aucheck\n\t\t\t\t\t];\n\t\t\t\t}\n\n\t\t\t\tUpdate::storeUpdateCheckData($response);\n\t\t\t}\n\n\t\t\treturn $this->response($response);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * import settings\n\t *\n\t * @param string $json_str\n\t *            content of exported froxlor-settings json file\n\t *\n\t * @access admin\n\t * @return string json-encoded bool\n\t * @throws Exception\n\t */\n\tpublic function importSettings()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$json_str = $this->getParam('json_str');\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"User \" . $this->getUserDetail('loginname') . \" imported settings\");\n\t\t\ttry {\n\t\t\t\tSImExporter::import($json_str);\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\tCronjob::inserttask(TaskId::CREATE_QUOTA);\n\t\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t\t\t// cron.d file\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_CRON);\n\t\t\t\treturn $this->response(true);\n\t\t\t} catch (Exception $e) {\n\t\t\t\tthrow new Exception($e->getMessage(), 406);\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * export settings\n\t *\n\t * @access admin\n\t * @return string json-string\n\t * @throws Exception\n\t */\n\tpublic function exportSettings()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"User \" . $this->getUserDetail('loginname') . \" exported settings\");\n\t\t\t$json_export = SImExporter::export();\n\t\t\treturn $this->response($json_export);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return a list of all settings\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listSettings()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_SETTINGS . \"` ORDER BY settinggroup ASC, varname ASC\n\t\t\t\");\n\t\t\tDatabase::pexecute($sel_stmt, null, true, true);\n\t\t\t$result = [];\n\t\t\twhile ($row = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$result[] = [\n\t\t\t\t\t'key' => $row['settinggroup'] . '.' . $row['varname'],\n\t\t\t\t\t'value' => $row['value']\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($result),\n\t\t\t\t'list' => $result\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return a setting by settinggroup.varname couple\n\t *\n\t * @param string $key\n\t *            settinggroup.varname couple\n\t *\n\t * @access admin\n\t * @return string\n\t * @throws Exception\n\t */\n\tpublic function getSetting()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$setting = $this->getParam('key');\n\t\t\treturn $this->response(Settings::Get($setting));\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * updates a setting\n\t *\n\t * @param string $key\n\t *            settinggroup.varname couple\n\t * @param string $value\n\t *            optional the new value, default is ''\n\t *\n\t * @access admin\n\t * @return string\n\t * @throws Exception\n\t */\n\tpublic function updateSetting()\n\t{\n\t\t// currently not implemented as it requires validation too so no wrong settings are being stored via API\n\t\tthrow new Exception(\"Not available yet.\", 501);\n\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$setting = $this->getParam('key');\n\t\t\t$value = $this->getParam('value', true, '');\n\t\t\t$oldvalue = Settings::Get($setting);\n\t\t\tif (is_null($oldvalue)) {\n\t\t\t\tthrow new Exception(\"Setting '\" . $setting . \"' could not be found\");\n\t\t\t}\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] Changing setting '\" . $setting . \"' from '\" . $oldvalue . \"' to '\" . $value . \"'\");\n\t\t\treturn $this->response(Settings::Set($setting, $value, true));\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns a random password based on froxlor settings for min-length, included characters, etc.\n\t *\n\t * @param int $length\n\t *            optional length of password, defaults to 0 (panel.password_min_length)\n\t *\n\t * @access admin, customer\n\t * @return string\n\t * @throws Exception\n\t */\n\tpublic function generatePassword(): string\n\t{\n\t\t$length = $this->getParam('length', true, 0);\n\t\treturn $this->response(Crypt::generatePassword($length));\n\t}\n\n\t/**\n\t * return a one-time login link URL for a given user\n\t *\n\t * @param int $customerid optional, required if $loginname is not specified, user to create link for\n\t * @param string $loginname optional, required if $customerid is not specified, user to create link for\n\t * @param int $valid_time optional, value in seconds how long the link will be valid, default is 10 seconds, valid values are numbers from 10 to 120\n\t * @param string $allowed_from optional, comma separated list of ip addresses or networks to allow login from via this link\n\t *\n\t * @access admin\n\t * @return string json-encoded array [base => domain, uri => relative link]\n\t * @throws Exception\n\t */\n\tpublic function generateLoginLink()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$customer = $this->getCustomerData();\n\n\t\t\t// cannot create link for deactivated users\n\t\t\tif ((int)$customer['deactivated'] == 1) {\n\t\t\t\tthrow new Exception(\"Cannot generate link for deactivated user\", 406);\n\t\t\t}\n\n\t\t\t$valid_time = (int)$this->getParam('valid_time', true, 10);\n\t\t\t$allowed_from = $this->getParam('allowed_from', true, '');\n\n\t\t\t$valid_time = Validate::validate($valid_time, 'valid time', '/^(1[0-1][0-9]|120|[1-9][0-9])$/', 'invalid_validtime', [10], true);\n\n\t\t\t// validate allowed_from\n\t\t\tif (!empty($allowed_from)) {\n\t\t\t\t$ip_list = array_map('trim', explode(\",\", $allowed_from));\n\t\t\t\t$_check_list = $ip_list;\n\t\t\t\tforeach ($_check_list as $idx => $ip) {\n\t\t\t\t\tif (Validate::validate_ip2($ip, true, 'invalidip', true, true, true) == false) {\n\t\t\t\t\t\tthrow new Exception('Invalid ip address', 406);\n\t\t\t\t\t}\n\t\t\t\t\t// check for cidr\n\t\t\t\t\tif (strpos($ip, '/') !== false) {\n\t\t\t\t\t\t$ipparts = explode(\"/\", $ip);\n\t\t\t\t\t\t// shorten IP\n\t\t\t\t\t\t$ip = inet_ntop(inet_pton($ipparts[0]));\n\t\t\t\t\t\t// re-add cidr\n\t\t\t\t\t\t$ip .= '/' . $ipparts[1];\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// shorten IP\n\t\t\t\t\t\t$ip = inet_ntop(inet_pton($ip));\n\t\t\t\t\t}\n\t\t\t\t\t$ip_list[$idx] = $ip;\n\t\t\t\t}\n\t\t\t\t$allowed_from = implode(\",\", array_unique($ip_list));\n\t\t\t}\n\n\t\t\t$hash = hash('sha256', openssl_random_pseudo_bytes(64 * 64));\n\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_LOGINLINKS . \"`\n\t\t\t\tSET `hash` = :hash, `loginname` = :loginname, `valid_until` = :validuntil, `allowed_from` = :allowedfrom\n\t\t\t\tON DUPLICATE KEY UPDATE `hash` = :hash, `valid_until` = :validuntil, `allowed_from` = :allowedfrom\n\t\t\t\");\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'hash' => $hash,\n\t\t\t\t'loginname' => $customer['loginname'],\n\t\t\t\t'validuntil' => time() + $valid_time,\n\t\t\t\t'allowedfrom' => $allowed_from\n\t\t\t]);\n\n\t\t\treturn $this->response([\n\t\t\t\t'base' => 'https://' . Settings::Get('system.hostname') . '/' . (Settings::Get('system.froxlordirectlyviahostname') != 1 ? basename(\\Froxlor\\Froxlor::getInstallDir()) . '/' : ''),\n\t\t\t\t'uri' => 'index.php?action=ll&ln=' . $customer['loginname'] . '&h=' . $hash\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * can be used to remotely run the integritiy checks froxlor implements\n\t *\n\t * @access admin\n\t * @return string\n\t * @throws Exception\n\t */\n\tpublic function integrityCheck()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$integrity = new IntegrityCheck();\n\t\t\t$result = $integrity->checkAll();\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response(null, 204);\n\t\t\t}\n\t\t\tthrow new Exception(\"Some checks failed.\", 406);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns a list of all available api functions\n\t *\n\t * @param string $module\n\t *            optional, return list of functions for a specific module\n\t * @param string $function\n\t *            optional, return parameter information for a specific module and function\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function listFunctions()\n\t{\n\t\t$module = $this->getParam('module', true, '');\n\t\t$function = $this->getParam('function', true, '');\n\n\t\t$functions = [];\n\t\tif ($module != null) {\n\t\t\t// check existence\n\t\t\t$this->requireModules($module);\n\t\t\t// now get all static functions\n\t\t\t$reflection = new ReflectionClass(__NAMESPACE__ . '\\\\' . $module);\n\t\t\t$_functions = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);\n\t\t\tforeach ($_functions as $func) {\n\t\t\t\tif (empty($function) || ($function != null && $func->name == $function)) {\n\t\t\t\t\tif ($func->class == __NAMESPACE__ . '\\\\' . $module && $func->isPublic()) {\n\t\t\t\t\t\tarray_push($functions, array_merge([\n\t\t\t\t\t\t\t'module' => $module,\n\t\t\t\t\t\t\t'function' => $func->name\n\t\t\t\t\t\t], $this->getParamListFromDoc($module, $func->name)));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// check all the modules\n\t\t\t$path = \\Froxlor\\Froxlor::getInstallDir() . '/lib/Froxlor/Api/Commands/';\n\t\t\t// valid directory?\n\t\t\tif (is_dir($path)) {\n\t\t\t\t// create RecursiveIteratorIterator\n\t\t\t\t$its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));\n\t\t\t\t// check every file\n\t\t\t\tforeach ($its as $it) {\n\t\t\t\t\t// does it match the Filename pattern?\n\t\t\t\t\t$matches = [];\n\t\t\t\t\tif (preg_match(\"/^(.+)\\.php$/i\", $it->getFilename(), $matches)) {\n\t\t\t\t\t\t// check for existence\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// set the module to be in our namespace\n\t\t\t\t\t\t\t$mod = $matches[1];\n\t\t\t\t\t\t\t$this->requireModules($mod);\n\t\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t\t// @todo log?\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// now get all static functions\n\t\t\t\t\t\t$reflection = new ReflectionClass(__NAMESPACE__ . '\\\\' . $mod);\n\t\t\t\t\t\t$_functions = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);\n\t\t\t\t\t\tforeach ($_functions as $func) {\n\t\t\t\t\t\t\tif ($func->class == __NAMESPACE__ . '\\\\' . $mod && $func->isPublic() && !$func->isStatic()) {\n\t\t\t\t\t\t\t\tarray_push($functions, array_merge([\n\t\t\t\t\t\t\t\t\t'module' => $matches[1],\n\t\t\t\t\t\t\t\t\t'function' => $func->name\n\t\t\t\t\t\t\t\t], $this->getParamListFromDoc($matches[1], $func->name)));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// yikes - no valid directory to check\n\t\t\t\tthrow new Exception(\"Cannot search directory '\" . $path . \"'. No such directory.\", 500);\n\t\t\t}\n\t\t}\n\n\t\t// return the list\n\t\treturn $this->response($functions);\n\t}\n\n\t/**\n\t * this functions is used to check the availability\n\t * of a given list of modules.\n\t * If either one of\n\t * them are not found, throw an Exception\n\t *\n\t * @param string|array $modules\n\t *\n\t * @throws Exception\n\t */\n\tprivate function requireModules($modules = null)\n\t{\n\t\tif ($modules != null) {\n\t\t\t// no array -> create one\n\t\t\tif (!is_array($modules)) {\n\t\t\t\t$modules = [\n\t\t\t\t\t$modules\n\t\t\t\t];\n\t\t\t}\n\t\t\t// check all the modules\n\t\t\tforeach ($modules as $module) {\n\t\t\t\ttry {\n\t\t\t\t\t$module = __NAMESPACE__ . '\\\\' . $module;\n\t\t\t\t\t// can we use the class?\n\t\t\t\t\tif (class_exists($module)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new Exception('The required class \"' . $module . '\" could not be found but the module-file exists', 404);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t// The autoloader will throw an Exception\n\t\t\t\t\t// that the required class could not be found\n\t\t\t\t\t// but we want a nicer error-message for this here\n\t\t\t\t\tthrow new Exception('The required module \"' . $module . '\" could not be found', 404);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * generate an api-response to list all parameters and the return-value of\n\t * a given module.function-combination\n\t *\n\t * @param string $module\n\t * @param string $function\n\t *\n\t * @return array|bool\n\t * @throws Exception\n\t */\n\tprivate function getParamListFromDoc($module = null, $function = null)\n\t{\n\t\ttry {\n\t\t\t// set the module\n\t\t\t$cls = new ReflectionMethod(__NAMESPACE__ . '\\\\' . $module, $function);\n\t\t\t$comment = $cls->getDocComment();\n\t\t\tif ($comment == false) {\n\t\t\t\treturn [\n\t\t\t\t\t'head' => 'There is no comment-block for \"' . $module . '.' . $function . '\"'\n\t\t\t\t];\n\t\t\t}\n\n\t\t\t$clines = explode(\"\\n\", $comment);\n\t\t\t$result = [];\n\t\t\t$result['params'] = [];\n\t\t\t$param_desc = false;\n\t\t\t$r = [];\n\t\t\tforeach ($clines as $c) {\n\t\t\t\t$c = trim($c);\n\t\t\t\t// check param-section\n\t\t\t\tif (strpos($c, '@param')) {\n\t\t\t\t\tpreg_match('/^\\*\\s\\@param\\s(.+)\\s(\\$\\w+)(\\s.*)?/', $c, $r);\n\t\t\t\t\t// cut $ off the parameter-name as it is not wanted in the api-request\n\t\t\t\t\t$result['params'][] = [\n\t\t\t\t\t\t'parameter' => substr($r[2], 1),\n\t\t\t\t\t\t'type' => $r[1],\n\t\t\t\t\t\t'desc' => (isset($r[3]) ? trim($r['3']) : '')\n\t\t\t\t\t];\n\t\t\t\t\t$param_desc = true;\n\t\t\t\t} elseif (strpos($c, '@access')) {\n\t\t\t\t\t// check access-section\n\t\t\t\t\tpreg_match('/^\\*\\s\\@access\\s(.*)/', $c, $r);\n\t\t\t\t\tif (!isset($r[0]) || empty($r[0])) {\n\t\t\t\t\t\t$r[1] = 'This function has no restrictions';\n\t\t\t\t\t}\n\t\t\t\t\t$result['access'] = [\n\t\t\t\t\t\t'groups' => (isset($r[1]) ? trim($r[1]) : '')\n\t\t\t\t\t];\n\t\t\t\t} elseif (strpos($c, '@return')) {\n\t\t\t\t\t// check return-section\n\t\t\t\t\tpreg_match('/^\\*\\s\\@return\\s(\\w+)(\\s.*)?/', $c, $r);\n\t\t\t\t\tif (!isset($r[0]) || empty($r[0])) {\n\t\t\t\t\t\t$r[1] = 'null';\n\t\t\t\t\t\t$r[2] = 'This function has no return value given';\n\t\t\t\t\t}\n\t\t\t\t\t$result['return'] = [\n\t\t\t\t\t\t'type' => $r[1],\n\t\t\t\t\t\t'desc' => (isset($r[2]) ? trim($r[2]) : '')\n\t\t\t\t\t];\n\t\t\t\t} elseif (!empty($c) && strpos($c, '@throws') === false) {\n\t\t\t\t\t// check throws-section\n\t\t\t\t\tif (substr($c, 0, 3) == \"/**\") {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (substr($c, 0, 2) == \"*/\") {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (substr($c, 0, 1) == \"*\") {\n\t\t\t\t\t\t$c = trim(substr($c, 1));\n\t\t\t\t\t\tif (empty($c)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ($param_desc) {\n\t\t\t\t\t\t\t$result['params'][count($result['params']) - 1]['desc'] .= $c;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (!isset($result['head']) || empty($result['head'])) {\n\t\t\t\t\t\t\t\t$result['head'] = $c . \" \";\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$result['head'] .= $c . \" \";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t$result['head'] = trim($result['head']);\n\t\t\treturn $result;\n\t\t} catch (ReflectionException $e) {\n\t\t\treturn [];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Ftps.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Ftps extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new ftp-user\n\t *\n\t * @param string $ftp_password\n\t *            password for the created database and database-user\n\t * @param string $path\n\t *            destination path relative to the customers-homedir\n\t * @param string $ftp_description\n\t *            optional, description for ftp-user\n\t * @param bool $sendinfomail\n\t *            optional, send created resource-information to customer, default: false\n\t * @param string $shell\n\t *            optional, default /bin/false (not changeable when deactivated)\n\t * @param string $ftp_username\n\t *            optional if customer.ftpatdomain is allowed, specify an username\n\t * @param string $ftp_domain\n\t *            optional if customer.ftpatdomain is allowed, specify a domain (customer must be owner)\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t * @param array $additional_members\n\t *            optional whether to add additional usernames to the group\n\t * @param bool $is_defaultuser\n\t *            optional whether this is the standard default ftp user which is being added so no usage is decreased\n\t * @param bool $login_enabled\n\t *            optional whether to allow login (default) or not\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'ftp')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$is_defaultuser = $this->getBoolParam('is_defaultuser', true, 0);\n\t\t$login_enabled = $this->getBoolParam('login_enabled', true, 1);\n\n\t\tif (($this->getUserDetail('ftps_used') < $this->getUserDetail('ftps') || $this->getUserDetail('ftps') == '-1') || $this->isAdmin() && $is_defaultuser == 1) {\n\t\t\t// required parameters\n\t\t\t$path = $this->getParam('path');\n\t\t\t$password = $this->getParam('ftp_password');\n\n\t\t\t// parameters\n\t\t\t$description = $this->getParam('ftp_description', true, '');\n\t\t\t$sendinfomail = $this->getBoolParam('sendinfomail', true, 0);\n\t\t\t$shell = $this->getParam('shell', true, '/bin/false');\n\n\t\t\t$ftpusername = $this->getParam('ftp_username', true, '');\n\t\t\t$ftpdomain = $this->getParam('ftp_domain', true, '');\n\n\t\t\t$additional_members = $this->getParam('additional_members', true, []);\n\n\t\t\t// validation\n\t\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\t\t\t$password = Crypt::validatePassword($password, true);\n\t\t\t$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\t\t// get needed customer info to reduce the ftp-user-counter by one\n\t\t\tif ($is_defaultuser) {\n\t\t\t\t// no resource check for default user\n\t\t\t\t$customer = $this->getCustomerData();\n\t\t\t} else {\n\t\t\t\t$customer = $this->getCustomerData('ftps');\n\t\t\t}\n\n\t\t\tif (Settings::Get('system.allow_customer_shell') == '1' && $customer['shell_allowed'] == '1') {\n\t\t\t\t$shell = Validate::validate(trim($shell), 'shell', '', '', [], true);\n\t\t\t\t$availableshells = explode(',', Settings::Get('system.available_shells'));\n\t\t\t\tif (!is_array($availableshells) || empty($availableshells) || !in_array($shell, $availableshells)) {\n\t\t\t\t\t$shell = \"/bin/false\";\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$shell = \"/bin/false\";\n\t\t\t}\n\n\t\t\tif (Settings::Get('customer.ftpatdomain') == '1') {\n\t\t\t\t$ftpusername = Validate::validate(trim($ftpusername), 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\\-_]+\\$?$/', '', [], true);\n\t\t\t\tif (substr($ftpdomain, 0, 4) != 'xn--') {\n\t\t\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t\t\t$ftpdomain = $idna_convert->encode(Validate::validate($ftpdomain, 'domain', '', '', [], true));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$params = [];\n\n\t\t\tif ($sendinfomail != 1) {\n\t\t\t\t$sendinfomail = 0;\n\t\t\t}\n\n\t\t\tif (Settings::Get('customer.ftpatdomain') == '1' && !$is_defaultuser) {\n\t\t\t\tif ($ftpusername == '') {\n\t\t\t\t\tResponse::standardError([\n\t\t\t\t\t\t'stringisempty',\n\t\t\t\t\t\t'username'\n\t\t\t\t\t], '', true);\n\t\t\t\t}\n\t\t\t\t$ftpdomain_check_stmt = Database::prepare(\"SELECT `id`, `domain`, `customerid` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\t\tWHERE `domain` = :domain\n\t\t\t\t\t\tAND `customerid` = :customerid\");\n\t\t\t\t$ftpdomain_check = Database::pexecute_first($ftpdomain_check_stmt, [\n\t\t\t\t\t\"domain\" => $ftpdomain,\n\t\t\t\t\t\"customerid\" => $customer['customerid']\n\t\t\t\t], true, true);\n\n\t\t\t\tif ($ftpdomain_check && $ftpdomain_check['domain'] != $ftpdomain) {\n\t\t\t\t\tResponse::standardError('maindomainnonexist', $ftpdomain, true);\n\t\t\t\t}\n\t\t\t\t$username = $ftpusername . \"@\" . $ftpdomain;\n\t\t\t} else {\n\t\t\t\tif ($is_defaultuser) {\n\t\t\t\t\t$username = $customer['loginname'];\n\t\t\t\t} else {\n\t\t\t\t\t$username = $customer['loginname'] . Settings::Get('customer.ftpprefix') . (intval($customer['ftp_lastaccountnumber']) + 1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$username_check_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_FTP_USERS . \"`\tWHERE `username` = :username\n\t\t\t\");\n\t\t\t$username_check = Database::pexecute_first($username_check_stmt, [\n\t\t\t\t\"username\" => $username\n\t\t\t], true, true);\n\n\t\t\tif (!empty($username_check) && $username_check['username'] = $username) {\n\t\t\t\tResponse::standardError('usernamealreadyexists', $username, true);\n\t\t\t} elseif ($username == $password) {\n\t\t\t\tResponse::standardError('passwordshouldnotbeusername', '', true);\n\t\t\t} else {\n\t\t\t\t$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);\n\t\t\t\t$cryptPassword = Crypt::makeCryptPassword($password, false, true);\n\n\t\t\t\t$stmt = Database::prepare(\"INSERT INTO `\" . TABLE_FTP_USERS . \"`\n\t\t\t\t\t\t(`customerid`, `username`, `description`, `password`, `homedir`, `login_enabled`, `uid`, `gid`, `shell`)\n\t\t\t\t\t\tVALUES (:customerid, :username, :description, :password, :homedir, :loginenabled, :guid, :guid, :shell)\");\n\t\t\t\t$params = [\n\t\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\t\"username\" => $username,\n\t\t\t\t\t\"description\" => $description,\n\t\t\t\t\t\"password\" => $cryptPassword,\n\t\t\t\t\t\"homedir\" => $path,\n\t\t\t\t\t\"loginenabled\" => $login_enabled ? 'Y' : 'N',\n\t\t\t\t\t\"guid\" => $customer['guid'],\n\t\t\t\t\t\"shell\" => $shell\n\t\t\t\t];\n\t\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `bytes_in_used` FROM `\" . TABLE_FTP_QUOTATALLIES . \"` WHERE `name` = :name\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t\t\"name\" => $customer['loginname']\n\t\t\t\t], true, true);\n\n\t\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$stmt = Database::prepare(\"INSERT INTO `\" . TABLE_FTP_QUOTATALLIES . \"`\n\t\t\t\t\t\t(`name`, `quota_type`, `bytes_in_used`, `bytes_out_used`, `bytes_xfer_used`, `files_in_used`, `files_out_used`, `files_xfer_used`)\n\t\t\t\t\t\tVALUES (:name, 'user', :bytes_in_used, '0', '0', '0', '0', '0')\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\t\"name\" => $username,\n\t\t\t\t\t\t\"bytes_in_used\" => $row['bytes_in_used']\n\t\t\t\t\t], true, true);\n\t\t\t\t}\n\n\t\t\t\t// create quotatallies entry if it not exists, refs #885\n\t\t\t\tif ($result_stmt->rowCount() == 0) {\n\t\t\t\t\t$stmt = Database::prepare(\"INSERT INTO `\" . TABLE_FTP_QUOTATALLIES . \"`\n\t\t\t\t\t\t(`name`, `quota_type`, `bytes_in_used`, `bytes_out_used`, `bytes_xfer_used`, `files_in_used`, `files_out_used`, `files_xfer_used`)\n\t\t\t\t\t\tVALUES (:name, 'user', '0', '0', '0', '0', '0', '0')\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\t\"name\" => $username\n\t\t\t\t\t], true, true);\n\t\t\t\t}\n\n\t\t\t\t$group_upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_FTP_GROUPS . \"`\n\t\t\t\t\tSET `members` = CONCAT_WS(',',`members`, :username)\n\t\t\t\t\tWHERE `customerid`= :customerid AND `gid`= :guid\n\t\t\t\t\");\n\t\t\t\t$params = [\n\t\t\t\t\t\"username\" => $username,\n\t\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\t\"guid\" => $customer['guid']\n\t\t\t\t];\n\n\t\t\t\tif ($is_defaultuser) {\n\t\t\t\t\t// add the new group\n\t\t\t\t\t$group_ins_stmt = Database::prepare(\"\n\t\t\t\t\t\tINSERT INTO `\" . TABLE_FTP_GROUPS . \"`\n\t\t\t\t\t\tSET `customerid`= :customerid, `gid`= :guid, `groupname` = :username, `members` = :username\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($group_ins_stmt, $params, true, true);\n\t\t\t\t} else {\n\t\t\t\t\t// just update\n\t\t\t\t\tDatabase::pexecute($group_upd_stmt, $params, true, true);\n\t\t\t\t}\n\n\t\t\t\tif (count($additional_members) > 0) {\n\t\t\t\t\tforeach ($additional_members as $add_member) {\n\t\t\t\t\t\t$params = [\n\t\t\t\t\t\t\t\"username\" => $add_member,\n\t\t\t\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\t\t\t\"guid\" => $customer['guid']\n\t\t\t\t\t\t];\n\t\t\t\t\t\tDatabase::pexecute($group_upd_stmt, $params, true, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!$is_defaultuser) {\n\t\t\t\t\t// update customer usage\n\t\t\t\t\tCustomers::increaseUsage($customer['customerid'], 'ftps_used');\n\t\t\t\t\tCustomers::increaseUsage($customer['customerid'], 'ftp_lastaccountnumber');\n\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_NSSUSERS);\n\t\t\t\t}\n\n\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added ftp-account '\" . $username . \" (\" . $path . \")'\");\n\t\t\t\tCronjob::inserttask(TaskId::CREATE_FTP);\n\n\t\t\t\tif ($sendinfomail == 1) {\n\t\t\t\t\t$replace_arr = [\n\t\t\t\t\t\t'SALUTATION' => User::getCorrectUserSalutation($customer),\n\t\t\t\t\t\t'CUST_NAME' => User::getCorrectUserSalutation($customer),\n\t\t\t\t\t\t// < keep this for compatibility\n\t\t\t\t\t\t'NAME' => $customer['name'],\n\t\t\t\t\t\t'FIRSTNAME' => $customer['firstname'],\n\t\t\t\t\t\t'COMPANY' => $customer['company'],\n\t\t\t\t\t\t'USERNAME' => $customer['loginname'],\n\t\t\t\t\t\t'CUSTOMER_NO' => $customer['customernumber'],\n\t\t\t\t\t\t'USR_NAME' => $username,\n\t\t\t\t\t\t'USR_PASS' => htmlentities(htmlentities($password)),\n\t\t\t\t\t\t'USR_PATH' => FileDir::makeCorrectDir(str_replace($customer['documentroot'], \"/\", $path))\n\t\t\t\t\t];\n\t\t\t\t\t// get template for mail subject\n\t\t\t\t\t$mail_subject = $this->getMailTemplate($customer, 'mails', 'new_ftpaccount_by_customer_subject', $replace_arr, lng('mails.new_ftpaccount_by_customer.subject'));\n\t\t\t\t\t// get template for mail body\n\t\t\t\t\t$mail_body = $this->getMailTemplate($customer, 'mails', 'new_ftpaccount_by_customer_mailbody', $replace_arr, lng('mails.new_ftpaccount_by_customer.mailbody'));\n\n\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$this->mailer()->Subject = $mail_subject;\n\t\t\t\t\t\t$this->mailer()->AltBody = $mail_body;\n\t\t\t\t\t\t$this->mailer()->Body = str_replace(\"\\n\", \"<br />\", $mail_body);\n\t\t\t\t\t\t$this->mailer()->addAddress($customer['email'], User::getCorrectUserSalutation($customer));\n\t\t\t\t\t\t$this->mailer()->send();\n\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_ERR, \"[API] Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\t\tResponse::standardError('errorsendingmail', $customer['email'], true);\n\t\t\t\t\t}\n\n\t\t\t\t\t$this->mailer()->clearAddresses();\n\t\t\t\t}\n\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added ftp-user '\" . $username . \"'\");\n\n\t\t\t\t$result = $this->apiCall('Ftps.get', [\n\t\t\t\t\t'username' => $username\n\t\t\t\t]);\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"No more resources available\", 406);\n\t}\n\n\t/**\n\t * return a ftp-user entry by either id or username\n\t *\n\t * @param int $id\n\t *            optional, the customer-id\n\t * @param string $username\n\t *            optional, the username\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$un_optional = $id > 0;\n\t\t$username = $this->getParam('username', $un_optional, '');\n\n\t\t$params = [];\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($this->getUserDetail('customers_see_all') == false) {\n\t\t\t\t// if it's a reseller or an admin who cannot see all customers, we need to check\n\t\t\t\t// whether the database belongs to one of his customers\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t\t$customer_ids = [];\n\t\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t\t}\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_FTP_USERS . \"`\n\t\t\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\t\t\tAND (`id` = :idun OR `username` = :idun)\n\t\t\t\t\");\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_FTP_USERS . \"`\n\t\t\t\t\tWHERE (`id` = :idun OR `username` = :idun)\n\t\t\t\t\");\n\t\t\t}\n\t\t} else {\n\t\t\tif (Settings::IsInList('panel.customer_hide_options', 'ftp')) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_FTP_USERS . \"`\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\tAND (`id` = :idun OR `username` = :idun)\n\t\t\t\");\n\t\t\t$params['customerid'] = $this->getUserDetail('customerid');\n\t\t}\n\t\t$params['idun'] = ($id <= 0 ? $username : $id);\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get ftp-user '\" . $result['username'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\t$key = ($id > 0 ? \"id #\" . $id : \"username '\" . $username . \"'\");\n\t\tthrow new Exception(\"FTP user with \" . $key . \" could not be found\", 404);\n\t}\n\n\t/**\n\t * update a given ftp-user by id or username\n\t *\n\t * @param int $id\n\t *            optional, the ftp-user-id\n\t * @param string $username\n\t *            optional, the username\n\t * @param string $ftp_password\n\t *            optional, update password if specified\n\t * @param string $path\n\t *            destination path relative to the customers-homedir\n\t * @param string $ftp_description\n\t *            optional, description for ftp-user\n\t * @param string $shell\n\t *            optional, default /bin/false (not changeable when deactivated)\n\t * @param bool $login_enabled\n\t *            optional whether to allow login (default) or not\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'ftp')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$un_optional = $id > 0;\n\t\t$username = $this->getParam('username', $un_optional, '');\n\n\t\t$result = $this->apiCall('Ftps.get', [\n\t\t\t'id' => $id,\n\t\t\t'username' => $username\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// parameters\n\t\t$path = $this->getParam('path', true, '');\n\t\t$password = $this->getParam('ftp_password', true, '');\n\t\t$description = $this->getParam('ftp_description', true, $result['description']);\n\t\t$shell = $this->getParam('shell', true, $result['shell']);\n\t\t$login_enabled = $this->getBoolParam('login_enabled', true, ($result['login_enabled'] == 'Y' ? 1 : 0));\n\n\t\t// validation\n\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\t\t$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\t// get needed customer info to reduce the ftp-user-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\tif (Settings::Get('system.allow_customer_shell') == '1' && $customer['shell_allowed'] == '1') {\n\t\t\t$shell = Validate::validate(trim($shell), 'shell', '', '', [], true);\n\t\t\t$availableshells = explode(',', Settings::Get('system.available_shells'));\n\t\t\tif (!is_array($availableshells) || empty($availableshells) || !in_array($shell, $availableshells)) {\n\t\t\t\t$shell = \"/bin/false\";\n\t\t\t}\n\t\t} else {\n\t\t\t$shell = \"/bin/false\";\n\t\t}\n\n\t\tif ($login_enabled != 1) {\n\t\t\t$login_enabled = 0;\n\t\t}\n\n\t\t// password update?\n\t\tif ($password != '') {\n\t\t\t// validate password\n\t\t\t$password = Crypt::validatePassword($password, true);\n\n\t\t\tif ($password == $result['username']) {\n\t\t\t\tResponse::standardError('passwordshouldnotbeusername', '', true);\n\t\t\t}\n\t\t\t$cryptPassword = Crypt::makeCryptPassword($password, false, true);\n\n\t\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_FTP_USERS . \"`\n\t\t\t\tSET `password` = :password\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\tAND `id` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\"id\" => $id,\n\t\t\t\t\"password\" => $cryptPassword\n\t\t\t], true, true);\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] updated ftp-account password for '\" . $result['username'] . \"'\");\n\t\t}\n\n\t\t// path update?\n\t\tif ($path != '') {\n\t\t\t$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);\n\n\t\t\tif ($path != $result['homedir']) {\n\t\t\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_FTP_USERS . \"`\n\t\t\t\t\tSET `homedir` = :homedir\n\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\tAND `id` = :id\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\t\"homedir\" => $path,\n\t\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\t\"id\" => $id\n\t\t\t\t], true, true);\n\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] updated ftp-account homdir for '\" . $result['username'] . \"'\");\n\t\t\t}\n\t\t}\n\t\t// it's the task for \"new ftp\" but that will\n\t\t// create all directories and correct their permissions\n\t\tCronjob::inserttask(TaskId::CREATE_FTP);\n\t\tCronjob::inserttask(TaskId::REBUILD_NSSUSERS);\n\n\t\t$stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_FTP_USERS . \"`\n\t\t\tSET `description` = :desc, `shell` = :shell, `login_enabled` = :loginenabled\n\t\t\tWHERE `customerid` = :customerid\n\t\t\tAND `id` = :id\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"desc\" => $description,\n\t\t\t\"shell\" => $shell,\n\t\t\t\"loginenabled\" => $login_enabled ? 'Y' : 'N',\n\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\"id\" => $id\n\t\t], true, true);\n\n\t\t$result = $this->apiCall('Ftps.get', [\n\t\t\t'username' => $result['username']\n\t\t]);\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] updated ftp-user '\" . $result['username'] . \"'\");\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * list all ftp-users, if called from an admin, list all ftp-users of all customers you are allowed to view, or\n\t * specify id or loginname for one specific customer\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select ftp-users of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select ftp-users of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t$customer_ids = $this->getAllowedCustomerIds('ftp');\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_FTP_USERS . \"`\n\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \")\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$result[] = $row;\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list ftp-users\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of accessible ftp accounts\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select ftp-users of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select ftp-users of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\t$customer_ids = $this->getAllowedCustomerIds('ftp');\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(*) as num_ftps FROM `\" . TABLE_FTP_USERS . \"`\n\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_ftps']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * delete a ftp-user by either id or username\n\t *\n\t * @param int $id\n\t *            optional, the ftp-user-id\n\t * @param string $username\n\t *            optional, the username\n\t * @param bool $delete_userfiles\n\t *            optional, default false\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$un_optional = $id > 0;\n\t\t$username = $this->getParam('username', $un_optional, '');\n\t\t$delete_userfiles = $this->getBoolParam('delete_userfiles', true, 0);\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'ftp')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// get ftp-user\n\t\t$result = $this->apiCall('Ftps.get', [\n\t\t\t'id' => $id,\n\t\t\t'username' => $username\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif ($this->isAdmin()) {\n\t\t\t// get customer-data\n\t\t\t$customer_data = $this->apiCall('Customers.get', [\n\t\t\t\t'id' => $result['customerid']\n\t\t\t]);\n\t\t} else {\n\t\t\t$customer_data = $this->getUserData();\n\t\t}\n\n\t\t// add usage of this ftp-user to main-ftp user of customer if different\n\t\tif ($result['username'] != $customer_data['loginname']) {\n\t\t\t$stmt = Database::prepare(\"UPDATE `\" . TABLE_FTP_USERS . \"`\n\t\t\t\tSET `up_count` = `up_count` + :up_count,\n\t\t\t\t`up_bytes` = `up_bytes` + :up_bytes,\n\t\t\t\t`down_count` = `down_count` + :down_count,\n\t\t\t\t`down_bytes` = `down_bytes` + :down_bytes\n\t\t\t\tWHERE `username` = :username\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"up_count\" => $result['up_count'],\n\t\t\t\t\"up_bytes\" => $result['up_bytes'],\n\t\t\t\t\"down_count\" => $result['down_count'],\n\t\t\t\t\"down_bytes\" => $result['down_bytes'],\n\t\t\t\t\"username\" => $customer_data['loginname']\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t} else {\n\t\t\t// do not allow removing default ftp-account\n\t\t\tResponse::standardError('ftp_cantdeletemainaccount', '', true);\n\t\t}\n\n\t\t// remove all quotatallies\n\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_FTP_QUOTATALLIES . \"` WHERE `name` = :name\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"name\" => $result['username']\n\t\t], true, true);\n\n\t\t// remove user itself\n\t\t$stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_FTP_USERS . \"` WHERE `customerid` = :customerid AND `id` = :id\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"customerid\" => $customer_data['customerid'],\n\t\t\t\"id\" => $id\n\t\t], true, true);\n\n\t\t// update ftp-groups\n\t\t$stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_FTP_GROUPS . \"` SET\n\t\t\t`members` = REPLACE(`members`, :username,'')\n\t\t\tWHERE `customerid` = :customerid\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"username\" => \",\" . $result['username'],\n\t\t\t\"customerid\" => $customer_data['customerid']\n\t\t], true, true);\n\n\t\t// refs #293\n\t\tif ($delete_userfiles == 1) {\n\t\t\tCronjob::inserttask(TaskId::DELETE_FTP_DATA, $customer_data['loginname'], $result['homedir']);\n\t\t} else {\n\t\t\tif (Settings::Get('system.nssextrausers') == 1) {\n\t\t\t\t// this is used so that the libnss-extrausers cron is fired\n\t\t\t\tCronjob::inserttask(TaskId::CREATE_FTP);\n\t\t\t}\n\t\t}\n\t\tCronjob::inserttask(TaskId::REBUILD_NSSUSERS);\n\n\t\t// decrease ftp-user usage for customer\n\t\t$resetaccnumber = ($customer_data['ftps_used'] == '1') ? \" , `ftp_lastaccountnumber`='0'\" : '';\n\t\tCustomers::decreaseUsage($customer_data['customerid'], 'ftps_used', $resetaccnumber);\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, \"[API] deleted ftp-user '\" . $result['username'] . \"'\");\n\t\treturn $this->response($result);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/HostingPlans.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass HostingPlans extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * list all available hosting plans\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] list hosting-plans\");\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT p.*, a.loginname as adminname\n\t\t\t\tFROM `\" . TABLE_PANEL_PLANS . \"` p, `\" . TABLE_PANEL_ADMINS . \"` a\n\t\t\t\tWHERE `p`.`adminid` = `a`.`adminid`\" . ($this->getUserDetail('customers_see_all') ? '' : \" AND `p`.`adminid` = :adminid \") . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\t\t$params = [];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t}\n\t\t\t$params = array_merge($params, $query_fields);\n\t\t\tDatabase::pexecute($result_stmt, $params, true, true);\n\t\t\t$result = [];\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$result[] = $row;\n\t\t\t}\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($result),\n\t\t\t\t'list' => $result\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns the total number of accessible hosting plans\n\t *\n\t * @access admin\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_plans\n\t\t\t\tFROM `\" . TABLE_PANEL_PLANS . \"` p, `\" . TABLE_PANEL_ADMINS . \"` a\n\t\t\t\tWHERE `p`.`adminid` = `a`.`adminid`\" . ($this->getUserDetail('customers_see_all') ? '' : \" AND `p`.`adminid` = :adminid \") . $this->getSearchWhere($query_fields, true));\n\t\t\t$params = [];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t}\n\t\t\t$params = array_merge($params, $query_fields);\n\t\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_plans']);\n\t\t\t}\n\t\t\treturn $this->response(0);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * add new hosting-plan\n\t *\n\t * @param string $name\n\t *            name of the plan\n\t * @param string $description\n\t *            optional, description for hosting-plan\n\t * @param int $diskspace\n\t *            optional disk-space available for customer in MB, default 0\n\t * @param bool $diskspace_ul\n\t *            optional, whether customer should have unlimited diskspace, default 0 (false)\n\t * @param int $traffic\n\t *            optional traffic available for customer in GB, default 0\n\t * @param bool $traffic_ul\n\t *            optional, whether customer should have unlimited traffic, default 0 (false)\n\t * @param int $subdomains\n\t *            optional amount of subdomains available for customer, default 0\n\t * @param bool $subdomains_ul\n\t *            optional, whether customer should have unlimited subdomains, default 0 (false)\n\t * @param int $emails\n\t *            optional amount of emails available for customer, default 0\n\t * @param bool $emails_ul\n\t *            optional, whether customer should have unlimited emails, default 0 (false)\n\t * @param int $email_accounts\n\t *            optional amount of email-accounts available for customer, default 0\n\t * @param bool $email_accounts_ul\n\t *            optional, whether customer should have unlimited email-accounts, default 0 (false)\n\t * @param int $email_forwarders\n\t *            optional amount of email-forwarders available for customer, default 0\n\t * @param bool $email_forwarders_ul\n\t *            optional, whether customer should have unlimited email-forwarders, default 0 (false)\n\t * @param int $email_quota\n\t *            optional size of email-quota available for customer in MB, default is system-setting mail_quota\n\t * @param bool $email_quota_ul\n\t *            optional, whether customer should have unlimited email-quota, default 0 (false)\n\t * @param bool $email_imap\n\t *            optional, whether to allow IMAP access, default 0 (false)\n\t * @param bool $email_pop3\n\t *            optional, whether to allow POP3 access, default 0 (false)\n\t * @param int $ftps\n\t *            optional amount of ftp-accounts available for customer, default 0\n\t * @param bool $ftps_ul\n\t *            optional, whether customer should have unlimited ftp-accounts, default 0 (false)\n\t * @param int $mysqls\n\t *            optional amount of mysql-databases available for customer, default 0\n\t * @param bool $mysqls_ul\n\t *            optional, whether customer should have unlimited mysql-databases, default 0 (false)\n\t * @param bool $phpenabled\n\t *            optional, whether to allow usage of PHP, default 0 (false)\n\t * @param array $allowed_phpconfigs\n\t *            optional, array of IDs of php-config that the customer is allowed to use, default empty (none)\n\t * @param bool $perlenabled\n\t *            optional, whether to allow usage of Perl/CGI, default 0 (false)\n\t * @param bool $dnsenabled\n\t *            optional, whether to allow usage of the DNS editor (requires activated nameserver in settings),\n\t *            default 0 (false)\n\t * @param bool $logviewenabled\n\t *            optional, whether to allow access to webserver access/error-logs, default 0 (false)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$name = $this->getParam('name');\n\t\t\t$description = $this->getParam('description', true, '');\n\n\t\t\t$value_arr = [];\n\t\t\t$value_arr['diskspace'] = $this->getUlParam('diskspace', 'diskspace_ul', true, 0);\n\t\t\t$value_arr['traffic'] = $this->getUlParam('traffic', 'traffic_ul', true, 0);\n\t\t\t$value_arr['subdomains'] = $this->getUlParam('subdomains', 'subdomains_ul', true, 0);\n\t\t\t$value_arr['emails'] = $this->getUlParam('emails', 'emails_ul', true, 0);\n\t\t\t$value_arr['email_accounts'] = $this->getUlParam('email_accounts', 'email_accounts_ul', true, 0);\n\t\t\t$value_arr['email_forwarders'] = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, 0);\n\t\t\t$value_arr['email_quota'] = $this->getUlParam('email_quota', 'email_quota_ul', true, Settings::Get('system.mail_quota'));\n\t\t\t$value_arr['email_imap'] = $this->getBoolParam('email_imap', true, 0);\n\t\t\t$value_arr['email_pop3'] = $this->getBoolParam('email_pop3', true, 0);\n\t\t\t$value_arr['ftps'] = $this->getUlParam('ftps', 'ftps_ul', true, 0);\n\t\t\t$value_arr['mysqls'] = $this->getUlParam('mysqls', 'mysqls_ul', true, 0);\n\t\t\t$value_arr['phpenabled'] = $this->getBoolParam('phpenabled', true, 0);\n\t\t\t$p_allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, []);\n\t\t\t$value_arr['perlenabled'] = $this->getBoolParam('perlenabled', true, 0);\n\t\t\t$value_arr['dnsenabled'] = $this->getBoolParam('dnsenabled', true, 0);\n\t\t\t$value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, 0);\n\n\t\t\t// validation\n\t\t\t$name = Validate::validate(trim($name), 'name', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$description = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $description), 'description', Validate::REGEX_DESC_TEXT);\n\n\t\t\tif (Settings::Get('system.mail_quota_enabled') != '1') {\n\t\t\t\t$value_arr['email_quota'] = -1;\n\t\t\t}\n\n\t\t\t$value_arr['allowed_phpconfigs'] = [];\n\t\t\tif (!empty($p_allowed_phpconfigs) && is_array($p_allowed_phpconfigs)) {\n\t\t\t\tforeach ($p_allowed_phpconfigs as $allowed_phpconfig) {\n\t\t\t\t\t$allowed_phpconfig = intval($allowed_phpconfig);\n\t\t\t\t\t$value_arr['allowed_phpconfigs'][] = $allowed_phpconfig;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$value_arr['allowed_phpconfigs'] = array_map('intval', $value_arr['allowed_phpconfigs']);\n\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_PLANS . \"`\n\t\t\t\tSET `adminid` = :adminid, `name` = :name, `description` = :desc, `value` = :valuearr, `ts` = UNIX_TIMESTAMP();\n\t\t\t\");\n\t\t\t$ins_data = [\n\t\t\t\t'adminid' => $this->getUserDetail('adminid'),\n\t\t\t\t'name' => $name,\n\t\t\t\t'desc' => $description,\n\t\t\t\t'valuearr' => json_encode($value_arr)\n\t\t\t];\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data, true, true);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] added hosting-plan '\" . $name . \"'\");\n\t\t\t$result = $this->apiCall('HostingPlans.get', [\n\t\t\t\t'planname' => $name\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return a hosting-plan entry by either id or plan-name\n\t *\n\t * @param int $id\n\t *            optional, the hosting-plan-id\n\t * @param string $planname\n\t *            optional, the hosting-plan-name\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$dn_optional = $id > 0;\n\t\t\t$planname = $this->getParam('planname', $dn_optional, '');\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_PLANS . \"` WHERE \" . ($id > 0 ? \"`id` = :iddn\" : \"`name` = :iddn\") . ($this->getUserDetail('customers_see_all') ? '' : \" AND `adminid` = :adminid\"));\n\t\t\t$params = [\n\t\t\t\t'iddn' => ($id <= 0 ? $planname : $id)\n\t\t\t];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '0') {\n\t\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t}\n\t\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\t\tif ($result) {\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] get hosting-plan '\" . $result['name'] . \"'\");\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t\t$key = ($id > 0 ? \"id #\" . $id : \"planname '\" . $planname . \"'\");\n\t\t\tthrow new Exception(\"Hosting-plan with \" . $key . \" could not be found\", 404);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * update hosting-plan by either id or plan-name\n\t *\n\t * @param int $id\n\t *            optional the hosting-plan-id\n\t * @param string $planname\n\t *            optional the hosting-plan-name\n\t * @param string $name\n\t *            optional name of the plan\n\t * @param string $description\n\t *            optional description for hosting-plan\n\t * @param int $diskspace\n\t *            optional disk-space available for customer in MB, default 0\n\t * @param bool $diskspace_ul\n\t *            optional, whether customer should have unlimited diskspace, default 0 (false)\n\t * @param int $traffic\n\t *            optional traffic available for customer in GB, default 0\n\t * @param bool $traffic_ul\n\t *            optional, whether customer should have unlimited traffic, default 0 (false)\n\t * @param int $subdomains\n\t *            optional amount of subdomains available for customer, default 0\n\t * @param bool $subdomains_ul\n\t *            optional, whether customer should have unlimited subdomains, default 0 (false)\n\t * @param int $emails\n\t *            optional amount of emails available for customer, default 0\n\t * @param bool $emails_ul\n\t *            optional, whether customer should have unlimited emails, default 0 (false)\n\t * @param int $email_accounts\n\t *            optional amount of email-accounts available for customer, default 0\n\t * @param bool $email_accounts_ul\n\t *            optional, whether customer should have unlimited email-accounts, default 0 (false)\n\t * @param int $email_forwarders\n\t *            optional amount of email-forwarders available for customer, default 0\n\t * @param bool $email_forwarders_ul\n\t *            optional, whether customer should have unlimited email-forwarders, default 0 (false)\n\t * @param int $email_quota\n\t *            optional size of email-quota available for customer in MB, default is system-setting mail_quota\n\t * @param bool $email_quota_ul\n\t *            optional, whether customer should have unlimited email-quota, default 0 (false)\n\t * @param bool $email_imap\n\t *            optional, whether to allow IMAP access, default 0 (false)\n\t * @param bool $email_pop3\n\t *            optional, whether to allow POP3 access, default 0 (false)\n\t * @param int $ftps\n\t *            optional amount of ftp-accounts available for customer, default 0\n\t * @param bool $ftps_ul\n\t *            optional, whether customer should have unlimited ftp-accounts, default 0 (false)\n\t * @param int $mysqls\n\t *            optional amount of mysql-databases available for customer, default 0\n\t * @param bool $mysqls_ul\n\t *            optional, whether customer should have unlimited mysql-databases, default 0 (false)\n\t * @param bool $phpenabled\n\t *            optional, whether to allow usage of PHP, default 0 (false)\n\t * @param array $allowed_phpconfigs\n\t *            optional, array of IDs of php-config that the customer is allowed to use, default empty (none)\n\t * @param bool $perlenabled\n\t *            optional, whether to allow usage of Perl/CGI, default 0 (false)\n\t * @param bool $dnsenabled\n\t *            optional, either to allow usage of the DNS editor (requires activated nameserver in settings),\n\t *            default 0 (false)\n\t * @param bool $logviewenabled\n\t *            optional, either to allow access to webserver access/error-logs, default 0 (false)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t// parameters\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$dn_optional = $id > 0;\n\t\t\t$planname = $this->getParam('planname', $dn_optional, '');\n\n\t\t\t// get requested hosting-plan\n\t\t\t$result = $this->apiCall('HostingPlans.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'planname' => $planname\n\t\t\t]);\n\t\t\t$id = $result['id'];\n\n\t\t\t$result['value'] = json_decode($result['value'], true);\n\t\t\tforeach ($result['value'] as $index => $value) {\n\t\t\t\t$result[$index] = $value;\n\t\t\t}\n\n\t\t\t$name = $this->getParam('name', true, $result['name']);\n\t\t\t$description = $this->getParam('description', true, $result['description']);\n\n\t\t\t$value_arr = [];\n\t\t\t$value_arr['diskspace'] = $this->getUlParam('diskspace', 'diskspace_ul', true, $result['diskspace']);\n\t\t\t$value_arr['traffic'] = $this->getUlParam('traffic', 'traffic_ul', true, $result['traffic']);\n\t\t\t$value_arr['subdomains'] = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']);\n\t\t\t$value_arr['emails'] = $this->getUlParam('emails', 'emails_ul', true, $result['emails']);\n\t\t\t$value_arr['email_accounts'] = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']);\n\t\t\t$value_arr['email_forwarders'] = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']);\n\t\t\t$value_arr['email_quota'] = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']);\n\t\t\t$value_arr['email_imap'] = $this->getParam('email_imap', true, $result['email_imap']);\n\t\t\t$value_arr['email_pop3'] = $this->getParam('email_pop3', true, $result['email_pop3']);\n\t\t\t$value_arr['ftps'] = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']);\n\t\t\t$value_arr['mysqls'] = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']);\n\t\t\t$value_arr['phpenabled'] = $this->getBoolParam('phpenabled', true, $result['phpenabled']);\n\t\t\t$p_allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, $result['allowed_phpconfigs']);\n\t\t\t$value_arr['perlenabled'] = $this->getBoolParam('perlenabled', true, $result['perlenabled']);\n\t\t\t$value_arr['dnsenabled'] = $this->getBoolParam('dnsenabled', true, $result['dnsenabled']);\n\t\t\t$value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, $result['logviewenabled']);\n\n\t\t\t// validation\n\t\t\t$name = Validate::validate(trim($name), 'name', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$description = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $description), 'description', Validate::REGEX_DESC_TEXT);\n\n\t\t\tif (Settings::Get('system.mail_quota_enabled') != '1') {\n\t\t\t\t$value_arr['email_quota'] = -1;\n\t\t\t}\n\n\t\t\tif (empty($name)) {\n\t\t\t\t$name = $result['name'];\n\t\t\t}\n\n\t\t\t$value_arr['allowed_phpconfigs'] = [];\n\t\t\tif (!empty($p_allowed_phpconfigs) && is_array($p_allowed_phpconfigs)) {\n\t\t\t\tforeach ($p_allowed_phpconfigs as $allowed_phpconfig) {\n\t\t\t\t\t$allowed_phpconfig = intval($allowed_phpconfig);\n\t\t\t\t\t$value_arr['allowed_phpconfigs'][] = $allowed_phpconfig;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$value_arr['allowed_phpconfigs'] = array_map('intval', $value_arr['allowed_phpconfigs']);\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_PLANS . \"`\n\t\t\t\tSET `name` = :name, `description` = :desc, `value` = :valuearr, `ts` = UNIX_TIMESTAMP()\n\t\t\t\tWHERE `id` = :id\n\t\t\t\");\n\t\t\t$update_data = [\n\t\t\t\t'name' => $name,\n\t\t\t\t'desc' => $description,\n\t\t\t\t'valuearr' => json_encode($value_arr),\n\t\t\t\t'id' => $id\n\t\t\t];\n\t\t\tDatabase::pexecute($upd_stmt, $update_data, true, true);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] updated hosting-plan '\" . $result['name'] . \"'\");\n\t\t\treturn $this->response($update_data);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * delete hosting-plan by either id or plan-name\n\t *\n\t * @param int $id\n\t *            optional the hosting-plan-id\n\t * @param string $planname\n\t *            optional the hosting-plan-name\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id', true, 0);\n\t\t\t$dn_optional = $id > 0;\n\t\t\t$planname = $this->getParam('planname', $dn_optional, '');\n\n\t\t\t// get requested hosting-plan\n\t\t\t$result = $this->apiCall('HostingPlans.get', [\n\t\t\t\t'id' => $id,\n\t\t\t\t'planname' => $planname\n\t\t\t]);\n\t\t\t$id = $result['id'];\n\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_PLANS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] deleted hosting-plan '\" . $result['name'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/IpsAndPorts.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass IpsAndPorts extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * lists all ip/port entries\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] list ips and ports\");\n\t\t\t$ip_where = \"\";\n\t\t\t$append_where = false;\n\t\t\tif (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) {\n\t\t\t\t$ip_where = \"WHERE `id` IN (\" . implode(\", \", json_decode($this->getUserDetail('ip'), true)) . \")\";\n\t\t\t\t$append_where = true;\n\t\t\t}\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` \" . $ip_where . $this->getSearchWhere($query_fields, $append_where) . $this->getOrderBy() . $this->getLimit());\n\t\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\t\t$result = [];\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$result[] = $row;\n\t\t\t}\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($result),\n\t\t\t\t'list' => $result\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns the total number of accessible ip/port entries\n\t *\n\t * @access admin\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) {\n\t\t\t$ip_where = \"\";\n\t\t\t$query_fields = [];\n\t\t\tif (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) {\n\t\t\t\t$ip_where = \"WHERE `id` IN (\" . implode(\", \", json_decode($this->getUserDetail('ip'), true)) . \") \" . $this->getSearchWhere($query_fields, true);\n\t\t\t} else {\n\t\t\t\t$ip_where =  $this->getSearchWhere($query_fields);\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_ips FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` \" . $ip_where);\n\t\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_ips']);\n\t\t\t}\n\t\t\treturn $this->response(0);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * create a new ip/port entry\n\t *\n\t * @param string $ip\n\t * @param int $port\n\t *            optional, default 80\n\t * @param bool $listen_statement\n\t *            optional, default 0 (false)\n\t * @param bool $namevirtualhost_statement\n\t *            optional, default 0 (false)\n\t * @param bool $vhostcontainer\n\t *            optional, default 0 (false)\n\t * @param string $specialsettings\n\t *            optional, default empty\n\t * @param bool $vhostcontainer_servername_statement\n\t *            optional, default 0 (false)\n\t * @param string $default_vhostconf_domain\n\t *            optional, defatul empty\n\t * @param string $docroot\n\t *            optional, default empty (point to froxlor)\n\t * @param bool $ssl\n\t *            optional, default 0 (false)\n\t * @param string $ssl_cert_file\n\t *            optional, requires $ssl = 1, default empty\n\t * @param string $ssl_key_file\n\t *            optional, requires $ssl = 1, default empty\n\t * @param string $ssl_ca_file\n\t *            optional, requires $ssl = 1, default empty\n\t * @param string $ssl_cert_chainfile\n\t *            optional, requires $ssl = 1, default empty\n\t * @param string $ssl_specialsettings\n\t *            optional, requires $ssl = 1, default empty\n\t * @param bool $include_specialsettings\n\t *            optional, requires $ssl = 1, whether or not to include non-ssl specialsettings, default false\n\t * @param string $ssl_default_vhostconf_domain\n\t *            optional, requires $ssl = 1, defatul empty\n\t * @param bool $include_default_vhostconf_domain\n\t *            optional, requires $ssl = 1, whether or not to include non-ssl default_vhostconf_domain, default false\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$ip = Validate::validate_ip2($this->getParam('ip'), false, 'invalidip', false, true, false, false, true);\n\t\t\t$port = Validate::validate($this->getParam('port', true, 80), 'port', Validate::REGEX_PORT, [\n\t\t\t\t'stringisempty',\n\t\t\t\t'myport'\n\t\t\t], [], true);\n\t\t\t$listen_statement = !empty($this->getBoolParam('listen_statement', true, 0)) ? 1 : 0;\n\t\t\t$namevirtualhost_statement = !empty($this->getBoolParam('namevirtualhost_statement', true, 0)) ? 1 : 0;\n\t\t\t$vhostcontainer = !empty($this->getBoolParam('vhostcontainer', true, 0)) ? 1 : 0;\n\t\t\t$ss = $this->getParam('specialsettings', true, '');\n\t\t\t$specialsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $ss ?? \"\"), 'specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t$vhostcontainer_servername_statement = !empty($this->getBoolParam('vhostcontainer_servername_statement', true, 1)) ? 1 : 0;\n\t\t\t$dvd = $this->getParam('default_vhostconf_domain', true, '');\n\t\t\t$default_vhostconf_domain = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $dvd), 'default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t$docroot = Validate::validate($this->getParam('docroot', true, ''), 'docroot', Validate::REGEX_DIR, '', [], true);\n\n\t\t\tif ((int)Settings::Get('system.use_ssl') == 1) {\n\t\t\t\t$ssl = (bool)$this->getBoolParam('ssl', true, 0);\n\t\t\t\t$cert_optional = !($ssl && empty(Settings::Get('system.ssl_cert_file')));\n\t\t\t\t$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $cert_optional, ''), 'ssl_cert_file', '', '', [], true);\n\t\t\t\t$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $cert_optional, ''), 'ssl_key_file', '', '', [], true);\n\t\t\t\t$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', [], true);\n\t\t\t\t$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', [], true);\n\t\t\t\t$sslss = $this->getParam('ssl_specialsettings', true, '');\n\t\t\t\t$ssl_specialsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $sslss ?? \"\"), 'ssl_specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t\t$include_specialsettings = !empty($this->getBoolParam('include_specialsettings', true, 0)) ? 1 : 0;\n\t\t\t\t$ssldvd = $this->getParam('ssl_default_vhostconf_domain', true, '');\n\t\t\t\t$ssl_default_vhostconf_domain = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $ssldvd ?? \"\"), 'ssl_default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t\t$include_default_vhostconf_domain = !empty($this->getBoolParam('include_default_vhostconf_domain', true, 0)) ? 1 : 0;\n\t\t\t} else {\n\t\t\t\t$ssl = 0;\n\t\t\t\t$ssl_cert_file = '';\n\t\t\t\t$ssl_key_file = '';\n\t\t\t\t$ssl_ca_file = '';\n\t\t\t\t$ssl_cert_chainfile = '';\n\t\t\t\t$ssl_specialsettings = '';\n\t\t\t\t$include_specialsettings = 0;\n\t\t\t\t$ssl_default_vhostconf_domain = '';\n\t\t\t\t$include_default_vhostconf_domain = 0;\n\t\t\t}\n\n\t\t\tif ($listen_statement != '1') {\n\t\t\t\t$listen_statement = '0';\n\t\t\t}\n\n\t\t\tif ($namevirtualhost_statement != '1') {\n\t\t\t\t$namevirtualhost_statement = '0';\n\t\t\t}\n\n\t\t\tif ($vhostcontainer != '1') {\n\t\t\t\t$vhostcontainer = '0';\n\t\t\t}\n\n\t\t\tif ($vhostcontainer_servername_statement != '1') {\n\t\t\t\t$vhostcontainer_servername_statement = '0';\n\t\t\t}\n\n\t\t\tif ($ssl != '1') {\n\t\t\t\t$ssl = '0';\n\t\t\t}\n\n\t\t\tif ($ssl_cert_file != '') {\n\t\t\t\t$ssl_cert_file = FileDir::makeCorrectFile($ssl_cert_file);\n\t\t\t}\n\n\t\t\tif ($ssl_key_file != '') {\n\t\t\t\t$ssl_key_file = FileDir::makeCorrectFile($ssl_key_file);\n\t\t\t}\n\n\t\t\tif ($ssl_ca_file != '') {\n\t\t\t\t$ssl_ca_file = FileDir::makeCorrectFile($ssl_ca_file);\n\t\t\t}\n\n\t\t\tif ($ssl_cert_chainfile != '') {\n\t\t\t\t$ssl_cert_chainfile = FileDir::makeCorrectFile($ssl_cert_chainfile);\n\t\t\t}\n\n\t\t\tif (strlen(trim($docroot)) > 0) {\n\t\t\t\t$docroot = FileDir::makeCorrectDir($docroot);\n\t\t\t} else {\n\t\t\t\t$docroot = '';\n\t\t\t}\n\n\t\t\t// always use compressed ipv6 format\n\t\t\t$ip = inet_ntop(inet_pton($ip));\n\n\t\t\t$result_checkfordouble_stmt = Database::prepare(\"\n\t\t\tSELECT `id` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\tWHERE `ip` = :ip AND `port` = :port\");\n\t\t\t$result_checkfordouble = Database::pexecute_first($result_checkfordouble_stmt, [\n\t\t\t\t'ip' => $ip,\n\t\t\t\t'port' => $port\n\t\t\t]);\n\n\t\t\tif ($result_checkfordouble && $result_checkfordouble['id'] != '') {\n\t\t\t\tResponse::standardError('myipnotdouble', '', true);\n\t\t\t}\n\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\tSET\n\t\t\t\t`ip` = :ip, `port` = :port, `listen_statement` = :ls,\n\t\t\t\t`namevirtualhost_statement` = :nvhs, `vhostcontainer` = :vhc,\n\t\t\t\t`vhostcontainer_servername_statement` = :vhcss,\n\t\t\t\t`specialsettings` = :ss, `ssl` = :ssl,\n\t\t\t\t`ssl_cert_file` = :ssl_cert, `ssl_key_file` = :ssl_key,\n\t\t\t\t`ssl_ca_file` = :ssl_ca, `ssl_cert_chainfile` = :ssl_chain,\n\t\t\t\t`default_vhostconf_domain` = :dvhd, `docroot` = :docroot,\n\t\t\t\t`ssl_specialsettings` = :ssl_ss, `include_specialsettings` = :incss,\n\t\t\t\t`ssl_default_vhostconf_domain` = :ssl_dvhd, `include_default_vhostconf_domain` = :incdvhd;\n\t\t\t\");\n\t\t\t$ins_data = [\n\t\t\t\t'ip' => $ip,\n\t\t\t\t'port' => $port,\n\t\t\t\t'ls' => $listen_statement,\n\t\t\t\t'nvhs' => $namevirtualhost_statement,\n\t\t\t\t'vhc' => $vhostcontainer,\n\t\t\t\t'vhcss' => $vhostcontainer_servername_statement,\n\t\t\t\t'ss' => $specialsettings,\n\t\t\t\t'ssl' => $ssl,\n\t\t\t\t'ssl_cert' => $ssl_cert_file,\n\t\t\t\t'ssl_key' => $ssl_key_file,\n\t\t\t\t'ssl_ca' => $ssl_ca_file,\n\t\t\t\t'ssl_chain' => $ssl_cert_chainfile,\n\t\t\t\t'dvhd' => $default_vhostconf_domain,\n\t\t\t\t'docroot' => $docroot,\n\t\t\t\t'ssl_ss' => $ssl_specialsettings,\n\t\t\t\t'incss' => $include_specialsettings,\n\t\t\t\t'ssl_dvhd' => $ssl_default_vhostconf_domain,\n\t\t\t\t'incdvhd' => $include_default_vhostconf_domain\n\t\t\t];\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\t\t\t$ins_data['id'] = Database::lastInsertId();\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\n\t\t\tif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t$ip = '[' . $ip . ']';\n\t\t\t}\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] added IP/port '\" . $ip . \":\" . $port . \"'\");\n\t\t\t// get ip for return-array\n\t\t\t$result = $this->apiCall('IpsAndPorts.get', [\n\t\t\t\t'id' => $ins_data['id']\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return an ip/port entry by id\n\t *\n\t * @param int $id\n\t *            ip-port-id\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) {\n\t\t\t$id = $this->getParam('id');\n\t\t\tif (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) {\n\t\t\t\t$allowed_ips = json_decode($this->getUserDetail('ip'), true);\n\t\t\t\tif (!in_array($id, $allowed_ips)) {\n\t\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t\t}\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\tif ($result) {\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] get ip \" . $result['ip'] . \" \" . $result['port']);\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t\tthrow new Exception(\"IP/port with id #\" . $id . \" could not be found\", 404);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * update ip/port entry by given id\n\t *\n\t * @param int $id\n\t * @param string $ip\n\t *            optional\n\t * @param int $port\n\t *            optional, default 80\n\t * @param bool $listen_statement\n\t *            optional, default 0 (false)\n\t * @param bool $namevirtualhost_statement\n\t *            optional, default 0 (false)\n\t * @param bool $vhostcontainer\n\t *            optional, default 0 (false)\n\t * @param string $specialsettings\n\t *            optional, default empty\n\t * @param bool $vhostcontainer_servername_statement\n\t *            optional, default 0 (false)\n\t * @param string $default_vhostconf_domain\n\t *            optional, defatul empty\n\t * @param string $docroot\n\t *            optional, default empty (point to froxlor)\n\t * @param bool $ssl\n\t *            optional, default 0 (false)\n\t * @param string $ssl_cert_file\n\t *            optional, requires $ssl = 1, default empty\n\t * @param string $ssl_key_file\n\t *            optional, requires $ssl = 1, default empty\n\t * @param string $ssl_ca_file\n\t *            optional, requires $ssl = 1, default empty\n\t * @param string $ssl_cert_chainfile\n\t *            optional, requires $ssl = 1, default empty\n\t * @param string $ssl_specialsettings\n\t *            optional, requires $ssl = 1, default empty\n\t * @param bool $include_specialsettings\n\t *            optional, requires $ssl = 1, whether or not to include non-ssl specialsettings, default false\n\t * @param string $ssl_default_vhostconf_domain\n\t *            optional, requires $ssl = 1, defatul empty\n\t * @param bool $include_default_vhostconf_domain\n\t *            optional, requires $ssl = 1, whether or not to include non-ssl default_vhostconf_domain, default false\n\t *\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result = $this->apiCall('IpsAndPorts.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\t$ip = Validate::validate_ip2($this->getParam('ip', true, $result['ip']), false, 'invalidip', false, true, false, false, true);\n\t\t\t$port = Validate::validate($this->getParam('port', true, $result['port']), 'port', Validate::REGEX_PORT, [\n\t\t\t\t'stringisempty',\n\t\t\t\t'myport'\n\t\t\t], [], true);\n\t\t\t$listen_statement = $this->getBoolParam('listen_statement', true, $result['listen_statement']);\n\t\t\t$namevirtualhost_statement = $this->getBoolParam('namevirtualhost_statement', true, $result['namevirtualhost_statement']);\n\t\t\t$vhostcontainer = $this->getBoolParam('vhostcontainer', true, $result['vhostcontainer']);\n\t\t\t$ss = $this->getParam('specialsettings', true, $result['specialsettings']);\n\t\t\t$specialsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $ss ?? \"\"), 'specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t$vhostcontainer_servername_statement = $this->getParam('vhostcontainer_servername_statement', true, $result['vhostcontainer_servername_statement']);\n\t\t\t$dvd = $this->getParam('default_vhostconf_domain', true, $result['default_vhostconf_domain']);\n\t\t\t$default_vhostconf_domain = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $dvd ?? \"\"), 'default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t$docroot = Validate::validate($this->getParam('docroot', true, $result['docroot']), 'docroot', Validate::REGEX_DIR, '', [], true);\n\n\t\t\tif ((int)Settings::Get('system.use_ssl') == 1) {\n\t\t\t\t$ssl = (bool)$this->getBoolParam('ssl', true, $result['ssl']);\n\t\t\t\t$cert_optional = !($ssl && empty(Settings::Get('system.ssl_cert_file')));\n\t\t\t\t$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $cert_optional, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);\n\t\t\t\t$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $cert_optional, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);\n\t\t\t\t$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', [], true);\n\t\t\t\t$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', [], true);\n\t\t\t\t$sslss = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']);\n\t\t\t\t$ssl_specialsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $sslss ?? \"\"), 'ssl_specialsettings', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t\t$include_specialsettings = $this->getBoolParam('include_specialsettings', true, $result['include_specialsettings']);\n\t\t\t\t$ssldvd = $this->getParam('ssl_default_vhostconf_domain', true, $result['ssl_default_vhostconf_domain']);\n\t\t\t\t$ssl_default_vhostconf_domain = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $ssldvd ?? \"\"), 'ssl_default_vhostconf_domain', Validate::REGEX_CONF_TEXT, '', [], true);\n\t\t\t\t$include_default_vhostconf_domain = $this->getBoolParam('include_default_vhostconf_domain', true, $result['include_default_vhostconf_domain']);\n\t\t\t} else {\n\t\t\t\t$ssl = 0;\n\t\t\t\t$ssl_cert_file = '';\n\t\t\t\t$ssl_key_file = '';\n\t\t\t\t$ssl_ca_file = '';\n\t\t\t\t$ssl_cert_chainfile = '';\n\t\t\t\t$ssl_specialsettings = '';\n\t\t\t\t$include_specialsettings = 0;\n\t\t\t\t$ssl_default_vhostconf_domain = '';\n\t\t\t\t$include_default_vhostconf_domain = 0;\n\t\t\t}\n\n\t\t\t$result_checkfordouble_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\tWHERE `ip` = :ip AND `port` = :port\n\t\t\t\");\n\t\t\t$result_checkfordouble = Database::pexecute_first($result_checkfordouble_stmt, [\n\t\t\t\t'ip' => $ip,\n\t\t\t\t'port' => $port\n\t\t\t]);\n\n\t\t\t$result_sameipotherport_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\tWHERE `ip` = :ip AND `id` <> :id\n\t\t\t\");\n\t\t\t$result_sameipotherport = Database::pexecute_first($result_sameipotherport_stmt, [\n\t\t\t\t'ip' => $ip,\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\tif ($listen_statement != '1') {\n\t\t\t\t$listen_statement = '0';\n\t\t\t}\n\n\t\t\tif ($namevirtualhost_statement != '1') {\n\t\t\t\t$namevirtualhost_statement = '0';\n\t\t\t}\n\n\t\t\tif ($vhostcontainer != '1') {\n\t\t\t\t$vhostcontainer = '0';\n\t\t\t}\n\n\t\t\tif ($vhostcontainer_servername_statement != '1') {\n\t\t\t\t$vhostcontainer_servername_statement = '0';\n\t\t\t}\n\n\t\t\tif ($ssl != '1') {\n\t\t\t\t$ssl = '0';\n\t\t\t}\n\n\t\t\tif ($ssl_cert_file != '') {\n\t\t\t\t$ssl_cert_file = FileDir::makeCorrectFile($ssl_cert_file);\n\t\t\t}\n\n\t\t\tif ($ssl_key_file != '') {\n\t\t\t\t$ssl_key_file = FileDir::makeCorrectFile($ssl_key_file);\n\t\t\t}\n\n\t\t\tif ($ssl_ca_file != '') {\n\t\t\t\t$ssl_ca_file = FileDir::makeCorrectFile($ssl_ca_file);\n\t\t\t}\n\n\t\t\tif ($ssl_cert_chainfile != '') {\n\t\t\t\t$ssl_cert_chainfile = FileDir::makeCorrectFile($ssl_cert_chainfile);\n\t\t\t}\n\n\t\t\tif (strlen(trim($docroot)) > 0) {\n\t\t\t\t$docroot = FileDir::makeCorrectDir($docroot);\n\t\t\t} else {\n\t\t\t\t$docroot = '';\n\t\t\t}\n\n\t\t\t// always use compressed ipv6 format\n\t\t\t$ip = inet_ntop(inet_pton($ip));\n\n\t\t\tif ($result['ip'] != $ip && $result['ip'] == Settings::Get('system.ipaddress') && $result_sameipotherport == false) {\n\t\t\t\tResponse::standardError('cantchangesystemip', '', true);\n\t\t\t} elseif ($result_checkfordouble && $result_checkfordouble['id'] != '' && $result_checkfordouble['id'] != $id) {\n\t\t\t\tResponse::standardError('myipnotdouble', '', true);\n\t\t\t} else {\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\t\tSET\n\t\t\t\t\t`ip` = :ip, `port` = :port, `listen_statement` = :ls,\n\t\t\t\t\t`namevirtualhost_statement` = :nvhs, `vhostcontainer` = :vhc,\n\t\t\t\t\t`vhostcontainer_servername_statement` = :vhcss,\n\t\t\t\t\t`specialsettings` = :ss, `ssl` = :ssl,\n\t\t\t\t\t`ssl_cert_file` = :ssl_cert, `ssl_key_file` = :ssl_key,\n\t\t\t\t\t`ssl_ca_file` = :ssl_ca, `ssl_cert_chainfile` = :ssl_chain,\n\t\t\t\t\t`default_vhostconf_domain` = :dvhd, `docroot` = :docroot,\n\t\t\t\t\t`ssl_specialsettings` = :ssl_ss, `include_specialsettings` = :incss,\n\t\t\t\t\t`ssl_default_vhostconf_domain` = :ssl_dvhd, `include_default_vhostconf_domain` = :incdvhd\n\t\t\t\t\tWHERE `id` = :id;\n\t\t\t\t\");\n\t\t\t\t$upd_data = [\n\t\t\t\t\t'ip' => $ip,\n\t\t\t\t\t'port' => $port,\n\t\t\t\t\t'ls' => $listen_statement,\n\t\t\t\t\t'nvhs' => $namevirtualhost_statement,\n\t\t\t\t\t'vhc' => $vhostcontainer,\n\t\t\t\t\t'vhcss' => $vhostcontainer_servername_statement,\n\t\t\t\t\t'ss' => $specialsettings,\n\t\t\t\t\t'ssl' => $ssl,\n\t\t\t\t\t'ssl_cert' => $ssl_cert_file,\n\t\t\t\t\t'ssl_key' => $ssl_key_file,\n\t\t\t\t\t'ssl_ca' => $ssl_ca_file,\n\t\t\t\t\t'ssl_chain' => $ssl_cert_chainfile,\n\t\t\t\t\t'dvhd' => $default_vhostconf_domain,\n\t\t\t\t\t'docroot' => $docroot,\n\t\t\t\t\t'ssl_ss' => $ssl_specialsettings,\n\t\t\t\t\t'incss' => $include_specialsettings,\n\t\t\t\t\t'ssl_dvhd' => $ssl_default_vhostconf_domain,\n\t\t\t\t\t'incdvhd' => $include_default_vhostconf_domain,\n\t\t\t\t\t'id' => $id\n\t\t\t\t];\n\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\n\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] changed IP/port from '\" . $result['ip'] . \":\" . $result['port'] . \"' to '\" . $ip . \":\" . $port . \"'\");\n\n\t\t\t\t$result = $this->apiCall('IpsAndPorts.get', [\n\t\t\t\t\t'id' => $result['id']\n\t\t\t\t]);\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * delete an ip/port entry by id\n\t *\n\t * @param int $id\n\t *            ip-port-id\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result = $this->apiCall('IpsAndPorts.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\t$result_checkdomain_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id_domain` FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_ipandports` = :id\n\t\t\t\");\n\t\t\t$result_checkdomain = Database::pexecute_first($result_checkdomain_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\tif (empty($result_checkdomain)) {\n\t\t\t\tif (!in_array($result['id'], explode(',', Settings::Get('system.defaultip'))) && !in_array($result['id'], explode(',', Settings::Get('system.defaultsslip')))) {\n\t\t\t\t\t// check whether there is the same IP with a different port\n\t\t\t\t\t// in case this ip-address is the system.ipaddress and therefore\n\t\t\t\t\t// when there is one - we have an alternative\n\t\t\t\t\t$result_sameipotherport_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `id` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\t\t\tWHERE `ip` = :ip AND `id` <> :id\");\n\t\t\t\t\t$result_sameipotherport = Database::pexecute_first($result_sameipotherport_stmt, [\n\t\t\t\t\t\t'id' => $id,\n\t\t\t\t\t\t'ip' => $result['ip']\n\t\t\t\t\t]);\n\n\t\t\t\t\tif (($result['ip'] != Settings::Get('system.ipaddress')) || ($result['ip'] == Settings::Get('system.ipaddress') && $result_sameipotherport != false)) {\n\t\t\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\t\t\t\tWHERE `id` = :id\n\t\t\t\t\t\t\");\n\t\t\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t\t], true, true);\n\n\t\t\t\t\t\t// also, remove connections to domains (multi-stack)\n\t\t\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tDELETE FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_ipandports` = :id\n\t\t\t\t\t\t\");\n\t\t\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t\t\t'id' => $id\n\t\t\t\t\t\t], true, true);\n\n\t\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\n\t\t\t\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] deleted IP/port '\" . $result['ip'] . \":\" . $result['port'] . \"'\");\n\t\t\t\t\t\treturn $this->response($result);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tResponse::standardError('cantdeletesystemip', '', true);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tResponse::standardError('cantdeletedefaultip', '', true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tResponse::standardError('ipstillhasdomains', '', true);\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/MysqlServer.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\nuse PDOException;\n\nclass MysqlServer extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * check whether the user is allowed\n\t *\n\t * @throws Exception\n\t */\n\tprivate function validateAccess()\n\t{\n\t\tif ($this->isAdmin() == false || ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 0)) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\t}\n\n\t/**\n\t * add a new mysql-server\n\t *\n\t * @param string $mysql_host\n\t *             ip/hostname of mysql-server\n\t * @param string $mysql_port\n\t *             optional, port to connect to\n\t * @param string $mysql_ca\n\t *             optional, path to certificate file\n\t * @param string $mysql_verifycert\n\t *             optional, verify server certificate\n\t * @param string $privileged_user\n\t *             privileged user on the mysql-server (must have GRANT privileges)\n\t * @param string $privileged_password\n\t *             password of privileged user\n\t * @param string $description\n\t *             optional, description for server\n\t * @param bool $allow_all_customers\n\t *             optional add this configuration to the list of every existing customer's allowed-mysqlserver-config list, default is false (no)\n\t * @param bool $test_connection\n\t *             optional, test connection with given credentials, default is true (yes)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\t$this->validateAccess();\n\n\t\t$mysql_host = $this->getParam('mysql_host');\n\t\t$mysql_port = $this->getParam('mysql_port', true, 3306);\n\t\t$mysql_ca = $this->getParam('mysql_ca', true, '');\n\t\t$mysql_verifycert = $this->getBoolParam('mysql_verifycert', true, 0);\n\t\t$privileged_user = $this->getParam('privileged_user');\n\t\t$privileged_password = $this->getParam('privileged_password');\n\t\t$description = $this->getParam('description', true, '');\n\t\t$allow_all_customers = $this->getParam('allow_all_customers', true, 0);\n\t\t$test_connection = $this->getParam('test_connection', true, 1);\n\n\t\t// validation\n\t\t$mysql_host_chk = Validate::validate_ip2($mysql_host, true, 'invalidip', true, true, false);\n\t\tif ($mysql_host_chk === false) {\n\t\t\t$mysql_host_chk = Validate::validateLocalHostname($mysql_host);\n\t\t\tif ($mysql_host_chk === false) {\n\t\t\t\t$mysql_host_chk = Validate::validateDomain($mysql_host);\n\t\t\t\tif ($mysql_host_chk === false) {\n\t\t\t\t\tthrow new Exception(\"Invalid mysql server ip/hostname\", 406);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t$mysql_port = Validate::validate($mysql_port, 'port', Validate::REGEX_PORT, '', [3306], true);\n\t\t$mysql_ca = !empty($mysql_ca) ? FileDir::makeCorrectFile($mysql_ca) : '';\n\t\t$privileged_user = Validate::validate($privileged_user, 'privileged_user', '/^[a-z][a-z0-9\\-_]+$/i', '', [], true);\n\t\t$privileged_password = Validate::validate($privileged_password, 'password', '', '', [], true);\n\t\t$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\t// testing connection with given credentials\n\t\tif ($test_connection) {\n\t\t\t$options = array(\n\t\t\t\tPDO::MYSQL_ATTR_INIT_COMMAND => 'SET names utf8'\n\t\t\t);\n\t\t\tif (!empty($mysql_ca)) {\n\t\t\t\t$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;\n\t\t\t\t$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;\n\t\t\t}\n\n\t\t\t$dsn = \"mysql:host=\" . $mysql_host . \";port=\" . $mysql_port . \";\";\n\t\t\ttry {\n\t\t\t\t$db_test = new \\PDO($dsn, $privileged_user, $privileged_password, $options);\n\t\t\t\tunset($db_test);\n\t\t\t} catch (PDOException $e) {\n\t\t\t\tthrow new Exception(\"Connection to given mysql database could not be established. Error-message: \" . $e->getMessage(), $e->getCode());\n\t\t\t}\n\t\t}\n\n\t\t$sql = [];\n\t\t$sql_root = [];\n\n\t\t// get all data from lib/userdata\n\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\n\t\t// le format\n\t\tif (isset($sql['root_user']) && isset($sql['root_password']) && !is_array($sql_root)) {\n\t\t\t$sql_root = array(\n\t\t\t\t0 => array(\n\t\t\t\t\t'caption' => 'Default',\n\t\t\t\t\t'host' => $sql['host'],\n\t\t\t\t\t'socket' => (isset($sql['socket']) ? $sql['socket'] : null),\n\t\t\t\t\t'user' => $sql['root_user'],\n\t\t\t\t\t'password' => $sql['root_password']\n\t\t\t\t)\n\t\t\t);\n\t\t\tunset($sql['root_user']);\n\t\t\tunset($sql['root_password']);\n\t\t}\n\n\t\t// add new values to sql_root array\n\t\t$sql_root[] = [\n\t\t\t'caption' => $description,\n\t\t\t'host' => $mysql_host,\n\t\t\t'port' => $mysql_port,\n\t\t\t'user' => $privileged_user,\n\t\t\t'password' => $privileged_password,\n\t\t\t'ssl' => [\n\t\t\t\t'caFile' => $mysql_ca ?? \"\",\n\t\t\t\t'verifyServerCertificate' => $mysql_verifycert\n\t\t\t]\n\t\t];\n\n\t\t$this->generateNewUserData($sql, $sql_root);\n\n\t\t// last added to array\n\t\t$newdbserver = array_key_last($sql_root);\n\n\t\tif ($allow_all_customers) {\n\t\t\t$this->addDatabaseFromCustomerAllowedList($newdbserver);\n\t\t}\n\n\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] added new database server '\" . $description . \"' (\" . $mysql_host . \")\");\n\n\t\treturn $this->response(['dbserver' => $newdbserver]);\n\t}\n\n\t/**\n\t * remove a mysql-server\n\t *\n\t * @param int $id\n\t *             optional the number of the mysql server (either id or dbserver must be set)\n\t * @param int $dbserver\n\t *             optional the number of the mysql server (either id or dbserver must be set)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\t$this->validateAccess();\n\n\t\t$id = (int)$this->getParam('id', true, -1);\n\t\t$dn_optional = $id >= 0;\n\t\t$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);\n\t\t$dbserver = $id >= 0 ? $id : $dbserver;\n\n\t\tif ($dbserver == 0) {\n\t\t\tthrow new Exception('Cannot delete first/default mysql-server', 406);\n\t\t}\n\n\t\t$sql_root = [];\n\t\t// get all data from lib/userdata\n\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\n\t\tif (!isset($sql_root[$dbserver])) {\n\t\t\tthrow new Exception('Mysql server not found', 404);\n\t\t}\n\n\t\t// check whether the server is in use by any customer\n\t\t$result_ms = $this->databasesOnServer(true, $dbserver);\n\t\tif ($result_ms > 0) {\n\t\t\tthrow new Exception(lng('error.mysqlserverstillhasdbs'), 406);\n\t\t}\n\n\t\t// when removing, remove from list of allowed_mysqlservers from any customers\n\t\t$this->removeDatabaseFromCustomerAllowedList($dbserver);\n\n\t\t$description = $sql_root[$dbserver]['caption'] ?? \"unknown\";\n\t\t$mysql_host = $sql_root[$dbserver]['host'] ?? \"unknown\";\n\t\tunset($sql_root[$dbserver]);\n\n\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] removed database server '\" . $description . \"' (\" . $mysql_host . \")\");\n\n\t\t$this->generateNewUserData($sql, $sql_root);\n\t\treturn $this->response(['true']);\n\t}\n\n\t/**\n\t * list available mysql-server\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t */\n\tpublic function listing()\n\t{\n\t\t$sql = [];\n\t\t$sql_root = [];\n\t\t// get all data from lib/userdata\n\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\n\t\t// limit customer to its allowed servers\n\t\t$allowed_mysqls = [];\n\t\tif ($this->isAdmin() == false) {\n\t\t\t$allowed_mysqls = json_decode($this->getUserDetail('allowed_mysqlserver'), true);\n\t\t}\n\n\t\t$result = [];\n\t\tforeach ($sql_root as $index => $sqlrootdata) {\n\t\t\tif ($this->isAdmin() == false) {\n\t\t\t\tif ($allowed_mysqls === false || empty($allowed_mysqls)) {\n\t\t\t\t\tbreak;\n\t\t\t\t} elseif (!in_array($index, $allowed_mysqls)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// no usernames required for non-admins\n\t\t\t\tunset($sqlrootdata['user']);\n\t\t\t}\n\t\t\t// passwords will not be returned in any case for security reasons\n\t\t\tunset($sqlrootdata['password']);\n\t\t\t$sqlrootdata['id'] = $index;\n\t\t\t$result[$index] = $sqlrootdata;\n\t\t}\n\n\t\treturn $this->response(['list' => $result, 'count' => count($result)]);\n\t}\n\n\t/**\n\t * returns the total number of mysql servers\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin() == false) {\n\t\t\t$allowed_mysqls = json_decode($this->getUserDetail('allowed_mysqlserver'), true);\n\t\t\tif ($allowed_mysqls) {\n\t\t\t\treturn $this->response(count($allowed_mysqls));\n\t\t\t}\n\t\t\treturn $this->response(0);\n\t\t}\n\t\t$sql_root = [];\n\t\t// get all data from lib/userdata\n\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\t\treturn $this->response(count($sql_root));\n\t}\n\n\t/**\n\t * Return info about a specific mysql-server\n\t *\n\t * @param int $id\n\t *             optional the number of the mysql server (either id or dbserver must be set)\n\t * @param int $dbserver\n\t *             optional the number of the mysql server (either id or dbserver must be set)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = (int)$this->getParam('id', true, -1);\n\t\t$dn_optional = $id >= 0;\n\t\t$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);\n\t\t$dbserver = $id >= 0 ? $id : $dbserver;\n\n\t\t$sql_root = [];\n\t\t// get all data from lib/userdata\n\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\n\t\tif (!isset($sql_root[$dbserver])) {\n\t\t\tthrow new Exception('Mysql server not found', 404);\n\t\t}\n\n\t\t// limit customer to its allowed servers\n\t\tif ($this->isAdmin() == false) {\n\t\t\t$allowed_mysqls = json_decode($this->getUserDetail('allowed_mysqlserver'), true);\n\t\t\tif ($allowed_mysqls === false || empty($allowed_mysqls) || !in_array($dbserver, $allowed_mysqls)) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t// no usernames required for non-admins\n\t\t\tunset($sql_root[$dbserver]['user']);\n\t\t}\n\n\t\tunset($sql_root[$dbserver]['password']);\n\t\t$sql_root[$dbserver]['id'] = $dbserver;\n\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] get database-server '\" . $sql_root[$dbserver]['caption'] . \"'\");\n\t\treturn $this->response($sql_root[$dbserver]);\n\t}\n\n\t/**\n\t * update given mysql-server\n\t *\n\t * @param int $id\n\t *             optional the number of the mysql server (either id or dbserver must be set)\n\t * @param int $dbserver\n\t *             optional the number of the mysql server (either id or dbserver must be set)\n\t * @param string $mysql_host\n\t *             ip/hostname of mysql-server\n\t * @param string $mysql_port\n\t *             optional, port to connect to\n\t * @param string $mysql_ca\n\t *             optional, path to certificate file\n\t * @param string $mysql_verifycert\n\t *             optional, verify server certificate\n\t * @param string $privileged_user\n\t *             privileged user on the mysql-server (must have GRANT privileges)\n\t * @param string $privileged_password\n\t *             password of privileged user\n\t * @param string $description\n\t *             optional, description for server\n\t * @param bool $allow_all_customers\n\t *             optional add this configuration to the list of every existing customer's allowed-mysqlserver-config list, default is false (no)\n\t * @param bool $test_connection\n\t *             optional, test connection with given credentials, default is true (yes)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\t$this->validateAccess();\n\n\t\t$id = (int)$this->getParam('id', true, -1);\n\t\t$dn_optional = $id >= 0;\n\t\t$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);\n\t\t$dbserver = $id >= 0 ? $id : $dbserver;\n\n\t\t$sql_root = [];\n\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\n\t\tif (!isset($sql_root[$dbserver])) {\n\t\t\tthrow new Exception('Mysql server not found', 404);\n\t\t}\n\n\t\t$result = $sql_root[$dbserver];\n\n\t\tif ($dbserver == 0) {\n\t\t\t$mysql_host = $result['host'];\n\t\t} else {\n\t\t\t$mysql_host = $this->getParam('mysql_host', true, $result['host']);\n\t\t}\n\t\t$mysql_port = $this->getParam('mysql_port', true, $result['port'] ?? 3306);\n\t\t$mysql_ca = $this->getParam('mysql_ca', true, $result['ssl']['caFile'] ?? '');\n\t\t$mysql_verifycert = $this->getBoolParam('mysql_verifycert', true, $result['ssl']['verifyServerCertificate'] ?? 0);\n\t\t$privileged_user = $this->getParam('privileged_user', true, $result['user']);\n\t\t$privileged_password = $this->getParam('privileged_password', true, '');\n\t\t$description = $this->getParam('description', true, $result['caption']);\n\t\t$allow_all_customers = $this->getParam('allow_all_customers', true, 0);\n\t\t$test_connection = $this->getParam('test_connection', true, 1);\n\n\t\t// validation\n\t\t$mysql_host_chk = Validate::validate_ip2($mysql_host, true, 'invalidip', true, true, false);\n\t\tif ($mysql_host_chk === false) {\n\t\t\t$mysql_host_chk = Validate::validateLocalHostname($mysql_host);\n\t\t\tif ($mysql_host_chk === false) {\n\t\t\t\t$mysql_host_chk = Validate::validateDomain($mysql_host);\n\t\t\t\tif ($mysql_host_chk === false) {\n\t\t\t\t\tthrow new Exception(\"Invalid mysql server ip/hostname\", 406);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t$mysql_port = Validate::validate($mysql_port, 'port', Validate::REGEX_PORT, '', [3306], true);\n\t\t$mysql_ca = !empty($mysql_ca) ? FileDir::makeCorrectFile($mysql_ca) : '';\n\t\t$privileged_user = Validate::validate($privileged_user, 'privileged_user', '/^[a-z][a-z0-9\\-_]+$/i', '', [], true);\n\t\t$privileged_password = Validate::validate($privileged_password, 'password', '', '', [], true);\n\t\t$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\t// keep old password?\n\t\tif (empty($privileged_password)) {\n\t\t\t$privileged_password = $result['password'];\n\t\t}\n\n\t\tif ($mysql_host != $result['host']) {\n\t\t\t// check whether the server is in use by any customer\n\t\t\t$result_ms = $this->databasesOnServer(true, $dbserver);\n\t\t\tif ($result_ms > 0) {\n\t\t\t\tthrow new Exception(\"Unable to update mysql-host as there are still databases on it\", 406);\n\t\t\t}\n\t\t}\n\n\t\t// testing connection with given credentials\n\t\tif ($test_connection) {\n\t\t\t$options = array(\n\t\t\t\tPDO::MYSQL_ATTR_INIT_COMMAND => 'SET names utf8'\n\t\t\t);\n\t\t\tif (!empty($mysql_ca)) {\n\t\t\t\t$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;\n\t\t\t\t$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;\n\t\t\t}\n\n\t\t\t$dsn = \"mysql:host=\" . $mysql_host . \";port=\" . $mysql_port . \";\";\n\t\t\ttry {\n\t\t\t\t$db_test = new \\PDO($dsn, $privileged_user, $privileged_password, $options);\n\t\t\t\tunset($db_test);\n\t\t\t} catch (PDOException $e) {\n\t\t\t\tthrow new Exception(\"Connection to given mysql database could not be established. Error-message: \" . $e->getMessage(), $e->getCode());\n\t\t\t}\n\t\t}\n\n\t\t// set new values to sql_root array\n\t\t$sql_root[$dbserver] = [\n\t\t\t'caption' => $description,\n\t\t\t'host' => $mysql_host,\n\t\t\t'port' => $mysql_port,\n\t\t\t'user' => $privileged_user,\n\t\t\t'password' => $privileged_password,\n\t\t\t'ssl' => [\n\t\t\t\t'caFile' => $mysql_ca ?? \"\",\n\t\t\t\t'verifyServerCertificate' => $mysql_verifycert ?? false\n\t\t\t]\n\t\t];\n\n\t\t$this->generateNewUserData($sql, $sql_root);\n\n\t\tif ($allow_all_customers) {\n\t\t\t$this->addDatabaseFromCustomerAllowedList($dbserver);\n\t\t}\n\n\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] edited database server '\" . $description . \"' (\" . $mysql_host . \")\");\n\n\t\treturn $this->response(['true']);\n\t}\n\n\t/**\n\t * check whether a given customer / current user (as customer) has\n\t * databases on the given dbserver\n\t *\n\t * @param int $mysql_server\n\t * @param int $customerid\n\t *            optional, admin-only, select ftp-users of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select ftp-users of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count\n\t */\n\tpublic function databasesOnServer(bool $internal_all = false, int $dbserver = 0)\n\t{\n\t\tif ($internal_all) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) num_dbs FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\t\tWHERE `dbserver` = :dbserver\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, ['dbserver' => $dbserver], true, true);\n\t\t\treturn (int)$result['num_dbs'];\n\t\t} else {\n\t\t\t$dbserver = $this->getParam('mysql_server');\n\t\t\t$customer_ids = $this->getAllowedCustomerIds();\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) num_dbs FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \") AND `dbserver` = :dbserver\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, ['dbserver' => $dbserver], true, true);\n\t\t\treturn $this->response(['count' => $result['num_dbs']]);\n\t\t}\n\t}\n\n\tprivate function removeDatabaseFromCustomerAllowedList(int $dbserver)\n\t{\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT customerid, allowed_mysqlserver FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\");\n\t\tDatabase::pexecute($sel_stmt);\n\t\t$upd_stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t`allowed_mysqlserver` = :am WHERE `customerid` = :cid\n\t\t\");\n\t\twhile ($customer = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$allowed_mysqls = json_decode(($customer['allowed_mysqlserver'] ?? '[]'), true);\n\t\t\tif (($key = array_search($dbserver, $allowed_mysqls)) !== false) {\n\t\t\t\tunset($allowed_mysqls[$key]);\n\t\t\t\t$allowed_mysqls = json_encode($allowed_mysqls);\n\t\t\t\tDatabase::pexecute($upd_stmt, ['am' => $allowed_mysqls, 'cid' => $customer['customerid']]);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate function addDatabaseFromCustomerAllowedList(int $dbserver)\n\t{\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT customerid, allowed_mysqlserver FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\");\n\t\tDatabase::pexecute($sel_stmt);\n\t\t$upd_stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t`allowed_mysqlserver` = :am WHERE `customerid` = :cid\n\t\t\");\n\t\twhile ($customer = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$allowed_mysqls = json_decode(($customer['allowed_mysqlserver'] ?: '[]'), true);\n\t\t\tif (!in_array($dbserver, $allowed_mysqls)) {\n\t\t\t\t$allowed_mysqls[] = $dbserver;\n\t\t\t\t$allowed_mysqls = json_encode($allowed_mysqls);\n\t\t\t\tDatabase::pexecute($upd_stmt, ['am' => $allowed_mysqls, 'cid' => $customer['customerid']]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * write new userdata.inc.php file\n\t */\n\tprivate function generateNewUserData(array $sql, array $sql_root)\n\t{\n\t\t$content = PhpHelper::parseArrayToPhpFile(\n\t\t\t['sql' => $sql, 'sql_root' => $sql_root],\n\t\t\t'automatically generated userdata.inc.php for froxlor'\n\t\t);\n\t\tchmod(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\", 0700);\n\t\tfile_put_contents(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\", $content);\n\t\tchmod(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\", 0400);\n\t\tclearstatcache();\n\t\tif (function_exists('opcache_invalidate')) {\n\t\t\t@opcache_invalidate(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\", true);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Mysqls.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Database\\DbManager;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\User;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Mysqls extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new mysql-database\n\t *\n\t * @param string $mysql_password\n\t *            password for the created database and database-user\n\t * @param int $mysql_server\n\t *            optional, default is 0\n\t * @param string $description\n\t *            optional, description for database\n\t * @param string $custom_suffix\n\t *            optional, name for database if customer.mysqlprefix setting is set to \"DBNAME\"\n\t * @param bool $sendinfomail\n\t *            optional, send created resource-information to customer, default: false\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif (($this->getUserDetail('mysqls_used') < $this->getUserDetail('mysqls') || $this->getUserDetail('mysqls') == '-1') || $this->isAdmin()) {\n\t\t\t// required parameters\n\t\t\t$password = $this->getParam('mysql_password');\n\n\t\t\t// parameters\n\t\t\t$databasedescription = $this->getParam('description', true, '');\n\t\t\t$databasename = $this->getParam('custom_suffix', true, '');\n\t\t\t$sendinfomail = $this->getBoolParam('sendinfomail', true, 0);\n\t\t\t// get needed customer info to reduce the mysql-usage-counter by one\n\t\t\t$customer = $this->getCustomerData('mysqls');\n\t\t\t$dbserver = $this->getParam('mysql_server', true, $this->getDefaultMySqlServer($customer));\n\n\t\t\t// validation\n\t\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\t\t\t$password = Crypt::validatePassword($password, true);\n\t\t\t$databasedescription = Validate::validate(trim($databasedescription), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\tif (!empty($databasename)) {\n\t\t\t\t$databasename = Validate::validate(trim($databasename), 'database_name', '/^[A-Za-z0-9][A-Za-z0-9\\-_]+$/i', '', [], true);\n\t\t\t}\n\n\t\t\t// validate whether the dbserver exists\n\t\t\t$dbserver = Validate::validate($dbserver, html_entity_decode(lng('mysql.mysql_server')), '/^[0-9]+$/', '', 0, true);\n\t\t\t// enforce per-customer allowed_mysqlserver allowlist\n\t\t\tif (!$this->isAdmin()) {\n\t\t\t\t$allowed = json_decode($customer['allowed_mysqlserver'] ?? '[]', true);\n\t\t\t\tif (!is_array($allowed) || empty($allowed)\n\t\t\t\t\t|| !in_array((int)$dbserver, array_map('intval', $allowed), true)) {\n\t\t\t\t\tthrow new Exception('You cannot access this resource', 405);\n\t\t\t\t}\n\t\t\t}\n\t\t\tDatabase::needRoot(true, $dbserver, false);\n\t\t\tDatabase::needSqlData();\n\t\t\t$sql_root = Database::getSqlData();\n\t\t\tDatabase::needRoot(false);\n\t\t\tif (!is_array($sql_root)) {\n\t\t\t\tthrow new Exception(\"Database server with index #\" . $dbserver . \" is unknown\", 404);\n\t\t\t}\n\n\t\t\tif ($sendinfomail != 1) {\n\t\t\t\t$sendinfomail = 0;\n\t\t\t}\n\n\t\t\t$newdb_params = [\n\t\t\t\t'loginname' => ($this->isAdmin() ? $customer['loginname'] : $this->getUserDetail('loginname')),\n\t\t\t\t'mysql_lastaccountnumber' => ($this->isAdmin() ? $customer['mysql_lastaccountnumber'] : $this->getUserDetail('mysql_lastaccountnumber'))\n\t\t\t];\n\t\t\t// create database, user, set permissions, etc.pp.\n\t\t\t$dbm = new DbManager($this->logger());\n\n\t\t\tif (strtoupper(Settings::Get('customer.mysqlprefix')) == 'DBNAME' && !empty($databasename)) {\n\t\t\t\tif (strlen($newdb_params['loginname'] . '_' . $databasename) > Database::getSqlUsernameLength()) {\n\t\t\t\t\tthrow new Exception(\"Database name cannot be longer than \" . (Database::getSqlUsernameLength() - strlen($newdb_params['loginname'] . '_')) . \" characters.\", 406);\n\t\t\t\t}\n\t\t\t\t$username = $dbm->createDatabase($newdb_params['loginname'] . '_' . $databasename, $password, $dbserver, 0, $newdb_params['loginname']);\n\t\t\t} else {\n\t\t\t\t$username = $dbm->createDatabase($newdb_params['loginname'], $password, $dbserver, $newdb_params['mysql_lastaccountnumber'], $newdb_params['loginname']);\n\t\t\t}\n\n\t\t\t// we've checked against the password in dbm->createDatabase\n\t\t\tif ($username == false) {\n\t\t\t\tResponse::standardError('passwordshouldnotbeusername', '', true);\n\t\t\t}\n\n\t\t\t// add database info to froxlor\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\t\tSET\n\t\t\t\t`customerid` = :customerid,\n\t\t\t\t`databasename` = :databasename,\n\t\t\t\t`description` = :description,\n\t\t\t\t`dbserver` = :dbserver\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\"databasename\" => $username,\n\t\t\t\t\"description\" => $databasedescription,\n\t\t\t\t\"dbserver\" => $dbserver\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t\t$databaseid = Database::lastInsertId();\n\t\t\t$params['id'] = $databaseid;\n\n\t\t\t// update customer usage\n\t\t\tCustomers::increaseUsage($customer['customerid'], 'mysqls_used');\n\t\t\tCustomers::increaseUsage($customer['customerid'], 'mysql_lastaccountnumber');\n\n\t\t\t// send info-mail?\n\t\t\tif ($sendinfomail == 1) {\n\t\t\t\t$pma = lng('admin.notgiven');\n\t\t\t\tif (Settings::Get('panel.phpmyadmin_url') != '') {\n\t\t\t\t\t$pma = Settings::Get('panel.phpmyadmin_url');\n\t\t\t\t}\n\n\t\t\t\tDatabase::needRoot(true, $dbserver, false);\n\t\t\t\tDatabase::needSqlData();\n\t\t\t\t$sql_root = Database::getSqlData();\n\t\t\t\tDatabase::needRoot(false);\n\t\t\t\t$userinfo = $customer;\n\n\t\t\t\t$replace_arr = [\n\t\t\t\t\t'SALUTATION' => User::getCorrectUserSalutation($userinfo),\n\t\t\t\t\t'CUST_NAME' => User::getCorrectUserSalutation($userinfo), // < keep this for compatibility\n\t\t\t\t\t'NAME' => $userinfo['name'],\n\t\t\t\t\t'FIRSTNAME' => $userinfo['firstname'],\n\t\t\t\t\t'COMPANY' => $userinfo['company'],\n\t\t\t\t\t'USERNAME' => $userinfo['loginname'],\n\t\t\t\t\t'CUSTOMER_NO' => $userinfo['customernumber'],\n\t\t\t\t\t'DB_NAME' => $username,\n\t\t\t\t\t'DB_PASS' => htmlentities(htmlentities($password)),\n\t\t\t\t\t'DB_DESC' => $databasedescription,\n\t\t\t\t\t'DB_SRV' => $sql_root['host'],\n\t\t\t\t\t'PMA_URI' => $pma\n\t\t\t\t];\n\n\t\t\t\t// get template for mail subject\n\t\t\t\t$mail_subject = $this->getMailTemplate($userinfo, 'mails', 'new_database_by_customer_subject', $replace_arr, lng('mails.new_database_by_customer.subject'));\n\t\t\t\t// get template for mail body\n\t\t\t\t$mail_body = $this->getMailTemplate($userinfo, 'mails', 'new_database_by_customer_mailbody', $replace_arr, lng('mails.new_database_by_customer.mailbody'));\n\n\t\t\t\t$_mailerror = false;\n\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\ttry {\n\t\t\t\t\t$this->mailer()->Subject = $mail_subject;\n\t\t\t\t\t$this->mailer()->AltBody = $mail_body;\n\t\t\t\t\t$this->mailer()->Body = str_replace(\"\\n\", \"<br />\", $mail_body);\n\t\t\t\t\t$this->mailer()->addAddress($userinfo['email'], User::getCorrectUserSalutation($userinfo));\n\t\t\t\t\t$this->mailer()->send();\n\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t$_mailerror = true;\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t$_mailerror = true;\n\t\t\t\t}\n\n\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_ERR, \"[API] Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\tResponse::standardError('errorsendingmail', $userinfo['email'], true);\n\t\t\t\t}\n\n\t\t\t\t$this->mailer()->clearAddresses();\n\t\t\t}\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added mysql-database '\" . $username . \"'\");\n\n\t\t\t$result = $this->apiCall('Mysqls.get', [\n\t\t\t\t'dbname' => $username,\n\t\t\t\t'mysql_server' => $dbserver\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"No more resources available\", 406);\n\t}\n\n\t/**\n\t * return a mysql database entry by either id or dbname\n\t *\n\t * @param int $id\n\t *            optional, the database-id\n\t * @param string $dbname\n\t *            optional, the databasename\n\t * @param int $mysql_server\n\t *            optional, specify database-server, default is none\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$dbname = $this->getParam('dbname', $dn_optional, '');\n\t\t$dbserver = $this->getParam('mysql_server', true, -1);\n\n\t\tif ($dbserver != -1) {\n\t\t\t$dbserver = Validate::validate($dbserver, html_entity_decode(lng('mysql.mysql_server')), '/^[0-9]+$/', '', 0, true);\n\t\t}\n\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($this->getUserDetail('customers_see_all') != 1) {\n\t\t\t\t// if it's a reseller or an admin who cannot see all customers, we need to check\n\t\t\t\t// whether the database belongs to one of his customers\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t\t$customer_ids = [];\n\t\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t\t}\n\t\t\t\tif (count($customer_ids) > 0) {\n\t\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\t\t\t\tWHERE \" . ($id > 0 ? \"`id` = :iddn\" : \"`databasename` = :iddn\") . ($dbserver >= 0 ? \" AND `dbserver` = :dbserver\" : \"\") . \" AND `customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\t\t\t\");\n\t\t\t\t\t$params = [\n\t\t\t\t\t\t'iddn' => ($id <= 0 ? $dbname : $id)\n\t\t\t\t\t];\n\t\t\t\t\tif ($dbserver >= 0) {\n\t\t\t\t\t\t$params['dbserver'] = $dbserver;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Exception(\"You do not have any customers yet\", 406);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\t\t\tWHERE \" . ($id > 0 ? \"`id` = :iddn\" : \"`databasename` = :iddn\") . ($dbserver >= 0 ? \" AND `dbserver` = :dbserver\" : \"\"));\n\t\t\t\t$params = [\n\t\t\t\t\t'iddn' => ($id <= 0 ? $dbname : $id)\n\t\t\t\t];\n\t\t\t\tif ($dbserver >= 0) {\n\t\t\t\t\t$params['dbserver'] = $dbserver;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (Settings::IsInList('panel.customer_hide_options', 'mysql')) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\t\tWHERE `customerid`= :customerid AND \" . ($id > 0 ? \"`id` = :iddn\" : \"`databasename` = :iddn\") . ($dbserver >= 0 ? \" AND `dbserver` = :dbserver\" : \"\"));\n\t\t\t$params = [\n\t\t\t\t'customerid' => $this->getUserDetail('customerid'),\n\t\t\t\t'iddn' => ($id <= 0 ? $dbname : $id)\n\t\t\t];\n\t\t\tif ($dbserver >= 0) {\n\t\t\t\t$params['dbserver'] = $dbserver;\n\t\t\t}\n\t\t}\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\tDatabase::needRoot(true, $result['dbserver'], false);\n\t\t\t$mbdata_stmt = Database::prepare(\"\n\t\t\t\tSELECT SUM(data_length + index_length) as MB FROM information_schema.TABLES\n\t\t\t\tWHERE table_schema = :table_schema\n\t\t\t\tGROUP BY table_schema\n\t\t\t\");\n\t\t\tDatabase::pexecute($mbdata_stmt, [\n\t\t\t\t\"table_schema\" => $result['databasename']\n\t\t\t], true, true);\n\t\t\t$mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\tDatabase::needRoot(false);\n\t\t\t$result['size'] = $mbdata['MB'] ?? 0;\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get database '\" . $result['databasename'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\t$key = ($id > 0 ? \"id #\" . $id : \"dbname '\" . $dbname . \"'\");\n\t\tthrow new Exception(\"MySQL database with \" . $key . \" could not be found\", 404);\n\t}\n\n\t/**\n\t * update a mysql database entry by either id or dbname\n\t *\n\t * @param int $id\n\t *            optional, the database-id\n\t * @param string $dbname\n\t *            optional, the databasename\n\t * @param int $mysql_server\n\t *            optional, specify database-server, default is none\n\t * @param string $mysql_password\n\t *            optional, update password for the database\n\t * @param string $description\n\t *            optional, description for database\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$dbname = $this->getParam('dbname', $dn_optional, '');\n\t\t$dbserver = $this->getParam('mysql_server', true, -1);\n\t\t$customer = $this->getCustomerData();\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$result = $this->apiCall('Mysqls.get', [\n\t\t\t'id' => $id,\n\t\t\t'dbname' => $dbname,\n\t\t\t'mysql_server' => $dbserver\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// parameters\n\t\t$password = $this->getParam('mysql_password', true, '');\n\t\t$databasedescription = $this->getParam('description', true, $result['description']);\n\n\t\t// validation\n\t\t$password = Validate::validate($password, 'password', '', '', [], true);\n\t\t$databasedescription = Validate::validate(trim($databasedescription), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\tif ($password != '') {\n\t\t\t// validate password\n\t\t\t$password = Crypt::validatePassword($password, true);\n\n\t\t\tif ($password == $result['databasename']) {\n\t\t\t\tResponse::standardError('passwordshouldnotbeusername', '', true);\n\t\t\t}\n\n\t\t\t// Begin root-session\n\t\t\tDatabase::needRoot(true, $result['dbserver'], false);\n\t\t\t$dbmgr = new DbManager($this->logger());\n\t\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t\t$dbmgr->getManager()->grantPrivilegesTo($result['databasename'], $password, $mysql_access_host, false, true);\n\t\t\t}\n\n\t\t\t$stmt = Database::prepare(\"FLUSH PRIVILEGES\");\n\t\t\tDatabase::pexecute($stmt, null, true, true);\n\t\t\tDatabase::needRoot(false);\n\t\t\t// End root-session\n\t\t}\n\t\t$stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\tSET `description` = :desc\n\t\t\tWHERE `customerid` = :customerid\n\t\t\tAND `id` = :id\n\t\t\");\n\t\t$params = [\n\t\t\t\"desc\" => $databasedescription,\n\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\"id\" => $id\n\t\t];\n\t\tDatabase::pexecute($stmt, $params, true, true);\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] updated mysql-database '\" . $result['databasename'] . \"'\");\n\t\t$result = $this->apiCall('Mysqls.get', [\n\t\t\t'dbname' => $result['databasename']\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * list all databases, if called from an admin, list all databases of all customers you are allowed to view, or\n\t * specify id or loginname for one specific customer\n\t *\n\t * @param int $mysql_server\n\t *            optional, specify dbserver to select from, else use all available\n\t * @param int $customerid\n\t *            optional, admin-only, select dbs of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select dbs of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t$result = [];\n\t\t$dbserver = $this->getParam('mysql_server', true, -1);\n\t\t$customer_ids = $this->getAllowedCustomerIds('mysql');\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\tWHERE `customerid`= :customerid AND `dbserver` = :dbserver\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\tif ($dbserver < 0) {\n\t\t\t// use all dbservers\n\t\t\t$dbservers_stmt = Database::query(\"SELECT DISTINCT `dbserver` FROM `\" . TABLE_PANEL_DATABASES . \"`\");\n\t\t\t$dbservers = $dbservers_stmt->fetchAll(PDO::FETCH_ASSOC);\n\t\t} else {\n\t\t\t// use specific dbserver\n\t\t\t$dbservers = [\n\t\t\t\t[\n\t\t\t\t\t'dbserver' => $dbserver\n\t\t\t\t]\n\t\t\t];\n\t\t}\n\n\t\tforeach ($customer_ids as $customer_id) {\n\t\t\tforeach ($dbservers as $_dbserver) {\n\t\t\t\tDatabase::pexecute($result_stmt, array_merge([\n\t\t\t\t\t'customerid' => $customer_id,\n\t\t\t\t\t'dbserver' => $_dbserver['dbserver']\n\t\t\t\t], $query_fields), true, true);\n\t\t\t\t// Begin root-session\n\t\t\t\tDatabase::needRoot(true, $_dbserver['dbserver'], false);\n\t\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$mbdata_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT SUM(data_length + index_length) as MB FROM information_schema.TABLES\n\t\t\t\t\t\tWHERE table_schema = :table_schema\n\t\t\t\t\t\tGROUP BY table_schema\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($mbdata_stmt, [\n\t\t\t\t\t\t\"table_schema\" => $row['databasename']\n\t\t\t\t\t], true, true);\n\t\t\t\t\t$mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\t\t$row['size'] = $mbdata['MB'] ?? 0;\n\t\t\t\t\t$result[] = $row;\n\t\t\t\t}\n\t\t\t\tDatabase::needRoot(false);\n\t\t\t}\n\t\t}\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of accessible databases\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select dbs of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select dbs of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\t$customer_ids = $this->getAllowedCustomerIds('mysql');\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(*) as num_dbs FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_dbs']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * delete a mysql database by either id or dbname\n\t *\n\t * @param int $id\n\t *            optional, the database-id\n\t * @param string $dbname\n\t *            optional, the databasename\n\t * @param int $mysql_server\n\t *            optional, specify database-server, default is none\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$dbname = $this->getParam('dbname', $dn_optional, '');\n\t\t$dbserver = $this->getParam('mysql_server', true, -1);\n\t\t$customer = $this->getCustomerData();\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$result = $this->apiCall('Mysqls.get', [\n\t\t\t'id' => $id,\n\t\t\t'dbname' => $dbname,\n\t\t\t'mysql_server' => $dbserver\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// Begin root-session\n\t\tDatabase::needRoot(true, $result['dbserver'], false);\n\t\t$dbm = new DbManager($this->logger());\n\t\t$dbm->getManager()->deleteDatabase($result['databasename'], $customer['loginname']);\n\t\tDatabase::needRoot(false);\n\t\t// End root-session\n\n\t\t// delete from table\n\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_DATABASES . \"` WHERE `id` = :id\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"id\" => $id\n\t\t], true, true);\n\n\t\t// get needed customer info to reduce the mysql-usage-counter by one\n\t\t$mysql_used = $customer['mysqls_used'];\n\n\t\t// reduce mysql-usage-counter\n\t\t$resetaccnumber = ($mysql_used == '1') ? \" , `mysql_lastaccountnumber` = '0' \" : '';\n\t\tCustomers::decreaseUsage($customer['customerid'], 'mysqls_used', $resetaccnumber);\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, \"[API] deleted database '\" . $result['databasename'] . \"'\");\n\t\treturn $this->response($result);\n\t}\n\n\tprivate function getDefaultMySqlServer(array $customer) {\n\t\t$allowed_mysqlservers = json_decode($customer['allowed_mysqlserver'] ?? '[]', true);\n\t\tasort($allowed_mysqlservers, SORT_NUMERIC);\n\t\tif (count($allowed_mysqlservers) == 1 && $allowed_mysqlservers[0] != 0) {\n\t\t\treturn (int) $allowed_mysqlservers[0];\n\t\t}\n\t\treturn (int) array_shift($allowed_mysqlservers);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/PhpSettings.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass PhpSettings extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * lists all php-setting entries\n\t *\n\t * @param bool $with_subdomains\n\t *            optional, also include subdomains to the list domains that use the config, default 0 (false)\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, \"[API] list php-configs\");\n\n\t\t\t$with_subdomains = $this->getBoolParam('with_subdomains', true, false);\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT c.*, fd.description as fpmdesc\n\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fd ON fd.id = c.fpmsettingid\" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());\n\t\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\t\t$phpconfigs = [];\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$query_params = [\n\t\t\t\t\t'id' => $row['id']\n\t\t\t\t];\n\n\t\t\t\t$query = \"SELECT * FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `phpsettingid` = :id AND `email_only` = '0' AND `phpenabled` = '1'\";\n\n\t\t\t\tif (!$with_subdomains) {\n\t\t\t\t\t$query .= \" AND `parentdomainid` = '0'\";\n\t\t\t\t}\n\n\t\t\t\tif ((int)$this->getUserDetail('customers_see_all') == 0) {\n\t\t\t\t\t$query .= \" AND `adminid` = :adminid\";\n\t\t\t\t\t$query_params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t\t}\n\n\t\t\t\tif ((int)Settings::Get('panel.phpconfigs_hidestdsubdomain') == 1) {\n\t\t\t\t\t$ssdids_res = Database::query(\"\n\t\t\t\t\tSELECT DISTINCT `standardsubdomain` FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\t\tWHERE `standardsubdomain` > 0 ORDER BY `standardsubdomain` ASC;\");\n\t\t\t\t\t$ssdids = [];\n\t\t\t\t\twhile ($ssd = $ssdids_res->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\t$ssdids[] = $ssd['standardsubdomain'];\n\t\t\t\t\t}\n\t\t\t\t\tif (count($ssdids) > 0) {\n\t\t\t\t\t\t$query .= \" AND `id` NOT IN (\" . implode(', ', $ssdids) . \")\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$domains = [];\n\t\t\t\t$subdomains = [];\n\t\t\t\t$domainresult_stmt = Database::prepare($query);\n\t\t\t\tDatabase::pexecute($domainresult_stmt, $query_params, true, true);\n\n\t\t\t\tif (Database::num_rows() > 0) {\n\t\t\t\t\twhile ($row2 = $domainresult_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\tif ($row2['parentdomainid'] != 0) {\n\t\t\t\t\t\t\t$subdomains[] = $row2['domain'];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$domains[] = $row2['domain'];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// check whether we use that config as froxor-vhost config\n\t\t\t\tif ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $row['id']) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.vhost_defaultini') == $row['id'])) {\n\t\t\t\t\t$domains[] = Settings::Get('system.hostname');\n\t\t\t\t}\n\n\t\t\t\t// check whether this is our default config\n\t\t\t\tif ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini') == $row['id']) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.defaultini') == $row['id'])) {\n\t\t\t\t\t$row['is_default'] = true;\n\t\t\t\t}\n\n\t\t\t\t$row['domains'] = $domains;\n\t\t\t\t$row['subdomains'] = $subdomains;\n\t\t\t\t$phpconfigs[] = $row;\n\t\t\t}\n\n\t\t\treturn $this->response([\n\t\t\t\t'count' => count($phpconfigs),\n\t\t\t\t'list' => $phpconfigs\n\t\t\t]);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * return a php-setting entry by id\n\t *\n\t * @param int $id\n\t *            php-settings-id\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_PHPCONFIGS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result);\n\t\t\t}\n\t\t\tthrow new Exception(\"php-config with id #\" . $id . \" could not be found\", 404);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * returns the total number of accessible php-setting entries\n\t *\n\t * @access admin\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$query_fields = [];\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_phps\n\t\t\t\tFROM `\" . TABLE_PANEL_PHPCONFIGS . \"` c\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_FPMDAEMONS . \"` fd ON fd.id = c.fpmsettingid\" .\n\t\t\t\t$this->getSearchWhere($query_fields));\n\t\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_phps']);\n\t\t\t}\n\t\t\treturn $this->response(0);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * add new php-settings entry\n\t *\n\t * @param string $description\n\t *            description of the php-config\n\t * @param string $phpsettings\n\t *            the actual ini-settings\n\t * @param string $binary\n\t *            optional the binary to php-cgi if FCGID is used\n\t * @param string $file_extensions\n\t *            optional allowed php-file-extensions if FCGID is used, default is 'php'\n\t * @param int $mod_fcgid_starter\n\t *            optional number of fcgid-starters if FCGID is used, default is -1\n\t * @param int $mod_fcgid_maxrequests\n\t *            optional number of fcgid-maxrequests if FCGID is used, default is -1\n\t * @param string $mod_fcgid_umask\n\t *            optional umask if FCGID is used, default is '022'\n\t * @param int $fpmconfig\n\t *            optional id of the fpm-daemon-config if FPM is used\n\t * @param bool $phpfpm_enable_slowlog\n\t *            optional whether to write a slowlog or not if FPM is used, default is 0 (false)\n\t * @param string $phpfpm_reqtermtimeout\n\t *            optional request terminate timeout if FPM is used, default is '60s'\n\t * @param string $phpfpm_reqslowtimeout\n\t *            optional request slowlog timeout if FPM is used, default is '5s'\n\t * @param bool $pass_authorizationheader\n\t *            optional whether to pass authorization header to webserver if FPM/FCGID is used, default is 0 (false)\n\t * @param bool $override_fpmconfig\n\t *            optional whether to override fpm-daemon-config value for the following settings if FPM is used,\n\t *            default is 0 (false)\n\t * @param string $pm\n\t *            optional process-manager to use if FPM is used (allowed values are 'static', 'dynamic' and\n\t *            'ondemand'), default is fpm-daemon-value\n\t * @param int $max_children\n\t *            optional number of max children if FPM is used, default is the fpm-daemon-value\n\t * @param int $start_server\n\t *            optional number of servers to start if FPM is used, default is fpm-daemon-value\n\t * @param int $min_spare_servers\n\t *            optional number of minimum spare servers if FPM is used, default is fpm-daemon-value\n\t * @param int $max_spare_servers\n\t *            optional number of maximum spare servers if FPM is used, default is fpm-daemon-value\n\t * @param int $max_requests\n\t *            optional number of maximum requests if FPM is used, default is fpm-daemon-value\n\t * @param int $idle_timeout\n\t *            optional number of seconds for idle-timeout if FPM is used, default is fpm-daemon-value\n\t * @param string $limit_extensions\n\t *            optional limitation of php-file-extensions if FPM is used, default is fpm-daemon-value\n\t * @param bool $allow_all_customers\n\t *            optional add this configuration to the list of every existing customer's allowed-fpm-config list,\n\t *            default is false (no)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t// required parameter\n\t\t\t$description = $this->getParam('description');\n\t\t\t$phpsettings = $this->getParam('phpsettings');\n\n\t\t\tif (Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t\t$binary = $this->getParam('binary');\n\t\t\t\t$fpm_config_id = 1;\n\t\t\t} elseif (Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$fpm_config_id = intval($this->getParam('fpmconfig'));\n\t\t\t} else {\n\t\t\t\t$fpm_config_id = 1;\n\t\t\t}\n\n\t\t\t// parameters\n\t\t\t$file_extensions = $this->getParam('file_extensions', true, 'php');\n\t\t\t$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, -1);\n\t\t\t$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, -1);\n\t\t\t$mod_fcgid_umask = $this->getParam('mod_fcgid_umask', true, \"022\");\n\t\t\t$fpm_enableslowlog = $this->getBoolParam('phpfpm_enable_slowlog', true, 0);\n\t\t\t$fpm_reqtermtimeout = $this->getParam('phpfpm_reqtermtimeout', true, \"60s\");\n\t\t\t$fpm_reqslowtimeout = $this->getParam('phpfpm_reqslowtimeout', true, \"5s\");\n\t\t\t$pass_authorizationheader = $this->getBoolParam('pass_authorizationheader', true, 0);\n\n\t\t\t$override_fpmconfig = $this->getBoolParam('override_fpmconfig', true, 0);\n\t\t\t$def_fpmconfig = $this->apiCall('FpmDaemons.get', [\n\t\t\t\t'id' => $fpm_config_id\n\t\t\t]);\n\t\t\t$pmanager = $this->getParam('pm', true, $def_fpmconfig['pm']);\n\t\t\t$max_children = $this->getParam('max_children', true, $def_fpmconfig['max_children']);\n\t\t\t$start_servers = $this->getParam('start_servers', true, $def_fpmconfig['start_servers']);\n\t\t\t$min_spare_servers = $this->getParam('min_spare_servers', true, $def_fpmconfig['min_spare_servers']);\n\t\t\t$max_spare_servers = $this->getParam('max_spare_servers', true, $def_fpmconfig['max_spare_servers']);\n\t\t\t$max_requests = $this->getParam('max_requests', true, $def_fpmconfig['max_requests']);\n\t\t\t$idle_timeout = $this->getParam('idle_timeout', true, $def_fpmconfig['idle_timeout']);\n\t\t\t$limit_extensions = $this->getParam('limit_extensions', true, $def_fpmconfig['limit_extensions']);\n\t\t\t$allow_all_customers = $this->getBoolParam('allow_all_customers', true, 0);\n\n\t\t\t// validation\n\t\t\t$description = Validate::validate($description, 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$phpsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $phpsettings), 'phpsettings', '/^[^\\0]*$/', '', [], true);\n\t\t\tif (Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t\t$binary = FileDir::makeCorrectFile(Validate::validate($binary, 'binary', '', '', [], true));\n\t\t\t\t$file_extensions = Validate::validate($file_extensions, 'file_extensions', '/^[a-zA-Z0-9\\s]*$/', '', [], true);\n\t\t\t\t$mod_fcgid_starter = Validate::validate($mod_fcgid_starter, 'mod_fcgid_starter', '/^[0-9]*$/', '', [\n\t\t\t\t\t'-1',\n\t\t\t\t\t''\n\t\t\t\t], true);\n\t\t\t\t$mod_fcgid_maxrequests = Validate::validate($mod_fcgid_maxrequests, 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', [\n\t\t\t\t\t'-1',\n\t\t\t\t\t''\n\t\t\t\t], true);\n\t\t\t\t$mod_fcgid_umask = Validate::validate($mod_fcgid_umask, 'mod_fcgid_umask', '/^[0-9]*$/', '', [], true);\n\t\t\t\t// disable fpm stuff\n\t\t\t\t$fpm_config_id = 1;\n\t\t\t\t$fpm_enableslowlog = 0;\n\t\t\t\t$fpm_reqtermtimeout = 0;\n\t\t\t\t$fpm_reqslowtimeout = 0;\n\t\t\t\t$override_fpmconfig = 0;\n\t\t\t} elseif (Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$fpm_reqtermtimeout = Validate::validate($fpm_reqtermtimeout, 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', [], true);\n\t\t\t\t$fpm_reqslowtimeout = Validate::validate($fpm_reqslowtimeout, 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', [], true);\n\t\t\t\tif (!in_array($pmanager, [\n\t\t\t\t\t'static',\n\t\t\t\t\t'dynamic',\n\t\t\t\t\t'ondemand'\n\t\t\t\t])) {\n\t\t\t\t\tthrow new Exception(\"Unknown process manager\", 406);\n\t\t\t\t}\n\t\t\t\tif (empty($limit_extensions)) {\n\t\t\t\t\t$limit_extensions = '.php';\n\t\t\t\t}\n\t\t\t\t$limit_extensions = Validate::validate($limit_extensions, 'limit_extensions', '/^(\\.[a-z]([a-z0-9]+)\\ ?)+$/', '', [], true);\n\n\t\t\t\t// disable fcgid stuff\n\t\t\t\t$binary = '/usr/bin/php-cgi';\n\t\t\t\t$file_extensions = 'php';\n\t\t\t\t$mod_fcgid_starter = 0;\n\t\t\t\t$mod_fcgid_maxrequests = 0;\n\t\t\t\t$mod_fcgid_umask = \"022\";\n\t\t\t}\n\n\t\t\tif (strlen($description) == 0 || strlen($description) > 50) {\n\t\t\t\tResponse::standardError('descriptioninvalid', '', true);\n\t\t\t}\n\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_PHPCONFIGS . \"` SET\n\t\t\t\t`description` = :desc,\n\t\t\t\t`binary` = :binary,\n\t\t\t\t`file_extensions` = :fext,\n\t\t\t\t`mod_fcgid_starter` = :starter,\n\t\t\t\t`mod_fcgid_maxrequests` = :mreq,\n\t\t\t\t`mod_fcgid_umask` = :umask,\n\t\t\t\t`fpm_slowlog` = :fpmslow,\n\t\t\t\t`fpm_reqterm` = :fpmreqterm,\n\t\t\t\t`fpm_reqslow` = :fpmreqslow,\n\t\t\t\t`phpsettings` = :phpsettings,\n\t\t\t\t`fpmsettingid` = :fpmsettingid,\n\t\t\t\t`pass_authorizationheader` = :fpmpassauth,\n\t\t\t\t`override_fpmconfig` = :ofc,\n\t\t\t\t`pm` = :pm,\n\t\t\t\t`max_children` = :max_children,\n\t\t\t\t`start_servers` = :start_servers,\n\t\t\t\t`min_spare_servers` = :min_spare_servers,\n\t\t\t\t`max_spare_servers` = :max_spare_servers,\n\t\t\t\t`max_requests` = :max_requests,\n\t\t\t\t`idle_timeout` = :idle_timeout,\n\t\t\t\t`limit_extensions` = :limit_extensions\n\t\t\t\");\n\t\t\t$ins_data = [\n\t\t\t\t'desc' => $description,\n\t\t\t\t'binary' => $binary,\n\t\t\t\t'fext' => $file_extensions,\n\t\t\t\t'starter' => $mod_fcgid_starter,\n\t\t\t\t'mreq' => $mod_fcgid_maxrequests,\n\t\t\t\t'umask' => $mod_fcgid_umask,\n\t\t\t\t'fpmslow' => $fpm_enableslowlog,\n\t\t\t\t'fpmreqterm' => $fpm_reqtermtimeout,\n\t\t\t\t'fpmreqslow' => $fpm_reqslowtimeout,\n\t\t\t\t'phpsettings' => $phpsettings,\n\t\t\t\t'fpmsettingid' => $fpm_config_id,\n\t\t\t\t'fpmpassauth' => $pass_authorizationheader,\n\t\t\t\t'ofc' => $override_fpmconfig,\n\t\t\t\t'pm' => $pmanager,\n\t\t\t\t'max_children' => $max_children,\n\t\t\t\t'start_servers' => $start_servers,\n\t\t\t\t'min_spare_servers' => $min_spare_servers,\n\t\t\t\t'max_spare_servers' => $max_spare_servers,\n\t\t\t\t'max_requests' => $max_requests,\n\t\t\t\t'idle_timeout' => $idle_timeout,\n\t\t\t\t'limit_extensions' => $limit_extensions\n\t\t\t];\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data, true, true);\n\t\t\t$ins_data['id'] = Database::lastInsertId();\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] php setting with description '\" . $description . \"' has been created by '\" . $this->getUserDetail('loginname') . \"'\");\n\n\t\t\t$result = $this->apiCall('PhpSettings.get', [\n\t\t\t\t'id' => $ins_data['id']\n\t\t\t]);\n\n\t\t\t$this->addForAllCustomers($allow_all_customers, $ins_data['id']);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * add given php-config id to the list of allowed php-config to all currently existing customers\n\t * if allow_all_customers parameter is true in PhpSettings::add() or PhpSettings::update()\n\t *\n\t * @param bool $allow_all_customers\n\t * @param int $config_id\n\t */\n\tprivate function addForAllCustomers(bool $allow_all_customers, int $config_id)\n\t{\n\t\t// should this config be added to the allowed list of all existing customers?\n\t\tif ($allow_all_customers) {\n\t\t\t$sel_stmt = Database::prepare(\"SELECT customerid, allowed_phpconfigs FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\");\n\t\t\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET allowed_phpconfigs = :ap WHERE customerid = :cid\");\n\t\t\tDatabase::pexecute($sel_stmt);\n\t\t\twhile ($cust = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t// get existing entries of customer\n\t\t\t\t$ap = json_decode($cust['allowed_phpconfigs'], true);\n\t\t\t\t// initialize array if it's empty\n\t\t\t\tif (empty($ap)) {\n\t\t\t\t\t$ap = [];\n\t\t\t\t}\n\t\t\t\t// add this config\n\t\t\t\t$ap[] = $config_id;\n\t\t\t\t// check for duplicates and force value-type to be int\n\t\t\t\t$ap = array_map('intval', array_unique($ap));\n\t\t\t\t// update customer-entry\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'ap' => json_encode($ap),\n\t\t\t\t\t'cid' => $cust['customerid']\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * update a php-setting entry by given id\n\t *\n\t * @param int $id\n\t * @param string $description\n\t *            description of the php-config\n\t * @param string $phpsettings\n\t *            the actual ini-settings\n\t * @param string $binary\n\t *            optional the binary to php-cgi if FCGID is used\n\t * @param string $file_extensions\n\t *            optional allowed php-file-extensions if FCGID is used, default is 'php'\n\t * @param int $mod_fcgid_starter\n\t *            optional number of fcgid-starters if FCGID is used, default is -1\n\t * @param int $mod_fcgid_maxrequests\n\t *            optional number of fcgid-maxrequests if FCGID is used, default is -1\n\t * @param string $mod_fcgid_umask\n\t *            optional umask if FCGID is used, default is '022'\n\t * @param int $fpmconfig\n\t *            optional id of the fpm-daemon-config if FPM is used\n\t * @param bool $phpfpm_enable_slowlog\n\t *            optional whether to write a slowlog or not if FPM is used, default is 0 (false)\n\t * @param string $phpfpm_reqtermtimeout\n\t *            optional request terminate timeout if FPM is used, default is '60s'\n\t * @param string $phpfpm_reqslowtimeout\n\t *            optional request slowlog timeout if FPM is used, default is '5s'\n\t * @param bool $pass_authorizationheader\n\t *            optional whether to pass authorization header to webserver if FPM is used, default is 0 (false)\n\t * @param bool $override_fpmconfig\n\t *            optional whether to override fpm-daemon-config value for the following settings if FPM is used,\n\t *            default is 0 (false)\n\t * @param string $pm\n\t *            optional process-manager to use if FPM is used (allowed values are 'static', 'dynamic' and\n\t *            'ondemand'), default is fpm-daemon-value\n\t * @param int $max_children\n\t *            optional number of max children if FPM is used, default is the fpm-daemon-value\n\t * @param int $start_server\n\t *            optional number of servers to start if FPM is used, default is fpm-daemon-value\n\t * @param int $min_spare_servers\n\t *            optional number of minimum spare servers if FPM is used, default is fpm-daemon-value\n\t * @param int $max_spare_servers\n\t *            optional number of maximum spare servers if FPM is used, default is fpm-daemon-value\n\t * @param int $max_requests\n\t *            optional number of maximum requests if FPM is used, default is fpm-daemon-value\n\t * @param int $idle_timeout\n\t *            optional number of seconds for idle-timeout if FPM is used, default is fpm-daemon-value\n\t * @param string $limit_extensions\n\t *            optional limitation of php-file-extensions if FPM is used, default is fpm-daemon-value\n\t * @param bool $allow_all_customers\n\t *            optional add this configuration to the list of every existing customer's allowed-fpm-config list,\n\t *            default is false (no)\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t// required parameter\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result = $this->apiCall('PhpSettings.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\t// parameters\n\t\t\t$description = $this->getParam('description', true, $result['description']);\n\t\t\t$phpsettings = $this->getParam('phpsettings', true, $result['phpsettings']);\n\t\t\t$binary = $this->getParam('binary', true, $result['binary']);\n\t\t\t$fpm_config_id = intval($this->getParam('fpmconfig', true, $result['fpmsettingid']));\n\t\t\t$file_extensions = $this->getParam('file_extensions', true, $result['file_extensions']);\n\t\t\t$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']);\n\t\t\t$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, $result['mod_fcgid_maxrequests']);\n\t\t\t$mod_fcgid_umask = $this->getParam('mod_fcgid_umask', true, $result['mod_fcgid_umask']);\n\t\t\t$fpm_enableslowlog = $this->getBoolParam('phpfpm_enable_slowlog', true, $result['fpm_slowlog']);\n\t\t\t$fpm_reqtermtimeout = $this->getParam('phpfpm_reqtermtimeout', true, $result['fpm_reqterm']);\n\t\t\t$fpm_reqslowtimeout = $this->getParam('phpfpm_reqslowtimeout', true, $result['fpm_reqslow']);\n\t\t\t$pass_authorizationheader = $this->getBoolParam('pass_authorizationheader', true, $result['pass_authorizationheader']);\n\t\t\t$override_fpmconfig = $this->getBoolParam('override_fpmconfig', true, $result['override_fpmconfig']);\n\t\t\t$pmanager = $this->getParam('pm', true, $result['pm']);\n\t\t\t$max_children = $this->getParam('max_children', true, $result['max_children']);\n\t\t\t$start_servers = $this->getParam('start_servers', true, $result['start_servers']);\n\t\t\t$min_spare_servers = $this->getParam('min_spare_servers', true, $result['min_spare_servers']);\n\t\t\t$max_spare_servers = $this->getParam('max_spare_servers', true, $result['max_spare_servers']);\n\t\t\t$max_requests = $this->getParam('max_requests', true, $result['max_requests']);\n\t\t\t$idle_timeout = $this->getParam('idle_timeout', true, $result['idle_timeout']);\n\t\t\t$limit_extensions = $this->getParam('limit_extensions', true, $result['limit_extensions']);\n\t\t\t$allow_all_customers = $this->getBoolParam('allow_all_customers', true, 0);\n\n\t\t\t// validation\n\t\t\t$description = Validate::validate($description, 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\t\t\t$phpsettings = Validate::validate(str_replace(\"\\r\\n\", \"\\n\", $phpsettings), 'phpsettings', '/^[^\\0]*$/', '', [], true);\n\t\t\tif (Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t\t$binary = FileDir::makeCorrectFile(Validate::validate($binary, 'binary', '', '', [], true));\n\t\t\t\t$file_extensions = Validate::validate($file_extensions, 'file_extensions', '/^[a-zA-Z0-9\\s]*$/', '', [], true);\n\t\t\t\t$mod_fcgid_starter = Validate::validate($mod_fcgid_starter, 'mod_fcgid_starter', '/^[0-9]*$/', '', [\n\t\t\t\t\t'-1',\n\t\t\t\t\t''\n\t\t\t\t], true);\n\t\t\t\t$mod_fcgid_maxrequests = Validate::validate($mod_fcgid_maxrequests, 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', [\n\t\t\t\t\t'-1',\n\t\t\t\t\t''\n\t\t\t\t], true);\n\t\t\t\t$mod_fcgid_umask = Validate::validate($mod_fcgid_umask, 'mod_fcgid_umask', '/^[0-9]*$/', '', [], true);\n\t\t\t\t// disable fpm stuff\n\t\t\t\t$fpm_config_id = 1;\n\t\t\t\t$fpm_enableslowlog = 0;\n\t\t\t\t$fpm_reqtermtimeout = 0;\n\t\t\t\t$fpm_reqslowtimeout = 0;\n\t\t\t\t$override_fpmconfig = 0;\n\t\t\t} elseif (Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$fpm_reqtermtimeout = Validate::validate($fpm_reqtermtimeout, 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', [], true);\n\t\t\t\t$fpm_reqslowtimeout = Validate::validate($fpm_reqslowtimeout, 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', [], true);\n\t\t\t\tif (!in_array($pmanager, [\n\t\t\t\t\t'static',\n\t\t\t\t\t'dynamic',\n\t\t\t\t\t'ondemand'\n\t\t\t\t])) {\n\t\t\t\t\tthrow new Exception(\"Unknown process manager\", 406);\n\t\t\t\t}\n\t\t\t\tif (empty($limit_extensions)) {\n\t\t\t\t\t$limit_extensions = '.php';\n\t\t\t\t}\n\t\t\t\t$limit_extensions = Validate::validate($limit_extensions, 'limit_extensions', '/^(\\.[a-z]([a-z0-9]+)\\ ?)+$/', '', [], true);\n\n\t\t\t\t// disable fcgid stuff\n\t\t\t\t$binary = '/usr/bin/php-cgi';\n\t\t\t\t$file_extensions = 'php';\n\t\t\t\t$mod_fcgid_starter = 0;\n\t\t\t\t$mod_fcgid_maxrequests = 0;\n\t\t\t\t$mod_fcgid_umask = \"022\";\n\t\t\t}\n\n\t\t\tif (strlen($description) == 0 || strlen($description) > 50) {\n\t\t\t\tResponse::standardError('descriptioninvalid', '', true);\n\t\t\t}\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_PHPCONFIGS . \"` SET\n\t\t\t\t`description` = :desc,\n\t\t\t\t`binary` = :binary,\n\t\t\t\t`file_extensions` = :fext,\n\t\t\t\t`mod_fcgid_starter` = :starter,\n\t\t\t\t`mod_fcgid_maxrequests` = :mreq,\n\t\t\t\t`mod_fcgid_umask` = :umask,\n\t\t\t\t`fpm_slowlog` = :fpmslow,\n\t\t\t\t`fpm_reqterm` = :fpmreqterm,\n\t\t\t\t`fpm_reqslow` = :fpmreqslow,\n\t\t\t\t`phpsettings` = :phpsettings,\n\t\t\t\t`fpmsettingid` = :fpmsettingid,\n\t\t\t\t`pass_authorizationheader` = :fpmpassauth,\n\t\t\t\t`override_fpmconfig` = :ofc,\n\t\t\t\t`pm` = :pm,\n\t\t\t\t`max_children` = :max_children,\n\t\t\t\t`start_servers` = :start_servers,\n\t\t\t\t`min_spare_servers` = :min_spare_servers,\n\t\t\t\t`max_spare_servers` = :max_spare_servers,\n\t\t\t\t`max_requests` = :max_requests,\n\t\t\t\t`idle_timeout` = :idle_timeout,\n\t\t\t\t`limit_extensions` = :limit_extensions\n\t\t\t\tWHERE `id` = :id\n\t\t\t\");\n\t\t\t$upd_data = [\n\t\t\t\t'desc' => $description,\n\t\t\t\t'binary' => $binary,\n\t\t\t\t'fext' => $file_extensions,\n\t\t\t\t'starter' => $mod_fcgid_starter,\n\t\t\t\t'mreq' => $mod_fcgid_maxrequests,\n\t\t\t\t'umask' => $mod_fcgid_umask,\n\t\t\t\t'fpmslow' => $fpm_enableslowlog,\n\t\t\t\t'fpmreqterm' => $fpm_reqtermtimeout,\n\t\t\t\t'fpmreqslow' => $fpm_reqslowtimeout,\n\t\t\t\t'phpsettings' => $phpsettings,\n\t\t\t\t'fpmsettingid' => $fpm_config_id,\n\t\t\t\t'fpmpassauth' => $pass_authorizationheader,\n\t\t\t\t'ofc' => $override_fpmconfig,\n\t\t\t\t'pm' => $pmanager,\n\t\t\t\t'max_children' => $max_children,\n\t\t\t\t'start_servers' => $start_servers,\n\t\t\t\t'min_spare_servers' => $min_spare_servers,\n\t\t\t\t'max_spare_servers' => $max_spare_servers,\n\t\t\t\t'max_requests' => $max_requests,\n\t\t\t\t'idle_timeout' => $idle_timeout,\n\t\t\t\t'limit_extensions' => $limit_extensions,\n\t\t\t\t'id' => $id\n\t\t\t];\n\t\t\tDatabase::pexecute($upd_stmt, $upd_data, true, true);\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, \"[API] php setting with description '\" . $description . \"' has been updated by '\" . $this->getUserDetail('loginname') . \"'\");\n\n\t\t\t$result = $this->apiCall('PhpSettings.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\t$this->addForAllCustomers($allow_all_customers, $id);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n\n\t/**\n\t * delete a php-setting entry by id\n\t *\n\t * @param int $id\n\t *            php-settings-id\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {\n\t\t\t$id = $this->getParam('id');\n\n\t\t\t$result = $this->apiCall('PhpSettings.get', [\n\t\t\t\t'id' => $id\n\t\t\t]);\n\n\t\t\tif ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.vhost_defaultini') == $id)) {\n\t\t\t\tResponse::standardError('cannotdeletehostnamephpconfig', '', true);\n\t\t\t}\n\n\t\t\tif ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.defaultini') == $id)) {\n\t\t\t\tResponse::standardError('cannotdeletedefaultphpconfig', '', true);\n\t\t\t}\n\n\t\t\t// set php-config to default for all domains using the\n\t\t\t// config that is to be deleted\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t`phpsettingid` = '1' WHERE `phpsettingid` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_PHPCONFIGS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'id' => $id\n\t\t\t], true, true);\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] php setting '\" . $result['description'] . \"' has been deleted by '\" . $this->getUserDetail('loginname') . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/SshKeys.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\Validate\\Validate;\nuse phpseclib3\\Crypt\\PublicKeyLoader;\n\n/**\n * @since 2.3.0\n */\nclass SshKeys extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new ssh-key\n\t *\n\t * @param int $id\n\t *            optional id of ftp-user to add the ssh-key for, required if `ftpuser` is empty\n\t * @param string $ftpuser\n\t *            optional loginname of ftp-user to add the ssh-key for, required if `id` is empty\n\t * @param int $customerid\n\t *             optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *             optional, required when called as admin (if $customerid is not specified)\n\t * @param string $ssh_pubkey\n\t *            ssh public key to add for the given user\n\t * @param string $description\n\t *            optional, description for ssh-key\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif ($this->isAdmin() == false &&\n\t\t\t(Settings::IsInList('panel.customer_hide_options', 'ftp')\n\t\t\t\t|| intval(Settings::Get('system.allow_customer_shell')) == 0\n\t\t\t\t|| intval($this->getUserDetail('shell_allowed')) == 0)\n\t\t) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// get needed customer info\n\t\t$customer = $this->getCustomerData();\n\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$ea_optional = $id > 0;\n\t\t$ftpuser = $this->getParam('ftpuser', $ea_optional);\n\n\t\t// get ftp user\n\t\t$ftp_user = $this->apiCall('Ftps.get', [\n\t\t\t'id' => $id,\n\t\t\t'username' => $ftpuser\n\t\t]);\n\t\t$id = $ftp_user['id'];\n\n\t\t// parameters\n\t\t$ssh_pubkey = $this->getParam('ssh_pubkey');\n\t\t$description = $this->getParam('description', true, '');\n\n\t\t// validation\n\t\tif ($customer['customerid'] != $ftp_user['customerid']) {\n\t\t\tthrow new Exception(\"ftp user not found\", 404);\n\t\t}\n\t\tif (!$this->isValidSshPublicKey($ssh_pubkey)) {\n\t\t\tthrow new Exception(\"Given SSH-key does not seem to be a valid public key\", 406);\n\t\t}\n\t\t$key = PublicKeyLoader::loadPublicKey(trim($ssh_pubkey));\n\t\tif (empty($description)) {\n\t\t\t$description = $key->getComment() ?? '';\n\t\t}\n\t\t$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\t// check for existing ssh-key for given user\n\t\t$check_stmt = Database::prepare(\"\n\t\t\tSELECT `ssh_pubkey`\n\t\t\tFROM `\" . TABLE_PANEL_USER_SSHKEYS . \"`\n\t\t\tWHERE `ftp_user_id` = :fuid\n\t\t\");\n\t\tDatabase::pexecute($check_stmt, ['fuid' => $id]);\n\t\twhile ($row = $check_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t$rowkey = PublicKeyLoader::loadPublicKey($row['ssh_pubkey']);\n\t\t\tif ($rowkey->getFingerprint('sha256') == $key->getFingerprint('sha256')) {\n\t\t\t\tthrow new Exception(\"This SSH-key already exists for the given user\", 406);\n\t\t\t}\n\t\t}\n\n\t\t// insert data\n\t\t$stmt = Database::prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_USER_SSHKEYS . \"` SET\n\t\t\t`customerid` = :cid,\n\t\t\t`ftp_user_id` = :fid,\n\t\t\t`ssh_pubkey` = :sshpub,\n\t\t\t`description` = :desc\n\t\t\");\n\t\t$params = [\n\t\t\t\"cid\" => $customer['customerid'],\n\t\t\t\"fid\" => $id,\n\t\t\t\"sshpub\" => trim($ssh_pubkey),\n\t\t\t\"desc\" => $description\n\t\t];\n\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t$sshkeyid = Database::lastInsertId();\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] added ssh key for '\" . $ftp_user['username'] . \"'\");\n\t\t$result = $this->apiCall('SshKeys.get', [\n\t\t\t'id' => $sshkeyid\n\t\t]);\n\n\t\tif (Settings::Get('system.nssextrausers') == 1) {\n\t\t\t// this is used so that the libnss-extrausers cron is fired\n\t\t\tCronjob::inserttask(TaskId::REBUILD_NSSUSERS);\n\t\t}\n\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * return a ssh-key entry by id\n\t *\n\t * @param int $id\n\t *            the ssh-key id\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = $this->getParam('id');\n\n\t\t$params = [];\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($this->getUserDetail('customers_see_all') == false) {\n\t\t\t\t// if it's a reseller or an admin who cannot see all customers, we need to check\n\t\t\t\t// whether the database belongs to one of his customers\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t\t$customer_ids = [];\n\t\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t\t}\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT s.*, f.username\n\t\t\t\t\tFROM `\" . TABLE_PANEL_USER_SSHKEYS . \"` s\n\t\t\t\t\tLEFT JOIN `\" . TABLE_FTP_USERS . \"` f ON f.id = s.ftp_user_id\n\t\t\t\t\tWHERE s.`customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\t\t\tAND s.`id` = :id\n\t\t\t\t\");\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT s.*, f.username\n\t\t\t\t\tFROM `\" . TABLE_PANEL_USER_SSHKEYS . \"` s\n\t\t\t\t\tLEFT JOIN `\" . TABLE_FTP_USERS . \"` f ON f.id = s.ftp_user_id\n\t\t\t\t\tWHERE s.`id` = :id\n\t\t\t\t\");\n\t\t\t}\n\t\t} else {\n\t\t\tif (Settings::IsInList('panel.customer_hide_options', 'ftp')\n\t\t\t\t|| intval(Settings::Get('system.allow_customer_shell')) == 0\n\t\t\t\t|| intval($this->getUserDetail('shell_allowed')) == 0) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT s.*, f.username\n\t\t\t\tFROM `\" . TABLE_PANEL_USER_SSHKEYS . \"` s\n\t\t\t\tLEFT JOIN `\" . TABLE_FTP_USERS . \"` f ON f.id = s.ftp_user_id\n\t\t\t\tWHERE s.`customerid` = :customerid\n\t\t\t\tAND s.`id` = :id\n\t\t\t\");\n\t\t\t$params['customerid'] = $this->getUserDetail('customerid');\n\t\t}\n\t\t$params['id'] = $id;\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\t$key = PublicKeyLoader::loadPublicKey($result['ssh_pubkey']);\n\t\t\t$result['fingerprint'] = 'SHA256:' . $key->getFingerprint('sha256');\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get ssh-key for ftp-user '\" . $result['ftp_user_id'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\t$key = \"id #\" . $id;\n\t\tthrow new Exception(\"FTP user with \" . $key . \" could not be found\", 404);\n\t}\n\n\t/**\n\t * update a given ftp-user by id or username\n\t *\n\t * @param int $id\n\t *            the ssh-key id\n\t * @param string $description\n\t *            optional, description for ssh-key\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tif ($this->isAdmin() == false &&\n\t\t\t(Settings::IsInList('panel.customer_hide_options', 'ftp')\n\t\t\t\t|| intval(Settings::Get('system.allow_customer_shell')) == 0\n\t\t\t\t|| intval($this->getUserDetail('shell_allowed')) == 0)\n\t\t) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$id = $this->getParam('id');\n\n\t\t$result = $this->apiCall('SshKeys.get', [\n\t\t\t'id' => $id\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// parameters\n\t\t$description = $this->getParam('description', true, $result['description']);\n\n\t\t// validation\n\t\t$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);\n\n\t\t// get needed customer info\n\t\t$customer = $this->getCustomerData();\n\n\t\t$stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_PANEL_USER_SSHKEYS . \"`\n\t\t\tSET `description` = :desc\n\t\t\tWHERE `customerid` = :customerid\n\t\t\tAND `id` = :id\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"desc\" => $description,\n\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\"id\" => $id\n\t\t], true, true);\n\n\t\t$result = $this->apiCall('SshKeys.get', [\n\t\t\t'id' => $result['id']\n\t\t]);\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] updated ssh-key '\" . $result['id'] . \"'\");\n\n\t\tif (Settings::Get('system.nssextrausers') == 1) {\n\t\t\t// this is used so that the libnss-extrausers cron is fired\n\t\t\tCronjob::inserttask(TaskId::REBUILD_NSSUSERS);\n\t\t}\n\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * list all ssh-keys, if called from an admin, list all ssh-keys of all customers you are allowed to view, or\n\t * specify id or loginname for one specific customer\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select ftp-users of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select ftp-users of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\tif ($this->isAdmin() == false &&\n\t\t\t(Settings::IsInList('panel.customer_hide_options', 'ftp')\n\t\t\t\t|| intval(Settings::Get('system.allow_customer_shell')) == 0\n\t\t\t\t|| intval($this->getUserDetail('shell_allowed')) == 0)\n\t\t) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$customer_ids = $this->getAllowedCustomerIds('ftp');\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT s.*, f.username\n\t\t\tFROM `\" . TABLE_PANEL_USER_SSHKEYS . \"` s\n\t\t\tLEFT JOIN `\" . TABLE_FTP_USERS . \"` f ON f.id = s.ftp_user_id\n\t\t\tWHERE s.`customerid` IN (\" . implode(\", \", $customer_ids) . \")\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\twhile ($row = $result_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t$key = PublicKeyLoader::loadPublicKey($row['ssh_pubkey']);\n\t\t\t$row['fingerprint'] = 'SHA256:' . $key->getFingerprint('sha256');\n\t\t\t$result[] = $row;\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list ssh-keys\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of accessible ssh keys\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select ftp-users of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select ftp-users of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin() == false &&\n\t\t\t(Settings::IsInList('panel.customer_hide_options', 'ftp')\n\t\t\t\t|| intval(Settings::Get('system.allow_customer_shell')) == 0\n\t\t\t\t|| intval($this->getUserDetail('shell_allowed')) == 0)\n\t\t) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$customer_ids = $this->getAllowedCustomerIds('ftp');\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(*) as num_sshkeys FROM `\" . TABLE_PANEL_USER_SSHKEYS . \"`\n\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t$result = Database::pexecute_first($result_stmt, $query_fields, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_sshkeys']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * delete a ftp-user by either id or username\n\t *\n\t * @param int $id\n\t *            the ssh-key id\n\t * @param int $customerid\n\t *             optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *             optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\t$id = $this->getParam('id');\n\n\t\tif ($this->isAdmin() == false &&\n\t\t\t(Settings::IsInList('panel.customer_hide_options', 'ftp')\n\t\t\t\t|| intval(Settings::Get('system.allow_customer_shell')) == 0\n\t\t\t\t|| intval($this->getUserDetail('shell_allowed')) == 0)\n\t\t) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t// get ssh-key\n\t\t$result = $this->apiCall('SshKeys.get', [\n\t\t\t'id' => $id\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t$customer = $this->getCustomerData();\n\n\t\t// remove entry\n\t\t$stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_PANEL_USER_SSHKEYS . \"` WHERE `customerid` = :customerid AND `id` = :id\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\"id\" => $id\n\t\t], true, true);\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, \"[API] deleted ssh-key '\" . $result['id'] . \"'\");\n\n\t\tif (Settings::Get('system.nssextrausers') == 1) {\n\t\t\t// this is used so that the libnss-extrausers cron is fired\n\t\t\tCronjob::inserttask(TaskId::REBUILD_NSSUSERS);\n\t\t}\n\n\t\treturn $this->response($result);\n\t}\n\n\tprivate function isValidSshPublicKey(string $key): bool\n\t{\n\t\ttry {\n\t\t\t$loaded = PublicKeyLoader::loadPublicKey($key);\n\t\t\treturn $loaded !== null;\n\t\t} catch (\\Exception $e) {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/SubDomains.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass SubDomains extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * add a new subdomain\n\t *\n\t * @param string $subdomain\n\t *            part before domain.tld to create as subdomain\n\t * @param string $domain\n\t *            domainname of main-domain\n\t * @param int $alias\n\t *            optional, domain-id of a domain that the new domain should be an alias of\n\t * @param string $path\n\t *            optional, destination path relative to the customers-homedir, default is customers-homedir\n\t * @param string $url\n\t *            optional, overwrites path value with an URL to generate a redirect, alternatively use the path\n\t *            parameter also for URLs\n\t * @param int $openbasedir_path\n\t *            optional, either 0 for domains-docroot [default], 1 for customers-homedir or 2 for parent-directory of domains-docroot\n\t * @param int $phpsettingid\n\t *            optional, php-settings-id, if empty the $domain value is used\n\t * @param int $redirectcode\n\t *            optional, redirect-code-id from TABLE_PANEL_REDIRECTCODES\n\t * @param int $speciallogfile\n\t *            optional, whether to create an exclusive web-logfile for this domain (1) or not (0) or inherit value from parentdomain (2, default)\n\t * @param bool $sslenabled\n\t *            optional, whether or not SSL is enabled for this domain, regardless of the assigned ssl-ips, default\n\t *            1 (true)\n\t * @param bool $ssl_redirect\n\t *            optional, whether to generate a https-redirect or not, default false; requires SSL to be enabled\n\t * @param bool $letsencrypt\n\t *            optional, whether to generate a Let's Encrypt certificate for this domain, default false; requires\n\t *            SSL to be enabled\n\t * @param bool $http2\n\t *            optional, whether to enable http/2 for this subdomain (requires to be enabled in the settings),\n\t *            default 0 (false)\n\t * @param bool $http3\n\t *            optional, whether to enable http/3 for this subdomain (requires to be enabled in the settings),\n\t *            default 0 (false)\n\t * @param int $hsts_maxage\n\t *            optional max-age value for HSTS header, default 0\n\t * @param bool $hsts_sub\n\t *            optional whether or not to add subdomains to the HSTS header, default 0\n\t * @param bool $hsts_preload\n\t *            optional whether or not to preload HSTS header value, default 0\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tif (($this->getUserDetail('subdomains_used') < $this->getUserDetail('subdomains') || $this->getUserDetail('subdomains') == '-1') || $this->isAdmin()) {\n\t\t\t// parameters\n\t\t\t$subdomain = $this->getParam('subdomain');\n\t\t\t$domain = $this->getParam('domain');\n\n\t\t\t// optional parameters\n\t\t\t$aliasdomain = $this->getParam('alias', true, 0);\n\t\t\t$path = $this->getParam('path', true, '');\n\t\t\t$url = $this->getParam('url', true, '');\n\t\t\t$openbasedir_path = $this->getParam('openbasedir_path', true, 0);\n\t\t\t$phpsettingid = $this->getParam('phpsettingid', true, 0);\n\t\t\t$redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default'));\n\t\t\t$speciallogfile = intval($this->getParam('speciallogfile', true, 2));\n\t\t\t$isemaildomain = $this->getParam('isemaildomain', true, 0);\n\t\t\tif (Settings::Get('system.use_ssl')) {\n\t\t\t\t$sslenabled = $this->getBoolParam('sslenabled', true, 1);\n\t\t\t\t$ssl_redirect = $this->getBoolParam('ssl_redirect', true, 0);\n\t\t\t\t$letsencrypt = $this->getBoolParam('letsencrypt', true, 0);\n\t\t\t\t$http2 = $this->getBoolParam('http2', true, 0);\n\t\t\t\t$http3 = $this->getBoolParam('http3', true, 0);\n\t\t\t\t$hsts_maxage = $this->getParam('hsts_maxage', true, 0);\n\t\t\t\t$hsts_sub = $this->getBoolParam('hsts_sub', true, 0);\n\t\t\t\t$hsts_preload = $this->getBoolParam('hsts_preload', true, 0);\n\t\t\t} else {\n\t\t\t\t$sslenabled = 0;\n\t\t\t\t$ssl_redirect = 0;\n\t\t\t\t$letsencrypt = 0;\n\t\t\t\t$http2 = 0;\n\t\t\t\t$http3 = 0;\n\t\t\t\t$hsts_maxage = 0;\n\t\t\t\t$hsts_sub = 0;\n\t\t\t\t$hsts_preload = 0;\n\t\t\t}\n\n\t\t\t// get needed customer info to reduce the subdomain-usage-counter by one\n\t\t\t$customer = $this->getCustomerData('subdomains');\n\n\t\t\t// validation\n\t\t\t$subdomain = strtolower($subdomain);\n\t\t\tif (substr($subdomain, 0, 4) == 'xn--') {\n\t\t\t\tResponse::standardError('domain_nopunycode', '', true);\n\t\t\t}\n\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$subdomain = $idna_convert->encode(preg_replace([\n\t\t\t\t'/\\:(\\d)+$/',\n\t\t\t\t'/^https?\\:\\/\\//'\n\t\t\t], '', Validate::validate($subdomain, 'subdomain', '', 'subdomainiswrong', [], true)));\n\n\t\t\t// merge the two parts together\n\t\t\t$completedomain = $subdomain . '.' . $domain;\n\n\t\t\tif (Settings::Get('system.validate_domain') && !Validate::validateDomain($completedomain)) {\n\t\t\t\tResponse::standardError([\n\t\t\t\t\t'stringiswrong',\n\t\t\t\t\t'mydomain'\n\t\t\t\t], '', true);\n\t\t\t}\n\t\t\tif ($completedomain == strtolower(Settings::Get('system.hostname'))) {\n\t\t\t\tResponse::standardError('admin_domain_emailsystemhostname', '', true);\n\t\t\t}\n\n\t\t\t// check whether the domain already exists\n\t\t\t$completedomain_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tWHERE `domain` = :domain\n\t\t\t\tAND `customerid` = :customerid\n\t\t\t\tAND `email_only` = '0'\n\t\t\t\");\n\t\t\t$completedomain_check = Database::pexecute_first($completedomain_stmt, [\n\t\t\t\t\"domain\" => $completedomain,\n\t\t\t\t\"customerid\" => $customer['customerid']\n\t\t\t], true, true);\n\n\t\t\tif ($completedomain_check) {\n\t\t\t\t// no exception so far - domain exists\n\t\t\t\tResponse::standardError('domainexistalready', $completedomain, true);\n\t\t\t}\n\n\t\t\t// alias domain checked?\n\t\t\tif ($aliasdomain != 0) {\n\t\t\t\t// also check ip/port combination to be the same, #176\n\t\t\t\t$aliasdomain_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `d`.`id` FROM `\" . TABLE_PANEL_DOMAINS . \"` `d` , `\" . TABLE_PANEL_CUSTOMERS . \"` `c` , `\" . TABLE_DOMAINTOIP . \"` `dip`\n\t\t\t\t\tWHERE `d`.`aliasdomain` IS NULL\n\t\t\t\t\tAND `d`.`id` = :id\n\t\t\t\t\tAND `c`.`standardsubdomain` <> `d`.`id`\n\t\t\t\t\tAND `d`.`customerid` = :customerid\n\t\t\t\t\tAND `c`.`customerid` = `d`.`customerid`\n\t\t\t\t\tAND `d`.`id` = `dip`.`id_domain`\n\t\t\t\t\tAND `dip`.`id_ipandports`\n\t\t\t\t\tIN (SELECT `id_ipandports` FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :id )\n\t\t\t\t\tGROUP BY `d`.`domain`\n\t\t\t\t\tORDER BY `d`.`domain` ASC\n\t\t\t\t\");\n\t\t\t\t$aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, [\n\t\t\t\t\t\"id\" => $aliasdomain,\n\t\t\t\t\t\"customerid\" => $customer['customerid']\n\t\t\t\t], true, true);\n\t\t\t\tif ($aliasdomain_check['id'] != $aliasdomain) {\n\t\t\t\t\tResponse::standardError('domainisaliasorothercustomer', '', true);\n\t\t\t\t}\n\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());\n\t\t\t}\n\n\t\t\t// validate / correct path/url of domain\n\t\t\t$_doredirect = false;\n\t\t\t$path = $this->validateDomainDocumentRoot($path, $url, $customer, $completedomain, $_doredirect);\n\n\t\t\tif ($openbasedir_path > 2 && $openbasedir_path < 0) {\n\t\t\t\t$openbasedir_path = 0;\n\t\t\t}\n\n\t\t\t// get main domain for various checks\n\t\t\t$domain_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tWHERE `domain` = :domain\n\t\t\t\tAND `customerid` = :customerid\n\t\t\t\tAND `parentdomainid` = '0'\n\t\t\t\tAND `email_only` = '0'\n\t\t\t\");\n\t\t\t$domain_check = Database::pexecute_first($domain_stmt, [\n\t\t\t\t\"domain\" => $domain,\n\t\t\t\t\"customerid\" => $customer['customerid']\n\t\t\t], true, true);\n\n\t\t\tif (!$domain_check) {\n\t\t\t\t// the given main-domain\n\t\t\t\tResponse::standardError('maindomainnonexist', $domain, true);\n\t\t\t} elseif ($subdomain == 'www' && $domain_check['wwwserveralias'] == '1') {\n\t\t\t\t// you cannot add 'www' as subdomain when the maindomain generates a www-alias\n\t\t\t\tResponse::standardError('wwwnotallowed', '', true);\n\t\t\t} elseif ($completedomain_check && strtolower($completedomain_check['domain']) == strtolower($completedomain)) {\n\t\t\t\t// the domain does already exist as main-domain\n\t\t\t\tResponse::standardError('domainexistalready', $completedomain, true);\n\t\t\t} elseif ((int)$domain_check['deactivated'] == 1) {\n\t\t\t\t// main domain is deactivated\n\t\t\t\tResponse::standardError('maindomaindeactivated', $domain, true);\n\t\t\t}\n\n\t\t\t// if allowed, check for 'is email domain'-flag\n\t\t\tif ($domain_check['subcanemaildomain'] == '1' || $domain_check['subcanemaildomain'] == '2') {\n\t\t\t\t$isemaildomain = intval($isemaildomain);\n\t\t\t} else {\n\t\t\t\t$isemaildomain = $domain_check['subcanemaildomain'] == '3' ? 1 : 0;\n\t\t\t}\n\n\t\t\tif ($ssl_redirect != 0) {\n\t\t\t\t// a ssl-redirect only works if there actually is a\n\t\t\t\t// ssl ip/port assigned to the domain\n\t\t\t\tif (Domain::domainHasSslIpPort($domain_check['id']) == true) {\n\t\t\t\t\t$ssl_redirect = '1';\n\t\t\t\t\t$_doredirect = true;\n\t\t\t\t} else {\n\t\t\t\t\tResponse::standardError('sslredirectonlypossiblewithsslipport', '', true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($letsencrypt != 0) {\n\t\t\t\t// let's encrypt only works if there actually is a\n\t\t\t\t// ssl ip/port assigned to the domain\n\t\t\t\tif (Domain::domainHasSslIpPort($domain_check['id']) == true) {\n\t\t\t\t\t$letsencrypt = '1';\n\t\t\t\t} else {\n\t\t\t\t\tResponse::standardError('letsencryptonlypossiblewithsslipport', '', true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// validate dns if lets encrypt is enabled to check whether we can use it at all\n\t\t\tif ($letsencrypt == '1' && Settings::Get('system.le_domain_dnscheck') == '1') {\n\t\t\t\t$our_ips = Domain::getIpsOfDomain($domain_check['id']);\n\t\t\t\t$domain_ips = PhpHelper::gethostbynamel6($completedomain, true, Settings::Get('system.le_domain_dnscheck_resolver'));\n\t\t\t\tif ($domain_ips == false || count(array_intersect($our_ips, $domain_ips)) <= 0) {\n\t\t\t\t\tResponse::standardError('invaliddnsforletsencrypt', '', true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated\n\t\t\tif ($ssl_redirect > 0 && $letsencrypt == 1) {\n\t\t\t\t$ssl_redirect = 2;\n\t\t\t}\n\n\t\t\t// validate speciallogfile value\n\t\t\tif ($speciallogfile < 0 || $speciallogfile > 2) {\n\t\t\t\t$speciallogfile = 2; // inherit from parent-domain\n\t\t\t}\n\n\t\t\t// get the phpsettingid from parentdomain, #107\n\t\t\t$phpsid_stmt = Database::prepare(\"\n\t\t\t\tSELECT `phpsettingid` FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\t$phpsid_result = Database::pexecute_first($phpsid_stmt, [\n\t\t\t\t\"id\" => $domain_check['id']\n\t\t\t], true, true);\n\n\t\t\tif (!isset($phpsid_result['phpsettingid']) || (int)$phpsid_result['phpsettingid'] <= 0) {\n\t\t\t\t// assign default config\n\t\t\t\t$phpsid_result['phpsettingid'] = 1;\n\t\t\t}\n\n\t\t\tif ($domain_check['phpenabled'] == 1) {\n\t\t\t\t// check whether the customer has chosen its own php-config\n\t\t\t\tif ($phpsettingid > 0 && $phpsettingid != $phpsid_result['phpsettingid']) {\n\t\t\t\t\t$phpsid_result['phpsettingid'] = intval($phpsettingid);\n\t\t\t\t}\n\n\t\t\t\t$allowed_phpconfigs = $customer['allowed_phpconfigs'];\n\t\t\t\tif (!empty($allowed_phpconfigs)) {\n\t\t\t\t\t$allowed_phpconfigs = json_decode($allowed_phpconfigs, true);\n\t\t\t\t} else {\n\t\t\t\t\t$allowed_phpconfigs = [];\n\t\t\t\t}\n\t\t\t\t// only with fcgid/fpm enabled will it be possible to select a php-setting\n\t\t\t\tif ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t\tif (!in_array($phpsid_result['phpsettingid'], $allowed_phpconfigs)) {\n\t\t\t\t\t\tResponse::standardError('notallowedphpconfigused', '', true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// actually insert domain\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t`customerid` = :customerid,\n\t\t\t\t`adminid` = :adminid,\n\t\t\t\t`domain` = :domain,\n\t\t\t\t`domain_ace` = :domain_ace,\n\t\t\t\t`documentroot` = :documentroot,\n\t\t\t\t`aliasdomain` = :aliasdomain,\n\t\t\t\t`parentdomainid` = :parentdomainid,\n\t\t\t\t`wwwserveralias` = :wwwserveralias,\n\t\t\t\t`isemaildomain` = :isemaildomain,\n\t\t\t\t`iswildcarddomain` = :iswildcarddomain,\n\t\t\t\t`phpenabled` = :phpenabled,\n\t\t\t\t`openbasedir` = :openbasedir,\n\t\t\t\t`openbasedir_path` = :openbasedir_path,\n\t\t\t\t`speciallogfile` = :speciallogfile,\n\t\t\t\t`specialsettings` = :specialsettings,\n\t\t\t\t`ssl_specialsettings` = :ssl_specialsettings,\n\t\t\t\t`include_specialsettings` = :include_specialsettings,\n\t\t\t\t`ssl_redirect` = :ssl_redirect,\n\t\t\t\t`phpsettingid` = :phpsettingid,\n\t\t\t\t`letsencrypt` = :letsencrypt,\n\t\t\t\t`http2` = :http2,\n\t\t\t\t`http3` = :http3,\n\t\t\t\t`hsts` = :hsts,\n\t\t\t\t`hsts_sub` = :hsts_sub,\n\t\t\t\t`hsts_preload` = :hsts_preload,\n\t\t\t\t`ocsp_stapling` = :ocsp_stapling,\n\t\t\t\t`override_tls` = :override_tls,\n\t\t\t\t`ssl_protocols` = :ssl_protocols,\n\t\t\t\t`ssl_cipher_list` = :ssl_cipher_list,\n\t\t\t\t`tlsv13_cipher_list` = :tlsv13_cipher_list,\n\t\t\t\t`ssl_enabled` = :sslenabled,\n\t\t\t\t`dkim` = :dkim\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\"adminid\" => $customer['adminid'],\n\t\t\t\t\"domain\" => $completedomain,\n\t\t\t\t\"domain_ace\" => $idna_convert->decode($completedomain),\n\t\t\t\t\"documentroot\" => $path,\n\t\t\t\t\"aliasdomain\" => $aliasdomain != 0 ? $aliasdomain : null,\n\t\t\t\t\"parentdomainid\" => $domain_check['id'],\n\t\t\t\t\"wwwserveralias\" => $domain_check['wwwserveralias'] == '1' ? '1' : '0',\n\t\t\t\t\"iswildcarddomain\" => $domain_check['iswildcarddomain'] == '1' ? '1' : '0',\n\t\t\t\t\"isemaildomain\" => $isemaildomain,\n\t\t\t\t\"openbasedir\" => $domain_check['openbasedir'],\n\t\t\t\t\"openbasedir_path\" => $openbasedir_path,\n\t\t\t\t\"phpenabled\" => $domain_check['phpenabled'],\n\t\t\t\t\"speciallogfile\" => $speciallogfile == 2 ? $domain_check['speciallogfile'] : $speciallogfile,\n\t\t\t\t\"specialsettings\" => $domain_check['specialsettings'],\n\t\t\t\t\"ssl_specialsettings\" => $domain_check['ssl_specialsettings'],\n\t\t\t\t\"include_specialsettings\" => $domain_check['include_specialsettings'],\n\t\t\t\t\"ssl_redirect\" => $ssl_redirect,\n\t\t\t\t\"phpsettingid\" => $phpsid_result['phpsettingid'],\n\t\t\t\t\"letsencrypt\" => $letsencrypt,\n\t\t\t\t\"http2\" => $http2,\n\t\t\t\t\"http3\" => $http3,\n\t\t\t\t\"hsts\" => $hsts_maxage,\n\t\t\t\t\"hsts_sub\" => $hsts_sub,\n\t\t\t\t\"hsts_preload\" => $hsts_preload,\n\t\t\t\t\"ocsp_stapling\" => $domain_check['ocsp_stapling'],\n\t\t\t\t\"override_tls\" => $domain_check['override_tls'],\n\t\t\t\t\"ssl_protocols\" => $domain_check['ssl_protocols'],\n\t\t\t\t\"ssl_cipher_list\" => $domain_check['ssl_cipher_list'],\n\t\t\t\t\"tlsv13_cipher_list\" => $domain_check['tlsv13_cipher_list'],\n\t\t\t\t\"sslenabled\" => $sslenabled,\n\t\t\t\t\"dkim\" => $domain_check['dkim'],\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t\t$subdomain_id = Database::lastInsertId();\n\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\t(`id_domain`, `id_ipandports`)\n\t\t\t\tSELECT LAST_INSERT_ID(), `id_ipandports`\n\t\t\t\tFROM `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\tWHERE `id_domain` = :id_domain\n\t\t\t\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\"id_domain\" => $domain_check['id']\n\t\t\t]);\n\n\t\t\tif ($_doredirect) {\n\t\t\t\tDomain::addRedirectToDomain($subdomain_id, $redirectcode);\n\t\t\t}\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\n\t\t\tCustomers::increaseUsage($customer['customerid'], 'subdomains_used');\n\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] added subdomain '\" . $completedomain . \"'\");\n\n\t\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t\t'id' => $subdomain_id\n\t\t\t]);\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"No more resources available\", 406);\n\t}\n\n\t/**\n\t * return a subdomain entry by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param bool $with_ips\n\t *            optional, default true\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\t\t$with_ips = $this->getParam('with_ips', true, true);\n\n\t\t// convert possible idn domain to punycode\n\t\tif (substr($domainname, 0, 4) != 'xn--') {\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$domainname = $idna_convert->encode($domainname);\n\t\t}\n\n\t\tif ($this->isAdmin()) {\n\t\t\tif ($this->getUserDetail('customers_see_all') != 1) {\n\t\t\t\t// if it's a reseller or an admin who cannot see all customers, we need to check\n\t\t\t\t// whether the database belongs to one of his customers\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t\t$customer_ids = [];\n\t\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t\t}\n\t\t\t\tif (count($customer_ids) > 0) {\n\t\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT d.*, pd.`subcanemaildomain`, pd.`isbinddomain` as subisbinddomain\n\t\t\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` d, `\" . TABLE_PANEL_DOMAINS . \"` pd\n\t\t\t\t\t\tWHERE \" . ($id > 0 ? \"d.`id` = :iddn\" : \"d.`domain` = :iddn\") . \" AND d.`customerid` IN (\" . implode(\", \", $customer_ids) . \")\n\t\t\t\t\t\tAND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`))\n\t\t\t\t\t\");\n\t\t\t\t\t$params = [\n\t\t\t\t\t\t'iddn' => ($id <= 0 ? $domainname : $id)\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Exception(\"You do not have any customers yet\", 406);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT d.*, pd.`subcanemaildomain`, pd.`isbinddomain` as subisbinddomain\n\t\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` d, `\" . TABLE_PANEL_DOMAINS . \"` pd\n\t\t\t\t\tWHERE \" . ($id > 0 ? \"d.`id` = :iddn\" : \"d.`domain` = :iddn\") . \"\n\t\t\t\t\tAND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`))\n\t\t\t\t\");\n\t\t\t\t$params = [\n\t\t\t\t\t'iddn' => ($id <= 0 ? $domainname : $id)\n\t\t\t\t];\n\t\t\t}\n\t\t} else {\n\t\t\tif (!$this->isInternal() && Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT d.*, pd.`subcanemaildomain`, pd.`isbinddomain` as subisbinddomain, pd.`domain` as parentdomain\n\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` d, `\" . TABLE_PANEL_DOMAINS . \"` pd\n\t\t\t\tWHERE d.`customerid`= :customerid AND \" . ($id > 0 ? \"d.`id` = :iddn\" : \"d.`domain` = :iddn\") . \"\n\t\t\t\tAND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`))\n\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t'customerid' => $this->getUserDetail('customerid'),\n\t\t\t\t'iddn' => ($id <= 0 ? $domainname : $id)\n\t\t\t];\n\t\t}\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\t$result['ipsandports'] = [];\n\t\t\tif ($with_ips) {\n\t\t\t\t$result['ipsandports'] = $this->getIpsForDomain($result['id']);\n\t\t\t}\n\t\t\t$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] get subdomain '\" . $result['domain'] . \"'\");\n\t\t\treturn $this->response($result);\n\t\t}\n\t\tthrow new Exception(\"Requested subdomain could not be found\", 404);\n\t}\n\n\tprivate function getHasCertValueForDomain(int $domainid, int $parentdomainid): int\n\t{\n\t\t// nothing (ssl_global)\n\t\t$domain_hascert = 0;\n\t\t$ssl_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = :domainid\");\n\t\tDatabase::pexecute($ssl_stmt, array(\n\t\t\t\"domainid\" => $domainid\n\t\t));\n\t\t$ssl_result = $ssl_stmt->fetch(PDO::FETCH_ASSOC);\n\t\tif (is_array($ssl_result) && isset($ssl_result['ssl_cert_file']) && $ssl_result['ssl_cert_file'] != '') {\n\t\t\t// own certificate (ssl_customer_green)\n\t\t\t$domain_hascert = 1;\n\t\t} else {\n\t\t\t// check if it's parent has one set (shared)\n\t\t\tif ($parentdomainid != 0) {\n\t\t\t\t$ssl_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = :domainid\");\n\t\t\t\tDatabase::pexecute($ssl_stmt, array(\n\t\t\t\t\t\"domainid\" => $parentdomainid\n\t\t\t\t));\n\t\t\t\t$ssl_result = $ssl_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\tif (is_array($ssl_result) && isset($ssl_result['ssl_cert_file']) && $ssl_result['ssl_cert_file'] != '') {\n\t\t\t\t\t// parent has a certificate (ssl_shared)\n\t\t\t\t\t$domain_hascert = 2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $domain_hascert;\n\t}\n\n\t/**\n\t * validate given path and replace with url if given and valid\n\t *\n\t * @param string $path\n\t * @param string $url\n\t * @param array $customer\n\t * @param string $completedomain\n\t * @param boolean $_doredirect\n\t *\n\t * @return string validated path\n\t * @throws Exception\n\t */\n\tprivate function validateDomainDocumentRoot($path = null, $url = null, $customer = null, $completedomain = null, &$_doredirect = false)\n\t{\n\t\t$_doredirect = false;\n\t\t$idna = new IdnaWrapper();\n\n\t\t// url mode: either $url or $path begins with http:// or https://\n\t\t$maybeUrl = !empty($url) ? $url : (preg_match('/^https?\\:\\/\\//', $path) ? $path : '');\n\t\tif ($maybeUrl !== '') {\n\t\t\t$encoded = $idna->encode($maybeUrl);\n\t\t\tif (!Validate::validateUrl($encoded, true)) {\n\t\t\t\tResponse::standardError('invaliddocumentrooturl', '', true);\n\t\t\t}\n\t\t\t$_doredirect = true;\n\t\t\treturn $encoded;\n\t\t}\n\n\t\t// path mode: regular directory path\n\t\t$path = Validate::validate($path, 'path', Validate::REGEX_DIR, '', [], true);\n\n\t\t// default path if empty and setting active\n\t\tif (($path === '' || $path === '/') && Settings::Get('system.documentroot_use_default_value') == 1) {\n\t\t\treturn FileDir::makeCorrectDir($customer['documentroot'] . '/' . $completedomain, $customer['documentroot']);\n\t\t}\n\t\t// check if path does not contain a colon\n\t\tif (strpos($path, ':') !== false) {\n\t\t\tResponse::standardError('pathmaynotcontaincolon', '', true);\n\t\t}\n\n\t\treturn FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);\n\t}\n\n\t/**\n\t * update subdomain entry by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param int $alias\n\t *            optional, domain-id of a domain that the new domain should be an alias of\n\t * @param string $path\n\t *            optional, destination path relative to the customers-homedir, default is customers-homedir\n\t * @param string $url\n\t *            optional, overwrites path value with an URL to generate a redirect, alternatively use the path\n\t *            parameter also for URLs\n\t * @param int $selectserveralias\n\t *            optional, 0 = wildcard, 1 = www-alias, 2 = none\n\t * @param bool $isemaildomain\n\t *            optional\n\t * @param int $openbasedir_path\n\t *            optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot\n\t * @param int $phpsettingid\n\t *            optional, php-settings-id, if empty the $domain value is used\n\t * @param int $redirectcode\n\t *            optional, redirect-code-id from TABLE_PANEL_REDIRECTCODES\n\t * @param bool $speciallogfile\n\t *            optional, whether to create an exclusive web-logfile for this domain\n\t * @param bool $speciallogverified\n\t *            optional, when setting $speciallogfile to false, this needs to be set to true to confirm the action,\n\t *            default 0 (false)\n\t * @param bool $sslenabled\n\t *            optional, whether or not SSL is enabled for this domain, regardless of the assigned ssl-ips, default\n\t *            1 (true)\n\t * @param bool $ssl_redirect\n\t *            optional, whether to generate a https-redirect or not, default false; requires SSL to be enabled\n\t * @param bool $letsencrypt\n\t *            optional, whether to generate a Let's Encrypt certificate for this domain, default false; requires\n\t *            SSL to be enabled\n\t * @param bool $http2\n\t *            optional, whether to enable http/2 for this domain (requires to be enabled in the settings), default\n\t *            0 (false)\n\t * @param bool $http3\n\t *            optional, whether to enable http/3 for this domain (requires to be enabled in the settings), default\n\t *            0 (false)\n\t * @param int $hsts_maxage\n\t *            optional max-age value for HSTS header\n\t * @param bool $hsts_sub\n\t *            optional whether or not to add subdomains to the HSTS header\n\t * @param bool $hsts_preload\n\t *            optional whether or not to preload HSTS header value\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\tif ($this->isAdmin() == false && (int)$result['caneditdomain'] == 0) {\n\t\t\tthrow new Exception(lng('error.domaincannotbeedited', [$result['domain']]), 406);\n\t\t}\n\n\t\t// parameters\n\t\t$aliasdomain = $this->getParam('alias', true, 0);\n\t\t$path = $this->getParam('path', true, $result['documentroot']);\n\t\t$url = $this->getParam('url', true, '');\n\t\t// default: 0 = wildcard, 1 = www-alias, 2 = none\n\t\t$_serveraliasdefault = $result['iswildcarddomain'] == '1' ? 0 : ($result['wwwserveralias'] == '1' ? 1 : 2);\n\t\t$selectserveralias = $this->getParam('selectserveralias', true, $_serveraliasdefault);\n\t\t$isemaildomain = $this->getBoolParam('isemaildomain', true, $result['isemaildomain']);\n\t\t$openbasedir_path = $this->getParam('openbasedir_path', true, $result['openbasedir_path']);\n\t\t$phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']);\n\t\t$redirectcode = $this->getParam('redirectcode', true, Domain::getDomainRedirectId($id));\n\t\t$speciallogfile = $this->getBoolParam('speciallogfile', true, $result['speciallogfile']);\n\t\t$speciallogverified = $this->getBoolParam('speciallogverified', true, 0);\n\t\tif (Settings::Get('system.use_ssl')) {\n\t\t\t$sslenabled = $this->getBoolParam('sslenabled', true, $result['ssl_enabled']);\n\t\t\t$ssl_redirect = $this->getBoolParam('ssl_redirect', true, $result['ssl_redirect']);\n\t\t\t$letsencrypt = $this->getBoolParam('letsencrypt', true, $result['letsencrypt']);\n\t\t\t$http2 = $this->getBoolParam('http2', true, $result['http2']);\n\t\t\t$http3 = $this->getBoolParam('http3', true, $result['http3']);\n\t\t\t$hsts_maxage = $this->getParam('hsts_maxage', true, $result['hsts']);\n\t\t\t$hsts_sub = $this->getBoolParam('hsts_sub', true, $result['hsts_sub']);\n\t\t\t$hsts_preload = $this->getBoolParam('hsts_preload', true, $result['hsts_preload']);\n\t\t} else {\n\t\t\t$sslenabled = 0;\n\t\t\t$ssl_redirect = 0;\n\t\t\t$letsencrypt = 0;\n\t\t\t$http2 = 0;\n\t\t\t$http3 = 0;\n\t\t\t$hsts_maxage = 0;\n\t\t\t$hsts_sub = 0;\n\t\t\t$hsts_preload = 0;\n\t\t}\n\n\t\t// get needed customer info to reduce the subdomain-usage-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\t$alias_stmt = Database::prepare(\"SELECT COUNT(`id`) AS count FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `aliasdomain`= :aliasdomain\");\n\t\t$alias_check = Database::pexecute_first($alias_stmt, [\n\t\t\t\"aliasdomain\" => $result['id']\n\t\t]);\n\t\t$alias_check = $alias_check['count'];\n\n\t\t// alias domain checked?\n\t\tif ($aliasdomain != 0) {\n\t\t\t$aliasdomain_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id` FROM `\" . TABLE_PANEL_DOMAINS . \"` `d`,`\" . TABLE_PANEL_CUSTOMERS . \"` `c`\n\t\t\t\tWHERE `d`.`customerid`= :customerid\n\t\t\t\tAND `d`.`aliasdomain` IS NULL\n\t\t\t\tAND `d`.`id`<>`c`.`standardsubdomain`\n\t\t\t\tAND `c`.`customerid`= :customerid\n\t\t\t\tAND `d`.`id`= :id\n\t\t\t\");\n\t\t\t$aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, [\n\t\t\t\t\"id\" => $aliasdomain,\n\t\t\t\t\"customerid\" => $customer['customerid']\n\t\t\t], true, true);\n\t\t\tif ($aliasdomain_check['id'] != $aliasdomain) {\n\t\t\t\tResponse::standardError('domainisaliasorothercustomer', '', true);\n\t\t\t}\n\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());\n\t\t}\n\n\t\t// validate / correct path/url of domain\n\t\t$_doredirect = false;\n\t\t$path = $this->validateDomainDocumentRoot($path, $url, $customer, $result['domain'], $_doredirect);\n\n\t\t// set alias-fields according to selected alias mode\n\t\t$iswildcarddomain = ($selectserveralias == '0') ? '1' : '0';\n\t\t$wwwserveralias = ($selectserveralias == '1') ? '1' : '0';\n\n\t\t// if allowed, check for 'is email domain'-flag\n\t\tif ($isemaildomain != $result['isemaildomain']) {\n\t\t\tif ($result['parentdomainid'] != '0' && ($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2')) {\n\t\t\t\t$isemaildomain = intval($isemaildomain);\n\t\t\t} elseif ($result['parentdomainid'] != '0') {\n\t\t\t\t$isemaildomain = $result['subcanemaildomain'] == '3' ? 1 : 0;\n\t\t\t}\n\t\t}\n\n\t\t// check changes of openbasedir-path variable\n\t\tif ($openbasedir_path > 2 && $openbasedir_path < 0) {\n\t\t\t$openbasedir_path = 0;\n\t\t}\n\n\t\tif ($ssl_redirect != 0) {\n\t\t\t// a ssl-redirect only works if there actually is a\n\t\t\t// ssl ip/port assigned to the domain\n\t\t\tif (Domain::domainHasSslIpPort($result['id']) == true) {\n\t\t\t\t$ssl_redirect = '1';\n\t\t\t\t$_doredirect = true;\n\t\t\t} else {\n\t\t\t\tResponse::standardError('sslredirectonlypossiblewithsslipport', '', true);\n\t\t\t}\n\t\t}\n\n\t\tif ($letsencrypt != 0) {\n\t\t\t// let's encrypt only works if there actually is a\n\t\t\t// ssl ip/port assigned to the domain\n\t\t\tif (Domain::domainHasSslIpPort($result['id']) == true) {\n\t\t\t\t$letsencrypt = '1';\n\t\t\t} else {\n\t\t\t\tResponse::standardError('letsencryptonlypossiblewithsslipport', '', true);\n\t\t\t}\n\t\t}\n\n\t\t// validate dns if lets encrypt is enabled to check whether we can use it at all\n\t\tif ($result['letsencrypt'] != $letsencrypt && $letsencrypt == '1' && Settings::Get('system.le_domain_dnscheck') == '1') {\n\t\t\t$our_ips = Domain::getIpsOfDomain($result['parentdomainid']);\n\t\t\t$domain_ips = PhpHelper::gethostbynamel6($result['domain'], true, Settings::Get('system.le_domain_dnscheck_resolver'));\n\t\t\tif ($domain_ips == false || count(array_intersect($our_ips, $domain_ips)) <= 0) {\n\t\t\t\tResponse::standardError('invaliddnsforletsencrypt', '', true);\n\t\t\t}\n\t\t}\n\n\t\t// We can't enable let's encrypt for wildcard-domains\n\t\tif ($iswildcarddomain == '1' && $letsencrypt == '1') {\n\t\t\tResponse::standardError('nowildcardwithletsencrypt', '', true);\n\t\t}\n\n\t\t// Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated\n\t\tif ($ssl_redirect > 0 && $letsencrypt == 1 && $result['letsencrypt'] != $letsencrypt) {\n\t\t\t$ssl_redirect = 2;\n\t\t}\n\n\t\tif ($speciallogfile != $result['speciallogfile'] && $speciallogverified != '1') {\n\t\t\t$speciallogfile = $result['speciallogfile'];\n\t\t}\n\n\t\t// is-email-domain flag changed - remove mail accounts and mail-addresses\n\t\tif (($result['isemaildomain'] == '1') && $isemaildomain == '0') {\n\t\t\t$params = [\n\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\"domainid\" => $id\n\t\t\t];\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_MAIL_USERS . \"` WHERE `customerid`= :customerid AND `domainid`= :domainid\");\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t\t$stmt = Database::prepare(\"DELETE FROM `\" . TABLE_MAIL_VIRTUAL . \"` WHERE `customerid`= :customerid AND `domainid`= :domainid\");\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] automatically deleted mail-table entries for '\" . $idna_convert->decode($result['domain']) . \"'\");\n\t\t}\n\n\t\t$allowed_phpconfigs = $customer['allowed_phpconfigs'];\n\t\tif (!empty($allowed_phpconfigs)) {\n\t\t\t$allowed_phpconfigs = json_decode($allowed_phpconfigs, true);\n\t\t} else {\n\t\t\t$allowed_phpconfigs = [];\n\t\t}\n\t\t// only with fcgid/fpm enabled will it be possible to select a php-setting\n\t\tif ((int)$result['phpenabled'] == 1 && ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1)) {\n\t\t\tif (!in_array($phpsettingid, $allowed_phpconfigs)) {\n\t\t\t\tResponse::standardError('notallowedphpconfigused', '', true);\n\t\t\t}\n\t\t}\n\n\t\t// handle redirect\n\t\tif ($_doredirect) {\n\t\t\tDomain::updateRedirectOfDomain($id, $redirectcode);\n\t\t}\n\n\t\tif ($path != $result['documentroot']\n\t\t\t|| $isemaildomain != $result['isemaildomain']\n\t\t\t|| $wwwserveralias != $result['wwwserveralias']\n\t\t\t|| $iswildcarddomain != $result['iswildcarddomain']\n\t\t\t|| $aliasdomain != (int)$result['aliasdomain']\n\t\t\t|| $openbasedir_path != $result['openbasedir_path']\n\t\t\t|| $sslenabled != $result['ssl_enabled']\n\t\t\t|| $ssl_redirect != $result['ssl_redirect']\n\t\t\t|| $letsencrypt != $result['letsencrypt']\n\t\t\t|| $hsts_maxage != $result['hsts']\n\t\t\t|| $hsts_sub != $result['hsts_sub']\n\t\t\t|| $hsts_preload != $result['hsts_preload']\n\t\t\t|| $phpsettingid != $result['phpsettingid']\n\t\t\t|| $http2 != $result['http2']\n\t\t\t|| $http3 != $result['http3']\n\t\t\t|| ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1')\n\t\t) {\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t\t`documentroot` = :documentroot,\n\t\t\t\t\t`isemaildomain` = :isemaildomain,\n\t\t\t\t\t`wwwserveralias` = :wwwserveralias,\n\t\t\t\t\t`iswildcarddomain` = :iswildcarddomain,\n\t\t\t\t\t`aliasdomain` = :aliasdomain,\n\t\t\t\t\t`openbasedir_path` = :openbasedir_path,\n\t\t\t\t\t`ssl_enabled` = :sslenabled,\n\t\t\t\t\t`ssl_redirect` = :ssl_redirect,\n\t\t\t\t\t`letsencrypt` = :letsencrypt,\n\t\t\t\t\t`http2` = :http2,\n\t\t\t\t\t`http3` = :http3,\n\t\t\t\t\t`hsts` = :hsts,\n\t\t\t\t\t`hsts_sub` = :hsts_sub,\n\t\t\t\t\t`hsts_preload` = :hsts_preload,\n\t\t\t\t\t`phpsettingid` = :phpsettingid,\n\t\t\t\t\t`speciallogfile` = :speciallogfile\n\t\t\t\t\tWHERE `customerid`= :customerid AND `id`= :id\n\t\t\t\t\");\n\t\t\t$params = [\n\t\t\t\t\"documentroot\" => $path,\n\t\t\t\t\"isemaildomain\" => $isemaildomain,\n\t\t\t\t\"wwwserveralias\" => $wwwserveralias,\n\t\t\t\t\"iswildcarddomain\" => $iswildcarddomain,\n\t\t\t\t\"aliasdomain\" => ($aliasdomain != 0 && $alias_check == 0) ? $aliasdomain : null,\n\t\t\t\t\"openbasedir_path\" => $openbasedir_path,\n\t\t\t\t\"sslenabled\" => $sslenabled,\n\t\t\t\t\"ssl_redirect\" => $ssl_redirect,\n\t\t\t\t\"letsencrypt\" => $letsencrypt,\n\t\t\t\t\"http2\" => $http2,\n\t\t\t\t\"http3\" => $http3,\n\t\t\t\t\"hsts\" => $hsts_maxage,\n\t\t\t\t\"hsts_sub\" => $hsts_sub,\n\t\t\t\t\"hsts_preload\" => $hsts_preload,\n\t\t\t\t\"phpsettingid\" => $phpsettingid,\n\t\t\t\t\"speciallogfile\" => $speciallogfile,\n\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\"id\" => $id\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params, true, true);\n\n\t\t\tif ($result['aliasdomain'] != $aliasdomain && is_numeric($result['aliasdomain'])) {\n\t\t\t\t// trigger when domain id for alias destination has changed: both for old and new destination\n\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());\n\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());\n\t\t\t}\n\t\t\tif ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) {\n\t\t\t\t// or when wwwserveralias or letsencrypt was changed\n\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());\n\t\t\t\tif ((int)$aliasdomain === 0) {\n\t\t\t\t\t// in case the wwwserveralias is set on a main domain, $aliasdomain is 0\n\t\t\t\t\t// --> the call just above to triggerLetsEncryptCSRForAliasDestinationDomain\n\t\t\t\t\t// is a noop...let's repeat it with the domain id of the main domain\n\t\t\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($id, $this->logger());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check whether LE has been disabled, so we remove the certificate\n\t\t\tif ($letsencrypt == '0' && $result['letsencrypt'] == '1') {\n\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = :id\n\t\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t'id' => $id\n\t\t\t\t], true, true);\n\t\t\t\t// remove domain from acme.sh / lets encrypt if used\n\t\t\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']);\n\t\t\t}\n\n\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, \"[API] edited domain '\" . $idna_convert->decode($result['domain']) . \"'\");\n\t\t}\n\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id\n\t\t]);\n\t\treturn $this->response($result);\n\t}\n\n\t/**\n\t * lists all customer domain/subdomain entries\n\t *\n\t * @param bool $with_ips\n\t *            optional, default true\n\t * @param int $customerid\n\t *            optional, admin-only, select (sub)domains of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select (sub)domains of a specific customer by loginname\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t$with_ips = $this->getParam('with_ips', true, true);\n\t\tif ($this->isAdmin()) {\n\t\t\t// if we're an admin, list all subdomains of all the admins customers\n\t\t\t// or optionally for one specific customer identified by id or loginname\n\t\t\t$customerid = $this->getParam('customerid', true, 0);\n\t\t\t$loginname = $this->getParam('loginname', true, '');\n\n\t\t\tif (!empty($customerid) || !empty($loginname)) {\n\t\t\t\t$result = $this->apiCall('Customers.get', [\n\t\t\t\t\t'id' => $customerid,\n\t\t\t\t\t'loginname' => $loginname\n\t\t\t\t]);\n\t\t\t\t$custom_list_result = [\n\t\t\t\t\t$result\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t}\n\t\t\t$customer_ids = [];\n\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t}\n\t\t\tif (empty($customer_ids)) {\n\t\t\t\tthrow new Exception(\"Required resource unsatisfied.\", 405);\n\t\t\t}\n\n\t\t\t$select_fields = [\n\t\t\t\t'`d`.*'\n\t\t\t];\n\t\t} else {\n\t\t\tif (Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$customer_ids = [\n\t\t\t\t$this->getUserDetail('customerid')\n\t\t\t];\n\n\t\t\t$select_fields = [\n\t\t\t\t'`d`.`id`',\n\t\t\t\t'`d`.`customerid`',\n\t\t\t\t'`d`.`domain`',\n\t\t\t\t'`d`.`domain_ace`',\n\t\t\t\t'`d`.`documentroot`',\n\t\t\t\t'`d`.`isbinddomain`',\n\t\t\t\t'`d`.`isemaildomain`',\n\t\t\t\t'`d`.`caneditdomain`',\n\t\t\t\t'`d`.`iswildcarddomain`',\n\t\t\t\t'`d`.`parentdomainid`',\n\t\t\t\t'`d`.`letsencrypt`',\n\t\t\t\t'`d`.`registration_date`',\n\t\t\t\t'`d`.`termination_date`',\n\t\t\t\t'`d`.`deactivated`',\n\t\t\t\t'`d`.`email_only`',\n\t\t\t];\n\t\t}\n\n\t\t$query_fields = [];\n\n\t\t// prepare select statement\n\t\t$domains_stmt = Database::prepare(\"\n\t\t\tSELECT \" . implode(\",\", $select_fields) . \", IF(`d`.`parentdomainid` > 0, `pd`.`domain_ace`, `d`.`domain_ace`) AS `parentdomainname`, `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain`, `da`.`id` AS `domainaliasid`, `da`.`domain` AS `domainalias`\n\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` `d`\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` `ad` ON `d`.`aliasdomain`=`ad`.`id`\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` `da` ON `da`.`aliasdomain`=`d`.`id`\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` `pd` ON `pd`.`id`=`d`.`parentdomainid`\n\t\t\tWHERE `d`.`customerid` IN (\" . implode(', ', $customer_ids) . \")\n\t\t\t\" . $this->getSearchWhere($query_fields, true) . \" GROUP BY `d`.`id` ORDER BY `parentdomainname` ASC, `d`.`parentdomainid` ASC \" . $this->getOrderBy(true) . $this->getLimit());\n\n\t\t$result = [];\n\t\tDatabase::pexecute($domains_stmt, $query_fields, true, true);\n\t\twhile ($row = $domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$row['ipsandports'] = [];\n\t\t\tif ($with_ips) {\n\t\t\t\t$row['ipsandports'] = $this->getIpsForDomain($row['id']);\n\t\t\t}\n\t\t\t$row['domain_hascert'] = $this->getHasCertValueForDomain((int)$row['id'], (int)$row['parentdomainid']);\n\t\t\t$result[] = $row;\n\t\t}\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * get ips connected to given domain as array\n\t *\n\t * @param number $domain_id\n\t * @param bool $ssl_only\n\t *            optional, return only ssl enabled ip's, default false\n\t * @return array\n\t */\n\tprivate function getIpsForDomain($domain_id = 0, $ssl_only = false)\n\t{\n\t\t$fields = '`ips`.ip, `ips`.port, `ips`.ssl';\n\t\tif ($this->isAdmin()) {\n\t\t\t$fields = '`ips`.*';\n\t\t}\n\t\t$resultips_stmt = Database::prepare(\"\n\t\t\tSELECT \" . $fields . \" FROM `\" . TABLE_DOMAINTOIP . \"` AS `dti`, `\" . TABLE_PANEL_IPSANDPORTS . \"` AS `ips`\n\t\t\tWHERE `dti`.`id_ipandports` = `ips`.`id` AND `dti`.`id_domain` = :domainid \" . ($ssl_only ? \" AND `ips`.`ssl` = '1'\" : \"\"));\n\n\t\tDatabase::pexecute($resultips_stmt, [\n\t\t\t'domainid' => $domain_id\n\t\t]);\n\n\t\t$ipandports = [];\n\t\twhile ($rowip = $resultips_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (filter_var($rowip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t$rowip['is_ipv6'] = true;\n\t\t\t}\n\t\t\t$ipandports[] = $rowip;\n\t\t}\n\n\t\treturn $ipandports;\n\t}\n\n\t/**\n\t * returns the total number of accessible subdomain entries\n\t *\n\t * @param int $customerid\n\t *            optional, admin-only, select (sub)domains of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select (sub)domains of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t// if we're an admin, list all databases of all the admins customers\n\t\t\t// or optionally for one specific customer identified by id or loginname\n\t\t\t$customerid = $this->getParam('customerid', true, 0);\n\t\t\t$loginname = $this->getParam('loginname', true, '');\n\n\t\t\tif (!empty($customerid) || !empty($loginname)) {\n\t\t\t\t$result = $this->apiCall('Customers.get', [\n\t\t\t\t\t'id' => $customerid,\n\t\t\t\t\t'loginname' => $loginname\n\t\t\t\t]);\n\t\t\t\t$custom_list_result = [\n\t\t\t\t\t$result\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t}\n\t\t\t$customer_ids = [];\n\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t$customer_ids[] = $customer['customerid'];\n\t\t\t}\n\t\t} else {\n\t\t\tif (Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t\t}\n\t\t\t$customer_ids = [\n\t\t\t\t$this->getUserDetail('customerid')\n\t\t\t];\n\t\t}\n\n\t\tif (!empty($customer_ids)) {\n\t\t\t$query_fields = [];\n\t\t\t// prepare select statement\n\t\t\t$domains_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_subdom\n\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` `d`\n\t\t\t\tWHERE `d`.`customerid` IN (\" . implode(', ', $customer_ids) . \")\n\t\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t\t$result = Database::pexecute_first($domains_stmt, $query_fields, true, true);\n\t\t\tif ($result) {\n\t\t\t\treturn $this->response($result['num_subdom']);\n\t\t\t}\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * delete a subdomain by either id or domainname\n\t *\n\t * @param int $id\n\t *            optional, the domain-id\n\t * @param string $domainname\n\t *            optional, the domainname\n\t * @param int $customerid\n\t *            optional, required when called as admin (if $loginname is not specified)\n\t * @param string $loginname\n\t *            optional, required when called as admin (if $customerid is not specified)\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\t$id = $this->getParam('id', true, 0);\n\t\t$dn_optional = $id > 0;\n\t\t$domainname = $this->getParam('domainname', $dn_optional, '');\n\n\t\tif ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\tthrow new Exception(\"You cannot access this resource\", 405);\n\t\t}\n\n\t\t$result = $this->apiCall('SubDomains.get', [\n\t\t\t'id' => $id,\n\t\t\t'domainname' => $domainname\n\t\t]);\n\t\t$id = $result['id'];\n\n\t\t// get needed customer info to reduce the subdomain-usage-counter by one\n\t\t$customer = $this->getCustomerData();\n\n\t\tif (!$this->isAdmin() && $result['caneditdomain'] == 0) {\n\t\t\tthrow new Exception(\"You cannot edit this resource\", 405);\n\t\t}\n\n\t\tif ($result['isemaildomain'] == '1') {\n\t\t\t// check for e-mail addresses\n\t\t\t$emails_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(`id`) AS `count` FROM `\" . TABLE_MAIL_VIRTUAL . \"`\n\t\t\t\tWHERE `customerid` = :customerid AND `domainid` = :domainid\n\t\t\t\");\n\t\t\t$emails = Database::pexecute_first($emails_stmt, [\n\t\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\t\"domainid\" => $id\n\t\t\t], true, true);\n\n\t\t\tif ($emails['count'] != '0') {\n\t\t\t\tResponse::standardError('domains_cantdeletedomainwithemail', '', true);\n\t\t\t}\n\t\t}\n\n\t\tif ((int)$result['aliasdomain'] !== 0) {\n\t\t\tDomain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());\n\t\t}\n\n\t\t// delete domain from table\n\t\t$stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `customerid` = :customerid AND `id` = :id\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"customerid\" => $customer['customerid'],\n\t\t\t\"id\" => $id\n\t\t], true, true);\n\n\t\t// remove connections to ips and domainredirects\n\t\t$del_stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_DOMAINTOIP . \"`\n\t\t\tWHERE `id_domain` = :domainid\n\t\t\");\n\t\tDatabase::pexecute($del_stmt, [\n\t\t\t'domainid' => $id\n\t\t], true, true);\n\n\t\t// remove redirect-codes\n\t\t$del_stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAINREDIRECTS . \"`\n\t\t\tWHERE `did` = :domainid\n\t\t\");\n\t\tDatabase::pexecute($del_stmt, [\n\t\t\t'domainid' => $id\n\t\t], true, true);\n\n\t\t// remove certificate from domain_ssl_settings, fixes #1596\n\t\t$del_stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\t\tWHERE `domainid` = :domainid\n\t\t\");\n\t\tDatabase::pexecute($del_stmt, [\n\t\t\t'domainid' => $id\n\t\t], true, true);\n\n\t\t// remove possible existing DNS entries\n\t\t$del_stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_DOMAIN_DNS . \"`\n\t\t\tWHERE `domain_id` = :domainid\n\t\t\");\n\t\tDatabase::pexecute($del_stmt, [\n\t\t\t'domainid' => $id\n\t\t], true, true);\n\n\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t// Using nameserver, insert a task which rebuilds the server config\n\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t// remove domains DNS from powerDNS if used, #581\n\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_PDNS, $result['domain']);\n\t\t// remove domain from acme.sh / lets encrypt if used\n\t\tCronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']);\n\n\t\t// reduce subdomain-usage-counter\n\t\tCustomers::decreaseUsage($customer['customerid'], 'subdomains_used');\n\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, \"[API] deleted subdomain '\" . $result['domain'] . \"'\");\n\t\treturn $this->response($result);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/SysLog.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse PDO;\n\n/**\n * @since 0.10.6\n */\nclass SysLog extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * list all log-entries\n\t *\n\t * @param array $sql_search\n\t *            optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),\n\t *            LIKE is used if left empty and 'value' => searchvalue\n\t * @param int $sql_limit\n\t *            optional specify number of results to be returned\n\t * @param int $sql_offset\n\t *            optional specify offset for resultset\n\t * @param array $sql_orderby\n\t *            optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more\n\t *            fields\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t$result = [];\n\t\t$query_fields = [];\n\t\tif ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '1') {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_LOG . \"` \" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());\n\t\t} elseif ($this->isAdmin()) {\n\t\t\t// get all admin customers\n\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t$customer_names = [];\n\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t$customer_names[] = $customer['loginname'];\n\t\t\t}\n\t\t\tif (count($customer_names) > 0) {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_LOG . \"`\n\t\t\t\tWHERE `user` = :loginname OR `user` IN ('\" . implode(\"', '\", $customer_names) . \"')\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_LOG . \"`\n\t\t\t\tWHERE `user` = :loginname\" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\t\t}\n\t\t\t$query_fields['loginname'] = $this->getUserDetail('loginname');\n\t\t} else {\n\t\t\t// every one else just sees their logs\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_LOG . \"`\n\t\t\tWHERE `user` = :loginname AND `action` <> 99 \" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit());\n\t\t\t$query_fields['loginname'] = $this->getUserDetail('loginname');\n\t\t}\n\t\tDatabase::pexecute($result_stmt, $query_fields, true, true);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t// clean log-text\n\t\t\t$row['text'] = preg_replace(\"/[^\\w @#\\\"':.,()\\[\\]+\\-_\\/\\\\\\!]/i\", \"_\", $row['text']);\n\t\t\t$result[] = $row;\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list log-entries\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * returns the total number of log-entries\n\t *\n\t * @access admin\n\t * @return string json-encoded response message\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\t$params = [];\n\t\t$query_fields = [];\n\t\tif ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '1') {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_logs FROM `\" . TABLE_PANEL_LOG . \"`\n\t\t\t\" . $this->getSearchWhere($query_fields));\n\t\t} elseif ($this->isAdmin()) {\n\t\t\t// get all admin customers\n\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t$customer_names = [];\n\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t$customer_names[] = $customer['loginname'];\n\t\t\t}\n\t\t\tif (count($customer_names) > 0) {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT COUNT(*) as num_logs FROM `\" . TABLE_PANEL_LOG . \"`\n\t\t\t\t\tWHERE `user` = :loginname OR `user` IN ('\" . implode(\"', '\", $customer_names) . \"')\n\t\t\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t\t} else {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT COUNT(*) as num_logs FROM `\" . TABLE_PANEL_LOG . \"`\n\t\t\t\t\tWHERE `user` = :loginname\n\t\t\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t\t}\n\t\t\t$params = [\n\t\t\t\t'loginname' => $this->getUserDetail('loginname')\n\t\t\t];\n\t\t} else {\n\t\t\t// every one else just sees their logs\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(*) as num_logs FROM `\" . TABLE_PANEL_LOG . \"`\n\t\t\t\tWHERE `user` = :loginname AND `action` <> 99\n\t\t\t\" . $this->getSearchWhere($query_fields, true));\n\t\t\t$params = [\n\t\t\t\t'loginname' => $this->getUserDetail('loginname')\n\t\t\t];\n\t\t}\n\t\t$params = array_merge($params, $query_fields);\n\t\t$result = Database::pexecute_first($result_stmt, $params, true, true);\n\t\tif ($result) {\n\t\t\treturn $this->response($result['num_logs']);\n\t\t}\n\t\treturn $this->response(0);\n\t}\n\n\t/**\n\t * You cannot get log entries\n\t */\n\tpublic function get()\n\t{\n\t\tthrow new Exception('You cannot get log entries', 303);\n\t}\n\n\t/**\n\t * You cannot add log entries\n\t */\n\tpublic function add()\n\t{\n\t\tthrow new Exception('You cannot add log entries', 303);\n\t}\n\n\t/**\n\t * You cannot update log entries\n\t */\n\tpublic function update()\n\t{\n\t\tthrow new Exception('You cannot update log entries', 303);\n\t}\n\n\t/**\n\t * delete log entries\n\t *\n\t * @param int $min_to_keep\n\t *            optional minutes to keep, default is 10\n\t *\n\t * @access admin\n\t * @return string json-encoded array\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tif ($this->isAdmin()) {\n\t\t\t$min_to_keep = self::getParam('min_to_keep', true, 10);\n\t\t\tif ($min_to_keep < 0) {\n\t\t\t\t$min_to_keep = 0;\n\t\t\t}\n\t\t\t$truncatedate = time() - (60 * $min_to_keep);\n\t\t\t$params = [];\n\t\t\tif ($this->getUserDetail('customers_see_all') == '1') {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_LOG . \"` WHERE `date` < :trunc\n\t\t\t\t\");\n\t\t\t} else {\n\t\t\t\t// get all admin customers\n\t\t\t\t$_custom_list_result = $this->apiCall('Customers.listing');\n\t\t\t\t$custom_list_result = $_custom_list_result['list'];\n\t\t\t\t$customer_names = [];\n\t\t\t\tforeach ($custom_list_result as $customer) {\n\t\t\t\t\t$customer_names[] = $customer['loginname'];\n\t\t\t\t}\n\t\t\t\tif (count($customer_names) > 0) {\n\t\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_LOG . \"` WHERE `date` < :trunc AND `user` = :loginname OR `user` IN ('\" . implode(\"', '\", $customer_names) . \"')\n\t\t\t\t\t\");\n\t\t\t\t} else {\n\t\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_LOG . \"` WHERE `date` < :trunc AND `user` = :loginname\n\t\t\t\t\t\");\n\t\t\t\t}\n\t\t\t\t$params = [\n\t\t\t\t\t'loginname' => $this->getUserDetail('loginname')\n\t\t\t\t];\n\t\t\t}\n\t\t\t$params['trunc'] = $truncatedate;\n\t\t\tDatabase::pexecute($result_stmt, $params, true, true);\n\t\t\t$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, \"[API] truncated the froxlor syslog\");\n\t\t\treturn $this->response(true);\n\t\t}\n\t\tthrow new Exception(\"Not allowed to execute given command.\", 403);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/Traffic.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api\\Commands;\n\nuse Exception;\nuse Froxlor\\Api\\ApiCommand;\nuse Froxlor\\Api\\ResourceEntity;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse PDO;\n\n/**\n * @since 0.10.0\n */\nclass Traffic extends ApiCommand implements ResourceEntity\n{\n\n\t/**\n\t * You cannot add traffic data\n\t *\n\t * @throws Exception\n\t */\n\tpublic function add()\n\t{\n\t\tthrow new Exception('You cannot add traffic data', 303);\n\t}\n\n\t/**\n\t * to get specific traffic details use year, month and/or day parameter for Traffic.listing()\n\t *\n\t * @throws Exception\n\t */\n\tpublic function get()\n\t{\n\t\tthrow new Exception('To get specific traffic details use year, month and/or day parameter for Traffic.listing()', 303);\n\t}\n\n\t/**\n\t * You cannot update traffic data\n\t *\n\t * @throws Exception\n\t */\n\tpublic function update()\n\t{\n\t\tthrow new Exception('You cannot update traffic data', 303);\n\t}\n\n\t/**\n\t * list traffic information\n\t *\n\t * @param int $year\n\t *            optional, default empty\n\t * @param int $month\n\t *            optional, default empty\n\t * @param int $day\n\t *            optional, default empty\n\t * @param int $date_from\n\t *            optional timestamp, default empty, if specified, $year, $month and $day will be ignored\n\t * @param int $date_until\n\t *            optional timestamp, default empty, if specified, $year, $month and $day will be ignored\n\t * @param bool $customer_traffic\n\t *            optional, admin-only, whether to output ones own traffic or all of ones customers, default is 0\n\t *            (false)\n\t * @param int $customerid\n\t *            optional, admin-only, select traffic of a specific customer by id\n\t * @param string $loginname\n\t *            optional, admin-only, select traffic of a specific customer by loginname\n\t *\n\t * @access admin, customer\n\t * @return string json-encoded array count|list\n\t * @throws Exception\n\t */\n\tpublic function listing()\n\t{\n\t\t$year = $this->getParam('year', true, \"\");\n\t\t$month = $this->getParam('month', true, \"\");\n\t\t$day = $this->getParam('day', true, \"\");\n\t\t$date_from = $this->getParam('date_from', true, -1);\n\t\t$date_until = $this->getParam('date_until', true, -1);\n\t\t$customer_traffic = $this->getBoolParam('customer_traffic', true, 0);\n\t\t$customer_ids = $this->getAllowedCustomerIds();\n\t\t$result = [];\n\t\t$params = [];\n\n\t\t// validate parameters\n\t\tif ($date_from >= 0 || $date_until >= 0) {\n\t\t\t$year = \"\";\n\t\t\t$month = \"\";\n\t\t\t$day = \"\";\n\t\t\tif ($date_from == $date_until) {\n\t\t\t\t$date_until = -1;\n\t\t\t}\n\t\t\tif ($date_from >= 0 && $date_until >= 0 && $date_until < $date_from) {\n\t\t\t\t// switch\n\t\t\t\t$temp_ts = $date_from;\n\t\t\t\t$date_from = $date_until;\n\t\t\t\t$date_until = $temp_ts;\n\t\t\t}\n\t\t}\n\n\t\t// check for year/month/day\n\t\t$where_str = \"\";\n\t\tif (!empty($year) && is_numeric($year)) {\n\t\t\t$where_str .= \" AND `year` = :year\";\n\t\t\t$params['year'] = $year;\n\t\t}\n\t\tif (!empty($month) && is_numeric($month)) {\n\t\t\t$where_str .= \" AND `month` = :month\";\n\t\t\t$params['month'] = $month;\n\t\t}\n\t\tif (!empty($day) && is_numeric($day)) {\n\t\t\t$where_str .= \" AND `day` = :day\";\n\t\t\t$params['day'] = $day;\n\t\t}\n\t\tif ($date_from >= 0 && $date_until >= 0) {\n\t\t\t$where_str .= \" AND `stamp` BETWEEN :df AND :du\";\n\t\t\t$params['df'] = $date_from;\n\t\t\t$params['du'] = $date_until;\n\t\t} elseif ($date_from >= 0 && $date_until < 0) {\n\t\t\t$where_str .= \" AND `stamp` > :df\";\n\t\t\t$params['df'] = $date_from;\n\t\t} elseif ($date_from < 0 && $date_until >= 0) {\n\t\t\t$where_str .= \" AND `stamp` < :du\";\n\t\t\t$params['du'] = $date_until;\n\t\t}\n\n\t\tif (!$this->isAdmin() || ($this->isAdmin() && $customer_traffic)) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_TRAFFIC . \"`\n\t\t\t\tWHERE `customerid` IN (\" . implode(\", \", $customer_ids) . \")\" . $where_str);\n\t\t} else {\n\t\t\t$params['adminid'] = $this->getUserDetail('adminid');\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_TRAFFIC_ADMINS . \"`\n\t\t\t\tWHERE `adminid` = :adminid\" . $where_str);\n\t\t}\n\t\tDatabase::pexecute($result_stmt, $params, true, true);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t// make Bytes from KB\n\t\t\t$row['http'] *= 1024;\n\t\t\t$row['ftp_up'] *= 1024;\n\t\t\t$row['ftp_down'] *= 1024;\n\t\t\t$row['mail'] *= 1024;\n\t\t\t$result[] = $row;\n\t\t}\n\t\t$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, \"[API] list traffic\");\n\t\treturn $this->response([\n\t\t\t'count' => count($result),\n\t\t\t'list' => $result\n\t\t]);\n\t}\n\n\t/**\n\t * You cannot count the traffic data list\n\t *\n\t * @throws Exception\n\t */\n\tpublic function listingCount()\n\t{\n\t\tthrow new Exception('You cannot count the traffic data list', 303);\n\t}\n\n\t/**\n\t * You cannot delete traffic data\n\t *\n\t * @throws Exception\n\t */\n\tpublic function delete()\n\t{\n\t\tthrow new Exception('You cannot delete traffic data', 303);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Commands/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Api/FroxlorRPC.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\System\\IPTools;\n\nclass FroxlorRPC\n{\n\t/**\n\t * validate a given request\n\t *\n\t * @param $request\n\t * @return array\n\t * @throws Exception\n\t */\n\tpublic static function validateRequest($request): array\n\t{\n\t\t// make basic authentication\n\t\tif (!isset($_SERVER['PHP_AUTH_USER']) || !self::validateAuth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) {\n\t\t\tif (@php_sapi_name() !== 'cli') {\n\t\t\t\theader('WWW-Authenticate: Basic realm=\"API\"');\n\t\t\t}\n\t\t\tthrow new Exception('Unauthenticated. Please provide api user credentials.', 401);\n\t\t}\n\n\t\t// check if present\n\t\tif (empty($request)) {\n\t\t\tthrow new Exception('Empty request body.', 400);\n\t\t}\n\n\t\t// decode json request\n\t\t$decoded_request = json_decode($request, true);\n\n\t\t// is it valid?\n\t\tif (is_null($decoded_request)) {\n\t\t\tthrow new Exception('Invalid JSON Format.', 400);\n\t\t}\n\n\t\treturn self::validateBody($decoded_request);\n\t}\n\n\t/**\n\t * validates the given api credentials\n\t *\n\t * @param string $key\n\t * @param string $secret\n\t *\n\t * @return bool\n\t */\n\tprivate static function validateAuth(string $key, string $secret): bool\n\t{\n\t\t$sel_stmt = Database::prepare(\n\t\t\t\"\n\t\t\tSELECT ak.*, a.api_allowed as admin_api_allowed, c.api_allowed as cust_api_allowed, c.deactivated\n\t\t\tFROM `api_keys` ak\n\t\t\tLEFT JOIN `panel_admins` a ON a.adminid = ak.adminid\n\t\t\tLEFT JOIN `panel_customers` c ON c.customerid = ak.customerid\n\t\t\tWHERE `apikey` = :ak AND `secret` = :as\n\t\t\"\n\t\t);\n\t\t$result = Database::pexecute_first($sel_stmt, [\n\t\t\t'ak' => $key,\n\t\t\t'as' => $secret\n\t\t], true, true);\n\t\tif ($result) {\n\t\t\tif ($result['apikey'] == $key && $result['secret'] == $secret && ($result['valid_until'] == -1 || $result['valid_until'] >= time()) && (($result['customerid'] == 0 && $result['admin_api_allowed'] == 1) || ($result['customerid'] > 0 && $result['cust_api_allowed'] == 1 && $result['deactivated'] == 0))) {\n\t\t\t\t// get user to check whether api call is allowed\n\t\t\t\tif (!empty($result['allowed_from'])) {\n\t\t\t\t\t// @todo allow specification and validating of whole subnets later\n\t\t\t\t\t$ip_list = explode(\",\", $result['allowed_from']);\n\t\t\t\t\tif (self::validateAllowedFrom($ip_list, $_SERVER['REMOTE_ADDR'])) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow new Exception('Invalid authorization credentials', 403);\n\t}\n\n\t/**\n\t * validate if given remote_addr is within the list of allowed ip/ip-ranges\n\t *\n\t * @param array $allowed_from\n\t * @param string $remote_addr\n\t *\n\t * @return bool\n\t */\n\tpublic static function validateAllowedFrom(array $allowed_from, string $remote_addr): bool\n\t{\n\t\t// shorten IP for comparison\n\t\t$remote_addr = inet_ntop(inet_pton($remote_addr));\n\t\t// check for direct matches\n\t\tif (in_array($remote_addr, $allowed_from)) {\n\t\t\treturn true;\n\t\t}\n\t\t// check for possible cidr ranges\n\t\tforeach ($allowed_from as $ip) {\n\t\t\t$ip_cidr = explode(\"/\", $ip);\n\t\t\tif (count($ip_cidr) == 2 && IPTools::ip_in_range($ip_cidr, $remote_addr)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * validates the given command\n\t *\n\t * @param array $request\n\t *\n\t * @return array\n\t * @throws Exception\n\t */\n\tprivate static function validateBody($request)\n\t{\n\t\t// check command exists\n\t\tif (empty($request['command'])) {\n\t\t\tthrow new Exception(\"Please provide a command.\", 400);\n\t\t}\n\n\t\t$command = explode(\".\", $request['command']);\n\n\t\tif (count($command) != 2) {\n\t\t\tthrow new Exception(\"The given command is invalid.\", 400);\n\t\t}\n\t\t// simply check for file-existance, as we do not want to use our autoloader because this way\n\t\t// it will recognize non-api classes+methods as valid commands\n\t\t$apiclass = '\\\\Froxlor\\\\Api\\\\Commands\\\\' . $command[0];\n\t\tif (!class_exists($apiclass) || !@method_exists($apiclass, $command[1])) {\n\t\t\tthrow new Exception(\"Unknown command\", 400);\n\t\t}\n\t\treturn [\n\t\t\t'command' => [\n\t\t\t\t'class' => $command[0],\n\t\t\t\t'method' => $command[1]\n\t\t\t],\n\t\t\t'params' => $request['params'] ?? null\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/ResourceEntity.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api;\n\n/**\n * @since 0.10.0\n */\ninterface ResourceEntity\n{\n\n\tpublic function listing();\n\n\tpublic function listingCount();\n\n\tpublic function get();\n\n\tpublic function add();\n\n\tpublic function update();\n\n\tpublic function delete();\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/Response.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Api;\n\nclass Response\n{\n\tpublic static function jsonDataResponse($data = null, int $response_code = 200)\n\t{\n\t\treturn self::jsonResponse(['data' => $data], $response_code);\n\t}\n\n\tpublic static function jsonResponse($data = null, int $response_code = 200)\n\t{\n\t\tif (!defined('TRAVIS_CI') || TRAVIS_CI == 0) {\n\t\t\thttp_response_code($response_code);\n\t\t}\n\n\t\treturn json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);\n\t}\n\n\tpublic static function jsonErrorResponse($message = null, int $response_code = 400)\n\t{\n\t\treturn self::jsonResponse(['message' => $message], $response_code);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Api/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Bulk/BulkAction.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Bulk;\n\nuse Exception;\nuse Froxlor\\FileDir;\n\n/**\n * Abstract Class BulkAction to mass-import entities\n *\n * @author Michael Kaufmann (d00p) <d00p@froxlor.org>\n *\n */\nabstract class BulkAction\n{\n\n\t/**\n\t * logged in user\n\t *\n\t * @var array\n\t */\n\tprotected $userinfo = [];\n\t/**\n\t * complete path including filename of file to be imported\n\t *\n\t * @var string\n\t */\n\tprivate $impFile = null;\n\t/**\n\t * api-function to call for addingg entity\n\t *\n\t * @var string\n\t */\n\tprivate $api_call = null;\n\t/**\n\t * api-function parameter names, read from import-file (first line)\n\t *\n\t * @var array\n\t */\n\tprivate $api_params = null;\n\t/**\n\t * errors while importing\n\t *\n\t * @var array\n\t */\n\tprivate $errors = [];\n\n\t/**\n\t * class constructor, optionally sets file and customer-id\n\t *\n\t * @param string $import_file\n\t * @param array $userinfo\n\t *\n\t * @return object BulkAction instance\n\t */\n\tprotected function __construct(string $import_file = null, array $userinfo = [])\n\t{\n\t\tif (!empty($import_file)) {\n\t\t\t$this->impFile = FileDir::makeCorrectFile($import_file);\n\t\t}\n\t\t$this->userinfo = $userinfo;\n\t}\n\n\t/**\n\t * import the parsed import file data with an optional separator other then semicolon\n\t * and offset (maybe for header-line in csv or similar)\n\t *\n\t * @param string $separator\n\t * @param int $offset\n\t *\n\t * @return array 'all' => amount of records processed, 'imported' => number of imported records\n\t */\n\tabstract public function doImport(string $separator = \";\", int $offset = 0);\n\n\t/**\n\t * setter for import-file\n\t *\n\t * @param string $import_file\n\t *\n\t * @return void\n\t */\n\tpublic function setImportFile($import_file = null)\n\t{\n\t\t$this->impFile = FileDir::makeCorrectFile($import_file);\n\t}\n\n\t/**\n\t * return the list of errors\n\t *\n\t * @return array\n\t */\n\tpublic function getErrors()\n\t{\n\t\treturn $this->errors;\n\t}\n\n\t/**\n\t * setter for api_call\n\t *\n\t * @param string $api_call\n\t *\n\t * @return void\n\t */\n\tprotected function setApiCall($api_call = \"\")\n\t{\n\t\t$this->api_call = $api_call;\n\t}\n\n\tprotected function importEntity($data_array = null)\n\t{\n\t\tif (empty($data_array)) {\n\t\t\treturn null;\n\t\t}\n\n\t\t$module = '\\\\Froxlor\\\\Api\\\\Commands\\\\' . substr($this->api_call, 0, strpos($this->api_call, \".\"));\n\t\t$function = substr($this->api_call, strpos($this->api_call, \".\") + 1);\n\n\t\t$new_data = [];\n\t\tforeach ($this->api_params as $idx => $param) {\n\t\t\tif (isset($data_array[$idx])) {\n\t\t\t\t$new_data[$param] = $data_array[$idx];\n\t\t\t}\n\t\t}\n\n\t\t$result = null;\n\t\ttry {\n\t\t\t$json_result = $module::getLocal($this->userinfo, $new_data)->$function();\n\t\t\t$result = json_decode($json_result, true)['data'];\n\t\t} catch (Exception $e) {\n\t\t\t$this->errors[] = $e->getMessage();\n\t\t}\n\t\treturn !empty($result);\n\t}\n\n\t/**\n\t * reads in the csv import file and returns an array with\n\t * all the entities to be imported\n\t *\n\t * @param string $separator\n\t *\n\t * @return array\n\t */\n\tprotected function parseImportFile($separator = \";\")\n\t{\n\t\tif (empty($this->impFile)) {\n\t\t\tthrow new Exception(\"No file was given for import\");\n\t\t}\n\n\t\tif (!file_exists($this->impFile)) {\n\t\t\tthrow new Exception(\"The file '\" . $this->impFile . \"' could not be found\");\n\t\t}\n\n\t\tif (!is_readable($this->impFile)) {\n\t\t\tthrow new Exception(\"Unable to read file '\" . $this->impFile . \"'\");\n\t\t}\n\n\t\tif (empty($separator) || strlen($separator) != 1) {\n\t\t\tthrow new Exception(\"Invalid separator specified: '\" . $separator . \"'\");\n\t\t}\n\n\t\t$file_data = [];\n\t\t$is_params_line = true;\n\t\t$fh = @fopen($this->impFile, \"r\");\n\t\tif ($fh) {\n\t\t\twhile (($line = fgets($fh)) !== false) {\n\t\t\t\t$tmp_arr = explode($separator, $line);\n\t\t\t\t$data_arr = [];\n\t\t\t\tforeach ($tmp_arr as $idx => $data) {\n\t\t\t\t\tif ($is_params_line) {\n\t\t\t\t\t\t$this->api_params[$idx] = $data;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$data_arr[$idx] = $data;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ($is_params_line) {\n\t\t\t\t\t$is_params_line = false;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$file_data[] = array_map(\"trim\", $data_arr);\n\t\t\t}\n\t\t\t$this->api_params = array_map(\"trim\", $this->api_params);\n\t\t} else {\n\t\t\tthrow new Exception(\"Unable to open file '\" . $this->impFile . \"'\");\n\t\t}\n\t\tfclose($fh);\n\n\t\treturn $file_data;\n\t}\n\n}\n"
  },
  {
    "path": "lib/Froxlor/Bulk/DomainBulkAction.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Bulk;\n\nuse Exception;\n\n/**\n * Class DomainBulkAction to mass-import domains for a given customer\n */\nclass DomainBulkAction extends BulkAction\n{\n\n\t/**\n\t * @param string $import_file\n\t * @param array $userinfo\n\t *\n\t * @return DomainBulkAction\n\t */\n\tpublic function __construct(string $import_file = null, array $userinfo = [])\n\t{\n\t\tparent::__construct($import_file, $userinfo);\n\t\t$this->setApiCall('Domains.add');\n\t}\n\n\t/**\n\t * import the parsed import file data with an optional separator other then semicolon\n\t * and offset (maybe for header-line in csv or similar)\n\t *\n\t * @param string $separator\n\t * @param int $offset\n\t *\n\t * @return array 'all' => amount of records processed, 'imported' => number of imported records\n\t */\n\tpublic function doImport(string $separator = \";\", int $offset = 0)\n\t{\n\t\tif ($this->userinfo['domains'] == \"-1\") {\n\t\t\t$dom_unlimited = true;\n\t\t} else {\n\t\t\t$dom_unlimited = false;\n\t\t}\n\n\t\t$domains_used = (int)$this->userinfo['domains_used'];\n\t\t$domains_avail = (int)$this->userinfo['domains'];\n\n\t\tif (!is_int($offset) || $offset < 0) {\n\t\t\tthrow new Exception(\"Invalid offset specified\");\n\t\t}\n\n\t\ttry {\n\t\t\t$domain_array = $this->parseImportFile($separator);\n\t\t} catch (Exception $e) {\n\t\t\tthrow $e;\n\t\t}\n\n\t\tif (count($domain_array) <= 0) {\n\t\t\tthrow new Exception(\"No domains were read from the file.\");\n\t\t}\n\n\t\t$global_counter = 0;\n\t\t$import_counter = 0;\n\t\t$note = '';\n\t\tforeach ($domain_array as $idx => $dom) {\n\t\t\tif ($idx >= $offset) {\n\t\t\t\tif ($dom_unlimited || (!$dom_unlimited && $domains_used < $domains_avail)) {\n\t\t\t\t\t$result = $this->importEntity($dom);\n\t\t\t\t\tif ($result) {\n\t\t\t\t\t\t$import_counter++;\n\t\t\t\t\t\t$domains_used++;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$note .= 'You have reached your maximum allocation of domains (' . $domains_avail . ').';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$global_counter++;\n\t\t}\n\n\t\treturn [\n\t\t\t'all' => $global_counter,\n\t\t\t'imported' => $import_counter,\n\t\t\t'notice' => $note\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Bulk/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cli/CliCommand.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse PDO;\nuse Symfony\\Component\\Console\\Command\\Command;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\n\nclass CliCommand extends Command\n{\n\n\tprotected function validateRequirements(OutputInterface $output, bool $ignore_has_updates = false): int\n\t{\n\t\tif (!file_exists(Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {\n\t\t\t$output->writeln(\"<error>Could not find froxlor's userdata.inc.php file. You should use this script only with an installed froxlor system.</>\");\n\t\t\treturn self::INVALID;\n\t\t}\n\t\t// try database connection\n\t\ttry {\n\t\t\tDatabase::query(\"SELECT 1\");\n\t\t} catch (Exception $e) {\n\t\t\t// Do not proceed further if no database connection could be established\n\t\t\t$output->writeln(\"<error>\" . $e->getMessage() . \"</>\");\n\t\t\treturn self::INVALID;\n\t\t}\n\t\tif (!$ignore_has_updates && (Froxlor::hasUpdates() || Froxlor::hasDbUpdates())) {\n\t\t\tif ((int)Settings::Get('system.cron_allowautoupdate') == 1) {\n\t\t\t\treturn $this->runUpdate($output);\n\t\t\t} else {\n\t\t\t\t$output->writeln(\"<error>It seems that the froxlor files have been updated. Please login and finish the update procedure.</>\");\n\t\t\t\treturn self::INVALID;\n\t\t\t}\n\t\t}\n\t\treturn self::SUCCESS;\n\t}\n\n\tprotected function getUserByName(?string $loginname, bool $deactivated_check = true): array\n\t{\n\t\tif (empty($loginname)) {\n\t\t\tthrow new Exception(\"Empty username\");\n\t\t}\n\n\t\t$stmt = Database::prepare(\"\n\t\t\tSELECT `loginname` AS `customer`\n\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\tWHERE `loginname`= :loginname\n\t\t\");\n\t\tDatabase::pexecute($stmt, [\n\t\t\t\"loginname\" => $loginname\n\t\t]);\n\t\t$row = $stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\tif ($row && $row['customer'] == $loginname) {\n\t\t\t$table = \"`\" . TABLE_PANEL_CUSTOMERS . \"`\";\n\t\t\t$adminsession = '0';\n\t\t} else {\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tSELECT `loginname` AS `admin` FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\t\tWHERE `loginname`= :loginname\n\t\t\t\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\"loginname\" => $loginname\n\t\t\t]);\n\t\t\t$row = $stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t\tif ($row && $row['admin'] == $loginname) {\n\t\t\t\t$table = \"`\" . TABLE_PANEL_ADMINS . \"`\";\n\t\t\t\t$adminsession = '1';\n\t\t\t} else {\n\t\t\t\tthrow new Exception(\"Unknown user '\" . $loginname . \"'\");\n\t\t\t}\n\t\t}\n\n\t\t$userinfo_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM $table\n\t\t\tWHERE `loginname`= :loginname\n\t\t\");\n\t\tDatabase::pexecute($userinfo_stmt, [\n\t\t\t\"loginname\" => $loginname\n\t\t]);\n\t\t$userinfo = $userinfo_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t$userinfo['adminsession'] = $adminsession;\n\n\t\tif ($deactivated_check && $userinfo['deactivated']) {\n\t\t\tthrow new Exception(\"User '\" . $loginname . \"' is currently deactivated\");\n\t\t}\n\n\t\treturn $userinfo;\n\t}\n\n\tprotected function runUpdate(OutputInterface $output, bool $manual = false): int\n\t{\n\t\tif (!$manual) {\n\t\t\t$output->writeln('<comment>Automatic update is activated and we are going to proceed without any notices</>');\n\t\t}\n\t\tinclude_once Froxlor::getInstallDir() . '/lib/tables.inc.php';\n\t\tdefine('_CRON_UPDATE', 1);\n\t\tob_start([\n\t\t\t$this,\n\t\t\t'cleanUpdateOutput'\n\t\t]);\n\t\tinclude_once Froxlor::getInstallDir() . '/install/updatesql.php';\n\t\tob_end_flush();\n\t\t$output->writeln('<info>' . ($manual ? 'Database' : 'Automatic') . ' update done - you should check your settings to be sure everything is fine</>');\n\t\treturn self::SUCCESS;\n\t}\n\n\tprivate function cleanUpdateOutput($buffer): string\n\t{\n\t\treturn strip_tags(preg_replace(\"/<br\\W*?\\/>/\", \"\\n\", $buffer));\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/ConfigDiff.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Froxlor\\Config\\ConfigParser;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\n\nfinal class ConfigDiff extends CliCommand\n{\n\tprotected function configure(): void\n\t{\n\t\t$this->setName('froxlor:config-diff')\n\t\t\t->setDescription('Shows differences in config templates between OS versions')\n\t\t\t->addArgument('from', InputArgument::OPTIONAL, 'OS version to compare against')\n\t\t\t->addArgument('to', InputArgument::OPTIONAL, 'OS version to compare from')\n\t\t\t->addOption('list', 'l', InputOption::VALUE_NONE, 'List all possible OS versions')\n\t\t\t->addOption('diff-params', '', InputOption::VALUE_REQUIRED, 'Additional parameters for `diff`, e.g. --diff-params=\"--color=always\"');\n\t}\n\n\t/**\n\t * @throws \\Exception\n\t */\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\trequire Froxlor::getInstallDir() . '/lib/functions.php';\n\n\t\t$parsers = $versions = [];\n\t\tforeach (glob(Froxlor::getInstallDir() . '/lib/configfiles/*.xml') as $config) {\n\t\t\t$name = str_replace(\".xml\", \"\", strtolower(basename($config)));\n\t\t\t$parser = new ConfigParser($config);\n\t\t\t$versions[$name] = $parser->getCompleteDistroName();\n\t\t\t$parsers[$name] = $parser;\n\t\t}\n\t\tasort($versions);\n\n\t\tif ($input->getOption('list') === true) {\n\t\t\t$output->writeln('The following OS version templates are available:');\n\t\t\tforeach ($versions as $k => $v) {\n\t\t\t\t$output->writeln(str_pad($k, 20) . $v);\n\t\t\t}\n\t\t\treturn self::SUCCESS;\n\t\t}\n\n\t\tif (!$input->hasArgument('from') || !array_key_exists($input->getArgument('from'), $versions)) {\n\t\t\t$output->writeln('<error>Missing or invalid \"from\" argument.</error>');\n\t\t\t$output->writeln('Available versions: ' . implode(', ', array_keys($versions)));\n\t\t\treturn self::INVALID;\n\t\t}\n\n\t\tif (!$input->hasArgument('to') || !array_key_exists($input->getArgument('to'), $versions)) {\n\t\t\t$output->writeln('<error>Missing or invalid \"to\" argument.</error>');\n\t\t\t$output->writeln('Available versions: ' . implode(', ', array_keys($versions)));\n\t\t\treturn self::INVALID;\n\t\t}\n\n\t\t// Make sure diff is installed\n\t\t$check_diff_installed = FileDir::safe_exec('which diff');\n\t\tif (count($check_diff_installed) === 0) {\n\t\t\t$output->writeln('<error>Unable to find \"diff\" installation on your system.</error>');\n\t\t\treturn self::INVALID;\n\t\t}\n\n\t\t$parser_from = $parsers[$input->getArgument('from')];\n\t\t$parser_to = $parsers[$input->getArgument('to')];\n\t\t$tmp_from = tempnam(sys_get_temp_dir(), 'froxlor_config_diff_from');\n\t\t$tmp_to = tempnam(sys_get_temp_dir(), 'froxlor_config_diff_to');\n\t\t$files = [];\n\t\t$titles_by_key = [];\n\n\t\t// Aggregate content for each config file\n\t\tforeach ([[$parser_from, 'from'], [$parser_to, 'to']] as $todo) {\n\t\t\tforeach ($todo[0]->getServices() as $service_type => $service) {\n\t\t\t\tforeach ($service->getDaemons() as $daemon_name => $daemon) {\n\t\t\t\t\tforeach ($daemon->getConfig() as $instruction) {\n\t\t\t\t\t\tif ($instruction['type'] !== 'file') {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (isset($instruction['subcommands'])) {\n\t\t\t\t\t\t\tforeach ($instruction['subcommands'] as $subinstruction) {\n\t\t\t\t\t\t\t\tif ($subinstruction['type'] !== 'file') {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t$content = $subinstruction['content'];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$content = $instruction['content'];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!isset($content)) {\n\t\t\t\t\t\t\tthrow new \\Exception(\"Cannot find content for {$instruction['name']}\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$key = \"{$service_type}_{$daemon_name}_{$instruction['name']}\";\n\t\t\t\t\t\t$titles_by_key[$key] = \"{$service->title} : {$daemon->title} : {$instruction['name']}\";\n\t\t\t\t\t\tif (!isset($files[$key])) {\n\t\t\t\t\t\t\t$files[$key] = ['from' => '', 'to' => ''];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$files[$key][$todo[1]] = $this->filterContent($content);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tksort($files);\n\n\t\t$diff_params = '';\n\t\tif ($input->hasOption('diff-params') && trim($input->getOption('diff-params')) !== '') {\n\t\t\t$diff_params = trim($input->getOption('diff-params'));\n\t\t}\n\n\t\t// Run diff on each file and output, if anything changed\n\t\tforeach ($files as $file_key => $content) {\n\t\t\tfile_put_contents($tmp_from, $content['from']);\n\t\t\tfile_put_contents($tmp_to, $content['to']);\n\t\t\t$diff_output = FileDir::safe_exec(\"{$check_diff_installed[0]} {$diff_params} {$tmp_from} {$tmp_to}\");\n\n\t\t\tif (count($diff_output) === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$output->writeln('<info># ' . $titles_by_key[$file_key] . '</info>');\n\t\t\t$output->writeln(implode(\"\\n\", $diff_output) . \"\\n\");\n\t\t\tunset($diff_output);\n\t\t}\n\n\t\t// Remove tmp files again\n\t\tunlink($tmp_from);\n\t\tunlink($tmp_to);\n\n\t\treturn self::SUCCESS;\n\t}\n\n\tprivate function filterContent(string $content): string\n\t{\n\t\t$new_content = '';\n\n\t\tforeach (explode(\"\\n\", $content) as $n) {\n\t\t\t$n = trim($n);\n\t\t\tif (!$n) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (str_starts_with($n, '#')) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$new_content .= $n . \"\\n\";\n\t\t}\n\n\t\treturn $new_content;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/ConfigServices.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Exception;\nuse Froxlor\\Config\\ConfigParser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\SImExporter;\nuse Froxlor\\System\\Crypt;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nfinal class ConfigServices extends CliCommand\n{\n\tprivate $yes_to_all_supported = [\n\t\t'trixie',\n\t\t'bookworm',\n\t\t'bullseye',\n\t\t'focal',\n\t\t'jammy',\n\t\t'noble',\n\t];\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:config-services');\n\t\t$this->setDescription('Configure system services');\n\t\t$this->addOption('create', 'c', InputOption::VALUE_NONE, 'Create a services list configuration for the --apply option.')\n\t\t\t->addOption('apply', 'a', InputOption::VALUE_REQUIRED, 'Configure your services by given configuration file/string. To create one run the command with the --create option.')\n\t\t\t->addOption('list', 'l', InputOption::VALUE_NONE, 'Output the services that are going to be configured using a given config file (--apply option). No services will be configured.')\n\t\t\t->addOption('daemon', 'd', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'When used with --apply you can specify one or multiple daemons. These will be the only services that get configured.')\n\t\t\t->addOption('import-settings', 'i', InputOption::VALUE_REQUIRED, 'Import settings from another froxlor installation. This can be done standalone or in addition to --apply.')\n\t\t\t->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Install packages without asking questions (Debian/Ubuntu only currently)')\n\t\t\t->addOption('delete-file', 'D', InputOption::VALUE_NONE, 'If --apply is called with a local file, remove it after successful configurations.');\n\t}\n\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$result = $this->validateRequirements($output);\n\n\t\trequire Froxlor::getInstallDir() . '/lib/functions.php';\n\n\t\tif ($result == self::SUCCESS && $input->getOption('import-settings') == false && $input->getOption('create') == false && $input->getOption('apply') == false) {\n\t\t\t$output->writeln('<error>No option given to do something, exiting.</>');\n\t\t\treturn self::INVALID;\n\t\t}\n\n\t\t// import settings if given\n\t\tif ($result == self::SUCCESS && $input->getOption('import-settings')) {\n\t\t\t$result = $this->importSettings($input, $output);\n\t\t}\n\n\t\tif ($result == self::SUCCESS && $input->getOption('yes-to-all')) {\n\t\t\tif (in_array(Settings::Get('system.distribution'), $this->yes_to_all_supported)) {\n\t\t\t\tputenv(\"DEBIAN_FRONTEND=noninteractive\");\n\t\t\t\texec(\"echo 'APT::Get::Assume-Yes \\\"true\\\";' > /tmp/_tmp_apt.conf\");\n\t\t\t\tputenv(\"APT_CONFIG=/tmp/_tmp_apt.conf\");\n\t\t\t} else {\n\t\t\t\t$output->writeln('<comment>--yes-to-all ignored, not configured for supported distribution</>');\n\t\t\t}\n\t\t}\n\n\t\tif ($result == self::SUCCESS) {\n\t\t\t$io = new SymfonyStyle($input, $output);\n\t\t\tif ($input->getOption('create')) {\n\t\t\t\t$result = $this->createConfig($output, $io);\n\t\t\t} elseif ($input->getOption('apply')) {\n\t\t\t\t$result = $this->applyConfig($input, $output, $io);\n\t\t\t} elseif ($input->getOption('list') || $input->getOption('daemon')) {\n\t\t\t\t$output->writeln('<error>Options --list and --daemon only work together with --apply.</>');\n\t\t\t\t$result = self::INVALID;\n\t\t\t}\n\t\t}\n\n\t\tif ($input->getOption('yes-to-all') && in_array(Settings::Get('system.distribution'), $this->yes_to_all_supported)) {\n\t\t\tputenv(\"DEBIAN_FRONTEND\");\n\t\t\tunlink(\"/tmp/_tmp_apt.conf\");\n\t\t\tputenv(\"APT_CONFIG\");\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\tprivate function importSettings(InputInterface $input, OutputInterface $output)\n\t{\n\t\t$importFile = $input->getOption('import-settings');\n\n\t\tif (strtoupper(substr($importFile, 0, 4)) == 'HTTP') {\n\t\t\t$output->writeln(\"Settings file seems to be an URL, trying to download\");\n\t\t\t$target = \"/tmp/froxlor-import-settings-\" . time() . \".json\";\n\t\t\tif (@file_exists($target)) {\n\t\t\t\t@unlink($target);\n\t\t\t}\n\t\t\t$this->downloadFile($importFile, $target);\n\t\t\t$importFile = $target;\n\t\t}\n\t\tif (!is_file($importFile)) {\n\t\t\t$output->writeln('<error>Given settings file is not a file</>');\n\t\t\treturn self::INVALID;\n\t\t} elseif (!file_exists($importFile)) {\n\t\t\t$output->writeln('<error>Given settings file cannot be found (' . $importFile . ')</>');\n\t\t\treturn self::INVALID;\n\t\t} elseif (!is_readable($importFile)) {\n\t\t\t$output->writeln('<error>Given settings file cannot be read (' . $importFile . ')</>');\n\t\t\treturn self::INVALID;\n\t\t}\n\t\t$imp_content = file_get_contents($importFile);\n\t\tSImExporter::import($imp_content);\n\t\t$output->writeln(\"<info>Successfully imported settings from '\" . $input->getOption('import-settings') . \"'</info>\");\n\t\treturn self::SUCCESS;\n\t}\n\n\tprivate function downloadFile($src, $dest)\n\t{\n\t\tset_time_limit(0);\n\t\t// This is the file where we save the information\n\t\t$fp = fopen($dest, 'w+');\n\t\t// Here is the file we are downloading, replace spaces with %20\n\t\t$ch = curl_init(str_replace(\" \", \"%20\", $src));\n\t\tcurl_setopt($ch, CURLOPT_TIMEOUT, 50);\n\t\tcurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);\n\t\t// write curl response to file\n\t\tcurl_setopt($ch, CURLOPT_FILE, $fp);\n\t\tcurl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\n\t\t// get curl response\n\t\tcurl_exec($ch);\n\t\tfclose($fp);\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function createConfig(OutputInterface $output, SymfonyStyle $io): int\n\t{\n\t\t$_daemons_config = [\n\t\t\t'distro' => \"\"\n\t\t];\n\n\t\t$config_dir = Froxlor::getInstallDir() . '/lib/configfiles/';\n\t\t// show list of available distro's\n\t\t$distros = glob($config_dir . '*.xml');\n\t\t// tmp array\n\t\t$distributions_select_data = [];\n\n\t\t//set default os.\n\t\t$os_dist = ['ID' => 'trixie'];\n\t\t$os_version = ['0' => '13'];\n\t\t$os_default = $os_dist['ID'];\n\n\t\t//read os-release\n\t\tif (file_exists('/etc/os-release')) {\n\t\t\t$os_dist = parse_ini_file('/etc/os-release', false);\n\t\t\tif (is_array($os_dist) && array_key_exists('ID', $os_dist) && array_key_exists('VERSION_ID', $os_dist)) {\n\t\t\t\t$os_version = explode('.', $os_dist['VERSION_ID'])[0];\n\t\t\t}\n\t\t}\n\n\t\t// read in all the distros\n\t\tforeach ($distros as $_distribution) {\n\t\t\t// get configparser object\n\t\t\t$dist = new ConfigParser($_distribution);\n\t\t\t// get distro-info\n\t\t\t$dist_display = $dist->getCompleteDistroName();\n\t\t\t// store in tmp array\n\t\t\t$distributions_select_data[$dist_display] = str_replace(\".xml\", \"\", strtolower(basename($_distribution)));\n\n\t\t\t//guess if this is the current distro.\n\t\t\t$ver = explode('.', $dist->distributionVersion)[0];\n\t\t\tif (strtolower($os_dist['ID']) == strtolower($dist->distributionName) && $os_version == $ver) {\n\t\t\t\t$os_default = str_replace(\".xml\", \"\", strtolower(basename($_distribution)));\n\t\t\t}\n\t\t}\n\n\t\t// sort by distribution name\n\t\tksort($distributions_select_data);\n\n\t\t// list all distributions\n\t\t$table_rows = [];\n\t\t$valid_dists = [];\n\t\tforeach ($distributions_select_data as $name => $filename) {\n\t\t\t$table_rows[] = [$filename, $name];\n\t\t\t$valid_dists[] = $filename;\n\t\t}\n\t\t$io->table(\n\t\t\t['ID', 'Distribution'],\n\t\t\t$table_rows\n\t\t);\n\n\t\t$_daemons_config['distro'] = $io->choice('Choose distribution', $valid_dists, $os_default);\n\n\t\t// go through all services and let user check whether to include it or not\n\t\tif (empty($_daemons_config['distro']) || !file_exists($config_dir . '/' . $_daemons_config['distro'] . \".xml\")) {\n\t\t\t$output->writeln('<error>Empty or non-existing distribution given.</>');\n\t\t\treturn self::INVALID;\n\t\t}\n\t\t$configfiles = new ConfigParser($config_dir . '/' . $_daemons_config['distro'] . \".xml\");\n\t\t$services = $configfiles->getServices();\n\n\t\tforeach ($services as $si => $service) {\n\t\t\t$output->writeln(\"--- \" . strtoupper($si) . \" ---\");\n\t\t\t$_daemons_config[$si] = \"\";\n\n\t\t\t$daemons = $service->getDaemons();\n\t\t\t$default_daemon = \"\";\n\t\t\t$table_rows = [];\n\t\t\t$valid_options = [];\n\t\t\tif ($si != 'system') {\n\t\t\t\t$table_rows[] = ['x', 'No'];\n\t\t\t\t$valid_options[] = 'x';\n\t\t\t}\n\t\t\tforeach ($daemons as $di => $dd) {\n\t\t\t\t$title = $dd->title;\n\t\t\t\tif ($dd->default) {\n\t\t\t\t\t$default_daemon = $di;\n\t\t\t\t\t$title .= \" (default)\";\n\t\t\t\t}\n\t\t\t\t$table_rows[] = [$di, $title];\n\t\t\t\t$valid_options[] = $di;\n\t\t\t}\n\t\t\t$io->table(\n\t\t\t\t['Value', 'Name'],\n\t\t\t\t$table_rows\n\t\t\t);\n\n\t\t\t$daemons['x'] = 'x';\n\t\t\tif ($si == 'system') {\n\t\t\t\t$_daemons_config[$si] = [];\n\t\t\t\t// for the system/other services we need a multiple choice possibility\n\t\t\t\t$output->writeln(\"<comment>Select every service you need. Enter empty value when done</>\");\n\t\t\t\t$sysservice = \"\";\n\t\t\t\tdo {\n\t\t\t\t\t$sysservice = $io->ask('Choose service');\n\t\t\t\t\tif (!empty($sysservice)) {\n\t\t\t\t\t\t$_daemons_config[$si][] = $sysservice;\n\t\t\t\t\t}\n\t\t\t\t} while (!empty($sysservice));\n\t\t\t\t// add 'cron' as fixed part (doesn't hurt if it exists)\n\t\t\t\tif (!in_array('cron', $_daemons_config[$si])) {\n\t\t\t\t\t$_daemons_config[$si][] = 'cron';\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// for all others -> only one value\n\t\t\t\t$_daemons_config[$si] = $io->choice('Choose service', $valid_options, $default_daemon);\n\t\t\t}\n\t\t}\n\n\t\t$daemons_config = json_encode($_daemons_config);\n\t\t$output_file = $io->ask(\"Choose output-filename\", \"/tmp/froxlor-config-\" . date('Ymd') . \".json\");\n\t\tfile_put_contents($output_file, $daemons_config);\n\t\t$output->writeln(\"<info>Successfully generated service-configfile '\" . $output_file . \"'</>\");\n\t\t$output->writeln([\n\t\t\t\"\",\n\t\t\t\"<info>You can now apply this config running:</>\",\n\t\t\t\"php \" . Froxlor::getInstallDir() . \"bin/froxlor-cli froxlor:config-services --apply=\" . $output_file,\n\t\t\t\"\"\n\t\t]);\n\t\t$proceed = $io->confirm(\"Do you want to apply the config now?\", false);\n\t\tif ($proceed) {\n\t\t\tpassthru(\"php \" . Froxlor::getInstallDir() . \"bin/froxlor-cli froxlor:config-services --apply=\" . $output_file);\n\t\t}\n\t\treturn self::SUCCESS;\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function applyConfig(InputInterface $input, OutputInterface $output, SymfonyStyle $io): int\n\t{\n\t\t$applyFile = $input->getOption('apply');\n\n\t\t// check if plain JSON\n\t\t$decoded_config = json_decode($applyFile, true);\n\t\t$skipFileCheck = false;\n\t\tif (json_last_error() == JSON_ERROR_NONE) {\n\t\t\t$skipFileCheck = true;\n\t\t}\n\n\t\tif (!$skipFileCheck) {\n\t\t\tif (strtoupper(substr($applyFile, 0, 4)) == 'HTTP') {\n\t\t\t\t$output->writeln(\"Config file seems to be an URL, trying to download\");\n\t\t\t\t$target = \"/tmp/froxlor-config-\" . time() . \".json\";\n\t\t\t\tif (@file_exists($target)) {\n\t\t\t\t\t@unlink($target);\n\t\t\t\t}\n\t\t\t\t$this->downloadFile($applyFile, $target);\n\t\t\t\t$applyFile = $target;\n\t\t\t}\n\t\t\tif (!is_file($applyFile)) {\n\t\t\t\t$output->writeln('<error>Given config file is not a file</>');\n\t\t\t\treturn self::INVALID;\n\t\t\t} elseif (!file_exists($applyFile)) {\n\t\t\t\t$output->writeln('<error>Given config file cannot be found (' . $applyFile . ')</>');\n\t\t\t\treturn self::INVALID;\n\t\t\t} elseif (!is_readable($applyFile)) {\n\t\t\t\t$output->writeln('<error>Given config file cannot be read (' . $applyFile . ')</>');\n\t\t\t\treturn self::INVALID;\n\t\t\t}\n\n\t\t\t$config = file_get_contents($applyFile);\n\t\t\t$decoded_config = json_decode($config, true);\n\t\t}\n\n\t\tif ($input->getOption('list') != false) {\n\t\t\t$table_rows = [];\n\t\t\tforeach ($decoded_config as $service => $daemon) {\n\t\t\t\tif (is_array($daemon) && count($daemon) > 0) {\n\t\t\t\t\tforeach ($daemon as $sysdaemon) {\n\t\t\t\t\t\t$table_rows[] = [$service, $sysdaemon];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif ($daemon == 'x') {\n\t\t\t\t\t\t$daemon = '--- skipped ---';\n\t\t\t\t\t}\n\t\t\t\t\t$table_rows[] = [$service, $daemon];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$io->table(\n\t\t\t\t['Service', 'Selected daemon'],\n\t\t\t\t$table_rows\n\t\t\t);\n\t\t\treturn self::SUCCESS;\n\t\t}\n\n\t\t$only_daemon = [];\n\t\tif ($input->getOption('daemon') != false) {\n\t\t\t$only_daemon = $input->getOption('daemon');\n\t\t}\n\n\t\tif (!empty($decoded_config)) {\n\n\t\t\t$config_dir = Froxlor::getInstallDir() . 'lib/configfiles/';\n\t\t\tif (empty($decoded_config['distro']) || !file_exists($config_dir . '/' . $decoded_config['distro'] . \".xml\")) {\n\t\t\t\t$output->writeln('<error>Empty or non-existing distribution given. Please login with an admin, go to \"System -> Configuration\" and select your correct distribution in the top-right corner or specify valid distribution name for \"distro\" parameter.</>');\n\t\t\t\treturn self::INVALID;\n\t\t\t}\n\t\t\t$configfiles = new ConfigParser($config_dir . '/' . $decoded_config['distro'] . \".xml\");\n\t\t\t$services = $configfiles->getServices();\n\t\t\t$replace_arr = $this->getReplacerArray();\n\t\t\t$clean_replace_arr = array_map(function ($v) {\n\t\t\t\treturn escapeshellarg((string)($v ?? ''));\n\t\t\t}, $replace_arr);\n\n\t\t\t// be sure the fallback certificate specified in the settings exists\n\t\t\t$certFile = Settings::Get('system.ssl_cert_file');\n\t\t\t$keyFile = Settings::Get('system.ssl_key_file');\n\t\t\tif (empty($certFile) || empty($keyFile) || !file_exists($certFile) || !file_exists($keyFile)) {\n\t\t\t\t$output->writeln('<comment>Creating missing certificate ' . $certFile . '</>');\n\t\t\t\tCrypt::createSelfSignedCertificate();\n\t\t\t}\n\n\t\t\tforeach ($services as $si => $service) {\n\t\t\t\t$output->writeln(\"--- Configuring: \" . strtoupper($si) . \" ---\");\n\t\t\t\tif (!isset($decoded_config[$si]) || $decoded_config[$si] == 'x') {\n\t\t\t\t\t$output->writeln('<comment>Skipping ' . strtoupper($si) . ' configuration as desired</>');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$daemons = $service->getDaemons();\n\t\t\t\tforeach ($daemons as $di => $dd) {\n\t\t\t\t\t// check for desired service\n\t\t\t\t\tif (($si != 'system' && $decoded_config[$si] != $di) || (is_array($decoded_config[$si]) && !in_array($di, $decoded_config[$si]))) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t$output->writeln(\"Configuring '\" . $di . \"'\");\n\n\t\t\t\t\tif (!empty($only_daemon) && !in_array($di, $only_daemon)) {\n\t\t\t\t\t\t$output->writeln('<comment>Skipping ' . $di . ' configuration as desired</>');\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// run all cmds\n\t\t\t\t\t$confarr = $dd->getConfig();\n\t\t\t\t\tforeach ($confarr as $action) {\n\t\t\t\t\t\tswitch ($action['type']) {\n\t\t\t\t\t\t\tcase \"install\":\n\t\t\t\t\t\t\t\t$output->writeln(\"Installing required packages\");\n\t\t\t\t\t\t\t\t$result = null;\n\t\t\t\t\t\t\t\tpassthru(strtr($action['content'], $clean_replace_arr), $result);\n\t\t\t\t\t\t\t\tif (strlen($result) > 1) {\n\t\t\t\t\t\t\t\t\techo $result;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"command\":\n\t\t\t\t\t\t\t\texec(strtr($action['content'], $clean_replace_arr));\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"file\":\n\t\t\t\t\t\t\t\tif (array_key_exists('content', $action)) {\n\t\t\t\t\t\t\t\t\t$output->writeln('<comment>Creating file \"' . $action['name'] . '\"</>');\n\t\t\t\t\t\t\t\t\tfile_put_contents($action['name'], trim(strtr($action['content'], $replace_arr)) . PHP_EOL);\n\t\t\t\t\t\t\t\t} elseif (array_key_exists('subcommands', $action)) {\n\t\t\t\t\t\t\t\t\tforeach ($action['subcommands'] as $fileaction) {\n\t\t\t\t\t\t\t\t\t\tif (array_key_exists('execute', $fileaction) && $fileaction['execute'] == \"pre\") {\n\t\t\t\t\t\t\t\t\t\t\texec(strtr($fileaction['content'], $replace_arr));\n\t\t\t\t\t\t\t\t\t\t} elseif (array_key_exists('execute', $fileaction) && $fileaction['execute'] == \"post\") {\n\t\t\t\t\t\t\t\t\t\t\texec(strtr($fileaction['content'], $replace_arr));\n\t\t\t\t\t\t\t\t\t\t} elseif ($fileaction['type'] == 'file') {\n\t\t\t\t\t\t\t\t\t\t\t$output->writeln('<comment>Creating file \"' . $fileaction['name'] . '\"</>');\n\t\t\t\t\t\t\t\t\t\t\tfile_put_contents($fileaction['name'], trim(strtr($fileaction['content'], $replace_arr)) . PHP_EOL);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// set is_configured flag\n\t\t\tSettings::Set('panel.is_configured', '1', true);\n\t\t\t// run cronjob at the end to ensure configs are all up to date\n\t\t\texec('php ' . Froxlor::getInstallDir() . 'bin/froxlor-cli froxlor:cron --force');\n\t\t\t// and done\n\t\t\t$output->writeln('<info>All services have been configured</>');\n\n\t\t\tif ($input->getOption('delete-file') && file_exists($applyFile)) {\n\t\t\t\t@unlink($applyFile);\n\t\t\t}\n\t\t\treturn self::SUCCESS;\n\t\t} else {\n\t\t\t$output->writeln('<error>Unable to decode given JSON file</>');\n\t\t\treturn self::INVALID;\n\t\t}\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function getReplacerArray(): array\n\t{\n\t\t$customer_tmpdir = '/tmp/';\n\t\tif (Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_tmpdir') != '') {\n\t\t\t$customer_tmpdir = Settings::Get('system.mod_fcgid_tmpdir');\n\t\t} elseif (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.tmpdir') != '') {\n\t\t\t$customer_tmpdir = Settings::Get('phpfpm.tmpdir');\n\t\t}\n\n\t\t// try to convert nameserver hosts to ip's\n\t\t$ns_ips = \"\";\n\t\t$known_ns_ips = [];\n\t\tif (Settings::Get('system.nameservers') != '') {\n\t\t\t$nameservers = explode(',', Settings::Get('system.nameservers'));\n\t\t\tforeach ($nameservers as $nameserver) {\n\t\t\t\t$nameserver = trim($nameserver);\n\t\t\t\t// DNS servers might be multi homed; allow transfer from all ip\n\t\t\t\t// addresses of the DNS server\n\t\t\t\t$nameserver_ips = PhpHelper::gethostbynamel6($nameserver);\n\t\t\t\t// append dot to hostname\n\t\t\t\tif (substr($nameserver, -1, 1) != '.') {\n\t\t\t\t\t$nameserver .= '.';\n\t\t\t\t}\n\t\t\t\t// ignore invalid responses\n\t\t\t\tif (!is_array($nameserver_ips)) {\n\t\t\t\t\t// act like PhpHelper::gethostbynamel6() and return unmodified hostname on error\n\t\t\t\t\t$nameserver_ips = [\n\t\t\t\t\t\t$nameserver\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\t$known_ns_ips = array_merge($known_ns_ips, $nameserver_ips);\n\t\t\t\t}\n\t\t\t\tif (!empty($ns_ips)) {\n\t\t\t\t\t$ns_ips .= ',';\n\t\t\t\t}\n\t\t\t\t$ns_ips .= implode(\",\", $nameserver_ips);\n\t\t\t}\n\t\t}\n\n\t\t// AXFR server\n\t\tif (Settings::Get('system.axfrservers') != '') {\n\t\t\t$axfrservers = explode(',', Settings::Get('system.axfrservers'));\n\t\t\tforeach ($axfrservers as $axfrserver) {\n\t\t\t\tif (!in_array(trim($axfrserver), $known_ns_ips)) {\n\t\t\t\t\tif (!empty($ns_ips)) {\n\t\t\t\t\t\t$ns_ips .= ',';\n\t\t\t\t\t}\n\t\t\t\t\t$ns_ips .= trim($axfrserver);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tDatabase::needSqlData();\n\t\t$sql = Database::getSqlData();\n\n\t\treturn [\n\t\t\t'<SQL_UNPRIVILEGED_USER>' => $sql['user'],\n\t\t\t'<SQL_UNPRIVILEGED_PASSWORD>' => $sql['passwd'],\n\t\t\t'<SQL_DB>' => $sql['db'],\n\t\t\t'<SQL_HOST>' => $sql['host'],\n\t\t\t'<SQL_SOCKET>' => $sql['socket'] ?? null,\n\t\t\t'<SERVERNAME>' => Settings::Get('system.hostname'),\n\t\t\t'<SERVERIP>' => Settings::Get('system.ipaddress'),\n\t\t\t'<NAMESERVERS>' => Settings::Get('system.nameservers'),\n\t\t\t'<NAMESERVERS_IP>' => $ns_ips,\n\t\t\t'<VIRTUAL_MAILBOX_BASE>' => Settings::Get('system.vmail_homedir'),\n\t\t\t'<VIRTUAL_UID_MAPS>' => Settings::Get('system.vmail_uid'),\n\t\t\t'<VIRTUAL_GID_MAPS>' => Settings::Get('system.vmail_gid'),\n\t\t\t'<SSLPROTOCOLS>' => (Settings::Get('system.use_ssl') == '1') ? 'imaps pop3s' : '',\n\t\t\t'<CUSTOMER_TMP>' => FileDir::makeCorrectDir($customer_tmpdir),\n\t\t\t'<BASE_PATH>' => Froxlor::getInstallDir(),\n\t\t\t'<BIND_CONFIG_PATH>' => FileDir::makeCorrectDir(Settings::Get('system.bindconf_directory')),\n\t\t\t'<WEBSERVER_RELOAD_CMD>' => Settings::Get('system.apachereload_command'),\n\t\t\t'<CUSTOMER_LOGS>' => FileDir::makeCorrectDir(Settings::Get('system.logfiles_directory')),\n\t\t\t'<FPM_IPCDIR>' => FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')),\n\t\t\t'<WEBSERVER_GROUP>' => Settings::Get('system.httpgroup'),\n\t\t\t'<SSL_CERT_FILE>' => Settings::Get('system.ssl_cert_file'),\n\t\t\t'<SSL_KEY_FILE>' => Settings::Get('system.ssl_key_file'),\n\t\t\t'<ADMIN_MAIL>' => Settings::Get('panel.adminmail'),\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/InstallCommand.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Exception;\nuse Froxlor\\Config\\ConfigParser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Install\\Install;\nuse Froxlor\\Install\\Install\\Core;\nuse Froxlor\\Settings;\nuse Symfony\\Component\\Console\\Command\\Command;\nuse Symfony\\Component\\Console\\Helper\\Table;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nfinal class InstallCommand extends Command\n{\n\n\tprivate $io = null;\n\n\tprivate $formfielddata = [];\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:install');\n\t\t$this->setDescription('Installation process to use instead of web-ui');\n\t\t$this->addArgument('input-file', InputArgument::OPTIONAL, 'Optional JSON array file to use for unattended installations');\n\t\t$this->addOption('print-example-file', 'p', InputOption::VALUE_NONE, 'Outputs an example JSON content to be used with the input file parameter')\n\t\t\t->addOption('create-userdata-from-str', 'c', InputOption::VALUE_REQUIRED, 'Creates lib/userdata.inc.php file from string created by web-install process')\n\t\t\t->addOption('show-sysinfo', 's', InputOption::VALUE_NONE, 'Outputs system information about your froxlor installation');\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$result = self::SUCCESS;\n\n\t\tif ($input->getOption('create-userdata-from-str') !== null) {\n\t\t\t$ud_str = $input->getOption('create-userdata-from-str');\n\t\t\t$ud_dec = @json_decode(@base64_decode($ud_str), true);\n\t\t\tif (is_array($ud_dec) && !empty($ud_dec) && count($ud_dec) == 8) {\n\t\t\t\t$core = new Core($ud_dec);\n\t\t\t\t$core->createUserdataConf();\n\t\t\t\treturn $result;\n\t\t\t}\n\t\t\t$output->writeln(\"<error>Invalid parameter value.</>\");\n\t\t\treturn self::INVALID;\n\t\t}\n\n\t\tif ($input->getOption('show-sysinfo') !== false) {\n\t\t\tif (!file_exists(Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {\n\t\t\t\t$output->writeln(\"<error>Could not find froxlor's userdata.inc.php file. You can use this parameter only with an installed froxlor system.</>\");\n\t\t\t\treturn self::INVALID;\n\t\t\t}\n\t\t\t$this->printSysInfo($output);\n\t\t\treturn self::SUCCESS;\n\t\t}\n\n\t\tsession_start();\n\n\t\trequire __DIR__ . '/install.functions.php';\n\n\t\t// set a few defaults CLI cannot know\n\t\t$_SERVER['SERVER_SOFTWARE'] = 'apache';\n\t\t$host = [];\n\t\texec('hostname -f', $host);\n\t\t$_SERVER['SERVER_NAME'] = $host[0] ?? '';\n\t\t$ips = [];\n\t\texec('hostname -I', $ips);\n\t\t$ips = explode(\" \", $ips[0] ?? \"\");\n\t\t// ipv4 address?\n\t\t$_SERVER['SERVER_ADDR'] = filter_var($ips[0] ?? \"\", FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? ($ips[0] ?? '') : '';\n\t\tif (empty($_SERVER['SERVER_ADDR'])) {\n\t\t\t// possible ipv6 address?\n\t\t\t$_SERVER['SERVER_ADDR'] = filter_var($ips[0] ?? \"\", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? ($ips[0] ?? '') : '';\n\t\t}\n\n\t\tif ($input->getOption('print-example-file') !== false) {\n\t\t\t$this->printExampleFile($output);\n\t\t\treturn self::SUCCESS;\n\t\t}\n\n\t\tif (file_exists(Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {\n\t\t\t$output->writeln(\"<error>froxlor seems to be installed already.</>\");\n\t\t\treturn self::INVALID;\n\t\t}\n\n\t\t$this->io = new SymfonyStyle($input, $output);\n\t\t$this->io->title('Froxlor installation');\n\n\t\tif ($input->getArgument('input-file')) {\n\t\t\t$inputFile = $input->getArgument('input-file');\n\t\t\tif (strtoupper(substr($inputFile, 0, 4)) == 'HTTP') {\n\t\t\t\t$output->writeln(\"Input file seems to be an URL, trying to download\");\n\t\t\t\t$target = \"/tmp/froxlor-install-\" . time() . \".json\";\n\t\t\t\tif (@file_exists($target)) {\n\t\t\t\t\t@unlink($target);\n\t\t\t\t}\n\t\t\t\t$this->downloadFile($inputFile, $target);\n\t\t\t\t$inputFile = $target;\n\t\t\t}\n\t\t\tif (!is_file($inputFile)) {\n\t\t\t\t$output->writeln('<error>Given input file is not a file</>');\n\t\t\t\treturn self::INVALID;\n\t\t\t} elseif (!file_exists($inputFile)) {\n\t\t\t\t$output->writeln('<error>Given input file cannot be found (' . $inputFile . ')</>');\n\t\t\t\treturn self::INVALID;\n\t\t\t} elseif (!is_readable($inputFile)) {\n\t\t\t\t$output->writeln('<error>Given input file cannot be read (' . $inputFile . ')</>');\n\t\t\t\treturn self::INVALID;\n\t\t\t}\n\n\t\t\t$inputcontent = file_get_contents($inputFile);\n\t\t\t$decoded_input = json_decode($inputcontent, true) ?? [];\n\t\t\t$extended = true;\n\n\t\t\tif (empty($decoded_input)) {\n\t\t\t\t$output->writeln('<error>Given input file seems to be invalid JSON</>');\n\t\t\t\treturn self::INVALID;\n\t\t\t}\n\t\t\t$this->io->info('Running unattended installation');\n\t\t} else {\n\t\t\t$extended = $this->io->confirm('Use advanced installation mode?', false);\n\t\t\t$decoded_input = [];\n\t\t}\n\n\t\treturn $this->showStep(0, $extended, $decoded_input);\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function showStep(int $step = 0, bool $extended = false, array $decoded_input = []): int\n\t{\n\t\t$result = self::SUCCESS;\n\t\t$inst = new Install(['step' => $step, 'extended' => $extended]);\n\t\tswitch ($step) {\n\t\t\tcase 0:\n\t\t\t\t$this->io->section(lng('install.preflight'));\n\t\t\t\t$crresult = $inst->checkRequirements();\n\t\t\t\t$this->io->info($crresult['text']);\n\t\t\t\tif (!empty($crresult['criticals'])) {\n\t\t\t\t\tforeach ($crresult['criticals'] as $ctype => $critical) {\n\t\t\t\t\t\tif (!empty($ctype) && $ctype == 'wrong_ownership') {\n\t\t\t\t\t\t\t$this->io->error(lng('install.errors.' . $ctype, [$critical['user'], $critical['group']]));\n\t\t\t\t\t\t} elseif (!empty($ctype) && $ctype == 'missing_extensions') {\n\t\t\t\t\t\t\t$this->io->error([\n\t\t\t\t\t\t\t\tlng('install.errors.' . $ctype),\n\t\t\t\t\t\t\t\timplode(\"\\n\", $critical)\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$this->io->error($critical);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$result = self::FAILURE;\n\t\t\t\t}\n\t\t\t\tif (!empty($crresult['suggestions'])) {\n\t\t\t\t\tforeach ($crresult['suggestions'] as $ctype => $suggestion) {\n\t\t\t\t\t\tif ($ctype == 'missing_extensions') {\n\t\t\t\t\t\t\t$this->io->warning([\n\t\t\t\t\t\t\t\tlng('install.errors.suggestedextensions'),\n\t\t\t\t\t\t\t\timplode(\"\\n\", $suggestion)\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$this->io->warning($suggestion);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ($result == self::SUCCESS) {\n\t\t\t\t\treturn $this->showStep(++$step, $extended, $decoded_input);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\tcase 2:\n\t\t\tcase 3:\n\t\t\t\t$section = $inst->formfield['install']['sections']['step' . $step] ?? [];\n\t\t\t\t$this->io->section($section['title']);\n\t\t\t\tif (empty($decoded_input)) {\n\t\t\t\t\t$this->io->note($section['description']);\n\t\t\t\t}\n\t\t\t\tforeach ($section['fields'] as $fieldname => $fielddata) {\n\t\t\t\t\tif ($extended == false && isset($fielddata['advanced']) && $fielddata['advanced'] == true) {\n\t\t\t\t\t\tif ($fieldname == 'httpuser' || $fieldname == 'httpgroup') {\n\t\t\t\t\t\t\t// overwrite posix_getgrgid(posix_getgid())['name'] as it would result in 'root'\n\t\t\t\t\t\t\t$this->formfielddata[$fieldname] = 'www-data';\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$this->formfielddata[$fieldname] = $fielddata['value'];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t$ask_field = true;\n\t\t\t\t\t// preset from input-file\n\t\t\t\t\tif (!empty($decoded_input) && isset($decoded_input[$fieldname])) {\n\t\t\t\t\t\t$this->formfielddata[$fieldname] = $decoded_input[$fieldname];\n\t\t\t\t\t\t$ask_field = false;\n\t\t\t\t\t}\n\t\t\t\t\t$fielddata['value'] = $this->formfielddata[$fieldname] ?? ($fielddata['value'] ?? null);\n\t\t\t\t\t$fielddata['label'] = $this->cliTextFormat($fielddata['label'], \" \");\n\t\t\t\t\tif ($ask_field) {\n\t\t\t\t\t\tif ($fielddata['type'] == 'password') {\n\t\t\t\t\t\t\t$this->formfielddata[$fieldname] = $this->io->askHidden($fielddata['label'], function ($value) use ($fielddata) {\n\t\t\t\t\t\t\t\tif (isset($fielddata['mandatory']) && $fielddata['mandatory'] && empty($value)) {\n\t\t\t\t\t\t\t\t\tthrow new \\RuntimeException('You must enter a value.');\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn $value;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} elseif ($fielddata['type'] == 'checkbox') {\n\t\t\t\t\t\t\t$this->formfielddata[$fieldname] = $this->io->confirm($fielddata['label'], $fielddata['value'] ?? false);\n\t\t\t\t\t\t} elseif ($fielddata['type'] == 'select') {\n\t\t\t\t\t\t\t$this->formfielddata[$fieldname] = $this->io->choice($fielddata['label'], $fielddata['select_var'], $fielddata['selected'] ?? '');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$this->formfielddata[$fieldname] = $this->io->ask($fielddata['label'], $fielddata['value'] ?? '', function ($value) use ($fielddata) {\n\t\t\t\t\t\t\t\tif (isset($fielddata['mandatory']) && $fielddata['mandatory'] && empty($value)) {\n\t\t\t\t\t\t\t\t\tthrow new \\RuntimeException('You must enter a value.');\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn $value;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$this->io->text(\"Setting field '\" . $fieldname . \"' to value '\" . ($fielddata['type'] == 'password' ? '*hidden*' : $fielddata['value']) . \"'\");\n\t\t\t\t\t\tif (isset($fielddata['mandatory']) && $fielddata['mandatory'] && empty($fielddata['value'])) {\n\t\t\t\t\t\t\t$this->io->error(\"Mandatory field '\" . $fieldname . \"' not specified/empty value in input file\");\n\t\t\t\t\t\t\treturn self::FAILURE;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif ($step == 1) {\n\t\t\t\t\t\t$inst->checkDatabase($this->formfielddata);\n\t\t\t\t\t} elseif ($step == 2) {\n\t\t\t\t\t\t$inst->checkAdminUser($this->formfielddata);\n\t\t\t\t\t} elseif ($step == 3) {\n\t\t\t\t\t\t$inst->checkSystem($this->formfielddata);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t$this->io->error($e->getMessage());\n\t\t\t\t\tif ($this->io->confirm('Retry?', empty($decoded_input))) {\n\t\t\t\t\t\treturn $this->showStep($step, $extended, $decoded_input);\n\t\t\t\t\t}\n\t\t\t\t\treturn self::FAILURE;\n\t\t\t\t}\n\t\t\t\tif ($step == 3) {\n\t\t\t\t\t// do actual install with data from $this->formfielddata\n\t\t\t\t\t$core = new Core($this->formfielddata);\n\t\t\t\t\t$core->doInstall(false);\n\t\t\t\t\t$core->createUserdataConf();\n\t\t\t\t}\n\t\t\t\treturn $this->showStep(++$step, $extended, $decoded_input);\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\t$section = $inst->formfield['install']['sections']['step' . $step] ?? [];\n\t\t\t\t$this->io->section($section['title']);\n\t\t\t\t$this->io->note($this->cliTextFormat($section['description']));\n\t\t\t\t$cmdfield = $section['fields']['system'];\n\t\t\t\t$this->io->success([\n\t\t\t\t\t$cmdfield['label'],\n\t\t\t\t\t$cmdfield['value']\n\t\t\t\t]);\n\t\t\t\tif (!isset($decoded_input['manual_config']) || (bool)$decoded_input['manual_config'] === false) {\n\t\t\t\t\tif (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) {\n\t\t\t\t\t\tpassthru($cmdfield['value']);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\treturn $result;\n\t}\n\n\tprivate function printExampleFile(OutputInterface $output)\n\t{\n\t\t// show list of available distro's\n\t\t$distros = glob(dirname(__DIR__, 3) . '/lib/configfiles/*.xml');\n\t\t// read in all the distros\n\t\tforeach ($distros as $distribution) {\n\t\t\t// get configparser object\n\t\t\t$dist = new ConfigParser($distribution);\n\t\t\t// store in tmp array\n\t\t\t$supportedOS[str_replace(\".xml\", \"\", strtolower(basename($distribution)))] = $dist->getCompleteDistroName();\n\t\t}\n\t\t// sort by distribution name\n\t\tasort($supportedOS);\n\t\t$webserverBackend = [\n\t\t\t'php-fpm' => 'PHP-FPM',\n\t\t\t'fcgid' => 'FCGID',\n\t\t\t'mod_php' => 'mod_php (not recommended)',\n\t\t];\n\t\t$guessedDistribution = \"\";\n\t\t$guessedWebserver = \"\";\n\t\t$fields = include dirname(dirname(__DIR__)) . '/formfields/install/formfield.install.php';\n\t\t$json_output = [];\n\t\tforeach ($fields['install']['sections'] as $section => $section_fields) {\n\t\t\tforeach ($section_fields['fields'] as $name => $field) {\n\t\t\t\tif ($name == 'system' || $name == 'target_servername') {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif ($field['type'] == 'text' || $field['type'] == 'email') {\n\t\t\t\t\tif ($name == 'httpuser' || $name == 'httpgroup') {\n\t\t\t\t\t\t$fieldval = 'www-data';\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$fieldval = $field['value'] ?? \"\";\n\t\t\t\t\t}\n\t\t\t\t} elseif ($field['type'] == 'password') {\n\t\t\t\t\t$fieldval = '******';\n\t\t\t\t} elseif ($field['type'] == 'select') {\n\t\t\t\t\t$fieldval = implode(\"|\", array_keys($field['select_var']));\n\t\t\t\t} elseif ($field['type'] == 'checkbox') {\n\t\t\t\t\t$fieldval = \"1|0\";\n\t\t\t\t} else {\n\t\t\t\t\t$fieldval = \"?\";\n\t\t\t\t}\n\t\t\t\t$json_output[$name] = $fieldval;\n\t\t\t}\n\t\t}\n\t\t$output->writeln(json_encode($json_output, JSON_PRETTY_PRINT));\n\t}\n\n\tprivate function downloadFile($src, $dest)\n\t{\n\t\tset_time_limit(0);\n\t\t// This is the file where we save the information\n\t\t$fp = fopen($dest, 'w+');\n\t\t// Here is the file we are downloading, replace spaces with %20\n\t\t$ch = curl_init(str_replace(\" \", \"%20\", $src));\n\t\tcurl_setopt($ch, CURLOPT_TIMEOUT, 50);\n\t\tcurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);\n\t\t// write curl response to file\n\t\tcurl_setopt($ch, CURLOPT_FILE, $fp);\n\t\tcurl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\n\t\t// get curl response\n\t\tcurl_exec($ch);\n\t\tfclose($fp);\n\t}\n\n\tprivate function printSysInfo(OutputInterface $output)\n\t{\n\n\t\t$php_sapi = 'mod_php';\n\t\t$php_version = phpversion();\n\t\tif (Settings::Get('system.mod_fcgid') == '1') {\n\t\t\t$php_sapi = 'FCGID';\n\t\t\tif (Settings::Get('system.mod_fcgid_ownvhost') == '1') {\n\t\t\t\t$php_sapi .= ' (+ froxlor)';\n\t\t\t}\n\t\t} elseif (Settings::Get('phpfpm.enabled') == '1') {\n\t\t\t$php_sapi = 'PHP-FPM';\n\t\t\tif (Settings::Get('phpfpm.enabled_ownvhost') == '1') {\n\t\t\t\t$php_sapi .= ' (+ froxlor)';\n\t\t\t}\n\t\t}\n\n\t\t$kernel = 'unknown';\n\t\tif (function_exists('posix_uname')) {\n\t\t\t$kernel_nfo = posix_uname();\n\t\t\t$kernel = $kernel_nfo['release'] . ' (' . $kernel_nfo['machine'] . ')';\n\t\t}\n\n\t\t$ips = [];\n\t\t$ips_stmt = Database::query(\"SELECT CONCAT(`ip`, ' (', `port`, ')') as ipaddr FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` ORDER BY `id`\");\n\t\twhile ($ip = $ips_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t$ips[] = $ip['ipaddr'];\n\t\t}\n\n\t\t$table = new Table($output);\n\t\t$table\n\t\t\t->setHeaders([\n\t\t\t\t'Key', 'Value'\n\t\t\t])\n\t\t\t->setRows([\n\t\t\t\t['Froxlor', Froxlor::getVersionString()],\n\t\t\t\t['Update-channel', Settings::Get('system.update_channel')],\n\t\t\t\t['Hostname', Settings::Get('system.hostname')],\n\t\t\t\t['Install-dir', Froxlor::getInstallDir()],\n\t\t\t\t['PHP CLI', $php_version],\n\t\t\t\t['PHP SAPI', $php_sapi],\n\t\t\t\t['Webserver', Settings::Get('system.webserver')],\n\t\t\t\t['Kernel', $kernel],\n\t\t\t\t['Database', Database::getAttribute(\\PDO::ATTR_SERVER_VERSION)],\n\t\t\t\t['Distro config', Settings::Get('system.distribution')],\n\t\t\t\t['IP addresses', implode(\"\\n\", $ips)],\n\t\t\t]);\n\t\t$table->setStyle('box');\n\t\t$table->render();\n\t}\n\n\tprivate function cliTextFormat(string $text, string $nl_char = \"\\n\"): string\n\t{\n\t\t$text = str_replace(['<br>', '<br/>', '<br />'], [$nl_char, $nl_char, $nl_char], $text);\n\t\treturn strip_tags($text);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/MasterCron.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Exception;\nuse Froxlor\\Cron\\CronConfig;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse PDO;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\n\nfinal class MasterCron extends CliCommand\n{\n\tprivate $lockFile = null;\n\n\tprivate $cronLog = null;\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:cron');\n\t\t$this->setDescription('Regulary perform tasks created by froxlor');\n\t\t$this->addArgument('job', InputArgument::IS_ARRAY, 'Job(s) to run');\n\t\t$this->addOption('run-task', 'r', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Run a specific task [1 = re-generate configs, 4 = re-generate dns zones, 9 = re-generate rspamd configs, 10 = re-set quotas, 99 = re-create cron.d-file]')\n\t\t\t->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces given job or, if none given, forces re-generating of config-files (webserver, nameserver, etc.)')\n\t\t\t->addOption('debug', 'd', InputOption::VALUE_NONE, 'Output debug information about what is going on to STDOUT.')\n\t\t\t->addOption('no-fork', 'N', InputOption::VALUE_NONE, 'Do not fork to background (traffic cron only).');\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$result = $this->validateRequirements($output);\n\n\t\tif ($result != self::SUCCESS) {\n\t\t\t// requirements failed, exit\n\t\t\treturn $result;\n\t\t}\n\n\t\t$jobs = $input->getArgument('job');\n\n\t\t// handle force option\n\t\tif ($input->getOption('force')) {\n\t\t\tif (empty($jobs) || in_array('tasks', $jobs)) {\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_RSPAMD);\n\t\t\t\tCronjob::inserttask(TaskId::CREATE_QUOTA);\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_CRON);\n\t\t\t\tCronjob::inserttask(TaskId::UPDATE_LE_SERVICES);\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_NSSUSERS);\n\t\t\t\t$jobs[] = 'tasks';\n\t\t\t}\n\t\t\tdefine('CRON_IS_FORCED', 1);\n\t\t}\n\t\t// handle debug option\n\t\tif ($input->getOption('debug')) {\n\t\t\tdefine('CRON_DEBUG_FLAG', 1);\n\t\t}\n\t\t// handle no-fork option\n\t\tif ($input->getOption('no-fork')) {\n\t\t\tdefine('CRON_NOFORK_FLAG', 1);\n\t\t}\n\t\t// handle run-task option\n\t\tif ($input->getOption('run-task')) {\n\t\t\t$tasks_to_run = $input->getOption('run-task');\n\t\t\tforeach ($tasks_to_run as $ttr) {\n\t\t\t\tif (in_array($ttr, [TaskId::REBUILD_VHOST, TaskId::REBUILD_DNS, TaskId::REBUILD_RSPAMD, TaskId::CREATE_QUOTA, TaskId::REBUILD_CRON, TaskId::UPDATE_LE_SERVICES, TaskId::REBUILD_NSSUSERS])) {\n\t\t\t\t\tCronjob::inserttask($ttr);\n\t\t\t\t\t$jobs[] = 'tasks';\n\t\t\t\t} else {\n\t\t\t\t\t$output->writeln('<comment>Unknown task number \"' . $ttr . '\"</>');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// unique job-array\n\t\t$jobs = array_unique($jobs);\n\n\t\t// check for given job(s) to execute and return if empty\n\t\tif (empty($jobs)) {\n\t\t\t$output->writeln('<error>No job given. Nothing to do.</>');\n\t\t\treturn self::INVALID;\n\t\t}\n\n\t\t$this->validateOwnership($output);\n\n\t\t$this->cronLog = FroxlorLogger::getInstanceOf([\n\t\t\t'loginname' => 'cronjob'\n\t\t]);\n\t\t$this->cronLog->setCronDebugFlag(defined('CRON_DEBUG_FLAG'));\n\n\t\t// check whether there are actual tasks to perform by 'tasks'-cron, so\n\t\t// we don't regenerate files unnecessarily\n\t\t$tasks_cnt_stmt = Database::query(\"SELECT COUNT(*) as jobcnt FROM `panel_tasks`\");\n\t\t$tasks_cnt = $tasks_cnt_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t// iterate through all needed jobs\n\t\tforeach ($jobs as $job) {\n\t\t\t// lock the job\n\t\t\tif ($this->lockJob($job, $output)) {\n\t\t\t\t// get FQDN of cron-class\n\t\t\t\t$cronfile = $this->getCronModule($job, $output);\n\t\t\t\t// validate\n\t\t\t\tif ($cronfile && class_exists($cronfile)) {\n\t\t\t\t\t// info\n\t\t\t\t\t$output->writeln('<info>Running \"' . $job . '\" job' . (defined('CRON_IS_FORCED') ? ' (forced)' : '') . (defined('CRON_DEBUG_FLAG') ? ' (debug)' : '') . (defined('CRON_NOFORK_FLAG') ? ' (not forking)' : '') . '</>');\n\t\t\t\t\t// update time of last run\n\t\t\t\t\tCronjob::updateLastRunOfCron($job);\n\t\t\t\t\t// set logger\n\t\t\t\t\t$cronfile::setCronlog($this->cronLog);\n\t\t\t\t\t// run the job\n\t\t\t\t\t$cronfile::run();\n\t\t\t\t}\n\t\t\t\t// free the lockfile\n\t\t\t\t$this->unlockJob();\n\t\t\t}\n\t\t}\n\n\t\t// possible long-running jobs disconnect from the database\n\t\tSettings::refreshState();\n\n\t\t// we have to check the system's last guid with every cron run\n\t\t// in case the admin installed new software which added a new user\n\t\t//so users in the database don't conflict with system users\n\t\t$this->cronLog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Checking system\\'s last guid');\n\t\tCronjob::checkLastGuid();\n\t\t$this->cronLog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Checking system\\'s OS version');\n\t\tCronjob::checkCurrentDistro();\n\t\t// validate if we're on fcgid/php-fpm that the local froxlor user is in the http-group to access log files\n\t\tif ((int)Settings::Get('phpfpm.enabled') == 1 || (int)Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t$this->cronLog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Checking group membership of local user');\n\t\t\tCronjob::checkLocalUserGroupMembership();\n\t\t}\n\n\n\t\t// check for cron.d-generation task and create it if necessary\n\t\tCronConfig::checkCrondConfigurationFile();\n\n\t\t// check for old/compatibility cronjob file\n\t\tif (file_exists(Froxlor::getInstallDir() . '/scripts/froxlor_master_cronjob.php')) {\n\t\t\t@unlink(Froxlor::getInstallDir() . '/scripts/froxlor_master_cronjob.php');\n\t\t\t@rmdir(Froxlor::getInstallDir() . '/scripts');\n\t\t}\n\n\t\t// reset cronlog-flag if set to \"once\"\n\t\tif ((int)Settings::Get('logger.log_cron') == 1) {\n\t\t\tFroxlorLogger::getInstanceOf()->setCronLog(0);\n\t\t}\n\n\t\t// clean up possible old login-links and 2fa tokens\n\t\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_LOGINLINKS . \"` WHERE `valid_until` < UNIX_TIMESTAMP()\");\n\t\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_2FA_TOKENS . \"` WHERE `valid_until` < UNIX_TIMESTAMP()\");\n\n\t\treturn $result;\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function validateOwnership(OutputInterface $output)\n\t{\n\t\t// when using fcgid or fpm for froxlor-vhost itself, we have to check\n\t\t// whether the permission of the files are still correct\n\t\t$output->write('Checking froxlor file permissions...');\n\t\t$_mypath = FileDir::makeCorrectDir(Froxlor::getInstallDir());\n\n\t\tif (((int)Settings::Get('system.mod_fcgid') == 1 && (int)Settings::Get('system.mod_fcgid_ownvhost') == 1) || ((int)Settings::Get('phpfpm.enabled') == 1 && (int)Settings::Get('phpfpm.enabled_ownvhost') == 1)) {\n\t\t\t$user = Settings::Get('system.mod_fcgid_httpuser');\n\t\t\t$group = Settings::Get('system.mod_fcgid_httpgroup');\n\n\t\t\tif (Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$user = Settings::Get('phpfpm.vhost_httpuser');\n\t\t\t\t$group = Settings::Get('phpfpm.vhost_httpgroup');\n\t\t\t}\n\t\t\t// all the files and folders have to belong to the local user\n\t\t\tFileDir::safe_exec('chown -R ' . $user . ':' . $group . ' ' . escapeshellarg($_mypath));\n\t\t} else {\n\t\t\t// back to webserver permission\n\t\t\t$user = Settings::Get('system.httpuser');\n\t\t\t$group = Settings::Get('system.httpgroup');\n\t\t\tFileDir::safe_exec('chown -R ' . $user . ':' . $group . ' ' . escapeshellarg($_mypath));\n\t\t}\n\t\t$output->writeln('OK');\n\t}\n\n\tprivate function lockJob(string $job, OutputInterface $output): bool\n\t{\n\n\t\t$this->lockFile = '/run/lock/froxlor_' . $job . '.lock';\n\n\t\tif (file_exists($this->lockFile)) {\n\t\t\t$jobinfo = json_decode(file_get_contents($this->lockFile), true);\n\t\t\tif ($jobinfo === false || !is_array($jobinfo)) {\n\t\t\t\t// looks like an invalid lockfile\n\t\t\t\t$check_pid_return = 1;\n\t\t\t} else {\n\t\t\t\t$check_pid_return = null;\n\t\t\t\t// get status of process\n\t\t\t\tsystem(\"kill -CHLD \" . (int)$jobinfo['pid'] . \" 1> /dev/null 2> /dev/null\", $check_pid_return);\n\t\t\t}\n\t\t\tif ($check_pid_return == 1) {\n\t\t\t\t// Process does not seem to run, most likely it has died\n\t\t\t\t$this->unlockJob();\n\t\t\t} else {\n\t\t\t\t// cronjob still running, output info and stop\n\t\t\t\t$output->writeln([\n\t\t\t\t\t'<comment>Job \"' . $jobinfo['job'] . '\" is currently running.',\n\t\t\t\t\t'Started: ' . date('d.m.Y H:i', (int)$jobinfo['startts']),\n\t\t\t\t\t'PID: ' . $jobinfo['pid'] . '</>'\n\t\t\t\t]);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t$jobinfo = [\n\t\t\t'job' => $job,\n\t\t\t'startts' => time(),\n\t\t\t'pid' => getmypid()\n\t\t];\n\t\treturn file_put_contents($this->lockFile, json_encode($jobinfo)) !== false;\n\t}\n\n\tprivate function unlockJob(): bool\n\t{\n\t\treturn @unlink($this->lockFile);\n\t}\n\n\tprivate function getCronModule(string $cronname, OutputInterface $output)\n\t{\n\t\t$upd_stmt = Database::prepare(\"\n\t\t\tSELECT `cronclass` FROM `\" . TABLE_PANEL_CRONRUNS . \"` WHERE `cronfile` = :cron;\n\t\t\");\n\t\t$cron = Database::pexecute_first($upd_stmt, [\n\t\t\t'cron' => $cronname\n\t\t]);\n\t\tif ($cron) {\n\t\t\treturn $cron['cronclass'];\n\t\t}\n\t\t$output->writeln(\"<error>Requested cronjob '\" . $cronname . \"' could not be found.</>\");\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/PhpSessionclean.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Settings;\nuse PDO;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\n\nfinal class PhpSessionclean extends CliCommand\n{\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:php-sessionclean');\n\t\t$this->setDescription('Cleans old php-session files from tmp folder');\n\t\t$this->addArgument('max-lifetime', InputArgument::OPTIONAL, 'The number of seconds after which data will be seen as \"garbage\" and potentially cleaned up. Defaults to \"1440\"');\n\t}\n\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$result = $this->validateRequirements($output);\n\n\t\tif ($result == self::SUCCESS) {\n\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\tif ($input->hasArgument('max-lifetime') && is_numeric($input->getArgument('max-lifetime')) && $input->getArgument('max-lifetime') > 0) {\n\t\t\t\t\t$this->cleanSessionfiles((int)$input->getArgument('max-lifetime'));\n\t\t\t\t} else {\n\t\t\t\t\t// use default max-lifetime value\n\t\t\t\t\t$this->cleanSessionfiles();\n\t\t\t\t}\n\t\t\t\t$result = self::SUCCESS;\n\t\t\t} else {\n\t\t\t\t// php-fpm not enabled\n\t\t\t\t$output->writeln('<comment>PHP-FPM not enabled for this installation.</comment>');\n\t\t\t\t$result = self::INVALID;\n\t\t\t}\n\t\t}\n\t\treturn $result;\n\t}\n\n\tprivate function cleanSessionfiles(int $maxlifetime = 1440)\n\t{\n\t\t// store paths to clean up\n\t\t$paths_to_clean = [];\n\t\t// get all pool-config directories configured\n\t\t$sel_stmt = Database::prepare(\"SELECT DISTINCT `config_dir` FROM `\" . TABLE_PANEL_FPMDAEMONS . \"`\");\n\t\tDatabase::pexecute($sel_stmt);\n\t\twhile ($fpmd = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$poolfiles = glob(FileDir::makeCorrectFile($fpmd['config_dir'] . '/*.conf'));\n\t\t\tforeach ($poolfiles as $cf) {\n\t\t\t\t$contents = file_get_contents($cf);\n\t\t\t\t$pattern = preg_quote('session.save_path', '/');\n\t\t\t\t$pattern = \"/\" . $pattern . \".+?\\=(.*)/\";\n\t\t\t\tif (preg_match_all($pattern, $contents, $matches)) {\n\t\t\t\t\t$session_path = trim($matches[1][0]);\n\t\t\t\t\t// Skip non-file-based session storage (Redis, Memcached, etc.)\n\t\t\t\t\t// These typically contain protocol indicators like :// or are not valid directories\n\t\t\t\t\tif (strpos($session_path, '://') !== false) {\n\t\t\t\t\t\t// Skip paths with protocol indicators (tcp://, redis://, memcached://, etc.)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t$paths_to_clean[] = FileDir::makeCorrectDir(trim($matches[1][0]));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// every path is just needed once\n\t\t$paths_to_clean = array_unique($paths_to_clean);\n\n\t\tif (count($paths_to_clean) > 0) {\n\t\t\tforeach ($paths_to_clean as $ptc) {\n\t\t\t\t// find all files older than maxlifetime and delete them\n\t\t\t\tFileDir::safe_exec(\"find -O3 \\\"\" . $ptc . \"\\\" -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin \\\"+\" . $maxlifetime . \"\\\" -delete\");\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/RunApiCommand.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Exception;\nuse Froxlor\\Froxlor;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nfinal class RunApiCommand extends CliCommand\n{\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:api-call');\n\t\t$this->setDescription('Run an API command as given user');\n\t\t$this->addArgument('user', InputArgument::REQUIRED, 'Loginname of the user you want to run the command as')\n\t\t\t->addArgument('api-command', InputArgument::REQUIRED, 'The command to execute in the form \"Module.function\"')\n\t\t\t->addArgument('parameters', InputArgument::OPTIONAL, 'Parameters to pass to the command as JSON array');\n\t\t$this->addOption('show-params', 's', InputOption::VALUE_NONE, 'Show possible parameters for given api-command (given command will *not* be called)');\n\t}\n\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$result = $this->validateRequirements($output);\n\n\t\trequire Froxlor::getInstallDir() . '/lib/functions.php';\n\n\t\t// set error-handler\n\t\t@set_error_handler([\n\t\t\t'\\\\Froxlor\\\\Api\\\\Api',\n\t\t\t'phpErrHandler'\n\t\t]);\n\n\t\tif ($result == self::SUCCESS) {\n\t\t\ttry {\n\t\t\t\t$loginname = $input->getArgument('user');\n\t\t\t\t$userinfo = $this->getUserByName($loginname);\n\t\t\t\t$command = $input->getArgument('api-command');\n\t\t\t\t$apicmd = $this->validateCommand($command);\n\t\t\t\t$module = \"\\\\Froxlor\\\\Api\\\\Commands\\\\\" . $apicmd['class'];\n\t\t\t\t$function = $apicmd['function'];\n\t\t\t\tif ($input->getOption('show-params') !== false) {\n\t\t\t\t\t$json_result = \\Froxlor\\Api\\Commands\\Froxlor::getLocal($userinfo, ['module' => $apicmd['class'], 'function' => $function])->listFunctions();\n\t\t\t\t\t$io = new SymfonyStyle($input, $output);\n\t\t\t\t\t$result = $this->outputParamsList($json_result, $io);\n\t\t\t\t} else {\n\t\t\t\t\t$params_json = $input->getArgument('parameters');\n\t\t\t\t\t$params = json_decode($params_json ?? '', true);\n\t\t\t\t\t$json_result = $module::getLocal($userinfo, $params)->{$function}();\n\t\t\t\t\t$output->write($json_result);\n\t\t\t\t\t$result = self::SUCCESS;\n\t\t\t\t}\n\t\t\t} catch (Exception $e) {\n\t\t\t\t$output->writeln('<error>' . $e->getMessage() . '</>');\n\t\t\t\t$result = self::FAILURE;\n\t\t\t}\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\tprivate function outputParamsList(string $json, SymfonyStyle $io): int\n\t{\n\t\t$docs = json_decode($json, true);\n\t\t$docs = array_shift($docs['data']);\n\t\tif (!isset($docs['params'])) {\n\t\t\t$io->warning(($docs['head'] ?? \"unknown return\"));\n\t\t\treturn self::INVALID;\n\t\t}\n\t\tif (empty($docs['params'])) {\n\t\t\t$io->success(\"No parameters required\");\n\t\t} else {\n\t\t\t$rows = [];\n\t\t\tforeach ($docs['params'] as $param) {\n\t\t\t\t$rows[] = [$param['type'], '<options=bold>' . $param['parameter'] . '</>', $param['desc'] ?? \"\"];\n\t\t\t}\n\t\t\t$io->table(['Type', 'Name', 'Description'], $rows);\n\t\t}\n\t\treturn self::SUCCESS;\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function validateCommand(string $command): array\n\t{\n\t\t$command = explode(\".\", $command);\n\n\t\tif (count($command) != 2) {\n\t\t\tthrow new Exception(\"The given command is invalid.\");\n\t\t}\n\t\t// simply check for file-existance, as we do not want to use our autoloader because this way\n\t\t// it will recognize non-api classes+methods as valid commands\n\t\t$apiclass = '\\\\Froxlor\\\\Api\\\\Commands\\\\' . $command[0];\n\t\tif (!class_exists($apiclass) || !@method_exists($apiclass, $command[1])) {\n\t\t\tthrow new Exception(\"Unknown command\");\n\t\t}\n\t\treturn ['class' => $command[0], 'function' => $command[1]];\n\t}\n\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/SwitchServerIp.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Froxlor\\Database\\Database;\nuse PDO;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nfinal class SwitchServerIp extends CliCommand\n{\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:switch-server-ip');\n\t\t$this->setDescription('Easily switch IP addresses e.g. after server migration');\n\t\t$this->addOption('switch', 's', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Switch IP-address pair. A pair is separated by comma. For example: --switch=A,B')\n\t\t\t->addOption('list', 'l', InputOption::VALUE_NONE, 'List all IP addresses currently added for this server in froxlor');\n\t}\n\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$result = $this->validateRequirements($output);\n\n\t\tif ($result == self::SUCCESS && $input->getOption('list') == false && $input->getOption('switch') == false) {\n\t\t\t$output->writeln('<error>Either --list or --switch option must be provided. Nothing to do, exiting.</>');\n\t\t\t$result = self::INVALID;\n\t\t}\n\n\t\t$io = new SymfonyStyle($input, $output);\n\n\t\tif ($result == self::SUCCESS && $input->getOption('list')) {\n\t\t\t$sel_stmt = Database::prepare(\"SELECT * FROM panel_ipsandports ORDER BY ip ASC, port ASC\");\n\t\t\tDatabase::pexecute($sel_stmt);\n\t\t\t$ips = $sel_stmt->fetchAll(PDO::FETCH_ASSOC);\n\t\t\t$table_rows = [];\n\t\t\tforeach ($ips as $ipdata) {\n\t\t\t\t$table_rows[] = [$ipdata['id'], $ipdata['ip'], $ipdata['port']];\n\t\t\t}\n\n\t\t\t$io->table(\n\t\t\t\t['#', 'IP address', 'Port'],\n\t\t\t\t$table_rows\n\t\t\t);\n\t\t}\n\n\t\tif ($result == self::SUCCESS && $input->getOption('switch')) {\n\t\t\t$result = $this->switchIPs($input, $output);\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\tprivate function switchIPs(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$ip_list = $input->getOption('switch');\n\n\t\t$has_error = false;\n\t\t$ips_to_switch = [];\n\t\tforeach ($ip_list as $ips_combo) {\n\t\t\t$ip_pair = explode(\",\", $ips_combo);\n\t\t\tif (count($ip_pair) != 2) {\n\t\t\t\t$output->writeln('<error>Invalid option parameter, not a valid IP address pair.</>');\n\t\t\t\t$has_error = true;\n\t\t\t} else {\n\t\t\t\tif (filter_var($ip_pair[0], FILTER_VALIDATE_IP) == false) {\n\t\t\t\t\t$output->writeln('<error>Invalid source ip address: ' . $ip_pair[0] . '</>');\n\t\t\t\t\t$has_error = true;\n\t\t\t\t}\n\t\t\t\tif (filter_var($ip_pair[1], FILTER_VALIDATE_IP) == false) {\n\t\t\t\t\t$output->writeln('<error>Invalid target ip address: ' . $ip_pair[1] . '</>');\n\t\t\t\t\t$has_error = true;\n\t\t\t\t}\n\t\t\t\tif ($ip_pair[0] == $ip_pair[1] && !$has_error) {\n\t\t\t\t\t$output->writeln('<error>Source and target ip address are equal</>');\n\t\t\t\t\t$has_error = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$ips_to_switch[] = $ip_pair;\n\t\t}\n\t\tif ($has_error) {\n\t\t\treturn self::FAILURE;\n\t\t}\n\n\t\tif (count($ips_to_switch) > 0) {\n\t\t\t$check_stmt = Database::prepare(\"SELECT `id` FROM panel_ipsandports WHERE `ip` = :newip\");\n\t\t\t$upd_stmt = Database::prepare(\"UPDATE panel_ipsandports SET `ip` = :newip WHERE `ip` = :oldip\");\n\n\t\t\t// system.ipaddress\n\t\t\t$check_sysip_stmt = Database::prepare(\"SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'ipaddress'\");\n\t\t\t$check_sysip = Database::pexecute_first($check_sysip_stmt);\n\n\t\t\t// system.mysql_access_host\n\t\t\t$check_mysqlip_stmt = Database::prepare(\"SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'mysql_access_host'\");\n\t\t\t$check_mysqlip = Database::pexecute_first($check_mysqlip_stmt);\n\n\t\t\t// system.axfrservers\n\t\t\t$check_axfrip_stmt = Database::prepare(\"SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'axfrservers'\");\n\t\t\t$check_axfrip = Database::pexecute_first($check_axfrip_stmt);\n\n\t\t\tforeach ($ips_to_switch as $ip_pair) {\n\t\t\t\t$output->writeln('Switching IP <comment>' . $ip_pair[0] . '</> to IP <comment>' . $ip_pair[1] . '</>');\n\n\t\t\t\t$ip_check = Database::pexecute_first($check_stmt, [\n\t\t\t\t\t'newip' => $ip_pair[1]\n\t\t\t\t]);\n\t\t\t\tif ($ip_check) {\n\t\t\t\t\t$output->writeln('<error>Note: ' . $ip_pair[0] . ' not updated to ' . $ip_pair[1] . ' - IP already exists in froxlor\\'s database</>');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'newip' => $ip_pair[1],\n\t\t\t\t\t'oldip' => $ip_pair[0]\n\t\t\t\t]);\n\t\t\t\t$rows_updated = $upd_stmt->rowCount();\n\n\t\t\t\tif ($rows_updated == 0) {\n\t\t\t\t\t$output->writeln('<error>Note: ' . $ip_pair[0] . ' not updated to ' . $ip_pair[1] . ' (possibly no entry found in froxlor database. Use --list to see what IP addresses are added in froxlor');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// check whether the system.ipaddress needs updating\n\t\t\t\tif ($check_sysip['value'] == $ip_pair[0]) {\n\t\t\t\t\t$upd2_stmt = Database::prepare(\"UPDATE `panel_settings` SET `value` = :newip WHERE `settinggroup` = 'system' and `varname` = 'ipaddress'\");\n\t\t\t\t\tDatabase::pexecute($upd2_stmt, [\n\t\t\t\t\t\t'newip' => $ip_pair[1]\n\t\t\t\t\t]);\n\t\t\t\t\t$output->writeln('<info>Updated system-ipaddress from <comment>' . $ip_pair[0] . '</comment> to <comment>' . $ip_pair[1] . '</comment></info>');\n\t\t\t\t}\n\n\t\t\t\t// check whether the system.mysql_access_host needs updating\n\t\t\t\tif (strstr($check_mysqlip['value'], $ip_pair[0]) !== false) {\n\t\t\t\t\t$new_mysqlip = str_replace($ip_pair[0], $ip_pair[1], $check_mysqlip['value']);\n\t\t\t\t\t$upd2_stmt = Database::prepare(\"UPDATE `panel_settings` SET `value` = :newmysql WHERE `settinggroup` = 'system' and `varname` = 'mysql_access_host'\");\n\t\t\t\t\tDatabase::pexecute($upd2_stmt, [\n\t\t\t\t\t\t'newmysql' => $new_mysqlip\n\t\t\t\t\t]);\n\t\t\t\t\t$output->writeln('<info>Updated mysql_access_host from <comment>' . $check_mysqlip['value'] . '</comment> to <comment>' . $new_mysqlip . '</comment></info>');\n\t\t\t\t}\n\n\t\t\t\t// check whether the system.axfrservers needs updating\n\t\t\t\tif (strstr($check_axfrip['value'], $ip_pair[0]) !== false) {\n\t\t\t\t\t$new_axfrip = str_replace($ip_pair[0], $ip_pair[1], $check_axfrip['value']);\n\t\t\t\t\t$upd2_stmt = Database::prepare(\"UPDATE `panel_settings` SET `value` = :newaxfr WHERE `settinggroup` = 'system' and `varname` = 'axfrservers'\");\n\t\t\t\t\tDatabase::pexecute($upd2_stmt, [\n\t\t\t\t\t\t'newaxfr' => $new_axfrip\n\t\t\t\t\t]);\n\t\t\t\t\t$output->writeln('<info>Updated axfr-servers from <comment>' . $check_axfrip['value'] . '</comment> to <comment>' . $new_axfrip . '</comment></info>');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$output->writeln(\"\");\n\t\t$output->writeln(\"<comment>*** ATTENTION *** Remember to replace IP addresses in configuration files if used anywhere.</>\");\n\t\t$output->writeln(\"<info>IP addresses updated</>\");\n\t\treturn self::SUCCESS;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/UpdateCommand.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Install\\AutoUpdate;\nuse Froxlor\\Install\\Preconfig;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Mailer;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Question\\ChoiceQuestion;\nuse Symfony\\Component\\Console\\Question\\ConfirmationQuestion;\nuse Symfony\\Component\\Console\\Question\\Question;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nfinal class UpdateCommand extends CliCommand\n{\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:update');\n\t\t$this->setDescription('Check for newer version and update froxlor');\n\t\t$this->addOption('check-only', 'c', InputOption::VALUE_NONE, 'Only check for newer version and exit')\n\t\t\t->addOption('show-update-options', 'o', InputOption::VALUE_NONE, 'Show possible update option parameter for the update if any. Only usable in combination with \"check-only\".')\n\t\t\t->addOption('update-options', 'O', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Parameter list of update options.')\n\t\t\t->addOption('database', 'd', InputOption::VALUE_NONE, 'Only run database updates in case updates are done via apt or manually.')\n\t\t\t->addOption('mail-notify', 'm', InputOption::VALUE_NONE, 'Additionally inform administrator via email if a newer version was found')\n\t\t\t->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Do not ask for download, extract and database-update, just do it (if not --check-only is set)')\n\t\t\t->addOption('integer-return', 'i', InputOption::VALUE_NONE, 'Return integer whether a new version is available or not (implies --check-only). Useful for programmatic use.');\n\t}\n\n\tprotected function execute(InputInterface $input, OutputInterface $output)\n\t{\n\t\t$result = self::SUCCESS;\n\n\t\t// database update only\n\t\tif ($input->getOption('database')) {\n\t\t\t$result = $this->validateRequirements($output, true);\n\t\t\tif ($result == self::SUCCESS) {\n\t\t\t\trequire Froxlor::getInstallDir() . '/lib/functions.php';\n\t\t\t\tif (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {\n\t\t\t\t\t$output->writeln('<info>' . lng('update.dbupdate_required') . '</>');\n\t\t\t\t\tif ($input->getOption('check-only')) {\n\t\t\t\t\t\t$output->writeln('<comment>Doing nothing because of \"check-only\" flag.</>');\n\t\t\t\t\t\t$this->askUpdateOptions($input, $output, null, false);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$yestoall = $input->getOption('yes-to-all') !== false;\n\t\t\t\t\t\t$helper = $this->getHelper('question');\n\n\t\t\t\t\t\t$this->askUpdateOptions($input, $output, $helper, $yestoall);\n\n\t\t\t\t\t\t$question = new ConfirmationQuestion('Update database? [no] ', false, '/^(y|j)/i');\n\t\t\t\t\t\tif ($yestoall || $helper->ask($input, $output, $question)) {\n\t\t\t\t\t\t\t$result = $this->runUpdate($output, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn $result;\n\t\t\t\t}\n\t\t\t\t$output->writeln('<info>' . lng('update.noupdatesavail', [(Settings::Get('system.update_channel') == 'testing' ? lng('serversettings.uc_testing') . ' ' : '')]) . '</>');\n\t\t\t}\n\t\t\treturn $result;\n\t\t}\n\n\t\t$result = $this->validateRequirements($output);\n\n\t\tif ($result != self::SUCCESS) {\n\t\t\t// requirements failed, exit\n\t\t\treturn $result;\n\t\t}\n\n\t\trequire Froxlor::getInstallDir() . '/lib/functions.php';\n\n\t\t// version check\n\t\t$newversionavail = false;\n\t\tif ($result == self::SUCCESS) {\n\t\t\ttry {\n\t\t\t\t$aucheck = AutoUpdate::checkVersion();\n\n\t\t\t\tif ($aucheck == 1) {\n\t\t\t\t\t$this->mailNotify($input, $output);\n\t\t\t\t\tif ($input->getOption('integer-return')) {\n\t\t\t\t\t\t$output->write(1);\n\t\t\t\t\t\treturn self::SUCCESS;\n\t\t\t\t\t}\n\t\t\t\t\t// there is a new version\n\t\t\t\t\tif ($input->getOption('check-only')) {\n\t\t\t\t\t\t$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel') . ' ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$text = lng('admin.newerversionavailable') . ' ' . lng('admin.newerversiondetails', [AutoUpdate::getFromResult('version'), Froxlor::VERSION]);\n\t\t\t\t\t}\n\t\t\t\t\t$text = str_replace(\"<br/>\", \" \", $text);\n\t\t\t\t\t$text = str_replace(\"<b>\", \"<info>\", $text);\n\t\t\t\t\t$text = str_replace(\"</b>\", \"</info>\", $text);\n\t\t\t\t\t$newversionavail = true;\n\t\t\t\t\t$output->writeln('<comment>' . $text . '</>');\n\t\t\t\t\t$result = self::SUCCESS;\n\t\t\t\t} elseif ($aucheck < 0 || $aucheck > 1) {\n\t\t\t\t\tif ($input->getOption('integer-return')) {\n\t\t\t\t\t\t$output->write(-1);\n\t\t\t\t\t\treturn self::INVALID;\n\t\t\t\t\t}\n\t\t\t\t\t// errors\n\t\t\t\t\tif ($aucheck < 0) {\n\t\t\t\t\t\t$output->writeln('<error>' . AutoUpdate::getLastError() . '</>');\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$errmsg = 'error.autoupdate_' . $aucheck;\n\t\t\t\t\t\tif ($aucheck == 3) {\n\t\t\t\t\t\t\t$errmsg = 'error.customized_version';\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$output->writeln('<error>' . lng($errmsg) . '</>');\n\t\t\t\t\t}\n\t\t\t\t\t$result = self::INVALID;\n\t\t\t\t} else {\n\t\t\t\t\tif ($input->getOption('integer-return')) {\n\t\t\t\t\t\t$output->write(0);\n\t\t\t\t\t\treturn self::SUCCESS;\n\t\t\t\t\t}\n\t\t\t\t\t// no new version\n\t\t\t\t\t$output->writeln('<info>' . AutoUpdate::getFromResult('info') . '</>');\n\t\t\t\t\t$result = self::SUCCESS;\n\t\t\t\t}\n\t\t\t} catch (Exception $e) {\n\t\t\t\tif ($input->getOption('integer-return')) {\n\t\t\t\t\t$output->write(-1);\n\t\t\t\t\treturn self::FAILURE;\n\t\t\t\t}\n\t\t\t\t$output->writeln('<error>' . $e->getMessage() . '</>');\n\t\t\t\t$result = self::FAILURE;\n\t\t\t}\n\t\t}\n\n\t\t// if there's a newer version, proceed\n\t\tif ($result == self::SUCCESS && $newversionavail) {\n\n\t\t\t// check whether we only wanted to check\n\t\t\tif ($input->getOption('check-only')) {\n\t\t\t\t//$output->writeln('<comment>Not proceeding as \"check-only\" is specified</>');\n\t\t\t\t$this->askUpdateOptions($input, $output, null, false);\n\t\t\t\treturn $result;\n\t\t\t} else {\n\t\t\t\t$yestoall = $input->getOption('yes-to-all') !== false;\n\n\t\t\t\t$helper = $this->getHelper('question');\n\n\t\t\t\t// ask download\n\t\t\t\t$question = new ConfirmationQuestion('Download newer version? [no] ', false, '/^(y|j)/i');\n\t\t\t\tif ($yestoall || $helper->ask($input, $output, $question)) {\n\t\t\t\t\t// do download\n\t\t\t\t\t$output->writeln('Downloading...');\n\t\t\t\t\t$audl = AutoUpdate::downloadZip(AutoUpdate::getFromResult('version'));\n\t\t\t\t\tif (!is_numeric($audl)) {\n\t\t\t\t\t\t// ask extract\n\t\t\t\t\t\t$question = new ConfirmationQuestion('Extract downloaded archive? [no] ', false, '/^(y|j)/i');\n\t\t\t\t\t\tif ($yestoall || $helper->ask($input, $output, $question)) {\n\t\t\t\t\t\t\t// do extract\n\t\t\t\t\t\t\t$output->writeln('Extracting...');\n\t\t\t\t\t\t\t$auex = AutoUpdate::extractZip(Froxlor::getInstallDir() . '/updates/' . $audl);\n\t\t\t\t\t\t\tif ($auex == 0) {\n\t\t\t\t\t\t\t\t$output->writeln(\"<info>Froxlor files updated successfully.</>\");\n\t\t\t\t\t\t\t\t$result = self::SUCCESS;\n\n\t\t\t\t\t\t\t\t// restart fpm if used to clear opcache\n\t\t\t\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1 && Settings::Get('phpfpm.enabled_ownvhost') == '1') {\n\t\t\t\t\t\t\t\t\t// get fpm restart cmd\n\t\t\t\t\t\t\t\t\t$startstop_sel = Database::prepare(\"\n\t\t\t\t\t\t\t\t\t\tSELECT f.reload_cmd, f.config_dir\n\t\t\t\t\t\t\t\t\t\tFROM `\" . TABLE_PANEL_FPMDAEMONS . \"` f\n\t\t\t\t\t\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_PHPCONFIGS . \"` p ON p.fpmsettingid = f.id\n\t\t\t\t\t\t\t\t\t\tWHERE p.id = :phpconfigid\n\t\t\t\t\t\t\t\t\t\");\n\t\t\t\t\t\t\t\t\t$restart_cmd = Database::pexecute_first($startstop_sel, [\n\t\t\t\t\t\t\t\t\t\t'phpconfigid' => Settings::Get('phpfpm.vhost_defaultini')\n\t\t\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t\t\t// restart php-fpm instance\n\t\t\t\t\t\t\t\t\tFileDir::safe_exec(escapeshellcmd($restart_cmd['reload_cmd']));\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t$this->askUpdateOptions($input, $output, $helper, $yestoall);\n\n\t\t\t\t\t\t\t\t$question = new ConfirmationQuestion('Update database? [no] ', false, '/^(y|j)/i');\n\t\t\t\t\t\t\t\tif ($yestoall || $helper->ask($input, $output, $question)) {\n\t\t\t\t\t\t\t\t\t// run in separate process to ensure the use of newly unpacked files\n\t\t\t\t\t\t\t\t\tpassthru(Froxlor::getInstallDir() . '/bin/froxlor-cli froxlor:update -dA', $result);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$errmsg = 'error.autoupdate_' . $auex;\n\t\t\t\t\t\t\t\t$output->writeln('<error>' . lng($errmsg) . '</>');\n\t\t\t\t\t\t\t\t$result = self::FAILURE;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$errmsg = 'error.autoupdate_' . $audl;\n\t\t\t\t\t\t$output->writeln('<error>' . lng($errmsg) . '</>');\n\t\t\t\t\t\t$result = self::FAILURE;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * @param InputInterface $input\n\t * @param OutputInterface $output\n\t * @param $helper\n\t * @param bool $yestoall\n\t * @return void\n\t */\n\tprivate function askUpdateOptions(InputInterface $input, OutputInterface $output, $helper, bool $yestoall = false)\n\t{\n\t\t// check for preconfigs\n\t\t$preconfig = Preconfig::getPreConfig(true);\n\t\t$show_options_only = $input->getOption('show-update-options') !== false;\n\t\tif (!is_null($helper) && $show_options_only) {\n\t\t\t$output->writeln('<comment>Unsetting \"show-update-options\" due to not being called with \"check-only\".</>');\n\t\t\t$show_options_only = false;\n\t\t}\n\t\t$update_options = [];\n\t\t// set parameters\n\t\t$uOptions = $input->getOption('update-options');\n\t\tif (!empty($uOptions)) {\n\t\t\t$options_value = [];\n\t\t\tforeach ($uOptions as $givenOption) {\n\t\t\t\t$optVal = explode(\"=\", $givenOption);\n\t\t\t\tif (count($optVal) == 2) {\n\t\t\t\t\t$options_value[$optVal[0]] = $optVal[1];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!empty($preconfig)) {\n\t\t\tkrsort($preconfig);\n\t\t\tforeach ($preconfig as $section) {\n\t\t\t\tif (!$show_options_only) {\n\t\t\t\t\t$output->writeln(\"<info>Updater questions for \" . $section['title'] . \"</>\");\n\t\t\t\t}\n\t\t\t\tforeach ($section['fields'] as $update_field => $metainfo) {\n\t\t\t\t\tif (isset($options_value[$update_field])) {\n\t\t\t\t\t\t$output->writeln('Setting given parameter \"' . $update_field . '\" to \"' . $options_value[$update_field] . '\"');\n\t\t\t\t\t\t$_POST[$update_field] = $options_value[$update_field];\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t$default = null;\n\t\t\t\t\t$question_text = html_entity_decode(strip_tags($metainfo['label']), ENT_QUOTES | ENT_IGNORE, \"UTF-8\");\n\t\t\t\t\tif ($metainfo['type'] == 'checkbox') {\n\t\t\t\t\t\t$default = (int)$metainfo['checked'];\n\t\t\t\t\t\tif ($show_options_only) {\n\t\t\t\t\t\t\t$update_options[] = [\n\t\t\t\t\t\t\t\t'name' => $update_field,\n\t\t\t\t\t\t\t\t'question' => $question_text,\n\t\t\t\t\t\t\t\t'default' => $default,\n\t\t\t\t\t\t\t\t'choices' => '0: No' . PHP_EOL . '1: Yes' . PHP_EOL\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$question = new ConfirmationQuestion($question_text . ' [' . ($metainfo['checked'] ? 'yes' : 'no') . '] ', (bool)$metainfo['checked'], '/^(y|j)/i');\n\t\t\t\t\t\t}\n\t\t\t\t\t} elseif ($metainfo['type'] == 'select') {\n\t\t\t\t\t\t$default = $metainfo['selected'];\n\t\t\t\t\t\t$choices = \"\";\n\t\t\t\t\t\tforeach (array_values($metainfo['select_var'] ?? []) as $index => $choice) {\n\t\t\t\t\t\t\t$choices .= $index . ': ' . $choice . PHP_EOL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ($show_options_only) {\n\t\t\t\t\t\t\t$update_options[] = [\n\t\t\t\t\t\t\t\t'name' => $update_field,\n\t\t\t\t\t\t\t\t'question' => $question_text,\n\t\t\t\t\t\t\t\t'default' => !empty($default) ? $default : '-',\n\t\t\t\t\t\t\t\t'choices' => $choices\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$question = new ChoiceQuestion(\n\t\t\t\t\t\t\t\t$question_text,\n\t\t\t\t\t\t\t\tarray_values($metainfo['select_var'] ?? []),\n\t\t\t\t\t\t\t\t$metainfo['selected']\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t$question->setValidator(function ($answer) use ($metainfo): string {\n\t\t\t\t\t\t\t\t$key = array_keys($metainfo['select_var'])[(int)$answer] ?? false; // Find the key based on the selected value\n\t\t\t\t\t\t\t\tif ($key === false) {\n\t\t\t\t\t\t\t\t\tthrow new \\RuntimeException('Invalid selection.');\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn $key;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} elseif ($metainfo['type'] == 'text') {\n\t\t\t\t\t\t$default = $metainfo['value'] ?? '';\n\t\t\t\t\t\tif ($show_options_only) {\n\t\t\t\t\t\t\t$update_options[] = [\n\t\t\t\t\t\t\t\t'name' => $update_field,\n\t\t\t\t\t\t\t\t'question' => $question_text,\n\t\t\t\t\t\t\t\t'default' => $default,\n\t\t\t\t\t\t\t\t'choices' => PHP_EOL\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$question = new Question($question_text . (!empty($metainfo['value']) ? ' [' . $metainfo['value'] . ']' : ''), $default);\n\t\t\t\t\t\t\t$question->setValidator(function (string $answer) use ($metainfo): string {\n\t\t\t\t\t\t\t\tif (($metainfo['mandatory'] ?? false) && empty($answer)) {\n\t\t\t\t\t\t\t\t\tthrow new \\RuntimeException(\n\t\t\t\t\t\t\t\t\t\t'Answer cannot be empty'\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!empty($metainfo['pattern'] ?? \"\") && !preg_match(\"/\" . $metainfo['pattern'] . \"/\", $answer)) {\n\t\t\t\t\t\t\t\t\tthrow new \\RuntimeException('Answer does not seem to be in valid format');\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn $answer;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$output->writeln(\"<error>Unknown type \" . $metainfo['type'] . \"</error>\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!$show_options_only) {\n\t\t\t\t\t\tif ($yestoall) {\n\t\t\t\t\t\t\t$_POST[$update_field] = $default;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$_POST[$update_field] = $helper->ask($input, $output, $question);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($show_options_only) {\n\t\t\t\t$io = new SymfonyStyle($input, $output);\n\t\t\t\t$io->table(\n\t\t\t\t\t['Parameter', 'Description', 'Default', 'Choices'],\n\t\t\t\t\t$update_options\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate function mailNotify(InputInterface $input, OutputInterface $output)\n\t{\n\t\tif ($input->getOption('mail-notify')) {\n\t\t\t$last_check_version = Settings::Get('system.update_notify_last');\n\t\t\tif (Update::versionInUpdate($last_check_version, AutoUpdate::getFromResult('version'))) {\n\t\t\t\t$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel') . ' ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);\n\t\t\t\t$mail = new Mailer(true);\n\t\t\t\t$mail->Body = $text;\n\t\t\t\t$mail->Subject = \"[froxlor] \" . lng('update.notify_subject');\n\t\t\t\t$mail->AddAddress(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));\n\t\t\t\tif (!$mail->Send() && $input->getOption('integer-return') == null) {\n\t\t\t\t\t$output->writeln('<error>' . $mail->ErrorInfo . '</>');\n\t\t\t\t}\n\t\t\t\tSettings::Set('system.update_notify_last', AutoUpdate::getFromResult('version'));\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/UserCommand.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Exception;\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Froxlor;\nuse Froxlor\\System\\Crypt;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nfinal class UserCommand extends CliCommand\n{\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:user');\n\t\t$this->setDescription('Various user actions');\n\t\t$this->addArgument('user', InputArgument::REQUIRED, 'Loginname of the target user')\n\t\t\t->addArgument('admin', InputArgument::OPTIONAL, 'Loginname of the executing admin/reseller user', 'admin');\n\t\t$this->addOption('unlock', 'u', InputOption::VALUE_NONE, 'Unlock user after too many failed login attempts')\n\t\t\t->addOption('change-passwd', 'p', InputOption::VALUE_NONE, 'Set new password for given user')\n\t\t\t->addOption('show-info', 's', InputOption::VALUE_NONE, 'Output information details of given user');\n\t}\n\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$result = self::SUCCESS;\n\n\t\t$result = $this->validateRequirements($output);\n\n\t\trequire Froxlor::getInstallDir() . '/lib/functions.php';\n\n\t\t// set error-handler\n\t\t@set_error_handler([\n\t\t\t'\\\\Froxlor\\\\Api\\\\Api',\n\t\t\t'phpErrHandler'\n\t\t]);\n\n\t\tif ($result == self::SUCCESS) {\n\t\t\ttry {\n\t\t\t\t$adminname = $input->getArgument('admin');\n\t\t\t\t$admininfo = $this->getUserByName($adminname);\n\t\t\t\t$loginname = $input->getArgument('user');\n\t\t\t\t$userinfo = $this->getUserByName($loginname, false);\n\n\t\t\t\t$do_unlock = $input->getOption('unlock');\n\t\t\t\t$do_passwd = $input->getOption('change-passwd');\n\t\t\t\t$do_show = $input->getOption('show-info');\n\n\t\t\t\tif ($do_unlock === false && $do_passwd === false && $do_show === false) {\n\t\t\t\t\t$output->writeln('<error>No option given, nothing to do.</>');\n\t\t\t\t\t$result = self::INVALID;\n\t\t\t\t}\n\n\t\t\t\tif ($do_unlock !== false) {\n\t\t\t\t\t// unlock given user\n\t\t\t\t\tif ((int)$userinfo['adminsession'] == 1) {\n\t\t\t\t\t\tAdmins::getLocal($admininfo, ['loginname' => $loginname])->unlock();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tCustomers::getLocal($admininfo, ['loginname' => $loginname])->unlock();\n\t\t\t\t\t}\n\t\t\t\t\t$output->writeln('<info>User ' . $loginname . ' unlocked successfully</>');\n\t\t\t\t\t$result = self::SUCCESS;\n\t\t\t\t}\n\t\t\t\tif ($result == self::SUCCESS && $do_passwd !== false) {\n\t\t\t\t\t$io = new SymfonyStyle($input, $output);\n\t\t\t\t\t$passwd = $io->askHidden(\"Enter new password\", function ($value) {\n\t\t\t\t\t\tif (empty($value)) {\n\t\t\t\t\t\t\tthrow new \\RuntimeException('You must enter a value.');\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$value = Crypt::validatePassword($value, 'new password');\n\t\t\t\t\t\treturn $value;\n\t\t\t\t\t});\n\t\t\t\t\t$passwd2 = $io->askHidden(\"Confirm new password\", function ($value) use ($passwd) {\n\t\t\t\t\t\tif (empty($value)) {\n\t\t\t\t\t\t\tthrow new \\RuntimeException('You must enter a value.');\n\t\t\t\t\t\t} elseif ($value != $passwd) {\n\t\t\t\t\t\t\tthrow new \\RuntimeException('Passwords do not match');\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn $value;\n\t\t\t\t\t});\n\t\t\t\t\tif ((int)$userinfo['adminsession'] == 1) {\n\t\t\t\t\t\tAdmins::getLocal($admininfo, ['id' => $userinfo['adminid'], 'admin_password' => $passwd])->update();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tCustomers::getLocal($admininfo, ['id' => $userinfo['customerid'], 'new_customer_password' => $passwd])->update();\n\t\t\t\t\t}\n\t\t\t\t\t$output->writeln('<info>Changed password for ' . $loginname . '</>');\n\t\t\t\t\t$result = self::SUCCESS;\n\t\t\t\t}\n\t\t\t\tif ($result == self::SUCCESS && $do_show !== false) {\n\t\t\t\t\t$userinfo['password'] = '[hidden]';\n\t\t\t\t\t$userinfo['data_2fa'] = '[hidden]';\n\t\t\t\t\t$io = new SymfonyStyle($input, $output);\n\t\t\t\t\t$io->horizontalTable(\n\t\t\t\t\t\tarray_keys($userinfo),\n\t\t\t\t\t\t[array_values($userinfo)]\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (Exception $e) {\n\t\t\t\t$output->writeln('<error>' . $e->getMessage() . '</>');\n\t\t\t\t$result = self::FAILURE;\n\t\t\t}\n\t\t}\n\n\t\treturn $result;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/ValidateAcmeWebroot.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cli;\n\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse PDO;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Question\\ConfirmationQuestion;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nfinal class ValidateAcmeWebroot extends CliCommand\n{\n\n\tprotected function configure()\n\t{\n\t\t$this->setName('froxlor:validate-acme-webroot');\n\t\t$this->setDescription('Validates the Le_Webroot value is correct for froxlor managed domains with Let\\'s Encrypt certificate.');\n\t\t$this->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Do not ask for confirmation, update files if necessary');\n\t}\n\n\t/**\n\t * @throws \\Exception\n\t */\n\tprotected function execute(InputInterface $input, OutputInterface $output): int\n\t{\n\t\t$result = $this->validateRequirements($output, true);\n\n\t\t$io = new SymfonyStyle($input, $output);\n\n\t\tif ((int)Settings::Get('system.leenabled') == 0) {\n\t\t\t$io->info(\"Let's Encrypt not activated in froxlor settings.\");\n\t\t\t$result = self::INVALID;\n\t\t}\n\n\t\tif ($result == self::SUCCESS) {\n\t\t\t$yestoall = $input->getOption('yes-to-all') !== false;\n\t\t\t$helper = $this->getHelper('question');\n\t\t\t$count_changes = 0;\n\t\t\t// get all Let's Encrypt enabled domains\n\t\t\t$sel_stmt = Database::prepare(\"SELECT id, domain FROM panel_domains WHERE `letsencrypt` = '1' AND aliasdomain IS NULL ORDER BY id ASC\");\n\t\t\tDatabase::pexecute($sel_stmt);\n\t\t\t$domains = $sel_stmt->fetchAll(PDO::FETCH_ASSOC);\n\t\t\t// check for froxlor-vhost\n\t\t\tif (Settings::Get('system.le_froxlor_enabled') == '1') {\n\t\t\t\t$domains[] = [\n\t\t\t\t\t'id' => 0,\n\t\t\t\t\t'domain' => Settings::Get('system.hostname')\n\t\t\t\t];\n\t\t\t}\n\t\t\t$upd_stmt = Database::prepare(\"UPDATE domain_ssl_settings SET `validtodate`=NULL WHERE `domainid` = :did\");\n\t\t\t$acmesh_dir = dirname(Settings::Get('system.acmeshpath'));\n\t\t\t$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), \"/\");\n\t\t\t$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), \"/\");\n\n\t\t\tif ($acmesh_challenge_dir != $recommended) {\n\t\t\t\t$io->warning([\n\t\t\t\t\t\"ACME challenge docroot from settings differs from the current installation directory.\",\n\t\t\t\t\t\"Settings: '\" . $acmesh_challenge_dir . \"'\",\n\t\t\t\t\t\"Default/recommended value: '\" . $recommended . \"'\",\n\t\t\t\t]);\n\t\t\t\t$question = new ConfirmationQuestion('Fix ACME challenge docroot setting? [yes] ', true, '/^(y|j)/i');\n\t\t\t\tif ($yestoall || $helper->ask($input, $output, $question)) {\n\t\t\t\t\tSettings::Set('system.letsencryptchallengepath', $recommended);\n\t\t\t\t\t$former_value = $acmesh_challenge_dir;\n\t\t\t\t\t$acmesh_challenge_dir = $recommended;\n\t\t\t\t\t// need to update the corresponding acme-alias config-file\n\t\t\t\t\t$acme_alias_file = Settings::Get('system.letsencryptacmeconf');\n\t\t\t\t\t$sed_params = \"s@\" . $former_value . \"@\" . $acmesh_challenge_dir . \"@\";\n\t\t\t\t\tFileDir::safe_exec('sed -i -e \"' . $sed_params . '\" ' . escapeshellarg($acme_alias_file));\n\t\t\t\t\t$count_changes++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tforeach ($domains as $domain_arr) {\n\t\t\t\t$domain = $domain_arr['domain'];\n\t\t\t\t$acme_domain_conf = FileDir::makeCorrectFile($acmesh_dir . '/' . $domain . '/' . $domain . '.conf');\n\t\t\t\tif (file_exists($acme_domain_conf)) {\n\t\t\t\t\t$io->text(\"Getting info from \" . $acme_domain_conf);\n\t\t\t\t\t$conf_content = file_get_contents($acme_domain_conf);\n\t\t\t\t} else {\n\t\t\t\t\t$acme_domain_conf = FileDir::makeCorrectFile($acmesh_dir . '/' . $domain . '_ecc/' . $domain . '.conf');\n\t\t\t\t\tif (file_exists($acme_domain_conf)) {\n\t\t\t\t\t\t$io->text(\"Getting info from \" . $acme_domain_conf);\n\t\t\t\t\t\t$conf_content = file_get_contents($acme_domain_conf);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$io->info(\"No domain configuration file found in '\" . $acmesh_dir . \"'\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!empty($conf_content)) {\n\t\t\t\t\t$lines = explode(\"\\n\", $conf_content);\n\t\t\t\t\tforeach ($lines as $line) {\n\t\t\t\t\t\t$val_key = explode(\"=\", $line);\n\t\t\t\t\t\tif ($val_key[0] == 'Le_Webroot') {\n\t\t\t\t\t\t\t$domain_webroot = trim(trim($val_key[1], \"'\"), '\"');\n\t\t\t\t\t\t\tif ($domain_webroot != $acmesh_challenge_dir) {\n\t\t\t\t\t\t\t\t$io->warning(\"Domain '\" . $domain . \"' has old/wrong Le_Webroot setting: '\" . $domain_webroot . ' <> ' . $acmesh_challenge_dir . \"'\");\n\t\t\t\t\t\t\t\t$question = new ConfirmationQuestion('Fix Le_Webroot? [yes] ', true, '/^(y|j)/i');\n\t\t\t\t\t\t\t\tif ($yestoall || $helper->ask($input, $output, $question)) {\n\t\t\t\t\t\t\t\t\t$sed_params = \"s@Le_Webroot=.*@Le_Webroot='\" . $acmesh_challenge_dir . \"'@\";\n\t\t\t\t\t\t\t\t\tFileDir::safe_exec('sed -i -e \"' . $sed_params . '\" ' . escapeshellarg($acme_domain_conf));\n\t\t\t\t\t\t\t\t\tDatabase::pexecute($upd_stmt, ['did' => $domain_arr['id']]);\n\t\t\t\t\t\t\t\t\t$io->success(\"Correction of Le_Webroot successful\");\n\t\t\t\t\t\t\t\t\t$count_changes++;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$io->info(\"Domain '\" . $domain . \"' Le_Webroot value is correct\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ($count_changes > 0) {\n\t\t\t\tif (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {\n\t\t\t\t\t$io->info(\"Changes detected but froxlor has been updated. Inserting task to rebuild vhosts after update.\");\n\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t} else {\n\t\t\t\t\t$question = new ConfirmationQuestion('Changes detected. Force cronjob to refresh certificates? [yes] ', true, '/^(y|j)/i');\n\t\t\t\t\tif ($yestoall || $helper->ask($input, $output, $question)) {\n\t\t\t\t\t\tpassthru(FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -f -d');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$io->success(\"No changes necessary.\");\n\t\t\t}\n\t\t}\n\n\t\treturn $result;\n\t}\n\n}\n"
  },
  {
    "path": "lib/Froxlor/Cli/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cli/install.functions.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Language;\n\nfunction lng(string $identifier, array $arguments = [])\n{\n\treturn Language::getTranslation($identifier, $arguments);\n}\n\nfunction old(string $identifier, string $default = null, string $session = null)\n{\n\treturn $default;\n}\n"
  },
  {
    "path": "lib/Froxlor/Config/ConfigDaemon.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Config;\n\nuse Exception;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse SimpleXMLElement;\n\n/**\n * Class ConfigDaemon\n *\n * Parses a distributions XML - file and gives access to the configuration\n * Not to be used directly\n */\nclass ConfigDaemon\n{\n\n\t/**\n\t * Human - readable title of this service\n\t *\n\t * @var string\n\t */\n\tpublic $title;\n\t/**\n\t * Whether this is the default daemon of the service-category\n\t *\n\t * @var boolean\n\t */\n\tpublic $default;\n\t/**\n\t * Holding the commands for this daemon\n\t *\n\t * @var array\n\t */\n\tprivate $orders = [];\n\t/**\n\t * Store the parsed SimpleXMLElement for usage\n\t *\n\t * @var SimpleXMLElement\n\t */\n\tprivate $fullxml;\n\t/**\n\t * Memorize if we already parsed the XML\n\t *\n\t * @var bool\n\t */\n\tprivate $isparsed = false;\n\t/**\n\t * Sub - area of the full - XML only holding the daemon - data we are interested in\n\t *\n\t * @var SimpleXMLElement\n\t */\n\tprivate $daemon;\n\t/**\n\t * xpath leading to this daemon in the full XML\n\t *\n\t * @var string\n\t */\n\tprivate $xpath;\n\t/**\n\t * cache of sql-data if used\n\t */\n\tprivate $sqldata_cache = null;\n\n\tpublic function __construct($xml, $xpath)\n\t{\n\t\t$this->fullxml = $xml;\n\t\t$this->xpath = $xpath;\n\t\t$this->daemon = $this->fullxml->xpath($this->xpath);\n\t\tif (count($this->daemon) !== 1) {\n\t\t\tthrow new Exception('XPath \"' . $this->xpath . '\" didn\\'t return exactly one element');\n\t\t}\n\t\t$attributes = $this->daemon[0]->attributes();\n\t\tif ($attributes['title'] != '') {\n\t\t\t$this->title = $this->parseContent((string)$attributes['title']);\n\t\t}\n\t\tif (isset($attributes['default'])) {\n\t\t\t$this->default = ($attributes['default'] == true);\n\t\t}\n\t}\n\n\t/**\n\t * Replace placeholders with content\n\t *\n\t * @param string $content\n\t * @return string $content w/o placeholder\n\t */\n\tprivate function parseContent($content)\n\t{\n\t\t$content = preg_replace_callback('/\\{\\{(.*)\\}\\}/Ui', function ($matches) {\n\t\t\t$match = null;\n\t\t\tif (preg_match('/^settings\\.(.*)$/', $matches[1], $match)) {\n\t\t\t\treturn Settings::Get($match[1]);\n\t\t\t} elseif (preg_match('/^lng\\.(.*)(?:\\.(.*)(?:\\.(.*)))$/U', $matches[1], $match)) {\n\t\t\t\tif (isset($match[1]) && $match[1] != '' && isset($match[2]) && $match[2] != '' && isset($match[3]) && $match[3] != '') {\n\t\t\t\t\treturn lng($match[1] . '.' . $match[2] . '.' . $match[3]);\n\t\t\t\t} elseif (isset($match[1]) && $match[1] != '' && isset($match[2]) && $match[2] != '') {\n\t\t\t\t\treturn lng($match[1] . '.' . $match[2]);\n\t\t\t\t} elseif (isset($match[1]) && $match[1] != '') {\n\t\t\t\t\treturn lng($match[1]);\n\t\t\t\t}\n\t\t\t\treturn '';\n\t\t\t} elseif (preg_match('/^const\\.(.*)$/', $matches[1], $match)) {\n\t\t\t\treturn $this->returnDynamic($match[1]);\n\t\t\t} elseif (preg_match('/^sql\\.(.*)$/', $matches[1], $match)) {\n\t\t\t\tif (is_null($this->sqldata_cache)) {\n\t\t\t\t\t// read in sql-data (if exists)\n\t\t\t\t\tif (file_exists(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\")) {\n\t\t\t\t\t\t$sql = [];\n\t\t\t\t\t\t$sql_root = [];\n\t\t\t\t\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\t\t\t\t\t\tunset($sql_root);\n\t\t\t\t\t\t$this->sqldata_cache = $sql;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn isset($this->sqldata_cache[$match[1]]) ? $this->sqldata_cache[$match[1]] : '';\n\t\t\t}\n\t\t}, $content);\n\t\treturn $content;\n\t}\n\n\tprivate function returnDynamic($key = null)\n\t{\n\t\t$dynamics = [\n\t\t\t'install_dir' => Froxlor::getInstallDir()\n\t\t];\n\t\treturn $dynamics[$key] ?? '';\n\t}\n\n\t/**\n\t * Get config for this daemon\n\t *\n\t * The returned array will be an array of array, each sub-array looking like this:\n\t * array('type' => 'install|file|command', 'content' => '<TEXT>')\n\t * If the type is \"file\", an additional \"name\" - element will be added to the array\n\t * To configure a daemon, the steps in the array must be followed in order\n\t *\n\t * @return array\n\t */\n\tpublic function getConfig()\n\t{\n\t\t$this->parse();\n\t\treturn $this->orders;\n\t}\n\n\t/**\n\t * Parse the XML and populate $this->orders\n\t *\n\t * @return bool\n\t */\n\tprivate function parse()\n\t{\n\t\t// We only want to parse the stuff one time\n\t\tif ($this->isparsed == true) {\n\t\t\treturn true;\n\t\t}\n\n\t\t$preparsed = [];\n\t\t// First: let's push everything into an array and expand all includes\n\t\tforeach ($this->daemon[0]->children() as $order) {\n\t\t\tswitch ((string)$order->getName()) {\n\t\t\t\tcase \"install\":\n\t\t\t\tcase \"file\":\n\t\t\t\tcase \"command\":\n\t\t\t\t\t// Normal stuff, just add it to the preparsed - array\n\t\t\t\t\t$preparsed[] = $order;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"include\":\n\t\t\t\t\t// Includes, get the part we want via xpath\n\t\t\t\t\t$includes = $this->fullxml->xpath((string)$order);\n\t\t\t\t\tforeach ($includes[0] as $include) {\n\t\t\t\t\t\t// The \"include\" is also a child, so just skip it, would make a mess later\n\t\t\t\t\t\tif ((string)$include->getName() == 'include') {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$preparsed[] = $include;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t\t// The next 3 are groupings, <visibility> MUST come first in this to work properly\n\t\t\t\tcase \"commands\":\n\t\t\t\tcase \"files\":\n\t\t\t\tcase \"installs\":\n\t\t\t\t\t// Hold the results\n\t\t\t\t\t$visibility = 1;\n\t\t\t\t\tforeach ($order->children() as $child) {\n\t\t\t\t\t\tswitch ((string)$child->getName()) {\n\t\t\t\t\t\t\tcase \"visibility\":\n\t\t\t\t\t\t\t\t$visibility += $this->checkVisibility($child);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"install\":\n\t\t\t\t\t\t\tcase \"file\":\n\t\t\t\t\t\t\tcase \"command\":\n\t\t\t\t\t\t\t\tif ($visibility > 0) {\n\t\t\t\t\t\t\t\t\t$preparsed[] = $child;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"include\":\n\t\t\t\t\t\t\t\t// Includes, get the part we want via xpath\n\t\t\t\t\t\t\t\t$includes = $this->fullxml->xpath((string)$child);\n\t\t\t\t\t\t\t\tforeach ($includes[0] as $include) {\n\t\t\t\t\t\t\t\t\t// The \"include\" is also a child, so just skip it, would make a mess later\n\t\t\t\t\t\t\t\t\tif ((string)$include->getName() == 'include') {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t$preparsed[] = $include;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Go through every preparsed order and evaluate what should happen to it\n\t\tforeach ($preparsed as $order) {\n\t\t\t$parsedorder = $this->parseOrder($order);\n\t\t\t// We got an array (= valid order) and the array already has a type -> add to stack\n\t\t\tif (is_array($parsedorder) && array_key_exists('type', $parsedorder)) {\n\t\t\t\t$this->orders[] = $parsedorder;\n\t\t\t\t// We got an array, but no type, means we got multiple orders back, at them to the stack one at a time\n\t\t\t} elseif (is_array($parsedorder)) {\n\t\t\t\tforeach ($parsedorder as $neworder) {\n\t\t\t\t\t$this->orders[] = $neworder;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Switch flag to indicate we parsed our data\n\t\t$this->isparsed = true;\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if visibility should be changed\n\t *\n\t * @param SimpleXMLElement $order\n\t * @return int 0|-1\n\t */\n\tprivate function checkVisibility($order)\n\t{\n\t\t$attributes = [];\n\t\tforeach ($order->attributes() as $key => $value) {\n\t\t\t$attributes[(string)$key] = $this->parseContent(trim((string)$value));\n\t\t}\n\n\t\t$order = $this->parseContent(trim((string)$order));\n\t\tif (!array_key_exists('mode', $attributes)) {\n\t\t\tthrow new Exception('\"<visibility>' . $order . '</visibility>\" is missing mode');\n\t\t}\n\n\t\t$return = 0;\n\t\tswitch ($attributes['mode']) {\n\t\t\tcase \"isfile\":\n\t\t\t\tif (!is_file($order)) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"notisfile\":\n\t\t\t\tif (is_file($order)) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"isdir\":\n\t\t\t\tif (!is_dir($order)) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"notisdir\":\n\t\t\t\tif (is_dir($order)) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"false\":\n\t\t\t\tif ($order == true) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"true\":\n\t\t\t\tif ($order == false) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"notempty\":\n\t\t\t\tif ($order == \"\") {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"userexists\":\n\t\t\t\tif (posix_getpwuid($order) === false) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"groupexists\":\n\t\t\t\tif (posix_getgrgid($order) === false) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"usernotexists\":\n\t\t\t\tif (is_array(posix_getpwuid($order))) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"groupnotexists\":\n\t\t\t\tif (is_array(posix_getgrgid($order))) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"usernamenotexists\":\n\t\t\t\tif (is_array(posix_getpwnam($order))) {\n\t\t\t\t\t$return = -1;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"equals\":\n\t\t\t\t$return = (isset($attributes['value']) && $attributes['value'] == $order ? 0 : -1);\n\t\t\t\tbreak;\n\t\t}\n\t\treturn $return;\n\t}\n\n\t/**\n\t * Parse a single order and return it in a format for easier usage\n\t *\n\t * @param\n\t *            SimpleXMLElement object holding a single order from the distribution - XML\n\t * @return array|string\n\t */\n\tprivate function parseOrder($order)\n\t{\n\t\t$attributes = [];\n\t\tforeach ($order->attributes() as $key => $value) {\n\t\t\t$attributes[(string)$key] = (string)$value;\n\t\t}\n\n\t\t$parsedorder = '';\n\t\tswitch ((string)$order->getName()) {\n\t\t\tcase \"file\":\n\t\t\t\t$parsedorder = $this->parseFile($order, $attributes);\n\t\t\t\tbreak;\n\t\t\tcase \"command\":\n\t\t\t\t$parsedorder = $this->parseCommand($order, $attributes);\n\t\t\t\tbreak;\n\t\t\tcase \"install\":\n\t\t\t\t$parsedorder = $this->parseInstall($order, $attributes);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new Exception('Invalid order: ' . (string)$order->getName());\n\t\t}\n\n\t\treturn $parsedorder;\n\t}\n\n\t/**\n\t * Parse a file - order and return it in a format for easier usage\n\t *\n\t * @param\n\t *            SimpleXMLElement object holding a single file from the distribution - XML\n\t * @return array|string\n\t */\n\tprivate function parseFile($order, $attributes)\n\t{\n\t\t$visibility = 1;\n\t\t// No sub - elements, so the content can be returned directly\n\t\tif ($order->count() == 0) {\n\t\t\t$content = (string)$order;\n\t\t} else {\n\t\t\t// Hold the results\n\t\t\tforeach ($order->children() as $child) {\n\t\t\t\tswitch ((string)$child->getName()) {\n\t\t\t\t\tcase \"visibility\":\n\t\t\t\t\t\t$visibility += $this->checkVisibility($child);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"content\":\n\t\t\t\t\t\t$content = (string)$child;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$return = [];\n\t\t// Check if the original file should be backupped\n\t\t// @TODO: Maybe have a backup - location somewhere central?\n\t\t// @TODO: Use IO - class\n\t\tif (array_key_exists('backup', $attributes)) {\n\t\t\tif (array_key_exists('mode', $attributes) && $attributes['mode'] == 'append') {\n\t\t\t\t$cmd = 'cp';\n\t\t\t} else {\n\t\t\t\t$cmd = 'mv';\n\t\t\t}\n\t\t\t$return[] = [\n\t\t\t\t'type' => 'command',\n\t\t\t\t'content' => '[ -f ' . $this->parseContent($attributes['name']) . ' ] && ' . $cmd . ' \"' . $this->parseContent($attributes['name']) . '\" \"' . $this->parseContent($attributes['name']) . '.frx.bak\"',\n\t\t\t\t'execute' => \"pre\"\n\t\t\t];\n\t\t}\n\n\t\t// Now the content of the file can be written\n\t\tif (isset($attributes['mode'])) {\n\t\t\t$return[] = [\n\t\t\t\t'type' => 'file',\n\t\t\t\t'content' => $this->parseContent($content),\n\t\t\t\t'name' => $this->parseContent($attributes['name']),\n\t\t\t\t'mode' => $this->parseContent($attributes['mode'])\n\t\t\t];\n\t\t} else {\n\t\t\t$return[] = [\n\t\t\t\t'type' => 'file',\n\t\t\t\t'content' => $this->parseContent($content),\n\t\t\t\t'name' => $this->parseContent($attributes['name'])\n\t\t\t];\n\t\t}\n\n\t\t// Let's check if the mode of the file should be changed\n\t\tif (array_key_exists('chmod', $attributes)) {\n\t\t\t$return[] = [\n\t\t\t\t'type' => 'command',\n\t\t\t\t'content' => 'chmod ' . $attributes['chmod'] . ' \"' . $this->parseContent($attributes['name']) . '\"',\n\t\t\t\t'execute' => \"post\"\n\t\t\t];\n\t\t}\n\n\t\t// Let's check if the owner of the file should be changed\n\t\tif (array_key_exists('chown', $attributes)) {\n\t\t\t$return[] = [\n\t\t\t\t'type' => 'command',\n\t\t\t\t'content' => 'chown ' . $attributes['chown'] . ' \"' . $this->parseContent($attributes['name']) . '\"',\n\t\t\t\t'execute' => \"post\"\n\t\t\t];\n\t\t}\n\n\t\t// If we have more than 1 element, we want to group this stuff for easier processing later\n\t\tif (count($return) > 1) {\n\t\t\t$return = [\n\t\t\t\t'type' => 'file',\n\t\t\t\t'subcommands' => $return,\n\t\t\t\t'name' => $this->parseContent($attributes['name'])\n\t\t\t];\n\t\t}\n\n\t\tif ($visibility > 0) {\n\t\t\treturn $return;\n\t\t} else {\n\t\t\treturn '';\n\t\t}\n\t}\n\n\t/**\n\t * Parse a command - order and return it in a format for easier usage\n\t *\n\t * @param\n\t *            SimpleXMLElement object holding a single command from the distribution - XML\n\t * @return array|string\n\t */\n\tprivate function parseCommand($order, $attributes)\n\t{\n\t\t// No sub - elements, so the content can be returned directly\n\t\tif ($order->count() == 0) {\n\t\t\treturn [\n\t\t\t\t'type' => 'command',\n\t\t\t\t'content' => $this->parseContent(trim((string)$order))\n\t\t\t];\n\t\t}\n\n\t\t// Hold the results\n\t\t$visibility = 1;\n\t\t$content = '';\n\t\tforeach ($order->children() as $child) {\n\t\t\tswitch ((string)$child->getName()) {\n\t\t\t\tcase \"visibility\":\n\t\t\t\t\t$visibility += $this->checkVisibility($child);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"content\":\n\t\t\t\t\t$content = trim((string)$child);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ($visibility > 0) {\n\t\t\treturn [\n\t\t\t\t'type' => 'command',\n\t\t\t\t'content' => $this->parseContent($content)\n\t\t\t];\n\t\t} else {\n\t\t\treturn '';\n\t\t}\n\t}\n\n\t/**\n\t * Parse a install - order and return it in a format for easier usage\n\t *\n\t * @param\n\t *            SimpleXMLElement object holding a single install from the distribution - XML\n\t * @return array|string\n\t */\n\tprivate function parseInstall($order, $attributes)\n\t{\n\t\t// No sub - elements, so the content can be returned directly\n\t\tif ($order->count() == 0) {\n\t\t\treturn [\n\t\t\t\t'type' => 'install',\n\t\t\t\t'content' => $this->parseContent(trim((string)$order))\n\t\t\t];\n\t\t}\n\n\t\t// Hold the results\n\t\t$visibility = 1;\n\t\t$content = '';\n\t\tforeach ($order->children() as $child) {\n\t\t\tswitch ((string)$child->getName()) {\n\t\t\t\tcase \"visibility\":\n\t\t\t\t\t$visibility += $this->checkVisibility($child);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"content\":\n\t\t\t\t\t$content = trim((string)$child);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ($visibility > 0) {\n\t\t\treturn [\n\t\t\t\t'type' => 'install',\n\t\t\t\t'content' => $this->parseContent($content)\n\t\t\t];\n\t\t} else {\n\t\t\treturn '';\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Config/ConfigDisplay.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Config;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\n\nclass ConfigDisplay\n{\n\t/**\n\t * @var array\n\t */\n\tprivate static $replace_arr;\n\n\t/**\n\t * @var string\n\t */\n\tprivate static $editor;\n\n\t/**\n\t * @var string\n\t */\n\tprivate static $theme;\n\n\t/**\n\t * @param array $confarr\n\t * @param string $editor\n\t * @param string $theme\n\t */\n\tpublic static function fromConfigArr(array $confarr, string $editor, string $theme)\n\t{\n\t\tself::$editor = $editor;\n\t\tself::$theme = $theme;\n\n\t\t$customer_tmpdir = '/tmp/';\n\t\tif (Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_tmpdir') != '') {\n\t\t\t$customer_tmpdir = Settings::Get('system.mod_fcgid_tmpdir');\n\t\t} elseif (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.tmpdir') != '') {\n\t\t\t$customer_tmpdir = Settings::Get('phpfpm.tmpdir');\n\t\t}\n\n\t\t// try to convert namserver hosts to ip's\n\t\t$ns_ips = \"\";\n\t\t$known_ns_ips = [];\n\t\tif (Settings::Get('system.nameservers') != '') {\n\t\t\t$nameservers = explode(',', Settings::Get('system.nameservers'));\n\t\t\tforeach ($nameservers as $nameserver) {\n\t\t\t\t$nameserver = trim($nameserver);\n\t\t\t\t// DNS servers might be multi homed; allow transfer from all ip\n\t\t\t\t// addresses of the DNS server\n\t\t\t\t$nameserver_ips = PhpHelper::gethostbynamel6($nameserver);\n\t\t\t\t// append dot to hostname\n\t\t\t\tif (substr($nameserver, -1, 1) != '.') {\n\t\t\t\t\t$nameserver .= '.';\n\t\t\t\t}\n\t\t\t\t// ignore invalid responses\n\t\t\t\tif (!is_array($nameserver_ips)) {\n\t\t\t\t\t// act like \\Froxlor\\PhpHelper::gethostbynamel6() and return unmodified hostname on error\n\t\t\t\t\t$nameserver_ips = [\n\t\t\t\t\t\t$nameserver\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\t$known_ns_ips = array_merge($known_ns_ips, $nameserver_ips);\n\t\t\t\t}\n\t\t\t\tif (!empty($ns_ips)) {\n\t\t\t\t\t$ns_ips .= ',';\n\t\t\t\t}\n\t\t\t\t$ns_ips .= implode(\",\", $nameserver_ips);\n\t\t\t}\n\t\t}\n\n\t\t// AXFR server\n\t\tif (Settings::Get('system.axfrservers') != '') {\n\t\t\t$axfrservers = explode(',', Settings::Get('system.axfrservers'));\n\t\t\tforeach ($axfrservers as $axfrserver) {\n\t\t\t\tif (!in_array(trim($axfrserver), $known_ns_ips)) {\n\t\t\t\t\tif (!empty($ns_ips)) {\n\t\t\t\t\t\t$ns_ips .= ',';\n\t\t\t\t\t}\n\t\t\t\t\t$ns_ips .= trim($axfrserver);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tDatabase::needSqlData();\n\t\t$sql = Database::getSqlData();\n\n\t\tself::$replace_arr = [\n\t\t\t'<SQL_UNPRIVILEGED_USER>' => $sql['user'],\n\t\t\t'<SQL_UNPRIVILEGED_PASSWORD>' => 'FROXLOR_MYSQL_PASSWORD',\n\t\t\t'<SQL_DB>' => $sql['db'],\n\t\t\t'<SQL_HOST>' => $sql['host'],\n\t\t\t'<SQL_SOCKET>' => $sql['socket'] ?? null,\n\t\t\t'<SERVERNAME>' => Settings::Get('system.hostname'),\n\t\t\t'<SERVERIP>' => Settings::Get('system.ipaddress'),\n\t\t\t'<NAMESERVERS>' => Settings::Get('system.nameservers'),\n\t\t\t'<NAMESERVERS_IP>' => $ns_ips,\n\t\t\t'<VIRTUAL_MAILBOX_BASE>' => Settings::Get('system.vmail_homedir'),\n\t\t\t'<VIRTUAL_UID_MAPS>' => Settings::Get('system.vmail_uid'),\n\t\t\t'<VIRTUAL_GID_MAPS>' => Settings::Get('system.vmail_gid'),\n\t\t\t'<SSLPROTOCOLS>' => (Settings::Get('system.use_ssl') == '1') ? 'imaps pop3s' : '',\n\t\t\t'<CUSTOMER_TMP>' => FileDir::makeCorrectDir($customer_tmpdir),\n\t\t\t'<BASE_PATH>' => Froxlor::getInstallDir(),\n\t\t\t'<BIND_CONFIG_PATH>' => FileDir::makeCorrectDir(Settings::Get('system.bindconf_directory')),\n\t\t\t'<WEBSERVER_RELOAD_CMD>' => Settings::Get('system.apachereload_command'),\n\t\t\t'<CUSTOMER_LOGS>' => FileDir::makeCorrectDir(Settings::Get('system.logfiles_directory')),\n\t\t\t'<FPM_IPCDIR>' => FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')),\n\t\t\t'<WEBSERVER_GROUP>' => Settings::Get('system.httpgroup'),\n\t\t\t'<SSL_CERT_FILE>' => Settings::Get('system.ssl_cert_file'),\n\t\t\t'<SSL_KEY_FILE>' => Settings::Get('system.ssl_key_file'),\n\t\t\t'<ADMIN_MAIL>' => Settings::Get('panel.adminmail'),\n\t\t];\n\n\t\t$commands_pre = \"\";\n\t\t$commands_file = \"\";\n\t\t$commands_post = \"\";\n\n\t\t$lasttype = '';\n\t\t$commands = '';\n\n\t\t$configpage = \"\";\n\n\t\tforeach ($confarr as $_action) {\n\t\t\tif ($lasttype != '' && $lasttype != $_action['type']) {\n\t\t\t\t$commands = trim($commands);\n\t\t\t\t$numbrows = count(explode(\"\\n\", $commands));\n\t\t\t\t$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [\n\t\t\t\t\t'commands' => $commands,\n\t\t\t\t\t'numbrows' => $numbrows\n\t\t\t\t]);\n\t\t\t\t$lasttype = '';\n\t\t\t\t$commands = '';\n\t\t\t}\n\t\t\tswitch ($_action['type']) {\n\t\t\t\tcase \"install\":\n\t\t\t\t\t$commands .= strtr($_action['content'], self::$replace_arr) . \"\\n\";\n\t\t\t\t\t$lasttype = \"install\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"command\":\n\t\t\t\t\t$commands .= strtr($_action['content'], self::$replace_arr) . \"\\n\";\n\t\t\t\t\t$lasttype = \"command\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"file\":\n\t\t\t\t\tif (array_key_exists('content', $_action)) {\n\t\t\t\t\t\t$commands_file = self::getFileContentContainer($_action['content'], $_action['name']);\n\t\t\t\t\t} elseif (array_key_exists('subcommands', $_action)) {\n\t\t\t\t\t\tforeach ($_action['subcommands'] as $fileaction) {\n\t\t\t\t\t\t\tif (array_key_exists('execute', $fileaction) && $fileaction['execute'] == \"pre\") {\n\t\t\t\t\t\t\t\t$commands_pre .= $fileaction['content'] . \"\\n\";\n\t\t\t\t\t\t\t} elseif (array_key_exists('execute', $fileaction) && $fileaction['execute'] == \"post\") {\n\t\t\t\t\t\t\t\t$commands_post .= $fileaction['content'] . \"\\n\";\n\t\t\t\t\t\t\t} elseif ($fileaction['type'] == 'file') {\n\t\t\t\t\t\t\t\t$commands_file = self::getFileContentContainer($fileaction['content'], $_action['name']);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$realname = $_action['name'];\n\t\t\t\t\t$commands = trim($commands_pre);\n\t\t\t\t\tif ($commands != \"\") {\n\t\t\t\t\t\t$numbrows = count(explode(\"\\n\", $commands));\n\t\t\t\t\t\t$commands_pre = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [\n\t\t\t\t\t\t\t'commands' => $commands,\n\t\t\t\t\t\t\t'numbrows' => $numbrows\n\t\t\t\t\t\t]);\n\t\t\t\t\t}\n\t\t\t\t\t$commands = trim($commands_post);\n\t\t\t\t\tif ($commands != \"\") {\n\t\t\t\t\t\t$numbrows = count(explode(\"\\n\", $commands));\n\t\t\t\t\t\t$commands_post = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [\n\t\t\t\t\t\t\t'commands' => $commands,\n\t\t\t\t\t\t\t'numbrows' => $numbrows\n\t\t\t\t\t\t]);\n\t\t\t\t\t}\n\t\t\t\t\t$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/fileblock.html.twig', self::$theme), [\n\t\t\t\t\t\t'realname' => $realname,\n\t\t\t\t\t\t'commands_pre' => $commands_pre,\n\t\t\t\t\t\t'commands_file' => $commands_file,\n\t\t\t\t\t\t'commands_post' => $commands_post\n\t\t\t\t\t]);\n\t\t\t\t\t$commands = '';\n\t\t\t\t\t$commands_pre = '';\n\t\t\t\t\t$commands_post = '';\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$commands = trim($commands);\n\t\tif ($commands != '') {\n\t\t\t$numbrows = count(explode(\"\\n\", $commands));\n\t\t\t$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [\n\t\t\t\t'commands' => $commands,\n\t\t\t\t'numbrows' => $numbrows\n\t\t\t]);\n\t\t}\n\t\treturn $configpage;\n\t}\n\n\t/**\n\t * @param string $file_content\n\t * @param string $realname\n\t *\n\t * @return string\n\t */\n\tprivate static function getFileContentContainer(string $file_content, string $realname): string\n\t{\n\t\t$files = \"\";\n\t\t$file_content = trim($file_content);\n\t\tif ($file_content != '') {\n\t\t\t$file_content = strtr($file_content, self::$replace_arr);\n\t\t\t$file_content = htmlspecialchars($file_content);\n\t\t\t$numbrows = count(explode(\"\\n\", $file_content));\n\t\t\t//eval(\"\\$files=\\\"\" . \\Froxlor\\UI\\Template::getTemplate(\"configfiles/configfiles_file\") . \"\\\";\");\n\t\t\t$files = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/file.html.twig', self::$theme), [\n\t\t\t\t'distro_editor' => self::$editor,\n\t\t\t\t'realname' => $realname,\n\t\t\t\t'numbrows' => $numbrows,\n\t\t\t\t'file_content' => $file_content\n\t\t\t]);\n\t\t}\n\t\treturn $files;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Config/ConfigParser.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Config;\n\nuse Exception;\nuse SimpleXMLElement;\n\n/**\n * Class ConfigParser\n *\n * Parses a distributions XML - file and gives access to the configuration\n */\nclass ConfigParser\n{\n\n\t/**\n\t * Name of the distribution this configuration is for\n\t *\n\t * @var string\n\t */\n\tpublic $distributionName = '';\n\t/**\n\t * Codename of the distribution this configuration is for\n\t *\n\t * @var string\n\t */\n\tpublic $distributionCodename = '';\n\t/**\n\t * Version of the distribution this configuration is for\n\t *\n\t * @var string\n\t */\n\tpublic $distributionVersion = '';\n\t/**\n\t * Recommended editor\n\t *\n\t * @var string\n\t */\n\tpublic $distributionEditor = '/bin/nano';\n\t/**\n\t * Show if this configuration is deprecated\n\t *\n\t * @var bool\n\t */\n\tpublic $deprecated = false;\n\t/**\n\t * Holding the available services in the XML\n\t *\n\t * @var array\n\t */\n\tprivate $services = [];\n\t/**\n\t * Holding the available defaults in the XML\n\t *\n\t * @var array\n\t */\n\tprivate $defaults = [];\n\t/**\n\t * Store the parsed SimpleXMLElement for usage\n\t *\n\t * @var SimpleXMLElement\n\t */\n\tprivate $xml;\n\t/**\n\t * Memorize if we already parsed the XML\n\t *\n\t * @var bool\n\t */\n\tprivate $isparsed = false;\n\n\t/**\n\t * Constructor\n\t *\n\t * Initialize the XML - ConfigParser\n\t *\n\t * @param string $filename\n\t *            filename of the froxlor - configurationfile\n\t * @return void\n\t */\n\tpublic function __construct($filename)\n\t{\n\t\tif (!is_readable($filename)) {\n\t\t\tthrow new Exception('File not readable');\n\t\t}\n\n\t\t$this->xml = simplexml_load_file($filename);\n\t\tif ($this->xml === false) {\n\t\t\t$error = '';\n\t\t\tforeach (libxml_get_errors() as $error) {\n\t\t\t\t$error .= \"\\t\" . $error->message;\n\t\t\t}\n\t\t\tthrow new Exception($error);\n\t\t}\n\n\t\t// Let's see if we can find a <distribution> block in the XML\n\t\t$distribution = $this->xml->xpath('//distribution');\n\n\t\t// No distribution found - can't use this file\n\t\tif (!is_array($distribution)) {\n\t\t\tthrow new Exception('Invalid XML, no distribution found');\n\t\t}\n\n\t\t// Search for attributes we understand\n\t\tforeach ($distribution[0]->attributes() as $key => $value) {\n\t\t\tswitch ((string)$key) {\n\t\t\t\tcase \"name\":\n\t\t\t\t\t$this->distributionName = (string)$value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"version\":\n\t\t\t\t\t$this->distributionVersion = (string)$value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"codename\":\n\t\t\t\t\t$this->distributionCodename = (string)$value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"defaulteditor\":\n\t\t\t\t\t$this->distributionEditor = (string)$value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"deprecated\":\n\t\t\t\t\t(string)$value == 'true' ? $this->deprecated = true : $this->deprecated = false;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Return all services defined by the XML\n\t *\n\t * The array will hold ConfigService - Objects for further handling\n\t *\n\t * @return array\n\t */\n\tpublic function getServices()\n\t{\n\t\t// Let's parse this shit(!)\n\t\t$this->parseServices();\n\n\t\t// Return our carefully searched for services\n\t\treturn $this->services;\n\t}\n\n\t/**\n\t * Parse the XML and populate $this->services\n\t *\n\t * @return bool\n\t */\n\tprivate function parseServices()\n\t{\n\t\t// We only want to parse the stuff one time\n\t\tif ($this->isparsed == true) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Get all services\n\t\t$services = $this->xml->xpath('//services/service');\n\t\tforeach ($services as $service) {\n\t\t\t// We don't want comments\n\t\t\tif ($service->getName() == 'comment') {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Search the attributes for \"type\"\n\t\t\tforeach ($service->attributes() as $key => $value) {\n\t\t\t\tif ($key == 'type') {\n\t\t\t\t\t$this->services[(string)$value] = new ConfigService($this->xml, '//services/service[@type=\"' . (string)$value . '\"]');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Switch flag to indicate we parsed our data\n\t\t$this->isparsed = true;\n\t\treturn true;\n\t}\n\n\t/**\n\t * Return all defaults defined by the XML\n\t *\n\t * The array will hold ConfigDefaults - Objects for further handling\n\t *\n\t * @return array\n\t */\n\tpublic function getDefaults()\n\t{\n\t\t// Let's parse this shit(!)\n\t\t$this->parseDefaults();\n\n\t\t// Return our carefully searched for defaults\n\t\treturn $this->defaults;\n\t}\n\n\t/**\n\t * Parse the XML and populate $this->services\n\t *\n\t * @return bool\n\t */\n\tprivate function parseDefaults()\n\t{\n\t\t// We only want to parse the stuff one time\n\t\tif ($this->isparsed == true) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Get all defaults\n\t\t$defaults = $this->xml->xpath('//defaults/default');\n\t\tforeach ($defaults as $default) {\n\t\t\t$this->defaults[] = $default;\n\t\t}\n\n\t\t// Switch flag to indicate we parsed our data\n\t\t$this->isparsed = true;\n\t\treturn true;\n\t}\n\n\t/**\n\t * return complete distribution string \"Name [codename] [ (version)] [deprecated]\n\t *\n\t * @return string\n\t */\n\tpublic function getCompleteDistroName(): string\n\t{\n\t\t// get distro-info\n\t\t$dist_display = $this->distributionName;\n\t\tif ($this->distributionCodename != '') {\n\t\t\t$dist_display .= \" \" . $this->distributionCodename;\n\t\t}\n\t\tif ($this->distributionVersion != '') {\n\t\t\t$dist_display .= \" (\" . $this->distributionVersion . \")\";\n\t\t}\n\t\tif ($this->deprecated) {\n\t\t\t$dist_display .= \" [deprecated]\";\n\t\t}\n\t\treturn $dist_display;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Config/ConfigService.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Config;\n\nuse Exception;\nuse Froxlor\\Settings;\nuse SimpleXMLElement;\n\n/**\n * Class ConfigService\n *\n * Parses a distributions XML - file and gives access to the services within\n * Not to be used directly\n */\nclass ConfigService\n{\n\n\t/**\n\t * Human - readable title of this service\n\t *\n\t * @var string\n\t */\n\tpublic $title;\n\t/**\n\t * Holding the available daemons in this service\n\t *\n\t * @var array\n\t */\n\tprivate $daemons = [];\n\t/**\n\t * Store the parsed SimpleXMLElement for usage\n\t *\n\t * @var SimpleXMLElement\n\t */\n\tprivate $fullxml;\n\t/**\n\t * Memorize if we already parsed the XML\n\t *\n\t * @var bool\n\t */\n\tprivate $isparsed = false;\n\t/**\n\t * xpath leading to this service in the full XML\n\t *\n\t * @var string\n\t */\n\tprivate $xpath;\n\n\tpublic function __construct($xml, $xpath)\n\t{\n\t\t$this->fullxml = $xml;\n\t\t$this->xpath = $xpath;\n\t\t$service = $this->fullxml->xpath($this->xpath);\n\t\t$attributes = $service[0]->attributes();\n\t\tif ($attributes['title'] != '') {\n\t\t\t$this->title = $this->parseContent((string)$attributes['title']);\n\t\t}\n\t}\n\n\t/**\n\t * Replace placeholders with content\n\t *\n\t * @param string $content\n\t * @return string $content w/o placeholder\n\t */\n\tprivate function parseContent($content)\n\t{\n\t\t$content = preg_replace_callback('/\\{\\{(.*)\\}\\}/Ui', function ($matches) {\n\t\t\t$match = null;\n\t\t\tif (preg_match('/^settings\\.(.*)$/', $matches[1], $match)) {\n\t\t\t\treturn Settings::Get($match[1]);\n\t\t\t} elseif (preg_match('/^lng\\.(.*)(?:\\.(.*)(?:\\.(.*)))$/U', $matches[1], $match)) {\n\t\t\t\tif (isset($match[1]) && $match[1] != '' && isset($match[2]) && $match[2] != '' && isset($match[3]) && $match[3] != '') {\n\t\t\t\t\treturn lng($match[1] . '.' . $match[2] . '.' . $match[3]);\n\t\t\t\t} elseif (isset($match[1]) && $match[1] != '' && isset($match[2]) && $match[2] != '') {\n\t\t\t\t\treturn lng($match[1] . '.' . $match[2]);\n\t\t\t\t} elseif (isset($match[1]) && $match[1] != '') {\n\t\t\t\t\treturn lng($match[1]);\n\t\t\t\t}\n\t\t\t\treturn '';\n\t\t\t}\n\t\t}, $content);\n\t\treturn $content;\n\t}\n\n\tpublic function getDaemons()\n\t{\n\t\t$this->parse();\n\t\treturn $this->daemons;\n\t}\n\n\t/**\n\t * Parse the XML and populate $this->daemons\n\t *\n\t * @return bool\n\t */\n\tprivate function parse()\n\t{\n\t\t// We only want to parse the stuff one time\n\t\tif ($this->isparsed == true) {\n\t\t\treturn true;\n\t\t}\n\n\t\t$daemons = $this->fullxml->xpath($this->xpath . '/daemon');\n\t\tforeach ($daemons as $daemon) {\n\t\t\tif ($daemon->getName() == 'comment') {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$name = '';\n\t\t\t$nametag = '';\n\t\t\t$versiontag = '';\n\t\t\tforeach ($daemon->attributes() as $key => $value) {\n\t\t\t\tif ($key == 'name' && $name == '') {\n\t\t\t\t\t$name = (string)$value;\n\t\t\t\t\t$nametag = \"[@name='\" . $value . \"']\";\n\t\t\t\t} elseif ($key == 'name' && $name != '') {\n\t\t\t\t\t$name = (string)$value . '_' . $name;\n\t\t\t\t\t$nametag = \"[@name='\" . $value . \"']\";\n\t\t\t\t} elseif ($key == 'version' && $name == '') {\n\t\t\t\t\t$name = str_replace('.', '', $value);\n\t\t\t\t\t$versiontag = \"[@version='\" . $value . \"']\";\n\t\t\t\t} elseif ($key == 'version' && $name != '') {\n\t\t\t\t\t$name .= str_replace('.', '', $value);\n\t\t\t\t\t$versiontag = \"[@version='\" . $value . \"']\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ($name == '') {\n\t\t\t\tthrow new Exception('No name attribute for daemon');\n\t\t\t}\n\t\t\t$this->daemons[$name] = new ConfigDaemon($this->fullxml, $this->xpath . \"/daemon\" . $nametag . $versiontag);\n\t\t}\n\n\t\t// Switch flag to indicate we parsed our data\n\t\t$this->isparsed = true;\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Config/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cron/CronConfig.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass CronConfig\n{\n\n\t/**\n\t * 1st: check for task of generation\n\t * 2nd: if task found, generate cron.d-file\n\t * 3rd: maybe restart cron?\n\t */\n\tpublic static function checkCrondConfigurationFile()\n\t{\n\t\t// check for task\n\t\tDatabase::query(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` = '99'\n\t\t\");\n\t\t$num_results = Database::num_rows();\n\n\t\t// is there a task for re-generating the cron.d-file?\n\t\tif ($num_results > 0) {\n\t\t\t// get all crons and their intervals\n\t\t\tif (FileDir::isFreeBSD()) {\n\t\t\t\t// FreeBSD does not need a header as we are writing directly to the crontab\n\t\t\t\t$cronfile = \"\\n\";\n\t\t\t} else {\n\t\t\t\t$cronfile = \"# automatically generated cron-configuration by froxlor\\n\";\n\t\t\t\t$cronfile .= \"# do not manually edit this file as it will be re-generated periodically.\\n\";\n\t\t\t\t$cronfile .= \"PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\\n#\\n\";\n\t\t\t}\n\n\t\t\t// get all the crons\n\t\t\t$result_stmt = Database::query(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_CRONRUNS . \"` WHERE `isactive` = '1'\n\t\t\t\");\n\n\t\t\t$binpath = Settings::Get(\"system.croncmdline\");\n\t\t\t// fallback as it is important\n\t\t\tif ($binpath === null) {\n\t\t\t\t$binpath = \"/usr/bin/nice -n 5 /usr/bin/php -q\";\n\t\t\t}\n\n\t\t\t$hour_delay = 0;\n\t\t\t$day_delay = 5;\n\t\t\t$month_delay = 7;\n\t\t\twhile ($row_cronentry = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t// create cron.d-entry\n\t\t\t\t$matches = [];\n\t\t\t\tif (preg_match(\"/(\\d+) (MINUTE|HOUR|DAY|WEEK|MONTH)/\", $row_cronentry['interval'], $matches)) {\n\t\t\t\t\tif ($matches[1] == 1) {\n\t\t\t\t\t\t$minvalue = \"*\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$minvalue = \"*/\" . $matches[1];\n\t\t\t\t\t}\n\t\t\t\t\tswitch ($matches[2]) {\n\t\t\t\t\t\tcase \"MINUTE\":\n\t\t\t\t\t\t\t$cronfile .= $minvalue . \" * * * * \";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"HOUR\":\n\t\t\t\t\t\t\t$cronfile .= $hour_delay . \" \" . $minvalue . \" * * * \";\n\t\t\t\t\t\t\t$hour_delay += 3;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"DAY\":\n\t\t\t\t\t\t\tif ($row_cronentry['cronfile'] == 'traffic') {\n\t\t\t\t\t\t\t\t// traffic at exactly 0:00 o'clock\n\t\t\t\t\t\t\t\t$cronfile .= \"0 0 \" . $minvalue . \" * * \";\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$cronfile .= $day_delay . \" 0 \" . $minvalue . \" * * \";\n\t\t\t\t\t\t\t\t$day_delay += 5;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"MONTH\":\n\t\t\t\t\t\t\t$cronfile .= $month_delay . \" 0 1 \" . $minvalue . \" * \";\n\t\t\t\t\t\t\t$month_delay += 7;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"WEEK\":\n\t\t\t\t\t\t\t$cronfile .= $day_delay . \" 0 \" . ($matches[1] * 7) . \" * * \";\n\t\t\t\t\t\t\t$day_delay += 5;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// create entry-line\n\t\t\t\t\t$cronfile .= \"root \" . $binpath . \" \" . FileDir::makeCorrectFile(Froxlor::getInstallDir() . \"/bin/froxlor-cli\") . \" froxlor:cron \" . escapeshellarg($row_cronentry['cronfile']) . \" -q 1> /dev/null\\n\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// php sessionclean if enabled\n\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$cronfile .= \"# Look for and purge old sessions every 30 minutes\" . PHP_EOL;\n\t\t\t\t$cronfile .= \"09,39 * * * * root \" . $binpath . \" \" . FileDir::makeCorrectFile(Froxlor::getInstallDir() . \"/bin/froxlor-cli\") . \" froxlor:php-sessionclean 1> /dev/null\" . PHP_EOL;\n\t\t\t}\n\n\t\t\tif (FileDir::isFreeBSD()) {\n\t\t\t\t// FreeBSD handles the cron-stuff in another way. We need to directly\n\t\t\t\t// write to the crontab file as there is not cron.d/froxlor file\n\t\t\t\t// (settings for system.cronconfig should be set correctly of course)\n\t\t\t\t$crontab = file_get_contents(Settings::Get(\"system.cronconfig\"));\n\n\t\t\t\tif ($crontab === false) {\n\t\t\t\t\tdie(\"Oh snap, we cannot read the crontab file. This should not happen.\\nPlease check the path and permissions, the cron will keep trying if you don't stop the cron-service.\\n\\n\");\n\t\t\t\t}\n\n\t\t\t\t// now parse out / replace our entries\n\t\t\t\t$crontablines = explode(\"\\n\", $crontab);\n\t\t\t\t$newcrontab = \"\";\n\t\t\t\tforeach ($crontablines as $ctl) {\n\t\t\t\t\t$ctl = trim($ctl);\n\t\t\t\t\tif (!empty($ctl) && !preg_match(\"/(.*)froxlor\\:cron(.*)/\", $ctl)) {\n\t\t\t\t\t\t$newcrontab .= $ctl . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// re-assemble old-content + new froxlor-content\n\t\t\t\t$newcrontab .= $cronfile;\n\n\t\t\t\t// now continue with writing the file\n\t\t\t\t$cronfile = $newcrontab;\n\t\t\t}\n\n\t\t\t// write the file\n\t\t\tif (file_put_contents(Settings::Get(\"system.cronconfig\"), $cronfile) === false) {\n\t\t\t\t// oh snap cannot create new crond-file\n\t\t\t\tdie(\"Oh snap, we cannot create the cron-file. This should not happen.\\nPlease check the path and permissions, the cron will keep trying if you don't stop the cron-service.\\n\\n\");\n\t\t\t}\n\t\t\t// correct permissions\n\t\t\tchmod(Settings::Get(\"system.cronconfig\"), 0640);\n\n\t\t\t// remove all re-generation tasks\n\t\t\tDatabase::query(\"DELETE FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` = '99'\");\n\n\t\t\t// run reload command\n\t\t\tFileDir::safe_exec(escapeshellcmd(Settings::Get('system.crondreload')));\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Dns/Bind.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Dns;\n\nuse Froxlor\\Dns\\Dns;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\Validate\\Validate;\nuse RecursiveDirectoryIterator;\nuse RecursiveIteratorIterator;\n\nclass Bind extends DnsBase\n{\n\n\tprivate $bindconf_file = \"\";\n\n\tpublic function writeConfigs()\n\t{\n\t\t// tell the world what we are doing\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task4 started - Rebuilding froxlor_bind.conf');\n\n\t\t// clean up\n\t\t$this->cleanZonefiles();\n\n\t\t// check for subfolder in bind-config-directory\n\t\tif (!file_exists(FileDir::makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))) {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'mkdir ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/')));\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/')));\n\t\t}\n\n\t\t$domains = $this->getDomainList();\n\n\t\tif (empty($domains)) {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, not creating any zones...');\n\t\t\t$this->bindconf_file = '';\n\t\t} else {\n\t\t\t$this->bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . \"\\n\" . '# Created ' . date('d.m.Y H:i') . \"\\n\" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . \"\\n\\n\";\n\t\t\tforeach ($domains as $domain) {\n\t\t\t\tif ($domain['is_child']) {\n\t\t\t\t\t// domains that are subdomains to other main domains are handled by recursion within walkDomainList()\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$this->walkDomainList($domain, $domains);\n\t\t\t}\n\t\t}\n\n\t\t$bindconf_file_handler = fopen(FileDir::makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w');\n\t\tfwrite($bindconf_file_handler, $this->bindconf_file);\n\t\tfclose($bindconf_file_handler);\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written');\n\t\t$this->reloadDaemon();\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task4 finished');\n\t}\n\n\tprivate function cleanZonefiles()\n\t{\n\t\t$config_dir = FileDir::makeCorrectFile(Settings::Get('system.bindconf_directory') . '/domains/');\n\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Cleaning dns zone files from ' . $config_dir);\n\n\t\t// check directory\n\t\tif (@is_dir($config_dir)) {\n\t\t\t// create directory iterator\n\t\t\t$its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($config_dir));\n\n\t\t\t// iterate through all subdirs, look for zone files and delete them\n\t\t\tforeach ($its as $it) {\n\t\t\t\tif ($it->isFile()) {\n\t\t\t\t\t// remove file\n\t\t\t\t\tFileDir::safe_exec('rm -f ' . escapeshellarg(FileDir::makeCorrectFile($its->getPathname())));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate function walkDomainList($domain, $domains)\n\t{\n\t\t$zoneContent = '';\n\t\t$subzones = '';\n\n\t\tforeach ($domain['children'] as $child_domain_id) {\n\t\t\t$subzones .= $this->walkDomainList($domains[$child_domain_id], $domains);\n\t\t}\n\n\t\tif ($domain['zonefile'] == '') {\n\t\t\t// check for system-hostname\n\t\t\t$isFroxlorHostname = false;\n\t\t\tif (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) {\n\t\t\t\t$isFroxlorHostname = true;\n\t\t\t}\n\n\t\t\tif (!$domain['is_child']) {\n\t\t\t\t$zoneContent = (string)Dns::createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname);\n\t\t\t\t$domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone';\n\t\t\t\t$zonefile_name = FileDir::makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']);\n\t\t\t\t$zonefile_handler = fopen($zonefile_name, 'w');\n\t\t\t\tfwrite($zonefile_handler, $zoneContent . $subzones);\n\t\t\t\tfclose($zonefile_handler);\n\t\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` written');\n\t\t\t\t$this->bindconf_file .= $this->generateDomainConfig($domain);\n\t\t\t} else {\n\t\t\t\treturn (string)Dns::createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname, true);\n\t\t\t}\n\t\t} else {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Added zonefile ' . $domain['zonefile'] . ' for domain ' . $domain['domain'] . ' - Note that you will also have to handle ALL records for ALL subdomains.');\n\t\t\t$this->bindconf_file .= $this->generateDomainConfig($domain);\n\t\t}\n\t}\n\n\tprivate function generateDomainConfig($domain = [])\n\t{\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Generating dns config for ' . $domain['domain']);\n\n\t\t$bindconf_file = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . \"\\n\";\n\t\t$bindconf_file .= 'zone \"' . $domain['domain'] . '\" in {' . \"\\n\";\n\t\t$bindconf_file .= '\ttype master;' . \"\\n\";\n\t\t$bindconf_file .= '\tfile \"' . FileDir::makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']) . '\";' . \"\\n\";\n\t\t$bindconf_file .= '\tallow-query { any; };' . \"\\n\";\n\n\t\tif (count($this->ns) > 0 || count($this->axfr) > 0) {\n\t\t\t// open allow-transfer\n\t\t\t$bindconf_file .= '\tallow-transfer {' . \"\\n\";\n\t\t\t// put nameservers in allow-transfer\n\t\t\tif (count($this->ns) > 0) {\n\t\t\t\tforeach ($this->ns as $ns) {\n\t\t\t\t\tforeach ($ns[\"ips\"] as $ip) {\n\t\t\t\t\t\t$ip = Validate::validate_ip2($ip, true, 'invalidip', true, true, true);\n\t\t\t\t\t\tif ($ip) {\n\t\t\t\t\t\t\t$bindconf_file .= '\t\t' . $ip . \";\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// AXFR server #100\n\t\t\tif (count($this->axfr) > 0) {\n\t\t\t\tforeach ($this->axfr as $axfrserver) {\n\t\t\t\t\t$bindconf_file .= '\t\t' . $axfrserver . ';' . \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t\t// close allow-transfer\n\t\t\t$bindconf_file .= '\t};' . \"\\n\";\n\t\t}\n\n\t\t$bindconf_file .= '};' . \"\\n\";\n\t\t$bindconf_file .= \"\\n\";\n\n\t\treturn $bindconf_file;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Dns/DnsBase.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Dns;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse PDO;\n\n/**\n * Class DnsBase\n *\n * Base class for all DNS server configs\n */\nabstract class DnsBase\n{\n\n\tprotected $logger = false;\n\n\tprotected $ns = [];\n\n\tprotected $mx = [];\n\n\tprotected $axfr = [];\n\n\tpublic function __construct($logger)\n\t{\n\t\t$this->logger = $logger;\n\n\t\t$known_ns_ips = [];\n\t\tif (Settings::Get('system.nameservers') != '') {\n\t\t\t$nameservers = explode(',', Settings::Get('system.nameservers'));\n\t\t\tforeach ($nameservers as $nameserver) {\n\t\t\t\t$nameserver = trim($nameserver);\n\t\t\t\t// DNS servers might be multi homed; allow transfer from all ip\n\t\t\t\t// addresses of the DNS server\n\t\t\t\t$nameserver_ips = PhpHelper::gethostbynamel6($nameserver);\n\t\t\t\t// append dot to hostname\n\t\t\t\tif (substr($nameserver, -1, 1) != '.') {\n\t\t\t\t\t$nameserver .= '.';\n\t\t\t\t}\n\t\t\t\t// ignore invalid responses\n\t\t\t\tif (!is_array($nameserver_ips)) {\n\t\t\t\t\t// act like \\Froxlor\\PhpHelper::gethostbynamel6() and return unmodified hostname on error\n\t\t\t\t\t$nameserver_ips = [\n\t\t\t\t\t\t$nameserver\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\t$known_ns_ips = array_merge($known_ns_ips, $nameserver_ips);\n\t\t\t\t}\n\t\t\t\t$this->ns[] = [\n\t\t\t\t\t'hostname' => $nameserver,\n\t\t\t\t\t'ips' => $nameserver_ips\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\n\t\tif (Settings::Get('system.mxservers') != '') {\n\t\t\t$mxservers = explode(',', Settings::Get('system.mxservers'));\n\t\t\tforeach ($mxservers as $mxserver) {\n\t\t\t\tif (substr($mxserver, -1, 1) != '.') {\n\t\t\t\t\t$mxserver .= '.';\n\t\t\t\t}\n\t\t\t\t$this->mx[] = $mxserver;\n\t\t\t}\n\t\t}\n\n\t\t// AXFR server #100\n\t\tif (Settings::Get('system.axfrservers') != '') {\n\t\t\t$axfrservers = explode(',', Settings::Get('system.axfrservers'));\n\t\t\tforeach ($axfrservers as $axfrserver) {\n\t\t\t\tif (!in_array(trim($axfrserver), $known_ns_ips)) {\n\t\t\t\t\t$this->axfr[] = trim($axfrserver);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tabstract public function writeConfigs();\n\n\tpublic function reloadDaemon()\n\t{\n\t\t// reload DNS daemon\n\t\t$cmd = Settings::Get('system.bindreload_command');\n\t\t$cmdStatus = 1;\n\t\tFileDir::safe_exec(escapeshellcmd($cmd), $cmdStatus);\n\t\tif ($cmdStatus === 0) {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, Settings::Get('system.dns_server') . ' daemon reloaded');\n\t\t} else {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'Error while running `' . $cmd . '`: exit code (' . $cmdStatus . ') - please check your system logs');\n\t\t}\n\t}\n\n\tprotected function getDomainList()\n\t{\n\t\t$result_domains_stmt = Database::query(\"\n\t\t\tSELECT\n\t\t\t\t`d`.`id`,\n\t\t\t\t`d`.`domain`,\n\t\t\t\t`d`.`isemaildomain`,\n\t\t\t\t`d`.`iswildcarddomain`,\n\t\t\t\t`d`.`wwwserveralias`,\n\t\t\t\t`d`.`customerid`,\n\t\t\t\t`d`.`zonefile`,\n\t\t\t\t`d`.`bindserial`,\n\t\t\t\t`d`.`dkim`,\n\t\t\t\t`d`.`dkim_id`,\n\t\t\t\t`d`.`dkim_pubkey`,\n\t\t\t\t`c`.`loginname`,\n\t\t\t\t`c`.`guid`\n\t\t\tFROM\n\t\t\t\t`\" . TABLE_PANEL_DOMAINS . \"` `d`\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` `c` USING(`customerid`)\n\t\t\tWHERE\n\t\t\t\t`d`.`isbinddomain` = '1'\n\t\t\tORDER BY\n\t\t\t\tLENGTH(`d`.`domain`), `d`.`domain` ASC\n\t\t\");\n\n\t\t$domains = [];\n\t\t// don't use fetchall() to be able to set the first column to the domain id and use it later on to set the rows'\n\t\t// array of direct children without having to search the outer array\n\t\twhile ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$domains[$domain[\"id\"]] = $domain;\n\t\t}\n\n\t\t// frolxor-hostname (#1090)\n\t\tif (Settings::get('system.dns_createhostnameentry') == 1) {\n\t\t\t$hostname_arr = [\n\t\t\t\t'id' => 'none',\n\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t'isbinddomain' => '1',\n\t\t\t\t'isemaildomain' => Settings::Get('system.dns_createmailentry'),\n\t\t\t\t'email_only' => '0',\n\t\t\t\t'customerid' => 'none',\n\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t'bindserial' => date('Ymd') . '00',\n\t\t\t\t'dkim' => '0',\n\t\t\t\t'iswildcarddomain' => '1',\n\t\t\t\t'zonefile' => '',\n\t\t\t\t'froxlorhost' => '1'\n\t\t\t];\n\t\t\t$domains[0] = $hostname_arr;\n\t\t}\n\n\t\tif (empty($domains)) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// collect domain IDs of direct child domains as arrays in ['children'] column\n\t\tforeach (array_keys($domains) as $key) {\n\t\t\tif (!isset($domains[$key]['children'])) {\n\t\t\t\t$domains[$key]['children'] = [];\n\t\t\t}\n\t\t\tif (!isset($domains[$key]['is_child'])) {\n\t\t\t\t$domains[$key]['is_child'] = false;\n\t\t\t}\n\t\t\t$children = Domain::getMainSubdomainIds($key);\n\t\t\tif (count($children) > 0) {\n\t\t\t\tforeach ($children as $child) {\n\t\t\t\t\tif (isset($domains[$child])) {\n\t\t\t\t\t\t$domains[$key]['children'][] = $domains[$child]['id'];\n\t\t\t\t\t\t$domains[$child]['is_child'] = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, str_pad('domId', 9, ' ') . str_pad('domain', 40, ' ') . \"list of child domain ids\");\n\t\tforeach ($domains as $domain) {\n\t\t\t$logLine = str_pad($domain['id'], 9, ' ') . str_pad($domain['domain'], 40, ' ') . join(', ', $domain['children']);\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, $logLine);\n\t\t}\n\n\t\treturn $domains;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Dns/PowerDNS.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Dns;\n\nuse Froxlor\\Dns\\Dns;\nuse Froxlor\\Dns\\DnsZone;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass PowerDNS extends DnsBase\n{\n\n\tpublic function writeConfigs()\n\t{\n\t\t// tell the world what we are doing\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task4 started - Refreshing DNS database');\n\n\t\t$domains = $this->getDomainList();\n\n\t\t// clean up\n\t\t$this->clearZoneTables($domains);\n\n\t\tif (empty($domains)) {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, not creating any zones...');\n\t\t} else {\n\t\t\tforeach ($domains as $domain) {\n\t\t\t\tif ($domain['is_child']) {\n\t\t\t\t\t// domains that are subdomains to other main domains are handled by recursion within walkDomainList()\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$this->walkDomainList($domain, $domains);\n\t\t\t}\n\t\t}\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'PowerDNS database updated');\n\t\t$this->reloadDaemon();\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task4 finished');\n\t}\n\n\tprivate function clearZoneTables($domains = null)\n\t{\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Cleaning dns zone entries from database');\n\n\t\t$pdns_domains_stmt = \\Froxlor\\Dns\\PowerDNS::getDB()->prepare(\"SELECT `id`, `name` FROM `domains` WHERE `name` = :domain\");\n\n\t\t$del_rec_stmt = \\Froxlor\\Dns\\PowerDNS::getDB()->prepare(\"DELETE FROM `records` WHERE `domain_id` = :did\");\n\t\t$del_meta_stmt = \\Froxlor\\Dns\\PowerDNS::getDB()->prepare(\"DELETE FROM `domainmetadata` WHERE `domain_id` = :did\");\n\t\t$del_dom_stmt = \\Froxlor\\Dns\\PowerDNS::getDB()->prepare(\"DELETE FROM `domains` WHERE `id` = :did\");\n\n\t\tforeach ($domains as $domain) {\n\t\t\t$pdns_domains_stmt->execute([\n\t\t\t\t'domain' => $domain['domain']\n\t\t\t]);\n\t\t\t$pdns_domain = $pdns_domains_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t\tif ($pdns_domain && !empty($pdns_domain['id'])) {\n\t\t\t\t$del_rec_stmt->execute([\n\t\t\t\t\t'did' => $pdns_domain['id']\n\t\t\t\t]);\n\t\t\t\t$del_meta_stmt->execute([\n\t\t\t\t\t'did' => $pdns_domain['id']\n\t\t\t\t]);\n\t\t\t\t$del_dom_stmt->execute([\n\t\t\t\t\t'did' => $pdns_domain['id']\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate function walkDomainList($domain, $domains)\n\t{\n\t\t$zoneContent = '';\n\t\t$subzones = [];\n\n\t\tforeach ($domain['children'] as $child_domain_id) {\n\t\t\t$subzones[] = $this->walkDomainList($domains[$child_domain_id], $domains);\n\t\t}\n\n\t\tif ($domain['zonefile'] == '') {\n\t\t\t// check for system-hostname\n\t\t\t$isFroxlorHostname = false;\n\t\t\tif (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) {\n\t\t\t\t$isFroxlorHostname = true;\n\t\t\t}\n\n\t\t\tif (!$domain['is_child']) {\n\t\t\t\t$zoneContent = Dns::createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname);\n\t\t\t\tif (count($subzones)) {\n\t\t\t\t\tforeach ($subzones as $subzone) {\n\t\t\t\t\t\t$zoneContent->records[] = $subzone;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$pdnsDomId = $this->insertZone($zoneContent->origin, $zoneContent->serial);\n\t\t\t\t$this->insertRecords($pdnsDomId, $zoneContent->records, $zoneContent->origin);\n\t\t\t\t$this->insertAllowedTransfers($pdnsDomId);\n\t\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'DB entries stored for zone `' . $domain['domain'] . '`');\n\t\t\t} else {\n\t\t\t\treturn Dns::createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname, true);\n\t\t\t}\n\t\t} else {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'Custom zonefiles are NOT supported when PowerDNS is selected as DNS daemon (triggered by: ' . $domain['domain'] . ')');\n\t\t}\n\t}\n\n\tprivate function insertZone($domainname, $serial = 0)\n\t{\n\t\t$ins_stmt = \\Froxlor\\Dns\\PowerDNS::getDB()->prepare(\"\n\t\t\tINSERT INTO domains set `name` = :domainname, `notified_serial` = :serial, `type` = :type\n\t\t\");\n\t\t$ins_stmt->execute([\n\t\t\t'domainname' => $domainname,\n\t\t\t'serial' => $serial,\n\t\t\t'type' => strtoupper(Settings::Get('system.powerdns_mode'))\n\t\t]);\n\t\t$lastid = \\Froxlor\\Dns\\PowerDNS::getDB()->lastInsertId();\n\t\treturn $lastid;\n\t}\n\n\tprivate function insertRecords($domainid = 0, $records = [], $origin = \"\")\n\t{\n\t\t$ins_stmt = \\Froxlor\\Dns\\PowerDNS::getDB()->prepare(\"\n\t\t\tINSERT INTO records set\n\t\t\t`domain_id` = :did,\n\t\t\t`name` = :rec,\n\t\t\t`type` = :type,\n\t\t\t`content` = :content,\n\t\t\t`ttl` = :ttl,\n\t\t\t`prio` = :prio,\n\t\t\t`disabled` = '0'\n\t\t\");\n\n\t\tforeach ($records as $record) {\n\t\t\tif ($record instanceof DnsZone) {\n\t\t\t\t$this->insertRecords($domainid, $record->records, $record->origin);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ($record->record == '@') {\n\t\t\t\t$_record = $origin;\n\t\t\t} else {\n\t\t\t\t$_record = $record->record . \".\" . $origin;\n\t\t\t}\n\n\t\t\t$ins_data = [\n\t\t\t\t'did' => $domainid,\n\t\t\t\t'rec' => $_record,\n\t\t\t\t'type' => $record->type,\n\t\t\t\t'content' => $record->content,\n\t\t\t\t'ttl' => $record->ttl,\n\t\t\t\t'prio' => $record->priority\n\t\t\t];\n\t\t\t$ins_stmt->execute($ins_data);\n\t\t}\n\t}\n\n\tprivate function insertAllowedTransfers($domainid)\n\t{\n\t\t$ins_stmt = \\Froxlor\\Dns\\PowerDNS::getDB()->prepare(\"\n\t\t\tINSERT INTO domainmetadata set `domain_id` = :did, `kind` = 'ALLOW-AXFR-FROM', `content` = :value\n\t\t\");\n\n\t\t$ins_data = [\n\t\t\t'did' => $domainid\n\t\t];\n\n\t\tif (count($this->ns) > 0 || count($this->axfr) > 0) {\n\t\t\t// put nameservers in allow-transfer\n\t\t\tif (count($this->ns) > 0) {\n\t\t\t\tforeach ($this->ns as $ns) {\n\t\t\t\t\tforeach ($ns[\"ips\"] as $ip) {\n\t\t\t\t\t\t$ins_data['value'] = $ip;\n\t\t\t\t\t\t$ins_stmt->execute($ins_data);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// AXFR server #100\n\t\t\tif (count($this->axfr) > 0) {\n\t\t\t\tforeach ($this->axfr as $axfrserver) {\n\t\t\t\t\t$ins_data['value'] = $axfrserver;\n\t\t\t\t\t$ins_stmt->execute($ins_data);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Dns/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cron/Forkable.php",
    "content": "<?php\n\nnamespace Froxlor\\Cron;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\n\ntrait Forkable\n{\n\tpublic static function runFork($closure, array $attributes = [], int $concurrentChildren = 3)\n\t{\n\t\t$childrenPids = [];\n\n\t\t// We only fork if pcntl_fork is available and nofork flag is not set\n\t\tif (function_exists('pcntl_fork') && !defined('CRON_NOFORK_FLAG')) {\n\t\t\tforeach ($attributes as $closureAttributes) {\n\t\t\t\t// We close the database - connection before we fork, so we don't share resources with the child\n\t\t\t\tDatabase::needRoot(false); // this forces the connection to be set to null\n\t\t\t\t$pid = pcntl_fork();\n\n\t\t\t\tif ($pid == -1) {\n\t\t\t\t\texit(\"Error forking...\\n\");\n\t\t\t\t} elseif ($pid == 0) {\n\t\t\t\t\t// re-create db\n\t\t\t\t\tDatabase::needRoot(false);\n\t\t\t\t\t$closure($closureAttributes);\n\t\t\t\t\texit();\n\t\t\t\t} else {\n\t\t\t\t\t$childrenPids[] = $pid;\n\t\t\t\t\twhile (count($childrenPids) >= $concurrentChildren) {\n\t\t\t\t\t\tforeach ($childrenPids as $key => $pid) {\n\t\t\t\t\t\t\t$res = pcntl_waitpid($pid, $status, WNOHANG);\n\t\t\t\t\t\t\t// If the process has already exited\n\t\t\t\t\t\t\tif ($res == -1 || $res > 0) {\n\t\t\t\t\t\t\t\tunset($childrenPids[$key]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsleep(1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\twhile (pcntl_waitpid(0, $status) != -1);\n\t\t} else {\n\t\t\tif (!defined('CRON_NOFORK_FLAG')) {\n\t\t\t\tif (extension_loaded('pcntl')) {\n\t\t\t\t\t$msg = \"PHP compiled with pcntl but pcntl_fork function is not available.\";\n\t\t\t\t} else {\n\t\t\t\t\t$msg = \"PHP compiled without pcntl.\";\n\t\t\t\t}\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, $msg . \" Not forking \" . self::class . \", this may take a long time!\");\n\t\t\t}\n\t\t\tforeach ($attributes as $closureAttributes) {\n\t\t\t\t$closure($closureAttributes);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/FroxlorCron.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron;\n\nabstract class FroxlorCron\n{\n\n\tprotected static $cronlog = null;\n\tprotected static $lockfile = null;\n\n\tabstract public static function run();\n\n\tpublic static function getLockfile()\n\t{\n\t\treturn static::$lockfile;\n\t}\n\n\tpublic static function setLockfile($lockfile = null)\n\t{\n\t\tstatic::$lockfile = $lockfile;\n\t}\n\n\tpublic static function setCronlog($cronlog = null)\n\t{\n\t\tstatic::$cronlog = $cronlog;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/Apache.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http;\n\nuse Froxlor\\Cron\\Http\\Php\\PhpInterface;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Http\\Directory;\nuse Froxlor\\Http\\Statistics;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\Crypt;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\nclass Apache extends HttpConfigBase\n{\n\n\t// protected\n\tprotected $known_diroptionsfilenames = [];\n\n\tprotected $known_htpasswdsfilenames = [];\n\n\tprotected $virtualhosts_data = [];\n\n\tprotected $diroptions_data = [];\n\n\tprotected $htpasswds_data = [];\n\n\t/**\n\t * indicator whether a customer is deactivated or not\n\t * if yes, only the webroot will be generated\n\t *\n\t * @var bool\n\t */\n\tprivate $deactivated = false;\n\n\tpublic function createIpPort()\n\t{\n\t\t$result_ipsandports_stmt = Database::query(\"SELECT * FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` ORDER BY `ip` ASC, `port` ASC\");\n\n\t\twhile ($row_ipsandports = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (filter_var($row_ipsandports['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t$ipport = '[' . $row_ipsandports['ip'] . ']:' . $row_ipsandports['port'];\n\t\t\t} else {\n\t\t\t\t$ipport = $row_ipsandports['ip'] . ':' . $row_ipsandports['port'];\n\t\t\t}\n\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'apache::createIpPort: creating ip/port settings for  ' . $ipport);\n\t\t\t$vhosts_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/10_froxlor_ipandport_' . trim(str_replace(':', '.', $row_ipsandports['ip']), '.') . '.' . $row_ipsandports['port'] . '.conf');\n\n\t\t\tif (!isset($this->virtualhosts_data[$vhosts_filename])) {\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] = '';\n\t\t\t}\n\n\t\t\tif ($row_ipsandports['listen_statement'] == '1') {\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= 'Listen ' . $ipport . \"\\n\";\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted listen-statement');\n\t\t\t}\n\n\t\t\tif ($row_ipsandports['namevirtualhost_statement'] == '1') {\n\t\t\t\t// >=apache-2.4 enabled?\n\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, $ipport . ' :: namevirtualhost-statement no longer needed for apache-2.4');\n\t\t\t\t} else {\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= 'NameVirtualHost ' . $ipport . \"\\n\";\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted namevirtualhost-statement');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($row_ipsandports['vhostcontainer'] == '1') {\n\t\t\t\t$without_vhost = $this->virtualhosts_data[$vhosts_filename];\n\t\t\t\t$close_vhost = true;\n\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '<VirtualHost ' . $ipport . '>' . \"\\n\";\n\n\t\t\t\t$mypath = $this->getMyPath($row_ipsandports);\n\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= 'DocumentRoot \"' . rtrim($mypath, \"/\") . '\"' . \"\\n\";\n\n\t\t\t\tif ($row_ipsandports['vhostcontainer_servername_statement'] == '1') {\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' ServerName ' . Settings::Get('system.hostname') . \"\\n\";\n\n\t\t\t\t\t$froxlor_aliases = Settings::Get('system.froxloraliases');\n\t\t\t\t\tif (!empty($froxlor_aliases)) {\n\t\t\t\t\t\t$froxlor_aliases = explode(\",\", $froxlor_aliases);\n\t\t\t\t\t\t$aliases = \"\";\n\t\t\t\t\t\tforeach ($froxlor_aliases as $falias) {\n\t\t\t\t\t\t\tif (Validate::validateDomain(trim($falias))) {\n\t\t\t\t\t\t\t\t$aliases .= trim($falias) . \" \";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$aliases = trim($aliases);\n\t\t\t\t\t\tif (!empty($aliases)) {\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' ServerAlias ' . $aliases . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$is_redirect = false;\n\t\t\t\t// check for SSL redirect\n\t\t\t\tif ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') {\n\t\t\t\t\t$is_redirect = true;\n\t\t\t\t\t// check whether froxlor uses Let's Encrypt and not cert is being generated yet\n\t\t\t\t\t// or a renewal is ongoing - disable redirect\n\t\t\t\t\tif (Settings::Get('system.leenabled') == '1' && Settings::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) {\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '# temp. disabled ssl-redirect due to Let\\'s Encrypt certificate generation.' . PHP_EOL;\n\t\t\t\t\t\t$is_redirect = false;\n\t\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$_sslport = $this->checkAlternativeSslPort();\n\n\t\t\t\t\t\t$mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/';\n\t\t\t\t\t\t$code = '301';\n\t\t\t\t\t\t$modrew_red = ' [R=' . $code . ';L,NE]';\n\n\t\t\t\t\t\t// redirect everything, not only root-directory, #541\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  <IfModule mod_rewrite.c>' . \"\\n\";\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    RewriteEngine On' . \"\\n\";\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    RewriteCond %{HTTPS} off' . \"\\n\";\n\t\t\t\t\t\tif (Settings::Get('system.leenabled') == '1' && Settings::Get('system.le_froxlor_enabled') == '1') {\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    RewriteCond %{REQUEST_URI} !^/\\.well-known/acme-challenge' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    RewriteRule ^/(.*) ' . $mypath . '$1' . $modrew_red . \"\\n\";\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  </IfModule>' . \"\\n\";\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  <IfModule !mod_rewrite.c>' . \"\\n\";\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Redirect ' . $code . ' / ' . $mypath . \"\\n\";\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  </IfModule>' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!$is_redirect) {\n\t\t\t\t\t// protect lib/userdata.inc.php\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  <Directory \"' . rtrim($mypath, \"/\") . '/lib/\">' . \"\\n\";\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    <Files \"userdata.inc.php\">' . \"\\n\";\n\t\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Require all denied' . \"\\n\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Order deny,allow' . \"\\n\";\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    deny from all' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    </Files>' . \"\\n\";\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  </Directory>' . \"\\n\";\n\t\t\t\t\t// protect bin/\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  <DirectoryMatch \"^' . rtrim($mypath, \"/\") . '/(bin|cache|logs|tests|vendor)/\">' . \"\\n\";\n\t\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Require all denied' . \"\\n\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Order deny,allow' . \"\\n\";\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    deny from all' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  </DirectoryMatch>' . \"\\n\";\n\n\t\t\t\t\t// create fcgid <Directory>-Part (starter is created in apache_fcgid)\n\t\t\t\t\tif (Settings::Get('system.mod_fcgid_ownvhost') == '1' && Settings::Get('system.mod_fcgid') == '1') {\n\t\t\t\t\t\t$configdir = FileDir::makeCorrectDir(Settings::Get('system.mod_fcgid_configdir') . '/froxlor.panel/' . Settings::Get('system.hostname'));\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  FcgidIdleTimeout ' . Settings::Get('system.mod_fcgid_idle_timeout') . \"\\n\";\n\t\t\t\t\t\tif ((int)Settings::Get('system.mod_fcgid_wrapper') == 0) {\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  SuexecUserGroup \"' . Settings::Get('system.mod_fcgid_httpuser') . '\" \"' . Settings::Get('system.mod_fcgid_httpgroup') . '\"' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  ScriptAlias /php/ ' . $configdir . \"\\n\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$domain = [\n\t\t\t\t\t\t\t\t'id' => 'none',\n\t\t\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t\t\t'adminid' => 1, /* first admin-user (superadmin) */\n\t\t\t\t\t\t\t\t'mod_fcgid_starter' => -1,\n\t\t\t\t\t\t\t\t'mod_fcgid_maxrequests' => -1,\n\t\t\t\t\t\t\t\t'guid' => Settings::Get('system.mod_fcgid_httpuser'),\n\t\t\t\t\t\t\t\t'openbasedir' => 0,\n\t\t\t\t\t\t\t\t'email' => Settings::Get('panel.adminmail'),\n\t\t\t\t\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t\t\t'customerroot' => $mypath\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t$php = new PhpInterface($domain);\n\t\t\t\t\t\t\t$phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost'));\n\t\t\t\t\t\t\tif ($phpconfig['pass_authorizationheader'] == '1') {\n\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  FcgidPassHeader     Authorization' . \"\\n\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$starter_filename = FileDir::makeCorrectFile($configdir . '/php-fcgi-starter');\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  SuexecUserGroup \"' . Settings::Get('system.mod_fcgid_httpuser') . '\" \"' . Settings::Get('system.mod_fcgid_httpgroup') . '\"' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  <Directory \"' . $mypath . '\">' . \"\\n\";\n\t\t\t\t\t\t\t$file_extensions = explode(' ', $phpconfig['file_extensions']);\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    <FilesMatch \"\\.(' . implode('|', $file_extensions) . ')$\">' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '      SetHandler fcgid-script' . \"\\n\";\n\t\t\t\t\t\t\tforeach ($file_extensions as $file_extension) {\n\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '      FcgidWrapper ' . $starter_filename . ' .' . $file_extension . \"\\n\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '      Options +ExecCGI' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    </FilesMatch>' . \"\\n\";\n\t\t\t\t\t\t\t// >=apache-2.4 enabled?\n\t\t\t\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\t\t\t\t$mypath_dir = new Directory($mypath);\n\t\t\t\t\t\t\t\t// only create the require all granted if there is not active directory-protection\n\t\t\t\t\t\t\t\t// for this path, as this would be the first require and therefore grant all access\n\t\t\t\t\t\t\t\tif ($mypath_dir->isUserProtected() == false) {\n\t\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Require all granted' . \"\\n\";\n\t\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    AllowOverride All' . \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Order allow,deny' . \"\\n\";\n\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    allow from all' . \"\\n\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  </Directory>' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t} elseif (Settings::Get('phpfpm.enabled') == '1' && (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) {\n\t\t\t\t\t\t// get fpm config\n\t\t\t\t\t\t$fpm_sel_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tSELECT f.id FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` f\n\t\t\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_PHPCONFIGS . \"` p ON p.fpmsettingid = f.id\n\t\t\t\t\t\t\tWHERE p.id = :phpconfigid\n\t\t\t\t\t\t\");\n\t\t\t\t\t\t$fpm_config = Database::pexecute_first($fpm_sel_stmt, [\n\t\t\t\t\t\t\t'phpconfigid' => Settings::Get('phpfpm.vhost_defaultini')\n\t\t\t\t\t\t]);\n\t\t\t\t\t\t// create php-fpm <Directory>-Part (config is created in apache_fcgid)\n\t\t\t\t\t\t$domain = [\n\t\t\t\t\t\t\t'id' => 'none',\n\t\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t\t'adminid' => 1, /* first admin-user (superadmin) */\n\t\t\t\t\t\t\t'mod_fcgid_starter' => -1,\n\t\t\t\t\t\t\t'mod_fcgid_maxrequests' => -1,\n\t\t\t\t\t\t\t'guid' => Settings::Get('phpfpm.vhost_httpuser'),\n\t\t\t\t\t\t\t'openbasedir' => 0,\n\t\t\t\t\t\t\t'email' => Settings::Get('panel.adminmail'),\n\t\t\t\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t\t'customerroot' => $mypath,\n\t\t\t\t\t\t\t'fpm_config_id' => isset($fpm_config['id']) ? $fpm_config['id'] : 1\n\t\t\t\t\t\t];\n\n\t\t\t\t\t\t$php = new phpinterface($domain);\n\t\t\t\t\t\t$phpconfig = $php->getPhpConfig(Settings::Get('phpfpm.vhost_defaultini'));\n\t\t\t\t\t\t$srvName = substr(md5($ipport), 0, 4) . '.fpm.external';\n\t\t\t\t\t\tif ($row_ipsandports['ssl']) {\n\t\t\t\t\t\t\t$srvName = substr(md5($ipport), 0, 4) . '.ssl-fpm.external';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  <Directory \"' . $mypath . '\">' . \"\\n\";\n\t\t\t\t\t\t// mod_proxy stuff for apache-2.4\n\t\t\t\t\t\tif (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') {\n\t\t\t\t\t\t\t$filesmatch = $phpconfig['fpm_settings']['limit_extensions'];\n\t\t\t\t\t\t\t$extensions = explode(\" \", $filesmatch);\n\t\t\t\t\t\t\t$filesmatch = \"\";\n\t\t\t\t\t\t\tforeach ($extensions as $ext) {\n\t\t\t\t\t\t\t\t$filesmatch .= substr($ext, 1) . '|';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// start block, cut off last pipe and close block\n\t\t\t\t\t\t\t$filesmatch = '(' . str_replace(\".\", \"\\.\", substr($filesmatch, 0, -1)) . ')';\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  <FilesMatch \\.' . $filesmatch . '$>' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    <If \"-f %{SCRIPT_FILENAME}\">' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  \tSetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    </If>' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  </FilesMatch>' . \"\\n\";\n\t\t\t\t\t\t\tif ($phpconfig['pass_authorizationheader'] == '1') {\n\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    CGIPassAuth On' . \"\\n\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$addheader = \"\";\n\t\t\t\t\t\t\tif ($phpconfig['pass_authorizationheader'] == '1') {\n\t\t\t\t\t\t\t\t$addheader = \" -pass-header Authorization\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . $phpconfig['fpm_settings']['idle_timeout'] . $addheader . \"\\n\";\n\t\t\t\t\t\t\t$filesmatch = $phpconfig['fpm_settings']['limit_extensions'];\n\t\t\t\t\t\t\t$extensions = explode(\" \", $filesmatch);\n\t\t\t\t\t\t\t$filesmatch = \"\";\n\t\t\t\t\t\t\tforeach ($extensions as $ext) {\n\t\t\t\t\t\t\t\t$filesmatch .= substr($ext, 1) . '|';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// start block, cut off last pipe and close block\n\t\t\t\t\t\t\t$filesmatch = '(' . str_replace(\".\", \"\\.\", substr($filesmatch, 0, -1)) . ')';\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '   <FilesMatch \\.' . $filesmatch . '$>' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '     AddHandler php-fastcgi .php' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '     Action php-fastcgi /fastcgiphp' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '      Options +ExecCGI' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    </FilesMatch>' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  Alias /fastcgiphp ' . $php->getInterface()->getAliasConfigDir() . $srvName . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// >=apache-2.4 enabled?\n\t\t\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Require all granted' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    AllowOverride All' . \"\\n\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Order allow,deny' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    allow from all' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '  </Directory>' . \"\\n\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// mod_php\n\t\t\t\t\t\t$domain = [\n\t\t\t\t\t\t\t'id' => 'none',\n\t\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t\t'adminid' => 1, /* first admin-user (superadmin) */\n\t\t\t\t\t\t\t'guid' => Settings::Get('system.httpuser'),\n\t\t\t\t\t\t\t'openbasedir' => 0,\n\t\t\t\t\t\t\t'email' => Settings::Get('panel.adminmail'),\n\t\t\t\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t\t'customerroot' => $mypath\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t\t// end of ssl-redirect check\n\t\t\t\t} else {\n\t\t\t\t\t// fallback of froxlor domain-data for processSpecialConfigTemplate()\n\t\t\t\t\t$domain = [\n\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t'customerroot' => $mypath\n\t\t\t\t\t];\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * dirprotection, see #72\n\t\t\t\t *\n\t\t\t\t * @todo deferred until 0.9.5, needs more testing\n\t\t\t\t *       $this->virtualhosts_data[$vhosts_filename] .= \"\\t<Directory \\\"'.$mypath.'(images|packages|templates)\\\">\\n\";\n\t\t\t\t *       $this->virtualhosts_data[$vhosts_filename] .= \"\\t\\tAllow from all\\n\";\n\t\t\t\t *       $this->virtualhosts_data[$vhosts_filename] .= \"\\t\\tOptions -Indexes\\n\";\n\t\t\t\t *       $this->virtualhosts_data[$vhosts_filename] .= \"\\t</Directory>\\n\";\n\t\t\t\t *\n\t\t\t\t *       $this->virtualhosts_data[$vhosts_filename] .= \"\\t<Directory \\\"'.$mypath.'*\\\">\\n\";\n\t\t\t\t *       $this->virtualhosts_data[$vhosts_filename] .= \"\\t\\tOrder Deny,Allow\\n\";\n\t\t\t\t *       $this->virtualhosts_data[$vhosts_filename] .= \"\\t\\tDeny from All\\n\";\n\t\t\t\t *       $this->virtualhosts_data[$vhosts_filename] .= \"\\t</Directory>\\n\";\n\t\t\t\t *       end of dirprotection\n\t\t\t\t */\n\n\t\t\t\tif ($row_ipsandports['specialsettings'] != '' && ($row_ipsandports['ssl'] == '0' || ($row_ipsandports['ssl'] == '1' && Settings::Get('system.use_ssl') == '1' && $row_ipsandports['include_specialsettings'] == '1'))) {\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['specialsettings'], $domain, $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif ($row_ipsandports['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') {\n\t\t\t\t\tif ($row_ipsandports['ssl_specialsettings'] != '') {\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['ssl_specialsettings'], $domain, $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . \"\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t// check for required fallback\n\t\t\t\t\tif (($row_ipsandports['ssl_cert_file'] == '' || !file_exists($row_ipsandports['ssl_cert_file'])) && (Settings::Get('system.le_froxlor_enabled') == '0' || $this->froxlorVhostHasLetsEncryptCert() == false)) {\n\t\t\t\t\t\t$row_ipsandports['ssl_cert_file'] = Settings::Get('system.ssl_cert_file');\n\t\t\t\t\t\tif (!file_exists($row_ipsandports['ssl_cert_file'])) {\n\t\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'System certificate file \"' . Settings::Get('system.ssl_cert_file') . '\" does not seem to exist. Creating self-signed certificate...');\n\t\t\t\t\t\t\tCrypt::createSelfSignedCertificate();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($row_ipsandports['ssl_key_file'] == '') {\n\t\t\t\t\t\t$row_ipsandports['ssl_key_file'] = Settings::Get('system.ssl_key_file');\n\t\t\t\t\t\tif (!file_exists($row_ipsandports['ssl_key_file'])) {\n\t\t\t\t\t\t\t// explicitly disable ssl for this vhost\n\t\t\t\t\t\t\t$row_ipsandports['ssl_cert_file'] = \"\";\n\t\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'System certificate key-file \"' . Settings::Get('system.ssl_key_file') . '\" does not seem to exist. Disabling SSL-vhost for \"' . Settings::Get('system.hostname') . '\"');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($row_ipsandports['ssl_ca_file'] == '') {\n\t\t\t\t\t\t$row_ipsandports['ssl_ca_file'] = Settings::Get('system.ssl_ca_file');\n\t\t\t\t\t}\n\n\t\t\t\t\t// #418\n\t\t\t\t\tif ($row_ipsandports['ssl_cert_chainfile'] == '') {\n\t\t\t\t\t\t$row_ipsandports['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile');\n\t\t\t\t\t}\n\n\t\t\t\t\t$domain = [\n\t\t\t\t\t\t'id' => 0,\n\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t'adminid' => 1, /* first admin-user (superadmin) */\n\t\t\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t'customerroot' => $mypath,\n\t\t\t\t\t\t'parentdomainid' => 0,\n\t\t\t\t\t\t'ssl_honorcipherorder' => Settings::Get('system.honorcipherorder'),\n\t\t\t\t\t\t'ssl_sessiontickets' => Settings::Get('system.sessiontickets')\n\t\t\t\t\t];\n\n\t\t\t\t\t// override corresponding array values\n\t\t\t\t\t$domain['ssl_cert_file'] = $row_ipsandports['ssl_cert_file'];\n\t\t\t\t\t$domain['ssl_key_file'] = $row_ipsandports['ssl_key_file'];\n\t\t\t\t\t$domain['ssl_ca_file'] = $row_ipsandports['ssl_ca_file'];\n\t\t\t\t\t$domain['ssl_cert_chainfile'] = $row_ipsandports['ssl_cert_chainfile'];\n\n\t\t\t\t\t// SSL STUFF\n\t\t\t\t\t$dssl = new DomainSSL();\n\t\t\t\t\t// this sets the ssl-related array-indices in the $domain array\n\t\t\t\t\t// if the domain has customer-defined ssl-certificates\n\t\t\t\t\t$dssl->setDomainSSLFilesArray($domain);\n\n\t\t\t\t\tif ($domain['ssl_cert_file'] != '') {\n\t\t\t\t\t\t// check for existence, #1485\n\t\t\t\t\t\tif (!file_exists($domain['ssl_cert_file'])) {\n\t\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file \"' . $domain['ssl_cert_file'] . '\" does not exist! Cannot create ssl-directives');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +' . str_replace(\",\", \" +\", Settings::Get('system.ssl_protocols')) . \"\\n\";\n\t\t\t\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\t\t\t\tif (Settings::Get('system.http2_support') == '1') {\n\t\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' Protocols h2 http/1.1' . \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!empty(Settings::Get('system.dhparams_file'))) {\n\t\t\t\t\t\t\t\t\t$dhparams = FileDir::makeCorrectFile(Settings::Get('system.dhparams_file'));\n\t\t\t\t\t\t\t\t\tif (!file_exists($dhparams)) {\n\t\t\t\t\t\t\t\t\t\tfile_put_contents($dhparams, self::FFDHE4096);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLOpenSSLConfCmd DHParameters \"' . $dhparams . '\"' . \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLCompression Off' . \"\\n\";\n\t\t\t\t\t\t\t\tif (Settings::Get('system.sessionticketsenabled') == '1') {\n\t\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLSessionTickets ' . ($domain['ssl_sessiontickets'] == '1' ? 'on' : 'off') . \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLHonorCipherOrder ' . ($domain['ssl_honorcipherorder'] == '1' ? 'on' : 'off') . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . \"\\n\";\n\t\t\t\t\t\t\t$protocols = array_map('trim', explode(\",\", Settings::Get('system.ssl_protocols')));\n\t\t\t\t\t\t\tif (in_array(\"TLSv1.3\", $protocols) && !empty(Settings::Get('system.tlsv13_cipher_list')) && Settings::Get('system.apache24') == 1) {\n\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite TLSv1.3 ' . Settings::Get('system.tlsv13_cipher_list') . \"\\n\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLVerifyDepth 10' . \"\\n\";\n\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateFile ' . FileDir::makeCorrectFile($domain['ssl_cert_file']) . \"\\n\";\n\n\t\t\t\t\t\t\tif ($domain['ssl_key_file'] != '') {\n\t\t\t\t\t\t\t\t// check for existence, #1485\n\t\t\t\t\t\t\t\tif (!file_exists($domain['ssl_key_file'])) {\n\t\t\t\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, $ipport . ' :: certificate key file \"' . $domain['ssl_key_file'] . '\" does not exist! Cannot create ssl-directives');\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateKeyFile ' . FileDir::makeCorrectFile($domain['ssl_key_file']) . \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ($domain['ssl_ca_file'] != '') {\n\t\t\t\t\t\t\t\t// check for existence, #1485\n\t\t\t\t\t\t\t\tif (!file_exists($domain['ssl_ca_file'])) {\n\t\t\t\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, $ipport . ' :: certificate CA file \"' . $domain['ssl_ca_file'] . '\" does not exist! Cannot create ssl-directives');\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLCACertificateFile ' . FileDir::makeCorrectFile($domain['ssl_ca_file']) . \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// #418\n\t\t\t\t\t\t\tif ($domain['ssl_cert_chainfile'] != '') {\n\t\t\t\t\t\t\t\t// check for existence, #1485\n\t\t\t\t\t\t\t\tif (!file_exists($domain['ssl_cert_chainfile'])) {\n\t\t\t\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, $ipport . ' :: certificate chain file \"' . $domain['ssl_cert_chainfile'] . '\" does not exist! Cannot create ssl-directives');\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateChainFile ' . FileDir::makeCorrectFile($domain['ssl_cert_chainfile']) . \"\\n\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// if there is no cert-file specified but we are generating a ssl-vhost,\n\t\t\t\t\t\t// we should return an empty string because this vhost would suck dick, ref #1583\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, $domain['domain'] . ' :: empty certificate file! Cannot create ssl-directives');\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] = $without_vhost;\n\t\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '# no ssl-certificate was specified for this domain, therefore no explicit vhost-container is being generated';\n\t\t\t\t\t\t$close_vhost = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ($close_vhost) {\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '</VirtualHost>' . \"\\n\";\n\t\t\t\t}\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted vhostcontainer');\n\t\t\t}\n\t\t\tunset($vhosts_filename);\n\t\t}\n\n\t\t/**\n\t\t * bug #32\n\t\t */\n\t\t$this->createStandardDirectoryEntry();\n\n\t\t/**\n\t\t * bug #unknown-yet\n\t\t */\n\t\t$this->createStandardErrorHandler();\n\t}\n\n\t/**\n\t * define a standard <Directory>-statement, bug #32\n\t */\n\tprivate function createStandardDirectoryEntry()\n\t{\n\t\t$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_dirfix_nofcgid.conf');\n\n\t\tif (!isset($this->virtualhosts_data[$vhosts_filename])) {\n\t\t\t$this->virtualhosts_data[$vhosts_filename] = '';\n\t\t}\n\n\t\t$this->virtualhosts_data[$vhosts_filename] .= '  <Directory \"' . FileDir::makeCorrectDir(Settings::Get('system.documentroot_prefix')) . '\">' . \"\\n\";\n\n\t\t// check for custom values, see #1638\n\t\t$custom_opts = Settings::Get('system.apacheglobaldiropt');\n\t\tif (!empty($custom_opts)) {\n\t\t\t$this->virtualhosts_data[$vhosts_filename] .= $custom_opts . \"\\n\";\n\t\t} else {\n\t\t\t// >=apache-2.4 enabled?\n\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Require all granted' . \"\\n\";\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    AllowOverride All' . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    Order allow,deny' . \"\\n\";\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '    allow from all' . \"\\n\";\n\t\t\t}\n\t\t}\n\t\t$this->virtualhosts_data[$vhosts_filename] .= '  </Directory>' . \"\\n\";\n\n\t\t$ocsp_cache_filename = $this->getCustomVhostFilename('03_froxlor_ocsp_cache.conf');\n\t\tif (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.apache24') == 1) {\n\t\t\t$this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . Settings::Get('system.apache24_ocsp_cache_path') . \"\\n\";\n\t\t} else {\n\t\t\tif (file_exists($ocsp_cache_filename)) {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'apache::_createStandardDirectoryEntry: unlinking ' . basename($ocsp_cache_filename));\n\t\t\t\tunlink(FileDir::makeCorrectFile($ocsp_cache_filename));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * define a default ErrorDocument-statement, bug #unknown-yet\n\t */\n\tprivate function createStandardErrorHandler()\n\t{\n\t\tif (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && (Settings::Get('defaultwebsrverrhandler.err401') != '' || Settings::Get('defaultwebsrverrhandler.err403') != '' || Settings::Get('defaultwebsrverrhandler.err404') != '' || Settings::Get('defaultwebsrverrhandler.err500') != '')) {\n\t\t\t$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_default_errorhandler.conf');\n\n\t\t\tif (!isset($this->virtualhosts_data[$vhosts_filename])) {\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] = '';\n\t\t\t}\n\n\t\t\t$statusCodes = [\n\t\t\t\t'401',\n\t\t\t\t'403',\n\t\t\t\t'404',\n\t\t\t\t'500'\n\t\t\t];\n\t\t\tforeach ($statusCodes as $statusCode) {\n\t\t\t\tif (Settings::Get('defaultwebsrverrhandler.err' . $statusCode) != '') {\n\t\t\t\t\t$defhandler = Settings::Get('defaultwebsrverrhandler.err' . $statusCode);\n\t\t\t\t\tif (!Validate::validateUrl($defhandler)) {\n\t\t\t\t\t\tif (substr($defhandler, 0, 1) != '\"' && substr($defhandler, -1, 1) != '\"') {\n\t\t\t\t\t\t\t$defhandler = '\"' . FileDir::makeCorrectFile($defhandler) . '\"';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= 'ErrorDocument ' . $statusCode . ' ' . $defhandler . \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic function createOwnVhostStarter()\n\t{\n\t\treturn;\n\t}\n\n\t/**\n\t * We compose the virtualhost entries for the domains\n\t */\n\tpublic function createVirtualHosts()\n\t{\n\t\t$domains = WebserverBase::getVhostsToCreate();\n\t\tforeach ($domains as $domain) {\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'apache::createVirtualHosts: creating vhost container for domain ' . $domain['id'] . ', customer ' . $domain['loginname']);\n\t\t\t$vhosts_filename = $this->getVhostFilename($domain);\n\n\t\t\t// Apply header\n\t\t\t$this->virtualhosts_data[$vhosts_filename] = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . \"\\n\";\n\n\t\t\t$ddr = Settings::Get('system.deactivateddocroot');\n\t\t\tif (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && empty($ddr)) {\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= '# Customer/domain deactivated and a docroot for deactivated users hasn\\'t been set.' . \"\\n\";\n\t\t\t} else {\n\t\t\t\t// Create vhost without ssl\n\t\t\t\t$this->virtualhosts_data[$vhosts_filename] .= $this->getVhostContent($domain, false);\n\n\t\t\t\tif ($domain['ssl_enabled'] == '1' && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1')) {\n\t\t\t\t\t// Adding ssl stuff if enabled\n\t\t\t\t\t$vhosts_filename_ssl = $this->getVhostFilename($domain, true);\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename_ssl] = '# Domain ID: ' . $domain['id'] . ' (SSL) - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . \"\\n\";\n\t\t\t\t\t$this->virtualhosts_data[$vhosts_filename_ssl] .= $this->getVhostContent($domain, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * We compose the virtualhost entry for one domain\n\t */\n\tprotected function getVhostContent($domain, $ssl_vhost = false)\n\t{\n\t\tif ($ssl_vhost === true && ($domain['ssl_redirect'] != '1' && $domain['ssl'] != '1')) {\n\t\t\treturn '';\n\t\t}\n\n\t\t$query = \"SELECT * FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` `i`, `\" . TABLE_DOMAINTOIP . \"` `dip`\n\t\t\tWHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports \";\n\n\t\tif ($ssl_vhost === true && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1')) {\n\t\t\t// by ordering by cert-file the row with filled out SSL-Fields will be shown last, thus it is enough to fill out 1 set of SSL-Fields\n\t\t\t$query .= \"AND i.ssl = '1' ORDER BY i.ssl_cert_file ASC;\";\n\t\t} else {\n\t\t\t$query .= \"AND i.ssl = '0';\";\n\t\t}\n\n\t\t$vhost_content = '';\n\t\t$result_stmt = Database::prepare($query);\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'domainid' => $domain['id']\n\t\t]);\n\n\t\t$ipportlist = '';\n\t\t$_vhost_content = '';\n\t\twhile ($ipandport = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$ipport = '';\n\t\t\t$domain['ip'] = $ipandport['ip'];\n\t\t\t$domain['port'] = $ipandport['port'];\n\t\t\tif ($domain['ssl'] == '1') {\n\t\t\t\t$domain['ssl_cert_file'] = $ipandport['ssl_cert_file'];\n\t\t\t\t$domain['ssl_key_file'] = $ipandport['ssl_key_file'];\n\t\t\t\t$domain['ssl_ca_file'] = $ipandport['ssl_ca_file'];\n\t\t\t\t$domain['ssl_cert_chainfile'] = $ipandport['ssl_cert_chainfile'];\n\n\t\t\t\t// SSL STUFF\n\t\t\t\t$dssl = new DomainSSL();\n\t\t\t\t// this sets the ssl-related array-indices in the $domain array\n\t\t\t\t// if the domain has customer-defined ssl-certificates\n\t\t\t\t$dssl->setDomainSSLFilesArray($domain);\n\t\t\t}\n\n\t\t\tif (filter_var($domain['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t$ipport = '[' . $domain['ip'] . ']:' . $domain['port'] . ' ';\n\t\t\t} else {\n\t\t\t\t$ipport = $domain['ip'] . ':' . $domain['port'] . ' ';\n\t\t\t}\n\n\t\t\tif ($ipandport['default_vhostconf_domain'] != '' && ($ssl_vhost == false || ($ssl_vhost == true && $ipandport['include_default_vhostconf_domain'] == '1'))) {\n\t\t\t\t$_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\";\n\t\t\t}\n\t\t\tif ($ipandport['ssl_default_vhostconf_domain'] != '' && $ssl_vhost == true) {\n\t\t\t\t$_vhost_content .= $this->processSpecialConfigTemplate($ipandport['ssl_default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\";\n\t\t\t}\n\t\t\t$ipportlist .= $ipport;\n\t\t}\n\n\t\t$vhost_content .= '<VirtualHost ' . trim($ipportlist) . '>' . \"\\n\";\n\t\t$vhost_content .= $this->getServerNames($domain);\n\n\t\t$domain['documentroot_norewrite'] = $domain['documentroot'];\n\t\tif (($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1')) {\n\t\t\t// We must not check if our port differs from port 443,\n\t\t\t// but if there is a destination-port != 443\n\t\t\t$_sslport = '';\n\t\t\t// This returns the first port that is != 443 with ssl enabled, if any\n\t\t\t// ordered by ssl-certificate (if any) so that the ip/port combo\n\t\t\t// with certificate is used\n\t\t\t$ssldestport_stmt = Database::prepare(\"\n\t\t\t\tSELECT `ip`.`port` FROM \" . TABLE_PANEL_IPSANDPORTS . \" `ip`\n\t\t\t\tLEFT JOIN `\" . TABLE_DOMAINTOIP . \"` `dip` ON (`ip`.`id` = `dip`.`id_ipandports`)\n\t\t\t\tWHERE `dip`.`id_domain` = :domainid\n\t\t\t\tAND `ip`.`ssl` = '1'  AND `ip`.`port` != 443\n\t\t\t\tORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1;\n\t\t\t\");\n\t\t\t$ssldestport = Database::pexecute_first($ssldestport_stmt, [\n\t\t\t\t'domainid' => $domain['id']\n\t\t\t]);\n\n\t\t\tif ($ssldestport && $ssldestport['port'] != '') {\n\t\t\t\t$_sslport = \":\" . $ssldestport['port'];\n\t\t\t}\n\n\t\t\t$domain['documentroot'] = 'https://%{HTTP_HOST}' . $_sslport . '/';\n\t\t\t$domain['documentroot_norewrite'] = 'https://' . $domain['domain'] . $_sslport . '/';\n\t\t}\n\n\t\tif ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') {\n\t\t\tif ($domain['ssl_cert_file'] == '' || !file_exists($domain['ssl_cert_file'])) {\n\t\t\t\t$domain['ssl_cert_file'] = Settings::Get('system.ssl_cert_file');\n\t\t\t\tif (!file_exists($domain['ssl_cert_file'])) {\n\t\t\t\t\t// explicitly disable ssl for this vhost\n\t\t\t\t\t$domain['ssl_cert_file'] = \"\";\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'System certificate file \"' . Settings::Get('system.ssl_cert_file') . '\" does not seem to exist. Disabling SSL-vhost for \"' . $domain['domain'] . '\"');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($domain['ssl_key_file'] == '' || !file_exists($domain['ssl_key_file'])) {\n\t\t\t\t$domain['ssl_key_file'] = Settings::Get('system.ssl_key_file');\n\t\t\t\tif (!file_exists($domain['ssl_key_file'])) {\n\t\t\t\t\t// explicitly disable ssl for this vhost\n\t\t\t\t\t$domain['ssl_cert_file'] = \"\";\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'System certificate key-file \"' . Settings::Get('system.ssl_key_file') . '\" does not seem to exist. Disabling SSL-vhost for \"' . $domain['domain'] . '\"');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($domain['ssl_ca_file'] == '') {\n\t\t\t\t$domain['ssl_ca_file'] = Settings::Get('system.ssl_ca_file');\n\t\t\t}\n\n\t\t\tif ($domain['ssl_cert_chainfile'] == '') {\n\t\t\t\t$domain['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile');\n\t\t\t}\n\n\t\t\tif ($domain['ssl_cert_file'] != '') {\n\t\t\t\t$ssl_protocols = ($domain['override_tls'] == '1' && !empty($domain['ssl_protocols'])) ? $domain['ssl_protocols'] : Settings::Get('system.ssl_protocols');\n\t\t\t\t$ssl_cipher_list = ($domain['override_tls'] == '1' && !empty($domain['ssl_cipher_list'])) ? $domain['ssl_cipher_list'] : Settings::Get('system.ssl_cipher_list');\n\t\t\t\t$tlsv13_cipher_list = ($domain['override_tls'] == '1' && !empty($domain['tlsv13_cipher_list'])) ? $domain['tlsv13_cipher_list'] : Settings::Get('system.tlsv13_cipher_list');\n\n\t\t\t\t$vhost_content .= '  SSLEngine On' . \"\\n\";\n\t\t\t\t$vhost_content .= '  SSLProtocol -ALL +' . str_replace(\",\", \" +\", $ssl_protocols) . \"\\n\";\n\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\tif (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1') {\n\t\t\t\t\t\t$vhost_content .= '  Protocols h2 http/1.1' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\tif (!empty(Settings::Get('system.dhparams_file'))) {\n\t\t\t\t\t\t$dhparams = FileDir::makeCorrectFile(Settings::Get('system.dhparams_file'));\n\t\t\t\t\t\tif (!file_exists($dhparams)) {\n\t\t\t\t\t\t\tfile_put_contents($dhparams, self::FFDHE4096);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$vhost_content .= '  SSLOpenSSLConfCmd DHParameters \"' . $dhparams . '\"' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\t$vhost_content .= '  SSLCompression Off' . \"\\n\";\n\t\t\t\t\tif (Settings::Get('system.sessionticketsenabled') == '1') {\n\t\t\t\t\t\t$vhost_content .= '  SSLSessionTickets ' . ($domain['ssl_sessiontickets'] == '1' ? 'on' : 'off') . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$vhost_content .= '  SSLHonorCipherOrder ' . ($domain['ssl_honorcipherorder'] == '1' ? 'on' : 'off') . \"\\n\";\n\t\t\t\t$vhost_content .= '  SSLCipherSuite ' . $ssl_cipher_list . \"\\n\";\n\t\t\t\t$protocols = array_map('trim', explode(\",\", $ssl_protocols));\n\t\t\t\tif (in_array(\"TLSv1.3\", $protocols) && !empty($tlsv13_cipher_list) && Settings::Get('system.apache24') == 1) {\n\t\t\t\t\t$vhost_content .= '  SSLCipherSuite TLSv1.3 ' . $tlsv13_cipher_list . \"\\n\";\n\t\t\t\t}\n\t\t\t\t$vhost_content .= '  SSLVerifyDepth 10' . \"\\n\";\n\t\t\t\t$vhost_content .= '  SSLCertificateFile ' . FileDir::makeCorrectFile($domain['ssl_cert_file']) . \"\\n\";\n\n\t\t\t\tif ($domain['ssl_key_file'] != '') {\n\t\t\t\t\t$vhost_content .= '  SSLCertificateKeyFile ' . FileDir::makeCorrectFile($domain['ssl_key_file']) . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif ($domain['ssl_ca_file'] != '') {\n\t\t\t\t\t$vhost_content .= '  SSLCACertificateFile ' . FileDir::makeCorrectFile($domain['ssl_ca_file']) . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif ($domain['ssl_cert_chainfile'] != '') {\n\t\t\t\t\t$vhost_content .= '  SSLCertificateChainFile ' . FileDir::makeCorrectFile($domain['ssl_cert_chainfile']) . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif (Settings::Get('system.apache24') == '1' && isset($domain['ocsp_stapling']) && $domain['ocsp_stapling'] == '1') {\n\t\t\t\t\t$vhost_content .= '  SSLUseStapling on' . PHP_EOL;\n\t\t\t\t}\n\n\t\t\t\tif ($domain['hsts'] >= 0) {\n\t\t\t\t\t$vhost_content .= '  <IfModule mod_headers.c>' . \"\\n\";\n\t\t\t\t\t$vhost_content .= '    Header always set Strict-Transport-Security \"max-age=' . $domain['hsts'];\n\t\t\t\t\tif ($domain['hsts_sub'] == 1) {\n\t\t\t\t\t\t$vhost_content .= '; includeSubDomains';\n\t\t\t\t\t}\n\t\t\t\t\tif ($domain['hsts_preload'] == 1) {\n\t\t\t\t\t\t$vhost_content .= '; preload';\n\t\t\t\t\t}\n\t\t\t\t\t$vhost_content .= '\"' . \"\\n\";\n\t\t\t\t\t$vhost_content .= '  </IfModule>' . \"\\n\";\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// if there is no cert-file specified but we are generating a ssl-vhost,\n\t\t\t\t// we should return an empty string because this vhost would suck dick, ref #1583\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, $domain['domain'] . ' :: empty certificate file! Cannot create ssl-directives');\n\t\t\t\treturn '# no ssl-certificate was specified for this domain, therefore no explicit vhost is being generated';\n\t\t\t}\n\t\t}\n\n\t\t// avoid using any whitespaces\n\t\t$domain['documentroot'] = trim($domain['documentroot']);\n\n\t\tif (preg_match('/^https?\\:\\/\\//', $domain['documentroot'])) {\n\t\t\t$possible_deactivated_webroot = $this->getWebroot($domain);\n\t\t\tif ($this->deactivated == false) {\n\t\t\t\t$corrected_docroot = $domain['documentroot'];\n\n\t\t\t\t// Get domain's redirect code\n\t\t\t\t$code = Domain::getDomainRedirectCode($domain['id']);\n\t\t\t\t$modrew_red = '';\n\t\t\t\tif ($code != '') {\n\t\t\t\t\t$modrew_red = ' [R=' . $code . ';L,NE]';\n\t\t\t\t}\n\n\t\t\t\t$vhost_content .= $this->getLogfiles($domain);\n\t\t\t\t// redirect everything, not only root-directory, #541\n\t\t\t\t$vhost_content .= '  <IfModule mod_rewrite.c>' . \"\\n\";\n\t\t\t\t$vhost_content .= '    RewriteEngine On' . \"\\n\";\n\t\t\t\tif (!$ssl_vhost) {\n\t\t\t\t\t$vhost_content .= '    RewriteCond %{HTTPS} off' . \"\\n\";\n\t\t\t\t}\n\t\t\t\tif ($domain['letsencrypt'] == '1') {\n\t\t\t\t\t$vhost_content .= '    RewriteCond %{REQUEST_URI} !^/\\.well-known/acme-challenge' . \"\\n\";\n\t\t\t\t}\n\t\t\t\t$vhost_content .= '    RewriteRule ^/(.*) ' . $corrected_docroot . '$1' . $modrew_red . \"\\n\";\n\t\t\t\t$vhost_content .= '  </IfModule>' . \"\\n\";\n\t\t\t\t$vhost_content .= '  <IfModule !mod_rewrite.c>' . \"\\n\";\n\t\t\t\t$vhost_content .= '    Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . \"\\n\";\n\t\t\t\t$vhost_content .= '  </IfModule>' . \"\\n\";\n\t\t\t} elseif (Settings::Get('system.deactivateddocroot') != '') {\n\t\t\t\t$vhost_content .= $possible_deactivated_webroot;\n\t\t\t}\n\t\t} else {\n\t\t\tFileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true);\n\t\t\t$vhost_content .= $this->getWebroot($domain);\n\t\t\tif ($this->deactivated == false) {\n\t\t\t\t$vhost_content .= $this->composePhpOptions($domain, $ssl_vhost);\n\t\t\t\t$vhost_content .= $this->getStats($domain);\n\t\t\t}\n\t\t\t$vhost_content .= $this->getLogfiles($domain);\n\n\t\t\tif ($this->deactivated == false) {\n\t\t\t\tif ($domain['specialsettings'] != '' && ($ssl_vhost == false || ($ssl_vhost == true && $domain['include_specialsettings'] == 1))) {\n\t\t\t\t\t$vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif ($domain['ssl_specialsettings'] != '' && $ssl_vhost == true) {\n\t\t\t\t\t$vhost_content .= $this->processSpecialConfigTemplate($domain['ssl_specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif ($_vhost_content != '') {\n\t\t\t\t\t$vhost_content .= $_vhost_content;\n\t\t\t\t}\n\n\t\t\t\tif (Settings::Get('system.default_vhostconf') != '' && ($ssl_vhost == false || ($ssl_vhost == true && Settings::Get('system.include_default_vhostconf') == 1))) {\n\t\t\t\t\t$vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif (Settings::Get('system.default_sslvhostconf') != '' && $ssl_vhost == true) {\n\t\t\t\t\t$vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_sslvhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$vhost_content .= '</VirtualHost>' . \"\\n\";\n\n\t\treturn $vhost_content;\n\t}\n\n\t/**\n\t * We collect all servernames and Aliases\n\t */\n\tprotected function getServerNames($domain)\n\t{\n\t\t$servernames_text = '  ServerName ' . $domain['domain'] . \"\\n\";\n\n\t\t$server_alias = '';\n\t\tif ($domain['iswildcarddomain'] == '1') {\n\t\t\t$server_alias = '*.' . $domain['domain'];\n\t\t} elseif ($domain['wwwserveralias'] == '1') {\n\t\t\t$server_alias = 'www.' . $domain['domain'];\n\t\t}\n\n\t\tif (trim($server_alias) != '') {\n\t\t\t$servernames_text .= '  ServerAlias ' . $server_alias . \"\\n\";\n\t\t}\n\n\t\t$alias_domains_stmt = Database::prepare(\"\n\t\t\tSELECT `domain`, `iswildcarddomain`, `wwwserveralias`\n\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\tWHERE `aliasdomain`= :domainid\n\t\t\");\n\t\tDatabase::pexecute($alias_domains_stmt, [\n\t\t\t'domainid' => $domain['id']\n\t\t]);\n\n\t\twhile (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) {\n\t\t\t$server_alias = '  ServerAlias ' . $alias_domain['domain'];\n\n\t\t\tif ($alias_domain['iswildcarddomain'] == '1') {\n\t\t\t\t$server_alias .= ' *.' . $alias_domain['domain'];\n\t\t\t} else {\n\t\t\t\tif ($alias_domain['wwwserveralias'] == '1') {\n\t\t\t\t\t$server_alias .= ' www.' . $alias_domain['domain'];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$servernames_text .= $server_alias . \"\\n\";\n\t\t}\n\n\t\tswitch (Settings::Get('system.webserver_serveradmin')) {\n\t\t\tcase 'customer':\n\t\t\t\t$servernames_text .= '  ServerAdmin ' . $domain['email'] . \"\\n\";\n\t\t\t\tbreak;\n\t\t\tcase 'admin':\n\t\t\t\t$servernames_text .= '  ServerAdmin ' . $domain['admin_email'] . \"\\n\";\n\t\t\t\tbreak;\n\t\t\tcase 'global':\n\t\t\t\t$servernames_text .= '  ServerAdmin ' . Settings::Get('panel.adminmail') . \"\\n\";\n\t\t\t\tbreak;\n\t\t\tcase 'none':\n\t\t\tdefault:\n\t\t\t\t// empty\n\t\t}\n\n\t\treturn $servernames_text;\n\t}\n\n\t/**\n\t * Let's get the webroot\n\t */\n\tprotected function getWebroot($domain)\n\t{\n\t\t$webroot_text = '';\n\t\t$domain['customerroot'] = FileDir::makeCorrectDir($domain['customerroot']);\n\t\t$domain['documentroot'] = FileDir::makeCorrectDir($domain['documentroot']);\n\n\t\tif (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && Settings::Get('system.deactivateddocroot') != '') {\n\t\t\t$webroot_text .= '  # Using docroot for deactivated users/domains...' . \"\\n\";\n\t\t\t$webroot_text .= '  DocumentRoot \"' . rtrim(FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')), \"/\") . \"\\\"\\n\";\n\t\t\t$webroot_text .= '  <Directory \"' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . '\">' . \"\\n\";\n\t\t\t// >=apache-2.4 enabled?\n\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t$webroot_text .= '    Require all granted' . \"\\n\";\n\t\t\t\t$webroot_text .= '    AllowOverride All' . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$webroot_text .= '    Order allow,deny' . \"\\n\";\n\t\t\t\t$webroot_text .= '    allow from all' . \"\\n\";\n\t\t\t}\n\t\t\t$webroot_text .= '  </Directory>' . \"\\n\";\n\t\t\t$this->deactivated = true;\n\t\t} else {\n\t\t\t$webroot_text .= '  DocumentRoot \"' . rtrim($domain['documentroot'], \"/\") . \"\\\"\\n\";\n\t\t\t$this->deactivated = false;\n\t\t}\n\n\t\treturn $webroot_text;\n\t}\n\n\t/**\n\t * We put together the needed php options in the virtualhost entries\n\t *\n\t * @param array $domain\n\t * @param bool $ssl_vhost\n\t *\n\t * @return string\n\t */\n\tprotected function composePhpOptions(&$domain, $ssl_vhost = false)\n\t{\n\t\t$php_options_text = '';\n\n\t\tif ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {\n\t\t\t// This vHost has PHP enabled and we are using the regular mod_php\n\t\t\t$cmail = Customer::getCustomerDetail($domain['customerid'], 'email');\n\t\t\t$php_options_text .= '  php_admin_value sendmail_path \"/usr/sbin/sendmail -t -f ' . $cmail . '\"' . PHP_EOL;\n\n\t\t\tif ($domain['openbasedir'] == '1') {\n\t\t\t\tif ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], \":\") !== false) {\n\t\t\t\t\t$_phpappendopenbasedir = Domain::appendOpenBasedirPath($domain['customerroot'], true);\n\t\t\t\t} else if ($domain['openbasedir_path'] == '2' && strpos(dirname($domain['documentroot']) . '/', $domain['customerroot']) !== false) {\n\t\t\t\t\t$_phpappendopenbasedir = Domain::appendOpenBasedirPath(dirname($domain['documentroot']) . '/', true);\n\t\t\t\t} else {\n\t\t\t\t\t$_phpappendopenbasedir = Domain::appendOpenBasedirPath($domain['documentroot'], true);\n\t\t\t\t}\n\n\t\t\t\t$_custom_openbasedir = explode(':', Settings::Get('system.phpappendopenbasedir'));\n\t\t\t\tforeach ($_custom_openbasedir as $cobd) {\n\t\t\t\t\t$_phpappendopenbasedir .= Domain::appendOpenBasedirPath($cobd);\n\t\t\t\t}\n\n\t\t\t\t$php_options_text .= '  php_admin_value open_basedir \"' . $_phpappendopenbasedir . '\"' . \"\\n\";\n\t\t\t}\n\t\t} else {\n\t\t\t$php_options_text .= '  # PHP is disabled for this vHost' . \"\\n\";\n\t\t\t$php_options_text .= '  php_flag engine off' . \"\\n\";\n\t\t}\n\n\t\t/**\n\t\t * check for apache-itk-support, #1400\n\t\t * why is this here? Because it only works with mod_php\n\t\t */\n\t\tif (Settings::get('system.apacheitksupport') == 1) {\n\t\t\t$php_options_text .= '  <IfModule mpm_itk_module>' . \"\\n\";\n\t\t\t$php_options_text .= '    AssignUserID ' . $domain['loginname'] . ' ' . $domain['loginname'] . \"\\n\";\n\t\t\t$php_options_text .= '  </IfModule>' . \"\\n\";\n\t\t}\n\n\t\treturn $php_options_text;\n\t}\n\n\t/**\n\t * Lets set the text part for the stats software\n\t */\n\tprotected function getStats($domain)\n\t{\n\t\t$stats_text = '';\n\n\t\t$statTool = Settings::Get('system.traffictool');\n\t\t$statDomain = \"\";\n\t\tif ($statTool == 'awstats') {\n\t\t\t// awstats generates for each domain regardless of speciallogfile\n\t\t\t$statDomain = \"/\" . $domain['domain'];\n\t\t}\n\t\tif ($domain['speciallogfile'] == '1') {\n\t\t\t$statDomain = \"/\" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']);\n\t\t}\n\t\t$statDocroot = FileDir::makeCorrectFile($domain['customerroot'] . '/' . $statTool . $statDomain);\n\n\t\t$stats_text .= '  Alias /' . $statTool . ' \"' . $statDocroot . '\"' . \"\\n\";\n\t\t// awstats special requirement for icons\n\t\tif ($statTool == 'awstats') {\n\t\t\t$stats_text .= '  Alias /awstats-icon \"' . FileDir::makeCorrectDir(Settings::Get('system.awstats_icons')) . '\"' . \"\\n\";\n\t\t}\n\n\t\treturn $stats_text;\n\t}\n\n\t/**\n\t * Lets set the logfiles\n\t */\n\tprotected function getLogfiles($domain)\n\t{\n\t\t$logfiles_text = '';\n\n\t\tif ($domain['speciallogfile'] == '1') {\n\t\t\tif ($domain['parentdomainid'] == '0') {\n\t\t\t\t$speciallogfile = '-' . $domain['domain'];\n\t\t\t} else {\n\t\t\t\t$speciallogfile = '-' . $domain['parentdomain'];\n\t\t\t}\n\t\t} else {\n\t\t\t$speciallogfile = '';\n\t\t}\n\n\t\tif ($domain['writeerrorlog']) {\n\t\t\t// The normal access/error - logging is enabled\n\t\t\t$error_log = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-error.log');\n\t\t\t// Create the logfile if it does not exist (fixes #46)\n\t\t\ttouch($error_log);\n\t\t\tchmod($error_log, 0640);\n\t\t\tchown($error_log, Settings::Get('system.httpuser'));\n\t\t\tchgrp($error_log, Settings::Get('system.httpgroup'));\n\t\t\t// set error log log-level\n\t\t\t$logfiles_text .= '  LogLevel ' . Settings::Get('system.errorlog_level') . \"\\n\";\n\t\t} else {\n\t\t\t$error_log = '/dev/null';\n\t\t}\n\n\t\tif ($domain['writeaccesslog']) {\n\t\t\t$access_log = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log');\n\t\t\t// Create the logfile if it does not exist (fixes #46)\n\t\t\ttouch($access_log);\n\t\t\tchmod($access_log, 0640);\n\t\t\tchown($access_log, Settings::Get('system.httpuser'));\n\t\t\tchgrp($access_log, Settings::Get('system.httpgroup'));\n\t\t} else {\n\t\t\t$access_log = '/dev/null';\n\t\t}\n\n\t\t$logtype = 'combined';\n\t\tif (Settings::Get('system.logfiles_format') != '') {\n\t\t\t$logtype = 'frx_custom';\n\t\t\t$logfiles_text .= '  LogFormat ' . Settings::Get('system.logfiles_format') . ' ' . $logtype . \"\\n\";\n\t\t}\n\t\tif (Settings::Get('system.logfiles_type') == '2' && Settings::Get('system.logfiles_format') == '') {\n\t\t\t$logtype = 'vhost_combined';\n\t\t}\n\n\t\tif (Settings::Get('system.logfiles_piped') == '1' && Settings::Get('system.logfiles_script') != '') {\n\t\t\tif ($domain['writeerrorlog']) {\n\t\t\t\t// replace for error_log\n\t\t\t\t$command = PhpHelper::replaceVariables(Settings::Get('system.logfiles_script'), [\n\t\t\t\t\t'LOGFILE' => $error_log,\n\t\t\t\t\t'DOMAIN' => $domain['domain'],\n\t\t\t\t\t'CUSTOMER' => $domain['loginname']\n\t\t\t\t]);\n\t\t\t\t$logfiles_text .= '  ErrorLog \"|' . $command . \"\\\"\\n\";\n\t\t\t} else {\n\t\t\t\t$logfiles_text .= '  ErrorLog \"' . $error_log . '\"' . \"\\n\";\n\t\t\t}\n\t\t\tif ($domain['writeaccesslog']) {\n\t\t\t\t// replace for access_log\n\t\t\t\t$command = PhpHelper::replaceVariables(Settings::Get('system.logfiles_script'), [\n\t\t\t\t\t'LOGFILE' => $access_log,\n\t\t\t\t\t'DOMAIN' => $domain['domain'],\n\t\t\t\t\t'CUSTOMER' => $domain['loginname']\n\t\t\t\t]);\n\t\t\t\t$logfiles_text .= '  CustomLog \"|' . $command . '\" ' . $logtype . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$logfiles_text .= '  CustomLog \"' . $access_log . '\" ' . $logtype . \"\\n\";\n\t\t\t}\n\t\t} else {\n\t\t\t$logfiles_text .= '  ErrorLog \"' . $error_log . '\"' . \"\\n\";\n\t\t\t$logfiles_text .= '  CustomLog \"' . $access_log . '\" ' . $logtype . \"\\n\";\n\t\t}\n\n\t\tif (Settings::Get('system.traffictool') == 'awstats') {\n\t\t\tif ((int)$domain['parentdomainid'] == 0) {\n\t\t\t\t// prepare the aliases and subdomains for stats config files\n\t\t\t\t$server_alias = '';\n\t\t\t\t$alias_domains_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `domain`, `iswildcarddomain`, `wwwserveralias`\n\t\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `aliasdomain` = :domainid OR `parentdomainid` = :domainid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($alias_domains_stmt, [\n\t\t\t\t\t'domainid' => $domain['id']\n\t\t\t\t]);\n\n\t\t\t\twhile (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) {\n\t\t\t\t\t$server_alias .= ' ' . $alias_domain['domain'] . ' ';\n\n\t\t\t\t\tif ($alias_domain['iswildcarddomain'] == '1') {\n\t\t\t\t\t\t$server_alias .= '*.' . $alias_domain['domain'];\n\t\t\t\t\t} elseif ($alias_domain['wwwserveralias'] == '1') {\n\t\t\t\t\t\t$server_alias .= 'www.' . $alias_domain['domain'];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$alias = '';\n\t\t\t\tif ($domain['iswildcarddomain'] == '1') {\n\t\t\t\t\t$alias = '*.' . $domain['domain'];\n\t\t\t\t} elseif ($domain['wwwserveralias'] == '1') {\n\t\t\t\t\t$alias = 'www.' . $domain['domain'];\n\t\t\t\t}\n\n\t\t\t\t// After inserting the AWStats information,\n\t\t\t\t// be sure to build the awstats conf file as well\n\t\t\t\t// and chown it using $awstats_params, #258\n\t\t\t\t// Bug 960 + Bug 970 : Use full $domain instead of custom $awstats_params as following classes depend on the information\n\t\t\t\tStatistics::createAWStatsConf(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log', $domain['domain'], $alias . $server_alias, $domain['customerroot'], $domain);\n\t\t\t}\n\t\t}\n\n\t\treturn $logfiles_text;\n\t}\n\n\t/**\n\t * We compose the diroption entries for the paths\n\t */\n\tpublic function createFileDirOptions()\n\t{\n\t\t$result_stmt = Database::query(\"\n\t\t\tSELECT `htac`.*, `c`.`guid`, `c`.`documentroot` AS `customerroot`\n\t\t\tFROM `\" . TABLE_PANEL_HTACCESS . \"` `htac`\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` `c` USING (`customerid`)\n\t\t\tORDER BY `htac`.`path`\n\t\t\");\n\t\t$diroptions = [];\n\n\t\twhile ($row_diroptions = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif ($row_diroptions['customerid'] != 0 && isset($row_diroptions['customerroot']) && $row_diroptions['customerroot'] != '') {\n\t\t\t\t$diroptions[$row_diroptions['path']] = $row_diroptions;\n\t\t\t\t$diroptions[$row_diroptions['path']]['htpasswds'] = [];\n\t\t\t}\n\t\t}\n\n\t\t$result_stmt = Database::query(\"\n\t\t\tSELECT `htpw`.*, `c`.`guid`, `c`.`documentroot` AS `customerroot`\n\t\t\tFROM `\" . TABLE_PANEL_HTPASSWDS . \"` `htpw`\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` `c` USING (`customerid`)\n\t\t\tORDER BY `htpw`.`path`, `htpw`.`username`\n\t\t\");\n\n\t\twhile ($row_htpasswds = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif ($row_htpasswds['customerid'] != 0 && isset($row_htpasswds['customerroot']) && $row_htpasswds['customerroot'] != '') {\n\t\t\t\tif (!isset($diroptions[$row_htpasswds['path']]) || !is_array($diroptions[$row_htpasswds['path']])) {\n\t\t\t\t\t$diroptions[$row_htpasswds['path']] = [];\n\t\t\t\t}\n\n\t\t\t\t$diroptions[$row_htpasswds['path']]['path'] = $row_htpasswds['path'];\n\t\t\t\t$diroptions[$row_htpasswds['path']]['guid'] = $row_htpasswds['guid'];\n\t\t\t\t$diroptions[$row_htpasswds['path']]['customerroot'] = $row_htpasswds['customerroot'];\n\t\t\t\t$diroptions[$row_htpasswds['path']]['customerid'] = $row_htpasswds['customerid'];\n\t\t\t\t$diroptions[$row_htpasswds['path']]['htpasswds'][] = $row_htpasswds;\n\t\t\t}\n\t\t}\n\n\t\tforeach ($diroptions as $row_diroptions) {\n\t\t\t$row_diroptions['path'] = FileDir::makeCorrectDir($row_diroptions['path']);\n\t\t\tFileDir::mkDirWithCorrectOwnership($row_diroptions['customerroot'], $row_diroptions['path'], $row_diroptions['guid'], $row_diroptions['guid']);\n\t\t\t$diroptions_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_diroptions') . '/40_froxlor_diroption_' . md5($row_diroptions['path']) . '.conf');\n\n\t\t\tif (!isset($this->diroptions_data[$diroptions_filename])) {\n\t\t\t\t$this->diroptions_data[$diroptions_filename] = '';\n\t\t\t}\n\n\t\t\tif (is_dir($row_diroptions['path'])) {\n\t\t\t\t$cperlenabled = Customer::customerHasPerlEnabled($row_diroptions['customerid']);\n\n\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '<Directory \"' . $row_diroptions['path'] . '\">' . \"\\n\";\n\n\t\t\t\tif (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '1') {\n\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  Options +Indexes';\n\n\t\t\t\t\t// add perl options if enabled\n\t\t\t\t\tif ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') {\n\t\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . \"\\n\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Setting Options +Indexes for ' . $row_diroptions['path']);\n\t\t\t\t}\n\n\t\t\t\tif (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '0') {\n\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  Options -Indexes';\n\n\t\t\t\t\t// add perl options if enabled\n\t\t\t\t\tif ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') {\n\t\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . \"\\n\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Setting Options -Indexes for ' . $row_diroptions['path']);\n\t\t\t\t}\n\n\t\t\t\t$statusCodes = [\n\t\t\t\t\t'404',\n\t\t\t\t\t'403',\n\t\t\t\t\t'500'\n\t\t\t\t];\n\t\t\t\tforeach ($statusCodes as $statusCode) {\n\t\t\t\t\tif (isset($row_diroptions['error' . $statusCode . 'path']) && $row_diroptions['error' . $statusCode . 'path'] != '') {\n\t\t\t\t\t\t$defhandler = $row_diroptions['error' . $statusCode . 'path'];\n\t\t\t\t\t\tif (!Validate::validateUrl($defhandler)) {\n\t\t\t\t\t\t\tif (substr($defhandler, 0, 1) != '\"' && substr($defhandler, -1, 1) != '\"') {\n\t\t\t\t\t\t\t\t$defhandler = '\"' . FileDir::makeCorrectFile($defhandler) . '\"';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  ErrorDocument ' . $statusCode . ' ' . $defhandler . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') {\n\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  AllowOverride None' . \"\\n\";\n\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  AddHandler cgi-script .cgi .pl' . \"\\n\";\n\t\t\t\t\t// >=apache-2.4 enabled?\n\t\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\t\t$mypath_dir = new Directory($row_diroptions['path']);\n\t\t\t\t\t\t// only create the' require all granted' if there is no active directory-protection\n\t\t\t\t\t\t// for this path, as this would be the first require and therefore grant all access\n\t\t\t\t\t\tif ($mypath_dir->isUserProtected() == false) {\n\t\t\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  Require all granted' . \"\\n\";\n\t\t\t\t\t\t\t// $this->diroptions_data[$diroptions_filename] .= ' AllowOverride All' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  Order allow,deny' . \"\\n\";\n\t\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  Allow from all' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Enabling perl execution for ' . $row_diroptions['path']);\n\n\t\t\t\t\t// check for suexec-workaround, #319\n\t\t\t\t\tif ((int)Settings::Get('perl.suexecworkaround') == 1) {\n\t\t\t\t\t\t// symlink this directory to suexec-safe-path\n\t\t\t\t\t\t$loginname = Customer::getCustomerDetail($row_diroptions['customerid'], 'loginname');\n\t\t\t\t\t\t$suexecpath = FileDir::makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/');\n\n\t\t\t\t\t\tif (!file_exists($suexecpath)) {\n\t\t\t\t\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($suexecpath));\n\t\t\t\t\t\t\tFileDir::safe_exec('chown -R ' . escapeshellarg($row_diroptions['guid']) . ':' . escapeshellarg($row_diroptions['guid']) . ' ' . escapeshellarg($suexecpath));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// symlink to {$givenpath}/cgi-bin\n\t\t\t\t\t\t// NOTE: symlinks are FILES, so do not append a / here\n\t\t\t\t\t\t$perlsymlink = FileDir::makeCorrectFile($row_diroptions['path'] . '/cgi-bin');\n\t\t\t\t\t\tif (!file_exists($perlsymlink)) {\n\t\t\t\t\t\t\tFileDir::safe_exec('ln -s ' . escapeshellarg($suexecpath) . ' ' . escapeshellarg($perlsymlink));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tFileDir::safe_exec('chown -h ' . escapeshellarg($row_diroptions['guid']) . ':' . escapeshellarg($row_diroptions['guid']) . ' ' . escapeshellarg($perlsymlink));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// if no perl-execution is enabled but the workaround is,\n\t\t\t\t\t// we have to remove the symlink and folder in suexecpath\n\t\t\t\t\tif ((int)Settings::Get('perl.suexecworkaround') == 1) {\n\t\t\t\t\t\t$loginname = Customer::getCustomerDetail($row_diroptions['customerid'], 'loginname');\n\t\t\t\t\t\t$suexecpath = FileDir::makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/');\n\t\t\t\t\t\t$perlsymlink = FileDir::makeCorrectFile($row_diroptions['path'] . '/cgi-bin');\n\n\t\t\t\t\t\t// remove symlink\n\t\t\t\t\t\tif (file_exists($perlsymlink)) {\n\t\t\t\t\t\t\tFileDir::safe_exec('rm -f ' . escapeshellarg($perlsymlink));\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// remove folder in suexec-path\n\t\t\t\t\t\tif (file_exists($suexecpath)) {\n\t\t\t\t\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($suexecpath));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (count($row_diroptions['htpasswds']) > 0) {\n\t\t\t\t\t$htpasswd_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $row_diroptions['customerid'] . '-' . md5($row_diroptions['path']) . '.htpasswd');\n\n\t\t\t\t\tif (!isset($this->htpasswds_data[$htpasswd_filename])) {\n\t\t\t\t\t\t$this->htpasswds_data[$htpasswd_filename] = '';\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach ($row_diroptions['htpasswds'] as $row_htpasswd) {\n\t\t\t\t\t\t$this->htpasswds_data[$htpasswd_filename] .= $row_htpasswd['username'] . ':' . $row_htpasswd['password'] . \"\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  AuthType Basic' . \"\\n\";\n\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  AuthName \"' . $row_htpasswd['authname'] . '\"' . \"\\n\";\n\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  AuthUserFile ' . $htpasswd_filename . \"\\n\";\n\t\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '  require valid-user' . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\t$this->diroptions_data[$diroptions_filename] .= '</Directory>' . \"\\n\";\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * We write the configs\n\t */\n\tpublic function writeConfigs()\n\t{\n\t\t// Write diroptions\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"apache::writeConfigs: rebuilding \" . Settings::Get('system.apacheconf_diroptions'));\n\n\t\tif (count($this->diroptions_data) > 0) {\n\t\t\t$optsDir = new Directory(Settings::Get('system.apacheconf_diroptions'));\n\t\t\tif (!$optsDir->isConfigDir()) {\n\t\t\t\t// Save one big file\n\t\t\t\t$diroptions_file = '';\n\n\t\t\t\tforeach ($this->diroptions_data as $diroptions_filename => $diroptions_content) {\n\t\t\t\t\t$diroptions_file .= $diroptions_content . \"\\n\\n\";\n\t\t\t\t}\n\n\t\t\t\t$diroptions_filename = Settings::Get('system.apacheconf_diroptions');\n\n\t\t\t\t// Apply header\n\t\t\t\t$diroptions_file = '# ' . basename($diroptions_filename) . \"\\n\" . '# Created ' . date('d.m.Y H:i') . \"\\n\" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . \"\\n\" . \"\\n\" . $diroptions_file;\n\t\t\t\t$diroptions_file_handler = fopen($diroptions_filename, 'w');\n\t\t\t\tfwrite($diroptions_file_handler, $diroptions_file);\n\t\t\t\tfclose($diroptions_file_handler);\n\t\t\t} else {\n\t\t\t\tif (!file_exists(Settings::Get('system.apacheconf_diroptions'))) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.apacheconf_diroptions'))));\n\t\t\t\t\tFileDir::safe_exec('mkdir ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.apacheconf_diroptions'))));\n\t\t\t\t}\n\n\t\t\t\t// Write a single file for every diroption\n\t\t\t\tforeach ($this->diroptions_data as $diroptions_filename => $diroptions_file) {\n\t\t\t\t\t$this->known_diroptionsfilenames[] = basename($diroptions_filename);\n\n\t\t\t\t\t// Apply header\n\t\t\t\t\t$diroptions_file = '# ' . basename($diroptions_filename) . \"\\n\" . '# Created ' . date('d.m.Y H:i') . \"\\n\" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . \"\\n\" . \"\\n\" . $diroptions_file;\n\t\t\t\t\t$diroptions_file_handler = fopen($diroptions_filename, 'w');\n\t\t\t\t\tfwrite($diroptions_file_handler, $diroptions_file);\n\t\t\t\t\tfclose($diroptions_file_handler);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Write htpasswds\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"apache::writeConfigs: rebuilding \" . Settings::Get('system.apacheconf_htpasswddir'));\n\n\t\tif (count($this->htpasswds_data) > 0) {\n\t\t\tif (!file_exists(Settings::Get('system.apacheconf_htpasswddir'))) {\n\t\t\t\t$umask = umask();\n\t\t\t\tumask(0000);\n\t\t\t\tmkdir(Settings::Get('system.apacheconf_htpasswddir'), 0751);\n\t\t\t\tumask($umask);\n\t\t\t}\n\n\t\t\t$htpasswdDir = new Directory(Settings::Get('system.apacheconf_htpasswddir'));\n\t\t\tif ($htpasswdDir->isConfigDir(true)) {\n\t\t\t\tforeach ($this->htpasswds_data as $htpasswd_filename => $htpasswd_file) {\n\t\t\t\t\t$this->known_htpasswdsfilenames[] = basename($htpasswd_filename);\n\t\t\t\t\t$htpasswd_file_handler = fopen($htpasswd_filename, 'w');\n\t\t\t\t\tfwrite($htpasswd_file_handler, $htpasswd_file);\n\t\t\t\t\tfclose($htpasswd_file_handler);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, 'WARNING!!! ' . Settings::Get('system.apacheconf_htpasswddir') . ' is not a directory. htpasswd directory protection is disabled!!!');\n\t\t\t}\n\t\t}\n\n\t\t// Write virtualhosts\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"apache::writeConfigs: rebuilding \" . Settings::Get('system.apacheconf_vhost'));\n\n\t\tif (count($this->virtualhosts_data) > 0) {\n\t\t\t$vhostDir = new Directory(Settings::Get('system.apacheconf_vhost'));\n\t\t\tif (!$vhostDir->isConfigDir()) {\n\t\t\t\t// Save one big file\n\t\t\t\t$vhosts_file = '';\n\n\t\t\t\t// sort by filename so the order is:\n\t\t\t\t// 1. subdomains x-29\n\t\t\t\t// 2. subdomains as main-domains 30\n\t\t\t\t// 3. main-domains 35\n\t\t\t\t// #437\n\t\t\t\tksort($this->virtualhosts_data);\n\n\t\t\t\tforeach ($this->virtualhosts_data as $vhosts_filename => $vhost_content) {\n\t\t\t\t\t$vhosts_file .= $vhost_content . \"\\n\\n\";\n\t\t\t\t}\n\n\t\t\t\t// Include diroptions file in case it exists\n\t\t\t\tif (file_exists(Settings::Get('system.apacheconf_diroptions'))) {\n\t\t\t\t\t$vhosts_file .= \"\\n\" . 'Include ' . Settings::Get('system.apacheconf_diroptions') . \"\\n\\n\";\n\t\t\t\t}\n\n\t\t\t\t$vhosts_filename = Settings::Get('system.apacheconf_vhost');\n\n\t\t\t\t// Apply header\n\t\t\t\t$vhosts_file = '# ' . basename($vhosts_filename) . \"\\n\" . '# Created ' . date('d.m.Y H:i') . \"\\n\" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . \"\\n\" . \"\\n\" . $vhosts_file;\n\t\t\t\t$vhosts_file_handler = fopen($vhosts_filename, 'w');\n\t\t\t\tfwrite($vhosts_file_handler, $vhosts_file);\n\t\t\t\tfclose($vhosts_file_handler);\n\t\t\t} else {\n\t\t\t\tif (!file_exists(Settings::Get('system.apacheconf_vhost'))) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'))));\n\t\t\t\t\tFileDir::safe_exec('mkdir ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'))));\n\t\t\t\t}\n\n\t\t\t\t// Write a single file for every vhost\n\t\t\t\tforeach ($this->virtualhosts_data as $vhosts_filename => $vhosts_file) {\n\t\t\t\t\t// Apply header\n\t\t\t\t\t$vhosts_file = '# ' . basename($vhosts_filename) . \"\\n\" . '# Created ' . date('d.m.Y H:i') . \"\\n\" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . \"\\n\" . \"\\n\" . $vhosts_file;\n\t\t\t\t\t$vhosts_file_handler = fopen($vhosts_filename, 'w');\n\t\t\t\t\tfwrite($vhosts_file_handler, $vhosts_file);\n\t\t\t\t\tfclose($vhosts_file_handler);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/ApacheFcgi.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http;\n\nuse Froxlor\\Cron\\Http\\Php\\PhpInterface;\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Http\\Directory;\nuse Froxlor\\Settings;\n\n/**\n * @author        Florian Lippert <flo@syscp.org> (2003-2009)\n * @author        Froxlor team <team@froxlor.org> (2010-)\n */\nclass ApacheFcgi extends Apache\n{\n\n\tpublic function createOwnVhostStarter()\n\t{\n\t\tif (Settings::Get('system.mod_fcgid_ownvhost') == '1' || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.enabled_ownvhost') == '1')) {\n\t\t\t$mypath = Froxlor::getInstallDir();\n\n\t\t\tif (Settings::Get('system.mod_fcgid_ownvhost') == '1') {\n\t\t\t\t$user = Settings::Get('system.mod_fcgid_httpuser');\n\t\t\t\t$group = Settings::Get('system.mod_fcgid_httpgroup');\n\t\t\t} elseif (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.enabled_ownvhost') == '1') {\n\t\t\t\t$user = Settings::Get('phpfpm.vhost_httpuser');\n\t\t\t\t$group = Settings::Get('phpfpm.vhost_httpgroup');\n\n\t\t\t\t// get fpm config\n\t\t\t\t$fpm_sel_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT f.id FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` f\n\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_PHPCONFIGS . \"` p ON p.fpmsettingid = f.id\n\t\t\t\t\tWHERE p.id = :phpconfigid\n\t\t\t\t\");\n\t\t\t\t$fpm_config = Database::pexecute_first($fpm_sel_stmt, [\n\t\t\t\t\t'phpconfigid' => Settings::Get('phpfpm.vhost_defaultini')\n\t\t\t\t]);\n\t\t\t}\n\n\t\t\t$domain = [\n\t\t\t\t'id' => 'none',\n\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t'adminid' => 1, /* first admin-user (superadmin) */\n\t\t\t\t'mod_fcgid_starter' => -1,\n\t\t\t\t'mod_fcgid_maxrequests' => -1,\n\t\t\t\t'guid' => $user,\n\t\t\t\t'openbasedir' => 0,\n\t\t\t\t'email' => Settings::Get('panel.adminmail'),\n\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t'customerroot' => $mypath,\n\t\t\t\t'fpm_config_id' => isset($fpm_config['id']) ? $fpm_config['id'] : 1\n\t\t\t];\n\n\t\t\t// all the files and folders have to belong to the local user\n\t\t\t// now because we also use fcgid for our own vhost\n\t\t\tFileDir::safe_exec('chown -R ' . $user . ':' . $group . ' ' . escapeshellarg($mypath));\n\n\t\t\t// get php.ini for our own vhost\n\t\t\t$php = new PhpInterface($domain);\n\n\t\t\t// get php-config\n\t\t\tif (Settings::Get('phpfpm.enabled') == '1') {\n\t\t\t\t// fpm\n\t\t\t\t$phpconfig = $php->getPhpConfig(Settings::Get('phpfpm.vhost_defaultini'));\n\t\t\t} else {\n\t\t\t\t// fcgid\n\t\t\t\t$phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost'));\n\t\t\t}\n\n\t\t\t// create starter-file | config-file\n\t\t\t$php->getInterface()->createConfig($phpconfig);\n\n\t\t\t// create php.ini (fpm does nothing here, as it\n\t\t\t// defines ini-settings in its pool config)\n\t\t\t$php->getInterface()->createIniFile($phpconfig);\n\t\t}\n\t}\n\n\tprotected function composePhpOptions(&$domain, $ssl_vhost = false)\n\t{\n\t\t$php_options_text = '';\n\n\t\tif ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {\n\t\t\t$php = new PhpInterface($domain);\n\t\t\t$phpconfig = $php->getPhpConfig((int)$domain['phpsettingid']);\n\n\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$srvName = 'fpm.external';\n\t\t\t\tif ($domain['ssl'] == 1 && $ssl_vhost) {\n\t\t\t\t\t$srvName = 'ssl-fpm.external';\n\t\t\t\t}\n\t\t\t\t// #1317 - perl is executed via apache and therefore, when using fpm, does not know the user\n\t\t\t\t// which perl is supposed to run as, hence the need for Suexec need\n\t\t\t\tif (Customer::customerHasPerlEnabled($domain['customerid'])) {\n\t\t\t\t\t$php_options_text .= '  SuexecUserGroup \"' . $domain['loginname'] . '\" \"' . $domain['loginname'] . '\"' . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\t$domain['fpm_socket'] = $php->getInterface()->getSocketFile();\n\n\t\t\t\t// mod_proxy stuff for apache-2.4\n\t\t\t\tif (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') {\n\n\t\t\t\t\t$php_options_text .= '  <Directory \"' . FileDir::makeCorrectDir($domain['documentroot']) . '\">' . \"\\n\";\n\n\t\t\t\t\t$filesmatch = $phpconfig['fpm_settings']['limit_extensions'];\n\t\t\t\t\t$extensions = explode(\" \", $filesmatch);\n\t\t\t\t\t$filesmatch = \"\";\n\t\t\t\t\tforeach ($extensions as $ext) {\n\t\t\t\t\t\t$filesmatch .= substr($ext, 1) . '|';\n\t\t\t\t\t}\n\t\t\t\t\t// start block, cut off last pipe and close block\n\t\t\t\t\t$filesmatch = '(' . str_replace(\".\", \"\\.\", substr($filesmatch, 0, -1)) . ')';\n\t\t\t\t\t$php_options_text .= '  <FilesMatch \\.' . $filesmatch . '$>' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '    <If \"-f %{SCRIPT_FILENAME}\">' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '      SetHandler proxy:unix:' . $domain['fpm_socket'] . '|fcgi://localhost' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '    </If>' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '  </FilesMatch>' . \"\\n\";\n\n\t\t\t\t\t$mypath_dir = new Directory($domain['documentroot']);\n\t\t\t\t\t// only create the \"require all granted\" directive if there is no active directory-protection\n\t\t\t\t\t// for this path, as this would be the first require and therefore grant all access\n\t\t\t\t\tif ($mypath_dir->isUserProtected() == false) {\n\t\t\t\t\t\tif ($phpconfig['pass_authorizationheader'] == '1') {\n\t\t\t\t\t\t\t$php_options_text .= '    CGIPassAuth On' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$php_options_text .= '    Require all granted' . \"\\n\";\n\t\t\t\t\t\t$php_options_text .= '    AllowOverride All' . \"\\n\";\n\t\t\t\t\t} elseif ($phpconfig['pass_authorizationheader'] == '1') {\n\t\t\t\t\t\t// allow Pass of Authorization header\n\t\t\t\t\t\t$php_options_text .= '    CGIPassAuth On' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\t$php_options_text .= '  </Directory>' . \"\\n\";\n\t\t\t\t} else {\n\t\t\t\t\t$addheader = \"\";\n\t\t\t\t\tif ($phpconfig['pass_authorizationheader'] == '1') {\n\t\t\t\t\t\t$addheader = \" -pass-header Authorization\";\n\t\t\t\t\t}\n\t\t\t\t\t$php_options_text .= '  FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $domain['fpm_socket'] . ' -idle-timeout ' . $phpconfig['fpm_settings']['idle_timeout'] . $addheader . \"\\n\";\n\t\t\t\t\t$php_options_text .= '  <Directory \"' . FileDir::makeCorrectDir($domain['documentroot']) . '\">' . \"\\n\";\n\t\t\t\t\t$filesmatch = $phpconfig['fpm_settings']['limit_extensions'];\n\t\t\t\t\t$extensions = explode(\" \", $filesmatch);\n\t\t\t\t\t$filesmatch = \"\";\n\t\t\t\t\tforeach ($extensions as $ext) {\n\t\t\t\t\t\t$filesmatch .= substr($ext, 1) . '|';\n\t\t\t\t\t}\n\t\t\t\t\t// start block, cut off last pipe and close block\n\t\t\t\t\t$filesmatch = '(' . str_replace(\".\", \"\\.\", substr($filesmatch, 0, -1)) . ')';\n\t\t\t\t\t$php_options_text .= '    <FilesMatch \\.' . $filesmatch . '$>' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '      SetHandler php-fastcgi' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '      Action php-fastcgi /fastcgiphp' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '      Options +ExecCGI' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '    </FilesMatch>' . \"\\n\";\n\t\t\t\t\t// >=apache-2.4 enabled?\n\t\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\t\t$mypath_dir = new Directory($domain['documentroot']);\n\t\t\t\t\t\t// only create the require all granted if there is not active directory-protection\n\t\t\t\t\t\t// for this path, as this would be the first require and therefore grant all access\n\t\t\t\t\t\tif ($mypath_dir->isUserProtected() == false) {\n\t\t\t\t\t\t\t$php_options_text .= '    Require all granted' . \"\\n\";\n\t\t\t\t\t\t\t$php_options_text .= '    AllowOverride All' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$php_options_text .= '    Order allow,deny' . \"\\n\";\n\t\t\t\t\t\t$php_options_text .= '    allow from all' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\t$php_options_text .= '  </Directory>' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '  Alias /fastcgiphp ' . $php->getInterface()->getAliasConfigDir() . $srvName . \"\\n\";\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$php_options_text .= '  FcgidIdleTimeout ' . Settings::Get('system.mod_fcgid_idle_timeout') . \"\\n\";\n\t\t\t\tif ($phpconfig['pass_authorizationheader'] == '1') {\n\t\t\t\t\t$php_options_text .= '  FcgidPassHeader     Authorization' . \"\\n\";\n\t\t\t\t}\n\t\t\t\tif ((int)Settings::Get('system.mod_fcgid_wrapper') == 0) {\n\t\t\t\t\t$php_options_text .= '  SuexecUserGroup \"' . $domain['loginname'] . '\" \"' . $domain['loginname'] . '\"' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '  ScriptAlias /php/ ' . $php->getInterface()->getConfigDir() . \"\\n\";\n\t\t\t\t} else {\n\t\t\t\t\t$php_options_text .= '  SuexecUserGroup \"' . $domain['loginname'] . '\" \"' . $domain['loginname'] . '\"' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '  <Directory \"' . FileDir::makeCorrectDir($domain['documentroot']) . '\">' . \"\\n\";\n\t\t\t\t\t$file_extensions = explode(' ', $phpconfig['file_extensions']);\n\t\t\t\t\t$php_options_text .= '    <FilesMatch \"\\.(' . implode('|', $file_extensions) . ')$\">' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '      SetHandler fcgid-script' . \"\\n\";\n\t\t\t\t\tforeach ($file_extensions as $file_extension) {\n\t\t\t\t\t\t$php_options_text .= '      FcgidWrapper ' . $php->getInterface()->getStarterFile() . ' .' . $file_extension . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\t$php_options_text .= '      Options +ExecCGI' . \"\\n\";\n\t\t\t\t\t$php_options_text .= '    </FilesMatch>' . \"\\n\";\n\t\t\t\t\t// >=apache-2.4 enabled?\n\t\t\t\t\tif (Settings::Get('system.apache24') == '1') {\n\t\t\t\t\t\t$mypath_dir = new Directory($domain['documentroot']);\n\t\t\t\t\t\t// only create the require all granted if there is not active directory-protection\n\t\t\t\t\t\t// for this path, as this would be the first require and therefore grant all access\n\t\t\t\t\t\tif ($mypath_dir->isUserProtected() == false) {\n\t\t\t\t\t\t\t$php_options_text .= '    Require all granted' . \"\\n\";\n\t\t\t\t\t\t\t$php_options_text .= '    AllowOverride All' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$php_options_text .= '    Order allow,deny' . \"\\n\";\n\t\t\t\t\t\t$php_options_text .= '    allow from all' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t\t$php_options_text .= '  </Directory>' . \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// create starter-file | config-file\n\t\t\t$php->getInterface()->createConfig($phpconfig);\n\n\t\t\t// create php.ini (fpm does nothing here, as it\n\t\t\t// defines ini-settings in its pool config)\n\t\t\t$php->getInterface()->createIniFile($phpconfig);\n\t\t} else {\n\t\t\t$php_options_text .= '  # PHP is disabled for this vHost' . \"\\n\";\n\t\t}\n\n\t\treturn $php_options_text;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/ConfigIO.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse PDO;\nuse RecursiveDirectoryIterator;\nuse RecursiveIteratorIterator;\n\nclass ConfigIO\n{\n\n\t/**\n\t * clean up former created configs, including (if enabled)\n\t * awstats, fcgid, php-fpm and of course automatically created\n\t * webserver vhost and diroption files\n\t *\n\t * @return null\n\t */\n\tpublic function cleanUp()\n\t{\n\t\t// old error logs\n\t\t$this->cleanErrLogs();\n\n\t\t// awstats files\n\t\t$this->cleanAwstatsFiles();\n\n\t\t// fcgid files\n\t\t$this->cleanFcgidFiles();\n\n\t\t// php-fpm files\n\t\t$this->cleanFpmFiles();\n\n\t\t// clean webserver-configs\n\t\t$this->cleanWebserverConfigs();\n\n\t\t// old htpasswd files\n\t\t$this->cleanHtpasswdFiles();\n\n\t\t// customer-specified ssl-certificates\n\t\t$this->cleanCustomerSslCerts();\n\t}\n\n\tprivate function cleanErrLogs()\n\t{\n\t\t$err_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . \"/logs/\");\n\t\tif (@is_dir($err_dir)) {\n\t\t\t// now get rid of old stuff\n\t\t\t// (but append /*.log so we don't delete the directory)\n\t\t\t$err_dir .= '/*.log';\n\t\t\tFileDir::safe_exec('rm -f ' . FileDir::makeCorrectFile($err_dir));\n\t\t}\n\t}\n\n\t/**\n\t * remove awstats related configuration files before regeneration\n\t *\n\t * @return null\n\t */\n\tprivate function cleanAwstatsFiles()\n\t{\n\t\tif (Settings::Get('system.traffictool') != 'awstats') {\n\t\t\treturn;\n\t\t}\n\n\t\t// dhr: cleanout froxlor-generated awstats configs prior to re-creation\n\t\t$awstatsclean = [];\n\t\t$awstatsclean['header'] = \"## GENERATED BY FROXLOR\\n\";\n\t\t$awstatsclean['headerold'] = \"## GENERATED BY SYSCP\\n\";\n\t\t$awstatsclean['path'] = $this->getFile('system', 'awstats_conf');\n\n\t\t/**\n\t\t * don't do anything if the directory does not exist\n\t\t * (e.g.\n\t\t * awstats not installed yet or whatever)\n\t\t * fixes #45\n\t\t */\n\t\tif ($awstatsclean['path'] !== false && is_dir($awstatsclean['path'])) {\n\t\t\t$awstatsclean['dir'] = dir($awstatsclean['path']);\n\t\t\twhile ($awstatsclean['entry'] = $awstatsclean['dir']->read()) {\n\t\t\t\t$awstatsclean['fullentry'] = FileDir::makeCorrectFile($awstatsclean['path'] . '/' . $awstatsclean['entry']);\n\t\t\t\t/**\n\t\t\t\t * don't do anything if the file does not exist\n\t\t\t\t */\n\t\t\t\tif (@file_exists($awstatsclean['fullentry']) && $awstatsclean['entry'] != '.' && $awstatsclean['entry'] != '..') {\n\t\t\t\t\t$awstatsclean['fh'] = fopen($awstatsclean['fullentry'], 'r');\n\t\t\t\t\t$awstatsclean['headerRead'] = fgets($awstatsclean['fh'], strlen($awstatsclean['header']) + 1);\n\t\t\t\t\tfclose($awstatsclean['fh']);\n\n\t\t\t\t\tif ($awstatsclean['headerRead'] == $awstatsclean['header'] || $awstatsclean['headerRead'] == $awstatsclean['headerold']) {\n\t\t\t\t\t\t$awstats_conf_file = FileDir::makeCorrectFile($awstatsclean['fullentry']);\n\t\t\t\t\t\t@unlink($awstats_conf_file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tunset($awstatsclean);\n\t\t// end dhr\n\t}\n\n\t/**\n\t * returns a file/directory from the settings and checks whether it exists\n\t *\n\t * @param string $group\n\t *            settings-group\n\t * @param string $varname\n\t *            var-name\n\t * @param boolean $check_exists\n\t *            check if the file exists\n\t *\n\t * @return string|boolean complete path including filename if any or false on error\n\t */\n\tprivate function getFile($group, $varname, $check_exists = true)\n\t{\n\t\t// read from settings\n\t\t$file = Settings::Get($group . '.' . $varname);\n\n\t\t// check whether it exists\n\t\tif ($check_exists && @file_exists($file) == false) {\n\t\t\treturn false;\n\t\t}\n\t\treturn $file;\n\t}\n\n\t/**\n\t * remove fcgid related configuration files before regeneration\n\t *\n\t * @return null\n\t */\n\tprivate function cleanFcgidFiles()\n\t{\n\t\tif (Settings::Get('system.mod_fcgid') == '0') {\n\t\t\treturn;\n\t\t}\n\n\t\t// get correct directory\n\t\t$configdir = $this->getFile('system', 'mod_fcgid_configdir');\n\t\tif ($configdir !== false) {\n\t\t\t$configdir = FileDir::makeCorrectDir($configdir);\n\n\t\t\tif (@is_dir($configdir)) {\n\t\t\t\t// create directory iterator\n\t\t\t\t$its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($configdir));\n\n\t\t\t\t// iterate through all subdirs,\n\t\t\t\t// look for php-fcgi-starter files\n\t\t\t\t// and take immutable-flag away from them\n\t\t\t\t// so we can delete them :)\n\t\t\t\tforeach ($its as $it) {\n\t\t\t\t\tif ($it->isFile() && $it->getFilename() == 'php-fcgi-starter') {\n\t\t\t\t\t\t// set chattr -i\n\t\t\t\t\t\tFileDir::removeImmutable($its->getPathname());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// now get rid of old stuff\n\t\t\t\t// (but append /* so we don't delete the directory)\n\t\t\t\t$configdir .= '/*';\n\t\t\t\tFileDir::safe_exec('rm -rf ' . FileDir::makeCorrectFile($configdir));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * remove php-fpm related configuration files before regeneration\n\t *\n\t * @return null\n\t */\n\tprivate function cleanFpmFiles()\n\t{\n\t\tif (Settings::Get('phpfpm.enabled') == '0') {\n\t\t\treturn;\n\t\t}\n\n\t\t// get all fpm config paths\n\t\t$fpmconf_sel = Database::prepare(\"SELECT config_dir FROM `\" . TABLE_PANEL_FPMDAEMONS . \"`\");\n\t\tDatabase::pexecute($fpmconf_sel);\n\t\t$fpmconf_paths = $fpmconf_sel->fetchAll(PDO::FETCH_ASSOC);\n\t\t// clean all php-fpm config-dirs\n\t\tforeach ($fpmconf_paths as $configdir) {\n\t\t\t$configdir = FileDir::makeCorrectDir($configdir['config_dir']);\n\t\t\tif (@is_dir($configdir)) {\n\t\t\t\t// now get rid of old stuff\n\t\t\t\t// (but append /*.conf so we don't delete the directory)\n\t\t\t\t$configdir .= '/*.conf';\n\t\t\t\tFileDir::safe_exec('rm -f ' . FileDir::makeCorrectFile($configdir));\n\t\t\t} else {\n\t\t\t\tFileDir::safe_exec('mkdir -p ' . $configdir);\n\t\t\t}\n\t\t}\n\n\t\t// also remove aliasconfigdir #1273\n\t\t$aliasconfigdir = $this->getFile('phpfpm', 'aliasconfigdir');\n\t\tif ($aliasconfigdir !== false) {\n\t\t\t$aliasconfigdir = FileDir::makeCorrectDir($aliasconfigdir);\n\t\t\tif (@is_dir($aliasconfigdir)) {\n\t\t\t\t$aliasconfigdir .= '/*';\n\t\t\t\tFileDir::safe_exec('rm -rf ' . FileDir::makeCorrectFile($aliasconfigdir));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * remove webserver related configuration files before regeneration\n\t *\n\t * @return null\n\t */\n\tprivate function cleanWebserverConfigs()\n\t{\n\t\t// get directories\n\t\t$configdirs = [];\n\t\t$dir = $this->getFile('system', 'apacheconf_vhost');\n\t\tif ($dir !== false) {\n\t\t\t$configdirs[] = FileDir::makeCorrectDir($dir);\n\t\t}\n\n\t\t$dir = $this->getFile('system', 'apacheconf_diroptions');\n\t\tif ($dir !== false) {\n\t\t\t$configdirs[] = FileDir::makeCorrectDir($dir);\n\t\t}\n\n\t\t// file pattern\n\t\t$pattern = \"/^([0-9]){2}_(froxlor|syscp)_(.+)\\.conf$/\";\n\n\t\t// check ALL the folders\n\t\tforeach ($configdirs as $config_dir) {\n\t\t\t// check directory\n\t\t\tif (@is_dir($config_dir)) {\n\t\t\t\t// create directory iterator\n\t\t\t\t$its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($config_dir));\n\n\t\t\t\t// iterate through all subdirs,\n\t\t\t\t// look for vhost/diroption files\n\t\t\t\t// and delete them\n\t\t\t\tforeach ($its as $it) {\n\t\t\t\t\tif ($it->isFile() && preg_match($pattern, $it->getFilename())) {\n\t\t\t\t\t\t// remove file\n\t\t\t\t\t\tFileDir::safe_exec('rm -f ' . escapeshellarg(FileDir::makeCorrectFile($its->getPathname())));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * remove htpasswd files before regeneration\n\t *\n\t * @return null\n\t */\n\tprivate function cleanHtpasswdFiles()\n\t{\n\t\t// get correct directory\n\t\t$configdir = $this->getFile('system', 'apacheconf_htpasswddir');\n\n\t\tif ($configdir !== false) {\n\t\t\t$configdir = FileDir::makeCorrectDir($configdir);\n\n\t\t\tif (@is_dir($configdir)) {\n\t\t\t\t// now get rid of old stuff\n\t\t\t\t// (but append /* so we don't delete the directory)\n\t\t\t\t$configdir .= '/*';\n\t\t\t\tFileDir::safe_exec('rm -f ' . FileDir::makeCorrectFile($configdir));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * remove customer-specified auto-generated ssl-certificates\n\t * (they are being regenerated)\n\t *\n\t * @return null\n\t */\n\tprivate function cleanCustomerSslCerts()\n\t{\n\t\t/*\n\t\t * only clean up if we're actually using SSL\n\t\t */\n\t\tif (Settings::Get('system.use_ssl') == '1') {\n\t\t\t// get correct directory\n\t\t\t$configdir = $this->getFile('system', 'customer_ssl_path');\n\t\t\tif ($configdir !== false) {\n\t\t\t\t$configdir = FileDir::makeCorrectDir($configdir);\n\n\t\t\t\tif (@is_dir($configdir)) {\n\t\t\t\t\t// now get rid of old stuff\n\t\t\t\t\t// (but append /* so we don't delete the directory)\n\t\t\t\t\t$configdir .= '/*';\n\t\t\t\t\tFileDir::safe_exec('rm -f ' . FileDir::makeCorrectFile($configdir));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/DomainSSL.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\n\nclass DomainSSL\n{\n\n\t/**\n\t * read domain-related (or if empty, parentdomain-related) ssl-certificates from the database\n\t * and (if not empty) set the corresponding array-indices (ssl_cert_file, ssl_key_file,\n\t * ssl_ca_file and ssl_cert_chainfile).\n\t * Hence the parameter as reference.\n\t *\n\t * @param array $domain\n\t *            domain-array as reference so we can set the corresponding array-indices\n\t *\n\t * @return null\n\t * @throws \\Exception\n\t */\n\tpublic function setDomainSSLFilesArray(?array &$domain = null)\n\t{\n\t\t// check if the domain itself has a certificate defined\n\t\t$dom_certs_stmt = Database::prepare(\"\n\t\t\tSELECT s.*, d.domain\n\t\t\tFROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` s\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` d ON d.id = s.domainid\n\t\t\tWHERE s.`domainid` = :domid\n\t\t\");\n\t\t$dom_certs = Database::pexecute_first($dom_certs_stmt, [\n\t\t\t'domid' => $domain['id']\n\t\t]);\n\n\t\t$parent_certificate = false;\n\t\tif (!is_array($dom_certs) || !isset($dom_certs['ssl_cert_file']) || $dom_certs['ssl_cert_file'] == '') {\n\t\t\t// maybe its parent?\n\t\t\tif (isset($domain['parentdomainid']) && $domain['parentdomainid'] != 0) {\n\t\t\t\t$dom_certs = Database::pexecute_first($dom_certs_stmt, [\n\t\t\t\t\t'domid' => $domain['parentdomainid']\n\t\t\t\t]);\n\t\t\t\t$parent_certificate = true;\n\t\t\t}\n\t\t}\n\n\t\t// check if it's an array and if the most important field is set\n\t\tif (is_array($dom_certs) && isset($dom_certs['ssl_cert_file']) && $dom_certs['ssl_cert_file'] != '') {\n\t\t\t// get destination path\n\t\t\t$sslcertpath = FileDir::makeCorrectDir(Settings::Get('system.customer_ssl_path'));\n\t\t\t// create path if it does not exist\n\t\t\tif (!file_exists($sslcertpath)) {\n\t\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($sslcertpath));\n\t\t\t}\n\t\t\t// make correct files for the certificates\n\t\t\t$ssl_files = [\n\t\t\t\t'ssl_cert_file' => FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '.crt'),\n\t\t\t\t'ssl_key_file' => FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '.key')\n\t\t\t];\n\n\t\t\tif (!$this->validateCertificate($dom_certs)) {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'Given SSL private key for ' . $domain['domain'] . ' does not seem to match the certificate. Cannot create ssl-directives');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// initialize optional files\n\t\t\t$ssl_files['ssl_ca_file'] = '';\n\t\t\t$ssl_files['ssl_cert_chainfile'] = '';\n\t\t\t// set them if they are != empty\n\t\t\tif ($dom_certs['ssl_ca_file'] != '') {\n\t\t\t\t$ssl_files['ssl_ca_file'] = FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '_CA.pem');\n\t\t\t}\n\t\t\tif ($dom_certs['ssl_cert_chainfile'] != '') {\n\t\t\t\tif (Settings::Get('system.webserver') == 'nginx') {\n\t\t\t\t\t// put ca.crt in my.crt, as nginx does not support a separate chain file.\n\t\t\t\t\t$dom_certs['ssl_cert_file'] = trim($dom_certs['ssl_cert_file']) . \"\\n\" . trim($dom_certs['ssl_cert_chainfile']) . \"\\n\";\n\t\t\t\t} else {\n\t\t\t\t\t$ssl_files['ssl_cert_chainfile'] = FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '_chain.pem');\n\t\t\t\t}\n\t\t\t}\n\t\t\t// will only be generated to be used externally, froxlor does not need this\n\t\t\tif ($dom_certs['ssl_fullchain_file'] != '') {\n\t\t\t\t$ssl_files['ssl_fullchain_file'] = FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '_fullchain.pem');\n\t\t\t}\n\t\t\t// create them on the filesystem\n\t\t\tforeach ($ssl_files as $type => $filename) {\n\t\t\t\tif ($filename != '') {\n\t\t\t\t\ttouch($filename);\n\t\t\t\t\t$_fh = fopen($filename, 'w');\n\t\t\t\t\tfwrite($_fh, $dom_certs[$type]);\n\t\t\t\t\tfclose($_fh);\n\t\t\t\t\tif ($type == 'ssl_key_file') {\n\t\t\t\t\t\tchmod($filename, 0600);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchmod($filename, 0644);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// override corresponding array values\n\t\t\t$domain['ssl_cert_file'] = $ssl_files['ssl_cert_file'];\n\t\t\t$domain['ssl_key_file'] = $ssl_files['ssl_key_file'];\n\t\t\t$domain['ssl_ca_file'] = $ssl_files['ssl_ca_file'];\n\t\t\t$domain['ssl_cert_chainfile'] = $ssl_files['ssl_cert_chainfile'];\n\t\t}\n\n\t\treturn;\n\t}\n\n\tprivate function validateCertificate($dom_certs = []): bool\n\t{\n\t\treturn openssl_x509_check_private_key($dom_certs['ssl_cert_file'], $dom_certs['ssl_key_file']);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/HttpConfigBase.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http;\n\nuse Froxlor\\Cron\\Http\\LetsEncrypt\\AcmeSh;\nuse Froxlor\\Cron\\Http\\Php\\Fpm;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse PDO;\n\n/**\n * Class HttpConfigBase\n *\n * Base class for all HTTP server configs\n */\nclass HttpConfigBase\n{\n\n\t/**\n\t * Pre-defined DHE groups to use as fallback if dhparams_file\n\t * is given, but non-existent, see also https://github.com/froxlor/Froxlor/issues/1270\n\t */\n\tconst FFDHE4096 = <<<EOC\n-----BEGIN DH PARAMETERS-----\nMIICCAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\nYdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD\nssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3\n7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32\nnuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZp4e\n8W5vUsMWTfT7eTDp5OWIV7asfV9C1p9tGHdjzx1VA0AEh/VbpX4xzHpxNciG77Qx\niu1qHgEtnmgyqQdgCpGBMMRtx3j5ca0AOAkpmaMzy4t6Gh25PXFAADwqTs6p+Y0K\nzAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQI=\n-----END DH PARAMETERS-----\nEOC;\n\n\tpublic function init()\n\t{\n\t\t// if Let's Encrypt is activated, run it before regeneration of webserver configfiles\n\t\tif (Settings::Get('system.leenabled') == 1) {\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Running Let\\'s Encrypt cronjob prior to regenerating webserver config files');\n\t\t\tAcmeSh::$no_inserttask = true;\n\t\t\tAcmeSh::run(true);\n\t\t\t// set last run timestamp of cronjob\n\t\t\tCronjob::updateLastRunOfCron('letsencrypt');\n\t\t}\n\t}\n\n\tpublic function reload()\n\t{\n\t\t$called_class = get_called_class();\n\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t// get all start/stop commands\n\t\t\t$startstop_sel = Database::prepare(\"SELECT reload_cmd, config_dir FROM `\" . TABLE_PANEL_FPMDAEMONS . \"`\");\n\t\t\tDatabase::pexecute($startstop_sel);\n\t\t\t$restart_cmds = $startstop_sel->fetchAll(PDO::FETCH_ASSOC);\n\t\t\t// restart all php-fpm instances\n\t\t\tforeach ($restart_cmds as $restart_cmd) {\n\t\t\t\t// check whether the config dir is empty (no domains uses this daemon)\n\t\t\t\t// so we need to create a dummy\n\t\t\t\t$_conffiles = glob(FileDir::makeCorrectFile($restart_cmd['config_dir'] . \"/*.conf\"));\n\t\t\t\tif ($_conffiles === false || empty($_conffiles)) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, $called_class . '::reload: fpm config directory \"' . $restart_cmd['config_dir'] . '\" is empty. Creating dummy.');\n\t\t\t\t\tFpm::createDummyPool($restart_cmd['config_dir']);\n\t\t\t\t}\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, $called_class . '::reload: running ' . $restart_cmd['reload_cmd']);\n\t\t\t\tFileDir::safe_exec(escapeshellcmd($restart_cmd['reload_cmd']));\n\t\t\t}\n\t\t}\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, $called_class . '::reload: reloading ' . $called_class);\n\t\tFileDir::safe_exec(escapeshellcmd(Settings::Get('system.apachereload_command')));\n\n\t\t/**\n\t\t * nginx does not auto-spawn fcgi-processes\n\t\t */\n\t\tif (Settings::Get('system.webserver') == \"nginx\" && Settings::Get('system.phpreload_command') != '' && (int)Settings::Get('phpfpm.enabled') == 0) {\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, $called_class . '::reload: restarting php processes');\n\t\t\tFileDir::safe_exec(Settings::Get('system.phpreload_command'));\n\t\t}\n\t}\n\n\t/**\n\t * process special config as template, by substituting {VARIABLE} with the\n\t * respective value.\n\t *\n\t * The following variables are known at the moment:\n\t *\n\t * {DOMAIN} - domain name\n\t * {IP} - IP for this domain\n\t * {PORT} - Port for this domain\n\t * {CUSTOMER} - customer name\n\t * {IS_SSL} - evaluates to 'ssl' if domain/ip is ssl, otherwise it is an empty string\n\t * {DOCROOT} - document root for this domain\n\t *\n\t * @param\n\t *            $template\n\t * @return string\n\t */\n\tprotected function processSpecialConfigTemplate($template, $domain, $ip, $port, $is_ssl_vhost)\n\t{\n\t\t$templateVars = [\n\t\t\t'DOMAIN' => $domain['domain'],\n\t\t\t'CUSTOMER' => $domain['loginname'],\n\t\t\t'IP' => $ip,\n\t\t\t'PORT' => $port,\n\t\t\t'SCHEME' => ($is_ssl_vhost) ? 'https' : 'http',\n\t\t\t'DOCROOT' => $domain['documentroot'],\n\t\t\t'FPMSOCKET' => ''\n\t\t];\n\t\tif ((int)Settings::Get('phpfpm.enabled') == 1 && isset($domain['fpm_socket']) && !empty($domain['fpm_socket'])) {\n\t\t\t$templateVars['FPMSOCKET'] = $domain['fpm_socket'];\n\t\t}\n\t\treturn PhpHelper::replaceVariables($template, $templateVars);\n\t}\n\n\tprotected function getMyPath($ip_port = null)\n\t{\n\t\tif (!empty($ip_port) && $ip_port['docroot'] == '') {\n\t\t\tif (Settings::Get('system.froxlordirectlyviahostname')) {\n\t\t\t\t$mypath = FileDir::makeCorrectDir(Froxlor::getInstallDir());\n\t\t\t} else {\n\t\t\t\t$mypath = FileDir::makeCorrectDir(dirname(Froxlor::getInstallDir()));\n\t\t\t}\n\t\t} else {\n\t\t\t// user-defined docroot, #417\n\t\t\t$mypath = FileDir::makeCorrectDir($ip_port['docroot']);\n\t\t}\n\t\treturn $mypath;\n\t}\n\n\tprotected function checkAlternativeSslPort()\n\t{\n\t\t// We must not check if our port differs from port 443,\n\t\t// but if there is a destination-port != 443\n\t\t$_sslport = '';\n\t\t// This returns the first port that is != 443 with ssl enabled,\n\t\t// ordered by ssl-certificate (if any) so that the ip/port combo\n\t\t// with certificate is used\n\t\t$ssldestport_stmt = Database::prepare(\"\n\t\t\tSELECT `ip`.`port` FROM \" . TABLE_PANEL_IPSANDPORTS . \" `ip`\n\t\t\tWHERE `ip`.`ssl` = '1'  AND `ip`.`port` != 443\n\t\t\tORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1;\n\t\t\");\n\t\t$ssldestport = Database::pexecute_first($ssldestport_stmt);\n\n\t\tif ($ssldestport && $ssldestport['port'] != '') {\n\t\t\t$_sslport = \":\" . $ssldestport['port'];\n\t\t}\n\n\t\treturn $_sslport;\n\t}\n\n\tprotected function froxlorVhostHasLetsEncryptCert()\n\t{\n\t\t// check whether we have an entry with valid certificates which just does not need\n\t\t// updating yet, so we need to skip this here\n\t\t$froxlor_ssl_settings_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = '0'\n\t\t\");\n\t\t$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);\n\t\tif ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprotected function froxlorVhostLetsEncryptNeedsRenew()\n\t{\n\t\t$froxlor_ssl_settings_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\t\tWHERE `domainid` = '0' AND\n\t\t\t(`validtodate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `validtodate` IS NULL)\n\t\t\");\n\t\t$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);\n\t\tif ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Get the filename for the virtualhost\n\t */\n\tprotected function getVhostFilename(array $domain, bool $ssl_vhost = false, bool $filename_only = false)\n\t{\n\t\t// number of dots in a domain specifies its position (and depth of subdomain) starting at 35 going downwards on higher depth\n\t\t$vhost_no = (string)(35 - substr_count($domain['domain'], \".\") + 1);\n\t\t$filename = $vhost_no . '_froxlor_' . ($ssl_vhost ? 'ssl' : 'normal') . '_vhost_' . $domain['domain'] . '.conf';\n\t\tif ($filename_only) {\n\t\t\treturn $filename;\n\t\t}\n\t\treturn FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $filename);\n\t}\n\n\tprotected function getCustomVhostFilename(string $name)\n\t{\n\t\t$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));\n\t\tif (is_dir(Settings::Get('system.apacheconf_vhost'))) {\n\t\t\t$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));\n\t\t}\n\t\treturn FileDir::makeCorrectFile($vhosts_folder . '/' . $name);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/LetsEncrypt/AcmeSh.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http\\LetsEncrypt;\n\nuse Froxlor\\Cron\\FroxlorCron;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\nuse PDOStatement;\n\nclass AcmeSh extends FroxlorCron\n{\n\n\tconst ACME_PROVIDER = [\n\t\t'letsencrypt' => \"https://acme-v02.api.letsencrypt.org/directory\",\n\t\t'letsencrypt_test' => \"https://acme-staging-v02.api.letsencrypt.org/directory\",\n\t\t'buypass' => \"https://api.buypass.com/acme/directory\",\n\t\t'buypass_test' => \"https://api.test4.buypass.no/acme/directory\",\n\t\t'zerossl' => \"https://acme.zerossl.com/v2/DV90\",\n\t\t'google' => \"https://dv.acme-v02.api.pki.goog/directory\",\n\t\t'google_test' => \"https://dv.acme-v02.test-api.pki.goog/directory\",\n\t];\n\tpublic static $no_inserttask = false;\n\tprivate static $apiserver = \"\";\n\tprivate static $acmesh = \"/root/.acme.sh/acme.sh\";\n\t/**\n\t *\n\t * @var PDOStatement\n\t */\n\tprivate static $updcert_stmt = null;\n\t/**\n\t *\n\t * @var PDOStatement\n\t */\n\tprivate static $upddom_stmt = null;\n\n\t/**\n\t * run the task\n\t *\n\t * @param bool $internal\n\t * @return int\n\t * @throws \\Exception\n\t */\n\tpublic static function run(bool $internal = false)\n\t{\n\t\t// usually, this is action is called from within the tasks-jobs\n\t\tif (!defined('CRON_IS_FORCED') && !defined('CRON_DEBUG_FLAG') && $internal == false) {\n\t\t\t// Let's Encrypt cronjob is combined with regeneration of webserver configuration files.\n\t\t\t// For debugging purposes you can use the --debug switch and the --force switch to run the cron manually.\n\t\t\t// check whether we MIGHT need to run although there is no task to regenerate config-files\n\t\t\t$issue_froxlor = self::issueFroxlorVhost();\n\t\t\t$issue_domains = self::issueDomains();\n\t\t\t$renew_froxlor = self::renewFroxlorVhost();\n\t\t\t$renew_domains = self::renewDomains(true);\n\t\t\tif ($issue_froxlor || !empty($issue_domains) || !empty($renew_froxlor) || $renew_domains) {\n\t\t\t\t// insert task to generate certificates and vhost-configs\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\tif ($renew_froxlor) {\n\t\t\t\t\tCronjob::inserttask(TaskId::UPDATE_LE_SERVICES);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\t// set server according to settings\n\t\tself::$apiserver = self::ACME_PROVIDER[Settings::Get('system.letsencryptca')];\n\n\t\t// validate acme.sh installation\n\t\tif (!self::checkInstall()) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tself::checkUpgrade();\n\n\t\t// flag for re-generation of vhost files\n\t\t$changedetected = 0;\n\n\t\t// prepare update sql\n\t\tself::$updcert_stmt = Database::prepare(\"\n\t\t\tREPLACE INTO\n\t\t\t\t`\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\t\tSET\n\t\t\t\t`id` = :id,\n\t\t\t\t`domainid` = :domainid,\n\t\t\t\t`ssl_cert_file` = :crt,\n\t\t\t\t`ssl_key_file` = :key,\n\t\t\t\t`ssl_ca_file` = :ca,\n\t\t\t\t`ssl_cert_chainfile` = :chain,\n\t\t\t\t`ssl_csr_file` = :csr,\n\t\t\t\t`ssl_fullchain_file` = :fullchain,\n\t\t\t\t`validfromdate` = :validfromdate,\n\t\t\t\t`validtodate` = :validtodate,\n\t\t\t\t`issuer` = :issuer\n\t\t\");\n\n\t\t// prepare domain update sql\n\t\tself::$upddom_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `ssl_redirect` = '1' WHERE `id` = :domainid\");\n\n\t\t// check whether there are certificates to issue\n\t\t$issue_froxlor = self::issueFroxlorVhost();\n\t\t$issue_domains = self::issueDomains();\n\n\t\t// first - generate LE for system-vhost if enabled\n\t\tif ($issue_froxlor) {\n\t\t\t// build row\n\t\t\t$certrow = [\n\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t'domainid' => 0,\n\t\t\t\t'documentroot' => Froxlor::getInstallDir(),\n\t\t\t\t'leprivatekey' => Settings::Get('system.leprivatekey'),\n\t\t\t\t'lepublickey' => Settings::Get('system.lepublickey'),\n\t\t\t\t'leregistered' => Settings::Get('system.leregistered'),\n\t\t\t\t'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),\n\t\t\t\t'validfromdate' => null,\n\t\t\t\t'validtodate' => null,\n\t\t\t\t'issuer' => \"\",\n\t\t\t\t'ssl_cert_file' => null,\n\t\t\t\t'ssl_key_file' => null,\n\t\t\t\t'ssl_ca_file' => null,\n\t\t\t\t'ssl_csr_file' => null,\n\t\t\t\t'id' => null,\n\t\t\t\t'wwwserveralias' => 0\n\t\t\t];\n\n\t\t\t// add to queue\n\t\t\t$issue_domains[] = $certrow;\n\t\t}\n\n\t\tif (count($issue_domains)) {\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Requesting \" . count($issue_domains) . \" new Let's Encrypt certificates\");\n\t\t\tself::runIssueFor($issue_domains);\n\t\t\t$changedetected = 1;\n\t\t}\n\n\t\t// compare file-system certificates with the ones in our database\n\t\t// and update if needed\n\t\t$renew_froxlor = self::renewFroxlorVhost();\n\t\t$renew_domains = self::renewDomains();\n\n\t\tif ($renew_froxlor) {\n\t\t\t// build row\n\t\t\t$certrow = [\n\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t'domainid' => 0,\n\t\t\t\t'documentroot' => Froxlor::getInstallDir(),\n\t\t\t\t'leprivatekey' => Settings::Get('system.leprivatekey'),\n\t\t\t\t'lepublickey' => Settings::Get('system.lepublickey'),\n\t\t\t\t'leregistered' => Settings::Get('system.leregistered'),\n\t\t\t\t'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),\n\t\t\t\t'validfromdate' => is_array($renew_froxlor) ? $renew_froxlor['validfromdate'] : date('Y-m-d H:i:s', 0),\n\t\t\t\t'validtodate' => is_array($renew_froxlor) ? $renew_froxlor['validtodate'] : date('Y-m-d H:i:s', 0),\n\t\t\t\t'issuer' => is_array($renew_froxlor) ? $renew_froxlor['issuer'] : \"\",\n\t\t\t\t'ssl_cert_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_cert_file'] : null,\n\t\t\t\t'ssl_key_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_key_file'] : null,\n\t\t\t\t'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null,\n\t\t\t\t'ssl_csr_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_csr_file'] : null,\n\t\t\t\t'id' => is_array($renew_froxlor) ? $renew_froxlor['id'] : null,\n\t\t\t\t'wwwserveralias' => 0\n\t\t\t];\n\t\t\t$renew_domains[] = $certrow;\n\t\t}\n\n\t\tforeach ($renew_domains as $domain) {\n\t\t\t$cronlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t'loginname' => $domain['loginname'],\n\t\t\t\t'adminsession' => 0\n\t\t\t]);\n\t\t\tif (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['validtodate'])) {\n\t\t\t\tself::certToDb($domain, $cronlog, []);\n\t\t\t\t$changedetected = 1;\n\t\t\t}\n\t\t}\n\n\t\t// If we have a change in a certificate, we need to update the webserver - configs\n\t\t// This is easiest done by just creating a new task ;)\n\t\tif ($changedetected) {\n\t\t\tif (self::$no_inserttask == false) {\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t}\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Let's Encrypt certificates have been updated\");\n\t\t} else {\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"No new certificates or certificate updates found\");\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * check whether we need to issue a new certificate for froxlor itself\n\t *\n\t * @return boolean\n\t * @throws \\Exception\n\t */\n\tprivate static function issueFroxlorVhost()\n\t{\n\t\tif (Settings::Get('system.le_froxlor_enabled') == '1') {\n\t\t\t// let's encrypt is enabled, now check whether we have a certificate\n\t\t\t$froxlor_ssl_settings_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\t\t\tWHERE `domainid` = '0'\n\t\t\t\");\n\t\t\t$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);\n\t\t\t// also check for possible existing certificate\n\t\t\tif (!$froxlor_ssl || empty($froxlor_ssl['validtodate'])) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static function checkFsFilesAreNewer($domain, $cert_date = 0): bool\n\t{\n\t\t$certificate_folder = self::getCertificateFolder(strtolower($domain));\n\t\tif (empty($certificate_folder)) {\n\t\t\treturn false;\n\t\t}\n\t\t$ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower($domain) . '.cer');\n\n\t\tif (is_dir($certificate_folder) && file_exists($ssl_file) && is_readable($ssl_file)) {\n\t\t\t$cert_data = openssl_x509_parse(file_get_contents($ssl_file));\n\t\t\tif ($cert_data && $cert_data['validTo_time_t'] > strtotime($cert_date)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static function getWorkingDirFromEnv($domain = \"\", $forced_ecc = false): string\n\t{\n\t\t// first try without _ecc either if it's enabled currently or not as\n\t\t// it might have been at some point so there is a chance we have certificates\n\t\t// with and without _ecc - the method getCertificateFolder() will check both\n\t\t// possibilities\n\t\tif ($forced_ecc) {\n\t\t\t$domain .= \"_ecc\";\n\t\t}\n\t\t$env_file = FileDir::makeCorrectFile(dirname(self::getAcmeSh()) . '/acme.sh.env');\n\t\tif (file_exists($env_file)) {\n\t\t\t$output = [];\n\t\t\t$cut = <<<EOC\ncut -d'\"' -f2\nEOC;\n\t\t\texec('grep \"LE_WORKING_DIR\" ' . escapeshellarg($env_file) . ' | ' . $cut, $output);\n\t\t\tif (is_array($output) && !empty($output) && !empty($output[0])) {\n\t\t\t\treturn FileDir::makeCorrectDir($output[0] . \"/\" . $domain);\n\t\t\t}\n\t\t}\n\t\treturn FileDir::makeCorrectDir(dirname(self::getAcmeSh()) . \"/\" . $domain);\n\t}\n\n\tpublic static function getAcmeSh()\n\t{\n\t\t$from_settings = Settings::Get('system.acmeshpath');\n\t\tif (file_exists($from_settings)) {\n\t\t\treturn $from_settings;\n\t\t}\n\t\treturn self::$acmesh;\n\t}\n\n\t/**\n\t * get a list of domains that require a new certificate (issue)\n\t */\n\tprivate static function issueDomains()\n\t{\n\t\t$certificates_stmt = Database::query(\"\n\t\t\tSELECT\n\t\t\t\tdomssl.`id`,\n\t\t\t\tdomssl.`domainid`,\n\t\t\t\tdomssl.`validfromdate`,\n\t\t\t\tdomssl.`validtodate`,\n\t\t\t\tdomssl.`issuer`,\n\t\t\t\tdomssl.`ssl_cert_file`,\n\t\t\t\tdomssl.`ssl_key_file`,\n\t\t\t\tdomssl.`ssl_ca_file`,\n\t\t\t\tdomssl.`ssl_csr_file`,\n\t\t\t\tdom.`domain`,\n\t\t\t\tdom.`wwwserveralias`,\n\t\t\t\tdom.`documentroot`,\n\t\t\t\tdom.`id` AS 'domainid',\n\t\t\t\tdom.`ssl_redirect`,\n\t\t\t\tcust.`leprivatekey`,\n\t\t\t\tcust.`lepublickey`,\n\t\t\t\tcust.`leregistered`,\n\t\t\t\tcust.`customerid`,\n\t\t\t\tcust.`loginname`\n\t\t\tFROM\n\t\t\t\t`\" . TABLE_PANEL_CUSTOMERS . \"` AS cust,\n\t\t\t\t`\" . TABLE_PANEL_DOMAINS . \"` AS dom\n\t\t\tLEFT JOIN\n\t\t\t\t`\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` AS domssl ON\n\t\t\t\t\tdom.`id` = domssl.`domainid`\n\t\t\tWHERE\n\t\t\t\tdom.`customerid` = cust.`customerid`\n\t\t\t\tAND cust.deactivated = 0\n\t\t\t\tAND dom.deactivated = 0\n\t\t\t\tAND dom.`ssl_enabled` = 1\n\t\t\t\tAND dom.`letsencrypt` = 1\n\t\t\t\tAND dom.`aliasdomain` IS NULL\n\t\t\t\tAND dom.`iswildcarddomain` = 0\n\t\t\t\tAND dom.`email_only` = 0\n\t\t\t\tAND domssl.`validtodate` IS NULL\n\t\t\");\n\t\t$customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);\n\t\tif ($customer_ssl) {\n\t\t\treturn $customer_ssl;\n\t\t}\n\t\treturn [];\n\t}\n\n\t/**\n\t * check whether we need to renew-check the certificate for froxlor itself\n\t *\n\t * @return boolean\n\t * @throws \\Exception\n\t */\n\tprivate static function renewFroxlorVhost()\n\t{\n\t\tif (Settings::Get('system.le_froxlor_enabled') == '1') {\n\t\t\t// let's encrypt is enabled, now check whether we have a certificate\n\t\t\t$froxlor_ssl_settings_stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\t\t\tWHERE `domainid` = '0'\n\t\t\t\");\n\t\t\t$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);\n\t\t\t// also check for possible existing certificate\n\t\t\tif ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['validtodate'])) {\n\t\t\t\treturn $froxlor_ssl;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * get a list of domains that have a lets encrypt certificate (possible renew)\n\t */\n\tprivate static function renewDomains($check = false)\n\t{\n\t\t$certificates_stmt = Database::query(\"\n\t\t\tSELECT\n\t\t\t\tdomssl.`id`,\n\t\t\t\tdomssl.`domainid`,\n\t\t\t\tdomssl.`validfromdate`,\n\t\t\t\tdomssl.`validtodate`,\n\t\t\t\tdomssl.`issuer`,\n\t\t\t\tdomssl.`ssl_cert_file`,\n\t\t\t\tdomssl.`ssl_key_file`,\n\t\t\t\tdom.`domain`,\n\t\t\t\tdom.`id` AS 'domainid',\n\t\t\t\tdom.`ssl_redirect`,\n\t\t\t\tcust.`loginname`\n\t\t\tFROM\n\t\t\t\t`\" . TABLE_PANEL_CUSTOMERS . \"` AS cust,\n\t\t\t\t`\" . TABLE_PANEL_DOMAINS . \"` AS dom\n\t\t\tLEFT JOIN\n\t\t\t\t`\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` AS domssl ON\n\t\t\t\t\tdom.`id` = domssl.`domainid`\n\t\t\tWHERE\n\t\t\t\tdom.`customerid` = cust.`customerid`\n\t\t\t\tAND cust.deactivated = 0\n\t\t\t\tAND dom.deactivated = 0\n\t\t\t\tAND dom.`ssl_enabled` = 1\n\t\t\t\tAND dom.`letsencrypt` = 1\n\t\t\t\tAND dom.`aliasdomain` IS NULL\n\t\t\t\tAND dom.`iswildcarddomain` = 0\n\t\t\t\tAND dom.`email_only` = 0\n\t\t\t\tAND dom.`ssl_redirect` != 2\n\t\t\");\n\t\t$renew_certs = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);\n\t\tif ($renew_certs) {\n\t\t\tif ($check) {\n\t\t\t\tforeach ($renew_certs as $cert) {\n\t\t\t\t\tif (self::checkFsFilesAreNewer($cert['domain'], $cert['validtodate'])) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $renew_certs;\n\t\t}\n\t\treturn [];\n\t}\n\n\t/**\n\t * install acme.sh if not found yet\n\t */\n\tprivate static function checkInstall($tries = 0)\n\t{\n\t\tif (!file_exists(self::getAcmeSh()) && $tries > 0) {\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Download/installation of acme.sh seems to have failed. Re-run cronjob to try again or install manually to '\" . self::getAcmeSh() . \"'\");\n\t\t\techo PHP_EOL . \"Download/installation of acme.sh seems to have failed. Re-run cronjob to try again or install manually to '\" . self::getAcmeSh() . \"'\" . PHP_EOL;\n\t\t\treturn false;\n\t\t} else {\n\t\t\tif (!file_exists(self::getAcmeSh())) {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Could not find acme.sh - installing it to /root/.acme.sh/\");\n\t\t\t\t$return = false;\n\t\t\t\tFileDir::safe_exec(\"wget -O - https://get.acme.sh | sh -s email=\" . escapeshellarg(Settings::Get('panel.adminmail')), $return, [\n\t\t\t\t\t'|'\n\t\t\t\t]);\n\t\t\t\t$set_path = self::getAcmeSh();\n\t\t\t\t// after this, regardless of what the user specified, the acme.sh installation will be in /root/.acme.sh\n\t\t\t\tif ($set_path != '/root/.acme.sh/acme.sh') {\n\t\t\t\t\tSettings::Set('system.acmeshpath', '/root/.acme.sh/acme.sh', true);\n\t\t\t\t\t// let the user know\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, \"Acme.sh could not be found in '\" . $set_path . \"' so froxlor installed it to the default location, which is '/root/.acme.sh/'\");\n\t\t\t\t\techo PHP_EOL . \"Acme.sh could not be found in '\" . $set_path . \"' so froxlor installed it to the default location, which is '/root/.acme.sh/'\" . PHP_EOL;\n\t\t\t\t}\n\t\t\t\t// check whether the installation worked\n\t\t\t\treturn self::checkInstall(++$tries);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * run upgrade\n\t */\n\tprivate static function checkUpgrade()\n\t{\n\t\t$acmesh_result = FileDir::safe_exec(self::getAcmeSh() . \" --upgrade --auto-upgrade 0\");\n\t\t// check for activated cron\n\t\t$acmesh_result2 = FileDir::safe_exec(self::getAcmeSh() . \" --install-cronjob\");\n\t\t// set default CA\n\t\t$acmesh_result3 = FileDir::safe_exec(self::getAcmeSh() . \" --set-default-ca --server \" . self::$apiserver);\n\t\t// log messages\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Checking for LetsEncrypt client upgrades before renewing certificates:\\n\" . implode(\"\\n\", $acmesh_result) . \"\\n\" . implode(\"\\n\", $acmesh_result2) . \"\\n\" . implode(\"\\n\", $acmesh_result3));\n\t}\n\n\t/**\n\t * issue certificates for a list of domains\n\t */\n\tprivate static function runIssueFor($certrows = [])\n\t{\n\t\t// prepare aliasdomain-check\n\t\t$aliasdomains_stmt = Database::prepare(\"\n\t\t\tSELECT\n\t\t\t\tdom.`id` as domainid,\n\t\t\t\tdom.`domain`,\n\t\t\t\tdom.`wwwserveralias`\n\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` AS dom\n\t\t\tWHERE\n\t\t\t\tdom.`aliasdomain` = :id\n\t\t\t\tAND dom.`letsencrypt` = 1\n\t\t\t\tAND dom.`iswildcarddomain` = 0\n\t\t\");\n\t\t// iterate through all domains\n\t\tforeach ($certrows as $certrow) {\n\t\t\t// set logger to corresponding loginname for the log to appear in the users system-log\n\t\t\t$cronlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t'loginname' => $certrow['loginname'],\n\t\t\t\t'adminsession' => 0\n\t\t\t]);\n\t\t\t// Only issue let's encrypt certificate if no broken ssl_redirect is enabled\n\t\t\tif ($certrow['ssl_redirect'] != 2) {\n\t\t\t\t$do_force = false;\n\t\t\t\tif (!empty($certrow['ssl_cert_file']) && empty($certrow['validtodate'])) {\n\t\t\t\t\t// domain changed (SAN or similar)\n\t\t\t\t\t$do_force = true;\n\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Re-creating certificate for \" . $certrow['domain']);\n\t\t\t\t} else {\n\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Creating certificate for \" . $certrow['domain']);\n\t\t\t\t}\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Adding common-name: \" . $certrow['domain']);\n\t\t\t\t$domains = [\n\t\t\t\t\tstrtolower($certrow['domain'])\n\t\t\t\t];\n\t\t\t\t// add www.<domain> to SAN list\n\t\t\t\tif ($certrow['wwwserveralias'] == 1) {\n\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Adding SAN entry: www.\" . $certrow['domain']);\n\t\t\t\t\t$domains[] = strtolower('www.' . $certrow['domain']);\n\t\t\t\t}\n\t\t\t\tif ($certrow['domainid'] == 0) {\n\t\t\t\t\t$froxlor_aliases = Settings::Get('system.froxloraliases');\n\t\t\t\t\tif (!empty($froxlor_aliases)) {\n\t\t\t\t\t\t$froxlor_aliases = explode(\",\", $froxlor_aliases);\n\t\t\t\t\t\tforeach ($froxlor_aliases as $falias) {\n\t\t\t\t\t\t\tif (Validate::validateDomain(trim($falias))) {\n\t\t\t\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Adding SAN entry: \" . strtolower(trim($falias)));\n\t\t\t\t\t\t\t\t$domains[] = strtolower(trim($falias));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// add alias domains (and possibly www.<aliasdomain>) to SAN list\n\t\t\t\t\tDatabase::pexecute($aliasdomains_stmt, [\n\t\t\t\t\t\t'id' => $certrow['domainid']\n\t\t\t\t\t]);\n\t\t\t\t\t$aliasdomains = $aliasdomains_stmt->fetchAll(PDO::FETCH_ASSOC);\n\t\t\t\t\tforeach ($aliasdomains as $aliasdomain) {\n\t\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Adding SAN entry: \" . $aliasdomain['domain']);\n\t\t\t\t\t\t$domains[] = strtolower($aliasdomain['domain']);\n\t\t\t\t\t\tif ($aliasdomain['wwwserveralias'] == 1) {\n\t\t\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Adding SAN entry: www.\" . $aliasdomain['domain']);\n\t\t\t\t\t\t\t$domains[] = strtolower('www.' . $aliasdomain['domain']);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tself::validateDns($domains, $certrow['domainid'], $cronlog);\n\n\t\t\t\tself::runAcmeSh($certrow, $domains, $cronlog, $do_force, $certrow['domainid'] == 0);\n\t\t\t} else {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, \"Skipping Let's Encrypt generation for \" . $certrow['domain'] . \" due to an enabled ssl_redirect\");\n\t\t\t\t// we need another reconfigure in order to get the certificate\n\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * validate dns (A / AAAA record) of domain against known system ips\n\t *\n\t * @param array $domains\n\t * @param int $domain_id\n\t * @param FroxlorLogger $cronlog\n\t * @throws \\Exception\n\t */\n\tprivate static function validateDns(array &$domains, $domain_id, &$cronlog)\n\t{\n\t\tif (Settings::Get('system.le_domain_dnscheck') == '1' && !empty($domains)) {\n\t\t\t$loop_domains = $domains;\n\t\t\t// ips according to our system\n\t\t\t$our_ips = Domain::getIpsOfDomain($domain_id);\n\t\t\tforeach ($loop_domains as $idx => $domain) {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Validating DNS of \" . $domain);\n\t\t\t\t// ips according to NS\n\t\t\t\t$domain_ips = PhpHelper::gethostbynamel6($domain, true, Settings::Get('system.le_domain_dnscheck_resolver'));\n\t\t\t\tif ($domain_ips == false || count(array_intersect($our_ips, $domain_ips)) <= 0) {\n\t\t\t\t\t// no common ips...\n\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, \"Skipping Let's Encrypt generation for \" . $domain . \" due to no system known IP address via DNS check\");\n\t\t\t\t\tunset($domains[$idx]);\n\t\t\t\t\t// in order to avoid a cron-loop that tries to get a certificate every 5 minutes, we disable let's encrypt for this domain\n\t\t\t\t\tif ($domain_id > 0) {\n\t\t\t\t\t\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `letsencrypt` = '0' WHERE `id` = :did\");\n\t\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t\t'did' => $domain_id\n\t\t\t\t\t\t]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// froxlor's hostname\n\t\t\t\t\t\tSettings::Set('system.le_froxlor_enabled', 0);\n\t\t\t\t\t}\n\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, \"Let's Encrypt deactivated for domain \" . $domain);\n\t\t\t\t\tif (!defined('CRON_IS_FORCED') && !defined('CRON_DEBUG_FLAG')) {\n\t\t\t\t\t\t// email info to admin that lets encrypt has been disabled for this domain\n\t\t\t\t\t\tCronjob::notifyMailToAdmin(\"Let's Encrypt has been deactivated for domain '\" . $domain . \"' due to failed dns validation (wrong or no IP address)\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static function runAcmeSh(array $certrow, array $domains, &$cronlog = null, bool $force = false, bool $renew_hook = false)\n\t{\n\t\tif (!empty($domains)) {\n\t\t\t$acmesh_cmd = self::getAcmeSh() . \" --server \" . self::$apiserver . \" --issue -d \" . implode(\" -d \", $domains);\n\t\t\t// challenge path\n\t\t\t$acmesh_cmd .= \" -w \" . Settings::Get('system.letsencryptchallengepath');\n\t\t\tif (Settings::Get('system.leecc') > 0) {\n\t\t\t\t// ecc certificate\n\t\t\t\t$acmesh_cmd .= \" --keylength ec-\" . Settings::Get('system.leecc');\n\t\t\t} else {\n\t\t\t\t$acmesh_cmd .= \" --keylength \" . Settings::Get('system.letsencryptkeysize');\n\t\t\t}\n\t\t\tif (Settings::Get('system.letsencryptreuseold') != '1') {\n\t\t\t\t$acmesh_cmd .= \" --always-force-new-domain-key\";\n\t\t\t}\n\t\t\tif (substr(Settings::Get('system.letsencryptca'), -5) == '_test') {\n\t\t\t\t$acmesh_cmd .= \" --staging\";\n\t\t\t}\n\t\t\tif ($force) {\n\t\t\t\t$acmesh_cmd .= \" --force\";\n\t\t\t}\n\t\t\tif ($renew_hook\n\t\t\t\t&& !empty(trim(Settings::Get('system.le_renew_services') ?? \"\"))\n\t\t\t\t&& !empty(trim(Settings::Get('system.le_renew_hook') ?? \"\"))\n\t\t\t) {\n\t\t\t\t$acmesh_cmd .= \" --renew-hook '\" . Settings::Get('system.le_renew_hook') . \"'\";\n\t\t\t}\n\t\t\tif (defined('CRON_DEBUG_FLAG')) {\n\t\t\t\t$acmesh_cmd .= \" --debug\";\n\t\t\t}\n\n\t\t\t$exit_code = null;\n\t\t\t$acme_result = FileDir::safe_exec($acmesh_cmd, $exit_code);\n\t\t\t// debug output of acme.sh run\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, implode(\"\\n\", $acme_result));\n\n\t\t\tif ($exit_code != 0) {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, \"Non-successful exit-code returned :(\");\n\t\t\t\tif (!defined('CRON_IS_FORCED') && !defined('CRON_DEBUG_FLAG')) {\n\t\t\t\t\tCronjob::notifyMailToAdmin(\"Let's Encrypt certificate could not be obtained for: \" . implode(\", \", $domains) . \"\\n\\n\" . implode(\"\\n\", $acme_result));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, \"Successful exit-code returned - storing certificate\");\n\t\t\t\t$cert_stored = self::certToDb($certrow, $cronlog, $acme_result);\n\n\t\t\t\tif ($cert_stored && $renew_hook) {\n\t\t\t\t\tself::renewHookConfigs($cronlog);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static function renewHookConfigs($cronlog)\n\t{\n\t\tif (!empty(trim(Settings::Get('system.le_renew_services') ?? \"\"))\n\t\t\t&& !empty(trim(Settings::Get('system.le_renew_hook') ?? \"\"))\n\t\t) {\n\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, \"Renew-hook is enabled - adjusting configurations\");\n\n\t\t\t$certificate_folder = self::getCertificateFolder(strtolower(Settings::Get('system.hostname')));\n\n\t\t\tif (empty($certificate_folder)) {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"No certificate folder for '\" . Settings::Get('system.hostname') . \"' found\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$fullchain = FileDir::makeCorrectFile($certificate_folder . '/fullchain.cer');\n\t\t\t$keyfile = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower(Settings::Get('system.hostname')) . '.key');\n\t\t\t$ca_file = FileDir::makeCorrectFile($certificate_folder . '/ca.cer');\n\n\t\t\tif (!file_exists($fullchain) || !file_exists($keyfile) || !file_exists($ca_file)) {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"At least one of the required certificate files for '\" . Settings::Get('system.hostname') . \"' could not be found\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$dovecot_conf = '/etc/dovecot/conf.d/99-froxlor.ssl.conf'; // @fixme setting?\n\n\t\t\tif (Settings::IsInList('system.le_renew_services', 'postfix')) {\n\t\t\t\t// \"postconf -e\" for postfix\n\t\t\t\tFileDir::safe_exec('postconf -e smtpd_tls_cert_file=' . escapeshellarg($fullchain));\n\t\t\t\tFileDir::safe_exec('postconf -e smtpd_tls_key_file=' . escapeshellarg($keyfile));\n\t\t\t}\n\t\t\tif (Settings::IsInList('system.le_renew_services', 'dovecot')) {\n\t\t\t\t// custom config for dovecot\n\t\t\t\t$ssl_content = <<<EOSSL\n# Autogenerated configuration by froxlor.\n# Do not manually edit this file as it will be overwritten.\n\nssl = yes\nssl_cert = <{$fullchain}\nssl_key = <{$keyfile}\nEOSSL;\n\t\t\t\tfile_put_contents($dovecot_conf, $ssl_content);\n\t\t\t} elseif (Settings::IsInList('system.le_renew_services', 'dovecot24')) {\n\t\t\t\t\t// custom config for dovecot\n\t\t\t\t\t$ssl_content = <<<EOSSL\n# Autogenerated configuration by froxlor.\n# Do not manually edit this file as it will be overwritten.\n\nssl = yes\nssl_server_cert_file = {$fullchain}\nssl_server_key_file = {$keyfile}\nEOSSL;\n\t\t\t\t\tfile_put_contents($dovecot_conf, $ssl_content);\n\t\t\t} elseif (file_exists($dovecot_conf)) {\n\t\t\t\t// safely remove the autogenerated config file\n\t\t\t\tunlink($dovecot_conf);\n\t\t\t}\n\t\t\tif (Settings::IsInList('system.le_renew_services', 'proftpd')) {\n\t\t\t\t$proftpd_conf = '/etc/proftpd/tls.conf'; // @fixme setting?\n\t\t\t\t$rval = false;\n\t\t\t\t// ECC certificate or not?\n\t\t\t\tif (strpos($certificate_folder, '_ecc') === false) {\n\t\t\t\t\t// comment out ECC related settings\n\t\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^TLSECCertificateFile|# TLSECCertificateFile|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^TLSECCertificateKeyFile|# TLSECCertificateKeyFile|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t\t\t// add RSA directives\n\t\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^#\\?\\s\\?TLSRSACertificateFile.*|TLSRSACertificateFile \" . $fullchain . \"|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^#\\?\\s\\?TLSRSACertificateKeyFile.*|TLSRSACertificateKeyFile \" . $keyfile . \"|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t\t} else {\n\t\t\t\t\t// comment out RSA related settings\n\t\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^TLSRSACertificateFile|# TLSRSACertificateFile|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^TLSRSACertificateKeyFile|# TLSRSACertificateKeyFile|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t\t\t// add ECC directives\n\t\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^#\\?\\s\\?TLSECCertificateFile.*|TLSECCertificateFile \" . $fullchain . \"|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^#\\?\\s\\?TLSECCertificateKeyFile.*|TLSECCertificateKeyFile \" . $keyfile . \"|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t\t}\n\t\t\t\tFileDir::safe_exec(\"sed -i.bak 's|^#\\?\\s\\?TLSCACertificateFile.*|TLSCACertificateFile \" . $ca_file . \"|' \" . escapeshellarg($proftpd_conf), $rval, ['|', '?']);\n\t\t\t}\n\n\t\t\t// reload the services\n\t\t\tFileDir::safe_exec(Settings::Get('system.le_renew_hook'));\n\t\t}\n\t}\n\n\tprivate static function certToDb($certrow, &$cronlog, $acme_result): bool\n\t{\n\t\t$return = [];\n\t\tself::readCertificateToVar(strtolower($certrow['domain']), $return, $cronlog);\n\n\t\tif (!empty($return['crt'])) {\n\t\t\t$newcert = openssl_x509_parse($return['crt']);\n\n\t\t\tif ($newcert) {\n\t\t\t\t// Store the new data\n\t\t\t\tDatabase::pexecute(self::$updcert_stmt, [\n\t\t\t\t\t'id' => $certrow['id'],\n\t\t\t\t\t'domainid' => $certrow['domainid'],\n\t\t\t\t\t'crt' => $return['crt'],\n\t\t\t\t\t'key' => $return['key'],\n\t\t\t\t\t'ca' => $return['chain'],\n\t\t\t\t\t'chain' => $return['chain'],\n\t\t\t\t\t'csr' => $return['csr'],\n\t\t\t\t\t'fullchain' => $return['fullchain'],\n\t\t\t\t\t'validfromdate' => date('Y-m-d H:i:s', $newcert['validFrom_time_t']),\n\t\t\t\t\t'validtodate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']),\n\t\t\t\t\t'issuer' => $newcert['issuer']['O'] ?? \"\"\n\t\t\t\t]);\n\n\t\t\t\tif ($certrow['ssl_redirect'] == 3) {\n\t\t\t\t\tDatabase::pexecute(self::$upddom_stmt, [\n\t\t\t\t\t\t'domainid' => $certrow['domainid']\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Updated Let's Encrypt certificate for \" . $certrow['domain']);\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Got non-successful Let's Encrypt response for \" . $certrow['domain'] . \":\\n\" . implode(\"\\n\", $acme_result));\n\t\t\t}\n\t\t} else {\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Could not get Let's Encrypt certificate for \" . $certrow['domain'] . \":\\n\" . implode(\"\\n\", $acme_result));\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * get certificate files from filesystem and store in $return array\n\t *\n\t * @param string $domain\n\t * @param array $return\n\t * @param object $cronlog\n\t */\n\tprivate static function readCertificateToVar($domain, &$return, &$cronlog)\n\t{\n\t\t$certificate_folder = self::getCertificateFolder($domain);\n\n\t\tif (!empty($certificate_folder)) {\n\t\t\t$certificate_files = [\n\t\t\t\t'crt' => $domain . '.cer',\n\t\t\t\t'key' => $domain . '.key',\n\t\t\t\t'chain' => 'ca.cer',\n\t\t\t\t'fullchain' => 'fullchain.cer',\n\t\t\t\t'csr' => $domain . '.csr'\n\t\t\t];\n\t\t\tforeach ($certificate_files as $index => $sslfile) {\n\t\t\t\t$ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . $sslfile);\n\t\t\t\tif (file_exists($ssl_file)) {\n\t\t\t\t\t$return[$index] = file_get_contents($ssl_file);\n\t\t\t\t} else {\n\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Could not find file '\" . $sslfile . \"' in '\" . $certificate_folder . \"'\");\n\t\t\t\t\t$return[$index] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Could not find certificate-folder '\" . $certificate_folder . \"'\");\n\t\t}\n\t}\n\n\tprivate static function getCertificateFolder(string $domain): string\n\t{\n\t\t$certificate_folder = self::getWorkingDirFromEnv(strtolower($domain));\n\t\tif (file_exists($certificate_folder)) {\n\t\t\treturn $certificate_folder;\n\t\t}\n\t\t$certificate_folder_ecc = self::getWorkingDirFromEnv($domain, true);\n\t\tif (file_exists($certificate_folder_ecc)) {\n\t\t\treturn $certificate_folder_ecc;\n\t\t}\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Could not find certificate-folder for domain '\" . $domain . \"'\");\n\t\treturn \"\";\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/LetsEncrypt/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cron/Http/Nginx.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http;\n\nuse Froxlor\\Cron\\Http\\Php\\PhpInterface;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Http\\Directory;\nuse Froxlor\\Http\\Statistics;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\Validate\\Validate;\nuse Froxlor\\System\\Crypt;\nuse PDO;\n\nclass Nginx extends HttpConfigBase\n{\n\n\tprotected $nginx_data = [];\n\n\t// protected\n\tprotected $needed_htpasswds = [];\n\tprotected $http2_on_directive = false;\n\tprotected $htpasswds_data = [];\n\tprotected $known_htpasswdsfilenames = [];\n\tprotected $vhost_root_autoindex = false;\n\n\t/**\n\t * indicator whether a customer is deactivated or not\n\t * if yes, only the webroot will be generated\n\t *\n\t * @var bool\n\t */\n\tprivate $deactivated = false;\n\n\tpublic function __construct()\n\t{\n\t\t$nores = false;\n\t\t$res = FileDir::safe_exec('nginx -v 2>&1', $nores, ['>', '&']);\n\t\t$ver_str = array_shift($res);\n\t\t$cNginxVer = substr($ver_str, strrpos($ver_str, \"/\") + 1);\n\t\tif (version_compare($cNginxVer, '1.25.1', '>=')) {\n\t\t\t// at least 1.25.1\n\t\t\t$this->http2_on_directive = true;\n\t\t}\n\t}\n\n\tpublic function createVirtualHosts()\n\t{\n\t\treturn;\n\t}\n\n\tpublic function createFileDirOptions()\n\t{\n\t\treturn;\n\t}\n\n\tpublic function createIpPort()\n\t{\n\t\t$this->createLogformatEntry();\n\n\t\t$result_ipsandports_stmt = Database::query(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` ORDER BY `ip` ASC, `port` ASC\n\t\t\");\n\n\t\twhile ($row_ipsandports = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (filter_var($row_ipsandports['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t$ip = '[' . $row_ipsandports['ip'] . ']';\n\t\t\t} else {\n\t\t\t\t$ip = $row_ipsandports['ip'];\n\t\t\t}\n\t\t\t$port = $row_ipsandports['port'];\n\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'nginx::createIpPort: creating ip/port settings for  ' . $ip . \":\" . $port);\n\t\t\t$vhost_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/10_froxlor_ipandport_' . trim(str_replace(':', '.', $row_ipsandports['ip']), '.') . '.' . $row_ipsandports['port'] . '.conf');\n\n\t\t\tif (!isset($this->nginx_data[$vhost_filename])) {\n\t\t\t\t$this->nginx_data[$vhost_filename] = '';\n\t\t\t}\n\n\t\t\tif ($row_ipsandports['vhostcontainer'] == '1') {\n\t\t\t\t$this->nginx_data[$vhost_filename] .= 'server { ' . \"\\n\";\n\n\t\t\t\t$mypath = $this->getMyPath($row_ipsandports);\n\n\t\t\t\t// check for ssl before anything else so\n\t\t\t\t// we know whether it's an ssl vhost or not\n\t\t\t\t$ssl_vhost = false;\n\t\t\t\tif ($row_ipsandports['ssl'] == '1') {\n\t\t\t\t\t// check for required fallback\n\t\t\t\t\tif (($row_ipsandports['ssl_cert_file'] == '' || !file_exists($row_ipsandports['ssl_cert_file'])) && (Settings::Get('system.le_froxlor_enabled') == '0' || $this->froxlorVhostHasLetsEncryptCert() == false)) {\n\t\t\t\t\t\t$row_ipsandports['ssl_cert_file'] = Settings::Get('system.ssl_cert_file');\n\t\t\t\t\t\tif (!file_exists($row_ipsandports['ssl_cert_file'])) {\n\t\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'System certificate file \"' . Settings::Get('system.ssl_cert_file') . '\" does not seem to exist. Creating self-signed certificate...');\n\t\t\t\t\t\t\tCrypt::createSelfSignedCertificate();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ($row_ipsandports['ssl_key_file'] == '') {\n\t\t\t\t\t\t$row_ipsandports['ssl_key_file'] = Settings::Get('system.ssl_key_file');\n\t\t\t\t\t\tif (!file_exists($row_ipsandports['ssl_key_file'])) {\n\t\t\t\t\t\t\t// explicitly disable ssl for this vhost\n\t\t\t\t\t\t\t$row_ipsandports['ssl_cert_file'] = \"\";\n\t\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'System certificate key-file \"' . Settings::Get('system.ssl_key_file') . '\" does not seem to exist. Disabling SSL-vhost for \"' . Settings::Get('system.hostname') . '\"');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ($row_ipsandports['ssl_ca_file'] == '') {\n\t\t\t\t\t\t$row_ipsandports['ssl_ca_file'] = Settings::Get('system.ssl_ca_file');\n\t\t\t\t\t}\n\t\t\t\t\tif ($row_ipsandports['ssl_cert_file'] != '' && file_exists($row_ipsandports['ssl_cert_file'])) {\n\t\t\t\t\t\t$ssl_vhost = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t$domain = [\n\t\t\t\t\t\t'id' => 0,\n\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t'adminid' => 1, /* first admin-user (superadmin) */\n\t\t\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t'customerroot' => $mypath,\n\t\t\t\t\t\t'parentdomainid' => 0\n\t\t\t\t\t];\n\n\t\t\t\t\t// override corresponding array values\n\t\t\t\t\t$domain['ssl_cert_file'] = $row_ipsandports['ssl_cert_file'];\n\t\t\t\t\t$domain['ssl_key_file'] = $row_ipsandports['ssl_key_file'];\n\t\t\t\t\t$domain['ssl_ca_file'] = $row_ipsandports['ssl_ca_file'];\n\t\t\t\t\t$domain['ssl_cert_chainfile'] = $row_ipsandports['ssl_cert_chainfile'];\n\n\t\t\t\t\t// SSL STUFF\n\t\t\t\t\t$dssl = new DomainSSL();\n\t\t\t\t\t// this sets the ssl-related array-indices in the $domain array\n\t\t\t\t\t// if the domain has customer-defined ssl-certificates\n\t\t\t\t\t$dssl->setDomainSSLFilesArray($domain);\n\n\t\t\t\t\tif ($domain['ssl_cert_file'] != '' && file_exists($domain['ssl_cert_file'])) {\n\t\t\t\t\t\t// override corresponding array values\n\t\t\t\t\t\t$row_ipsandports['ssl_cert_file'] = $domain['ssl_cert_file'];\n\t\t\t\t\t\t$row_ipsandports['ssl_key_file'] = $domain['ssl_key_file'];\n\t\t\t\t\t\t$row_ipsandports['ssl_ca_file'] = $domain['ssl_ca_file'];\n\t\t\t\t\t\t$row_ipsandports['ssl_cert_chainfile'] = $domain['ssl_cert_chainfile'];\n\t\t\t\t\t\t$ssl_vhost = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$http2 = $ssl_vhost == true && Settings::Get('system.http2_support') == '1';\n\t\t\t\t$http3 = $ssl_vhost == true && Settings::Get('system.http3_support') == '1';\n\n\t\t\t\t/**\n\t\t\t\t * this HAS to be set for the default host in nginx or else no vhost will work\n\t\t\t\t */\n\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'listen    ' . $ip . ':' . $port . ' default_server' . ($ssl_vhost == true ? ' ssl' : '') . ($http2 && !$this->http2_on_directive ? ' http2' : '') . ';' . \"\\n\";\n\t\t\t\tif ($http2 && $this->http2_on_directive) {\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'http2 on;' . \"\\n\";\n\t\t\t\t}\n\t\t\t\tif ($http3) {\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'listen    ' . $ip . ':' . $port . ' default_server quic reuseport;' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'http3 on;' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'quic_gso on;' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'quic_retry on;' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'add_header Alt-Svc \\'h3=\":' . $port . '\"; ma=86400\\' always;' . \"\\n\";\n\t\t\t\t}\n\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . '# Froxlor default vhost' . \"\\n\";\n\n\t\t\t\t$aliases = \"\";\n\t\t\t\t$froxlor_aliases = Settings::Get('system.froxloraliases');\n\t\t\t\tif (!empty($froxlor_aliases)) {\n\t\t\t\t\t$froxlor_aliases = explode(\",\", $froxlor_aliases);\n\t\t\t\t\tforeach ($froxlor_aliases as $falias) {\n\t\t\t\t\t\tif (Validate::validateDomain(trim($falias))) {\n\t\t\t\t\t\t\t$aliases .= trim($falias) . \" \";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t$aliases = \" \" . trim($aliases);\n\t\t\t\t}\n\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'server_name    ' . Settings::Get('system.hostname') . $aliases . ';' . \"\\n\";\n\n\t\t\t\t$logtype = 'combined';\n\t\t\t\tif (Settings::Get('system.logfiles_format') != '') {\n\t\t\t\t\t$logtype = 'frx_custom';\n\t\t\t\t}\n\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'access_log     /var/log/nginx/access.log ' . $logtype . ';' . \"\\n\";\n\n\t\t\t\tif (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.leenabled') == '1' && Settings::Get('system.le_froxlor_enabled') == '1') {\n\t\t\t\t\t$acmeConfFilename = Settings::Get('system.letsencryptacmeconf');\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'include ' . $acmeConfFilename . ';' . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\t$is_redirect = false;\n\t\t\t\t// check for SSL redirect\n\t\t\t\tif ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') {\n\t\t\t\t\t$is_redirect = true;\n\t\t\t\t\t// check whether froxlor uses Let's Encrypt and not cert is being generated yet\n\t\t\t\t\t// or a renewal is ongoing - disable redirect\n\t\t\t\t\tif (Settings::Get('system.leenabled') == '1' && Settings::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) {\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename] .= '# temp. disabled ssl-redirect due to Let\\'s Encrypt certificate generation.' . PHP_EOL;\n\t\t\t\t\t\t$is_redirect = false;\n\t\t\t\t\t\tCronjob::inserttask(TaskId::REBUILD_VHOST);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$_sslport = $this->checkAlternativeSslPort();\n\t\t\t\t\t\t$mypath = 'https://' . Settings::Get('system.hostname') . $_sslport;\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'location / {' . \"\\n\";\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\t\" . 'return 301 ' . $mypath . '$request_uri;' . \"\\n\";\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . '}' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!$is_redirect) {\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'root     ' . $mypath . ';' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'index    index.php index.html index.htm;' . \"\\n\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'location / {' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . '}' . \"\\n\";\n\n\t\t\t\t\tif (Settings::Get('system.froxlordirectlyviahostname')) {\n\t\t\t\t\t\t$relpath = \"/\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$relpath = \"/\".basename(Froxlor::getInstallDir());\n\t\t\t\t\t}\n\t\t\t\t\t// protect lib/userdata.inc.php\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'location = ' . rtrim($relpath, \"/\") . '/lib/userdata.inc.php {' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . '    deny all;' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . '}' . \"\\n\";\n\n\t\t\t\t\t// protect bin/\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . 'location ~ ^' . rtrim($relpath, \"/\") . '/(bin|cache|logs|tests|vendor) {' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . '    deny all;' . \"\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\" . '}' . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif ($row_ipsandports['specialsettings'] != '' && ($row_ipsandports['ssl'] == '0' || ($row_ipsandports['ssl'] == '1' && Settings::Get('system.use_ssl') == '1' && $row_ipsandports['include_specialsettings'] == '1'))) {\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['specialsettings'], [\n\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t'loginname' => Settings::Get('phpfpm.vhost_httpuser'),\n\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t'customerroot' => $mypath\n\t\t\t\t\t], $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * SSL config options\n\t\t\t\t */\n\t\t\t\tif ($row_ipsandports['ssl'] == '1') {\n\t\t\t\t\t$row_ipsandports['domain'] = Settings::Get('system.hostname');\n\t\t\t\t\t$row_ipsandports['ssl_honorcipherorder'] = Settings::Get('system.honorcipherorder');\n\t\t\t\t\t$row_ipsandports['ssl_sessiontickets'] = Settings::Get('system.sessiontickets');\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= $this->composeSslSettings($row_ipsandports);\n\t\t\t\t\tif ($row_ipsandports['ssl_specialsettings'] != '') {\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['ssl_specialsettings'], [\n\t\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t\t'loginname' => Settings::Get('phpfpm.vhost_httpuser'),\n\t\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t\t'customerroot' => $mypath\n\t\t\t\t\t\t], $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!$is_redirect) {\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\tlocation ~ \\.php {\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tfastcgi_split_path_info ^(.+?\\.php)(/.*)$;\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tinclude \" . Settings::Get('nginx.fastcgiparams') . \";\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tfastcgi_param SCRIPT_FILENAME \\$request_filename;\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\ttry_files \\$fastcgi_script_name =404;\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tset \\$path_info \\$fastcgi_path_info;\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tfastcgi_param PATH_INFO \\$path_info;\\n\";\n\n\n\t\t\t\t\tif ($row_ipsandports['ssl'] == '1') {\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tfastcgi_param HTTPS on;\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1 && (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) {\n\t\t\t\t\t\t// get fpm config\n\t\t\t\t\t\t$fpm_sel_stmt = Database::prepare(\"\n\t\t\t\t\t\t\tSELECT f.id FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` f\n\t\t\t\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_PHPCONFIGS . \"` p ON p.fpmsettingid = f.id\n\t\t\t\t\t\t\tWHERE p.id = :phpconfigid\n\t\t\t\t\t\t\");\n\t\t\t\t\t\t$fpm_config = Database::pexecute_first($fpm_sel_stmt, [\n\t\t\t\t\t\t\t'phpconfigid' => Settings::Get('phpfpm.vhost_defaultini')\n\t\t\t\t\t\t]);\n\t\t\t\t\t\t$domain = [\n\t\t\t\t\t\t\t'id' => 'none',\n\t\t\t\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t\t\t\t'adminid' => 1, /* first admin-user (superadmin) */\n\t\t\t\t\t\t\t'mod_fcgid_starter' => -1,\n\t\t\t\t\t\t\t'mod_fcgid_maxrequests' => -1,\n\t\t\t\t\t\t\t'guid' => Settings::Get('phpfpm.vhost_httpuser'),\n\t\t\t\t\t\t\t'openbasedir' => 0,\n\t\t\t\t\t\t\t'email' => Settings::Get('panel.adminmail'),\n\t\t\t\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t\t\t\t'customerroot' => $mypath,\n\t\t\t\t\t\t\t'fpm_config_id' => isset($fpm_config['id']) ? $fpm_config['id'] : 1\n\t\t\t\t\t\t];\n\n\t\t\t\t\t\t$php = new PhpInterface($domain);\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tfastcgi_pass unix:\" . $php->getInterface()->getSocketFile() . \";\\n\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tfastcgi_pass \" . Settings::Get('system.nginx_php_backend') . \";\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t\\tfastcgi_index index.php;\\n\";\n\t\t\t\t\t$this->nginx_data[$vhost_filename] .= \"\\t}\\n\";\n\t\t\t\t}\n\n\t\t\t\t$this->nginx_data[$vhost_filename] .= \"}\\n\\n\";\n\t\t\t\t// End of Froxlor server{}-part\n\t\t\t}\n\t\t}\n\n\t\t$this->createNginxHosts();\n\n\t\t/**\n\t\t * standard error pages\n\t\t */\n\t\t$this->createStandardErrorHandler();\n\t}\n\n\tprivate function createLogformatEntry()\n\t{\n\t\tif (Settings::Get('system.logfiles_format') != '') {\n\t\t\t$vhosts_folder = '';\n\t\t\tif (is_dir(Settings::Get('system.apacheconf_vhost'))) {\n\t\t\t\t$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));\n\t\t\t} else {\n\t\t\t\t$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));\n\t\t\t}\n\n\t\t\t$vhosts_filename = FileDir::makeCorrectFile($vhosts_folder . '/02_froxlor_logfiles_format.conf');\n\n\t\t\tif (!isset($this->nginx_data[$vhosts_filename])) {\n\t\t\t\t$this->nginx_data[$vhosts_filename] = '';\n\t\t\t}\n\n\t\t\t$logtype = 'frx_custom';\n\t\t\t$this->nginx_data[$vhosts_filename] = 'log_format ' . $logtype . ' ' . Settings::Get('system.logfiles_format') . ';' . \"\\n\";\n\t\t}\n\t}\n\n\tprotected function composeSslSettings($domain_or_ip)\n\t{\n\t\t$sslsettings = '';\n\n\t\tif ($domain_or_ip['ssl_cert_file'] == '' || !file_exists($domain_or_ip['ssl_cert_file'])) {\n\t\t\t$domain_or_ip['ssl_cert_file'] = Settings::Get('system.ssl_cert_file');\n\t\t\tif (!file_exists($domain_or_ip['ssl_cert_file'])) {\n\t\t\t\t// explicitly disable ssl for this vhost\n\t\t\t\t$domain_or_ip['ssl_cert_file'] = \"\";\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'System certificate file \"' . Settings::Get('system.ssl_cert_file') . '\" does not seem to exist. Disabling SSL-vhost for \"' . $domain_or_ip['domain'] . '\"');\n\t\t\t}\n\t\t}\n\n\t\tif ($domain_or_ip['ssl_key_file'] == '' || !file_exists($domain_or_ip['ssl_key_file'])) {\n\t\t\t// use fallback\n\t\t\t$domain_or_ip['ssl_key_file'] = Settings::Get('system.ssl_key_file');\n\t\t\t// check whether it exists\n\t\t\tif (!file_exists($domain_or_ip['ssl_key_file'])) {\n\t\t\t\t// explicitly disable ssl for this vhost\n\t\t\t\t$domain_or_ip['ssl_cert_file'] = \"\";\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'System certificate key-file \"' . Settings::Get('system.ssl_key_file') . '\" does not seem to exist. Disabling SSL-vhost for \"' . $domain_or_ip['domain'] . '\"');\n\t\t\t}\n\t\t}\n\n\t\tif ($domain_or_ip['ssl_ca_file'] == '') {\n\t\t\t$domain_or_ip['ssl_ca_file'] = Settings::Get('system.ssl_ca_file');\n\t\t}\n\n\t\t// #418\n\t\tif ($domain_or_ip['ssl_cert_chainfile'] == '') {\n\t\t\t$domain_or_ip['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile');\n\t\t}\n\n\t\tif ($domain_or_ip['ssl_cert_file'] != '') {\n\t\t\t// check for existence, #1485\n\t\t\tif (!file_exists($domain_or_ip['ssl_cert_file'])) {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate file \"' . $domain_or_ip['ssl_cert_file'] . '\" does not exist! Cannot create ssl-directives');\n\t\t\t} else {\n\t\t\t\t$ssl_protocols = (isset($domain_or_ip['override_tls']) && $domain_or_ip['override_tls'] == '1' && !empty($domain_or_ip['ssl_protocols'])) ? $domain_or_ip['ssl_protocols'] : Settings::Get('system.ssl_protocols');\n\t\t\t\t$ssl_cipher_list = (isset($domain_or_ip['override_tls']) && $domain_or_ip['override_tls'] == '1' && !empty($domain_or_ip['ssl_cipher_list'])) ? $domain_or_ip['ssl_cipher_list'] : Settings::Get('system.ssl_cipher_list');\n\n\t\t\t\t// obsolete: ssl on now belongs to the listen block as 'ssl' at the end\n\t\t\t\t// $sslsettings .= \"\\t\" . 'ssl on;' . \"\\n\";\n\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_protocols ' . str_replace(\",\", \" \", $ssl_protocols) . ';' . \"\\n\";\n\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_ciphers ' . $ssl_cipher_list . ';' . \"\\n\";\n\t\t\t\tif (!empty(Settings::Get('system.dhparams_file'))) {\n\t\t\t\t\t$dhparams = FileDir::makeCorrectFile(Settings::Get('system.dhparams_file'));\n\t\t\t\t\tif (!file_exists($dhparams)) {\n\t\t\t\t\t\tfile_put_contents($dhparams, self::FFDHE4096);\n\t\t\t\t\t}\n\t\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_dhparam ' . $dhparams . ';' . \"\\n\";\n\t\t\t\t}\n\t\t\t\t// When <1.11.0: Defaults to prime256v1, similar to first curve recommendation by Mozilla.\n\t\t\t\t// (When specifyng just one, there's no fallback when specific curve is not supported by client.)\n\t\t\t\t// When >1.11.0: Defaults to auto, using recommended curves provided by OpenSSL.\n\t\t\t\t// see https://github.com/Froxlor/Froxlor/issues/652\n\t\t\t\t// $sslsettings .= \"\\t\" . 'ssl_ecdh_curve secp384r1;' . \"\\n\";\n\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_prefer_server_ciphers ' . (isset($domain_or_ip['ssl_honorcipherorder']) && $domain_or_ip['ssl_honorcipherorder'] == '1' ? 'on' : 'off') . ';' . \"\\n\";\n\t\t\t\tif (Settings::Get('system.sessionticketsenabled') == '1') {\n\t\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_session_tickets ' . (isset($domain_or_ip['ssl_sessiontickets']) && $domain_or_ip['ssl_sessiontickets'] == '1' ? 'on' : 'off') . ';' . \"\\n\";\n\t\t\t\t}\n\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_session_cache shared:SSL:10m;' . \"\\n\";\n\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_certificate ' . FileDir::makeCorrectFile($domain_or_ip['ssl_cert_file']) . ';' . \"\\n\";\n\n\t\t\t\tif ($domain_or_ip['ssl_key_file'] != '') {\n\t\t\t\t\t// check for existence, #1485\n\t\t\t\t\tif (!file_exists($domain_or_ip['ssl_key_file'])) {\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate key file \"' . $domain_or_ip['ssl_key_file'] . '\" does not exist! Cannot create ssl-directives');\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_certificate_key ' . FileDir::makeCorrectFile($domain_or_ip['ssl_key_file']) . ';' . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isset($domain_or_ip['hsts']) && $domain_or_ip['hsts'] >= 0) {\n\t\t\t\t\t$sslsettings .= 'add_header Strict-Transport-Security \"max-age=' . $domain_or_ip['hsts'];\n\t\t\t\t\tif ($domain_or_ip['hsts_sub'] == 1) {\n\t\t\t\t\t\t$sslsettings .= '; includeSubDomains';\n\t\t\t\t\t}\n\t\t\t\t\tif ($domain_or_ip['hsts_preload'] == 1) {\n\t\t\t\t\t\t$sslsettings .= '; preload';\n\t\t\t\t\t}\n\t\t\t\t\t$sslsettings .= '\" always;' . \"\\n\";\n\t\t\t\t}\n\n\t\t\t\tif ((isset($domain_or_ip['ocsp_stapling']) && $domain_or_ip['ocsp_stapling'] == \"1\")) {\n\t\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_stapling on;' . \"\\n\";\n\t\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_stapling_verify on;' . \"\\n\";\n\t\t\t\t\t$sslsettings .= \"\\t\" . 'ssl_trusted_certificate ' . FileDir::makeCorrectFile($domain_or_ip['ssl_cert_file']) . ';' . \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $sslsettings;\n\t}\n\n\t/**\n\t * create vhosts\n\t */\n\tprotected function createNginxHosts()\n\t{\n\t\t$domains = WebserverBase::getVhostsToCreate();\n\t\tforeach ($domains as $domain) {\n\t\t\tif (is_dir(Settings::Get('system.apacheconf_vhost'))) {\n\t\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'))));\n\t\t\t}\n\n\t\t\t$vhost_filename = $this->getVhostFilename($domain);\n\n\t\t\tif (!isset($this->nginx_data[$vhost_filename])) {\n\t\t\t\t$this->nginx_data[$vhost_filename] = '';\n\t\t\t}\n\n\t\t\tif ((empty($this->nginx_data[$vhost_filename]) && !is_dir(Settings::Get('system.apacheconf_vhost'))) || is_dir(Settings::Get('system.apacheconf_vhost'))) {\n\t\t\t\t$domain['nonexistinguri'] = '/' . md5(uniqid(microtime(), 1)) . '.htm';\n\n\t\t\t\t// Create non-ssl host\n\t\t\t\t$this->nginx_data[$vhost_filename] .= $this->getVhostContent($domain, false);\n\t\t\t\tif ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1') {\n\t\t\t\t\t$vhost_filename_ssl = $this->getVhostFilename($domain, true);\n\t\t\t\t\tif (!isset($this->nginx_data[$vhost_filename_ssl])) {\n\t\t\t\t\t\t$this->nginx_data[$vhost_filename_ssl] = '';\n\t\t\t\t\t}\n\t\t\t\t\t// Now enable ssl stuff\n\t\t\t\t\t$this->nginx_data[$vhost_filename_ssl] .= $this->getVhostContent($domain, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected function getVhostContent($domain, $ssl_vhost = false)\n\t{\n\t\tif ($ssl_vhost === true && $domain['ssl'] != '1' && $domain['ssl_redirect'] != '1') {\n\t\t\treturn '';\n\t\t}\n\n\t\t// check whether the customer/domain is deactivated and NO docroot for deactivated users has been set#\n\t\t$ddr = Settings::Get('system.deactivateddocroot');\n\t\tif (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && empty($ddr)) {\n\t\t\treturn '# Customer deactivated and a docroot for deactivated users/domains hasn\\'t been set.' . \"\\n\";\n\t\t}\n\n\t\t$vhost_content = '';\n\t\t$_vhost_content = '';\n\t\t$has_http2_on = false;\n\n\t\t$query = \"SELECT * FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` `i`, `\" . TABLE_DOMAINTOIP . \"` `dip`\n\t\t\tWHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports \";\n\n\t\tif ($ssl_vhost === true && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1')) {\n\t\t\t// by ordering by cert-file the row with filled out SSL-Fields will be shown last,\n\t\t\t// thus it is enough to fill out 1 set of SSL-Fields\n\t\t\t$query .= \"AND i.ssl = 1 ORDER BY i.ssl_cert_file ASC;\";\n\t\t} else {\n\t\t\t$query .= \"AND i.ssl = '0';\";\n\t\t}\n\n\t\t// start vhost\n\t\t$vhost_content .= 'server { ' . \"\\n\";\n\n\t\t$result_stmt = Database::prepare($query);\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'domainid' => $domain['id']\n\t\t]);\n\n\t\t$http3 = $ssl_vhost == true && (isset($domain['http3']) && $domain['http3'] == '1' && Settings::Get('system.http3_support') == '1');\n\t\twhile ($ipandport = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$domain['ip'] = $ipandport['ip'];\n\t\t\t$domain['port'] = $ipandport['port'];\n\t\t\tif ($domain['ssl'] == '1') {\n\t\t\t\t$domain['ssl_cert_file'] = $ipandport['ssl_cert_file'];\n\t\t\t\t$domain['ssl_key_file'] = $ipandport['ssl_key_file'];\n\t\t\t\t$domain['ssl_ca_file'] = $ipandport['ssl_ca_file'];\n\t\t\t\t$domain['ssl_cert_chainfile'] = $ipandport['ssl_cert_chainfile'];\n\n\t\t\t\t// SSL STUFF\n\t\t\t\t$dssl = new DomainSSL();\n\t\t\t\t// this sets the ssl-related array-indices in the $domain array\n\t\t\t\t// if the domain has customer-defined ssl-certificates\n\t\t\t\t$dssl->setDomainSSLFilesArray($domain);\n\t\t\t}\n\n\t\t\tif (filter_var($domain['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t$ipport = '[' . $domain['ip'] . ']:' . $domain['port'];\n\t\t\t} else {\n\t\t\t\t$ipport = $domain['ip'] . ':' . $domain['port'];\n\t\t\t}\n\n\t\t\tif ($ipandport['default_vhostconf_domain'] != '' && ($ssl_vhost == false || ($ssl_vhost == true && $ipandport['include_default_vhostconf_domain'] == '1'))) {\n\t\t\t\t$_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\";\n\t\t\t}\n\t\t\tif ($ipandport['ssl_default_vhostconf_domain'] != '' && $ssl_vhost == true) {\n\t\t\t\t$_vhost_content .= $this->processSpecialConfigTemplate($ipandport['ssl_default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\";\n\t\t\t}\n\t\t\t$http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1');\n\n\t\t\t$vhost_content .= \"\\t\" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ($http2 && !$this->http2_on_directive ? ' http2' : '') . ';' . \"\\n\";\n\t\t\tif ($http2 && $this->http2_on_directive && !$has_http2_on) {\n\t\t\t\t$vhost_content .= \"\\t\" . 'http2 on;' . \"\\n\";\n\t\t\t\t$has_http2_on = true;\n\t\t\t}\n\t\t\tif ($http3) {\n\t\t\t\t$vhost_content .= \"\\t\" . 'listen    ' . $ipport . ' quic;' . \"\\n\";\n\t\t\t}\n\t\t}\n\n\t\tif ($http3) {\n\t\t\t$vhost_content .= \"\\t\" . 'add_header Alt-Svc \\'h3=\":' . $domain['port'] . '\"; ma=86400\\' always;' . \"\\n\";\n\t\t\t$vhost_content .= \"\\t\" . 'http3 on;' . \"\\n\";\n\t\t\t$vhost_content .= \"\\t\" . 'quic_gso on;' . \"\\n\";\n\t\t\t$vhost_content .= \"\\t\" . 'quic_retry on;' . \"\\n\";\n\t\t}\n\n\t\t// get all server-names\n\t\t$vhost_content .= $this->getServerNames($domain);\n\n\t\t// respect ssl_redirect settings, #542\n\t\tif ($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1') {\n\t\t\t// We must not check if our port differs from port 443,\n\t\t\t// but if there is a destination-port != 443\n\t\t\t$_sslport = '';\n\t\t\t// This returns the first port that is != 443 with ssl enabled, if any\n\t\t\t// ordered by ssl-certificate (if any) so that the ip/port combo\n\t\t\t// with certificate is used\n\t\t\t$ssldestport_stmt = Database::prepare(\"SELECT `ip`.`port` FROM \" . TABLE_PANEL_IPSANDPORTS . \" `ip`\n\t\t\t\tLEFT JOIN `\" . TABLE_DOMAINTOIP . \"` `dip` ON (`ip`.`id` = `dip`.`id_ipandports`)\n\t\t\t\tWHERE `dip`.`id_domain` = :domainid\n\t\t\t\tAND `ip`.`ssl` = '1'  AND `ip`.`port` != 443\n\t\t\t\tORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1;\");\n\t\t\t$ssldestport = Database::pexecute_first($ssldestport_stmt, [\n\t\t\t\t'domainid' => $domain['id']\n\t\t\t]);\n\n\t\t\tif ($ssldestport && $ssldestport['port'] != '') {\n\t\t\t\t$_sslport = \":\" . $ssldestport['port'];\n\t\t\t}\n\n\t\t\t$domain['documentroot'] = 'https://$host' . $_sslport . '/';\n\t\t}\n\n\t\t// avoid using any whitespaces\n\t\t$domain['documentroot'] = trim($domain['documentroot']);\n\n\t\t// create ssl settings first since they are required for normal and redirect vhosts\n\t\tif ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') {\n\t\t\t$vhost_content .= \"\\n\" . $this->composeSslSettings($domain) . \"\\n\";\n\t\t}\n\n\t\tif (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.leenabled') == '1') {\n\t\t\t$acmeConfFilename = Settings::Get('system.letsencryptacmeconf');\n\t\t\t$vhost_content .= \"\\t\" . 'include ' . $acmeConfFilename . ';' . \"\\n\";\n\t\t}\n\n\t\t// if the documentroot is an URL we just redirect\n\t\tif (preg_match('/^https?\\:\\/\\//', $domain['documentroot'])) {\n\t\t\t$possible_deactivated_webroot = $this->getWebroot($domain);\n\t\t\tif ($this->deactivated == false) {\n\t\t\t\t$uri = $domain['documentroot'];\n\t\t\t\tif (substr($uri, -1) == '/') {\n\t\t\t\t\t$uri = substr($uri, 0, -1);\n\t\t\t\t}\n\n\t\t\t\t// Get domain's redirect code\n\t\t\t\t$code = Domain::getDomainRedirectCode($domain['id']);\n\n\t\t\t\t$vhost_content .= $this->getLogFiles($domain);\n\t\t\t\t$vhost_content .= \"\\t\" . 'location / {' . \"\\n\";\n\t\t\t\t$vhost_content .= \"\\t\\t\" . 'return ' . $code . ' ' . $uri . '$request_uri;' . \"\\n\";\n\t\t\t\t$vhost_content .= \"\\t\" . '}' . \"\\n\";\n\t\t\t} elseif (Settings::Get('system.deactivateddocroot') != '') {\n\t\t\t\t$vhost_content .= $possible_deactivated_webroot;\n\t\t\t}\n\t\t} else {\n\t\t\tFileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true);\n\n\t\t\t$vhost_content .= $this->getLogFiles($domain);\n\t\t\t$vhost_content .= $this->getWebroot($domain);\n\n\t\t\tif ($this->deactivated == false) {\n\t\t\t\t$vhost_content = $this->mergeVhostCustom($vhost_content, $this->createPathOptions($domain)) . \"\\n\";\n\t\t\t\t$vhost_content .= $this->composePhpOptions($domain, $ssl_vhost);\n\n\t\t\t\t$vhost_content .= isset($this->needed_htpasswds[$domain['id']]) ? $this->needed_htpasswds[$domain['id']] . \"\\n\" : '';\n\n\t\t\t\tif ($domain['specialsettings'] != '' && ($ssl_vhost == false || ($ssl_vhost == true && $domain['include_specialsettings'] == 1))) {\n\t\t\t\t\t$vhost_content = $this->mergeVhostCustom($vhost_content, $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost));\n\t\t\t\t}\n\n\t\t\t\tif ($domain['ssl_specialsettings'] != '' && $ssl_vhost == true) {\n\t\t\t\t\t$vhost_content = $this->mergeVhostCustom($vhost_content, $this->processSpecialConfigTemplate($domain['ssl_specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost));\n\t\t\t\t}\n\n\t\t\t\tif ($_vhost_content != '') {\n\t\t\t\t\t$vhost_content = $this->mergeVhostCustom($vhost_content, $_vhost_content);\n\t\t\t\t}\n\n\t\t\t\tif (Settings::Get('system.default_vhostconf') != '' && ($ssl_vhost == false || ($ssl_vhost == true && Settings::Get('system.include_default_vhostconf') == 1))) {\n\t\t\t\t\t$vhost_content = $this->mergeVhostCustom($vhost_content, $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\");\n\t\t\t\t}\n\n\t\t\t\tif (Settings::Get('system.default_sslvhostconf') != '' && $ssl_vhost == true) {\n\t\t\t\t\t$vhost_content = $this->mergeVhostCustom($vhost_content, $this->processSpecialConfigTemplate(Settings::Get('system.default_sslvhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . \"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t$vhost_content .= \"\\n}\\n\\n\";\n\n\t\treturn $vhost_content;\n\t}\n\n\tprotected function getServerNames($domain)\n\t{\n\t\t$server_alias = '';\n\n\t\tif ($domain['iswildcarddomain'] == '1') {\n\t\t\t$server_alias = '*.' . $domain['domain'];\n\t\t} elseif ($domain['wwwserveralias'] == '1') {\n\t\t\t$server_alias = 'www.' . $domain['domain'];\n\t\t}\n\n\t\t$alias_domains_stmt = Database::prepare(\"\n\t\t\tSELECT `domain`, `iswildcarddomain`, `wwwserveralias`\n\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\tWHERE `aliasdomain` = :domainid\n\t\t\");\n\t\tDatabase::pexecute($alias_domains_stmt, [\n\t\t\t'domainid' => $domain['id']\n\t\t]);\n\n\t\twhile (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) {\n\t\t\t$server_alias .= ' ' . $alias_domain['domain'];\n\n\t\t\tif ($alias_domain['iswildcarddomain'] == '1') {\n\t\t\t\t$server_alias .= ' *.' . $alias_domain['domain'];\n\t\t\t} elseif ($alias_domain['wwwserveralias'] == '1') {\n\t\t\t\t$server_alias .= ' www.' . $alias_domain['domain'];\n\t\t\t}\n\t\t}\n\n\t\t$servernames_text = \"\\t\" . 'server_name    ' . $domain['domain'];\n\t\tif (trim($server_alias) != '') {\n\t\t\t$servernames_text .= ' ' . $server_alias;\n\t\t}\n\t\t$servernames_text .= ';' . \"\\n\";\n\n\t\treturn $servernames_text;\n\t}\n\n\tprotected function getLogFiles($domain)\n\t{\n\t\t$logfiles_text = '';\n\n\t\t$speciallogfile = '';\n\t\tif ($domain['speciallogfile'] == '1') {\n\t\t\tif ($domain['parentdomainid'] == '0') {\n\t\t\t\t$speciallogfile = '-' . $domain['domain'];\n\t\t\t} else {\n\t\t\t\t$speciallogfile = '-' . $domain['parentdomain'];\n\t\t\t}\n\t\t}\n\n\t\tif ($domain['writeerrorlog']) {\n\t\t\t// The normal access/error - logging is enabled\n\t\t\t$error_log = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-error.log');\n\t\t\t// Create the logfile if it does not exist (fixes #46)\n\t\t\ttouch($error_log);\n\t\t\tchmod($error_log, 0640);\n\t\t\tchown($error_log, Settings::Get('system.httpuser'));\n\t\t\tchgrp($error_log, Settings::Get('system.httpgroup'));\n\t\t} else {\n\t\t\t$error_log = '/dev/null';\n\t\t}\n\n\t\tif ($domain['writeaccesslog']) {\n\t\t\t$access_log = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log');\n\t\t\t// Create the logfile if it does not exist (fixes #46)\n\t\t\ttouch($access_log);\n\t\t\tchmod($access_log, 0640);\n\t\t\tchown($access_log, Settings::Get('system.httpuser'));\n\t\t\tchgrp($access_log, Settings::Get('system.httpgroup'));\n\t\t} else {\n\t\t\t$access_log = '/dev/null';\n\t\t}\n\n\t\t$logtype = 'combined';\n\t\tif (Settings::Get('system.logfiles_format') != '') {\n\t\t\t$logtype = 'frx_custom';\n\t\t}\n\n\t\t$logfiles_text .= \"\\t\" . 'access_log    ' . $access_log . ' ' . $logtype . ';' . \"\\n\";\n\t\t$logfiles_text .= \"\\t\" . 'error_log    ' . $error_log . ' ' . Settings::Get('system.errorlog_level') . ';' . \"\\n\";\n\n\t\tif (Settings::Get('system.traffictool') == 'awstats') {\n\t\t\tif ((int)$domain['parentdomainid'] == 0) {\n\t\t\t\t// prepare the aliases and subdomains for stats config files\n\t\t\t\t$server_alias = '';\n\t\t\t\t$alias_domains_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `domain`, `iswildcarddomain`, `wwwserveralias`\n\t\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\t\tWHERE `aliasdomain` = :domainid OR `parentdomainid` = :domainid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($alias_domains_stmt, [\n\t\t\t\t\t'domainid' => $domain['id']\n\t\t\t\t]);\n\n\t\t\t\twhile (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) {\n\t\t\t\t\t$server_alias .= ' ' . $alias_domain['domain'] . ' ';\n\n\t\t\t\t\tif ($alias_domain['iswildcarddomain'] == '1') {\n\t\t\t\t\t\t$server_alias .= '*.' . $domain['domain'];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif ($alias_domain['wwwserveralias'] == '1') {\n\t\t\t\t\t\t\t$server_alias .= 'www.' . $alias_domain['domain'];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$server_alias .= '';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$alias = '';\n\t\t\t\tif ($domain['iswildcarddomain'] == '1') {\n\t\t\t\t\t$alias = '*.' . $domain['domain'];\n\t\t\t\t} elseif ($domain['wwwserveralias'] == '1') {\n\t\t\t\t\t$alias = 'www.' . $domain['domain'];\n\t\t\t\t}\n\n\t\t\t\t// After inserting the AWStats information,\n\t\t\t\t// be sure to build the awstats conf file as well\n\t\t\t\t// and chown it using $awstats_params, #258\n\t\t\t\t// Bug 960 + Bug 970 : Use full $domain instead of custom $awstats_params as following classes depend on the information\n\t\t\t\tStatistics::createAWStatsConf(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log', $domain['domain'], $alias . $server_alias, $domain['customerroot'], $domain);\n\t\t\t}\n\t\t}\n\n\t\treturn $logfiles_text;\n\t}\n\n\tprotected function getWebroot($domain)\n\t{\n\t\t$webroot_text = '';\n\n\t\tif (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1' ) && Settings::Get('system.deactivateddocroot') != '') {\n\t\t\t$webroot_text .= \"\\t\" . '# Using docroot for deactivated users/domains...' . \"\\n\";\n\t\t\t$webroot_text .= \"\\t\" . 'root     ' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . ';' . \"\\n\";\n\t\t\t$this->deactivated = true;\n\t\t} else {\n\t\t\t$webroot_text .= \"\\t\" . 'root     ' . FileDir::makeCorrectDir($domain['documentroot']) . ';' . \"\\n\";\n\t\t\t$this->deactivated = false;\n\t\t}\n\n\t\t$webroot_text .= \"\\n\\t\" . 'location / {' . \"\\n\";\n\n\t\tif ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {\n\t\t\t$webroot_text .= \"\\t\" . 'index    index.php index.html index.htm;' . \"\\n\";\n\t\t\tif ($domain['notryfiles'] != 1) {\n\t\t\t\t$webroot_text .= \"\\t\\t\" . 'try_files $uri $uri/ @rewrites;' . \"\\n\";\n\t\t\t}\n\t\t} else {\n\t\t\t$webroot_text .= \"\\t\" . 'index    index.html index.htm;' . \"\\n\";\n\t\t}\n\n\t\tif ($this->vhost_root_autoindex) {\n\t\t\t$webroot_text .= \"\\t\\t\" . 'autoindex on;' . \"\\n\";\n\t\t\t$this->vhost_root_autoindex = false;\n\t\t}\n\n\t\t$webroot_text .= \"\\t\" . '}' . \"\\n\\n\";\n\t\tif ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1' && $domain['notryfiles'] != 1) {\n\t\t\t$webroot_text .= \"\\tlocation @rewrites {\\n\";\n\t\t\t$webroot_text .= \"\\t\\trewrite ^ /index.php last;\\n\";\n\t\t\t$webroot_text .= \"\\t}\\n\\n\";\n\t\t}\n\n\t\treturn $webroot_text;\n\t}\n\n\tprotected function mergeVhostCustom($vhost_frx, $vhost_usr)\n\t{\n\t\t// Clean froxlor defined settings\n\t\t$vhost_frx = $this->cleanVhostStruct($vhost_frx);\n\t\t// Clean user defined settings\n\t\t$vhost_usr = $this->cleanVhostStruct($vhost_usr);\n\n\t\t// Cycle through the user defined settings\n\t\t$currentBlock = [];\n\t\t$blockLevel = 0;\n\t\tforeach ($vhost_usr as $line) {\n\t\t\t$line = trim($line);\n\t\t\t$currentBlock[] = $line;\n\n\t\t\tif (strpos($line, \"{\") !== false) {\n\t\t\t\t$blockLevel++;\n\t\t\t}\n\t\t\tif (strpos($line, \"}\") !== false && $blockLevel > 0) {\n\t\t\t\t$blockLevel--;\n\t\t\t}\n\n\t\t\tif ($line == \"}\" && $blockLevel == 0) {\n\t\t\t\tif (in_array($currentBlock[0], $vhost_frx)) {\n\t\t\t\t\t// Add to existing block\n\t\t\t\t\t$pos = array_search($currentBlock[0], $vhost_frx);\n\t\t\t\t\tdo {\n\t\t\t\t\t\t$pos++;\n\t\t\t\t\t} while ($vhost_frx[$pos] != \"}\");\n\n\t\t\t\t\tfor ($i = 1; $i < count($currentBlock) - 1; $i++) {\n\t\t\t\t\t\tarray_splice($vhost_frx, $pos + $i - 1, 0, $currentBlock[$i]);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Add to end\n\t\t\t\t\tarray_splice($vhost_frx, count($vhost_frx), 0, $currentBlock);\n\t\t\t\t}\n\t\t\t\t$currentBlock = [];\n\t\t\t} elseif ($blockLevel == 0) {\n\t\t\t\tarray_splice($vhost_frx, count($vhost_frx), 0, $currentBlock);\n\t\t\t\t$currentBlock = [];\n\t\t\t}\n\t\t}\n\n\t\t$nextLevel = 0;\n\t\tfor ($i = 0; $i < count($vhost_frx); $i++) {\n\t\t\tif (substr_count($vhost_frx[$i], \"}\") != 0 && substr_count($vhost_frx[$i], \"{\") == 0) {\n\t\t\t\t$nextLevel -= 1;\n\t\t\t\t$vhost_frx[$i] .= \"\\n\";\n\t\t\t}\n\t\t\tif ($nextLevel > 0) {\n\t\t\t\tfor ($j = 0; $j < $nextLevel; $j++) {\n\t\t\t\t\t$vhost_frx[$i] = \"\t\" . $vhost_frx[$i];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (substr_count($vhost_frx[$i], \"{\") != 0 && substr_count($vhost_frx[$i], \"}\") == 0) {\n\t\t\t\t$nextLevel += 1;\n\t\t\t}\n\t\t}\n\n\t\treturn implode(\"\\n\", $vhost_frx);\n\t}\n\n\tprivate function cleanVhostStruct($vhost = null)\n\t{\n\t\t// Remove windows linebreaks\n\t\t$vhost = str_replace(\"\\r\", \"\\n\", $vhost);\n\t\t// remove comments\n\t\t$vhost = implode(\"\\n\", preg_replace('/^(\\s+)?#(.*)$/', '', explode(\"\\n\", $vhost)));\n\t\t// Break blocks into lines\n\t\t$vhost = preg_replace(\"/^(\\s+)?location(.+)\\{(.+)\\}$/misU\", \"location $2 {\\n $3 \\n}\", $vhost);\n\t\t// Break into array items\n\t\t$vhost = explode(\"\\n\", preg_replace('/[ \\t]+/', ' ', trim(preg_replace('/\\t+/', '', $vhost))));\n\t\t// Remove empty lines\n\t\t$vhost = array_filter($vhost, function ($a) {\n\t\t\treturn preg_match(\"#\\S#\", $a);\n\t\t});\n\n\t\t// remove unnecessary whitespaces\n\t\t$vhost = array_map(\"trim\", $vhost);\n\t\t// re-number array keys\n\t\t$vhost = array_values($vhost);\n\t\treturn $vhost;\n\t}\n\n\tprotected function createPathOptions($domain)\n\t{\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM \" . TABLE_PANEL_HTACCESS . \"\n\t\t\tWHERE `path` LIKE :docroot\n\t\t\");\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'docroot' => $domain['documentroot'] . '%'\n\t\t]);\n\n\t\t$path_options = '';\n\t\t$htpasswds = $this->getHtpasswds($domain);\n\n\t\t// for each entry in the htaccess table\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (!empty($row['error404path'])) {\n\t\t\t\t$defhandler = $row['error404path'];\n\t\t\t\tif (!Validate::validateUrl($defhandler)) {\n\t\t\t\t\t$defhandler = FileDir::makeCorrectFile($defhandler);\n\t\t\t\t}\n\t\t\t\t$path_options .= \"\\t\" . 'error_page   404    ' . $defhandler . ';' . \"\\n\";\n\t\t\t}\n\n\t\t\tif (!empty($row['error403path'])) {\n\t\t\t\t$defhandler = $row['error403path'];\n\t\t\t\tif (!Validate::validateUrl($defhandler)) {\n\t\t\t\t\t$defhandler = FileDir::makeCorrectFile($defhandler);\n\t\t\t\t}\n\t\t\t\t$path_options .= \"\\t\" . 'error_page   403    ' . $defhandler . ';' . \"\\n\";\n\t\t\t}\n\n\t\t\tif (!empty($row['error500path'])) {\n\t\t\t\t$defhandler = $row['error500path'];\n\t\t\t\tif (!Validate::validateUrl($defhandler)) {\n\t\t\t\t\t$defhandler = FileDir::makeCorrectFile($defhandler);\n\t\t\t\t}\n\t\t\t\t$path_options .= \"\\t\" . 'error_page   500 502 503 504    ' . $defhandler . ';' . \"\\n\";\n\t\t\t}\n\n\t\t\t// if ($row['options_indexes'] != '0') {\n\t\t\t$path = FileDir::makeCorrectDir(substr($row['path'], strlen($domain['documentroot']) - 1));\n\n\t\t\tFileDir::mkDirWithCorrectOwnership($domain['documentroot'], $row['path'], $domain['guid'], $domain['guid']);\n\n\t\t\t$path_options .= \"\\t\" . '# ' . $path . \"\\n\";\n\t\t\tif ($path == '/') {\n\t\t\t\tif ($row['options_indexes'] != '0') {\n\t\t\t\t\t$this->vhost_root_autoindex = true;\n\t\t\t\t}\n\t\t\t\t$path_options .= \"\\t\" . 'location ' . FileDir::makeCorrectDir($path) . ' {' . \"\\n\";\n\t\t\t\tif ($this->vhost_root_autoindex) {\n\t\t\t\t\t$path_options .= \"\\t\\t\" . 'autoindex  on;' . \"\\n\";\n\t\t\t\t\t$this->vhost_root_autoindex = false;\n\t\t\t\t}\n\n\t\t\t\t// check if we have a htpasswd for this path\n\t\t\t\t// (damn nginx does not like more than one\n\t\t\t\t// 'location'-part with the same path)\n\t\t\t\tif (count($htpasswds) > 0) {\n\t\t\t\t\tforeach ($htpasswds as $idx => $single) {\n\t\t\t\t\t\tswitch ($single['path']) {\n\t\t\t\t\t\t\tcase '/awstats/':\n\t\t\t\t\t\t\tcase '/webalizer/':\n\t\t\t\t\t\t\tcase '/goaccess/':\n\t\t\t\t\t\t\t\t// no stats-alias in \"location /\"-context\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tif ($single['path'] == '/') {\n\t\t\t\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'auth_basic            \"' . $single['authname'] . '\";' . \"\\n\";\n\t\t\t\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'auth_basic_user_file  ' . FileDir::makeCorrectFile($single['usrf']) . ';' . \"\\n\";\n\t\t\t\t\t\t\t\t\tif ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {\n\t\t\t\t\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'index    index.php index.html index.htm;' . \"\\n\";\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'index    index.html index.htm;' . \"\\n\";\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t// remove already used entries so we do not have doubles\n\t\t\t\t\t\t\t\t\tunset($htpasswds[$idx]);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$path_options .= \"\\t\" . '}' . \"\\n\";\n\n\t\t\t\t$this->vhost_root_autoindex = false;\n\t\t\t} else {\n\t\t\t\t$path_options .= \"\\t\" . 'location ^~ ' . FileDir::makeCorrectFile($path) . ' {' . \"\\n\";\n\t\t\t\tif ($this->vhost_root_autoindex || $row['options_indexes'] != '0') {\n\t\t\t\t\t$path_options .= \"\\t\\t\" . 'autoindex  on;' . \"\\n\";\n\t\t\t\t\t$this->vhost_root_autoindex = false;\n\t\t\t\t}\n\t\t\t\t$path_options .= \"\\t\" . '} ' . \"\\n\";\n\t\t\t}\n\t\t\t// }\n\n\t\t\t/**\n\t\t\t * Perl support\n\t\t\t * required the fastCGI wrapper to be running to receive the CGI requests.\n\t\t\t */\n\t\t\tif (Customer::customerHasPerlEnabled($domain['customerid']) && $row['options_cgi'] != '0') {\n\t\t\t\t$path = FileDir::makeCorrectDir(substr($row['path'], strlen($domain['documentroot']) - 1));\n\t\t\t\tFileDir::mkDirWithCorrectOwnership($domain['documentroot'], $row['path'], $domain['guid'], $domain['guid']);\n\n\t\t\t\t// We need to remove the last slash, otherwise the regex wouldn't work\n\t\t\t\tif ($row['path'] != $domain['documentroot']) {\n\t\t\t\t\t$path = substr($path, 0, -1);\n\t\t\t\t}\n\t\t\t\t$path_options .= \"\\t\" . 'location ~ \\(.pl|.cgi)$ {' . \"\\n\";\n\t\t\t\t$path_options .= \"\\t\\t\" . 'gzip off; #gzip makes scripts feel slower since they have to complete before getting gzipped' . \"\\n\";\n\t\t\t\t$path_options .= \"\\t\\t\" . 'fastcgi_pass  ' . Settings::Get('system.perl_server') . ';' . \"\\n\";\n\t\t\t\t$path_options .= \"\\t\\t\" . 'fastcgi_index index.cgi;' . \"\\n\";\n\t\t\t\t$path_options .= \"\\t\\t\" . 'include ' . Settings::Get('nginx.fastcgiparams') . ';' . \"\\n\";\n\t\t\t\t$path_options .= \"\\t\" . '}' . \"\\n\";\n\t\t\t}\n\t\t}\n\n\t\t// now the rest of the htpasswds\n\t\tif (count($htpasswds) > 0) {\n\t\t\tforeach ($htpasswds as $idx => $single) {\n\t\t\t\t// if ($single['path'] != '/') {\n\t\t\t\tswitch ($single['path']) {\n\t\t\t\t\tcase '/awstats/':\n\t\t\t\t\tcase '/webalizer/':\n\t\t\t\t\tcase '/goaccess/':\n\t\t\t\t\t\t$path_options .= $this->getStats($domain, $single);\n\t\t\t\t\t\tunset($htpasswds[$idx]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif ($single['path'] == '/') {\n\t\t\t\t\t\t\t$path_options .= \"\\t\" . 'location ' . FileDir::makeCorrectDir($single['path']) . ' {' . \"\\n\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$path_options .= \"\\t\" . 'location ^~ ' . FileDir::makeCorrectFile($single['path']) . ' {' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'auth_basic            \"' . $single['authname'] . '\";' . \"\\n\";\n\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'auth_basic_user_file  ' . FileDir::makeCorrectFile($single['usrf']) . ';' . \"\\n\";\n\t\t\t\t\t\tif ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {\n\t\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'index    index.php index.html index.htm;' . \"\\n\";\n\t\t\t\t\t\t\tif ($domain['notryfiles'] != 1) {\n\t\t\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'location ~ ^(.+?\\.php)(/.*)?$ {' . \"\\n\";\n\t\t\t\t\t\t\t\t$path_options .= \"\\t\\t\\t\" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . \"\\n\";\n\t\t\t\t\t\t\t\t$path_options .= \"\\t\\t\" . '}' . \"\\n\\n\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$path_options .= \"\\t\\t\" . 'index    index.html index.htm;' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$path_options .= \"\\t\" . '}' . \"\\n\";\n\t\t\t\t}\n\t\t\t\t// }\n\t\t\t\tunset($htpasswds[$idx]);\n\t\t\t}\n\t\t}\n\n\t\treturn $path_options;\n\t}\n\n\tprotected function getHtpasswds($domain)\n\t{\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT a.*\n\t\t\tFROM `\" . TABLE_PANEL_HTPASSWDS . \"` AS a\n\t\t\tJOIN `\" . TABLE_PANEL_DOMAINS . \"` AS b USING (`customerid`)\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` c ON c.customerid = b.customerid\n\t\t\tWHERE b.customerid = :customerid AND b.domain = :domain\n\t\t\tAND (a.path = CONCAT(c.documentroot, :ttool, '/') OR INSTR(a.path, b.documentroot));\n\t\t\");\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'customerid' => $domain['customerid'],\n\t\t\t'domain' => $domain['domain'],\n\t\t\t'ttool' => Settings::Get('system.traffictool')\n\t\t]);\n\n\t\t$returnval = [];\n\t\t$x = 0;\n\t\twhile ($row_htpasswds = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (count($row_htpasswds) > 0) {\n\t\t\t\t$htpasswd_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $row_htpasswds['customerid'] . '-' . md5($row_htpasswds['path']) . '.htpasswd');\n\n\t\t\t\t// ensure we can write to the array with index $htpasswd_filename\n\t\t\t\tif (!isset($this->htpasswds_data[$htpasswd_filename])) {\n\t\t\t\t\t$this->htpasswds_data[$htpasswd_filename] = '';\n\t\t\t\t}\n\n\t\t\t\t$this->htpasswds_data[$htpasswd_filename] .= $row_htpasswds['username'] . ':' . $row_htpasswds['password'] . \"\\n\";\n\n\t\t\t\t// if the domains and their web contents are located in a subdirectory of\n\t\t\t\t// the nginx user, we have to evaluate the right path which is to protect\n\t\t\t\tif (stripos($row_htpasswds['path'], $domain['documentroot']) !== false) {\n\t\t\t\t\t// if the website contents is located in the user directory\n\t\t\t\t\t$path = FileDir::makeCorrectDir(substr($row_htpasswds['path'], strlen($domain['documentroot']) - 1));\n\t\t\t\t} else {\n\t\t\t\t\t// if the website contents is located in a subdirectory of the user\n\t\t\t\t\t$matches = [];\n\t\t\t\t\tpreg_match('/^([\\/[:print:]]*\\/)([[:print:]\\/]+){1}$/i', $row_htpasswds['path'], $matches);\n\t\t\t\t\t$path = FileDir::makeCorrectDir(substr($row_htpasswds['path'], strlen($matches[1]) - 1));\n\t\t\t\t}\n\n\t\t\t\t$returnval[$x]['path'] = $path;\n\t\t\t\t$returnval[$x]['root'] = FileDir::makeCorrectDir($domain['documentroot']);\n\n\t\t\t\t// Ensure there is only one auth name per password block, otherwise\n\t\t\t\t// the directives are inserted multiple times -> invalid config\n\t\t\t\t$authname = $row_htpasswds['authname'];\n\t\t\t\tfor ($i = 0; $i < $x; $i++) {\n\t\t\t\t\tif ($returnval[$i]['usrf'] == $htpasswd_filename) {\n\t\t\t\t\t\t$authname = $returnval[$i]['authname'];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$returnval[$x]['authname'] = $authname;\n\n\t\t\t\t$returnval[$x]['usrf'] = $htpasswd_filename;\n\t\t\t\t$x++;\n\t\t\t}\n\t\t}\n\n\t\t// Remove duplicate entries\n\t\t$returnval = array_map(\"unserialize\", array_unique(array_map(\"serialize\", $returnval)));\n\n\t\treturn $returnval;\n\t}\n\n\tprotected function getStats($domain, $single)\n\t{\n\t\t$stats_text = '';\n\n\t\t$statTool = Settings::Get('system.traffictool');\n\t\t$statDomain = \"\";\n\t\tif ($statTool == 'awstats') {\n\t\t\t// awstats generates for each domain regardless of speciallogfile\n\t\t\t$statDomain = \"/\" . $domain['domain'];\n\t\t}\n\t\tif ($domain['speciallogfile'] == '1') {\n\t\t\t$statDomain = \"/\" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']);\n\t\t}\n\t\t$statDocroot = FileDir::makeCorrectFile($domain['customerroot'] . '/' . $statTool . $statDomain);\n\n\t\t$stats_text .= \"\\t\" . 'location ^~ /'.$statTool.' {' . \"\\n\";\n\t\t$stats_text .= \"\\t\\t\" . 'alias ' . $statDocroot . '/;' . \"\\n\";\n\t\t$stats_text .= \"\\t\\t\" . 'auth_basic            \"' . $single['authname'] . '\";' . \"\\n\";\n\t\t$stats_text .= \"\\t\\t\" . 'auth_basic_user_file  ' . FileDir::makeCorrectFile($single['usrf']) . ';' . \"\\n\";\n\t\t$stats_text .= \"\\t\" . '}' . \"\\n\\n\";\n\n\t\t// awstats special requirement for icons\n\t\tif ($statTool == 'awstats') {\n\t\t\t$stats_text .= \"\\t\" . 'location ~ ^/awstats-icon/(.*)$ {' . \"\\n\";\n\t\t\t$stats_text .= \"\\t\\t\" . 'alias ' . FileDir::makeCorrectDir(Settings::Get('system.awstats_icons')) . '$1;' . \"\\n\";\n\t\t\t$stats_text .= \"\\t\" . '}' . \"\\n\\n\";\n\t\t}\n\n\t\treturn $stats_text;\n\t}\n\n\tprotected function composePhpOptions(&$domain, $ssl_vhost = false)\n\t{\n\t\t$phpopts = '';\n\t\tif ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {\n\t\t\t$phpopts = \"\\t\" . 'location ~ ^(.+?\\.php)(/.*)?$ {' . \"\\n\";\n\t\t\tif ($domain['notryfiles'] != 1) {\n\t\t\t\t$phpopts .= \"\\t\\t\" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . \"\\n\";\n\t\t\t\t$phpopts .= \"\\t\" . '}' . \"\\n\\n\";\n\n\t\t\t\t$phpopts .= \"\\tlocation @php {\\n\";\n\t\t\t\t$phpopts .= \"\\t\\t\" . 'try_files $1 =404;' . \"\\n\\n\";\n\t\t\t}\n\t\t\t$phpopts .= \"\\t\\tfastcgi_split_path_info ^(.+?\\.php)(/.*)$;\\n\";\n\t\t\t$phpopts .= \"\\t\\tinclude \" . Settings::Get('nginx.fastcgiparams') . \";\\n\";\n\t\t\t$phpopts .= \"\\t\\tfastcgi_param SCRIPT_FILENAME \\$request_filename;\\n\";\n\t\t\t$phpopts .= \"\\t\\tset \\$path_info \\$fastcgi_path_info;\\n\";\n\t\t\t$phpopts .= \"\\t\\tfastcgi_param PATH_INFO \\$path_info;\\n\";\n\t\t\t$phpopts .= \"\\t\\tfastcgi_pass \" . Settings::Get('system.nginx_php_backend') . \";\\n\";\n\t\t\t$phpopts .= \"\\t\\tfastcgi_index index.php;\\n\";\n\t\t\tif ($domain['ssl'] == '1' && $ssl_vhost) {\n\t\t\t\t$phpopts .= \"\\t\\tfastcgi_param HTTPS on;\\n\";\n\t\t\t}\n\t\t\t$phpopts .= \"\\t}\\n\\n\";\n\t\t}\n\t\treturn $phpopts;\n\t}\n\n\t/**\n\t * define a default ErrorDocument-statement, bug #unknown-yet\n\t */\n\tprivate function createStandardErrorHandler()\n\t{\n\t\tif (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && (Settings::Get('defaultwebsrverrhandler.err401') != '' || Settings::Get('defaultwebsrverrhandler.err403') != '' || Settings::Get('defaultwebsrverrhandler.err404') != '' || Settings::Get('defaultwebsrverrhandler.err500') != '')) {\n\t\t\t$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_default_errorhandler.conf');\n\n\t\t\tif (!isset($this->nginx_data[$vhosts_filename])) {\n\t\t\t\t$this->nginx_data[$vhosts_filename] = '';\n\t\t\t}\n\n\t\t\t$statusCodes = [\n\t\t\t\t'401',\n\t\t\t\t'403',\n\t\t\t\t'404',\n\t\t\t\t'500'\n\t\t\t];\n\t\t\tforeach ($statusCodes as $statusCode) {\n\t\t\t\tif (Settings::Get('defaultwebsrverrhandler.err' . $statusCode) != '') {\n\t\t\t\t\t$defhandler = Settings::Get('defaultwebsrverrhandler.err' . $statusCode);\n\t\t\t\t\tif (!Validate::validateUrl($defhandler)) {\n\t\t\t\t\t\t$defhandler = FileDir::makeCorrectFile($defhandler);\n\t\t\t\t\t}\n\t\t\t\t\t$this->nginx_data[$vhosts_filename] .= 'error_page ' . $statusCode . ' ' . $defhandler . ';' . \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic function createOwnVhostStarter()\n\t{\n\t\treturn;\n\t}\n\n\tpublic function writeConfigs()\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"nginx::writeConfigs: rebuilding \" . Settings::Get('system.apacheconf_vhost'));\n\n\t\t$vhostDir = new Directory(Settings::Get('system.apacheconf_vhost'));\n\t\tif (!$vhostDir->isConfigDir()) {\n\t\t\t// Save one big file\n\t\t\t$vhosts_file = '';\n\n\t\t\t// sort by filename so the order is:\n\t\t\t// 1. subdomains\n\t\t\t// 2. subdomains as main-domains\n\t\t\t// 3. main-domains\n\t\t\tksort($this->nginx_data);\n\n\t\t\tforeach ($this->nginx_data as $vhosts_filename => $vhost_content) {\n\t\t\t\t$vhosts_file .= $vhost_content . \"\\n\\n\";\n\t\t\t}\n\n\t\t\t$vhosts_filename = Settings::Get('system.apacheconf_vhost');\n\n\t\t\t// Apply header\n\t\t\t$vhosts_file = '# ' . basename($vhosts_filename) . \"\\n\" . '# Created ' . date('d.m.Y H:i') . \"\\n\" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . \"\\n\" . \"\\n\" . $vhosts_file;\n\t\t\t$vhosts_file_handler = fopen($vhosts_filename, 'w');\n\t\t\tfwrite($vhosts_file_handler, $vhosts_file);\n\t\t\tfclose($vhosts_file_handler);\n\t\t} else {\n\t\t\tif (!file_exists(Settings::Get('system.apacheconf_vhost'))) {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'nginx::writeConfigs: mkdir ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'))));\n\t\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'))));\n\t\t\t}\n\n\t\t\t// Write a single file for every vhost\n\t\t\tforeach ($this->nginx_data as $vhosts_filename => $vhosts_file) {\n\t\t\t\t// Apply header\n\t\t\t\t$vhosts_file = '# ' . basename($vhosts_filename) . \"\\n\" . '# Created ' . date('d.m.Y H:i') . \"\\n\" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . \"\\n\" . \"\\n\" . $vhosts_file;\n\n\t\t\t\tif (!empty($vhosts_filename)) {\n\t\t\t\t\t$vhosts_file_handler = fopen($vhosts_filename, 'w');\n\t\t\t\t\tfwrite($vhosts_file_handler, $vhosts_file);\n\t\t\t\t\tfclose($vhosts_file_handler);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// htaccess stuff\n\t\tif (count($this->htpasswds_data) > 0) {\n\t\t\tif (!file_exists(Settings::Get('system.apacheconf_htpasswddir'))) {\n\t\t\t\t$umask = umask();\n\t\t\t\tumask(0000);\n\t\t\t\tmkdir(Settings::Get('system.apacheconf_htpasswddir'), 0751);\n\t\t\t\tumask($umask);\n\t\t\t} elseif (!is_dir(Settings::Get('system.apacheconf_htpasswddir'))) {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, 'WARNING!!! ' . Settings::Get('system.apacheconf_htpasswddir') . ' is not a directory. htpasswd directory protection is disabled!!!');\n\t\t\t}\n\n\t\t\tif (is_dir(Settings::Get('system.apacheconf_htpasswddir'))) {\n\t\t\t\tforeach ($this->htpasswds_data as $htpasswd_filename => $htpasswd_file) {\n\t\t\t\t\t$this->known_htpasswdsfilenames[] = basename($htpasswd_filename);\n\t\t\t\t\t$htpasswd_file_handler = fopen($htpasswd_filename, 'w');\n\t\t\t\t\t// Filter duplicate pairs of username and password\n\t\t\t\t\t$htpasswd_file = implode(\"\\n\", array_unique(explode(\"\\n\", $htpasswd_file)));\n\t\t\t\t\tfwrite($htpasswd_file_handler, $htpasswd_file);\n\t\t\t\t\tfclose($htpasswd_file_handler);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/NginxFcgi.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http;\n\nuse Froxlor\\Cron\\Http\\Php\\PhpInterface;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\n\nclass NginxFcgi extends Nginx\n{\n\n\tpublic function createOwnVhostStarter()\n\t{\n\t\tif (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.enabled_ownvhost') == '1') {\n\t\t\t$mypath = Froxlor::getInstallDir();\n\n\t\t\t$user = Settings::Get('phpfpm.vhost_httpuser');\n\t\t\t$group = Settings::Get('phpfpm.vhost_httpgroup');\n\n\t\t\t// get fpm config\n\t\t\t$fpm_sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT f.id FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` f\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_PHPCONFIGS . \"` p ON p.fpmsettingid = f.id\n\t\t\t\tWHERE p.id = :phpconfigid\n\t\t\t\");\n\t\t\t$fpm_config = Database::pexecute_first($fpm_sel_stmt, [\n\t\t\t\t'phpconfigid' => Settings::Get('phpfpm.vhost_defaultini')\n\t\t\t]);\n\n\t\t\t$domain = [\n\t\t\t\t'id' => 'none',\n\t\t\t\t'domain' => Settings::Get('system.hostname'),\n\t\t\t\t'adminid' => 1, /* first admin-user (superadmin) */\n\t\t\t\t'mod_fcgid_starter' => -1,\n\t\t\t\t'mod_fcgid_maxrequests' => -1,\n\t\t\t\t'guid' => $user,\n\t\t\t\t'openbasedir' => 0,\n\t\t\t\t'email' => Settings::Get('panel.adminmail'),\n\t\t\t\t'loginname' => 'froxlor.panel',\n\t\t\t\t'documentroot' => $mypath,\n\t\t\t\t'customerroot' => $mypath,\n\t\t\t\t'fpm_config_id' => isset($fpm_config['id']) ? $fpm_config['id'] : 1\n\t\t\t];\n\n\t\t\t// all the files and folders have to belong to the local user\n\t\t\t// now because we also use fcgid for our own vhost\n\t\t\tFileDir::safe_exec('chown -R ' . $user . ':' . $group . ' ' . escapeshellarg($mypath));\n\n\t\t\t// get php.ini for our own vhost\n\t\t\t$php = new PhpInterface($domain);\n\n\t\t\t// get php-config\n\t\t\tif (Settings::Get('phpfpm.enabled') == '1') {\n\t\t\t\t// fpm\n\t\t\t\t$phpconfig = $php->getPhpConfig(Settings::Get('phpfpm.vhost_defaultini'));\n\t\t\t} else {\n\t\t\t\t// fcgid\n\t\t\t\t$phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost'));\n\t\t\t}\n\n\t\t\t// create starter-file | config-file\n\t\t\t$php->getInterface()->createConfig($phpconfig);\n\n\t\t\t// create php.ini (fpm does nothing here, as it\n\t\t\t// defines ini-settings in its pool config)\n\t\t\t$php->getInterface()->createIniFile($phpconfig);\n\t\t}\n\t}\n\n\tprotected function composePhpOptions(&$domain, $ssl_vhost = false)\n\t{\n\t\t$php_options_text = '';\n\n\t\tif ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {\n\t\t\t$php = new PhpInterface($domain);\n\t\t\t$phpconfig = $php->getPhpConfig((int)$domain['phpsettingid']);\n\n\t\t\t$php_options_text = \"\\t\" . 'location ~ ^(.+?\\.php)(/.*)?$ {' . \"\\n\";\n\t\t\tif ($domain['notryfiles'] != 1) {\n\t\t\t\t$php_options_text .= \"\\t\\t\" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . \"\\n\";\n\t\t\t\t$php_options_text .= \"\\t\" . '}' . \"\\n\\n\";\n\n\t\t\t\t$php_options_text .= \"\\t\" . 'location @php {' . \"\\n\";\n\t\t\t\t$php_options_text .= \"\\t\\t\" . 'try_files $1 =404;' . \"\\n\\n\";\n\t\t\t}\n\t\t\t$php_options_text .= \"\\t\\t\" . 'include ' . Settings::Get('nginx.fastcgiparams') . \";\\n\";\n\t\t\t$php_options_text .= \"\\t\\t\" . 'fastcgi_split_path_info ^(.+?\\.php)(/.*)$;' . \"\\n\";\n\t\t\t$php_options_text .= \"\\t\\t\" . 'fastcgi_param SCRIPT_FILENAME $request_filename;' . \"\\n\";\n\t\t\t$php_options_text .= \"\\t\\t\" . 'set $path_info $fastcgi_path_info;' . \"\\n\";\n\t\t\t$php_options_text .= \"\\t\\t\" . 'fastcgi_param PATH_INFO $path_info;' . \"\\n\";\n\t\t\tif ($domain['ssl'] == '1' && $ssl_vhost) {\n\t\t\t\t$php_options_text .= \"\\t\\t\" . 'fastcgi_param HTTPS on;' . \"\\n\";\n\t\t\t}\n\t\t\t$domain['fpm_socket'] = $php->getInterface()->getSocketFile();\n\t\t\t$php_options_text .= \"\\t\\t\" . 'fastcgi_pass unix:' . $domain['fpm_socket'] . \";\\n\";\n\t\t\t$php_options_text .= \"\\t\\t\" . 'fastcgi_index index.php;' . \"\\n\";\n\t\t\t$php_options_text .= \"\\t}\\n\\n\";\n\n\t\t\t// create starter-file | config-file\n\t\t\t$php->getInterface()->createConfig($phpconfig);\n\n\t\t\t// create php.ini (fpm does nothing here, as it\n\t\t\t// defines ini-settings in its pool config)\n\t\t\t$php->getInterface()->createIniFile($phpconfig);\n\t\t} else {\n\t\t\t$php_options_text .= '  # PHP is disabled for this vHost' . \"\\n\";\n\t\t}\n\n\t\treturn $php_options_text;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/Php/Fcgid.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http\\Php;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\n\nclass Fcgid\n{\n\n\t/**\n\t * Domain-Data array\n\t *\n\t * @var array\n\t */\n\tprivate $domain = [];\n\n\t/**\n\t * Admin-Date cache array\n\t *\n\t * @var array\n\t */\n\tprivate $admin_cache = [];\n\n\t/**\n\t * main constructor\n\t */\n\tpublic function __construct($domain)\n\t{\n\t\t$this->domain = $domain;\n\t}\n\n\t/**\n\t * create fcgid-starter-file\n\t *\n\t * @param array $phpconfig\n\t */\n\tpublic function createConfig($phpconfig)\n\t{\n\t\t// create starter\n\t\t$starter_file = \"#!/bin/sh\\n\\n\";\n\t\t$starter_file .= \"#\\n\";\n\t\t$starter_file .= \"# starter created/changed on \" . date(\"Y.m.d H:i:s\") . \" for domain '\" . $this->domain['domain'] . \"' with id #\" . $this->domain['id'] . \" from php template '\" . $phpconfig['description'] . \"' with id #\" . $phpconfig['id'] . \"\\n\";\n\t\t$starter_file .= \"# Do not change anything in this file, it will be overwritten by the Froxlor Cronjob!\\n\";\n\t\t$starter_file .= \"#\\n\\n\";\n\t\t$starter_file .= \"umask \" . $phpconfig['mod_fcgid_umask'] . \"\\n\";\n\t\t$starter_file .= \"PHPRC=\" . escapeshellarg($this->getConfigDir()) . \"\\n\";\n\t\t$starter_file .= \"export PHPRC\\n\";\n\n\t\t// set number of processes for one domain\n\t\tif ((int)$this->domain['mod_fcgid_starter'] != -1) {\n\t\t\t$starter_file .= \"PHP_FCGI_CHILDREN=\" . (int)$this->domain['mod_fcgid_starter'] . \"\\n\";\n\t\t} else {\n\t\t\tif ((int)$phpconfig['mod_fcgid_starter'] != -1) {\n\t\t\t\t$starter_file .= \"PHP_FCGI_CHILDREN=\" . (int)$phpconfig['mod_fcgid_starter'] . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$starter_file .= \"PHP_FCGI_CHILDREN=\" . (int)Settings::Get('system.mod_fcgid_starter') . \"\\n\";\n\t\t\t}\n\t\t}\n\n\t\t$starter_file .= \"export PHP_FCGI_CHILDREN\\n\";\n\n\t\t// set number of maximum requests for one domain\n\t\tif ((int)$this->domain['mod_fcgid_maxrequests'] != -1) {\n\t\t\t$starter_file .= \"PHP_FCGI_MAX_REQUESTS=\" . (int)$this->domain['mod_fcgid_maxrequests'] . \"\\n\";\n\t\t} else {\n\t\t\tif ((int)$phpconfig['mod_fcgid_maxrequests'] != -1) {\n\t\t\t\t$starter_file .= \"PHP_FCGI_MAX_REQUESTS=\" . (int)$phpconfig['mod_fcgid_maxrequests'] . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$starter_file .= \"PHP_FCGI_MAX_REQUESTS=\" . (int)Settings::Get('system.mod_fcgid_maxrequests') . \"\\n\";\n\t\t\t}\n\t\t}\n\n\t\t$starter_file .= \"export PHP_FCGI_MAX_REQUESTS\\n\";\n\n\t\t// Set Binary\n\t\t$starter_file .= \"exec \" . $phpconfig['binary'] . \" -c \" . escapeshellarg($this->getConfigDir()) . \"\\n\";\n\n\t\t// remove +i attribute, so starter can be overwritten\n\t\tif (file_exists($this->getStarterFile())) {\n\t\t\tFileDir::removeImmutable($this->getStarterFile());\n\t\t}\n\n\t\t$starter_file_handler = fopen($this->getStarterFile(), 'w');\n\t\tfwrite($starter_file_handler, $starter_file);\n\t\tfclose($starter_file_handler);\n\t\tFileDir::safe_exec('chmod 750 ' . escapeshellarg($this->getStarterFile()));\n\t\tFileDir::safe_exec('chown ' . $this->domain['guid'] . ':' . $this->domain['guid'] . ' ' . escapeshellarg($this->getStarterFile()));\n\t\tFileDir::setImmutable($this->getStarterFile());\n\t}\n\n\t/**\n\t * fcgid-config directory\n\t *\n\t * @param boolean $createifnotexists\n\t *            create the directory if it does not exist\n\t *\n\t * @return string the directory\n\t */\n\tpublic function getConfigDir($createifnotexists = true)\n\t{\n\t\t$configdir = FileDir::makeCorrectDir(Settings::Get('system.mod_fcgid_configdir') . '/' . $this->domain['loginname'] . '/' . $this->domain['domain'] . '/');\n\n\t\tif (!is_dir($configdir) && $createifnotexists) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($configdir));\n\t\t\tFileDir::safe_exec('chmod 0755 ' . escapeshellarg(dirname($configdir)));\n\t\t\tFileDir::safe_exec('chmod 0755 ' . escapeshellarg($configdir));\n\t\t\tFileDir::safe_exec('chown ' . $this->domain['guid'] . ':' . $this->domain['guid'] . ' ' . escapeshellarg($configdir));\n\t\t}\n\n\t\treturn $configdir;\n\t}\n\n\t/**\n\t * return path of php-starter file\n\t *\n\t * @return string the directory\n\t */\n\tpublic function getStarterFile()\n\t{\n\t\t$starter_filename = FileDir::makeCorrectFile($this->getConfigDir() . '/php-fcgi-starter');\n\t\treturn $starter_filename;\n\t}\n\n\t/**\n\t * create customized php.ini\n\t *\n\t * @param array $phpconfig\n\t */\n\tpublic function createIniFile($phpconfig)\n\t{\n\t\t$openbasedir = '';\n\t\t$openbasedirc = ';';\n\n\t\tif ($this->domain['openbasedir'] == '1') {\n\t\t\t$openbasedirc = '';\n\t\t\t$_phpappendopenbasedir = '';\n\n\t\t\t$_custom_openbasedir = explode(':', Settings::Get('system.mod_fcgid_peardir'));\n\t\t\tforeach ($_custom_openbasedir as $cobd) {\n\t\t\t\t$_phpappendopenbasedir .= Domain::appendOpenBasedirPath($cobd);\n\t\t\t}\n\n\t\t\t$_custom_openbasedir = explode(':', Settings::Get('system.phpappendopenbasedir'));\n\t\t\tforeach ($_custom_openbasedir as $cobd) {\n\t\t\t\t$_phpappendopenbasedir .= Domain::appendOpenBasedirPath($cobd);\n\t\t\t}\n\n\t\t\tif ($this->domain['openbasedir_path'] == '0' && strstr($this->domain['documentroot'], \":\") === false) {\n\t\t\t\t$openbasedir = Domain::appendOpenBasedirPath($this->domain['documentroot'], true);\n\t\t\t} else if ($this->domain['openbasedir_path'] == '2' && strpos(dirname($this->domain['documentroot']).'/', $this->domain['customerroot']) !== false) {\n\t\t\t\t$openbasedir = Domain::appendOpenBasedirPath(dirname($this->domain['documentroot']).'/', true);\n\t\t\t} else {\n\t\t\t\t$openbasedir = Domain::appendOpenBasedirPath($this->domain['customerroot'], true);\n\t\t\t}\n\n\t\t\t$openbasedir .= Domain::appendOpenBasedirPath($this->getTempDir());\n\t\t\t$openbasedir .= $_phpappendopenbasedir;\n\t\t} else {\n\t\t\t$openbasedir = 'none';\n\t\t\t$openbasedirc = ';';\n\t\t}\n\n\t\t$admin = $this->getAdminData($this->domain['adminid']);\n\t\t$php_ini_variables = [\n\t\t\t'SAFE_MODE' => 'Off', // keep this for compatibility, just in case\n\t\t\t'PEAR_DIR' => Settings::Get('system.mod_fcgid_peardir'),\n\t\t\t'TMP_DIR' => $this->getTempDir(),\n\t\t\t'CUSTOMER_EMAIL' => $this->domain['email'],\n\t\t\t'ADMIN_EMAIL' => $admin['email'],\n\t\t\t'DOMAIN' => $this->domain['domain'],\n\t\t\t'CUSTOMER' => $this->domain['loginname'],\n\t\t\t'ADMIN' => $admin['loginname'],\n\t\t\t'OPEN_BASEDIR' => $openbasedir,\n\t\t\t'OPEN_BASEDIR_C' => $openbasedirc,\n\t\t\t'OPEN_BASEDIR_GLOBAL' => Settings::Get('system.phpappendopenbasedir'),\n\t\t\t'DOCUMENT_ROOT' => FileDir::makeCorrectDir($this->domain['documentroot']),\n\t\t\t'CUSTOMER_HOMEDIR' => FileDir::makeCorrectDir($this->domain['customerroot'])\n\t\t];\n\n\t\t// insert a small header for the file\n\t\t$phpini_file = \";\\n\";\n\t\t$phpini_file .= \"; php.ini created/changed on \" . date(\"Y.m.d H:i:s\") . \" for domain '\" . $this->domain['domain'] . \"' with id #\" . $this->domain['id'] . \" from php template '\" . $phpconfig['description'] . \"' with id #\" . $phpconfig['id'] . \"\\n\";\n\t\t$phpini_file .= \"; Do not change anything in this file, it will be overwritten by the Froxlor Cronjob!\\n\";\n\t\t$phpini_file .= \";\\n\\n\";\n\t\t$phpini_file .= PhpHelper::replaceVariables($phpconfig['phpsettings'], $php_ini_variables);\n\t\t$phpini_file = str_replace('\"none\"', 'none', $phpini_file);\n\t\t// $phpini_file = preg_replace('/\\\"+/', '\"', $phpini_file);\n\t\t$phpini_file_handler = fopen($this->getIniFile(), 'w');\n\t\tfwrite($phpini_file_handler, $phpini_file);\n\t\tfclose($phpini_file_handler);\n\t\tFileDir::safe_exec('chown root:0 ' . escapeshellarg($this->getIniFile()));\n\t\tFileDir::safe_exec('chmod 0644 ' . escapeshellarg($this->getIniFile()));\n\t}\n\n\t/**\n\t * fcgid-temp directory\n\t *\n\t * @param boolean $createifnotexists\n\t *            create the directory if it does not exist\n\t *\n\t * @return string the directory\n\t */\n\tpublic function getTempDir($createifnotexists = true)\n\t{\n\t\t$tmpdir = FileDir::makeCorrectDir(Settings::Get('system.mod_fcgid_tmpdir') . '/' . $this->domain['loginname'] . '/');\n\n\t\tif (!is_dir($tmpdir) && $createifnotexists) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($tmpdir));\n\t\t\tFileDir::safe_exec('chown -R ' . $this->domain['guid'] . ':' . $this->domain['guid'] . ' ' . escapeshellarg($tmpdir));\n\t\t\tFileDir::safe_exec('chmod 0750 ' . escapeshellarg($tmpdir));\n\t\t}\n\n\t\treturn $tmpdir;\n\t}\n\n\t/**\n\t * return the admin-data of a specific admin\n\t *\n\t * @param int $adminid\n\t *            id of the admin-user\n\t *\n\t * @return array\n\t */\n\tprivate function getAdminData($adminid)\n\t{\n\t\t$adminid = intval($adminid);\n\n\t\tif (!isset($this->admin_cache[$adminid])) {\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `email`, `loginname` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `adminid` = :id\");\n\t\t\t$this->admin_cache[$adminid] = Database::pexecute_first($stmt, [\n\t\t\t\t'id' => $adminid\n\t\t\t]);\n\t\t}\n\t\treturn $this->admin_cache[$adminid];\n\t}\n\n\t/**\n\t * return path of php.ini file\n\t *\n\t * @return string full with path file-name\n\t */\n\tpublic function getIniFile()\n\t{\n\t\t$phpini_filename = FileDir::makeCorrectFile($this->getConfigDir() . '/php.ini');\n\t\treturn $phpini_filename;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/Php/Fpm.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http\\Php;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse InvalidArgumentException;\n\nclass Fpm\n{\n\n\t/**\n\t * Domain-Data array\n\t *\n\t * @var array\n\t */\n\tprivate $domain = [];\n\n\t/**\n\t * fpm config\n\t *\n\t * @var array\n\t */\n\tprivate $fpm_cfg = [];\n\n\t/**\n\t * Admin-Date cache array\n\t *\n\t * @var array\n\t */\n\tprivate $admin_cache = [];\n\n\t/**\n\t * defines what can be used for pool-config from php.ini\n\t * Mostly taken from http://php.net/manual/en/ini.list.php\n\t *\n\t * @var array\n\t */\n\tprivate $ini = [];\n\n\t/**\n\t * main constructor\n\t */\n\tpublic function __construct($domain)\n\t{\n\t\tif (!isset($domain['fpm_config_id']) || empty($domain['fpm_config_id'])) {\n\t\t\t$domain['fpm_config_id'] = 1;\n\t\t}\n\t\t$this->domain = $domain;\n\t\t$this->readFpmConfig($domain['fpm_config_id']);\n\t\t$this->buildIniMapping();\n\t}\n\n\tprivate function readFpmConfig($fpm_config_id)\n\t{\n\t\t$stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` WHERE `id` = :id\");\n\t\t$this->fpm_cfg = Database::pexecute_first($stmt, [\n\t\t\t'id' => $fpm_config_id\n\t\t]);\n\t}\n\n\tprivate function buildIniMapping()\n\t{\n\t\t$this->ini = [\n\t\t\t'php_flag' => array_map('trim', explode(\"\\n\", Settings::Get('phpfpm.ini_flags'))),\n\t\t\t'php_value' => array_map('trim', explode(\"\\n\", Settings::Get('phpfpm.ini_values'))),\n\t\t\t'php_admin_flag' => array_map('trim', explode(\"\\n\", Settings::Get('phpfpm.ini_admin_flags'))),\n\t\t\t'php_admin_value' => array_map('trim', explode(\"\\n\", Settings::Get('phpfpm.ini_admin_values')))\n\t\t];\n\t}\n\n\t/**\n\t * create a dummy fpm pool config with minimal configuration\n\t * (this is used whenever a config directory is empty but needs at least one pool to startup/restart)\n\t *\n\t * @param string $configdir\n\t */\n\tpublic static function createDummyPool($configdir)\n\t{\n\t\tif (!is_dir($configdir)) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($configdir));\n\t\t}\n\t\t$config = FileDir::makeCorrectFile($configdir . '/dummy.conf');\n\t\t$dummy = \"[dummy]\nuser = \" . Settings::Get('system.httpuser') . \"\nlisten = /run/\" . md5($configdir) . \"-fpm.sock\npm = static\npm.max_children = 1\n\";\n\t\tfile_put_contents($config, $dummy);\n\t}\n\n\t/**\n\t * create fpm-pool config\n\t *\n\t * @param array $phpconfig\n\t */\n\tpublic function createConfig($phpconfig)\n\t{\n\t\t$fh = @fopen($this->getConfigFile(), 'w');\n\n\t\tif ($fh) {\n\t\t\tif ($phpconfig['override_fpmconfig'] == 1) {\n\t\t\t\t$this->fpm_cfg['pm'] = $phpconfig['pm'];\n\t\t\t\t$this->fpm_cfg['max_children'] = $phpconfig['max_children'];\n\t\t\t\t$this->fpm_cfg['start_servers'] = $phpconfig['start_servers'];\n\t\t\t\t$this->fpm_cfg['min_spare_servers'] = $phpconfig['min_spare_servers'];\n\t\t\t\t$this->fpm_cfg['max_spare_servers'] = $phpconfig['max_spare_servers'];\n\t\t\t\t$this->fpm_cfg['max_requests'] = $phpconfig['max_requests'];\n\t\t\t\t$this->fpm_cfg['idle_timeout'] = $phpconfig['idle_timeout'];\n\t\t\t\t$this->fpm_cfg['limit_extensions'] = $phpconfig['limit_extensions'];\n\t\t\t}\n\n\t\t\t$fpm_pm = $this->fpm_cfg['pm'];\n\t\t\t$fpm_children = (int)$this->fpm_cfg['max_children'];\n\t\t\t$fpm_start_servers = (int)$this->fpm_cfg['start_servers'];\n\t\t\t$fpm_min_spare_servers = (int)$this->fpm_cfg['min_spare_servers'];\n\t\t\t$fpm_max_spare_servers = (int)$this->fpm_cfg['max_spare_servers'];\n\t\t\t$fpm_requests = (int)$this->fpm_cfg['max_requests'];\n\t\t\t$fpm_process_idle_timeout = (int)$this->fpm_cfg['idle_timeout'];\n\t\t\t$fpm_limit_extensions = $this->fpm_cfg['limit_extensions'];\n\t\t\t$fpm_custom_config = $this->fpm_cfg['custom_config'];\n\n\t\t\tif ($fpm_children == 0) {\n\t\t\t\t$fpm_children = 1;\n\t\t\t}\n\n\t\t\t$fpm_config = ';PHP-FPM configuration for \"' . $this->domain['domain'] . '\" created on ' . date(\"Y.m.d H:i:s\") . \"\\n\";\n\t\t\t$fpm_config .= '[' . $this->domain['domain'] . ']' . \"\\n\";\n\t\t\t$fpm_config .= 'listen = ' . $this->getSocketFile() . \"\\n\";\n\t\t\tif ($this->domain['loginname'] == 'froxlor.panel') {\n\t\t\t\t$fpm_config .= 'listen.owner = ' . $this->domain['guid'] . \"\\n\";\n\t\t\t\t$fpm_config .= 'listen.group = ' . $this->domain['guid'] . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$fpm_config .= 'listen.owner = ' . $this->domain['loginname'] . \"\\n\";\n\t\t\t\t$fpm_config .= 'listen.group = ' . $this->domain['loginname'] . \"\\n\";\n\t\t\t}\n\t\t\t// see #1418 why this is 0660\n\t\t\t$fpm_config .= 'listen.mode = 0660' . \"\\n\";\n\n\t\t\tif ($this->domain['loginname'] == 'froxlor.panel') {\n\t\t\t\t$fpm_config .= 'user = ' . $this->domain['guid'] . \"\\n\";\n\t\t\t\t$fpm_config .= 'group = ' . $this->domain['guid'] . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$fpm_config .= 'user = ' . $this->domain['loginname'] . \"\\n\";\n\t\t\t\t$fpm_config .= 'group = ' . $this->domain['loginname'] . \"\\n\";\n\t\t\t}\n\n\t\t\t$fpm_config .= 'pm = ' . $fpm_pm . \"\\n\";\n\t\t\t$fpm_config .= 'pm.max_children = ' . $fpm_children . \"\\n\";\n\n\t\t\tif ($fpm_pm == 'dynamic') {\n\t\t\t\t// honor max_children\n\t\t\t\tif ($fpm_children < $fpm_min_spare_servers) {\n\t\t\t\t\t$fpm_min_spare_servers = $fpm_children;\n\t\t\t\t}\n\t\t\t\tif ($fpm_children < $fpm_max_spare_servers) {\n\t\t\t\t\t$fpm_max_spare_servers = $fpm_children;\n\t\t\t\t}\n\t\t\t\t// failsafe, refs #955\n\t\t\t\tif ($fpm_start_servers < $fpm_min_spare_servers) {\n\t\t\t\t\t$fpm_start_servers = $fpm_min_spare_servers;\n\t\t\t\t}\n\t\t\t\tif ($fpm_start_servers > $fpm_max_spare_servers) {\n\t\t\t\t\t$fpm_start_servers = $fpm_max_spare_servers;\n\t\t\t\t}\n\t\t\t\t$fpm_config .= 'pm.start_servers = ' . $fpm_start_servers . \"\\n\";\n\t\t\t\t$fpm_config .= 'pm.min_spare_servers = ' . $fpm_min_spare_servers . \"\\n\";\n\t\t\t\t$fpm_config .= 'pm.max_spare_servers = ' . $fpm_max_spare_servers . \"\\n\";\n\t\t\t} elseif ($fpm_pm == 'ondemand') {\n\t\t\t\t$fpm_config .= 'pm.process_idle_timeout = ' . $fpm_process_idle_timeout . \"\\n\";\n\t\t\t}\n\n\t\t\t$fpm_config .= 'pm.max_requests = ' . $fpm_requests . \"\\n\";\n\t\t\t$fpm_config .= 'request_terminate_timeout = ' . $phpconfig['fpm_reqterm'] . \"\\n\";\n\n\t\t\t// possible slowlog configs\n\t\t\tif ($phpconfig['fpm_slowlog'] == '1') {\n\t\t\t\t$this->durationCompare($phpconfig['fpm_reqterm'], $phpconfig['fpm_reqslow']);\n\t\t\t\t$fpm_config .= 'request_slowlog_timeout = ' . $phpconfig['fpm_reqslow'] . \"\\n\";\n\t\t\t\t$slowlog = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . '/' . $this->domain['loginname'] . '-php-slow.log');\n\t\t\t\t$fpm_config .= 'slowlog = ' . $slowlog . \"\\n\";\n\t\t\t\t$fpm_config .= 'catch_workers_output = yes' . \"\\n\";\n\t\t\t}\n\n\t\t\t$fpm_config .= ';chroot = ' . FileDir::makeCorrectDir($this->domain['documentroot']) . \"\\n\";\n\t\t\t$fpm_config .= 'security.limit_extensions = ' . $fpm_limit_extensions . \"\\n\";\n\n\t\t\t$tmpdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->domain['loginname'] . '/');\n\t\t\tif (!is_dir($tmpdir)) {\n\t\t\t\t$this->getTempDir();\n\t\t\t}\n\n\t\t\t$env_path = Settings::Get('phpfpm.envpath');\n\t\t\tif (!empty($env_path)) {\n\t\t\t\t$fpm_config .= 'env[PATH] = ' . $env_path . \"\\n\";\n\t\t\t}\n\t\t\t$fpm_config .= 'env[TMP] = ' . $tmpdir . \"\\n\";\n\t\t\t$fpm_config .= 'env[TMPDIR] = ' . $tmpdir . \"\\n\";\n\t\t\t$fpm_config .= 'env[TEMP] = ' . $tmpdir . \"\\n\";\n\n\t\t\t$openbasedir = '';\n\t\t\tif ($this->domain['loginname'] != 'froxlor.panel') {\n\t\t\t\tif ($this->domain['openbasedir'] == '1') {\n\t\t\t\t\t$_phpappendopenbasedir = '';\n\t\t\t\t\t$_custom_openbasedir = explode(':', Settings::Get('phpfpm.peardir'));\n\t\t\t\t\tforeach ($_custom_openbasedir as $cobd) {\n\t\t\t\t\t\t$_phpappendopenbasedir .= Domain::appendOpenBasedirPath($cobd);\n\t\t\t\t\t}\n\n\t\t\t\t\t$_custom_openbasedir = explode(':', Settings::Get('system.phpappendopenbasedir'));\n\t\t\t\t\tforeach ($_custom_openbasedir as $cobd) {\n\t\t\t\t\t\t$_phpappendopenbasedir .= Domain::appendOpenBasedirPath($cobd);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($this->domain['openbasedir_path'] == '0' && strstr($this->domain['documentroot'], \":\") === false) {\n\t\t\t\t\t\t$openbasedir = Domain::appendOpenBasedirPath($this->domain['documentroot'], true);\n\t\t\t\t\t} else if ($this->domain['openbasedir_path'] == '2' && strpos(dirname($this->domain['documentroot']) . '/', $this->domain['customerroot']) !== false) {\n\t\t\t\t\t\t$openbasedir = Domain::appendOpenBasedirPath(dirname($this->domain['documentroot']) . '/', true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$openbasedir = Domain::appendOpenBasedirPath($this->domain['customerroot'], true);\n\t\t\t\t\t}\n\n\t\t\t\t\t$openbasedir .= Domain::appendOpenBasedirPath($this->getTempDir());\n\t\t\t\t\t$openbasedir .= $_phpappendopenbasedir;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$fpm_config .= 'php_admin_value[upload_tmp_dir] = ' . FileDir::makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->domain['loginname'] . '/') . \"\\n\";\n\n\t\t\t$admin = $this->getAdminData($this->domain['adminid']);\n\t\t\t$php_ini_variables = [\n\t\t\t\t'SAFE_MODE' => 'Off', // keep this for compatibility, just in case\n\t\t\t\t'PEAR_DIR' => Settings::Get('phpfpm.peardir'),\n\t\t\t\t'TMP_DIR' => $this->getTempDir(),\n\t\t\t\t'CUSTOMER_EMAIL' => $this->domain['email'],\n\t\t\t\t'ADMIN_EMAIL' => $admin['email'],\n\t\t\t\t'DOMAIN' => $this->domain['domain'],\n\t\t\t\t'CUSTOMER' => $this->domain['loginname'],\n\t\t\t\t'ADMIN' => $admin['loginname'],\n\t\t\t\t'OPEN_BASEDIR' => $openbasedir,\n\t\t\t\t'OPEN_BASEDIR_C' => '',\n\t\t\t\t'OPEN_BASEDIR_GLOBAL' => Settings::Get('system.phpappendopenbasedir'),\n\t\t\t\t'DOCUMENT_ROOT' => FileDir::makeCorrectDir($this->domain['documentroot']),\n\t\t\t\t'CUSTOMER_HOMEDIR' => FileDir::makeCorrectDir($this->domain['customerroot'])\n\t\t\t];\n\n\t\t\t$phpini = PhpHelper::replaceVariables($phpconfig['phpsettings'], $php_ini_variables);\n\t\t\t$phpini_array = explode(\"\\n\", $phpini);\n\n\t\t\t$fpm_config .= \"\\n\\n\";\n\t\t\tforeach ($phpini_array as $inisection) {\n\t\t\t\t$is = explode(\"=\", trim($inisection), 2);\n\t\t\t\tif (empty($is[0])) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tforeach ($this->ini as $sec => $possibles) {\n\t\t\t\t\tif (in_array(trim($is[0]), $possibles)) {\n\t\t\t\t\t\t// check explicitly for open_basedir\n\t\t\t\t\t\tif (trim($is[0]) == 'open_basedir' && $openbasedir == '') {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$fpm_config .= $sec . '[' . trim($is[0]) . '] = ' . trim($is[1] ?? '') . \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// now check if 'sendmail_path' has not been set in the custom-php.ini\n\t\t\t// if not we use our fallback-default as usual\n\t\t\tif (strpos($fpm_config, 'php_admin_value[sendmail_path]') === false) {\n\t\t\t\t$fpm_config .= 'php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f postmaster@' . $this->domain['domain'] . \"\\n\";\n\t\t\t}\n\n\t\t\t// check for session.save_path, whether it has been specified by the user, if not, set a default\n\t\t\tif (strpos($fpm_config, 'php_value[session.save_path]') === false && strpos($fpm_config, 'php_admin_value[session.save_path]') === false) {\n\t\t\t\t$fpm_config .= 'php_admin_value[session.save_path] = ' . $this->getTempDir() . \"\\n\";\n\t\t\t}\n\n\t\t\t// append custom phpfpm configuration\n\t\t\tif (!empty($fpm_custom_config)) {\n\t\t\t\t$fpm_config .= \"\\n; Custom Configuration\\n\";\n\t\t\t\t$fpm_config .= PhpHelper::replaceVariables($fpm_custom_config, $php_ini_variables);\n\t\t\t}\n\n\t\t\tfwrite($fh, $fpm_config, strlen($fpm_config));\n\t\t\tfclose($fh);\n\t\t}\n\t}\n\n\t/**\n\t * fpm-config file\n\t *\n\t * @param boolean $createifnotexists\n\t *            create the directory if it does not exist\n\t *\n\t * @return string the full path to the file\n\t */\n\tpublic function getConfigFile($createifnotexists = true)\n\t{\n\t\t$configdir = $this->fpm_cfg['config_dir'];\n\t\t$config = FileDir::makeCorrectFile($configdir . '/' . $this->domain['domain'] . '.conf');\n\n\t\tif (!is_dir($configdir) && $createifnotexists) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($configdir));\n\t\t}\n\n\t\treturn $config;\n\t}\n\n\t/**\n\t * return path of fpm-socket file\n\t *\n\t * @param boolean $createifnotexists\n\t *            create the directory if it does not exist\n\t *\n\t * @return string the full path to the socket\n\t */\n\tpublic function getSocketFile($createifnotexists = true)\n\t{\n\t\t$socketdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir'));\n\t\t// add fpm-config-id to filename, so it's unique for the fpm-daemon and doesn't interfere with running configs when reuilding\n\t\t$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['domain'] . '-php-fpm.socket';\n\t\tif (strlen($socket_filename) > 100) {\n\t\t\t// respect the unix socket-length limitation\n\t\t\t$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['id'] . '-php-fpm.socket';\n\t\t\tif (strlen($socket_filename) > 100) {\n\t\t\t\t// even a long loginname it seems\n\t\t\t\t$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['guid'] . '-' . $this->domain['id'] . '-php-fpm.socket';\n\t\t\t}\n\t\t}\n\t\t$socket = strtolower(FileDir::makeCorrectFile($socket_filename));\n\n\t\tif (!is_dir($socketdir) && $createifnotexists) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($socketdir));\n\t\t\tFileDir::safe_exec('chown -R ' . Settings::Get('system.httpuser') . ':' . Settings::Get('system.httpgroup') . ' ' . escapeshellarg($socketdir));\n\t\t}\n\n\t\treturn $socket;\n\t}\n\n\t/**\n\t * fpm-temp directory\n\t *\n\t * @param boolean $createifnotexists\n\t *            create the directory if it does not exist\n\t *\n\t * @return string the directory\n\t */\n\tpublic function getTempDir($createifnotexists = true)\n\t{\n\t\t$tmpdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->domain['loginname'] . '/');\n\n\t\tif (!is_dir($tmpdir) && $createifnotexists) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($tmpdir));\n\t\t\tFileDir::safe_exec('chown -R ' . $this->domain['guid'] . ':' . $this->domain['guid'] . ' ' . escapeshellarg($tmpdir));\n\t\t\tFileDir::safe_exec('chmod 0750 ' . escapeshellarg($tmpdir));\n\t\t}\n\n\t\treturn $tmpdir;\n\t}\n\n\t/**\n\t * return the admin-data of a specific admin\n\t *\n\t * @param int $adminid\n\t *            id of the admin-user\n\t *\n\t * @return array\n\t */\n\tprivate function getAdminData($adminid)\n\t{\n\t\t$adminid = intval($adminid);\n\n\t\tif (!isset($this->admin_cache[$adminid])) {\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `email`, `loginname` FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `adminid` = :id\");\n\t\t\t$this->admin_cache[$adminid] = Database::pexecute_first($stmt, [\n\t\t\t\t'id' => $adminid\n\t\t\t]);\n\t\t}\n\t\treturn $this->admin_cache[$adminid];\n\t}\n\n\t/**\n\t * this is done via createConfig as php-fpm defines\n\t * the ini-values/flags in its pool-config\n\t *\n\t * @param string $phpconfig\n\t */\n\tpublic function createIniFile($phpconfig)\n\t{\n\t\treturn;\n\t}\n\n\t/**\n\t * fastcgi-fakedirectory directory\n\t *\n\t * @param boolean $createifnotexists\n\t *            create the directory if it does not exist\n\t *\n\t * @return string the directory\n\t */\n\tpublic function getAliasConfigDir($createifnotexists = true)\n\t{\n\t\t// ensure default...\n\t\tif (Settings::Get('phpfpm.aliasconfigdir') == null) {\n\t\t\tSettings::Set('phpfpm.aliasconfigdir', '/var/www/php-fpm');\n\t\t}\n\n\t\t$configdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.aliasconfigdir') . '/' . $this->domain['loginname'] . '/' . $this->domain['domain'] . '/');\n\t\tif (!is_dir($configdir) && $createifnotexists) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($configdir));\n\t\t\tFileDir::safe_exec('chown ' . $this->domain['guid'] . ':' . $this->domain['guid'] . ' ' . escapeshellarg($configdir));\n\t\t}\n\n\t\treturn $configdir;\n\t}\n\n\t/**\n\t * 'request_slowlog_timeout' can't be greater than 'request_terminate_timeout'\n\t *\n\t * @param $request_terminate_timeout\n\t * @param $request_slowlog_timeout\n\t * @return void\n\t */\n\tprivate function durationCompare(&$request_terminate_timeout, &$request_slowlog_timeout)\n\t{\n\t\t$to_seconds = function ($str) {\n\t\t\tif (!preg_match('/^([0-9]+)([smhd])?$/', $str, $matches)) {\n\t\t\t\tthrow new InvalidArgumentException(\"Invalid format: $str\");\n\t\t\t}\n\t\t\t$value = (int)$matches[1];\n\t\t\t$unit = $matches[2] ?? 's';\n\n\t\t\tswitch ($unit) {\n\t\t\t\tcase 'm':\n\t\t\t\t\treturn $value * 60;\n\t\t\t\tcase 'h':\n\t\t\t\t\treturn $value * 3600;\n\t\t\t\tcase 'd':\n\t\t\t\t\treturn $value * 86400;\n\t\t\t\tdefault:\n\t\t\t\t\treturn $value;\n\t\t\t}\n\t\t};\n\n\t\t$aSec = $to_seconds($request_terminate_timeout);\n\t\t$bSec = $to_seconds($request_slowlog_timeout);\n\n\t\tif ($bSec > $aSec) {\n\t\t\t$request_slowlog_timeout = $request_terminate_timeout;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/Php/PhpInterface.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http\\Php;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Settings;\n\nclass PhpInterface\n{\n\n\t/**\n\t * Domain-Data array\n\t *\n\t * @var array\n\t */\n\tprivate $domain = [];\n\n\t/**\n\t * Interface object\n\t *\n\t * @var object\n\t */\n\tprivate $interface = null;\n\n\t/**\n\t * PHP-Config data array\n\t *\n\t * @var array\n\t */\n\tprivate $php_configs_cache = [];\n\n\t/**\n\t * main constructor\n\t */\n\tpublic function __construct($domain)\n\t{\n\t\t$this->domain = $domain;\n\t\t$this->setInterface();\n\t}\n\n\t/**\n\t * set interface-object by type of\n\t * php-interface: fcgid or php-fpm\n\t * sets private $_interface variable\n\t */\n\tprivate function setInterface()\n\t{\n\t\t// php-fpm\n\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t$this->interface = new Fpm($this->domain);\n\t\t} elseif ((int)Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t$this->interface = new Fcgid($this->domain);\n\t\t}\n\t}\n\n\t/**\n\t * returns the interface-object\n\t * from where we can control it\n\t */\n\tpublic function getInterface()\n\t{\n\t\treturn $this->interface;\n\t}\n\n\t/**\n\t * return the php-configuration from the database\n\t *\n\t * @param int $php_config_id\n\t *            id of the php-configuration\n\t *\n\t * @return array\n\t */\n\tpublic function getPhpConfig(int $php_config_id)\n\t{\n\t\t// If domain has no config, we will use the default one.\n\t\tif ($php_config_id == 0) {\n\t\t\t$php_config_id = 1;\n\t\t}\n\n\t\tif (!isset($this->php_configs_cache[$php_config_id])) {\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_PHPCONFIGS . \"` WHERE `id` = :id\n\t\t\t\");\n\t\t\t$this->php_configs_cache[$php_config_id] = Database::pexecute_first($stmt, [\n\t\t\t\t'id' => $php_config_id\n\t\t\t]);\n\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tSELECT * FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` WHERE `id` = :id\n\t\t\t\t\");\n\t\t\t\t$this->php_configs_cache[$php_config_id]['fpm_settings'] = Database::pexecute_first($stmt, [\n\t\t\t\t\t'id' => $this->php_configs_cache[$php_config_id]['fpmsettingid']\n\t\t\t\t]);\n\t\t\t\t// override fpm daemon settings if set in php-config\n\t\t\t\tif ($this->php_configs_cache[$php_config_id]['override_fpmconfig'] == 1) {\n\t\t\t\t\t$this->php_configs_cache[$php_config_id]['fpm_settings']['limit_extensions'] = $this->php_configs_cache[$php_config_id]['limit_extensions'];\n\t\t\t\t\t$this->php_configs_cache[$php_config_id]['fpm_settings']['idle_timeout'] = $this->php_configs_cache[$php_config_id]['idle_timeout'];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $this->php_configs_cache[$php_config_id];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/Php/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cron/Http/WebserverBase.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Http;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass WebserverBase\n{\n\n\t/**\n\t * returns an array with all entries required for all\n\t * webserver-vhost-configs\n\t *\n\t * @return array\n\t */\n\tpublic static function getVhostsToCreate()\n\t{\n\t\t$query = \"SELECT `d`.*, `pd`.`domain` AS `parentdomain`, `c`.`loginname`,\n\t\t\t\t`d`.`phpsettingid`, `c`.`adminid`, `c`.`guid`, `c`.`email`,\n\t\t\t\t`c`.`documentroot` AS `customerroot`, `c`.`deactivated` as `customer_deactivated`,\n\t\t\t\t`c`.`phpenabled` AS `phpenabled_customer`,\n\t\t\t\t`d`.`phpenabled` AS `phpenabled_vhost`,\n\t\t\t\t`a`.`email` as `admin_email`\n\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` `d`\n\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` `c` USING(`customerid`)\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` `pd` ON (`pd`.`id` = `d`.`parentdomainid`)\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_ADMINS . \"` `a` ON (`a`.`adminid` = `c`.`adminid`)\n\n\t\t\t\tWHERE `d`.`aliasdomain` IS NULL AND `d`.`email_only` <> '1'\n\t\t\t\tORDER BY `d`.`parentdomainid` DESC, `d`.`iswildcarddomain`, `d`.`domain` ASC;\n\t\t\";\n\n\t\t$result_domains_stmt = Database::query($query);\n\n\t\t// prepare IP statement\n\t\t$ip_stmt = Database::prepare(\"\n\t\t\tSELECT `di`.`id_domain` , `p`.`ssl`, `p`.`ssl_cert_file`, `p`.`ssl_key_file`, `p`.`ssl_ca_file`, `p`.`ssl_cert_chainfile`\n\t\t\tFROM `\" . TABLE_DOMAINTOIP . \"` `di`, `\" . TABLE_PANEL_IPSANDPORTS . \"` `p`\n\t\t\tWHERE `p`.`id` = `di`.`id_ipandports`\n\t\t\tAND `di`.`id_domain` = :domainid\n\t\t\tAND `p`.`ssl` = '1'\n\t\t\");\n\n\t\t// prepare fpm-config select query\n\t\t$fpm_sel_stmt = Database::prepare(\"\n\t\t\tSELECT f.id FROM `\" . TABLE_PANEL_FPMDAEMONS . \"` f\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_PHPCONFIGS . \"` p ON p.fpmsettingid = f.id\n\t\t\tWHERE p.id = :phpconfigid\n\t\t\");\n\n\t\t$domains = [];\n\t\twhile ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t// set whole domain\n\t\t\t$domains[$domain['domain']] = $domain;\n\t\t\t// set empty-defaults for non-ssl\n\t\t\t$domains[$domain['domain']]['ssl'] = '';\n\t\t\t$domains[$domain['domain']]['ssl_cert_file'] = '';\n\t\t\t$domains[$domain['domain']]['ssl_key_file'] = '';\n\t\t\t$domains[$domain['domain']]['ssl_ca_file'] = '';\n\t\t\t$domains[$domain['domain']]['ssl_cert_chainfile'] = '';\n\n\t\t\t// now, if the domain has an ssl ip/port assigned, get\n\t\t\t// the corresponding information from the db\n\t\t\tif (Domain::domainHasSslIpPort($domain['id'])) {\n\t\t\t\t$ssl_ip = Database::pexecute_first($ip_stmt, [\n\t\t\t\t\t'domainid' => $domain['id']\n\t\t\t\t]);\n\n\t\t\t\t// set ssl info for domain\n\t\t\t\t$domains[$domain['domain']]['ssl'] = '1';\n\t\t\t\t$domains[$domain['domain']]['ssl_cert_file'] = $ssl_ip['ssl_cert_file'];\n\t\t\t\t$domains[$domain['domain']]['ssl_key_file'] = $ssl_ip['ssl_key_file'];\n\t\t\t\t$domains[$domain['domain']]['ssl_ca_file'] = $ssl_ip['ssl_ca_file'];\n\t\t\t\t$domains[$domain['domain']]['ssl_cert_chainfile'] = $ssl_ip['ssl_cert_chainfile'];\n\t\t\t}\n\n\t\t\t// read fpm-config-id if using fpm\n\t\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$fpm_config = Database::pexecute_first($fpm_sel_stmt, [\n\t\t\t\t\t'phpconfigid' => $domain['phpsettingid']\n\t\t\t\t]);\n\t\t\t\tif ($fpm_config) {\n\t\t\t\t\t$domains[$domain['domain']]['fpm_config_id'] = $fpm_config['id'];\n\t\t\t\t} else {\n\t\t\t\t\t// fallback\n\t\t\t\t\t$domains[$domain['domain']]['fpm_config_id'] = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $domains;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Http/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cron/Mail/Rspamd.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Mail;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\n\nclass Rspamd\n{\n\tconst DEFAULT_MARK_LVL = 7.0;\n\tconst DEFAULT_REJECT_LVL = 14.0;\n\n\tprivate string $frx_settings_file = \"\";\n\n\tprotected FroxlorLogger $logger;\n\n\tpublic function __construct(FroxlorLogger $logger)\n\t{\n\t\t$this->logger = $logger;\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic function writeConfigs()\n\t{\n\t\t// tell the world what we are doing\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task9 started - Rebuilding antispam configuration');\n\n\t\t// get all email addresses\n\t\t$antispam_stmt = Database::prepare(\"\n\t\t\tSELECT email, spam_tag_level, rewrite_subject, spam_kill_level, bypass_spam, policy_greylist, iscatchall\n\t\t\tFROM `\" . TABLE_MAIL_VIRTUAL . \"`\n\t\t\tORDER BY email\n\t\t\");\n\t\tDatabase::pexecute($antispam_stmt);\n\n\t\t$this->frx_settings_file = \"#\\n# Automatically generated file by froxlor. DO NOT EDIT manually as it will be overwritten!\\n# Generated: \" . date('d.m.Y H:i') . \"\\n#\\n\\n\";\n\t\twhile ($email = $antispam_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t$this->generateEmailAddrConfig($email);\n\t\t}\n\n\t\t$antispam_cfg_file = FileDir::makeCorrectFile(Settings::Get('antispam.config_file'));\n\t\tfile_put_contents($antispam_cfg_file, $this->frx_settings_file);\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, $antispam_cfg_file . ' written');\n\t\t$this->writeDkimConfigs();\n\t\t$this->reloadDaemon();\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task9 finished');\n\t}\n\n\t/**\n\t * # local.d/dkim_signing.conf\n\t * try_fallback = true;\n\t * path = \"/var/lib/rspamd/dkim/$domain.$selector.key\";\n\t * selector_map = \"/etc/rspamd/dkim_selectors.map\";\n\t *\n\t * @return void\n\t * @throws Exception\n\t */\n\tpublic function writeDkimConfigs()\n\t{\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Writing DKIM key-pairs');\n\n\t\t$dkim_selector_map = \"\";\n\t\t$result_domains_stmt = Database::query(\"\n\t\t\t\tSELECT `id`, `domain`, `dkim`, `dkim_id`, `dkim_pubkey`, `dkim_privkey`\n\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tWHERE `dkim` = '1'\n\t\t\t\tORDER BY `id` ASC\n\t\t\t\");\n\n\t\twhile ($domain = $result_domains_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\n\t\t\tif ($domain['dkim_privkey'] == '' || $domain['dkim_pubkey'] == '') {\n\t\t\t\t$max_dkim_id_stmt = Database::query(\"SELECT MAX(`dkim_id`) as `max_dkim_id` FROM `\" . TABLE_PANEL_DOMAINS . \"`\");\n\t\t\t\t$max_dkim_id = $max_dkim_id_stmt->fetch(\\PDO::FETCH_ASSOC);\n\t\t\t\t$domain['dkim_id'] = (int)$max_dkim_id['max_dkim_id'] + 1;\n\n\t\t\t\t$privkey_filename = FileDir::makeCorrectFile('/var/lib/rspamd/dkim/' . $domain['domain'] . '.dkim' . $domain['dkim_id'] . '.key');\n\t\t\t\t$pubkey_filename = FileDir::makeCorrectFile('/var/lib/rspamd/dkim/' . $domain['domain'] . '.dkim' . $domain['dkim_id'] . '.txt');\n\n\t\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Generating DKIM keys for \"' . $domain['domain'] . '\"');\n\t\t\t\t$rsret = [];\n\t\t\t\tFileDir::safe_exec(\n\t\t\t\t\t'rspamadm dkim_keygen -d ' . escapeshellarg($domain['domain']) . ' -k ' . $privkey_filename . ' -s dkim' . $domain['dkim_id'] . ' -b ' . Settings::Get('antispam.dkim_keylength') . ' -o plain > ' . escapeshellarg($pubkey_filename),\n\t\t\t\t\t$rsret,\n\t\t\t\t\t['>']\n\t\t\t\t);\n\t\t\t\tif (!file_exists($privkey_filename) || !file_exists($pubkey_filename)) {\n\t\t\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'DKIM Keypair for domain \"' . $domain['domain'] . '\" was not generated successfully.');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$domain['dkim_privkey'] = file_get_contents($privkey_filename);\n\t\t\t\tFileDir::safe_exec(\"chmod 0640 \" . escapeshellarg($privkey_filename));\n\t\t\t\tFileDir::safe_exec(\"chown _rspamd:_rspamd \" . escapeshellarg($privkey_filename));\n\t\t\t\t$domain['dkim_pubkey'] = file_get_contents($pubkey_filename);\n\t\t\t\tFileDir::safe_exec(\"chmod 0664 \" . escapeshellarg($pubkey_filename));\n\t\t\t\tFileDir::safe_exec(\"chown _rspamd:_rspamd \" . escapeshellarg($pubkey_filename));\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t\t\t`dkim_id` = :dkimid,\n\t\t\t\t\t\t`dkim_privkey` = :privkey,\n\t\t\t\t\t\t`dkim_pubkey` = :pubkey\n\t\t\t\t\t\tWHERE `id` = :id\n\t\t\t\t\t\");\n\t\t\t\t$upd_data = [\n\t\t\t\t\t'dkimid' => $domain['dkim_id'],\n\t\t\t\t\t'privkey' => $domain['dkim_privkey'],\n\t\t\t\t\t'pubkey' => $domain['dkim_pubkey'],\n\t\t\t\t\t'id' => $domain['id']\n\t\t\t\t];\n\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\t\t\t} else {\n\t\t\t\t$privkey_filename = FileDir::makeCorrectFile('/var/lib/rspamd/dkim/' . $domain['domain'] . '.dkim' . $domain['dkim_id'] . '.key');\n\t\t\t\t$pubkey_filename = FileDir::makeCorrectFile('/var/lib/rspamd/dkim/' . $domain['domain'] . '.dkim' . $domain['dkim_id'] . '.txt');\n\t\t\t}\n\n\t\t\tif (!file_exists($privkey_filename) && $domain['dkim_privkey'] != '') {\n\t\t\t\tfile_put_contents($privkey_filename, $domain['dkim_privkey']);\n\t\t\t\tFileDir::safe_exec(\"chmod 0640 \" . escapeshellarg($privkey_filename));\n\t\t\t\tFileDir::safe_exec(\"chown _rspamd:_rspamd \" . escapeshellarg($privkey_filename));\n\t\t\t}\n\n\t\t\tif (!file_exists($pubkey_filename) && $domain['dkim_pubkey'] != '') {\n\t\t\t\tfile_put_contents($pubkey_filename, $domain['dkim_pubkey']);\n\t\t\t\tFileDir::safe_exec(\"chmod 0644 \" . escapeshellarg($pubkey_filename));\n\t\t\t\tFileDir::safe_exec(\"chown _rspamd:_rspamd \" . escapeshellarg($pubkey_filename));\n\t\t\t}\n\n\t\t\t$dkim_selector_map .= $domain['domain'] . \" dkim\" . $domain['dkim_id'] . \"\\n\";\n\t\t}\n\n\t\t$dkim_selector_file = FileDir::makeCorrectFile('/etc/rspamd/dkim_selectors.map');\n\t\tfile_put_contents($dkim_selector_file, $dkim_selector_map);\n\t}\n\n\tprivate function generateEmailAddrConfig(array $email): void\n\t{\n\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Generating antispam config for ' . $email['email']);\n\n\t\t$email['spam_tag_level'] = floatval($email['spam_tag_level']);\n\t\t$email['spam_kill_level'] = $email['spam_kill_level'] == -1 ? \"null\" : floatval($email['spam_kill_level']);\n\t\t$email_id = md5($email['email']);\n\n\t\t$this->frx_settings_file .= '# Email: ' . $email['email'] . \"\\n\";\n\t\tforeach (['rcpt', 'from'] as $type) {\n\t\t\t$this->frx_settings_file .= 'frx_' . $email_id . '_' . $type . ' {' . \"\\n\";\n\t\t\t$this->frx_settings_file .= '\tid = \"frx_' . $email_id . '_' . $type . '\";' . \"\\n\";\n\t\t\tif ($email['iscatchall']) {\n\t\t\t\t$this->frx_settings_file .= '\tpriority = low;' . \"\\n\";\n\t\t\t\t$this->frx_settings_file .= '\t' . $type . ' = \"' . substr($email['email'], strpos($email['email'], '@')) . '\";' . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$this->frx_settings_file .= '\tpriority = medium;' . \"\\n\";\n\t\t\t\t$this->frx_settings_file .= '\t' . $type . ' = \"' . $email['email'] . '\";' . \"\\n\";\n\t\t\t}\n\t\t\tif ((int)$email['bypass_spam'] == 1) {\n\t\t\t\t$this->frx_settings_file .= '\twant_spam = yes;' . \"\\n\";\n\t\t\t} else {\n\t\t\t\t$this->frx_settings_file .= '\tapply {' . \"\\n\";\n\t\t\t\t$this->frx_settings_file .= '\t\tactions {' . \"\\n\";\n\t\t\t\t$this->frx_settings_file .= '\t\t\t\"add header\" = ' . $email['spam_tag_level'] . ';' . \"\\n\";\n\t\t\t\tif ((int)$email['rewrite_subject'] == 1) {\n\t\t\t\t\t$this->frx_settings_file .= '\t\t\trewrite_subject = ' . ($email['spam_tag_level'] + 0.01) . ';' . \"\\n\";\n\t\t\t\t}\n\t\t\t\t$this->frx_settings_file .= '\t\t\treject = ' . $email['spam_kill_level'] . ';' . \"\\n\";\n\t\t\t\tif ($type == 'rcpt' && (int)$email['policy_greylist'] == 0) {\n\t\t\t\t\t$this->frx_settings_file .= '\t\t\tgreylist = null;' . \"\\n\";\n\t\t\t\t}\n\t\t\t\t$this->frx_settings_file .= '\t\t}' . \"\\n\";\n\t\t\t\t$this->frx_settings_file .= '\t}' . \"\\n\";\n\t\t\t\tif ($type == 'rcpt' && (int)$email['policy_greylist'] == 0) {\n\t\t\t\t\t$this->frx_settings_file .= '\tsymbols [ \"DONT_GREYLIST\" ]' . \"\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t\t$this->frx_settings_file .= '}' . \"\\n\";\n\t\t}\n\t\t$this->frx_settings_file .= \"\\n\";\n\t}\n\n\tpublic function reloadDaemon()\n\t{\n\t\t// reload DNS daemon\n\t\t$cmd = Settings::Get('antispam.reload_command');\n\t\t$cmdStatus = 1;\n\t\tFileDir::safe_exec(escapeshellcmd($cmd), $cmdStatus);\n\t\tif ($cmdStatus === 0) {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Antispam daemon reloaded');\n\t\t} else {\n\t\t\t$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'Error while running `' . $cmd . '`: exit code (' . $cmdStatus . ') - please check your system logs');\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/System/ExportCron.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\System;\n\nuse Exception;\nuse Froxlor\\Cron\\Forkable;\nuse Froxlor\\Cron\\FroxlorCron;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\n\nclass ExportCron extends FroxlorCron\n{\n\tuse Forkable;\n\n\tpublic static function run()\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'ExportCron: started - creating customer data export');\n\n\t\t$result_tasks_stmt = Database::query(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` = '20' ORDER BY `id` ASC\n\t\t\");\n\t\t$all_jobs = $result_tasks_stmt->fetchAll();\n\n\t\tif (!empty($all_jobs)) {\n\t\t\tself::runFork([self::class, 'handle'], $all_jobs);\n\t\t}\n\t}\n\n\tpublic static function handle(array $row)\n\t{\n\t\t$del_stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `id` = :id\");\n\t\t$cronlog = FroxlorLogger::getInstanceOf();\n\n\t\tif ($row['data'] != '') {\n\t\t\t$row['data'] = json_decode($row['data'], true);\n\t\t}\n\n\t\tif (is_array($row['data'])) {\n\t\t\tif (isset($row['data']['customerid']) && isset($row['data']['loginname']) && isset($row['data']['destdir'])) {\n\t\t\t\t$row['data']['destdir'] = FileDir::makeCorrectDir($row['data']['destdir']);\n\t\t\t\t$customerdocroot = FileDir::makeCorrectDir(Settings::Get('system.documentroot_prefix') . '/' . $row['data']['loginname'] . '/');\n\n\t\t\t\t// create folder if not exists\n\t\t\t\tif (!file_exists($row['data']['destdir']) && $row['data']['destdir'] != '/' && $row['data']['destdir'] != Settings::Get('system.documentroot_prefix') && $row['data']['destdir'] != $customerdocroot) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Creating data export destination path for customer: ' . escapeshellarg($row['data']['destdir']));\n\t\t\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($row['data']['destdir']));\n\t\t\t\t}\n\n\t\t\t\tself::createCustomerExport($row['data'], $customerdocroot, $cronlog);\n\t\t\t}\n\t\t}\n\n\t\t// remove entry\n\t\tDatabase::pexecute($del_stmt, [\n\t\t\t'id' => $row['id']\n\t\t]);\n\t}\n\n\t/**\n\t * depending on the give choice, the customers web-data, email-data and databases are being exported\n\t *\n\t * @param array $data\n\t *\n\t * @return void\n\t *\n\t * @throws Exception\n\t */\n\tprivate static function createCustomerExport($data = null, $customerdocroot = null, &$cronlog = null)\n\t{\n\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Creating data export for user \"' . $data['loginname'] . '\"');\n\n\t\t// create tmp folder\n\t\t$tmpdir = FileDir::makeCorrectDir($data['destdir'] . '/.tmp/');\n\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Creating tmp-folder \"' . $tmpdir . '\"');\n\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> mkdir -p ' . escapeshellarg($tmpdir));\n\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($tmpdir));\n\t\t$create_export_tar_data = \"\";\n\n\t\t// MySQL databases\n\t\tif ($data['dump_dbs'] == 1) {\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Creating mysql-folder \"' . FileDir::makeCorrectDir($tmpdir . '/mysql') . '\"');\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir($tmpdir . '/mysql')));\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir($tmpdir . '/mysql')));\n\n\t\t\t// get all customer database-names\n\t\t\t$sel_stmt = Database::prepare(\"SELECT `databasename`, `dbserver` FROM `\" . TABLE_PANEL_DATABASES . \"` WHERE `customerid` = :cid ORDER BY `dbserver`\");\n\t\t\tDatabase::pexecute($sel_stmt, [\n\t\t\t\t'cid' => $data['customerid']\n\t\t\t]);\n\n\t\t\t$has_dbs = false;\n\t\t\t$current_dbserver = -1;\n\n\t\t\t// look for mysqldump\n\t\t\t$section = 'mysqldump';\n\t\t\tif (file_exists(\"/usr/bin/mysqldump\")) {\n\t\t\t\t$mysql_dump = '/usr/bin/mysqldump';\n\t\t\t} elseif (file_exists(\"/usr/local/bin/mysqldump\")) {\n\t\t\t\t$mysql_dump = '/usr/local/bin/mysqldump';\n\t\t\t} elseif (file_exists(\"/usr/bin/mariadb-dump\")) {\n\t\t\t\t$mysql_dump = '/usr/bin/mariadb-dump';\n\t\t\t\t$section = 'mariadb-dump';\n\t\t\t}\n\t\t\tif (!isset($mysql_dump)) {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'mysqldump/mariadb-dump executable could not be found. Please install mysql-client/mariadb-client package.');\n\t\t\t} else {\n\n\t\t\t\twhile ($row = $sel_stmt->fetch()) {\n\t\t\t\t\t// Get sql_root data for the specific database-server the database resides on\n\t\t\t\t\tif ($current_dbserver != $row['dbserver']) {\n\t\t\t\t\t\tDatabase::needRoot(true, $row['dbserver']);\n\t\t\t\t\t\tDatabase::needSqlData();\n\t\t\t\t\t\t$sql_root = Database::getSqlData();\n\t\t\t\t\t\tDatabase::needRoot(false);\n\t\t\t\t\t\t// create temporary mysql-defaults file for the connection-credentials/details\n\t\t\t\t\t\t$mysqlcnf_file = tempnam(\"/tmp\", \"frx\");\n\t\t\t\t\t\t$mysqlcnf = \"[\".$section.\"]\\npassword=\" . $sql_root['passwd'] . \"\\nhost=\" . $sql_root['host'] . \"\\n\";\n\t\t\t\t\t\tif (!empty($sql_root['port'])) {\n\t\t\t\t\t\t\t$mysqlcnf .= \"port=\" . $sql_root['port'] . \"\\n\";\n\t\t\t\t\t\t} elseif (!empty($sql_root['socket'])) {\n\t\t\t\t\t\t\t$mysqlcnf .= \"socket=\" . $sql_root['socket'] . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfile_put_contents($mysqlcnf_file, $mysqlcnf);\n\t\t\t\t\t}\n\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> '.basename($mysql_dump) . ' -u ' . escapeshellarg($sql_root['user']) . ' -pXXXXX ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'));\n\t\t\t\t\t$bool_false = false;\n\t\t\t\t\tFileDir::safe_exec($mysql_dump . ' --defaults-file=' . escapeshellarg($mysqlcnf_file) . ' -u ' . escapeshellarg($sql_root['user']) . ' ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'), $bool_false, [\n\t\t\t\t\t\t'>'\n\t\t\t\t\t]);\n\t\t\t\t\t$has_dbs = true;\n\t\t\t\t\t$current_dbserver = $row['dbserver'];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($has_dbs) {\n\t\t\t\t$create_export_tar_data .= './mysql ';\n\t\t\t}\n\n\t\t\tif (file_exists($mysqlcnf_file)) {\n\t\t\t\tunlink($mysqlcnf_file);\n\t\t\t}\n\n\t\t\tunset($sql_root);\n\t\t}\n\n\t\t// E-mail data\n\t\tif ($data['dump_mail'] == 1) {\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Creating mail-folder \"' . FileDir::makeCorrectDir($tmpdir . '/mail') . '\"');\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir($tmpdir . '/mail')));\n\n\t\t\t// get all customer mail-accounts\n\t\t\t$sel_stmt = Database::prepare(\"SELECT `homedir`, `maildir` FROM `\" . TABLE_MAIL_USERS . \"` WHERE `customerid` = :cid\");\n\t\t\tDatabase::pexecute($sel_stmt, [\n\t\t\t\t'cid' => $data['customerid']\n\t\t\t]);\n\n\t\t\t$tar_file_list = \"\";\n\t\t\t$mail_homedir = \"\";\n\t\t\twhile ($row = $sel_stmt->fetch()) {\n\t\t\t\t$tar_file_list .= escapeshellarg(\"./\" . $row['maildir']) . \" \";\n\t\t\t\t$mail_homedir = $row['homedir'];\n\t\t\t}\n\n\t\t\tif (!empty($tar_file_list)) {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(FileDir::makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' -C ' . escapeshellarg($mail_homedir) . ' ' . trim($tar_file_list));\n\t\t\t\tFileDir::safe_exec('tar cfz ' . escapeshellarg(FileDir::makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' -C ' . escapeshellarg($mail_homedir) . ' ' . trim($tar_file_list));\n\t\t\t\t$create_export_tar_data .= './mail ';\n\t\t\t}\n\t\t}\n\n\t\t// Web data\n\t\tif ($data['dump_web'] == 1) {\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Creating web-folder \"' . FileDir::makeCorrectDir($tmpdir . '/web') . '\"');\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir($tmpdir . '/web')));\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg(FileDir::makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude=' . escapeshellarg(str_replace($customerdocroot, \"./\", FileDir::makeCorrectFile($tmpdir . '/*'))) . ' --exclude=' . escapeshellarg(str_replace($customerdocroot, \"./\", substr(FileDir::makeCorrectDir($tmpdir), 0, -1))) . ' -C ' . escapeshellarg($customerdocroot) . ' .');\n\t\t\tFileDir::safe_exec('tar cfz ' . escapeshellarg(FileDir::makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude=' . escapeshellarg(str_replace($customerdocroot, \"./\", FileDir::makeCorrectFile($tmpdir . '/*'))) . ' --exclude=' . escapeshellarg(str_replace($customerdocroot, \"./\", substr(FileDir::makeCorrectFile($tmpdir), 0, -1))) . ' -C ' . escapeshellarg($customerdocroot) . ' .');\n\t\t\t$create_export_tar_data .= './web ';\n\t\t}\n\n\t\tif (!empty($create_export_tar_data)) {\n\t\t\t// set owner to customer\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> chown -R ' . (int)$data['uid'] . ':' . (int)$data['gid'] . ' ' . escapeshellarg($tmpdir));\n\t\t\tFileDir::safe_exec('chown -R ' . (int)$data['uid'] . ':' . (int)$data['gid'] . ' ' . escapeshellarg($tmpdir));\n\t\t\t// create tar-file\n\t\t\t$export_file = FileDir::makeCorrectFile($tmpdir . '/' . $data['loginname'] . '-export_' . date('YmdHi', time()) . '.tar.gz' . (!empty($data['pgp_public_key']) ? '.gpg' : ''));\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Creating export-file \"' . $export_file . '\"');\n\t\t\tif (!empty($data['pgp_public_key'])) {\n\t\t\t\t// pack all archives in tmp-dir to one archive and encrypt it with gpg\n\t\t\t\t$recipient_file = FileDir::makeCorrectFile($tmpdir . '/' . $data['loginname'] . '-recipients.gpg');\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Creating recipient-file \"' . $recipient_file . '\"');\n\t\t\t\tfile_put_contents($recipient_file, $data['pgp_public_key']);\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> tar cfz - -C ' . escapeshellarg($tmpdir) . ' ' . trim($create_export_tar_data) . ' | gpg --encrypt --recipient-file ' . escapeshellarg($recipient_file) . ' --output ' . escapeshellarg($export_file) . ' --trust-model always --batch --yes');\n\t\t\t\tFileDir::safe_exec('tar cfz - -C ' . escapeshellarg($tmpdir) . ' ' . trim($create_export_tar_data) . ' | gpg --encrypt --recipient-file ' . escapeshellarg($recipient_file) . ' --output ' . escapeshellarg($export_file) . ' --trust-model always --batch --yes', $return_value, ['|']);\n\t\t\t} else {\n\t\t\t\t// pack all archives in tmp-dir to one archive\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg($export_file) . ' -C ' . escapeshellarg($tmpdir) . ' ' . trim($create_export_tar_data));\n\t\t\t\tFileDir::safe_exec('tar cfz ' . escapeshellarg($export_file) . ' -C ' . escapeshellarg($tmpdir) . ' ' . trim($create_export_tar_data));\n\t\t\t}\n\t\t\t// move to destination directory\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> mv ' . escapeshellarg($export_file) . ' ' . escapeshellarg($data['destdir']));\n\t\t\tFileDir::safe_exec('mv ' . escapeshellarg($export_file) . ' ' . escapeshellarg($data['destdir']));\n\t\t\t// remove tmp-files\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> rm -rf ' . escapeshellarg($tmpdir));\n\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($tmpdir));\n\t\t\t// set owner to customer\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> chown -R ' . (int)$data['uid'] . ':' . (int)$data['gid'] . ' ' . escapeshellarg($data['destdir']));\n\t\t\tif (is_link(rtrim($data['destdir'], '/'))) {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'Export destination is a symlink, skipping chown for security: ' . $data['destdir']);\n\t\t\t} else {\n\t\t\t\tFileDir::safe_exec('chown -R ' . (int)$data['uid'] . ':' . (int)$data['gid'] . ' ' . escapeshellarg($data['destdir']));\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/System/Extrausers.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\System;\n\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse Froxlor\\User;\nuse PDO;\n\nclass Extrausers\n{\n\n\tpublic static function generateFiles(&$cronlog)\n\t{\n\t\t// passwd\n\t\t$passwd = '/var/lib/extrausers/passwd';\n\t\t$sql = \"SELECT customerid,username,'x' as password,uid,gid,'Froxlor User' as comment,homedir,shell, login_enabled FROM ftp_users ORDER BY uid, LENGTH(username) ASC\";\n\t\t$users_list = [];\n\t\tself::generateFile($passwd, $sql, $cronlog, $users_list);\n\n\t\t// group\n\t\t$group = '/var/lib/extrausers/group';\n\t\t$sql = \"SELECT groupname,'x' as password,gid,members FROM ftp_groups ORDER BY gid ASC\";\n\t\tself::generateFile($group, $sql, $cronlog, $users_list);\n\n\t\t// shadow\n\t\t$shadow = '/var/lib/extrausers/shadow';\n\t\t$sql = \"SELECT username,password FROM ftp_users ORDER BY gid ASC\";\n\t\tself::generateFile($shadow, $sql, $cronlog);\n\n\t\t// set correct permissions\n\t\t@chmod('/var/lib/extrausers/', 0755);\n\t\t@chmod('/var/lib/extrausers/passwd', 0644);\n\t\t@chmod('/var/lib/extrausers/group', 0644);\n\t\t@chmod('/var/lib/extrausers/shadow', 0640);\n\n\t\tSshKeys::generateFiles($cronlog);\n\t}\n\n\tprivate static function generateFile($file, $query, &$cronlog, &$result_list = null)\n\t{\n\t\t$type = basename($file);\n\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Creating ' . $type . ' file');\n\n\t\tif (!file_exists($file)) {\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, $type . ' file does not yet exist');\n\t\t\t@mkdir(dirname($file), 0750, true);\n\t\t\ttouch($file);\n\t\t}\n\n\t\t$data_sel_stmt = Database::query($query);\n\t\t$data_content = \"\";\n\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Writing ' . $data_sel_stmt->rowCount() . ' entries to ' . $type . ' file');\n\t\twhile ($u = $data_sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tswitch ($type) {\n\t\t\t\tcase 'passwd':\n\t\t\t\t\t// get user real name\n\t\t\t\t\t$salutation_array = [\n\t\t\t\t\t\t'firstname' => Customer::getCustomerDetail($u['customerid'], 'firstname'),\n\t\t\t\t\t\t'name' => Customer::getCustomerDetail($u['customerid'], 'name'),\n\t\t\t\t\t\t'company' => Customer::getCustomerDetail($u['customerid'], 'company')\n\t\t\t\t\t];\n\t\t\t\t\t$u['comment'] = self::cleanString(User::getCorrectUserSalutation($salutation_array));\n\t\t\t\t\tif ($u['login_enabled'] != 'Y') {\n\t\t\t\t\t\t$u['password'] = '*';\n\t\t\t\t\t\t$u['shell'] = '/bin/false';\n\t\t\t\t\t\t$u['comment'] = 'Locked Froxlor User';\n\t\t\t\t\t}\n\t\t\t\t\tif (Customer::getCustomerDetail($u['customerid'], 'shell_allowed') == '0') {\n\t\t\t\t\t\t$u['shell'] = '/bin/false';\n\t\t\t\t\t}\n\t\t\t\t\t$line = $u['username'] . ':' . $u['password'] . ':' . $u['uid'] . ':' . $u['gid'] . ':' . $u['comment'] . ':' . $u['homedir'] . ':' . $u['shell'] . PHP_EOL;\n\t\t\t\t\tif (is_array($result_list)) {\n\t\t\t\t\t\t$result_list[] = $u['username'];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'group':\n\t\t\t\t\t$line = $u['groupname'] . ':' . $u['password'] . ':' . $u['gid'] . ':' . $u['members'] . PHP_EOL;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'shadow':\n\t\t\t\t\t$line = $u['username'] . ':' . $u['password'] . ':' . floor(time() / 86400 - 1) . ':0:99999:7:::' . PHP_EOL;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t$data_content .= $line;\n\t\t}\n\n\t\t// check for local group to generate\n\t\tif ($type == 'group' && Settings::Get('system.froxlorusergroup') != '') {\n\t\t\t$guid = intval(Settings::Get('system.froxlorusergroup_gid'));\n\t\t\tif (empty($guid)) {\n\t\t\t\t$guid = intval(Settings::Get('system.lastguid')) + 1;\n\t\t\t\tSettings::Set('system.lastguid', $guid, true);\n\t\t\t\tSettings::Set('system.froxlorusergroup_gid', $guid, true);\n\t\t\t}\n\t\t\t$line = Settings::Get('system.froxlorusergroup') . ':x:' . $guid . ':' . implode(',', $result_list) . PHP_EOL;\n\t\t\t$data_content .= $line;\n\t\t}\n\n\t\tif (file_put_contents($file, $data_content) !== false) {\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Succesfully wrote ' . $type . ' file');\n\t\t} else {\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Error when writing ' . $type . ' file entries');\n\t\t}\n\t}\n\n\tprivate static function cleanString($string = null)\n\t{\n\t\t$allowed = \"/[^a-z0-9\\\\.\\\\-\\\\_\\\\ ]/i\";\n\t\treturn preg_replace($allowed, \"\", $string);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/System/MailboxsizeCron.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\System;\n\nuse Froxlor\\Cron\\FroxlorCron;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse PDO;\n\nclass MailboxsizeCron extends FroxlorCron\n{\n\n\tpublic static function run()\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'calculating mailspace usage');\n\n\t\t$maildirs_stmt = Database::query(\"\n\t\t\tSELECT `id`, CONCAT(`homedir`, `maildir`) AS `maildirpath` FROM `\" . TABLE_MAIL_USERS . \"` ORDER BY `id`\n\t\t\");\n\n\t\t$upd_stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_MAIL_USERS . \"` SET `mboxsize` = :size WHERE `id` = :id\n\t\t\");\n\n\t\twhile ($maildir = $maildirs_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$_maildir = FileDir::makeCorrectDir($maildir['maildirpath']);\n\n\t\t\tif (file_exists($_maildir) && is_dir($_maildir)) {\n\n\t\t\t\t// compute actual maildirsize with `du`\n\t\t\t\t// mail-address allows many special characters, see http://en.wikipedia.org/wiki/Email_address#Local_part\n\t\t\t\t$return = false;\n\t\t\t\t$back = FileDir::safe_exec('du -sk ' . escapeshellarg($_maildir), $return, [\n\t\t\t\t\t'|',\n\t\t\t\t\t'&',\n\t\t\t\t\t'`',\n\t\t\t\t\t'$',\n\t\t\t\t\t'~',\n\t\t\t\t\t'?'\n\t\t\t\t]);\n\t\t\t\tforeach ($back as $backrow) {\n\t\t\t\t\t$emailusage = explode(' ', $backrow);\n\t\t\t\t}\n\t\t\t\t$emailusage = floatval($emailusage['0']);\n\n\t\t\t\t// as freebsd does not have the -b flag for 'du' which gives\n\t\t\t\t// the size in bytes, we use \"-sk\" for all and calculate from KiB\n\t\t\t\t$emailusage *= 1024;\n\n\t\t\t\tunset($back);\n\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'size' => $emailusage,\n\t\t\t\t\t'id' => $maildir['id']\n\t\t\t\t]);\n\t\t\t} else {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, 'maildir ' . $_maildir . ' does not exist');\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/System/SshKeys.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\System;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass SshKeys\n{\n\t/**\n\t * @throws \\Exception\n\t */\n\tpublic static function generateFiles(&$cronlog)\n\t{\n\t\tif (intval(Settings::Get('system.allow_customer_shell')) == 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// authorized_keys\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT id,customerid,username,homedir,uid,gid,shell,login_enabled\n\t\t\tFROM `\" . TABLE_FTP_USERS . \"`\n\t\t\tORDER BY uid, LENGTH(username) ASC\n\t\t\");\n\t\tDatabase::pexecute($sel_stmt);\n\t\t$sshkeys_sel_stmt = Database::prepare(\"\n\t\t\tSELECT `id`, `ssh_pubkey` FROM `\" . TABLE_PANEL_USER_SSHKEYS . \"` WHERE `ftp_user_id` = :fuid AND `customerid` = :cid\n\t\t\");\n\t\twhile ($usr = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$userHomeDir = FileDir::makeCorrectDir($usr['homedir'] . '/.ssh', $usr['homedir']);\n\t\t\t$authkeysfile = FileDir::makeCorrectFile($userHomeDir . '/authorized_keys', $usr['homedir']);\n\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Creating file ' . $authkeysfile);\n\t\t\t// remove all entries with 'froxlor:id=...'\n\t\t\tself::removeFroxlorKeys($authkeysfile, $cronlog);\n\t\t\t// get keys\n\t\t\tDatabase::pexecute($sshkeys_sel_stmt, ['fuid' => $usr['id'], 'cid' => $usr['customerid']]);\n\t\t\tif ($sshkeys_sel_stmt->rowCount() > 0) {\n\t\t\t\tif (!file_exists(dirname($authkeysfile))) {\n\t\t\t\t\t@mkdir(dirname($authkeysfile), 0700);\n\t\t\t\t}\n\t\t\t\tif (!file_exists($authkeysfile)) {\n\t\t\t\t\t@touch($authkeysfile);\n\t\t\t\t}\n\n\t\t\t\twhile ($sshkey = $sshkeys_sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t// normalize: Algo + Base64 part (remove comment)\n\t\t\t\t\t$parts = preg_split('/\\s+/', trim($sshkey['ssh_pubkey']), 3);\n\t\t\t\t\tif (count($parts) < 2) {\n\t\t\t\t\t\t// Invalid public key format\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t$normalized = $parts[0] . ' ' . $parts[1];\n\n\t\t\t\t\t// Datei lesen (falls sie schon existiert)\n\t\t\t\t\t$existing = [];\n\t\t\t\t\tif (file_exists($authkeysfile)) {\n\t\t\t\t\t\t$lines = file($authkeysfile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);\n\t\t\t\t\t\tforeach ($lines as $line) {\n\t\t\t\t\t\t\t$lineParts = preg_split('/\\s+/', trim($line), 3);\n\t\t\t\t\t\t\tif (count($lineParts) >= 2) {\n\t\t\t\t\t\t\t\t$existing[] = $lineParts[0] . ' ' . $lineParts[1];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// does the key exist already?\n\t\t\t\t\tif (in_array($normalized, $existing, true)) {\n\t\t\t\t\t\t// skip\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// add key\n\t\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Adding ssh-key for user ' . $usr['username']);\n\t\t\t\t\tfile_put_contents($authkeysfile, trim($sshkey['ssh_pubkey']) . \" froxlor:id=\" . $sshkey['id'] . \"\\n\", FILE_APPEND | LOCK_EX);\n\t\t\t\t}\n\t\t\t}\n\t\t\t@chmod(dirname($authkeysfile), 0700);\n\t\t\t@chown(dirname($authkeysfile), $usr['uid']);\n\t\t\t@chgrp(dirname($authkeysfile), $usr['gid']);\n\t\t\t@chmod($authkeysfile, 0600);\n\t\t\t@chown($authkeysfile, $usr['uid']);\n\t\t\t@chgrp($authkeysfile, $usr['gid']);\n\t\t}\n\t}\n\n\tprivate static function removeFroxlorKeys(string $authFile, &$cronlog): bool\n\t{\n\t\tif (!file_exists($authFile)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$lines = file($authFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);\n\t\t$newLines = [];\n\n\t\tforeach ($lines as $line) {\n\t\t\t// kill whitespaces\n\t\t\t$trim = trim($line);\n\n\t\t\t// if comment 'froxlor:id=' skip line\n\t\t\t$matches = [];\n\t\t\tif (preg_match('/\\bfroxlor:id=(.+)/', $trim, $matches)) {\n\t\t\t\t$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Removing ' . $matches[0]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!empty($trim)) {\n\t\t\t\t$newLines[] = $line;\n\t\t\t}\n\t\t}\n\n\t\t// use LOCK_EX to avoid race\n\t\tif (empty($newLines)) {\n\t\t\t// empty file, we can safely remove it\n\t\t\t@unlink($authFile);\n\t\t} else {\n\t\t\t// keep existing (non-froxlor-managed entries)\n\t\t\tfile_put_contents($authFile, implode(\"\\n\", $newLines) . \"\\n\", LOCK_EX);\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/System/TasksCron.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\System;\n\nuse Exception;\nuse Froxlor\\Cron\\FroxlorCron;\nuse Froxlor\\Cron\\Http\\ConfigIO;\nuse Froxlor\\Cron\\Http\\HttpConfigBase;\nuse Froxlor\\Cron\\Http\\LetsEncrypt\\AcmeSh;\nuse Froxlor\\Cron\\Mail\\Rspamd;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Dns\\PowerDNS;\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass TasksCron extends FroxlorCron\n{\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic static function run()\n\t{\n\t\t/**\n\t\t * LOOK INTO TASKS TABLE TO SEE IF THERE ARE ANY UNDONE JOBS\n\t\t */\n\t\tself::$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"TasksCron: Searching for tasks to do\");\n\t\t// no type 99 (regenerate cron.d-file) and no type 20 (customer data export)\n\t\t// order by type descending to re-create bind and then webserver at the end\n\t\t$result_tasks_stmt = Database::query(\"\n\t\t\tSELECT `id`, `type`, `data` FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` <> '99' AND `type` <> '20' ORDER BY `type` DESC, `id` ASC\n\t\t\");\n\t\t$num_results = Database::num_rows();\n\t\t$resultIDs = [];\n\n\t\twhile ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$resultIDs[] = $row['id'];\n\n\t\t\tif ($row['data'] != '') {\n\t\t\t\t$row['data'] = json_decode($row['data'], true);\n\t\t\t}\n\n\t\t\tif ($row['type'] == TaskId::REBUILD_VHOST) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=1 MEANS TO REBUILD APACHE VHOSTS.CONF\n\t\t\t\t */\n\t\t\t\tself::rebuildWebserverConfigs();\n\t\t\t} elseif ($row['type'] == TaskId::CREATE_HOME) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=2 MEANS TO CREATE A NEW HOME AND CHOWN\n\t\t\t\t */\n\t\t\t\tself::createNewHome($row);\n\t\t\t} elseif ($row['type'] == TaskId::REBUILD_DNS && (int)Settings::Get('system.bind_enable') != 0) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=4 MEANS THAT SOMETHING IN THE BIND CONFIG HAS CHANGED.\n\t\t\t\t * REBUILD froxlor_bind.conf IF BIND IS ENABLED\n\t\t\t\t */\n\t\t\t\tself::rebuildDnsConfigs();\n\t\t\t} elseif ($row['type'] == TaskId::CREATE_FTP) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=5 MEANS THAT A NEW FTP-ACCOUNT HAS BEEN CREATED, CREATE THE DIRECTORY\n\t\t\t\t */\n\t\t\t\tself::createNewFtpHome($row);\n\t\t\t} elseif ($row['type'] == TaskId::DELETE_CUSTOMER_FILES) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=6 MEANS THAT A CUSTOMER HAS BEEN DELETED AND THAT WE HAVE TO REMOVE ITS FILES\n\t\t\t\t */\n\t\t\t\tself::deleteCustomerData($row);\n\t\t\t} elseif ($row['type'] == TaskId::DELETE_EMAIL_DATA) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=7 Customer deleted an email account and wants the data to be deleted on the filesystem\n\t\t\t\t */\n\t\t\t\tself::deleteEmailData($row);\n\t\t\t} elseif ($row['type'] == TaskId::DELETE_FTP_DATA) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=8 Customer deleted a ftp account and wants the homedir to be deleted on the filesystem\n\t\t\t\t * refs #293\n\t\t\t\t */\n\t\t\t\tself::deleteFtpData($row);\n\t\t\t} elseif ($row['type'] == TaskId::REBUILD_RSPAMD && (int)Settings::Get('antispam.activated') != 0) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=9 Rebuild antispam config\n\t\t\t\t */\n\t\t\t\tself::rebuildAntiSpamConfigs();\n\t\t\t} elseif ($row['type'] == TaskId::CREATE_QUOTA && (int)Settings::Get('system.diskquota_enabled') != 0) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=10 Set the filesystem - quota\n\t\t\t\t */\n\t\t\t\tself::setFilesystemQuota();\n\t\t\t} elseif ($row['type'] == TaskId::DELETE_DOMAIN_PDNS && Settings::Get('system.dns_server') == 'PowerDNS') {\n\t\t\t\t/**\n\t\t\t\t * TYPE=11 domain has been deleted, remove from pdns database if used\n\t\t\t\t */\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, \"Removing PowerDNS entries for domain \" . $row['data']['domain']);\n\t\t\t\tPowerDNS::cleanDomainZone($row['data']['domain']);\n\t\t\t} elseif ($row['type'] == TaskId::DELETE_DOMAIN_SSL) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=12 domain has been deleted, remove from acme.sh/let's encrypt directory if used\n\t\t\t\t */\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, \"Removing Let's Encrypt entries for domain \" . $row['data']['domain']);\n\t\t\t\tDomain::doLetsEncryptCleanUp($row['data']['domain']);\n\t\t\t} elseif ($row['type'] == TaskId::UPDATE_LE_SERVICES) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=13 set configuration for selected services regarding the use of Let's Encrypt certificate\n\t\t\t\t */\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, \"Updating Let's Encrypt configuration for selected services\");\n\t\t\t\tAcmeSh::renewHookConfigs(FroxlorLogger::getInstanceOf());\n\t\t\t} elseif ($row['type'] == TaskId::REBUILD_NSSUSERS) {\n\t\t\t\t/**\n\t\t\t\t * TYPE=14 regenerate libnss users/groups\n\t\t\t\t */\n\t\t\t\tself::refreshUsers();\n\t\t\t}\n\t\t}\n\n\t\tif ($num_results != 0) {\n\t\t\t$where = [];\n\t\t\t$where_data = [];\n\t\t\tforeach ($resultIDs as $id) {\n\t\t\t\t$where[] = \"`id` = :id_\" . (int)$id;\n\t\t\t\t$where_data['id_' . $id] = $id;\n\t\t\t}\n\t\t\t$where = implode(' OR ', $where);\n\t\t\t$del_stmt = Database::prepare(\"DELETE FROM `\" . TABLE_PANEL_TASKS . \"` WHERE \" . $where);\n\t\t\tDatabase::pexecute($del_stmt, $where_data);\n\t\t\tunset($resultIDs);\n\t\t\tunset($where);\n\t\t}\n\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `value` = UNIX_TIMESTAMP() WHERE `settinggroup` = 'system' AND `varname` = 'last_tasks_run';\");\n\t}\n\n\tprivate static function rebuildWebserverConfigs()\n\t{\n\t\tif (Settings::Get('system.webserver') == \"apache2\") {\n\t\t\t$websrv = '\\\\Froxlor\\\\Cron\\\\Http\\\\Apache';\n\t\t\tif (Settings::Get('system.mod_fcgid') == 1 || Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$websrv .= 'Fcgi';\n\t\t\t}\n\t\t} elseif (Settings::Get('system.webserver') == \"nginx\") {\n\t\t\t$websrv = '\\\\Froxlor\\\\Cron\\\\Http\\\\Nginx';\n\t\t\tif (Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t$websrv .= 'Fcgi';\n\t\t\t}\n\t\t}\n\n\t\t// get configuration-I/O object\n\t\t$configio = new ConfigIO();\n\t\t// get webserver object\n\t\t$webserver = new $websrv();\n\n\t\tif ($webserver instanceof HttpConfigBase) {\n\t\t\t$webserver->init();\n\t\t\t// clean up old configs\n\t\t\t$configio->cleanUp();\n\t\t\t$webserver->createIpPort();\n\t\t\t$webserver->createVirtualHosts();\n\t\t\t$webserver->createFileDirOptions();\n\t\t\t$webserver->writeConfigs();\n\t\t\t$webserver->createOwnVhostStarter();\n\t\t\t$webserver->reload();\n\t\t} else {\n\t\t\techo \"Please check you Webserver settings\\n\";\n\t\t}\n\n\t\t// if we use php-fpm and have a local user for froxlor, we need to\n\t\t// add the webserver-user to the local-group in order to allow the webserver\n\t\t// to access the fpm-socket\n\t\tif (Settings::Get('phpfpm.enabled') == 1 && function_exists(\"posix_getgrnam\")) {\n\t\t\t// get group info about the local-user's group (e.g. froxlorlocal)\n\t\t\t$groupinfo = posix_getgrnam(Settings::Get('phpfpm.vhost_httpgroup'));\n\t\t\t// check group members\n\t\t\tif (isset($groupinfo['members']) && !in_array(Settings::Get('system.httpuser'), $groupinfo['members'])) {\n\t\t\t\t// webserver has no access, add it\n\t\t\t\tif (FileDir::isFreeBSD()) {\n\t\t\t\t\tFileDir::safe_exec('pw usermod ' . escapeshellarg(Settings::Get('system.httpuser')) . ' -G ' . escapeshellarg(Settings::Get('phpfpm.vhost_httpgroup')));\n\t\t\t\t} else {\n\t\t\t\t\tFileDir::safe_exec('usermod -a -G ' . escapeshellarg(Settings::Get('phpfpm.vhost_httpgroup')) . ' ' . escapeshellarg(Settings::Get('system.httpuser')));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Tell the Let's Encrypt cron it's okay to generate the certificate and enable the redirect afterwards\n\t\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET `ssl_redirect` = '3' WHERE `ssl_redirect` = '2'\");\n\t\tDatabase::pexecute($upd_stmt);\n\t}\n\n\tprivate static function createNewHome($row = null)\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'TasksCron: Task2 started - create new home');\n\n\t\tif (is_array($row['data'])) {\n\t\t\t// define paths\n\t\t\t$userhomedir = FileDir::makeCorrectDir(Settings::Get('system.documentroot_prefix') . '/' . $row['data']['loginname'] . '/');\n\t\t\t$usermaildir = FileDir::makeCorrectDir(Settings::Get('system.vmail_homedir') . '/' . $row['data']['loginname'] . '/');\n\n\t\t\t// stats directory\n\t\t\t$statsdir = FileDir::makeCorrectDir($userhomedir . '/' . Settings::Get('system.traffictool'));\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: mkdir -p ' . escapeshellarg($statsdir));\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($statsdir));\n\n\t\t\tforeach (['webalizer', 'awstats', 'goaccess'] as $statstools) {\n\t\t\t\t$statsdir = FileDir::makeCorrectDir($userhomedir . '/' . $statstools);\n\t\t\t\t// in case we changed from the other stats -> remove old\n\t\t\t\tif (Settings::Get('system.traffictool') != $statstools && file_exists($statsdir)) {\n\t\t\t\t\t// (yes i know, the stats are lost - that's why you should not change all the time!)\n\t\t\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($statsdir));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// maildir\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: mkdir -p ' . escapeshellarg($usermaildir));\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($usermaildir));\n\n\t\t\t// check if admin of customer has added template for new customer directories\n\t\t\tif ((int)$row['data']['store_defaultindex'] == 1) {\n\t\t\t\tFileDir::storeDefaultIndex($row['data']['loginname'], $userhomedir, FroxlorLogger::getInstanceOf(), true);\n\t\t\t}\n\n\t\t\t// strip of last slash of paths to have correct chown results\n\t\t\t$userhomedir = (substr($userhomedir, 0, -1) == '/') ? substr($userhomedir, 0, -1) : $userhomedir;\n\t\t\t$usermaildir = (substr($usermaildir, 0, -1) == '/') ? substr($usermaildir, 0, -1) : $usermaildir;\n\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: chown -R ' . (int)$row['data']['uid'] . ':' . (int)$row['data']['gid'] . ' ' . escapeshellarg($userhomedir));\n\t\t\tFileDir::safe_exec('chown -R ' . (int)$row['data']['uid'] . ':' . (int)$row['data']['gid'] . ' ' . escapeshellarg($userhomedir));\n\t\t\t// don't allow others to access the directory (webserver will be the group via libnss-mysql)\n\t\t\tif (Settings::Get('system.mod_fcgid') == 1 || Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t\t// fcgid or fpm\n\t\t\t\tFileDir::safe_exec('chmod 0750 ' . escapeshellarg($userhomedir));\n\t\t\t} else {\n\t\t\t\t// mod_php -> no libnss-mysql -> no webserver-user in group\n\t\t\t\tFileDir::safe_exec('chmod 0755 ' . escapeshellarg($userhomedir));\n\t\t\t}\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: chown -R ' . (int)Settings::Get('system.vmail_uid') . ':' . (int)Settings::Get('system.vmail_gid') . ' ' . escapeshellarg($usermaildir));\n\t\t\tFileDir::safe_exec('chown -R ' . (int)Settings::Get('system.vmail_uid') . ':' . (int)Settings::Get('system.vmail_gid') . ' ' . escapeshellarg($usermaildir));\n\n\t\t\t// explicitly create files after user has been created to avoid unknown user issues for apache/php-fpm when task#1 runs after this\n\t\t\tself::refreshUsers();\n\t\t}\n\t}\n\n\tprivate static function rebuildDnsConfigs()\n\t{\n\t\t$dnssrv = '\\\\Froxlor\\\\Cron\\\\Dns\\\\' . Settings::Get('system.dns_server');\n\t\t$nameserver = new $dnssrv(FroxlorLogger::getInstanceOf());\n\t\t$nameserver->writeConfigs();\n\t}\n\n\tprivate static function createNewFtpHome($row = null)\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Creating new FTP-home');\n\t\t$result_directories_stmt = Database::query(\"\n\t\t\tSELECT `f`.`homedir`, `f`.`uid`, `f`.`gid`, `c`.`documentroot` AS `customerroot`\n\t\t\tFROM `\" . TABLE_FTP_USERS . \"` `f` LEFT JOIN `\" . TABLE_PANEL_CUSTOMERS . \"` `c` USING (`customerid`)\n\t\t\");\n\n\t\twhile ($directory = $result_directories_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tFileDir::mkDirWithCorrectOwnership($directory['customerroot'], $directory['homedir'], $directory['uid'], $directory['gid']);\n\t\t}\n\t}\n\n\tprivate static function deleteCustomerData($row = null)\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'TasksCron: Task6 started - deleting customer data');\n\n\t\tif (is_array($row['data'])) {\n\t\t\tif (isset($row['data']['loginname'])) {\n\t\t\t\t// remove homedir\n\t\t\t\t$homedir = FileDir::makeCorrectDir(Settings::Get('system.documentroot_prefix') . '/' . $row['data']['loginname']);\n\n\t\t\t\tif (file_exists($homedir) && $homedir != '/' && $homedir != Settings::Get('system.documentroot_prefix') && substr($homedir, 0, strlen(Settings::Get('system.documentroot_prefix'))) == Settings::Get('system.documentroot_prefix')) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . escapeshellarg($homedir));\n\t\t\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($homedir));\n\t\t\t\t}\n\n\t\t\t\t// remove maildir\n\t\t\t\t$maildir = FileDir::makeCorrectDir(Settings::Get('system.vmail_homedir') . '/' . $row['data']['loginname']);\n\n\t\t\t\tif (file_exists($maildir) && $maildir != '/' && $maildir != Settings::Get('system.vmail_homedir') && substr($maildir, 0, strlen(Settings::Get('system.vmail_homedir'))) == Settings::Get('system.vmail_homedir') && is_dir($maildir) && fileowner($maildir) == Settings::Get('system.vmail_uid') && filegroup($maildir) == Settings::Get('system.vmail_gid')) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . escapeshellarg($maildir));\n\t\t\t\t\t// mail-address allows many special characters, see http://en.wikipedia.org/wiki/Email_address#Local_part\n\t\t\t\t\t$return = false;\n\t\t\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($maildir), $return, [\n\t\t\t\t\t\t'|',\n\t\t\t\t\t\t'&',\n\t\t\t\t\t\t'`',\n\t\t\t\t\t\t'$',\n\t\t\t\t\t\t'?'\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\t// remove tmpdir if it exists\n\t\t\t\t$tmpdir = FileDir::makeCorrectDir(Settings::Get('system.mod_fcgid_tmpdir') . '/' . $row['data']['loginname'] . '/');\n\n\t\t\t\tif (file_exists($tmpdir) && is_dir($tmpdir) && $tmpdir != \"/\" && $tmpdir != Settings::Get('system.mod_fcgid_tmpdir') && substr($tmpdir, 0, strlen(Settings::Get('system.mod_fcgid_tmpdir'))) == Settings::Get('system.mod_fcgid_tmpdir')) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . escapeshellarg($tmpdir));\n\t\t\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($tmpdir));\n\t\t\t\t}\n\n\t\t\t\t// webserver logs\n\t\t\t\t$logsdir = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . '/' . $row['data']['loginname']);\n\n\t\t\t\tif (file_exists(dirname($logsdir)) && $logsdir != '/' && $logsdir != FileDir::makeCorrectDir(Settings::Get('system.logfiles_directory')) && substr($logsdir, 0, strlen(Settings::Get('system.logfiles_directory'))) == Settings::Get('system.logfiles_directory')) {\n\t\t\t\t\t// build up wildcard for webX-{access,error}.log{*}\n\t\t\t\t\t$logsdir .= '-*.log';\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . FileDir::makeCorrectFile($logsdir));\n\t\t\t\t\tFileDir::safe_exec('rm -f ' . FileDir::makeCorrectFile($logsdir));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static function deleteEmailData($row = null)\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'TasksCron: Task7 started - deleting customer e-mail data');\n\n\t\tif (is_array($row['data'])) {\n\t\t\tif (isset($row['data']['loginname']) && isset($row['data']['emailpath'])) {\n\t\t\t\t// remove specific maildir\n\t\t\t\t$email_full = $row['data']['emailpath'];\n\t\t\t\tif (empty($email_full)) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'FATAL: Task7 asks to delete a email account but emailpath field is empty!');\n\t\t\t\t}\n\n\t\t\t\t$maildir = FileDir::makeCorrectDir($email_full);\n\n\t\t\t\tif ($maildir != '/' && !empty($maildir) && $maildir != Settings::Get('system.vmail_homedir') && substr($maildir, 0, strlen(Settings::Get('system.vmail_homedir'))) == Settings::Get('system.vmail_homedir') && is_dir($maildir) && fileowner($maildir) == Settings::Get('system.vmail_uid') && filegroup($maildir) == Settings::Get('system.vmail_gid')) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . escapeshellarg($maildir));\n\t\t\t\t\t// mail-address allows many special characters, see http://en.wikipedia.org/wiki/Email_address#Local_part\n\t\t\t\t\t$return = false;\n\t\t\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($maildir), $return, [\n\t\t\t\t\t\t'|',\n\t\t\t\t\t\t'&',\n\t\t\t\t\t\t'`',\n\t\t\t\t\t\t'$',\n\t\t\t\t\t\t'~',\n\t\t\t\t\t\t'?'\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static function deleteFtpData($row = null)\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'TasksCron: Task8 started - deleting customer ftp homedir');\n\n\t\tif (is_array($row['data'])) {\n\t\t\tif (isset($row['data']['loginname']) && isset($row['data']['homedir'])) {\n\t\t\t\t// remove specific homedir\n\t\t\t\t$ftphomedir = FileDir::makeCorrectDir($row['data']['homedir']);\n\t\t\t\t$customerdocroot = FileDir::makeCorrectDir(Settings::Get('system.documentroot_prefix') . '/' . $row['data']['loginname'] . '/');\n\n\t\t\t\tif (file_exists($ftphomedir) && $ftphomedir != '/' && $ftphomedir != Settings::Get('system.documentroot_prefix') && $ftphomedir != $customerdocroot) {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . escapeshellarg($ftphomedir));\n\t\t\t\t\tFileDir::safe_exec('rm -rf ' . escapeshellarg($ftphomedir));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static function setFilesystemQuota()\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'TasksCron: Task10 started - setting filesystem quota');\n\n\t\t$usedquota = FileDir::getFilesystemQuota();\n\n\t\t// Check whether we really have entries to check\n\t\tif (is_array($usedquota) && count($usedquota) > 0) {\n\t\t\t// Select all customers Froxlor knows about\n\t\t\t$result_stmt = Database::query(\"SELECT `guid`, `loginname`, `diskspace` FROM `\" . TABLE_PANEL_CUSTOMERS . \"`;\");\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t// We do not want to set a quota for root by accident\n\t\t\t\tif ($row['guid'] != 0) {\n\t\t\t\t\t$used_quota = isset($usedquota[$row['guid']]) ? $usedquota[$row['guid']]['block']['hard'] : 0;\n\t\t\t\t\t// The user has no quota in Froxlor, but on the filesystem\n\t\t\t\t\tif (($row['diskspace'] == 0 || $row['diskspace'] == -1024) && $used_quota != 0) {\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, \"Disabling quota for \" . $row['loginname']);\n\t\t\t\t\t\tif (FileDir::isFreeBSD()) {\n\t\t\t\t\t\t\tFileDir::safe_exec(Settings::Get('system.diskquota_quotatool_path') . \" -e \" . escapeshellarg(Settings::Get('system.diskquota_customer_partition')) . \":0:0 \" . $row['guid']);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tFileDir::safe_exec(Settings::Get('system.diskquota_quotatool_path') . \" -u \" . $row['guid'] . \" -bl 0 -q 0 \" . escapeshellarg(Settings::Get('system.diskquota_customer_partition')));\n\t\t\t\t\t\t}\n\t\t\t\t\t} elseif ($row['diskspace'] != $used_quota && $row['diskspace'] != -1024) {\n\t\t\t\t\t\t// The user quota in Froxlor is different than on the filesystem\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, \"Setting quota for \" . $row['loginname'] . \" from \" . $used_quota . \" to \" . $row['diskspace']);\n\t\t\t\t\t\tif (FileDir::isFreeBSD()) {\n\t\t\t\t\t\t\tFileDir::safe_exec(Settings::Get('system.diskquota_quotatool_path') . \" -e \" . escapeshellarg(Settings::Get('system.diskquota_customer_partition')) . \":\" . $row['diskspace'] . \":\" . $row['diskspace'] . \" \" . $row['guid']);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tFileDir::safe_exec(Settings::Get('system.diskquota_quotatool_path') . \" -u \" . $row['guid'] . \" -bl \" . $row['diskspace'] . \" -q \" . $row['diskspace'] . \" \" . escapeshellarg(Settings::Get('system.diskquota_customer_partition')));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate static function rebuildAntiSpamConfigs()\n\t{\n\t\t$antispam = new Rspamd(FroxlorLogger::getInstanceOf());\n\t\t$antispam->writeConfigs();\n\t}\n\n\tprivate static function refreshUsers()\n\t{\n\t\tif (Settings::Get('system.nssextrausers') == 1) {\n\t\t\t$cronLog = FroxlorLogger::getInstanceOf();\n\t\t\tExtrausers::generateFiles($cronLog);\n\t\t\t// reload crond as shell users might use crontab and the user is only known to crond if reloaded\n\t\t\tFileDir::safe_exec(escapeshellcmd(Settings::Get('system.crondreload')));\n\t\t\treturn;\n\t\t}\n\n\t\t// clear NSCD cache if using fcgid or fpm, #1570 - not needed for nss-extrausers\n\t\tif ((Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && Settings::Get('system.nssextrausers') == 0) {\n\t\t\t$false_val = false;\n\t\t\tFileDir::safe_exec('nscd -i passwd 1> /dev/null', $false_val, [\n\t\t\t\t'>'\n\t\t\t]);\n\t\t\tFileDir::safe_exec('nscd -i group 1> /dev/null', $false_val, [\n\t\t\t\t'>'\n\t\t\t]);\n\t\t\t// reload crond as shell users might use crontab and the user is only known to crond if reloaded\n\t\t\tFileDir::safe_exec(escapeshellcmd(Settings::Get('system.crondreload')));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/System/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cron/TaskId.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron;\n\nuse ReflectionClass;\n\nfinal class TaskId\n{\n\t/**\n\t * TYPE=1 MEANS TO REBUILD APACHE VHOSTS.CONF\n\t */\n\tconst REBUILD_VHOST = 1;\n\n\t/**\n\t * TYPE=2 MEANS TO CREATE A NEW HOME AND CHOWN\n\t */\n\tconst CREATE_HOME = 2;\n\n\t/**\n\t * TYPE=4 MEANS THAT SOMETHING IN THE DNS CONFIG HAS CHANGED.\n\t * REBUILD froxlor_bind.conf IF BIND IS ENABLED, UPDATE DKIM KEYS\n\t */\n\tconst REBUILD_DNS = 4;\n\n\t/**\n\t * TYPE=5 MEANS THAT A NEW FTP-ACCOUNT HAS BEEN CREATED, CREATE THE DIRECTORY\n\t */\n\tconst CREATE_FTP = 5;\n\n\t/**\n\t * TYPE=6 MEANS THAT A CUSTOMER HAS BEEN DELETED AND THAT WE HAVE TO REMOVE ITS FILES\n\t */\n\tconst DELETE_CUSTOMER_FILES = 6;\n\n\t/**\n\t * TYPE=7 Customer deleted an email account and wants the data to be deleted on the filesystem\n\t */\n\tconst DELETE_EMAIL_DATA = 7;\n\n\t/**\n\t * TYPE=8 Customer deleted a ftp account and wants the homedir to be deleted on the filesystem\n\t * refs #293\n\t */\n\tconst DELETE_FTP_DATA = 8;\n\n\t/**\n\t * TYPE=9 MEANS THAT SOMETHING ANTISPAM RELATED HAS CHANGED.\n\t * REBUILD froxlor_settings.conf IF ANTISPAM IS ENABLED\n\t */\n\tconst REBUILD_RSPAMD = 9;\n\n\t/**\n\t * TYPE=10 Set the filesystem - quota\n\t */\n\tconst CREATE_QUOTA = 10;\n\n\t/**\n\t * TYPE=11 domain has been deleted, remove from pdns database if used\n\t */\n\tconst DELETE_DOMAIN_PDNS = 11;\n\n\t/**\n\t * TYPE=12 domain has been deleted, remove from acme.sh/let's encrypt directory if used\n\t */\n\tconst DELETE_DOMAIN_SSL = 12;\n\n\t/**\n\t * TYPE=13 set configuration for selected services regarding the use of Let's Encrypt certificate\n\t */\n\tconst UPDATE_LE_SERVICES = 13;\n\n\t/**\n\t * TYPE=14 regenerate libnss users/groups\n\t */\n\tconst REBUILD_NSSUSERS = 14;\n\n\t/**\n\t * TYPE=20 CUSTUMER DATA DUMP\n\t */\n\tconst CREATE_CUSTOMER_DATADUMP = 20;\n\n\t/**\n\t * TYPE=99 REGENERATE CRON\n\t */\n\tconst REBUILD_CRON = 99;\n\n\t/**\n\t * Return if a cron task id is valid\n\t *\n\t * @param int|string $id cron task id (legacy string support)\n\t * @return boolean\n\t */\n\tpublic static function isValid($id)\n\t{\n\t\tstatic $reflContants;\n\t\tif (!is_numeric($id)) {\n\t\t\treturn false;\n\t\t}\n\t\t$numericid = (int)$id;\n\t\tif (!is_array($reflContants)) {\n\t\t\t$reflClass = new ReflectionClass(get_called_class());\n\t\t\t$reflContants = $reflClass->getConstants();\n\t\t}\n\t\treturn in_array($numericid, $reflContants, true);\n\t}\n\n\t/**\n\t * Get constant name by id\n\t *\n\t * @param int|string $id cron task id (legacy string support)\n\t * @return string|false constant name or false if not found\n\t */\n\tpublic static function convertToConstant($id)\n\t{\n\t\tstatic $reflContants;\n\t\tif (!is_numeric($id)) {\n\t\t\treturn false;\n\t\t}\n\t\t$numericid = (int)$id;\n\t\tif (!is_array($reflContants)) {\n\t\t\t$reflClass = new ReflectionClass(get_called_class());\n\t\t\t$reflContants = $reflClass->getConstants();\n\t\t}\n\t\treturn array_search($numericid, $reflContants, true);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Traffic/ReportsCron.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Traffic;\n\n/**\n * @author        Florian Lippert <flo@syscp.org> (2003-2009)\n * @author        Froxlor team <team@froxlor.org> (2010-)\n */\n\nuse Exception;\nuse Froxlor\\Cron\\FroxlorCron;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Mailer;\nuse Froxlor\\User;\nuse Froxlor\\Language;\nuse PDO;\n\nclass ReportsCron extends FroxlorCron\n{\n\n\tpublic static function run()\n\t{\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Web- and Traffic-usage reporting started...');\n\t\t$yesterday = time() - (60 * 60 * 24);\n\n\t\t/**\n\t\t * Initialize the mailingsystem\n\t\t */\n\t\t$mail = new Mailer(true);\n\n\t\t// set default language before anything else to\n\t\t// ensure that we can display messages\n\t\tLanguage::setLanguage(Settings::Get('panel.standardlanguage'));\n\n\t\tif ((int)Settings::Get('system.report_trafficmax') > 0) {\n\t\t\t// Warn the customers at xx% traffic-usage\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `c`.`customerid`, `c`.`loginname`, `c`.`customernumber`, `c`.`adminid`, `c`.`name`, `c`.`firstname`,\n\t\t\t\t`c`.`company`, `c`.`traffic`, `c`.`email`, `c`.`def_language`,\n\t\t\t\t`a`.`name` AS `adminname`, `a`.`email` AS `adminmail`,\n\t\t\t\t(SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`)\n\t\t\t\tFROM `\" . TABLE_PANEL_TRAFFIC . \"` `t`\n\t\t\t\tWHERE `t`.`customerid` = `c`.`customerid` AND `t`.`year` = :year AND `t`.`month` = :month\n\t\t\t\t) as `traffic_used`\n\t\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` AS `c`\n\t\t\t\tLEFT JOIN `\" . TABLE_PANEL_ADMINS . \"` AS `a`\n\t\t\t\tON `a`.`adminid` = `c`.`adminid` WHERE `c`.`reportsent` & 1 = 0\n\t\t\t\");\n\n\t\t\t$result_data = [\n\t\t\t\t'year' => date(\"Y\", $yesterday),\n\t\t\t\t'month' => date(\"m\", $yesterday)\n\t\t\t];\n\t\t\tDatabase::pexecute($result_stmt, $result_data);\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `reportsent` = `reportsent` + 1\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\");\n\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$row['traffic'] *= 1024;\n\t\t\t\t$row['traffic_used'] *= 1024;\n\t\t\t\tif (isset($row['traffic']) && $row['traffic'] > 0 && $row['traffic_used'] != null && (($row['traffic_used'] * 100) / $row['traffic']) >= (int)Settings::Get('system.report_trafficmax')) {\n\t\t\t\t\t$rep_userinfo = [\n\t\t\t\t\t\t'name' => $row['name'],\n\t\t\t\t\t\t'firstname' => $row['firstname'],\n\t\t\t\t\t\t'company' => $row['company'],\n\t\t\t\t\t\t'loginname' => $row['loginname'],\n\t\t\t\t\t\t'customernumber' => $row['customernumber']\n\t\t\t\t\t];\n\t\t\t\t\t$replace_arr = [\n\t\t\t\t\t\t'SALUTATION' => User::getCorrectUserSalutation($rep_userinfo),\n\t\t\t\t\t\t'NAME' => $rep_userinfo['name'],\n\t\t\t\t\t\t'FIRSTNAME' => $rep_userinfo['firstname'],\n\t\t\t\t\t\t'COMPANY' => $rep_userinfo['company'],\n\t\t\t\t\t\t'USERNAME' => $rep_userinfo['loginname'],\n\t\t\t\t\t\t'CUSTOMER_NO' => $rep_userinfo['customernumber'],\n\t\t\t\t\t\t'TRAFFIC' => PhpHelper::sizeReadable((int)$row['traffic'], null, 'bi'),\n\t\t\t\t\t\t'TRAFFICUSED' => PhpHelper::sizeReadable((int)$row['traffic_used'], null, 'bi'),\n\t\t\t\t\t\t'USAGE_PERCENT' => round(($row['traffic_used'] * 100) / $row['traffic'], 2),\n\t\t\t\t\t\t'MAX_PERCENT' => Settings::Get('system.report_trafficmax')\n\t\t\t\t\t];\n\n\t\t\t\t\t// set target user language\n\t\t\t\t\tLanguage::setLanguage($row['def_language']);\n\n\t\t\t\t\t// Get mail templates from database; the ones from 'admin' are fetched for fallback\n\t\t\t\t\t$result2_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `value` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\t\t\tAND `language` = :lang\n\t\t\t\t\t\tAND `templategroup` = 'mails' AND `varname` = :varname\n\t\t\t\t\t\");\n\t\t\t\t\t$result2_data = [\n\t\t\t\t\t\t'adminid' => $row['adminid'],\n\t\t\t\t\t\t'lang' => $row['def_language'],\n\t\t\t\t\t\t'varname' => 'trafficmaxpercent_subject'\n\t\t\t\t\t];\n\t\t\t\t\t$result2 = Database::pexecute_first($result2_stmt, $result2_data);\n\t\t\t\t\t$mail_subject = html_entity_decode(PhpHelper::replaceVariables((($result2 !== false && $result2['value'] != '') ? $result2['value'] : Language::getTranslation('mails.trafficmaxpercent.subject')), $replace_arr));\n\n\t\t\t\t\t$result2_data['varname'] = 'trafficmaxpercent_mailbody';\n\t\t\t\t\t$result2 = Database::pexecute_first($result2_stmt, $result2_data);\n\t\t\t\t\t$mail_body = html_entity_decode(PhpHelper::replaceVariables((($result2 !== false && $result2['value'] != '') ? $result2['value'] : Language::getTranslation('mails.trafficmaxpercent.mailbody')), $replace_arr));\n\n\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$mail->setFrom(Settings::Get('panel.adminmail'), $row['adminname']);\n\t\t\t\t\t\t$mail->clearReplyTos();\n\t\t\t\t\t\t$mail->addReplyTo($row['adminmail'], $row['adminname']);\n\t\t\t\t\t\t$mail->Subject = $mail_subject;\n\t\t\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t\t\t$mail->MsgHTML(nl2br($mail_body));\n\t\t\t\t\t\t$mail->AddAddress($row['email'], $row['firstname'] . ' ' . $row['name']);\n\t\t\t\t\t\t$mail->Send();\n\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'Error sending mail: ' . $mailerr_msg);\n\t\t\t\t\t\techo 'Error sending mail: ' . $mailerr_msg . \"\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t$mail->ClearAddresses();\n\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t'customerid' => $row['customerid']\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Warn the admins at xx% traffic-usage\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `a`.*,\n\t\t\t\t(SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`)\n\t\t\t\tFROM `\" . TABLE_PANEL_TRAFFIC_ADMINS . \"` `t`\n\t\t\t\tWHERE `t`.`adminid` = `a`.`adminid` AND `t`.`year` = :year AND `t`.`month` = :month\n\t\t\t\t) as `traffic_used_total`\n\t\t\t\tFROM `\" . TABLE_PANEL_ADMINS . \"` `a` WHERE `a`.`reportsent` & 1 = 0\n\t\t\t\");\n\n\t\t\t$result_data = [\n\t\t\t\t'year' => date(\"Y\", $yesterday),\n\t\t\t\t'month' => date(\"m\", $yesterday)\n\t\t\t];\n\t\t\tDatabase::pexecute($result_stmt, $result_data);\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `reportsent` = `reportsent` + 1\n\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\");\n\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$row['traffic'] *= 1024;\n\t\t\t\t$row['traffic_used_total'] *= 1024;\n\t\t\t\tif (isset($row['traffic']) && $row['traffic'] > 0 && (($row['traffic_used_total'] * 100) / ($row['traffic'])) >= (int)Settings::Get('system.report_trafficmax')) {\n\t\t\t\t\t$replace_arr = [\n\t\t\t\t\t\t'NAME' => $row['name'],\n\t\t\t\t\t\t'TRAFFIC' => PhpHelper::sizeReadable((int)$row['traffic'], null, 'bi'),\n\t\t\t\t\t\t'TRAFFICUSED' => PhpHelper::sizeReadable((int)$row['traffic_used_total'], null, 'bi'),\n\t\t\t\t\t\t'USAGE_PERCENT' => round(($row['traffic_used_total'] * 100) / $row['traffic'], 2),\n\t\t\t\t\t\t'MAX_PERCENT' => Settings::Get('system.report_trafficmax')\n\t\t\t\t\t];\n\n\t\t\t\t\t// set target user language\n\t\t\t\t\tLanguage::setLanguage($row['def_language']);\n\n\t\t\t\t\t// Get mail templates from database; the ones from 'admin' are fetched for fallback\n\t\t\t\t\t$result2_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `value` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\t\t\tAND `language` = :lang\n\t\t\t\t\t\tAND `templategroup` = 'mails' AND `varname` = :varname\n\t\t\t\t\t\");\n\t\t\t\t\t$result2_data = [\n\t\t\t\t\t\t'adminid' => $row['adminid'],\n\t\t\t\t\t\t'lang' => $row['def_language'],\n\t\t\t\t\t\t'varname' => 'trafficmaxpercent_subject'\n\t\t\t\t\t];\n\t\t\t\t\t$result2 = Database::pexecute_first($result2_stmt, $result2_data);\n\t\t\t\t\t$mail_subject = html_entity_decode(PhpHelper::replaceVariables((($result2 !== false && $result2['value'] != '') ? $result2['value'] : Language::getTranslation('mails.trafficmaxpercent.subject')), $replace_arr));\n\n\t\t\t\t\t$result2_data['varname'] = 'trafficmaxpercent_mailbody';\n\t\t\t\t\t$result2 = Database::pexecute_first($result2_stmt, $result2_data);\n\t\t\t\t\t$mail_body = html_entity_decode(PhpHelper::replaceVariables((($result2 !== false && $result2['value'] != '') ? $result2['value'] : Language::getTranslation('mails.trafficmaxpercent.mailbody')), $replace_arr));\n\n\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));\n\t\t\t\t\t\t$mail->Subject = $mail_subject;\n\t\t\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t\t\t$mail->MsgHTML(nl2br($mail_body));\n\t\t\t\t\t\t$mail->AddAddress($row['email'], $row['name']);\n\t\t\t\t\t\t$mail->Send();\n\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\t\techo \"Error sending mail: \" . $mailerr_msg . \"\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t$mail->ClearAddresses();\n\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t'adminid' => $row['adminid']\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\t// Another month, let's build our report\n\t\t\t\tif (date('d') == '01') {\n\t\t\t\t\t$mail_subject = 'Trafficreport ' . date(\"m/y\", $yesterday) . ' for ' . $row['name'];\n\t\t\t\t\t$mail_body = 'Trafficreport ' . date(\"m/y\", $yesterday) . ' for ' . $row['name'] . \"\\n\";\n\t\t\t\t\t$mail_body .= '---------------------------------------------------------------' . \"\\n\";\n\t\t\t\t\t$mail_body .= 'Loginname       Traffic used  (Percent) | Traffic available' . \"\\n\";\n\t\t\t\t\t$customers_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `c`.*,\n\t\t\t\t\t\t(SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`)\n\t\t\t\t\t\tFROM `\" . TABLE_PANEL_TRAFFIC . \"` `t`\n\t\t\t\t\t\tWHERE `t`.`customerid` = `c`.`customerid` AND `t`.`year` = :year AND `t`.`month` = :month\n\t\t\t\t\t\t) as `traffic_used_total`\n\t\t\t\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` `c` WHERE `c`.`adminid` = :adminid\n\t\t\t\t\t\");\n\t\t\t\t\t$customers_data = [\n\t\t\t\t\t\t'year' => date(\"Y\", $yesterday),\n\t\t\t\t\t\t'month' => date(\"m\", $yesterday),\n\t\t\t\t\t\t'adminid' => $row['adminid']\n\t\t\t\t\t];\n\t\t\t\t\tDatabase::pexecute($customers_stmt, $customers_data);\n\n\t\t\t\t\twhile ($customer = $customers_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t\t$customer['traffic'] *= 1024;\n\t\t\t\t\t\t$t = (int) $customer['traffic_used_total'] * 1024;\n\t\t\t\t\t\tif ($customer['traffic'] > 0) {\n\t\t\t\t\t\t\t$p = (($t * 100) / $customer['traffic']);\n\t\t\t\t\t\t\t$tg = (int) $customer['traffic'];\n\t\t\t\t\t\t\t$str = sprintf('%s  ( %00.1f %% )', PhpHelper::sizeReadable($t, null, 'bi'), $p);\n\t\t\t\t\t\t\t$mail_body .= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%s', PhpHelper::sizeReadable($tg, null, 'bi')) . \"\\n\";\n\t\t\t\t\t\t} elseif ($customer['traffic'] == 0) {\n\t\t\t\t\t\t\t$str = sprintf('%s  (   -   )', PhpHelper::sizeReadable($t, null, 'bi'));\n\t\t\t\t\t\t\t$mail_body .= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . '0' . \"\\n\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$str = sprintf('%s  (   -   )', PhpHelper::sizeReadable($t, null, 'bi'));\n\t\t\t\t\t\t\t$mail_body .= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . 'unlimited' . \"\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t$mail_body .= '---------------------------------------------------------------' . \"\\n\";\n\n\t\t\t\t\t$t = (int) $row['traffic_used_total'];\n\t\t\t\t\tif ($row['traffic'] > 0) {\n\t\t\t\t\t\t$p = (($t * 100) / $row['traffic']);\n\t\t\t\t\t\t$tg = (int) $row['traffic'];\n\t\t\t\t\t\t$str = sprintf('%s  ( %00.1f %% )', PhpHelper::sizeReadable($t, null, 'bi'), $p);\n\t\t\t\t\t\t$mail_body .= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%s', PhpHelper::sizeReadable($tg, null, 'bi')) . \"\\n\";\n\t\t\t\t\t} elseif ($row['traffic'] == 0) {\n\t\t\t\t\t\t$str = sprintf('%s  (   -   )', PhpHelper::sizeReadable($t, null, 'bi'));\n\t\t\t\t\t\t$mail_body .= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . '0' . \"\\n\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$str = sprintf('%s  (   -   )', PhpHelper::sizeReadable($t, null, 'bi'));\n\t\t\t\t\t\t$mail_body .= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . 'unlimited' . \"\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));\n\t\t\t\t\t\t$mail->Subject = $mail_subject;\n\t\t\t\t\t\t$mail->Body = $mail_body;\n\t\t\t\t\t\t$mail->MsgHTML(nl2br($mail_body));\n\t\t\t\t\t\t$mail->AddAddress($row['email'], $row['name']);\n\t\t\t\t\t\t$mail->Send();\n\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'Error sending mail: ' . $mailerr_msg);\n\t\t\t\t\t\techo 'Error sending mail: ' . $mailerr_msg . \"\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t$mail->ClearAddresses();\n\t\t\t\t}\n\t\t\t}\n\t\t} // trafficmax > 0\n\n\t\t// include diskspace-usage report, #466\n\t\tself::usageDiskspace();\n\n\t\t// Another month, reset the reportstatus\n\t\tif (date('d') == '01') {\n\t\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `reportsent` = '0';\");\n\t\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `reportsent` = '0';\");\n\t\t}\n\t}\n\n\tprivate static function usageDiskspace()\n\t{\n\t\tif ((int)Settings::Get('system.report_webmax') > 0) {\n\t\t\t/**\n\t\t\t * report about diskusage for customers\n\t\t\t */\n\t\t\t$result_stmt = Database::query(\"\n\t\t\t\tSELECT `c`.`customerid`, `c`.`loginname`, `c`.`customernumber`, `c`.`adminid`, `c`.`name`, `c`.`firstname`,\n\t\t\t\t`c`.`company`, `c`.`diskspace`, `c`.`diskspace_used`, `c`.`email`, `c`.`def_language`,\n\t\t\t\t`a`.`name` AS `adminname`, `a`.`email` AS `adminmail`\n\t\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` AS `c`\n\t\t\t    LEFT JOIN `\" . TABLE_PANEL_ADMINS . \"` AS `a`\n\t\t\t    ON `a`.`adminid` = `c`.`adminid`\n\t\t\t    WHERE `c`.`diskspace` > '0' AND `c`.`reportsent` & 2 = 0\n\t\t\t\");\n\n\t\t\t$mail = new Mailer(true);\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `reportsent` = `reportsent` + 2\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\");\n\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$row['diskspace'] *= 1024;\n\t\t\t\t$row['diskspace_used'] *= 1024;\n\t\t\t\tif (isset($row['diskspace']) && $row['diskspace_used'] != null && $row['diskspace_used'] > 0 && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax')) {\n\t\t\t\t\t$rep_userinfo = [\n\t\t\t\t\t\t'name' => $row['name'],\n\t\t\t\t\t\t'firstname' => $row['firstname'],\n\t\t\t\t\t\t'company' => $row['company'],\n\t\t\t\t\t\t'loginname' => $row['loginname'],\n\t\t\t\t\t\t'customernumber' => $row['customernumber']\n\t\t\t\t\t];\n\t\t\t\t\t$replace_arr = [\n\t\t\t\t\t\t'SALUTATION' => User::getCorrectUserSalutation($rep_userinfo),\n\t\t\t\t\t\t'NAME' => $rep_userinfo['name'],\n\t\t\t\t\t\t'FIRSTNAME' => $rep_userinfo['firstname'],\n\t\t\t\t\t\t'COMPANY' => $rep_userinfo['company'],\n\t\t\t\t\t\t'USERNAME' => $rep_userinfo['loginname'],\n\t\t\t\t\t\t'CUSTOMER_NO' => $rep_userinfo['customernumber'],\n\t\t\t\t\t\t'DISKAVAILABLE' => PhpHelper::sizeReadable((int)$row['diskspace'], null, 'bi'),\n\t\t\t\t\t\t'DISKUSED' => PhpHelper::sizeReadable((int)$row['diskspace_used'], null, 'bi'),\n\t\t\t\t\t\t'USAGE_PERCENT' => round(($row['diskspace_used'] * 100) / $row['diskspace'], 2),\n\t\t\t\t\t\t'MAX_PERCENT' => Settings::Get('system.report_webmax')\n\t\t\t\t\t];\n\n\t\t\t\t\t// set target user language\n\t\t\t\t\tLanguage::setLanguage($row['def_language']);\n\n\t\t\t\t\t// Get mail templates from database; the ones from 'admin' are fetched for fallback\n\t\t\t\t\t$result2_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `value` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\t\t\tAND `language` = :lang\n\t\t\t\t\t\tAND `templategroup` = 'mails' AND `varname` = :varname\n\t\t\t\t\t\");\n\t\t\t\t\t$result2_data = [\n\t\t\t\t\t\t'adminid' => $row['adminid'],\n\t\t\t\t\t\t'lang' => $row['def_language'],\n\t\t\t\t\t\t'varname' => 'diskmaxpercent_subject'\n\t\t\t\t\t];\n\t\t\t\t\t$result2 = Database::pexecute_first($result2_stmt, $result2_data);\n\t\t\t\t\t$mail_subject = html_entity_decode(PhpHelper::replaceVariables((($result2 !== false && $result2['value'] != '') ? $result2['value'] : Language::getTranslation('mails.diskmaxpercent.subject')), $replace_arr));\n\n\t\t\t\t\t$result2_data['varname'] = 'diskmaxpercent_mailbody';\n\t\t\t\t\t$result2 = Database::pexecute_first($result2_stmt, $result2_data);\n\t\t\t\t\t$mail_body = html_entity_decode(PhpHelper::replaceVariables((($result2 !== false && $result2['value'] != '') ? $result2['value'] : Language::getTranslation('mails.diskmaxpercent.mailbody')), $replace_arr));\n\n\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$mail->setFrom(Settings::Get('panel.adminmail'), $row['adminname']);\n\t\t\t\t\t\t$mail->clearReplyTos();\n\t\t\t\t\t\t$mail->addReplyTo($row['adminmail'], $row['adminname']);\n\t\t\t\t\t\t$mail->Subject = $mail_subject;\n\t\t\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t\t\t$mail->MsgHTML(nl2br($mail_body));\n\t\t\t\t\t\t$mail->AddAddress($row['email'], $row['name']);\n\t\t\t\t\t\tif (Settings::Get('system.report_web_bccadmin')) {\n\t\t\t\t\t\t\t$mail->addBCC(Settings::Get('panel.adminmail'), $row['adminname']);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$mail->Send();\n\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\t\techo \"Error sending mail: \" . $mailerr_msg . \"\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t$mail->ClearAddresses();\n\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t'customerid' => $row['customerid']\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * report about diskusage for admins/reseller\n\t\t\t */\n\t\t\t$result_stmt = Database::query(\"\n\t\t\t\tSELECT `a`.* FROM `\" . TABLE_PANEL_ADMINS . \"` `a` WHERE `a`.`reportsent` & 2 = 0\n\t\t\t\");\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `reportsent` = `reportsent` + 2\n\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\");\n\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$row['diskspace'] *= 1024;\n\t\t\t\t$row['diskspace_used'] *= 1024;\n\t\t\t\tif (isset($row['diskspace']) && $row['diskspace_used'] != null && $row['diskspace_used'] > 0 && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax')) {\n\t\t\t\t\t$replace_arr = [\n\t\t\t\t\t\t'NAME' => $row['name'],\n\t\t\t\t\t\t'DISKAVAILABLE' => PhpHelper::sizeReadable((int)$row['diskspace'], null, 'bi'),\n\t\t\t\t\t\t'DISKUSED' => PhpHelper::sizeReadable((int)$row['diskspace_used'], null, 'bi'),\n\t\t\t\t\t\t'USAGE_PERCENT' => ($row['diskspace_used'] * 100) / $row['diskspace'],\n\t\t\t\t\t\t'MAX_PERCENT' => Settings::Get('system.report_webmax')\n\t\t\t\t\t];\n\n\t\t\t\t\t// set target user language\n\t\t\t\t\tLanguage::setLanguage($row['def_language']);\n\n\t\t\t\t\t// Get mail templates from database; the ones from 'admin' are fetched for fallback\n\t\t\t\t\t$result2_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `value` FROM `\" . TABLE_PANEL_TEMPLATES . \"`\n\t\t\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\t\t\tAND `language` = :lang\n\t\t\t\t\t\tAND `templategroup` = 'mails' AND `varname` = :varname\n\t\t\t\t\t\");\n\t\t\t\t\t$result2_data = [\n\t\t\t\t\t\t'adminid' => $row['adminid'],\n\t\t\t\t\t\t'lang' => $row['def_language'],\n\t\t\t\t\t\t'varname' => 'diskmaxpercent_subject'\n\t\t\t\t\t];\n\t\t\t\t\t$result2 = Database::pexecute_first($result2_stmt, $result2_data);\n\t\t\t\t\t$mail_subject = html_entity_decode(PhpHelper::replaceVariables((($result2 !== false && $result2['value'] != '') ? $result2['value'] : Language::getTranslation('mails.diskmaxpercent.subject')), $replace_arr));\n\n\t\t\t\t\t$result2_data['varname'] = 'diskmaxpercent_mailbody';\n\t\t\t\t\t$result2 = Database::pexecute_first($result2_stmt, $result2_data);\n\t\t\t\t\t$mail_body = html_entity_decode(PhpHelper::replaceVariables((($result2 !== false && $result2['value'] != '') ? $result2['value'] : Language::getTranslation('mails.diskmaxpercent.mailbody')), $replace_arr));\n\n\t\t\t\t\t$_mailerror = false;\n\t\t\t\t\t$mailerr_msg = \"\";\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));\n\t\t\t\t\t\t$mail->Subject = $mail_subject;\n\t\t\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t\t\t$mail->MsgHTML(nl2br($mail_body));\n\t\t\t\t\t\t$mail->AddAddress($row['email'], $row['name']);\n\t\t\t\t\t\t$mail->Send();\n\t\t\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t\t\t$_mailerror = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ($_mailerror) {\n\t\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, \"Error sending mail: \" . $mailerr_msg);\n\t\t\t\t\t\techo \"Error sending mail: \" . $mailerr_msg . \"\\n\";\n\t\t\t\t\t}\n\n\t\t\t\t\t$mail->ClearAddresses();\n\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t'adminid' => $row['adminid']\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\t\t} // webmax > 0\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Traffic/TrafficCron.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Cron\\Traffic;\n\n/**\n * @author        Florian Lippert <flo@syscp.org> (2003-2009)\n * @author        Froxlor team <team@froxlor.org> (2010-)\n */\n\nuse Froxlor\\Cron\\Forkable;\nuse Froxlor\\Cron\\FroxlorCron;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Http\\Statistics;\nuse Froxlor\\MailLogParser;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass TrafficCron extends FroxlorCron\n{\n\tuse Forkable;\n\n\tpublic static function run()\n\t{\n\t\tself::runFork([self::class, 'handle'], [true]);\n\t}\n\n\tpublic static function handle()\n\t{\n\t\t/**\n\t\t * TRAFFIC AND DISKUSAGE MEASURE\n\t\t */\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Traffic run started...');\n\t\t$admin_traffic = [];\n\t\t$domainlist = [];\n\t\t$speciallogfile_domainlist = [];\n\t\t$result_domainlist_stmt = Database::query(\"\n\t\t\tSELECT `id`, `domain`, `customerid`, `parentdomainid`, `speciallogfile`\n\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `aliasdomain` IS NULL AND `email_only` <> '1';\n\t\t\");\n\n\t\twhile ($row_domainlist = $result_domainlist_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (!isset($domainlist[$row_domainlist['customerid']])) {\n\t\t\t\t$domainlist[$row_domainlist['customerid']] = [];\n\t\t\t}\n\n\t\t\t$domainlist[$row_domainlist['customerid']][$row_domainlist['id']] = $row_domainlist['domain'];\n\n\t\t\tif ($row_domainlist['parentdomainid'] == '0' && $row_domainlist['speciallogfile'] == '1') {\n\t\t\t\tif (!isset($speciallogfile_domainlist[$row_domainlist['customerid']])) {\n\t\t\t\t\t$speciallogfile_domainlist[$row_domainlist['customerid']] = [];\n\t\t\t\t}\n\t\t\t\t$speciallogfile_domainlist[$row_domainlist['customerid']][$row_domainlist['id']] = $row_domainlist['domain'];\n\t\t\t}\n\t\t}\n\n\t\t$mysqlusage_all = [];\n\t\t$databases_stmt = Database::query(\"SELECT * FROM \" . TABLE_PANEL_DATABASES . \" ORDER BY `dbserver`\");\n\t\t$last_dbserver = 0;\n\n\t\t$databases_list = [];\n\t\tDatabase::needRoot(true);\n\t\t$databases_list_result_stmt = Database::query(\"SHOW DATABASES\");\n\t\twhile ($databases_list_row = $databases_list_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$databases_list[] = strtolower($databases_list_row['Database']);\n\t\t}\n\n\t\twhile ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif ($last_dbserver != $row_database['dbserver']) {\n\t\t\t\tDatabase::needRoot(true, $row_database['dbserver'], true);\n\t\t\t\t$last_dbserver = $row_database['dbserver'];\n\n\t\t\t\t$databases_list = [];\n\t\t\t\t$databases_list_result_stmt = Database::query(\"SHOW DATABASES\");\n\t\t\t\twhile ($databases_list_row = $databases_list_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$databases_list[] = strtolower($databases_list_row['Database']);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (in_array(strtolower($row_database['databasename']), $databases_list)) {\n\t\t\t\t// sum up data_length and index_length\n\t\t\t\t$mysql_usage_result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT SUM(data_length + index_length) AS customerusage\n\t\t\t\t\tFROM information_schema.TABLES\n\t\t\t\t\tWHERE table_schema = :database\n\t\t\t\t\tGROUP BY table_schema;\n\t\t\t\t\");\n\t\t\t\t// get the result\n\t\t\t\t$mysql_usage_row = Database::pexecute_first($mysql_usage_result_stmt, [\n\t\t\t\t\t'database' => $row_database['databasename']\n\t\t\t\t]);\n\t\t\t\t// initialize counter for customer\n\t\t\t\tif (!isset($mysqlusage_all[$row_database['customerid']])) {\n\t\t\t\t\t$mysqlusage_all[$row_database['customerid']] = 0;\n\t\t\t\t}\n\t\t\t\t// sum up result\n\t\t\t\tif ($mysql_usage_row) {\n\t\t\t\t\t$mysqlusage_all[$row_database['customerid']] += floatval($mysql_usage_row['customerusage']);\n\t\t\t\t} else {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, \"Cannot get usage for database \" . $row_database['databasename'] . \".\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, \"Seems like the database \" . $row_database['databasename'] . \" had been removed manually.\");\n\t\t\t}\n\t\t}\n\n\t\tDatabase::needRoot(false);\n\n\t\t// We are using the file-system quota, this will speed up the diskusage - collection\n\t\tif (Settings::Get('system.diskquota_enabled')) {\n\t\t\t$usedquota = FileDir::getFilesystemQuota();\n\t\t}\n\n\t\t/**\n\t\t * MAIL-Traffic\n\t\t */\n\t\tif (Settings::Get(\"system.mailtraffic_enabled\")) {\n\t\t\t$mailTrafficCalc = new MailLogParser(Settings::Get(\"system.last_traffic_run\"));\n\t\t}\n\n\t\t$result_stmt = Database::query(\"SELECT * FROM `\" . TABLE_PANEL_CUSTOMERS . \"` ORDER BY `customerid` ASC\");\n\n\t\t$currentDate = date(\"Y-m-d\");\n\n\t\t$current_stamp = time();\n\t\t$current_year = date('Y', $current_stamp);\n\t\t$current_month = date('m', $current_stamp);\n\t\t// @todo locale?\n\t\t$current_month_short = date('M', $current_stamp);\n\t\t$current_day = date('d', $current_stamp);\n\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t/**\n\t\t\t * HTTP-Traffic\n\t\t\t */\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'http traffic for ' . $row['loginname'] . ' started...');\n\t\t\t$httptraffic = 0;\n\n\t\t\tif (isset($domainlist[$row['customerid']]) && is_array($domainlist[$row['customerid']]) && count($domainlist[$row['customerid']]) != 0) {\n\t\t\t\t// Examining which caption to use for default webalizer stats...\n\t\t\t\tif ($row['standardsubdomain'] != '0' && isset($domainlist[$row['customerid']][$row['standardsubdomain']])) {\n\t\t\t\t\t// ... of course we'd prefer to use the standardsubdomain ...\n\t\t\t\t\t$caption = $domainlist[$row['customerid']][$row['standardsubdomain']];\n\t\t\t\t} else {\n\t\t\t\t\t// ... but if there is no standardsubdomain, we have to use the loginname ...\n\t\t\t\t\t$caption = $row['loginname'];\n\n\t\t\t\t\t// ... which results in non-usable links to files in the stats, so let's have a look if we find a domain which is not speciallogfiledomain\n\t\t\t\t\tforeach ($domainlist[$row['customerid']] as $domainid => $domain) {\n\t\t\t\t\t\tif (!isset($speciallogfile_domainlist[$row['customerid']]) || !isset($speciallogfile_domainlist[$row['customerid']][$domainid])) {\n\t\t\t\t\t\t\t$caption = $domain;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$httptraffic = 0;\n\t\t\t\treset($domainlist[$row['customerid']]);\n\n\t\t\t\t$statsTool = Settings::Get('system.traffictool');\n\n\t\t\t\tif (isset($speciallogfile_domainlist[$row['customerid']]) && is_array($speciallogfile_domainlist[$row['customerid']]) && count($speciallogfile_domainlist[$row['customerid']]) != 0) {\n\t\t\t\t\treset($speciallogfile_domainlist[$row['customerid']]);\n\t\t\t\t\tif ($statsTool != 'awstats') {\n\t\t\t\t\t\tforeach ($speciallogfile_domainlist[$row['customerid']] as $domainid => $domain) {\n\t\t\t\t\t\t\tif ($statsTool == 'goaccess') {\n\t\t\t\t\t\t\t\t$httptraffic += floatval(self::callGoaccessGetTraffic($row['customerid'], $row['loginname'] . '-' . $domain, $row['documentroot'] . '/goaccess/' . $domain . '/', $domain, ['month' => $current_month_short, 'year' => $current_year], $current_stamp));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$httptraffic += floatval(self::callWebalizerGetTraffic($row['loginname'] . '-' . $domain, $row['documentroot'] . '/webalizer/' . $domain . '/', $domain, $domainlist[$row['customerid']]));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// kind of a keep-alive-call as this unsets the link which leads to a new connection to the database\n\t\t\t\t\t\t\tDatabase::needRoot();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treset($domainlist[$row['customerid']]);\n\n\t\t\t\t// callAwstatsGetTraffic is called ONLY HERE and\n\t\t\t\t// *not* also in the special-logfiles-loop, because the function\n\t\t\t\t// will iterate through all customer-domains and the awstats-configs\n\t\t\t\t// know the logfile-name, #246\n\t\t\t\tif ($statsTool == 'awstats') {\n\t\t\t\t\t$httptraffic += floatval(self::callAwstatsGetTraffic($row['customerid'], $row['documentroot'] . '/awstats/', $domainlist[$row['customerid']], $current_stamp));\n\t\t\t\t} elseif ($statsTool == 'goaccess') {\n\t\t\t\t\t$httptraffic += floatval(self::callGoaccessGetTraffic($row['customerid'], $row['loginname'], $row['documentroot'] . '/goaccess/', $caption, ['month' => $current_month_short, 'year' => $current_year], $current_stamp));\n\t\t\t\t} else {\n\t\t\t\t\t$httptraffic += floatval(self::callWebalizerGetTraffic($row['loginname'], $row['documentroot'] . '/webalizer/', $caption, $domainlist[$row['customerid']]));\n\t\t\t\t}\n\t\t\t\t// kind of a keep-alive-call as this unsets the link which leads to a new connection to the database\n\t\t\t\tDatabase::needRoot();\n\n\t\t\t\t// make the stuff readable for the customer, #258\n\t\t\t\tStatistics::makeChownWithNewStats($row);\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * FTP-Traffic\n\t\t\t */\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'ftp traffic for ' . $row['loginname'] . ' started...');\n\t\t\t$ftptraffic_stmt = Database::prepare(\"\n\t\t\t\tSELECT SUM(`up_bytes`) AS `up_bytes_sum`, SUM(`down_bytes`) AS `down_bytes_sum`\n\t\t\t\tFROM `\" . TABLE_FTP_USERS . \"` WHERE `customerid` = :customerid\n\t\t\t\");\n\t\t\t$ftptraffic = Database::pexecute_first($ftptraffic_stmt, [\n\t\t\t\t'customerid' => $row['customerid']\n\t\t\t]);\n\n\t\t\tif (!is_array($ftptraffic)) {\n\t\t\t\t$ftptraffic = [\n\t\t\t\t\t'up_bytes_sum' => 0,\n\t\t\t\t\t'down_bytes_sum' => 0\n\t\t\t\t];\n\t\t\t}\n\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_FTP_USERS . \"` SET `up_bytes` = '0', `down_bytes` = '0' WHERE `customerid` = :customerid\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'customerid' => $row['customerid']\n\t\t\t]);\n\n\t\t\t/**\n\t\t\t * Mail-Traffic\n\t\t\t */\n\t\t\t$mailtraffic = 0;\n\t\t\tif (Settings::Get(\"system.mailtraffic_enabled\")) {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'mail traffic usage for ' . $row['loginname'] . \" started...\");\n\n\t\t\t\t$domains_stmt = Database::prepare(\"SELECT domain FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE `customerid` = :cid\");\n\t\t\t\tDatabase::pexecute($domains_stmt, [\n\t\t\t\t\t\"cid\" => $row['customerid']\n\t\t\t\t]);\n\t\t\t\twhile ($domainRow = $domains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$domainMailTraffic = $mailTrafficCalc->getDomainTraffic($domainRow[\"domain\"]);\n\t\t\t\t\tif (!is_array($domainMailTraffic)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach ($domainMailTraffic as $dateTraffic => $dayTraffic) {\n\t\t\t\t\t\t$dayTraffic = floatval($dayTraffic / 1024);\n\n\t\t\t\t\t\t[$year, $month, $day] = explode(\"-\", $dateTraffic);\n\t\t\t\t\t\tif ($dateTraffic == $currentDate) {\n\t\t\t\t\t\t\t$mailtraffic = $dayTraffic;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Check if an entry for the given day exists\n\t\t\t\t\t\t\t$stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_TRAFFIC . \"`\n\t\t\t\t\t\t\t\tWHERE `customerid` = :cid\n\t\t\t\t\t\t\t\tAND `year` = :year\n\t\t\t\t\t\t\t\tAND `month` = :month\n\t\t\t\t\t\t\t\tAND `day` = :day\n\t\t\t\t\t\t\t\");\n\t\t\t\t\t\t\t$params = [\n\t\t\t\t\t\t\t\t\"cid\" => $row['customerid'],\n\t\t\t\t\t\t\t\t\"year\" => $year,\n\t\t\t\t\t\t\t\t\"month\" => $month,\n\t\t\t\t\t\t\t\t\"day\" => $day\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\tDatabase::pexecute($stmt, $params);\n\t\t\t\t\t\t\tif ($stmt->rowCount() > 0) {\n\t\t\t\t\t\t\t\t$updRow = $stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t\t\t\t\t\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_PANEL_TRAFFIC . \"` SET\n\t\t\t\t\t\t\t\t\t`mail` = :mail\n\t\t\t\t\t\t\t\t\tWHERE `id` = :id\n\t\t\t\t\t\t\t\t\");\n\t\t\t\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t\t\t\t\"mail\" => $updRow['mail'] + $dayTraffic,\n\t\t\t\t\t\t\t\t\t\"id\" => $updRow['id']\n\t\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Total Traffic\n\t\t\t */\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'total traffic for ' . $row['loginname'] . ' started');\n\t\t\t$current_traffic = [];\n\t\t\t$current_traffic['http'] = floatval($httptraffic);\n\t\t\t$current_traffic['ftp_up'] = floatval(($ftptraffic['up_bytes_sum'] / 1024));\n\t\t\t$current_traffic['ftp_down'] = floatval(($ftptraffic['down_bytes_sum'] / 1024));\n\t\t\t$current_traffic['mail'] = floatval($mailtraffic);\n\t\t\t$current_traffic['all'] = $current_traffic['http'] + $current_traffic['ftp_up'] + $current_traffic['ftp_down'] + $current_traffic['mail'];\n\n\t\t\t$ins_data = [\n\t\t\t\t'customerid' => $row['customerid'],\n\t\t\t\t'year' => $current_year,\n\t\t\t\t'month' => $current_month,\n\t\t\t\t'day' => $current_day,\n\t\t\t\t'stamp' => $current_stamp,\n\t\t\t\t'http' => $current_traffic['http'],\n\t\t\t\t'ftp_up' => $current_traffic['ftp_up'],\n\t\t\t\t'ftp_down' => $current_traffic['ftp_down'],\n\t\t\t\t'mail' => $current_traffic['mail']\n\t\t\t];\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_TRAFFIC . \"` SET\n\t\t\t\t`customerid` = :customerid,\n\t\t\t\t`year` = :year,\n\t\t\t\t`month` = :month,\n\t\t\t\t`day` = :day,\n\t\t\t\t`stamp` = :stamp,\n\t\t\t\t`http` = :http,\n\t\t\t\t`ftp_up` = :ftp_up,\n\t\t\t\t`ftp_down` = :ftp_down,\n\t\t\t\t`mail` = :mail\n\t\t\t\");\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\n\t\t\t$sum_month_traffic_stmt = Database::prepare(\"\n\t\t\t\tSELECT SUM(`http`) AS `http`, SUM(`ftp_up`) AS `ftp_up`, SUM(`ftp_down`) AS `ftp_down`, SUM(`mail`) AS `mail`\n\t\t\t\tFROM `\" . TABLE_PANEL_TRAFFIC . \"` WHERE `year` = :year AND `month` = :month AND `customerid` = :customerid\n\t\t\t\");\n\t\t\t$sum_month_traffic = Database::pexecute_first($sum_month_traffic_stmt, [\n\t\t\t\t'year' => $current_year,\n\t\t\t\t'month' => $current_month,\n\t\t\t\t'customerid' => $row['customerid']\n\t\t\t]);\n\t\t\t$sum_month_traffic['all'] = $sum_month_traffic['http'] + $sum_month_traffic['ftp_up'] + $sum_month_traffic['ftp_down'] + $sum_month_traffic['mail'];\n\n\t\t\tif (!isset($admin_traffic[$row['adminid']]) || !is_array($admin_traffic[$row['adminid']])) {\n\t\t\t\t$admin_traffic[$row['adminid']]['http'] = 0;\n\t\t\t\t$admin_traffic[$row['adminid']]['ftp_up'] = 0;\n\t\t\t\t$admin_traffic[$row['adminid']]['ftp_down'] = 0;\n\t\t\t\t$admin_traffic[$row['adminid']]['mail'] = 0;\n\t\t\t\t$admin_traffic[$row['adminid']]['all'] = 0;\n\t\t\t\t$admin_traffic[$row['adminid']]['sum_month'] = 0;\n\t\t\t}\n\n\t\t\t$admin_traffic[$row['adminid']]['http'] += $current_traffic['http'];\n\t\t\t$admin_traffic[$row['adminid']]['ftp_up'] += $current_traffic['ftp_up'];\n\t\t\t$admin_traffic[$row['adminid']]['ftp_down'] += $current_traffic['ftp_down'];\n\t\t\t$admin_traffic[$row['adminid']]['mail'] += $current_traffic['mail'];\n\t\t\t$admin_traffic[$row['adminid']]['all'] += $current_traffic['all'];\n\t\t\t$admin_traffic[$row['adminid']]['sum_month'] += $sum_month_traffic['all'];\n\n\t\t\t/**\n\t\t\t * WebSpace-Usage\n\t\t\t */\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'calculating webspace usage for ' . $row['loginname']);\n\t\t\t$webspaceusage = 0;\n\n\t\t\t// Using repquota, it's faster using this tool than using du traversing the complete directory\n\t\t\tif (Settings::Get('system.diskquota_enabled') && isset($usedquota[$row['guid']]['block']['used']) && $usedquota[$row['guid']]['block']['used'] >= 1) {\n\t\t\t\t// We may use the array we created earlier, the used diskspace is stored in [<guid>][block][used]\n\t\t\t\t$webspaceusage = floatval($usedquota[$row['guid']]['block']['used']);\n\t\t\t} else {\n\t\t\t\t// Use the old fashioned way with \"du\"\n\t\t\t\tif (file_exists($row['documentroot']) && is_dir($row['documentroot'])) {\n\t\t\t\t\t$back = FileDir::safe_exec('du -sk ' . escapeshellarg($row['documentroot']));\n\t\t\t\t\tforeach ($back as $backrow) {\n\t\t\t\t\t\t$webspaceusage = explode(' ', $backrow);\n\t\t\t\t\t}\n\n\t\t\t\t\t$webspaceusage = floatval($webspaceusage['0']);\n\t\t\t\t\tunset($back);\n\t\t\t\t} else {\n\t\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, 'documentroot ' . $row['documentroot'] . ' does not exist');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * MailSpace-Usage\n\t\t\t */\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'calculating mailspace usage for ' . $row['loginname']);\n\t\t\t$emailusage = 0;\n\n\t\t\t$maildir = FileDir::makeCorrectDir(Settings::Get('system.vmail_homedir') . $row['loginname']);\n\t\t\tif (file_exists($maildir) && is_dir($maildir)) {\n\t\t\t\t$back = FileDir::safe_exec('du -sk ' . escapeshellarg($maildir));\n\t\t\t\tforeach ($back as $backrow) {\n\t\t\t\t\t$emailusage = explode(' ', $backrow);\n\t\t\t\t}\n\n\t\t\t\t$emailusage = floatval($emailusage['0']);\n\t\t\t\tunset($back);\n\t\t\t} else {\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, 'maildir ' . $maildir . ' does not exist');\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * MySQLSpace-Usage\n\t\t\t */\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'calculating mysqlspace usage for ' . $row['loginname']);\n\t\t\t$mysqlusage = 0;\n\n\t\t\tif (isset($mysqlusage_all[$row['customerid']])) {\n\t\t\t\t$mysqlusage = floatval($mysqlusage_all[$row['customerid']] / 1024);\n\t\t\t}\n\n\t\t\t$current_diskspace = [];\n\t\t\t$current_diskspace['webspace'] = floatval($webspaceusage);\n\t\t\t$current_diskspace['mail'] = floatval($emailusage);\n\t\t\t$current_diskspace['mysql'] = floatval($mysqlusage);\n\t\t\t$current_diskspace['all'] = $current_diskspace['webspace'] + $current_diskspace['mail'] + $current_diskspace['mysql'];\n\n\t\t\t$ins_data = [\n\t\t\t\t'customerid' => $row['customerid'],\n\t\t\t\t'year' => $current_year,\n\t\t\t\t'month' => $current_month,\n\t\t\t\t'day' => $current_day,\n\t\t\t\t'stamp' => $current_stamp,\n\t\t\t\t'webspace' => $current_diskspace['webspace'],\n\t\t\t\t'mail' => $current_diskspace['mail'],\n\t\t\t\t'mysql' => $current_diskspace['mysql']\n\t\t\t];\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_DISKSPACE . \"` SET\n\t\t\t\t`customerid` = :customerid,\n\t\t\t\t`year` = :year,\n\t\t\t\t`month` = :month,\n\t\t\t\t`day` = :day,\n\t\t\t\t`stamp` = :stamp,\n\t\t\t\t`webspace` = :webspace,\n\t\t\t\t`mail` = :mail,\n\t\t\t\t`mysql` = :mysql\n\t\t\t\");\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\n\t\t\tif (!isset($admin_diskspace[$row['adminid']]) || !is_array($admin_diskspace[$row['adminid']])) {\n\t\t\t\t$admin_diskspace[$row['adminid']] = [];\n\t\t\t\t$admin_diskspace[$row['adminid']]['webspace'] = 0;\n\t\t\t\t$admin_diskspace[$row['adminid']]['mail'] = 0;\n\t\t\t\t$admin_diskspace[$row['adminid']]['mysql'] = 0;\n\t\t\t\t$admin_diskspace[$row['adminid']]['all'] = 0;\n\t\t\t}\n\n\t\t\t$admin_diskspace[$row['adminid']]['webspace'] += $current_diskspace['webspace'];\n\t\t\t$admin_diskspace[$row['adminid']]['mail'] += $current_diskspace['mail'];\n\t\t\t$admin_diskspace[$row['adminid']]['mysql'] += $current_diskspace['mysql'];\n\t\t\t$admin_diskspace[$row['adminid']]['all'] += $current_diskspace['all'];\n\n\t\t\t/**\n\t\t\t * Total Usage\n\t\t\t */\n\t\t\t$diskusage = floatval($webspaceusage + $emailusage + $mysqlusage);\n\n\t\t\t$upd_data = [\n\t\t\t\t'diskspace' => $current_diskspace['all'],\n\t\t\t\t'traffic' => $sum_month_traffic['all'],\n\t\t\t\t'customerid' => $row['customerid']\n\t\t\t];\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET\n\t\t\t\t`diskspace_used` = :diskspace,\n\t\t\t\t`traffic_used` = :traffic\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\n\t\t\t/**\n\t\t\t * Proftpd Quota\n\t\t\t */\n\t\t\t$upd_data = [\n\t\t\t\t'biu' => ($current_diskspace['all'] * 1024),\n\t\t\t\t'loginname' => $row['loginname'],\n\t\t\t\t'loginnamelike' => $row['loginname'] . Settings::Get('customer.ftpprefix') . \"%\"\n\t\t\t];\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_FTP_QUOTATALLIES . \"` SET\n\t\t\t\t`bytes_in_used` = :biu WHERE `name` = :loginname OR `name` LIKE :loginnamelike\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\n\t\t\t/**\n\t\t\t * Pureftpd Quota\n\t\t\t */\n\t\t\tif (Settings::Get('system.ftpserver') == \"pureftpd\") {\n\t\t\t\t$result_quota_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT homedir FROM `\" . TABLE_FTP_USERS . \"` WHERE customerid = :customerid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($result_quota_stmt, [\n\t\t\t\t\t'customerid' => $row['customerid']\n\t\t\t\t]);\n\n\t\t\t\t// get correct user\n\t\t\t\tif ((Settings::Get('system.mod_fcgid') == 1 || Settings::Get('phpfpm.enabled') == 1) && $row['deactivated'] == '0') {\n\t\t\t\t\t$user = $row['loginname'];\n\t\t\t\t\t$group = $row['loginname'];\n\t\t\t\t} else {\n\t\t\t\t\t$user = $row['guid'];\n\t\t\t\t\t$group = $row['guid'];\n\t\t\t\t}\n\n\t\t\t\twhile ($row_quota = $result_quota_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$quotafile = \"\" . $row_quota['homedir'] . \".ftpquota\";\n\t\t\t\t\t$fh = fopen($quotafile, 'w');\n\t\t\t\t\t$stringdata = \"0 \" . $current_diskspace['all'] * 1024 . \"\";\n\t\t\t\t\tfwrite($fh, $stringdata);\n\t\t\t\t\tfclose($fh);\n\t\t\t\t\tFileDir::safe_exec('chown ' . $user . ':' . $group . ' ' . escapeshellarg($quotafile) . '');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Admin Usage\n\t\t */\n\t\t$result_stmt = Database::query(\"SELECT `adminid` FROM `\" . TABLE_PANEL_ADMINS . \"` ORDER BY `adminid` ASC\");\n\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (isset($admin_traffic[$row['adminid']])) {\n\t\t\t\t$ins_data = [\n\t\t\t\t\t'adminid' => $row['adminid'],\n\t\t\t\t\t'year' => $current_year,\n\t\t\t\t\t'month' => $current_month,\n\t\t\t\t\t'day' => $current_day,\n\t\t\t\t\t'stamp' => $current_stamp,\n\t\t\t\t\t'http' => $admin_traffic[$row['adminid']]['http'],\n\t\t\t\t\t'ftp_up' => $admin_traffic[$row['adminid']]['ftp_up'],\n\t\t\t\t\t'ftp_down' => $admin_traffic[$row['adminid']]['ftp_down'],\n\t\t\t\t\t'mail' => $admin_traffic[$row['adminid']]['mail']\n\t\t\t\t];\n\t\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\tINSERT INTO `\" . TABLE_PANEL_TRAFFIC_ADMINS . \"` SET\n\t\t\t\t\t`adminid` = :adminid,\n\t\t\t\t\t`year` = :year,\n\t\t\t\t\t`month` = :month,\n\t\t\t\t\t`day` = :day,\n\t\t\t\t\t`stamp` = :stamp,\n\t\t\t\t\t`http` = :http,\n\t\t\t\t\t`ftp_up` = :ftp_up,\n\t\t\t\t\t`ftp_down` = :ftp_down,\n\t\t\t\t\t`mail` = :mail\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\n\t\t\t\t$upd_data = [\n\t\t\t\t\t'traffic' => $admin_traffic[$row['adminid']]['sum_month'],\n\t\t\t\t\t'adminid' => $row['adminid']\n\t\t\t\t];\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET\n\t\t\t\t\t`traffic_used` = :traffic\n\t\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\t\t\t}\n\n\t\t\tif (isset($admin_diskspace[$row['adminid']])) {\n\t\t\t\t$upd_data = [\n\t\t\t\t\t'diskspace' => $admin_diskspace[$row['adminid']]['all'],\n\t\t\t\t\t'adminid' => $row['adminid']\n\t\t\t\t];\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET\n\t\t\t\t\t`diskspace_used` = :diskspace\n\t\t\t\t\tWHERE `adminid` = :adminid\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\t\t\t}\n\t\t}\n\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `value` = UNIX_TIMESTAMP() WHERE `settinggroup` = 'system' AND `varname` = 'last_traffic_run'\");\n\t}\n\n\t/**\n\t * Run goaccess to create statistics and return used traffic since last run\n\t *\n\t * @param int $customerid\n\t * @param string $logfile Name of logfile\n\t * @param string $outputdir Place where stats should be build\n\t * @param string $caption Caption for webalizer output\n\t * @param array $monthyear_arr\n\t * @param int $current_stamp\n\t *\n\t * @return int Used traffic\n\t */\n\tprivate static function callGoaccessGetTraffic($customerid, $logfile, $outputdir, $caption, array $monthyear_arr = [], int $current_stamp = 0)\n\t{\n\t\t$returnval = 0;\n\n\t\t$logfile = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . $logfile . '-access.log');\n\t\tif (file_exists($logfile)) {\n\t\t\t$outputdir = FileDir::makeCorrectDir($outputdir);\n\t\t\tif (!file_exists($outputdir)) {\n\t\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($outputdir));\n\t\t\t}\n\n\t\t\tif (file_exists($outputdir . '.tmp.json')) {\n\t\t\t\t@unlink($outputdir . '.tmp.json');\n\t\t\t}\n\n\t\t\t// goaccess <1.4\n\t\t\t$keep_params = '--keep-db-files --load-from-disk';\n\t\t\t$res = FileDir::safe_exec('goaccess --version');\n\t\t\t$ver_str = array_shift($res);\n\t\t\t$cGoVer = substr($ver_str, strrpos($ver_str, \" \") + 1, -1);\n\t\t\tif (version_compare($cGoVer, '1.4', '>=')) {\n\t\t\t\t// at least 1.4\n\t\t\t\t$keep_params = '--persist --restore';\n\t\t\t}\n\n\t\t\t$format = (Settings::Get('system.logfiles_type') == '2' && Settings::Get('system.webserver') == 'apache2') ? 'VCOMBINED' : 'COMBINED';\n\t\t\t$monthyear = $monthyear_arr['month'] . '/' . $monthyear_arr['year'];\n\t\t\t$return_value = false;\n\t\t\tFileDir::safe_exec(\"grep '\" . $monthyear . \"' \" . escapeshellarg($logfile) . \" | goaccess \" . $keep_params . \" --db-path=\" . escapeshellarg($outputdir) . \" -o \" . escapeshellarg($outputdir . '.tmp.json') . \" -o \" . escapeshellarg($outputdir . 'index.html') . \" --html-report-title=\" . escapeshellarg($caption) . \" --log-format=\" . $format . \" --no-parsing-spinner --no-progress - \", $return_value, ['|']);\n\n\t\t\tif (file_exists($outputdir . '.tmp.json')) {\n\t\t\t\t// need jq here because of potentially LARGE json files\n\t\t\t\t$returnval = FileDir::safe_exec(\"jq -c '.general.bandwidth' \" . escapeshellarg($outputdir . '.tmp.json'));\n\t\t\t\t$returnval = array_shift($returnval);\n\t\t\t\t// return KB as the others two do\n\t\t\t\t$returnval = floatval($returnval / 1024);\n\t\t\t\t@unlink($outputdir . '.tmp.json');\n\t\t\t}\n\t\t}\n\n\t\tif ($returnval > 0) {\n\t\t\t/**\n\t\t\t * now, because this traffic is being saved daily, we have to\n\t\t\t * subtract the values from all the month's values to return\n\t\t\t * a sane value for our panel_traffic and to remain the whole stats\n\t\t\t * (awstats overwrites the customers .html stats-files)\n\t\t\t */\n\t\t\tif ($customerid !== false) {\n\t\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT SUM(`http`) as `trafficmonth` FROM `\" . TABLE_PANEL_TRAFFIC . \"`\n\t\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\t\tAND `year` = :year AND `month` = :month\n\t\t\t\t\");\n\t\t\t\t$result_data = [\n\t\t\t\t\t'customerid' => $customerid,\n\t\t\t\t\t'year' => date('Y', $current_stamp),\n\t\t\t\t\t'month' => date('m', $current_stamp)\n\t\t\t\t];\n\t\t\t\t$result = Database::pexecute_first($result_stmt, $result_data);\n\n\t\t\t\tif (is_array($result) && isset($result['trafficmonth'])) {\n\t\t\t\t\t$returnval = ($returnval - floatval($result['trafficmonth']));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $returnval;\n\t}\n\n\t/**\n\t * Function which make webalizer statistics and returns used traffic since last run\n\t *\n\t * @param string $logfile Name of logfile\n\t * @param string $outputdir Place where stats should be build\n\t * @param string $caption Caption for webalizer output\n\t * @param array $usersdomainlist\n\t *\n\t * @return float Used traffic\n\t */\n\tprivate static function callWebalizerGetTraffic($logfile, $outputdir, $caption, array $usersdomainlist = [])\n\t{\n\t\t$returnval = 0;\n\n\t\t$logfile = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . $logfile . '-access.log');\n\t\tif (file_exists($logfile)) {\n\t\t\t$domainargs = '';\n\t\t\tforeach ($usersdomainlist as $domain) {\n\t\t\t\t// hide referer\n\t\t\t\t$domainargs .= ' -r ' . escapeshellarg($domain);\n\t\t\t}\n\n\t\t\t$outputdir = FileDir::makeCorrectDir($outputdir);\n\t\t\tif (!file_exists($outputdir)) {\n\t\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($outputdir));\n\t\t\t}\n\n\t\t\tif (file_exists($outputdir . 'webalizer.hist.1')) {\n\t\t\t\t@unlink($outputdir . 'webalizer.hist.1');\n\t\t\t}\n\n\t\t\tif (file_exists($outputdir . 'webalizer.hist') && !file_exists($outputdir . 'webalizer.hist.1')) {\n\t\t\t\tFileDir::safe_exec('cp ' . escapeshellarg($outputdir . 'webalizer.hist') . ' ' . escapeshellarg($outputdir . 'webalizer.hist.1'));\n\t\t\t}\n\n\t\t\t$verbosity = '';\n\t\t\tif (Settings::Get('system.webalizer_quiet') == '1') {\n\t\t\t\t$verbosity = '-q';\n\t\t\t} elseif (Settings::Get('system.webalizer_quiet') == '2') {\n\t\t\t\t$verbosity = '-Q';\n\t\t\t}\n\n\t\t\t$we = '/usr/bin/webalizer';\n\n\t\t\t// FreeBSD uses other paths, #140\n\t\t\tif (!file_exists($we)) {\n\t\t\t\t$we = '/usr/local/bin/webalizer';\n\t\t\t}\n\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Running webalizer for domain '\" . $caption . \"'\");\n\t\t\tFileDir::safe_exec($we . ' ' . $verbosity . ' -p -o ' . escapeshellarg($outputdir) . ' -n ' . escapeshellarg($caption) . $domainargs . ' ' . escapeshellarg($logfile));\n\n\t\t\t/**\n\t\t\t * Format of webalizer.hist-files:\n\t\t\t * Month: $webalizer_hist_row['0']\n\t\t\t * Year: $webalizer_hist_row['1']\n\t\t\t * KB: $webalizer_hist_row['5']\n\t\t\t */\n\t\t\t$httptraffic = [];\n\t\t\t$webalizer_hist = @file_get_contents($outputdir . 'webalizer.hist');\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Gathering traffic information from '\" . $webalizer_hist . \"'\");\n\n\t\t\t$webalizer_hist_rows = explode(\"\\n\", $webalizer_hist);\n\t\t\tforeach ($webalizer_hist_rows as $webalizer_hist_row) {\n\t\t\t\tif ($webalizer_hist_row != '') {\n\t\t\t\t\t$webalizer_hist_row = explode(' ', $webalizer_hist_row);\n\n\t\t\t\t\tif (isset($webalizer_hist_row['0']) && isset($webalizer_hist_row['1']) && isset($webalizer_hist_row['5'])) {\n\t\t\t\t\t\t$month = intval($webalizer_hist_row['0']);\n\t\t\t\t\t\t$year = intval($webalizer_hist_row['1']);\n\t\t\t\t\t\t$traffic = floatval($webalizer_hist_row['5']);\n\n\t\t\t\t\t\tif (!isset($httptraffic[$year])) {\n\t\t\t\t\t\t\t$httptraffic[$year] = [];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$httptraffic[$year][$month] = $traffic;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treset($httptraffic);\n\t\t\t$httptrafficlast = [];\n\t\t\t$webalizer_lasthist = @file_get_contents($outputdir . 'webalizer.hist.1');\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Gathering traffic information from '\" . $webalizer_lasthist . \"'\");\n\n\t\t\t$webalizer_lasthist_rows = explode(\"\\n\", $webalizer_lasthist);\n\t\t\tforeach ($webalizer_lasthist_rows as $webalizer_lasthist_row) {\n\t\t\t\tif ($webalizer_lasthist_row != '') {\n\t\t\t\t\t$webalizer_lasthist_row = explode(' ', $webalizer_lasthist_row);\n\n\t\t\t\t\tif (isset($webalizer_lasthist_row['0']) && isset($webalizer_lasthist_row['1']) && isset($webalizer_lasthist_row['5'])) {\n\t\t\t\t\t\t$month = intval($webalizer_lasthist_row['0']);\n\t\t\t\t\t\t$year = intval($webalizer_lasthist_row['1']);\n\t\t\t\t\t\t$traffic = floatval($webalizer_lasthist_row['5']);\n\n\t\t\t\t\t\tif (!isset($httptrafficlast[$year])) {\n\t\t\t\t\t\t\t$httptrafficlast[$year] = [];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$httptrafficlast[$year][$month] = $traffic;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treset($httptrafficlast);\n\t\t\tforeach ($httptraffic as $year => $months) {\n\t\t\t\tforeach ($months as $month => $traffic) {\n\t\t\t\t\tif (!isset($httptrafficlast[$year][$month])) {\n\t\t\t\t\t\t$returnval += $traffic;\n\t\t\t\t\t} elseif ($httptrafficlast[$year][$month] < $traffic) {\n\t\t\t\t\t\t$returnval += ($traffic - $httptrafficlast[$year][$month]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn floatval($returnval);\n\t}\n\n\tprivate static function callAwstatsGetTraffic($customerid, $outputdir, $usersdomainlist, $current_stamp)\n\t{\n\t\t$returnval = 0;\n\n\t\tforeach ($usersdomainlist as $singledomain) {\n\t\t\t// as we check for the config-model awstats will only parse\n\t\t\t// 'real' domains and no subdomains which are aliases in the\n\t\t\t// model-config-file.\n\t\t\t$returnval += self::awstatsDoSingleDomain($singledomain, $outputdir, $current_stamp);\n\t\t\t// kind of a keep-alive-call as this unsets the link which leads to a new connection to the database\n\t\t\tDatabase::needRoot();\n\t\t}\n\n\t\t/**\n\t\t * as of #124, awstats traffic is saved in bytes instead\n\t\t * of kilobytes (like webalizer does)\n\t\t */\n\t\t$returnval = floatval($returnval / 1024);\n\n\t\t/**\n\t\t * now, because this traffic is being saved daily, we have to\n\t\t * subtract the values from all the month's values to return\n\t\t * a sane value for our panel_traffic and to remain the whole stats\n\t\t * (awstats overwrites the customers .html stats-files)\n\t\t */\n\t\tif ($customerid !== false) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT SUM(`http`) as `trafficmonth` FROM `\" . TABLE_PANEL_TRAFFIC . \"`\n\t\t\t\tWHERE `customerid` = :customerid\n\t\t\t\tAND `year` = :year AND `month` = :month\n\t\t\t\");\n\t\t\t$result_data = [\n\t\t\t\t'customerid' => $customerid,\n\t\t\t\t'year' => date('Y', $current_stamp),\n\t\t\t\t'month' => date('m', $current_stamp)\n\t\t\t];\n\t\t\t$result = Database::pexecute_first($result_stmt, $result_data);\n\n\t\t\tif (is_array($result) && isset($result['trafficmonth'])) {\n\t\t\t\t$returnval = ($returnval - floatval($result['trafficmonth']));\n\t\t\t}\n\t\t}\n\n\t\treturn floatval($returnval);\n\t}\n\n\tprivate static function awstatsDoSingleDomain($domain, $outputdir, $current_stamp)\n\t{\n\t\t$returnval = 0;\n\n\t\t$domainconfig = FileDir::makeCorrectFile(Settings::Get('system.awstats_conf') . '/awstats.' . $domain . '.conf');\n\n\t\tif (file_exists($domainconfig)) {\n\t\t\t$outputdir = FileDir::makeCorrectDir($outputdir . '/' . $domain);\n\t\t\t$staticOutputdir = FileDir::makeCorrectDir($outputdir . '/' . date('Y') . '-' . date('m'));\n\n\t\t\tif (!is_dir($staticOutputdir)) {\n\t\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($staticOutputdir));\n\t\t\t}\n\n\t\t\t// check for correct path of awstats_buildstaticpages.pl\n\t\t\t$awbsp = FileDir::makeCorrectFile(Settings::Get('system.awstats_path') . '/awstats_buildstaticpages.pl');\n\t\t\t$awprog = FileDir::makeCorrectFile(Settings::Get('system.awstats_awstatspath') . '/awstats.pl');\n\n\t\t\tif (!file_exists($awbsp)) {\n\t\t\t\techo \"WANRING: Necessary awstats_buildstaticpages.pl script could not be found, no traffic is being calculated and no stats are generated. Please check your AWStats-Path setting\";\n\t\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, \"Necessary awstats_buildstaticpages.pl script could not be found, no traffic is being calculated and no stats are generated. Please check your AWStats-Path setting\");\n\t\t\t\texit();\n\t\t\t}\n\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Running awstats_buildstaticpages.pl for domain '\" . $domain . \"' (Output: '\" . $staticOutputdir . \"')\");\n\t\t\tFileDir::safe_exec($awbsp . ' -awstatsprog=' . escapeshellarg($awprog) . ' -update -month=' . date('m', $current_stamp) . ' -year=' . date('Y', $current_stamp) . ' -config=' . $domain . ' -dir=' . escapeshellarg($staticOutputdir));\n\n\t\t\t// update our awstats index files\n\t\t\tself::awstatsGenerateIndex($domain, $outputdir);\n\n\t\t\t// the default selection is 'current',\n\t\t\t// so link the latest dir to it\n\t\t\t$new_current = FileDir::makeCorrectFile($outputdir . '/current');\n\t\t\tFileDir::safe_exec('ln -fTs ' . escapeshellarg($staticOutputdir) . ' ' . escapeshellarg($new_current));\n\n\t\t\t// statistics file looks like: 'awstats[month][year].[domain].txt'\n\t\t\t$file = FileDir::makeCorrectFile($outputdir . '/awstats' . date('mY', time()) . '.' . $domain . '.txt');\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, \"Gathering traffic information from '\" . $file . \"'\");\n\n\t\t\tif (file_exists($file)) {\n\t\t\t\t$content = @file_get_contents($file);\n\t\t\t\tif ($content !== false) {\n\t\t\t\t\t$content_array = explode(\"\\n\", $content);\n\n\t\t\t\t\t$count_bdw = false;\n\t\t\t\t\tforeach ($content_array as $line) {\n\t\t\t\t\t\t// skip empty lines and comments\n\t\t\t\t\t\tif (trim($line) == '' || substr(trim($line), 0, 1) == '#') {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$parts = explode(' ', $line);\n\n\t\t\t\t\t\tif (isset($parts[0]) && strtoupper($parts[0]) == 'BEGIN_DOMAIN') {\n\t\t\t\t\t\t\t$count_bdw = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ($count_bdw) {\n\t\t\t\t\t\t\tif (isset($parts[0]) && strtoupper($parts[0]) == 'END_DOMAIN') {\n\t\t\t\t\t\t\t\t$count_bdw = false;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t} elseif (isset($parts[3])) {\n\t\t\t\t\t\t\t\t$returnval += floatval($parts[3]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $returnval;\n\t}\n\n\tprivate static function awstatsGenerateIndex($domain, $outputdir)\n\t{\n\t\t// Generation header\n\t\t$header = \"<!-- GENERATED BY FROXLOR -->\\n\";\n\n\t\t// Looking for {year}-{month} directories\n\t\t$entries = [];\n\t\tforeach (scandir($outputdir) as $a) {\n\t\t\tif (is_dir(FileDir::makeCorrectDir($outputdir . '/' . $a)) && preg_match('/^[0-9]{4}-[0-9]{2}$/', $a)) {\n\t\t\t\tarray_push($entries, '<option value=\"' . $a . '\">' . $a . '</option>');\n\t\t\t}\n\t\t}\n\n\t\t// These are the variables we will replace\n\t\t$regex = [\n\t\t\t'/\\{SITE_DOMAIN\\}/',\n\t\t\t'/\\{SELECT_ENTRIES\\}/'\n\t\t];\n\n\t\t$replace = [\n\t\t\t$domain,\n\t\t\timplode($entries)\n\t\t];\n\n\t\t// File names\n\t\t$index_file = Froxlor::getInstallDir() . '/templates/misc/awstats/index.html';\n\t\t$index_file = FileDir::makeCorrectFile($index_file);\n\t\t$nav_file = Froxlor::getInstallDir() . '/templates/misc/awstats/nav.html';\n\t\t$nav_file = FileDir::makeCorrectFile($nav_file);\n\n\t\t// Write the index file\n\t\t// 'index.html' used to be a symlink (ignore errors in case this is the first run and no index.html exists yet)\n\t\t@unlink(FileDir::makeCorrectFile($outputdir . '/' . 'index.html'));\n\n\t\t$awstats_index_file = fopen(FileDir::makeCorrectFile($outputdir . '/' . 'index.html'), 'w');\n\t\t$awstats_index_tpl = fopen($index_file, 'r');\n\n\t\t// Write the header\n\t\tfwrite($awstats_index_file, $header);\n\n\t\t// Write the configuration file\n\t\twhile (($line = fgets($awstats_index_tpl, 4096)) !== false) {\n\t\t\tif (!preg_match('/^#/', $line) && trim($line) != '') {\n\t\t\t\tfwrite($awstats_index_file, preg_replace($regex, $replace, $line));\n\t\t\t}\n\t\t}\n\t\tfclose($awstats_index_file);\n\t\tfclose($awstats_index_tpl);\n\n\t\t// Write the nav file\n\t\t$awstats_nav_file = fopen(FileDir::makeCorrectFile($outputdir . '/' . 'nav.html'), 'w');\n\t\t$awstats_nav_tpl = fopen($nav_file, 'r');\n\n\t\t// Write the header\n\t\tfwrite($awstats_nav_file, $header);\n\n\t\t// Write the configuration file\n\t\twhile (($line = fgets($awstats_nav_tpl, 4096)) !== false) {\n\t\t\tif (!preg_match('/^#/', $line) && trim($line) != '') {\n\t\t\t\tfwrite($awstats_nav_file, preg_replace($regex, $replace, $line));\n\t\t\t}\n\t\t}\n\t\tfclose($awstats_nav_file);\n\t\tfclose($awstats_nav_tpl);\n\n\t\treturn;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Cron/Traffic/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Cron/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/CurrentUser.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Exception;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\SubDomains;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\Response;\nuse RobThree\\Auth\\TwoFactorAuthException;\n\n/**\n * Class to manage the current user / session\n */\nclass CurrentUser\n{\n\n\t/**\n\t * returns whether there is an active session\n\t *\n\t * @return bool\n\t */\n\tpublic static function hasSession(): bool\n\t{\n\t\treturn !empty($_SESSION) && !empty($_SESSION['userinfo']);\n\t}\n\n\t/**\n\t * set userinfo field in session\n\t *\n\t * @param string $index\n\t * @param mixed $data\n\t *\n\t * @return boolean\n\t */\n\tpublic static function setField(string $index, $data): bool\n\t{\n\t\t$_SESSION['userinfo'][$index] = $data;\n\t\treturn true;\n\t}\n\n\t/**\n\t * re-read in the user data if a valid session exists\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic static function reReadUserData(): bool\n\t{\n\t\t$table = self::isAdmin() ? TABLE_PANEL_ADMINS : TABLE_PANEL_CUSTOMERS;\n\t\t$userinfo_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . $table . \"` WHERE `loginname`= :loginname AND `deactivated` = '0'\n\t\t\");\n\t\t$userinfo = Database::pexecute_first($userinfo_stmt, [\n\t\t\t\"loginname\" => self::getField('loginname')\n\t\t]);\n\t\tif ($userinfo) {\n\t\t\t// don't just set the data, we need to merge with current data\n\t\t\t// array_merge is a right-reduction - value existing in getData() will be overwritten with $userinfo,\n\t\t\t// other than the union-operator (+) which would keep the values already existing from getData()\n\t\t\t$newuserinfo = array_merge(self::getData(), $userinfo);\n\t\t\tself::setData($newuserinfo);\n\t\t\treturn true;\n\t\t}\n\t\t// unset / logout\n\t\tunset($_SESSION['userinfo']);\n\t\tself::setData([]);\n\t\treturn false;\n\t}\n\n\t/**\n\t * returns whether user has an adminsession\n\t *\n\t * @return bool\n\t */\n\tpublic static function isAdmin(): bool\n\t{\n\t\treturn (self::getField('adminsession') == 1 && self::getField('adminid') > 0 && empty(self::getField('customerid')));\n\t}\n\n\t/**\n\t * return content of a given field from userinfo-array\n\t *\n\t * @param string $index\n\t *\n\t * @return string|array\n\t */\n\tpublic static function getField(string $index)\n\t{\n\t\treturn $_SESSION['userinfo'][$index] ?? \"\";\n\t}\n\n\t/**\n\t * Return userinfo array\n\t *\n\t * @return array\n\t */\n\tpublic static function getData(): array\n\t{\n\t\treturn $_SESSION['userinfo'] ?? [];\n\t}\n\n\t/**\n\t * set the userinfo data to the session\n\t *\n\t * @param array $data\n\t */\n\tpublic static function setData(array $data = []): void\n\t{\n\t\t$_SESSION['userinfo'] = $data;\n\t}\n\n\t/**\n\t * @param string $resource\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic static function canAddResource(string $resource): bool\n\t{\n\t\t$addition = true;\n\t\t// special cases\n\t\tif ($resource == 'emails') {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT COUNT(`id`) as emaildomains\n\t\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tWHERE `customerid`= :cid AND `isemaildomain` = '1' AND `deactivated` = '0'\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t\"cid\" => $_SESSION['userinfo']['customerid']\n\t\t\t]);\n\t\t\t$addition = $result['emaildomains'] != 0;\n\t\t} elseif ($resource == 'subdomains') {\n\t\t\tif (Settings::IsInList('panel.customer_hide_options', 'domains')) {\n\t\t\t\t$addition = false;\n\t\t\t} else {\n\t\t\t\t$parentDomainCollection = (new Collection(\n\t\t\t\t\tSubDomains::class,\n\t\t\t\t\t$_SESSION['userinfo'],\n\t\t\t\t\t['sql_search' => [\n\t\t\t\t\t\t'd.parentdomainid' => 0,\n\t\t\t\t\t\t'd.deactivated' => 0,\n\t\t\t\t\t\t'd.id' => ['op' => '<>', 'value' => $_SESSION['userinfo']['standardsubdomain']]\n\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t));\n\t\t\t\t$addition = $parentDomainCollection->count() != 0;\n\t\t\t}\n\t\t} elseif ($resource == 'domains') {\n\t\t\t$customerCollection = (new Collection(Customers::class, $_SESSION['userinfo']));\n\t\t\t$addition = $customerCollection->count() != 0;\n\t\t}\n\n\t\treturn ($_SESSION['userinfo'][$resource . '_used'] < $_SESSION['userinfo'][$resource] || $_SESSION['userinfo'][$resource] == '-1') && $addition;\n\t}\n\n\t/**\n\t * @throws TwoFactorAuthException\n\t */\n\tpublic static function sendOtpEmail()\n\t{\n\t\tglobal $mail;\n\n\t\tif (self::getField('type_2fa') == 1) {\n\t\t\t// generate code\n\t\t\t$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));\n\t\t\t$secret = $tfa->createSecret();\n\t\t\t$code = $tfa->getCode($secret);\n\t\t\t// set code for user\n\t\t\t$table = TABLE_PANEL_CUSTOMERS;\n\t\t\t$uid = 'customerid';\n\t\t\tif (self::isAdmin()) {\n\t\t\t\t$table = TABLE_PANEL_ADMINS;\n\t\t\t\t$uid = 'adminid';\n\t\t\t}\n\t\t\t$stmt = Database::prepare(\"UPDATE $table SET `data_2fa` = :d2fa WHERE `$uid` = :uid\");\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\"d2fa\" => $secret,\n\t\t\t\t\"uid\" => self::getField($uid)\n\t\t\t]);\n\t\t\t// build up & send email\n\t\t\t$_mailerror = false;\n\t\t\t$mailerr_msg = \"\";\n\t\t\t$replace_arr = [\n\t\t\t\t'CODE' => $code\n\t\t\t];\n\t\t\t$mail_body = html_entity_decode(PhpHelper::replaceVariables(lng('mails.2fa.mailbody'), $replace_arr));\n\n\t\t\ttry {\n\t\t\t\t$mail->Subject = lng('mails.2fa.subject');\n\t\t\t\t$mail->AltBody = $mail_body;\n\t\t\t\t$mail->MsgHTML(str_replace(\"\\n\", \"<br />\", $mail_body));\n\t\t\t\t$mail->AddAddress(self::getField('email'), User::getCorrectUserSalutation(self::getData()));\n\t\t\t\t$mail->Send();\n\t\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t\t$_mailerror = true;\n\t\t\t} catch (Exception $e) {\n\t\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t\t$_mailerror = true;\n\t\t\t}\n\n\t\t\tif ($_mailerror) {\n\t\t\t\t$rstlog = FroxlorLogger::getInstanceOf([\n\t\t\t\t\t'loginname' => '2fa code-sending'\n\t\t\t\t]);\n\t\t\t\t$rstlog->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, \"Error sending mail: \" . $mailerr_msg);\n\t\t\t\tResponse::redirectTo('index.php', [\n\t\t\t\t\t'showmessage' => '4',\n\t\t\t\t\t'customermail' => self::getField('email')\n\t\t\t\t]);\n\t\t\t\texit();\n\t\t\t}\n\n\t\t\t$mail->ClearAddresses();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Customer/Customer.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Customer;\n\nuse Froxlor\\Database\\Database;\nuse PDO;\n\nclass Customer\n{\n\n\t/**\n\t * Get value of a specific field from a given customer\n\t *\n\t * @param int $customerid\n\t * @param string $varname\n\t * @return false|mixed\n\t * @throws \\Exception\n\t */\n\tpublic static function getCustomerDetail(int $customerid, string $varname)\n\t{\n\t\t$customer_stmt = Database::prepare(\"\n\t\t\tSELECT `\" . $varname . \"` FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `customerid` = :customerid\n\t\t\");\n\t\t$customer = Database::pexecute_first($customer_stmt, [\n\t\t\t'customerid' => $customerid\n\t\t]);\n\n\t\tif (isset($customer[$varname])) {\n\t\t\treturn $customer[$varname];\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * returns the loginname of a customer by given uid\n\t *\n\t * @param int $uid uid of customer\n\t *\n\t * @return string customers loginname\n\t * @throws \\Exception\n\t */\n\tpublic static function getLoginNameByUid(int $uid)\n\t{\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT `loginname` FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `guid` = :guid\n\t\t\");\n\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t'guid' => $uid\n\t\t]);\n\n\t\tif ($result && isset($result['loginname'])) {\n\t\t\treturn $result['loginname'];\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Function customerHasPerlEnabled\n\t *\n\t * returns true or false whether perl is\n\t * enabled for the given customer\n\t *\n\t * @param int $cid customer-id\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic static function customerHasPerlEnabled(int $cid = 0)\n\t{\n\t\tif ($cid > 0) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `perlenabled` FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `customerid` = :cid\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t'cid' => $cid\n\t\t\t]);\n\n\t\t\tif ($result && isset($result['perlenabled'])) {\n\t\t\t\treturn (bool)$result['perlenabled'];\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Customer/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Database/Database.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Database;\n\nuse Exception;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\nuse PDO;\nuse PDOException;\nuse PDOStatement;\n\n/**\n * Class Database\n *\n * Wrapper-class for PHP-PDO\n *\n * @method static PDOStatement prepare($statement, array $driver_options = null) Prepares a statement for execution\n *         and returns a statement object\n * @method static PDOStatement query ($statement) Executes an SQL statement, returning a result set as a PDOStatement\n *         object\n * @method static string lastInsertId ($name = null) Returns the ID of the last inserted row or sequence value\n * @method static string quote ($string, $parameter_type = null) Quotes a string for use in a query.\n * @method static mixed getAttribute(int $attribute) Retrieve a database connection attribute\n */\nclass Database\n{\n\n\t/**\n\t * current database link\n\t *\n\t * @var object\n\t */\n\tprivate static $link = null;\n\n\t/**\n\t * indicator whether to use root-connection or not\n\t */\n\tprivate static bool $needroot = false;\n\n\t/**\n\t * indicator which database-server we're on (not really used)\n\t */\n\tprivate static int $dbserver = 0;\n\n\t/**\n\t * used database-name\n\t */\n\tprivate static ?string $dbname = null;\n\n\t/**\n\t * sql-access data\n\t */\n\tprivate static bool $needsqldata = false;\n\n\tprivate static $sqldata = null;\n\n\tprivate static bool $need_dbname = true;\n\n\t/**\n\t * Wrapper for PDOStatement::execute, so we can catch the PDOException\n\t * and display the error nicely on the panel - also fetches the\n\t * result from the statement and returns the resulting array\n\t *\n\t * @param PDOStatement $stmt\n\t * @param array|null $params\n\t *            (optional)\n\t * @param bool $showerror\n\t *            suppress error display (default true)\n\t * @param bool $json_response\n\t *\n\t * @return mixed\n\t * @throws Exception\n\t */\n\tpublic static function pexecute_first(PDOStatement &$stmt, $params = null, bool $showerror = true, bool $json_response = false)\n\t{\n\t\tself::pexecute($stmt, $params, $showerror, $json_response);\n\t\treturn $stmt->fetch(PDO::FETCH_ASSOC);\n\t}\n\n\t/**\n\t * Wrapper for PDOStatement::execute so we can catch the PDOException\n\t * and display the error nicely on the panel\n\t *\n\t * @param PDOStatement $stmt\n\t * @param array|null $params\n\t *            (optional)\n\t * @param bool $showerror\n\t *            suppress error display (default true)\n\t * @param bool $json_response\n\t *\n\t * @throws Exception\n\t */\n\tpublic static function pexecute(PDOStatement &$stmt, $params = null, bool $showerror = true, bool $json_response = false)\n\t{\n\t\ttry {\n\t\t\t$stmt->execute($params);\n\t\t} catch (PDOException $e) {\n\t\t\tself::showerror($e, $showerror, $json_response, $stmt);\n\t\t}\n\t}\n\n\t/**\n\t * display a nice error if it occurs and log everything\n\t *\n\t * @param PDOException $error\n\t * @param bool $showerror\n\t *            if set to false, the error will be logged, but we go on\n\t * @throws Exception\n\t */\n\tprivate static function showerror(Exception $error, bool $showerror = true, bool $json_response = false, ?PDOStatement $stmt = null)\n\t{\n\t\tglobal $userinfo, $theme, $linker;\n\n\t\t$sql = [];\n\t\t$sql_root = [];\n\n\t\t// include userdata.inc.php\n\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\n\t\t// le format\n\t\tif (isset($sql['root_user']) && isset($sql['root_password']) && empty($sql_root)) {\n\t\t\t$sql_root = [\n\t\t\t\t0 => [\n\t\t\t\t\t'caption' => 'Default',\n\t\t\t\t\t'host' => $sql['host'],\n\t\t\t\t\t'socket' => ($sql['socket'] ?? null),\n\t\t\t\t\t'user' => $sql['root_user'],\n\t\t\t\t\t'password' => $sql['root_password']\n\t\t\t\t]\n\t\t\t];\n\t\t\tunset($sql['root_user']);\n\t\t\tunset($sql['root_password']);\n\t\t\t// write new layout so this won't happen again\n\t\t\tself::generateNewUserData($sql, $sql_root);\n\t\t\t// re-read file\n\t\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\t\t}\n\n\t\t$substitutions = [\n\t\t\t$sql['password'] => 'DB_UNPRIV_PWD',\n\t\t];\n\t\tforeach ($sql_root as $sql_root_data) {\n\t\t\t$substitutions[$sql_root_data['password']] = 'DB_ROOT_PWD';\n\t\t}\n\n\t\t// hide username/password in messages\n\t\t$error_message = $error->getMessage();\n\t\t$error_trace = $error->getTraceAsString();\n\t\t// error-message\n\t\t$error_message = self::substitute($error_message, $substitutions);\n\t\t// error-trace\n\t\t$error_trace = self::substitute($error_trace, $substitutions);\n\n\t\tif ($error->getCode() == 2003) {\n\t\t\t$error_message = \"Unable to connect to database. Either the mysql-server is not running or your user/password is wrong.\";\n\t\t\t$error_trace = \"\";\n\t\t}\n\n\t\t/**\n\t\t * log to a file, so we can actually ask people for the error\n\t\t * (no one seems to find the stuff in the syslog)\n\t\t */\n\t\t$sl_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . \"/logs/\");\n\t\tif (!file_exists($sl_dir)) {\n\t\t\t@mkdir($sl_dir, 0755);\n\t\t}\n\t\tif (!defined('TRAVIS_CI') || TRAVIS_CI == 0) {\n\t\t\topenlog(\"froxlor\", LOG_PID | LOG_PERROR, LOG_LOCAL0);\n\t\t\tsyslog(LOG_WARNING, str_replace(\"\\n\", \" \", $error_message));\n\t\t\tsyslog(LOG_WARNING, str_replace(\"\\n\", \" \", \"--- DEBUG: \" . $error_trace));\n\t\t\tcloselog();\n\t\t}\n\n\t\t/**\n\t\t * log error for reporting\n\t\t */\n\t\t$errid = self::genUniqueToken();\n\t\t$err_file = FileDir::makeCorrectFile($sl_dir . \"/\" . $errid . \"_sql-error.log\");\n\t\t$errlog = @fopen($err_file, 'w');\n\t\t@fwrite($errlog, \"|CODE \" . $error->getCode() . \"\\n\");\n\t\t@fwrite($errlog, \"|MSG \" . $error_message . \"\\n\");\n\t\t@fwrite($errlog, \"|FILE \" . $error->getFile() . \"\\n\");\n\t\t@fwrite($errlog, \"|LINE \" . $error->getLine() . \"\\n\");\n\t\t@fwrite($errlog, \"|TRACE\\n\" . $error_trace . \"\\n\");\n\t\t@fclose($errlog);\n\n\t\tif (empty($sql['debug'])) {\n\t\t\t$error_trace = '';\n\t\t} elseif (!is_null($stmt)) {\n\t\t\t$error_trace .= \"\\n\\n\" . $stmt->queryString;\n\t\t}\n\n\t\tif ($showerror && $json_response) {\n\t\t\t$exception_message = $error_message;\n\t\t\tif (isset($sql['debug']) && $sql['debug'] == true) {\n\t\t\t\t$exception_message .= \"\\n\\n\" . $error_trace;\n\t\t\t}\n\t\t\tthrow new Exception($exception_message, 500);\n\t\t}\n\n\t\tif ($showerror) {\n\t\t\t// clean up sensitive data\n\t\t\tunset($sql);\n\t\t\tunset($sql_root);\n\n\t\t\tif ((isset($theme) && $theme != '') && !isset($_SERVER['SHELL']) || (isset($_SERVER['SHELL']) && $_SERVER['SHELL'] == '')) {\n\t\t\t\t// if we're not on the shell, output a nice error\n\t\t\t\t$err_report_link = '';\n\t\t\t\tif (is_array($userinfo) && (($userinfo['adminsession'] == '1' && Settings::Get('system.allow_error_report_admin') == '1') || ($userinfo['adminsession'] == '0' && Settings::Get('system.allow_error_report_customer') == '1'))) {\n\t\t\t\t\t$err_report_link = $linker->getLink([\n\t\t\t\t\t\t'section' => 'index',\n\t\t\t\t\t\t'page' => 'send_error_report',\n\t\t\t\t\t\t'errorid' => $errid\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t\t// show\n\t\t\t\tUI::initTwig(true);\n\t\t\t\tUI::twig()->addGlobal('install_mode', '1');\n\t\t\t\tUI::view('misc/dberrornice.html.twig', [\n\t\t\t\t\t'page_title' => 'Database error',\n\t\t\t\t\t'message' => $error_message,\n\t\t\t\t\t'debug' => $error_trace,\n\t\t\t\t\t'report' => $err_report_link\n\t\t\t\t]);\n\t\t\t\tdie();\n\t\t\t}\n\t\t\tdie(\"We are sorry, but a MySQL - error occurred. The administrator may find more information in the syslog\");\n\t\t}\n\t}\n\n\t/**\n\t * Substitutes patterns in content.\n\t *\n\t * @param string $content\n\t * @param array $substitutions\n\t * @param int $minLength\n\t * @return string\n\t */\n\tprivate static function substitute(string $content, array $substitutions, int $minLength = 6): string\n\t{\n\t\t$replacements = [];\n\n\t\tforeach ($substitutions as $search => $replace) {\n\t\t\t$replacements += self::createShiftedSubstitutions($search, $replace, $minLength);\n\t\t}\n\n\t\treturn str_replace(array_keys($replacements), array_values($replacements), $content);\n\t}\n\n\t/**\n\t * Creates substitutions, shifted by length, e.g.\n\t *\n\t * _createShiftedSubstitutions('abcdefgh', 'value', 4):\n\t * array(\n\t * 'abcdefgh' => 'value',\n\t * 'abcdefg' => 'value',\n\t * 'abcdef' => 'value',\n\t * 'abcde' => 'value',\n\t * 'abcd' => 'value',\n\t * )\n\t *\n\t * @param string $search\n\t * @param string $replace\n\t * @param int $minLength\n\t * @return array\n\t */\n\tprivate static function createShiftedSubstitutions(string $search, string $replace, int $minLength): array\n\t{\n\t\t$substitutions = [];\n\t\t$length = strlen($search);\n\n\t\tif ($length > $minLength) {\n\t\t\tfor ($shiftedLength = $length; $shiftedLength >= $minLength; $shiftedLength--) {\n\t\t\t\t$substitutions[substr($search, 0, $shiftedLength)] = $replace;\n\t\t\t}\n\t\t}\n\n\t\treturn $substitutions;\n\t}\n\n\t/**\n\t * generate safe unique token\n\t *\n\t * @param int $length\n\t * @return string\n\t * @throws Exception\n\t */\n\tprivate static function genUniqueToken(int $length = 16): string\n\t{\n\t\tif (intval($length) <= 8) {\n\t\t\t$length = 16;\n\t\t}\n\t\tif (function_exists('random_bytes')) {\n\t\t\treturn bin2hex(random_bytes($length));\n\t\t}\n\t\tif (function_exists('mcrypt_create_iv') && defined('MCRYPT_DEV_URANDOM')) {\n\t\t\treturn bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM));\n\t\t}\n\t\tif (function_exists('openssl_random_pseudo_bytes')) {\n\t\t\treturn bin2hex(openssl_random_pseudo_bytes($length));\n\t\t}\n\t\t// if everything else fails, use unsafe fallback\n\t\treturn substr(md5(uniqid(microtime(), 1)), 0, $length);\n\t}\n\n\t/**\n\t * returns the number of found rows of the last select query\n\t *\n\t * @return int\n\t */\n\tpublic static function num_rows(): int\n\t{\n\t\treturn Database::query(\"SELECT FOUND_ROWS()\")->fetchColumn();\n\t}\n\n\t/**\n\t * returns the database-name which is used\n\t *\n\t * @return string\n\t */\n\tpublic static function getDbName(): ?string\n\t{\n\t\treturn self::$dbname;\n\t}\n\n\t/**\n\t * enabled the usage of a root-connection to the database\n\t * Note: must be called *before* any prepare/query/etc.\n\t * and should be called again with 'false'-parameter to resume\n\t * the 'normal' database-connection\n\t *\n\t * @param bool $needroot\n\t * @param int $dbserver optional\n\t * @param bool $need_db\n\t */\n\tpublic static function needRoot(bool $needroot = false, int $dbserver = 0, bool $need_db = true)\n\t{\n\t\t// force re-connecting to the db with corresponding user\n\t\t// and set the $dbserver (mostly to 0 = default)\n\t\tself::setServer($dbserver);\n\t\tself::$needroot = $needroot;\n\t\tself::$need_dbname = $need_db;\n\t}\n\n\t/**\n\t * set the database-server (relevant for root-connection)\n\t *\n\t * @param int $dbserver\n\t */\n\tprivate static function setServer(int $dbserver = 0)\n\t{\n\t\tself::$dbserver = $dbserver;\n\t\tself::$link = null;\n\t}\n\n\t/**\n\t * get the currently used database-server (relevant for root-connection)\n\t */\n\tpublic static function getServer()\n\t{\n\t\treturn self::$dbserver;\n\t}\n\n\t/**\n\t * enable the temporary access to sql-access data\n\t * note: if you want root-sqldata you need to\n\t * call needRoot(true) first.\n\t * Also, this will\n\t * only give you the data ONCE as it disable itself\n\t * after the first access to the data\n\t */\n\tpublic static function needSqlData()\n\t{\n\t\tself::$needsqldata = true;\n\t\tself::$sqldata = [];\n\t\tself::$link = null;\n\t\t// we need a connection here because\n\t\t// if getSqlData() is called RIGHT after\n\t\t// this function and no \"real\" PDO\n\t\t// function was called, getDB() wasn't\n\t\t// involved and no data collected\n\t\tself::getDB();\n\t}\n\n\t/**\n\t * function that will be called on every static call\n\t * which connects to the database if necessary\n\t *\n\t * @return object\n\t * @throws Exception\n\t */\n\tprivate static function getDB()\n\t{\n\t\tif (!extension_loaded('pdo') || !in_array(\"mysql\", PDO::getAvailableDrivers())) {\n\t\t\tself::showerror(new Exception(\"The php PDO extension or PDO-MySQL driver is not available\"));\n\t\t}\n\n\t\t// do we have a connection already?\n\t\tif (self::$link) {\n\t\t\t// return it\n\t\t\treturn self::$link;\n\t\t}\n\n\t\t// include userdata.inc.php\n\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\n\t\t// le format\n\t\tif (isset($sql['root_user']) && isset($sql['root_password']) && (!isset($sql_root) || !is_array($sql_root))) {\n\t\t\t$sql_root = [\n\t\t\t\t0 => [\n\t\t\t\t\t'caption' => 'Default',\n\t\t\t\t\t'host' => $sql['host'],\n\t\t\t\t\t'socket' => ($sql['socket'] ?? null),\n\t\t\t\t\t'user' => $sql['root_user'],\n\t\t\t\t\t'password' => $sql['root_password']\n\t\t\t\t]\n\t\t\t];\n\t\t\tunset($sql['root_user']);\n\t\t\tunset($sql['root_password']);\n\t\t\t// write new layout so this won't happen again\n\t\t\tself::generateNewUserData($sql, $sql_root);\n\t\t\t// re-read file\n\t\t\trequire Froxlor::getInstallDir() . \"/lib/userdata.inc.php\";\n\t\t}\n\n\t\t// either root or unprivileged user\n\t\tif (self::$needroot) {\n\t\t\t$caption = $sql_root[self::$dbserver]['caption'];\n\t\t\t$user = $sql_root[self::$dbserver]['user'];\n\t\t\t$password = $sql_root[self::$dbserver]['password'];\n\t\t\t$host = $sql_root[self::$dbserver]['host'];\n\t\t\t$socket = $sql_root[self::$dbserver]['socket'] ?? null;\n\t\t\t$port = $sql_root[self::$dbserver]['port'] ?? '3306';\n\t\t\t$sslCAFile = $sql_root[self::$dbserver]['ssl']['caFile'] ?? \"\";\n\t\t\t$sslVerifyServerCertificate = $sql_root[self::$dbserver]['ssl']['verifyServerCertificate'] ?? false;\n\t\t} else {\n\t\t\t$caption = 'localhost';\n\t\t\t$user = $sql[\"user\"];\n\t\t\t$password = $sql[\"password\"];\n\t\t\t$host = $sql[\"host\"];\n\t\t\t$socket = $sql['socket'] ?? null;\n\t\t\t$port = $sql['port'] ?? '3306';\n\t\t\t$sslCAFile = $sql['ssl']['caFile'] ?? \"\";\n\t\t\t$sslVerifyServerCertificate = $sql['ssl']['verifyServerCertificate'] ?? false;\n\t\t}\n\n\t\t// save sql-access-data if needed\n\t\tif (self::$needsqldata) {\n\t\t\tself::$sqldata = [\n\t\t\t\t'user' => $user,\n\t\t\t\t'passwd' => $password,\n\t\t\t\t'host' => $host,\n\t\t\t\t'port' => $port,\n\t\t\t\t'socket' => $socket,\n\t\t\t\t'db' => $sql[\"db\"],\n\t\t\t\t'caption' => $caption,\n\t\t\t\t'ssl_ca_file' => $sslCAFile,\n\t\t\t\t'ssl_verify_server_certificate' => $sslVerifyServerCertificate\n\t\t\t];\n\t\t}\n\n\t\t// build up connection string\n\t\t$driver = 'mysql';\n\t\t$dsn = $driver . \":\";\n\t\t$options = [\n\t\t\t'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8'\n\t\t];\n\t\t$attributes = [\n\t\t\t'ATTR_ERRMODE' => 'ERRMODE_EXCEPTION'\n\t\t];\n\n\t\t$dbconf[\"dsn\"] = ['charset' => 'utf8'];\n\n\t\tif (self::$need_dbname) {\n\t\t\t$dbconf[\"dsn\"]['dbname'] = $sql[\"db\"];\n\t\t}\n\n\t\tif ($socket != null) {\n\t\t\t$dbconf[\"dsn\"]['unix_socket'] = FileDir::makeCorrectFile($socket);\n\t\t} else {\n\t\t\t$dbconf[\"dsn\"]['host'] = $host;\n\t\t\t$dbconf[\"dsn\"]['port'] = $port;\n\n\t\t\tif (!empty(self::$sqldata['ssl_ca_file'])) {\n\t\t\t\t$options[PDO::MYSQL_ATTR_SSL_CA] = self::$sqldata['ssl_ca_file'];\n\t\t\t\t$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)self::$sqldata['ssl_verify_server_certificate'];\n\t\t\t}\n\t\t}\n\n\t\tself::$dbname = $sql[\"db\"];\n\n\t\t// add options to dsn-string\n\t\tforeach ($dbconf[\"dsn\"] as $k => $v) {\n\t\t\t$dsn .= $k . \"=\" . $v . \";\";\n\t\t}\n\n\t\t// clean up\n\t\tunset($dbconf);\n\n\t\t// try to connect\n\t\ttry {\n\t\t\tself::$link = new PDO($dsn, $user, $password, $options);\n\t\t} catch (PDOException $e) {\n\t\t\tself::showerror($e);\n\t\t}\n\n\t\t// set attributes\n\t\tforeach ($attributes as $k => $v) {\n\t\t\tself::$link->setAttribute(constant(\"PDO::\" . $k), constant(\"PDO::\" . $v));\n\t\t}\n\n\t\t$version_server = self::$link->getAttribute(PDO::ATTR_SERVER_VERSION);\n\t\t$sql_mode = 'NO_ENGINE_SUBSTITUTION';\n\t\tif (version_compare($version_server, '8.0.11', '<')) {\n\t\t\t$sql_mode .= ',NO_AUTO_CREATE_USER';\n\t\t}\n\t\tself::$link->exec('SET sql_mode = \"' . $sql_mode . '\"');\n\n\t\t// return PDO instance\n\t\treturn self::$link;\n\t}\n\n\t/**\n\t * returns the sql-access data as array using indices\n\t * 'user', 'passwd' and 'host'.\n\t * Returns false if not enabled\n\t *\n\t * @return array|bool\n\t */\n\tpublic static function getSqlData()\n\t{\n\t\t$return = false;\n\t\tif (self::$sqldata !== null && is_array(self::$sqldata) && isset(self::$sqldata['user'])) {\n\t\t\t$return = self::$sqldata;\n\t\t\t// automatically disable sql-data\n\t\t\tself::$sqldata = null;\n\t\t\tself::$needsqldata = false;\n\t\t}\n\t\treturn $return;\n\t}\n\n\t/**\n\t * return number of characters that are allowed to use as username\n\t *\n\t * @return int\n\t */\n\tpublic static function getSqlUsernameLength(): int\n\t{\n\t\t// MariaDB supports up to 80 characters but only 64 for databases and as we use the login-name also for\n\t\t// database names, we set the limit to 64 here\n\t\tif (strpos(strtolower(Database::getAttribute(\\PDO::ATTR_SERVER_VERSION)), \"mariadb\") !== false) {\n\t\t\t$mysql_max = 64;\n\t\t} else {\n\t\t\t// MySQL user-names can be up to 32 characters long (16 characters before MySQL 5.7.8).\n\t\t\t$mysql_max = 32;\n\t\t\tif (version_compare(Database::getAttribute(\\PDO::ATTR_SERVER_VERSION), '5.7.8', '<')) {\n\t\t\t\t$mysql_max = 16;\n\t\t\t}\n\t\t}\n\t\treturn $mysql_max;\n\t}\n\n\t/**\n\t * Lets us interact with the PDO-Object by using static\n\t * call like \"Database::function()\"\n\t *\n\t * @param string $name\n\t * @param mixed $args\n\t *\n\t * @return mixed\n\t * @throws Exception\n\t */\n\tpublic static function __callStatic(string $name, $args)\n\t{\n\t\t$callback = [\n\t\t\tself::getDB(),\n\t\t\t$name\n\t\t];\n\t\t$result = null;\n\t\ttry {\n\t\t\t$result = call_user_func_array($callback, $args);\n\t\t} catch (PDOException $e) {\n\t\t\tself::showerror($e);\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * write new userdata.inc.php file\n\t */\n\tprivate static function generateNewUserData(array $sql, array $sql_root)\n\t{\n\t\t$content = PhpHelper::parseArrayToPhpFile(\n\t\t\t['sql' => $sql, 'sql_root' => $sql_root],\n\t\t\t'automatically generated userdata.inc.php for froxlor'\n\t\t);\n\t\tchmod(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\", 0700);\n\t\tfile_put_contents(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\", $content);\n\t\tchmod(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\", 0400);\n\t\tclearstatcache();\n\t\tif (function_exists('opcache_invalidate')) {\n\t\t\t@opcache_invalidate(Froxlor::getInstallDir() . \"/lib/userdata.inc.php\", true);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Database/DbManager.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Database;\n\nuse Exception;\nuse Froxlor\\Database\\Manager\\DbManagerMySQL;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse PDO;\n\n/**\n * Class DbManager\n *\n * Wrapper-class for database-management like creating\n * and removing databases, users and permissions\n */\nclass DbManager\n{\n\n\t/**\n\t * FroxlorLogger object\n\t *\n\t * @var object\n\t */\n\tprivate $log = null;\n\n\t/**\n\t * Manager object\n\t *\n\t * @var object\n\t */\n\tprivate $manager = null;\n\n\t/**\n\t * main constructor\n\t *\n\t * @param FroxlorLogger $log\n\t */\n\tpublic function __construct($log = null)\n\t{\n\t\t$this->log = $log;\n\t\t$this->setManager();\n\t}\n\n\t/**\n\t * set manager-object by type of\n\t * dbms: mysql only for now\n\t *\n\t * sets private $_manager variable\n\t */\n\tprivate function setManager()\n\t{\n\t\t// TODO read different dbms from settings later\n\t\t$this->manager = new DbManagerMySQL($this->log);\n\t}\n\n\t/**\n\t * function called when the mysql-access-host setting changes\n\t *\n\t * @param array $mysql_access_host_array\n\t *\n\t * @return void\n\t * @throws Exception\n\t */\n\tpublic static function correctMysqlUsers(array $mysql_access_host_array)\n\t{\n\t\t// get all databases for all dbservers\n\t\t$databases = [];\n\t\t$databases_result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_DATABASES . \"`\n\t\t\tORDER BY `dbserver` ASC\n\t\t\");\n\t\tDatabase::pexecute($databases_result_stmt);\n\t\twhile ($databases_row = $databases_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (!isset($databases[$databases_row['dbserver']])) {\n\t\t\t\t$databases[$databases_row['dbserver']] = [];\n\t\t\t}\n\t\t\t$databases[$databases_row['dbserver']][] = $databases_row['databasename'];\n\t\t}\n\n\t\t$customers_sel = Database::query(\"\n\t\t\tSELECT DISTINCT c.loginname\n\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` c\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DATABASES . \"` d ON c.customerid = d.customerid\n\t\t\tWHERE c.deactivated = '0' AND d.id IS NOT NULL\n\t\t\");\n\t\t$customers = [];\n\t\twhile ($customer = $customers_sel->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t$customers[] = $customer['loginname'];\n\t\t}\n\n\t\t$dbservers_stmt = Database::query(\"SELECT DISTINCT `dbserver` FROM `\" . TABLE_PANEL_DATABASES . \"`\");\n\t\twhile ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {\n\n\t\t\t// add all customer loginnames to the $databases array for this database-server to correct\n\t\t\t// a possible existing global mysql-user for that customer\n\t\t\tforeach ($customers as $customer) {\n\t\t\t\t$databases[$dbserver['dbserver']][] = $customer;\n\t\t\t}\n\n\t\t\t// require privileged access for target db-server\n\t\t\tDatabase::needRoot(true, $dbserver['dbserver'], false);\n\n\t\t\t$dbm = new DbManager(FroxlorLogger::getInstanceOf());\n\t\t\t$users = $dbm->getManager()->getAllSqlUsers(false);\n\n\t\t\tforeach ($databases[$dbserver['dbserver']] as $username) {\n\t\t\t\tif (isset($users[$username]['hosts']) && is_array($users[$username]['hosts'])) {\n\n\t\t\t\t\tforeach ($mysql_access_host_array as $mysql_access_host) {\n\t\t\t\t\t\t$mysql_access_host = trim($mysql_access_host);\n\n\t\t\t\t\t\tif (!in_array($mysql_access_host, $users[$username]['hosts'])) {\n\t\t\t\t\t\t\t// if this is a new host, use credentials from localhost, which should always exist\n\t\t\t\t\t\t\t$password = [\n\t\t\t\t\t\t\t\t'password' => $users[$username]['hosts']['localhost']['password'],\n\t\t\t\t\t\t\t\t'plugin' => $users[$username]['hosts']['localhost']['plugin']\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t$dbm->getManager()->grantPrivilegesTo($username, $password, $mysql_access_host, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach ($users[$username]['hosts'] as $mysql_access_host) {\n\t\t\t\t\t\tif (!in_array($mysql_access_host, $mysql_access_host_array)) {\n\t\t\t\t\t\t\t$dbm->getManager()->deleteUser($username, $mysql_access_host);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$dbm->getManager()->flushPrivileges();\n\t\t\tDatabase::needRoot(false);\n\n\t\t\tunset($databases[$dbserver['dbserver']]);\n\t\t}\n\t}\n\n\t/**\n\t * creates a new database and a user with the\n\t * same name with all privileges granted on the db.\n\t * DB-name and user-name are being generated and\n\t * the password for the user will be set\n\t *\n\t * @param ?string $loginname\n\t * @param ?string $password\n\t * @param int $dbserver\n\t * @param int $last_accnumber\n\t * @param ?string $global_user\n\t *\n\t * @return string|bool $username if successful or false of username is equal to the password\n\t * @throws Exception\n\t */\n\tpublic function createDatabase(string $loginname = null, string $password = null, int $dbserver = 0, int $last_accnumber = 0, string $global_user = \"\")\n\t{\n\t\tDatabase::needRoot(true, $dbserver, false);\n\n\t\t// check whether we shall create a random username\n\t\tif (strtoupper(Settings::Get('customer.mysqlprefix')) == 'RANDOM') {\n\t\t\t// get all usernames from db-manager\n\t\t\t$allsqlusers = $this->getManager()->getAllSqlUsers();\n\t\t\t// generate random username\n\t\t\t$username = $loginname . '-' . substr(Froxlor::genSessionId(), 20, 3);\n\t\t\t// check whether it exists on the DBMS\n\t\t\twhile (in_array($username, $allsqlusers)) {\n\t\t\t\t$username = $loginname . '-' . substr(Froxlor::genSessionId(), 20, 3);\n\t\t\t}\n\t\t} elseif (strtoupper(Settings::Get('customer.mysqlprefix')) == 'DBNAME') {\n\t\t\t$username = $loginname;\n\t\t} else {\n\t\t\t$username = $loginname . Settings::Get('customer.mysqlprefix') . (intval($last_accnumber) + 1);\n\t\t}\n\n\t\t// don't use a password that is the same as the username\n\t\tif ($username == $password) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// now create the database itself\n\t\t$this->getManager()->createDatabase($username);\n\n\t\t// and give permission to the user on every access-host we have\n\t\tforeach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {\n\t\t\t$this->getManager()->grantPrivilegesTo($username, $password, $mysql_access_host);\n\t\t\tif (!empty($global_user)) {\n\t\t\t\t$this->getManager()->grantCreateToDb($global_user, $username, $mysql_access_host);\n\t\t\t}\n\t\t}\n\n\t\t$this->getManager()->flushPrivileges();\n\t\tDatabase::needRoot();\n\n\t\t$this->log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, \"created database '\" . $username . \"'\");\n\n\t\treturn $username;\n\t}\n\n\t/**\n\t * returns the manager-object\n\t * from where we can control it\n\t */\n\tpublic function getManager()\n\t{\n\t\treturn $this->manager;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Database/IntegrityCheck.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Database;\n\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass IntegrityCheck\n{\n\n\t// Store all available checks\n\tpublic $available = [];\n\n\t// logger object\n\tprivate $log = null;\n\n\t/**\n\t * Constructor\n\t * Parses all available checks into $this->available\n\t */\n\tpublic function __construct()\n\t{\n\t\tglobal $userinfo;\n\t\tif (!isset($userinfo) || !is_array($userinfo)) {\n\t\t\t$userinfo = [\n\t\t\t\t'loginname' => 'integrity-check'\n\t\t\t];\n\t\t}\n\t\t$this->log = FroxlorLogger::getInstanceOf($userinfo);\n\t\t$this->available = get_class_methods($this);\n\t\tunset($this->available[array_search('__construct', $this->available)]);\n\t\tunset($this->available[array_search('checkAll', $this->available)]);\n\t\tunset($this->available[array_search('fixAll', $this->available)]);\n\t\tsort($this->available);\n\t}\n\n\t/**\n\t * Check all occurring integrity problems at once\n\t */\n\tpublic function checkAll()\n\t{\n\t\t$integrityok = true;\n\t\tforeach ($this->available as $check) {\n\t\t\t$integrityok = $this->$check() ? $integrityok : false;\n\t\t}\n\t\treturn $integrityok;\n\t}\n\n\t/**\n\t * Fix all occurring integrity problems at once with default settings\n\t */\n\tpublic function fixAll()\n\t{\n\t\t$integrityok = true;\n\t\tforeach ($this->available as $check) {\n\t\t\t$integrityok = $this->$check(true) ? $integrityok : false;\n\t\t}\n\t\treturn $integrityok;\n\t}\n\n\t/**\n\t * check whether the froxlor database and its tables are in utf-8 character-set\n\t *\n\t * @param bool $fix fix db charset/collation if not utf8\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic function databaseCharset(bool $fix = false): bool\n\t{\n\t\t// get character-set\n\t\t$cs_stmt = Database::prepare('SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = :dbname');\n\t\t$resp = Database::pexecute_first($cs_stmt, [\n\t\t\t'dbname' => Database::getDbName()\n\t\t]);\n\t\t$charset = $resp['default_character_set_name'] ?? null;\n\t\tif (!empty($charset) && substr(strtolower($charset), 0, 4) != 'utf8') {\n\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\"database charset seems to be different from UTF-8, integrity-check can fix that\");\n\t\t\tif ($fix) {\n\t\t\t\t// fix database\n\t\t\t\tDatabase::query('ALTER DATABASE `' . Database::getDbName() . '` CHARACTER SET utf8 COLLATE utf8_general_ci');\n\t\t\t\t// fix all tables\n\t\t\t\t$handle = Database::query('SHOW FULL TABLES WHERE Table_type != \"VIEW\"');\n\t\t\t\twhile ($row = $handle->fetch(PDO::FETCH_BOTH)) {\n\t\t\t\t\t$table = $row[0];\n\t\t\t\t\tDatabase::query('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;');\n\t\t\t\t}\n\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,\n\t\t\t\t\t\"database charset was different from UTF-8, integrity-check fixed that\");\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif ($fix) {\n\t\t\treturn $this->databaseCharset();\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check the integrity of the domain to ip/port - association\n\t *\n\t * @param bool $fix fix everything found directly\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic function domainIpTable(bool $fix = false): bool\n\t{\n\t\t$ips = [];\n\t\t$domains = [];\n\t\t$ipstodomains = [];\n\t\t$admips = [];\n\n\t\tif ($fix) {\n\t\t\t// Prepare insert / delete statement for the fixes\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\tWHERE `id_domain` = :domainid AND `id_ipandports` = :ipandportid \");\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\tSET `id_domain` = :domainid, `id_ipandports` = :ipandportid \");\n\n\t\t\t// Cache all IPs the admins have assigned\n\t\t\t$adm_stmt = Database::prepare(\"SELECT `adminid`, `ip` FROM `\" . TABLE_PANEL_ADMINS . \"` ORDER BY `adminid` ASC\");\n\t\t\tDatabase::pexecute($adm_stmt);\n\t\t\t$default_ips = explode(',', Settings::Get('system.defaultip'));\n\t\t\t$default_ssl_ips = explode(',', Settings::Get('system.defaultsslip'));\n\t\t\twhile ($row = $adm_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ($row['ip'] < 0 || is_null($row['ip']) || empty($row['ip'])) {\n\t\t\t\t\t// Admin uses default-IP\n\t\t\t\t\t$admips[$row['adminid']] = array_merge($default_ips, $default_ssl_ips);\n\t\t\t\t} else {\n\t\t\t\t\t$admips[$row['adminid']] = [\n\t\t\t\t\t\t$row['ip']\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Cache all available ip/port - combinations\n\t\t$result_stmt = Database::prepare(\"SELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` ORDER BY `id` ASC\");\n\t\tDatabase::pexecute($result_stmt);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$ips[$row['id']] = $row['ip'] . ':' . $row['port'];\n\t\t}\n\n\t\t// Cache all configured domains\n\t\t$result_stmt = Database::prepare(\"SELECT `id`, `adminid` FROM `\" . TABLE_PANEL_DOMAINS . \"` ORDER BY `id` ASC\");\n\t\tDatabase::pexecute($result_stmt);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$domains[$row['id']] = $row['adminid'];\n\t\t}\n\n\t\t// Check if every domain to ip/port - association is valid in TABLE_DOMAINTOIP\n\t\t$result_stmt = Database::prepare(\"SELECT `id_domain`, `id_ipandports` FROM `\" . TABLE_DOMAINTOIP . \"`\");\n\t\tDatabase::pexecute($result_stmt);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (!array_key_exists($row['id_ipandports'], $ips)) {\n\t\t\t\tif ($fix) {\n\t\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t\t'domainid' => $row['id_domain'],\n\t\t\t\t\t\t'ipandportid' => $row['id_ipandports']\n\t\t\t\t\t]);\n\t\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,\n\t\t\t\t\t\t\"found an ip/port-id in domain <> ip table which does not exist, integrity check fixed this\");\n\t\t\t\t} else {\n\t\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\t\t\"found an ip/port-id in domain <> ip table which does not exist, integrity check can fix this\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!array_key_exists($row['id_domain'], $domains)) {\n\t\t\t\tif ($fix) {\n\t\t\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t\t\t'domainid' => $row['id_domain'],\n\t\t\t\t\t\t'ipandportid' => $row['id_ipandports']\n\t\t\t\t\t]);\n\t\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,\n\t\t\t\t\t\t\"found a domain-id in domain <> ip table which does not exist, integrity check fixed this\");\n\t\t\t\t} else {\n\t\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\t\t\"found a domain-id in domain <> ip table which does not exist, integrity check can fix this\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Save one IP/Port combination per domain, so we know, if one domain is missing an IP\n\t\t\t$ipstodomains[$row['id_domain']] = $row['id_ipandports'];\n\t\t}\n\n\t\t// Check that all domains have at least one IP/Port combination\n\t\tforeach ($domains as $domainid => $adminid) {\n\t\t\tif (!array_key_exists($domainid, $ipstodomains)) {\n\t\t\t\tif ($fix) {\n\t\t\t\t\tforeach ($admips[$adminid] as $defaultip) {\n\t\t\t\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t\t\t\t'domainid' => $domainid,\n\t\t\t\t\t\t\t'ipandportid' => $defaultip\n\t\t\t\t\t\t]);\n\t\t\t\t\t}\n\t\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,\n\t\t\t\t\t\t\"found a domain-id with no entry in domain <> ip table, integrity check fixed this\");\n\t\t\t\t} else {\n\t\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\t\t\"found a domain-id with no entry in domain <> ip table, integrity check can fix this\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($fix) {\n\t\t\treturn $this->domainIpTable();\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if all subdomains have ssl-redirect = 0 if domain has no ssl-port\n\t *\n\t * @param bool $fix fix everything found directly\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic function subdomainSslRedirect(bool $fix = false): bool\n\t{\n\t\t$ips = [];\n\t\t$parentdomains = [];\n\t\t$subdomains = [];\n\n\t\tif ($fix) {\n\t\t\t// Prepare update statement for the fixes\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tSET `ssl_redirect` = 0 WHERE `parentdomainid` = :domainid\");\n\t\t}\n\n\t\t// Cache all ssl ip/port - combinations\n\t\t$result_stmt = Database::prepare(\"SELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl` = 1 ORDER BY `id` ASC\");\n\t\tDatabase::pexecute($result_stmt);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$ips[$row['id']] = $row['ip'] . ':' . $row['port'];\n\t\t}\n\n\t\t// Cache all configured domains\n\t\t$result_stmt = Database::prepare(\"SELECT `id`, `parentdomainid`, `ssl_redirect` FROM `\" . TABLE_PANEL_DOMAINS . \"` ORDER BY `id` ASC\");\n\t\t$ip_stmt = Database::prepare(\"SELECT `id_domain`, `id_ipandports` FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :domainid\");\n\t\tDatabase::pexecute($result_stmt);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif ($row['parentdomainid'] == 0) {\n\t\t\t\t// All parentdomains by default have no ssl - ip/port\n\t\t\t\t$parentdomains[$row['id']] = false;\n\t\t\t\tDatabase::pexecute($ip_stmt, [\n\t\t\t\t\t'domainid' => $row['id']\n\t\t\t\t]);\n\t\t\t\twhile ($iprow = $ip_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t// If the parentdomain has an ip/port assigned which we know is SSL enabled, set the parentdomain to \"true\"\n\t\t\t\t\tif (array_key_exists($iprow['id_ipandports'], $ips)) {\n\t\t\t\t\t\t$parentdomains[$row['id']] = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} elseif ($row['ssl_redirect'] == 1) {\n\t\t\t\t// All subdomains with enabled ssl_redirect enabled are stored\n\t\t\t\tif (!isset($subdomains[$row['parentdomainid']])) {\n\t\t\t\t\t$subdomains[$row['parentdomainid']] = [];\n\t\t\t\t}\n\t\t\t\t$subdomains[$row['parentdomainid']][] = $row['id'];\n\t\t\t}\n\t\t}\n\n\t\t// Check if every parentdomain with enabled ssl_redirect as SSL enabled\n\t\tforeach ($parentdomains as $id => $sslavailable) {\n\t\t\t// This parentdomain has no subdomains\n\t\t\tif (!isset($subdomains[$id])) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// This parentdomain has SSL enabled, doesn't matter what status the subdomains have\n\t\t\tif ($sslavailable) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// At this point only parentdomains reside which have ssl_redirect enabled subdomains\n\t\t\tif ($fix) {\n\t\t\t\t// We make a blanket update to all subdomains of this parentdomain, doesn't matter which one is wrong, all have to be disabled\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'domainid' => $id\n\t\t\t\t]);\n\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,\n\t\t\t\t\t\"found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check fixed this\");\n\t\t\t} else {\n\t\t\t\t// It's just the check, let the function fail\n\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\t\"found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check can fix this\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif ($fix) {\n\t\t\treturn $this->subdomainSslRedirect();\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if all subdomain have letsencrypt = 0 if domain has no ssl-port\n\t *\n\t * @param bool $fix fix everything found directly\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic function subdomainLetsencrypt(bool $fix = false): bool\n\t{\n\t\t$ips = [];\n\t\t$parentdomains = [];\n\t\t$subdomains = [];\n\n\t\tif ($fix) {\n\t\t\t// Prepare update statement for the fixes\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tSET `letsencrypt` = 0 WHERE `parentdomainid` = :domainid\");\n\t\t}\n\n\t\t// Cache all ssl ip/port - combinations\n\t\t$result_stmt = Database::prepare(\"SELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl` = 1 ORDER BY `id` ASC\");\n\t\tDatabase::pexecute($result_stmt);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$ips[$row['id']] = $row['ip'] . ':' . $row['port'];\n\t\t}\n\n\t\t// Cache all configured domains\n\t\t$result_stmt = Database::prepare(\"SELECT `id`, `parentdomainid`, `letsencrypt` FROM `\" . TABLE_PANEL_DOMAINS . \"` ORDER BY `id` ASC\");\n\t\t$ip_stmt = Database::prepare(\"SELECT `id_domain`, `id_ipandports` FROM `\" . TABLE_DOMAINTOIP . \"` WHERE `id_domain` = :domainid\");\n\t\tDatabase::pexecute($result_stmt);\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif ($row['parentdomainid'] == 0) {\n\t\t\t\t// All parentdomains by default have no ssl - ip/port\n\t\t\t\t$parentdomains[$row['id']] = false;\n\t\t\t\tDatabase::pexecute($ip_stmt, [\n\t\t\t\t\t'domainid' => $row['id']\n\t\t\t\t]);\n\t\t\t\twhile ($iprow = $ip_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t// If the parentdomain has an ip/port assigned which we know is SSL enabled, set the parentdomain to \"true\"\n\t\t\t\t\tif (array_key_exists($iprow['id_ipandports'], $ips)) {\n\t\t\t\t\t\t$parentdomains[$row['id']] = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} elseif ($row['letsencrypt'] == 1) {\n\t\t\t\t// All subdomains with enabled letsencrypt enabled are stored\n\t\t\t\tif (!isset($subdomains[$row['parentdomainid']])) {\n\t\t\t\t\t$subdomains[$row['parentdomainid']] = [];\n\t\t\t\t}\n\t\t\t\t$subdomains[$row['parentdomainid']][] = $row['id'];\n\t\t\t}\n\t\t}\n\n\t\t// Check if every parentdomain with enabled letsencrypt as SSL enabled\n\t\tforeach ($parentdomains as $id => $sslavailable) {\n\t\t\t// This parentdomain has no subdomains\n\t\t\tif (!isset($subdomains[$id])) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// This parentdomain has SSL enabled, doesn't matter what status the subdomains have\n\t\t\tif ($sslavailable) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// At this point only parentdomains reside which have letsencrypt enabled subdomains\n\t\t\tif ($fix) {\n\t\t\t\t// We make a blanket update to all subdomains of this parentdomain, doesn't matter which one is wrong, all have to be disabled\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'domainid' => $id\n\t\t\t\t]);\n\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,\n\t\t\t\t\t\"found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check fixed this\");\n\t\t\t} else {\n\t\t\t\t// It's just the check, let the function fail\n\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\t\"found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check can fix this\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif ($fix) {\n\t\t\treturn $this->subdomainLetsencrypt();\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * check whether the webserveruser is in\n\t * the customers groups when fcgid / php-fpm is used\n\t *\n\t * @param bool $fix fix member/groups\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic function webserverGroupMemberForFcgidPhpFpm(bool $fix = false): bool\n\t{\n\t\tif (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// get all customers that don't have the webserver-user in their group\n\t\t$cwg_stmt = Database::prepare(\"\n\t       SELECT `id` FROM `\" . TABLE_FTP_GROUPS . \"` WHERE NOT FIND_IN_SET(:webserveruser, `members`)\n\t    \");\n\t\tDatabase::pexecute($cwg_stmt, [\n\t\t\t'webserveruser' => Settings::Get('system.httpuser')\n\t\t]);\n\n\t\tif ($cwg_stmt->rowCount() > 0) {\n\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\"Customers are missing the webserver-user as group-member, integrity-check can fix that\");\n\t\t\tif ($fix) {\n\t\t\t\t// prepare update statement\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t                UPDATE `\" . TABLE_FTP_GROUPS . \"` SET `members` = CONCAT(`members`, :additionaluser)\n\t                WHERE `id` = :id\n\t            \");\n\t\t\t\t$upd_data = [\n\t\t\t\t\t'additionaluser' => \",\" . Settings::Get('system.httpuser')\n\t\t\t\t];\n\n\t\t\t\twhile ($cwg_row = $cwg_stmt->fetch()) {\n\t\t\t\t\t$upd_data['id'] = $cwg_row['id'];\n\t\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\t\t\t\t}\n\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\t\"Customers were missing the webserver-user as group-member, integrity-check fixed that\");\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif ($fix) {\n\t\t\treturn $this->webserverGroupMemberForFcgidPhpFpm();\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * check whether the local froxlor user is in\n\t * the customers groups when fcgid / php-fpm and\n\t * fcgid/fpm in froxlor vhost is used\n\t *\n\t * @param bool $fix fix member/groups\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic function froxlorLocalGroupMemberForFcgidPhpFpm(bool $fix = false): bool\n\t{\n\t\tif (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (Settings::get('system.mod_fcgid') == 1) {\n\t\t\tif (Settings::get('system.mod_fcgid_ownvhost') == 0) {\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\t$localuser = Settings::Get('system.mod_fcgid_httpuser');\n\t\t\t}\n\t\t}\n\n\t\tif (Settings::get('phpfpm.enabled') == 1) {\n\t\t\tif (Settings::get('phpfpm.enabled_ownvhost') == 0) {\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\t$localuser = Settings::Get('phpfpm.vhost_httpuser');\n\t\t\t}\n\t\t}\n\n\t\t// get all customers that don't have the webserver-user in their group\n\t\t$cwg_stmt = Database::prepare(\"\n\t       SELECT `id` FROM `\" . TABLE_FTP_GROUPS . \"` WHERE NOT FIND_IN_SET(:localuser, `members`)\n\t    \");\n\t\tDatabase::pexecute($cwg_stmt, [\n\t\t\t'localuser' => $localuser\n\t\t]);\n\n\t\tif ($cwg_stmt->rowCount() > 0) {\n\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\"Customers are missing the local froxlor-user as group-member, integrity-check can fix that\");\n\t\t\tif ($fix) {\n\t\t\t\t// prepare update statement\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t                UPDATE `\" . TABLE_FTP_GROUPS . \"` SET `members` = CONCAT(`members`, :additionaluser)\n\t                WHERE `id` = :id\n\t            \");\n\t\t\t\t$upd_data = [\n\t\t\t\t\t'additionaluser' => \",\" . $localuser\n\t\t\t\t];\n\n\t\t\t\twhile ($cwg_row = $cwg_stmt->fetch()) {\n\t\t\t\t\t$upd_data['id'] = $cwg_row['id'];\n\t\t\t\t\tDatabase::pexecute($upd_stmt, $upd_data);\n\t\t\t\t}\n\t\t\t\t$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,\n\t\t\t\t\t\"Customers were missing the local froxlor-user as group-member, integrity-check fixed that\");\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif ($fix) {\n\t\t\treturn $this->froxlorLocalGroupMemberForFcgidPhpFpm();\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Database/Manager/DbManagerMySQL.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Database\\Manager;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse PDO;\n\n/**\n * Class DbManagerMySQL\n *\n * Explicit class for database-management like creating\n * and removing databases, users and permissions for MySQL\n */\nclass DbManagerMySQL\n{\n\n\t/**\n\t * FroxlorLogger object\n\t *\n\t * @var object\n\t */\n\tprivate $log = null;\n\n\t/**\n\t * main constructor\n\t *\n\t * @param FroxlorLogger|null $log\n\t */\n\tpublic function __construct(&$log = null)\n\t{\n\t\t$this->log = $log;\n\t}\n\n\t/**\n\t * creates a database\n\t *\n\t * @param string|null $dbname\n\t */\n\tpublic function createDatabase(string $dbname = null)\n\t{\n\t\tDatabase::query(\"CREATE DATABASE `\" . $dbname . \"`\");\n\t}\n\n\t/**\n\t * grants access privileges on a database with the same\n\t * username and sets the password for that user the given access_host\n\t *\n\t * @param string $username\n\t * @param string|array $password\n\t * @param ?string $access_host\n\t * @param bool $p_encrypted\n\t *            optional, whether the password is encrypted or not, default false\n\t * @param bool $update\n\t *            optional, whether to update the password only (not create user)\n\t * @param bool $grant_access_prefix\n\t *            optional, whether the given user will have access to all databases starting with the username, default false\n\t * @throws \\Exception\n\t */\n\tpublic function grantPrivilegesTo(string $username, $password, string $access_host = null, bool $p_encrypted = false, bool $update = false, bool $grant_access_prefix = false)\n\t{\n\t\t// this is required for mysql8\n\t\t$pwd_plugin = 'caching_sha2_password';\n\t\tif (is_array($password) && count($password) == 2) {\n\t\t\t$pwd_plugin = $password['plugin'];\n\t\t\t$password = $password['password'];\n\t\t}\n\n\t\tif (!$update) {\n\t\t\t// create user\n\t\t\tif ($p_encrypted) {\n\t\t\t\tif (version_compare(Database::getAttribute(\\PDO::ATTR_SERVER_VERSION), '5.7.0', '<') || version_compare(Database::getAttribute(\\PDO::ATTR_SERVER_VERSION), '10.0.0', '>=')) {\n\t\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\t\tCREATE USER '\" . $username . \"'@'\" . $access_host . \"' IDENTIFIED BY PASSWORD :password\n\t\t\t\t\t\");\n\t\t\t\t} else {\n\t\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\t\tCREATE USER '\" . $username . \"'@'\" . $access_host . \"' IDENTIFIED WITH \" . $pwd_plugin . \" AS :password\n\t\t\t\t\t\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tCREATE USER '\" . $username . \"'@'\" . $access_host . \"' IDENTIFIED BY :password\n\t\t\t\t\");\n\t\t\t}\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\"password\" => $password\n\t\t\t]);\n\t\t\t// grant privileges if not global user\n\t\t\tif (!$grant_access_prefix) {\n\t\t\t\tDatabase::query(\"GRANT ALL ON `\" . str_replace('_', '\\_', $username) . \"`.* TO `\" . $username . \"`@`\" . $access_host . \"`\");\n\t\t\t} else {\n\t\t\t\t// grant explicitly to existing databases\n\t\t\t\t$this->grantCreateToCustomerDbs($username, $access_host);\n\t\t\t}\n\t\t} else {\n\t\t\t// set password\n\t\t\tif (version_compare(Database::getAttribute(\\PDO::ATTR_SERVER_VERSION), '5.7.6', '<') || version_compare(Database::getAttribute(\\PDO::ATTR_SERVER_VERSION), '10.0.0', '>=')) {\n\t\t\t\tif ($p_encrypted) {\n\t\t\t\t\t$stmt = Database::prepare(\"SET PASSWORD FOR :username@:host = :password\");\n\t\t\t\t} else {\n\t\t\t\t\t$stmt = Database::prepare(\"SET PASSWORD FOR :username@:host = PASSWORD(:password)\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ($p_encrypted) {\n\t\t\t\t\t$stmt = Database::prepare(\"ALTER USER :username@:host IDENTIFIED WITH \" . $pwd_plugin . \" AS :password\");\n\t\t\t\t} else {\n\t\t\t\t\t$stmt = Database::prepare(\"ALTER USER :username@:host IDENTIFIED BY :password\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\"username\" => $username,\n\t\t\t\t\"host\" => $access_host,\n\t\t\t\t\"password\" => $password\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * removes the given database from the dbms and also\n\t * takes away any privileges from a user to that db\n\t *\n\t * @param string $dbname\n\t * @param ?string $global_user\n\t * @throws \\Exception\n\t */\n\tpublic function deleteDatabase(string $dbname, string $global_user = \"\")\n\t{\n\t\tif (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.0.2', '<')) {\n\t\t\t// failsafe if user has been deleted manually (requires MySQL 4.1.2+)\n\t\t\t$stmt = Database::prepare(\"REVOKE ALL PRIVILEGES, GRANT OPTION FROM `\" . $dbname . \"`\");\n\t\t\tDatabase::pexecute($stmt, [], false);\n\t\t}\n\n\t\t$host_res_stmt = Database::prepare(\"\n\t\t\tSELECT `Host` FROM `mysql`.`user` WHERE `User` = :dbname\");\n\t\tDatabase::pexecute($host_res_stmt, [\n\t\t\t'dbname' => $dbname\n\t\t]);\n\n\t\t// as of MySQL 5.0.2 this also revokes privileges. (requires MySQL 4.1.2+)\n\t\tif (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.7.0', '<')) {\n\t\t\t$drop_stmt = Database::prepare(\"DROP USER :dbname@:host\");\n\t\t} else {\n\t\t\t$drop_stmt = Database::prepare(\"DROP USER IF EXISTS :dbname@:host\");\n\t\t}\n\t\t$rev_stmt = Database::prepare(\"REVOKE ALL PRIVILEGES ON `\" . $dbname . \"`.* FROM :guser@:host;\");\n\t\twhile ($host = $host_res_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tDatabase::pexecute($drop_stmt, [\n\t\t\t\t'dbname' => $dbname,\n\t\t\t\t'host' => $host['Host']\n\t\t\t], false);\n\n\t\t\tif (!empty($global_user)) {\n\t\t\t\tDatabase::pexecute($rev_stmt, [\n\t\t\t\t\t'guser' => $global_user,\n\t\t\t\t\t'host' => $host['Host']\n\t\t\t\t], false);\n\t\t\t}\n\t\t}\n\n\t\t$drop_stmt = Database::prepare(\"DROP DATABASE IF EXISTS `\" . $dbname . \"`\");\n\t\tDatabase::pexecute($drop_stmt);\n\t}\n\n\t/**\n\t * removes a user from the dbms and revokes all privileges\n\t *\n\t * @param string $username\n\t * @param string $host\n\t * @throws \\Exception\n\t */\n\tpublic function deleteUser(string $username, string $host)\n\t{\n\t\tif ($this->userExistsOnHost($username, $host)) {\n\t\t\tif (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.0.2', '<')) {\n\t\t\t\t// Revoke privileges (only required for MySQL 4.1.2 - 5.0.1)\n\t\t\t\t$stmt = Database::prepare(\"REVOKE ALL PRIVILEGES ON * . * FROM `\" . $username . \"`@`\" . $host . \"`\");\n\t\t\t\tDatabase::pexecute($stmt);\n\t\t\t}\n\t\t\t// as of MySQL 5.0.2 this also revokes privileges. (requires MySQL 4.1.2+)\n\t\t\tif (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.7.0', '<')) {\n\t\t\t\t$stmt = Database::prepare(\"DROP USER :username@:host\");\n\t\t\t} else {\n\t\t\t\t$stmt = Database::prepare(\"DROP USER IF EXISTS :username@:host\");\n\t\t\t}\n\t\t\tDatabase::pexecute($stmt, [\n\t\t\t\t\"username\" => $username,\n\t\t\t\t\"host\" => $host\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * removes permissions from a user\n\t *\n\t * @param string $username\n\t * @param string $host\n\t * @throws \\Exception\n\t */\n\tpublic function disableUser(string $username, string $host)\n\t{\n\t\t$stmt = Database::prepare('REVOKE ALL PRIVILEGES, GRANT OPTION FROM `' . $username . '`@`' . $host . '`');\n\t\tDatabase::pexecute($stmt, [], false);\n\t}\n\n\t/**\n\t * re-grant permissions to a user\n\t *\n\t * @param string $username\n\t * @param string $host\n\t * @param bool $grant_access_prefix\n\t * @throws \\Exception\n\t */\n\tpublic function enableUser(string $username, string $host, bool $grant_access_prefix = false)\n\t{\n\t\t// check whether user exists to avoid errors\n\t\tif ($this->userExistsOnHost($username, $host)) {\n\t\t\tif (!$grant_access_prefix) {\n\t\t\t\tDatabase::query('GRANT ALL PRIVILEGES ON `' . str_replace('_', '\\_', $username) . '`.* TO `' . $username . '`@`' . $host . '`');\n\t\t\t} else {\n\t\t\t\t$this->grantCreateToCustomerDbs($username, $host);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check whether a given username exists for the given host\n\t *\n\t * @param string $username\n\t * @param string $host\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic function userExistsOnHost(string $username, string $host): bool\n\t{\n\t\t$exist_check_stmt = Database::prepare(\"SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '\" . $username . \"' AND host = '\" . $host . \"')\");\n\t\t$exist_check = Database::pexecute_first($exist_check_stmt);\n\t\treturn ($exist_check && array_pop($exist_check) == '1');\n\t}\n\n\t/**\n\t * flushes the privileges...pretty obvious eh?\n\t */\n\tpublic function flushPrivileges()\n\t{\n\t\tDatabase::query(\"FLUSH PRIVILEGES\");\n\t}\n\n\t/**\n\t * return an array of all usernames used in that DBMS\n\t *\n\t * @param bool $user_only if false, will be selected from mysql.user and slightly different array will be generated\n\t *\n\t * @return array\n\t * @throws \\Exception\n\t */\n\tpublic function getAllSqlUsers(bool $user_only = true): array\n\t{\n\t\tif (!$user_only) {\n\t\t\t$result_stmt = Database::prepare('SELECT * FROM mysql.user');\n\t\t} else {\n\t\t\t$result_stmt = Database::prepare('SELECT `User` FROM mysql.user');\n\t\t}\n\t\tDatabase::pexecute($result_stmt);\n\t\t$allsqlusers = [];\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif ($user_only == false) {\n\t\t\t\tif (!isset($allsqlusers[$row['User']]) || !is_array($allsqlusers[$row['User']])) {\n\t\t\t\t\t$allsqlusers[$row['User']] = [\n\t\t\t\t\t\t'hosts' => []\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\t$allsqlusers[$row['User']]['hosts'][$row['Host']] = [\n\t\t\t\t\t'password' => $row['Password'] ?? $row['authentication_string'],\n\t\t\t\t\t'plugin' => $row['plugin'] ?? 'caching_sha2_password',\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$allsqlusers[] = $row['User'];\n\t\t\t}\n\t\t}\n\t\treturn $allsqlusers;\n\t}\n\n\t/**\n\t * grant \"CREATE\" for prefix user to all existing databases of that customer\n\t *\n\t * @param string $username\n\t * @param string $access_host\n\t * @return void\n\t * @throws \\Exception\n\t */\n\tprivate function grantCreateToCustomerDbs(string $username, string $access_host)\n\t{\n\t\t// remember what (possible remote) db-server we're on\n\t\t$currentDbServer = Database::getServer();\n\t\t// use \"unprivileged\" connection\n\t\tDatabase::needRoot();\n\t\t$cus_stmt = Database::prepare(\"SELECT customerid FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE loginname = :username\");\n\t\t$cust = Database::pexecute_first($cus_stmt, ['username' => $username]);\n\t\tif ($cust) {\n\t\t\t$sel_stmt = Database::prepare(\"SELECT databasename FROM `\" . TABLE_PANEL_DATABASES . \"` WHERE `customerid` = :cid AND `dbserver` = :dbserver\");\n\t\t\tDatabase::pexecute($sel_stmt, ['cid' => $cust['customerid'], 'dbserver' => $currentDbServer]);\n\t\t\t// reset to root-connection for used dbserver\n\t\t\tDatabase::needRoot(true, $currentDbServer, false);\n\t\t\twhile ($dbdata = $sel_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t\t$stmt = Database::prepare(\"\n\t\t\t\t\tGRANT ALL ON `\" . str_replace('_', '\\_', $dbdata['databasename']) . \"`.* TO `\" . $username . \"`@`\" . $access_host . \"`\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($stmt);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * grant \"CREATE\" for prefix user to all existing databases of that customer\n\t *\n\t * @param string $username\n\t * @param string $database\n\t * @param string $access_host\n\t * @return void\n\t * @throws \\Exception\n\t */\n\tpublic function grantCreateToDb(string $username, string $database, string $access_host)\n\t{\n\t\t// only grant permission if the user exists\n\t\tif ($this->userExistsOnHost($username, $access_host)) {\n\t\t\t$stmt = Database::prepare(\"\n\t\t\t\tGRANT ALL ON `\" . str_replace('_', '\\_', $database) . \"`.* TO `\" . $username . \"`@`\" . $access_host . \"`\n\t\t\t\");\n\t\t\tDatabase::pexecute($stmt);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Database/Manager/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Database/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Dns/Dns.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Dns;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Response;\nuse PDO;\n\nclass Dns\n{\n\t/**\n\t * @param int $domain_id\n\t * @param string $area\n\t * @param array $userinfo\n\t *\n\t * @return string|void\n\t * @throws \\Exception\n\t */\n\tpublic static function getAllowedDomainEntry(int $domain_id, string $area = 'customer', array $userinfo = [])\n\t{\n\t\t$dom_data = [\n\t\t\t'did' => $domain_id\n\t\t];\n\n\t\t$where_clause = '';\n\t\tif ($area == 'admin') {\n\t\t\tif ((int)$userinfo['customers_see_all'] == 0) {\n\t\t\t\t$where_clause = '`adminid` = :uid AND ';\n\t\t\t\t$dom_data['uid'] = $userinfo['userid'];\n\t\t\t}\n\t\t} else {\n\t\t\t$where_clause = '`customerid` = :uid AND ';\n\t\t\t$dom_data['uid'] = $userinfo['userid'];\n\t\t}\n\n\t\t$dom_stmt = Database::prepare(\"\n\t\t\tSELECT domain, isbinddomain\n\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\tWHERE \" . $where_clause . \" id = :did\n\t\t\");\n\t\t$domain = Database::pexecute_first($dom_stmt, $dom_data);\n\n\t\tif ($domain) {\n\t\t\tif ($domain['isbinddomain'] != '1') {\n\t\t\t\tResponse::standardError('dns_domain_nodns');\n\t\t\t}\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\treturn $idna_convert->decode($domain['domain']);\n\t\t}\n\t\tResponse::standardError('dns_notfoundorallowed');\n\t}\n\n\t/**\n\t * @param int|array $domain_id id of domain or in case of froxlorhostname, a domain-array with the needed data\n\t * @param bool $froxlorhostname\n\t * @param bool $isMainButSubTo\n\t *\n\t * @return DnsZone|void\n\t * @throws \\Exception\n\t */\n\tpublic static function createDomainZone($domain_id, bool $froxlorhostname = false, bool $isMainButSubTo = false)\n\t{\n\t\tif (!$froxlorhostname) {\n\t\t\t// get domain-name\n\t\t\t$dom_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAINS . \"` WHERE id = :did\");\n\t\t\t$domain = Database::pexecute_first($dom_stmt, [\n\t\t\t\t'did' => $domain_id\n\t\t\t]);\n\t\t} else {\n\t\t\t$domain = $domain_id;\n\t\t}\n\n\t\tif (!isset($domain['isbinddomain']) || $domain['isbinddomain'] != '1') {\n\t\t\treturn;\n\t\t}\n\n\t\t$dom_entries = [];\n\t\tif (!$froxlorhostname) {\n\t\t\t// select all entries\n\t\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_DOMAIN_DNS . \"` WHERE domain_id = :did ORDER BY id ASC\");\n\t\t\tDatabase::pexecute($sel_stmt, [\n\t\t\t\t'did' => $domain_id\n\t\t\t]);\n\t\t\t$dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC);\n\t\t}\n\n\t\t// check for required records\n\t\t$required_entries = [];\n\n\t\tif ($domain['email_only'] == '0') {\n\t\t\tself::addRequiredEntry('@', 'A', $required_entries);\n\t\t\tself::addRequiredEntry('@', 'AAAA', $required_entries);\n\t\t}\n\t\tif (!$isMainButSubTo) {\n\t\t\tself::addRequiredEntry('@', 'NS', $required_entries);\n\t\t}\n\t\tif ($domain['isemaildomain'] == '1') {\n\t\t\tself::addRequiredEntry('@', 'MX', $required_entries);\n\t\t\tif (Settings::Get('system.dns_createmailentry')) {\n\t\t\t\tforeach (['imap', 'pop3', 'mail', 'smtp' ] as $record ) {\n\t\t\t\t\tforeach (['AAAA', 'A' ] as $type ) {\n\t\t\t\t\t\tself::addRequiredEntry($record, $type, $required_entries);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// additional required records by setting\n\t\tif ($domain['email_only'] == '0') {\n\t\t\tif ($domain['iswildcarddomain'] == '1') {\n\t\t\t\tself::addRequiredEntry('*', 'A', $required_entries);\n\t\t\t\tself::addRequiredEntry('*', 'AAAA', $required_entries);\n\t\t\t} elseif ($domain['wwwserveralias'] == '1') {\n\t\t\t\tself::addRequiredEntry('www', 'A', $required_entries);\n\t\t\t\tself::addRequiredEntry('www', 'AAAA', $required_entries);\n\t\t\t}\n\t\t}\n\n\t\tif (!$froxlorhostname) {\n\t\t\t// additional required records for subdomains\n\t\t\t$subdomains_stmt = Database::prepare(\"\n\t\t\t\tSELECT `domain`, `iswildcarddomain`, `wwwserveralias`, `isemaildomain`, `email_only` FROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\t\tWHERE `parentdomainid` = :domainid\n\t\t\t\");\n\t\t\tDatabase::pexecute($subdomains_stmt, [\n\t\t\t\t'domainid' => $domain_id\n\t\t\t]);\n\n\t\t\twhile ($subdomain = $subdomains_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$sub_record = str_replace('.' . $domain['domain'], '', $subdomain['domain']);\n\t\t\t\t// Listing domains is enough as there currently is no support for choosing\n\t\t\t\t// different ips for a subdomain => use same IPs as toplevel\n\t\t\t\tif ($subdomain['email_only'] == '0') {\n\t\t\t\t\tself::addRequiredEntry($sub_record, 'A', $required_entries);\n\t\t\t\t\tself::addRequiredEntry($sub_record, 'AAAA', $required_entries);\n\n\t\t\t\t\t// Check whether to add a www.-prefix\n\t\t\t\t\tif ($subdomain['iswildcarddomain'] == '1') {\n\t\t\t\t\t\tself::addRequiredEntry('*.' . $sub_record, 'A', $required_entries);\n\t\t\t\t\t\tself::addRequiredEntry('*.' . $sub_record, 'AAAA', $required_entries);\n\t\t\t\t\t} elseif ($subdomain['wwwserveralias'] == '1') {\n\t\t\t\t\t\tself::addRequiredEntry('www.' . $sub_record, 'A', $required_entries);\n\t\t\t\t\t\tself::addRequiredEntry('www.' . $sub_record, 'AAAA', $required_entries);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// check for email ability\n\t\t\t\tif ($subdomain['isemaildomain'] == '1') {\n\t\t\t\t\tif (Settings::Get('spf.use_spf') == '1') {\n\t\t\t\t\t\t// check for SPF content later\n\t\t\t\t\t\tself::addRequiredEntry('@SPF@.' . $sub_record, 'TXT', $required_entries);\n\t\t\t\t\t}\n\t\t\t\t\tif (Settings::Get('dmarc.use_dmarc') == '1') {\n\t\t\t\t\t\t// check for DMARC content later\n\t\t\t\t\t\tself::addRequiredEntry('@DMARC@.' . $sub_record, 'TXT', $required_entries);\n\t\t\t\t\t}\n\t\t\t\t\tif (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1') {\n\t\t\t\t\t\t// check for DKIM content later\n\t\t\t\t\t\tself::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey.' . $sub_record, 'TXT', $required_entries);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// additional required records for CAA if activated\n\t\tif (Settings::Get('system.dns_createcaaentry') && Settings::Get('system.use_ssl') == \"1\") {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT i.`ip`, i.`port`, i.`ssl`\n\t\t\t\tFROM \" . TABLE_PANEL_IPSANDPORTS . \" i\n\t\t\t\tLEFT JOIN \" . TABLE_DOMAINTOIP . \" dip ON dip.id_ipandports = i.id\n\t\t\t\tWHERE i.ssl = 1 AND dip.id_domain = :domainid\n\t\t\t\");\n\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t'domainid' => $domain['id']\n\t\t\t]);\n\n\t\t\t$ssl_ipandports = [];\n\t\t\twhile ($ssl_ipandport = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t$ssl_ipandports[] = $ssl_ipandport;\n\t\t\t}\n\n\t\t\tif (!empty($ssl_ipandports)) {\n\t\t\t\t// check for CAA content later\n\t\t\t\tself::addRequiredEntry('@CAA@', 'CAA', $required_entries);\n\t\t\t}\n\t\t}\n\n\t\t// additional required records for SPF and DKIM if activated\n\t\tif ($domain['isemaildomain'] == '1') {\n\t\t\tif (Settings::Get('spf.use_spf') == '1') {\n\t\t\t\t// check for SPF content later\n\t\t\t\tself::addRequiredEntry('@SPF@', 'TXT', $required_entries);\n\t\t\t}\n\t\t\tif (Settings::Get('dmarc.use_dmarc') == '1') {\n\t\t\t\t// check for DMARC content later\n\t\t\t\tself::addRequiredEntry('@DMARC@', 'TXT', $required_entries);\n\t\t\t}\n\t\t\tif (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1') {\n\t\t\t\t// check for DKIM content later\n\t\t\t\tself::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey', 'TXT', $required_entries);\n\t\t\t}\n\t\t}\n\n\t\t$primary_ns = null;\n\t\t$zonerecords = [];\n\n\t\t// now generate all records and unset the required entries we have\n\t\tforeach ($dom_entries as $entry) {\n\t\t\tif (array_key_exists($entry['type'], $required_entries)\n\t\t\t\t&& array_key_exists( md5($entry['record']), $required_entries[$entry['type']])\n\t\t\t) {\n\t\t\t\tunset($required_entries[$entry['type']][md5($entry['record'])]);\n\t\t\t}\n\t\t\tif (Settings::Get('system.dns_createcaaentry') == '1'\n\t\t\t\t&& $entry['type'] == 'CAA'\n\t\t\t\t&& strtolower(substr($entry['content'], 0, 7 )) == '\"v=caa1'\n\t\t\t) {\n\t\t\t\t// unset special CAA required-entry\n\t\t\t\tunset($required_entries[$entry['type']][md5(\"@CAA@\")]);\n\t\t\t}\n\t\t\tif (Settings::Get('spf.use_spf') == '1'\n\t\t\t\t&& $entry['type'] == 'TXT'\n\t\t\t\t&& (strtolower(substr($entry['content'], 0, 7)) == '\"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')\n\t\t\t) {\n\t\t\t\t// unset special spf required-entry\n\t\t\t\tif ($entry['record'] == '@') {\n\t\t\t\t\tunset($required_entries[$entry['type']][md5(\"@SPF@\")]);\n\t\t\t\t} else {\n\t\t\t\t\t// subdomain\n\t\t\t\t\tunset($required_entries[$entry['type']][md5(\"@SPF@.\" . $entry['record'])]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (Settings::Get('dmarc.use_dmarc') == '1'\n\t\t\t\t&& $entry['type'] == 'TXT'\n\t\t\t\t&& ($entry['record'] == '_dmarc' || substr($entry['record'], 0, 7) == '_dmarc.')\n\t\t\t\t&& (strtolower(substr($entry['content'], 0, 9)) == '\"v=dmarc1' || strtolower(substr($entry['content'], 0, 8)) == 'v=dmarc1')\n\t\t\t) {\n\t\t\t\t// unset special dmarc required-entry\n\t\t\t\tif ($entry['record'] == '_dmarc') {\n\t\t\t\t\tunset($required_entries[$entry['type']][md5(\"@DMARC@\")]);\n\t\t\t\t} else {\n\t\t\t\t\t// subdomain\n\t\t\t\t\tunset($required_entries[$entry['type']][md5(\"@DMARC@\" . substr($entry['record'], 6))]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (empty($primary_ns) && $entry['record'] == '@' && $entry['type'] == 'NS') {\n\t\t\t\t// use the first NS entry pertaining to the current domain as primary ns\n\t\t\t\t$primary_ns = $entry['content'];\n\t\t\t}\n\t\t\t// check for CNAME on @, www- or wildcard-Alias and remove A/AAAA record accordingly\n\t\t\tforeach (['@', 'www', '*'] as $crecord) {\n\t\t\t\tif ($entry['type'] == 'CNAME'\n\t\t\t\t\t&& $entry['record'] == '@'\n\t\t\t\t\t&& (array_key_exists(md5($crecord), $required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))\n\t\t\t\t) {\n\t\t\t\t\tunset($required_entries['A'][md5($crecord)]);\n\t\t\t\t\tunset($required_entries['AAAA'][md5($crecord)]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// also allow overriding of auto-generated values (imap,pop3,mail,smtp) if enabled in the settings\n\t\t\tif (Settings::Get('system.dns_createmailentry')) {\n\t\t\t\tforeach (['imap', 'pop3', 'mail', 'smtp'] as $crecord) {\n\t\t\t\t\tif ($entry['type'] == 'CNAME'\n\t\t\t\t\t\t&& $entry['record'] == $crecord\n\t\t\t\t\t\t&& (array_key_exists(md5($crecord), $required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))\n\t\t\t\t\t) {\n\t\t\t\t\t\tunset($required_entries['A'][md5($crecord)]);\n\t\t\t\t\t\tunset($required_entries['AAAA'][md5($crecord)]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t$zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'] ?? 0, $entry['ttl']);\n\t\t}\n\n\t\t// add missing required entries\n\t\tif (!empty($required_entries)) {\n\t\t\t// A / AAAA records\n\t\t\tif (array_key_exists(\"A\", $required_entries) || array_key_exists(\"AAAA\", $required_entries)) {\n\t\t\t\tif ($froxlorhostname) {\n\t\t\t\t\t// use all available IP's for the froxlor-hostname\n\t\t\t\t\t$result_ip_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `ip` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` GROUP BY `ip`\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($result_ip_stmt);\n\t\t\t\t} else {\n\t\t\t\t\t$result_ip_stmt = Database::prepare(\"\n\t\t\t\t\t\tSELECT `p`.`ip` AS `ip`\n\t\t\t\t\t\tFROM `\" . TABLE_PANEL_IPSANDPORTS . \"` `p`, `\" . TABLE_DOMAINTOIP . \"` `di`\n\t\t\t\t\t\tWHERE `di`.`id_domain` = :domainid AND `p`.`id` = `di`.`id_ipandports`\n\t\t\t\t\t\tGROUP BY `p`.`ip`;\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($result_ip_stmt, [\n\t\t\t\t\t\t'domainid' => $domain_id\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t\t$all_ips = $result_ip_stmt->fetchAll(PDO::FETCH_ASSOC);\n\n\t\t\t\tforeach ($all_ips as $ip) {\n\t\t\t\t\tforeach ($required_entries as $type => $records) {\n\t\t\t\t\t\tforeach ($records as $record) {\n\t\t\t\t\t\t\tif ($type == 'A' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {\n\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry($record, 'A', $ip['ip']);\n\t\t\t\t\t\t\t} elseif ($type == 'AAAA' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) !== false) {\n\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry($record, 'AAAA', $ip['ip']);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tunset($required_entries['A']);\n\t\t\t\tunset($required_entries['AAAA']);\n\t\t\t}\n\n\t\t\t// NS records\n\t\t\tif (array_key_exists(\"NS\", $required_entries)) {\n\t\t\t\tif (Settings::Get('system.nameservers') != '') {\n\t\t\t\t\t$nameservers = explode(',', Settings::Get('system.nameservers'));\n\t\t\t\t\tforeach ($nameservers as $nameserver) {\n\t\t\t\t\t\t$nameserver = trim($nameserver);\n\t\t\t\t\t\t// append dot to hostname\n\t\t\t\t\t\tif (substr($nameserver, -1, 1) != '.') {\n\t\t\t\t\t\t\t$nameserver .= '.';\n\t\t\t\t\t\t}\n\t\t\t\t\t\tforeach ($required_entries as $type => $records) {\n\t\t\t\t\t\t\tif ($type == 'NS') {\n\t\t\t\t\t\t\t\tforeach ($records as $record) {\n\t\t\t\t\t\t\t\t\tif (empty($primary_ns)) {\n\t\t\t\t\t\t\t\t\t\t// use the first NS entry as primary ns\n\t\t\t\t\t\t\t\t\t\t$primary_ns = $nameserver;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry($record, 'NS', $nameserver);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tunset($required_entries['NS']);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// MX records\n\t\t\tif (array_key_exists(\"MX\", $required_entries)) {\n\t\t\t\tif (Settings::Get('system.mxservers') != '') {\n\t\t\t\t\t$mxservers = explode(',', Settings::Get('system.mxservers'));\n\t\t\t\t\tforeach ($mxservers as $mxserver) {\n\t\t\t\t\t\t$mxserver = trim($mxserver);\n\t\t\t\t\t\tif (substr($mxserver, -1, 1) != '.') {\n\t\t\t\t\t\t\t$mxserver .= '.';\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// split in prio and server\n\t\t\t\t\t\t$mx_details = explode(\" \", $mxserver);\n\t\t\t\t\t\tif (count($mx_details) == 1) {\n\t\t\t\t\t\t\t$mx_details[1] = $mx_details[0];\n\t\t\t\t\t\t\t$mx_details[0] = 10;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tforeach ($required_entries as $type => $records) {\n\t\t\t\t\t\t\tif ($type == 'MX') {\n\t\t\t\t\t\t\t\tforeach ($records as $record) {\n\t\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry($record, 'MX', $mx_details[1], $mx_details[0]);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tunset($required_entries['MX']);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// TXT (SPF and DKIM)\n\t\t\tif (array_key_exists(\"TXT\", $required_entries)) {\n\t\t\t\t$dkim_entries = self::generateDkimEntries($domain);\n\n\t\t\t\tforeach ($required_entries as $type => $records) {\n\t\t\t\t\tif ($type == 'TXT') {\n\t\t\t\t\t\tforeach ($records as $record) {\n\t\t\t\t\t\t\tif ($record == '@SPF@') {\n\t\t\t\t\t\t\t\t// spf for main-domain\n\t\t\t\t\t\t\t\t$txt_content = Settings::Get('spf.spf_entry');\n\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry('@', 'TXT', self::encloseTXTContent($txt_content));\n\t\t\t\t\t\t\t} elseif (strlen($record) > 6 && substr($record, 0, 6) == '@SPF@.') {\n\t\t\t\t\t\t\t\t// spf for subdomain\n\t\t\t\t\t\t\t\t$txt_content = Settings::Get('spf.spf_entry');\n\t\t\t\t\t\t\t\t$sub_record = substr($record, 6);\n\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry($sub_record, 'TXT', self::encloseTXTContent($txt_content));\n\t\t\t\t\t\t\t} elseif ($record == '@DMARC@') {\n\t\t\t\t\t\t\t\t// dmarc for main-domain\n\t\t\t\t\t\t\t\t$txt_content = Settings::Get('dmarc.dmarc_entry');\n\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry('_dmarc', 'TXT', self::encloseTXTContent($txt_content));\n\t\t\t\t\t\t\t} elseif (strlen($record) > 8 && substr($record, 0, 8) == '@DMARC@.') {\n\t\t\t\t\t\t\t\t// dmarc for subdomain\n\t\t\t\t\t\t\t\t$txt_content = Settings::Get('dmarc.dmarc_entry');\n\t\t\t\t\t\t\t\t$sub_record = substr($record, 8);\n\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry('_dmarc.' . $sub_record, 'TXT', self::encloseTXTContent($txt_content));\n\t\t\t\t\t\t\t} elseif (!empty($dkim_entries)) {\n\t\t\t\t\t\t\t\t// DKIM entries\n\t\t\t\t\t\t\t\t$dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey';\n\t\t\t\t\t\t\t\tif ($record == $dkim_record) {\n\t\t\t\t\t\t\t\t\t// dkim for main-domain\n\t\t\t\t\t\t\t\t\t// check for multiline entry\n\t\t\t\t\t\t\t\t\t$multiline = false;\n\t\t\t\t\t\t\t\t\tif (substr($dkim_entries[0], 0, 1) == '(') {\n\t\t\t\t\t\t\t\t\t\t$multiline = true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));\n\t\t\t\t\t\t\t\t} elseif (strlen($record) > strlen($dkim_record) && substr($record, 0, strlen($dkim_record) + 1) == $dkim_record . '.') {\n\t\t\t\t\t\t\t\t\t// dkim for subdomain-domain\n\t\t\t\t\t\t\t\t\t// check for multiline entry\n\t\t\t\t\t\t\t\t\t$multiline = false;\n\t\t\t\t\t\t\t\t\tif (substr($dkim_entries[0], 0, 1) == '(') {\n\t\t\t\t\t\t\t\t\t\t$multiline = true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// CAA\n\t\t\tif (array_key_exists(\"CAA\", $required_entries)) {\n\t\t\t\tforeach ($required_entries as $type => $records) {\n\t\t\t\t\tif ($type == 'CAA') {\n\t\t\t\t\t\tforeach ($records as $record) {\n\t\t\t\t\t\t\tif ($record == '@CAA@') {\n\t\t\t\t\t\t\t\t$caa_entries = explode(PHP_EOL, Settings::Get('caa.caa_entry'));\n\t\t\t\t\t\t\t\t$caa_domain = \"letsencrypt.org\";\n\t\t\t\t\t\t\t\tif (Settings::Get('system.letsencryptca') == 'buypass' || Settings::Get('system.letsencryptca') == 'buypass_test') {\n\t\t\t\t\t\t\t\t\t$caa_domain = \"buypass.com\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif ($domain['letsencrypt'] == 1) {\n\t\t\t\t\t\t\t\t\tif (Settings::Get('system.letsencryptca') == 'zerossl') {\n\t\t\t\t\t\t\t\t\t\t$caa_domains = [\n\t\t\t\t\t\t\t\t\t\t\t\"sectigo.com\",\n\t\t\t\t\t\t\t\t\t\t\t\"trust-provider.com\",\n\t\t\t\t\t\t\t\t\t\t\t\"usertrust.com\",\n\t\t\t\t\t\t\t\t\t\t\t\"comodoca.com\",\n\t\t\t\t\t\t\t\t\t\t\t\"comodo.com\"\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\t\tforeach ($caa_domains as $caa_domain) {\n\t\t\t\t\t\t\t\t\t\t\t$le_entry = $domain['iswildcarddomain'] == '1' ? '0 issuewild \"' . $caa_domain . '\"' : '0 issue \"' . $caa_domain . '\"';\n\t\t\t\t\t\t\t\t\t\t\tarray_push($caa_entries, $le_entry);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t$le_entry = $domain['iswildcarddomain'] == '1' ? '0 issuewild \"' . $caa_domain . '\"' : '0 issue \"' . $caa_domain . '\"';\n\t\t\t\t\t\t\t\t\t\tarray_push($caa_entries, $le_entry);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tforeach ($caa_entries as $entry) {\n\t\t\t\t\t\t\t\t\tif (empty($entry)) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry('@', 'CAA', $entry);\n\t\t\t\t\t\t\t\t\t// additional required records by subdomain setting\n\t\t\t\t\t\t\t\t\tif ($domain['wwwserveralias'] == '1') {\n\t\t\t\t\t\t\t\t\t\t$zonerecords[] = new DnsEntry('www', 'CAA', $entry);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (empty($primary_ns)) {\n\t\t\t// TODO log error: no NS given, use system-hostname\n\t\t\t$primary_ns = Settings::Get('system.hostname');\n\t\t}\n\n\t\tif (!$isMainButSubTo) {\n\t\t\t$date = date('Ymd');\n\t\t\t$domain['bindserial'] = (preg_match('/^' . $date . '/', $domain['bindserial']) ? $domain['bindserial'] + 1 : $date . '00');\n\t\t\tif (!$froxlorhostname) {\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t\t`bindserial` = :serial\n\t\t\t\t\t WHERE `id` = :id\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'serial' => $domain['bindserial'],\n\t\t\t\t\t'id' => $domain['id']\n\t\t\t\t]);\n\t\t\t}\n\n\t\t\t// PowerDNS does not like multi-line-format\n\t\t\t$soa_email = Settings::Get('system.soaemail');\n\t\t\tif ($soa_email == \"\") {\n\t\t\t\t$soa_email = Settings::Get('panel.adminmail');\n\t\t\t}\n\t\t\t$soa_content = $primary_ns . \" \" . self::escapeSoaAdminMail($soa_email) . \" \";\n\t\t\t$soa_content .= $domain['bindserial'] . \" \";\n\t\t\t// TODO for now, dummy time-periods\n\t\t\t$soa_content .= \"3600 900 1209600 1200\";\n\n\t\t\t$soa_record = new DnsEntry('@', 'SOA', $soa_content);\n\t\t\tarray_unshift($zonerecords, $soa_record);\n\t\t}\n\n\t\t$zone = new DnsZone(\n\t\t\t(int)Settings::Get('system.defaultttl'),\n\t\t\t$domain['domain'],\n\t\t\t$domain['bindserial'],\n\t\t\t$zonerecords\n\t\t);\n\n\t\treturn $zone;\n\t}\n\n\t/**\n\t * @param string $record\n\t * @param string $type\n\t * @param array $required\n\t * @return void\n\t */\n\tprivate static function addRequiredEntry(string $record = '@', string $type = 'A', array &$required = [])\n\t{\n\t\tif (!isset($required[$type])) {\n\t\t\t$required[$type] = [];\n\t\t}\n\t\t$required[$type][md5($record)] = $record;\n\t}\n\n\t/**\n\t * @param array $domain\n\t * @return array\n\t */\n\tprivate static function generateDkimEntries(array $domain): array\n\t{\n\t\t$zone_dkim = [];\n\n\t\tif (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1' && $domain['dkim_pubkey'] != '') {\n\t\t\t// start\n\t\t\t$dkim_txt = 'v=DKIM1;';\n\t\t\t// key\n\t\t\t$dkim_txt .= 'k=rsa;p=' . trim($domain['dkim_pubkey']) . ';';\n\t\t\t// dkim-entry\n\t\t\t$zone_dkim[] = $dkim_txt;\n\t\t}\n\n\t\treturn $zone_dkim;\n\t}\n\n\t/**\n\t * @param string $txt_content\n\t * @param bool $isMultiLine\n\t * @return string\n\t */\n\tpublic static function encloseTXTContent(string $txt_content, bool $isMultiLine = false): string\n\t{\n\t\t// check that TXT content is enclosed in \" \"\n\t\tif (!$isMultiLine && Settings::Get('system.dns_server') != 'PowerDNS') {\n\t\t\tif (substr($txt_content, 0, 1) != '\"') {\n\t\t\t\t$txt_content = '\"' . $txt_content;\n\t\t\t}\n\t\t\tif (substr($txt_content, -1) != '\"') {\n\t\t\t\t$txt_content .= '\"';\n\t\t\t}\n\t\t}\n\t\tif (Settings::Get('system.dns_server') == 'PowerDNS') {\n\t\t\t// no quotation for PowerDNS\n\t\t\tif (substr($txt_content, 0, 1) == '\"') {\n\t\t\t\t$txt_content = substr($txt_content, 1);\n\t\t\t}\n\t\t\tif (substr($txt_content, -1) == '\"') {\n\t\t\t\t$txt_content = substr($txt_content, 0, -1);\n\t\t\t}\n\t\t}\n\t\treturn $txt_content;\n\t}\n\n\t/**\n\t * @param string $email\n\t * @return string\n\t */\n\tprivate static function escapeSoaAdminMail(string $email): string\n\t{\n\t\t$mail_parts = explode(\"@\", $email);\n\t\treturn str_replace(\".\", \"\\.\", $mail_parts[0]) . \".\" . $mail_parts[1] . \".\";\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Dns/DnsEntry.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Dns;\n\nuse Froxlor\\Settings;\n\nclass DnsEntry\n{\n\tpublic string $record;\n\tpublic int $ttl;\n\tpublic string $class = 'IN';\n\tpublic string $type;\n\tpublic int $priority;\n\tpublic ?string $content;\n\n\t/**\n\t * @param string $record\n\t * @param string $type\n\t * @param string|null $content\n\t * @param int $prio\n\t * @param int $ttl\n\t * @param string $class\n\t */\n\tpublic function __construct(string $record = '', string $type = 'A', string $content = null, int $prio = 0, int $ttl = 0, string $class = 'IN')\n\t{\n\t\t$this->record = $record;\n\t\t$this->type = $type;\n\t\t$this->content = $content;\n\t\t$this->priority = $prio;\n\t\t$this->ttl = ($ttl <= 0 ? Settings::Get('system.defaultttl') : $ttl);\n\t\t$this->class = $class;\n\t}\n\n\tpublic function __toString()\n\t{\n\t\t$_content = $this->content;\n\t\t// check content length for txt records for bind9 (multiline)\n\t\tif (Settings::Get('system.dns_server') != 'pdns' && $this->type == 'TXT' && strlen($_content) >= 255) {\n\t\t\t// split string\n\t\t\t$_contentlines = str_split($_content, 254);\n\t\t\t// first line\n\t\t\t$_l = array_shift($_contentlines);\n\t\t\t// check for starting quote\n\t\t\tif (substr($_l, 0, 1) == '\"') {\n\t\t\t\t$_l = substr($_l, 1);\n\t\t\t}\n\t\t\t$_content = '(\"' . $_l . '\"' . PHP_EOL;\n\t\t\t$_l = array_pop($_contentlines);\n\t\t\t// check for ending quote\n\t\t\tif (substr($_l, -1) == '\"') {\n\t\t\t\t$_l = substr($_l, 0, -1);\n\t\t\t}\n\t\t\tforeach ($_contentlines as $_cl) {\n\t\t\t\t// lines in between\n\t\t\t\t$_content .= \"\\t\\t\\t\\t\" . '\"' . $_cl . '\"' . PHP_EOL;\n\t\t\t}\n\t\t\t// last line\n\t\t\t$_content .= \"\\t\\t\\t\\t\" . '\"' . $_l . '\")';\n\t\t}\n\t\treturn $this->record . \"\\t\" . $this->ttl . \"\\t\" . $this->class . \"\\t\" . $this->type . \"\\t\" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . \"\\t\" : \"\") . $_content . PHP_EOL;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Dns/DnsZone.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Dns;\n\nuse Froxlor\\Settings;\n\nclass DnsZone\n{\n\tpublic int $ttl;\n\tpublic string $origin;\n\tpublic string $serial;\n\tpublic ?array $records;\n\n\t/**\n\t * @param int $ttl\n\t * @param string $origin\n\t * @param string $serial\n\t * @param array|null $records\n\t */\n\tpublic function __construct(int $ttl = 0, string $origin = '', string $serial = '', array $records = null)\n\t{\n\t\t$this->ttl = ($ttl <= 0 ? Settings::Get('system.defaultttl') : $ttl);\n\t\t$this->origin = $origin;\n\t\t$this->serial = $serial;\n\t\t$this->records = $records;\n\t}\n\n\tpublic function __toString()\n\t{\n\t\t$zone_file = \"\\$TTL \" . $this->ttl . PHP_EOL;\n\t\t$zone_file .= \"\\$ORIGIN \" . $this->origin . \".\" . PHP_EOL;\n\t\tif (!empty($this->records)) {\n\t\t\tforeach ($this->records as $record) {\n\t\t\t\t$zone_file .= (string)$record;\n\t\t\t}\n\t\t}\n\t\treturn $zone_file;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Dns/PowerDNS.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Dns;\n\nuse Froxlor\\FileDir;\nuse Froxlor\\Settings;\nuse PDO;\nuse PDOException;\n\nclass PowerDNS\n{\n\tprivate static $pdns_db = null;\n\n\t/**\n\t * remove all records and entries of a given domain\n\t *\n\t * @param string|null $domain\n\t */\n\tpublic static function cleanDomainZone(string $domain = null)\n\t{\n\t\tif (!empty($domain)) {\n\t\t\t$pdns_domains_stmt = self::getDB()->prepare(\"SELECT `id`, `name` FROM `domains` WHERE `name` = :domain\");\n\t\t\t$del_rec_stmt = self::getDB()->prepare(\"DELETE FROM `records` WHERE `domain_id` = :did\");\n\t\t\t$del_meta_stmt = self::getDB()->prepare(\"DELETE FROM `domainmetadata` WHERE `domain_id` = :did\");\n\t\t\t$del_dom_stmt = self::getDB()->prepare(\"DELETE FROM `domains` WHERE `id` = :did\");\n\n\t\t\t$pdns_domains_stmt->execute([\n\t\t\t\t'domain' => $domain\n\t\t\t]);\n\t\t\t$pdns_domain = $pdns_domains_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t\t$del_rec_stmt->execute([\n\t\t\t\t'did' => $pdns_domain['id']\n\t\t\t]);\n\t\t\t$del_meta_stmt->execute([\n\t\t\t\t'did' => $pdns_domain['id']\n\t\t\t]);\n\t\t\t$del_dom_stmt->execute([\n\t\t\t\t'did' => $pdns_domain['id']\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * get pdo database connection to powerdns database\n\t *\n\t * @return \\PDO\n\t */\n\tpublic static function getDB(): \\PDO\n\t{\n\t\tif (!isset(self::$pdns_db) || !(self::$pdns_db instanceof PDO)) {\n\t\t\tself::connectToPdnsDb();\n\t\t}\n\t\treturn self::$pdns_db;\n\t}\n\n\t/**\n\t * @return void\n\t */\n\tprivate static function connectToPdnsDb()\n\t{\n\t\t// get froxlor pdns config\n\t\t$cf = Settings::Get('system.bindconf_directory') . '/froxlor/pdns_froxlor.conf';\n\t\t$config = FileDir::makeCorrectFile($cf);\n\n\t\tif (!file_exists($config)) {\n\t\t\tdie('PowerDNS configuration file (' . $config . ') not found. Did you go through the configuration templates?' . PHP_EOL);\n\t\t}\n\t\t$lines = file($config);\n\t\t$mysql_data = [];\n\t\tforeach ($lines as $line) {\n\t\t\t$line = trim($line);\n\t\t\tif (strtolower(substr($line, 0, 6)) == 'gmysql') {\n\t\t\t\t$namevalue = explode(\"=\", $line);\n\t\t\t\t$mysql_data[$namevalue[0]] = $namevalue[1];\n\t\t\t}\n\t\t}\n\n\t\t// build up connection string\n\t\t$driver = 'mysql';\n\t\t$dsn = $driver . \":\";\n\t\t$options = [\n\t\t\t'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8'\n\t\t];\n\t\t$attributes = [\n\t\t\t'ATTR_ERRMODE' => 'ERRMODE_EXCEPTION'\n\t\t];\n\t\t$dbconf = [];\n\n\t\t$dbconf[\"dsn\"] = [\n\t\t\t'dbname' => $mysql_data[\"gmysql-dbname\"],\n\t\t\t'charset' => 'utf8'\n\t\t];\n\n\t\tif (isset($mysql_data['gmysql-socket']) && !empty($mysql_data['gmysql-socket'])) {\n\t\t\t$dbconf[\"dsn\"]['unix_socket'] = FileDir::makeCorrectFile($mysql_data['gmysql-socket']);\n\t\t} else {\n\t\t\t$dbconf[\"dsn\"]['host'] = $mysql_data['gmysql-host'];\n\t\t\t$dbconf[\"dsn\"]['port'] = $mysql_data['gmysql-port'];\n\n\t\t\tif (!empty($mysql_data['gmysql-ssl-ca-file'])) {\n\t\t\t\t$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_data['gmysql-ssl-ca-file'];\n\t\t\t\t$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_data['gmysql-ssl-verify-server-certificate'];\n\t\t\t}\n\t\t}\n\n\t\t// add options to dsn-string\n\t\tforeach ($dbconf[\"dsn\"] as $k => $v) {\n\t\t\t$dsn .= $k . \"=\" . $v . \";\";\n\t\t}\n\n\t\t// clean up\n\t\tunset($dbconf);\n\n\t\t// try to connect\n\t\ttry {\n\t\t\tself::$pdns_db = new PDO($dsn, $mysql_data['gmysql-user'], $mysql_data['gmysql-password'], $options);\n\t\t} catch (PDOException $e) {\n\t\t\tdie($e->getMessage());\n\t\t}\n\n\t\t// set attributes\n\t\tforeach ($attributes as $k => $v) {\n\t\t\tself::$pdns_db->setAttribute(constant(\"PDO::\" . $k), constant(\"PDO::\" . $v));\n\t\t}\n\n\t\t$version_server = self::$pdns_db->getAttribute(PDO::ATTR_SERVER_VERSION);\n\t\t$sql_mode = 'NO_ENGINE_SUBSTITUTION';\n\t\tif (version_compare($version_server, '8.0.11', '<')) {\n\t\t\t$sql_mode .= ',NO_AUTO_CREATE_USER';\n\t\t}\n\t\tself::$pdns_db->exec('SET sql_mode = \"' . $sql_mode . '\"');\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Dns/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Domain/Domain.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Domain;\n\nuse Froxlor\\Cron\\Http\\LetsEncrypt\\AcmeSh;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass Domain\n{\n\n\t/**\n\t * return all ip addresses associated with given domain,\n\t * returns all ips if domain-id = 0 (froxlor.vhost)\n\t *\n\t * @param int $domain_id\n\t * @return array\n\t * @throws \\Exception\n\t */\n\tpublic static function getIpsOfDomain(int $domain_id = 0): array\n\t{\n\t\tif ($domain_id > 0) {\n\t\t\t$sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT i.ip FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` `i`\n\t\t\t\tLEFT JOIN `\" . TABLE_DOMAINTOIP . \"` `dip` ON dip.id_ipandports = i.id\n\t\t\t\tAND dip.id_domain = :domainid\n\t\t\t\tGROUP BY i.ip\n\t\t\t\");\n\t\t\t$sel_param = [\n\t\t\t\t'domainid' => $domain_id\n\t\t\t];\n\t\t} else {\n\t\t\t// assuming froxlor.vhost (id = 0)\n\t\t\t$sel_stmt = Database::prepare(\"\n\t\t\t\tSELECT ip FROM `\" . TABLE_PANEL_IPSANDPORTS . \"`\n\t\t\t\tGROUP BY ip\n\t\t\t\");\n\t\t\t$sel_param = [];\n\t\t}\n\t\tDatabase::pexecute($sel_stmt, $sel_param);\n\t\t$result = [];\n\t\twhile ($ip = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$result[] = $ip['ip'];\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * return an array of all enabled redirect-codes\n\t *\n\t * @return array array of enabled redirect-codes\n\t */\n\tpublic static function getRedirectCodesArray(): array\n\t{\n\t\t$sql = \"SELECT * FROM `\" . TABLE_PANEL_REDIRECTCODES . \"` WHERE `enabled` = '1' ORDER BY `id` ASC\";\n\t\t$result_stmt = Database::query($sql);\n\n\t\t$codes = [];\n\t\twhile ($rc = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$codes[] = $rc;\n\t\t}\n\n\t\treturn $codes;\n\t}\n\n\t/**\n\t * returns the redirect-code for a given\n\t * domain-id\n\t *\n\t * @param int $domainid id of the domain\n\t *\n\t * @return string redirect-code\n\t * @throws \\Exception\n\t */\n\tpublic static function getDomainRedirectCode(int $domainid = 0): string\n\t{\n\t\t// get system default\n\t\t$default = '301';\n\t\tif (Settings::Get('customredirect.enabled') == '1') {\n\t\t\t$all_codes = self::getRedirectCodes(false);\n\t\t\t$_default = $all_codes[Settings::Get('customredirect.default')];\n\t\t\t$default = ($_default == '---') ? $default : $_default;\n\t\t}\n\t\t$code = $default;\n\t\tif ($domainid > 0) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `r`.`code` as `redirect`\n\t\t\t\tFROM `\" . TABLE_PANEL_REDIRECTCODES . \"` `r`, `\" . TABLE_PANEL_DOMAINREDIRECTS . \"` `rc`\n\t\t\t\tWHERE `r`.`id` = `rc`.`rid` and `rc`.`did` = :domainid\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t'domainid' => $domainid\n\t\t\t]);\n\n\t\t\tif (is_array($result) && isset($result['redirect'])) {\n\t\t\t\t$code = ($result['redirect'] == '---') ? $default : $result['redirect'];\n\t\t\t}\n\t\t}\n\t\treturn $code;\n\t}\n\n\t/**\n\t * return an array of all enabled redirect-codes\n\t * for the settings form\n\t *\n\t * @param bool $add_desc optional, default true, add the code-description\n\t *\n\t * @return array array of enabled redirect-codes\n\t */\n\tpublic static function getRedirectCodes(bool $add_desc = true): array\n\t{\n\t\t$sql = \"SELECT * FROM `\" . TABLE_PANEL_REDIRECTCODES . \"` WHERE `enabled` = '1' ORDER BY `id` ASC\";\n\t\t$result_stmt = Database::query($sql);\n\n\t\t$codes = [];\n\t\twhile ($rc = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$codes[$rc['id']] = $rc['code'];\n\t\t\tif ($add_desc) {\n\t\t\t\t$codes[$rc['id']] .= ' (' . lng('redirect_desc.' . $rc['desc']) . ')';\n\t\t\t}\n\t\t}\n\n\t\treturn $codes;\n\t}\n\n\t/**\n\t * returns the redirect-id for a given\n\t * domain-id\n\t *\n\t * @param int $domainid id of the domain\n\t *\n\t * @return int redirect-code-id\n\t * @throws \\Exception\n\t */\n\tpublic static function getDomainRedirectId(int $domainid = 0): int\n\t{\n\t\t$code = 1;\n\t\tif ($domainid > 0) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `r`.`id` as `redirect`\n\t\t\t\tFROM `\" . TABLE_PANEL_REDIRECTCODES . \"` `r`, `\" . TABLE_PANEL_DOMAINREDIRECTS . \"` `rc`\n\t\t\t\tWHERE `r`.`id` = `rc`.`rid` and `rc`.`did` = :domainid\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t'domainid' => $domainid\n\t\t\t]);\n\n\t\t\tif ($result && isset($result['redirect'])) {\n\t\t\t\t$code = (int)$result['redirect'];\n\t\t\t}\n\t\t}\n\t\treturn $code;\n\t}\n\n\t/**\n\t * adds a redirect-code for a domain\n\t *\n\t * @param int $domainid id of the domain to add the code for\n\t * @param int $redirect selected redirect-id\n\t *\n\t * @return null\n\t * @throws \\Exception\n\t */\n\tpublic static function addRedirectToDomain(int $domainid = 0, int $redirect = 1)\n\t{\n\t\tif ($domainid > 0) {\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_DOMAINREDIRECTS . \"` SET `rid` = :rid, `did` = :did\n\t\t\t\");\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'rid' => $redirect,\n\t\t\t\t'did' => $domainid\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * updates the redirect-code of a domain\n\t * if redirect-code is false, nothing happens\n\t *\n\t * @param int $domainid id of the domain to update\n\t * @param int $redirect selected redirect-id\n\t *\n\t * @return null\n\t * @throws \\Exception\n\t */\n\tpublic static function updateRedirectOfDomain(int $domainid = 0, int $redirect = 0)\n\t{\n\t\tif (!$redirect) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ($domainid > 0) {\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAINREDIRECTS . \"` WHERE `did` = :domainid\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'domainid' => $domainid\n\t\t\t]);\n\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\tINSERT INTO `\" . TABLE_PANEL_DOMAINREDIRECTS . \"` SET `rid` = :rid, `did` = :did\n\t\t\t\");\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'rid' => $redirect,\n\t\t\t\t'did' => $domainid\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * get ids of domains that are main domains but a subdomain of another main domain (for DNS)\n\t *\n\t * @param int $id main-domain to check\n\t *\n\t * @return array\n\t * @throws \\Exception\n\t */\n\tpublic static function getMainSubdomainIds(int $id): array\n\t{\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT id\n\t\t\tFROM `\" . TABLE_PANEL_DOMAINS . \"`\n\t\t\tWHERE\n\t\t\tisbinddomain = 1 AND\n\t\t\tdomain LIKE CONCAT('%.', ( SELECT d.domain FROM `\" . TABLE_PANEL_DOMAINS . \"` AS d WHERE d.id = :id ))\n\t\t\");\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'id' => $id\n\t\t]);\n\t\t$result = [];\n\t\twhile ($entry = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$result[] = $entry['id'];\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * Check whether a given domain has an ssl-ip/port assigned\n\t *\n\t * @param int $domainid\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic static function domainHasSslIpPort(int $domainid): bool\n\t{\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT `dt`.* FROM `\" . TABLE_DOMAINTOIP . \"` `dt`, `\" . TABLE_PANEL_IPSANDPORTS . \"` `iap`\n\t\t\tWHERE `dt`.`id_ipandports` = `iap`.`id` AND `iap`.`ssl` = '1' AND `dt`.`id_domain` = :domainid;\");\n\t\tDatabase::pexecute($result_stmt, [\n\t\t\t'domainid' => $domainid\n\t\t]);\n\t\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\tif ($result && isset($result['id_ipandports'])) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * returns true or false whether a given domain id\n\t * is the std-subdomain of a customer\n\t *\n\t * @param int $did domain-id\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic static function isCustomerStdSubdomain(int $did): bool\n\t{\n\t\tif ($did > 0) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\t\tSELECT `customerid` FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\t\tWHERE `standardsubdomain` = :did\n\t\t\t\");\n\t\t\t$result = Database::pexecute_first($result_stmt, [\n\t\t\t\t'did' => $did\n\t\t\t]);\n\n\t\t\tif ($result && isset($result['customerid'])) {\n\t\t\t\treturn $result['customerid'] > 0;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @param int $aliasDestinationDomainID\n\t * @param FroxlorLogger $log\n\t *\n\t * @return void\n\t * @throws \\Exception\n\t */\n\tpublic static function triggerLetsEncryptCSRForAliasDestinationDomain(\n\t\tint           $aliasDestinationDomainID,\n\t\tFroxlorLogger $log\n\t) {\n\t\tif ($aliasDestinationDomainID > 0) {\n\t\t\t$log->logAction(\n\t\t\t\tFroxlorLogger::ADM_ACTION,\n\t\t\t\tLOG_INFO,\n\t\t\t\t\"LetsEncrypt CSR triggered for domain ID \" . $aliasDestinationDomainID\n\t\t\t);\n\t\t\t$upd_stmt = Database::prepare(\"UPDATE\n\t\t\t\t\t`\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\t\t\tSET\n\t\t\t\t\t`validtodate` = null\n\t\t\t\tWHERE\n\t\t\t\t\tdomainid = :domainid\n\t\t\t\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'domainid' => $aliasDestinationDomainID\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * @param string $domainname\n\t * @return true\n\t */\n\tpublic static function doLetsEncryptCleanUp(string $domainname): bool\n\t{\n\t\t// @ see \\Froxlor\\Cron\\Http\\LetsEncrypt\\AcmeSh.php\n\t\t$acmesh = AcmeSh::getAcmeSh();\n\t\tif (file_exists($acmesh)) {\n\t\t\t$certificate_folder = AcmeSh::getWorkingDirFromEnv($domainname);\n\t\t\t$certificate_ecc_folder = AcmeSh::getWorkingDirFromEnv($domainname, true);\n\t\t\tif (file_exists($certificate_folder) || file_exists($certificate_ecc_folder)) {\n\t\t\t\t$params = \" --remove -d \" . $domainname;\n\t\t\t\tif (file_exists($certificate_ecc_folder)) {\n\t\t\t\t\t$params .= \" --ecc\";\n\t\t\t\t}\n\t\t\t\t// run remove command\n\t\t\t\tFileDir::safe_exec($acmesh . $params);\n\t\t\t\t// remove certificates directory\n\t\t\t\tif (file_exists($certificate_folder)) {\n\t\t\t\t\tFileDir::safe_exec('rm -rf ' . $certificate_folder);\n\t\t\t\t} elseif (file_exists($certificate_ecc_folder)) {\n\t\t\t\t\tFileDir::safe_exec('rm -rf ' . $certificate_ecc_folder);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * checks give path for security issues\n\t * and returns a string that can be appended\n\t * to a line for an open_basedir directive\n\t *\n\t * @param string $path the path to check and append\n\t * @param bool $first if true, no ':' will be prefixed to the path\n\t *\n\t * @return string\n\t * @throws \\Exception\n\t */\n\tpublic static function appendOpenBasedirPath(string $path = '', bool $first = false): string\n\t{\n\t\tif ($path != '' && $path != '/' && (!preg_match(\"#^/dev#i\", $path) || preg_match(\"#^/dev/urandom#i\",\n\t\t\t\t\t$path)) && !preg_match(\"#^/proc#i\", $path) && !preg_match(\"#^/etc#i\",\n\t\t\t\t$path) && !preg_match(\"#^/sys#i\", $path) && !preg_match(\"#:#\", $path)) {\n\t\t\tif (preg_match(\"#^/dev/urandom#i\", $path)) {\n\t\t\t\t$path = FileDir::makeCorrectFile($path);\n\t\t\t} else {\n\t\t\t\t$path = FileDir::makeCorrectDir($path);\n\t\t\t}\n\n\t\t\t// check for php-version that requires the trailing\n\t\t\t// slash to be removed as it does not allow the usage\n\t\t\t// of the sub-folders within the given folder, fixes #797\n\t\t\tif ((PHP_MINOR_VERSION == 2 && PHP_VERSION_ID >= 50216) || PHP_VERSION_ID >= 50304) {\n\t\t\t\t// check trailing slash\n\t\t\t\tif (substr($path, -1, 1) == '/') {\n\t\t\t\t\t// remove it\n\t\t\t\t\t$path = substr($path, 0, -1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ($first) {\n\t\t\t\treturn $path;\n\t\t\t}\n\n\t\t\treturn ':' . $path;\n\t\t}\n\t\treturn '';\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Domain/IpAddr.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Domain;\n\nuse Froxlor\\Database\\Database;\nuse PDO;\n\nclass IpAddr\n{\n\t/**\n\t * @return array\n\t */\n\tpublic static function getIpAddresses(): array\n\t{\n\t\t$result_stmt = Database::query(\"\n\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` ORDER BY `ip` ASC, `port` ASC\n\t\t\");\n\n\t\t$system_ipaddress_array = [];\n\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (!isset($system_ipaddress_array[$row['ip']]) && !in_array($row['ip'], $system_ipaddress_array)) {\n\t\t\t\t$system_ipaddress_array[$row['ip']] = $row['ip'];\n\t\t\t}\n\t\t}\n\n\t\treturn $system_ipaddress_array;\n\t}\n\n\t/**\n\t * @return array\n\t * @throws \\Exception\n\t */\n\tpublic static function getSslIpPortCombinations(): array\n\t{\n\t\treturn [\n\t\t\t\t'' => lng('panel.none_value')\n\t\t\t] + self::getIpPortCombinations(true);\n\t}\n\n\t/**\n\t * @param bool $ssl\n\t * @return array\n\t * @throws \\Exception\n\t */\n\tpublic static function getIpPortCombinations(bool $ssl = false): array\n\t{\n\t\tglobal $userinfo;\n\n\t\t$additional_conditions_params = [];\n\t\t$additional_conditions_array = [];\n\n\t\tif (!empty($userinfo) && $userinfo['ip'] != '-1') {\n\t\t\t$admin_ip_stmt = Database::prepare(\"\n\t\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `id` = IN (:ipid)\n\t\t\t\");\n\t\t\t$myips = implode(\",\", json_decode($userinfo['ip'], true));\n\t\t\tDatabase::pexecute($admin_ip_stmt, [\n\t\t\t\t'ipid' => $myips\n\t\t\t]);\n\t\t\t$additional_conditions_array[] = \"`ip` IN (:adminips)\";\n\t\t\t$additional_conditions_params['adminips'] = $myips;\n\t\t}\n\n\t\tif ($ssl !== null) {\n\t\t\t$additional_conditions_array[] = \"`ssl` = :ssl\";\n\t\t\t$additional_conditions_params['ssl'] = ($ssl === true ? '1' : '0');\n\t\t}\n\n\t\t$additional_conditions = '';\n\t\tif (count($additional_conditions_array) > 0) {\n\t\t\t$additional_conditions = \" WHERE \" . implode(\" AND \", $additional_conditions_array) . \" \";\n\t\t}\n\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT `id`, `ip`, `port` FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` \" . $additional_conditions . \" ORDER BY `ip` ASC, `port` ASC\n\t\t\");\n\n\t\tDatabase::pexecute($result_stmt, $additional_conditions_params);\n\t\t$system_ipaddress_array = [];\n\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif (filter_var($row['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n\t\t\t\t$row['ip'] = '[' . $row['ip'] . ']';\n\t\t\t}\n\t\t\t$system_ipaddress_array[$row['id']] = $row['ip'] . ':' . $row['port'];\n\t\t}\n\n\t\treturn $system_ipaddress_array;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Domain/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/ErrorBag.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Exception;\n\n/**\n * Class to manage the current user / session\n */\nclass ErrorBag\n{\n\n\t/**\n\t * returns whether there are errors stored\n\t *\n\t * @return bool\n\t */\n\tpublic static function hasErrors(): bool\n\t{\n\t\treturn !empty($_SESSION) && !empty($_SESSION['_errors']);\n\t}\n\n\t/**\n\t * add error\n\t *\n\t * @param string $data\n\t *\n\t * @return void\n\t */\n\tpublic static function addError(string $data): void\n\t{\n\t\tif (!isset($_SESSION['_errors']) || !is_array($_SESSION['_errors'])) {\n\t\t\t$_SESSION['_errors'] = [];\n\t\t}\n\t\t$_SESSION['_errors'][] = $data;\n\t}\n\n\t/**\n\t * Return errors and clear session\n\t *\n\t * @return array\n\t * @throws Exception\n\t */\n\tpublic static function getErrors(): array\n\t{\n\t\t$errors = $_SESSION['_errors'] ?? [];\n\t\tunset($_SESSION['_errors']);\n\t\tif (Settings::Config('display_php_errors')) {\n\t\t\treturn $errors;\n\t\t}\n\t\treturn [];\n\t}\n\n}\n"
  },
  {
    "path": "lib/Froxlor/FileDir.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Exception;\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\Database\\Database;\nuse PDO;\nuse RecursiveCallbackFilterIterator;\n\nclass FileDir\n{\n\t/**\n\t * Creates a directory below a users homedir and sets all directories,\n\t * which had to be created below with correct Owner/Group\n\t * (Copied from cron_tasks.php:rev1189 as we'll need this more often in future)\n\t *\n\t * @param string $homeDir The homedir of the user\n\t * @param string $dirToCreate The dir which should be created\n\t * @param int $uid The uid of the user\n\t * @param int $gid The gid of the user\n\t * @param bool $placeindex Place standard-index.html into the new folder\n\t * @param bool $allow_notwithinhomedir Allow creating a directory out of the customers docroot\n\t *\n\t * @return bool true if everything went okay, false if something went wrong\n\t * @throws Exception\n\t */\n\tpublic static function mkDirWithCorrectOwnership(\n\t\tstring $homeDir,\n\t\tstring $dirToCreate,\n\t\tint    $uid,\n\t\tint    $gid,\n\t\tbool   $placeindex = false,\n\t\tbool   $allow_notwithinhomedir = false\n\t): bool\n\t{\n\t\tif ($homeDir != '' && $dirToCreate != '') {\n\t\t\t$homeDir = self::makeCorrectDir($homeDir);\n\t\t\t$dirToCreate = self::makeCorrectDir($dirToCreate);\n\n\t\t\tif (substr($dirToCreate, 0, strlen($homeDir)) == $homeDir) {\n\t\t\t\t$subdir = substr($dirToCreate, strlen($homeDir) - 1);\n\t\t\t\t$within_homedir = true;\n\t\t\t} else {\n\t\t\t\t$subdir = $dirToCreate;\n\t\t\t\t$within_homedir = false;\n\t\t\t}\n\n\t\t\t$subdir = self::makeCorrectDir($subdir);\n\t\t\t$subdirs = [];\n\n\t\t\tif ($within_homedir || !$allow_notwithinhomedir) {\n\t\t\t\t$subdirlen = strlen($subdir);\n\t\t\t\t$offset = 0;\n\n\t\t\t\twhile ($offset < $subdirlen) {\n\t\t\t\t\t$offset = strpos($subdir, '/', $offset);\n\t\t\t\t\t$subdirelem = substr($subdir, 0, $offset);\n\t\t\t\t\t$offset++;\n\t\t\t\t\tarray_push($subdirs, self::makeCorrectDir($homeDir . $subdirelem));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tarray_push($subdirs, $dirToCreate);\n\t\t\t}\n\n\t\t\t$subdirs = array_unique($subdirs);\n\t\t\tsort($subdirs);\n\t\t\tforeach ($subdirs as $sdir) {\n\t\t\t\tif (!is_dir($sdir)) {\n\t\t\t\t\t$sdir = self::makeCorrectDir($sdir);\n\t\t\t\t\tself::safe_exec('mkdir -p ' . escapeshellarg($sdir));\n\t\t\t\t\t// place index\n\t\t\t\t\tif ($placeindex) {\n\t\t\t\t\t\t$loginname = Customer::getLoginNameByUid($uid);\n\t\t\t\t\t\tif ($loginname !== false) {\n\t\t\t\t\t\t\tself::storeDefaultIndex($loginname, $sdir, null);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tself::safe_exec('chown -R ' . (int)$uid . ':' . (int)$gid . ' ' . escapeshellarg($sdir));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns a correct/secure dirname, means to add slashes at the beginning and at the end if there weren't\n\t * some. If $fixes_homedir is specified,\n\t *\n\t *\n\t * @param string $dir the path to correct\n\t *\n\t * @return string the corrected path\n\t * @throws Exception\n\t */\n\tpublic static function makeCorrectDir(string $dir, string $fixed_homedir = \"\"): string\n\t{\n\t\tif (strlen($dir) > 0) {\n\t\t\t$dir = trim($dir);\n\t\t\tif (substr($dir, -1, 1) != '/') {\n\t\t\t\t$dir .= '/';\n\t\t\t}\n\t\t\tif (substr($dir, 0, 1) != '/') {\n\t\t\t\t$dir = '/' . $dir;\n\t\t\t}\n\n\t\t\t// if given, check that the target path is within the $fixed_homedir\n\t\t\t// by checking each folder for being a symlink and whether it targets\n\t\t\t// the customers homedir or points outside of it\n\t\t\tif (!empty($fixed_homedir)) {\n\t\t\t\t$to_check = explode(\"/\", substr($dir, strlen($fixed_homedir) + 1), -1);\n\t\t\t\t$check_dir = substr($fixed_homedir, 0, -1);\n\t\t\t\t// Symlink check\n\t\t\t\tforeach ($to_check as $sub_dir) {\n\t\t\t\t\t$check_dir .= '/' . $sub_dir;\n\t\t\t\t\tif (is_link($check_dir)) {\n\t\t\t\t\t\t$original_target = $check_dir;\n\t\t\t\t\t\t$check_dir = readlink($check_dir);\n\t\t\t\t\t\t$link_dir = dirname($original_target);\n\t\t\t\t\t\t// check whether the link is relative or absolute\n\t\t\t\t\t\tif (substr($check_dir, 0, 1) != '/') {\n\t\t\t\t\t\t\t// relative directory, prepend link_dir\n\t\t\t\t\t\t\t$check_dir = $link_dir . '/' . $check_dir;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (substr($check_dir, 0, strlen($fixed_homedir)) != $fixed_homedir) {\n\t\t\t\t\t\t\tthrow new Exception(\"Found symlink pointing outside of customer home directory: \" . substr($original_target, strlen($fixed_homedir)));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// check for the path to be within the given homedir\n\t\t\t\tif (substr($dir, 0, strlen($fixed_homedir)) != $fixed_homedir) {\n\t\t\t\t\tthrow new Exception(\"Target path not within the required customer home directory\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn self::makeSecurePath($dir);\n\t\t}\n\t\tthrow new Exception(\"Cannot validate directory in \" . __FUNCTION__ . \" which is very dangerous.\");\n\t}\n\n\t/**\n\t * Function which returns a secure path, means to remove all multiple dots and slashes\n\t *\n\t * @param string $path the path to secure\n\t *\n\t * @return string the corrected path\n\t */\n\tpublic static function makeSecurePath(string $path): string\n\t{\n\t\t// check for bad characters, some are allowed with escaping,\n\t\t// but we generally don't want them in our directory-names,\n\t\t// thx to aaronmueller for this snippet\n\t\t$badchars = [\n\t\t\t':',\n\t\t\t';',\n\t\t\t'|',\n\t\t\t'&',\n\t\t\t'>',\n\t\t\t'<',\n\t\t\t'`',\n\t\t\t'$',\n\t\t\t'~',\n\t\t\t'?',\n\t\t\t\"\\0\",\n\t\t\t\"\\n\",\n\t\t\t\"\\r\",\n\t\t\t\"\\t\",\n\t\t\t\"\\f\"\n\t\t];\n\t\tforeach ($badchars as $bc) {\n\t\t\t$path = str_replace($bc, \"\", $path);\n\t\t}\n\n\t\t$search = [\n\t\t\t'#/+#',\n\t\t\t'#\\.+#'\n\t\t];\n\t\t$replace = [\n\t\t\t'/',\n\t\t\t'.'\n\t\t];\n\t\t$path = preg_replace($search, $replace, $path);\n\t\t// don't just replace a space with an escaped space\n\t\t// it might be escaped already\n\t\t$path = str_replace(\"\\ \", \" \", $path);\n\t\t$path = str_replace(\" \", \"\\ \", $path);\n\n\t\treturn $path;\n\t}\n\n\t/**\n\t * Wrapper around the exec command.\n\t *\n\t * @param string $exec_string command to be executed\n\t * @param mixed $return_value referenced variable where the output is stored\n\t * @param ?array $allowedChars optional array of allowed characters in path/command\n\t *\n\t * @return array result of exec()\n\t */\n\tpublic static function safe_exec(string $exec_string, &$return_value = false, $allowedChars = null)\n\t{\n\t\t$disallowed = [\n\t\t\t';',\n\t\t\t'|',\n\t\t\t'&',\n\t\t\t'>',\n\t\t\t'<',\n\t\t\t'`',\n\t\t\t'$',\n\t\t\t'~',\n\t\t\t'?'\n\t\t];\n\n\t\t$acheck = false;\n\t\tif ($allowedChars != null && is_array($allowedChars) && count($allowedChars) > 0) {\n\t\t\t$acheck = true;\n\t\t}\n\n\t\tforeach ($disallowed as $dc) {\n\t\t\tif ($acheck && in_array($dc, $allowedChars)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// check for bad signs in execute command\n\t\t\tif (stristr($exec_string, $dc)) {\n\t\t\t\tdie(\"SECURITY CHECK FAILED!\\nThe execute string '\" . $exec_string . \"' is a possible security risk!\\nPlease check your whole server for security problems by hand!\\n\");\n\t\t\t}\n\t\t}\n\n\t\t// execute the command and return output\n\t\t$return = [];\n\n\t\t// -------------------------------------------------------------------------------\n\t\tif ($return_value == false) {\n\t\t\texec($exec_string, $return);\n\t\t} else {\n\t\t\texec($exec_string, $return, $return_value);\n\t\t}\n\n\t\treturn $return;\n\t}\n\n\t/**\n\t * Read unconfigured-domain template from database if exists or fallback to default\n\t *\n\t * @param string $servername\n\t *\n\t * @return string\n\t * @throws Exception\n\t */\n\tpublic static function getUnknownDomainTemplate(string $servername = \"\")\n\t{\n\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_TEMPLATES . \"` WHERE `templategroup` = 'files' AND `varname` = 'unconfigured_html'\n\t\t\");\n\t\tDatabase::pexecute($result_stmt);\n\t\tif (Database::num_rows() > 0) {\n\t\t\t$template = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t\t$replace_arr = [\n\t\t\t\t'SERVERNAME' => $servername,\n\t\t\t];\n\t\t\t$tpl_content = PhpHelper::replaceVariables($template['value'], $replace_arr);\n\t\t\t$tpl_ext = $template['file_extension'];\n\t\t} else {\n\t\t\t$tpl_ext = 'html';\n\t\t\t$unconfiguredPath = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/templates/misc/unconfigured/index.html');\n\t\t\tif (file_exists($unconfiguredPath)) {\n\t\t\t\t$tpl_content = file_get_contents($unconfiguredPath);\n\t\t\t} else {\n\t\t\t\t$tpl_content = lng('admin.templates.unconfigured_content_fallback');\n\t\t\t}\n\t\t}\n\t\t$redirect_file = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/notice.' . $tpl_ext);\n\t\tfile_put_contents($redirect_file, $tpl_content);\n\t\treturn basename($redirect_file);\n\t}\n\n\t/**\n\t * store the default index-file in a given destination folder\n\t *\n\t * @param string $loginname customers loginname\n\t * @param string $destination path where to create the file\n\t * @param object $logger FroxlorLogger object\n\t * @param bool $force force creation whatever the settings say (needed for task #2, create new user)\n\t *\n\t * @return void\n\t * @throws Exception\n\t */\n\tpublic static function storeDefaultIndex(\n\t\tstring $loginname,\n\t\tstring $destination,\n\t\t       $logger = null,\n\t\tbool   $force = false\n\t)\n\t{\n\t\tif ($force || (int)Settings::Get('system.store_index_file_subs') == 1) {\n\t\t\t$result_stmt = Database::prepare(\"\n\t\t\tSELECT `t`.`value`, `t`.`file_extension`, `c`.`email` AS `customer_email`, `a`.`email` AS `admin_email`, `c`.`loginname` AS `customer_login`, `a`.`loginname` AS `admin_login`\n\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` AS `c` INNER JOIN `\" . TABLE_PANEL_ADMINS . \"` AS `a`\n\t\t\tON `c`.`adminid` = `a`.`adminid`\n\t\t\tINNER JOIN `\" . TABLE_PANEL_TEMPLATES . \"` AS `t`\n\t\t\tON `a`.`adminid` = `t`.`adminid`\n\t\t\tWHERE `varname` = 'index_html' AND `c`.`loginname` = :loginname\");\n\t\t\tDatabase::pexecute($result_stmt, [\n\t\t\t\t'loginname' => $loginname\n\t\t\t]);\n\n\t\t\tif (Database::num_rows() > 0) {\n\t\t\t\t$template = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\n\t\t\t\t$replace_arr = [\n\t\t\t\t\t'SERVERNAME' => Settings::Get('system.hostname'),\n\t\t\t\t\t'CUSTOMER' => $template['customer_login'],\n\t\t\t\t\t'ADMIN' => $template['admin_login'],\n\t\t\t\t\t'CUSTOMER_EMAIL' => $template['customer_email'],\n\t\t\t\t\t'ADMIN_EMAIL' => $template['admin_email']\n\t\t\t\t];\n\n\t\t\t\t// replaceVariables\n\t\t\t\t$htmlcontent = PhpHelper::replaceVariables($template['value'], $replace_arr);\n\t\t\t\t$indexhtmlpath = self::makeCorrectFile($destination . '/index.' . $template['file_extension']);\n\t\t\t\t$index_html_handler = fopen($indexhtmlpath, 'w');\n\t\t\t\tfwrite($index_html_handler, $htmlcontent);\n\t\t\t\tfclose($index_html_handler);\n\t\t\t\tif ($logger !== null) {\n\t\t\t\t\t$logger->logAction(\n\t\t\t\t\t\tFroxlorLogger::CRON_ACTION,\n\t\t\t\t\t\tLOG_NOTICE,\n\t\t\t\t\t\t'Creating \\'index.' . $template['file_extension'] . '\\' for Customer \\'' . $template['customer_login'] . '\\' based on template in directory ' . escapeshellarg($indexhtmlpath)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$destination = self::makeCorrectDir($destination);\n\t\t\t\tif ($logger !== null) {\n\t\t\t\t\t$logger->logAction(\n\t\t\t\t\t\tFroxlorLogger::CRON_ACTION,\n\t\t\t\t\t\tLOG_NOTICE,\n\t\t\t\t\t\t'Running: cp -a ' . Froxlor::getInstallDir() . '/templates/misc/standardcustomer/* ' . escapeshellarg($destination)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tself::safe_exec('cp -a ' . Froxlor::getInstallDir() . '/templates/misc/standardcustomer/* ' . escapeshellarg($destination));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Function which returns a correct filename, means to add a slash at the beginning if there wasn't one\n\t *\n\t * @param string $filename the filename\n\t * @param string $fixed_homedir whether to check that the given file is within the fixed home-directory\n\t *\n\t * @return string the corrected filename\n\t * @throws Exception\n\t */\n\tpublic static function makeCorrectFile(string $filename, string $fixed_homedir = \"\"): string\n\t{\n\t\tif (trim($filename) == '') {\n\t\t\t$error = 'Given filename for function ' . __FUNCTION__ . ' is empty.' . \"\\n\";\n\t\t\t$error .= 'This is very dangerous and should not happen.' . \"\\n\";\n\t\t\t$error .= 'Please inform the Froxlor team about this issue so they can fix it.';\n\t\t\techo $error;\n\t\t\t// so we can see WHERE this happened\n\t\t\tdebug_print_backtrace();\n\t\t\tdie();\n\t\t}\n\n\t\tif (substr($filename, 0, 1) != '/') {\n\t\t\t$filename = '/' . $filename;\n\t\t}\n\n\t\t$filename = FileDir::makeCorrectDir(dirname($filename)) . '/' . basename($filename);\n\n\t\t// if given, check that the target file is within the $fixed_homedir\n\t\t// by checking each folder and the file for being a symlink and whether it targets\n\t\t// the customers homedir or points outside of it\n\t\tif (!empty($fixed_homedir)) {\n\t\t\t$to_check = explode(\"/\", substr($filename, strlen(self::makeCorrectDir($fixed_homedir))), -1);\n\t\t\t$check_dir = substr($fixed_homedir, -1) == '/' ? substr($fixed_homedir, 0, -1) : $fixed_homedir;\n\t\t\t// Symlink check\n\t\t\tforeach ($to_check as $sub_dir) {\n\t\t\t\t$check_dir .= '/' . $sub_dir;\n\t\t\t\tif (is_link($check_dir)) {\n\t\t\t\t\t$original_target = $check_dir;\n\t\t\t\t\t$check_dir = readlink($check_dir);\n\t\t\t\t\t$link_dir = dirname($original_target);\n\t\t\t\t\t// check whether the link is relative or absolute\n\t\t\t\t\tif (substr($check_dir, 0, 1) != '/') {\n\t\t\t\t\t\t// relative directory, prepend link_dir\n\t\t\t\t\t\t$check_dir = $link_dir . '/' . $check_dir;\n\t\t\t\t\t}\n\t\t\t\t\tif (substr($check_dir, 0, strlen($fixed_homedir)) != $fixed_homedir) {\n\t\t\t\t\t\tthrow new Exception(\"Found symlink pointing outside of customer home directory: \" . substr($original_target, strlen($fixed_homedir)));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// check for the path to be within the given homedir\n\t\t\tif (substr($filename, 0, strlen($fixed_homedir)) != $fixed_homedir) {\n\t\t\t\tthrow new Exception(\"Target path/file not within the required customer home directory\");\n\t\t\t}\n\t\t\t// check whether file is symlink itself\n\t\t\tif (is_link($filename)) {\n\t\t\t\t$filename = readlink($filename);\n\t\t\t\t$check_dir = FileDir::makeCorrectDir(dirname($filename), $fixed_homedir);\n\t\t\t\tif (substr($check_dir, 0, strlen($fixed_homedir)) != $fixed_homedir) {\n\t\t\t\t\tthrow new Exception(\"Found symlink pointing outside of customer home directory: \" . substr($filename, strlen($fixed_homedir)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn self::makeSecurePath($filename);\n\t}\n\n\t/**\n\t * checks a directory against disallowed paths which could\n\t * lead to a damaged system if you use them\n\t *\n\t * @param string|null $path\n\t *\n\t * @return bool\n\t * @throws Exception\n\t */\n\tpublic static function checkDisallowedPaths(string $path): bool\n\t{\n\t\t/*\n\t\t * disallow base-directories and /\n\t\t */\n\t\t$disallowed_values = [\n\t\t\t\"/\",\n\t\t\t\"/bin/\",\n\t\t\t\"/boot/\",\n\t\t\t\"/dev/\",\n\t\t\t\"/etc/\",\n\t\t\t\"/home/\",\n\t\t\t\"/lib/\",\n\t\t\t\"/lib32/\",\n\t\t\t\"/lib64/\",\n\t\t\t\"/opt/\",\n\t\t\t\"/proc/\",\n\t\t\t\"/root/\",\n\t\t\t\"/run/\",\n\t\t\t\"/sbin/\",\n\t\t\t\"/sys/\",\n\t\t\t\"/tmp/\",\n\t\t\t\"/usr/\",\n\t\t\t\"/var/\"\n\t\t];\n\n\t\t$path = self::makeCorrectDir($path);\n\n\t\t// check if it's a disallowed path\n\t\tif (in_array($path, $disallowed_values)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Function which returns a correct destination for Postfix Virtual Table\n\t *\n\t * @param string $destination The destinations\n\t *\n\t * @return string the corrected destinations\n\t */\n\tpublic static function makeCorrectDestination(string $destination): string\n\t{\n\t\t$search = '/ +/';\n\t\t$replace = ' ';\n\t\t$destination = preg_replace($search, $replace, $destination);\n\n\t\tif (substr($destination, 0, 1) == ' ') {\n\t\t\t$destination = substr($destination, 1);\n\t\t}\n\n\t\tif (substr($destination, -1, 1) == ' ') {\n\t\t\t$destination = substr($destination, 0, strlen($destination) - 1);\n\t\t}\n\n\t\treturn $destination;\n\t}\n\n\t/**\n\t * Returns a valid html tag for the chosen $fieldType for paths\n\t *\n\t * @param string $path The path to start searching in\n\t * @param int $uid The uid which must match the found directories\n\t * @param int $gid The gid which must match the found directories\n\t * @param string $value the value for the input-field\n\t * @param bool $dom\n\t *\n\t * @return array\n\t *\n\t * @throws Exception\n\t * @author Manuel Bernhardt <manuel.bernhardt@syscp.de>\n\t * @author Martin Burchert <martin.burchert@syscp.de>\n\t */\n\tpublic static function makePathfield(string $path, int $uid, int $gid, string $value = '', bool $dom = false): array\n\t{\n\t\t$value = str_replace($path, '', $value);\n\t\t$field = [];\n\n\t\t// path is given without starting slash\n\t\t// but dirList holds the paths with starting slash,\n\t\t// so we just add one here to get the correct\n\t\t// default path selected, #225\n\t\tif (substr($value, 0, 1) != '/' && !$dom) {\n\t\t\t$value = '/' . $value;\n\t\t}\n\n\t\t$fieldType = strtolower(Settings::Get('panel.pathedit'));\n\n\t\tif ($fieldType == 'manual') {\n\t\t\t$field = [\n\t\t\t\t'type' => 'text',\n\t\t\t\t'value' => htmlspecialchars($value)\n\t\t\t];\n\t\t} elseif ($fieldType == 'dropdown') {\n\t\t\t$dirList = self::findDirs($path, $uid, $gid);\n\t\t\tnatcasesort($dirList);\n\n\t\t\tif (sizeof($dirList) > 0) {\n\t\t\t\t$_field = [];\n\t\t\t\tforeach ($dirList as $dir) {\n\t\t\t\t\tif (strpos($dir, $path) === 0) {\n\t\t\t\t\t\t$dir = substr($dir, strlen($path));\n\t\t\t\t\t\t// docroot cut off of current directory == empty -> directory is the docroot\n\t\t\t\t\t\tif (empty($dir)) {\n\t\t\t\t\t\t\t$dir = '/';\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$dir = self::makeCorrectDir($dir);\n\t\t\t\t\t}\n\t\t\t\t\t$_field[$dir] = $dir;\n\t\t\t\t}\n\t\t\t\t$field = [\n\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t'select_var' => $_field,\n\t\t\t\t\t'selected' => $value,\n\t\t\t\t\t'value' => $value\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$field = [\n\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t'value' => '/',\n\t\t\t\t\t'note' => lng('panel.dirsmissing')\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\n\t\treturn $field;\n\t}\n\n\t/**\n\t * Returns an array of found directories\n\t *\n\t * This function checks every found directory if they match either $uid or $gid, if they do\n\t * the found directory is valid. It uses recursive-iterators to find subdirectories.\n\t *\n\t * @param string $path the path to start searching in\n\t * @param int $uid the uid which must match the found directories\n\t * @param int $gid the gid which must match the found directories\n\t *\n\t * @return array Array of found valid paths\n\t * @throws Exception\n\t */\n\tprivate static function findDirs(string $path, int $uid, int $gid): array\n\t{\n\t\t$_fileList = [];\n\t\t$path = self::makeCorrectDir($path);\n\n\t\t// valid directory?\n\t\tif (is_dir($path)) {\n\t\t\t// Will exclude everything under these directories\n\t\t\t$exclude = [\n\t\t\t\t'awstats',\n\t\t\t\t'webalizer',\n\t\t\t\t'goaccess'\n\t\t\t];\n\n\t\t\t/**\n\t\t\t *\n\t\t\t * @param SplFileInfo $file\n\t\t\t * @param mixed $key\n\t\t\t * @param RecursiveCallbackFilterIterator $iterator\n\t\t\t * @return bool True if you need to recurse or if the item is acceptable\n\t\t\t */\n\t\t\t$filter = function ($file, $key, $iterator) use ($exclude) {\n\t\t\t\tif (in_array($file->getFilename(), $exclude)) {\n\t\t\t\t\treturn false;\n\t\t\t\t} elseif (substr($file->getFilename(), 0, 1) == '.') {\n\t\t\t\t\t// also hide hidden folders\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\t// create RecursiveIteratorIterator\n\t\t\t$its = new \\RecursiveIteratorIterator(\n\t\t\t\tnew \\RecursiveCallbackFilterIterator(\n\t\t\t\t\tnew \\RecursiveDirectoryIterator($path, \\RecursiveDirectoryIterator::SKIP_DOTS),\n\t\t\t\t\t$filter\n\t\t\t\t),\n\t\t\t\t\\RecursiveIteratorIterator::SELF_FIRST,\n\t\t\t\t\\RecursiveIteratorIterator::CATCH_GET_CHILD\n\t\t\t);\n\t\t\t// we can limit the recursion-depth, but will it be helpful or\n\t\t\t// will people start asking \"why do I only see 2 subdirectories, i want to use /a/b/c\"\n\t\t\t// let's keep this in mind and see whether it will be useful\n\t\t\t$its->setMaxDepth(2);\n\n\t\t\t// check every file\n\t\t\tforeach ($its as $fullFileName => $it) {\n\t\t\t\tif ($it->isDir() && (fileowner($fullFileName) == $uid || filegroup($fullFileName) == $gid)) {\n\t\t\t\t\t$_fileList[] = self::makeCorrectDir($fullFileName);\n\t\t\t\t}\n\t\t\t}\n\t\t\t$_fileList[] = $path;\n\t\t}\n\n\t\treturn array_unique($_fileList);\n\t}\n\n\t/**\n\t * set the immutable flag for a file\n\t *\n\t * @param string $filename the file to set the flag for\n\t *\n\t * @return void\n\t */\n\tpublic static function setImmutable(string $filename)\n\t{\n\t\tself::safe_exec(self::getImmutableFunction(false) . escapeshellarg($filename));\n\t}\n\n\t/**\n\t * internal function to check whether\n\t * to use chattr (Linux) or chflags (FreeBSD)\n\t *\n\t * @param bool $remove whether to use +i|schg (false) or -i|noschg (true)\n\t *\n\t * @return string functionname + parameter (not the file)\n\t */\n\tprivate static function getImmutableFunction(bool $remove = false): string\n\t{\n\t\tif (self::isFreeBSD()) {\n\t\t\t// FreeBSD style\n\t\t\treturn 'chflags ' . (($remove === true) ? 'noschg ' : 'schg ');\n\t\t} else {\n\t\t\t// Linux style\n\t\t\treturn 'chattr ' . (($remove === true) ? '-i ' : '+i ');\n\t\t}\n\t}\n\n\t/**\n\t * check if the system is FreeBSD (if exact)\n\t * or BSD-based (NetBSD, OpenBSD, etc.\n\t * if exact = false [default])\n\t *\n\t * @param bool $exact whether to check explicitly for FreeBSD or *BSD\n\t *\n\t * @return bool\n\t */\n\tpublic static function isFreeBSD(bool $exact = false): bool\n\t{\n\t\tif (($exact && PHP_OS == 'FreeBSD') || (!$exact && stristr(PHP_OS, 'BSD'))) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * removes the immutable flag for a file\n\t *\n\t * @param string $filename the file to set the flag for\n\t *\n\t * @return void\n\t */\n\tpublic static function removeImmutable(string $filename)\n\t{\n\t\tFileDir::safe_exec(self::getImmutableFunction(true) . escapeshellarg($filename));\n\t}\n\n\t/**\n\t *\n\t * @return array|false\n\t */\n\tpublic static function getFilesystemQuota()\n\t{\n\t\t// enabled at all?\n\t\tif (Settings::Get('system.diskquota_enabled')) {\n\t\t\t// set linux defaults\n\t\t\t$repquota_params = \"-np\";\n\t\t\t// $quota_line_regex = \"/^#([0-9]+)\\s*[+-]{2}\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)/i\";\n\t\t\t$quota_line_regex = \"/^#([0-9]+)\\s+[+-]{2}\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)/i\";\n\n\t\t\t// check for freebsd - which needs other values\n\t\t\tif (self::isFreeBSD()) {\n\t\t\t\t$repquota_params = \"-nu\";\n\t\t\t\t$quota_line_regex = \"/^([0-9]+)\\s+[+-]{2}\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)/i\";\n\t\t\t}\n\n\t\t\t// Fetch all quota in the desired partition\n\t\t\t$repquota = [];\n\t\t\texec(\n\t\t\t\tSettings::Get('system.diskquota_repquota_path') . \" \" . $repquota_params . \" \" . escapeshellarg(Settings::Get('system.diskquota_customer_partition')),\n\t\t\t\t$repquota\n\t\t\t);\n\n\t\t\t$usedquota = [];\n\t\t\tforeach ($repquota as $tmpquota) {\n\t\t\t\t$matches = null;\n\t\t\t\t// Let's see if the line matches a quota - line\n\t\t\t\tif (preg_match($quota_line_regex, $tmpquota, $matches)) {\n\t\t\t\t\t// It matches - put it into an array with userid as key (for easy lookup later)\n\t\t\t\t\t$usedquota[$matches[1]] = [\n\t\t\t\t\t\t'block' => [\n\t\t\t\t\t\t\t'used' => $matches[2],\n\t\t\t\t\t\t\t'soft' => $matches[3],\n\t\t\t\t\t\t\t'hard' => $matches[4],\n\t\t\t\t\t\t\t'grace' => (self::isFreeBSD() ? '0' : $matches[5])\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'file' => [\n\t\t\t\t\t\t\t'used' => $matches[6],\n\t\t\t\t\t\t\t'soft' => $matches[7],\n\t\t\t\t\t\t\t'hard' => $matches[8],\n\t\t\t\t\t\t\t'grace' => (self::isFreeBSD() ? '0' : $matches[9])\n\t\t\t\t\t\t]\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $usedquota;\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Froxlor.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Froxlor\\Database\\Database;\n\nfinal class Froxlor\n{\n\n\t// Main version variable\n\tconst VERSION = '2.3.7';\n\n\t// Database version (YYYYMMDDC where C is a daily counter)\n\tconst DBVERSION = '202603100';\n\n\t// Distribution branding-tag (used for Debian etc.)\n\tconst BRANDING = '';\n\n\tconst DOCS_URL = 'https://docs.froxlor.org';\n\n\t/**\n\t * return path to where froxlor is installed, e.g.\n\t * /var/www/froxlor/\n\t *\n\t * @return string\n\t */\n\tpublic static function getInstallDir(): string\n\t{\n\t\treturn dirname(__DIR__, 2) . '/';\n\t}\n\n\tpublic static function getDocsUrl(): string\n\t{\n\t\tif (preg_match('/(.+)-(dev|beta|rc)\\d+$/', self::VERSION)) {\n\t\t\treturn self::DOCS_URL . '/dev/';\n\t\t}\n\t\treturn self::DOCS_URL . '/v' . self::getShortVersion() . '/';\n\t}\n\n\t/**\n\t * return basic version\n\t *\n\t * @return string\n\t */\n\tpublic static function getVersion(): string\n\t{\n\t\treturn self::VERSION;\n\t}\n\n\t/**\n\t * return short basic version\n\t *\n\t * @return string\n\t */\n\tpublic static function getShortVersion(): string\n\t{\n\t\treturn explode(\".\", self::VERSION)[0] . '.' . explode(\".\", self::VERSION)[1];\n\t}\n\n\t/**\n\t * return version + branding and database-version\n\t *\n\t * @return string\n\t */\n\tpublic static function getVersionString(): string\n\t{\n\t\treturn self::getFullVersion() . ' (' . self::DBVERSION . ')';\n\t}\n\n\t/**\n\t * return version + branding\n\t *\n\t * @return string\n\t */\n\tpublic static function getFullVersion(): string\n\t{\n\t\treturn self::VERSION . self::BRANDING;\n\t}\n\n\t/**\n\t * Function hasUpdates\n\t *\n\t * checks if a given version is not equal the current one\n\t *\n\t * @param string $to_check version to check, if empty current version is used\n\t *\n\t * @return bool true if version to check does not match, else false\n\t */\n\tpublic static function hasUpdates(string $to_check = ''): bool\n\t{\n\t\tif (empty($to_check)) {\n\t\t\t$to_check = self::VERSION;\n\t\t}\n\t\tif (Settings::Get('panel.version') == null || Settings::Get('panel.version') != $to_check) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Function hasDbUpdates\n\t *\n\t * checks if a given database-version is not equal the current one\n\t *\n\t * @param string $to_check version to check, if empty current dbversion is used\n\t *\n\t * @return bool true if version to check does not match, else false\n\t */\n\tpublic static function hasDbUpdates(string $to_check = ''): bool\n\t{\n\t\tif (empty($to_check)) {\n\t\t\t$to_check = self::DBVERSION;\n\t\t}\n\t\tif (Settings::Get('panel.db_version') == null || Settings::Get('panel.db_version') != $to_check) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Function isDatabaseVersion\n\t *\n\t * checks if a given database-version is the current one\n\t *\n\t * @param string $to_check version to check\n\t *\n\t * @return bool true if version to check matches, else false\n\t */\n\tpublic static function isDatabaseVersion(string $to_check): bool\n\t{\n\t\tif (Settings::Get('panel.frontend') == 'froxlor' && Settings::Get('panel.db_version') == $to_check) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Function updateToDbVersion\n\t *\n\t * updates the panel.version field\n\t * to the given value (no checks here!)\n\t *\n\t * @param string $new_version new-version\n\t *\n\t * @return bool true on success, else false\n\t * @throws \\Exception\n\t */\n\tpublic static function updateToDbVersion(string $new_version): bool\n\t{\n\t\tif ($new_version != '') {\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `value` = :newversion\n\t\t\t\tWHERE `settinggroup` = 'panel' AND `varname` = 'db_version'\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'newversion' => $new_version\n\t\t\t]);\n\t\t\tSettings::Set('panel.db_version', $new_version);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Function updateToVersion\n\t *\n\t * updates the panel.version field\n\t * to the given value (no checks here!)\n\t *\n\t * @param string $new_version new-version\n\t *\n\t * @return bool true on success, else false\n\t * @throws \\Exception\n\t */\n\tpublic static function updateToVersion(string $new_version): bool\n\t{\n\t\tif ($new_version != '') {\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `value` = :newversion\n\t\t\t\tWHERE `settinggroup` = 'panel' AND `varname` = 'version'\");\n\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t'newversion' => $new_version\n\t\t\t]);\n\t\t\tSettings::Set('panel.version', $new_version);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Function isFroxlor\n\t *\n\t * checks if the panel is froxlor\n\t *\n\t * @return bool true if panel is froxlor, else false\n\t */\n\tpublic static function isFroxlor(): bool\n\t{\n\t\tif (Settings::Get('panel.frontend') !== null && Settings::Get('panel.frontend') == 'froxlor') {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Function isFroxlorVersion\n\t *\n\t * checks if a given version is the\n\t * current one (and panel is froxlor)\n\t *\n\t * @param string $to_check version to check\n\t *\n\t * @return bool true if version to check matches, else false\n\t */\n\tpublic static function isFroxlorVersion(string $to_check): bool\n\t{\n\t\tif (Settings::Get('panel.frontend') == 'froxlor' && Settings::Get('panel.version') == $to_check) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * generate safe unique session id\n\t *\n\t * @param int $length\n\t * @return string\n\t * @throws \\Exception\n\t */\n\tpublic static function genSessionId(int $length = 16): string\n\t{\n\t\tif ($length <= 8) {\n\t\t\t$length = 16;\n\t\t}\n\t\tif (function_exists('random_bytes')) {\n\t\t\treturn bin2hex(random_bytes($length));\n\t\t}\n\t\tif (function_exists('mcrypt_create_iv') && defined('MCRYPT_DEV_URANDOM')) {\n\t\t\treturn bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM));\n\t\t}\n\t\tif (function_exists('openssl_random_pseudo_bytes')) {\n\t\t\treturn bin2hex(openssl_random_pseudo_bytes($length));\n\t\t}\n\t\t// if everything else fails, use unsafe fallback\n\t\treturn md5(uniqid(microtime(), 1));\n\t}\n\n\t/**\n\t * compare of froxlor versions\n\t *\n\t * @param string $a\n\t * @param string $b\n\t *\n\t * @return int 0 if equal, 1 if a>b and -1 if b>a\n\t */\n\tpublic static function versionCompare2(string $a, string $b): int\n\t{\n\t\t// split version into pieces and remove trailing .0\n\t\t$a = explode(\".\", $a);\n\t\t$b = explode(\".\", $b);\n\n\t\tself::parseVersionArray($a);\n\t\tself::parseVersionArray($b);\n\n\t\twhile (count($a) != count($b)) {\n\t\t\tif (count($a) < count($b)) {\n\t\t\t\t$a[] = '0';\n\t\t\t} elseif (count($b) < count($a)) {\n\t\t\t\t$b[] = '0';\n\t\t\t}\n\t\t}\n\n\t\tforeach ($a as $depth => $aVal) {\n\t\t\t// iterate over each piece of A\n\t\t\tif (isset($b[$depth])) {\n\t\t\t\t// if B matches A to this depth, compare the values\n\t\t\t\tif ($aVal > $b[$depth]) {\n\t\t\t\t\treturn 1; // A > B\n\t\t\t\t} elseif ($aVal < $b[$depth]) {\n\t\t\t\t\treturn -1; // B > A\n\t\t\t\t}\n\t\t\t\t// an equal result is inconclusive at this point\n\t\t\t} else {\n\t\t\t\t// if B does not match A to this depth, then A comes after B in sort order\n\t\t\t\treturn 1; // so A > B\n\t\t\t}\n\t\t}\n\t\t// at this point, we know that to the depth that A and B extend to, they are equivalent.\n\t\t// either the loop ended because A is shorter than B, or both are equal.\n\t\treturn (count($a) < count($b)) ? -1 : 0;\n\t}\n\n\t/**\n\t * @param array|null $arr\n\t * @return void\n\t */\n\tprivate static function parseVersionArray(?array &$arr)\n\t{\n\t\t// -dev or -beta or -rc ?\n\t\tif (stripos($arr[count($arr) - 1], '-') !== false) {\n\t\t\t$x = explode(\"-\", $arr[count($arr) - 1]);\n\t\t\t$arr[count($arr) - 1] = $x[0];\n\t\t\tif (stripos($x[1], 'rc') !== false) {\n\t\t\t\t$arr[] = '-1';\n\t\t\t\t$arr[] = '2'; // dev < beta < rc\n\t\t\t\t// number of rc\n\t\t\t\t$arr[] = substr($x[1], 2);\n\t\t\t} else {\n\t\t\t\tif (stripos($x[1], 'beta') !== false) {\n\t\t\t\t\t$arr[] = '-1';\n\t\t\t\t\t$arr[] = '1'; // dev < beta < rc\n\t\t\t\t\t// number of beta\n\t\t\t\t\t$arr[] = substr($x[1], 3);\n\t\t\t\t} else {\n\t\t\t\t\tif (stripos($x[1], 'dev') !== false) {\n\t\t\t\t\t\t$arr[] = '-1';\n\t\t\t\t\t\t$arr[] = '0'; // dev < beta < rc\n\t\t\t\t\t\t// number of dev\n\t\t\t\t\t\t$arr[] = substr($x[1], 3);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/FroxlorLogger.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Froxlor\\System\\MysqlHandler;\nuse Monolog\\Handler\\StreamHandler;\nuse Monolog\\Handler\\SyslogHandler;\nuse Monolog\\Logger;\n\n/**\n * Class FroxlorLogger\n */\nclass FroxlorLogger\n{\n\n\tconst USR_ACTION = '10';\n\tconst RES_ACTION = '20';\n\tconst ADM_ACTION = '30';\n\tconst CRON_ACTION = '40';\n\tconst LOGIN_ACTION = '50';\n\tconst LOG_ERROR = '99';\n\t/**\n\t * current \\Monolog\\Logger object\n\t *\n\t * @var ?Logger\n\t */\n\tprivate static ?Logger $ml = null;\n\t/**\n\t * LogTypes Array\n\t *\n\t * @var ?array\n\t */\n\tprivate static ?array $logtypes = null;\n\t/**\n\t * whether to output log-messages to STDOUT (cron)\n\t *\n\t * @var bool\n\t */\n\tprivate static bool $crondebug_flag = false;\n\t/**\n\t * user info of logged-in user\n\t *\n\t * @var array\n\t */\n\tprivate static array $userinfo = [];\n\t/**\n\t * whether the logger object has already been initialized\n\t *\n\t * @var bool\n\t */\n\tprivate static bool $is_initialized = false;\n\n\t/**\n\t * Class constructor.\n\t *\n\t * @param array $userinfo\n\t *\n\t * @throws \\Exception\n\t */\n\tprotected function __construct(array $userinfo = [])\n\t{\n\t\t$this->initMonolog();\n\t\tself::$userinfo = $userinfo;\n\t\tself::$logtypes = [];\n\n\t\tif ((Settings::Get('logger.logtypes') == null || Settings::Get('logger.logtypes') == '') && (Settings::Get('logger.enabled') !== null && Settings::Get('logger.enabled'))) {\n\t\t\tself::$logtypes[0] = 'syslog';\n\t\t\tself::$logtypes[1] = 'mysql';\n\t\t} else {\n\t\t\tif (Settings::Get('logger.logtypes') !== null && Settings::Get('logger.logtypes') != '') {\n\t\t\t\tself::$logtypes = explode(',', Settings::Get('logger.logtypes'));\n\t\t\t} else {\n\t\t\t\tself::$logtypes = null;\n\t\t\t}\n\t\t}\n\n\t\tif (self::$is_initialized == false) {\n\t\t\tforeach (self::$logtypes as $logger) {\n\t\t\t\tswitch ($logger) {\n\t\t\t\t\tcase 'syslog':\n\t\t\t\t\t\tself::$ml->pushHandler(new SyslogHandler('froxlor', LOG_USER, Logger::DEBUG));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'file':\n\t\t\t\t\t\t$setings_logfile = Settings::Get('logger.logfile');\n\t\t\t\t\t\tif (empty($setings_logfile)) {\n\t\t\t\t\t\t\tSettings::Set('logger.logfile', 'froxlor.log');\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$logger_logfile = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/logs/' . Settings::Get('logger.logfile'));\n\t\t\t\t\t\t// is_writable needs an existing file to check if it's actually writable\n\t\t\t\t\t\tif (!@touch($logger_logfile) || !is_writable($logger_logfile)) {\n\t\t\t\t\t\t\t// not writable in our own directory? Skip\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tself::$ml->pushHandler(new StreamHandler($logger_logfile, Logger::DEBUG));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'mysql':\n\t\t\t\t\t\tself::$ml->pushHandler(new MysqlHandler(Logger::DEBUG));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tself::$is_initialized = true;\n\t\t}\n\t}\n\n\t/**\n\t * initiate monolog object\n\t *\n\t * @return Logger\n\t */\n\tprivate function initMonolog()\n\t{\n\t\tif (empty(self::$ml)) {\n\t\t\t// get Theme object\n\t\t\tself::$ml = new Logger('froxlor');\n\t\t}\n\t\treturn self::$ml;\n\t}\n\n\t/**\n\t * return FroxlorLogger instance\n\t *\n\t * @param array $userinfo\n\t *\n\t * @return FroxlorLogger\n\t * @throws \\Exception\n\t */\n\tpublic static function getInstanceOf(array $userinfo = [])\n\t{\n\t\tif (empty($userinfo)) {\n\t\t\t$userinfo = [\n\t\t\t\t'loginname' => 'system'\n\t\t\t];\n\t\t}\n\t\treturn new FroxlorLogger($userinfo);\n\t}\n\n\t/**\n\t * logs a given text to all enabled logger-facilities\n\t *\n\t * @param int $action\n\t * @param int $type\n\t * @param ?string $text\n\t */\n\tpublic function logAction($action = FroxlorLogger::USR_ACTION, int $type = LOG_NOTICE, ?string $text = null)\n\t{\n\t\t// not logging normal stuff if not set to \"paranoid\" logging\n\t\tif (!self::$crondebug_flag && Settings::Get('logger.severity') == '1' && $type > LOG_NOTICE) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (empty(self::$ml)) {\n\t\t\t$this->initMonolog();\n\t\t}\n\n\t\t// clean log-text\n\t\t$text = preg_replace(\"/[^\\w @#\\\"':.,()\\[\\]+\\-_\\/\\\\\\!]/i\", \"_\", $text);\n\n\t\tif (self::$crondebug_flag || ($action == FroxlorLogger::CRON_ACTION && $type <= LOG_WARNING)) {\n\t\t\techo \"[\" . $this->getLogLevelDesc($type) . \"] \" . $text . PHP_EOL;\n\t\t}\n\n\t\t// warnings, errors and critical messages WILL be logged\n\t\tif (Settings::Get('logger.log_cron') == '0' && $action == FroxlorLogger::CRON_ACTION && $type > LOG_WARNING) {\n\t\t\treturn;\n\t\t}\n\n\t\t$logExtra = [\n\t\t\t'source' => $this->getActionTypeDesc($action),\n\t\t\t'action' => $action,\n\t\t\t'user' => self::$userinfo['loginname']\n\t\t];\n\n\t\tswitch ($type) {\n\t\t\tcase LOG_DEBUG:\n\t\t\t\tself::$ml->addDebug($text, $logExtra);\n\t\t\t\tbreak;\n\t\t\tcase LOG_INFO:\n\t\t\t\tself::$ml->addInfo($text, $logExtra);\n\t\t\t\tbreak;\n\t\t\tcase LOG_NOTICE:\n\t\t\t\tself::$ml->addNotice($text, $logExtra);\n\t\t\t\tbreak;\n\t\t\tcase LOG_WARNING:\n\t\t\t\tself::$ml->addWarning($text, $logExtra);\n\t\t\t\tbreak;\n\t\t\tcase LOG_ERR:\n\t\t\t\tself::$ml->addError($text, $logExtra);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tself::$ml->addDebug($text, $logExtra);\n\t\t}\n\t}\n\n\t/**\n\t * @param int $type\n\t * @return string\n\t */\n\tpublic function getLogLevelDesc(int $type): string\n\t{\n\t\tswitch ($type) {\n\t\t\tcase LOG_INFO:\n\t\t\t\t$_type = 'information';\n\t\t\t\tbreak;\n\t\t\tcase LOG_NOTICE:\n\t\t\t\t$_type = 'notice';\n\t\t\t\tbreak;\n\t\t\tcase LOG_WARNING:\n\t\t\t\t$_type = 'warning';\n\t\t\t\tbreak;\n\t\t\tcase LOG_ERR:\n\t\t\t\t$_type = 'error';\n\t\t\t\tbreak;\n\t\t\tcase LOG_CRIT:\n\t\t\t\t$_type = 'critical';\n\t\t\t\tbreak;\n\t\t\tcase LOG_DEBUG:\n\t\t\t\t$_type = 'debug';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t$_type = 'unknown';\n\t\t\t\tbreak;\n\t\t}\n\t\treturn $_type;\n\t}\n\n\t/**\n\t * @param $action\n\t * @return string\n\t */\n\tprivate function getActionTypeDesc($action): string\n\t{\n\t\tswitch ($action) {\n\t\t\tcase FroxlorLogger::USR_ACTION:\n\t\t\t\t$_action = 'user';\n\t\t\t\tbreak;\n\t\t\tcase FroxlorLogger::ADM_ACTION:\n\t\t\t\t$_action = 'admin';\n\t\t\t\tbreak;\n\t\t\tcase FroxlorLogger::RES_ACTION:\n\t\t\t\t$_action = 'reseller';\n\t\t\t\tbreak;\n\t\t\tcase FroxlorLogger::CRON_ACTION:\n\t\t\t\t$_action = 'cron';\n\t\t\t\tbreak;\n\t\t\tcase FroxlorLogger::LOGIN_ACTION:\n\t\t\t\t$_action = 'login';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t$_action = 'unknown';\n\t\t\t\tbreak;\n\t\t}\n\t\treturn $_action;\n\t}\n\n\t/**\n\t * Set whether to log cron-runs\n\t *\n\t * @param int $cronlog\n\t *\n\t * @return int\n\t */\n\tpublic function setCronLog(int $cronlog = 0): int\n\t{\n\t\tif ($cronlog < 0 || $cronlog > 2) {\n\t\t\t$cronlog = 0;\n\t\t}\n\t\tSettings::Set('logger.log_cron', $cronlog);\n\t\treturn $cronlog;\n\t}\n\n\t/**\n\t * setter for crondebug-flag\n\t *\n\t * @param bool $flag\n\t *\n\t * @return void\n\t */\n\tpublic function setCronDebugFlag(bool $flag = false)\n\t{\n\t\tself::$crondebug_flag = $flag;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/FroxlorTwoFactorAuth.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse RobThree\\Auth\\TwoFactorAuth;\n\nclass FroxlorTwoFactorAuth extends TwoFactorAuth\n{\n}\n"
  },
  {
    "path": "lib/Froxlor/Http/Directory.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Http;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\n\n/**\n * Class frxDirectory handles directory actions and gives information\n * about a given directory in connections with its usage in froxlor\n */\nclass Directory\n{\n\n\t/**\n\t * directory string\n\t *\n\t * @var string\n\t */\n\tprivate $dir = null;\n\n\t/**\n\t * class constructor, optionally set directory\n\t *\n\t * @param string $dir\n\t */\n\tpublic function __construct(?string $dir = null)\n\t{\n\t\t$this->dir = $dir;\n\t}\n\n\t/**\n\t * check whether the directory has options set in panel_htaccess\n\t *\n\t * @return bool\n\t */\n\tpublic function hasUserOptions(): bool\n\t{\n\t\t$uo_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(`id`) as `usropts` FROM `\" . TABLE_PANEL_HTACCESS . \"` WHERE `path` = :dir\n\t\t\");\n\t\t$uo_res = Database::pexecute_first($uo_stmt, [\n\t\t\t'dir' => FileDir::makeCorrectDir($this->dir)\n\t\t]);\n\t\tif ($uo_res && isset($uo_res['usropts'])) {\n\t\t\treturn $uo_res['usropts'] > 0;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * check whether the directory is protected using panel_htpasswd\n\t *\n\t * @return bool\n\t */\n\tpublic function isUserProtected(): bool\n\t{\n\t\t$up_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(`id`) as `usrprot` FROM `\" . TABLE_PANEL_HTPASSWDS . \"` WHERE `path` = :dir\n\t\t\");\n\t\t$up_res = Database::pexecute_first($up_stmt, [\n\t\t\t'dir' => FileDir::makeCorrectDir($this->dir)\n\t\t]);\n\t\tif ($up_res && isset($up_res['usrprot'])) {\n\t\t\treturn $up_res['usrprot'] > 0;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Checks if a given directory is valid for multiple configurations\n\t * or should rather be used as a single file\n\t *\n\t * @param bool $ifexists also check whether file/dir exists\n\t *\n\t * @return bool true if usable as dir, false otherwise\n\t */\n\tpublic function isConfigDir(bool $ifexists = false): bool\n\t{\n\t\tif (is_null($this->dir)) {\n\t\t\ttrigger_error(__CLASS__ . '::' . __FUNCTION__ . ' has been called with a null value', E_USER_WARNING);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (file_exists($this->dir)) {\n\t\t\tif (is_dir($this->dir)) {\n\t\t\t\t$returnval = true;\n\t\t\t} else {\n\t\t\t\t$returnval = false;\n\t\t\t}\n\t\t} else {\n\t\t\tif (!$ifexists) {\n\t\t\t\tif (substr($this->dir, -1) == '/') {\n\t\t\t\t\t$returnval = true;\n\t\t\t\t} else {\n\t\t\t\t\t$returnval = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$returnval = false;\n\t\t\t}\n\t\t}\n\t\treturn $returnval;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Http/HttpClient.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Http;\n\nuse Exception;\nuse Froxlor\\Froxlor;\n\nclass HttpClient\n{\n\t/**\n\t * Executes simple GET request\n\t *\n\t * @param string $url\n\t * @param bool $follow_location\n\t * @param int $timeout\n\t *\n\t * @return bool|string\n\t * @throws Exception\n\t */\n\tpublic static function urlGet(string $url, bool $follow_location = true, int $timeout = 10)\n\t{\n\t\t$ch = curl_init();\n\t\tcurl_setopt($ch, CURLOPT_URL, $url);\n\t\tcurl_setopt($ch, CURLOPT_USERAGENT, 'Froxlor/' . Froxlor::getVersion());\n\t\tif ($follow_location) {\n\t\t\tcurl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\n\t\t}\n\t\tcurl_setopt($ch, CURLOPT_TIMEOUT, (int)$timeout);\n\t\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n\t\t$output = curl_exec($ch);\n\t\tif ($output === false) {\n\t\t\t$e = curl_error($ch);\n\t\t\tthrow new Exception(\"Curl error: \" . $e);\n\t\t}\n\t\treturn $output;\n\t}\n\n\t/**\n\t * Downloads and stores a file from an url\n\t *\n\t * @param string $url\n\t * @param string $target\n\t *\n\t * @return bool|string\n\t * @throws Exception\n\t */\n\tpublic static function fileGet(string $url, string $target)\n\t{\n\t\t$fh = fopen($target, 'w');\n\t\t$ch = curl_init();\n\t\tcurl_setopt($ch, CURLOPT_URL, $url);\n\t\tcurl_setopt($ch, CURLOPT_USERAGENT, 'Froxlor/' . Froxlor::getVersion());\n\t\tcurl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\n\t\tcurl_setopt($ch, CURLOPT_TIMEOUT, 50);\n\t\t// give curl the file pointer so that it can write to it\n\t\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n\t\tcurl_setopt($ch, CURLOPT_FILE, $fh);\n\t\t$output = curl_exec($ch);\n\t\tif ($output === false) {\n\t\t\t$e = curl_error($ch);\n\t\t\tthrow new Exception(\"Curl error: \" . $e);\n\t\t}\n\t\treturn $output;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Http/PhpConfig.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Http;\n\nuse Froxlor\\Database\\Database;\nuse PDO;\n\nclass PhpConfig\n{\n\n\t/**\n\t * returns an array of existing php-configurations\n\t * in our database for the settings-array\n\t *\n\t * @return array\n\t */\n\tpublic static function getPhpConfigs(): array\n\t{\n\t\t$configs_array = [];\n\n\t\t// check if table exists because this is used in a preconfig\n\t\t// where the tables possibly does not exist yet\n\t\t$results = Database::query(\"SHOW TABLES LIKE '\" . TABLE_PANEL_PHPCONFIGS . \"'\");\n\t\tif (!$results) {\n\t\t\t$configs_array[1] = 'Default php.ini';\n\t\t} else {\n\t\t\t// get all configs\n\t\t\t$result_stmt = Database::query(\"SELECT * FROM `\" . TABLE_PANEL_PHPCONFIGS . \"`\");\n\t\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif (!isset($configs_array[$row['id']]) && !in_array($row['id'], $configs_array)) {\n\t\t\t\t\t$configs_array[$row['id']] = html_entity_decode($row['description']);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $configs_array;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Http/RateLimiter.php",
    "content": "<?php\n\nnamespace Froxlor\\Http;\n\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\n\nclass RateLimiter\n{\n\tprivate static int $limit_per_interval = 60;\n\tprivate static int $reset_time = 0;\n\n\tpublic static function run(bool $install_mode = false)\n\t{\n\t\t// default interval = 60 sec\n\t\tself::$reset_time = time() + 60;\n\n\t\tif (!$install_mode) {\n\t\t\tself::$limit_per_interval = Settings::Get('system.req_limit_per_interval') ?? 60;\n\t\t\tself::$reset_time = time() + Settings::Get('system.req_limit_interval') ?? 60;\n\t\t}\n\n\t\t// Get the remaining requests and reset time from the headers\n\t\t$remaining = isset($_SESSION['HTTP_X_RATELIMIT_REMAINING']) ? (int)$_SESSION['HTTP_X_RATELIMIT_REMAINING'] : self::$limit_per_interval;\n\t\t$reset = isset($_SESSION['HTTP_X_RATELIMIT_RESET']) ? (int)$_SESSION['HTTP_X_RATELIMIT_RESET'] : self::$reset_time;\n\n\t\t// check if reset time is due\n\t\tif (time() > $reset) {\n\t\t\t$remaining = self::$limit_per_interval;\n\t\t\t$reset = self::$reset_time;\n\t\t}\n\n\t\t// If we've hit the limit, return an error\n\t\tif ($remaining <= 0) {\n\t\t\theader('HTTP/1.1 429 Too Many Requests');\n\t\t\theader(\"Retry-After: $reset\");\n\t\t\tUI::twig()->addGlobal('install_mode', '1');\n\t\t\techo UI::twig()->render('Froxlor/misc/ratelimithint.html.twig', [\n\t\t\t\t'retry' => $reset,\n\t\t\t\t'installdir' => Froxlor::getInstallDir()\n\t\t\t]);\n\t\t\tdie();\n\t\t}\n\n\t\t// Decrement the remaining requests and update the headers\n\t\t$remaining--;\n\t\t$_SESSION['HTTP_X_RATELIMIT_REMAINING'] = $remaining;\n\t\t$_SESSION['HTTP_X_RATELIMIT_RESET'] = $reset;\n\n\t\theader(\"X-RateLimit-Limit: \" . self::$limit_per_interval);\n\t\theader(\"X-RateLimit-Remaining: \" . $remaining);\n\t\theader(\"X-RateLimit-Reset: \" . $reset);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Http/Statistics.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Http;\n\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\n\nclass Statistics\n{\n\n\t/**\n\t * Create or modify the AWStats configuration file for the given domain.\n\t * Modified by Berend Dekens to allow custom configurations.\n\t *\n\t * @param string $logFile\n\t * @param string $siteDomain\n\t * @param string $hostAliases\n\t * @param string $customerDocroot\n\t * @param array $domain_data\n\t *\n\t * @return null\n\t * @throws \\Exception\n\t */\n\tpublic static function createAWStatsConf(\n\t\tstring $logFile,\n\t\tstring $siteDomain,\n\t\tstring $hostAliases,\n\t\tstring $customerDocroot,\n\t\tarray $domain_data = []\n\t) {\n\t\t// Generation header\n\t\t$header = \"## GENERATED BY FROXLOR\\n\";\n\t\t$header2 = \"## Do not remove the line above! This tells Froxlor to update this configuration\\n## If you wish to manually change this configuration file, remove the first line to make sure Froxlor won't rebuild this file\\n## Generated for domain {SITE_DOMAIN} on \" . date('l dS \\of F Y h:i:s A') . \"\\n\";\n\n\t\t$awstats_dir = FileDir::makeCorrectDir($customerDocroot . '/awstats/' . $siteDomain . '/');\n\t\tif (!is_dir($awstats_dir)) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg($awstats_dir));\n\t\t}\n\t\t// chown created folder, #258\n\t\tself::makeChownWithNewStats($domain_data);\n\n\t\t// weird but could happen...\n\t\tif (!is_dir(Settings::Get('system.awstats_conf'))) {\n\t\t\tFileDir::safe_exec('mkdir -p ' . escapeshellarg(Settings::Get('system.awstats_conf')));\n\t\t}\n\n\t\t$logformat = Settings::Get('system.awstats_logformat');\n\t\tif (!is_numeric($logformat)) {\n\t\t\t// if LogFormat is NOT numeric (e.g. 1,2,3,4), we quote it.\n\t\t\t// 1-4 are pre-defined formats by awstats which must not be quoted to work properly. So if\n\t\t\t// it is not a integer, it is something customized and we simply quote it.\n\t\t\t// Only escaping double-quote should be fine, as we only put the whole string under double-quote.\n\t\t\t$logformat = '\"' . str_replace('\"', '\\\"', Settings::Get('system.awstats_logformat')) . '\"';\n\t\t}\n\n\t\t// These are the variables we will replace\n\t\t$regex = [\n\t\t\t'/\\{LOG_FILE\\}/',\n\t\t\t'/\\{SITE_DOMAIN\\}/',\n\t\t\t'/\\{HOST_ALIASES\\}/',\n\t\t\t'/\\{CUSTOMER_DOCROOT\\}/',\n\t\t\t'/\\{AWSTATS_CONF\\}/',\n\t\t\t'/\\{AWSTATS_LOGFORMAT\\}/'\n\t\t];\n\t\t$replace = [\n\t\t\tFileDir::makeCorrectFile($logFile),\n\t\t\t$siteDomain,\n\t\t\t$hostAliases,\n\t\t\t$awstats_dir,\n\t\t\tFileDir::makeCorrectDir(Settings::Get('system.awstats_conf')),\n\t\t\t$logformat\n\t\t];\n\n\t\t// File names\n\t\t$domain_file = FileDir::makeCorrectFile(Settings::Get('system.awstats_conf') . '/awstats.' . $siteDomain . '.conf');\n\t\t$model_file = Froxlor::getInstallDir() . '/templates/misc/awstats/awstats.froxlor.model.conf';\n\t\t$model_file = FileDir::makeCorrectFile($model_file);\n\n\t\t// Test if the file exists\n\t\tif (file_exists($domain_file)) {\n\t\t\t// Check for the generated header - if this is a manual modification we won't update\n\t\t\t$awstats_domain_conf = fopen($domain_file, 'r');\n\n\t\t\tif (fgets($awstats_domain_conf, strlen($header)) != $header) {\n\t\t\t\tfclose($awstats_domain_conf);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Close the file\n\t\t\tfclose($awstats_domain_conf);\n\t\t}\n\n\t\t$awstats_domain_conf = fopen($domain_file, 'w');\n\t\t$awstats_model_conf = fopen($model_file, 'r');\n\n\t\t// Write the header\n\t\tfwrite($awstats_domain_conf, $header);\n\t\tfwrite($awstats_domain_conf, preg_replace($regex, $replace, $header2));\n\n\t\t// Write the configuration file\n\t\twhile (($line = fgets($awstats_model_conf, 4096)) !== false) {\n\t\t\tif (!preg_match('/^#/', $line) && trim($line) != '') {\n\t\t\t\tfwrite($awstats_domain_conf, preg_replace($regex, $replace, $line));\n\t\t\t}\n\t\t}\n\n\t\tfclose($awstats_domain_conf);\n\t\tfclose($awstats_model_conf);\n\t}\n\n\t/**\n\t * chowns stats-tools folder, either with webserver-user or\n\t * if fcgid/php-fpm is used, the customers name, #258\n\t *\n\t * @param array $row array of panel_customers\n\t *\n\t * @return void\n\t * @throws \\Exception\n\t */\n\tpublic static function makeChownWithNewStats(array $row)\n\t{\n\t\t// get correct user\n\t\tif ((Settings::Get('system.mod_fcgid') == '1' || Settings::Get('phpfpm.enabled') == '1') && isset($row['deactivated']) && $row['deactivated'] == '0') {\n\t\t\t$user = $row['loginname'];\n\t\t\t$group = $row['loginname'];\n\t\t} else {\n\t\t\t$user = $row['guid'];\n\t\t\t$group = $row['guid'];\n\t\t}\n\n\t\t// get correct directory\n\t\t$dir = $row['documentroot'] . '/' . Settings::Get('system.traffictool') . '/';\n\n\t\t// only run chown if directory exists\n\t\tif (file_exists($dir)) {\n\t\t\t// run chown\n\t\t\tFileDir::safe_exec('chown -R ' . escapeshellarg($user) . ':' . escapeshellarg($group) . ' ' . escapeshellarg(FileDir::makeCorrectDir($dir)));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Http/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Idna/IdnaWrapper.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Idna;\n\nuse Algo26\\IdnaConvert\\IdnaConvert;\nuse InvalidArgumentException;\n\n/**\n * Class for wrapping a specific idna conversion class and offering a standard interface\n *\n * @author     Michael Duergner <michael@duergner.com> (2003-2009)\n */\nclass IdnaWrapper\n{\n\n\t/**\n\t * idna converter we use\n\t *\n\t * @var object\n\t */\n\tprivate $idna_converter;\n\n\t/**\n\t * Class constructor.\n\t * Creates a new idna converter\n\t */\n\tpublic function __construct()\n\t{\n\t\t// Instantiate it\n\t\t$this->idna_converter = new IdnaConvert();\n\t}\n\n\t/**\n\t * Encode a domain name, an email address or a list of one of both.\n\t *\n\t * @param string $to_encode May be either a single domain name, e single email address or a list of one\n\t *            separated either by ',', ';' or ' '.\n\t *\n\t * @return string Returns either a single domain name, a single email address or a list of one of\n\t *         both separated by the same string as the input.\n\t */\n\tpublic function encode(string $to_encode): string\n\t{\n\t\t$to_encode = $this->isUtf8($to_encode) ? $to_encode : mb_convert_encoding($to_encode, 'UTF-8');\n\t\ttry {\n\t\t\treturn $this->idna_converter->encode($to_encode);\n\t\t} catch (InvalidArgumentException $iae) {\n\t\t\tif ($iae->getCode() == 100) {\n\t\t\t\treturn $to_encode;\n\t\t\t}\n\t\t\tthrow $iae;\n\t\t}\n\t}\n\n\t/**\n\t * check whether a string is utf-8 encoded or not\n\t *\n\t * @param string $string\n\t *\n\t * @return boolean\n\t */\n\tprivate function isUtf8(string $string)\n\t{\n\t\tif (function_exists(\"mb_detect_encoding\")) {\n\t\t\tif (mb_detect_encoding($string, 'UTF-8, ISO-8859-1') === 'UTF-8') {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t$strlen = strlen($string);\n\t\tfor ($i = 0; $i < $strlen; $i++) {\n\t\t\t$ord = ord($string[$i]);\n\t\t\tif ($ord < 0x80) {\n\t\t\t\tcontinue; // 0bbbbbbb\n\t\t\t} elseif (($ord & 0xE0) === 0xC0 && $ord > 0xC1) {\n\t\t\t\t$n = 1; // 110bbbbb (exkl C0-C1)\n\t\t\t} elseif (($ord & 0xF0) === 0xE0) {\n\t\t\t\t$n = 2; // 1110bbbb\n\t\t\t} elseif (($ord & 0xF8) === 0xF0 && $ord < 0xF5) {\n\t\t\t\t$n = 3; // 11110bbb (exkl F5-FF)\n\t\t\t} else {\n\t\t\t\t// ungültiges UTF-8-Zeichen\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// $n Folgebytes? // 10bbbbbb\n\t\t\tfor ($c = 0; $c < $n; $c++) {\n\t\t\t\tif (++$i === $strlen || (ord($string[$i]) & 0xC0) !== 0x80) {\n\t\t\t\t\t// ungültiges UTF-8-Zeichen\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// kein ungültiges UTF-8-Zeichen gefunden\n\t\treturn true;\n\t}\n\n\t/**\n\t * Decode a domain name, an email address or a list of one of both.\n\t *\n\t * @param string $to_decode May be either a single domain name, e single email address or a list of one\n\t *            separated either by ',', ';' or ' '.\n\t *\n\t * @return string Returns either a single domain name, a single email address or a list of one of\n\t *         both separated by the same string as the input.\n\t */\n\tpublic function decode(string $to_decode): string\n\t{\n\t\treturn $this->idna_converter->decode($to_decode);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Idna/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Install/AutoUpdate.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Install;\n\nuse Exception;\nuse ZipArchive;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse Froxlor\\Http\\HttpClient;\n\nclass AutoUpdate\n{\n\t// define update-uri\n\tconst UPDATE_URI = \"https://version.froxlor.org/froxlor/api/v2/\";\n\tconst RELEASE_URI = \"https://autoupdate.froxlor.org/froxlor-{version}.zip\";\n\tconst CHECKSUM_URI = \"https://autoupdate.froxlor.org/froxlor-{version}.zip.sha256\";\n\n\tconst ERR_NOZIPEXT = 2;\n\tconst ERR_COULDNOTSTORE = 4;\n\tconst ERR_ZIPNOTFOUND = 7;\n\tconst ERR_COULDNOTEXTRACT = 8;\n\tconst ERR_CHKSUM_MISMATCH = 9;\n\tconst ERR_MINPHP = 10;\n\n\tprivate static $latestversion = \"\";\n\n\tprivate static $lasterror = \"\";\n\n\t/**\n\t * returns status about whether there is a newer version\n\t *\n\t * 0 = no new version available\n\t * 1 = new version available\n\t * -1 = remote error message\n\t * >1 = local error message\n\t *\n\t * @return int\n\t */\n\tpublic static function checkVersion(): int\n\t{\n\t\t$result = self::checkPrerequisites();\n\n\t\tif ($result == 0) {\n\t\t\ttry {\n\t\t\t\t$channel = '';\n\t\t\t\tif (Settings::Get('system.update_channel') == 'testing') {\n\t\t\t\t\t$channel = '/testing';\n\t\t\t\t} elseif (Settings::Get('system.update_channel') == 'nightly') {\n\t\t\t\t\tif (empty(Froxlor::BRANDING) || substr(Froxlor::BRANDING, 0, 1) == '-') {\n\t\t\t\t\t\t$channel = '/nightly.0000000';\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$channel = '/' . substr(Froxlor::BRANDING, 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$latestversion = HttpClient::urlGet(self::UPDATE_URI . Froxlor::VERSION . $channel, true, 3);\n\t\t\t} catch (Exception $e) {\n\t\t\t\tself::$lasterror = \"Version-check currently unavailable, please try again later\";\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tself::$latestversion = json_decode($latestversion, true);\n\n\t\t\tif (self::$latestversion) {\n\t\t\t\tif (!empty(self::$latestversion['error']) && self::$latestversion['error']) {\n\t\t\t\t\t$result = -1;\n\t\t\t\t\tself::$lasterror = self::$latestversion['message'];\n\t\t\t\t} elseif (isset(self::$latestversion['has_latest']) && self::$latestversion['has_latest'] == false) {\n\t\t\t\t\t$result = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $result;\n\t}\n\n\tpublic static function downloadZip(string $newversion)\n\t{\n\t\t// define files to get\n\t\t$toLoad = str_replace('{version}', $newversion, self::RELEASE_URI);\n\t\t$toCheck = str_replace('{version}', $newversion, self::CHECKSUM_URI);\n\n\t\t// check for local destination folder\n\t\tif (!is_dir(Froxlor::getInstallDir() . '/updates/')) {\n\t\t\tmkdir(Froxlor::getInstallDir() . '/updates/');\n\t\t}\n\n\t\t// name archive\n\t\t$localArchive = Froxlor::getInstallDir() . '/updates/' . basename($toLoad);\n\n\t\t// remove old archive\n\t\tif (file_exists($localArchive)) {\n\t\t\t@unlink($localArchive);\n\t\t}\n\n\t\t// get archive data\n\t\ttry {\n\t\t\tHttpClient::fileGet($toLoad, $localArchive);\n\t\t} catch (Exception $e) {\n\t\t\treturn self::ERR_COULDNOTSTORE;\n\t\t}\n\n\t\t// validate the integrity of the downloaded file\n\t\t$_shouldsum = HttpClient::urlGet($toCheck);\n\t\tif (!empty($_shouldsum)) {\n\t\t\t$_t = explode(\" \", $_shouldsum);\n\t\t\t$shouldsum = $_t[0];\n\t\t} else {\n\t\t\t$shouldsum = null;\n\t\t}\n\t\t$filesum = hash_file('sha256', $localArchive);\n\n\t\tif ($filesum != $shouldsum) {\n\t\t\treturn self::ERR_CHKSUM_MISMATCH;\n\t\t}\n\n\t\treturn basename($localArchive);\n\t}\n\n\tpublic static function extractZip(string $localArchive): int\n\t{\n\t\tif (!file_exists($localArchive)) {\n\t\t\treturn self::ERR_ZIPNOTFOUND;\n\t\t}\n\t\t// decompress from zip\n\t\t$zip = new ZipArchive();\n\t\t$res = $zip->open($localArchive);\n\t\tif ($res === true) {\n\t\t\t$zip->extractTo(Froxlor::getInstallDir());\n\t\t\t$zip->close();\n\t\t\t// success - remove unused archive\n\t\t\t@unlink($localArchive);\n\t\t\t// reset cached version check\n\t\t\tSettings::Set('system.updatecheck_data', '');\n\t\t\t// wait a bit before we redirect to be sure\n\t\t\tsleep(3);\n\t\t\treturn 0;\n\t\t}\n\t\treturn self::ERR_COULDNOTEXTRACT;\n\t}\n\n\tprivate static function checkPrerequisites(): int\n\t{\n\t\tif (!extension_loaded('zip')) {\n\t\t\treturn self::ERR_NOZIPEXT;\n\t\t}\n\t\tif (version_compare(\"7.4.0\", PHP_VERSION, \">=\")) {\n\t\t\treturn self::ERR_MINPHP;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tpublic static function getLastError(): string\n\t{\n\t\treturn self::$lasterror ?? \"\";\n\t}\n\n\tpublic static function getFromResult(string $index)\n\t{\n\t\treturn self::$latestversion[$index] ?? \"\";\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Install/Install/Core.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Install\\Install;\n\nuse Exception;\nuse Froxlor\\Config\\ConfigParser;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\PhpHelper;\nuse PDO;\nuse PDOException;\nuse PDOStatement;\n\n/**\n * Installation of the froxlor core database and set settings.\n */\nclass Core\n{\n\tprotected array $validatedData;\n\n\tpublic function __construct(array $validatedData)\n\t{\n\t\t$this->validatedData = $validatedData;\n\t}\n\n\t/**\n\t * no missing fields or data -> perform actual install\n\t *\n\t * @return void\n\t * @throws Exception\n\t */\n\tpublic function doInstall(bool $create_ud_str = true)\n\t{\n\t\t$options = [\n\t\t\t'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8'\n\t\t];\n\n\t\tif (!empty($this->validatedData['mysql_ssl_ca_file'])) {\n\t\t\t$options[PDO::MYSQL_ATTR_SSL_CA] = $this->validatedData['mysql_ssl_ca_file'];\n\t\t\t$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$this->validatedData['mysql_ssl_verify_server_certificate'];\n\t\t}\n\n\t\t$dsn = \"mysql:host=\" . $this->validatedData['mysql_host'] . \";\";\n\t\ttry {\n\t\t\t$db_root = new PDO($dsn, $this->validatedData['mysql_root_user'], $this->validatedData['mysql_root_pass'], $options);\n\t\t} catch (PDOException $e) {\n\t\t\t// login failed; try to log in without passwd\n\t\t\ttry {\n\t\t\t\t$db_root = new PDO($dsn, $this->validatedData['mysql_root_user'], '', $options);\n\t\t\t\t// set the given password\n\t\t\t\t$passwd_stmt = $db_root->prepare(\"\n\t\t\t\t\tSET PASSWORD = PASSWORD(:passwd)\n\t\t\t\t\");\n\t\t\t\t$passwd_stmt->execute([\n\t\t\t\t\t'passwd' => $this->validatedData['mysql_root_pass']\n\t\t\t\t]);\n\t\t\t} catch (PDOException $e) {\n\t\t\t\t// login has failed; with and without password\n\t\t\t\tthrow new Exception(lng('install.errors.privileged_sql_connection_failed'), 0, $e);\n\t\t\t}\n\t\t}\n\n\t\t$version_server = $db_root->getAttribute(PDO::ATTR_SERVER_VERSION);\n\t\t$sql_mode = 'NO_ENGINE_SUBSTITUTION';\n\t\tif (version_compare($version_server, '8.0.11', '<')) {\n\t\t\t$sql_mode .= ',NO_AUTO_CREATE_USER';\n\t\t}\n\t\t$db_root->exec('SET sql_mode = \"' . $sql_mode . '\"');\n\n\t\t// ok, if we are here, the database connection is up and running\n\t\t// check for existing pdo and create backup if so\n\t\t$this->backupExistingDatabase($db_root);\n\t\t// create unprivileged user and the database itself\n\t\t$this->createDatabaseAndUser($db_root);\n\t\t// importing data to new database\n\t\t$this->importDatabaseData();\n\n\t\t// create DB object for new database\n\t\t$options = [\n\t\t\t'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8'\n\t\t];\n\n\t\tif (!empty($this->validatedData['mysql_ssl_ca_file']) && isset($this->validatedData['mysql_ssl_verify_server_certificate'])) {\n\t\t\t$options[PDO::MYSQL_ATTR_SSL_CA] = $this->validatedData['mysql_ssl_ca_file'];\n\t\t\t$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$this->validatedData['mysql_ssl_verify_server_certificate'];\n\t\t}\n\n\t\t$pdo = $this->getUnprivilegedPdo();\n\n\t\t// change settings accordingly\n\t\t$this->doSettings($pdo);\n\t\t// create entries\n\t\t$this->doDataEntries($pdo);\n\t\t// create JSON array for config-services\n\t\t$this->createJsonArray($pdo);\n\t\tif ($create_ud_str) {\n\t\t\t$this->createUserdataParamStr();\n\t\t}\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic function getUnprivilegedPdo(): PDO\n\t{\n\t\t$options = [\n\t\t\t'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8'\n\t\t];\n\n\t\tif (!empty($this->validatedData['mysql_ssl_ca_file'])) {\n\t\t\t$options[PDO::MYSQL_ATTR_SSL_CA] = $this->validatedData['mysql_ssl_ca_file'];\n\t\t\t$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$this->validatedData['mysql_ssl_verify_server_certificate'];\n\t\t}\n\n\t\t$dsn = \"mysql:host=\" . $this->validatedData['mysql_host'] . \";dbname=\" . $this->validatedData['mysql_database'] . \";\";\n\t\ttry {\n\t\t\t$pdo = new PDO($dsn, $this->validatedData['mysql_unprivileged_user'], $this->validatedData['mysql_unprivileged_pass'], $options);\n\t\t\t$version_server = $pdo->getAttribute(PDO::ATTR_SERVER_VERSION);\n\t\t\t$sql_mode = 'NO_ENGINE_SUBSTITUTION';\n\t\t\tif (version_compare($version_server, '8.0.11', '<')) {\n\t\t\t\t$sql_mode .= ',NO_AUTO_CREATE_USER';\n\t\t\t}\n\t\t\t$pdo->exec('SET sql_mode = \"' . $sql_mode . '\"');\n\t\t\treturn $pdo;\n\t\t} catch (PDOException $e) {\n\t\t\tthrow new Exception(lng('install.errors.unexpected_database_error', [$e->getMessage()]), 0, $e);\n\t\t}\n\t}\n\n\t/**\n\t * Check if an old database exists and back it up if necessary\n\t *\n\t * @param object $db_root\n\t * @return void\n\t * @throws Exception\n\t */\n\tprivate function backupExistingDatabase(object &$db_root)\n\t{\n\t\t// check for existing of former database\n\t\t$stmt = $db_root->prepare(\"SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = :database\");\n\t\t$stmt->execute([\n\t\t\t'database' => $this->validatedData['mysql_database']\n\t\t]);\n\t\t$rows = $db_root->query(\"SELECT FOUND_ROWS()\")->fetchColumn();\n\n\t\t// backup tables if exist\n\t\tif ($rows > 0) {\n\t\t\tif (!$this->validatedData['mysql_force_create']) {\n\t\t\t\tthrow new Exception(lng('install.errors.database_already_exiting'));\n\t\t\t}\n\n\t\t\t// create temporary backup-filename\n\t\t\t$filename = \"/tmp/froxlor_backup_\" . date('YmdHi') . \".sql\";\n\n\t\t\t// look for mysqldump\n\t\t\t$section = 'mysqldump';\n\t\t\tif (file_exists(\"/usr/bin/mysqldump\")) {\n\t\t\t\t$mysql_dump = '/usr/bin/mysqldump';\n\t\t\t} elseif (file_exists(\"/usr/local/bin/mysqldump\")) {\n\t\t\t\t$mysql_dump = '/usr/local/bin/mysqldump';\n\t\t\t} elseif (file_exists(\"/usr/bin/mariadb-dump\")) {\n\t\t\t\t$mysql_dump = '/usr/bin/mariadb-dump';\n\t\t\t\t$section = 'mariadb-dump';\n\t\t\t}\n\n\t\t\t// create temporary .cnf file\n\t\t\t$cnffilename = \"/tmp/froxlor_dump.cnf\";\n\t\t\t$dumpcnf = \"[\".$section.\"]\" . PHP_EOL . \"password=\\\"\" . $this->validatedData['mysql_root_pass'] . \"\\\"\" . PHP_EOL;\n\t\t\tfile_put_contents($cnffilename, $dumpcnf);\n\n\t\t\t// make the backup\n\t\t\tif (isset($mysql_dump)) {\n\t\t\t\t$command = $mysql_dump . \" --defaults-extra-file=\" . $cnffilename . \" \" . escapeshellarg($this->validatedData['mysql_database']) . \" -u \" . escapeshellarg($this->validatedData['mysql_root_user']) . \" --result-file=\" . $filename;\n\t\t\t\t$output = [];\n\t\t\t\texec($command, $output);\n\t\t\t\t@unlink($cnffilename);\n\t\t\t\tif (stristr(implode(\" \", $output), \"error\")) {\n\t\t\t\t\tthrow new Exception(lng('install.errors.mysqldump_backup_failed'));\n\t\t\t\t} elseif (!file_exists($filename)) {\n\t\t\t\t\tthrow new Exception(lng('install.errors.sql_backup_file_missing'));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new Exception(lng('install.errors.backup_binary_missing'));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create database and database-user\n\t *\n\t * @param object $db_root\n\t * @return void\n\t * @throws Exception\n\t */\n\tprivate function createDatabaseAndUser(object &$db_root)\n\t{\n\t\t$this->validatedData['mysql_access_host'] = $this->validatedData['mysql_host'];\n\n\t\t// so first we have to delete the database and\n\t\t// the user given for the unpriv-user if they exit\n\t\t$del_stmt = $db_root->prepare(\"DELETE FROM `mysql`.`user` WHERE `User` = :user AND `Host` = :accesshost\");\n\t\t$del_stmt->execute([\n\t\t\t'user' => $this->validatedData['mysql_unprivileged_user'],\n\t\t\t'accesshost' => $this->validatedData['mysql_access_host']\n\t\t]);\n\n\t\t$del_stmt = $db_root->prepare(\"DELETE FROM `mysql`.`db` WHERE `User` = :user AND `Host` = :accesshost\");\n\t\t$del_stmt->execute([\n\t\t\t'user' => $this->validatedData['mysql_unprivileged_user'],\n\t\t\t'accesshost' => $this->validatedData['mysql_access_host']\n\t\t]);\n\n\t\t$del_stmt = $db_root->prepare(\"DELETE FROM `mysql`.`tables_priv` WHERE `User` = :user AND `Host` =:accesshost\");\n\t\t$del_stmt->execute([\n\t\t\t'user' => $this->validatedData['mysql_unprivileged_user'],\n\t\t\t'accesshost' => $this->validatedData['mysql_access_host']\n\t\t]);\n\n\t\t$del_stmt = $db_root->prepare(\"DELETE FROM `mysql`.`columns_priv` WHERE `User` = :user AND `Host` = :accesshost\");\n\t\t$del_stmt->execute([\n\t\t\t'user' => $this->validatedData['mysql_unprivileged_user'],\n\t\t\t'accesshost' => $this->validatedData['mysql_access_host']\n\t\t]);\n\n\t\t$del_stmt = $db_root->prepare(\"DROP DATABASE IF EXISTS `\" . str_replace('`', '', $this->validatedData['mysql_database']) . \"`;\");\n\t\t$del_stmt->execute();\n\n\t\t$db_root->query(\"FLUSH PRIVILEGES;\");\n\n\t\t// we have to create a new user and database for the froxlor unprivileged mysql access\n\t\t$ins_stmt = $db_root->prepare(\"CREATE DATABASE `\" . str_replace('`', '', $this->validatedData['mysql_database']) . \"` CHARACTER SET=utf8 COLLATE=utf8_general_ci\");\n\t\t$ins_stmt->execute();\n\n\n\t\t$mysql_access_host_array = array_map('trim', explode(',', $this->validatedData['mysql_access_host']));\n\n\t\tif (in_array('127.0.0.1', $mysql_access_host_array) && !in_array('localhost', $mysql_access_host_array)) {\n\t\t\t$mysql_access_host_array[] = 'localhost';\n\t\t}\n\t\tif (!in_array('127.0.0.1', $mysql_access_host_array) && in_array('localhost', $mysql_access_host_array)) {\n\t\t\t$mysql_access_host_array[] = '127.0.0.1';\n\t\t}\n\t\tif (!empty($this->validatedData['serveripv4']) && !in_array($this->validatedData['serveripv4'], $mysql_access_host_array)) {\n\t\t\t$mysql_access_host_array[] = $this->validatedData['serveripv4'];\n\t\t}\n\t\tif (!empty($this->validatedData['serveripv6']) && !in_array($this->validatedData['serveripv6'], $mysql_access_host_array)) {\n\t\t\t$mysql_access_host_array[] = $this->validatedData['serveripv6'];\n\t\t}\n\t\t$mysql_access_host_array = array_unique($mysql_access_host_array);\n\n\t\tforeach ($mysql_access_host_array as $mysql_access_host) {\n\t\t\t$this->grantDbPrivilegesTo($db_root, $this->validatedData['mysql_database'], $this->validatedData['mysql_unprivileged_user'], $this->validatedData['mysql_unprivileged_pass'], $mysql_access_host);\n\t\t}\n\n\t\t$db_root->query(\"FLUSH PRIVILEGES;\");\n\t\t$this->validatedData['mysql_access_host'] = implode(',', $mysql_access_host_array);\n\t}\n\n\t/**\n\t * Grant privileges to given user.\n\t *\n\t * @param $db_root\n\t * @param $database\n\t * @param $username\n\t * @param $password\n\t * @param $access_host\n\t * @return void\n\t * @throws Exception\n\t */\n\tprivate function grantDbPrivilegesTo(&$db_root, $database, $username, $password, $access_host)\n\t{\n\t\tif ($this->validatedData['mysql_force_create']) {\n\t\t\ttry {\n\t\t\t\t// try to drop the user, but ignore exceptions as the mysql-access-hosts might\n\t\t\t\t// have changed and we would try to drop a non-existing user\n\t\t\t\t$drop_stmt = $db_root->prepare(\"DROP USER :username@:host\");\n\t\t\t\t$drop_stmt->execute([\n\t\t\t\t\t\"username\" => $username,\n\t\t\t\t\t\"host\" => $access_host\n\t\t\t\t]);\n\t\t\t} catch (PDOException $e) {\n\t\t\t\t/* continue */\n\t\t\t}\n\t\t}\n\t\tif (version_compare($db_root->getAttribute(PDO::ATTR_SERVER_VERSION), '8.0.11', '>=')) {\n\t\t\t// mariadb & mysql8\n\t\t\t// create user\n\t\t\t$stmt = $db_root->prepare(\"CREATE USER '\" . $username . \"'@'\" . $access_host . \"' IDENTIFIED BY :password\");\n\t\t\t$stmt->execute([\n\t\t\t\t\"password\" => $password\n\t\t\t]);\n\t\t\t// grant privileges\n\t\t\t$stmt = $db_root->prepare(\"GRANT ALL ON `\" . $database . \"`.* TO :username@:host\");\n\t\t\t$stmt->execute([\n\t\t\t\t\"username\" => $username,\n\t\t\t\t\"host\" => $access_host\n\t\t\t]);\n\t\t} else {\n\t\t\t// grant privileges\n\t\t\t$stmt = $db_root->prepare(\"GRANT ALL PRIVILEGES ON `\" . $database . \"`.* TO :username@:host IDENTIFIED BY :password\");\n\t\t\t$stmt->execute([\n\t\t\t\t\"username\" => $username,\n\t\t\t\t\"host\" => $access_host,\n\t\t\t\t\"password\" => $password\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * Import froxlor.sql into database\n\t *\n\t * @return void\n\t * @throws Exception\n\t */\n\tprivate function importDatabaseData()\n\t{\n\t\ttry {\n\t\t\t$pdo = $this->getUnprivilegedPdo();\n\t\t} catch (PDOException $e) {\n\t\t\tthrow new Exception(lng('install.errors.unprivileged_sql_connection_failed'));\n\t\t}\n\n\t\t// actually import data\n\t\ttry {\n\t\t\t$froxlorSQL = include dirname(__FILE__, 5) . '/install/froxlor.sql.php';\n\n\t\t\t$pdo->query($froxlorSQL);\n\t\t} catch (PDOException $e) {\n\t\t\tthrow new Exception(lng('install.errors.sql_import_failed', [$e->getMessage()]), 0, $e);\n\t\t}\n\t}\n\n\t/**\n\t * change settings according to users input\n\t *\n\t * @param object $db_user\n\t * @return void\n\t * @throws Exception\n\t */\n\tprivate function doSettings(object &$db_user)\n\t{\n\t\t$upd_stmt = $db_user->prepare(\"\n\t\t\tUPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `value` = :value\n\t\t\tWHERE `settinggroup` = :group AND `varname` = :varname\n\t\t\");\n\n\t\t$mainip = !empty($this->validatedData['serveripv6']) ? $this->validatedData['serveripv6'] : $this->validatedData['serveripv4'];\n\n\t\tif ($this->validatedData['use_admin_email_as_sender'] == '1') {\n\t\t\t$adminmail_value = $this->validatedData['admin_email'];\n\t\t} elseif ($this->validatedData['use_admin_email_as_sender'] == '0' && !empty($this->validatedData['sender_email'])) {\n\t\t\t$adminmail_value = $this->validatedData['sender_email'];\n\t\t} else {\n\t\t\t$adminmail_value = 'admin@' . $this->validatedData['servername'];\n\t\t}\n\t\t$this->updateSetting($upd_stmt, $adminmail_value, 'panel', 'adminmail');\n\t\t$this->updateSetting($upd_stmt, $mainip, 'system', 'ipaddress');\n\t\tif ($this->validatedData['use_ssl']) {\n\t\t\t$this->updateSetting($upd_stmt, 1, 'system', 'use_ssl');\n\t\t\t$this->updateSetting($upd_stmt, 1, 'system', 'leenabled');\n\t\t\t$this->updateSetting($upd_stmt, 1, 'system', 'le_froxlor_enabled');\n\t\t}\n\t\t$this->updateSetting($upd_stmt, strtolower($this->validatedData['servername']), 'system', 'hostname');\n\t\t$this->updateSetting($upd_stmt, 'en', 'panel', 'standardlanguage'); // TODO: set language\n\t\t$this->updateSetting($upd_stmt, $this->validatedData['mysql_access_host'], 'system', 'mysql_access_host');\n\t\t$this->updateSetting($upd_stmt, $this->validatedData['webserver'], 'system', 'webserver');\n\t\t$this->updateSetting($upd_stmt, $this->validatedData['httpuser'], 'system', 'httpuser');\n\t\t$this->updateSetting($upd_stmt, $this->validatedData['httpgroup'], 'system', 'httpgroup');\n\t\t$this->updateSetting($upd_stmt, $this->validatedData['distribution'], 'system', 'distribution');\n\n\t\t// necessary changes for webservers != apache2\n\t\tif ($this->validatedData['webserver'] == \"apache24\") {\n\t\t\t$this->updateSetting($upd_stmt, 'apache2', 'system', 'webserver');\n\t\t\t$this->updateSetting($upd_stmt, '1', 'system', 'apache24');\n\t\t} elseif ($this->validatedData['webserver'] == \"nginx\") {\n\t\t\t$this->updateSetting($upd_stmt, '/var/run/', 'phpfpm', 'fastcgi_ipcdir');\n\t\t\t$this->updateSetting($upd_stmt, 'error', 'system', 'errorlog_level');\n\t\t}\n\n\t\t$distros = glob(FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/') . '*.xml');\n\t\tforeach ($distros as $_distribution) {\n\t\t\tif ($this->validatedData['distribution'] == str_replace(\".xml\", \"\", strtolower(basename($_distribution)))) {\n\t\t\t\t$dist = new ConfigParser($_distribution);\n\t\t\t\t$defaults = $dist->getDefaults();\n\t\t\t\tif (!empty($defaults)) {\n\t\t\t\t\tforeach ($defaults as $property) {\n\t\t\t\t\t\tif (!isset($property->attributes()->for) || (isset($property->attributes()->for) && $property->attributes()->for == $this->validatedData['webserver'])) {\n\t\t\t\t\t\t\t$this->updateSetting($upd_stmt, $property->attributes()->value, $property->attributes()->settinggroup, $property->attributes()->varname);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$this->updateSetting($upd_stmt, $this->validatedData['activate_newsfeed'], 'admin', 'show_news_feed');\n\t\t$this->updateSetting($upd_stmt, dirname(__FILE__, 5), 'system', 'letsencryptchallengepath');\n\t\t$this->updateSetting($upd_stmt, dirname(__FILE__, 5) . '/templates/misc/deactivated/', 'system', 'deactivateddocroot');\n\n\t\t// insert the lastcronrun to be the installation date\n\t\t$this->updateSetting($upd_stmt, time(), 'system', 'lastcronrun');\n\n\t\t// set settings according to selected php-backend\n\t\tif ($this->validatedData['webserver_backend'] == 'php-fpm') {\n\t\t\t$this->updateSetting($upd_stmt, '1', 'phpfpm', 'enabled');\n\t\t\t$this->updateSetting($upd_stmt, '1', 'phpfpm', 'enabled_ownvhost');\n\t\t} elseif ($this->validatedData['webserver_backend'] == 'fcgid') {\n\t\t\t$this->updateSetting($upd_stmt, '1', 'system', 'mod_fcgid');\n\t\t\t$this->updateSetting($upd_stmt, '1', 'system', 'mod_fcgid_ownvhost');\n\t\t}\n\n\t\t// check currently used php version and set values of fpm/fcgid accordingly\n\t\tif (defined('PHP_MAJOR_VERSION') && defined('PHP_MINOR_VERSION')) {\n\t\t\t// php-fpm\n\t\t\t$reload = \"service php\" . PHP_MAJOR_VERSION . \".\" . PHP_MINOR_VERSION . \"-fpm restart\";\n\t\t\t$config_dir = \"/etc/php/\" . PHP_MAJOR_VERSION . \".\" . PHP_MINOR_VERSION . \"/fpm/pool.d/\";\n\t\t\t// fcgid\n\t\t\tif ($this->validatedData['distribution'] == 'bookworm' || $this->validatedData['distribution'] == 'trixie') {\n\t\t\t\t$binary = \"/usr/bin/php-cgi\" . PHP_MAJOR_VERSION . \".\" . PHP_MINOR_VERSION;\n\t\t\t} else {\n\t\t\t\t$binary = \"/usr/bin/php\" . PHP_MAJOR_VERSION . \".\" . PHP_MINOR_VERSION . \"-cgi\";\n\t\t\t}\n\t\t\t$db_user->query(\"UPDATE `\" . TABLE_PANEL_FPMDAEMONS . \"` SET `reload_cmd` = '\" . $reload . \"', `config_dir` = '\" . $config_dir . \"' WHERE `id` ='1';\");\n\t\t\t$db_user->query(\"UPDATE `\" . TABLE_PANEL_PHPCONFIGS . \"` SET `binary` = '\" . $binary . \"';\");\n\t\t}\n\n\t\tif ($this->validatedData['use_ssl']) {\n\t\t\t// enable let's encrypt cron\n\t\t\t$db_user->query(\"UPDATE `\" . TABLE_PANEL_CRONRUNS . \"` SET `isactive` = '1' WHERE `module` = 'froxlor/letsencrypt';\");\n\t\t}\n\n\t\t// set specific times for some crons (traffic only at night, etc.)\n\t\t$timestamp = mktime(0, 0, 0, date('m', time()), date('d', time()), date('Y', time()));\n\t\t$db_user->query(\"UPDATE `\" . TABLE_PANEL_CRONRUNS . \"` SET `lastrun` = '\" . $timestamp . \"' WHERE `cronfile` ='cron_traffic';\");\n\n\t\t// insert task 99 to generate a correct cron.d-file automatically\n\t\t$db_user->query(\"INSERT INTO `\" . TABLE_PANEL_TASKS . \"` SET `type` = '99';\");\n\t}\n\n\t/**\n\t * execute prepared statement to update settings\n\t *\n\t * @param PDOStatement|null $stmt\n\t * @param string|null $group\n\t * @param string|null $varname\n\t * @param string|null $value\n\t */\n\tprivate function updateSetting(PDOStatement &$stmt = null, string $value = null, string $group = null, string $varname = null)\n\t{\n\t\t$stmt->execute([\n\t\t\t'group' => $group,\n\t\t\t'varname' => $varname,\n\t\t\t'value' => $value\n\t\t]);\n\t}\n\n\t/**\n\t * create corresponding entries in froxlor database\n\t *\n\t * @param $db_user\n\t * @return void\n\t */\n\tprivate function doDataEntries(&$db_user)\n\t{\n\t\t// lets insert the default ip and port\n\t\t$stmt = $db_user->prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_IPSANDPORTS . \"` SET\n\t\t\t`ip`= :serverip,\n\t\t\t`port` = :serverport,\n\t\t\t`namevirtualhost_statement` = :nvh,\n\t\t\t`vhostcontainer` = '1',\n\t\t\t`vhostcontainer_servername_statement` = '1',\n\t\t\t`ssl` = :ssl\n\t\t\");\n\t\t$nvh = $this->validatedData['webserver'] == 'apache2' ? '1' : '0';\n\t\t$defaultip = false;\n\t\tif (!empty($this->validatedData['serveripv6'])) {\n\t\t\t$stmt->execute([\n\t\t\t\t'nvh' => $nvh,\n\t\t\t\t'serverip' => $this->validatedData['serveripv6'],\n\t\t\t\t'serverport' => 80,\n\t\t\t\t'ssl' => 0\n\t\t\t]);\n\t\t\t$defaultip = $db_user->lastInsertId();\n\t\t}\n\t\tif (!empty($this->validatedData['serveripv4'])) {\n\t\t\t$stmt->execute([\n\t\t\t\t'nvh' => $nvh,\n\t\t\t\t'serverip' => $this->validatedData['serveripv4'],\n\t\t\t\t'serverport' => 80,\n\t\t\t\t'ssl' => 0\n\t\t\t]);\n\t\t\t$lastinsert = $db_user->lastInsertId();\n\t\t\t$defaultip = $defaultip != false ? $defaultip . ',' . $lastinsert : $lastinsert;\n\t\t}\n\n\t\t$defaultsslip = false;\n\t\tif ($this->validatedData['use_ssl']) {\n\t\t\tif (!empty($this->validatedData['serveripv6'])) {\n\t\t\t\t$stmt->execute([\n\t\t\t\t\t'nvh' => $this->validatedData['webserver'] == 'apache2' ? '1' : '0',\n\t\t\t\t\t'serverip' => $this->validatedData['serveripv6'],\n\t\t\t\t\t'serverport' => 443,\n\t\t\t\t\t'ssl' => 1\n\t\t\t\t]);\n\t\t\t\t$defaultsslip = $db_user->lastInsertId();\n\t\t\t}\n\t\t\tif (!empty($this->validatedData['serveripv4'])) {\n\t\t\t\t$stmt->execute([\n\t\t\t\t\t'nvh' => $this->validatedData['webserver'] == 'apache2' ? '1' : '0',\n\t\t\t\t\t'serverip' => $this->validatedData['serveripv4'],\n\t\t\t\t\t'serverport' => 443,\n\t\t\t\t\t'ssl' => 1\n\t\t\t\t]);\n\t\t\t\t$lastinsert = $db_user->lastInsertId();\n\t\t\t\t$defaultsslip = $defaultsslip != false ? $defaultsslip . ',' . $lastinsert : $lastinsert;\n\t\t\t}\n\t\t}\n\n\t\t// insert the defaultip\n\t\t$upd_stmt = $db_user->prepare(\"\n\t\t\tUPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET\n\t\t\t`value` = :defaultip\n\t\t\tWHERE `settinggroup` = 'system' AND `varname` = :defipfld\n\t\t\");\n\t\t$upd_stmt->execute([\n\t\t\t'defaultip' => $defaultip,\n\t\t\t'defipfld' => 'defaultip'\n\t\t]);\n\n\t\tif ($defaultsslip) {\n\t\t\t$upd_stmt->execute([\n\t\t\t\t'defaultip' => $defaultsslip,\n\t\t\t\t'defipfld' => 'defaultsslip'\n\t\t\t]);\n\t\t}\n\n\t\t// last but not least create the main admin\n\t\t$ins_data = [\n\t\t\t'loginname' => $this->validatedData['admin_user'],\n\t\t\t'password' => password_hash($this->validatedData['admin_pass'], PASSWORD_DEFAULT),\n\t\t\t'adminname' => $this->validatedData['admin_name'],\n\t\t\t'email' => $this->validatedData['admin_email'],\n\t\t\t'deflang' => 'en' // TODO: set language\n\t\t];\n\t\t$ins_stmt = $db_user->prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_ADMINS . \"` SET\n\t\t\t`loginname` = :loginname,\n\t\t\t`password` = :password,\n\t\t\t`name` = :adminname,\n\t\t\t`email` = :email,\n\t\t\t`def_language` = :deflang,\n\t\t\t`api_allowed` = 1,\n\t\t\t`customers` = -1,\n\t\t\t`customers_see_all` = 1,\n\t\t\t`caneditphpsettings` = 1,\n\t\t\t`domains` = -1,\n\t\t\t`change_serversettings` = 1,\n\t\t\t`diskspace` = -1024,\n\t\t\t`mysqls` = -1,\n\t\t\t`emails` = -1,\n\t\t\t`email_accounts` = -1,\n\t\t\t`email_forwarders` = -1,\n\t\t\t`email_quota` = -1,\n\t\t\t`ftps` = -1,\n\t\t\t`subdomains` = -1,\n\t\t\t`traffic` = -1048576\n\t\t\");\n\n\t\t$ins_stmt->execute($ins_data);\n\t}\n\n\t/**\n\t * Create userdata.inc.php file\n\t *\n\t * @return void\n\t * @throws Exception\n\t */\n\tpublic function createUserdataConf()\n\t{\n\t\t$userdata = [\n\t\t\t'sql' => [\n\t\t\t\t'debug' => false,\n\t\t\t\t'host' => $this->validatedData['mysql_host'],\n\t\t\t\t'user' => $this->validatedData['mysql_unprivileged_user'],\n\t\t\t\t'password' => $this->validatedData['mysql_unprivileged_pass'],\n\t\t\t\t'db' => $this->validatedData['mysql_database'],\n\t\t\t],\n\t\t\t'sql_root' => [\n\t\t\t\t'0' => [\n\t\t\t\t\t'caption' => 'Default',\n\t\t\t\t\t'host' => $this->validatedData['mysql_host'],\n\t\t\t\t\t'user' => $this->validatedData['mysql_root_user'],\n\t\t\t\t\t'password' => $this->validatedData['mysql_root_pass'],\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\n\t\t// enable sql ssl in userdata for unprivileged and root db user\n\t\tif (!empty($this->validatedData['mysql_ssl_ca_file']) && isset($this->validatedData['mysql_ssl_verify_server_certificate'])) {\n\t\t\t$userdata['sql']['ssl'] = [\n\t\t\t\t'caFile' => $this->validatedData['mysql_ssl_ca_file'],\n\t\t\t\t'verifyServerCertificate' => (bool)$this->validatedData['mysql_ssl_verify_server_certificate'],\n\t\t\t];\n\t\t\t$userdata['sql_root']['0']['ssl'] = [\n\t\t\t\t'caFile' => $this->validatedData['mysql_ssl_ca_file'],\n\t\t\t\t'verifyServerCertificate' => (bool)$this->validatedData['mysql_ssl_verify_server_certificate'],\n\t\t\t];\n\t\t}\n\n\t\t// test if we can store the userdata.inc.php in ../lib\n\t\t$umask = @umask(077);\n\t\t$userdata = PhpHelper::parseArrayToPhpFile($userdata);\n\t\t$userdata_file = dirname(__FILE__, 5) . '/lib/userdata.inc.php';\n\t\tif (@touch($userdata_file) && @is_writable($userdata_file)) {\n\t\t\t$fp = @fopen($userdata_file, 'w');\n\t\t\t@fputs($fp, $userdata, strlen($userdata));\n\t\t\t@fclose($fp);\n\t\t} else {\n\t\t\t@unlink($userdata_file);\n\t\t\t// try creating it in a temporary file\n\t\t\t$temp_file = @tempnam(sys_get_temp_dir(), 'fx');\n\t\t\tif ($temp_file) {\n\t\t\t\t$fp = @fopen($temp_file, 'w');\n\t\t\t\t@fputs($fp, $userdata, strlen($userdata));\n\t\t\t\t@fclose($fp);\n\t\t\t} else {\n\t\t\t\tthrow new Exception(lng('install.errors.creating_configfile_failed'));\n\t\t\t}\n\t\t}\n\t\t@umask($umask);\n\t}\n\n\tprivate function createJsonArray(&$db_user)\n\t{\n\t\t// use traffic analyzer and ftpserver from settings as we could define defaults in the lib/configfiles/*.xml templates\n\t\t// which can be useful for third-party package-maintainer (e.g. other distros) to have more control\n\t\t// over the installation defaults (less hardcoded values)\n\t\t$custom_dependency = $db_user->query(\"\n\t\t\tSELECT `varname`, `value` FROM `\" . TABLE_PANEL_SETTINGS . \"`\n\t\t\tWHERE `settinggroup` = 'system' AND (`varname` = 'traffictool' OR `varname` = 'ftpserver')\n\t\t\");\n\t\t$cd_result = $custom_dependency->fetchAll(\\PDO::FETCH_KEY_PAIR);\n\t\t$system_params = [\"cron\", \"libnssextrausers\", \"logrotate\"];\n\t\tif (isset($cd_result['traffictool'])) {\n\t\t\t$system_params[] = $cd_result['traffictool'];\n\t\t}\n\t\tif ($this->validatedData['webserver_backend'] == 'php-fpm') {\n\t\t\t$system_params[] = 'php-fpm';\n\t\t} elseif ($this->validatedData['webserver_backend'] == 'fcgid') {\n\t\t\t$system_params[] = 'fcgid';\n\t\t}\n\t\t$json_params = [\n\t\t\t'distro' => $this->validatedData['distribution'],\n\t\t\t'dns' => 'x',\n\t\t\t'http' => $this->validatedData['webserver'],\n\t\t\t'smtp' => 'postfix_dovecot',\n\t\t\t'mail' => 'dovecot_postfix2',\n\t\t\t'antispam' => 'rspamd',\n\t\t\t'ftp' => $cd_result['ftpserver'] ?? 'x',\n\t\t\t'system' => $system_params\n\t\t];\n\t\t$_SESSION['installation']['json_params'] = json_encode($json_params);\n\t}\n\n\tprivate function createUserdataParamStr()\n\t{\n\t\t$req_fields = [\n\t\t\t'mysql_host',\n\t\t\t'mysql_unprivileged_user',\n\t\t\t'mysql_unprivileged_pass',\n\t\t\t'mysql_database',\n\t\t\t'mysql_root_user',\n\t\t\t'mysql_root_pass',\n\t\t\t'mysql_ssl_ca_file',\n\t\t\t'mysql_ssl_verify_server_certificate'\n\t\t];\n\t\t$json_params = [];\n\t\tforeach ($req_fields as $field) {\n\t\t\t$json_params[$field] = $this->validatedData[$field] ?? \"\";\n\t\t}\n\t\t$_SESSION['installation']['ud_str'] = base64_encode(json_encode($json_params));\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Install/Install.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Install;\n\nuse Exception;\nuse Froxlor\\Config\\ConfigParser;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Install\\Install\\Core;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\IPTools;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\nclass Install\n{\n\tpublic $currentStep;\n\tpublic $extendedView;\n\tpublic $maxSteps;\n\tpublic $phpVersion;\n\tpublic $formfield;\n\tpublic array $suggestions = [];\n\tpublic array $criticals = [];\n\tpublic array $loadedExtensions;\n\tpublic array $supportedOS = [];\n\tpublic array $webserverBackend = [\n\t\t'php-fpm' => 'PHP-FPM',\n\t\t'fcgid' => 'FCGID (apache2 only)',\n\t\t'mod_php' => 'mod_php (not recommended)',\n\t];\n\n\tpublic function __construct(array $cliData = [])\n\t{\n\t\t// set actual php version and extensions\n\t\t$this->phpVersion = phpversion();\n\t\t$this->loadedExtensions = get_loaded_extensions();\n\n\t\t// get all supported OS\n\t\t// show list of available distro's\n\t\t$distros = glob(dirname(__DIR__, 3) . '/lib/configfiles/*.xml');\n\t\t$distributions_select[''] = '-';\n\t\tif (in_array('xml', $this->loadedExtensions)) {\n\t\t\t// read in all the distros\n\t\t\tforeach ($distros as $distribution) {\n\t\t\t\t// get configparser object\n\t\t\t\t$dist = new ConfigParser($distribution);\n\t\t\t\t// store in tmp array\n\t\t\t\t$this->supportedOS[str_replace(\".xml\", \"\", strtolower(basename($distribution)))] = $dist->getCompleteDistroName();\n\t\t\t}\n\t\t\t// sort by distribution name\n\t\t\tasort($this->supportedOS);\n\t\t}\n\n\t\t// guess distribution and webserver to preselect in formfield\n\t\t$webserverBackend = $this->webserverBackend;\n\t\t$supportedOS = $this->supportedOS;\n\t\t$guessedDistribution = $this->guessDistribution();\n\t\t$guessedWebserver = $this->guessWebserver();\n\n\t\t// set formfield, so we can get the fields and steps etc.\n\t\t$this->formfield = require dirname(__DIR__, 3) . '/lib/formfields/install/formfield.install.php';\n\n\t\t// set actual step\n\t\t$this->currentStep = $cliData['step'] ?? Request::any('step', 0);\n\t\t$this->extendedView = $cliData['extended'] ?? Request::any('extended', 0);\n\t\t$this->maxSteps = count($this->formfield['install']['sections']);\n\n\t\tif (empty($cliData)) {\n\t\t\t// set global variables\n\t\t\tUI::twig()->addGlobal('install_mode', true);\n\t\t\tUI::twig()->addGlobal('basehref', '../');\n\n\t\t\t// unset session if user goes back to step 0\n\t\t\tif (isset($_SESSION['installation']) && $this->currentStep == 0) {\n\t\t\t\tunset($_SESSION['installation']);\n\t\t\t}\n\n\t\t\t// check for url manipulation or wrong step\n\t\t\tif ((isset($_SESSION['installation']['stepCompleted']) && $this->currentStep > $_SESSION['installation']['stepCompleted'])\n\t\t\t\t|| (!isset($_SESSION['installation']['stepCompleted']) && $this->currentStep > 0)\n\t\t\t) {\n\t\t\t\t$this->currentStep = isset($_SESSION['installation']['stepCompleted']) ? $_SESSION['installation']['stepCompleted'] + 1 : 1;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @return void\n\t * @throws Exception\n\t */\n\tpublic function handle(): void\n\t{\n\t\t// handle form data\n\t\tif (!is_null(Request::any('submit')) && $this->currentStep) {\n\t\t\ttry {\n\t\t\t\t$this->handleFormData($this->formfield['install']);\n\t\t\t} catch (Exception $e) {\n\t\t\t\t$error = $e->getMessage();\n\t\t\t}\n\t\t}\n\n\t\t// load template\n\t\tUI::twigBuffer('/install/index.html.twig', [\n\t\t\t'setup' => [\n\t\t\t\t'step' => $this->currentStep,\n\t\t\t\t'max_steps' => $this->maxSteps,\n\t\t\t],\n\t\t\t'preflight' => $this->checkRequirements(),\n\t\t\t'page' => [\n\t\t\t\t'title' => 'Database',\n\t\t\t\t'description' => 'Test',\n\t\t\t],\n\t\t\t'section' => $this->formfield['install']['sections']['step' . $this->currentStep] ?? [],\n\t\t\t'error' => $error ?? null,\n\t\t\t'extended' => $this->extendedView,\n\t\t\t'csrf_token' => Froxlor::genSessionId(20),\n\t\t]);\n\n\t\t// output view\n\t\tUI::twigOutputBuffer();\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function handleFormData(array $formfield): void\n\t{\n\t\t// handle current step\n\t\tif ($this->currentStep <= $this->maxSteps) {\n\t\t\t// Validate user data\n\t\t\t$validatedData = $this->validateRequest($formfield['sections']['step' . $this->currentStep]['fields']);\n\t\t\tif ($this->currentStep == 1) {\n\t\t\t\t// Check database connection\n\t\t\t\t$this->checkDatabase($validatedData);\n\t\t\t} elseif ($this->currentStep == 2) {\n\t\t\t\t// Check validity of admin user data\n\t\t\t\t$this->checkAdminUser($validatedData);\n\t\t\t} elseif ($this->currentStep == 3) {\n\t\t\t\t// Check validity of system data\n\t\t\t\t$this->checkSystem($validatedData);\n\t\t\t}\n\t\t\t$validatedData['stepCompleted'] = ($this->currentStep < $this->maxSteps) ? $this->currentStep : ($this->maxSteps - 1);\n\t\t\t// Store validated data for later use\n\t\t\t$_SESSION['installation'] = array_merge($_SESSION['installation'] ?? [], $validatedData);\n\t\t}\n\n\t\t// also handle completion of installation if it's the step before the last step\n\t\tif ($this->currentStep == ($this->maxSteps - 1)) {\n\t\t\t$core = new Core($_SESSION['installation']);\n\t\t\t$core->doInstall();\n\t\t}\n\n\t\t// redirect user to home if the installation is done\n\t\tif ($this->currentStep == $this->maxSteps) {\n\t\t\t// check setting for \"panel.is_configured\" whether user has\n\t\t\t// run the config-services script (or checked the manual mode)\n\t\t\tif ($this->checkInstallStateFinished()) {\n\t\t\t\theader('Location: ../');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new Exception(lng('install.errors.notyetconfigured'));\n\t\t}\n\n\t\t// redirect to next step\n\t\theader('Location: ?step=' . ($this->currentStep + 1));\n\t}\n\n\tprivate function checkInstallStateFinished(): bool\n\t{\n\t\t$core = new Core($_SESSION['installation']);\n\t\tif (isset($_SESSION['installation']['manual_config']) && (int)$_SESSION['installation']['manual_config'] == 1) {\n\t\t\t$core->createUserdataConf();\n\t\t\treturn true;\n\t\t}\n\t\t$pdo = $core->getUnprivilegedPdo();\n\t\t$stmt = $pdo->prepare(\"SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'panel' AND `varname` = 'is_configured'\");\n\t\t$stmt->execute();\n\t\t$result = $stmt->fetch(PDO::FETCH_ASSOC);\n\t\tif ($result && (int)$result['value'] == 1) {\n\t\t\t$core->createUserdataConf();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @return array\n\t */\n\tpublic function checkRequirements(): array\n\t{\n\t\t// check whether we can read the userdata file\n\t\tif (!@touch(dirname(__DIR__, 2) . '/.~writecheck')) {\n\t\t\t// get possible owner\n\t\t\t$posixusername = posix_getpwuid(posix_getuid())['name'];\n\t\t\t$posixgroup = posix_getgrgid(posix_getgid())['name'];\n\t\t\t$this->criticals['wrong_ownership'] = ['user' => $posixusername, 'group' => $posixgroup];\n\t\t} else {\n\t\t\t@unlink(dirname(__DIR__, 2) . '/.~writecheck');\n\t\t}\n\n\t\t// check for required extensions\n\t\tforeach (Requirements::REQUIRED_EXTENSIONS as $requiredExtension) {\n\t\t\tif (in_array($requiredExtension, $this->loadedExtensions)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$this->criticals['missing_extensions'][] = $requiredExtension;\n\t\t}\n\n\t\t// check for suggested extensions\n\t\tforeach (Requirements::SUGGESTED_EXTENSIONS as $suggestedExtension) {\n\t\t\tif (in_array($suggestedExtension, $this->loadedExtensions)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$this->suggestions['missing_extensions'][] = $suggestedExtension;\n\t\t}\n\n\t\treturn [\n\t\t\t'text' => $this->getInformationText(),\n\t\t\t'suggestions' => $this->suggestions,\n\t\t\t'criticals' => $this->criticals,\n\t\t];\n\t}\n\n\t/**\n\t * @return string\n\t */\n\tprivate function getInformationText(): string\n\t{\n\t\tif (version_compare(Requirements::REQUIRED_VERSION, PHP_VERSION, \"<\")) {\n\t\t\t$text = lng('install.phpinfosuccess', [$this->phpVersion]);\n\t\t} else {\n\t\t\t$text = lng('install.phpinfowarn', [Requirements::REQUIRED_VERSION]);\n\t\t\t$this->criticals[] = lng('install.phpinfoupdate', [$this->phpVersion, Requirements::REQUIRED_VERSION]);\n\t\t}\n\t\treturn $text;\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate function validateRequest(array $fields): array\n\t{\n\t\t$attributes = [];\n\t\tforeach ($fields as $name => $field) {\n\t\t\t$attributes[$name] = $this->validateAttribute(Request::any($name), $field);\n\t\t\tif (isset($field['next_to'])) {\n\t\t\t\t$attributes = array_merge($attributes, $this->validateRequest($field['next_to']));\n\t\t\t}\n\t\t}\n\t\treturn $attributes;\n\t}\n\n\t/**\n\t * @return mixed\n\t * @throws Exception\n\t */\n\tprivate function validateAttribute($attribute, array $field)\n\t{\n\t\t// TODO: do validations\n\t\tif (isset($field['mandatory']) && $field['mandatory'] && empty($attribute)) {\n\t\t\tthrow new Exception(lng('install.errors.mandatory_field_not_set', [$field['label']]));\n\t\t}\n\t\treturn $attribute;\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic function checkSystem(array $validatedData): void\n\t{\n\t\t$serveripv4 = $validatedData['serveripv4'] ?? '';\n\t\t$serveripv6 = $validatedData['serveripv6'] ?? '';\n\t\t$servername = $validatedData['servername'] ?? '';\n\t\t$httpuser = $validatedData['httpuser'] ?? 'www-data';\n\t\t$httpgroup = $validatedData['httpgroup'] ?? 'www-data';\n\n\t\tif (empty($serveripv4) && empty($serveripv6)) {\n\t\t\tthrow new Exception(lng('install.errors.nov4andnov6ip'));\n\t\t} elseif (!empty($serveripv4) && (!Validate::validate_ip2($serveripv4, true, '', false, true) || IPTools::is_ipv6($serveripv4))) {\n\t\t\tthrow new Exception(lng('error.invalidip', [$serveripv4]));\n\t\t} elseif (!empty($serveripv6) && (!Validate::validate_ip2($serveripv6, true, '', false, true) || !IPTools::is_ipv6($serveripv6))) {\n\t\t\tthrow new Exception(lng('error.invalidip', [$serveripv6]));\n\t\t} elseif (!Validate::validateDomain($servername)) {\n\t\t\tthrow new Exception(lng('install.errors.servernameneedstobevalid'));\n\t\t} elseif (posix_getpwnam($httpuser) === false) {\n\t\t\tthrow new Exception(lng('install.errors.websrvuserdoesnotexist'));\n\t\t} elseif (posix_getgrnam($httpgroup) === false) {\n\t\t\tthrow new Exception(lng('install.errors.websrvgrpdoesnotexist'));\n\t\t}\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic function checkAdminUser(array $validatedData): void\n\t{\n\t\t$name = $validatedData['admin_name'] ?? 'Administrator';\n\t\t$loginname = $validatedData['admin_user'] ?? '';\n\t\t$email = $validatedData['admin_email'] ?? '';\n\t\t$password = $validatedData['admin_pass'] ?? '';\n\t\t$password_confirm = $validatedData['admin_pass_confirm'] ?? '';\n\t\t$useadminmailassender = $validatedData['use_admin_email_as_sender'] ?? '1';\n\t\t$senderemail = $validatedData['sender_email'] ?? '';\n\n\t\tif (!preg_match('/^[^\\r\\n\\t\\f\\0]*$/D', $name)) {\n\t\t\tthrow new Exception(lng('error.stringformaterror', ['admin_name']));\n\t\t} elseif (empty(trim($loginname)) || !preg_match('/^[a-z][a-z0-9]+$/Di', $loginname)) {\n\t\t\tthrow new Exception(lng('error.loginnameiswrong', [$loginname]));\n\t\t} elseif (empty(trim($email)) || !Validate::validateEmail($email)) {\n\t\t\tthrow new Exception(lng('error.emailiswrong', [$email]));\n\t\t} elseif ((int)$useadminmailassender == 0 && !empty(trim($senderemail)) && !Validate::validateEmail($senderemail)) {\n\t\t\tthrow new Exception(lng('error.emailiswrong', [$senderemail]));\n\t\t} elseif (empty($password) || $password != $password_confirm) {\n\t\t\tthrow new Exception(lng('error.newpasswordconfirmerror'));\n\t\t} elseif ($password == $loginname) {\n\t\t\tthrow new Exception(lng('error.passwordshouldnotbeusername'));\n\t\t}\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic function checkDatabase(array $validatedData): void\n\t{\n\t\t$dsn = sprintf('mysql:host=%s;charset=utf8', $validatedData['mysql_host']);\n\t\t$pdo = new \\PDO($dsn, $validatedData['mysql_root_user'], $validatedData['mysql_root_pass']);\n\n\t\t// check if the database already exist\n\t\t$stmt = $pdo->prepare('SHOW DATABASES LIKE ?');\n\t\t$stmt->execute([\n\t\t\t$validatedData['mysql_database']\n\t\t]);\n\t\t$hasDatabase = $stmt->fetch();\n\t\tif ($hasDatabase && !$validatedData['mysql_force_create']) {\n\t\t\tthrow new Exception(lng('install.errors.databaseexists'));\n\t\t}\n\n\t\t// check if we can create a new database\n\t\t$testDatabase = uniqid('froxlor_tmp_');\n\t\tif ($pdo->exec('CREATE DATABASE IF NOT EXISTS ' . $testDatabase . ';') === false) {\n\t\t\tthrow new Exception(lng('install.errors.unabletocreatedb'));\n\t\t}\n\t\tif ($pdo->exec('DROP DATABASE IF EXISTS ' . $testDatabase . ';') === false) {\n\t\t\tthrow new Exception(lng('install.errors.unabletodropdb'));\n\t\t}\n\n\t\t// check if the user already exist\n\t\t$stmt = $pdo->prepare(\"SELECT `User` FROM `mysql`.`user` WHERE `User` = ?\");\n\t\t$stmt->execute([$validatedData['mysql_unprivileged_user']]);\n\t\tif ($stmt->rowCount() && !$validatedData['mysql_force_create']) {\n\t\t\tthrow new Exception(lng('install.errors.mysqlusernameexists'));\n\t\t}\n\n\t\t// check if we can create a new user\n\t\t$testUser = uniqid('froxlor_tmp_');\n\t\t$stmt = $pdo->prepare('CREATE USER ?@? IDENTIFIED BY ?');\n\t\tif ($stmt->execute([$testUser, $validatedData['mysql_host'], uniqid()]) === false) {\n\t\t\tthrow new Exception(lng('install.errors.unabletocreateuser'));\n\t\t}\n\t\t$stmt = $pdo->prepare('DROP USER ?@?');\n\t\tif ($stmt->execute([$testUser, $validatedData['mysql_host']]) === false) {\n\t\t\tthrow new Exception(lng('install.errors.unabletodropuser'));\n\t\t}\n\t\tif ($pdo->prepare('FLUSH PRIVILEGES')->execute() === false) {\n\t\t\tthrow new Exception(lng('install.errors.unabletoflushprivs'));\n\t\t}\n\t}\n\n\tprivate function guessWebserver(): ?string\n\t{\n\t\tif (strtoupper(@php_sapi_name()) == \"APACHE2HANDLER\" || stristr($_SERVER['SERVER_SOFTWARE'], \"apache\")) {\n\t\t\treturn 'apache24';\n\t\t} elseif (substr(strtoupper(@php_sapi_name()), 0, 8) == \"NGINX\" || stristr($_SERVER['SERVER_SOFTWARE'], \"nginx\")) {\n\t\t\treturn 'nginx';\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate function guessDistribution(): ?string\n\t{\n\t\treturn Cronjob::checkCurrentDistro(true);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Install/Preconfig.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Install;\n\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\n\nclass Preconfig\n{\n\tprivate $preconfig_data = [];\n\n\t/**\n\t * returns whether there are preconfig items in an update\n\t *\n\t * @return bool\n\t */\n\tpublic function hasPreConfig(): bool\n\t{\n\t\treturn count($this->preconfig_data) > 0;\n\t}\n\n\t/**\n\t * return all collected preconfig data\n\t *\n\t * @return array\n\t */\n\tpublic function getData(): array\n\t{\n\t\treturn $this->preconfig_data;\n\t}\n\n\t/**\n\t * adds an preconfig result-array to the preconfig-data\n\t *\n\t * @param array $array\n\t *\n\t * @return void\n\t */\n\tpublic function addToPreConfig(array $array)\n\t{\n\t\tif (isset($array['title']) && isset($array['fields']) && count($array['fields']) > 0) {\n\t\t\t$this->preconfig_data[] = $array;\n\t\t}\n\t}\n\n\t/**\n\t * read in all preconfig files and build up data-array for admin_updates\n\t */\n\tpublic function __construct()\n\t{\n\t\t$preconfigs = glob(Froxlor::getInstallDir() . '/install/updates/preconfig/*.php');\n\n\t\tif (!empty($preconfigs)) {\n\t\t\t$current_version = Settings::Get('panel.version');\n\t\t\t$current_db_version = Settings::Get('panel.db_version');\n\t\t\tif (empty($current_db_version)) {\n\t\t\t\t$current_db_version = \"0\";\n\t\t\t}\n\t\t\tforeach (array_reverse($preconfigs) as $preconfig_file) {\n\t\t\t\t$pconf = include $preconfig_file;\n\t\t\t\t$this->addToPreConfig($pconf);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Function getPreConfig\n\t *\n\t * outputs various form-field-arrays before the update process\n\t * can be continued (asks for agreement whatever is being asked)\n\t *\n\t * @param bool $no_check\n\t * @return array\n\t */\n\tpublic static function getPreConfig(bool $no_check = false): array\n\t{\n\t\t$preconfig = new self();\n\n\t\tif ($preconfig->hasPreConfig()) {\n\t\t\tif (!$no_check) {\n\t\t\t\t$agree = [\n\t\t\t\t\t'title' => 'Check',\n\t\t\t\t\t'fields' => [\n\t\t\t\t\t\t'update_changesagreed' => ['mandatory' => true, 'type' => 'checkrequired', 'value' => 1, 'label' => '<strong>I have read the update notifications above and I am aware of the changes made to my system.</strong>'],\n\t\t\t\t\t\t'update_preconfig' => ['type' => 'hidden', 'value' => 1]\n\t\t\t\t\t]\n\t\t\t\t];\n\t\t\t\t$preconfig->addToPreConfig($agree);\n\t\t\t}\n\t\t\treturn $preconfig->getData();\n\t\t}\n\t\treturn [];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Install/Requirements.php",
    "content": "<?php\n\nnamespace Froxlor\\Install;\n\nclass Requirements\n{\n\tconst REQUIRED_VERSION = '7.4.0';\n\tconst REQUIRED_EXTENSIONS = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'pdo_mysql', 'curl', 'gmp', 'json', 'gd'];\n\tconst SUGGESTED_EXTENSIONS = ['bcmath', 'zip', 'gnupg'];\n}\n"
  },
  {
    "path": "lib/Froxlor/Install/Update.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Install;\n\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\n\nclass Update\n{\n\tprivate static $update_tasks = [];\n\n\tprivate static $task_counter = 0;\n\n\t/**\n\t * Function showUpdateStep\n\t *\n\t * stores and logs the current update progress\n\t *\n\t * @param string $task\n\t * @param bool $needs_status (if false, a linebreak will be added)\n\t *\n\t * @return void\n\t */\n\tpublic static function showUpdateStep(string $task, bool $needs_status = true)\n\t{\n\t\tset_time_limit(30);\n\n\t\t// output\n\t\tself::$update_tasks[self::$task_counter] = ['title' => $task, 'result' => 0];\n\n\t\tif (!$needs_status) {\n\t\t\tself::$task_counter++;\n\t\t}\n\n\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::ADM_ACTION, \\LOG_WARNING, $task);\n\t}\n\n\t/**\n\t * Function lastStepStatus\n\t *\n\t * outputs status of the last update-step\n\t *\n\t * @param int $status (0 = success, 1 = warning, 2 = failure)\n\t * @param string $message\n\t * @param string $additional_info\n\t *\n\t * @return void\n\t */\n\tpublic static function lastStepStatus(int $status = -1, string $message = 'OK', string $additional_info = '')\n\t{\n\t\tself::$update_tasks[self::$task_counter]['result_txt'] = $message;\n\t\tself::$update_tasks[self::$task_counter]['result_desc'] = $additional_info;\n\n\t\tswitch ($status) {\n\t\t\tcase 0:\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tself::$update_tasks[self::$task_counter]['result'] = 2;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tself::$update_tasks[self::$task_counter]['result'] = 1;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tself::$update_tasks[self::$task_counter]['result'] = -1;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tself::$task_counter++;\n\n\t\tif ($status == -1 || $status == 2) {\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::ADM_ACTION, \\LOG_WARNING, 'Attention - last update task failed!!!');\n\t\t} elseif ($status == 0 || $status == 1) {\n\t\t\tFroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::ADM_ACTION, \\LOG_WARNING, 'Success');\n\t\t}\n\t}\n\n\tpublic static function versionInUpdate($current_version, $version_to_check)\n\t{\n\t\tif (!Froxlor::isFroxlor()) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn Froxlor::versionCompare2($current_version, $version_to_check) == -1;\n\t}\n\n\tpublic static function storeUpdateCheckData(array $response)\n\t{\n\t\t$data = [\n\t\t\t'ts' => time(),\n\t\t\t'channel' => Settings::Get('system.update_channel'),\n\t\t\t'data' => $response\n\t\t];\n\t\tSettings::Set('system.updatecheck_data', json_encode($data));\n\t}\n\n\tpublic static function getUpdateCheckData()\n\t{\n\t\t$uc_data = Settings::Get('system.updatecheck_data');\n\t\tif (!empty($uc_data)) {\n\t\t\t$data = json_decode($uc_data, true);\n\t\t\treturn $data;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static function getUpdateTasks(): array\n\t{\n\t\treturn self::$update_tasks;\n\t}\n\n\tpublic static function getTaskCounter(): int\n\t{\n\t\treturn self::$task_counter;\n\t}\n\n\tpublic static function cleanOldFiles(array $to_clean)\n\t{\n\t\tself::showUpdateStep(\"Cleaning up old files\");\n\t\t$disabled = explode(',', ini_get('disable_functions'));\n\t\t$exec_allowed = !in_array('exec', $disabled);\n\t\t$del_list = \"\";\n\t\tforeach ($to_clean as $filedir) {\n\t\t\t$complete_filedir = Froxlor::getInstallDir() . $filedir;\n\t\t\tif (file_exists($complete_filedir)) {\n\t\t\t\tif ($exec_allowed) {\n\t\t\t\t\tFileDir::safe_exec(\"rm -rf \" . escapeshellarg($complete_filedir));\n\t\t\t\t} else {\n\t\t\t\t\t$del_list .= \"rm -rf \" . escapeshellarg($complete_filedir) . PHP_EOL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif ($exec_allowed) {\n\t\t\tself::lastStepStatus(0);\n\t\t} else {\n\t\t\tif (empty($del_list)) {\n\t\t\t\t// none of the files existed\n\t\t\t\tself::lastStepStatus(0);\n\t\t\t} else {\n\t\t\t\tself::lastStepStatus(\n\t\t\t\t\t1,\n\t\t\t\t\t'manual commands needed',\n\t\t\t\t\t'Please run the following commands manually:<br><pre>' . $del_list . '</pre>'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Language.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse RecursiveArrayIterator;\nuse RecursiveIteratorIterator;\n\nclass Language\n{\n\tprotected static ?array $lng = null;\n\tprotected static string $defaultLanguage = 'en';\n\tprotected static ?string $requestedLanguage = null;\n\n\t/**\n\t * @return array\n\t */\n\tpublic static function getLanguages(): array\n\t{\n\t\t$languages = [];\n\t\t$directory = dirname(__DIR__, 2) . '/lng';\n\n\t\tforeach (array_diff(scandir($directory), ['..', '.', 'index.html']) as $language) {\n\t\t\t$iso = explode('.', $language)[0];\n\t\t\t$languages[$iso] = self::getTranslation('languages.' . $iso);\n\t\t}\n\n\t\treturn $languages;\n\t}\n\n\tpublic static function getTranslation(string $identifier, array $arguments = [])\n\t{\n\t\t// initialize\n\t\tif (is_null(self::$lng)) {\n\t\t\t// load fallback language\n\t\t\tself::$lng = self::loadLanguage(self::$defaultLanguage);\n\n\t\t\t// load user requested language\n\t\t\tif (self::$requestedLanguage) {\n\t\t\t\tself::$lng = array_merge(self::$lng, self::loadLanguage(self::$requestedLanguage));\n\t\t\t}\n\n\t\t\t// load fallback from browser if nothing requested\n\t\t\t$iso = trim(substr(strtok(strtok(($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'en'), ','), ';'), 0, 5));\n\t\t\tif (!self::$requestedLanguage && strlen($iso) == 2 && $iso !== self::$defaultLanguage) {\n\t\t\t\tself::$lng = array_merge(self::$lng, self::loadLanguage($iso));\n\t\t\t}\n\t\t}\n\n\t\t// shortcut for identifier with => [title, description]\n\t\tif (!isset(self::$lng[$identifier]) && isset(self::$lng[$identifier . '.title'])) {\n\t\t\treturn [\n\t\t\t\t'title' => vsprintf(self::$lng[$identifier . '.title'] ?? $identifier, $arguments),\n\t\t\t\t'description' => vsprintf(self::$lng[$identifier . '.description'] ?? $identifier, $arguments),\n\t\t\t];\n\t\t}\n\t\t// search by identifier\n\t\treturn vsprintf(self::$lng[$identifier] ?? $identifier, $arguments);\n\t}\n\n\t/**\n\t * @TODO: Possible iso: de, de-DE, de-AT (fallback to de)\n\t *\n\t * @param $iso\n\t * @return array\n\t */\n\tprivate static function loadLanguage($iso): array\n\t{\n\t\t// Reject path traversal attempts\n\t\tif ($iso !== basename($iso) || str_contains($iso, '..')) {\n\t\t\treturn [];\n\t\t}\n\t\t$languageFile = dirname(__DIR__, 2) . sprintf('/lng/%s.lng.php', $iso);\n\n\t\tif (!file_exists($languageFile)) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// load default language\n\t\t$lng = require $languageFile;\n\n\t\t// multidimensional array to dot notation keys\n\t\t$reItIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($lng));\n\t\t$result = [];\n\t\tforeach ($reItIt as $leafValue) {\n\t\t\t$keys = [];\n\t\t\tforeach (range(0, $reItIt->getDepth()) as $depth) {\n\t\t\t\t$keys[] = $reItIt->getSubIterator($depth)->key();\n\t\t\t}\n\t\t\t$result[join('.', $keys)] = $leafValue;\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\tpublic static function setDefaultLanguage(string $string)\n\t{\n\t\tself::$defaultLanguage = $string;\n\t}\n\n\tpublic static function setLanguage(string $string)\n\t{\n\t\tself::$requestedLanguage = $string;\n\t\tself::$lng = null;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/MailLogParser.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse PDO;\n\nclass MailLogParser\n{\n\tprivate $startTime;\n\tprivate $domainTraffic = [];\n\tprivate $myDomains = [];\n\tprivate $mails = [];\n\n\t/**\n\t * constructor\n\t *\n\t * @param\n\t *            string logFile\n\t * @param\n\t *            int startTime\n\t * @param\n\t *            string logFileExim\n\t */\n\tpublic function __construct($startTime = 0)\n\t{\n\t\t$this->startTime = $startTime;\n\n\t\t// Get all domains from Database\n\t\t$stmt = Database::prepare(\"SELECT domain FROM `\" . TABLE_PANEL_DOMAINS . \"`\");\n\t\tDatabase::pexecute($stmt, []);\n\t\twhile ($domain_row = $stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$this->myDomains[] = $domain_row[\"domain\"];\n\t\t}\n\n\t\t// Parse MTA traffic\n\t\tif (Settings::Get(\"system.mtaserver\") == \"postfix\") {\n\t\t\t$this->parsePostfixLog(Settings::Get(\"system.mtalog\"));\n\t\t\t$this->parsePostfixLog(Settings::Get(\"system.mtalog\") . \".1\");\n\t\t} elseif (Settings::Get(\"system.mtaserver\") == \"exim4\") {\n\t\t\t$this->parseExim4Log(Settings::Get(\"system.mtalog\"));\n\t\t}\n\n\t\t// Parse MDA traffic\n\t\tif (Settings::Get(\"system.mdaserver\") == \"dovecot\") {\n\t\t\t$this->parseDovecotLog(Settings::Get(\"system.mdalog\"));\n\t\t\t$this->parseDovecotLog(Settings::Get(\"system.mdalog\") . \".1\");\n\t\t} elseif (Settings::Get(\"system.mdaserver\") == \"courier\") {\n\t\t\t$this->parseCourierLog(Settings::Get(\"system.mdalog\"));\n\t\t\t$this->parseCourierLog(Settings::Get(\"system.mdalog\") . \".1\");\n\t\t}\n\t}\n\n\t/**\n\t * parsePostfixLog\n\t * parses the traffic from a postfix logfile\n\t *\n\t * @param string $logFile\n\t *            logFile\n\t */\n\tprivate function parsePostfixLog($logFile)\n\t{\n\t\t// Check if file exists\n\t\tif (!file_exists($logFile)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Open the log file\n\t\ttry {\n\t\t\t$file_handle = fopen($logFile, \"r\");\n\t\t\tif (!$file_handle) {\n\t\t\t\tthrow new Exception(\"Could not open the file!\");\n\t\t\t}\n\t\t} catch (Exception $e) {\n\t\t\techo \"Error (File: \" . $e->getFile() . \", line \" . $e->getLine() . \"): \" . $e->getMessage();\n\t\t\treturn false;\n\t\t}\n\n\t\twhile (!feof($file_handle)) {\n\t\t\tunset($matches);\n\t\t\t$line = fgets($file_handle);\n\n\t\t\tif (strpos($line, 'postfix') === false) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$timestamp = $this->getLogTimestamp($line);\n\t\t\tif ($this->startTime < $timestamp) {\n\t\t\t\tif (preg_match(\"/postfix\\/qmgr.*(?::|\\])\\s([A-Z\\d]+).*from=<?(?:.*\\@([a-zA-Z\\d\\.\\-]+))?>?, size=(\\d+),/\", $line, $matches)) {\n\t\t\t\t\t// Postfix from\n\t\t\t\t\t$this->mails[$matches[1]] = [\n\t\t\t\t\t\t\"domainFrom\" => strtolower($matches[2]),\n\t\t\t\t\t\t\"size\" => $matches[3]\n\t\t\t\t\t];\n\t\t\t\t} elseif (preg_match(\"/postfix\\/(?:pipe|smtp|lmtp).*(?::|\\])\\s([A-Z\\d]+).*to=<?(?:.*\\@([a-zA-Z\\d\\.\\-]+))?>?,/\", $line, $matches)) {\n\t\t\t\t\t// Postfix to\n\t\t\t\t\tif (array_key_exists($matches[1], $this->mails)) {\n\t\t\t\t\t\t$this->mails[$matches[1]][\"domainTo\"] = strtolower($matches[2]);\n\n\t\t\t\t\t\t// Only mails from/to outside the system should be added\n\t\t\t\t\t\t$mail = $this->mails[$matches[1]];\n\t\t\t\t\t\tif (in_array($mail[\"domainFrom\"], $this->myDomains) || in_array($mail[\"domainTo\"], $this->myDomains)) {\n\t\t\t\t\t\t\t// Outgoing traffic\n\t\t\t\t\t\t\tif (array_key_exists(\"domainFrom\", $mail)) {\n\t\t\t\t\t\t\t\t$this->addDomainTraffic($mail[\"domainFrom\"], $mail[\"size\"], $timestamp);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Incoming traffic\n\t\t\t\t\t\t\tif (array_key_exists(\"domainTo\", $mail) && in_array($mail[\"domainTo\"], $this->myDomains)) {\n\t\t\t\t\t\t\t\t$this->addDomainTraffic($mail[\"domainTo\"], $mail[\"size\"], $timestamp);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tunset($mail);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfclose($file_handle);\n\t\treturn true;\n\t}\n\n\t/**\n\t * getLogTimestamp\n\t *\n\t * @param\n\t *            string line\n\t *            return int\n\t */\n\tprivate function getLogTimestamp($line)\n\t{\n\t\t$matches = null;\n\t\tif (preg_match(\"/((?:[A-Z]{3}\\s{1,2}\\d{1,2}|\\d{4}-\\d{2}-\\d{2}).\\d{2}:\\d{2}:\\d{2})/i\", $line, $matches)) {\n\t\t\t$timestamp = strtotime($matches[1]);\n\t\t\tif ($timestamp > ($this->startTime + 60 * 60 * 24)) {\n\t\t\t\treturn strtotime($matches[1] . \" -1 year\");\n\t\t\t} else {\n\t\t\t\treturn strtotime($matches[1]);\n\t\t\t}\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * _addDomainTraffic\n\t * adds the traffic to the domain array if we own the domain\n\t *\n\t * @param\n\t *            string domain\n\t * @param\n\t *            int traffic\n\t */\n\tprivate function addDomainTraffic($domain, $traffic, $timestamp)\n\t{\n\t\t$date = date(\"Y-m-d\", $timestamp);\n\t\tif (in_array($domain, $this->myDomains)) {\n\t\t\tif (array_key_exists($domain, $this->domainTraffic) && array_key_exists($date, $this->domainTraffic[$domain])) {\n\t\t\t\t$this->domainTraffic[$domain][$date] += (int)$traffic;\n\t\t\t} else {\n\t\t\t\tif (!array_key_exists($domain, $this->domainTraffic)) {\n\t\t\t\t\t$this->domainTraffic[$domain] = [];\n\t\t\t\t}\n\t\t\t\t$this->domainTraffic[$domain][$date] = (int)$traffic;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * parseExim4Log\n\t * parses the smtp traffic from a exim4 logfile\n\t *\n\t * @param string $logFile\n\t *            logFile\n\t */\n\tprivate function parseExim4Log($logFile)\n\t{\n\t\t// Check if file exists\n\t\tif (!file_exists($logFile)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Open the log file\n\t\ttry {\n\t\t\t$file_handle = fopen($logFile, \"r\");\n\t\t\tif (!$file_handle) {\n\t\t\t\tthrow new Exception(\"Could not open the file!\");\n\t\t\t}\n\t\t} catch (Exception $e) {\n\t\t\techo \"Error (File: \" . $e->getFile() . \", line \" . $e->getLine() . \"): \" . $e->getMessage();\n\t\t\treturn false;\n\t\t}\n\n\t\twhile (!feof($file_handle)) {\n\t\t\tunset($matches);\n\t\t\t$line = fgets($file_handle);\n\n\t\t\t$timestamp = $this->getLogTimestamp($line);\n\t\t\tif ($this->startTime < $timestamp) {\n\t\t\t\tif (preg_match(\"/<= .*@([a-z0-9.\\-]+) .*S=(\\d+)/i\", $line, $matches)) {\n\t\t\t\t\t// Outgoing traffic\n\t\t\t\t\t$this->addDomainTraffic($matches[1], $matches[2], $timestamp);\n\t\t\t\t} elseif (preg_match(\"/=> .*<?.*@([a-z0-9.\\-]+)>? .*S=(\\d+)/i\", $line, $matches)) {\n\t\t\t\t\t// Incoming traffic\n\t\t\t\t\t$this->addDomainTraffic($matches[1], $matches[2], $timestamp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfclose($file_handle);\n\t\treturn true;\n\t}\n\n\t/**\n\t * parseDovecotLog\n\t * parses the dovecot imap/pop3 traffic from logfile\n\t *\n\t * @param string $logFile\n\t *            logFile\n\t */\n\tprivate function parseDovecotLog($logFile)\n\t{\n\t\t// Check if file exists\n\t\tif (!file_exists($logFile)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Open the log file\n\t\ttry {\n\t\t\t$file_handle = fopen($logFile, \"r\");\n\t\t\tif (!$file_handle) {\n\t\t\t\tthrow new Exception(\"Could not open the file!\");\n\t\t\t}\n\t\t} catch (Exception $e) {\n\t\t\techo \"Error (File: \" . $e->getFile() . \", line \" . $e->getLine() . \"): \" . $e->getMessage();\n\t\t\treturn false;\n\t\t}\n\n\t\twhile (!feof($file_handle)) {\n\t\t\tunset($matches);\n\t\t\t$line = fgets($file_handle);\n\n\t\t\tif (strpos($line, 'dovecot') === false) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$timestamp = $this->getLogTimestamp($line);\n\t\t\tif ($this->startTime < $timestamp) {\n\t\t\t\tif (preg_match(\"/dovecot.*(?::|\\]) imap\\(.*@([a-z0-9\\.\\-]+)\\)(<\\d+><[a-z0-9+\\/=]+>)?:.*(?:in=(\\d+) out=(\\d+)|bytes=(\\d+)\\/(\\d+))/i\", $line, $matches)) {\n\t\t\t\t\t// Dovecot IMAP\n\t\t\t\t\t$this->addDomainTraffic($matches[1], (int)$matches[3] + (int)$matches[4], $timestamp);\n\t\t\t\t} elseif (preg_match(\"/dovecot.*(?::|\\]) pop3\\(.*@([a-z0-9\\.\\-]+)\\)(<\\d+><[a-z0-9+\\/=]+>)?:.*in=(\\d+).*out=(\\d+)/i\", $line, $matches)) {\n\t\t\t\t\t// Dovecot POP3\n\t\t\t\t\t$this->addDomainTraffic($matches[1], (int)$matches[3] + (int)$matches[4], $timestamp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfclose($file_handle);\n\t\treturn true;\n\t}\n\n\t/**\n\t * parseCourierLog\n\t * parses the dovecot imap/pop3 traffic from logfile\n\t *\n\t * @param string $logFile\n\t *            logFile\n\t */\n\tprivate function parseCourierLog($logFile)\n\t{\n\t\t// Check if file exists\n\t\tif (!file_exists($logFile)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Open the log file\n\t\ttry {\n\t\t\t$file_handle = fopen($logFile, \"r\");\n\t\t\tif (!$file_handle) {\n\t\t\t\tthrow new Exception(\"Could not open the file!\");\n\t\t\t}\n\t\t} catch (Exception $e) {\n\t\t\techo \"Error (File: \" . $e->getFile() . \", line \" . $e->getLine() . \"): \" . $e->getMessage();\n\t\t\treturn false;\n\t\t}\n\n\t\twhile (!feof($file_handle)) {\n\t\t\tunset($matches);\n\t\t\t$line = fgets($file_handle);\n\n\t\t\t$timestamp = $this->getLogTimestamp($line);\n\t\t\tif ($this->startTime < $timestamp) {\n\t\t\t\tif (preg_match(\"/(?:imapd|pop3d)(?:-ssl)?.*(?::|\\]).*user=.*@([a-z0-9\\.\\-]+),.*rcvd=(\\d+), sent=(\\d+),/i\", $line, $matches)) {\n\t\t\t\t\t// Courier IMAP & POP3\n\t\t\t\t\t$this->addDomainTraffic($matches[1], (int)$matches[2] + (int)$matches[3], $timestamp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfclose($file_handle);\n\t\treturn true;\n\t}\n\n\t/**\n\t * getDomainTraffic\n\t * returns the traffic of a given domain or 0 if the domain has no traffic\n\t *\n\t * @param\n\t *            string domain\n\t *            return array\n\t */\n\tpublic function getDomainTraffic($domain)\n\t{\n\t\tif (array_key_exists($domain, $this->domainTraffic)) {\n\t\t\treturn $this->domainTraffic[$domain];\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/PhpHelper.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Exception;\nuse Froxlor\\UI\\Panel\\UI;\nuse Net_DNS2_Exception;\nuse Net_DNS2_Resolver;\nuse Throwable;\nuse voku\\helper\\AntiXSS;\n\nclass PhpHelper\n{\n\t/**\n\t * Wrapper around htmlentities to handle arrays, with the advantage that you\n\t * can select which fields should be handled by htmlentities\n\t *\n\t * @param array|string $subject The subject array\n\t * @param array|string $fields The fields which should be checked for, separated by spaces\n\t * @param int $quote_style See php documentation about this\n\t * @param string $charset See php documentation about this\n\t *\n\t * @return array|string The string or an array with htmlentities converted strings\n\t * @author Florian Lippert <flo@syscp.org> (2003-2009)\n\t */\n\tpublic static function htmlentitiesArray($subject, $fields = '', $quote_style = ENT_QUOTES, $charset = 'UTF-8')\n\t{\n\t\tif (is_array($subject)) {\n\t\t\tif (!is_array($fields)) {\n\t\t\t\t$fields = self::arrayTrim(explode(' ', $fields));\n\t\t\t}\n\n\t\t\tforeach ($subject as $field => $value) {\n\t\t\t\tif ((!is_array($fields) || empty($fields)) || (in_array($field, $fields))) {\n\t\t\t\t\t// Just call ourselve to manage multi-dimensional arrays\n\t\t\t\t\t$subject[$field] = self::htmlentitiesArray($value, $fields, $quote_style, $charset);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t$subject = empty($subject) ? \"\" : htmlentities($subject, $quote_style, $charset);\n\t\t}\n\n\t\treturn $subject;\n\t}\n\n\t/**\n\t * Returns array with all empty-values removed\n\t *\n\t * @param array $source The array to trim\n\t * @return array The trim'med array\n\t */\n\tpublic static function arrayTrim(array $source): array\n\t{\n\t\t$source = array_map('trim', $source);\n\t\treturn array_filter($source, function ($value) {\n\t\t\treturn $value !== '';\n\t\t});\n\t}\n\n\t/**\n\t * froxlor php error handler\n\t *\n\t * @param int $errno\n\t * @param string $errstr\n\t * @param string $errfile\n\t * @param int $errline\n\t *\n\t * @return void|boolean\n\t */\n\tpublic static function phpErrHandler($errno, $errstr, $errfile, $errline)\n\t{\n\t\tif (!(error_reporting() & $errno)) {\n\t\t\t// This error code is not included in error_reporting\n\t\t\treturn;\n\t\t}\n\n\t\tif (!isset($_SERVER['SHELL']) || (isset($_SERVER['SHELL']) && $_SERVER['SHELL'] == '')) {\n\t\t\t// prevent possible file-path-disclosure\n\t\t\t$errfile = str_replace(Froxlor::getInstallDir(), \"\", $errfile);\n\t\t\t// build alert\n\t\t\t$type = 'danger';\n\t\t\tif ($errno == E_NOTICE || $errno == E_DEPRECATED || $errno == E_STRICT) {\n\t\t\t\t$type = 'info';\n\t\t\t} elseif ($errno = E_WARNING) {\n\t\t\t\t$type = 'warning';\n\t\t\t}\n\t\t\t$err_display = '<div class=\"alert alert-' . $type . ' my-1\" role=\"alert\">';\n\t\t\t$err_display .= '<strong>#' . $errno . ' ' . $errstr . '</strong><br>';\n\t\t\t$err_display .= $errfile . ':' . $errline;\n\t\t\t// later depended on whether to show or now\n\t\t\t$err_display .= '<br><p><pre>';\n\t\t\t$debug = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n\t\t\tforeach ($debug as $dline) {\n\t\t\t\t$err_display .= $dline['function'] . '() called at [' . str_replace(Froxlor::getInstallDir(), '',\n\t\t\t\t\t\t($dline['file'] ?? 'unknown')) . ':' . ($dline['line'] ?? 0) . ']<br>';\n\t\t\t}\n\t\t\t$err_display .= '</pre></p>';\n\t\t\t// end later\n\t\t\t$err_display .= '</div>';\n\t\t\t// set errors to session\n\t\t\tErrorBag::addError($err_display);\n\t\t\t// return true to ignore php standard error-handler\n\t\t\treturn true;\n\t\t}\n\n\t\t// of on shell, use the php standard error-handler\n\t\treturn false;\n\t}\n\n\t/**\n\t * @param Throwable $exception\n\t * @return void\n\t */\n\tpublic static function phpExceptionHandler(Throwable $exception)\n\t{\n\t\tif (!isset($_SERVER['SHELL']) || $_SERVER['SHELL'] == '') {\n\t\t\t// show\n\t\t\tUI::initTwig(true);\n\t\t\tUI::twig()->addGlobal('install_mode', '1');\n\t\t\tUI::view('misc/alert_nosession.html.twig', [\n\t\t\t\t'page_title' => 'Uncaught exception',\n\t\t\t\t'heading' => 'Uncaught exception',\n\t\t\t\t'type' => 'danger',\n\t\t\t\t'alert_msg' => $exception->getCode() . ' ' . $exception->getMessage(),\n\t\t\t\t'alert_info' => $exception->getTraceAsString()\n\t\t\t]);\n\t\t\tdie();\n\t\t}\n\t}\n\n\t/**\n\t * @param ...$configdirs\n\t * @return array|null\n\t */\n\tpublic static function loadConfigArrayDir(...$configdirs)\n\t{\n\t\tif (count($configdirs) <= 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\t$data = [];\n\t\t$data_files = [];\n\t\t$has_data = false;\n\n\t\tforeach ($configdirs as $data_dirname) {\n\t\t\tif (is_dir($data_dirname)) {\n\t\t\t\t$data_dirhandle = opendir($data_dirname);\n\t\t\t\twhile (false !== ($data_filename = readdir($data_dirhandle))) {\n\t\t\t\t\tif ($data_filename != '.'\n\t\t\t\t\t\t&& $data_filename != '..'\n\t\t\t\t\t\t&& $data_filename != ''\n\t\t\t\t\t\t&& substr($data_filename, -4) == '.php'\n\t\t\t\t\t) {\n\t\t\t\t\t\t$data_files[] = $data_dirname . $data_filename;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$has_data = true;\n\t\t\t}\n\t\t}\n\n\t\tif ($has_data) {\n\t\t\tsort($data_files);\n\t\t\tforeach ($data_files as $data_filename) {\n\t\t\t\t$data = array_merge_recursive($data, include $data_filename);\n\t\t\t}\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t/**\n\t * ipv6 aware gethostbynamel function\n\t *\n\t * @param string $host\n\t * @param boolean $try_a default true\n\t * @param string|null $nameserver set additional resolver nameserver to use (e.g. 1.1.1.1)\n\t * @return bool|array\n\t */\n\tpublic static function gethostbynamel6(string $host, bool $try_a = true, ?string $nameserver = null)\n\t{\n\t\t$ips = [];\n\n\t\ttry {\n\t\t\t// set the default nameservers to use, use the system default if none are provided\n\t\t\t$resolver = new Net_DNS2_Resolver($nameserver ? ['nameservers' => [$nameserver]] : []);\n\n\t\t\t// get all ip addresses from the A record and normalize them\n\t\t\tif ($try_a) {\n\t\t\t\ttry {\n\t\t\t\t\t$answer = $resolver->query($host, 'A')->answer;\n\t\t\t\t\tforeach ($answer as $rr) {\n\t\t\t\t\t\tif ($rr instanceof \\Net_DNS2_RR_A) {\n\t\t\t\t\t\t\t$ips[] = inet_ntop(inet_pton($rr->address));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Net_DNS2_Exception $e) {\n\t\t\t\t\t// we can't do anything here, just continue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get all ip addresses from the AAAA record and normalize them\n\t\t\ttry {\n\t\t\t\t$answer = $resolver->query($host, 'AAAA')->answer;\n\t\t\t\tforeach ($answer as $rr) {\n\t\t\t\t\tif ($rr instanceof \\Net_DNS2_RR_AAAA) {\n\t\t\t\t\t\t$ips[] = inet_ntop(inet_pton($rr->address));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Net_DNS2_Exception $e) {\n\t\t\t\t// we can't do anything here, just continue\n\t\t\t}\n\t\t} catch (Net_DNS2_Exception $e) {\n\t\t\t// fallback to php's dns_get_record if Net_DNS2 has no resolver available, but this may cause\n\t\t\t// problems if the system's dns is not configured correctly; for example, the acme pre-check\n\t\t\t// will fail because some providers put a local ip in /etc/hosts\n\n\t\t\t// get all ip addresses from the A record and normalize them\n\t\t\tif ($try_a) {\n\t\t\t\t$answer = @dns_get_record($host, DNS_A);\n\t\t\t\tforeach ($answer as $rr) {\n\t\t\t\t\t$ips[] = inet_ntop(inet_pton($rr['ip']));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get all ip addresses from the AAAA record and normalize them\n\t\t\t$answer = @dns_get_record($host, DNS_AAAA);\n\t\t\tforeach ($answer as $rr) {\n\t\t\t\t$ips[] = inet_ntop(inet_pton($rr['ipv6']));\n\t\t\t}\n\t\t}\n\n\t\treturn count($ips) > 0 ? $ips : false;\n\t}\n\n\t/**\n\t * Function randomStr\n\t *\n\t * generate a pseudo-random string of bytes\n\t *\n\t * @param int $length\n\t * @return string\n\t * @throws Exception\n\t */\n\tpublic static function randomStr(int $length): string\n\t{\n\t\tif (function_exists('openssl_random_pseudo_bytes')) {\n\t\t\treturn openssl_random_pseudo_bytes($length);\n\t\t}\n\t\treturn random_bytes($length);\n\t}\n\n\t/**\n\t * Return human-readable sizes\n\t *\n\t * @param int $size size in bytes\n\t * @param ?string $max maximum unit\n\t * @param string $system 'si' for SI, 'bi' for binary prefixes\n\t * @param string $retstring string-format\n\t *\n\t * @return string\n\t */\n\tpublic static function sizeReadable(\n\t\t$size,\n\t\t?string $max = '',\n\t\tstring $system = 'si',\n\t\tstring $retstring = '%01.2f %s'\n\t): string\n\t{\n\t\t// Pick units\n\t\t$systems = [\n\t\t\t'si' => [\n\t\t\t\t'prefix' => [\n\t\t\t\t\t'B',\n\t\t\t\t\t'KB',\n\t\t\t\t\t'MB',\n\t\t\t\t\t'GB',\n\t\t\t\t\t'TB',\n\t\t\t\t\t'PB'\n\t\t\t\t],\n\t\t\t\t'size' => 1000\n\t\t\t],\n\t\t\t'bi' => [\n\t\t\t\t'prefix' => [\n\t\t\t\t\t'B',\n\t\t\t\t\t'KiB',\n\t\t\t\t\t'MiB',\n\t\t\t\t\t'GiB',\n\t\t\t\t\t'TiB',\n\t\t\t\t\t'PiB'\n\t\t\t\t],\n\t\t\t\t'size' => 1024\n\t\t\t]\n\t\t];\n\t\t$sys = $systems[$system] ?? $systems['si'];\n\n\t\t// Max unit to display\n\t\t$depth = count($sys['prefix']) - 1;\n\t\tif ($max && false !== $d = array_search($max, $sys['prefix'])) {\n\t\t\t$depth = $d;\n\t\t}\n\t\t// Loop\n\t\t$i = 0;\n\t\twhile ($size >= $sys['size'] && $i < $depth) {\n\t\t\t$size /= $sys['size'];\n\t\t\t$i++;\n\t\t}\n\n\t\treturn sprintf($retstring, $size, $sys['prefix'][$i]);\n\t}\n\n\t/**\n\t * Replaces all occurrences of variables defined in the second argument\n\t * in the first argument with their values.\n\t *\n\t * @param string $text The string that should be searched for variables\n\t * @param array $vars The array containing the variables with their values\n\t *\n\t * @return string The submitted string with the variables replaced.\n\t */\n\tpublic static function replaceVariables(string $text, array $vars): string\n\t{\n\t\t$pattern = \"/\\{([a-zA-Z0-9\\-_]+)\\}/\";\n\t\t$matches = [];\n\n\t\tif (count($vars) > 0 && preg_match_all($pattern, $text, $matches)) {\n\t\t\tfor ($i = 0; $i < count($matches[1]); $i++) {\n\t\t\t\t$current = $matches[1][$i];\n\n\t\t\t\tif (isset($vars[$current])) {\n\t\t\t\t\t$var = $vars[$current];\n\t\t\t\t\t$text = str_replace(\"{\" . $current . \"}\", $var, $text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn str_replace('\\n', \"\\n\", $text);\n\t}\n\n\t/**\n\t * @param string $needle\n\t * @param array $haystack\n\t * @param array $keys\n\t * @param string $currentKey\n\t * @return true\n\t */\n\tpublic static function recursive_array_search(\n\t\tstring $needle,\n\t\tarray  $haystack,\n\t\tarray  &$keys = [],\n\t\tstring $currentKey = ''\n\t): bool\n\t{\n\t\tforeach ($haystack as $key => $value) {\n\t\t\tif (empty($value)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$pathkey = empty($currentKey) ? $key : $currentKey . '.' . $key;\n\t\t\tif (is_array($value)) {\n\t\t\t\tself::recursive_array_search($needle, $value, $keys, $pathkey);\n\t\t\t} else {\n\t\t\t\tif (stripos($value, $needle) !== false) {\n\t\t\t\t\t$keys[] = $pathkey;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * function to check a super-global passed by reference,\n\t * so it gets automatically updated\n\t *\n\t * @param array $global\n\t * @param AntiXSS $antiXss\n\t */\n\tpublic static function cleanGlobal(array &$global, AntiXSS &$antiXss)\n\t{\n\t\t$ignored_fields = [\n\t\t\t'system_default_vhostconf',\n\t\t\t'system_default_sslvhostconf',\n\t\t\t'system_apache_globaldiropt',\n\t\t\t'specialsettings',\n\t\t\t'ssl_specialsettings',\n\t\t\t'default_vhostconf_domain',\n\t\t\t'ssl_default_vhostconf_domain',\n\t\t\t'filecontent',\n\t\t\t'admin_password',\n\t\t\t'password',\n\t\t\t'new_customer_password',\n\t\t\t'privileged_password',\n\t\t\t'email_password',\n\t\t\t'directory_password',\n\t\t\t'ftp_password',\n\t\t\t'mysql_password',\n\t\t\t'mysql_root_pass',\n\t\t\t'mysql_unprivileged_pass',\n\t\t\t'admin_pass',\n\t\t\t'admin_pass_confirm',\n\t\t\t'panel_password_special_char',\n\t\t\t'old_password',\n\t\t\t'new_password',\n\t\t\t'new_password_confirm',\n\t\t];\n\t\tif (!empty($global)) {\n\t\t\t$tmp = $global;\n\t\t\tforeach ($tmp as $index => $value) {\n\t\t\t\tif (!in_array($index, $ignored_fields)) {\n\t\t\t\t\t$global[$index] = $antiXss->xss_clean($value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Generate php file from array.\n\t *\n\t * @param array $array\n\t * @param string|null $comment\n\t * @param bool $asReturn\n\t * @return string\n\t */\n\tpublic static function parseArrayToPhpFile(array $array, ?string $comment = null, bool $asReturn = false): string\n\t{\n\t\t$str = sprintf(\"<?php\\n// %s\\n\\n\", $comment ?? 'autogenerated froxlor file');\n\n\t\tif ($asReturn) {\n\t\t\treturn $str . sprintf(\"return %s;\\n\", rtrim(self::parseArrayToString($array), \"\\n,\"));\n\t\t}\n\n\t\tforeach ($array as $var => $arr) {\n\t\t\t$str .= sprintf(\"\\$%s = %s;\\n\", $var, rtrim(self::parseArrayToString($arr), \"\\n,\"));\n\t\t}\n\t\treturn $str;\n\t}\n\n\t/**\n\t * Parse array to array string.\n\t *\n\t * @param array $array\n\t * @param ?string $key\n\t * @param int $depth\n\t * @return string\n\t */\n\tpublic static function parseArrayToString(array $array, ?string $key = null, int $depth = 1): string\n\t{\n\t\t$str = '';\n\t\tif (!is_null($key)) {\n\t\t\t$str .= self::tabPrefix(($depth - 1), \"'{$key}' => [\\n\");\n\t\t} else {\n\t\t\t$str .= self::tabPrefix(($depth - 1), \"[\\n\");\n\t\t}\n\t\tforeach ($array as $key => $value) {\n\t\t\tif (!is_array($value)) {\n\t\t\t\tif (is_bool($value)) {\n\t\t\t\t\t$str .= self::tabPrefix($depth, sprintf(\"'%s' => %s,\\n\", $key, $value ? 'true' : 'false'));\n\t\t\t\t} elseif (is_int($value)) {\n\t\t\t\t\t$str .= self::tabPrefix($depth, \"'{$key}' => $value,\\n\");\n\t\t\t\t} else {\n\t\t\t\t\tif ($key == 'password') {\n\t\t\t\t\t\t// special case for passwords (nowdoc)\n\t\t\t\t\t\t$str .= self::tabPrefix($depth, \"'{$key}' => <<<'EOT'\\n{$value}\\nEOT,\\n\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// escape backslashes first, then single quotes:\n\t\t\t\t\t\t$escaped = str_replace(['\\\\', \"'\"], ['\\\\\\\\', \"\\\\'\"], $value);\n\t\t\t\t\t\t$str .= self::tabPrefix($depth, \"'{$key}' => '{$escaped}',\\n\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$str .= self::parseArrayToString($value, $key, ($depth + 1));\n\t\t\t}\n\t\t}\n\t\t$str .= self::tabPrefix(($depth - 1), \"],\\n\");\n\t\treturn $str;\n\t}\n\n\t/**\n\t * Apply tabs with given depth to string.\n\t *\n\t * @param int $depth\n\t * @param string $str\n\t * @return string\n\t */\n\tprivate static function tabPrefix(int $depth, string $str = ''): string\n\t{\n\t\t$tab = '';\n\t\tfor ($i = 1; $i <= $depth; $i++) {\n\t\t\t$tab .= \"\\t\";\n\t\t}\n\t\treturn $tab . $str;\n\t}\n\n\tpublic static function array_merge_recursive_distinct(array &$array1, array &$array2)\n\t{\n\t\t$merged = $array1;\n\t\tforeach ($array2 as $key => &$value) {\n\t\t\tif (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {\n\t\t\t\t$merged[$key] = self::array_merge_recursive_distinct($merged[$key], $value);\n\t\t\t} else {\n\t\t\t\t$merged[$key] = $value;\n\t\t\t}\n\t\t}\n\t\treturn $merged;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/SImExporter.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\UI\\Form;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\n/**\n * Class SImExporter\n *\n * Import/Export settings to JSON\n */\nclass SImExporter\n{\n\t/**\n\t * settings which are not being exported\n\t *\n\t * @var array\n\t */\n\tprivate static $no_export = [\n\t\t'panel.adminmail',\n\t\t'admin.show_news_feed',\n\t\t'system.lastaccountnumber',\n\t\t'system.lastguid',\n\t\t'system.ipaddress',\n\t\t'system.last_traffic_run',\n\t\t'system.hostname',\n\t\t'system.mysql_access_host',\n\t\t'system.lastcronrun',\n\t\t'system.defaultip',\n\t\t'system.defaultsslip',\n\t\t'system.last_tasks_run',\n\t\t'system.last_archive_run',\n\t\t'system.leprivatekey',\n\t\t'system.lepublickey',\n\t\t'system.updatecheck_data',\n\t];\n\n\tpublic static function export()\n\t{\n\t\t$settings_definitions = [];\n\t\tforeach (PhpHelper::loadConfigArrayDir(Froxlor::getInstallDir() . '/actions/admin/settings/')['groups'] as $group) {\n\t\t\tforeach ($group['fields'] as $field) {\n\t\t\t\t$settings_definitions[$field['settinggroup']][$field['varname']] = $field;\n\t\t\t}\n\t\t}\n\n\t\t$result_stmt = Database::query(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_SETTINGS . \"` ORDER BY `settingid` ASC\n\t\t\");\n\t\t$_data = [];\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$index = $row['settinggroup'] . \".\" . $row['varname'];\n\t\t\tif (!in_array($index, self::$no_export)) {\n\t\t\t\t$_data[$index] = $row['value'];\n\t\t\t}\n\n\t\t\tif (array_key_exists($row['settinggroup'], $settings_definitions) && array_key_exists($row['varname'],\n\t\t\t\t\t$settings_definitions[$row['settinggroup']])) {\n\t\t\t\t// Export image file\n\t\t\t\tif ($settings_definitions[$row['settinggroup']][$row['varname']]['type'] === \"image\") {\n\t\t\t\t\tif ($row['value'] === \"\") {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$_data[$index . '.image_data'] = base64_encode(file_get_contents(explode('?', $row['value'],\n\t\t\t\t\t\t2)[0]));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// add checksum for validation\n\t\t$_data['_sha'] = sha1(var_export($_data, true));\n\t\t$_export = json_encode($_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);\n\t\tif (!$_export) {\n\t\t\tthrow new Exception(\"Error exporting settings: \" . json_last_error_msg());\n\t\t}\n\n\t\treturn $_export;\n\t}\n\n\tpublic static function import($json_str = null)\n\t{\n\t\t// decode data\n\t\t$_data = json_decode($json_str, true);\n\t\tif ($_data) {\n\t\t\t// get validity check data\n\t\t\t$_sha = isset($_data['_sha']) ? $_data['_sha'] : false;\n\t\t\t$_version = isset($_data['panel.version']) ? $_data['panel.version'] : false;\n\t\t\t$_dbversion = isset($_data['panel.db_version']) ? $_data['panel.db_version'] : false;\n\t\t\t// check if we have everything we need\n\t\t\tif (!$_sha || !$_version || !$_dbversion) {\n\t\t\t\tthrow new Exception(\"Invalid froxlor settings data. Unable to import.\");\n\t\t\t}\n\t\t\t// validate import file\n\t\t\tunset($_data['_sha']);\n\t\t\t// compare\n\t\t\tif ($_sha != sha1(var_export($_data, true))) {\n\t\t\t\tthrow new Exception(\"SHA check of import data failed. Unable to import.\");\n\t\t\t}\n\t\t\t// do not import version info - but we need that to possibly update settings\n\t\t\t// when there were changes in the variable-name or similar\n\t\t\tunset($_data['panel.version']);\n\t\t\tunset($_data['panel.db_version']);\n\t\t\t// validate we got ssl enabled ips when ssl is enabled\n\t\t\t// otherwise deactivate it\n\t\t\tif ($_data['system.use_ssl'] == 1) {\n\t\t\t\t$result_ssl_ipsandports_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT COUNT(*) as count_ssl_ip FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `ssl`='1'\n\t\t\t\t\");\n\t\t\t\t$result = Database::pexecute_first($result_ssl_ipsandports_stmt);\n\t\t\t\tif ($result['count_ssl_ip'] <= 0) {\n\t\t\t\t\t// no ssl-ip -> deactivate\n\t\t\t\t\t$_data['system.use_ssl'] = 0;\n\t\t\t\t\t// deactivate other ssl-related settings\n\t\t\t\t\t$_data['system.leenabled'] = 0;\n\t\t\t\t\t$_data['system.le_froxlor_enabled'] = 0;\n\t\t\t\t\t$_data['system.le_froxlor_redirect'] = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$form_data = [];\n\t\t\t$image_data = [];\n\t\t\t// read in all current settings\n\t\t\t$current_settings = Settings::getAll();\n\t\t\tforeach ($current_settings as $setting_group => $setting) {\n\t\t\t\tforeach ($setting as $varname => $value) {\n\t\t\t\t\t// set all group/varname:values which are not in the import file\n\t\t\t\t\tif (!array_key_exists($setting_group . '.' . $varname, $_data)) {\n\t\t\t\t\t\t$_data[$setting_group . '.' . $varname] = $value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// re-format the array-key for Form::processForm\n\t\t\tforeach ($_data as $key => $value) {\n\t\t\t\t$index_split = explode('.', $key, 3);\n\t\t\t\tif (!isset($current_settings[$index_split[0]][$index_split[1]])) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (isset($index_split[2]) && $index_split[2] === 'image_data' && !empty($_data[$index_split[0] . '.' . $index_split[1]])) {\n\t\t\t\t\t$image_data[$key] = $value;\n\t\t\t\t} else {\n\t\t\t\t\t$form_data[str_replace(\".\", \"_\", $key)] = $value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// store new data\n\t\t\t$settings_data = PhpHelper::loadConfigArrayDir(Froxlor::getInstallDir() . '/actions/admin/settings/');\n\t\t\tSettings::loadSettingsInto($settings_data);\n\n\t\t\tif (Form::processForm($settings_data, $form_data, [], null, true)) {\n\t\t\t\t// save to DB\n\t\t\t\tSettings::Flush();\n\n\t\t\t\t// Process image_data and save it\n\t\t\t\tif (count($image_data) > 0) {\n\t\t\t\t\tforeach ($image_data as $index => $value) {\n\t\t\t\t\t\t$index_split = explode('.', $index, 3);\n\t\t\t\t\t\t$path = Froxlor::getInstallDir() . '/img/';\n\t\t\t\t\t\tif (!is_dir($path) && !mkdir($path, 0775)) {\n\t\t\t\t\t\t\tthrow new Exception(\"img directory does not exist and cannot be created\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Make sure we can write to the upload directory\n\t\t\t\t\t\tif (!is_writable($path)) {\n\t\t\t\t\t\t\tif (!chmod($path, 0775)) {\n\t\t\t\t\t\t\t\tthrow new Exception(\"Cannot write to img directory\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (Validate::validateBase64Image($value)) {\n\t\t\t\t\t\t\t$img_data = base64_decode($value);\n\t\t\t\t\t\t\t$img_filename = explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0];\n\n\t\t\t\t\t\t\t$spl = explode('.', $img_filename);\n\t\t\t\t\t\t\t$file_extension = strtolower(array_pop($spl));\n\t\t\t\t\t\t\tunset($spl);\n\n\t\t\t\t\t\t\tif (!in_array($file_extension, [\n\t\t\t\t\t\t\t\t'jpeg',\n\t\t\t\t\t\t\t\t'jpg',\n\t\t\t\t\t\t\t\t'png',\n\t\t\t\t\t\t\t\t'gif'\n\t\t\t\t\t\t\t])) {\n\t\t\t\t\t\t\t\tthrow new Exception(\"Invalid file-extension, use one of: jpeg, jpg, png, gif\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$img_filename = 'img/' . bin2hex(random_bytes(16)) . '.' . $file_extension;\n\t\t\t\t\t\t\tfile_put_contents(Froxlor::getInstallDir() . '/' . $img_filename, $img_data);\n\t\t\t\t\t\t\t$img_index = $index_split[0].'.'.$index_split[1];\n\t\t\t\t\t\t\tSettings::Set($img_index, $img_filename . '?v=' . time());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// all good\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tthrow new Exception(\"Importing settings failed\");\n\t\t\t}\n\t\t}\n\t\tthrow new Exception(\"Invalid JSON data: \" . json_last_error_msg());\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Settings/FroxlorVhostSettings.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Settings;\n\nuse Froxlor\\Database\\Database;\n\nclass FroxlorVhostSettings\n{\n\n\t/**\n\t * @param bool $need_ssl\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic static function hasVhostContainerEnabled(bool $need_ssl = false): bool\n\t{\n\t\t$sel_stmt = Database::prepare(\"SELECT COUNT(*) as vcentries FROM `\" . TABLE_PANEL_IPSANDPORTS . \"` WHERE `vhostcontainer`= '1'\" . ($need_ssl ? \" AND `ssl` = '1'\" : \"\"));\n\t\t$result = Database::pexecute_first($sel_stmt);\n\t\tif ($result) {\n\t\t\treturn $result['vcentries'] > 0;\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Settings/Store.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Settings;\n\nuse Exception;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Database\\DbManager;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Cronjob;\nuse Froxlor\\System\\IPTools;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\Validate\\Validate;\nuse PDO;\n\nclass Store\n{\n\n\tpublic static function storeSettingClearCertificates($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false\n\t\t\t&& is_array($fielddata)\n\t\t\t&& isset($fielddata['settinggroup'])\n\t\t\t&& $fielddata['settinggroup'] == 'system'\n\t\t\t&& isset($fielddata['varname'])\n\t\t) {\n\t\t\tif ($fielddata['varname'] == 'le_froxlor_enabled' && $newfieldvalue == '0') {\n\t\t\t\tDatabase::query(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` WHERE `domainid` = '0'\n\t\t\t\t\");\n\t\t\t} elseif ($fielddata['varname'] == 'froxloraliases' && $newfieldvalue != $fielddata['value']) {\n\t\t\t\tDatabase::query(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"` SET `validtodate`= NULL WHERE `domainid` = '0'\n\t\t\t\t\");\n\t\t\t}\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingField($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\tif (is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] != '' && isset($fielddata['varname']) && $fielddata['varname'] != '') {\n\t\t\tif (Settings::Set($fielddata['settinggroup'] . '.' . $fielddata['varname'], $newfieldvalue) !== false) {\n\t\t\t\t/*\n\t\t\t\t * when fielddata[cronmodule] is set, this means enable/disable a cronjob\n\t\t\t\t */\n\t\t\t\tif (isset($fielddata['cronmodule']) && $fielddata['cronmodule'] != '') {\n\t\t\t\t\tCronjob::toggleCronStatus($fielddata['cronmodule'], $newfieldvalue);\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * satisfy dependencies\n\t\t\t\t */\n\t\t\t\tif (isset($fielddata['dependency']) && is_array($fielddata['dependency'])) {\n\t\t\t\t\tif ((int)$fielddata['dependency']['onlyif'] == (int)$newfieldvalue) {\n\t\t\t\t\t\tself::storeSettingField($fielddata['dependency']['fieldname'], $fielddata['dependency']['fielddata'], $newfieldvalue);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn [\n\t\t\t\t\t$fielddata['settinggroup'] . '.' . $fielddata['varname'] => $newfieldvalue\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static function storeSettingDefaultIp($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$defaultips_old = Settings::Get('system.defaultip');\n\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'defaultip') {\n\t\t\tself::updateStdSubdomainDefaultIp($newfieldvalue, $defaultips_old);\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tprivate static function updateStdSubdomainDefaultIp($newfieldvalue, $defaultips_old)\n\t{\n\t\t// update standard-subdomain of customer if exists\n\t\t$customerstddomains_result_stmt = Database::prepare(\"\n\t\t\tSELECT `standardsubdomain` FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `standardsubdomain` <> '0'\n\t\t\");\n\t\tDatabase::pexecute($customerstddomains_result_stmt);\n\n\t\t$ids = [];\n\t\twhile ($customerstddomains_row = $customerstddomains_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$ids[] = (int)$customerstddomains_row['standardsubdomain'];\n\t\t}\n\n\t\tif (count($ids) > 0) {\n\n\t\t\tif (!empty($defaultips_old)) {\n\t\t\t\t// Delete the existing mappings linking to default IPs\n\t\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\t\tDELETE FROM `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\t\tWHERE `id_domain` IN (\" . implode(', ', $ids) . \")\n\t\t\t\t\tAND `id_ipandports` IN (\" . $defaultips_old . \")\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($del_stmt);\n\t\t\t}\n\n\t\t\t$defaultips_new = !empty($newfieldvalue) ? explode(\",\", $newfieldvalue) : [];\n\t\t\tif (count($defaultips_new) > 0) {\n\n\t\t\t\t// Insert the new mappings\n\t\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\tINSERT INTO `\" . TABLE_DOMAINTOIP . \"`\n\t\t\t\t\tSET `id_domain` = :domainid, `id_ipandports` = :ipandportid\n\t\t\t\t\");\n\n\t\t\t\tforeach ($ids as $id) {\n\t\t\t\t\tforeach ($defaultips_new as $defaultip_new) {\n\t\t\t\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t\t\t\t'domainid' => $id,\n\t\t\t\t\t\t\t'ipandportid' => $defaultip_new\n\t\t\t\t\t\t]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static function storeSettingDefaultSslIp($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$defaultips_old = Settings::Get('system.defaultsslip');\n\n\t\tself::cleanIpSelection($defaultips_old);\n\t\tself::cleanIpSelection($newfieldvalue);\n\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'defaultsslip') {\n\t\t\tself::updateStdSubdomainDefaultIp($newfieldvalue, $defaultips_old);\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tprivate static function cleanIpSelection(&$selection)\n\t{\n\t\t$selection_arr = array_filter(explode(',', $selection), function ($value) {\n\t\t\treturn !empty($value);\n\t\t});\n\t\t$selection = implode(\",\", $selection_arr);\n\t}\n\n\t/**\n\t * updates the setting for the default panel-theme\n\t * and also the user themes (customers and admins) if\n\t * the changing of themes is disallowed for them\n\t *\n\t * @param string $fieldname\n\t * @param array $fielddata\n\t * @param mixed $newfieldvalue\n\t *\n\t * @return boolean|array\n\t */\n\tpublic static function storeSettingDefaultTheme($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t// first save the setting itself\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'panel' && isset($fielddata['varname']) && $fielddata['varname'] == 'default_theme') {\n\t\t\t// now, if changing themes is disabled we manually set\n\t\t\t// the new theme (customers and admin, depending on settings)\n\t\t\tif (Settings::Get('panel.allow_theme_change_customer') == '0') {\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `theme` = :theme\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'theme' => $newfieldvalue\n\t\t\t\t]);\n\t\t\t}\n\t\t\tif (Settings::Get('panel.allow_theme_change_admin') == '0') {\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `theme` = :theme\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'theme' => $newfieldvalue\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingFieldInsertBindTask($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t// first save the setting itself\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false) {\n\t\t\tCronjob::inserttask(TaskId::REBUILD_DNS);\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingFieldInsertAntispamTask($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t// first save the setting itself\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false) {\n\t\t\tCronjob::inserttask(TaskId::REBUILD_RSPAMD);\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingFieldInsertUpdateServicesTask($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t// first save the setting itself\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false) {\n\t\t\tCronjob::inserttask(TaskId::UPDATE_LE_SERVICES);\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingHostname($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && ($fielddata['varname'] == 'hostname' || $fielddata['varname'] == 'stdsubdomain')) {\n\t\t\t$idna_convert = new IdnaWrapper();\n\t\t\t$newfieldvalue = $idna_convert->encode($newfieldvalue);\n\n\t\t\tif (($fielddata['varname'] == 'hostname' && Settings::Get('system.stdsubdomain') == '') || $fielddata['varname'] == 'stdsubdomain') {\n\t\t\t\tif ($fielddata['varname'] == 'stdsubdomain' && $newfieldvalue == '') {\n\t\t\t\t\t// clear field, reset stdsubdomain to system-hostname\n\t\t\t\t\t$oldhost = $idna_convert->encode(Settings::Get('system.stdsubdomain'));\n\t\t\t\t\t$newhost = $idna_convert->encode(Settings::Get('system.hostname'));\n\t\t\t\t} elseif ($fielddata['varname'] == 'stdsubdomain' && Settings::Get('system.stdsubdomain') == '') {\n\t\t\t\t\t// former std-subdomain was system-hostname\n\t\t\t\t\t$oldhost = $idna_convert->encode(Settings::Get('system.hostname'));\n\t\t\t\t\t$newhost = $newfieldvalue;\n\t\t\t\t} elseif ($fielddata['varname'] == 'stdsubdomain') {\n\t\t\t\t\t// std-subdomain just changed\n\t\t\t\t\t$oldhost = $idna_convert->encode(Settings::Get('system.stdsubdomain'));\n\t\t\t\t\t$newhost = $newfieldvalue;\n\t\t\t\t} elseif ($fielddata['varname'] == 'hostname' && Settings::Get('system.stdsubdomain') == '') {\n\t\t\t\t\t// system-hostname has changed and no system-stdsubdomain is not set\n\t\t\t\t\t$oldhost = $idna_convert->encode(Settings::Get('system.hostname'));\n\t\t\t\t\t$newhost = $newfieldvalue;\n\t\t\t\t}\n\n\t\t\t\t$customerstddomains_result_stmt = Database::prepare(\"\n\t\t\t\t\tSELECT `standardsubdomain` FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `standardsubdomain` <> '0'\n\t\t\t\t\");\n\t\t\t\tDatabase::pexecute($customerstddomains_result_stmt);\n\n\t\t\t\t$ids = [];\n\n\t\t\t\twhile ($customerstddomains_row = $customerstddomains_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\t\t$ids[] = (int)$customerstddomains_row['standardsubdomain'];\n\t\t\t\t}\n\n\t\t\t\tif (count($ids) > 0) {\n\t\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\t\tUPDATE `\" . TABLE_PANEL_DOMAINS . \"` SET\n\t\t\t\t\t\t`domain` = REPLACE(`domain`, :host, :newval)\n\t\t\t\t\t\tWHERE `id` IN ('\" . implode(', ', $ids) . \"')\n\t\t\t\t\t\");\n\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t'host' => $oldhost,\n\t\t\t\t\t\t'newval' => $newhost\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingIpAddress($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'ipaddress') {\n\t\t\t$mysql_access_host_array = array_map('trim', explode(',', Settings::Get('system.mysql_access_host')));\n\t\t\t$mysql_access_host_array[] = $newfieldvalue;\n\t\t\t$mysql_access_host_array = array_unique(PhpHelper::arrayTrim($mysql_access_host_array));\n\t\t\tDbManager::correctMysqlUsers($mysql_access_host_array);\n\t\t\t$mysql_access_host = implode(',', $mysql_access_host_array);\n\t\t\tSettings::Set('system.mysql_access_host', $mysql_access_host);\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingMysqlAccessHost($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$ips = $newfieldvalue;\n\t\t// Convert cidr to netmask for mysql, if needed be\n\t\tif (strpos($ips, ',') !== false) {\n\t\t\t$ips = explode(',', $ips);\n\t\t}\n\t\tif (is_array($ips) && count($ips) > 0) {\n\t\t\t$newfieldvalue = [];\n\t\t\tforeach ($ips as $ip) {\n\t\t\t\t$org_ip = $ip;\n\t\t\t\t$ip_cidr = explode(\"/\", $ip);\n\t\t\t\tif (count($ip_cidr) === 2) {\n\t\t\t\t\t$ip = $ip_cidr[0];\n\t\t\t\t\tif (strlen($ip_cidr[1]) <= 2) {\n\t\t\t\t\t\t$ip_cidr[1] = IPTools::cidr2NetmaskAddr($org_ip);\n\t\t\t\t\t}\n\t\t\t\t\t$newfieldvalue[] = $ip . '/' . $ip_cidr[1];\n\t\t\t\t} else {\n\t\t\t\t\t$newfieldvalue[] = $org_ip;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$newfieldvalue = implode(',', $newfieldvalue);\n\t\t}\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'mysql_access_host') {\n\t\t\t$mysql_access_host_array = array_map('trim', explode(',', $newfieldvalue));\n\n\t\t\tif (in_array('127.0.0.1', $mysql_access_host_array) && !in_array('localhost', $mysql_access_host_array)) {\n\t\t\t\t$mysql_access_host_array[] = 'localhost';\n\t\t\t}\n\n\t\t\tif (!in_array('127.0.0.1', $mysql_access_host_array) && in_array('localhost', $mysql_access_host_array)) {\n\t\t\t\t$mysql_access_host_array[] = '127.0.0.1';\n\t\t\t}\n\n\t\t\t// be aware that ipv6 addresses are enclosed in [ ] when passed here\n\t\t\t$mysql_access_host_array = array_map([\n\t\t\t\t'\\\\Froxlor\\\\Settings\\\\Store',\n\t\t\t\t'cleanMySQLAccessHost'\n\t\t\t], $mysql_access_host_array);\n\n\t\t\t$mysql_access_host_array = array_unique(PhpHelper::arrayTrim($mysql_access_host_array));\n\t\t\t$newfieldvalue = implode(',', $mysql_access_host_array);\n\t\t\tDbManager::correctMysqlUsers($mysql_access_host_array);\n\t\t\t$mysql_access_host = implode(',', $mysql_access_host_array);\n\t\t\tSettings::Set('system.mysql_access_host', $mysql_access_host);\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingResetCatchall($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'catchall' && isset($fielddata['varname']) && $fielddata['varname'] == 'catchall_enabled' && $newfieldvalue == '0') {\n\t\t\tDatabase::query(\"\n\t\t\t\tUPDATE `\" . TABLE_MAIL_VIRTUAL . \"` SET `iscatchall` = '0' WHERE `iscatchall` = '1'\n\t\t\t\");\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\t/**\n\t * Whenever the webserver- / FCGID- or FPM-user gets updated\n\t * we need to update ftp_groups accordingly\n\t */\n\tpublic static function storeSettingWebserverFcgidFpmUser($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\tif (is_array($fielddata) && isset($fielddata['settinggroup']) && isset($fielddata['varname'])) {\n\t\t\t$update_user = null;\n\n\t\t\t// webserver\n\t\t\tif ($fielddata['settinggroup'] == 'system' && $fielddata['varname'] == 'httpuser') {\n\t\t\t\t$update_user = Settings::Get('system.httpuser');\n\t\t\t}\n\n\t\t\t// fcgid\n\t\t\tif ($fielddata['settinggroup'] == 'system' && $fielddata['varname'] == 'mod_fcgid_httpuser') {\n\t\t\t\t$update_user = Settings::Get('system.mod_fcgid_httpuser');\n\t\t\t}\n\n\t\t\t// webserver\n\t\t\tif ($fielddata['settinggroup'] == 'phpfpm' && $fielddata['varname'] == 'vhost_httpuser') {\n\t\t\t\t$update_user = Settings::Get('phpfpm.vhost_httpuser');\n\t\t\t}\n\n\t\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\t\tif ($returnvalue !== false) {\n\t\t\t\t/**\n\t\t\t\t * only update if anything changed\n\t\t\t\t */\n\t\t\t\tif ($update_user != null && $newfieldvalue != $update_user) {\n\t\t\t\t\t$upd_stmt = Database::prepare(\"UPDATE `\" . TABLE_FTP_GROUPS . \"` SET `members` = REPLACE(`members`, :olduser, :newuser)\");\n\t\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t\t'olduser' => $update_user,\n\t\t\t\t\t\t'newuser' => $newfieldvalue\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function storeSettingImage($fieldname, $fielddata)\n\t{\n\t\tif (isset($fielddata['settinggroup'], $fielddata['varname']) && is_array($fielddata) && $fielddata['settinggroup'] !== '' && $fielddata['varname'] !== '') {\n\t\t\t$save_to = null;\n\t\t\t$path = Froxlor::getInstallDir() . '/img/';\n\t\t\t$path = FileDir::makeCorrectDir($path);\n\n\t\t\t// New file?\n\t\t\tif (isset($_FILES[$fieldname]) && $_FILES[$fieldname]['tmp_name']) {\n\t\t\t\t// Make sure upload directory exists\n\t\t\t\tif (!is_dir($path) && !mkdir($path, 0775)) {\n\t\t\t\t\tthrow new Exception(\"img directory does not exist and cannot be created\");\n\t\t\t\t}\n\n\t\t\t\t// Make sure we can write to the upload directory\n\t\t\t\tif (!is_writable($path)) {\n\t\t\t\t\tif (!chmod($path, 0775)) {\n\t\t\t\t\t\tthrow new Exception(\"Cannot write to img directory\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Make sure mime-type matches an image\n\t\t\t\t$image_content = file_get_contents($_FILES[$fieldname]['tmp_name']);\n\t\t\t\t$value = base64_encode($image_content);\n\t\t\t\tif (Validate::validateBase64Image($value)) {\n\t\t\t\t\t$img_filename = $_FILES[$fieldname]['name'];\n\n\t\t\t\t\t$spl = explode('.', $img_filename);\n\t\t\t\t\t$file_extension = strtolower(array_pop($spl));\n\t\t\t\t\tunset($spl);\n\n\t\t\t\t\tif (!in_array($file_extension, [\n\t\t\t\t\t\t'jpeg',\n\t\t\t\t\t\t'jpg',\n\t\t\t\t\t\t'png',\n\t\t\t\t\t\t'gif'\n\t\t\t\t\t])) {\n\t\t\t\t\t\tthrow new Exception(\"Invalid file-extension, use one of: jpeg, jpg, png, gif\");\n\t\t\t\t\t}\n\t\t\t\t\t$filename = bin2hex(random_bytes(16)) . '.' . $file_extension;\n\t\t\t\t\t// Move file\n\t\t\t\t\tif (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $filename)) {\n\t\t\t\t\t\tthrow new Exception(\"Unable to save image to img folder\");\n\t\t\t\t\t}\n\t\t\t\t\t$save_to = 'img/' . $filename . '?v=' . time();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Delete file?\n\t\t\tif ($fielddata['value'] !== \"\" && array_key_exists($fieldname . '_delete', $_POST) && Request::post($fieldname . '_delete')) {\n\t\t\t\t@unlink(Froxlor::getInstallDir() . '/' . explode('?', $fielddata['value'], 2)[0]);\n\t\t\t\t$save_to = '';\n\t\t\t}\n\n\t\t\t// Nothing changed\n\t\t\tif ($save_to === null) {\n\t\t\t\treturn [\n\t\t\t\t\t$fielddata['settinggroup'] . '.' . $fielddata['varname'] => $fielddata['value']\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tif (Settings::Set($fielddata['settinggroup'] . '.' . $fielddata['varname'], $save_to) === false) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn [\n\t\t\t\t$fielddata['settinggroup'] . '.' . $fielddata['varname'] => $save_to\n\t\t\t];\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate static function cleanMySQLAccessHost($value)\n\t{\n\t\tif (substr($value, 0, 1) == '[' && substr($value, -1) == ']') {\n\t\t\treturn substr($value, 1, -1);\n\t\t}\n\t\treturn $value;\n\t}\n\n\tpublic static function storeSettingUpdateTrafficTool($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);\n\n\t\tif ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'traffictool' && $newfieldvalue != $fielddata['value']) {\n\t\t\t$oldpath = '/' . $fielddata['value'] . '/';\n\t\t\t$newpath = '/' . $newfieldvalue . '/';\n\t\t\t$sel_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_HTPASSWDS . \"` WHERE `path` LIKE :oldpath\");\n\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_HTPASSWDS . \"` SET `path` = :newpath WHERE `id` = :id\n\t\t\t\");\n\t\t\tDatabase::pexecute($sel_stmt, [\n\t\t\t\t'oldpath' => '%' . $oldpath\n\t\t\t]);\n\t\t\twhile ($entry = $sel_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t\t$full_path = str_replace($oldpath, $newpath, $entry['path']);\n\t\t\t\t$eid = (int)$entry['id'];\n\t\t\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t\t\t'newpath' => $full_path,\n\t\t\t\t\t'id' => $eid\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Settings/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Settings.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse PDO;\nuse PDOStatement;\n\n/**\n * Class Settings\n *\n * Interaction with settings from the db\n */\nclass Settings\n{\n\t/**\n\t * settings data\n\t *\n\t * @var array\n\t */\n\tprivate static $data = null;\n\n\t/**\n\t * local config overrides\n\t *\n\t * @var array\n\t */\n\tprivate static $conf = null;\n\n\t/**\n\t * changed and unsaved settings data\n\t *\n\t * @var array\n\t */\n\tprivate static $updatedata = null;\n\n\t/**\n\t * prepared statement for updating the\n\t * settings table\n\t *\n\t * @var PDOStatement\n\t */\n\tprivate static $updstmt = null;\n\n\t/**\n\t * tests if a setting-value that i s a comma separated list contains an entry\n\t *\n\t * @param string $setting\n\t *            a group and a varname separated by a dot (group.varname)\n\t * @param string $entry\n\t *            the entry that is expected to be in the list\n\t *\n\t * @return boolean true, if the list contains $entry\n\t */\n\tpublic static function IsInList($setting = null, $entry = null)\n\t{\n\t\tself::init();\n\t\t$svalue = self::Get($setting);\n\t\tif ($svalue == null) {\n\t\t\treturn false;\n\t\t}\n\t\t$slist = explode(\",\", $svalue);\n\t\treturn in_array($entry, $slist);\n\t}\n\n\t/**\n\t * private constructor, reads in all settings\n\t */\n\tprivate static function init()\n\t{\n\t\tif (empty(self::$data)) {\n\t\t\tself::readSettings();\n\t\t\tself::readConfig();\n\t\t\tself::$updatedata = [];\n\n\t\t\t// prepare statement\n\t\t\tself::$updstmt = Database::prepare(\"\n\t\t\t\tUPDATE `\" . TABLE_PANEL_SETTINGS . \"` SET `value` = :value\n\t\t\t\tWHERE `settinggroup` = :group AND `varname` = :varname\n\t\t\t\");\n\t\t}\n\t}\n\n\t/**\n\t * Read in all settings from the database\n\t * and set the internal $_data array\n\t */\n\tprivate static function readSettings()\n\t{\n\t\t$result_stmt = Database::query(\"\n\t\t\tSELECT `settingid`, `settinggroup`, `varname`, `value`\n\t\t\tFROM `\" . TABLE_PANEL_SETTINGS . \"`\n\t\t\");\n\t\tself::$data = [];\n\t\twhile ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tself::$data[$row['settinggroup']][$row['varname']] = $row['value'];\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Read in all config overrides from\n\t * config/config.inc.php\n\t */\n\tprivate static function readConfig()\n\t{\n\t\t// set defaults\n\t\tself::$conf = [\n\t\t\t'enable_webupdate' => false,\n\t\t\t'disable_otp_security_check' => false,\n\t\t\t'display_php_errors' => false,\n\t\t];\n\n\t\t$configfile = Froxlor::getInstallDir() . '/lib/config.inc.php';\n\t\tif (@file_exists($configfile) && is_readable($configfile)) {\n\t\t\tself::$conf = array_merge(self::$conf, include $configfile);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * return a setting-value by its group and varname\n\t *\n\t * @param string $setting\n\t *            a group and a varname separated by a dot (group.varname)\n\t *\n\t * @return mixed\n\t */\n\tpublic static function Get($setting = null)\n\t{\n\t\tself::init();\n\t\t$sstr = explode(\".\", $setting);\n\t\t// no separator - do'h\n\t\tif (!isset($sstr[1])) {\n\t\t\treturn null;\n\t\t}\n\t\t$result = null;\n\t\tif (isset(self::$data[$sstr[0]][$sstr[1]])) {\n\t\t\t$result = self::$data[$sstr[0]][$sstr[1]];\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * update a setting / set a new value\n\t *\n\t * @param string $setting\n\t *            a group and a varname separated by a dot (group.varname)\n\t * @param string $value\n\t * @param boolean $instant_save\n\t *\n\t * @return bool\n\t */\n\tpublic static function Set($setting = null, $value = null, $instant_save = true)\n\t{\n\t\tself::init();\n\t\t// check whether the setting exists\n\t\tif (self::Get($setting) !== null) {\n\t\t\t// set new value in array\n\t\t\t$sstr = explode(\".\", $setting);\n\t\t\tif (!isset($sstr[1])) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tself::$data[$sstr[0]][$sstr[1]] = $value;\n\t\t\t// should we store to db instantly?\n\t\t\tif ($instant_save) {\n\t\t\t\tself::storeSetting($sstr[0], $sstr[1], $value);\n\t\t\t} else {\n\t\t\t\t// set temporary data for usage\n\t\t\t\tif (!isset(self::$data[$sstr[0]]) || !is_array(self::$data[$sstr[0]])) {\n\t\t\t\t\tself::$data[$sstr[0]] = [];\n\t\t\t\t}\n\t\t\t\tself::$data[$sstr[0]][$sstr[1]] = $value;\n\t\t\t\t// set update-data when invoking Flush()\n\t\t\t\tif (!isset(self::$updatedata[$sstr[0]]) || !is_array(self::$updatedata[$sstr[0]])) {\n\t\t\t\t\tself::$updatedata[$sstr[0]] = [];\n\t\t\t\t}\n\t\t\t\tself::$updatedata[$sstr[0]][$sstr[1]] = $value;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * update a value in the database\n\t *\n\t * @param string $group\n\t * @param string $varname\n\t * @param string $value\n\t */\n\tprivate static function storeSetting($group = null, $varname = null, $value = null)\n\t{\n\t\t$upd_data = [\n\t\t\t'group' => $group,\n\t\t\t'varname' => $varname,\n\t\t\t'value' => $value\n\t\t];\n\t\tDatabase::pexecute(self::$updstmt, $upd_data);\n\t}\n\n\t/**\n\t * add a new setting to the database (mainly used in updater)\n\t *\n\t * @param string $setting\n\t *            a group and a varname separated by a dot (group.varname)\n\t * @param string $value\n\t *\n\t * @return boolean\n\t */\n\tpublic static function AddNew($setting = null, $value = null)\n\t{\n\t\tself::init();\n\t\t// first check if it doesn't exist\n\t\tif (self::Get($setting) === null) {\n\t\t\t// validate parameter\n\t\t\t$sstr = explode(\".\", $setting);\n\t\t\tif (!isset($sstr[1])) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// prepare statement\n\t\t\t$ins_stmt = Database::prepare(\"\n\t\t\t\t\tINSERT INTO `\" . TABLE_PANEL_SETTINGS . \"` SET\n\t\t\t\t\t`settinggroup` = :group,\n\t\t\t\t\t`varname` = :varname,\n\t\t\t\t\t`value` = :value\n\t\t\t\t\t\");\n\t\t\t$ins_data = [\n\t\t\t\t'group' => $sstr[0],\n\t\t\t\t'varname' => $sstr[1],\n\t\t\t\t'value' => $value\n\t\t\t];\n\t\t\tDatabase::pexecute($ins_stmt, $ins_data);\n\t\t\t// also set new value to internal array and make it available\n\t\t\tself::$data[$sstr[0]][$sstr[1]] = $value;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Store all un-saved changes to the database and\n\t * re-read in all settings\n\t */\n\tpublic static function Flush()\n\t{\n\t\tself::init();\n\t\tif (is_array(self::$updatedata) && count(self::$updatedata) > 0) {\n\t\t\t// save all un-saved changes to the settings\n\t\t\tforeach (self::$updatedata as $group => $vargroup) {\n\t\t\t\tforeach ($vargroup as $varname => $value) {\n\t\t\t\t\tself::storeSetting($group, $varname, $value);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// now empty the array\n\t\t\tself::$updatedata = [];\n\t\t\t// re-read in all settings\n\t\t\treturn self::readSettings();\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * forget all un-saved changes to settings\n\t */\n\tpublic static function Stash()\n\t{\n\t\tself::init();\n\t\t// empty update array\n\t\tself::$updatedata = [];\n\t\t// re-read in all settings\n\t\treturn self::readSettings();\n\t}\n\n\tpublic static function loadSettingsInto(&$settings_data)\n\t{\n\t\tif (is_array($settings_data) && isset($settings_data['groups']) && is_array($settings_data['groups'])) {\n\t\t\t// prepare for use in for-loop\n\t\t\t$row_stmt = Database::prepare(\"\n\t\t\t\tSELECT `settinggroup`, `varname`, `value`\n\t\t\t\tFROM `\" . TABLE_PANEL_SETTINGS . \"`\n\t\t\t\tWHERE `settinggroup` = :group AND `varname` = :varname\n\t\t\t\");\n\n\t\t\tforeach ($settings_data['groups'] as $settings_part => $settings_part_details) {\n\t\t\t\tif (is_array($settings_part_details) && isset($settings_part_details['fields']) && is_array($settings_part_details['fields'])) {\n\t\t\t\t\tforeach ($settings_part_details['fields'] as $field_name => $field_details) {\n\t\t\t\t\t\tif (isset($field_details['settinggroup']) && isset($field_details['varname']) && isset($field_details['default'])) {\n\t\t\t\t\t\t\t// execute prepared statement\n\t\t\t\t\t\t\t$row = Database::pexecute_first($row_stmt, [\n\t\t\t\t\t\t\t\t'group' => $field_details['settinggroup'],\n\t\t\t\t\t\t\t\t'varname' => $field_details['varname']\n\t\t\t\t\t\t\t]);\n\n\t\t\t\t\t\t\tif (!empty($row)) {\n\t\t\t\t\t\t\t\t$varvalue = $row['value'];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$varvalue = $field_details['default'];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$varvalue = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$settings_data['groups'][$settings_part]['fields'][$field_name]['value'] = $varvalue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static function refreshState(): void\n\t{\n\t\tself::$data = null;\n\t\tself::init();\n\t}\n\n\tpublic static function getAll(): array\n\t{\n\t\tself::init();\n\t\treturn self::$data;\n\t}\n\n\t/**\n\t * get value from config by identifier\n\t * @throws Exception\n\t */\n\tpublic static function Config(string $config)\n\t{\n\t\tself::init();\n\t\t$result = self::$conf[$config] ?? null;\n\t\tif (is_null($result)) {\n\t\t\tthrow new Exception('Unknown local config name \"' . $config . '\"');\n\t\t}\n\t\treturn $result;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/System/Cronjob.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\System;\n\nuse Exception;\nuse Froxlor\\Cron\\TaskId;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Settings;\nuse PDO;\n\nclass Cronjob\n{\n\n\t/**\n\t * Function checkLastGuid\n\t *\n\t * Checks if the system's last guid is not higher than the one saved\n\t * in froxlor's database. If it's higher, froxlor needs to\n\t * set its last guid to this one to avoid conflicts with libnss-users\n\t *\n\t * @return null\n\t */\n\tpublic static function checkLastGuid()\n\t{\n\t\t$mylog = FroxlorLogger::getInstanceOf();\n\n\t\t$group_lines = [];\n\t\t$group_guids = [];\n\t\t$update_to_guid = 0;\n\n\t\t$froxlor_guid = 0;\n\t\t$result_stmt = Database::query(\"SELECT MAX(`guid`) as `fguid` FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\");\n\t\t$result = $result_stmt->fetch(PDO::FETCH_ASSOC);\n\t\t$froxlor_guid = $result['fguid'];\n\n\t\t// possibly no customers yet or f*cked up lastguid settings\n\t\tif ($froxlor_guid < Settings::Get('system.lastguid')) {\n\t\t\t$froxlor_guid = Settings::Get('system.lastguid');\n\t\t}\n\n\t\t$g_file = '/etc/group';\n\n\t\tif (file_exists($g_file)) {\n\t\t\tif (is_readable($g_file)) {\n\t\t\t\tif (true == ($groups = file_get_contents($g_file))) {\n\t\t\t\t\t$group_lines = explode(\"\\n\", $groups);\n\n\t\t\t\t\tforeach ($group_lines as $group) {\n\t\t\t\t\t\t$group_guids[] = explode(\":\", $group);\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach ($group_guids as $group) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * nogroup | nobody have very high guids\n\t\t\t\t\t\t * ignore them\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif ($group[0] == 'nogroup' || $group[0] == 'nobody') {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$guid = isset($group[2]) ? (int)$group[2] : 0;\n\n\t\t\t\t\t\tif ($guid > $update_to_guid) {\n\t\t\t\t\t\t\t$update_to_guid = $guid;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// if it's lower, then froxlor's highest guid is the last\n\t\t\t\t\tif ($update_to_guid < $froxlor_guid) {\n\t\t\t\t\t\t$update_to_guid = $froxlor_guid;\n\t\t\t\t\t} elseif ($update_to_guid == $froxlor_guid) {\n\t\t\t\t\t\t// if it's equal, that means we already have a collision\n\t\t\t\t\t\t// to ensure it won't happen again, increase the guid by one\n\t\t\t\t\t\t$update_to_guid = (int)$update_to_guid++;\n\t\t\t\t\t}\n\n\t\t\t\t\t// now check if it differs from our settings\n\t\t\t\t\tif ($update_to_guid != Settings::Get('system.lastguid')) {\n\t\t\t\t\t\t$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE,\n\t\t\t\t\t\t\t'Updating froxlor last guid to ' . $update_to_guid);\n\t\t\t\t\t\tSettings::Set('system.lastguid', $update_to_guid);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE,\n\t\t\t\t\t\t'File /etc/group not readable; cannot check for latest guid');\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE,\n\t\t\t\t\t'File /etc/group not readable; cannot check for latest guid');\n\t\t\t}\n\t\t} else {\n\t\t\t$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE,\n\t\t\t\t'File /etc/group does not exist; cannot check for latest guid');\n\t\t}\n\t}\n\n\tpublic static function checkCurrentDistro(bool $is_install = false): string\n\t{\n\t\t// set default os.\n\t\tif ($is_install) {\n\t\t\t$distro = \"trixie\";\n\t\t} else {\n\t\t\t$distro = Settings::Get('system.distribution');\n\t\t}\n\n\t\t// read os-release\n\t\tif (@file_exists('/etc/os-release') && is_readable('/etc/os-release')) {\n\t\t\tif (function_exists('parse_ini_file')) {\n\t\t\t\t$os_dist = parse_ini_file('/etc/os-release', false);\n\t\t\t} else {\n\t\t\t\t$osrf = explode(\"\\n\", file_get_contents('/etc/os-release'));\n\t\t\t\tforeach ($osrf as $line) {\n\t\t\t\t\t$osrfline = explode(\"=\", $line);\n\t\t\t\t\tif ($osrfline[0] == 'VERSION_CODENAME') {\n\t\t\t\t\t\t$os_dist['VERSION_CODENAME'] = $osrfline[1];\n\t\t\t\t\t} elseif ($osrfline[0] == 'ID') {\n\t\t\t\t\t\t$os_dist['ID'] = $osrfline[1];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t$distro = strtolower($os_dist['VERSION_CODENAME'] ?? ($os_dist['ID'] ?? $distro));\n\t\t}\n\n\t\tif (!$is_install && $distro != Settings::Get('system.distribution') && Settings::Get('system.distro_mismatch') != '2') {\n\t\t\tSettings::Set('system.distro_mismatch', '1');\n\t\t}\n\n\t\treturn $distro;\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tpublic static function checkLocalUserGroupMembership(): bool\n\t{\n\t\tif ((int)Settings::Get('phpfpm.enabled') == 1) {\n\t\t\t$username = Settings::Get('phpfpm.vhost_httpuser');\n\t\t} elseif ((int)Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t$username = Settings::Get('system.mod_fcgid_httpuser');\n\t\t} else {\n\t\t\t$username = Settings::Get('system.httpuser');\n\t\t}\n\t\t$user = posix_getpwnam($username);\n\t\t$group = posix_getgrnam(Settings::Get('system.httpgroup'));\n\n\t\t$mylog = FroxlorLogger::getInstanceOf();\n\t\tif (!$user || !$group) {\n\t\t\t$mylog->logAction(\n\t\t\t\tFroxlorLogger::CRON_ACTION,\n\t\t\t\tLOG_NOTICE,\n\t\t\t\t'Either local froxlor user or webserver-group could not be found/read. Please check settings.'\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\t// primary group?\n\t\tif ($user['gid'] === $group['gid']) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// supplementary groups?\n\t\tif (in_array($username, $group['members'])) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// not yet in group, add it\n\t\t$mylog->logAction(\n\t\t\tFroxlorLogger::CRON_ACTION,\n\t\t\tLOG_NOTICE,\n\t\t\t'Local froxlor user not in webserver-group. Adding user \"' . $username . '\" to group \"' . Settings::Get('system.httpgroup') . '\"'\n\t\t);\n\t\tFileDir::safe_exec('usermod -aG ' . escapeshellarg(Settings::Get('system.httpgroup')) . ' ' . escapeshellarg($username));\n\t\treturn true;\n\t}\n\n\t/**\n\t * Inserts a task into the PANEL_TASKS-Table\n\t *\n\t * @param int $type Type of task\n\t * @param string $params Parameter (possible to pass multiple times)\n\t *\n\t * @throws Exception\n\t * @author Froxlor team <team@froxlor.org> (2010-)\n\t */\n\tpublic static function inserttask(int $type, ...$params)\n\t{\n\t\t// prepare the insert-statement\n\t\t$ins_stmt = Database::prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_TASKS . \"` SET `type` = :type, `data` = :data\n\t\t\");\n\n\t\tif ($type == TaskId::REBUILD_VHOST || $type == TaskId::REBUILD_DNS || $type == TaskId::CREATE_FTP || $type == TaskId::REBUILD_RSPAMD || $type == TaskId::CREATE_QUOTA || $type == TaskId::REBUILD_CRON || $type == TaskId::UPDATE_LE_SERVICES || $type == TaskId::REBUILD_NSSUSERS) {\n\t\t\t// 4 = bind -> if bind disabled -> no task\n\t\t\tif ($type == TaskId::REBUILD_DNS && Settings::Get('system.bind_enable') == '0') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// 9 = rspamd -> if antispam disabled -> no task\n\t\t\tif ($type == TaskId::REBUILD_RSPAMD && Settings::Get('antispam.activated') == '0') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// 10 = quota -> if quota disabled -> no task\n\t\t\tif ($type == TaskId::CREATE_QUOTA && Settings::Get('system.diskquota_enabled') == '0') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// 13 = let's encrypt for services -> if services empty = no task\n\t\t\tif ($type == TaskId::UPDATE_LE_SERVICES && (Settings::Get('system.le_froxlor_enabled') == '0' || Settings::Get('system.le_renew_services') == '')) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// delete previously inserted tasks if they are the same as we only need ONE\n\t\t\t$del_stmt = Database::prepare(\"\n\t\t\t\tDELETE FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` = :type\n\t\t\t\");\n\t\t\tDatabase::pexecute($del_stmt, [\n\t\t\t\t'type' => $type\n\t\t\t]);\n\n\t\t\t// insert the new task\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'type' => $type,\n\t\t\t\t'data' => ''\n\t\t\t]);\n\t\t} elseif ($type == TaskId::CREATE_HOME && count($params) == 4 && $params[0] != '' && $params[1] != '' && $params[2] != '' && ($params[3] == 0 || $params[3] == 1)) {\n\t\t\t$data = [];\n\t\t\t$data['loginname'] = $params[0];\n\t\t\t$data['uid'] = $params[1];\n\t\t\t$data['gid'] = $params[2];\n\t\t\t$data['store_defaultindex'] = $params[3];\n\t\t\t$data = json_encode($data);\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'type' => TaskId::CREATE_HOME,\n\t\t\t\t'data' => $data\n\t\t\t]);\n\t\t} elseif ($type == TaskId::DELETE_CUSTOMER_FILES && isset($params[0]) && $params[0] != '') {\n\t\t\t$data = [];\n\t\t\t$data['loginname'] = $params[0];\n\t\t\t$data = json_encode($data);\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'type' => TaskId::DELETE_CUSTOMER_FILES,\n\t\t\t\t'data' => $data\n\t\t\t]);\n\t\t} elseif ($type == TaskId::DELETE_EMAIL_DATA && count($params) == 2 && $params[0] != '' && $params[1] != '') {\n\t\t\t$data = [];\n\t\t\t$data['loginname'] = $params[0];\n\t\t\t$data['emailpath'] = $params[1];\n\t\t\t$data = json_encode($data);\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'type' => TaskId::DELETE_EMAIL_DATA,\n\t\t\t\t'data' => $data\n\t\t\t]);\n\t\t} elseif ($type == TaskId::DELETE_FTP_DATA && count($params) == 2 && $params[0] != '' && $params[1] != '') {\n\t\t\t$data = [];\n\t\t\t$data['loginname'] = $params[0];\n\t\t\t$data['homedir'] = $params[1];\n\t\t\t$data = json_encode($data);\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'type' => TaskId::DELETE_FTP_DATA,\n\t\t\t\t'data' => $data\n\t\t\t]);\n\t\t} elseif ($type == TaskId::DELETE_DOMAIN_PDNS && isset($params[0]) && $params[0] != '' && Settings::Get('system.bind_enable') == '1' && Settings::Get('system.dns_server') == 'PowerDNS') {\n\t\t\t// -> if bind disabled or dns-server not PowerDNS -> no task\n\t\t\t$data = [];\n\t\t\t$data['domain'] = $params[0];\n\t\t\t$data = json_encode($data);\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'type' => TaskId::DELETE_DOMAIN_PDNS,\n\t\t\t\t'data' => $data\n\t\t\t]);\n\t\t} elseif ($type == TaskId::DELETE_DOMAIN_SSL && isset($params[0]) && $params[0] != '') {\n\t\t\t$data = [];\n\t\t\t$data['domain'] = $params[0];\n\t\t\t$data = json_encode($data);\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'type' => TaskId::DELETE_DOMAIN_SSL,\n\t\t\t\t'data' => $data\n\t\t\t]);\n\t\t} elseif ($type == TaskId::CREATE_CUSTOMER_DATADUMP && isset($params[0]) && is_array($params[0])) {\n\t\t\t$data = json_encode($params[0]);\n\t\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t\t'type' => TaskId::CREATE_CUSTOMER_DATADUMP,\n\t\t\t\t'data' => $data\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * returns an array of all cronjobs and when they last were executed\n\t *\n\t * @return array\n\t */\n\tpublic static function getCronjobsLastRun(): array\n\t{\n\t\t$query = \"SELECT `lastrun`, `desc_lng_key` FROM `\" . TABLE_PANEL_CRONRUNS . \"` WHERE `isactive` = '1' ORDER BY `cronfile` ASC\";\n\t\t$result = Database::query($query);\n\n\t\t$cronjobs_last_run = [];\n\t\twhile ($row = $result->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$cronjobs_last_run[] = [\n\t\t\t\t'title' => lng('crondesc.' . $row['desc_lng_key']),\n\t\t\t\t'lastrun' => $row['lastrun']\n\t\t\t];\n\t\t}\n\t\treturn $cronjobs_last_run;\n\t}\n\n\t/**\n\t * @param string $module\n\t * @param int $isactive\n\t * @return void\n\t * @throws Exception\n\t */\n\tpublic static function toggleCronStatus(string $module, int $isactive = 0)\n\t{\n\t\tif ($isactive != 1) {\n\t\t\t$isactive = 0;\n\t\t}\n\n\t\t$upd_stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_PANEL_CRONRUNS . \"` SET `isactive` = :active WHERE `module` = :module\n\t\t\");\n\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t'active' => $isactive,\n\t\t\t'module' => $module\n\t\t]);\n\t}\n\n\t/**\n\t * returns an array of tasks that are queued to be run by the cronjob\n\t *\n\t * @return array\n\t */\n\tpublic static function getOutstandingTasks(): array\n\t{\n\t\t$query = \"SELECT * FROM `\" . TABLE_PANEL_TASKS . \"` ORDER BY `type` ASC\";\n\t\t$result = Database::query($query);\n\n\t\t$tasks = [];\n\t\twhile ($row = $result->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tif ($row['data'] != '') {\n\t\t\t\t$row['data'] = json_decode($row['data'], true);\n\t\t\t}\n\n\t\t\t$task_id = $row['type'];\n\t\t\tif (TaskId::isValid($task_id)) {\n\t\t\t\t$task_constname = TaskId::convertToConstant($task_id);\n\t\t\t\t$lngParams = [];\n\t\t\t\tif (is_array($row['data'])) {\n\t\t\t\t\t// task includes loginname\n\t\t\t\t\tif (isset($row['data']['loginname'])) {\n\t\t\t\t\t\t$lngParams = [$row['data']['loginname']];\n\t\t\t\t\t}\n\t\t\t\t\t// task includes domain data\n\t\t\t\t\tif (isset($row['data']['domain'])) {\n\t\t\t\t\t\t$lngParams = [$row['data']['domain']];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$task = [\n\t\t\t\t\t'desc' => lng('tasks.' . $task_constname, $lngParams)\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t// unknown\n\t\t\t\t$task = ['desc' => \"ERROR: Unknown task type '\" . $row['type'] . \"'\"];\n\t\t\t}\n\n\t\t\t$tasks[] = $task;\n\t\t}\n\n\t\tif (empty($tasks)) {\n\t\t\t$tasks = [['desc' => lng('tasks.noneoutstanding')]];\n\t\t}\n\n\t\treturn $tasks;\n\t}\n\n\t/**\n\t * Send notification to system admin via email\n\t *\n\t * @param string $message\n\t * @param string $subject\n\t *\n\t * @return void\n\t */\n\tpublic static function notifyMailToAdmin(string $message, string $subject = \"[froxlor] Important notice\")\n\t{\n\t\t$mail = new Mailer(true);\n\t\t$mailerror = false;\n\t\t$mailerr_msg = \"\";\n\t\ttry {\n\t\t\t$mail->Subject = $subject;\n\t\t\t$mail->AltBody = $message;\n\t\t\t$mail->MsgHTML(nl2br($message));\n\t\t\t$mail->AddAddress(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));\n\t\t\t$mail->Send();\n\t\t} catch (\\PHPMailer\\PHPMailer\\Exception $e) {\n\t\t\t$mailerr_msg = $e->errorMessage();\n\t\t\t$mailerror = true;\n\t\t} catch (Exception $e) {\n\t\t\t$mailerr_msg = $e->getMessage();\n\t\t\t$mailerror = true;\n\t\t}\n\n\t\t$mail->ClearAddresses();\n\n\t\tif ($mailerror) {\n\t\t\techo 'Error sending mail: ' . $mailerr_msg . \"\\n\";\n\t\t}\n\t}\n\n\t/**\n\t * @param string $cronname\n\t * @return void\n\t * @throws Exception\n\t */\n\tpublic static function updateLastRunOfCron(string $cronname)\n\t{\n\t\t$upd_stmt = Database::prepare(\"\n\t\t\tUPDATE `\" . TABLE_PANEL_CRONRUNS . \"` SET `lastrun` = UNIX_TIMESTAMP() WHERE `cronfile` = :cron;\n\t\t\");\n\t\tDatabase::pexecute($upd_stmt, [\n\t\t\t'cron' => $cronname\n\t\t]);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/System/Crypt.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\System;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\Settings;\nuse Froxlor\\Validate\\Validate;\n\nclass Crypt\n{\n\n\t/**\n\t * Generates a random password\n\t *\n\t * @param int $length optional, will be read from settings if not given\n\t * @param bool $isSalt optional, default false, do not include special characters\n\t *\n\t * @return string\n\t */\n\tpublic static function generatePassword(int $length = 0, bool $isSalt = false): string\n\t{\n\t\t$alpha_lower = 'abcdefghijklmnopqrstuvwxyz';\n\t\t$alpha_upper = strtoupper($alpha_lower);\n\t\t$numeric = '0123456789';\n\t\t$special = Settings::Get('panel.password_special_char');\n\t\tif (empty($length)) {\n\t\t\t$length = Settings::Get('panel.password_min_length') > 3 ? Settings::Get('panel.password_min_length') : 10;\n\t\t}\n\n\t\t$pw = self::specialShuffle($alpha_lower);\n\t\t$n = floor(($length) / 4);\n\n\t\tif (Settings::Get('panel.password_alpha_upper')) {\n\t\t\t$pw .= mb_substr(self::specialShuffle($alpha_upper), 0, $n);\n\t\t}\n\n\t\tif (Settings::Get('panel.password_numeric')) {\n\t\t\t$pw .= mb_substr(self::specialShuffle($numeric), 0, $n);\n\t\t}\n\n\t\tif (Settings::Get('panel.password_special_char_required') && !$isSalt) {\n\t\t\t$pw .= mb_substr(self::specialShuffle($special), 0, $n);\n\t\t}\n\n\t\t$pw = mb_substr($pw, -$length);\n\n\t\treturn self::specialShuffle($pw);\n\t}\n\n\t/**\n\t * multibyte-character safe shuffle function\n\t *\n\t * @param string $str\n\t *\n\t * @return string\n\t */\n\tprivate static function specialShuffle(string $str): string\n\t{\n\t\t$len = mb_strlen($str);\n\t\t$sploded = [];\n\t\twhile ($len-- > 0) {\n\t\t\t$sploded[] = mb_substr($str, $len, 1);\n\t\t}\n\t\tshuffle($sploded);\n\t\treturn join('', $sploded);\n\t}\n\n\t/**\n\t * return an array of available hashes\n\t *\n\t * @return array\n\t */\n\tpublic static function getAvailablePasswordHashes(): array\n\t{\n\t\t// get available pwd-hases\n\t\t$available_pwdhashes = [\n\t\t\tPASSWORD_DEFAULT => lng('serversettings.systemdefault')\n\t\t];\n\t\tif (defined('PASSWORD_BCRYPT')) {\n\t\t\t$available_pwdhashes[PASSWORD_BCRYPT] = 'Bcrypt/Blowfish' . (PASSWORD_DEFAULT == PASSWORD_BCRYPT ? ' (' . lng('serversettings.systemdefault') . ')' : '');\n\t\t}\n\t\tif (defined('PASSWORD_ARGON2I')) {\n\t\t\t$available_pwdhashes[PASSWORD_ARGON2I] = 'Argon2i' . (PASSWORD_DEFAULT == PASSWORD_ARGON2I ? ' (' . lng('serversettings.systemdefault') . ')' : '');\n\t\t}\n\t\tif (defined('PASSWORD_ARGON2ID')) {\n\t\t\t$available_pwdhashes[PASSWORD_ARGON2ID] = 'Argon2id' . (PASSWORD_DEFAULT == PASSWORD_ARGON2ID ? ' (' . lng('serversettings.systemdefault') . ')' : '');\n\t\t}\n\n\t\treturn $available_pwdhashes;\n\t}\n\n\t/**\n\t * Function validatePassword\n\t *\n\t * if password-min-length is set in settings\n\t * we check against the length, if not matched\n\t * an error message will be output and 'exit' is called\n\t *\n\t * @param string $password the password to validate\n\t * @param bool $json_response\n\t *\n\t * @return string either the password or an errormessage+exit\n\t */\n\tpublic static function validatePassword(string $password, bool $json_response = false): string\n\t{\n\t\tif (Settings::Get('panel.password_min_length') > 0) {\n\t\t\t$password = Validate::validate($password, Settings::Get('panel.password_min_length'),\n\t\t\t\t'/^.{' . (int)Settings::Get('panel.password_min_length') . ',}$/D', 'notrequiredpasswordlength', [],\n\t\t\t\t$json_response);\n\t\t}\n\n\t\tif (Settings::Get('panel.password_regex') != '') {\n\t\t\t$password = Validate::validate($password, Settings::Get('panel.password_regex'),\n\t\t\t\tSettings::Get('panel.password_regex'), 'notrequiredpasswordcomplexity', [], $json_response);\n\t\t} else {\n\t\t\tif (Settings::Get('panel.password_alpha_lower')) {\n\t\t\t\t$password = Validate::validate($password, '/.*[a-z]+.*/', '/.*[a-z]+.*/',\n\t\t\t\t\t'notrequiredpasswordcomplexity', [], $json_response);\n\t\t\t}\n\t\t\tif (Settings::Get('panel.password_alpha_upper')) {\n\t\t\t\t$password = Validate::validate($password, '/.*[A-Z]+.*/', '/.*[A-Z]+.*/',\n\t\t\t\t\t'notrequiredpasswordcomplexity', [], $json_response);\n\t\t\t}\n\t\t\tif (Settings::Get('panel.password_numeric')) {\n\t\t\t\t$password = Validate::validate($password, '/.*[0-9]+.*/', '/.*[0-9]+.*/',\n\t\t\t\t\t'notrequiredpasswordcomplexity', [], $json_response);\n\t\t\t}\n\t\t\tif (Settings::Get('panel.password_special_char_required')) {\n\t\t\t\t$password = Validate::validate($password,\n\t\t\t\t\t'/.*[' . preg_quote(Settings::Get('panel.password_special_char'), '/') . ']+.*/',\n\t\t\t\t\t'/.*[' . preg_quote(Settings::Get('panel.password_special_char'), '/') . ']+.*/',\n\t\t\t\t\t'notrequiredpasswordcomplexity', [], $json_response);\n\t\t\t}\n\t\t}\n\n\t\treturn $password;\n\t}\n\n\t/**\n\t * Function validatePasswordLogin\n\t *\n\t * compare user password-hash with given user-password\n\t * and check if they are the same\n\t * additionally it updates the hash if the system settings changed\n\t * or if the very old md5() sum is used\n\t *\n\t * @param array $userinfo user-data from table\n\t * @param string $password the password to validate\n\t * @param string $table either panel_customers or panel_admins\n\t * @param string $uid user-id-field in $table\n\t *\n\t * @return bool\n\t * @throws \\Exception\n\t */\n\tpublic static function validatePasswordLogin(\n\t\tarray $userinfo,\n\t\tstring $password,\n\t\tstring $table = 'panel_customers',\n\t\tstring $uid = 'customerid'\n\t): bool {\n\t\t$algo = Settings::Get('system.passwordcryptfunc') !== null ? Settings::Get('system.passwordcryptfunc') : PASSWORD_DEFAULT;\n\t\tif (is_numeric($algo)) {\n\t\t\t// old setting format\n\t\t\t$algo = PASSWORD_DEFAULT;\n\t\t\tSettings::Set('system.passwordcryptfunc', $algo);\n\t\t}\n\t\t$pwd_hash = $userinfo['password'];\n\n\t\t$update_hash = false;\n\t\t$pwd_check = \"\";\n\t\t// check for good'ole md5\n\t\tif (strlen($pwd_hash) == 32 && ctype_xdigit($pwd_hash)) {\n\t\t\t$pwd_check = md5($password);\n\t\t\t$update_hash = true;\n\t\t}\n\n\t\tif ($pwd_hash === $pwd_check || password_verify($password, $pwd_hash)) {\n\t\t\t// check for update of hash (only if our database is ready to handle the bigger string)\n\t\t\t$is_ready = Froxlor::versionCompare2(\"0.9.33\", Froxlor::getVersion()) <= 0;\n\t\t\tif ((password_needs_rehash($pwd_hash, $algo) || $update_hash) && $is_ready) {\n\t\t\t\t$upd_stmt = Database::prepare(\"\n\t\t\t\t\tUPDATE \" . $table . \" SET `password` = :newpasswd WHERE `\" . $uid . \"` = :uid\n\t\t\t\t\");\n\t\t\t\t$params = [\n\t\t\t\t\t'newpasswd' => self::makeCryptPassword($password),\n\t\t\t\t\t'uid' => $userinfo[$uid]\n\t\t\t\t];\n\t\t\t\tDatabase::pexecute($upd_stmt, $params);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Make encrypted password from clear text password\n\t *\n\t * @param string $password Password to be encrypted\n\t * @param bool $htpasswd optional whether to generate a bcrypt password for directory protection\n\t * @param bool $ftpd optional generates sha256 password strings for proftpd/pureftpd\n\t *\n\t * @return string encrypted password\n\t */\n\tpublic static function makeCryptPassword(string $password, bool $htpasswd = false, bool $ftpd = false): string\n\t{\n\t\tif ($htpasswd || $ftpd) {\n\t\t\tif ($ftpd) {\n\t\t\t\t// sha256 compatible for proftpd and pure-ftpd\n\t\t\t\treturn crypt($password, '$5$' . self::generatePassword(16, true) . '$');\n\t\t\t}\n\t\t\t// bcrypt hash for dir-protection\n\t\t\treturn password_hash($password, PASSWORD_BCRYPT);\n\t\t}\n\t\t// crypt using the specified crypt-algorithm or system default\n\t\t$algo = Settings::Get('system.passwordcryptfunc') !== null ? Settings::Get('system.passwordcryptfunc') : PASSWORD_DEFAULT;\n\t\treturn password_hash($password, $algo);\n\t}\n\n\t/**\n\t * creates a self-signed ECC-certificate for the froxlor-vhost\n\t * and sets the content to the corresponding files set in the\n\t * settings for ssl-certificate-file and ssl-certificate-key\n\t *\n\t * @return void\n\t */\n\tpublic static function createSelfSignedCertificate()\n\t{\n\t\t// validate that we have file names in the settings\n\t\t$certFile = Settings::Get('system.ssl_cert_file');\n\t\t$keyFile = Settings::Get('system.ssl_key_file');\n\t\tif (empty($certFile)) {\n\t\t\t$certFile = '/etc/ssl/froxlor_selfsigned.pem';\n\t\t\tSettings::Set('system.ssl_cert_file', $certFile);\n\t\t}\n\t\tif (empty($keyFile)) {\n\t\t\t$keyFile = '/etc/ssl/froxlor_selfsigned.key';\n\t\t\tSettings::Set('system.ssl_key_file', $keyFile);\n\t\t}\n\n\t\t// certificate info\n\t\t$dn = [\n\t\t\t\"countryName\" => \"DE\",\n\t\t\t\"stateOrProvinceName\" => \"Hessen\",\n\t\t\t\"localityName\" => \"Frankfurt am Main\",\n\t\t\t\"organizationName\" => \"froxlor\",\n\t\t\t\"organizationalUnitName\" => \"froxlor Server Management Panel\",\n\t\t\t\"commonName\" => Settings::Get('system.hostname'),\n\t\t\t\"emailAddress\" => Settings::Get('panel.adminmail')\n\t\t];\n\t\t// create private key\n\t\t$privkey = openssl_pkey_new([\n\t\t\t\"private_key_type\" => OPENSSL_KEYTYPE_EC,\n\t\t\t\"curve_name\" => 'prime256v1',\n\t\t]);\n\t\t// create signing request\n\t\t$csr = openssl_csr_new($dn, $privkey, array('digest_alg' => 'sha384'));\n\t\t// sign csr\n\t\t$x509 = openssl_csr_sign($csr, null, $privkey, 365, array('digest_alg' => 'sha384'));\n\t\t// export to files\n\t\topenssl_x509_export_to_file($x509, $certFile);\n\t\topenssl_pkey_export_to_file($privkey, $keyFile);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/System/IPTools.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\System;\n\nclass IPTools\n{\n\n\t/**\n\t * Converts CIDR to a netmask address\n\t *\n\t * @thx to https://stackoverflow.com/a/5711080/3020926\n\t * @param string $cidr\n\t *\n\t * @return string\n\t */\n\tpublic static function cidr2NetmaskAddr(string $cidr): string\n\t{\n\t\t$ta = substr($cidr, strpos($cidr, '/') + 1) * 1;\n\t\t$netmask = str_split(str_pad(str_pad('', $ta, '1'), 32, '0'), 8);\n\n\t\tforeach ($netmask as &$element) {\n\t\t\t$element = bindec($element);\n\t\t}\n\n\t\treturn implode('.', $netmask);\n\t}\n\n\t/**\n\t * Checks whether the given $ip is in range of given ip/cidr range\n\t *\n\t * @param array $ip_cidr 0 => ip, 1 => netmask in decimal, e.g. [0 => '123.123.123.123', 1 => 24]\n\t * @param string $ip ip-address to check\n\t *\n\t * @return bool\n\t */\n\tpublic static function ip_in_range(array $ip_cidr, string $ip): bool\n\t{\n\t\t$netip = $ip_cidr[0];\n\t\tif (self::is_ipv6($netip)) {\n\t\t\treturn self::ipv6_in_range($ip_cidr, $ip);\n\t\t}\n\t\t$netmask = $ip_cidr[1];\n\t\t$range_decimal = ip2long($netip);\n\t\t$ip_decimal = ip2long($ip);\n\t\t$wildcard_decimal = pow(2, (32 - $netmask)) - 1;\n\t\t$netmask_decimal = ~$wildcard_decimal;\n\t\treturn (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));\n\t}\n\n\t/**\n\t * Checks if an $address (IP) is IPv6\n\t *\n\t * @param string $address\n\t *\n\t * @return string|bool ip address on success, false on failure\n\t */\n\tpublic static function is_ipv6(string $address)\n\t{\n\t\treturn filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);\n\t}\n\n\t/**\n\t * Checks whether the given ipv6 $ip is in range of given ip/cidr range\n\t *\n\t * @param array $ip_cidr 0 => ip, 1 => netmask in decimal, e.g. [0 => '123:123::1', 1 => 64]\n\t * @param string $ip ip-address to check\n\t *\n\t * @return bool\n\t */\n\tprivate static function ipv6_in_range(array $ip_cidr, string $ip): bool\n\t{\n\t\t$in_range = false;\n\n\t\t$size = 128 - $ip_cidr[1];\n\t\tif ($size == 0) {\n\t\t\treturn inet_ntop(inet_pton($ip_cidr[0])) == inet_ntop(inet_pton($ip));\n\t\t}\n\t\t$addr = gmp_init('0x' . str_replace(':', '', self::inet6_expand($ip_cidr[0])));\n\t\t$mask = gmp_init('0x' . str_replace(':', '', self::inet6_expand(self::inet6_prefix_to_mask($ip_cidr[1]))));\n\t\t$prefix = gmp_and($addr, $mask);\n\t\t$start = gmp_strval(gmp_add($prefix, '0x1'), 16);\n\t\t$end = '0b';\n\t\tfor ($i = 0; $i < $size; $i++) {\n\t\t\t$end .= '1';\n\t\t}\n\t\t$end = gmp_strval(gmp_add($prefix, gmp_init($end)), 16);\n\t\t$start_result = '';\n\t\tfor ($i = 0; $i < 8; $i++) {\n\t\t\t$start_result .= substr($start, $i * 4, 4);\n\t\t\tif ($i != 7) {\n\t\t\t\t$start_result .= ':';\n\t\t\t}\n\t\t}\n\t\t$end_result = '';\n\t\tfor ($i = 0; $i < 8; $i++) {\n\t\t\t$end_result .= substr($end, $i * 4, 4);\n\t\t\tif ($i != 7) {\n\t\t\t\t$end_result .= ':';\n\t\t\t}\n\t\t}\n\n\t\t$first = self::ip2long6($start_result);\n\t\t$last = self::ip2long6($end_result);\n\t\t$ip = self::ip2long6($ip);\n\n\t\t$in_range = ($ip >= $first && $ip <= $last);\n\t\treturn $in_range;\n\t}\n\n\t/**\n\t * @param string $addr\n\t * @return false|string\n\t */\n\tprivate static function inet6_expand(string $addr)\n\t{\n\t\t// Check if there are segments missing, insert if necessary\n\t\tif (strpos($addr, '::') !== false) {\n\t\t\t$part = explode('::', $addr);\n\t\t\t$part[0] = explode(':', $part[0]);\n\t\t\t$part[1] = explode(':', $part[1]);\n\t\t\t$missing = [];\n\t\t\tfor ($i = 0; $i < (8 - (count($part[0]) + count($part[1]))); $i++) {\n\t\t\t\t$missing[] = '0000';\n\t\t\t}\n\t\t\t$missing = array_merge($part[0], $missing);\n\t\t\t$part = array_merge($missing, $part[1]);\n\t\t} else {\n\t\t\t$part = explode(\":\", $addr);\n\t\t}\n\t\t// Pad each segment until it has 4 digits\n\t\tforeach ($part as &$p) {\n\t\t\twhile (strlen($p) < 4) {\n\t\t\t\t$p = '0' . $p;\n\t\t\t}\n\t\t}\n\t\tunset($p);\n\t\t// Join segments\n\t\t$result = implode(':', $part);\n\t\t// Quick check to make sure the length is as expected\n\t\tif (strlen($result) == 39) {\n\t\t\treturn $result;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @param int $prefix\n\t * @return false|string\n\t */\n\tprivate static function inet6_prefix_to_mask(int $prefix)\n\t{\n\t\t/* Make sure the prefix is a number between 1 and 127 (inclusive) */\n\t\tif ($prefix < 0 || $prefix > 128) {\n\t\t\treturn false;\n\t\t}\n\t\t$mask = '0b';\n\t\t$mask .= str_repeat('1', $prefix);\n\t\tfor ($i = strlen($mask) - 2; $i < 128; $i++) {\n\t\t\t$mask .= '0';\n\t\t}\n\t\t$mask = gmp_strval(gmp_init($mask), 16);\n\t\t$result = '';\n\t\tfor ($i = 0; $i < 8; $i++) {\n\t\t\t$result .= substr($mask, $i * 4, 4);\n\t\t\tif ($i != 7) {\n\t\t\t\t$result .= ':';\n\t\t\t}\n\t\t} // for\n\t\treturn inet_ntop(inet_pton($result));\n\t}\n\n\t/**\n\t * @param string $ip\n\t * @return string\n\t */\n\tprivate static function ip2long6(string $ip): string\n\t{\n\t\t$ip_n = inet_pton($ip);\n\t\t$bits = 15; // 16 x 8 bit = 128bit\n\t\t$ipv6long = '';\n\t\twhile ($bits >= 0) {\n\t\t\t$bin = sprintf(\"%08b\", (ord($ip_n[$bits])));\n\t\t\t$ipv6long = $bin . $ipv6long;\n\t\t\t$bits--;\n\t\t}\n\t\treturn gmp_strval(gmp_init($ipv6long, 2), 10);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/System/Mailer.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\System;\n\nuse Froxlor\\Settings;\nuse PHPMailer\\PHPMailer\\Exception;\nuse PHPMailer\\PHPMailer\\PHPMailer;\n\nclass Mailer extends PHPMailer\n{\n\n\t/**\n\t * class constructor\n\t *\n\t * @param bool $exceptions whether to throw exceptions or not\n\t *\n\t * @throws Exception\n\t */\n\tpublic function __construct(bool $exceptions = false)\n\t{\n\t\tparent::__construct($exceptions);\n\t\t$this->CharSet = \"UTF-8\";\n\n\t\tif (Settings::Get('system.mail_use_smtp')) {\n\t\t\t$this->isSMTP();\n\t\t\t$this->Host = Settings::Get('system.mail_smtp_host');\n\t\t\t$this->SMTPAuth = Settings::Get('system.mail_smtp_auth') == '1';\n\t\t\t$this->Username = Settings::Get('system.mail_smtp_user');\n\t\t\t$this->Password = Settings::Get('system.mail_smtp_passwd');\n\t\t\tif (Settings::Get('system.mail_smtp_usetls')) {\n\t\t\t\t$this->SMTPSecure = 'tls';\n\t\t\t} else {\n\t\t\t\t$this->SMTPAutoTLS = false;\n\t\t\t}\n\t\t\t$this->Port = Settings::Get('system.mail_smtp_port');\n\t\t}\n\n\t\t/**\n\t\t * use froxlor's email-validation\n\t\t */\n\t\tself::$validator = [\n\t\t\t'\\Froxlor\\\\Validate\\\\Validate',\n\t\t\t'validateEmail'\n\t\t];\n\n\t\tif (self::ValidateAddress(Settings::Get('panel.adminmail')) !== false) {\n\t\t\t// set return-to address and custom sender-name, see #76\n\t\t\t$this->setFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));\n\t\t\tif (Settings::Get('panel.adminmail_return') != '') {\n\t\t\t\t$this->addReplyTo(Settings::Get('panel.adminmail_return'), Settings::Get('panel.adminmail_defname'));\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/System/Markdown.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\System;\n\nuse League\\CommonMark\\Exception\\CommonMarkException;\nuse League\\CommonMark\\GithubFlavoredMarkdownConverter;\n\nclass Markdown\n{\n\n\tprivate static $converter = null;\n\n\tpublic static function converter(): ?GithubFlavoredMarkdownConverter\n\t{\n\t\tif (is_null(self::$converter)) {\n\t\t\tself::$converter = new GithubFlavoredMarkdownConverter([\n\t\t\t\t'html_input' => 'strip',\n\t\t\t\t'allow_unsafe_links' => false,\n\t\t\t]);\n\t\t}\n\t\treturn self::$converter;\n\t}\n\n\tpublic static function cleanCustomNotes(string $note = \"\"): string\n\t{\n\t\tif (!empty($note)) {\n\t\t\ttry {\n\t\t\t\t$note = self::converter()->convert($note)->getContent();\n\t\t\t} catch (CommonMarkException $e) {\n\t\t\t\t$note = \"\";\n\t\t\t}\n\t\t}\n\t\treturn $note;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/System/MysqlHandler.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\System;\n\nuse Froxlor\\Database\\Database;\nuse Monolog\\Handler\\AbstractProcessingHandler;\nuse Monolog\\Logger;\n\nclass MysqlHandler extends AbstractProcessingHandler\n{\n\n\tprotected static array $froxlorLevels = [\n\t\tLogger::DEBUG => LOG_DEBUG,\n\t\tLogger::INFO => LOG_INFO,\n\t\tLogger::NOTICE => LOG_NOTICE,\n\t\tLogger::WARNING => LOG_WARNING,\n\t\tLogger::ERROR => LOG_ERR,\n\t\tLogger::CRITICAL => LOG_ERR,\n\t\tLogger::ALERT => LOG_ERR,\n\t\tLogger::EMERGENCY => LOG_ERR\n\t];\n\tprotected $pdoStatement = null;\n\n\t/**\n\t * Constructor\n\t *\n\t * @param bool|int $level Debug level which this handler should store\n\t * @param bool $bubble\n\t */\n\tpublic function __construct($level = Logger::DEBUG, bool $bubble = true)\n\t{\n\t\tparent::__construct($level, $bubble);\n\t}\n\n\t/**\n\t * Writes the record down to the log\n\t *\n\t * @param array $record\n\t * @return void\n\t */\n\tprotected function write(array $record)\n\t{\n\t\t$this->insert([\n\t\t\t':message' => $record['message'],\n\t\t\t':contextUser' => ($record['context']['user'] ?? 'unknown'),\n\t\t\t':contextAction' => ($record['context']['action'] ?? '0'),\n\t\t\t':level' => self::$froxlorLevels[$record['level']],\n\t\t\t':datetime' => $record['datetime']->format('U')\n\t\t]);\n\t}\n\n\t/**\n\t * Insert the data to the logger table\n\t *\n\t * @param array $data\n\t * @return bool\n\t */\n\tprotected function insert(array $data): bool\n\t{\n\t\tif ($this->pdoStatement === null) {\n\t\t\t$sql = \"INSERT INTO `panel_syslog` SET `text` = :message, `user` = :contextUser, `action` = :contextAction, `type` = :level, `date` = :datetime\";\n\t\t\t$this->pdoStatement = Database::prepare($sql);\n\t\t}\n\t\treturn $this->pdoStatement->execute($data);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/System/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Traffic/Traffic.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Traffic;\n\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Traffic as TrafficAPI;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\UI\\Collection;\n\nclass Traffic\n{\n\t/**\n\t * @param array $userinfo\n\t * @param ?string $range\n\t * @return array\n\t * @throws \\Exception\n\t */\n\tpublic static function getCustomerStats(array $userinfo, string $range = null, bool $overview = false): array\n\t{\n\t\t$trafficCollectionObj = (new Collection(TrafficAPI::class, $userinfo,\n\t\t\tself::getParamsByRange($range, ['customer_traffic' => true])));\n\t\tif (($userinfo['adminsession'] ?? 0) == 1) {\n\t\t\t$trafficCollectionObj->has('customer', Customers::class, 'customerid', 'customerid');\n\t\t}\n\t\t$trafficCollection = $trafficCollectionObj->get();\n\n\t\t// build stats for each user\n\t\t$users = [];\n\t\t$years = [];\n\t\t$months = [];\n\t\t$days = [];\n\t\tforeach ($trafficCollection['data']['list'] as $item) {\n\t\t\t$http = $item['http'];\n\t\t\t$ftp = ($item['ftp_up'] + $item['ftp_down']);\n\t\t\t$mail = $item['mail'];\n\t\t\t$total = $http + $ftp + $mail;\n\n\t\t\tif (empty($users[$item['customerid']])) {\n\t\t\t\t$users[$item['customerid']] = [\n\t\t\t\t\t'total' => 0.00,\n\t\t\t\t\t'http' => 0.00,\n\t\t\t\t\t'ftp' => 0.00,\n\t\t\t\t\t'mail' => 0.00,\n\t\t\t\t];\n\t\t\t}\n\n\t\t\t// per user total\n\t\t\tif (($userinfo['adminsession'] ?? 0) == 1) {\n\t\t\t\t$users[$item['customerid']]['loginname'] = $item['customer']['loginname'];\n\t\t\t}\n\t\t\t$users[$item['customerid']]['total'] += $total;\n\t\t\t$users[$item['customerid']]['http'] += $http;\n\t\t\t$users[$item['customerid']]['ftp'] += $ftp;\n\t\t\t$users[$item['customerid']]['mail'] += $mail;\n\t\t\tif (!$overview) {\n\t\t\t\tif (empty($years[$item['year']])) {\n\t\t\t\t\t$years[$item['year']] = [\n\t\t\t\t\t\t'total' => 0.00,\n\t\t\t\t\t\t'http' => 0.00,\n\t\t\t\t\t\t'ftp' => 0.00,\n\t\t\t\t\t\t'mail' => 0.00,\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tif (empty($months[$item['month'] . '/' . $item['year']])) {\n\t\t\t\t\t$months[$item['month'] . '/' . $item['year']] = [\n\t\t\t\t\t\t'total' => 0.00,\n\t\t\t\t\t\t'http' => 0.00,\n\t\t\t\t\t\t'ftp' => 0.00,\n\t\t\t\t\t\t'mail' => 0.00,\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tif (empty($days[$item['day'] . '.' . $item['month'] . '.' . $item['year']])) {\n\t\t\t\t\t$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']] = [\n\t\t\t\t\t\t'total' => 0.00,\n\t\t\t\t\t\t'http' => 0.00,\n\t\t\t\t\t\t'ftp' => 0.00,\n\t\t\t\t\t\t'mail' => 0.00,\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\t// per year\n\t\t\t\t$years[$item['year']]['total'] += $total;\n\t\t\t\t$years[$item['year']]['http'] += $http;\n\t\t\t\t$years[$item['year']]['ftp'] += $ftp;\n\t\t\t\t$years[$item['year']]['mail'] += $mail;\n\t\t\t\t// per month\n\t\t\t\t$months[$item['month'] . '/' . $item['year']]['total'] += $total;\n\t\t\t\t$months[$item['month'] . '/' . $item['year']]['http'] += $http;\n\t\t\t\t$months[$item['month'] . '/' . $item['year']]['ftp'] += $ftp;\n\t\t\t\t$months[$item['month'] . '/' . $item['year']]['mail'] += $mail;\n\t\t\t\t// per day\n\t\t\t\t$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']]['total'] += $total;\n\t\t\t\t$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']]['http'] += $http;\n\t\t\t\t$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']]['ftp'] += $ftp;\n\t\t\t\t$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']]['mail'] += $mail;\n\t\t\t}\n\t\t}\n\n\t\t// calculate overview for given range from users\n\t\t$metrics = [\n\t\t\t'total' => 0.00,\n\t\t\t'http' => 0.00,\n\t\t\t'ftp' => 0.00,\n\t\t\t'mail' => 0.00,\n\t\t];\n\t\tforeach ($users as $user) {\n\t\t\t$metrics['total'] += $user['total'];\n\t\t\t$metrics['http'] += $user['http'];\n\t\t\t$metrics['ftp'] += $user['ftp'];\n\t\t\t$metrics['mail'] += $user['mail'];\n\t\t}\n\n\t\t$years_avail = [];\n\t\tif (!$overview) {\n\t\t\t// get all possible years for filter\n\t\t\t$sel_stmt = Database::prepare(\"SELECT DISTINCT year FROM `\" . TABLE_PANEL_TRAFFIC . \"` WHERE 1 ORDER BY `year` DESC\");\n\t\t\tDatabase::pexecute($sel_stmt);\n\t\t\t$years_avail = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\t}\n\n\t\t// sort users by total traffic\n\t\tuasort($users, function ($user_a, $user_b) {\n\t\t\tif ($user_a['total'] == $user_b['total']) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn ($user_a['total'] < $user_b['total']) ? 1 : -1;\n\t\t});\n\n\t\treturn [\n\t\t\t'metrics' => $metrics,\n\t\t\t'users' => $users,\n\t\t\t'years' => $years,\n\t\t\t'months' => $months,\n\t\t\t'days' => $days,\n\t\t\t'range' => $range,\n\t\t\t'years_avail' => $years_avail\n\t\t];\n\t}\n\n\t/**\n\t * @param ?string $range\n\t * @param array $params\n\t * @return array\n\t * @throws \\Exception\n\t */\n\tprivate static function getParamsByRange(string $range = null, array $params = []): array\n\t{\n\t\t$dateParams = [];\n\n\t\tif (preg_match(\"/year:([0-9]{4})/\", $range, $matches)) {\n\t\t\t$dateParams = ['year' => $matches[1]];\n\t\t} elseif (preg_match(\"/months:([1-9]([0-9]+)?)/\", $range, $matches)) {\n\t\t\t$dt = (new \\DateTime())->sub(new \\DateInterval('P' . $matches[1] . 'M'))->format('U');\n\t\t\t$dateParams = ['date_from' => $dt];\n\t\t} elseif (preg_match(\"/days:([1-9]([0-9]+)?)/\", $range, $matches)) {\n\t\t\t$dt = (new \\DateTime())->sub(new \\DateInterval('P' . $matches[1] . 'D'))->format('U');\n\t\t\t$dateParams = ['date_from' => $dt];\n\t\t} elseif (preg_match(\"/hours:([1-9]([0-9]+)?)/\", $range, $matches)) {\n\t\t\t$dt = (new \\DateTime())->sub(new \\DateInterval('PT' . $matches[1] . 'H'))->format('U');\n\t\t\t$dateParams = ['date_from' => $dt];\n\t\t} elseif (preg_match(\"/currentmonth/\", $range, $matches)) {\n\t\t\t$dt = (new \\DateTime(\"first day of this month\"))->setTime(0, 0, 0, 1)->format('U');\n\t\t\t$dateParams = ['date_from' => $dt];\n\t\t} elseif (preg_match(\"/currentyear/\", $range, $matches)) {\n\t\t\t$dt = \\DateTime::createFromFormat(\"d.m.Y\", '01.01.' . date('Y'))->setTime(0, 0, 0, 1)->format('U');\n\t\t\t$dateParams = ['date_from' => $dt];\n\t\t}\n\n\t\treturn array_merge($dateParams, $params);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Traffic/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Admin.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\UI\\Panel\\UI;\n\nclass Admin\n{\n\tpublic static function canChangeServerSettings(array $attributes)\n\t{\n\t\treturn (bool)UI::getCurrentUser()['change_serversettings'];\n\t}\n\n\tpublic static function isNotMe(array $attributes)\n\t{\n\t\treturn (UI::getCurrentUser()['adminid'] != $attributes['fields']['adminid']);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Customer.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Markdown;\n\nclass Customer\n{\n\tpublic static function isLocked(array $attributes): bool\n\t{\n\t\treturn $attributes['fields']['loginfail_count'] >= Settings::Get('login.maxloginattempts')\n\t\t\t&& $attributes['fields']['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'));\n\t}\n\n\tpublic static function hasNote(array $attributes): bool\n\t{\n\t\t$cleanNote = Markdown::cleanCustomNotes($attributes['fields']['custom_notes'] ?? \"\");\n\t\treturn !empty($cleanNote);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Dns.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nclass Dns\n{\n\tpublic static function prio(array $attributes): string\n\t{\n\t\treturn ($attributes['fields']['prio'] <= 0\n\t\t\t&& $attributes['fields']['type'] != 'MX'\n\t\t\t&& $attributes['fields']['type'] != 'SRV') ? '' : $attributes['data'];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Domain.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Domain\\Domain as DDomain;\nuse Froxlor\\FileDir;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\n\nclass Domain\n{\n\tpublic static function domainEditLink(array $attributes): array\n\t{\n\t\t$linker = UI::getLinker();\n\t\treturn [\n\t\t\t'macro' => 'link',\n\t\t\t'data' => [\n\t\t\t\t'text' => $attributes['data'],\n\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domains',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $attributes['fields']['id'],\n\t\t\t\t]),\n\t\t\t\t'target' => '_blank'\n\t\t\t]\n\t\t];\n\t}\n\n\tpublic static function domainWithCustomerLink(array $attributes): string\n\t{\n\t\t$linker = UI::getLinker();\n\t\t$result = '<a href=\"https://' . $attributes['data'] . '\" target=\"_blank\">' . $attributes['data'] . '</a>';\n\t\tif ((int)UI::getCurrentUser()['adminsession'] == 1 && $attributes['fields']['customerid']) {\n\t\t\t$result .= ' (<a href=\"' . $linker->getLink([\n\t\t\t\t\t'section' => 'customers',\n\t\t\t\t\t'page' => 'customers',\n\t\t\t\t\t'action' => 'su',\n\t\t\t\t\t'sort' => $attributes['fields']['loginname'],\n\t\t\t\t\t'id' => $attributes['fields']['customerid'],\n\t\t\t\t]) . '\">' . $attributes['fields']['loginname'] . '</a>)';\n\t\t}\n\t\treturn $result;\n\t}\n\n\tpublic static function domainTarget(array $attributes)\n\t{\n\t\tif (empty($attributes['fields']['aliasdomain'])) {\n\t\t\tif ($attributes['fields']['deactivated']) {\n\t\t\t\treturn lng('admin.deactivated');\n\t\t\t}\n\t\t\tif ($attributes['fields']['email_only']) {\n\t\t\t\treturn lng('domains.email_only');\n\t\t\t}\n\t\t\t// path or redirect\n\t\t\tif (preg_match('/^https?\\:\\/\\//', $attributes['fields']['documentroot'])) {\n\t\t\t\treturn [\n\t\t\t\t\t'macro' => 'link',\n\t\t\t\t\t'data' => [\n\t\t\t\t\t\t'text' => $attributes['fields']['documentroot'],\n\t\t\t\t\t\t'href' => $attributes['fields']['documentroot'],\n\t\t\t\t\t\t'target' => '_blank'\n\t\t\t\t\t]\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t// show docroot nicely\n\t\t\t\tif (strpos($attributes['fields']['documentroot'], UI::getCurrentUser()['documentroot']) === 0) {\n\t\t\t\t\t$attributes['fields']['documentroot'] = FileDir::makeCorrectDir(str_replace(UI::getCurrentUser()['documentroot'], \"/\", $attributes['fields']['documentroot']));\n\t\t\t\t}\n\t\t\t\treturn $attributes['fields']['documentroot'];\n\t\t\t}\n\t\t}\n\t\treturn lng('domains.aliasdomain') . ' ' . $attributes['fields']['aliasdomain'];\n\t}\n\n\tpublic static function domainExternalLinkInfo(array $attributes): string\n\t{\n\t\t$result = '';\n\t\tif ($attributes['fields']['parentdomainid'] != 0) {\n\t\t\t$result = '<i class=\"fa-solid fa-turn-up me-2 fa-rotate-90 opacity-50\"></i>';\n\t\t}\n\t\t$result .= '<a href=\"http://' . $attributes['data'] . '\" target=\"_blank\">' . $attributes['data'] . '</a>';\n\t\t// check for statistics if parentdomainid==0 to show stats-link for customers\n\t\tif ((int)UI::getCurrentUser()['adminsession'] == 0\n\t\t\t&& $attributes['fields']['parentdomainid'] == 0\n\t\t\t&& $attributes['fields']['deactivated'] == 0\n\t\t\t&& !$attributes['fields']['email_only']\n\t\t\t&& preg_match('/^https?:\\/\\/(.*)/i', $attributes['fields']['documentroot']) == false\n\t\t) {\n\t\t\t$statsapp = Settings::Get('system.traffictool');\n\t\t\t$result .= ' <a href=\"http://' . $attributes['data'] . '/' . $statsapp . '\" rel=\"external\" target=\"_blank\" title=\"' . lng('domains.statstics') . '\"><i class=\"fa-solid fa-chart-line text-secondary\"></i></a>';\n\t\t}\n\t\tif ($attributes['fields']['registration_date'] != '') {\n\t\t\t$result .= '<br><small>' . lng('domains.registration_date') . ': ' . $attributes['fields']['registration_date'] . '</small>';\n\t\t}\n\t\tif ($attributes['fields']['termination_date'] != '') {\n\t\t\t$result .= '<br><small>' . lng('domains.termination_date_overview') . ': ' . $attributes['fields']['termination_date'] . '</small>';\n\t\t}\n\t\treturn $result;\n\t}\n\n\tpublic static function canEdit(array $attributes): bool\n\t{\n\t\treturn $attributes['fields']['caneditdomain'] && !$attributes['fields']['deactivated'];\n\t}\n\n\tpublic static function canViewLogs(array $attributes): bool\n\t{\n\t\tif ((int)$attributes['fields']['email_only'] == 0 && !$attributes['fields']['deactivated']) {\n\t\t\tif ((int)UI::getCurrentUser()['adminsession'] == 0 && (bool)UI::getCurrentUser()['logviewenabled']) {\n\t\t\t\treturn true;\n\t\t\t} elseif ((int)UI::getCurrentUser()['adminsession'] == 1) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static function canDelete(array $attributes): bool\n\t{\n\t\treturn $attributes['fields']['parentdomainid'] != '0'\n\t\t\t&& empty($attributes['fields']['domainaliasid']);\n\t}\n\n\tpublic static function adminCanDelete(array $attributes): bool\n\t{\n\t\treturn $attributes['fields']['id'] != Settings::Get('system.hostname_id')\n\t\t\t&& empty($attributes['fields']['domainaliasid'])\n\t\t\t&& $attributes['fields']['standardsubdomain'] != $attributes['fields']['id'];\n\t}\n\n\tpublic static function canEditDNS(array $attributes): bool\n\t{\n\t\treturn $attributes['fields']['isbinddomain'] == '1'\n\t\t\t&& UI::getCurrentUser()['dnsenabled'] == '1'\n\t\t\t&& $attributes['fields']['caneditdomain'] == '1'\n\t\t\t&& Settings::Get('system.bind_enable') == '1'\n\t\t\t&& Settings::Get('system.dnsenabled') == '1'\n\t\t\t&& !$attributes['fields']['deactivated'];\n\t}\n\n\tpublic static function adminCanEditDNS(array $attributes): bool\n\t{\n\t\treturn $attributes['fields']['isbinddomain'] == '1'\n\t\t\t&& Settings::Get('system.bind_enable') == '1'\n\t\t\t&& Settings::Get('system.dnsenabled') == '1';\n\t}\n\n\tpublic static function hasLetsEncryptActivated(array $attributes): bool\n\t{\n\t\treturn ((bool)$attributes['fields']['letsencrypt'] && (int)$attributes['fields']['email_only'] == 0);\n\t}\n\n\t/**\n\t * @throws \\Exception\n\t */\n\tpublic static function canEditSSL(array $attributes): bool\n\t{\n\t\tif (Settings::Get('system.use_ssl') == '1'\n\t\t\t&& DDomain::domainHasSslIpPort($attributes['fields']['id'])\n\t\t\t&& (CurrentUser::isAdmin() || (!CurrentUser::isAdmin() && (int)$attributes['fields']['caneditdomain'] == 1))\n\t\t\t&& (int)$attributes['fields']['letsencrypt'] == 0\n\t\t\t&& !(int)$attributes['fields']['email_only']\n\t\t\t&& !$attributes['fields']['deactivated']\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static function canEditAlias(array $attributes): bool\n\t{\n\t\treturn !empty($attributes['fields']['domainaliasid']);\n\t}\n\n\tpublic static function isAssigned(array $attributes): bool\n\t{\n\t\treturn ($attributes['fields']['parentdomainid'] == 0 && empty($attributes['fields']['domainaliasid']));\n\t}\n\n\tpublic static function editSSLButtons(array $attributes): array\n\t{\n\t\t$result = [\n\t\t\t'icon' => 'fa-solid fa-shield',\n\t\t\t'title' => lng('panel.ssleditor'),\n\t\t\t'href' => [\n\t\t\t\t'section' => 'domains',\n\t\t\t\t'page' => 'domainssleditor',\n\t\t\t\t'action' => 'view',\n\t\t\t\t'id' => ':id'\n\t\t\t],\n\t\t];\n\n\t\tif ($attributes['fields']['domain_hascert'] == 1) {\n\t\t\t// specified certificate for domain\n\t\t\t$result['icon'] .= ' text-success';\n\t\t} elseif ($attributes['fields']['domain_hascert'] == 2) {\n\t\t\t// shared certificates (e.g. subdomain of domain where certificate is specified)\n\t\t\t$result['icon'] .= ' text-warning';\n\t\t\t$result['title'] .= \"\\n\" . lng('panel.ssleditor_infoshared');\n\t\t} elseif ($attributes['fields']['domain_hascert'] == 0) {\n\t\t\t// no certificate specified, using global fallbacks (IPs and Ports or if empty SSL settings)\n\t\t\t$result['icon'] .= ' text-danger';\n\t\t\t$result['title'] .= \"\\n\" . lng('panel.ssleditor_infoglobal');\n\t\t}\n\n\t\t$result['visible'] = [Domain::class, 'canEditSSL'];\n\n\t\treturn $result;\n\t}\n\n\tpublic static function listIPs(array $attributes): string\n\t{\n\t\tif (!empty($attributes['fields']['ipsandports'])) {\n\t\t\t$iplist = \"\";\n\t\t\tforeach ($attributes['fields']['ipsandports'] as $ipport) {\n\t\t\t\t$iplist .= $ipport['ip'] . ':' . $ipport['port'] . '<br>';\n\t\t\t}\n\t\t\treturn $iplist;\n\t\t}\n\t\treturn lng('panel.listing_empty');\n\t}\n\n\t/**\n\t * @throws \\Exception\n\t */\n\tpublic static function getPhpConfigName(array $attributes): string\n\t{\n\t\t$sel_stmt = Database::prepare(\"SELECT `description` FROM `\" . TABLE_PANEL_PHPCONFIGS . \"` WHERE `id` = :id\");\n\t\t$phpconfig = Database::pexecute_first($sel_stmt, ['id' => $attributes['data']]);\n\t\tif ((int)UI::getCurrentUser()['adminsession'] == 1) {\n\t\t\t$linker = UI::getLinker();\n\t\t\t$result = '<a href=\"' . $linker->getLink([\n\t\t\t\t\t'section' => 'phpsettings',\n\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t'searchfield' => 'c.id',\n\t\t\t\t\t'searchtext' => $attributes['data'],\n\t\t\t\t]) . '\">' . $phpconfig['description'] . '</a>';\n\t\t} else {\n\t\t\t$result = $phpconfig['description'];\n\t\t}\n\t\treturn $result;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Email.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\n\nclass Email\n{\n\tpublic static function account(array $attributes)\n\t{\n\t\treturn [\n\t\t\t'macro' => 'booleanWithInfo',\n\t\t\t'data' => [\n\t\t\t\t'checked' => $attributes['data'] != 0,\n\t\t\t\t'info' => $attributes['data'] != 0 ? PhpHelper::sizeReadable($attributes['fields']['mboxsize'], 'GiB', 'bi', '%01.' . (int)Settings::Get('panel.decimal_places') . 'f %s') : ''\n\t\t\t]\n\t\t];\n\t}\n\n\tpublic static function forwarderList(array $attributes)\n\t{\n\t\t$forwarders = explode(\" \", $attributes['data']);\n\t\tif (($key = array_search($attributes['fields']['email_full'], $forwarders)) !== false) {\n\t\t\tunset($forwarders[$key]);\n\t\t}\n\t\tif (count($forwarders) > 0) {\n\t\t\treturn implode(\"<br>\", $forwarders);\n\t\t}\n\t\treturn \"\";\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Ftp.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\FileDir;\nuse Froxlor\\UI\\Panel\\UI;\n\nclass Ftp\n{\n\tpublic static function pathRelative(array $attributes): string\n\t{\n\t\tif (strpos($attributes['data'], UI::getCurrentUser()['documentroot']) === 0) {\n\t\t\t$attributes['data'] = str_replace(UI::getCurrentUser()['documentroot'], \"/\", $attributes['data']);\n\t\t}\n\t\t$attributes['data'] = FileDir::makeCorrectDir($attributes['data']);\n\n\t\treturn $attributes['data'];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Impersonate.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\UI\\Panel\\UI;\n\nclass Impersonate\n{\n\tpublic static function apiAdminCustomerLink(array $attributes)\n\t{\n\t\t// my own key\n\t\t$isMyKey = false;\n\t\tif ($attributes['fields']['adminid'] == UI::getCurrentUser()['adminid']\n\t\t\t&& ((AREA == 'admin' && $attributes['fields']['customerid'] == 0)\n\t\t\t\t|| (AREA == 'customer' && $attributes['fields']['customerid'] == UI::getCurrentUser()['customerid'])\n\t\t\t)\n\t\t) {\n\t\t\t// this is mine\n\t\t\t$isMyKey = true;\n\t\t}\n\n\t\t$adminCustomerLink = \"\";\n\t\tif (AREA == 'admin') {\n\t\t\tif ($isMyKey) {\n\t\t\t\t$adminCustomerLink = $attributes['fields']['adminname'];\n\t\t\t} else {\n\t\t\t\tif (empty($attributes['fields']['customerid'])) {\n\t\t\t\t\t$adminCustomerLink = self::admin($attributes);\n\t\t\t\t} else {\n\t\t\t\t\t$attributes['data'] = $attributes['fields']['loginname'];\n\t\t\t\t\t$adminCustomerLink = self::customer($attributes);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// customer do not need links\n\t\t\t$adminCustomerLink = $attributes['fields']['loginname'];\n\t\t}\n\n\t\treturn $adminCustomerLink;\n\t}\n\n\tpublic static function admin(array $attributes)\n\t{\n\t\tif (UI::getCurrentUser()['adminid'] != $attributes['fields']['adminid']) {\n\t\t\t$linker = UI::getLinker();\n\t\t\treturn [\n\t\t\t\t'macro' => 'link',\n\t\t\t\t'data' => [\n\t\t\t\t\t'text' => $attributes['data'],\n\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t'section' => 'admins',\n\t\t\t\t\t\t'page' => 'admins',\n\t\t\t\t\t\t'action' => 'su',\n\t\t\t\t\t\t'id' => $attributes['fields']['adminid'],\n\t\t\t\t\t]),\n\t\t\t\t]\n\t\t\t];\n\t\t}\n\t\treturn $attributes['data'];\n\t}\n\n\tpublic static function customer(array $attributes): array\n\t{\n\t\t$linker = UI::getLinker();\n\t\treturn [\n\t\t\t'macro' => 'link',\n\t\t\t'data' => [\n\t\t\t\t'text' => $attributes['data'],\n\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t'section' => 'customers',\n\t\t\t\t\t'page' => 'customers',\n\t\t\t\t\t'action' => 'su',\n\t\t\t\t\t'sort' => $attributes['fields']['loginname'],\n\t\t\t\t\t'id' => $attributes['fields']['customerid'],\n\t\t\t\t]),\n\t\t\t]\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Mysql.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\Database\\Database;\n\nclass Mysql\n{\n\tpublic static function dbserver(array $attributes): string\n\t{\n\t\t// get sql-root access data\n\t\tDatabase::needRoot(true, (int)$attributes['data'], false);\n\t\tDatabase::needSqlData();\n\t\t$sql_root = Database::getSqlData();\n\t\tDatabase::needRoot(false);\n\n\t\treturn $sql_root['caption'] . '<br><small>' . $sql_root['host'] . '</small>';\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/PHPConf.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\Settings;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\UI\\Panel\\UI;\n\nclass PHPConf\n{\n\tpublic static function domainList(array $attributes): string\n\t{\n\t\t$idna = new IdnaWrapper;\n\t\t$domains = \"\";\n\t\t$subdomains_count = count($attributes['fields']['subdomains']);\n\t\tforeach ($attributes['fields']['domains'] as $configdomain) {\n\t\t\t$domains .= $idna->decode($configdomain) . \"<br>\";\n\t\t}\n\t\tif ($subdomains_count == 0 && empty($domains)) {\n\t\t\t$domains = lng('admin.phpsettings.notused');\n\t\t} else {\n\t\t\tif (Settings::Get('panel.phpconfigs_hidesubdomains') == '1') {\n\t\t\t\t$domains .= !empty($subdomains_count) ? ((!empty($domains) ? '+ ' : '') . $subdomains_count . ' ' . lng('customer.subdomains')) : '';\n\t\t\t} else {\n\t\t\t\tforeach ($attributes['fields']['subdomains'] as $configdomain) {\n\t\t\t\t\t$domains .= $idna->decode($configdomain) . \"<br>\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $domains;\n\t}\n\n\tpublic static function configsList(array $attributes)\n\t{\n\t\t$configs = \"\";\n\t\tforeach ($attributes['fields']['configs'] as $configused) {\n\t\t\t$configs .= $configused . \"<br>\";\n\t\t}\n\t\treturn $configs;\n\t}\n\n\tpublic static function isNotDefault(array $attributes)\n\t{\n\t\tif (UI::getCurrentUser()['change_serversettings']) {\n\t\t\treturn $attributes['fields']['id'] != 1;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static function fpmConfLink(array $attributes)\n\t{\n\t\tif (UI::getCurrentUser()['change_serversettings']) {\n\t\t\t$linker = UI::getLinker();\n\t\t\treturn [\n\t\t\t\t'macro' => 'link',\n\t\t\t\t'data' => [\n\t\t\t\t\t'text' => $attributes['data'],\n\t\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t\t'section' => 'phpsettings',\n\t\t\t\t\t\t'page' => 'fpmdaemons',\n\t\t\t\t\t\t'searchfield' => 'id',\n\t\t\t\t\t\t'searchtext' => $attributes['fields']['fpmsettingid'],\n\t\t\t\t\t]),\n\t\t\t\t]\n\t\t\t];\n\t\t}\n\t\treturn $attributes['data'];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/ProgressBar.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Exception;\nuse Froxlor\\Traffic\\Traffic;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Response;\n\nclass ProgressBar\n{\n\t/**\n\t * get progressbar data for used diskspace\n\t *\n\t * @param array $attributes\n\t * @return array\n\t */\n\tpublic static function diskspace(array $attributes): array\n\t{\n\t\t$infotext = null;\n\t\tif (isset($attributes['fields']['webspace_used']) && isset($attributes['fields']['mailspace_used']) && isset($attributes['fields']['dbspace_used'])) {\n\t\t\t$infotext = lng('panel.used') . ':' . PHP_EOL;\n\t\t\t$infotext .= 'web: ' . PhpHelper::sizeReadable($attributes['fields']['webspace_used'] * 1024, null, 'bi') . PHP_EOL;\n\t\t\t$infotext .= 'mail: ' . PhpHelper::sizeReadable($attributes['fields']['mailspace_used'] * 1024, null, 'bi') . PHP_EOL;\n\t\t\t$infotext .= 'mysql: ' . PhpHelper::sizeReadable($attributes['fields']['dbspace_used'] * 1024, null, 'bi');\n\t\t}\n\n\t\treturn self::pbData('diskspace', $attributes['fields'], 1024, (int)Settings::Get('system.report_webmax'), $infotext);\n\t}\n\n\t/**\n\t * do needed calculations\n\t */\n\tprivate static function pbData(string $field, array $attributes, int $size_factor = 1024, int $report_max = 90, $infotext = null): array\n\t{\n\t\t$percent = 0;\n\t\t$style = 'bg-primary';\n\t\t$text = PhpHelper::sizeReadable($attributes[$field . '_used'] * $size_factor, null, 'bi') . ' / ' . lng('panel.unlimited');\n\t\tif ((int)$attributes[$field] >= 0) {\n\t\t\tif (($attributes[$field] / 100) * $report_max < $attributes[$field . '_used']) {\n\t\t\t\t$style = 'bg-danger';\n\t\t\t} elseif (($attributes[$field] / 100) * ($report_max - 15) < $attributes[$field . '_used']) {\n\t\t\t\t$style = 'bg-warning';\n\t\t\t}\n\t\t\t$percent = round(($attributes[$field . '_used'] * 100) / ($attributes[$field] == 0 ? 1 : $attributes[$field]), 0);\n\t\t\tif ($percent > 100) {\n\t\t\t\t$percent = 100;\n\t\t\t}\n\t\t\t$text = PhpHelper::sizeReadable($attributes[$field . '_used'] * $size_factor, null, 'bi') . ' / ' . PhpHelper::sizeReadable($attributes[$field] * $size_factor, null, 'bi');\n\t\t}\n\n\t\treturn [\n\t\t\t'macro' => 'progressbar',\n\t\t\t'data' => [\n\t\t\t\t'percent' => $percent,\n\t\t\t\t'style' => $style,\n\t\t\t\t'text' => $text,\n\t\t\t\t'infotext' => $infotext\n\t\t\t]\n\t\t];\n\t}\n\n\t/**\n\t * get progressbar data for traffic\n\t *\n\t * @param array $attributes ['fields']\n\t * @return array\n\t */\n\tpublic static function traffic(array $attributes): array\n\t{\n\t\t$skip_customer_traffic = false;\n\t\ttry {\n\t\t\t$attributes['fields']['deactivated'] = 0;\n\t\t\t$result = Traffic::getCustomerStats($attributes['fields'], 'currentmonth', true);\n\t\t} catch (Exception $e) {\n\t\t\tif ($e->getCode() === 405) {\n\t\t\t\t$skip_customer_traffic = true;\n\t\t\t} else {\n\t\t\t\tResponse::dynamicError($e->getMessage());\n\t\t\t}\n\t\t}\n\t\t$infotext = null;\n\t\tif (isset($result['metrics']['http']) && !$skip_customer_traffic) {\n\t\t\t$infotext = lng('panel.used') . ':' . PHP_EOL;\n\t\t\t$infotext .= 'http: ' . PhpHelper::sizeReadable($result['metrics']['http'], null, 'bi') . PHP_EOL;\n\t\t\t$infotext .= 'ftp: ' . PhpHelper::sizeReadable($result['metrics']['ftp'], null, 'bi') . PHP_EOL;\n\t\t\t$infotext .= 'mail: ' . PhpHelper::sizeReadable($result['metrics']['mail'], null, 'bi');\n\t\t}\n\t\treturn self::pbData('traffic', $attributes['fields'], 1024, (int)Settings::Get('system.report_trafficmax'), $infotext);\n\t}\n\n\t/**\n\t * get progressbar data for traffic for the admin overview\n\t * (key is to set 'adminsession' for the admin-users so the traffic-API selects\n\t * the correct customer data for the corresponsing admin/reseller)\n\t *\n\t * @param array $attributes ['fields']\n\t * @return array\n\t */\n\tpublic static function traffic_admins(array $attributes): array\n\t{\n\t\t$attributes['fields']['adminsession'] = 1;\n\t\treturn self::traffic($attributes);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/SSLCertificate.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nclass SSLCertificate\n{\n\tpublic static function domainWithSan(array $attributes): array\n\t{\n\t\treturn [\n\t\t\t'macro' => 'domainWithSan',\n\t\t\t'data' => [\n\t\t\t\t'domain' => $attributes['data'],\n\t\t\t\t'san' => implode(', ', $attributes['fields']['san'] ?? []),\n\t\t\t]\n\t\t];\n\t}\n\n\tpublic static function canEditSSL(array $attributes): bool\n\t{\n\t\tif ((int)$attributes['fields']['domainid'] > 0\n\t\t\t&& (int)$attributes['fields']['letsencrypt'] == 0\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static function isNotLetsEncrypt(array $attributes): bool\n\t{\n\t\treturn (int)$attributes['fields']['letsencrypt'] == 0;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Style.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Settings;\n\nclass Style\n{\n\tpublic static function deactivated(array $attributes): string\n\t{\n\t\treturn $attributes['fields']['deactivated'] ? 'table-danger' : '';\n\t}\n\n\tpublic static function loginDisabled(array $attributes): string\n\t{\n\t\treturn $attributes['fields']['login_enabled'] == 'N' ? 'table-danger' : '';\n\t}\n\n\tpublic static function resultIntegrityBad(array $attributes): string\n\t{\n\t\treturn $attributes['fields']['result'] ? '' : 'table-warning';\n\t}\n\n\tpublic static function invalidApiKey(array $attributes): string\n\t{\n\t\t// check whether the api key is not valid anymore\n\t\t$isValid = true;\n\t\tif ($attributes['fields']['valid_until'] >= 0) {\n\t\t\tif ($attributes['fields']['valid_until'] < time()) {\n\t\t\t\t$isValid = false;\n\t\t\t}\n\t\t}\n\t\treturn $isValid ? '' : 'table-danger';\n\t}\n\n\tpublic static function resultDomainTerminatedOrDeactivated(array $attributes): string\n\t{\n\t\t$termination_date = str_replace(\"0000-00-00\", \"\", $attributes['fields']['termination_date'] ?? '');\n\t\t$termination_css = '';\n\t\tif (!empty($termination_date)) {\n\t\t\t$cdate = strtotime($termination_date . \" 23:59:59\");\n\t\t\t$today = time();\n\t\t\t$termination_css = 'table-warning';\n\t\t\tif ($cdate < $today) {\n\t\t\t\t$termination_css = 'table-danger';\n\t\t\t}\n\t\t}\n\t\t$deactivated = $attributes['fields']['deactivated'] || (CurrentUser::isAdmin() && $attributes['fields']['customer_deactivated']);\n\t\treturn $deactivated ? 'table-info' : $termination_css;\n\t}\n\n\tpublic static function resultCustomerLockedOrDeactivated(array $attributes): string\n\t{\n\t\t$row_css = '';\n\t\tif ((int)$attributes['fields']['deactivated'] == 1) {\n\t\t\t$row_css = 'table-info';\n\t\t} elseif ($attributes['fields']['loginfail_count'] >= Settings::Get('login.maxloginattempts')\n\t\t\t&& $attributes['fields']['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))\n\t\t) {\n\t\t\t$row_css = 'table-warning';\n\t\t}\n\n\t\treturn $row_css;\n\t}\n\n\tpublic static function diskspaceWarning(array $attributes): string\n\t{\n\t\treturn self::getWarningStyle('diskspace', $attributes['fields'], (int)Settings::Get('system.report_webmax'));\n\t}\n\n\tprivate static function getWarningStyle(string $field, array $attributes, int $report_max = 90): string\n\t{\n\t\t$style = '';\n\t\tif ((int)$attributes[$field] >= 0) {\n\t\t\tif (($attributes[$field] / 100) * $report_max < $attributes[$field . '_used']) {\n\t\t\t\t$style = 'table-danger';\n\t\t\t} elseif (($attributes[$field] / 100) * ($report_max - 15) < $attributes[$field . '_used']) {\n\t\t\t\t$style = 'table-warning';\n\t\t\t}\n\t\t}\n\t\treturn $style;\n\t}\n\n\tpublic static function trafficWarning(array $attributes): string\n\t{\n\t\treturn self::getWarningStyle('traffic', $attributes['fields'], (int)Settings::Get('system.report_trafficmax'));\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/SysLog.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\FroxlorLogger;\n\nclass SysLog\n{\n\n\tpublic static function typeDescription(array $attributes): string\n\t{\n\t\treturn FroxlorLogger::getInstanceOf()->getLogLevelDesc($attributes['data']);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/Text.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Callbacks;\n\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\System\\Markdown;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\User;\nuse PDO;\n\nclass Text\n{\n\tpublic static function boolean(array $attributes): array\n\t{\n\t\treturn [\n\t\t\t'macro' => 'boolean',\n\t\t\t'data' => (bool)$attributes['data']\n\t\t];\n\t}\n\n\tpublic static function yesno(array $attributes): array\n\t{\n\t\treturn [\n\t\t\t'macro' => 'boolean',\n\t\t\t'data' => $attributes['data'] == 'Y'\n\t\t];\n\t}\n\n\tpublic static function type2fa(array $attributes): array\n\t{\n\t\treturn [\n\t\t\t'macro' => 'type2fa',\n\t\t\t'data' => (int)$attributes['data']\n\t\t];\n\t}\n\n\tpublic static function customerfullname(array $attributes): string\n\t{\n\t\treturn User::getCorrectFullUserDetails($attributes['fields'], true);\n\t}\n\n\tpublic static function size(array $attributes): string\n\t{\n\t\treturn PhpHelper::sizeReadable($attributes['data'], null, 'bi');\n\t}\n\n\tpublic static function timestamp(array $attributes): string\n\t{\n\t\treturn (int)$attributes['data'] > 0 ? date('d.m.Y H:i', (int)$attributes['data']) : lng('panel.never');\n\t}\n\n\tpublic static function timestampUntil(array $attributes): string\n\t{\n\t\treturn (int)$attributes['data'] > 0 ? date('d.m.Y H:i', (int)$attributes['data']) : lng('panel.unlimited');\n\t}\n\n\tpublic static function crondesc(array $attributes): string\n\t{\n\t\treturn lng('crondesc.' . $attributes['data']);\n\t}\n\n\tpublic static function shorten(array $attributes): string\n\t{\n\t\treturn substr($attributes['data'], 0, 20) . '...';\n\t}\n\n\tpublic static function wordwrap(array $attributes): string\n\t{\n\t\treturn wordwrap($attributes['data'], 100, '<br>', true);\n\t}\n\n\tpublic static function customerNoteDetailModal(array $attributes): array\n\t{\n\t\t$note = $attributes['fields']['custom_notes'] ?? '';\n\t\t$key = $attributes['fields']['customerid'] ?? $attributes['fields']['adminid'];\n\t\treturn [\n\t\t\t'entry' => $key,\n\t\t\t'id' => 'cnModal' . $key,\n\t\t\t'title' => lng('usersettings.custom_notes.title') . ': ' . ($attributes['fields']['loginname'] ?? $attributes['fields']['adminname']),\n\t\t\t'body' => nl2br(Markdown::cleanCustomNotes($note))\n\t\t];\n\t}\n\n\tpublic static function apikeyDetailModal(array $attributes): array\n\t{\n\t\t$linker = UI::getLinker();\n\t\t$result = $attributes['fields'];\n\t\t$apikey_data = include Froxlor::getInstallDir() . '/lib/formfields/formfield.api_key.php';\n\n\t\t$body = UI::twig()->render(UI::validateThemeTemplate('/user/inline-form.html.twig'), [\n\t\t\t'formaction' => $linker->getLink(['section' => 'index', 'page' => 'apikeys']),\n\t\t\t'formdata' => $apikey_data['apikey'],\n\t\t\t'editid' => $attributes['fields']['id']\n\t\t]);\n\t\treturn [\n\t\t\t'entry' => $attributes['fields']['id'],\n\t\t\t'id' => 'akModal' . $attributes['fields']['id'],\n\t\t\t'title' => 'API-key ' . ($attributes['fields']['loginname'] ?? $attributes['fields']['adminname']),\n\t\t\t'action' => 'apikeys',\n\t\t\t'body' => $body\n\t\t];\n\t}\n\n\tpublic static function domainDuplicateModal(array $attributes): array\n\t{\n\t\t$linker = UI::getLinker();\n\t\t$result = $attributes['fields'];\n\n\t\t$customers = [\n\t\t\t0 => lng('panel.please_choose')\n\t\t];\n\t\t$result_customers_stmt = Database::prepare(\"\n\t\t\tSELECT `customerid`, `loginname`, `name`, `firstname`, `company`\n\t\t\tFROM `\" . TABLE_PANEL_CUSTOMERS . \"` \" . (CurrentUser::getField('customers_see_all') ? '' : \" WHERE `adminid` = :adminid \") . \"\n\t\t\tORDER BY COALESCE(NULLIF(`name`,''), `company`) ASC\n\t\t\");\n\t\t$params = [];\n\t\tif (CurrentUser::getField('customers_see_all') == '0') {\n\t\t\t$params['adminid'] = CurrentUser::getField('adminid');\n\t\t}\n\t\tDatabase::pexecute($result_customers_stmt, $params);\n\n\t\twhile ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t$customers[$row_customer['customerid']] = User::getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')';\n\t\t}\n\n\t\t$domdup_data = include Froxlor::getInstallDir() . '/lib/formfields/admin/domains/formfield.domains_duplicate.php';\n\n\t\t$body = UI::twig()->render(UI::validateThemeTemplate('/user/inline-form.html.twig'), [\n\t\t\t'formaction' => $linker->getLink(['section' => 'domains', 'page' => 'domains', 'action' => 'duplicate']),\n\t\t\t'formdata' => $domdup_data['domain_duplicate'],\n\t\t\t'editid' => $attributes['fields']['id'],\n\t\t\t'nosubmit' => 0\n\t\t]);\n\t\treturn [\n\t\t\t'entry' => $attributes['fields']['id'],\n\t\t\t'id' => 'ddModal' . $attributes['fields']['id'],\n\t\t\t'title' => lng('admin.domain_duplicate_named', [$attributes['fields']['domain']]),\n\t\t\t'action' => 'duplicate',\n\t\t\t'body' => $body\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Callbacks/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/UI/Collection.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nuse Froxlor\\Settings;\n\nclass Collection\n{\n\tprivate string $class;\n\tprivate array $has = [];\n\tprivate array $params;\n\tprivate array $userinfo;\n\tprivate ?Pagination $pagination = null;\n\tprivate bool $internal = false;\n\n\tpublic function __construct(string $class, array $userInfo, array $params = [])\n\t{\n\t\t$this->class = $class;\n\t\t$this->params = $params;\n\t\t$this->userinfo = $userInfo;\n\t}\n\n\tpublic function getList(): array\n\t{\n\t\treturn $this->getData()['list'];\n\t}\n\n\tpublic function getData(): array\n\t{\n\t\treturn $this->get()['data'];\n\t}\n\n\tpublic function get(): array\n\t{\n\t\t$result = $this->getListing($this->class, $this->params);\n\n\t\t// check if the api result contains any items (not the overall listingCount as we might be in a search-resultset)\n\t\tif (count($result)) {\n\t\t\tforeach ($this->has as $has) {\n\t\t\t\t$attributes = $this->getListing($has['class'], $has['params']);\n\n\t\t\t\tforeach ($result['data']['list'] as $key => $item) {\n\t\t\t\t\tforeach ($attributes['data']['list'] as $list) {\n\t\t\t\t\t\tif ($item[$has['parentKey']] == $list[$has['childKey']]) {\n\t\t\t\t\t\t\t$result['data']['list'][$key][$has['column']] = $list;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// attach pagination if available\n\t\tif ($this->pagination) {\n\t\t\t$result = array_merge($result, $this->pagination->getApiResponseParams());\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\tprivate function getListing($class, $params): array\n\t{\n\t\treturn json_decode($class::getLocal($this->userinfo, $params, $this->internal)->listing(), true);\n\t}\n\n\tpublic function getJson(): string\n\t{\n\t\treturn json_encode($this->get());\n\t}\n\n\tpublic function has(string $column, string $class, string $parentKey = 'id', string $childKey = 'id', array $params = []): Collection\n\t{\n\t\t$this->has[] = [\n\t\t\t'column' => $column,\n\t\t\t'class' => $class,\n\t\t\t'parentKey' => $parentKey,\n\t\t\t'childKey' => $childKey,\n\t\t\t'params' => $params\n\t\t];\n\n\t\treturn $this;\n\t}\n\n\tpublic function addParam(array $keyval): Collection\n\t{\n\t\t$this->params = array_merge($this->params, $keyval);\n\n\t\treturn $this;\n\t}\n\n\tpublic function withPagination(array $columns, array $default_sorting = [], array $pagination_additional_params = []): Collection\n\t{\n\t\t// Get only searchable columns\n\t\t$sortableColumns = [];\n\t\tforeach ($columns as $key => $column) {\n\t\t\tif (!isset($column['sortable']) || (isset($column['sortable']) && $column['sortable'])) {\n\t\t\t\t$sortableColumns[$key] = $column;\n\t\t\t}\n\t\t}\n\n\t\t// Prepare pagination\n\t\t$this->pagination = new Pagination($sortableColumns, $this->count(), (int)Settings::Get('panel.paging'), $default_sorting, $pagination_additional_params);\n\t\t$this->params = array_merge($this->params, $this->pagination->getApiCommandParams());\n\t\t$this->pagination->setEntries($this->count(true));\n\t\treturn $this;\n\t}\n\n\tpublic function count(bool $with_pagination = false): int\n\t{\n\t\tif ($with_pagination) {\n\t\t\t$this->params = array_merge($this->params, $this->pagination->getApiCommandParams());\n\t\t}\n\t\treturn json_decode($this->class::getLocal($this->userinfo, $this->params, $this->internal)->listingCount(), true)['data'];\n\t}\n\n\tpublic function getPagination(): ?Pagination\n\t{\n\t\treturn $this->pagination;\n\t}\n\n\tpublic function setInternal(bool $internal): Collection {\n\t\t$this->internal = $internal;\n\t\treturn $this;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Data.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nclass Data\n{\n\n\tpublic static function getFormFieldDataEmail($fieldname, $fielddata, $input)\n\t{\n\t\treturn self::getFormFieldDataText($fieldname, $fielddata, $input);\n\t}\n\n\tpublic static function getFormFieldDataText($fieldname, $fielddata, $input)\n\t{\n\t\tif (isset($input[$fieldname])) {\n\t\t\t$newfieldvalue = str_replace(\"\\r\\n\", \"\\n\", $input[$fieldname]);\n\t\t} else {\n\t\t\t$newfieldvalue = $fielddata['default'];\n\t\t}\n\n\t\treturn $newfieldvalue;\n\t}\n\n\tpublic static function getFormFieldDataUrl($fieldname, $fielddata, $input)\n\t{\n\t\treturn self::getFormFieldDataText($fieldname, $fielddata, $input);\n\t}\n\n\tpublic static function getFormFieldDataSelect($fieldname, $fielddata, $input)\n\t{\n\t\tif (isset($input[$fieldname])) {\n\t\t\t$newfieldvalue = $input[$fieldname];\n\t\t} else {\n\t\t\t$newfieldvalue = $fielddata['default'];\n\t\t}\n\n\t\tif (is_array($newfieldvalue)) {\n\t\t\t$newfieldvalue = implode(',', $newfieldvalue);\n\t\t}\n\n\t\treturn $newfieldvalue;\n\t}\n\n\tpublic static function getFormFieldDataNumber($fieldname, $fielddata, $input)\n\t{\n\t\tif (isset($input[$fieldname])) {\n\t\t\t$newfieldvalue = (int)$input[$fieldname];\n\t\t} else {\n\t\t\t$newfieldvalue = (int)$fielddata['default'];\n\t\t}\n\n\t\treturn $newfieldvalue;\n\t}\n\n\tpublic static function getFormFieldDataCheckbox($fieldname, $fielddata, $input)\n\t{\n\t\tif (isset($input[$fieldname]) && ($input[$fieldname] === '1' || $input[$fieldname] === 1 || $input[$fieldname] === true || strtolower($input[$fieldname]) === 'yes' || strtolower($input[$fieldname]) === 'ja')) {\n\t\t\t$newfieldvalue = '1';\n\t\t} else {\n\t\t\t$newfieldvalue = '0';\n\t\t}\n\n\t\treturn $newfieldvalue;\n\t}\n\n\tpublic static function getFormFieldDataImage($fieldname, $fielddata, $input)\n\t{\n\t\t// We always make the system think we have new data to trigger the save function where we actually check everything\n\t\treturn time();\n\t}\n\n\tpublic static function manipulateFormFieldDataDate($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\tif (isset($fielddata['date_timestamp']) && $fielddata['date_timestamp'] === true) {\n\t\t\t$newfieldvalue = strtotime($newfieldvalue);\n\t\t}\n\n\t\treturn $newfieldvalue;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Form.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nuse Froxlor\\CurrentUser;\nuse Froxlor\\FroxlorTwoFactorAuth;\nuse Froxlor\\Settings;\nuse Froxlor\\Validate\\Check;\n\nclass Form\n{\n\tpublic static function buildForm(array $form, string $part = ''): array\n\t{\n\t\t$fields = [];\n\n\t\tif (\\Froxlor\\Validate\\Form::validateFormDefinition($form)) {\n\t\t\tforeach ($form['groups'] as $groupname => $groupdetails) {\n\t\t\t\t// check for advanced mode sections\n\t\t\t\tif (isset($groupdetails['advanced_mode']) && $groupdetails['advanced_mode'] && (int)Settings::Get('panel.settings_mode') == 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// show overview\n\t\t\t\tif ($part == '' || $part == 'all') {\n\t\t\t\t\tif (isset($groupdetails['title']) && $groupdetails['title'] != '') {\n\t\t\t\t\t\t$fields[] = self::getFormOverviewGroupOutput($groupname, $groupdetails);\n\t\t\t\t\t}\n\t\t\t\t} elseif ($part != '' && $groupname == $part) {\n\t\t\t\t\t// only show one section\n\t\t\t\t\t/**\n\t\t\t\t\t * this part checks for the 'websrv_avail' entry in the settings-array\n\t\t\t\t\t * if found, we check if the current webserver is in the array.\n\t\t\t\t\t * If this\n\t\t\t\t\t * is not the case, we change the setting type to \"hidden\", #502\n\t\t\t\t\t */\n\t\t\t\t\t$do_show = true;\n\t\t\t\t\tif (isset($groupdetails['websrv_avail']) && is_array($groupdetails['websrv_avail'])) {\n\t\t\t\t\t\t$websrv = Settings::Get('system.webserver');\n\t\t\t\t\t\tif (!in_array($websrv, $groupdetails['websrv_avail'])) {\n\t\t\t\t\t\t\t$do_show = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// visible = Settings::Get('phpfpm.enabled') for example would result in false if not enabled\n\t\t\t\t\t// and therefore not shown as intended. Only check if do_show is still true as it might\n\t\t\t\t\t// be false due to websrv_avail\n\t\t\t\t\tif (isset($groupdetails['visible']) && $do_show) {\n\t\t\t\t\t\t$do_show = $groupdetails['visible'];\n\t\t\t\t\t}\n\n\t\t\t\t\t$fields['_group'] = [\n\t\t\t\t\t\t'title' => $groupdetails['title'] ?? 'unknown group',\n\t\t\t\t\t\t'do_show' => $do_show\n\t\t\t\t\t];\n\n\t\t\t\t\tif (\\Froxlor\\Validate\\Form::validateFieldDefinition($groupdetails)) {\n\t\t\t\t\t\t// Collect form field output\n\t\t\t\t\t\tforeach ($groupdetails['fields'] as $fieldname => $fielddetails) {\n\t\t\t\t\t\t\t// check for advanced mode sections\n\t\t\t\t\t\t\tif (isset($fielddetails['advanced_mode']) && $fielddetails['advanced_mode'] && (int)Settings::Get('panel.settings_mode') == 0) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$fields[$fieldname] = self::getFormFieldOutput($fieldname, $fielddetails);\n\t\t\t\t\t\t\t$fields[$fieldname] = array_merge($fields[$fieldname], self::prefetchFormFieldData($fieldname, $fielddetails));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $fields;\n\t}\n\n\tpublic static function getFormOverviewGroupOutput($groupname, $groupdetails)\n\t{\n\t\t$activated = true;\n\t\tif (isset($groupdetails['fields'])) {\n\t\t\tforeach ($groupdetails['fields'] as $fielddetails) {\n\t\t\t\tif (isset($fielddetails['overview_option']) && $fielddetails['overview_option'] == true) {\n\t\t\t\t\tif ($fielddetails['type'] != 'checkbox') {\n\t\t\t\t\t\t// throw exception here as this is most likely an internal issue\n\t\t\t\t\t\t// if we messed up the arrays\n\t\t\t\t\t\tResponse::standardError('overviewsettingoptionisnotavalidfield', '', true);\n\t\t\t\t\t}\n\t\t\t\t\t$activated = (int)Settings::Get($fielddetails['settinggroup'] . '.' . $fielddetails['varname']);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$item = [\n\t\t\t'title' => $groupdetails['title'],\n\t\t\t'icon' => $groupdetails['icon'] ?? 'fa-solid fa-circle-question',\n\t\t\t'part' => $groupname,\n\t\t\t'activated' => $activated\n\t\t];\n\n\t\t/**\n\t\t * this part checks for the 'websrv_avail' entry in the settings\n\t\t * if found, we check if the current webserver is in the array.\n\t\t * If this is not the case, we change the setting type to \"hidden\", #502\n\t\t */\n\t\tif (isset($groupdetails['websrv_avail']) && is_array($groupdetails['websrv_avail'])) {\n\t\t\t$websrv = Settings::Get('system.webserver');\n\t\t\tif (!in_array($websrv, $groupdetails['websrv_avail'])) {\n\t\t\t\t$item['info'] = lng('serversettings.option_unavailable_websrv', [implode(\", \", $groupdetails['websrv_avail'])]);\n\t\t\t\t$item['visible'] = false;\n\t\t\t}\n\t\t}\n\n\t\treturn $item;\n\t}\n\n\tpublic static function getFormFieldOutput($fieldname, $fielddata): array\n\t{\n\t\t$returnvalue = [];\n\t\tif (is_array($fielddata) && isset($fielddata['type']) && $fielddata['type'] != '') {\n\t\t\tif (!isset($fielddata['value'])) {\n\t\t\t\tif (isset($fielddata['default'])) {\n\t\t\t\t\t$fielddata['value'] = $fielddata['default'];\n\t\t\t\t} else {\n\t\t\t\t\t$fielddata['value'] = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// set value according to type\n\t\t\tswitch ($fielddata['type']) {\n\t\t\t\tcase 'select':\n\t\t\t\t\t$fielddata['selected'] = $fielddata['value'];\n\t\t\t\t\tunset($fielddata['value']);\n\t\t\t\t\tif (isset($fielddata['select_mode']) && $fielddata['select_mode'] == 'multiple') {\n\t\t\t\t\t\t$fielddata['selected'] = array_flip(explode(\",\", $fielddata['selected']));\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'checkbox':\n\t\t\t\t\t$fielddata['checked'] = (bool)$fielddata['value'];\n\t\t\t\t\t$fielddata['value'] = 1;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * this part checks for the 'websrv_avail' entry in the settings-array\n\t\t\t * if found, we check if the current webserver is in the array.\n\t\t\t * If this\n\t\t\t * is not the case, we change the setting type to \"hidden\", #502\n\t\t\t */\n\t\t\t$do_show = true;\n\t\t\tif (isset($fielddata['websrv_avail']) && is_array($fielddata['websrv_avail'])) {\n\t\t\t\t$websrv = Settings::Get('system.webserver');\n\t\t\t\tif (!in_array($websrv, $fielddata['websrv_avail'])) {\n\t\t\t\t\t$do_show = false;\n\t\t\t\t\t$fielddata['note'] = lng('serversettings.option_unavailable_websrv', [implode(\", \", $fielddata['websrv_avail'])]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// visible = Settings::Get('phpfpm.enabled') for example would result in false if not enabled\n\t\t\t// and therefore not shown as intended. Only check if do_show is still true as it might\n\t\t\t// be false due to websrv_avail\n\t\t\tif (isset($fielddata['visible']) && $do_show) {\n\t\t\t\t$do_show = $fielddata['visible'];\n\t\t\t\tif (!$do_show) {\n\t\t\t\t\t$fielddata['note'] = lng('serversettings.option_unavailable');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// OTP security validation for sensitive settings\n\t\t\tif (!Settings::Config('disable_otp_security_check') && isset($fielddata['required_otp']) && $do_show) {\n\t\t\t\t$otp_enabled_system = (bool)Settings::Get('2fa.enabled');\n\t\t\t\t$otp_enabled_user = (int)CurrentUser::getField('type_2fa') != 0;\n\t\t\t\t$do_show = !$fielddata['required_otp'] || ($otp_enabled_system && $otp_enabled_user);\n\t\t\t\tif (!$do_show) {\n\t\t\t\t\t$fielddata['note'] = lng('serversettings.option_requires_otp');\n\t\t\t\t\tif (!$otp_enabled_system) {\n\t\t\t\t\t\t$fielddata['disabled'] = true;\n\t\t\t\t\t\t$fielddata['note'] .= '<br>' . lng('2fa.2fa_not_activated');\n\t\t\t\t\t} elseif (!$otp_enabled_user) {\n\t\t\t\t\t\t$fielddata['disabled'] = true;\n\t\t\t\t\t\t$fielddata['note'] .= '<br>' . lng('2fa.2fa_not_activated_for_user');\n\t\t\t\t\t}\n\t\t\t\t\t// show field in any case\n\t\t\t\t\t$do_show = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!$do_show) {\n\t\t\t\t$fielddata['visible'] = false;\n\t\t\t}\n\n\t\t\t$returnvalue = $fielddata;\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function prefetchFormFieldData($fieldname, $fielddata)\n\t{\n\t\t$returnvalue = [];\n\t\tif (is_array($fielddata) && isset($fielddata['type']) && $fielddata['type'] == 'select') {\n\t\t\tif ((empty($fielddata['select_var']) || !is_array($fielddata['select_var'])) && (isset($fielddata['option_options_method']))\n\t\t\t) {\n\t\t\t\t$returnvalue['select_var'] = call_user_func($fielddata['option_options_method']);\n\t\t\t}\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function processForm(&$form, &$input, $url_params = [], $part = null, bool $settings_all = false, $settings_part = null, bool $only_enabledisable = false)\n\t{\n\t\tif (\\Froxlor\\Validate\\Form::validateFormDefinition($form)) {\n\t\t\t$submitted_fields = [];\n\t\t\t$changed_fields = [];\n\t\t\t$saved_fields = [];\n\n\t\t\tforeach ($form['groups'] as $groupname => $groupdetails) {\n\t\t\t\tif (($settings_part && $part == $groupname) || $settings_all || $only_enabledisable) {\n\t\t\t\t\tif (\\Froxlor\\Validate\\Form::validateFieldDefinition($groupdetails)) {\n\t\t\t\t\t\t// Prefetch form fields\n\t\t\t\t\t\tforeach ($groupdetails['fields'] as $fieldname => $fielddetails) {\n\t\t\t\t\t\t\tif (!$only_enabledisable || isset($fielddetails['overview_option'])) {\n\t\t\t\t\t\t\t\t$groupdetails['fields'][$fieldname] = array_merge($fielddetails, self::prefetchFormFieldData($fieldname, $fielddetails));\n\t\t\t\t\t\t\t\t$form['groups'][$groupname]['fields'][$fieldname] = $groupdetails['fields'][$fieldname];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tforeach ($form['groups'] as $groupname => $groupdetails) {\n\t\t\t\tif (($settings_part && $part == $groupname) || $settings_all || $only_enabledisable) {\n\t\t\t\t\tif (\\Froxlor\\Validate\\Form::validateFieldDefinition($groupdetails)) {\n\t\t\t\t\t\t// Validate fields\n\t\t\t\t\t\tforeach ($groupdetails['fields'] as $fieldname => $fielddetails) {\n\t\t\t\t\t\t\tif (((isset($fielddetails['visible']) && $fielddetails['visible']) || !isset($fielddetails['visible'])) && (!$only_enabledisable || ($only_enabledisable && isset($fielddetails['overview_option'])))) {\n\t\t\t\t\t\t\t\t$newfieldvalue = self::getFormFieldData($fieldname, $fielddetails, $input);\n\t\t\t\t\t\t\t\tif ($newfieldvalue != $fielddetails['value']) {\n\t\t\t\t\t\t\t\t\tif (($error = \\Froxlor\\Validate\\Form::validateFormField($fieldname, $fielddetails, $newfieldvalue)) !== true) {\n\t\t\t\t\t\t\t\t\t\tResponse::standardError($error, $fieldname);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t$changed_fields[$fieldname] = $newfieldvalue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t$submitted_fields[$fieldname] = $newfieldvalue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tforeach ($form['groups'] as $groupname => $groupdetails) {\n\t\t\t\tif (($settings_part && $part == $groupname) || $settings_all || $only_enabledisable) {\n\t\t\t\t\tif (\\Froxlor\\Validate\\Form::validateFieldDefinition($groupdetails)) {\n\t\t\t\t\t\t// Check fields for plausibility\n\t\t\t\t\t\tforeach ($groupdetails['fields'] as $fieldname => $fielddetails) {\n\t\t\t\t\t\t\tif (!isset($submitted_fields[$fieldname])) {\n\t\t\t\t\t\t\t\t// skip unset fields due to unavailability for this system/settings-set\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!$only_enabledisable || ($only_enabledisable && isset($fielddetails['overview_option']))) {\n\t\t\t\t\t\t\t\tif (($plausibility_check = self::checkPlausibilityFormField($fieldname, $fielddetails, $submitted_fields[$fieldname], $submitted_fields)) !== false) {\n\t\t\t\t\t\t\t\t\tif (is_array($plausibility_check) && isset($plausibility_check[0])) {\n\t\t\t\t\t\t\t\t\t\tif ($plausibility_check[0] == Check::FORMFIELDS_PLAUSIBILITY_CHECK_OK) {\n\t\t\t\t\t\t\t\t\t\t\t// Nothing to do here, everything's okay\n\t\t\t\t\t\t\t\t\t\t} elseif ($plausibility_check[0] == Check::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR) {\n\t\t\t\t\t\t\t\t\t\t\tunset($plausibility_check[0]);\n\t\t\t\t\t\t\t\t\t\t\t$error = $plausibility_check[1];\n\t\t\t\t\t\t\t\t\t\t\tunset($plausibility_check[1]);\n\t\t\t\t\t\t\t\t\t\t\t$targetname = implode(' ', $plausibility_check);\n\t\t\t\t\t\t\t\t\t\t\tResponse::standardError($error, $targetname);\n\t\t\t\t\t\t\t\t\t\t} elseif ($plausibility_check[0] == Check::FORMFIELDS_PLAUSIBILITY_CHECK_QUESTION) {\n\t\t\t\t\t\t\t\t\t\t\tunset($plausibility_check[0]);\n\t\t\t\t\t\t\t\t\t\t\t$question = $plausibility_check[1];\n\t\t\t\t\t\t\t\t\t\t\tunset($plausibility_check[1]);\n\t\t\t\t\t\t\t\t\t\t\t$targetname = implode(' ', $plausibility_check);\n\t\t\t\t\t\t\t\t\t\t\tif (!isset($input[$question])) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (is_array($url_params) && isset($url_params['filename'])) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t$filename = $url_params['filename'];\n\t\t\t\t\t\t\t\t\t\t\t\t\tunset($url_params['filename']);\n\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t$filename = '';\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\tHTML::askYesNo($question, $filename, array_merge($url_params, $submitted_fields, [\n\t\t\t\t\t\t\t\t\t\t\t\t\t$question => $question\n\t\t\t\t\t\t\t\t\t\t\t\t]), $targetname);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tResponse::standardError('plausibilitychecknotunderstood');\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!Settings::Config('disable_otp_security_check') && isset($fielddetails['required_otp']) && isset($changed_fields[$fieldname])) {\n\t\t\t\t\t\t\t\t\t$otp_enabled_system = (bool)Settings::Get('2fa.enabled');\n\t\t\t\t\t\t\t\t\t$otp_enabled_user = (int)CurrentUser::getField('type_2fa') != 0;\n\t\t\t\t\t\t\t\t\t$do_update = !$fielddetails['required_otp'] || ($otp_enabled_system && $otp_enabled_user);\n\t\t\t\t\t\t\t\t\tif ($do_update) {\n\t\t\t\t\t\t\t\t\t\t// setting that requires OTP verification\n\t\t\t\t\t\t\t\t\t\tif (empty($input['otp_verification'])) {\n\t\t\t\t\t\t\t\t\t\t\t// in case email 2fa is enabled, send it now\n\t\t\t\t\t\t\t\t\t\t\tCurrentUser::sendOtpEmail();\n\t\t\t\t\t\t\t\t\t\t\t// build up form\n\t\t\t\t\t\t\t\t\t\t\tif (is_array($url_params) && isset($url_params['filename'])) {\n\t\t\t\t\t\t\t\t\t\t\t\t$filename = $url_params['filename'];\n\t\t\t\t\t\t\t\t\t\t\t\tunset($url_params['filename']);\n\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t$filename = '';\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tHTML::askOTP('please_enter_otp', $filename, array_merge($url_params, $submitted_fields));\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t// validate given OTP code\n\t\t\t\t\t\t\t\t\t\t\t$code = trim($input['otp_verification']);\n\t\t\t\t\t\t\t\t\t\t\t$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));\n\t\t\t\t\t\t\t\t\t\t\t$result = $tfa->verifyCode(CurrentUser::getField('data_2fa'), $code, 3);\n\t\t\t\t\t\t\t\t\t\t\tif (!$result) {\n\t\t\t\t\t\t\t\t\t\t\t\tResponse::standardError('otpnotvalidated');\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// do not update this setting\n\t\t\t\t\t\t\t\t\t\tunset($changed_fields[$fieldname]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tforeach ($form['groups'] as $groupname => $groupdetails) {\n\t\t\t\tif (($settings_part && $part == $groupname) || $settings_all || $only_enabledisable) {\n\t\t\t\t\tif (\\Froxlor\\Validate\\Form::validateFieldDefinition($groupdetails)) {\n\t\t\t\t\t\t// Save fields\n\t\t\t\t\t\tforeach ($groupdetails['fields'] as $fieldname => $fielddetails) {\n\t\t\t\t\t\t\tif (!$only_enabledisable || (isset($fielddetails['overview_option']))) {\n\t\t\t\t\t\t\t\tif (isset($changed_fields[$fieldname])) {\n\t\t\t\t\t\t\t\t\tif (($saved_field = self::saveFormField($fieldname, $fielddetails, self::manipulateFormFieldData($fieldname, $fielddetails, $changed_fields[$fieldname]))) !== false) {\n\t\t\t\t\t\t\t\t\t\t$saved_fields = array_merge($saved_fields, $saved_field);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tResponse::standardError('errorwhensaving', $fieldname);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Save form\n\t\t\treturn self::saveForm($form, $saved_fields);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static function getFormFieldData($fieldname, $fielddata, &$input)\n\t{\n\t\tif (is_array($fielddata) && isset($fielddata['type']) && $fielddata['type'] != '' && method_exists('\\\\Froxlor\\\\UI\\\\Data', 'getFormFieldData' . ucfirst($fielddata['type']))) {\n\t\t\t$newfieldvalue = call_user_func([\n\t\t\t\t'\\\\Froxlor\\\\UI\\\\Data',\n\t\t\t\t'getFormFieldData' . ucfirst($fielddata['type'])\n\t\t\t], $fieldname, $fielddata, $input);\n\t\t} else {\n\t\t\tif (isset($input[$fieldname])) {\n\t\t\t\t$newfieldvalue = $input[$fieldname];\n\t\t\t} elseif (isset($fielddata['default'])) {\n\t\t\t\t$newfieldvalue = $fielddata['default'];\n\t\t\t} else {\n\t\t\t\t$newfieldvalue = false;\n\t\t\t}\n\t\t}\n\n\t\treturn trim($newfieldvalue);\n\t}\n\n\tpublic static function checkPlausibilityFormField($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\t$returnvalue = '';\n\t\tif (is_array($fielddata) && isset($fielddata['plausibility_check_method']) && $fielddata['plausibility_check_method'] != '' && method_exists($fielddata['plausibility_check_method'][0], $fielddata['plausibility_check_method'][1])) {\n\t\t\t$returnvalue = call_user_func($fielddata['plausibility_check_method'], $fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues);\n\t\t} else {\n\t\t\t$returnvalue = false;\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function saveFormField($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = '';\n\t\tif (is_array($fielddata) && isset($fielddata['save_method']) && $fielddata['save_method'] != '') {\n\t\t\t$returnvalue = call_user_func([\n\t\t\t\t'\\\\Froxlor\\\\Settings\\\\Store',\n\t\t\t\t$fielddata['save_method']\n\t\t\t], $fieldname, $fielddata, $newfieldvalue);\n\t\t} elseif (is_array($fielddata) && !isset($fielddata['save_method'])) {\n\t\t\t$returnvalue = [];\n\t\t} else {\n\t\t\t$returnvalue = false;\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function manipulateFormFieldData($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\tif (is_array($fielddata) && isset($fielddata['type']) && $fielddata['type'] != '' && method_exists('\\\\Froxlor\\\\UI\\\\Data', 'manipulateFormFieldData' . ucfirst($fielddata['type']))) {\n\t\t\t$newfieldvalue = call_user_func([\n\t\t\t\t'\\\\Froxlor\\\\UI\\\\Data',\n\t\t\t\t'manipulateFormFieldData' . ucfirst($fielddata['type'])\n\t\t\t], $fieldname, $fielddata, $newfieldvalue);\n\t\t}\n\n\t\treturn $newfieldvalue;\n\t}\n\n\tpublic static function saveForm($fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = '';\n\t\tif (is_array($fielddata) && isset($fielddata['save_method']) && $fielddata['save_method'] != '') {\n\t\t\t$returnvalue = call_user_func([\n\t\t\t\t'\\\\Froxlor\\\\Settings\\\\Store',\n\t\t\t\t$fielddata['save_method']\n\t\t\t], $fielddata, $newfieldvalue);\n\t\t} elseif (is_array($fielddata) && !isset($fielddata['save_method'])) {\n\t\t\t$returnvalue = true;\n\t\t} else {\n\t\t\t$returnvalue = false;\n\t\t}\n\t\treturn $returnvalue;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/HTML.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nuse Froxlor\\Settings;\n\nclass HTML\n{\n\n\t/**\n\t * Build Navigation Sidebar\n\t *\n\t * @param array $navigation data\n\t * @param array $userinfo the userinfo of the user\n\t *\n\t * @return array the content of the navigation bar according to user-permissions\n\t */\n\tpublic static function buildNavigation(array $navigation, array $userinfo)\n\t{\n\t\t$returnvalue = [];\n\n\t\t// sanitize user-given input (url-manipulation)\n\t\t$req_page = Request::get('page');\n\t\tif (!empty($req_page) && is_array($req_page)) {\n\t\t\t$req_page = (string)array_shift($req_page);\n\t\t}\n\t\t// need to preserve this\n\t\t$_GET['page'] = $req_page;\n\n\t\t$req_action = Request::get('action');\n\t\tif (!empty($req_action) && is_array($req_action)) {\n\t\t\t$req_action = (string)array_shift($req_action);\n\t\t}\n\t\t// need to preserve this\n\t\t$_GET['action'] = $req_action;\n\n\t\tforeach ($navigation as $box) {\n\t\t\tif ((!isset($box['show_element']) || $box['show_element'] === true) && (!isset($box['required_resources']) || $box['required_resources'] == '' || (isset($userinfo[$box['required_resources']]) && ((int)$userinfo[$box['required_resources']] > 0 || $userinfo[$box['required_resources']] == '-1')))) {\n\t\t\t\t$navigation_links = [];\n\t\t\t\t$box_active = false;\n\t\t\t\tif (isset($box['url']) && $box['url'] == basename($_SERVER[\"SCRIPT_FILENAME\"])) {\n\t\t\t\t\t$box_active = true;\n\t\t\t\t}\n\t\t\t\tforeach ($box['elements'] as $element) {\n\t\t\t\t\tif ((!isset($element['show_element']) || $element['show_element'] === true) && (!isset($element['required_resources']) || $element['required_resources'] == '' || (isset($userinfo[$element['required_resources']]) && ((int)$userinfo[$element['required_resources']] > 0 || $userinfo[$element['required_resources']] == '-1')))) {\n\t\t\t\t\t\t$target = '';\n\t\t\t\t\t\t$active = false;\n\t\t\t\t\t\t$navurl = '#';\n\t\t\t\t\t\tif (isset($element['url']) && trim($element['url']) != '') {\n\t\t\t\t\t\t\tif (isset($element['new_window']) && $element['new_window'] == true) {\n\t\t\t\t\t\t\t\t$target = ' target=\"_blank\"';\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t((empty($req_page) && substr_count($element['url'], \"page=\") == 0) || (!empty($req_page) && substr_count($element['url'], \"page=\" . $req_page) > 0))\n\t\t\t\t\t\t\t\t&& substr_count($element['url'], basename($_SERVER[\"SCRIPT_FILENAME\"])) > 0\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t$active = true;\n\t\t\t\t\t\t\t\t$box_active = true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t$navurl = htmlspecialchars($element['url']);\n\t\t\t\t\t\t\t$navlabel = $element['label'];\n\t\t\t\t\t\t\t$icon = $element['icon'] ?? null;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$navlabel = $element['label'];\n\t\t\t\t\t\t\t$icon = $element['icon'] ?? null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$navigation_links[] = [\n\t\t\t\t\t\t\t'url' => $navurl,\n\t\t\t\t\t\t\t'target' => $target,\n\t\t\t\t\t\t\t'active' => $active,\n\t\t\t\t\t\t\t'label' => $navlabel,\n\t\t\t\t\t\t\t'icon' => $icon,\n\t\t\t\t\t\t\t'add_shortlink' => $element['add_shortlink'] ?? null,\n\t\t\t\t\t\t\t'is_external' => $element['is_external'] ?? false,\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!empty($navigation_links)) {\n\t\t\t\t\t$target = '';\n\t\t\t\t\tif (isset($box['url']) && trim($box['url']) != '') {\n\t\t\t\t\t\tif (isset($box['new_window']) && $box['new_window'] == true) {\n\t\t\t\t\t\t\t$target = ' target=\"_blank\"';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$navurl = htmlspecialchars($box['url']);\n\t\t\t\t\t\t$navlabel = $box['label'];\n\t\t\t\t\t\t$icon = $box['icon'] ?? null;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$navurl = \"#\";\n\t\t\t\t\t\t$navlabel = $box['label'];\n\t\t\t\t\t\t$icon = $box['icon'] ?? null;\n\t\t\t\t\t}\n\n\t\t\t\t\t$returnvalue[] = [\n\t\t\t\t\t\t'url' => $navurl,\n\t\t\t\t\t\t'target' => $target,\n\t\t\t\t\t\t'label' => $navlabel,\n\t\t\t\t\t\t'icon' => $icon,\n\t\t\t\t\t\t'items' => $navigation_links,\n\t\t\t\t\t\t'active' => ((int)Settings::Get('panel.menu_collapsed') == 0 ? 1 : $box_active)\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\t/**\n\t * Return HTML Code for an option within a <select>\n\t *\n\t * @param string $title\n\t *            The caption\n\t * @param string $value\n\t *            The Value which will be returned\n\t * @param string $selvalue\n\t *            Values which will be selected by default.\n\t * @param bool $title_trusted\n\t *            Whether the title may contain html or not\n\t * @param bool $value_trusted\n\t *            Whether the value may contain html or not\n\t * @param int $id\n\t * @param bool $disabled\n\t *\n\t * @return string HTML Code\n\t *\n\t * @deprecated\n\t */\n\tpublic static function makeoption($title, $value, $selvalue = null, $title_trusted = false, $value_trusted = false, $id = null, $disabled = false)\n\t{\n\t\tif ($selvalue !== null && ((is_array($selvalue) && in_array($value, $selvalue)) || $value == $selvalue)) {\n\t\t\t$selected = 'selected=\"selected\"';\n\t\t} else {\n\t\t\t$selected = '';\n\t\t}\n\n\t\tif ($disabled) {\n\t\t\t$selected .= ' disabled=\"disabled\"';\n\t\t}\n\n\t\tif (!$title_trusted) {\n\t\t\t$title = htmlspecialchars($title);\n\t\t}\n\n\t\tif (!$value_trusted) {\n\t\t\t$value = htmlspecialchars($value);\n\t\t}\n\n\t\t$id_str = ' ';\n\t\tif ($id !== null) {\n\t\t\t$id_str = 'id=\"' . $id . '\"';\n\t\t}\n\n\t\t$option = '<option value=\"' . $value . '\" ' . $id_str . $selected . ' >' . $title . '</option>';\n\t\treturn $option;\n\t}\n\n\t/**\n\t * Output boolean confirm-dialog\n\t *\n\t * @param string $text\n\t *            The question\n\t * @param string $yesfile\n\t *            File which will be called with POST if user clicks yes\n\t * @param array $params\n\t *            Values which will be given to $yesfile. Format: array(variable1=>value1, variable2=>value2,\n\t *            variable3=>value3)\n\t * @param string $replacer\n\t *            value of a possible existing string-replacer in the question\n\t * @param array $back_link\n\t *\n\t * @return string\n\t * @author Froxlor team <team@froxlor.org> (2010-)\n\t *\n\t */\n\tpublic static function askYesNo(string $text, string $yesfile, array $params = [], string $replacer = '', array $back_link = [])\n\t{\n\t\t$text = lng('question.' . $text, [htmlspecialchars($replacer)]);\n\n\t\tPanel\\UI::view('form/yesnoquestion.html.twig', [\n\t\t\t'action' => $yesfile,\n\t\t\t'url_params' => $params,\n\t\t\t'question' => $text,\n\t\t\t'back_link' => $back_link\n\t\t]);\n\t\texit();\n\t}\n\n\tpublic static function askYesNoWithCheckbox(string $text, string $chk_text, string $yesfile, array $params = [], string $replacer = '', bool $show_checkbox = true)\n\t{\n\t\t$text = lng('question.' . $text, [htmlspecialchars($replacer)]);\n\t\t$chk_text = lng('question.' . $chk_text);\n\n\t\tPanel\\UI::view('form/yesnoquestion.html.twig', [\n\t\t\t'action' => $yesfile,\n\t\t\t'url_params' => $params,\n\t\t\t'question' => $text,\n\t\t\t'with_checkbox' => [\n\t\t\t\t'chk_text' => $chk_text,\n\t\t\t\t'show' => $show_checkbox\n\t\t\t]\n\t\t]);\n\t\texit();\n\t}\n\n\tpublic static function askOTP(string $text, string $targetfile, array $params = [], string $replacer = '', array $back_link = [])\n\t{\n\t\t$text = lng('question.' . $text, [htmlspecialchars($replacer)]);\n\n\t\tPanel\\UI::view('form/otpquestion.html.twig', [\n\t\t\t'action' => $targetfile,\n\t\t\t'url_params' => $params,\n\t\t\t'question' => $text,\n\t\t\t'back_link' => $back_link\n\t\t]);\n\t\texit();\n\t}\n\n\tpublic static function askUserPasswd(string $text, string $targetfile, array $params = [], string $replacer = '', array $back_link = [])\n\t{\n\t\t$text = lng('question.' . $text, [htmlspecialchars($replacer)]);\n\n\t\tPanel\\UI::view('form/askuserpasswd.html.twig', [\n\t\t\t'action' => $targetfile,\n\t\t\t'url_params' => $params,\n\t\t\t'question' => $text,\n\t\t\t'back_link' => $back_link\n\t\t]);\n\t\texit();\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Linker.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nclass Linker\n{\n\tprivate $protocol = '';\n\tprivate $username = '';\n\tprivate $password = '';\n\tprivate $hostname = '';\n\tprivate $port = 80;\n\tprivate $filename = 'index.php';\n\tprivate $args = [];\n\n\tpublic function __construct($file = 'index.php', $hostname = '', $protocol = '', $port = '', $username = '', $password = '')\n\t{\n\t\t// Set the basic parts of our URL\n\t\t$this->protocol = $protocol;\n\t\t$this->username = $username;\n\t\t$this->password = $password;\n\t\t$this->hostname = $hostname;\n\t\t$this->port = $port;\n\t\t$this->filename = $file;\n\t}\n\n\tpublic function __set($key, $value)\n\t{\n\t\tswitch (strtolower($key)) {\n\t\t\tcase 'protocol':\n\t\t\t\t$this->protocol = $value;\n\t\t\t\tbreak;\n\t\t\tcase 'username':\n\t\t\t\t$this->username = $value;\n\t\t\t\tbreak;\n\t\t\tcase 'password':\n\t\t\t\t$this->password = $value;\n\t\t\t\tbreak;\n\t\t\tcase 'hostname':\n\t\t\t\t$this->hostname = $value;\n\t\t\t\tbreak;\n\t\t\tcase 'port':\n\t\t\t\t$this->port = $value;\n\t\t\t\tbreak;\n\t\t\tcase 'filename':\n\t\t\t\t$this->filename = $value;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic function add($key, $value)\n\t{\n\t\t// Add a new value to our parameters (overwrite = enabled)\n\t\t$this->args[$key] = $value;\n\t}\n\n\tpublic function del($key)\n\t{\n\t\t// If the key exists in our array -> delete it\n\t\tif (isset($this->args[$key])) {\n\t\t\tunset($this->args[$key]);\n\t\t}\n\t}\n\n\tpublic function getLink()\n\t{\n\t\t$link = '';\n\n\t\t// Build the basic URL\n\t\tif (strlen($this->protocol) > 0 && strlen($this->hostname) > 0) {\n\t\t\t$link = $this->protocol . '://';\n\t\t}\n\n\t\t// Let's see if we shall use a username in the URL\n\t\t// This is only available if a hostname is used as well\n\t\tif (strlen($this->username) > 0 && strlen($this->hostname) > 0) {\n\t\t\t$link .= urlencode($this->username);\n\n\t\t\t// Maybe we even have to append a password?\n\t\t\tif ($this->password != '') {\n\t\t\t\t$link .= ':' . urlencode($this->password);\n\t\t\t}\n\n\t\t\t// At least a username was given, add the @ to allow appending the hostname\n\t\t\t$link .= '@';\n\t\t}\n\n\t\t// Add hostname, port and filename to the URL\n\t\tif (strlen($this->hostname) > 0) {\n\t\t\t$link .= $this->hostname;\n\n\t\t\t// A port may only be used if hostname is used as well\n\t\t\tif (strlen($this->port) > 0) {\n\t\t\t\t$link .= ':' . $this->port;\n\t\t\t}\n\t\t}\n\n\t\t// Overwrite $this->args with parameters of this function (if necessary)\n\t\tif (func_num_args() == 1 && is_array(func_get_arg(0))) {\n\t\t\t$arguments = func_get_arg(0);\n\t\t\t$this->args = array_merge($this->args, $arguments);\n\t\t}\n\n\t\t// temporary until frontcontroller exists\n\t\t// We got a section in the URL -> morph AREA and section into filename\n\t\t// @TODO: Remove this\n\t\tif (isset($this->args['section']) && strlen($this->args['section']) > 0) {\n\t\t\t$link .= AREA . '_' . $this->args['section'] . '.php';\n\t\t\tunset($this->args['section']);\n\t\t} else {\n\t\t\t// filename has a prefixed slash\n\t\t\t$link .= $this->filename;\n\t\t}\n\n\t\t// Let's see if we are done (no arguments in query)\n\t\tif (count($this->args) == 0) {\n\t\t\treturn $link;\n\t\t}\n\n\t\t// We have parameters, add them with a \"?\"\n\t\t$link .= \"?\";\n\n\t\t// Loop through arguments and add them to the link\n\t\tforeach ($this->args as $key => $value) {\n\t\t\t// For all but the first argument, prepend \"&amp;\"\n\t\t\tif (substr($link, -1) != \"?\") {\n\t\t\t\t$link .= \"&\";\n\t\t\t}\n\n\t\t\t// Encode parameters and add them to the link\n\t\t\t$link .= urlencode($key) . ($value !== \"\" ? '=' . urlencode($value) : '');\n\t\t}\n\n\t\t// Reset our class for further use\n\t\t$this->delAll();\n\t\treturn $link;\n\t}\n\n\tpublic function delAll()\n\t{\n\t\t// Just resetting the array\n\t\t$this->args = [];\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Listing.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nuse Froxlor\\CurrentUser;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Froxlor;\nuse Froxlor\\UI\\Panel\\UI;\nuse InvalidArgumentException;\n\nclass Listing\n{\n\tpublic static function format(Collection $collection, array $tabellisting, string $id, array $listing_search_additional_param = []): array\n\t{\n\t\t$tabellisting = $tabellisting[$id];\n\t\t$collection_data = $collection->get();\n\n\t\treturn [\n\t\t\t'id' => $id,\n\t\t\t'title' => $tabellisting['title'],\n\t\t\t'description' => $tabellisting['description'] ?? null,\n\t\t\t'icon' => $tabellisting['icon'] ?? null,\n\t\t\t'table' => [\n\t\t\t\t'th' => self::generateTableHeadings($tabellisting),\n\t\t\t\t'tr' => self::generateTableRows($collection_data['data']['list'], $tabellisting),\n\t\t\t],\n\t\t\t'pagination' => $collection_data['pagination'],\n\t\t\t'empty_msg' => $tabellisting['empty_msg'] ?? null,\n\t\t\t'total_entries' => ($collection->getPagination() instanceof Pagination) ? $collection->getPagination()->getEntries() : 0,\n\t\t\t'is_search' => $collection->getPagination() instanceof Pagination && $collection->getPagination()->isSearchResult(),\n\t\t\t'self_overview' => $tabellisting['self_overview'] ?? [],\n\t\t\t'available_columns' => self::getAvailableColumnsForListing($tabellisting),\n\t\t\t'no_search' => $tabellisting['no_search'] ?? false,\n\t\t\t'listing_search_additional_param' => $listing_search_additional_param,\n\t\t];\n\t}\n\n\tpublic static function formatFromArray(array $collection, array $tabellisting, string $id): array\n\t{\n\t\treturn [\n\t\t\t'id' => $id,\n\t\t\t'title' => $tabellisting['title'],\n\t\t\t'description' => $tabellisting['description'] ?? null,\n\t\t\t'icon' => $tabellisting['icon'] ?? null,\n\t\t\t'table' => [\n\t\t\t\t'th' => self::generateTableHeadings($tabellisting),\n\t\t\t\t'tr' => self::generateTableRows($collection['data'], $tabellisting),\n\t\t\t],\n\t\t\t'pagination' => $collection['pagination'],\n\t\t\t'empty_msg' => $tabellisting['empty_msg'] ?? null,\n\t\t\t'self_overview' => $tabellisting['self_overview'] ?? [],\n\t\t\t'available_columns' => self::getAvailableColumnsForListing($tabellisting),\n\t\t\t'no_search' => $tabellisting['no_search'] ?? false,\n\t\t];\n\t}\n\n\tprivate static function generateTableHeadings(array $tabellisting): array\n\t{\n\t\t$heading = [];\n\n\t\t// Table headings for columns\n\t\tforeach ($tabellisting['visible_columns'] as $visible_column) {\n\t\t\tif (isset($tabellisting['columns'][$visible_column]['visible']) && !$tabellisting['columns'][$visible_column]['visible']) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$heading[$visible_column] = [\n\t\t\t\t'text' => $tabellisting['columns'][$visible_column]['label'],\n\t\t\t\t'class' => $tabellisting['columns'][$visible_column]['class'] ?? null,\n\t\t\t];\n\t\t}\n\n\t\t// Table headings for actions\n\t\tif (isset($tabellisting['actions'])) {\n\t\t\t$heading['actions'] = [\n\t\t\t\t'text' => lng('panel.options'),\n\t\t\t\t'class' => 'text-end',\n\t\t\t];\n\t\t}\n\n\t\treturn $heading;\n\t}\n\n\t/**\n\t * @throws Exception\n\t */\n\tprivate static function generateTableRows(array $list, array $tabellisting): array\n\t{\n\t\t$rows = [];\n\n\t\t// Create new row from item\n\t\tforeach ($list as $row => $fields) {\n\t\t\t// Generate columns from item\n\t\t\tforeach ($tabellisting['visible_columns'] as $col => $visible_column) {\n\t\t\t\t// Continue if column is not visible\n\t\t\t\tif (isset($tabellisting['columns'][$visible_column]['visible']) && !$tabellisting['columns'][$visible_column]['visible']) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Get data from filed if it is defined\n\t\t\t\t$field = $tabellisting['columns'][$visible_column]['field'] ?? null;\n\t\t\t\t$data = $field ? self::getMultiArrayFromString($fields, $field) : null;\n\n\t\t\t\t// Call user function for given column if defined or return data from field, otherwise throw exception\n\t\t\t\t$callback = $tabellisting['columns'][$visible_column]['callback'] ?? null;\n\t\t\t\tif ($callback) {\n\t\t\t\t\t$rows[$row]['td'][$col]['data'] = call_user_func($callback, ['data' => $data, 'fields' => $fields]);\n\t\t\t\t} elseif ($field) {\n\t\t\t\t\t$rows[$row]['td'][$col]['data'] = $data;\n\t\t\t\t} else {\n\t\t\t\t\tthrow new InvalidArgumentException('The visible column \"' . $visible_column . '\" has neither a \"callback\" nor a \"field\" set.');\n\t\t\t\t}\n\n\t\t\t\t// Set class for table-row if defined\n\t\t\t\t$rows[$row]['td'][$col]['class'] = $tabellisting['columns'][$visible_column]['class'] ?? null;\n\t\t\t}\n\n\t\t\t// Set row classes from format_callback\n\t\t\tif (isset($tabellisting['format_callback'])) {\n\t\t\t\t$class = [];\n\t\t\t\tforeach ($tabellisting['format_callback'] as $format_callback) {\n\t\t\t\t\t$class[] = call_user_func($format_callback, ['fields' => $fields]);\n\t\t\t\t}\n\t\t\t\t$rows[$row]['class'] = implode(' ', $class);\n\t\t\t}\n\n\t\t\t// Set all actions for row\n\t\t\tif (isset($tabellisting['actions'])) {\n\t\t\t\t$actions = self::setLinks($tabellisting['actions'], $fields);\n\n\t\t\t\t$rows[$row]['td'][] = [\n\t\t\t\t\t'class' => 'text-end',\n\t\t\t\t\t'data' => [\n\t\t\t\t\t\t'macro' => 'actions',\n\t\t\t\t\t\t'data' => $actions\n\t\t\t\t\t]\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\n\t\treturn $rows;\n\t}\n\n\tpublic static function getMultiArrayFromString(array $arr, ?string $str)\n\t{\n\t\tforeach (explode('.', $str) as $key) {\n\t\t\tif (!array_key_exists($key, $arr)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t$arr = $arr[$key];\n\t\t}\n\n\t\treturn $arr;\n\t}\n\n\tprivate static function setLinks(array $actions, array $item): array\n\t{\n\t\t$linker = UI::getLinker();\n\n\t\t// Check each action for a href\n\t\tforeach ($actions as $key => $action) {\n\n\t\t\t// complete link built via callback\n\t\t\tif (isset($action['callback']) && !empty($action['callback'])) {\n\t\t\t\t$action = call_user_func($action['callback'], ['fields' => $item]);\n\t\t\t\t$actions[$key] = $action;\n\t\t\t}\n\n\t\t\t// Call user function if visible is an array\n\t\t\tif (isset($action['visible']) && is_array($action['visible'])) {\n\t\t\t\t$actions[$key]['visible'] = call_user_func($action['visible'], ['fields' => $item]);\n\t\t\t}\n\n\t\t\t// Set link if href is an array\n\t\t\tif (isset($action['href']) && is_array($action['href'])) {\n\t\t\t\t// Search for \"columns\" in our href array\n\t\t\t\tforeach ($action['href'] as $href_key => $href_value) {\n\t\t\t\t\t$length = strlen(':');\n\t\t\t\t\tif (substr($href_value, 0, $length) === ':') {\n\t\t\t\t\t\t$column = ltrim($href_value, ':');\n\t\t\t\t\t\t$action['href'][$href_key] = $item[$column];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Set actual link from linker\n\t\t\t\t$actions[$key]['href'] = $linker->getLink($action['href']);\n\t\t\t}\n\n\t\t\t// modal trigger - always require a valid callback\n\t\t\tif (isset($action['modal']) && !empty($action['modal'])) {\n\t\t\t\t$actions[$key]['modal'] = call_user_func($action['modal'], ['fields' => $item]);\n\t\t\t}\n\t\t}\n\n\t\treturn $actions;\n\t}\n\n\tprivate static function getAvailableColumnsForListing(array $tabellisting): array\n\t{\n\t\t$result = [];\n\t\tif (isset($tabellisting['columns'])) {\n\t\t\tforeach ($tabellisting['columns'] as $column => $coldata) {\n\t\t\t\tif (isset($coldata['visible']) && !$coldata['visible']) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t$result[$column] = [\n\t\t\t\t\t'label' => $coldata['label'],\n\t\t\t\t\t'checked' => in_array($column, $tabellisting['visible_columns']),\n\t\t\t\t\t'searchable' => $coldata['searchable'] ?? true,\n\t\t\t\t\t'isdefaultsearchfield' => $coldata['isdefaultsearchfield'] ?? false,\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * store column listing selection of user to database\n\t * the selection array should look like this:\n\t * [\n\t *     'section_name' => [\n\t *         'column_name',\n\t *         'column_name',\n\t *         'column_name'\n\t *     ]\n\t * ]\n\t *\n\t * @param array $tabellisting\n\t * @return array\n\t */\n\tpublic static function storeColumnListingForUser(array $tabellisting): array\n\t{\n\t\t$section = array_key_first($tabellisting);\n\t\tif (empty($section) || !is_array($tabellisting[$section]) || empty($tabellisting[$section])) {\n\t\t\tthrow new InvalidArgumentException(\"Invalid selection array for \" . __METHOD__);\n\t\t}\n\t\t$userid = 'customerid';\n\t\tif (CurrentUser::isAdmin()) {\n\t\t\t$userid = 'adminid';\n\t\t}\n\t\t// include all possible tablelisting-definitions to check for the right section\n\t\tforeach(glob(Froxlor::getInstallDir().'lib/tablelisting/{,*/}*.php', GLOB_BRACE) as $tbl_file) {\n\t\t\t$table_listings = include $tbl_file;\n\t\t\tif (!isset($table_listings[$section])) {\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$columns_available = array_keys($table_listings[$section]['columns']);\n\t\t// filter out unknown columns\n\t\tforeach ($tabellisting[$section] as $index => $column_changed) {\n\t\t\tif (!in_array($column_changed, $columns_available)) {\n\t\t\t\tunset($tabellisting[$section][$index]);\n\t\t\t}\n\t\t}\n\t\t// delete possible existing entry\n\t\tself::deleteColumnListingForUser($tabellisting);\n\t\t// add new entry\n\t\t$ins_stmt = Database::prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_USERCOLUMNS . \"` SET\n\t\t\t`\" . $userid . \"` = :uid,\n\t\t\t`section` = :section,\n\t\t\t`columns` = :cols\n\t\t\");\n\t\tDatabase::pexecute($ins_stmt, [\n\t\t\t'uid' => CurrentUser::getField($userid),\n\t\t\t'section' => $section,\n\t\t\t'cols' => json_encode($tabellisting[$section])\n\t\t]);\n\t\treturn $tabellisting[$section];\n\t}\n\n\t/**\n\t * delete column listing selection of user from database\n\t *\n\t * @param array $tabellisting\n\t * @return bool\n\t */\n\tpublic static function deleteColumnListingForUser(array $tabellisting): bool\n\t{\n\t\t$section = array_key_first($tabellisting);\n\t\tif (empty($section)) {\n\t\t\tthrow new InvalidArgumentException(\"Invalid selection array for \" . __METHOD__);\n\t\t}\n\t\t$userid = 'customerid';\n\t\tif (CurrentUser::isAdmin()) {\n\t\t\t$userid = 'adminid';\n\t\t}\n\t\t$del_stmt = Database::prepare(\"\n\t\t\tDELETE FROM `\" . TABLE_PANEL_USERCOLUMNS . \"` WHERE `\" . $userid . \"` = :uid AND `section` = :section\n\t\t\");\n\t\tDatabase::pexecute($del_stmt, ['uid' => CurrentUser::getField($userid), 'section' => $section]);\n\t\treturn true;\n\t}\n\n\tpublic static function getVisibleColumnsForListing(string $listing, array $default_columns): array\n\t{\n\t\t$userid = 'customerid';\n\t\tif (CurrentUser::isAdmin()) {\n\t\t\t$userid = 'adminid';\n\t\t}\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT `columns` FROM `\" . TABLE_PANEL_USERCOLUMNS . \"` WHERE `\" . $userid . \"` = :uid AND `section` = :section\n\t\t\");\n\t\t$columns_json = Database::pexecute_first($sel_stmt, [\n\t\t\t'uid' => CurrentUser::getField($userid),\n\t\t\t'section' => $listing\n\t\t]);\n\t\tif ($columns_json && isset($columns_json['columns'])) {\n\t\t\treturn json_decode($columns_json['columns'], true);\n\t\t}\n\t\treturn $default_columns;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Pagination.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nuse Froxlor\\Settings;\n\n/**\n * Class to manage pagination, limiting and ordering\n */\nclass Pagination\n{\n\tpublic string $sortorder = 'ASC';\n\tpublic $sortfield = null;\n\tprivate array $data = [];\n\tprivate ?array $fields = null;\n\tprivate ?string $searchtext = null;\n\n\tprivate $searchfield = null;\n\n\tprivate bool $is_search = false;\n\n\tprivate int $pageno = 0;\n\n\tprivate int $entries = 0;\n\n\tprivate int $perPage;\n\n\tprivate string $paginationAdditional = \"\";\n\n\t/**\n\t * Create new pagination object to search/filter, limit and sort Api-listing() calls\n\t *\n\t * @param array $fields\n\t * @param int $total_entries\n\t * @param int $perPage\n\t * @param array $default_sorting array of key=sortfield,value=sortorder for default sorting\n\t * @param array $pagination_additional_params\n\t */\n\tpublic function __construct(\n\t\tarray $fields = [],\n\t\tint   $total_entries = 0,\n\t\tint   $perPage = 20,\n\t\tarray $default_sorting = [],\n\t\tarray $pagination_additional_params = []\n\t)\n\t{\n\t\t$this->fields = $fields;\n\t\t$this->entries = $total_entries;\n\t\t$this->perPage = $perPage;\n\t\t$this->pageno = 1;\n\t\t// add default limitation by settings\n\t\tif (Settings::Get('panel.paging') > 0) {\n\t\t\t$this->addLimit(Settings::Get('panel.paging'));\n\t\t}\n\t\t// check search request\n\t\t$this->searchtext = '';\n\t\tif (count($fields) > 0) {\n\t\t\t$orderfields = array_keys($fields);\n\t\t\t$this->searchfield = $orderfields[0];\n\t\t}\n\t\t$searchtext = Request::any('searchtext');\n\t\tif (isset($searchtext) && (preg_match('/[-_@\\p{L}\\p{N}*.]+$/u',\t$searchtext) || $searchtext === '')) {\n\t\t\t$this->searchtext = trim($searchtext);\n\t\t}\n\t\t$searchfield = Request::any('searchfield');\n\t\tif (isset($searchfield) && isset($fields[$searchfield])) {\n\t\t\t$this->searchfield = $searchfield;\n\t\t}\n\t\tif (!empty($this->searchtext) && !empty($this->searchfield)) {\n\t\t\t$this->addSearch($this->searchtext, $this->searchfield);\n\t\t\t$pagination_additional_params[] = 'searchfield=' . $this->searchfield;\n\t\t\t$pagination_additional_params[] = 'searchtext=' . $this->searchtext;\n\t\t}\n\n\t\t// check other ordering requests\n\t\t$sortorder = Request::any('sortorder');\n\t\tif (!empty($sortorder) && (strtolower($sortorder) == 'desc' || strtolower($sortorder) == 'asc')) {\n\t\t\t$this->sortorder = strtoupper($sortorder);\n\t\t}\n\t\t$sortfield = Request::any('sortfield');\n\t\tif (!empty($sortfield) && isset($fields[$sortfield])) {\n\t\t\t$this->sortfield = $sortfield;\n\t\t\t$this->addOrderBy($this->sortfield, $this->sortorder);\n\t\t} else {\n\t\t\t// add default ordering by given order\n\t\t\tif (!empty($default_sorting)) {\n\t\t\t\twhile (!empty($default_sorting)) {\n\t\t\t\t\t$this->sortfield = array_key_first($default_sorting);\n\t\t\t\t\t$this->sortorder = array_shift($default_sorting) ?? $this->sortorder;\n\t\t\t\t\t$this->addOrderBy($this->sortfield, $this->sortorder);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// add default ordering by given fields\n\t\t\tif (count($fields) > 0 && empty($this->sortfield)) {\n\t\t\t\t$orderfields = array_keys($fields);\n\t\t\t\t$this->sortfield = $orderfields[0];\n\t\t\t\t$this->addOrderBy($orderfields[0], $this->sortorder);\n\t\t\t}\n\t\t}\n\n\t\t$this->checkPageNumber();\n\n\t\t// pagination additional parameters for url\n\t\tif (!empty($pagination_additional_params)) {\n\t\t\tforeach ($pagination_additional_params as $pap) {\n\t\t\t\t$this->paginationAdditional .= \"&\" . $pap;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate function checkPageNumber(): void\n\t{\n\t\t// check current page / pages\n\t\t$pageno = Request::any('pageno');\n\t\tif (!empty($pageno) && intval($pageno) != 0) {\n\t\t\t$this->pageno = intval($pageno);\n\t\t}\n\t\tif (($this->pageno - 1) * Settings::Get('panel.paging') > $this->entries) {\n\t\t\t$this->pageno = 1;\n\t\t}\n\t\t$this->addOffset(($this->pageno - 1) * Settings::Get('panel.paging'));\n\t}\n\n\t/**\n\t * add a limit\n\t *\n\t * @param int $limit\n\t *            optional, default 0\n\t *\n\t * @return Pagination\n\t */\n\tpublic function addLimit(int $limit = 0): Pagination\n\t{\n\t\t$this->data['sql_limit'] = $limit;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * add a search operation\n\t *\n\t * @param string|null $searchtext\n\t * @param string|null $field\n\t * @param string|null $operator\n\t *\n\t * @return Pagination\n\t */\n\tpublic function addSearch(?string $searchtext = null, string $field = null, string $operator = null): Pagination\n\t{\n\t\tif (!isset($this->data['sql_search'])) {\n\t\t\t$this->data['sql_search'] = [];\n\t\t}\n\t\t$this->data['sql_search'][$field] = [\n\t\t\t'value' => $searchtext,\n\t\t\t'op' => $operator\n\t\t];\n\t\t// remember this is a search (-result)\n\t\t$this->is_search = true;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * add a field for ordering\n\t *\n\t * @param string $field\n\t * @param string $order optional, default 'ASC'\n\t * @return Pagination\n\t */\n\tpublic function addOrderBy($field = null, $order = 'ASC'): Pagination\n\t{\n\t\tif (!isset($this->data['sql_orderby'])) {\n\t\t\t$this->data['sql_orderby'] = [];\n\t\t}\n\t\t$this->data['sql_orderby'][$field] = $order;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * add an offset\n\t *\n\t * @param int $offset optional, default 0\n\t * @return Pagination\n\t */\n\tpublic function addOffset(int $offset = 0): Pagination\n\t{\n\t\t$this->data['sql_offset'] = $offset;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * return number of total entries the user can access from the current resource\n\t *\n\t * @return number\n\t */\n\tpublic function getEntries(): int\n\t{\n\t\treturn $this->entries;\n\t}\n\n\tpublic function setEntries(int $entries): void\n\t{\n\t\t$this->entries = $entries;\n\t\t$this->checkPageNumber();\n\t}\n\n\tpublic function getApiCommandParams(): array\n\t{\n\t\treturn $this->data;\n\t}\n\n\tpublic function getApiResponseParams(): array\n\t{\n\t\treturn [\n\t\t\t'pagination' => [\n\t\t\t\t\"total\" => $this->entries,\n\t\t\t\t\"per_page\" => $this->perPage,\n\t\t\t\t\"current_page\" => $this->pageno,\n\t\t\t\t\"last_page\" => (Settings::Get('panel.paging') > 0) ? ceil($this->entries / $this->perPage) : -1,\n\t\t\t\t\"from\" => $this->data['sql_offset'] ?? null,\n\t\t\t\t\"to\" => min($this->data['sql_offset'] + $this->perPage, $this->entries),\n\t\t\t\t\"sortfields\" => array_keys($this->fields),\n\t\t\t\t\"link_additions\" => $this->paginationAdditional,\n\t\t\t]\n\t\t];\n\t}\n\n\tpublic function isSearchResult(): bool\n\t{\n\t\treturn $this->is_search;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Panel/CustomReflection.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI\\Panel;\n\nuse reflectionClass;\nuse RuntimeException;\nuse Twig\\Extension\\AbstractExtension;\nuse Twig\\TwigFunction;\n\nclass CustomReflection extends AbstractExtension\n{\n\n\t/**\n\t *\n\t * {@inheritdoc}\n\t */\n\tpublic function getFunctions()\n\t{\n\t\treturn [\n\t\t\tnew TwigFunction('call_static', [\n\t\t\t\t$this,\n\t\t\t\t'callStaticMethod'\n\t\t\t]),\n\t\t\tnew TwigFunction('get_static', [\n\t\t\t\t$this,\n\t\t\t\t'getStaticProperty'\n\t\t\t])\n\t\t];\n\t}\n\n\tpublic function callStaticMethod($class, $method, array $args = [])\n\t{\n\t\t$refl = new reflectionClass($class);\n\t\t// Check that method is static AND public\n\t\tif ($refl->hasMethod($method) && $refl->getMethod($method)->isStatic() && $refl->getMethod($method)->isPublic()) {\n\t\t\treturn call_user_func_array($class . '::' . $method, $args);\n\t\t}\n\t\tthrow new RuntimeException(sprintf('Invalid static method call for class %s and method %s', $class, $method));\n\t}\n\n\tpublic function getStaticProperty($class, $property)\n\t{\n\t\t$refl = new reflectionClass($class);\n\t\t// Check that property is static AND public\n\t\tif ($refl->hasProperty($property) && $refl->getProperty($property)->isStatic() && $refl->getProperty($property)->isPublic()) {\n\t\t\treturn $refl->getProperty($property)->getValue();\n\t\t}\n\t\tthrow new RuntimeException(sprintf('Invalid static property get for class %s and property %s', $class, $property));\n\t}\n\n\t/**\n\t *\n\t * {@inheritdoc}\n\t */\n\tpublic function getName()\n\t{\n\t\treturn 'customreflection';\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Panel/FroxlorTwig.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\ndeclare(strict_types=1);\n\nnamespace Froxlor\\UI\\Panel;\n\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Markdown;\nuse Parsedown;\nuse Twig\\Extension\\AbstractExtension;\nuse Twig\\TwigFilter;\nuse Twig\\TwigFunction;\nuse Twig\\TwigTest;\n\nclass FroxlorTwig extends AbstractExtension\n{\n\n\tpublic function getFilters()\n\t{\n\t\treturn [\n\t\t\tnew TwigFilter('formatBytes', [\n\t\t\t\t$this,\n\t\t\t\t'formatBytesFilter'\n\t\t\t]),\n\t\t\tnew TwigFilter('formatIP', [\n\t\t\t\t$this,\n\t\t\t\t'formatIPFilter'\n\t\t\t]),\n\t\t\tnew TwigFilter('idnDecode', [\n\t\t\t\t$this,\n\t\t\t\t'idnDecodeFilter'\n\t\t\t]),\n\t\t\tnew TwigFilter('markdown', [\n\t\t\t\t$this,\n\t\t\t\t'callMarkdown'\n\t\t\t])\n\t\t];\n\t}\n\n\tpublic function getTests()\n\t{\n\t\treturn [\n\t\t\tnew TwigTest('numeric', function ($value) {\n\t\t\t\treturn is_numeric($value);\n\t\t\t})\n\t\t];\n\t}\n\n\tpublic function getFunctions()\n\t{\n\t\treturn [\n\t\t\tnew TwigFunction('get_setting', [\n\t\t\t\t$this,\n\t\t\t\t'getSetting'\n\t\t\t]),\n\t\t\tnew TwigFunction('get_config', [\n\t\t\t\t$this,\n\t\t\t\t'getConfig'\n\t\t\t]),\n\t\t\tnew TwigFunction('lng', [\n\t\t\t\t$this,\n\t\t\t\t'getLang'\n\t\t\t]),\n\t\t\tnew TwigFunction('linker', [\n\t\t\t\t$this,\n\t\t\t\t'getLink'\n\t\t\t]),\n\t\t\tnew TwigFunction('mix', [\n\t\t\t\t$this,\n\t\t\t\t'getMix'\n\t\t\t]),\n\t\t\tnew TwigFunction('vite', [\n\t\t\t\t$this,\n\t\t\t\t'getVite'\n\t\t\t])\n\t\t];\n\t}\n\n\tpublic function formatBytesFilter($size, $suffix = \"B\", $factor = 1)\n\t{\n\t\t$size *= $factor;\n\t\t$units = [\n\t\t\t'',\n\t\t\t'K',\n\t\t\t'M',\n\t\t\t'G',\n\t\t\t'T',\n\t\t\t'P',\n\t\t\t'E',\n\t\t\t'Z',\n\t\t\t'Y'\n\t\t];\n\t\t$power = $size > 0 ? floor(log($size, 1024)) : 0;\n\t\tif ($power < 0) {\n\t\t\t$size = 0.00;\n\t\t\t$power = 0;\n\t\t}\n\t\treturn number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $units[$power] . $suffix;\n\t}\n\n\tpublic function formatIPFilter($addr)\n\t{\n\t\treturn inet_ntop(inet_pton($addr));\n\t}\n\n\tpublic function idnDecodeFilter($entity)\n\t{\n\t\t$idna_convert = new IdnaWrapper();\n\t\treturn $idna_convert->decode($entity);\n\t}\n\n\tpublic function getSetting($setting = null)\n\t{\n\t\treturn Settings::Get($setting);\n\t}\n\n\tpublic function getConfig($config = null)\n\t{\n\t\treturn Settings::Config($config);\n\t}\n\n\tpublic function getLang($identifier = null, array $arguments = [])\n\t{\n\t\treturn lng($identifier, $arguments);\n\t}\n\n\tpublic function getLink($linkopts)\n\t{\n\t\treturn UI::getLinker()->getLink($linkopts);\n\t}\n\n\tpublic function callMarkdown($string): string\n\t{\n\t\treturn Markdown::cleanCustomNotes($string ?? \"\");\n\t}\n\n\t/**\n\t *\n\t * {@inheritdoc}\n\t */\n\tpublic function getName()\n\t{\n\t\treturn 'froxlortwig';\n\t}\n\n\tpublic function getMix($mix = '')\n\t{\n\t\treturn mix($mix);\n\t}\n\n\tpublic function getVite($basehref = '', $vite = [], $defaults = [])\n\t{\n\t\treturn vite($basehref, $vite ?? $defaults);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Panel/UI.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\ndeclare(strict_types=1);\n\nnamespace Froxlor\\UI\\Panel;\n\nuse DirectoryIterator;\nuse Exception;\nuse Froxlor\\CurrentUser;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Linker;\nuse Twig\\Environment;\nuse Twig\\Extension\\DebugExtension;\nuse Twig\\Loader\\FilesystemLoader;\n\nclass UI\n{\n\n\t/**\n\t * twig object\n\t *\n\t * @var Environment\n\t */\n\tprivate static $twig = null;\n\n\t/**\n\t * twig buffer\n\t *\n\t * @var array\n\t */\n\tprivate static $twigbuf = [];\n\n\t/**\n\t * linker class object\n\t */\n\tprivate static $linker = null;\n\n\t/**\n\t * current logged in user\n\t *\n\t * @var array\n\t */\n\tprivate static $userinfo = [];\n\n\t/**\n\t * default fallback theme\n\t *\n\t * @var string\n\t */\n\tprivate static $default_theme = 'Froxlor';\n\n\tprivate static $install_mode = false;\n\n\tpublic static function requestIsHttps(): bool\n\t{\n\t\t$isHttps =\n\t\t\t$_SERVER['HTTPS']\n\t\t\t?? $_SERVER['REQUEST_SCHEME']\n\t\t\t?? $_SERVER['HTTP_X_FORWARDED_PROTO']\n\t\t\t?? null;\n\n\t\treturn $isHttps && (strcasecmp('on', $isHttps) == 0 || strcasecmp('https', $isHttps) == 0);\n\t}\n\n\t/**\n\t * Extract the cookie host from HTTP_HOST, stripping the port.\n\t */\n\tpublic static function getCookieHost(): ?string\n\t{\n\t\tif (empty($_SERVER['HTTP_HOST']))\n\t\t\treturn null;\n\n\t\t$colonPosition = strrpos($_SERVER['HTTP_HOST'], ':');\n\t\t// There's no port in the host\n\t\tif ($colonPosition === false)\n\t\t\treturn $_SERVER['HTTP_HOST'];\n\n\t\t$closingSquareBracketPosition = strrpos($_SERVER['HTTP_HOST'], ']');\n\t\t// The host is an IPv4 address or hostname with port\n\t\tif ($closingSquareBracketPosition === false)\n\t\t\treturn substr($_SERVER['HTTP_HOST'], 0, $colonPosition);\n\n\t\t// The host is an IPv6 address with port\n\t\treturn substr($_SERVER['HTTP_HOST'], 0, $closingSquareBracketPosition + 1);\n\t}\n\n\t/**\n\t * send various security related headers\n\t */\n\tpublic static function sendHeaders()\n\t{\n\t\tsession_set_cookie_params([\n\t\t\t'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php\n\t\t\t'path' => '/',\n\t\t\t'domain' => self::getCookieHost(),\n\t\t\t'secure' => self::requestIsHttps(),\n\t\t\t'httponly' => true,\n\t\t\t'samesite' => 'Lax'\n\t\t]);\n\t\tsession_start();\n\n\t\theader(\"Content-Type: text/html; charset=UTF-8\");\n\n\t\t// prevent Froxlor pages from being cached\n\t\theader(\"Cache-Control: no-store, no-cache, must-revalidate\");\n\t\theader(\"Pragma: no-cache\");\n\t\theader('Last-Modified: ' . gmdate('D, d M Y H:i:s \\G\\M\\T', time()));\n\t\theader('Expires: ' . gmdate('D, d M Y H:i:s \\G\\M\\T', time()));\n\n\t\t// Prevent inline - JS to be executed (i.e. XSS) in browsers which support this,\n\t\t// Inline-JS is no longer allowed and used\n\t\t// See: http://people.mozilla.org/~bsterne/content-security-policy/index.html\n\t\t// New stuff see: https://www.owasp.org/index.php/List_of_useful_HTTP_headers and https://www.owasp.org/index.php/Content_Security_Policy\n\t\t$csp_content = \"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; object-src 'self'; frame-src 'self'; frame-ancestors 'self';\";\n\t\theader(\"Content-Security-Policy: \" . $csp_content);\n\t\theader(\"X-Content-Security-Policy: \" . $csp_content);\n\t\theader(\"X-WebKit-CSP: \" . $csp_content);\n\n\t\t// Don't allow to load Froxlor in an iframe to prevent i.e. clickjacking\n\t\theader(\"X-Frame-Options: DENY\");\n\n\t\t// Internet Explorer shall not guess the Content-Type, see:\n\t\t// http://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx\n\t\theader(\"X-Content-Type-Options: nosniff\");\n\n\t\t// ensure that default timezone is set\n\t\tif (function_exists(\"date_default_timezone_set\") && function_exists(\"date_default_timezone_get\")) {\n\t\t\t@date_default_timezone_set(@date_default_timezone_get());\n\t\t}\n\t}\n\n\tpublic static function sendSslHeaders()\n\t{\n\t\t/**\n\t\t * If Froxlor was called via HTTPS -> enforce it for the next time by settings HSTS header according to settings\n\t\t */\n\t\tif (isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) {\n\t\t\t$maxage = Settings::Get('system.hsts_maxage');\n\t\t\tif (empty($maxage)) {\n\t\t\t\t$maxage = 0;\n\t\t\t}\n\t\t\t$hsts_header = \"Strict-Transport-Security: max-age=\" . $maxage;\n\t\t\tif (Settings::Get('system.hsts_incsub') == '1') {\n\t\t\t\t$hsts_header .= \"; includeSubDomains\";\n\t\t\t}\n\t\t\tif (Settings::Get('system.hsts_preload') == '1') {\n\t\t\t\t$hsts_header .= \"; preload\";\n\t\t\t}\n\t\t\theader($hsts_header);\n\t\t}\n\t}\n\n\t/**\n\t * initialize Twig template engine\n\t */\n\tpublic static function initTwig(bool $install_mode = false)\n\t{\n\t\tself::$install_mode = $install_mode;\n\t\t// init twig template engine\n\t\t$loader = new FilesystemLoader(Froxlor::getInstallDir() . '/templates/');\n\t\t$twig_params = [\n\t\t\t'auto_reload' => true,\n\t\t\t'debug' => false,\n\t\t];\n\t\tif (is_writable(Froxlor::getInstallDir() . '/cache')) {\n\t\t\t$twig_params['cache'] = Froxlor::getInstallDir() . '/cache';\n\t\t}\n\t\tself::$twig = new Environment($loader, $twig_params);\n\t\tself::$twig->addExtension(new DebugExtension());\n\t\tself::$twig->addExtension(new CustomReflection());\n\t\tself::$twig->addExtension(new FroxlorTwig());\n\t\t// empty buffer\n\t\tself::$twigbuf = [];\n\t}\n\n\t/**\n\t * twig wrapper\n\t *\n\t * @return Environment\n\t */\n\tpublic static function twig(): ?Environment\n\t{\n\t\treturn self::$twig;\n\t}\n\n\tpublic static function getLinker(): Linker\n\t{\n\t\treturn self::$linker;\n\t}\n\n\tpublic static function setLinker($linker = null)\n\t{\n\t\tself::$linker = $linker;\n\t}\n\n\tpublic static function setCurrentUser($userinfo = null)\n\t{\n\t\tself::$userinfo = $userinfo;\n\t}\n\n\tpublic static function getCurrentUser(): array\n\t{\n\t\treturn self::$userinfo;\n\t}\n\n\t/**\n\t * returns an array of available themes\n\t *\n\t * @return array\n\t * @throws Exception\n\t */\n\tpublic static function getThemes(): array\n\t{\n\t\t$themespath = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/templates/');\n\t\t$themes_available = [];\n\n\t\tif (is_dir($themespath)) {\n\t\t\t$its = new DirectoryIterator($themespath);\n\n\t\t\tforeach ($its as $it) {\n\t\t\t\tif ($it->isDir() && $it->getFilename() != '.' && $it->getFilename() != '..' && $it->getFilename() != 'misc') {\n\t\t\t\t\t$theme = $themespath . $it->getFilename();\n\t\t\t\t\tif (file_exists($theme . '/config.json')) {\n\t\t\t\t\t\t$themeconfig = json_decode(file_get_contents($theme . '/config.json'), true);\n\t\t\t\t\t\tif (array_key_exists('variants', $themeconfig) && is_array($themeconfig['variants'])) {\n\t\t\t\t\t\t\tforeach ($themeconfig['variants'] as $variant => $data) {\n\t\t\t\t\t\t\t\tif ($variant == \"default\") {\n\t\t\t\t\t\t\t\t\t$themes_available[$it->getFilename()] = $it->getFilename();\n\t\t\t\t\t\t\t\t} elseif (array_key_exists('description', $data)) {\n\t\t\t\t\t\t\t\t\t$themes_available[$it->getFilename() . '_' . $variant] = $data['description'];\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t$themes_available[$it->getFilename() . '_' . $variant] = $it->getFilename() . ' (' . $variant . ')';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$themes_available[$it->getFilename()] = $it->getFilename();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $themes_available;\n\t}\n\n\tpublic static function view($name, array $context = [])\n\t{\n\t\tself::twigBuffer($name, $context);\n\t\tself::twigOutputBuffer();\n\t}\n\n\t/**\n\t * wrapper for twig's \"render\" function to buffer the output\n\t *\n\t * @see \\Twig\\Environment::render()\n\t */\n\tpublic static function twigBuffer($name, array $context = [])\n\t{\n\t\t$template_file = self::validateThemeTemplate($name);\n\n\t\tself::$twigbuf[] = [\n\t\t\t$template_file => $context\n\t\t];\n\t}\n\n\tpublic static function validateThemeTemplate(string $name, string $theme = \"\")\n\t{\n\t\tif (empty(trim($theme))) {\n\t\t\t$theme = self::getTheme();\n\t\t}\n\t\t$template_file = $theme . '/' . $name;\n\t\tif (!file_exists(Froxlor::getInstallDir() . '/templates/' . $template_file)) {\n\t\t\tPhpHelper::phpErrHandler(E_USER_WARNING, \"Template '\" . $template_file . \"' could not be found, trying fallback theme\", __FILE__, __LINE__);\n\t\t\t$template_file = self::$default_theme . '/'. $name;\n\t\t\tif (!file_exists(Froxlor::getInstallDir() . '/templates/' . $template_file)) {\n\t\t\t\tPhpHelper::phpErrHandler(E_USER_ERROR, \"Unknown template '\" . $template_file . \"'\", __FILE__, __LINE__);\n\t\t\t}\n\t\t}\n\t\treturn $template_file;\n\t}\n\n\tpublic static function getTheme()\n\t{\n\t\t// fallback\n\t\t$theme = self::$default_theme;\n\t\tif (!self::$install_mode) {\n\t\t\t// system default\n\t\t\tif (Froxlor::versionCompare2(Settings::Get('panel.version'), '2.0.0-beta1') == -1) {\n\t\t\t\t// pre 2.0\n\t\t\t\tSettings::Set('panel.default_theme', 'Froxlor');\n\t\t\t} else {\n\t\t\t\t$theme = (Settings::Get('panel.default_theme') !== null) ? Settings::Get('panel.default_theme') : $theme;\n\t\t\t\t// customer theme\n\t\t\t\tif (CurrentUser::hasSession() && CurrentUser::getField('theme') != $theme) {\n\t\t\t\t\t$theme = CurrentUser::getField('theme');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// check for template-variant\n\t\tif (preg_match(\"/([a-z0-9.\\-]+)_([a-z0-9.\\-]+)/i\", $theme, $matches)) {\n\t\t\t$theme = $matches[1];\n\t\t}\n\t\tif (!file_exists(Froxlor::getInstallDir() . '/templates/' . $theme)) {\n\t\t\tPhpHelper::phpErrHandler(E_USER_WARNING, \"Theme '\" . $theme . \"' could not be found.\", __FILE__, __LINE__);\n\t\t\t$theme = self::$default_theme;\n\t\t}\n\t\treturn $theme;\n\t}\n\n\t/**\n\t * echo output buffer and empty buffer-content\n\t */\n\tpublic static function twigOutputBuffer()\n\t{\n\t\t$output = \"\";\n\t\tforeach (self::$twigbuf as $buf) {\n\t\t\tforeach ($buf as $name => $context) {\n\t\t\t\ttry {\n\t\t\t\t\t$output .= self::$twig->render($name, $context);\n\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t// whoops, template error\n\t\t\t\t\t$errtpl = 'alert_nosession.html.twig';\n\t\t\t\t\tif (self::activeUserSession()) {\n\t\t\t\t\t\t$errtpl = 'alert.html.twig';\n\t\t\t\t\t}\n\t\t\t\t\t$edata = [\n\t\t\t\t\t\t'type' => \"danger\",\n\t\t\t\t\t\t'heading' => \"Template error\",\n\t\t\t\t\t\t'alert_msg' => $e->getMessage(),\n\t\t\t\t\t\t'alert_info' => $e->getTraceAsString()\n\t\t\t\t\t];\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// try with user theme if set\n\t\t\t\t\t\t$output .= self::$twig->render(self::getTheme() . '/misc/' . $errtpl, $edata);\n\t\t\t\t\t} catch (Exception $e) {\n\t\t\t\t\t\t// try with default theme if different from user theme\n\t\t\t\t\t\tif (self::getTheme() != self::$default_theme) {\n\t\t\t\t\t\t\t$output .= self::$twig->render(self::$default_theme . '/misc/' . $errtpl, $edata);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow $e;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\techo $output;\n\t\t// empty buffer\n\t\tself::$twigbuf = [];\n\t}\n\n\tpublic static function activeUserSession(): bool\n\t{\n\t\treturn !empty(self::$userinfo);\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Panel/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/UI/Request.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nuse Froxlor\\PhpHelper;\nuse voku\\helper\\AntiXSS;\n\nclass Request\n{\n\tprivate static $cleaned = false;\n\n\t/**\n\t * Get key from current $_GET or $_POST request.\n\t *\n\t * @param $key\n\t * @param mixed|null $default\n\t * @return mixed|string|null\n\t */\n\tpublic static function any($key, $default = null)\n\t{\n\t\tself::cleanAll();\n\n\t\treturn $_GET[$key] ?? $_POST[$key] ?? $default;\n\t}\n\n\t/**\n\t * Get key from current $_GET request.\n\t *\n\t * @param $key\n\t * @param mixed|null $default\n\t * @return mixed|string|null\n\t */\n\tpublic static function get($key, $default = null)\n\t{\n\t\tself::cleanAll();\n\n\t\treturn $_GET[$key] ?? $default;\n\t}\n\n\t/**\n\t * Get key from current $_POST request.\n\t *\n\t * @param $key\n\t * @param mixed|null $default\n\t * @return mixed|string|null\n\t */\n\tpublic static function post($key, $default = null)\n\t{\n\t\tself::cleanAll();\n\n\t\treturn $_POST[$key] ?? $default;\n\t}\n\n\t/**\n\t * return complete $_POST array\n\t *\n\t * @return array\n\t */\n\tpublic static function postAll()\n\t{\n\t\tself::cleanAll();\n\n\t\treturn $_POST ?? [];\n\t}\n\n\t/**\n\t * Check for xss attempts and clean important globals and\n\t * unsetting every variable registered in $_REQUEST and as variable itself\n\t */\n\tpublic static function cleanAll()\n\t{\n\t\tif (!self::$cleaned) {\n\t\t\tforeach ($_REQUEST as $key => $value) {\n\t\t\t\tif (isset($$key)) {\n\t\t\t\t\tunset($$key);\n\t\t\t\t}\n\t\t\t}\n\t\t\tunset($value);\n\n\t\t\t$antiXss = new AntiXSS();\n\t\t\t$antiXss->addNeverAllowedRegex([\n\t\t\t\t'{{(.*)}}' => ''\n\t\t\t]);\n\n\t\t\t// check $_GET\n\t\t\tPhpHelper::cleanGlobal($_GET, $antiXss);\n\t\t\t// check $_POST\n\t\t\tPhpHelper::cleanGlobal($_POST, $antiXss);\n\t\t\t// check $_COOKIE\n\t\t\tPhpHelper::cleanGlobal($_COOKIE, $antiXss);\n\n\t\t\tself::$cleaned = true;\n\t\t}\n\t}\n\n\t/**\n\t * Check if key is existing in current request.\n\t *\n\t * @param $key\n\t * @return bool|mixed\n\t */\n\tpublic static function exist($key)\n\t{\n\t\treturn (bool)$_GET[$key] ?? $_POST[$key] ?? false;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/Response.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\UI;\n\nuse Exception;\nuse Froxlor\\UI\\Panel\\UI;\n\nclass Response\n{\n\n\t/**\n\t * Sends a header ( 'Location ...' ) to the browser.\n\t *\n\t * @param string $destination\n\t *            Destination\n\t * @param array $get_variables\n\t *            Get-Variables\n\t * @param boolean $isRelative\n\t *            if the target we are creating for a redirect\n\t *            should be a relative or an absolute url\n\t *\n\t * @return boolean false if params is not an array\n\t */\n\tpublic static function redirectTo($destination, $get_variables = null, $isRelative = true)\n\t{\n\t\tif (is_array($get_variables)) {\n\t\t\t$linker = new Linker($destination);\n\n\t\t\tforeach ($get_variables as $key => $value) {\n\t\t\t\t$linker->add($key, $value);\n\t\t\t}\n\n\t\t\tif ($isRelative) {\n\t\t\t\t$linker->protocol = '';\n\t\t\t\t$linker->hostname = '';\n\t\t\t\t$path = './';\n\t\t\t} else {\n\t\t\t\tif (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') {\n\t\t\t\t\t$linker->protocol = 'https';\n\t\t\t\t} else {\n\t\t\t\t\t$linker->protocol = 'http';\n\t\t\t\t}\n\n\t\t\t\t$linker->hostname = $_SERVER['HTTP_HOST'];\n\n\t\t\t\tif (dirname($_SERVER['PHP_SELF']) == '/') {\n\t\t\t\t\t$path = '/';\n\t\t\t\t} else {\n\t\t\t\t\t$path = dirname($_SERVER['PHP_SELF']) . '/';\n\t\t\t\t}\n\t\t\t\t$linker->filename = $path . $destination;\n\t\t\t}\n\t\t\theader('Location: ' . $linker->getLink());\n\t\t\texit;\n\t\t} elseif ($get_variables == null) {\n\t\t\t$linker = new Linker($destination);\n\t\t\theader('Location: ' . $linker->getLink());\n\t\t\texit;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Prints one or more errormessages on screen\n\t *\n\t * @param array $errors\n\t *            Errormessages\n\t * @param string $replacer\n\t *            A %s in the errormessage will be replaced by this string.\n\t * @param bool $throw_exception\n\t *\n\t * @throws Exception\n\t * @author Ron Brand <ron.brand@web.de>\n\t * @author Florian Lippert <flo@syscp.org> (2003-2009)\n\t */\n\tpublic static function standardError($errors = '', $replacer = '', $throw_exception = false)\n\t{\n\t\t$_SESSION['requestData'] = $_POST;\n\t\t$replacer = htmlentities($replacer);\n\n\t\tif (!is_array($errors)) {\n\t\t\t$errors = [\n\t\t\t\t$errors\n\t\t\t];\n\t\t}\n\n\t\t$link_ref = '';\n\t\tif (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) !== false) {\n\t\t\t$link_ref = htmlentities($_SERVER['HTTP_REFERER']);\n\t\t}\n\n\t\t$error = '';\n\t\tforeach ($errors as $single_error) {\n\t\t\tif (strpos($single_error, \".\") === false) {\n\t\t\t\t$single_error = 'error.' . $single_error;\n\t\t\t}\n\t\t\t$single_error = lng($single_error, [htmlentities($replacer)]);\n\t\t\tif (empty($error)) {\n\t\t\t\t$error = $single_error;\n\t\t\t} else {\n\t\t\t\t$error .= ' ' . $single_error;\n\t\t\t}\n\t\t}\n\n\t\tif ($throw_exception) {\n\t\t\tthrow new Exception(strip_tags($error), 400);\n\t\t}\n\t\tUI::view('misc/alert.html.twig', [\n\t\t\t'type' => 'danger',\n\t\t\t'btntype' => 'light',\n\t\t\t'heading' => lng('error.error'),\n\t\t\t'alert_msg' => $error,\n\t\t\t'redirect_link' => $link_ref\n\t\t]);\n\t\texit;\n\t}\n\n\tpublic static function dynamicError($message, bool $nosession = false)\n\t{\n\t\t$_SESSION['requestData'] = $_POST;\n\t\t$link_ref = '';\n\t\tif (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) !== false) {\n\t\t\t$link_ref = htmlentities($_SERVER['HTTP_REFERER']);\n\t\t}\n\n\t\t$tpl = $nosession ? 'misc/alert_nosession.html.twig' : 'misc/alert.html.twig';\n\t\tUI::view($tpl, [\n\t\t\t'type' => 'danger',\n\t\t\t'btntype' => 'light',\n\t\t\t'heading' => lng('error.error'),\n\t\t\t'alert_msg' => $message,\n\t\t\t'redirect_link' => $link_ref\n\t\t]);\n\t\texit;\n\t}\n\n\t/**\n\t * Prints one or more errormessages on screen\n\t *\n\t * @param array $success_message\n\t *            Errormessages\n\t * @param string $replacer\n\t *            A %s in the errormessage will be replaced by this string.\n\t * @param array $params\n\t * @param bool $throw_exception\n\t *\n\t * @throws Exception\n\t * @author Florian Lippert <flo@syscp.org> (2003-2009)\n\t */\n\tpublic static function standardSuccess($success_message = '', $replacer = '', $params = [], $throw_exception = false)\n\t{\n\t\tif (strpos($success_message, \".\") === false) {\n\t\t\t$success_message = 'success.' . $success_message;\n\t\t}\n\t\t$success_message = lng($success_message, [htmlentities($replacer)]);\n\n\t\tif ($throw_exception) {\n\t\t\tthrow new Exception(strip_tags($success_message), 200);\n\t\t}\n\n\t\tif (is_array($params) && isset($params['filename'])) {\n\t\t\t$redirect_url = $params['filename'];\n\t\t\tunset($params['filename']);\n\n\t\t\t$first = true;\n\t\t\tforeach ($params as $varname => $value) {\n\t\t\t\tif ($value != '') {\n\t\t\t\t\t$redirect_url .= ($first ? '?' : '&amp;') . $varname . '=' . $value;\n\t\t\t\t\tif ($first) {\n\t\t\t\t\t\t$first = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t$redirect_url = '';\n\t\t}\n\n\t\tUI::view('misc/alert.html.twig', [\n\t\t\t'type' => 'success',\n\t\t\t'btntype' => 'light',\n\t\t\t'heading' => lng('success.success'),\n\t\t\t'alert_msg' => $success_message,\n\t\t\t'redirect_link' => $redirect_url\n\t\t]);\n\t\texit;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/UI/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/User.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Froxlor\\Database\\Database;\nuse PDO;\n\nclass User\n{\n\t/**\n\t * Returns full style user details \"Name, Firstname | Company\"\n\t *\n\t * @param array An array with keys firstname, name and company\n\t * @return string The full details\n\t *\n\t * @author Florian Lippert <flo@syscp.org> (2003-2009)\n\t */\n\tpublic static function getCorrectFullUserDetails($userinfo, $html = false): string\n\t{\n\t\t$returnval = '';\n\n\t\tif (isset($userinfo['firstname']) && isset($userinfo['name']) && isset($userinfo['company'])) {\n\t\t\tif ($userinfo['company'] == '') {\n\t\t\t\t$returnval = $userinfo['name'] . ', ' . $userinfo['firstname'];\n\t\t\t} else {\n\t\t\t\tif ($userinfo['name'] != '' && $userinfo['firstname'] != '') {\n\t\t\t\t\tif ($html) {\n\t\t\t\t\t\t$returnval = $userinfo['name'] . ', ' . $userinfo['firstname'] . '<br><small>' . $userinfo['company'] . '</small>';\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$returnval = $userinfo['name'] . ', ' . $userinfo['firstname'] . ', ' . $userinfo['company'];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$returnval = $userinfo['company'];\n\t\t\t\t}\n\t\t\t}\n\t\t} elseif (isset($userinfo['name'])) {\n\t\t\t$returnval = $userinfo['name'];\n\t\t}\n\n\t\treturn $returnval;\n\t}\n\n\t/**\n\t * Returns correct user salutation, either \"Firstname Name\" or \"Company\"\n\t *\n\t * @param array An array with keys firstname, name and company\n\t * @return string The correct salutation\n\t *\n\t * @author Florian Lippert <flo@syscp.org> (2003-2009)\n\t */\n\tpublic static function getCorrectUserSalutation($userinfo)\n\t{\n\t\t$returnval = '';\n\n\t\tif (isset($userinfo['firstname']) && isset($userinfo['name']) && isset($userinfo['company'])) {\n\t\t\t// Always prefer firstname name\n\n\t\t\tif ($userinfo['company'] != '' && $userinfo['name'] == '' && $userinfo['firstname'] == '') {\n\t\t\t\t$returnval = $userinfo['company'];\n\t\t\t} else {\n\t\t\t\t$returnval = $userinfo['firstname'] . ' ' . $userinfo['name'];\n\t\t\t}\n\t\t}\n\n\t\treturn $returnval;\n\t}\n\n\t/**\n\t * Function which updates all counters of used resources in panel_admins and panel_customers\n\t *\n\t * @param bool $returndebuginfo Set to true to get an array with debug information\n\t * @return array Contains debug information if parameter 'returndebuginfo' is set to true\n\t *\n\t * @author Florian Lippert <flo@syscp.org> (2003-2009)\n\t * @author Froxlor team <team@froxlor.org> (2010-)\n\t */\n\tpublic static function updateCounters($returndebuginfo = false)\n\t{\n\t\t$returnval = [];\n\n\t\tif ($returndebuginfo === true) {\n\t\t\t$returnval = [\n\t\t\t\t'admins' => [],\n\t\t\t\t'customers' => []\n\t\t\t];\n\t\t}\n\n\t\t// Customers\n\t\t$customers_stmt = Database::prepare('SELECT * FROM `' . TABLE_PANEL_CUSTOMERS . '` ORDER BY `customerid`');\n\t\tDatabase::pexecute($customers_stmt);\n\t\t// array to store currently used resources per admin\n\t\t$admin_resources = [];\n\t\twhile ($customer = $customers_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t// set current admin\n\t\t\t$cur_adm = $customer['adminid'];\n\t\t\t// initialize admin-resources array for admin $customer['adminid']\n\t\t\tif (!isset($admin_resources[$cur_adm])) {\n\t\t\t\t$admin_resources[$cur_adm] = [];\n\t\t\t}\n\n\t\t\t// fill admin resource usage array with customer data\n\t\t\tself::addResourceCountEx($admin_resources[$cur_adm], $customer, 'diskspace_used', 'diskspace');\n\t\t\tself::addResourceCountEx($admin_resources[$cur_adm], $customer, 'traffic_used', 'traffic_used'); // !!! yes, USED and USED\n\n\t\t\tforeach ([\n\t\t\t\t'mysqls',\n\t\t\t\t'ftps',\n\t\t\t\t'emails',\n\t\t\t\t'email_accounts',\n\t\t\t\t'email_forwarders',\n\t\t\t\t'email_quota',\n\t\t\t\t'subdomains'\n\t\t\t] as $field) {\n\t\t\t\tself::addResourceCount($admin_resources[$cur_adm], $customer, $field . '_used', $field);\n\t\t\t}\n\n\t\t\t// calculate real usage\n\t\t\t$customer_mysqls_stmt = Database::prepare('SELECT COUNT(*) AS `number_mysqls` FROM `' . TABLE_PANEL_DATABASES . '`\n\t\t\tWHERE `customerid` = :cid');\n\t\t\t$customer_mysqls = Database::pexecute_first($customer_mysqls_stmt, [\n\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t]);\n\t\t\t$customer['mysqls_used_new'] = (int)$customer_mysqls['number_mysqls'];\n\n\t\t\t$customer_emails_stmt = Database::prepare('SELECT COUNT(*) AS `number_emails` FROM `' . TABLE_MAIL_VIRTUAL . '`\n\t\t\tWHERE `customerid` = :cid');\n\t\t\t$customer_emails = Database::pexecute_first($customer_emails_stmt, [\n\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t]);\n\t\t\t$customer['emails_used_new'] = (int)$customer_emails['number_emails'];\n\n\t\t\t$customer_emails_result_stmt = Database::prepare('SELECT `email`, `email_full`, `destination`, `popaccountid` FROM `' . TABLE_MAIL_VIRTUAL . '`\n\t\t\tWHERE `customerid` = :cid');\n\t\t\tDatabase::pexecute($customer_emails_result_stmt, [\n\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t]);\n\t\t\t$customer_email_forwarders = 0;\n\t\t\t$customer_email_accounts = 0;\n\n\t\t\twhile ($customer_emails_row = $customer_emails_result_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\t\tif ($customer_emails_row['destination'] != '') {\n\t\t\t\t\t$customer_emails_row['destination'] = explode(' ', FileDir::makeCorrectDestination($customer_emails_row['destination']));\n\t\t\t\t\t$customer_email_forwarders += count($customer_emails_row['destination']);\n\n\t\t\t\t\tif (in_array($customer_emails_row['email_full'], $customer_emails_row['destination'])) {\n\t\t\t\t\t\t$customer_email_forwarders -= 1;\n\t\t\t\t\t\t$customer_email_accounts++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$customer['email_accounts_used_new'] = $customer_email_accounts;\n\t\t\t$customer['email_forwarders_used_new'] = $customer_email_forwarders;\n\n\t\t\t$customer_ftps_stmt = Database::prepare('SELECT COUNT(*) AS `number_ftps` FROM `' . TABLE_FTP_USERS . '` WHERE `customerid` = :cid');\n\t\t\t$customer_ftps = Database::pexecute_first($customer_ftps_stmt, [\n\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t]);\n\t\t\t$customer['ftps_used_new'] = ((int)$customer_ftps['number_ftps'] - 1);\n\n\t\t\t$customer_subdomains_stmt = Database::prepare('SELECT COUNT(*) AS `number_subdomains` FROM `' . TABLE_PANEL_DOMAINS . '` WHERE `customerid` = :cid AND `parentdomainid` <> \"0\"');\n\t\t\t$customer_subdomains = Database::pexecute_first($customer_subdomains_stmt, [\n\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t]);\n\t\t\t$customer['subdomains_used_new'] = (int)$customer_subdomains['number_subdomains'];\n\n\t\t\t$customer_email_quota_stmt = Database::prepare('SELECT SUM(`quota`) AS `email_quota` FROM `' . TABLE_MAIL_USERS . '` WHERE `customerid` = :cid');\n\t\t\t$customer_email_quota = Database::pexecute_first($customer_email_quota_stmt, [\n\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t]);\n\t\t\t$customer['email_quota_used_new'] = (int)$customer_email_quota['email_quota'];\n\n\t\t\t// update database accordingly\n\t\t\t$stmt = Database::prepare('UPDATE `' . TABLE_PANEL_CUSTOMERS . '`\n\t\t\tSET `mysqls_used` = :mysqls_used,\n\t\t\t\t`emails_used` = :emails_used,\n\t\t\t\t`email_accounts_used` = :email_accounts_used,\n\t\t\t\t`email_forwarders_used` = :email_forwarders_used,\n\t\t\t\t`email_quota_used` = :email_quota_used,\n\t\t\t\t`ftps_used` = :ftps_used,\n\t\t\t\t`subdomains_used` = :subdomains_used\n\t\t\tWHERE `customerid` = :cid');\n\t\t\t$params = [\n\t\t\t\t\"mysqls_used\" => $customer['mysqls_used_new'],\n\t\t\t\t\"emails_used\" => $customer['emails_used_new'],\n\t\t\t\t\"email_accounts_used\" => $customer['email_accounts_used_new'],\n\t\t\t\t\"email_forwarders_used\" => $customer['email_forwarders_used_new'],\n\t\t\t\t\"email_quota_used\" => $customer['email_quota_used_new'],\n\t\t\t\t\"ftps_used\" => $customer['ftps_used_new'],\n\t\t\t\t\"subdomains_used\" => $customer['subdomains_used_new'],\n\t\t\t\t\"cid\" => $customer['customerid']\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params);\n\n\t\t\tif ($returndebuginfo === true) {\n\t\t\t\t$returnval['customers'][$customer['customerid']] = $customer;\n\t\t\t}\n\t\t}\n\n\t\t// Admins\n\t\t$admins_stmt = Database::prepare('SELECT * FROM `' . TABLE_PANEL_ADMINS . '` ORDER BY `adminid`');\n\t\tDatabase::pexecute($admins_stmt, []);\n\n\t\t$resource_fields = [\n\t\t\t'diskspace_used',\n\t\t\t'traffic_used',\n\t\t\t'mysqls_used',\n\t\t\t'ftps_used',\n\t\t\t'emails_used',\n\t\t\t'email_accounts_used',\n\t\t\t'email_forwarders_used',\n\t\t\t'email_quota_used',\n\t\t\t'subdomains_used'\n\t\t];\n\n\t\t$admin_customers_stmt = Database::prepare('SELECT * FROM `' . TABLE_PANEL_CUSTOMERS . '` WHERE `adminid` = :aid');\n\t\twhile ($admin = $admins_stmt->fetch(PDO::FETCH_ASSOC)) {\n\t\t\tDatabase::pexecute($admin_customers_stmt, [\n\t\t\t\t\"aid\" => $admin['adminid']\n\t\t\t]);\n\t\t\t$admin_customers = $admin_customers_stmt->fetchAll(PDO::FETCH_ASSOC);\n\t\t\t$admin['customers_used_new'] = count($admin_customers);\n\n\t\t\t$admin_domains_stmt = Database::prepare('SELECT COUNT(*) AS `number_domains` FROM `' . TABLE_PANEL_DOMAINS . '` WHERE `adminid` = :aid AND `parentdomainid` = \"0\"');\n\t\t\t$admin_domains = Database::pexecute_first($admin_domains_stmt, [\n\t\t\t\t\"aid\" => $admin['adminid']\n\t\t\t]);\n\t\t\t// subtract the amount of domains that are std-subdomains later when we iterated through all customers and know for sure\n\t\t\t$admin['domains_used_new'] = $admin_domains['number_domains'];\n\t\t\t// set current admin\n\t\t\t$cur_adm = $admin['adminid'];\n\t\t\t// if there's an admin without any customers it might be possible that the id is not yet known in $admin_resources\n\t\t\tif (!isset($admin_resources[$cur_adm])) {\n\t\t\t\t$admin_resources[$cur_adm] = [];\n\t\t\t}\n\t\t\t// be sure that all fields are set in the array\n\t\t\tforeach ($resource_fields as $field) {\n\t\t\t\tself::initArrField($field, $admin_resources[$cur_adm], 0);\n\t\t\t\t// initialize new values\n\t\t\t\t$admin[$field . '_new'] = 0;\n\t\t\t}\n\t\t\t// now get the customer resource usage which we have re-calculated previously\n\t\t\tforeach ($admin_customers as $acustomer) {\n\t\t\t\tforeach ($resource_fields as $field) {\n\t\t\t\t\tif ($field == 'diskspace_used') {\n\t\t\t\t\t\t// admin/reseller-usage == what has been assign to the customer\n\t\t\t\t\t\tif (($acustomer['diskspace'] / 1024) != -1) {\n\t\t\t\t\t\t\t$admin[$field . '_new'] += $acustomer['diskspace'];\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if ($field != 'traffic_used') {\n\t\t\t\t\t\tif ($acustomer[str_replace(\"_used\", \"\", $field)] != '-1') {\n\t\t\t\t\t\t\t$admin[$field . '_new'] += $acustomer[str_replace(\"_used\", \"\", $field)];\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// traffic is always the current usage, not the assigned value\n\t\t\t\t\t\t$admin[$field . '_new'] += $acustomer[$field];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// check for std-subdomain\n\t\t\t\tif ($acustomer['standardsubdomain'] > 0) {\n\t\t\t\t\t// std-subdomain does not count as assigned resource\n\t\t\t\t\t$admin['domains_used_new']--;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// update database entry accordingly\n\t\t\t$stmt = Database::prepare('UPDATE `' . TABLE_PANEL_ADMINS . '`\n\t\t\tSET `customers_used` = :customers_used,\n\t\t\t\t`domains_used` = :domains_used,\n\t\t\t\t`diskspace_used` = :diskspace_used,\n\t\t\t\t`mysqls_used` = :mysqls_used,\n\t\t\t\t`emails_used` = :emails_used,\n\t\t\t\t`email_accounts_used` = :email_accounts_used,\n\t\t\t\t`email_forwarders_used` = :email_forwarders_used,\n\t\t\t\t`email_quota_used` = :email_quota_used,\n\t\t\t\t`ftps_used` = :ftps_used,\n\t\t\t\t`subdomains_used` = :subdomains_used,\n\t\t\t\t`traffic_used` = :traffic_used\n\t\t\tWHERE `adminid` = :aid');\n\n\t\t\t$params = [\n\t\t\t\t\"customers_used\" => $admin['customers_used_new'],\n\t\t\t\t\"domains_used\" => $admin['domains_used_new'],\n\t\t\t\t\"diskspace_used\" => $admin['diskspace_used_new'],\n\t\t\t\t\"mysqls_used\" => $admin['mysqls_used_new'],\n\t\t\t\t\"emails_used\" => $admin['emails_used_new'],\n\t\t\t\t\"email_accounts_used\" => $admin['email_accounts_used_new'],\n\t\t\t\t\"email_forwarders_used\" => $admin['email_forwarders_used_new'],\n\t\t\t\t\"email_quota_used\" => $admin['email_quota_used_new'],\n\t\t\t\t\"ftps_used\" => $admin['ftps_used_new'],\n\t\t\t\t\"subdomains_used\" => $admin['subdomains_used_new'],\n\t\t\t\t\"traffic_used\" => $admin['traffic_used_new'],\n\t\t\t\t\"aid\" => $admin['adminid']\n\t\t\t];\n\t\t\tDatabase::pexecute($stmt, $params);\n\n\t\t\tif ($returndebuginfo === true) {\n\t\t\t\t$returnval['admins'][$admin['adminid']] = $admin;\n\t\t\t}\n\t\t}\n\n\t\treturn $returnval;\n\t}\n\n\t/**\n\t * if the customer does not have unlimited resources, add the used resources\n\t * to the admin-resource-counter\n\t * Special function wrapper for diskspace and traffic as they need to\n\t * be calculated otherwise to get the -1 for unlimited\n\t *\n\t * @param array $arr reference\n\t * @param array $customer_arr\n\t * @param string $used_field\n\t * @param string $field\n\t *\n\t * @return void\n\t */\n\tprivate static function addResourceCountEx(&$arr, $customer_arr, $used_field = null, $field = null)\n\t{\n\t\tself::initArrField($used_field, $arr, 0);\n\t\tif ($field == 'diskspace' && ($customer_arr[$field] / 1024) != '-1') {\n\t\t\t$arr[$used_field] += intval($customer_arr[$field]);\n\t\t} elseif ($field == 'traffic_used') {\n\t\t\t// no check for -1 here because we don't want the assigned traffic for admins/resellers but\n\t\t\t// the actually used (for stats reasons)\n\t\t\t$arr[$used_field] += intval($customer_arr[$field]);\n\t\t}\n\t}\n\n\t/**\n\t * initialize a field-value of an array if not yet initialized\n\t *\n\t * @param string $field\n\t * @param array $arr reference\n\t * @param int $init_value\n\t *\n\t * @return void\n\t */\n\tprivate static function initArrField($field = null, &$arr = [], $init_value = 0)\n\t{\n\t\tif (!isset($arr[$field])) {\n\t\t\t$arr[$field] = $init_value;\n\t\t}\n\t}\n\n\t/**\n\t * if the customer does not have unlimited resources, add the used resources\n\t * to the admin-resource-counter\n\t *\n\t * @param array $arr reference\n\t * @param array $customer_arr\n\t * @param string $used_field\n\t * @param string $field\n\t *\n\t * @return void\n\t */\n\tprivate static function addResourceCount(&$arr, $customer_arr, $used_field = null, $field = null)\n\t{\n\t\tself::initArrField($used_field, $arr, 0);\n\t\tif ($customer_arr[$field] != '-1') {\n\t\t\t$arr[$used_field] += intval($customer_arr[$used_field]);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Validate/Check.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Validate;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FileDir;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Request;\n\nclass Check\n{\n\n\tconst FORMFIELDS_PLAUSIBILITY_CHECK_OK = 0;\n\n\tconst FORMFIELDS_PLAUSIBILITY_CHECK_ERROR = 1;\n\n\tconst FORMFIELDS_PLAUSIBILITY_CHECK_QUESTION = 2;\n\n\t/**\n\t * @param $fieldname\n\t * @param $fielddata\n\t * @param $newfieldvalue\n\t * @param $allnewfieldvalues\n\t * @return array|int[]\n\t */\n\tpublic static function checkFcgidPhpFpm($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\t$returnvalue = [\n\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t];\n\n\t\t$check_array = [\n\t\t\t'system_mod_fcgid' => [\n\t\t\t\t'other_post_field' => 'phpfpm_enabled',\n\t\t\t\t'other_enabled' => 'phpfpm.enabled',\n\t\t\t\t'other_enabled_lng' => 'phpfpmstillenabled',\n\t\t\t\t'deactivate' => [\n\t\t\t\t\t'phpfpm.enabled_ownvhost' => 0\n\t\t\t\t]\n\t\t\t],\n\t\t\t'phpfpm_enabled' => [\n\t\t\t\t'other_post_field' => 'system_mod_fcgid',\n\t\t\t\t'other_enabled' => 'system.mod_fcgid',\n\t\t\t\t'other_enabled_lng' => 'fcgidstillenabled',\n\t\t\t\t'deactivate' => [\n\t\t\t\t\t'system.mod_fcgid_ownvhost' => 0\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\n\t\t// interface is to be enabled\n\t\tif ((int)$newfieldvalue == 1) {\n\t\t\t// check for POST value of the other field == 1 (active)\n\t\t\tif ((int)Request::post($check_array[$fieldname]['other_post_field'], 0) == 1) {\n\t\t\t\t// the other interface is activated already and STAYS activated\n\t\t\t\tif ((int)Settings::Get($check_array[$fieldname]['other_enabled']) == 1) {\n\t\t\t\t\t$returnvalue = [\n\t\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t\t\t$check_array[$fieldname]['other_enabled_lng']\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\t// fcgid is being validated before fpm -> \"ask\" fpm about its state\n\t\t\t\t\tif ($fieldname == 'system_mod_fcgid_enabled') {\n\t\t\t\t\t\t$returnvalue = self::checkFcgidPhpFpm(\n\t\t\t\t\t\t\t'system_phpfpm_enabled',\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t$check_array[$fieldname]['other_post_field'],\n\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// not, bot are nogo\n\t\t\t\t\t\t$returnvalue = $returnvalue = [\n\t\t\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t\t\t\t'fcgidandphpfpmnogoodtogether'\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} elseif ((int)Settings::Get($check_array[$fieldname]['other_enabled']) == 1) {\n\t\t\t\t// not in the same POST so we still need to check whether the other one's enabled\n\t\t\t\t$returnvalue = [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t\t$check_array[$fieldname]['other_enabled_lng']\n\t\t\t\t];\n\t\t\t}\n\t\t\tif (in_array(self::FORMFIELDS_PLAUSIBILITY_CHECK_OK, $returnvalue)) {\n\t\t\t\t// be sure to deactivate the other one for the froxlor-vhost\n\t\t\t\t// to avoid having a settings-deadlock\n\t\t\t\tforeach ($check_array[$fieldname]['deactivate'] as $setting => $value) {\n\t\t\t\t\tSettings::Set($setting, $value, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function checkMysqlAccessHost($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\t$mysql_access_host_array = array_unique(array_map('trim', explode(',', $newfieldvalue)));\n\n\t\tforeach ($mysql_access_host_array as $host_entry) {\n\t\t\tif (Validate::validate_ip2(\n\t\t\t\t\t$host_entry,\n\t\t\t\t\ttrue,\n\t\t\t\t\t'invalidip',\n\t\t\t\t\ttrue,\n\t\t\t\t\ttrue,\n\t\t\t\t\ttrue,\n\t\t\t\t\ttrue,\n\t\t\t\t\tfalse\n\t\t\t\t) == false && Validate::validateDomain($host_entry) == false && Validate::validateLocalHostname($host_entry) == false && $host_entry != '%') {\n\t\t\t\treturn [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t\t'invalidmysqlhost',\n\t\t\t\t\t$host_entry\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\n\t\treturn [\n\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t];\n\t}\n\n\t/**\n\t * @param $fieldname\n\t * @param $fielddata\n\t * @param $newfieldvalue\n\t * @param $allnewfieldvalues\n\t * @return array|int[]\n\t */\n\tpublic static function checkHostname($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\tif (0 == strlen(trim($newfieldvalue)) || Validate::validateDomain($newfieldvalue) === false) {\n\t\t\treturn [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t'invalidhostname'\n\t\t\t];\n\t\t} else {\n\t\t\treturn [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t\t];\n\t\t}\n\t}\n\n\t/**\n\t * check whether an email account is to be deleted\n\t * reference: #1519\n\t *\n\t * @param string $email_addr\n\t *\n\t * @return bool true if the domain is to be deleted, false otherwise\n\t * @throws \\Exception\n\t */\n\tpublic static function checkMailAccDeletionState(string $email_addr): bool\n\t{\n\t\t// example data of task 7: a:2:{s:9:\"loginname\";s:4:\"webX\";s:5:\"email\";s:20:\"deleteme@example.tld\";}\n\n\t\t// check for task\n\t\t$result_tasks_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_TASKS . \"` WHERE `type` = '7' AND `data` LIKE :emailaddr\n\t\t\");\n\t\tDatabase::pexecute($result_tasks_stmt, [\n\t\t\t'emailaddr' => \"%\" . $email_addr . \"%\"\n\t\t]);\n\t\t$num_results = Database::num_rows();\n\n\t\t// is there a task for deleting this email account?\n\t\tif ($num_results > 0) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @param $fieldname\n\t * @param $fielddata\n\t * @param $newfieldvalue\n\t * @param $allnewfieldvalues\n\t * @return array|int[]\n\t * @throws \\Exception\n\t */\n\tpublic static function checkPathConflicts($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\tif ((int)Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t// fcgid-configdir has changed -> check against customer-doc-prefix\n\t\t\tif ($fieldname == \"system_mod_fcgid_configdir\") {\n\t\t\t\t$newdir = FileDir::makeCorrectDir($newfieldvalue);\n\t\t\t\t$cdir = FileDir::makeCorrectDir(Settings::Get('system.documentroot_prefix'));\n\t\t\t} elseif ($fieldname == \"system_documentroot_prefix\") {\n\t\t\t\t// customer-doc-prefix has changed -> check against fcgid-configdir\n\t\t\t\t$newdir = FileDir::makeCorrectDir($newfieldvalue);\n\t\t\t\t$cdir = FileDir::makeCorrectDir(Settings::Get('system.mod_fcgid_configdir'));\n\t\t\t}\n\n\t\t\t// neither dir can be within the other nor can they be equal\n\t\t\tif (substr($newdir, 0, strlen($cdir)) == $cdir || substr(\n\t\t\t\t\t$cdir,\n\t\t\t\t\t0,\n\t\t\t\t\tstrlen($newdir)\n\t\t\t\t) == $newdir || $newdir == $cdir) {\n\t\t\t\t$returnvalue = [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t\t'fcgidpathcannotbeincustomerdoc'\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$returnvalue = [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t\t\t];\n\t\t\t}\n\t\t} else {\n\t\t\t$returnvalue = [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t\t];\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\t/**\n\t * @param $fieldname\n\t * @param $fielddata\n\t * @param $newfieldvalue\n\t * @param $allnewfieldvalues\n\t * @return array|int[]\n\t */\n\tpublic static function checkPhpInterfaceSetting($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\t$returnvalue = [\n\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t];\n\n\t\tif ((int)Settings::Get('system.mod_fcgid') == 1) {\n\t\t\t// fcgid only works for apache\n\t\t\tif (strtolower($newfieldvalue) != 'apache2') {\n\t\t\t\t$returnvalue = [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t\t'fcgidstillenableddeadlock'\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\t/**\n\t * @param $fieldname\n\t * @param $fielddata\n\t * @param $newfieldvalue\n\t * @param $allnewfieldvalues\n\t * @return array|int[]\n\t */\n\tpublic static function checkUsername($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\tif (!isset($allnewfieldvalues['customer_mysqlprefix'])) {\n\t\t\t$allnewfieldvalues['customer_mysqlprefix'] = Settings::Get('customer.mysqlprefix');\n\t\t}\n\n\t\t$returnvalue = [];\n\t\tif (Validate::validateUsername(\n\t\t\t\t$newfieldvalue,\n\t\t\t\tSettings::Get('panel.unix_names'),\n\t\t\t\tDatabase::getSqlUsernameLength() - strlen($allnewfieldvalues['customer_mysqlprefix'])\n\t\t\t) === true) {\n\t\t\t$returnvalue = [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t\t];\n\t\t} else {\n\t\t\t$errmsg = 'accountprefixiswrong';\n\t\t\tif ($fieldname == 'customer_mysqlprefix') {\n\t\t\t\t$errmsg = 'mysqlprefixiswrong';\n\t\t\t}\n\t\t\t$returnvalue = [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t$errmsg\n\t\t\t];\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\t/**\n\t * @param $fieldname\n\t * @param $fielddata\n\t * @param $newfieldvalue\n\t * @param $allnewfieldvalues\n\t * @return array|int[]\n\t */\n\tpublic static function checkLocalGroup($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\tif (empty($newfieldvalue) || $fielddata['value'] == $newfieldvalue) {\n\t\t\t$returnvalue = [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t\t];\n\t\t} elseif (function_exists('posix_getgrnam') && posix_getgrnam($newfieldvalue) == false) {\n\t\t\tif (Validate::validateUsername($newfieldvalue, Settings::Get('panel.unix_names'), 32)) {\n\t\t\t\t$returnvalue = [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$returnvalue = [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t\t'local_group_invalid'\n\t\t\t\t];\n\t\t\t}\n\t\t} else {\n\t\t\t$returnvalue = [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t'local_group_exists'\n\t\t\t];\n\t\t}\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function checkPgpPublicKeySetting($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\t// if the field is empty, we don't need to check anything\n\t\tif ($newfieldvalue === '') {\n\t\t\treturn [self::FORMFIELDS_PLAUSIBILITY_CHECK_OK];\n\t\t}\n\n\t\t// check if gnupg extension is loaded\n\t\tif (!extension_loaded('gnupg')) {\n\t\t\treturn [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t'gnupgextensionnotavailable'\n\t\t\t];\n\t\t}\n\t\t// check if the pgp public key is a valid key\n\t\tputenv('GNUPGHOME=' . sys_get_temp_dir());\n\t\tif (gnupg_import(gnupg_init(), $newfieldvalue) === false) {\n\t\t\treturn [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t'invalidpgppublickey'\n\t\t\t];\n\t\t}\n\n\t\treturn [self::FORMFIELDS_PLAUSIBILITY_CHECK_OK];\n\t}\n\n\t/**\n\t * @param $fieldname\n\t * @param $fielddata\n\t * @param $newfieldvalue\n\t * @param $allnewfieldvalues\n\t * @return array|int[]\n\t * @throws \\Exception\n\t */\n\tpublic static function checkSystemUsername($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)\n\t{\n\t\tif (empty($newfieldvalue) || $fielddata['value'] == $newfieldvalue) {\n\t\t\t$returnvalue = [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t\t];\n\t\t} elseif (function_exists('posix_getpwnam') && posix_getpwnam($newfieldvalue) == false) {\n\t\t\t$returnvalue = [\n\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t'local_user_invalid'\n\t\t\t];\n\t\t} else {\n\t\t\t// user exists, but cannot be one of the froxlor-customers\n\t\t\t$sel_stmt = Database::prepare(\"SELECT COUNT(*) as numUsers FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `loginname` = :username\");\n\t\t\t$result = Database::pexecute_first($sel_stmt, [':username' => $newfieldvalue]);\n\t\t\tif ($result && $result['numUsers'] > 0) {\n\t\t\t\t$returnvalue = [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,\n\t\t\t\t\t'local_user_isfroxloruser'\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$returnvalue = [\n\t\t\t\t\tself::FORMFIELDS_PLAUSIBILITY_CHECK_OK\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t\treturn $returnvalue;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Validate/Form/Data.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Validate\\Form;\n\nuse Froxlor\\FileDir;\nuse Froxlor\\Validate\\Validate;\n\nclass Data\n{\n\tpublic static function validateFormFieldText($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\treturn self::validateFormFieldString($fieldname, $fielddata, $newfieldvalue);\n\t}\n\n\tpublic static function validateFormFieldPassword($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\treturn self::validateFormFieldString($fieldname, $fielddata, $newfieldvalue);\n\t}\n\n\tpublic static function validateFormFieldString($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\tif (isset($fielddata['string_delimiter']) && $fielddata['string_delimiter'] != '') {\n\t\t\t$newfieldvalues = array_map('trim', explode($fielddata['string_delimiter'], $newfieldvalue));\n\t\t\tunset($fielddata['string_delimiter']);\n\n\t\t\t$returnvalue = true;\n\t\t\tforeach ($newfieldvalues as $single_newfieldvalue) {\n\t\t\t\t/**\n\t\t\t\t * don't use tabs in value-fields, #81\n\t\t\t\t */\n\t\t\t\t$single_newfieldvalue = str_replace(\"\\t\", \" \", $single_newfieldvalue);\n\t\t\t\t$single_returnvalue = self::validateFormFieldString($fieldname, $fielddata, $single_newfieldvalue);\n\t\t\t\tif ($single_returnvalue !== true) {\n\t\t\t\t\t$returnvalue = $single_returnvalue;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t$returnvalue = false;\n\n\t\t\t/**\n\t\t\t * don't use tabs in value-fields, #81\n\t\t\t */\n\t\t\t$newfieldvalue = str_replace(\"\\t\", \" \", $newfieldvalue);\n\n\t\t\tif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'mail') {\n\t\t\t\t$returnvalue = ($newfieldvalue == Validate::validateEmail($newfieldvalue));\n\t\t\t} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'url') {\n\t\t\t\t$returnvalue = ($newfieldvalue == Validate::validateUrl($newfieldvalue));\n\t\t\t} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'dir') {\n\t\t\t\t// check for empty value (it might be allowed)\n\t\t\t\tif (trim($newfieldvalue) == '') {\n\t\t\t\t\t$newfieldvalue = '';\n\t\t\t\t\t$returnvalue = 'stringmustntbeempty';\n\t\t\t\t} else {\n\t\t\t\t\t// add trailing slash to validate path if needed\n\t\t\t\t\t// refs #331\n\t\t\t\t\tif (substr($newfieldvalue, -1) != '/') {\n\t\t\t\t\t\t$newfieldvalue .= '/';\n\t\t\t\t\t}\n\t\t\t\t\t$returnvalue = ($newfieldvalue == FileDir::makeCorrectDir($newfieldvalue));\n\t\t\t\t}\n\t\t\t} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'confdir') {\n\t\t\t\t// check for empty value (it might be allowed)\n\t\t\t\tif (trim($newfieldvalue) == '') {\n\t\t\t\t\t$newfieldvalue = '';\n\t\t\t\t\t$returnvalue = 'stringmustntbeempty';\n\t\t\t\t} else {\n\t\t\t\t\t// add trailing slash to validate path if needed\n\t\t\t\t\t// refs #331\n\t\t\t\t\tif (substr($newfieldvalue, -1) != '/') {\n\t\t\t\t\t\t$newfieldvalue .= '/';\n\t\t\t\t\t}\n\t\t\t\t\t// if this is a configuration directory, check for stupidity of admins :p\n\t\t\t\t\tif (FileDir::checkDisallowedPaths($newfieldvalue) !== true) {\n\t\t\t\t\t\t$newfieldvalue = '';\n\t\t\t\t\t\t$returnvalue = 'givendirnotallowed';\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$returnvalue = ($newfieldvalue == FileDir::makeCorrectDir($newfieldvalue));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'file') {\n\t\t\t\t// check for empty value (it might be allowed)\n\t\t\t\tif (trim($newfieldvalue) == '') {\n\t\t\t\t\t$newfieldvalue = '';\n\t\t\t\t\t$returnvalue = 'stringmustntbeempty';\n\t\t\t\t} else {\n\t\t\t\t\t$returnvalue = ($newfieldvalue == FileDir::makeCorrectFile($newfieldvalue));\n\t\t\t\t}\n\t\t\t} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'filedir') {\n\t\t\t\t// check for empty value (it might be allowed)\n\t\t\t\tif (trim($newfieldvalue) == '') {\n\t\t\t\t\t$newfieldvalue = '';\n\t\t\t\t\t$returnvalue = 'stringmustntbeempty';\n\t\t\t\t} else {\n\t\t\t\t\t$returnvalue = (($newfieldvalue == FileDir::makeCorrectDir($newfieldvalue)) || ($newfieldvalue == FileDir::makeCorrectFile($newfieldvalue)));\n\t\t\t\t}\n\t\t\t} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'validate_ip') {\n\t\t\t\t// check for empty value (it might be allowed)\n\t\t\t\tif (trim($newfieldvalue) == '') {\n\t\t\t\t\t$newfieldvalue = '';\n\t\t\t\t\t$returnvalue = 'stringmustntbeempty';\n\t\t\t\t} else {\n\t\t\t\t\t$newfieldvalue = Validate::validate_ip2($newfieldvalue, true);\n\t\t\t\t\t$returnvalue = ($newfieldvalue !== false ? true : 'invalidip');\n\t\t\t\t}\n\t\t\t} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'validate_ip_incl_private') {\n\t\t\t\t// check for empty value (it might be allowed)\n\t\t\t\tif (trim($newfieldvalue) == '') {\n\t\t\t\t\t$newfieldvalue = '';\n\t\t\t\t\t$returnvalue = 'stringmustntbeempty';\n\t\t\t\t} else {\n\t\t\t\t\t$newfieldvalue = Validate::validate_ip2($newfieldvalue, true, 'invalidip', true, true, true);\n\t\t\t\t\t$returnvalue = ($newfieldvalue !== false ? true : 'invalidip');\n\t\t\t\t}\n\t\t\t} elseif (preg_match('/^[^\\r\\n\\t\\f\\0]*$/D', $newfieldvalue)) {\n\t\t\t\t$returnvalue = true;\n\t\t\t}\n\n\t\t\tif (isset($fielddata['string_regexp']) && $fielddata['string_regexp'] != '') {\n\t\t\t\tif (preg_match($fielddata['string_regexp'], $newfieldvalue)) {\n\t\t\t\t\t$returnvalue = true;\n\t\t\t\t} else {\n\t\t\t\t\t$returnvalue = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isset($fielddata['string_emptyallowed']) && $fielddata['string_emptyallowed'] === true && $newfieldvalue === '') {\n\t\t\t\t$returnvalue = true;\n\t\t\t} elseif (isset($fielddata['string_emptyallowed']) && $fielddata['string_emptyallowed'] === false && $newfieldvalue === '') {\n\t\t\t\t$returnvalue = 'stringmustntbeempty';\n\t\t\t}\n\t\t}\n\n\t\tif ($returnvalue === true) {\n\t\t\treturn true;\n\t\t} elseif ($returnvalue === false) {\n\t\t\treturn 'stringformaterror';\n\t\t} else {\n\t\t\treturn $returnvalue;\n\t\t}\n\t}\n\n\tpublic static function validateFormFieldEmail($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$fielddata['string_type'] = 'mail';\n\t\treturn self::validateFormFieldString($fieldname, $fielddata, $newfieldvalue);\n\t}\n\n\tpublic static function validateFormFieldUrl($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$fielddata['string_type'] = 'url';\n\t\treturn self::validateFormFieldString($fieldname, $fielddata, $newfieldvalue);\n\t}\n\n\tpublic static function validateFormFieldCheckbox($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\tif ($newfieldvalue === '1' || $newfieldvalue === 1 || $newfieldvalue === true || strtolower($newfieldvalue) === 'yes' || strtolower($newfieldvalue) === 'ja' || $newfieldvalue === '0' || $newfieldvalue === 0 || $newfieldvalue === false || strtolower($newfieldvalue) === 'no' || strtolower($newfieldvalue) === 'nein' || strtolower($newfieldvalue) === '') {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn 'noboolean';\n\t\t}\n\t}\n\n\tpublic static function validateFormFieldDate($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\tif ($newfieldvalue == '0000-00-00' || preg_match('/^(19|20)\\d\\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', $newfieldvalue)) {\n\t\t\t$returnvalue = true;\n\t\t} else {\n\t\t\t$returnvalue = false;\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function validateFormFieldFile($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\treturn true;\n\t}\n\n\tpublic static function validateFormFieldHidden($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t/**\n\t\t * don't show error on cronjob-timestamps changing\n\t\t * because it might be possible that the cronjob ran\n\t\t * while settings have been edited (bug #52)\n\t\t */\n\t\tif ($newfieldvalue === $fielddata['value'] || $fieldname == 'system_last_tasks_run' || $fieldname == 'system_last_traffic_run' || $fieldname == 'system_lastcronrun') {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn 'hiddenfieldvaluechanged';\n\t\t}\n\t}\n\n\tpublic static function validateFormFieldNumber($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\tif (isset($fielddata['min']) && (int)$newfieldvalue < (int)$fielddata['min']) {\n\t\t\treturn ('intvaluetoolow');\n\t\t}\n\n\t\tif (isset($fielddata['max']) && (int)$newfieldvalue > (int)$fielddata['max']) {\n\t\t\treturn ('intvaluetoohigh');\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic static function validateFormFieldSelect($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = true;\n\n\t\tif (isset($fielddata['select_mode']) && $fielddata['select_mode'] == 'multiple') {\n\t\t\t$options = explode(',', $newfieldvalue);\n\t\t\tforeach ($options as $option) {\n\t\t\t\t$returnvalue = ($returnvalue && isset($fielddata['select_var'][$option]));\n\t\t\t}\n\t\t} else {\n\t\t\t$returnvalue = isset($fielddata['select_var'][$newfieldvalue]);\n\t\t}\n\n\t\tif ($returnvalue === true || (isset($fielddata['visible']) && $fielddata['visible'] == false)) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tif (isset($fielddata['option_emptyallowed']) && $fielddata['option_emptyallowed']) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn 'not in option (field: ' . $fieldname . ')';\n\t\t}\n\t}\n\n\tpublic static function validateFormFieldTextarea($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = 'stringformaterror';\n\n\t\tif (isset($fielddata['string_regexp']) && $fielddata['string_regexp'] != '') {\n\t\t\tif (preg_match($fielddata['string_regexp'], $newfieldvalue)) {\n\t\t\t\t$returnvalue = true;\n\t\t\t}\n\t\t} elseif (preg_match('/^[^\\0]*$/', $newfieldvalue)) {\n\t\t\t$returnvalue = true;\n\t\t}\n\n\t\treturn $returnvalue;\n\t}\n\n\tpublic static function validateFormFieldImage($fieldname, $fielddata, $newfieldvalue)\n\t{\n\t\t// validation is handled in \\Froxlor\\Settings\\Store::storeSettingImage()\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Validate/Form/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/Validate/Form.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Validate;\n\nclass Form\n{\n\n\t/**\n\t * @param array $form\n\t * @return bool\n\t */\n\tpublic static function validateFormDefinition(array $form): bool\n\t{\n\t\tif (!empty($form) && isset($form['groups']) && is_array($form['groups']) && !empty($form['groups'])) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @param array $field\n\t * @return bool\n\t */\n\tpublic static function validateFieldDefinition(array $field): bool\n\t{\n\t\tif (!empty($field) && isset($field['fields']) && is_array($field['fields']) && !empty($field['fields'])) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @param $fieldname\n\t * @param array $fielddata\n\t * @param $newfieldvalue\n\t * @return mixed|string\n\t */\n\tpublic static function validateFormField($fieldname, array $fielddata, $newfieldvalue)\n\t{\n\t\t$returnvalue = '';\n\t\tif (isset($fielddata['type']) && $fielddata['type'] != '' && method_exists('\\\\Froxlor\\\\Validate\\\\Form\\\\Data', 'validateFormField' . ucfirst($fielddata['type']))) {\n\t\t\t$returnvalue = call_user_func([\n\t\t\t\t'\\\\Froxlor\\\\Validate\\\\Form\\\\Data',\n\t\t\t\t'validateFormField' . ucfirst($fielddata['type'])\n\t\t\t], $fieldname, $fielddata, $newfieldvalue);\n\t\t} else {\n\t\t\t$returnvalue = 'validation method not found';\n\t\t}\n\t\treturn $returnvalue;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Validate/Validate.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor\\Validate;\n\nuse Exception;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\System\\IPTools;\nuse Froxlor\\UI\\Response;\n\nclass Validate\n{\n\n\tconst REGEX_DIR = '/^|(\\/[\\w-]+)+$/';\n\n\tconst REGEX_PORT = '/^(([1-9])|([1-9][0-9])|([1-9][0-9][0-9])|([1-9][0-9][0-9][0-9])|([1-5][0-9][0-9][0-9][0-9])|(6[0-4][0-9][0-9][0-9])|(65[0-4][0-9][0-9])|(655[0-2][0-9])|(6553[0-5]))$/Di';\n\n\tconst REGEX_CONF_TEXT = '/^[^\\0]*$/';\n\n\tconst REGEX_DESC_TEXT = '/^[^\\0\\r\\n<>]*$/';\n\n\tconst REGEX_YYYY_MM_DD = '/^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/';\n\n\t/**\n\t * Validates the given string by matching against the pattern, prints an error on failure and exits.\n\t * If the default pattern is used and the string does not match, we try to replace the 'bad' values and log the action.\n\t *\n\t * @param string $str the string to be tested (user input)\n\t * @param string $fieldname to be used in error messages\n\t * @param string $pattern the regular expression to be used for testing\n\t * @param string|array $lng id for the error\n\t * @param string|array $emptydefault fallback value\n\t * @param bool $throw_exception whether to display error or throw an exception, default false\n\t *\n\t * @return string|void the clean string or error\n\t * @throws Exception\n\t */\n\tpublic static function validate(\n\t\tstring $str,\n\t\tstring $fieldname,\n\t\tstring $pattern = '',\n\t\t       $lng = '',\n\t\t       $emptydefault = [],\n\t\tbool   $throw_exception = false\n\t)\n\t{\n\t\tif (!is_array($emptydefault)) {\n\t\t\t$emptydefault_array = [\n\t\t\t\t$emptydefault\n\t\t\t];\n\t\t\tunset($emptydefault);\n\t\t\t$emptydefault = $emptydefault_array;\n\t\t\tunset($emptydefault_array);\n\t\t}\n\n\t\t// Check if the $str is one of the values which represent the default for an 'empty' value\n\t\tif (is_array($emptydefault) && !empty($emptydefault) && in_array($str, $emptydefault)) {\n\t\t\treturn $str;\n\t\t}\n\n\t\tif ($pattern == '') {\n\t\t\t$pattern = '/^[^\\r\\n\\t\\f\\0]*$/D';\n\n\t\t\tif (!preg_match($pattern, $str)) {\n\t\t\t\t// Allows letters a-z, digits, space (\\\\040), hyphen (\\\\-), underscore (\\\\_) and backslash (\\\\\\\\),\n\t\t\t\t// everything else is removed from the string.\n\t\t\t\t$allowed = \"/[^a-z0-9\\\\040\\\\.\\\\-\\\\_\\\\\\\\]/i\";\n\t\t\t\t$str = preg_replace($allowed, \"\", $str);\n\t\t\t\t$log = FroxlorLogger::getInstanceOf();\n\t\t\t\t$log->logAction(FroxlorLogger::USR_ACTION, LOG_WARNING, \"cleaned bad formatted string (\" . $str . \")\");\n\t\t\t}\n\t\t}\n\n\t\tif (preg_match($pattern, $str)) {\n\t\t\treturn $str;\n\t\t}\n\n\t\tif ($lng == '') {\n\t\t\t$lng = 'stringformaterror';\n\t\t}\n\n\t\tResponse::standardError($lng, $fieldname, $throw_exception);\n\t}\n\n\t/**\n\t * Checks whether it is a valid ip\n\t *\n\t * @param string $ip ip-address to check\n\t * @param bool $return_bool whether to return bool or call \\Froxlor\\UI\\Response::standard_error()\n\t * @param string $lng index for error-message (if $return_bool is false)\n\t * @param bool $allow_localhost whether to allow 127.0.0.1\n\t * @param bool $allow_priv whether to allow private network addresses\n\t * @param bool $allow_cidr whether to allow CIDR values e.g. 10.10.10.10/16\n\t * @param bool $cidr_as_netmask whether to format CIDR notation to netmask notation\n\t * @param bool $throw_exception whether to throw an exception on failure\n\t *\n\t * @return string|bool|void ip address on success, false on failure (or nothing if error is displayed)\n\t * @throws Exception\n\t */\n\tpublic static function validate_ip2(\n\t\tstring $ip,\n\t\tbool   $return_bool = false,\n\t\tstring $lng = 'invalidip',\n\t\tbool   $allow_localhost = false,\n\t\tbool   $allow_priv = false,\n\t\tbool   $allow_cidr = false,\n\t\tbool   $cidr_as_netmask = false,\n\t\tbool   $throw_exception = false\n\t)\n\t{\n\t\t$cidr = \"\";\n\t\tif ($allow_cidr) {\n\t\t\t$org_ip = $ip;\n\t\t\t$ip_cidr = explode(\"/\", $ip);\n\t\t\tif (count($ip_cidr) === 2) {\n\t\t\t\t$cidr_range_max = 32;\n\t\t\t\tif (IPTools::is_ipv6($ip_cidr[0])) {\n\t\t\t\t\t$cidr_range_max = 128;\n\t\t\t\t}\n\t\t\t\tif (strlen($ip_cidr[1]) <= 3 && in_array((int)$ip_cidr[1], array_values(range(1, $cidr_range_max)),\n\t\t\t\t\t\ttrue) === false) {\n\t\t\t\t\tif ($return_bool) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tResponse::standardError($lng, $ip, $throw_exception);\n\t\t\t\t}\n\t\t\t\tif ($cidr_as_netmask && IPTools::is_ipv6($ip_cidr[0])) {\n\t\t\t\t\t// MySQL does not handle CIDR of IPv6 addresses, return error\n\t\t\t\t\tif ($return_bool) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tResponse::standardError($lng, $ip, $throw_exception);\n\t\t\t\t}\n\t\t\t\t$ip = $ip_cidr[0];\n\t\t\t\tif ($cidr_as_netmask && strlen($ip_cidr[1]) <= 3) {\n\t\t\t\t\t$ip_cidr[1] = IPTools::cidr2NetmaskAddr($org_ip);\n\t\t\t\t}\n\t\t\t\t$cidr = \"/\" . $ip_cidr[1];\n\t\t\t} else {\n\t\t\t\t$ip = $org_ip;\n\t\t\t}\n\t\t} elseif (strpos($ip, \"/\") !== false) {\n\t\t\tif ($return_bool) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tResponse::standardError($lng, $ip, $throw_exception);\n\t\t}\n\n\t\t$filter_lan = $allow_priv ? FILTER_FLAG_NO_RES_RANGE : (FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE);\n\n\t\tif ((filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) || filter_var($ip, FILTER_VALIDATE_IP,\n\t\t\t\t\tFILTER_FLAG_IPV4)) && filter_var($ip, FILTER_VALIDATE_IP, $filter_lan)) {\n\t\t\treturn $ip . $cidr;\n\t\t}\n\n\t\t// special case where localhost ip is allowed (mysql-access-hosts for example)\n\t\tif ($allow_localhost && ($ip == '127.0.0.1' || $ip == '::1')) {\n\t\t\treturn $ip . $cidr;\n\t\t}\n\n\t\tif ($return_bool) {\n\t\t\treturn false;\n\t\t}\n\t\tResponse::standardError($lng, $ip, $throw_exception);\n\t}\n\n\t/**\n\t * Returns whether a URL is in a correct format or not\n\t *\n\t * @param string $url URL to be tested\n\t * @param bool $allow_private_ip optional, default is false\n\t *\n\t * @return bool\n\t */\n\tpublic static function validateUrl(string $url, bool $allow_private_ip = false): bool\n\t{\n\t\tif (strtolower(substr($url, 0, 7)) != \"http://\" && strtolower(substr($url, 0, 8)) != \"https://\") {\n\t\t\t$url = 'http://' . $url;\n\t\t}\n\n\t\t// Parse parts\n\t\t$parts = parse_url($url);\n\t\tif ($parts === false || !isset($parts['scheme'], $parts['host'])) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check allowed schemes\n\t\tif (!in_array(strtolower($parts['scheme']), ['http', 'https'], true)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if host is valid domain or valid IP (v4 or v6)\n\t\t$host = $parts['host'];\n\t\tif (substr($host, 0, 1) == '[' && substr($host, -1) == ']') {\n\t\t\t$host = substr($host, 1, -1);\n\t\t}\n\n\t\t$opts = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE;\n\t\t$opts6 = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE;\n\t\tif ($allow_private_ip) {\n\t\t\t$opts = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE;\n\t\t\t$opts6 = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE;\n\t\t}\n\t\tif (filter_var($host, FILTER_VALIDATE_IP, $opts)) {\n\t\t\treturn true;\n\t\t} elseif (substr($parts['host'], 0, 1) == '[' && substr($parts['host'], -1) == ']' && filter_var($host, FILTER_VALIDATE_IP, $opts6)) {\n\t\t\treturn true;\n\t\t} elseif (!preg_match('/^([0-9]{1,3}\\.)+[0-9]{1,3}$/', $host) && self::validateDomain($host) !== false) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if the submitted string is a valid domainname\n\t *\n\t * @param string $domainname The domainname which should be checked.\n\t * @param bool $allow_underscore optional if true, allows the underscore character in a domain label (DKIM etc.)\n\t *\n\t * @return string|boolean the domain-name if the domain is valid, false otherwise\n\t */\n\tpublic static function validateDomain(string $domainname, bool $allow_underscore = false)\n\t{\n\t\t$char_validation = '([a-z\\d](-*[a-z\\d])*)(\\.?([a-z\\d](-*[a-z\\d])*))*\\.(xn\\-\\-)?([a-z\\d])+';\n\t\tif ($allow_underscore) {\n\t\t\t$char_validation = '([a-z\\d\\_](-*[a-z\\d\\_])*)(\\.([a-z\\d\\_](-*[a-z\\d])*))*(\\.?([a-z\\d](-*[a-z\\d])*))+\\.(xn\\-\\-)?([a-z\\d])+';\n\t\t}\n\n\t\t// valid chars check && overall length check && length of each label\n\t\tif (preg_match(\"/^\" . $char_validation . \"$/i\", $domainname) && preg_match(\"/^.{1,253}$/\",\n\t\t\t\t$domainname) && preg_match(\"/^[^\\.]{1,63}(\\.[^\\.]{1,63})*$/\", $domainname)) {\n\t\t\treturn $domainname;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * validate a local-hostname by regex\n\t *\n\t * @param string $hostname\n\t *\n\t * @return string|boolean hostname on success, else false\n\t */\n\tpublic static function validateLocalHostname(string $hostname)\n\t{\n\t\t$pattern = '/^[a-z0-9][a-z0-9\\-]{0,62}$/i';\n\t\tif (preg_match($pattern, $hostname)) {\n\t\t\treturn $hostname;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns if an email-address is in correct format or not\n\t *\n\t * @param string $email The email address to check\n\t *\n\t * @return mixed\n\t */\n\tpublic static function validateEmail(string $email)\n\t{\n\t\t$email = strtolower($email);\n\t\t// as of php-7.1\n\t\tif (defined('FILTER_FLAG_EMAIL_UNICODE')) {\n\t\t\treturn filter_var($email, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE);\n\t\t}\n\t\treturn filter_var($email, FILTER_VALIDATE_EMAIL);\n\t}\n\n\t/**\n\t * Returns if a username is in correct format or not.\n\t *\n\t * @param string $username The username to check\n\t * @param bool $unix_names optional, default true, checks whether it must be UNIX compatible\n\t * @param int $mysql_max optional, number of max mysql username characters, default empty\n\t *\n\t * @return bool\n\t */\n\tpublic static function validateUsername(string $username, bool $unix_names = true, int $mysql_max = 0): bool\n\t{\n\t\tif (empty($mysql_max) || $mysql_max <= 0) {\n\t\t\t$mysql_max = Database::getSqlUsernameLength() - 1;\n\t\t} else {\n\t\t\t$mysql_max--;\n\t\t}\n\t\tif (!$unix_names) {\n\t\t\tif (strpos($username, '--') === false) {\n\t\t\t\treturn (preg_match('/^[a-z][a-z0-9\\-_]{0,' . $mysql_max . '}[a-z0-9]{1}$/Di', $username) != false);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\treturn (preg_match('/^[a-z][a-z0-9]{0,' . $mysql_max . '}$/Di', $username) != false);\n\t}\n\n\t/**\n\t * validate sql interval string\n\t *\n\t * @param string $interval\n\t *\n\t * @return bool\n\t */\n\tpublic static function validateSqlInterval(string $interval = ''): bool\n\t{\n\t\tif (!empty($interval) && strstr($interval, ' ') !== false) {\n\t\t\t/*\n\t\t\t * [0] = ([0-9]+)\n\t\t\t * [1] = valid SQL-Interval expression\n\t\t\t */\n\t\t\t$valid_expr = [\n\t\t\t\t'SECOND',\n\t\t\t\t'MINUTE',\n\t\t\t\t'HOUR',\n\t\t\t\t'DAY',\n\t\t\t\t'WEEK',\n\t\t\t\t'MONTH',\n\t\t\t\t'YEAR'\n\t\t\t];\n\n\t\t\t$interval_parts = explode(' ', $interval);\n\n\t\t\tif (count($interval_parts) == 2 && preg_match('/[0-9]+/',\n\t\t\t\t\t$interval_parts[0]) && in_array(strtoupper($interval_parts[1]), $valid_expr)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * validates whether a given base64 string decodes to an image\n\t *\n\t * @param string $base64string\n\t * @return bool\n\t * @throws Exception\n\t */\n\tpublic static function validateBase64Image(string $base64string)\n\t{\n\n\t\tif (!extension_loaded('gd')) {\n\t\t\tResponse::standardError('phpgdextensionnotavailable', null, true);\n\t\t}\n\n\t\t// Decode the base64 string\n\t\t$data = base64_decode($base64string);\n\n\t\t// Create an image from the decoded data\n\t\t$image = @imagecreatefromstring($data);\n\n\t\t// Check if the image was created successfully\n\t\tif (!$image) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Get the MIME type of the image\n\t\t$mime = image_type_to_mime_type(getimagesizefromstring($data)[2]);\n\n\t\t// Check if the MIME type is a valid image MIME type\n\t\tif (strpos($mime, 'image/') !== 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// If everything is okay, return true\n\t\treturn true;\n\t}\n\n\tpublic static function validateDnsLoc(string $input)\n\t{\n\t\t$pattern = '/^\n        (\\d{1,2})[ \\t]+                # latitude degrees\n        (\\d{1,2})[ \\t]+                # latitude minutes\n        (\\d{1,2}(?:\\.\\d+)?)[ \\t]+      # latitude seconds\n        ([NS])[ \\t]+                   # latitude direction\n        (\\d{1,3})[ \\t]+                # longitude degrees\n        (\\d{1,2})[ \\t]+                # longitude minutes\n        (\\d{1,2}(?:\\.\\d+)?)[ \\t]+      # longitude seconds\n        ([EW])[ \\t]+                   # longitude direction\n        (-?\\d+(?:\\.\\d+)?)m          # altitude\n        (?:[ \\t]+(\\d+(?:\\.\\d+)?)m      # size (optional)\n        (?:[ \\t]+(\\d+(?:\\.\\d+)?)m      # horiz precision (optional)\n        (?:[ \\t]+(\\d+(?:\\.\\d+)?)m)?    # vert precision (optional)\n        )?)?$/x';\n\n\t\tif (!preg_match($pattern, $input, $matches)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t[\n\t\t\t,\n\t\t\t$latDeg, $latMin, $latSec, $latDir,\n\t\t\t$lonDeg, $lonMin, $lonSec, $lonDir,\n\t\t\t$alt,\n\t\t\t$size, $hPrec, $vPrec\n\t\t] = $matches + array_fill(0, 13, null);\n\n\t\t// Range checks\n\t\tif ($latDeg > 90) return false;\n\t\tif ($latMin > 59) return false;\n\t\tif ($latSec >= 60) return false;\n\n\t\tif ($lonDeg > 180) return false;\n\t\tif ($lonMin > 59) return false;\n\t\tif ($lonSec >= 60) return false;\n\n\t\treturn $input;\n\t}\n\n\tpublic static function validateDnsRp(string $input)\n\t{\n\t\t$parts = preg_split('/\\s+/', trim($input));\n\n\t\tif (count($parts) !== 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\t[$mboxDname, $txtDname] = $parts;\n\n\t\t// remove trailing dot if any\n\t\t$mboxDname = rtrim($mboxDname, '.');\n\t\t$txtDname = rtrim($txtDname, '.');\n\n\t\tif (!self::validateDomain($mboxDname)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!self::validateDomain($txtDname)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $input;\n\t}\n\n\tpublic static function validateDnsSshfp(string $input)\n\t{\n\t\t$parts = preg_split('/\\s+/', trim($input));\n\n\t\tif (count($parts) !== 3) {\n\t\t\treturn false;\n\t\t}\n\n\t\t[$algorithm, $type, $fingerprint] = $parts;\n\n\t\t// ---- algorithm ----\n\t\t$validAlgorithms = [1, 2, 3, 4, 6];\n\n\t\tif (!ctype_digit($algorithm) || !in_array((int)$algorithm, $validAlgorithms, true)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// ---- fingerprint type ----\n\t\t$validTypes = [1, 2];\n\n\t\tif (!ctype_digit($type) || !in_array((int)$type, $validTypes, true)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// ---- check fingerprint ----\n\t\tif (!ctype_xdigit($fingerprint)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$type = (int)$type;\n\n\t\tswitch ($type) {\n\t\t\tcase 1: // SHA-1\n\t\t\t\t$expectedLength = 40;\n\t\t\t\tbreak;\n\n\t\t\tcase 2: // SHA-256\n\t\t\t\t$expectedLength = 64;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t$expectedLength = 0;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (strlen($fingerprint) !== $expectedLength) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $input;\n\t}\n\n\tpublic static function validateDnsTlsa(string $input)\n\t{\n\t\t$parts = preg_split('/\\s+/', trim($input));\n\n\t\tif (count($parts) !== 4) {\n\t\t\treturn false;\n\t\t}\n\n\t\t[$usage, $selector, $matchingType, $data] = $parts;\n\n\t\t// ---- usage ----\n\t\t$validUsage = [0, 1, 2, 3];\n\n\t\tif (!ctype_digit($usage) || !in_array((int)$usage, $validUsage, true)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// ---- selector ----\n\t\t$validSelector = [0, 1];\n\n\t\tif (!ctype_digit($selector) || !in_array((int)$selector, $validSelector, true)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// ---- matching type ----\n\t\t$validMatching = [0, 1, 2];\n\n\t\tif (!ctype_digit($matchingType) || !in_array((int)$matchingType, $validMatching, true)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// ---- certificate association data ----\n\t\tif (!ctype_xdigit($data)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$matchingType = (int)$matchingType;\n\n\t\tif ($matchingType === 1 && strlen($data) !== 64) {\n\t\t\treturn false; // SHA-256\n\t\t}\n\n\t\tif ($matchingType === 2 && strlen($data) !== 128) {\n\t\t\treturn false; // SHA-512\n\t\t}\n\n\t\tif ($matchingType === 0 && (strlen($data) < 2 || strlen($data) > 4096)) {\n\t\t\treturn false; // at least 1 byte hex\n\t\t}\n\n\t\treturn $input;\n\t}\n\n\tpublic static function validateDnsNaptr(string $input): bool\n\t{\n\t\t// Split respecting quoted strings\n\t\t$pattern = '/^\n        (\\d{1,5})\\s+                # order\n        (\\d{1,5})\\s+                # preference\n        \"([^\"]*)\"\\s+                # flags\n        \"([^\"]*)\"\\s+                # services\n        \"([^\"]*)\"\\s+                # regexp\n        (\\S+)                      # replacement\n    $/x';\n\n\t\tif (!preg_match($pattern, $input, $matches)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t[, $order, $preference, $flags, $services, $regexp, $replacement] = $matches;\n\n\t\t// 1. order & preference: 0–65535\n\t\tif ($order < 0 || $order > 65535 || $preference < 0 || $preference > 65535) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// 2. flags: allowed chars (RFC says single letters typically, but allow multiple)\n\t\tif (!preg_match('/^[A-Za-z0-9]*$/', $flags)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// 3. services: usually like \"E2U+sip\"\n\t\tif (!preg_match('/^[A-Za-z0-9+:\\-]*$/', $services)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// 4. regexp: delimiter-based substitution (very loose validation)\n\t\t// Example: !^.*$!sip:info@example.com!\n\t\tif ($regexp !== '') {\n\t\t\t$delim = $regexp[0];\n\t\t\tif (substr_count($regexp, $delim) < 3) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// 5. replacement: must be \".\" or valid domain\n\t\tif ($replacement !== '.') {\n\t\t\tif (!filter_var($replacement, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "lib/Froxlor/Validate/index.html",
    "content": ""
  },
  {
    "path": "lib/Froxlor/index.html",
    "content": ""
  },
  {
    "path": "lib/ajax.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nnamespace Froxlor;\n\nuse Exception;\nuse Froxlor\\Ajax\\Ajax;\n\nrequire_once dirname(__DIR__) . '/vendor/autoload.php';\n\n// Load the user settings\nif (!file_exists('./userdata.inc.php')) {\n\tdie();\n}\nrequire_once dirname(__DIR__) . '/lib/userdata.inc.php';\nrequire_once dirname(__DIR__) . '/lib/functions.php';\nrequire_once dirname(__DIR__) . '/lib/tables.inc.php';\n\n// Return response\ntry {\n\techo (new Ajax)->handle();\n} catch (Exception $e) {\n\theader(\"Content-Type: application/json\");\n\techo \\Froxlor\\Api\\Response::jsonErrorResponse($e->getMessage(), 500);\n}\n"
  },
  {
    "path": "lib/config.example.inc.php",
    "content": "<?php\n\n/**\n * change the options below to either true or false\n */\nreturn [\n\t/**\n\t * enable/disable the possibility to update froxlor from within the web-interface,\n\t * recommended value for debian/ubuntu package users is false to rely on apt and not have version mixup.\n\t * This is also useful for providers that manage the servers but give admin access to froxlor to handle\n\t * updates the way the providers does it (e.g. automation, etc.)\n\t *\n\t * Default: false\n\t */\n\t'enable_webupdate' => false,\n\n\t/**\n\t * settings that have a major impact on the system or which values are used to be executed with high\n\t * privileges on the system require the admin-user to have set up and enabled OTP for the corresponding\n\t * account to change these values.\n\t * To disable this extra security validation, set the value of this to true\n\t *\n\t * Default: false\n\t */\n\t'disable_otp_security_check' => false,\n\n\t/**\n\t * For debugging/development purposes only.\n\t * Enable to display all php related issue (notices, warnings, etc.; depending on php.ini) for froxlor itself\n\t *\n\t * Default: false\n\t */\n\t'display_php_errors' => false,\n];\n"
  },
  {
    "path": "lib/configfiles/bookworm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<froxlor>\n\t<distribution name=\"Debian\" codename=\"Bookworm\"\n\t\tversion=\"12.x\" defaulteditor=\"/bin/nano\">\n\t\t<!-- OS defaults to be loaded on installation -->\n\t\t<defaults>\n\t\t\t<default settinggroup=\"system\" varname=\"nssextrausers\" value=\"1\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_vhost\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_diroptions\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_htpasswddir\" value=\"/etc/nginx/froxlor-htpasswd/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apachereload_command\" value=\"service nginx reload\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"letsencryptacmeconf\" value=\"/etc/nginx/acme.conf\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"phpfpm\" varname=\"fastcgi_ipcdir\" value=\"/var/run/php/\"></default>\n\t\t</defaults>\n\t\t<services>\n\t\t\t<!-- HTTP -->\n\t\t\t<service type=\"http\" title=\"{{lng.admin.configfiles.http}}\">\n\t\t\t\t<!-- general HTTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.documentroot_prefix}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.logfiles_directory}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"notempty\">{{settings.system.deactivateddocroot}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.deactivateddocroot}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- HTTP Apache -->\n\t\t\t\t<daemon name=\"apache\" version=\"2.4\" title=\"Apache 2.4\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2]]></install>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command><![CDATA[a2dismod userdir]]></command>\n\t\t\t\t\t<command><![CDATA[a2enmod headers]]></command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.use_ssl}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[a2enmod ssl]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n# Please remember to activate the use of mod_proxy / mod_proxy_fcgi in the PHP-FPM settings!!!\na2enmod proxy_fcgi\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nAlias \"/.well-known/acme-challenge\" \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\"\n<Directory \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\">\n\tRequire all granted\n</Directory>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- HTTP Nginx -->\n\t\t\t\t<daemon name=\"nginx\" title=\"nginx\">\n\t\t\t\t\t<install><![CDATA[apt-get install nginx]]></install>\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install php-cgi]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<file name=\"/etc/nginx/nginx.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nuser www-data;\nworker_processes auto;\npid /var/run/nginx.pid;\ninclude /etc/nginx/modules-enabled/*.conf;\n\nevents {\n\tworker_connections 768;\n\t# multi_accept on;\n}\n\nhttp {\n\n\t##\n\t# Basic Settings\n\t##\n\n\tsendfile on;\n\ttcp_nopush on;\n\ttcp_nodelay on;\n\tkeepalive_timeout 65;\n\ttypes_hash_max_size 2048;\n\t# server_tokens off;\n\n\t# server_names_hash_bucket_size 64;\n\t# server_name_in_redirect off;\n\n\tinclude /etc/nginx/mime.types;\n\tdefault_type application/octet-stream;\n\n\t##\n\t# Logging Settings\n\t##\n\n\taccess_log /var/log/nginx/access.log;\n\terror_log /var/log/nginx/error.log;\n\n\t##\n\t# Gzip Settings\n\t##\n\n\tgzip on;\n\tgzip_disable \"msie6\";\n\n\t# gzip_vary on;\n\t# gzip_proxied any;\n\t# gzip_comp_level 6;\n\t# gzip_buffers 16 8k;\n\t# gzip_http_version 1.1;\n\t# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;\n\n\t##\n\t# nginx-naxsi config\n\t##\n\t# Uncomment it if you installed nginx-naxsi\n\t##\n\n\t#include /etc/nginx/naxsi_core.rules;\n\n\t##\n\t# nginx-passenger config\n\t##\n\t# Uncomment it if you installed nginx-passenger\n\t##\n\n\t#passenger_root /usr;\n\t#passenger_ruby /usr/bin/ruby;\n\n\t##\n\t# Virtual Host Configs\n\t##\n\n\tinclude /etc/nginx/conf.d/*.conf;\n\tinclude /etc/nginx/sites-enabled/*;\n}\n\n\n#mail {\n#\t# See sample authentication script at:\n#\t# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript\n#\n#\t# auth_http localhost/auth.php;\n#\t# pop3_capabilities \"TOP\" \"USER\";\n#\t# imap_capabilities \"IMAP4rev1\" \"UIDPLUS\";\n#\n#\tserver {\n#\t\tlisten     localhost:110;\n#\t\tprotocol   pop3;\n#\t\tproxy      on;\n#\t}\n#\n#\tserver {\n#\t\tlisten     localhost:143;\n#\t\tprotocol   imap;\n#\t\tproxy      on;\n#\t}\n#}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/nginx/fastcgi_params\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nfastcgi_connect_timeout 65;\nfastcgi_send_timeout    180;\nfastcgi_read_timeout    180;\n\nfastcgi_param  QUERY_STRING       $query_string;\nfastcgi_param  REQUEST_METHOD     $request_method;\nfastcgi_param  CONTENT_TYPE       $content_type;\nfastcgi_param  CONTENT_LENGTH     $content_length;\n\nfastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\nfastcgi_param  REQUEST_URI        $request_uri;\nfastcgi_param  DOCUMENT_URI       $document_uri;\nfastcgi_param  DOCUMENT_ROOT      $document_root;\nfastcgi_param  SERVER_PROTOCOL    $server_protocol;\nfastcgi_param  HTTPS              $https if_not_empty;\n\nfastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\nfastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\nfastcgi_param  REMOTE_ADDR        $remote_addr;\nfastcgi_param  REMOTE_PORT        $remote_port;\nfastcgi_param  SERVER_ADDR        $server_addr;\nfastcgi_param  SERVER_PORT        $server_port;\nfastcgi_param  SERVER_NAME        $server_name;\n\n# PHP only, required if PHP was built with --enable-force-cgi-redirect\nfastcgi_param  REDIRECT_STATUS    200;\n\n# Fix for HTTP/3 ($http_host is not populated automatically)\nfastcgi_param  HTTP_HOST          $host;\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nlocation /.well-known/acme-challenge {\n\talias {{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge;\n\n\tlocation ~ /.well-known/acme-challenge/(.*) {\n\t\tdefault_type text/plain;\n\t}\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/init.d/php-fcgi\" backup=\"true\" chmod=\"u+x\">\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n#!/bin/bash\nBIND=\"127.0.0.1:8888\"\nUSER=\"www-data\"\nPHP_FCGI_CHILDREN=\"15\"\nPHP_FCGI_MAX_REQUESTS=\"1000\"\n\nPHP_CGI=\"/usr/bin/php-cgi\"\nPHP_CGI_NAME=\"$(basename ${PHP_CGI})\"\nPHP_CGI_ARGS=\"- USER=${USER} PATH=/usr/bin PHP_FCGI_CHILDREN=${PHP_FCGI_CHILDREN} PHP_FCGI_MAX_REQUESTS=${PHP_FCGI_MAX_REQUESTS} ${PHP_CGI} -b ${BIND}\"\nRETVAL=\"0\"\n\nstart() {\n      echo -n \"Starting PHP FastCGI: \"\n      start-stop-daemon --quiet --start --background --chuid \"$USER\" --exec /usr/bin/env -- $PHP_CGI_ARGS\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\nstop() {\n      echo -n \"Stopping PHP FastCGI: \"\n      killall -q -w -u ${USER} ${PHP_CGI}\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\n\ncase \"$1\" in\n    start)\n      start\n  ;;\n    stop)\n      stop\n  ;;\n    restart)\n      stop\n      start\n  ;;\n    *)\n      echo \"Usage: php-fastcgi {start|stop|restart}\"\n      exit 1\n  ;;\nesac\nexit \"$RETVAL\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[/etc/init.d/php-fcgi restart]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!--DNS -->\n\t\t\t<service type=\"dns\" title=\"{{lng.admin.configfiles.dns}}\">\n\t\t\t\t<!--Bind9 -->\n\t\t\t\t<daemon name=\"bind\" title=\"Bind9 nameserver\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install bind9]]></install>\n\t\t\t\t\t<command><![CDATA[echo \"include \\\"{{settings.system.bindconf_directory}}froxlor_bind.conf\\\";\" >> /etc/bind/named.conf.local]]></command>\n\t\t\t\t\t<command><![CDATA[touch {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chown bind:0 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chmod 0644 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[service bind9 restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns\" title=\"PowerDNS (standalone)\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server pdns-backend-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\nallow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# mysql-settings / you need to create the power-dns database for yourself!\nlaunch=gmysql\ngmysql-host=127.0.0.1\ngmysql-port=3306\ngmysql-dbname=pdns\ngmysql-user=powerdns\ngmysql-group=client\ngmysql-password=\n#gmysql-ssl-ca-file=\n#gmysql-ssl-verify-server-certificate=0\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns_bind\"\n\t\t\t\t\ttitle=\"PowerDNS via bind-backend\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\n# allow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\nlaunch=bind\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# Bind backend configuration\n\n# Location of the Bind configuration file to parse.\nbind-config=<BIND_CONFIG_PATH>named.conf\n\n# How often to check for zone changes. See 'Operation' section.\nbind-check-interval=180\n\n# Uncomment to enable Huffman compression on zone data.\n# Currently saves around 20% of memory actually used, but slows down operation.\n# bind-enable-huffman\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- SMTP services -->\n\t\t\t<service type=\"smtp\" title=\"{{lng.admin.configfiles.smtp}}\">\n\t\t\t\t<!-- general SMTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"groupnotexists\">{{settings.system.vmail_gid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[groupadd -g {{settings.system.vmail_gid}} vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"usernotexists\">{{settings.system.vmail_uid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[useradd -u {{settings.system.vmail_uid}} -g vmail vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install postfix postfix-mysql]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/etc/pam.d]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/var/run/mysqld]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R {{settings.system.vmail_uid}}:{{settings.system.vmail_gid}} {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0750  {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"0\">\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_alias_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT destination FROM mail_virtual AS v, panel_customers AS c WHERE c.customerid = v.customerid AND c.deactivated = 0 AND v.email = '%s' AND trim(v.destination) <> ''\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_domains.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' AND deactivated = 0\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_sender_permissions.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT GROUP_CONCAT(DISTINCT mu.username SEPARATOR ' ') AS sasl_users FROM mail_users mu WHERE mu.username = '%s' OR mu.email IN ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = CONCAT('@', SUBSTRING_INDEX('%s','@',-1))));\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_uid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT uid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_gid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT gid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/aliases\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# /etc/aliases\nmailer-daemon: postmaster\npostmaster: root\nnobody: root\nhostmaster: root\nusenet: root\nnews: root\nwebmaster: root\nwww: root\nftp: root\nabuse: root\nnoc: root\nsecurity: root\n\n# change this to a valid e-mail address you can access\nroot:               <ADMIN_MAIL>\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[newaliases]]></command>\n\t\t\t\t\t\t<command><![CDATA[service postfix restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- postfix with dovecot -->\n\t\t\t\t<daemon name=\"postfix_dovecot\" title=\"Postfix with dovecot\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<file name=\"/etc/postfix/main.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Global Postfix configuration file. This file lists only a subset\n# of all parameters. For the syntax, and for a complete parameter\n# list, see the postconf(5) manual page (command: \"man 5 postconf\").\n#\n# For common configuration examples, see BASIC_CONFIGURATION_README\n# and STANDARD_CONFIGURATION_README. To find these documents, use\n# the command \"postconf html_directory readme_directory\", or go to\n# http://www.postfix.org/.\n#\n# For best results, change no more than 2-3 parameters at a time,\n# and test if Postfix still works after every change.\n\n# SOFT BOUNCE\n#\n# The soft_bounce parameter provides a limited safety net for\n# testing.  When soft_bounce is enabled, mail will remain queued that\n# would otherwise bounce. This parameter disables locally-generated\n# bounces, and prevents the SMTP server from rejecting mail permanently\n# (by changing 5xx replies into 4xx replies). However, soft_bounce\n# is no cure for address rewriting mistakes or mail routing mistakes.\n#\n#soft_bounce = no\n\n# LOCAL PATHNAME INFORMATION\n#\n# The queue_directory specifies the location of the Postfix queue.\n# This is also the root directory of Postfix daemons that run chrooted.\n# See the files in examples/chroot-setup for setting up Postfix chroot\n# environments on different UNIX systems.\n#\n#queue_directory = /var/spool/postfix\n\n# The command_directory parameter specifies the location of all\n# postXXX commands.\n#\ncommand_directory = /usr/sbin\n\n# The daemon_directory parameter specifies the location of all Postfix\n# daemon programs (i.e. programs listed in the master.cf file). This\n# directory must be owned by root.\n#\ndaemon_directory = /usr/lib/postfix/sbin\n\n# The data_directory parameter specifies the location of Postfix-writable\n# data files (caches, random numbers). This directory must be owned\n# by the mail_owner account (see below).\n#\ndata_directory = /var/lib/postfix\n\n# QUEUE AND PROCESS OWNERSHIP\n#\n# The mail_owner parameter specifies the owner of the Postfix queue\n# and of most Postfix daemon processes.  Specify the name of a user\n# account THAT DOES NOT SHARE ITS USER OR GROUP ID WITH OTHER ACCOUNTS\n# AND THAT OWNS NO OTHER FILES OR PROCESSES ON THE SYSTEM.  In\n# particular, don't specify nobody or daemon. PLEASE USE A DEDICATED\n# USER.\n#\n#mail_owner = postfix\n\n# The default_privs parameter specifies the default rights used by\n# the local delivery agent for delivery to external file or command.\n# These rights are used in the absence of a recipient user context.\n# DO NOT SPECIFY A PRIVILEGED USER OR THE POSTFIX OWNER.\n#\n#default_privs = nobody\n\n# INTERNET HOST AND DOMAIN NAMES\n#\n# The myhostname parameter specifies the internet hostname of this\n# mail system. The default is to use the fully-qualified domain name\n# from gethostname(). $myhostname is used as a default value for many\n# other configuration parameters.\n#\n# Froxlor Note: $myhostname can and should be the same as $mydomain as long as\n# you don't intend to send mail to it (it will be considered local, not virtual)\n# for the case of a subdomain, $mydomain *must* be equal to $myhostname,\n# otherwise you cannot use the main domain for virtual transport.\n# also check the note about $mydomain below.\nmyhostname = $mydomain\n#myhostname = virtual.domain.tld\n\n# The mydomain parameter specifies the local internet domain name.\n# The default is to use $myhostname minus the first component.\n# $mydomain is used as a default value for many other configuration\n# parameters.\n#\n# Froxlor Note: We are using a default here but that may or may not make sense,\n# depending on your dns configuration, please check yourself.\n\n# FQDN from Froxlor\nmydomain = <SERVERNAME>\n\n# SENDING MAIL\n#\n# The myorigin parameter specifies the domain that locally-posted\n# mail appears to come from. The default is to append $myhostname,\n# which is fine for small sites.  If you run a domain with multiple\n# machines, you should (1) change this to $mydomain and (2) set up\n# a domain-wide alias database that aliases each user to\n# user@that.users.mailhost.\n#\n# For the sake of consistency between sender and recipient addresses,\n# myorigin also specifies the default domain name that is appended\n# to recipient addresses that have no @domain part.\n#\n# Debian GNU/Linux specific:  Specifying a file name will cause the\n# first line of that file to be used as the name.  The Debian default\n# is /etc/mailname.\n#\n#myorigin = /etc/mailname\n#myorigin = $myhostname\n#myorigin = $mydomain\n\n# RECEIVING MAIL\n\n# The inet_interfaces parameter specifies the network interface\n# addresses that this mail system receives mail on.  By default,\n# the software claims all active interfaces on the machine. The\n# parameter also controls delivery of mail to user@[ip.address].\n#\n# See also the proxy_interfaces parameter, for network addresses that\n# are forwarded to us via a proxy or network address translator.\n#\n# Note: you need to stop/start Postfix when this parameter changes.\n#\ninet_interfaces = all\n#inet_interfaces = $myhostname\n#inet_interfaces = $myhostname, localhost\n\n# The proxy_interfaces parameter specifies the network interface\n# addresses that this mail system receives mail on by way of a\n# proxy or network address translation unit. This setting extends\n# the address list specified with the inet_interfaces parameter.\n#\n# You must specify your proxy/NAT addresses when your system is a\n# backup MX host for other domains, otherwise mail delivery loops\n# will happen when the primary MX host is down.\n#\n#proxy_interfaces =\n#proxy_interfaces = 1.2.3.4\n\n# The mydestination parameter specifies the list of domains that this\n# machine considers itself the final destination for.\n#\n# These domains are routed to the delivery agent specified with the\n# local_transport parameter setting. By default, that is the UNIX\n# compatible delivery agent that lookups all recipients in /etc/passwd\n# and /etc/aliases or their equivalent.\n#\n# The default is $myhostname + localhost.$mydomain.  On a mail domain\n# gateway, you should also include $mydomain.\n#\n# Do not specify the names of virtual domains - those domains are\n# specified elsewhere (see VIRTUAL_README).\n#\n# Do not specify the names of domains that this machine is backup MX\n# host for. Specify those names via the relay_domains settings for\n# the SMTP server, or use permit_mx_backup if you are lazy (see\n# STANDARD_CONFIGURATION_README).\n#\n# The local machine is always the final destination for mail addressed\n# to user@[the.net.work.address] of an interface that the mail system\n# receives mail on (see the inet_interfaces parameter).\n#\n# Specify a list of host or domain names, /file/name or type:table\n# patterns, separated by commas and/or whitespace. A /file/name\n# pattern is replaced by its contents; a type:table is matched when\n# a name matches a lookup key (the right-hand side is ignored).\n# Continue long lines by starting the next line with whitespace.\n#\n# See also below, section \"REJECTING MAIL FOR UNKNOWN LOCAL USERS\".\n#\nmydestination = $myhostname, localhost.$mydomain, localhost\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain,\n#\tmail.$mydomain, www.$mydomain, ftp.$mydomain\n\n# REJECTING MAIL FOR UNKNOWN LOCAL USERS\n#\n# The local_recipient_maps parameter specifies optional lookup tables\n# with all names or addresses of users that are local with respect\n# to $mydestination, $inet_interfaces or $proxy_interfaces.\n#\n# If this parameter is defined, then the SMTP server will reject\n# mail for unknown local users. This parameter is defined by default.\n#\n# To turn off local recipient checking in the SMTP server, specify\n# local_recipient_maps = (i.e. empty).\n#\n# The default setting assumes that you use the default Postfix local\n# delivery agent for local delivery. You need to update the\n# local_recipient_maps setting if:\n#\n# - You define $mydestination domain recipients in files other than\n#   /etc/passwd, /etc/aliases, or the $virtual_alias_maps files.\n#   For example, you define $mydestination domain recipients in\n#   the $virtual_mailbox_maps files.\n#\n# - You redefine the local delivery agent in master.cf.\n#\n# - You redefine the \"local_transport\" setting in main.cf.\n#\n# - You use the \"luser_relay\", \"mailbox_transport\", or \"fallback_transport\"\n#   feature of the Postfix local delivery agent (see local(8)).\n#\n# Details are described in the LOCAL_RECIPIENT_README file.\n#\n# Beware: if the Postfix SMTP server runs chrooted, you probably have\n# to access the passwd file via the proxymap service, in order to\n# overcome chroot restrictions. The alternative, having a copy of\n# the system passwd file in the chroot jail is just not practical.\n#\n# The right-hand side of the lookup tables is conveniently ignored.\n# In the left-hand side, specify a bare username, an @domain.tld\n# wild-card, or specify a user@domain.tld address.\n#\n#local_recipient_maps = unix:passwd.byname $alias_maps\n#local_recipient_maps = proxy:unix:passwd.byname $alias_maps\n#local_recipient_maps =\n\n# The unknown_local_recipient_reject_code specifies the SMTP server\n# response code when a recipient domain matches $mydestination or\n# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty\n# and the recipient address or address local-part is not found.\n#\n# The default setting is 550 (reject mail) but it is safer to start\n# with 450 (try again later) until you are certain that your\n# local_recipient_maps settings are OK.\n#\nunknown_local_recipient_reject_code = 550\n\n# TRUST AND RELAY CONTROL\n\n# The mynetworks parameter specifies the list of \"trusted\" SMTP\n# clients that have more privileges than \"strangers\".\n#\n# In particular, \"trusted\" SMTP clients are allowed to relay mail\n# through Postfix.  See the smtpd_recipient_restrictions parameter\n# in postconf(5).\n#\n# You can specify the list of \"trusted\" network addresses by hand\n# or you can let Postfix do it for you (which is the default).\n#\n# By default (mynetworks_style = subnet), Postfix \"trusts\" SMTP\n# clients in the same IP subnetworks as the local machine.\n# On Linux, this does works correctly only with interfaces specified\n# with the \"ifconfig\" command.\n#\n# Specify \"mynetworks_style = class\" when Postfix should \"trust\" SMTP\n# clients in the same IP class A/B/C networks as the local machine.\n# Don't do this with a dialup site - it would cause Postfix to \"trust\"\n# your entire provider's network.  Instead, specify an explicit\n# mynetworks list by hand, as described below.\n#\n# Specify \"mynetworks_style = host\" when Postfix should \"trust\"\n# only the local machine.\n#\n#mynetworks_style = class\n#mynetworks_style = subnet\n#mynetworks_style = host\n\n# Alternatively, you can specify the mynetworks list by hand, in\n# which case Postfix ignores the mynetworks_style setting.\n#\n# Specify an explicit list of network/netmask patterns, where the\n# mask specifies the number of bits in the network part of a host\n# address.\n#\n# You can also specify the absolute pathname of a pattern file instead\n# of listing the patterns here. Specify type:table for table-based lookups\n# (the value on the table right-hand side is not used).\n#\n#mynetworks = 168.100.189.0/28, 127.0.0.0/8\n#mynetworks = $config_directory/mynetworks\n#mynetworks = hash:/etc/postfix/network_table\nmynetworks = 127.0.0.0/8\n\n# The relay_domains parameter restricts what destinations this system will\n# relay mail to.  See the smtpd_recipient_restrictions description in\n# postconf(5) for detailed information.\n#\n# By default, Postfix relays mail\n# - from \"trusted\" clients (IP address matches $mynetworks) to any destination,\n# - from \"untrusted\" clients to destinations that match $relay_domains or\n#   subdomains thereof, except addresses with sender-specified routing.\n# The default relay_domains value is $mydestination.\n#\n# In addition to the above, the Postfix SMTP server by default accepts mail\n# that Postfix is final destination for:\n# - destinations that match $inet_interfaces or $proxy_interfaces,\n# - destinations that match $mydestination\n# - destinations that match $virtual_alias_domains,\n# - destinations that match $virtual_mailbox_domains.\n# These destinations do not need to be listed in $relay_domains.\n#\n# Specify a list of hosts or domains, /file/name patterns or type:name\n# lookup tables, separated by commas and/or whitespace.  Continue\n# long lines by starting the next line with whitespace. A file name\n# is replaced by its contents; a type:name table is matched when a\n# (parent) domain appears as lookup key.\n#\n# NOTE: Postfix will not automatically forward mail for domains that\n# list this system as their primary or backup MX host. See the\n# permit_mx_backup restriction description in postconf(5).\n#\n#relay_domains = $mydestination\n\n# INTERNET OR INTRANET\n\n# The relayhost parameter specifies the default host to send mail to\n# when no entry is matched in the optional transport(5) table. When\n# no relayhost is given, mail is routed directly to the destination.\n#\n# On an intranet, specify the organizational domain name. If your\n# internal DNS uses no MX records, specify the name of the intranet\n# gateway host instead.\n#\n# In the case of SMTP, specify a domain, host, host:port, [host]:port,\n# [address] or [address]:port; the form [host] turns off MX lookups.\n#\n# If you're connected via UUCP, see also the default_transport parameter.\n#\n#relayhost = $mydomain\n#relayhost = [gateway.my.domain]\n#relayhost = [mailserver.isp.tld]\n#relayhost = uucphost\n#relayhost = [an.ip.add.ress]\n\n# REJECTING UNKNOWN RELAY USERS\n#\n# The relay_recipient_maps parameter specifies optional lookup tables\n# with all addresses in the domains that match $relay_domains.\n#\n# If this parameter is defined, then the SMTP server will reject\n# mail for unknown relay users. This feature is off by default.\n#\n# The right-hand side of the lookup tables is conveniently ignored.\n# In the left-hand side, specify an @domain.tld wild-card, or specify\n# a user@domain.tld address.\n#\n#relay_recipient_maps = hash:/etc/postfix/relay_recipients\n\n# INPUT RATE CONTROL\n#\n# The in_flow_delay configuration parameter implements mail input\n# flow control. This feature is turned on by default, although it\n# still needs further development (it's disabled on SCO UNIX due\n# to an SCO bug).\n#\n# A Postfix process will pause for $in_flow_delay seconds before\n# accepting a new message, when the message arrival rate exceeds the\n# message delivery rate. With the default 100 SMTP server process\n# limit, this limits the mail inflow to 100 messages a second more\n# than the number of messages delivered per second.\n#\n# Specify 0 to disable the feature. Valid delays are 0..10.\n#\n#in_flow_delay = 1s\n\n# ADDRESS REWRITING\n#\n# The ADDRESS_REWRITING_README document gives information about\n# address masquerading or other forms of address rewriting including\n# username->Firstname.Lastname mapping.\n\n# ADDRESS REDIRECTION (VIRTUAL DOMAIN)\n#\n# The VIRTUAL_README document gives information about the many forms\n# of domain hosting that Postfix supports.\n\n# \"USER HAS MOVED\" BOUNCE MESSAGES\n#\n# See the discussion in the ADDRESS_REWRITING_README document.\n\n# TRANSPORT MAP\n#\n# See the discussion in the ADDRESS_REWRITING_README document.\n\n# ALIAS DATABASE\n#\n# The alias_maps parameter specifies the list of alias databases used\n# by the local delivery agent. The default list is system dependent.\n#\n# On systems with NIS, the default is to search the local alias\n# database, then the NIS alias database. See aliases(5) for syntax\n# details.\n#\n# If you change the alias database, run \"postalias /etc/aliases\" (or\n# wherever your system stores the mail alias file), or simply run\n# \"newaliases\" to build the necessary DBM or DB file.\n#\n# It will take a minute or so before changes become visible.  Use\n# \"postfix reload\" to eliminate the delay.\n#\n#alias_maps = dbm:/etc/aliases\n#alias_maps = hash:/etc/aliases\n#alias_maps = hash:/etc/aliases, nis:mail.aliases\n#alias_maps = netinfo:/aliases\n\n# The alias_database parameter specifies the alias database(s) that\n# are built with \"newaliases\" or \"sendmail -bi\".  This is a separate\n# configuration parameter, because alias_maps (see above) may specify\n# tables that are not necessarily all under control by Postfix.\n#\n#alias_database = dbm:/etc/aliases\n#alias_database = dbm:/etc/mail/aliases\n#alias_database = hash:/etc/aliases\n#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases\n\n# ADDRESS EXTENSIONS (e.g., user+foo)\n#\n# The recipient_delimiter parameter specifies the separator between\n# user names and address extensions (user+foo). See canonical(5),\n# local(8), relocated(5) and virtual(5) for the effects this has on\n# aliases, canonical, virtual, relocated and .forward file lookups.\n# Basically, the software tries user+foo and .forward+foo before\n# trying user and .forward.\n#\n#recipient_delimiter = +\n\n# DELIVERY TO MAILBOX\n#\n# The home_mailbox parameter specifies the optional pathname of a\n# mailbox file relative to a user's home directory. The default\n# mailbox file is /var/spool/mail/user or /var/mail/user.  Specify\n# \"Maildir/\" for qmail-style delivery (the / is required).\n#\n#home_mailbox = Mailbox\n#home_mailbox = Maildir/\n\n# The mail_spool_directory parameter specifies the directory where\n# UNIX-style mailboxes are kept. The default setting depends on the\n# system type.\n#\n#mail_spool_directory = /var/mail\n#mail_spool_directory = /var/spool/mail\n\n# The mailbox_command parameter specifies the optional external\n# command to use instead of mailbox delivery. The command is run as\n# the recipient with proper HOME, SHELL and LOGNAME environment settings.\n# Exception:  delivery for root is done as $default_user.\n#\n# Other environment variables of interest: USER (recipient username),\n# EXTENSION (address extension), DOMAIN (domain part of address),\n# and LOCAL (the address localpart).\n#\n# Unlike other Postfix configuration parameters, the mailbox_command\n# parameter is not subjected to $parameter substitutions. This is to\n# make it easier to specify shell syntax (see example below).\n#\n# Avoid shell meta characters because they will force Postfix to run\n# an expensive shell process. Procmail alone is expensive enough.\n#\n# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN\n# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER.\n#\nmailbox_command = /usr/lib/dovecot/deliver\n#mailbox_command = /usr/bin/procmail -a \"$EXTENSION\"\n\n# The mailbox_transport specifies the optional transport in master.cf\n# to use after processing aliases and .forward files. This parameter\n# has precedence over the mailbox_command, fallback_transport and\n# luser_relay parameters.\n#\n# Specify a string of the form transport:nexthop, where transport is\n# the name of a mail delivery transport defined in master.cf.  The\n# :nexthop part is optional. For more details see the sample transport\n# configuration file.\n#\n# NOTE: if you use this feature for accounts not in the UNIX password\n# file, then you must update the \"local_recipient_maps\" setting in\n# the main.cf file, otherwise the SMTP server will reject mail for\n# non-UNIX accounts with \"User unknown in local recipient table\".\n#\n# Cyrus IMAP over LMTP. Specify ``lmtpunix      cmd=\"lmtpd\"\n# listen=\"/var/imap/socket/lmtp\" prefork=0'' in cyrus.conf.\n#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp\n#\n# Cyrus IMAP via command line. Uncomment the \"cyrus...pipe\" and\n# subsequent line in master.cf.\n#mailbox_transport = cyrus\n\n# The fallback_transport specifies the optional transport in master.cf\n# to use for recipients that are not found in the UNIX passwd database.\n# This parameter has precedence over the luser_relay parameter.\n#\n# Specify a string of the form transport:nexthop, where transport is\n# the name of a mail delivery transport defined in master.cf.  The\n# :nexthop part is optional. For more details see the sample transport\n# configuration file.\n#\n# NOTE: if you use this feature for accounts not in the UNIX password\n# file, then you must update the \"local_recipient_maps\" setting in\n# the main.cf file, otherwise the SMTP server will reject mail for\n# non-UNIX accounts with \"User unknown in local recipient table\".\n#\n#fallback_transport = lmtp:unix:/file/name\n#fallback_transport = cyrus\n#fallback_transport =\n\n# The luser_relay parameter specifies an optional destination address\n# for unknown recipients.  By default, mail for unknown@$mydestination,\n# unknown@[$inet_interfaces] or unknown@[$proxy_interfaces] is returned\n# as undeliverable.\n#\n# The following expansions are done on luser_relay: $user (recipient\n# username), $shell (recipient shell), $home (recipient home directory),\n# $recipient (full recipient address), $extension (recipient address\n# extension), $domain (recipient domain), $local (entire recipient\n# localpart), $recipient_delimiter. Specify ${name?value} or\n# ${name:value} to expand value only when $name does (does not) exist.\n#\n# luser_relay works only for the default Postfix local delivery agent.\n#\n# NOTE: if you use this feature for accounts not in the UNIX password\n# file, then you must specify \"local_recipient_maps =\" (i.e. empty) in\n# the main.cf file, otherwise the SMTP server will reject mail for\n# non-UNIX accounts with \"User unknown in local recipient table\".\n#\n#luser_relay = $user@other.host\n#luser_relay = $local@other.host\n#luser_relay = admin+$local\n\n# JUNK MAIL CONTROLS\n#\n# The controls listed here are only a very small subset. The file\n# SMTPD_ACCESS_README provides an overview.\n\n# The header_checks parameter specifies an optional table with patterns\n# that each logical message header is matched against, including\n# headers that span multiple physical lines.\n#\n# By default, these patterns also apply to MIME headers and to the\n# headers of attached messages. With older Postfix versions, MIME and\n# attached message headers were treated as body text.\n#\n# For details, see \"man header_checks\".\n#\n#header_checks = regexp:/etc/postfix/header_checks\n\n# FAST ETRN SERVICE\n#\n# Postfix maintains per-destination logfiles with information about\n# deferred mail, so that mail can be flushed quickly with the SMTP\n# \"ETRN domain.tld\" command, or by executing \"sendmail -qRdomain.tld\".\n# See the ETRN_README document for a detailed description.\n#\n# The fast_flush_domains parameter controls what destinations are\n# eligible for this service. By default, they are all domains that\n# this server is willing to relay mail to.\n#\n#fast_flush_domains = $relay_domains\n\n# SHOW SOFTWARE VERSION OR NOT\n#\n# The smtpd_banner parameter specifies the text that follows the 220\n# code in the SMTP server's greeting banner. Some people like to see\n# the mail version advertised. By default, Postfix shows no version.\n#\n# You MUST specify $myhostname at the start of the text. That is an\n# RFC requirement. Postfix itself does not care.\n#\n#smtpd_banner = $myhostname ESMTP $mail_name\n#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version)\nsmtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)\n\n\n# PARALLEL DELIVERY TO THE SAME DESTINATION\n#\n# How many parallel deliveries to the same user or domain? With local\n# delivery, it does not make sense to do massively parallel delivery\n# to the same user, because mailbox updates must happen sequentially,\n# and expensive pipelines in .forward files can cause disasters when\n# too many are run at the same time. With SMTP deliveries, 10\n# simultaneous connections to the same domain could be sufficient to\n# raise eyebrows.\n#\n# Each message delivery transport has its XXX_destination_concurrency_limit\n# parameter.  The default is $default_destination_concurrency_limit for\n# most delivery transports. For the local delivery agent the default is 2.\n\n#local_destination_concurrency_limit = 2\n#default_destination_concurrency_limit = 20\n\n# DEBUGGING CONTROL\n#\n# The debug_peer_level parameter specifies the increment in verbose\n# logging level when an SMTP client or server host name or address\n# matches a pattern in the debug_peer_list parameter.\n#\n#debug_peer_level = 2\n\n# The debug_peer_list parameter specifies an optional list of domain\n# or network patterns, /file/name patterns or type:name tables. When\n# an SMTP client or server host name or address matches a pattern,\n# increase the verbose logging level by the amount specified in the\n# debug_peer_level parameter.\n#\n#debug_peer_list = 127.0.0.1\n#debug_peer_list = some.domain\n\n# The debugger_command specifies the external command that is executed\n# when a Postfix daemon program is run with the -D option.\n#\n# Use \"command .. & sleep 5\" so that the debugger can attach before\n# the process marches on. If you use an X-based debugger, be sure to\n# set up your XAUTHORITY environment variable before starting Postfix.\n#\ndebugger_command =\n\t PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin\n\t ddd $daemon_directory/$process_name $process_id & sleep 5\n\n# If you can't use X, use this to capture the call stack when a\n# daemon crashes. The result is in a file in the configuration\n# directory, and is named after the process name and the process ID.\n#\n# debugger_command =\n#\tPATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont;\n#\techo where) | gdb $daemon_directory/$process_name $process_id 2>&1\n#\t>$config_directory/$process_name.$process_id.log & sleep 5\n#\n# Another possibility is to run gdb under a detached screen session.\n# To attach to the screen session, su root and run \"screen -r\n# <id_string>\" where <id_string> uniquely matches one of the detached\n# sessions (from \"screen -list\").\n#\n# debugger_command =\n#\tPATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen\n#\t-dmS $process_name gdb $daemon_directory/$process_name\n#\t$process_id & sleep 1\n\n# INSTALL-TIME CONFIGURATION INFORMATION\n#\n# The following parameters are used when installing a new Postfix version.\n#\n# sendmail_path: The full pathname of the Postfix sendmail command.\n# This is the Sendmail-compatible mail posting interface.\n#\nsendmail_path = /usr/sbin/sendmail\n\n# newaliases_path: The full pathname of the Postfix newaliases command.\n# This is the Sendmail-compatible command to build alias databases.\n#\nnewaliases_path = /usr/bin/newaliases\n\n# mailq_path: The full pathname of the Postfix mailq command.  This\n# is the Sendmail-compatible mail queue listing command.\n#\nmailq_path = /usr/bin/mailq\n\n# setgid_group: The group for mail submission and queue management\n# commands.  This must be a group name with a numerical group ID that\n# is not shared with other accounts, not even with the Postfix account.\n#\nsetgid_group = postdrop\n\n# html_directory: The location of the Postfix HTML documentation.\n#\nhtml_directory = no\n\n# manpage_directory: The location of the Postfix on-line manual pages.\n#\nmanpage_directory = /usr/share/man\n\n# sample_directory: The location of the Postfix sample configuration files.\n# This parameter is obsolete as of Postfix 2.1.\n#\nsample_directory = /usr/share/doc/postfix\n\n# readme_directory: The location of the Postfix README files.\n#\nreadme_directory = /usr/share/doc/postfix\ninet_protocols = ipv4\n\nappend_dot_mydomain = no\nbiff = no\nsmtpd_helo_required = yes\nsmtpd_recipient_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unauth_destination,\n\treject_unauth_pipelining,\n\treject_non_fqdn_recipient\nsmtpd_sender_restrictions = permit_mynetworks,\n\treject_sender_login_mismatch,\n\tpermit_sasl_authenticated,\n\treject_unknown_helo_hostname,\n\treject_unknown_recipient_domain,\n\treject_unknown_sender_domain\nsmtpd_client_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unknown_client_hostname\n\n# Postfix 2.10 requires this option. Postfix < 2.10 ignores this.\n# The option is intentionally left empty.\nsmtpd_relay_restrictions =\n\n# Maximum size of Message in bytes (50MB)\nmessage_size_limit = 52428800\n\n## SASL Auth Settings\nsmtpd_sasl_auth_enable = yes\nsmtpd_sasl_local_domain = $myhostname\nbroken_sasl_auth_clients = yes\n## Dovecot Settings for deliver, SASL Auth and virtual transport\nsmtpd_sasl_type = dovecot\nvirtual_transport = dovecot\ndovecot_destination_recipient_limit = 1\nsmtpd_sasl_path = private/auth\n\n# Virtual delivery settings\nvirtual_mailbox_base = /\nvirtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf\nvirtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf\nvirtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_alias_maps.cf\nsmtpd_sender_login_maps = proxy:mysql:/etc/postfix/mysql-virtual_sender_permissions.cf\nvirtual_uid_maps = static:<VIRTUAL_UID_MAPS>\nvirtual_gid_maps = static:<VIRTUAL_GID_MAPS>\n\n# Local delivery settings\nlocal_transport = local\nalias_maps = $alias_database\n\n# Default Mailbox size, is set to 0 which means unlimited!\nmailbox_size_limit = 0\nvirtual_mailbox_limit = 0\n\n### TLS settings\n###\n## TLS for outgoing mails from the server to another server\nsmtp_tls_security_level = may\nsmtp_tls_note_starttls_offer = yes\n## TLS for incoming connections (clients or other mail servers)\nsmtpd_tls_security_level = may\nsmtpd_tls_cert_file = <SSL_CERT_FILE>\nsmtpd_tls_key_file = <SSL_KEY_FILE>\n#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt\nsmtpd_tls_loglevel = 1\nsmtpd_tls_received_header = yes\nsmtp_use_tls = yes\nsmtpd_use_tls = yes\nsmtpd_tls_session_cache_timeout = 3600s\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/files[@index=0]</include>\n\t\t\t\t\t<file name=\"/etc/postfix/master.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Postfix master process configuration file.  For details on the format\n# of the file, see the master(5) manual page (command: \"man 5 master\" or\n# on-line: http://www.postfix.org/master.5.html).\n#\n# Do not forget to execute \"postfix reload\" after editing this file.\n#\n# ==========================================================================\n# service type  private unpriv  chroot  wakeup  maxproc command + args\n#               (yes)   (yes)   (yes)   (never) (100)\n# ==========================================================================\n#smtp      inet  n       -       y       -       -       smtpd\nsmtp      inet  n       -       y       -       1       postscreen\nsmtpd     pass  -       -       y       -       -       smtpd\ndnsblog   unix  -       -       y       -       0       dnsblog\ntlsproxy  unix  -       -       y       -       0       tlsproxy\nsubmission inet n       -       y       -       -       smtpd\n  -o syslog_name=postfix/submission\n  -o smtpd_tls_security_level=encrypt\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\nsmtps     inet  n       -       y       -       -       smtpd\n  -o syslog_name=postfix/smtps\n  -o smtpd_tls_wrappermode=yes\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\n#628       inet  n       -       y       -       -       qmqpd\npickup    unix  n       -       y       60      1       pickup\n         -o content_filter=\n         -o receive_override_options=no_header_body_checks\ncleanup   unix  n       -       y       -       0       cleanup\nqmgr      unix  n       -       n       300     1       qmgr\n#qmgr     unix  n       -       n       300     1       oqmgr\ntlsmgr    unix  -       -       y       1000?   1       tlsmgr\nrewrite   unix  -       -       y       -       -       trivial-rewrite\nbounce    unix  -       -       y       -       0       bounce\ndefer     unix  -       -       y       -       0       bounce\ntrace     unix  -       -       y       -       0       bounce\nverify    unix  -       -       y       -       1       verify\nflush     unix  n       -       y       1000?   0       flush\nproxymap  unix  -       -       n       -       -       proxymap\nproxywrite unix -       -       n       -       1       proxymap\nsmtp      unix  -       -       y       -       -       smtp\nrelay     unix  -       -       y       -       -       smtp\n#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5\nshowq     unix  n       -       y       -       -       showq\nerror     unix  -       -       y       -       -       error\nretry     unix  -       -       y       -       -       error\ndiscard   unix  -       -       y       -       -       discard\nlocal     unix  -       n       n       -       -       local\nvirtual   unix  -       n       n       -       -       virtual\nlmtp      unix  -       -       y       -       -       lmtp\nanvil     unix  -       -       y       -       1       anvil\nscache    unix  -       -       y       -       1       scache\n#\n# ====================================================================\n# Interfaces to non-Postfix software. Be sure to examine the manual\n# pages of the non-Postfix software to find out what options it wants.\n#\n# Many of the following services use the Postfix pipe(8) delivery\n# agent.  See the pipe(8) man page for information about ${recipient}\n# and other message envelope options.\n# ====================================================================\n#\n# maildrop. See the Postfix MAILDROP_README file for details.\n# Also specify in main.cf: maildrop_destination_recipient_limit=1\n#\nmaildrop  unix  -       n       n       -       -       pipe\n  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}\n#\n# ====================================================================\n#\n# Recent Cyrus versions can use the existing \"lmtp\" master.cf entry.\n#\n# Specify in cyrus.conf:\n#   lmtp    cmd=\"lmtpd -a\" listen=\"localhost:lmtp\" proto=tcp4\n#\n# Specify in main.cf one or more of the following:\n#  mailbox_transport = lmtp:inet:localhost\n#  virtual_transport = lmtp:inet:localhost\n#\n# ====================================================================\n#\n# Cyrus 2.1.5 (Amos Gouaux)\n# Also specify in main.cf: cyrus_destination_recipient_limit=1\n#\n#cyrus     unix  -       n       n       -       -       pipe\n#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}\n#\n# ====================================================================\n# Old example of delivery via Cyrus.\n#\n#old-cyrus unix  -       n       n       -       -       pipe\n#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}\n#\n# ====================================================================\n#\n# See the Postfix UUCP_README file for configuration details.\n#\nuucp      unix  -       n       n       -       -       pipe\n  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)\n#\n# Other external delivery methods.\n#\nifmail    unix  -       n       n       -       -       pipe\n  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)\nbsmtp     unix  -       n       n       -       -       pipe\n  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient\nscalemail-backend unix\t-\tn\tn\t-\t2\tpipe\n  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}\nmailman   unix  -       n       n       -       -       pipe\n  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py\n  ${nexthop} ${user}\n# Dovecot LDA\ndovecot\t  unix\t-\tn\tn\t-\t-\tpipe\n\tflags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ${recipient}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- IMAP/POP3 services -->\n\t\t\t<service type=\"mail\" title=\"{{lng.admin.configfiles.mail}}\">\n\t\t\t\t<!-- valid for both dovecots -->\n\t\t\t\t<general>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-managesieved dovecot-sieve]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/dovecot/dovecot-sql.conf.ext\"\n\t\t\t\t\t\t\tchown=\"root:root\" chmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\ndriver = mysql\nconnect = \"host=<SQL_HOST> dbname=<SQL_DB> user=<SQL_UNPRIVILEGED_USER> password=<SQL_UNPRIVILEGED_PASSWORD>\"\n#default_pass_scheme = CRYPT\nuser_query = SELECT CONCAT(homedir, maildir) AS home, CONCAT('maildir:', homedir, maildir) AS mail, uid, gid, CONCAT('*:storage=', quota, 'M') as quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u')\npassword_query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid,  CONCAT('maildir:', homedir, maildir) AS userdb_mail, CONCAT('*:storage=', quota, 'M') as userdb_quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') AND ((imap = 1 AND '%Ls' = 'imap') OR (pop3 = 1 AND '%Ls' = 'pop3') OR ((postfix = 'Y' AND '%Ls' = 'smtp') OR (postfix = 'Y' AND '%Ls' = 'sieve')))\niterate_query = \"SELECT username AS user FROM mail_users WHERE (imap = 1 OR pop3 = 1)\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/99-froxlor.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\ndisable_plaintext_auth = no\nauth_mechanisms = plain login\n!include auth-sql.conf.ext\nmail_location = mbox:~/mail:INBOX=/var/mail/%u\n\nnamespace inbox {\n  inbox = yes\n}\nmail_privileged_group = mail\n\nservice auth {\n  # Postfix smtp-auth\n  unix_listener /var/spool/postfix/private/auth {\n    mode = 0660\n    user = postfix\n    group = postfix\n  }\n  # Exim4 smtp-auth\n  unix_listener auth-client {\n    mode = 0660\n    user = mail\n    #group = Debian-exim\n  }\n}\n\nservice stats {\n  unix_listener stats-reader {\n    group = vmail\n    mode = 0666\n  }\n  unix_listener stats-writer {\n    group = vmail\n    mode = 0666\n  }\n}\n\nssl = yes\nssl_cert = <<SSL_CERT_FILE>\nssl_key = <<SSL_KEY_FILE>\nssl_dh = </usr/share/dovecot/dh.pem\n\npostmaster_address = postmaster@<SERVERNAME>\n\nprotocol imap {\n  mail_plugins = $mail_plugins quota imap_quota\n}\n\npop3_logout_format = in=%i out=%o top=%t/%p, retr=%r/%b, del=%d/%m, size=%s\n\nplugin {\n  sieve = file:~/sieve;active=~/.dovecot.sieve\n  sieve_dir = ~/sieve\n}\n\nplugin {\n  quota = maildir:User quota\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[sed -i.bak 's/^!include auth-system.conf.ext/#!include auth-system.conf.ext/' /etc/dovecot/conf.d/10-auth.conf]]></command>\n\t\t\t\t\t\t<command><![CDATA[service dovecot restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- Dovecot with postfix -->\n\t\t\t\t<daemon name=\"dovecot_postfix\" version=\"2\"\n\t\t\t\t\ttitle=\"Dovecot with postfix\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='mail']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- Antispam services -->\n\t\t\t<service type=\"antispam\" title=\"Antispam\">\n\t\t\t\t<!-- general RSpamd commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install wget gnupg]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/apt/keyrings]]></command>\n\t\t\t\t\t\t<command><![CDATA[wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | tee /etc/apt/keyrings/rspamd.gpg > /dev/null]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ bookworm main\" > /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb-src [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ bookworm main\" >> /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[apt-get update]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install rspamd]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/local.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/override.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/rspamd/dkim/]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/actions.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Set rewrite subject to this value (%s is replaced by the original subject)\nsubject = \"***SPAM*** %s\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/arc.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\ntry_fallback = true;\n### Enable DKIM signing for alias sender addresses\nallow_username_mismatch = true;\npath = \"/var/lib/rspamd/dkim/$domain.$selector.key\";\nselector_map = \"/etc/rspamd/dkim_selectors.map\";\nuse_esld = false;\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/milter_headers.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nuse = [\"x-spamd-bar\", \"x-spam-level\", \"authentication-results\"];\nauthenticated_headers = [\"authentication-results\"];\nextended_spam_headers = true\nskip_local = false\nskip_authenticated = false\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/replies.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n## If a user has replied to an email, don’t mark other emails in the same thread as spam\naction = \"no action\";\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/settings.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n## Feel free to include your own settings or adjustments here, for example:\n#whitelist {\n#  priority = low;\n#  rcpt = \"postmaster@example.com\";\n#  want_spam = yes;\n#}\n\n## Include froxlor generated settings\n.include(try=true,priority=1,duplicate=merge) \"{{settings.antispam.config_file}}\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[cp /etc/rspamd/local.d/arc.conf /etc/rspamd/local.d/dkim_signing.conf]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_protocol = 6\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_default_action = accept\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"non_smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R _rspamd:_rspamd /var/lib/rspamd/dkim]]></command>\n\t\t\t\t\t\t<command><![CDATA[service rspamd restart]]></command>\n\t\t\t\t\t\t<command><![CDATA[service postfix restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- rspamd -->\n\t\t\t\t<daemon name=\"rspamd\" title=\"Rspamd\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- FTP services -->\n\t\t\t<service type=\"ftp\" title=\"{{lng.admin.configfiles.ftp}}\">\n\t\t\t\t<!-- Proftpd -->\n\t\t\t\t<daemon name=\"proftpd\" title=\"ProFTPd\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install proftpd-basic proftpd-mod-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/proftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/certs/proftpd.crt ] || openssl req -new -x509 -newkey rsa:4096 -days 3650 -nodes -out /etc/ssl/certs/proftpd.crt -keyout /etc/ssl/private/proftpd.key -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\n[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nchmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/proftpd/proftpd.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# /etc/proftpd/proftpd.conf -- This is a basic ProFTPD configuration file.\n# To really apply changes, reload proftpd after modifications, if\n# it runs in daemon mode. It is not required in inetd/xinetd mode.\n#\n\n# Includes DSO modules\nInclude /etc/proftpd/modules.conf\n\n# Set off to disable IPv6 support which is annoying on IPv4 only boxes.\nUseIPv6\t\t\t\ton\n# If set on you can experience a longer connection delay in many cases.\n<IfModule mod_ident.c>\n\tIdentLookups\t\t\t\toff\n</IfModule>\n\nServerName                     \"<SERVERNAME> FTP Server\"\nServerType\t\t\tstandalone\nDeferWelcome\t\t\toff\n\nDefaultServer\t\t\ton\nShowSymlinks\t\t\ton\n\nTimeoutNoTransfer\t\t600\nTimeoutStalled\t\t\t600\nTimeoutIdle\t\t\t1200\n\nDisplayLogin                    welcome.msg\nDisplayChdir               \t.message true\nListOptions                \t\"-l\"\n\nDenyFilter\t\t\t\\*.*/\n\n# Use this to jail all users in their homes\n# DefaultRoot\t\t\t~\n\n# Users require a valid shell listed in /etc/shells to login.\n# Use this directive to release that constrain.\n# RequireValidShell\t\toff\n\n# Port 21 is the standard FTP port.\nPort\t\t\t\t21\n\n# In some cases you have to specify passive ports range to by-pass\n# firewall limitations. Ephemeral ports can be used for that, but\n# feel free to use a more narrow range.\n# PassivePorts                  49152 65534\n\n# If your host was NATted, this option is useful in order to\n# allow passive transfers to work. You have to use your public\n# address and opening the passive ports used on your firewall as well.\n# MasqueradeAddress\t\t1.2.3.4\n\n# This is useful for masquerading address with dynamic IPs:\n# refresh any configured MasqueradeAddress directives every 8 hours\n<IfModule mod_dynmasq.c>\n# DynMasqRefresh 28800\n</IfModule>\n\n# To prevent DoS attacks, set the maximum number of child processes\n# to 30.  If you need to allow more than 30 concurrent connections\n# at once, simply increase this value.  Note that this ONLY works\n# in standalone mode, in inetd mode you should use an inetd server\n# that allows you to limit maximum number of processes per service\n# (such as xinetd)\nMaxInstances\t\t\t30\n\n# Set the user and group that the server normally runs at.\nUser\t\t\t\tproftpd\nGroup\t\t\t\tnogroup\n\n# Umask 022 is a good standard umask to prevent new files and dirs\n# (second parm) from being group and world writable.\nUmask\t\t\t\t022  022\n# Normally, we want files to be overwritable.\nAllowOverwrite\t\t\ton\n\n# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords:\n# PersistentPasswd\t\toff\n\n# This is required to use both PAM-based authentication and local passwords\n# AuthOrder\t\t\tmod_auth_pam.c* mod_auth_unix.c\n\n# Be warned: use of this directive impacts CPU average load!\n# Uncomment this if you like to see progress and transfer rate with ftpwho\n# in downloads. That is not needed for uploads rates.\n#\n# UseSendFile\t\t\toff\n\nTransferLog /var/log/proftpd/xferlog\nSystemLog   /var/log/proftpd/proftpd.log\n\n# Logging onto /var/log/lastlog is enabled but set to off by default\n#UseLastlog on\n\n# In order to keep log file dates consistent after chroot, use timezone info\n# from /etc/localtime.  If this is not set, and proftpd is configured to\n# chroot (e.g. DefaultRoot or <Anonymous>), it will use the non-daylight\n# savings timezone regardless of whether DST is in effect.\n#SetEnv TZ :/etc/localtime\n\n<IfModule mod_quotatab.c>\nQuotaEngine on\n</IfModule>\n\n<IfModule mod_ratio.c>\nRatios off\n</IfModule>\n\n\n# Delay engine reduces impact of the so-called Timing Attack described in\n# http://www.securityfocus.com/bid/11430/discuss\n# It is on by default.\n<IfModule mod_delay.c>\nDelayEngine on\n</IfModule>\n\n<IfModule mod_ctrls.c>\nControlsEngine        off\nControlsMaxClients    2\nControlsLog           /var/log/proftpd/controls.log\nControlsInterval      5\nControlsSocket        /var/run/proftpd/proftpd.sock\n</IfModule>\n\n<IfModule mod_ctrls_admin.c>\nAdminControlsEngine off\n</IfModule>\n\n#\n# Alternative authentication frameworks\n#\n#Include /etc/proftpd/ldap.conf\nInclude /etc/proftpd/sql.conf\n\n#\n# This is used for FTPS connections\n#\nInclude /etc/proftpd/tls.conf\n\n#\n# Useful to keep VirtualHost/VirtualRoot directives separated\n#\n#Include /etc/proftpd/virtuals.conf\n\n# A basic anonymous configuration, no upload directories.\n\n# <Anonymous ~ftp>\n#   User\t\t\t\tftp\n#   Group\t\t\t\tnogroup\n#   # We want clients to be able to login with \"anonymous\" as well as \"ftp\"\n#   UserAlias\t\t\tanonymous ftp\n#   # Cosmetic changes, all files belongs to ftp user\n#   DirFakeUser\ton ftp\n#   DirFakeGroup on ftp\n#\n#   RequireValidShell\t\toff\n#\n#   # Limit the maximum number of anonymous logins\n#   MaxClients\t\t\t10\n#\n#   # We want 'welcome.msg' displayed at login, and '.message' displayed\n#   # in each newly chdired directory.\n#   DisplayLogin\t\t\twelcome.msg\n#   DisplayChdir\t\t.message\n#\n#   # Limit WRITE everywhere in the anonymous chroot\n#   <Directory *>\n#     <Limit WRITE>\n#       DenyAll\n#     </Limit>\n#   </Directory>\n#\n#   # Uncomment this if you're brave.\n#   # <Directory incoming>\n#   #   # Umask 022 is a good standard umask to prevent new files and dirs\n#   #   # (second parm) from being group and world writable.\n#   #   Umask\t\t\t\t022  022\n#   #            <Limit READ WRITE>\n#   #            DenyAll\n#   #            </Limit>\n#   #            <Limit STOR>\n#   #            AllowAll\n#   #            </Limit>\n#   # </Directory>\n#\n# </Anonymous>\n\n# Include other custom configuration files\nInclude /etc/proftpd/conf.d/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/modules.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# This file is used to manage DSO modules and features.\n#\n\n# This is the directory where DSO modules reside\n\nModulePath /usr/lib/proftpd\n\n# Allow only user root to load and unload modules, but allow everyone\n# to see which modules have been loaded\n\nModuleControlsACLs insmod,rmmod allow user root\nModuleControlsACLs lsmod allow user *\n\nLoadModule mod_ctrls_admin.c\nLoadModule mod_tls.c\n\nLoadModule mod_ident.c\n\n# Install one of proftpd-mod-mysql, proftpd-mod-pgsql or any other\n# SQL backend engine to use this module and the required backend.\n# This module must be mandatory loaded before anyone of\n# the existent SQL backeds.\nLoadModule mod_sql.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_ldap.c\n\n#\n# 'SQLBackend mysql' or 'SQLBackend postgres' (or any other valid backend) directives\n# are required to have SQL authorization working. You can also comment out the\n# unused module here, in alternative.\n#\n\n# Install proftpd-mod-mysql and decomment the previous\n# mod_sql.c module to use this.\nLoadModule mod_sql_mysql.c\n\n# Install proftpd-mod-pgsql and decomment the previous\n# mod_sql.c module to use this.\n#LoadModule mod_sql_postgres.c\n\n# Install proftpd-mod-sqlite and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_sqlite.c\n\n# Install proftpd-mod-odbc and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_odbc.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sql_passwd.c\n\nLoadModule mod_radius.c\nLoadModule mod_quotatab.c\nLoadModule mod_quotatab_file.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_quotatab_ldap.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\nLoadModule mod_quotatab_sql.c\nLoadModule mod_quotatab_radius.c\nLoadModule mod_wrap.c\nLoadModule mod_rewrite.c\nLoadModule mod_load.c\nLoadModule mod_ban.c\nLoadModule mod_wrap2.c\nLoadModule mod_wrap2_file.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_wrap2_sql.c\nLoadModule mod_dynmasq.c\nLoadModule mod_exec.c\nLoadModule mod_shaper.c\nLoadModule mod_ratio.c\nLoadModule mod_site_misc.c\n\nLoadModule mod_sftp.c\nLoadModule mod_sftp_pam.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sftp_sql.c\n\nLoadModule mod_facl.c\nLoadModule mod_unique_id.c\nLoadModule mod_copy.c\nLoadModule mod_deflate.c\nLoadModule mod_ifversion.c\nLoadModule mod_tls_memcache.c\n\n# Install proftpd-mod-geoip to use the GeoIP feature\n#LoadModule mod_geoip.c\n\n# keep this module the last one\nLoadModule mod_ifsession.c\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/sql.conf\" chown=\"root:0\" chmod=\"0600\"\n\t\t\t\t\t\tbackup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Proftpd sample configuration for SQL-based authentication.\n#\n# (This is not to be used if you prefer a PAM-based SQL authentication)\n#\n\n<IfModule mod_sql.c>\n\nDefaultRoot ~\nRequireValidShell off\nAuthOrder mod_sql.c\n\n#\n# Choose a SQL backend among MySQL or PostgreSQL.\n# Both modules are loaded in default configuration, so you have to specify the backend\n# or comment out the unused module in /etc/proftpd/modules.conf.\n# Use 'mysql' or 'postgres' as possible values.\n#\nSQLBackend\tmysql\n#\nSQLEngine on\nSQLAuthenticate on\n#\n# Use both an encrypted or plaintext password\nSQLAuthTypes Crypt OpenSSL\n\nSQLAuthenticate users* groups*\n\n#\n# Connection\nSQLConnectInfo <SQL_DB>@<SQL_HOST> <SQL_UNPRIVILEGED_USER> <SQL_UNPRIVILEGED_PASSWORD>\n#\n# Describes both users/groups tables\n#\nSQLUserInfo ftp_users username password uid gid homedir shell\nSQLGroupInfo ftp_groups groupname gid members\n#\nSQLUserWhereClause \"login_enabled = 'y'\"\n\nSQLLog PASS login\nSQLNamedQuery login UPDATE \"last_login=now(), login_count=login_count+1 WHERE username='%u'\" ftp_users\n\nSQLLog RETR download\nSQLNamedQuery download UPDATE \"down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'\" ftp_users\n\nSQLLog STOR upload\nSQLNamedQuery upload UPDATE \"up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'\" ftp_users\n\nQuotaEngine on\nQuotaShowQuotas on\nQuotaDisplayUnits Mb\nQuotaLock /var/lock/ftpd.quotatab.lock\nQuotaLimitTable sql:/get-quota-limit\nQuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally\nSQLNamedQuery get-quota-limit SELECT \"ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'\"\nSQLNamedQuery get-quota-tally SELECT \"name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'\"\nSQLNamedQuery update-quota-tally UPDATE \"bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'\" ftp_quotatallies\nSQLNamedQuery insert-quota-tally INSERT \"%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}\" ftp_quotatallies\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/tls.conf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n<IfModule mod_tls.c>\nTLSEngine                               on\nTLSLog                                  /var/log/proftpd/tls.log\nTLSProtocol                             TLSv1.2 TLSv1.3\nTLSRSACertificateFile                   /etc/ssl/certs/proftpd.crt\nTLSRSACertificateKeyFile                /etc/ssl/private/proftpd.key\nTLSECCertificateFile                    /etc/ssl/certs/proftpd_ec.crt\nTLSECCertificateKeyFile                 /etc/ssl/private/proftpd_ec.key\n# TLSCACertificateFile\nTLSOptions                              NoSessionReuseRequired\nTLSVerifyClient                         off\n\n# Are clients required to use FTP over TLS when talking to this server?\nTLSRequired                             on\n\n# Allow SSL/TLS renegotiations when the client requests them, but\n# do not force the renegotiations.  Some clients do not support\n# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these\n# clients will close the data connection, or there will be a timeout\n# on an idle data connection.\n#\n#TLSRenegotiate                          required off\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/conf.d/99-froxlor-ratelimit.conf\" chown=\"root:0\"\n\t\t\t\t\t\t  chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n<Class whitelist>\nFrom 127.0.0.1\n</Class>\n\nMaxLoginAttempts 3\n<IfModule mod_ban.c>\n <IfClass whitelist>\n  BanEngine off\n </IfClass>\n <IfClass !whitelist>\n  BanEngine on\n </IfClass>\nBanLog /var/log/proftpd/ban.log\nBanTable /etc/proftpd/ban.tab\nBanMessage \"User %u was banned.\"\nBanOnEvent ClientConnectRate 10/00:00:02 02:00:00 \"Stop connecting frequently\"\nBanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00\nBanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99\nBanControlsACLs all allow user root\n</IfModule>\n\n<IfClass whitelist>\nBanEngine off\nDelayEngine off\n</IfClass>\n\t\t\t\t\t]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service proftpd restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Pureftpd -->\n\t\t\t\t<daemon name=\"pureftpd\" title=\"PureFTPd\">\n\t\t\t\t\t<install><![CDATA[apt-get install pure-ftpd-common pure-ftpd-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/private/pure-ftpd.pem ] || openssl req -x509 -nodes -days 7300 -newkey rsa:4096 -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nopenssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072\nchmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/TLS\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MinUID\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1000\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MySQLConfigFile\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n/etc/pure-ftpd/db/mysql.conf\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/NoAnonymous\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MaxIdleTime\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n15\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/ChrootEveryone\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/PAMAuthentication\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nno\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/db/mysql.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0640\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n##############################################\n#                                            #\n# Sample Pure-FTPd Mysql configuration file. #\n# See README.MySQL for explanations.         #\n#                                            #\n##############################################\n\n\n# Optional : MySQL server name or IP. Don't define this for unix sockets.\n\n# MYSQLServer     127.0.0.1\n\n\n# Optional : MySQL port. Don't define this if a local unix socket is used.\n\n# MYSQLPort       3306\n\n\n# Optional : define the location of mysql.sock if the server runs on this host.\n\nMYSQLSocket      /var/run/mysqld/mysqld.sock\n\n\n# Mandatory : user to bind the server as.\n\nMYSQLUser       <SQL_UNPRIVILEGED_USER>\n\n\n# Mandatory : user password. You must have a password.\n\nMYSQLPassword   <SQL_UNPRIVILEGED_PASSWORD>\n\n\n# Mandatory : database to open.\n\nMYSQLDatabase   <SQL_DB>\n\n\n# Mandatory : how passwords are stored\n# Valid values are : \"cleartext\", \"crypt\", \"sha1\", \"md5\" and \"password\"\n# (\"password\" = MySQL password() function)\n# You can also use \"any\" to try \"crypt\", \"sha1\", \"md5\" *and* \"password\"\n\nMYSQLCrypt      any\n\n\n# In the following directives, parts of the strings are replaced at\n# run-time before performing queries :\n#\n# \\L is replaced by the login of the user trying to authenticate.\n# \\I is replaced by the IP address the user connected to.\n# \\P is replaced by the port number the user connected to.\n# \\R is replaced by the IP address the user connected from.\n# \\D is replaced by the remote IP address, as a long decimal number.\n#\n# Very complex queries can be performed using these substitution strings,\n# especially for virtual hosting.\n\n\n# Query to execute in order to fetch the password\n\nMYSQLGetPW      SELECT password FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Query to execute in order to fetch the system user name or uid\n\nMYSQLGetUID     SELECT uid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default UID - if set this overrides MYSQLGetUID\n\n#MYSQLDefaultUID 1000\n\n\n# Query to execute in order to fetch the system user group or gid\n\nMYSQLGetGID     SELECT gid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default GID - if set this overrides MYSQLGetGID\n\n#MYSQLDefaultGID 1000\n\n\n# Query to execute in order to fetch the home directory\n\nMYSQLGetDir     SELECT homedir FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : query to get the maximal number of files\n# Pure-FTPd must have been compiled with virtual quotas support.\n\n# MySQLGetQTAFS  SELECT QuotaFiles FROM users WHERE User='\\L'\n\n\n# Optional : query to get the maximal disk usage (virtual quotas)\n# The number should be in Megabytes.\n# Pure-FTPd must have been compiled with virtual quotas support.\n\nMySQLGetQTASZ SELECT CASE WHEN panel_customers.diskspace = 0 THEN -1 WHEN panel_customers.diskspace <= -1 THEN 0 ELSE panel_customers.diskspace/1024 END AS QuotaSize FROM panel_customers, ftp_users WHERE username = \"\\L\" AND panel_customers.loginname = SUBSTRING_INDEX('\\L', 'ftp', 1)\n\n\n# Optional : ratios. The server has to be compiled with ratio support.\n\n# MySQLGetRatioUL SELECT ULRatio FROM users WHERE User='\\L'\n# MySQLGetRatioDL SELECT DLRatio FROM users WHERE User='\\L'\n\n\n# Optional : bandwidth throttling.\n# The server has to be compiled with throttling support.\n# Values are in KB/s .\n\n# MySQLGetBandwidthUL SELECT ULBandwidth FROM users WHERE User='\\L'\n# MySQLGetBandwidthDL SELECT DLBandwidth FROM users WHERE User='\\L'\n\n# Enable ~ expansion. NEVER ENABLE THIS BLINDLY UNLESS :\n# 1) You know what you are doing.\n# 2) Real and virtual users match.\n\n# MySQLForceTildeExpansion 1\n\n\n# If you're using a transactionnal storage engine, you can enable SQL\n# transactions to avoid races. Leave this commented if you are using the\n# traditional MyIsam engine.\n\n# MySQLTransactions On\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/CustomerProof\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/Bind\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n21\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/default/pure-ftpd-common\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Configuration for pure-ftpd\n# (this file is sourced by /bin/sh, edit accordingly)\n\n# STANDALONE_OR_INETD\n# valid values are \"standalone\" and \"inetd\".\n# Any change here overrides the setting in debconf.\nSTANDALONE_OR_INETD=standalone\n\n# VIRTUALCHROOT:\n# whether to use binary with virtualchroot support\n# valid values are \"true\" or \"false\"\n# Any change here overrides the setting in debconf.\nVIRTUALCHROOT=false\n\n# UPLOADSCRIPT: if this is set and the daemon is run in standalone mode,\n# pure-uploadscript will also be run to spawn the program given below\n# for handling uploads. see /usr/share/doc/pure-ftpd/README.gz or\n# pure-uploadscript(8)\n\n# example: UPLOADSCRIPT=/usr/local/sbin/uploadhandler.pl\nUPLOADSCRIPT=\n\n# if set, pure-uploadscript will spawn  running as the\n# given uid and gid\nUPLOADUID=\nUPLOADGID=\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pure-ftpd-mysql restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- System tools/services -->\n\t\t\t<service type=\"system\" title=\"{{lng.admin.configfiles.etc}}\">\n\t\t\t\t<!-- Webalizer -->\n\t\t\t\t<daemon name=\"webalizer\"\n\t\t\t\t\ttitle=\"Webalizer (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install webalizer]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- goaccess -->\n\t\t\t\t<daemon name=\"goaccess\"\n\t\t\t\t\ttitle=\"goaccess (traffic analyzer)\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install goaccess jq]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- AWstats -->\n\t\t\t\t<daemon name=\"awstats\"\n\t\t\t\t\ttitle=\"Awstats  (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install awstats]]></install>\n\t\t\t\t\t<command><![CDATA[mv {{settings.system.awstats_conf}}/awstats.conf {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's/^DirData/# DirData/' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's|^\\\\(DirIcons=\\\\).*$|\\\\1\\\\\"/awstats-icon\\\\\"|' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/cron.d/awstats]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/logrotate.d/httpd-prerotate/awstats]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- libnss-extrausers -->\n\t\t\t\t<daemon name=\"libnssextrausers\"\n\t\t\t\t\ttitle=\"libnss-extrausers\">\n\t\t\t\t\t<install><![CDATA[apt-get install libnss-extrausers]]></install>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/extrausers]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/passwd]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/group]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/shadow]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/nsswitch.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Make sure that `passwd`, `group` and `shadow` have extrausers in their lines\n# You should place extrausers at the end, so that it is queried after the other mechanisams\n#\npasswd:         compat extrausers\ngroup:          compat extrausers\nshadow:         compat extrausers\n\nhosts:       files dns\nnetworks:    files dns\n\nservices:    db files\nprotocols:   db files\nrpc:         db files\nethers:      db files\nnetmasks:    files\nnetgroup:    files\nbootparams:  files\n\nautomount:   files\naliases:     files\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Logrotate -->\n\t\t\t\t<daemon name=\"logrotate\" title=\"Logrotate\">\n\t\t\t\t\t<install><![CDATA[apt-get install logrotate]]></install>\n\t\t\t\t\t<file name=\"/etc/logrotate.d/froxlor\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Froxlor logrotate snippet\n#\n<CUSTOMER_LOGS>*.log {\n  missingok\n  daily\n  rotate 7\n  compress\n  delaycompress\n  notifempty\n  create\n  sharedscripts\n  postrotate\n  <WEBSERVER_RELOAD_CMD> > /dev/null 2>&1 || true\n  endscript\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- FCGID -->\n\t\t\t\t<daemon name=\"fcgid\" title=\"FCGID\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2-suexec-pristine libapache2-mod-fcgid php-cgi]]></install>\n\t\t\t\t\t<command><![CDATA[a2enmod suexec fcgid]]></command>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.mod_fcgid_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.system.mod_fcgid_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.system.mod_fcgid_httpgroup}} {{settings.system.mod_fcgid_httpuser}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_configdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 1777 {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php8.2]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- PHP-FPM -->\n\t\t\t\t<daemon name=\"php-fpm\"\n\t\t\t\t\ttitle=\"PHP-FPM\">\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install apache2-suexec-pristine]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<install><![CDATA[apt-get install php-fpm]]></install>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2enmod suexec proxy_fcgi actions]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"usernamenotexists\">{{settings.phpfpm.vhost_httpuser}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.phpfpm.vhost_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.phpfpm.vhost_httpgroup}} {{settings.phpfpm.vhost_httpuser}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"4\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php8.2]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"5\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2disconf php8.2-fpm]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Cronjob -->\n\t\t\t\t<daemon name=\"cron\" title=\"Cronjob for froxlor\"\n\t\t\t\t\tmandatory=\"true\">\n\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install cron gnupg]]></install>\n\t\t\t\t\t<command><![CDATA[[ ! -e /usr/local/bin/froxlor-cli ] && ln -s <BASE_PATH>bin/froxlor-cli /usr/local/bin/froxlor-cli]]></command>\n\t\t\t\t\t<command><![CDATA[/usr/bin/php <BASE_PATH>bin/froxlor-cli froxlor:cron --run-task 99]]></command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.crondreload}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t</services>\n\t</distribution>\n</froxlor>\n"
  },
  {
    "path": "lib/configfiles/bullseye.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<froxlor>\n\t<distribution name=\"Debian\" codename=\"Bullseye\"\n\t\tversion=\"11.x\" defaulteditor=\"/bin/nano\">\n\t\t<!-- OS defaults to be loaded on installation -->\n\t\t<defaults>\n\t\t\t<default settinggroup=\"system\" varname=\"nssextrausers\" value=\"1\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_vhost\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_diroptions\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_htpasswddir\" value=\"/etc/nginx/froxlor-htpasswd/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apachereload_command\" value=\"service nginx reload\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"letsencryptacmeconf\" value=\"/etc/nginx/acme.conf\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"phpfpm\" varname=\"fastcgi_ipcdir\" value=\"/var/run/php/\"></default>\n\t\t</defaults>\n\t\t<services>\n\t\t\t<!-- HTTP -->\n\t\t\t<service type=\"http\" title=\"{{lng.admin.configfiles.http}}\">\n\t\t\t\t<!-- general HTTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.documentroot_prefix}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.logfiles_directory}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"notempty\">{{settings.system.deactivateddocroot}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.deactivateddocroot}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- HTTP Apache -->\n\t\t\t\t<daemon name=\"apache\" version=\"2.4\" title=\"Apache 2.4\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2]]></install>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command><![CDATA[a2dismod userdir]]></command>\n\t\t\t\t\t<command><![CDATA[a2enmod headers]]></command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.use_ssl}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[a2enmod ssl]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n# Please remember to activate the use of mod_proxy / mod_proxy_fcgi in the PHP-FPM settings!!!\na2enmod proxy_fcgi\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nAlias \"/.well-known/acme-challenge\" \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\"\n<Directory \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\">\n\tRequire all granted\n</Directory>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- HTTP Nginx -->\n\t\t\t\t<daemon name=\"nginx\" title=\"nginx\">\n\t\t\t\t\t<install><![CDATA[apt-get install nginx]]></install>\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install php-cgi]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<file name=\"/etc/nginx/nginx.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nuser www-data;\nworker_processes auto;\npid /var/run/nginx.pid;\ninclude /etc/nginx/modules-enabled/*.conf;\n\nevents {\n\tworker_connections 768;\n\t# multi_accept on;\n}\n\nhttp {\n\n\t##\n\t# Basic Settings\n\t##\n\n\tsendfile on;\n\ttcp_nopush on;\n\ttcp_nodelay on;\n\tkeepalive_timeout 65;\n\ttypes_hash_max_size 2048;\n\t# server_tokens off;\n\n\t# server_names_hash_bucket_size 64;\n\t# server_name_in_redirect off;\n\n\tinclude /etc/nginx/mime.types;\n\tdefault_type application/octet-stream;\n\n\t##\n\t# Logging Settings\n\t##\n\n\taccess_log /var/log/nginx/access.log;\n\terror_log /var/log/nginx/error.log;\n\n\t##\n\t# Gzip Settings\n\t##\n\n\tgzip on;\n\tgzip_disable \"msie6\";\n\n\t# gzip_vary on;\n\t# gzip_proxied any;\n\t# gzip_comp_level 6;\n\t# gzip_buffers 16 8k;\n\t# gzip_http_version 1.1;\n\t# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;\n\n\t##\n\t# nginx-naxsi config\n\t##\n\t# Uncomment it if you installed nginx-naxsi\n\t##\n\n\t#include /etc/nginx/naxsi_core.rules;\n\n\t##\n\t# nginx-passenger config\n\t##\n\t# Uncomment it if you installed nginx-passenger\n\t##\n\n\t#passenger_root /usr;\n\t#passenger_ruby /usr/bin/ruby;\n\n\t##\n\t# Virtual Host Configs\n\t##\n\n\tinclude /etc/nginx/conf.d/*.conf;\n\tinclude /etc/nginx/sites-enabled/*;\n}\n\n\n#mail {\n#\t# See sample authentication script at:\n#\t# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript\n#\n#\t# auth_http localhost/auth.php;\n#\t# pop3_capabilities \"TOP\" \"USER\";\n#\t# imap_capabilities \"IMAP4rev1\" \"UIDPLUS\";\n#\n#\tserver {\n#\t\tlisten     localhost:110;\n#\t\tprotocol   pop3;\n#\t\tproxy      on;\n#\t}\n#\n#\tserver {\n#\t\tlisten     localhost:143;\n#\t\tprotocol   imap;\n#\t\tproxy      on;\n#\t}\n#}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/nginx/fastcgi_params\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nfastcgi_connect_timeout 65;\nfastcgi_send_timeout    180;\nfastcgi_read_timeout    180;\n\nfastcgi_param  QUERY_STRING       $query_string;\nfastcgi_param  REQUEST_METHOD     $request_method;\nfastcgi_param  CONTENT_TYPE       $content_type;\nfastcgi_param  CONTENT_LENGTH     $content_length;\n\nfastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\nfastcgi_param  REQUEST_URI        $request_uri;\nfastcgi_param  DOCUMENT_URI       $document_uri;\nfastcgi_param  DOCUMENT_ROOT      $document_root;\nfastcgi_param  SERVER_PROTOCOL    $server_protocol;\nfastcgi_param  HTTPS              $https if_not_empty;\n\nfastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\nfastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\nfastcgi_param  REMOTE_ADDR        $remote_addr;\nfastcgi_param  REMOTE_PORT        $remote_port;\nfastcgi_param  SERVER_ADDR        $server_addr;\nfastcgi_param  SERVER_PORT        $server_port;\nfastcgi_param  SERVER_NAME        $server_name;\n\n# PHP only, required if PHP was built with --enable-force-cgi-redirect\nfastcgi_param  REDIRECT_STATUS    200;\n\n# Fix for HTTP/3 ($http_host is not populated automatically)\nfastcgi_param  HTTP_HOST          $host;\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nlocation /.well-known/acme-challenge {\n\talias {{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge;\n\n\tlocation ~ /.well-known/acme-challenge/(.*) {\n\t\tdefault_type text/plain;\n\t}\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/init.d/php-fcgi\" backup=\"true\" chmod=\"u+x\">\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n#!/bin/bash\nBIND=\"127.0.0.1:8888\"\nUSER=\"www-data\"\nPHP_FCGI_CHILDREN=\"15\"\nPHP_FCGI_MAX_REQUESTS=\"1000\"\n\nPHP_CGI=\"/usr/bin/php-cgi\"\nPHP_CGI_NAME=\"$(basename ${PHP_CGI})\"\nPHP_CGI_ARGS=\"- USER=${USER} PATH=/usr/bin PHP_FCGI_CHILDREN=${PHP_FCGI_CHILDREN} PHP_FCGI_MAX_REQUESTS=${PHP_FCGI_MAX_REQUESTS} ${PHP_CGI} -b ${BIND}\"\nRETVAL=\"0\"\n\nstart() {\n      echo -n \"Starting PHP FastCGI: \"\n      start-stop-daemon --quiet --start --background --chuid \"$USER\" --exec /usr/bin/env -- $PHP_CGI_ARGS\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\nstop() {\n      echo -n \"Stopping PHP FastCGI: \"\n      killall -q -w -u ${USER} ${PHP_CGI}\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\n\ncase \"$1\" in\n    start)\n      start\n  ;;\n    stop)\n      stop\n  ;;\n    restart)\n      stop\n      start\n  ;;\n    *)\n      echo \"Usage: php-fastcgi {start|stop|restart}\"\n      exit 1\n  ;;\nesac\nexit \"$RETVAL\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[/etc/init.d/php-fcgi restart]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!--DNS -->\n\t\t\t<service type=\"dns\" title=\"{{lng.admin.configfiles.dns}}\">\n\t\t\t\t<!--Bind9 -->\n\t\t\t\t<daemon name=\"bind\" title=\"Bind9 nameserver\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install bind9]]></install>\n\t\t\t\t\t<command><![CDATA[echo \"include \\\"{{settings.system.bindconf_directory}}froxlor_bind.conf\\\";\" >> /etc/bind/named.conf.local]]></command>\n\t\t\t\t\t<command><![CDATA[touch {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chown bind:0 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chmod 0644 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[service bind9 restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns\" title=\"PowerDNS (standalone)\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server pdns-backend-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\nallow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# mysql-settings / you need to create the power-dns database for yourself!\nlaunch=gmysql\ngmysql-host=127.0.0.1\ngmysql-port=3306\ngmysql-dbname=pdns\ngmysql-user=powerdns\ngmysql-group=client\ngmysql-password=\n#gmysql-ssl-ca-file=\n#gmysql-ssl-verify-server-certificate=0\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns_bind\"\n\t\t\t\t\ttitle=\"PowerDNS via bind-backend\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\n# allow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\nlaunch=bind\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# Bind backend configuration\n\n# Location of the Bind configuration file to parse.\nbind-config=<BIND_CONFIG_PATH>named.conf\n\n# How often to check for zone changes. See 'Operation' section.\nbind-check-interval=180\n\n# Uncomment to enable Huffman compression on zone data.\n# Currently saves around 20% of memory actually used, but slows down operation.\n# bind-enable-huffman\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- SMTP services -->\n\t\t\t<service type=\"smtp\" title=\"{{lng.admin.configfiles.smtp}}\">\n\t\t\t\t<!-- general SMTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"groupnotexists\">{{settings.system.vmail_gid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[groupadd -g {{settings.system.vmail_gid}} vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"usernotexists\">{{settings.system.vmail_uid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[useradd -u {{settings.system.vmail_uid}} -g vmail vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install postfix postfix-mysql]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/etc/pam.d]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/var/run/mysqld]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R {{settings.system.vmail_uid}}:{{settings.system.vmail_gid}} {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0750  {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"0\">\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_alias_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT destination FROM mail_virtual AS v, panel_customers AS c WHERE c.customerid = v.customerid AND c.deactivated = 0 AND v.email = '%s' AND trim(v.destination) <> ''\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_domains.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' AND deactivated = 0\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_sender_permissions.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT GROUP_CONCAT(DISTINCT mu.username SEPARATOR ' ') AS sasl_users FROM mail_users mu WHERE mu.username = '%s' OR mu.email IN ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = CONCAT('@', SUBSTRING_INDEX('%s','@',-1))));\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_uid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT uid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_gid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT gid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/aliases\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# /etc/aliases\nmailer-daemon: postmaster\npostmaster: root\nnobody: root\nhostmaster: root\nusenet: root\nnews: root\nwebmaster: root\nwww: root\nftp: root\nabuse: root\nnoc: root\nsecurity: root\n\n# change this to a valid e-mail address you can access\nroot:               <ADMIN_MAIL>\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[newaliases]]></command>\n\t\t\t\t\t\t<command><![CDATA[service postfix restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- postfix with dovecot -->\n\t\t\t\t<daemon name=\"postfix_dovecot\" title=\"Postfix with dovecot\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<file name=\"/etc/postfix/main.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Global Postfix configuration file. This file lists only a subset\n# of all parameters. For the syntax, and for a complete parameter\n# list, see the postconf(5) manual page (command: \"man 5 postconf\").\n#\n# For common configuration examples, see BASIC_CONFIGURATION_README\n# and STANDARD_CONFIGURATION_README. To find these documents, use\n# the command \"postconf html_directory readme_directory\", or go to\n# http://www.postfix.org/.\n#\n# For best results, change no more than 2-3 parameters at a time,\n# and test if Postfix still works after every change.\n\n# SOFT BOUNCE\n#\n# The soft_bounce parameter provides a limited safety net for\n# testing.  When soft_bounce is enabled, mail will remain queued that\n# would otherwise bounce. This parameter disables locally-generated\n# bounces, and prevents the SMTP server from rejecting mail permanently\n# (by changing 5xx replies into 4xx replies). However, soft_bounce\n# is no cure for address rewriting mistakes or mail routing mistakes.\n#\n#soft_bounce = no\n\n# LOCAL PATHNAME INFORMATION\n#\n# The queue_directory specifies the location of the Postfix queue.\n# This is also the root directory of Postfix daemons that run chrooted.\n# See the files in examples/chroot-setup for setting up Postfix chroot\n# environments on different UNIX systems.\n#\n#queue_directory = /var/spool/postfix\n\n# The command_directory parameter specifies the location of all\n# postXXX commands.\n#\ncommand_directory = /usr/sbin\n\n# The daemon_directory parameter specifies the location of all Postfix\n# daemon programs (i.e. programs listed in the master.cf file). This\n# directory must be owned by root.\n#\ndaemon_directory = /usr/lib/postfix/sbin\n\n# The data_directory parameter specifies the location of Postfix-writable\n# data files (caches, random numbers). This directory must be owned\n# by the mail_owner account (see below).\n#\ndata_directory = /var/lib/postfix\n\n# QUEUE AND PROCESS OWNERSHIP\n#\n# The mail_owner parameter specifies the owner of the Postfix queue\n# and of most Postfix daemon processes.  Specify the name of a user\n# account THAT DOES NOT SHARE ITS USER OR GROUP ID WITH OTHER ACCOUNTS\n# AND THAT OWNS NO OTHER FILES OR PROCESSES ON THE SYSTEM.  In\n# particular, don't specify nobody or daemon. PLEASE USE A DEDICATED\n# USER.\n#\n#mail_owner = postfix\n\n# The default_privs parameter specifies the default rights used by\n# the local delivery agent for delivery to external file or command.\n# These rights are used in the absence of a recipient user context.\n# DO NOT SPECIFY A PRIVILEGED USER OR THE POSTFIX OWNER.\n#\n#default_privs = nobody\n\n# INTERNET HOST AND DOMAIN NAMES\n#\n# The myhostname parameter specifies the internet hostname of this\n# mail system. The default is to use the fully-qualified domain name\n# from gethostname(). $myhostname is used as a default value for many\n# other configuration parameters.\n#\n# Froxlor Note: $myhostname can and should be the same as $mydomain as long as\n# you don't intend to send mail to it (it will be considered local, not virtual)\n# for the case of a subdomain, $mydomain *must* be equal to $myhostname,\n# otherwise you cannot use the main domain for virtual transport.\n# also check the note about $mydomain below.\nmyhostname = $mydomain\n#myhostname = virtual.domain.tld\n\n# The mydomain parameter specifies the local internet domain name.\n# The default is to use $myhostname minus the first component.\n# $mydomain is used as a default value for many other configuration\n# parameters.\n#\n# Froxlor Note: We are using a default here but that may or may not make sense,\n# depending on your dns configuration, please check yourself.\n\n# FQDN from Froxlor\nmydomain = <SERVERNAME>\n\n# SENDING MAIL\n#\n# The myorigin parameter specifies the domain that locally-posted\n# mail appears to come from. The default is to append $myhostname,\n# which is fine for small sites.  If you run a domain with multiple\n# machines, you should (1) change this to $mydomain and (2) set up\n# a domain-wide alias database that aliases each user to\n# user@that.users.mailhost.\n#\n# For the sake of consistency between sender and recipient addresses,\n# myorigin also specifies the default domain name that is appended\n# to recipient addresses that have no @domain part.\n#\n# Debian GNU/Linux specific:  Specifying a file name will cause the\n# first line of that file to be used as the name.  The Debian default\n# is /etc/mailname.\n#\n#myorigin = /etc/mailname\n#myorigin = $myhostname\n#myorigin = $mydomain\n\n# RECEIVING MAIL\n\n# The inet_interfaces parameter specifies the network interface\n# addresses that this mail system receives mail on.  By default,\n# the software claims all active interfaces on the machine. The\n# parameter also controls delivery of mail to user@[ip.address].\n#\n# See also the proxy_interfaces parameter, for network addresses that\n# are forwarded to us via a proxy or network address translator.\n#\n# Note: you need to stop/start Postfix when this parameter changes.\n#\ninet_interfaces = all\n#inet_interfaces = $myhostname\n#inet_interfaces = $myhostname, localhost\n\n# The proxy_interfaces parameter specifies the network interface\n# addresses that this mail system receives mail on by way of a\n# proxy or network address translation unit. This setting extends\n# the address list specified with the inet_interfaces parameter.\n#\n# You must specify your proxy/NAT addresses when your system is a\n# backup MX host for other domains, otherwise mail delivery loops\n# will happen when the primary MX host is down.\n#\n#proxy_interfaces =\n#proxy_interfaces = 1.2.3.4\n\n# The mydestination parameter specifies the list of domains that this\n# machine considers itself the final destination for.\n#\n# These domains are routed to the delivery agent specified with the\n# local_transport parameter setting. By default, that is the UNIX\n# compatible delivery agent that lookups all recipients in /etc/passwd\n# and /etc/aliases or their equivalent.\n#\n# The default is $myhostname + localhost.$mydomain.  On a mail domain\n# gateway, you should also include $mydomain.\n#\n# Do not specify the names of virtual domains - those domains are\n# specified elsewhere (see VIRTUAL_README).\n#\n# Do not specify the names of domains that this machine is backup MX\n# host for. Specify those names via the relay_domains settings for\n# the SMTP server, or use permit_mx_backup if you are lazy (see\n# STANDARD_CONFIGURATION_README).\n#\n# The local machine is always the final destination for mail addressed\n# to user@[the.net.work.address] of an interface that the mail system\n# receives mail on (see the inet_interfaces parameter).\n#\n# Specify a list of host or domain names, /file/name or type:table\n# patterns, separated by commas and/or whitespace. A /file/name\n# pattern is replaced by its contents; a type:table is matched when\n# a name matches a lookup key (the right-hand side is ignored).\n# Continue long lines by starting the next line with whitespace.\n#\n# See also below, section \"REJECTING MAIL FOR UNKNOWN LOCAL USERS\".\n#\nmydestination = $myhostname, localhost.$mydomain, localhost\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain,\n#\tmail.$mydomain, www.$mydomain, ftp.$mydomain\n\n# REJECTING MAIL FOR UNKNOWN LOCAL USERS\n#\n# The local_recipient_maps parameter specifies optional lookup tables\n# with all names or addresses of users that are local with respect\n# to $mydestination, $inet_interfaces or $proxy_interfaces.\n#\n# If this parameter is defined, then the SMTP server will reject\n# mail for unknown local users. This parameter is defined by default.\n#\n# To turn off local recipient checking in the SMTP server, specify\n# local_recipient_maps = (i.e. empty).\n#\n# The default setting assumes that you use the default Postfix local\n# delivery agent for local delivery. You need to update the\n# local_recipient_maps setting if:\n#\n# - You define $mydestination domain recipients in files other than\n#   /etc/passwd, /etc/aliases, or the $virtual_alias_maps files.\n#   For example, you define $mydestination domain recipients in\n#   the $virtual_mailbox_maps files.\n#\n# - You redefine the local delivery agent in master.cf.\n#\n# - You redefine the \"local_transport\" setting in main.cf.\n#\n# - You use the \"luser_relay\", \"mailbox_transport\", or \"fallback_transport\"\n#   feature of the Postfix local delivery agent (see local(8)).\n#\n# Details are described in the LOCAL_RECIPIENT_README file.\n#\n# Beware: if the Postfix SMTP server runs chrooted, you probably have\n# to access the passwd file via the proxymap service, in order to\n# overcome chroot restrictions. The alternative, having a copy of\n# the system passwd file in the chroot jail is just not practical.\n#\n# The right-hand side of the lookup tables is conveniently ignored.\n# In the left-hand side, specify a bare username, an @domain.tld\n# wild-card, or specify a user@domain.tld address.\n#\n#local_recipient_maps = unix:passwd.byname $alias_maps\n#local_recipient_maps = proxy:unix:passwd.byname $alias_maps\n#local_recipient_maps =\n\n# The unknown_local_recipient_reject_code specifies the SMTP server\n# response code when a recipient domain matches $mydestination or\n# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty\n# and the recipient address or address local-part is not found.\n#\n# The default setting is 550 (reject mail) but it is safer to start\n# with 450 (try again later) until you are certain that your\n# local_recipient_maps settings are OK.\n#\nunknown_local_recipient_reject_code = 550\n\n# TRUST AND RELAY CONTROL\n\n# The mynetworks parameter specifies the list of \"trusted\" SMTP\n# clients that have more privileges than \"strangers\".\n#\n# In particular, \"trusted\" SMTP clients are allowed to relay mail\n# through Postfix.  See the smtpd_recipient_restrictions parameter\n# in postconf(5).\n#\n# You can specify the list of \"trusted\" network addresses by hand\n# or you can let Postfix do it for you (which is the default).\n#\n# By default (mynetworks_style = subnet), Postfix \"trusts\" SMTP\n# clients in the same IP subnetworks as the local machine.\n# On Linux, this does works correctly only with interfaces specified\n# with the \"ifconfig\" command.\n#\n# Specify \"mynetworks_style = class\" when Postfix should \"trust\" SMTP\n# clients in the same IP class A/B/C networks as the local machine.\n# Don't do this with a dialup site - it would cause Postfix to \"trust\"\n# your entire provider's network.  Instead, specify an explicit\n# mynetworks list by hand, as described below.\n#\n# Specify \"mynetworks_style = host\" when Postfix should \"trust\"\n# only the local machine.\n#\n#mynetworks_style = class\n#mynetworks_style = subnet\n#mynetworks_style = host\n\n# Alternatively, you can specify the mynetworks list by hand, in\n# which case Postfix ignores the mynetworks_style setting.\n#\n# Specify an explicit list of network/netmask patterns, where the\n# mask specifies the number of bits in the network part of a host\n# address.\n#\n# You can also specify the absolute pathname of a pattern file instead\n# of listing the patterns here. Specify type:table for table-based lookups\n# (the value on the table right-hand side is not used).\n#\n#mynetworks = 168.100.189.0/28, 127.0.0.0/8\n#mynetworks = $config_directory/mynetworks\n#mynetworks = hash:/etc/postfix/network_table\nmynetworks = 127.0.0.0/8\n\n# The relay_domains parameter restricts what destinations this system will\n# relay mail to.  See the smtpd_recipient_restrictions description in\n# postconf(5) for detailed information.\n#\n# By default, Postfix relays mail\n# - from \"trusted\" clients (IP address matches $mynetworks) to any destination,\n# - from \"untrusted\" clients to destinations that match $relay_domains or\n#   subdomains thereof, except addresses with sender-specified routing.\n# The default relay_domains value is $mydestination.\n#\n# In addition to the above, the Postfix SMTP server by default accepts mail\n# that Postfix is final destination for:\n# - destinations that match $inet_interfaces or $proxy_interfaces,\n# - destinations that match $mydestination\n# - destinations that match $virtual_alias_domains,\n# - destinations that match $virtual_mailbox_domains.\n# These destinations do not need to be listed in $relay_domains.\n#\n# Specify a list of hosts or domains, /file/name patterns or type:name\n# lookup tables, separated by commas and/or whitespace.  Continue\n# long lines by starting the next line with whitespace. A file name\n# is replaced by its contents; a type:name table is matched when a\n# (parent) domain appears as lookup key.\n#\n# NOTE: Postfix will not automatically forward mail for domains that\n# list this system as their primary or backup MX host. See the\n# permit_mx_backup restriction description in postconf(5).\n#\n#relay_domains = $mydestination\n\n# INTERNET OR INTRANET\n\n# The relayhost parameter specifies the default host to send mail to\n# when no entry is matched in the optional transport(5) table. When\n# no relayhost is given, mail is routed directly to the destination.\n#\n# On an intranet, specify the organizational domain name. If your\n# internal DNS uses no MX records, specify the name of the intranet\n# gateway host instead.\n#\n# In the case of SMTP, specify a domain, host, host:port, [host]:port,\n# [address] or [address]:port; the form [host] turns off MX lookups.\n#\n# If you're connected via UUCP, see also the default_transport parameter.\n#\n#relayhost = $mydomain\n#relayhost = [gateway.my.domain]\n#relayhost = [mailserver.isp.tld]\n#relayhost = uucphost\n#relayhost = [an.ip.add.ress]\n\n# REJECTING UNKNOWN RELAY USERS\n#\n# The relay_recipient_maps parameter specifies optional lookup tables\n# with all addresses in the domains that match $relay_domains.\n#\n# If this parameter is defined, then the SMTP server will reject\n# mail for unknown relay users. This feature is off by default.\n#\n# The right-hand side of the lookup tables is conveniently ignored.\n# In the left-hand side, specify an @domain.tld wild-card, or specify\n# a user@domain.tld address.\n#\n#relay_recipient_maps = hash:/etc/postfix/relay_recipients\n\n# INPUT RATE CONTROL\n#\n# The in_flow_delay configuration parameter implements mail input\n# flow control. This feature is turned on by default, although it\n# still needs further development (it's disabled on SCO UNIX due\n# to an SCO bug).\n#\n# A Postfix process will pause for $in_flow_delay seconds before\n# accepting a new message, when the message arrival rate exceeds the\n# message delivery rate. With the default 100 SMTP server process\n# limit, this limits the mail inflow to 100 messages a second more\n# than the number of messages delivered per second.\n#\n# Specify 0 to disable the feature. Valid delays are 0..10.\n#\n#in_flow_delay = 1s\n\n# ADDRESS REWRITING\n#\n# The ADDRESS_REWRITING_README document gives information about\n# address masquerading or other forms of address rewriting including\n# username->Firstname.Lastname mapping.\n\n# ADDRESS REDIRECTION (VIRTUAL DOMAIN)\n#\n# The VIRTUAL_README document gives information about the many forms\n# of domain hosting that Postfix supports.\n\n# \"USER HAS MOVED\" BOUNCE MESSAGES\n#\n# See the discussion in the ADDRESS_REWRITING_README document.\n\n# TRANSPORT MAP\n#\n# See the discussion in the ADDRESS_REWRITING_README document.\n\n# ALIAS DATABASE\n#\n# The alias_maps parameter specifies the list of alias databases used\n# by the local delivery agent. The default list is system dependent.\n#\n# On systems with NIS, the default is to search the local alias\n# database, then the NIS alias database. See aliases(5) for syntax\n# details.\n#\n# If you change the alias database, run \"postalias /etc/aliases\" (or\n# wherever your system stores the mail alias file), or simply run\n# \"newaliases\" to build the necessary DBM or DB file.\n#\n# It will take a minute or so before changes become visible.  Use\n# \"postfix reload\" to eliminate the delay.\n#\n#alias_maps = dbm:/etc/aliases\n#alias_maps = hash:/etc/aliases\n#alias_maps = hash:/etc/aliases, nis:mail.aliases\n#alias_maps = netinfo:/aliases\n\n# The alias_database parameter specifies the alias database(s) that\n# are built with \"newaliases\" or \"sendmail -bi\".  This is a separate\n# configuration parameter, because alias_maps (see above) may specify\n# tables that are not necessarily all under control by Postfix.\n#\n#alias_database = dbm:/etc/aliases\n#alias_database = dbm:/etc/mail/aliases\n#alias_database = hash:/etc/aliases\n#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases\n\n# ADDRESS EXTENSIONS (e.g., user+foo)\n#\n# The recipient_delimiter parameter specifies the separator between\n# user names and address extensions (user+foo). See canonical(5),\n# local(8), relocated(5) and virtual(5) for the effects this has on\n# aliases, canonical, virtual, relocated and .forward file lookups.\n# Basically, the software tries user+foo and .forward+foo before\n# trying user and .forward.\n#\n#recipient_delimiter = +\n\n# DELIVERY TO MAILBOX\n#\n# The home_mailbox parameter specifies the optional pathname of a\n# mailbox file relative to a user's home directory. The default\n# mailbox file is /var/spool/mail/user or /var/mail/user.  Specify\n# \"Maildir/\" for qmail-style delivery (the / is required).\n#\n#home_mailbox = Mailbox\n#home_mailbox = Maildir/\n\n# The mail_spool_directory parameter specifies the directory where\n# UNIX-style mailboxes are kept. The default setting depends on the\n# system type.\n#\n#mail_spool_directory = /var/mail\n#mail_spool_directory = /var/spool/mail\n\n# The mailbox_command parameter specifies the optional external\n# command to use instead of mailbox delivery. The command is run as\n# the recipient with proper HOME, SHELL and LOGNAME environment settings.\n# Exception:  delivery for root is done as $default_user.\n#\n# Other environment variables of interest: USER (recipient username),\n# EXTENSION (address extension), DOMAIN (domain part of address),\n# and LOCAL (the address localpart).\n#\n# Unlike other Postfix configuration parameters, the mailbox_command\n# parameter is not subjected to $parameter substitutions. This is to\n# make it easier to specify shell syntax (see example below).\n#\n# Avoid shell meta characters because they will force Postfix to run\n# an expensive shell process. Procmail alone is expensive enough.\n#\n# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN\n# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER.\n#\nmailbox_command = /usr/lib/dovecot/deliver\n#mailbox_command = /usr/bin/procmail -a \"$EXTENSION\"\n\n# The mailbox_transport specifies the optional transport in master.cf\n# to use after processing aliases and .forward files. This parameter\n# has precedence over the mailbox_command, fallback_transport and\n# luser_relay parameters.\n#\n# Specify a string of the form transport:nexthop, where transport is\n# the name of a mail delivery transport defined in master.cf.  The\n# :nexthop part is optional. For more details see the sample transport\n# configuration file.\n#\n# NOTE: if you use this feature for accounts not in the UNIX password\n# file, then you must update the \"local_recipient_maps\" setting in\n# the main.cf file, otherwise the SMTP server will reject mail for\n# non-UNIX accounts with \"User unknown in local recipient table\".\n#\n# Cyrus IMAP over LMTP. Specify ``lmtpunix      cmd=\"lmtpd\"\n# listen=\"/var/imap/socket/lmtp\" prefork=0'' in cyrus.conf.\n#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp\n#\n# Cyrus IMAP via command line. Uncomment the \"cyrus...pipe\" and\n# subsequent line in master.cf.\n#mailbox_transport = cyrus\n\n# The fallback_transport specifies the optional transport in master.cf\n# to use for recipients that are not found in the UNIX passwd database.\n# This parameter has precedence over the luser_relay parameter.\n#\n# Specify a string of the form transport:nexthop, where transport is\n# the name of a mail delivery transport defined in master.cf.  The\n# :nexthop part is optional. For more details see the sample transport\n# configuration file.\n#\n# NOTE: if you use this feature for accounts not in the UNIX password\n# file, then you must update the \"local_recipient_maps\" setting in\n# the main.cf file, otherwise the SMTP server will reject mail for\n# non-UNIX accounts with \"User unknown in local recipient table\".\n#\n#fallback_transport = lmtp:unix:/file/name\n#fallback_transport = cyrus\n#fallback_transport =\n\n# The luser_relay parameter specifies an optional destination address\n# for unknown recipients.  By default, mail for unknown@$mydestination,\n# unknown@[$inet_interfaces] or unknown@[$proxy_interfaces] is returned\n# as undeliverable.\n#\n# The following expansions are done on luser_relay: $user (recipient\n# username), $shell (recipient shell), $home (recipient home directory),\n# $recipient (full recipient address), $extension (recipient address\n# extension), $domain (recipient domain), $local (entire recipient\n# localpart), $recipient_delimiter. Specify ${name?value} or\n# ${name:value} to expand value only when $name does (does not) exist.\n#\n# luser_relay works only for the default Postfix local delivery agent.\n#\n# NOTE: if you use this feature for accounts not in the UNIX password\n# file, then you must specify \"local_recipient_maps =\" (i.e. empty) in\n# the main.cf file, otherwise the SMTP server will reject mail for\n# non-UNIX accounts with \"User unknown in local recipient table\".\n#\n#luser_relay = $user@other.host\n#luser_relay = $local@other.host\n#luser_relay = admin+$local\n\n# JUNK MAIL CONTROLS\n#\n# The controls listed here are only a very small subset. The file\n# SMTPD_ACCESS_README provides an overview.\n\n# The header_checks parameter specifies an optional table with patterns\n# that each logical message header is matched against, including\n# headers that span multiple physical lines.\n#\n# By default, these patterns also apply to MIME headers and to the\n# headers of attached messages. With older Postfix versions, MIME and\n# attached message headers were treated as body text.\n#\n# For details, see \"man header_checks\".\n#\n#header_checks = regexp:/etc/postfix/header_checks\n\n# FAST ETRN SERVICE\n#\n# Postfix maintains per-destination logfiles with information about\n# deferred mail, so that mail can be flushed quickly with the SMTP\n# \"ETRN domain.tld\" command, or by executing \"sendmail -qRdomain.tld\".\n# See the ETRN_README document for a detailed description.\n#\n# The fast_flush_domains parameter controls what destinations are\n# eligible for this service. By default, they are all domains that\n# this server is willing to relay mail to.\n#\n#fast_flush_domains = $relay_domains\n\n# SHOW SOFTWARE VERSION OR NOT\n#\n# The smtpd_banner parameter specifies the text that follows the 220\n# code in the SMTP server's greeting banner. Some people like to see\n# the mail version advertised. By default, Postfix shows no version.\n#\n# You MUST specify $myhostname at the start of the text. That is an\n# RFC requirement. Postfix itself does not care.\n#\n#smtpd_banner = $myhostname ESMTP $mail_name\n#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version)\nsmtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)\n\n\n# PARALLEL DELIVERY TO THE SAME DESTINATION\n#\n# How many parallel deliveries to the same user or domain? With local\n# delivery, it does not make sense to do massively parallel delivery\n# to the same user, because mailbox updates must happen sequentially,\n# and expensive pipelines in .forward files can cause disasters when\n# too many are run at the same time. With SMTP deliveries, 10\n# simultaneous connections to the same domain could be sufficient to\n# raise eyebrows.\n#\n# Each message delivery transport has its XXX_destination_concurrency_limit\n# parameter.  The default is $default_destination_concurrency_limit for\n# most delivery transports. For the local delivery agent the default is 2.\n\n#local_destination_concurrency_limit = 2\n#default_destination_concurrency_limit = 20\n\n# DEBUGGING CONTROL\n#\n# The debug_peer_level parameter specifies the increment in verbose\n# logging level when an SMTP client or server host name or address\n# matches a pattern in the debug_peer_list parameter.\n#\n#debug_peer_level = 2\n\n# The debug_peer_list parameter specifies an optional list of domain\n# or network patterns, /file/name patterns or type:name tables. When\n# an SMTP client or server host name or address matches a pattern,\n# increase the verbose logging level by the amount specified in the\n# debug_peer_level parameter.\n#\n#debug_peer_list = 127.0.0.1\n#debug_peer_list = some.domain\n\n# The debugger_command specifies the external command that is executed\n# when a Postfix daemon program is run with the -D option.\n#\n# Use \"command .. & sleep 5\" so that the debugger can attach before\n# the process marches on. If you use an X-based debugger, be sure to\n# set up your XAUTHORITY environment variable before starting Postfix.\n#\ndebugger_command =\n\t PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin\n\t ddd $daemon_directory/$process_name $process_id & sleep 5\n\n# If you can't use X, use this to capture the call stack when a\n# daemon crashes. The result is in a file in the configuration\n# directory, and is named after the process name and the process ID.\n#\n# debugger_command =\n#\tPATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont;\n#\techo where) | gdb $daemon_directory/$process_name $process_id 2>&1\n#\t>$config_directory/$process_name.$process_id.log & sleep 5\n#\n# Another possibility is to run gdb under a detached screen session.\n# To attach to the screen session, su root and run \"screen -r\n# <id_string>\" where <id_string> uniquely matches one of the detached\n# sessions (from \"screen -list\").\n#\n# debugger_command =\n#\tPATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen\n#\t-dmS $process_name gdb $daemon_directory/$process_name\n#\t$process_id & sleep 1\n\n# INSTALL-TIME CONFIGURATION INFORMATION\n#\n# The following parameters are used when installing a new Postfix version.\n#\n# sendmail_path: The full pathname of the Postfix sendmail command.\n# This is the Sendmail-compatible mail posting interface.\n#\nsendmail_path = /usr/sbin/sendmail\n\n# newaliases_path: The full pathname of the Postfix newaliases command.\n# This is the Sendmail-compatible command to build alias databases.\n#\nnewaliases_path = /usr/bin/newaliases\n\n# mailq_path: The full pathname of the Postfix mailq command.  This\n# is the Sendmail-compatible mail queue listing command.\n#\nmailq_path = /usr/bin/mailq\n\n# setgid_group: The group for mail submission and queue management\n# commands.  This must be a group name with a numerical group ID that\n# is not shared with other accounts, not even with the Postfix account.\n#\nsetgid_group = postdrop\n\n# html_directory: The location of the Postfix HTML documentation.\n#\nhtml_directory = no\n\n# manpage_directory: The location of the Postfix on-line manual pages.\n#\nmanpage_directory = /usr/share/man\n\n# sample_directory: The location of the Postfix sample configuration files.\n# This parameter is obsolete as of Postfix 2.1.\n#\nsample_directory = /usr/share/doc/postfix\n\n# readme_directory: The location of the Postfix README files.\n#\nreadme_directory = /usr/share/doc/postfix\ninet_protocols = ipv4\n\nappend_dot_mydomain = no\nbiff = no\nsmtpd_helo_required = yes\nsmtpd_recipient_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unauth_destination,\n\treject_unauth_pipelining,\n\treject_non_fqdn_recipient\nsmtpd_sender_restrictions = permit_mynetworks,\n\treject_sender_login_mismatch,\n\tpermit_sasl_authenticated,\n\treject_unknown_helo_hostname,\n\treject_unknown_recipient_domain,\n\treject_unknown_sender_domain\nsmtpd_client_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unknown_client_hostname\n\n# Postfix 2.10 requires this option. Postfix < 2.10 ignores this.\n# The option is intentionally left empty.\nsmtpd_relay_restrictions =\n\n# Maximum size of Message in bytes (50MB)\nmessage_size_limit = 52428800\n\n## SASL Auth Settings\nsmtpd_sasl_auth_enable = yes\nsmtpd_sasl_local_domain = $myhostname\nbroken_sasl_auth_clients = yes\n## Dovecot Settings for deliver, SASL Auth and virtual transport\nsmtpd_sasl_type = dovecot\nvirtual_transport = dovecot\ndovecot_destination_recipient_limit = 1\nsmtpd_sasl_path = private/auth\n\n# Virtual delivery settings\nvirtual_mailbox_base = /\nvirtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf\nvirtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf\nvirtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_alias_maps.cf\nsmtpd_sender_login_maps = proxy:mysql:/etc/postfix/mysql-virtual_sender_permissions.cf\nvirtual_uid_maps = static:<VIRTUAL_UID_MAPS>\nvirtual_gid_maps = static:<VIRTUAL_GID_MAPS>\n\n# Local delivery settings\nlocal_transport = local\nalias_maps = $alias_database\n\n# Default Mailbox size, is set to 0 which means unlimited!\nmailbox_size_limit = 0\nvirtual_mailbox_limit = 0\n\n### TLS settings\n###\n## TLS for outgoing mails from the server to another server\nsmtp_tls_security_level = may\nsmtp_tls_note_starttls_offer = yes\n## TLS for incoming connections (clients or other mail servers)\nsmtpd_tls_security_level = may\nsmtpd_tls_cert_file = <SSL_CERT_FILE>\nsmtpd_tls_key_file = <SSL_KEY_FILE>\n#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt\nsmtpd_tls_loglevel = 1\nsmtpd_tls_received_header = yes\nsmtp_use_tls = yes\nsmtpd_use_tls = yes\nsmtpd_tls_session_cache_timeout = 3600s\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/files[@index=0]</include>\n\t\t\t\t\t<file name=\"/etc/postfix/master.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Postfix master process configuration file.  For details on the format\n# of the file, see the master(5) manual page (command: \"man 5 master\" or\n# on-line: http://www.postfix.org/master.5.html).\n#\n# Do not forget to execute \"postfix reload\" after editing this file.\n#\n# ==========================================================================\n# service type  private unpriv  chroot  wakeup  maxproc command + args\n#               (yes)   (yes)   (yes)   (never) (100)\n# ==========================================================================\n#smtp      inet  n       -       y       -       -       smtpd\nsmtp      inet  n       -       y       -       1       postscreen\nsmtpd     pass  -       -       y       -       -       smtpd\ndnsblog   unix  -       -       y       -       0       dnsblog\ntlsproxy  unix  -       -       y       -       0       tlsproxy\nsubmission inet n       -       y       -       -       smtpd\n  -o syslog_name=postfix/submission\n  -o smtpd_tls_security_level=encrypt\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\nsmtps     inet  n       -       y       -       -       smtpd\n  -o syslog_name=postfix/smtps\n  -o smtpd_tls_wrappermode=yes\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\n#628       inet  n       -       y       -       -       qmqpd\npickup    unix  n       -       y       60      1       pickup\n         -o content_filter=\n         -o receive_override_options=no_header_body_checks\ncleanup   unix  n       -       y       -       0       cleanup\nqmgr      unix  n       -       n       300     1       qmgr\n#qmgr     unix  n       -       n       300     1       oqmgr\ntlsmgr    unix  -       -       y       1000?   1       tlsmgr\nrewrite   unix  -       -       y       -       -       trivial-rewrite\nbounce    unix  -       -       y       -       0       bounce\ndefer     unix  -       -       y       -       0       bounce\ntrace     unix  -       -       y       -       0       bounce\nverify    unix  -       -       y       -       1       verify\nflush     unix  n       -       y       1000?   0       flush\nproxymap  unix  -       -       n       -       -       proxymap\nproxywrite unix -       -       n       -       1       proxymap\nsmtp      unix  -       -       y       -       -       smtp\nrelay     unix  -       -       y       -       -       smtp\n#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5\nshowq     unix  n       -       y       -       -       showq\nerror     unix  -       -       y       -       -       error\nretry     unix  -       -       y       -       -       error\ndiscard   unix  -       -       y       -       -       discard\nlocal     unix  -       n       n       -       -       local\nvirtual   unix  -       n       n       -       -       virtual\nlmtp      unix  -       -       y       -       -       lmtp\nanvil     unix  -       -       y       -       1       anvil\nscache    unix  -       -       y       -       1       scache\n#\n# ====================================================================\n# Interfaces to non-Postfix software. Be sure to examine the manual\n# pages of the non-Postfix software to find out what options it wants.\n#\n# Many of the following services use the Postfix pipe(8) delivery\n# agent.  See the pipe(8) man page for information about ${recipient}\n# and other message envelope options.\n# ====================================================================\n#\n# maildrop. See the Postfix MAILDROP_README file for details.\n# Also specify in main.cf: maildrop_destination_recipient_limit=1\n#\nmaildrop  unix  -       n       n       -       -       pipe\n  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}\n#\n# ====================================================================\n#\n# Recent Cyrus versions can use the existing \"lmtp\" master.cf entry.\n#\n# Specify in cyrus.conf:\n#   lmtp    cmd=\"lmtpd -a\" listen=\"localhost:lmtp\" proto=tcp4\n#\n# Specify in main.cf one or more of the following:\n#  mailbox_transport = lmtp:inet:localhost\n#  virtual_transport = lmtp:inet:localhost\n#\n# ====================================================================\n#\n# Cyrus 2.1.5 (Amos Gouaux)\n# Also specify in main.cf: cyrus_destination_recipient_limit=1\n#\n#cyrus     unix  -       n       n       -       -       pipe\n#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}\n#\n# ====================================================================\n# Old example of delivery via Cyrus.\n#\n#old-cyrus unix  -       n       n       -       -       pipe\n#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}\n#\n# ====================================================================\n#\n# See the Postfix UUCP_README file for configuration details.\n#\nuucp      unix  -       n       n       -       -       pipe\n  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)\n#\n# Other external delivery methods.\n#\nifmail    unix  -       n       n       -       -       pipe\n  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)\nbsmtp     unix  -       n       n       -       -       pipe\n  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient\nscalemail-backend unix\t-\tn\tn\t-\t2\tpipe\n  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}\nmailman   unix  -       n       n       -       -       pipe\n  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py\n  ${nexthop} ${user}\n# Dovecot LDA\ndovecot\t  unix\t-\tn\tn\t-\t-\tpipe\n\tflags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ${recipient}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- IMAP/POP3 services -->\n\t\t\t<service type=\"mail\" title=\"{{lng.admin.configfiles.mail}}\">\n\t\t\t\t<!-- valid for both dovecots -->\n\t\t\t\t<general>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-managesieved dovecot-sieve]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/dovecot/dovecot.conf\" chown=\"root:root\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## Dovecot configuration file\n\n# If you're in a hurry, see http://wiki2.dovecot.org/QuickConfiguration\n\n# \"doveconf -n\" command gives a clean output of the changed settings. Use it\n# instead of copy&pasting files when posting to the Dovecot mailing list.\n\n# '#' character and everything after it is treated as comments. Extra spaces\n# and tabs are ignored. If you want to use either of these explicitly, put the\n# value inside quotes, eg.: key = \"# char and trailing whitespace  \"\n\n# Most (but not all) settings can be overridden by different protocols and/or\n# source/destination IPs by placing the settings inside sections, for example:\n# protocol imap { }, local 127.0.0.1 { }, remote 10.0.0.0/8 { }\n\n# Default values are shown for each setting, it's not required to uncomment\n# those. These are exceptions to this though: No sections (e.g. namespace {})\n# or plugin settings are added by default, they're listed only as examples.\n# Paths are also just examples with the real defaults being based on configure\n# options. The paths listed here are for configure --prefix=/usr\n# --sysconfdir=/etc --localstatedir=/var\n\n# Enable installed protocols\n!include_try /usr/share/dovecot/protocols.d/*.protocol\n\n# A comma separated list of IPs or hosts where to listen in for connections.\n# \"*\" listens in all IPv4 interfaces, \"::\" listens in all IPv6 interfaces.\n# If you want to specify non-default ports or anything more complex,\n# edit conf.d/master.conf.\n#listen = *, ::\n\n# Base directory where to store runtime data.\n#base_dir = /var/run/dovecot/\n\n# Name of this instance. In multi-instance setup doveadm and other commands\n# can use -i <instance_name> to select which instance is used (an alternative\n# to -c <config_path>). The instance name is also added to Dovecot processes\n# in ps output.\n#instance_name = dovecot\n\n# Greeting message for clients.\n#login_greeting = Dovecot ready.\n\n# Space separated list of trusted network ranges. Connections from these\n# IPs are allowed to override their IP addresses and ports (for logging and\n# for authentication checks). disable_plaintext_auth is also ignored for\n# these networks. Typically you'd specify your IMAP proxy servers here.\n#login_trusted_networks =\n\n# Space separated list of login access check sockets (e.g. tcpwrap)\n#login_access_sockets =\n\n# With proxy_maybe=yes if proxy destination matches any of these IPs, don't do\n# proxying. This isn't necessary normally, but may be useful if the destination\n# IP is e.g. a load balancer's IP.\n#auth_proxy_self =\n\n# Show more verbose process titles (in ps). Currently shows user name and\n# IP address. Useful for seeing who are actually using the IMAP processes\n# (eg. shared mailboxes or if same uid is used for multiple accounts).\n#verbose_proctitle = no\n\n# Should all processes be killed when Dovecot master process shuts down.\n# Setting this to \"no\" means that Dovecot can be upgraded without\n# forcing existing client connections to close (although that could also be\n# a problem if the upgrade is e.g. because of a security fix).\n#shutdown_clients = yes\n\n# If non-zero, run mail commands via this many connections to doveadm server,\n# instead of running them directly in the same process.\n#doveadm_worker_count = 0\n# UNIX socket or host:port used for connecting to doveadm server\n#doveadm_socket_path = doveadm-server\n\n# Space separated list of environment variables that are preserved on Dovecot\n# startup and passed down to all of its child processes. You can also give\n# key=value pairs to always set specific settings.\n#import_environment = TZ\n\n##\n## Dictionary server settings\n##\n\n# Dictionary can be used to store key=value lists. This is used by several\n# plugins. The dictionary can be accessed either directly or though a\n# dictionary server. The following dict block maps dictionary names to URIs\n# when the server is used. These can then be referenced using URIs in format\n# \"proxy::<name>\".\n\ndict {\n  #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext\n  #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext\n}\n\n# Most of the actual configuration gets included below. The filenames are\n# first sorted by their ASCII value and parsed in that order. The 00-prefixes\n# in filenames are intended to make it easier to understand the ordering.\n!include conf.d/*.conf\n\n# A config file can also tried to be included without giving an error if\n# it's not found:\n!include_try local.conf\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/dovecot-sql.conf.ext\"\n\t\t\t\t\t\t\tchown=\"root:root\" chmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# This file is commonly accessed via passdb {} or userdb {} section in\n# conf.d/auth-sql.conf.ext\n\n# This file is opened as root, so it should be owned by root and mode 0600.\n#\n# http://wiki2.dovecot.org/AuthDatabase/SQL\n#\n# For the sql passdb module, you'll need a database with a table that\n# contains fields for at least the username and password. If you want to\n# use the user@domain syntax, you might want to have a separate domain\n# field as well.\n#\n# If your users all have the same uig/gid, and have predictable home\n# directories, you can use the static userdb module to generate the home\n# dir based on the username and domain. In this case, you won't need fields\n# for home, uid, or gid in the database.\n#\n# If you prefer to use the sql userdb module, you'll want to add fields\n# for home, uid, and gid. Here is an example table:\n#\n# CREATE TABLE users (\n#     username VARCHAR(128) NOT NULL,\n#     domain VARCHAR(128) NOT NULL,\n#     password VARCHAR(64) NOT NULL,\n#     home VARCHAR(255) NOT NULL,\n#     uid INTEGER NOT NULL,\n#     gid INTEGER NOT NULL,\n#     active CHAR(1) DEFAULT 'Y' NOT NULL\n# );\n\n# Database driver: mysql, pgsql, sqlite\ndriver = mysql\n\n# Database connection string. This is driver-specific setting.\n#\n# HA / round-robin load-balancing is supported by giving multiple host\n# settings, like: host=sql1.host.org host=sql2.host.org\n#\n# pgsql:\n#   For available options, see the PostgreSQL documentation for the\n#   PQconnectdb function of libpq.\n#   Use maxconns=n (default 5) to change how many connections Dovecot can\n#   create to pgsql.\n#\n# mysql:\n#   Basic options emulate PostgreSQL option names:\n#     host, port, user, password, dbname\n#\n#   But also adds some new settings:\n#     client_flags           - See MySQL manual\n#     connect_timeout        - Connect timeout in seconds (default: 5)\n#     read_timeout           - Read timeout in seconds (default: 30)\n#     write_timeout          - Write timeout in seconds (default: 30)\n#     ssl_ca, ssl_ca_path    - Set either one or both to enable SSL\n#     ssl_cert, ssl_key      - For sending client-side certificates to server\n#     ssl_cipher             - Set minimum allowed cipher security (default: HIGH)\n#     ssl_verify_server_cert - Verify that the name in the server SSL certificate\n#                              matches the host (default: no)\n#     option_file            - Read options from the given file instead of\n#                              the default my.cnf location\n#     option_group           - Read options from the given group (default: client)\n#\n#   You can connect to UNIX sockets by using host: host=/var/run/mysql.sock\n#   Note that currently you can't use spaces in parameters.\n#\n# sqlite:\n#   The path to the database file.\n#\n# Examples:\n#   connect = host=192.168.1.1 dbname=users\n#   connect = host=sql.example.com dbname=virtual user=virtual password=blarg\n#   connect = /etc/dovecot/authdb.sqlite\n#\nconnect = \"host=<SQL_HOST> dbname=<SQL_DB> user=<SQL_UNPRIVILEGED_USER> password=<SQL_UNPRIVILEGED_PASSWORD>\"\n\n# Default password scheme.\n#\n# List of supported schemes is in\n# http://wiki2.dovecot.org/Authentication/PasswordSchemes\n#\n#default_pass_scheme = CRYPT\n\n# passdb query to retrieve the password. It can return fields:\n#   password - The user's password. This field must be returned.\n#   user - user@domain from the database. Needed with case-insensitive lookups.\n#   username and domain - An alternative way to represent the \"user\" field.\n#\n# The \"user\" field is often necessary with case-insensitive lookups to avoid\n# e.g. \"name\" and \"nAme\" logins creating two different mail directories. If\n# your user and domain names are in separate fields, you can return \"username\"\n# and \"domain\" fields instead of \"user\".\n#\n# The query can also return other fields which have a special meaning, see\n# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields\n#\n# Commonly used available substitutions (see http://wiki2.dovecot.org/Variables\n# for full list):\n#   %u = entire user@domain\n#   %n = user part of user@domain\n#   %d = domain part of user@domain\n#\n# Note that these can be used only as input to SQL query. If the query outputs\n# any of these substitutions, they're not touched. Otherwise it would be\n# difficult to have eg. usernames containing '%' characters.\n#\n# Example:\n#   password_query = SELECT userid AS user, pw AS password \\\n#     FROM users WHERE userid = '%u' AND active = 'Y'\n#\n#password_query = \\\n#  SELECT username, domain, password \\\n#  FROM users WHERE username = '%n' AND domain = '%d'\n\n# userdb query to retrieve the user information. It can return fields:\n#   uid - System UID (overrides mail_uid setting)\n#   gid - System GID (overrides mail_gid setting)\n#   home - Home directory\n#   mail - Mail location (overrides mail_location setting)\n#\n# None of these are strictly required. If you use a single UID and GID, and\n# home or mail directory fits to a template string, you could use userdb static\n# instead. For a list of all fields that can be returned, see\n# http://wiki2.dovecot.org/UserDatabase/ExtraFields\n#\n# Examples:\n#   user_query = SELECT home, uid, gid FROM users WHERE userid = '%u'\n#   user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u'\n#   user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u'\n#\n#user_query = \\\n#  SELECT home, uid, gid \\\n#  FROM users WHERE username = '%n' AND domain = '%d'\nuser_query = SELECT CONCAT(homedir, maildir) AS home, CONCAT('maildir:', homedir, maildir) AS mail, uid, gid, CONCAT('*:storage=', quota, 'M') as quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u')\n\n# If you wish to avoid two SQL lookups (passdb + userdb), you can use\n# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll\n# also have to return userdb fields in password_query prefixed with \"userdb_\"\n# string. For example:\n#password_query = \\\n#  SELECT userid AS user, password, \\\n#    home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \\\n#  FROM users WHERE userid = '%u'\npassword_query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid,  CONCAT('maildir:', homedir, maildir) AS userdb_mail, CONCAT('*:storage=', quota, 'M') as userdb_quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') AND ((imap = 1 AND '%Ls' = 'imap') OR (pop3 = 1 AND '%Ls' = 'pop3') OR ((postfix = 'Y' AND '%Ls' = 'smtp') OR (postfix = 'Y' AND '%Ls' = 'sieve')))\n\n# Query to get a list of all usernames.\niterate_query = \"SELECT username AS user FROM mail_users WHERE (imap = 1 OR pop3 = 1)\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-auth.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Authentication processes\n##\n\n# Disable LOGIN command and all other plaintext authentications unless\n# SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP\n# matches the local IP (ie. you're connecting from the same computer), the\n# connection is considered secure and plaintext authentication is allowed.\n# See also ssl=required setting.\ndisable_plaintext_auth = no\n\n# Authentication cache size (e.g. 10M). 0 means it's disabled. Note that\n# bsdauth and PAM require cache_key to be set for caching to be used.\n#auth_cache_size = 0\n# Time to live for cached data. After TTL expires the cached record is no\n# longer used, *except* if the main database lookup returns internal failure.\n# We also try to handle password changes automatically: If user's previous\n# authentication was successful, but this one wasn't, the cache isn't used.\n# For now this works only with plaintext authentication.\n#auth_cache_ttl = 1 hour\n# TTL for negative hits (user not found, password mismatch).\n# 0 disables caching them completely.\n#auth_cache_negative_ttl = 1 hour\n\n# Space separated list of realms for SASL authentication mechanisms that need\n# them. You can leave it empty if you don't want to support multiple realms.\n# Many clients simply use the first one listed here, so keep the default realm\n# first.\n#auth_realms =\n\n# Default realm/domain to use if none was specified. This is used for both\n# SASL realms and appending @domain to username in plaintext logins.\n#auth_default_realm =\n\n# List of allowed characters in username. If the user-given username contains\n# a character not listed in here, the login automatically fails. This is just\n# an extra check to make sure user can't exploit any potential quote escaping\n# vulnerabilities with SQL/LDAP databases. If you want to allow all characters,\n# set this value to empty.\n#auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@\n\n# Username character translations before it's looked up from databases. The\n# value contains series of from -> to characters. For example \"#@/@\" means\n# that '#' and '/' characters are translated to '@'.\n#auth_username_translation =\n\n# Username formatting before it's looked up from databases. You can use\n# the standard variables here, eg. %Lu would lowercase the username, %n would\n# drop away the domain if it was given, or \"%n-AT-%d\" would change the '@' into\n# \"-AT-\". This translation is done after auth_username_translation changes.\n#auth_username_format = %Lu\n\n# If you want to allow master users to log in by specifying the master\n# username within the normal username string (ie. not using SASL mechanism's\n# support for it), you can specify the separator character here. The format\n# is then <username><separator><master username>. UW-IMAP uses \"*\" as the\n# separator, so that could be a good choice.\n#auth_master_user_separator =\n\n# Username to use for users logging in with ANONYMOUS SASL mechanism\n#auth_anonymous_username = anonymous\n\n# Maximum number of dovecot-auth worker processes. They're used to execute\n# blocking passdb and userdb queries (eg. MySQL and PAM). They're\n# automatically created and destroyed as needed.\n#auth_worker_max_count = 30\n\n# Host name to use in GSSAPI principal names. The default is to use the\n# name returned by gethostname(). Use \"$ALL\" (with quotes) to allow all keytab\n# entries.\n#auth_gssapi_hostname =\n\n# Kerberos keytab to use for the GSSAPI mechanism. Will use the system\n# default (usually /etc/krb5.keytab) if not specified. You may need to change\n# the auth service to run as root to be able to read this file.\n#auth_krb5_keytab =\n\n# Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and\n# ntlm_auth helper. <doc/wiki/Authentication/Mechanisms/Winbind.txt>\n#auth_use_winbind = no\n\n# Path for Samba's ntlm_auth helper binary.\n#auth_winbind_helper_path = /usr/bin/ntlm_auth\n\n# Time to delay before replying to failed authentications.\n#auth_failure_delay = 2 secs\n\n# Require a valid SSL client certificate or the authentication fails.\n#auth_ssl_require_client_cert = no\n\n# Take the username from client's SSL certificate, using\n# X509_NAME_get_text_by_NID() which returns the subject's DN's\n# CommonName.\n#auth_ssl_username_from_cert = no\n\n# Space separated list of wanted authentication mechanisms:\n#   plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp\n#   gss-spnego\n# NOTE: See also disable_plaintext_auth setting.\nauth_mechanisms = plain login\n\n##\n## Password and user databases\n##\n\n#\n# Password database is used to verify user's password (and nothing more).\n# You can have multiple passdbs and userdbs. This is useful if you want to\n# allow both system users (/etc/passwd) and virtual users to login without\n# duplicating the system users into virtual database.\n#\n# <doc/wiki/PasswordDatabase.txt>\n#\n# User database specifies where mails are located and what user/group IDs\n# own them. For single-UID configuration use \"static\" userdb.\n#\n# <doc/wiki/UserDatabase.txt>\n\n#!include auth-deny.conf.ext\n#!include auth-master.conf.ext\n\n#!include auth-system.conf.ext\n!include auth-sql.conf.ext\n#!include auth-ldap.conf.ext\n#!include auth-passwdfile.conf.ext\n#!include auth-checkpassword.conf.ext\n#!include auth-static.conf.ext\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-mail.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Mailbox locations and namespaces\n##\n\n# Location for users' mailboxes. The default is empty, which means that Dovecot\n# tries to find the mailboxes automatically. This won't work if the user\n# doesn't yet have any mail, so you should explicitly tell Dovecot the full\n# location.\n#\n# If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%u)\n# isn't enough. You'll also need to tell Dovecot where the other mailboxes are\n# kept. This is called the \"root mail directory\", and it must be the first\n# path given in the mail_location setting.\n#\n# There are a few special variables you can use, eg.:\n#\n#   %u - username\n#   %n - user part in user@domain, same as %u if there's no domain\n#   %d - domain part in user@domain, empty if there's no domain\n#   %h - home directory\n#\n# See doc/wiki/Variables.txt for full list. Some examples:\n#\n#   mail_location = maildir:~/Maildir\n#   mail_location = mbox:~/mail:INBOX=/var/mail/%u\n#   mail_location = mbox:/var/mail/%d/%1n/%n:INDEX=/var/indexes/%d/%1n/%n\n#\n# <doc/wiki/MailLocation.txt>\n#\nmail_location = mbox:~/mail:INBOX=/var/mail/%u\n\n# If you need to set multiple mailbox locations or want to change default\n# namespace settings, you can do it by defining namespace sections.\n#\n# You can have private, shared and public namespaces. Private namespaces\n# are for user's personal mails. Shared namespaces are for accessing other\n# users' mailboxes that have been shared. Public namespaces are for shared\n# mailboxes that are managed by sysadmin. If you create any shared or public\n# namespaces you'll typically want to enable ACL plugin also, otherwise all\n# users can access all the shared mailboxes, assuming they have permissions\n# on filesystem level to do so.\nnamespace inbox {\n  # Namespace type: private, shared or public\n  #type = private\n\n  # Hierarchy separator to use. You should use the same separator for all\n  # namespaces or some clients get confused. '/' is usually a good one.\n  # The default however depends on the underlying mail storage format.\n  #separator =\n\n  # Prefix required to access this namespace. This needs to be different for\n  # all namespaces. For example \"Public/\".\n  #prefix =\n\n  # Physical location of the mailbox. This is in same format as\n  # mail_location, which is also the default for it.\n  #location =\n\n  # There can be only one INBOX, and this setting defines which namespace\n  # has it.\n  inbox = yes\n\n  # If namespace is hidden, it's not advertised to clients via NAMESPACE\n  # extension. You'll most likely also want to set list=no. This is mostly\n  # useful when converting from another server with different namespaces which\n  # you want to deprecate but still keep working. For example you can create\n  # hidden namespaces with prefixes \"~/mail/\", \"~%u/mail/\" and \"mail/\".\n  #hidden = no\n\n  # Show the mailboxes under this namespace with LIST command. This makes the\n  # namespace visible for clients that don't support NAMESPACE extension.\n  # \"children\" value lists child mailboxes, but hides the namespace prefix.\n  #list = yes\n\n  # Namespace handles its own subscriptions. If set to \"no\", the parent\n  # namespace handles them (empty prefix should always have this as \"yes\")\n  #subscriptions = yes\n\n  # See 15-mailboxes.conf for definitions of special mailboxes.\n}\n\n# Example shared namespace configuration\n#namespace {\n  #type = shared\n  #separator = /\n\n  # Mailboxes are visible under \"shared/user@domain/\"\n  # %%n, %%d and %%u are expanded to the destination user.\n  #prefix = shared/%%u/\n\n  # Mail location for other users' mailboxes. Note that %variables and ~/\n  # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the\n  # destination user's data.\n  #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u\n\n  # Use the default namespace for saving subscriptions.\n  #subscriptions = no\n\n  # List the shared/ namespace only if there are visible shared mailboxes.\n  #list = children\n#}\n# Should shared INBOX be visible as \"shared/user\" or \"shared/user/INBOX\"?\n#mail_shared_explicit_inbox = no\n\n# System user and group used to access mails. If you use multiple, userdb\n# can override these by returning uid or gid fields. You can use either numbers\n# or names. <doc/wiki/UserIds.txt>\n#mail_uid =\n#mail_gid =\n\n# Group to enable temporarily for privileged operations. Currently this is\n# used only with INBOX when either its initial creation or dotlocking fails.\n# Typically this is set to \"mail\" to give access to /var/mail.\nmail_privileged_group = mail\n\n# Grant access to these supplementary groups for mail processes. Typically\n# these are used to set up access to shared mailboxes. Note that it may be\n# dangerous to set these if users can create symlinks (e.g. if \"mail\" group is\n# set here, ln -s /var/mail ~/mail/var could allow a user to delete others'\n# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it).\n#mail_access_groups =\n\n# Allow full filesystem access to clients. There's no access checks other than\n# what the operating system does for the active UID/GID. It works with both\n# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/\n# or ~user/.\n#mail_full_filesystem_access = no\n\n# Dictionary for key=value mailbox attributes. This is used for example by\n# URLAUTH and METADATA extensions.\n#mail_attribute_dict =\n\n# A comment or note that is associated with the server. This value is\n# accessible for authenticated users through the IMAP METADATA server\n# entry \"/shared/comment\".\n#mail_server_comment = \"\"\n\n# Indicates a method for contacting the server administrator. According to\n# RFC 5464, this value MUST be a URI (e.g., a mailto: or tel: URL), but that\n# is currently not enforced. Use for example mailto:admin@example.com. This\n# value is accessible for authenticated users through the IMAP METADATA server\n# entry \"/shared/admin\".\n#mail_server_admin =\n\n##\n## Mail processes\n##\n\n# Don't use mmap() at all. This is required if you store indexes to shared\n# filesystems (NFS or clustered filesystem).\n#mmap_disable = no\n\n# Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL\n# since version 3, so this should be safe to use nowadays by default.\n#dotlock_use_excl = yes\n\n# When to use fsync() or fdatasync() calls:\n#   optimized (default): Whenever necessary to avoid losing important data\n#   always: Useful with e.g. NFS when write()s are delayed\n#   never: Never use it (best performance, but crashes can lose data)\n#mail_fsync = optimized\n\n# Locking method for index files. Alternatives are fcntl, flock and dotlock.\n# Dotlocking uses some tricks which may create more disk I/O than other locking\n# methods. NFS users: flock doesn't work, remember to change mmap_disable.\n#lock_method = fcntl\n\n# Directory where mails can be temporarily stored. Usually it's used only for\n# mails larger than >= 128 kB. It's used by various parts of Dovecot, for\n# example LDA/LMTP while delivering large mails or zlib plugin for keeping\n# uncompressed mails.\n#mail_temp_dir = /tmp\n\n# Valid UID range for users, defaults to 500 and above. This is mostly\n# to make sure that users can't log in as daemons or other system users.\n# Note that denying root logins is hardcoded to dovecot binary and can't\n# be done even if first_valid_uid is set to 0.\n#first_valid_uid = 500\n#last_valid_uid = 0\n\n# Valid GID range for users, defaults to non-root/wheel. Users having\n# non-valid GID as primary group ID aren't allowed to log in. If user\n# belongs to supplementary groups with non-valid GIDs, those groups are\n# not set.\n#first_valid_gid = 1\n#last_valid_gid = 0\n\n# Maximum allowed length for mail keyword name. It's only forced when trying\n# to create new keywords.\n#mail_max_keyword_length = 50\n\n# ':' separated list of directories under which chrooting is allowed for mail\n# processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too).\n# This setting doesn't affect login_chroot, mail_chroot or auth chroot\n# settings. If this setting is empty, \"/./\" in home dirs are ignored.\n# WARNING: Never add directories here which local users can modify, that\n# may lead to root exploit. Usually this should be done only if you don't\n# allow shell access for users. <doc/wiki/Chrooting.txt>\n#valid_chroot_dirs =\n\n# Default chroot directory for mail processes. This can be overridden for\n# specific users in user database by giving /./ in user's home directory\n# (eg. /home/./user chroots into /home). Note that usually there is no real\n# need to do chrooting, Dovecot doesn't allow users to access files outside\n# their mail directory anyway. If your home directories are prefixed with\n# the chroot directory, append \"/.\" to mail_chroot. <doc/wiki/Chrooting.txt>\n#mail_chroot =\n\n# UNIX socket path to master authentication server to find users.\n# This is used by imap (for shared users) and lda.\n#auth_socket_path = /var/run/dovecot/auth-userdb\n\n# Directory where to look up mail plugins.\n#mail_plugin_dir = /usr/lib/dovecot/modules\n\n# Space separated list of plugins to load for all services. Plugins specific to\n# IMAP, LDA, etc. are added to this list in their own .conf files.\n#mail_plugins =\n\n##\n## Mailbox handling optimizations\n##\n\n# Mailbox list indexes can be used to optimize IMAP STATUS commands. They are\n# also required for IMAP NOTIFY extension to be enabled.\n#mailbox_list_index = yes\n\n# Trust mailbox list index to be up-to-date. This reduces disk I/O at the cost\n# of potentially returning out-of-date results after e.g. server crashes.\n# The results will be automatically fixed once the folders are opened.\n#mailbox_list_index_very_dirty_syncs = yes\n\n# Should INBOX be kept up-to-date in the mailbox list index? By default it's\n# not, because most of the mailbox accesses will open INBOX anyway.\n#mailbox_list_index_include_inbox = no\n\n# The minimum number of mails in a mailbox before updates are done to cache\n# file. This allows optimizing Dovecot's behavior to do less disk writes at\n# the cost of more disk reads.\n#mail_cache_min_mail_count = 0\n\n# When IDLE command is running, mailbox is checked once in a while to see if\n# there are any new mails or other changes. This setting defines the minimum\n# time to wait between those checks. Dovecot can also use inotify and\n# kqueue to find out immediately when changes occur.\n#mailbox_idle_check_interval = 30 secs\n\n# Save mails with CR+LF instead of plain LF. This makes sending those mails\n# take less CPU, especially with sendfile() syscall with Linux and FreeBSD.\n# But it also creates a bit more disk I/O which may just make it slower.\n# Also note that if other software reads the mboxes/maildirs, they may handle\n# the extra CRs wrong and cause problems.\n#mail_save_crlf = no\n\n# Max number of mails to keep open and prefetch to memory. This only works with\n# some mailbox formats and/or operating systems.\n#mail_prefetch_count = 0\n\n# How often to scan for stale temporary files and delete them (0 = never).\n# These should exist only after Dovecot dies in the middle of saving mails.\n#mail_temp_scan_interval = 1w\n\n# How many slow mail accesses sorting can perform before it returns failure.\n# With IMAP the reply is: NO [LIMIT] Requested sort would have taken too long.\n# The untagged SORT reply is still returned, but it's likely not correct.\n#mail_sort_max_read_count = 0\n\nprotocol !indexer-worker {\n  # If folder vsize calculation requires opening more than this many mails from\n  # disk (i.e. mail sizes aren't in cache already), return failure and finish\n  # the calculation via indexer process. Disabled by default. This setting must\n  # be 0 for indexer-worker processes.\n  #mail_vsize_bg_after_count = 0\n}\n\n##\n## Maildir-specific settings\n##\n\n# By default LIST command returns all entries in maildir beginning with a dot.\n# Enabling this option makes Dovecot return only entries which are directories.\n# This is done by stat()ing each entry, so it causes more disk I/O.\n# (For systems setting struct dirent->d_type, this check is free and it's\n# done always regardless of this setting)\n#maildir_stat_dirs = no\n\n# When copying a message, do it with hard links whenever possible. This makes\n# the performance much better, and it's unlikely to have any side effects.\n#maildir_copy_with_hardlinks = yes\n\n# Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only\n# when its mtime changes unexpectedly or when we can't find the mail otherwise.\n#maildir_very_dirty_syncs = no\n\n# If enabled, Dovecot doesn't use the S=<size> in the Maildir filenames for\n# getting the mail's physical size, except when recalculating Maildir++ quota.\n# This can be useful in systems where a lot of the Maildir filenames have a\n# broken size. The performance hit for enabling this is very small.\n#maildir_broken_filename_sizes = no\n\n# Always move mails from new/ directory to cur/, even when the \\Recent flags\n# aren't being reset.\n#maildir_empty_new = no\n\n##\n## mbox-specific settings\n##\n\n# Which locking methods to use for locking mbox. There are four available:\n#  dotlock: Create <mailbox>.lock file. This is the oldest and most NFS-safe\n#           solution. If you want to use /var/mail/ like directory, the users\n#           will need write access to that directory.\n#  dotlock_try: Same as dotlock, but if it fails because of permissions or\n#               because there isn't enough disk space, just skip it.\n#  fcntl  : Use this if possible. Works with NFS too if lockd is used.\n#  flock  : May not exist in all systems. Doesn't work with NFS.\n#  lockf  : May not exist in all systems. Doesn't work with NFS.\n#\n# You can use multiple locking methods; if you do the order they're declared\n# in is important to avoid deadlocks if other MTAs/MUAs are using multiple\n# locking methods as well. Some operating systems don't allow using some of\n# them simultaneously.\n#\n# The Debian value for mbox_write_locks differs from upstream Dovecot. It is\n# changed to be compliant with Debian Policy (section 11.6) for NFS safety.\n#       Dovecot: mbox_write_locks = dotlock fcntl\n#       Debian:  mbox_write_locks = fcntl dotlock\n#\n#mbox_read_locks = fcntl\n#mbox_write_locks = fcntl dotlock\n\n# Maximum time to wait for lock (all of them) before aborting.\n#mbox_lock_timeout = 5 mins\n\n# If dotlock exists but the mailbox isn't modified in any way, override the\n# lock file after this much time.\n#mbox_dotlock_change_timeout = 2 mins\n\n# When mbox changes unexpectedly we have to fully read it to find out what\n# changed. If the mbox is large this can take a long time. Since the change\n# is usually just a newly appended mail, it'd be faster to simply read the\n# new mails. If this setting is enabled, Dovecot does this but still safely\n# fallbacks to re-reading the whole mbox file whenever something in mbox isn't\n# how it's expected to be. The only real downside to this setting is that if\n# some other MUA changes message flags, Dovecot doesn't notice it immediately.\n# Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK\n# commands.\n#mbox_dirty_syncs = yes\n\n# Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE,\n# EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored.\n#mbox_very_dirty_syncs = no\n\n# Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK\n# commands and when closing the mailbox). This is especially useful for POP3\n# where clients often delete all mails. The downside is that our changes\n# aren't immediately visible to other MUAs.\n#mbox_lazy_writes = yes\n\n# If mbox size is smaller than this (e.g. 100k), don't write index files.\n# If an index file already exists it's still read, just not updated.\n#mbox_min_index_size = 0\n\n# Mail header selection algorithm to use for MD5 POP3 UIDLs when\n# pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired\n# algorithm, but it fails if the first Received: header isn't unique in all\n# mails. An alternative algorithm is \"all\" that selects all headers.\n#mbox_md5 = apop3d\n\n##\n## mdbox-specific settings\n##\n\n# Maximum dbox file size until it's rotated.\n#mdbox_rotate_size = 10M\n\n# Maximum dbox file age until it's rotated. Typically in days. Day begins\n# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled.\n#mdbox_rotate_interval = 0\n\n# When creating new mdbox files, immediately preallocate their size to\n# mdbox_rotate_size. This setting currently works only in Linux with some\n# filesystems (ext4, xfs).\n#mdbox_preallocate_space = no\n\n##\n## Mail attachments\n##\n\n# sdbox and mdbox support saving mail attachments to external files, which\n# also allows single instance storage for them. Other backends don't support\n# this for now.\n\n# Directory root where to store mail attachments. Disabled, if empty.\n#mail_attachment_dir =\n\n# Attachments smaller than this aren't saved externally. It's also possible to\n# write a plugin to disable saving specific attachments externally.\n#mail_attachment_min_size = 128k\n\n# Filesystem backend to use for saving attachments:\n#  posix : No SiS done by Dovecot (but this might help FS's own deduplication)\n#  sis posix : SiS with immediate byte-by-byte comparison during saving\n#  sis-queue posix : SiS with delayed comparison and deduplication\n#mail_attachment_fs = sis posix\n\n# Hash format to use in attachment filenames. You can add any text and\n# variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}.\n# Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits\n#mail_attachment_hash = %{sha1}\n\n# Settings to control adding $HasAttachment or $HasNoAttachment keywords.\n# By default, all MIME parts with Content-Disposition=attachment, or inlines\n# with filename parameter are consired attachments.\n#   add-flags - Add the keywords when saving new mails or when fetching can\n#     do it efficiently.\n#   content-type=type or !type - Include/exclude content type. Excluding will\n#     never consider the matched MIME part as attachment. Including will only\n#     negate an exclusion (e.g. content-type=!foo/* content-type=foo/bar).\n#   exclude-inlined - Exclude any Content-Disposition=inline MIME part.\n#mail_attachment_detection_options =\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-master.conf\"\n\t\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n#default_process_limit = 100\n#default_client_limit = 1000\n\n# Default VSZ (virtual memory size) limit for service processes. This is mainly\n# intended to catch and kill processes that leak memory before they eat up\n# everything.\n#default_vsz_limit = 256M\n\n# Login user is internally used by login processes. This is the most untrusted\n# user in Dovecot system. It shouldn't have access to anything at all.\n#default_login_user = dovenull\n\n# Internal user is used by unprivileged processes. It should be separate from\n# login user, so that login processes can't disturb other processes.\n#default_internal_user = dovecot\n\nservice imap-login {\n  inet_listener imap {\n    #port = 143\n  }\n  inet_listener imaps {\n    #port = 993\n    #ssl = yes\n  }\n\n  # Number of connections to handle before starting a new process. Typically\n  # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0\n  # is faster. <doc/wiki/LoginProcess.txt>\n  #service_count = 1\n\n  # Number of processes to always keep waiting for more connections.\n  #process_min_avail = 0\n\n  # If you set service_count=0, you probably need to grow this.\n  #vsz_limit = $default_vsz_limit\n}\n\nservice pop3-login {\n  inet_listener pop3 {\n    #port = 110\n  }\n  inet_listener pop3s {\n    #port = 995\n    #ssl = yes\n  }\n}\n\nservice submission-login {\n  inet_listener submission {\n    #port = 587\n  }\n}\n\nservice lmtp {\n  unix_listener lmtp {\n    #mode = 0666\n  }\n\n  # Create inet listener only if you can't use the above UNIX socket\n  #inet_listener lmtp {\n    # Avoid making LMTP visible for the entire internet\n    #address =\n    #port =\n  #}\n}\n\nservice imap {\n  # Most of the memory goes to mmap()ing files. You may need to increase this\n  # limit if you have huge mailboxes.\n  #vsz_limit = $default_vsz_limit\n\n  # Max. number of IMAP processes (connections)\n  #process_limit = 1024\n}\n\nservice pop3 {\n  # Max. number of POP3 processes (connections)\n  #process_limit = 1024\n}\n\nservice submission {\n  # Max. number of SMTP Submission processes (connections)\n  #process_limit = 1024\n}\n\nservice auth {\n  # auth_socket_path points to this userdb socket by default. It's typically\n  # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have\n  # full permissions to this socket are able to get a list of all usernames and\n  # get the results of everyone's userdb lookups.\n  #\n  # The default 0666 mode allows anyone to connect to the socket, but the\n  # userdb lookups will succeed only if the userdb returns an \"uid\" field that\n  # matches the caller process's UID. Also if caller's uid or gid matches the\n  # socket's uid or gid the lookup succeeds. Anything else causes a failure.\n  #\n  # To give the caller full permissions to lookup all users, set the mode to\n  # something else than 0666 and Dovecot lets the kernel enforce the\n  # permissions (e.g. 0777 allows everyone full permissions).\n  unix_listener auth-userdb {\n    #mode = 0666\n    #user =\n    #group =\n  }\n\n  # Postfix smtp-auth\n  unix_listener /var/spool/postfix/private/auth {\n    mode = 0660\n    user = postfix\n    group = postfix\n  }\n\n  # Exim4 smtp-auth\n  unix_listener auth-client {\n    mode = 0660\n    user = mail\n    #group = Debian-exim\n  }\n\n  # Auth process is run as this user.\n  #user = $default_internal_user\n}\n\nservice auth-worker {\n  # Auth worker process is run as root by default, so that it can access\n  # /etc/shadow. If this isn't necessary, the user should be changed to\n  # $default_internal_user.\n  #user = root\n}\n\nservice dict {\n  # If dict proxy is used, mail processes should have access to its socket.\n  # For example: mode=0660, group=vmail and global mail_access_groups=vmail\n  unix_listener dict {\n    #mode = 0600\n    #user =\n    #group =\n  }\n}\n\nservice stats {\n  unix_listener stats-reader {\n    group = vmail\n    mode = 0666\n  }\n  unix_listener stats-writer {\n    group = vmail\n    mode = 0666\n  }\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-ssl.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## SSL settings\n##\n\n# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>\nssl = yes\n\n# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before\n# dropping root privileges, so keep the key file unreadable by anyone but\n# root. Included doc/mkcert.sh can be used to easily generate self-signed\n# certificate, just make sure to update the domains in dovecot-openssl.cnf\nssl_cert = <<SSL_CERT_FILE>\nssl_key = <<SSL_KEY_FILE>\n\n# If key file is password protected, give the password here. Alternatively\n# give it when starting dovecot with -p parameter. Since this file is often\n# world-readable, you may want to place this setting instead to a different\n# root owned 0600 file by using ssl_key_password = <path.\n#ssl_key_password =\n\n# PEM encoded trusted certificate authority. Set this only if you intend to use\n# ssl_verify_client_cert=yes. The file should contain the CA certificate(s)\n# followed by the matching CRL(s). (e.g. ssl_ca = </etc/ssl/certs/ca.pem)\n#ssl_ca =\n\n# Require that CRL check succeeds for client certificates.\n#ssl_require_crl = yes\n\n# Directory and/or file for trusted SSL CA certificates. These are used only\n# when Dovecot needs to act as an SSL client (e.g. imapc backend or\n# submission service). The directory is usually /etc/ssl/certs in\n# Debian-based systems and the file is /etc/pki/tls/cert.pem in\n# RedHat-based systems. Note that ssl_client_ca_file isn't recommended with\n# large CA bundles, because it leads to excessive memory usage.\nssl_client_ca_dir = /etc/ssl/certs\n#ssl_client_ca_file =\n\n# Require valid cert when connecting to a remote server\n#ssl_client_require_valid_cert = yes\n\n# Request client to send a certificate. If you also want to require it, set\n# auth_ssl_require_client_cert=yes in auth section.\n#ssl_verify_client_cert = no\n\n# Which field from certificate to use for username. commonName and\n# x500UniqueIdentifier are the usual choices. You'll also need to set\n# auth_ssl_username_from_cert=yes.\n#ssl_cert_username_field = commonName\n\n# SSL DH parameters\n# Generate new params with `openssl dhparam -out /etc/dovecot/dh.pem 4096`\n# Or migrate from old ssl-parameters.dat file with the command dovecot\n# gives on startup when ssl_dh is unset.\nssl_dh = </usr/share/dovecot/dh.pem\n\n# Minimum SSL protocol version to use. Potentially recognized values are SSLv3,\n# TLSv1, TLSv1.1, and TLSv1.2, depending on the OpenSSL version used.\n#ssl_min_protocol = TLSv1\n\n# SSL ciphers to use, the default is:\n#ssl_cipher_list = ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH\n# To disable non-EC DH, use:\n#ssl_cipher_list = ALL:!DH:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH\n\n# Colon separated list of elliptic curves to use. Empty value (the default)\n# means use the defaults from the SSL library. P-521:P-384:P-256 would be an\n# example of a valid value.\n#ssl_curve_list =\n\n# Prefer the server's order of ciphers over client's.\n#ssl_prefer_server_ciphers = no\n\n# SSL crypto device to use, for valid values run \"openssl engine\"\n#ssl_crypto_device =\n\n# SSL extra options. Currently supported options are:\n#   compression - Enable compression.\n#   no_ticket - Disable SSL session tickets.\n#ssl_options =\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/15-lda.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## LDA specific settings (also used by LMTP)\n##\n\n# Address to use when sending rejection mails.\n# Default is postmaster@%d. %d expands to recipient domain.\npostmaster_address = postmaster@<SERVERNAME>\n\n# Hostname to use in various parts of sent mails (e.g. in Message-Id) and\n# in LMTP replies. Default is the system's real hostname@domain.\n#hostname =\n\n# If user is over quota, return with temporary failure instead of\n# bouncing the mail.\n#quota_full_tempfail = no\n\n# Binary to use for sending mails.\n#sendmail_path = /usr/sbin/sendmail\n\n# If non-empty, send mails via this SMTP host[:port] instead of sendmail.\n#submission_host =\n\n# Subject: header to use for rejection mails. You can use the same variables\n# as for rejection_reason below.\n#rejection_subject = Rejected: %s\n\n# Human readable error message for rejection mails. You can use variables:\n#  %n = CRLF, %r = reason, %s = original subject, %t = recipient\n#rejection_reason = Your message to <%t> was automatically rejected:%n%r\n\n# Delimiter character between local-part and detail in email address.\n#recipient_delimiter = +\n\n# Header where the original recipient address (SMTP's RCPT TO: address) is taken\n# from if not available elsewhere. With dovecot-lda -a parameter overrides this.\n# A commonly used header for this is X-Original-To.\n#lda_original_recipient_header =\n\n# Should saving a mail to a nonexistent mailbox automatically create it?\n#lda_mailbox_autocreate = no\n\n# Should automatically created mailboxes be also automatically subscribed?\n#lda_mailbox_autosubscribe = no\n\nprotocol lda {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  mail_plugins = $mail_plugins quota sieve\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-imap.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## IMAP specific settings\n##\n\n# If nothing happens for this long while client is IDLEing, move the connection\n# to imap-hibernate process and close the old imap process. This saves memory,\n# because connections use very little memory in imap-hibernate process. The\n# downside is that recreating the imap process back uses some resources.\n#imap_hibernate_timeout = 0\n\n# Maximum IMAP command line length. Some clients generate very long command\n# lines with huge mailboxes, so you may need to raise this if you get\n# \"Too long argument\" or \"IMAP command line too large\" errors often.\n#imap_max_line_length = 64k\n\n# IMAP logout format string:\n#  %i - total number of bytes read from client\n#  %o - total number of bytes sent to client\n#  %{fetch_hdr_count} - Number of mails with mail header data sent to client\n#  %{fetch_hdr_bytes} - Number of bytes with mail header data sent to client\n#  %{fetch_body_count} - Number of mails with mail body data sent to client\n#  %{fetch_body_bytes} - Number of bytes with mail body data sent to client\n#  %{deleted} - Number of mails where client added \\Deleted flag\n#  %{expunged} - Number of mails that client expunged, which does not\n#                include automatically expunged mails\n#  %{autoexpunged} - Number of mails that were automatically expunged after\n#                    client disconnected\n#  %{trashed} - Number of mails that client copied/moved to the\n#               special_use=\\Trash mailbox.\n#  %{appended} - Number of mails saved during the session\n#imap_logout_format = in=%i out=%o deleted=%{deleted} expunged=%{expunged} \\\n#  trashed=%{trashed} hdr_count=%{fetch_hdr_count} \\\n#  hdr_bytes=%{fetch_hdr_bytes} body_count=%{fetch_body_count} \\\n#  body_bytes=%{fetch_body_bytes}\n\n# Override the IMAP CAPABILITY response. If the value begins with '+',\n# add the given capabilities on top of the defaults (e.g. +XFOO XBAR).\n#imap_capability =\n\n# How long to wait between \"OK Still here\" notifications when client is\n# IDLEing.\n#imap_idle_notify_interval = 2 mins\n\n# ID field names and values to send to clients. Using * as the value makes\n# Dovecot use the default value. The following fields have default values\n# currently: name, version, os, os-version, support-url, support-email,\n# revision\n#imap_id_send =\n\n# ID fields sent by client to log. * means everything.\n#imap_id_log =\n\n# Workarounds for various client bugs:\n#   delay-newmail:\n#     Send EXISTS/RECENT new mail notifications only when replying to NOOP\n#     and CHECK commands. Some clients ignore them otherwise, for example OSX\n#     Mail (<v2.1). Outlook Express breaks more badly though, without this it\n#     may show user \"Message no longer in server\" errors. Note that OE6 still\n#     breaks even with this workaround if synchronization is set to\n#     \"Headers Only\".\n#   tb-extra-mailbox-sep:\n#     Thunderbird gets somehow confused with LAYOUT=fs (mbox and dbox) and\n#     adds extra '/' suffixes to mailbox names. This option causes Dovecot to\n#     ignore the extra '/' instead of treating it as invalid mailbox name.\n#   tb-lsub-flags:\n#     Show \\Noselect flags for LSUB replies with LAYOUT=fs (e.g. mbox).\n#     This makes Thunderbird realize they aren't selectable and show them\n#     greyed out, instead of only later giving \"not selectable\" popup error.\n#\n# The list is space-separated.\n#imap_client_workarounds =\n\n# Host allowed in URLAUTH URLs sent by client. \"*\" allows all.\n#imap_urlauth_host =\n\n# Enable IMAP LITERAL- extension (replaces LITERAL+)\n#imap_literal_minus = no\n\n# What happens when FETCH fails due to some internal error:\n#   disconnect-immediately:\n#     The FETCH is aborted immediately and the IMAP client is disconnected.\n#   disconnect-after:\n#     The FETCH runs for all the requested mails returning as much data as\n#     possible. The client is finally disconnected without a tagged reply.\n#   no-after:\n#     Same as disconnect-after, but tagged NO reply is sent instead of\n#     disconnecting the client. If the client attempts to FETCH the same failed\n#     mail more than once, the client is disconnected. This is to avoid clients\n#     from going into infinite loops trying to FETCH a broken mail.\n#imap_fetch_failure = disconnect-immediately\n\nprotocol imap {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  mail_plugins = $mail_plugins quota imap_quota\n\n  # Maximum number of IMAP connections allowed for a user from each IP address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-managesieve.conf\"\n\t\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## ManageSieve specific settings\n##\n\n# Uncomment to enable managesieve protocol:\n#protocols = $protocols sieve\n\n# Service definitions\n\n#service managesieve-login {\n  #inet_listener sieve {\n  #  port = 4190\n  #}\n\n  #inet_listener sieve_deprecated {\n  #  port = 2000\n  #}\n\n  # Number of connections to handle before starting a new process. Typically\n  # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0\n  # is faster. <doc/wiki/LoginProcess.txt>\n  #service_count = 1\n\n  # Number of processes to always keep waiting for more connections.\n  #process_min_avail = 0\n\n  # If you set service_count=0, you probably need to grow this.\n  #vsz_limit = 64M\n#}\n\n#service managesieve {\n  # Max. number of ManageSieve processes (connections)\n  #process_limit = 1024\n#}\n\n# Service configuration\n\nprotocol sieve {\n  # Maximum ManageSieve command line length in bytes. ManageSieve usually does\n  # not involve overly long command lines, so this setting will not normally\n  # need adjustment\n  #managesieve_max_line_length = 65536\n\n  # Maximum number of ManageSieve connections allowed for a user from each IP\n  # address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n\n  # Space separated list of plugins to load (none known to be useful so far).\n  # Do NOT try to load IMAP plugins here.\n  #mail_plugins =\n\n  # MANAGESIEVE logout format string:\n  #  %i - total number of bytes read from client\n  #  %o - total number of bytes sent to client\n  #  %{put_bytes} - Number of bytes saved using PUTSCRIPT command\n  #  %{put_count} - Number of scripts saved using PUTSCRIPT command\n  #  %{get_bytes} - Number of bytes read using GETCRIPT command\n  #  %{get_count} - Number of scripts read using GETSCRIPT command\n  #  %{get_bytes} - Number of bytes processed using CHECKSCRIPT command\n  #  %{get_count} - Number of scripts checked using CHECKSCRIPT command\n  #  %{deleted_count} - Number of scripts deleted using DELETESCRIPT command\n  #  %{renamed_count} - Number of scripts renamed using RENAMESCRIPT command\n  #managesieve_logout_format = bytes=%i/%o\n\n  # To fool ManageSieve clients that are focused on CMU's timesieved you can\n  # specify the IMPLEMENTATION capability that Dovecot reports to clients.\n  # For example: 'Cyrus timsieved v2.2.13'\n  #managesieve_implementation_string = Dovecot Pigeonhole\n\n  # Explicitly specify the SIEVE and NOTIFY capability reported by the server\n  # before login. If left unassigned these will be reported dynamically\n  # according to what the Sieve interpreter supports by default (after login\n  # this may differ depending on the user).\n  #managesieve_sieve_capability =\n  #managesieve_notify_capability =\n\n  # The maximum number of compile errors that are returned to the client upon\n  # script upload or script verification.\n  #managesieve_max_compile_errors = 5\n\n  # Refer to 90-sieve.conf for script quota configuration and configuration of\n  # Sieve execution limits.\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-pop3.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## POP3 specific settings\n##\n\n# Don't try to set mails non-recent or seen with POP3 sessions. This is\n# mostly intended to reduce disk I/O. With maildir it doesn't move files\n# from new/ to cur/, with mbox it doesn't write Status-header.\n#pop3_no_flag_updates = no\n\n# Support LAST command which exists in old POP3 specs, but has been removed\n# from new ones. Some clients still wish to use this though. Enabling this\n# makes RSET command clear all \\Seen flags from messages.\n#pop3_enable_last = no\n\n# If mail has X-UIDL header, use it as the mail's UIDL.\n#pop3_reuse_xuidl = no\n\n# Allow only one POP3 session to run simultaneously for the same user.\n#pop3_lock_session = no\n\n# POP3 requires message sizes to be listed as if they had CR+LF linefeeds.\n# Many POP3 servers violate this by returning the sizes with LF linefeeds,\n# because it's faster to get. When this setting is enabled, Dovecot still\n# tries to do the right thing first, but if that requires opening the\n# message, it fallbacks to the easier (but incorrect) size.\n#pop3_fast_size_lookups = no\n\n# POP3 UIDL (unique mail identifier) format to use. You can use following\n# variables, along with the variable modifiers described in\n# doc/wiki/Variables.txt (e.g. %Uf for the filename in uppercase)\n#\n#  %v - Mailbox's IMAP UIDVALIDITY\n#  %u - Mail's IMAP UID\n#  %m - MD5 sum of the mailbox headers in hex (mbox only)\n#  %f - filename (maildir only)\n#  %g - Mail's GUID\n#\n# If you want UIDL compatibility with other POP3 servers, use:\n#  UW's ipop3d         : %08Xv%08Xu\n#  Courier             : %f or %v-%u (both might be used simultaneously)\n#  Cyrus (<= 2.1.3)    : %u\n#  Cyrus (>= 2.1.4)    : %v.%u\n#  Dovecot v0.99.x     : %v.%u\n#  tpop3d              : %Mf\n#\n# Note that Outlook 2003 seems to have problems with %v.%u format which was\n# Dovecot's default, so if you're building a new server it would be a good\n# idea to change this. %08Xu%08Xv should be pretty fail-safe.\n#\n#pop3_uidl_format = %08Xu%08Xv\n\n# Permanently save UIDLs sent to POP3 clients, so pop3_uidl_format changes\n# won't change those UIDLs. Currently this works only with Maildir.\n#pop3_save_uidl = no\n\n# What to do about duplicate UIDLs if they exist?\n#   allow: Show duplicates to clients.\n#   rename: Append a temporary -2, -3, etc. counter after the UIDL.\n#pop3_uidl_duplicates = allow\n\n# This option changes POP3 behavior so that it's not possible to actually\n# delete mails via POP3, only hide them from future POP3 sessions. The mails\n# will still be counted towards user's quota until actually deleted via IMAP.\n# Use e.g. \"$POP3Deleted\" as the value (it will be visible as IMAP keyword).\n# Make sure you can legally archive mails before enabling this setting.\n#pop3_deleted_flag =\n\n# POP3 logout format string:\n#  %i - total number of bytes read from client\n#  %o - total number of bytes sent to client\n#  %t - number of TOP commands\n#  %p - number of bytes sent to client as a result of TOP command\n#  %r - number of RETR commands\n#  %b - number of bytes sent to client as a result of RETR command\n#  %d - number of deleted messages\n#  %{deleted_bytes} - number of bytes in deleted messages\n#  %m - number of messages (before deletion)\n#  %s - mailbox size in bytes (before deletion)\n#  %u - old/new UIDL hash. may help finding out if UIDLs changed unexpectedly\npop3_logout_format = in=%i out=%o top=%t/%p, retr=%r/%b, del=%d/%m, size=%s\n\n# Workarounds for various client bugs:\n#   outlook-no-nuls:\n#     Outlook and Outlook Express hang if mails contain NUL characters.\n#     This setting replaces them with 0x80 character.\n#   oe-ns-eoh:\n#     Outlook Express and Netscape Mail breaks if end of headers-line is\n#     missing. This option simply sends it if it's missing.\n# The list is space-separated.\n#pop3_client_workarounds =\n\nprotocol pop3 {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  #mail_plugins = $mail_plugins\n\n  # Maximum number of POP3 connections allowed for a user from each IP address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/90-sieve.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Settings for the Sieve interpreter\n##\n\n# Do not forget to enable the Sieve plugin in 15-lda.conf and 20-lmtp.conf\n# by adding it to the respective mail_plugins= settings.\n\n# The Sieve interpreter can retrieve Sieve scripts from several types of\n# locations. The default `file' location type is a local filesystem path\n# pointing to a Sieve script file or a directory containing multiple Sieve\n# script files. More complex setups can use other location types such as\n# `ldap' or `dict' to fetch Sieve scripts from remote databases.\n#\n# All settings that specify the location of one ore more Sieve scripts accept\n# the following syntax:\n#\n# location = [<type>:]path[;<option>[=<value>][;...]]\n#\n# If the type prefix is omitted, the script location type is 'file' and the\n# location is interpreted as a local filesystem path pointing to a Sieve script\n# file or directory. Refer to Pigeonhole wiki or INSTALL file for more\n# information.\n\nplugin {\n  # The location of the user's main Sieve script or script storage. The LDA\n  # Sieve plugin uses this to find the active script for Sieve filtering at\n  # delivery. The \"include\" extension uses this location for retrieving\n  # :personal\" scripts. This is also where the  ManageSieve service will store\n  # the user's scripts, if supported.\n  #\n  # Currently only the 'file:' location type supports ManageSieve operation.\n  # Other location types like 'dict:' and 'ldap:' can currently only\n  # be used as a read-only script source ().\n  #\n  # For the 'file:' type: use the ';active=' parameter to specify where the\n  # active script symlink is located.\n  # For other types: use the ';name=' parameter to specify the name of the\n  # default/active script.\n  sieve = file:~/sieve;active=~/.dovecot.sieve\n\n  # The default Sieve script when the user has none. This is the location of a\n  # global sieve script file, which gets executed ONLY if user's personal Sieve\n  # script doesn't exist. Be sure to pre-compile this script manually using the\n  # sievec command line tool if the binary is not stored in a global location.\n  # --> See sieve_before for executing scripts before the user's personal\n  #     script.\n  #sieve_default = /var/lib/dovecot/sieve/default.sieve\n  sieve_dir = ~/sieve\n\n  # The name by which the default Sieve script (as configured by the\n  # sieve_default setting) is visible to the user through ManageSieve.\n  #sieve_default_name =\n\n  # Location for \":global\" include scripts as used by the \"include\" extension.\n  #sieve_global =\n\n  # The location of a Sieve script that is run for any message that is about to\n  # be discarded; i.e., it is not delivered anywhere by the normal Sieve\n  # execution. This only happens when the \"implicit keep\" is canceled, by e.g.\n  # the \"discard\" action, and no actions that deliver the message are executed.\n  # This \"discard script\" can prevent discarding the message, by executing\n  # alternative actions. If the discard script does nothing, the message is\n\t# still discarded as it would be when no discard script is configured.\n  #sieve_discard =\n\n  # Location Sieve of scripts that need to be executed before the user's\n  # personal script. If a 'file' location path points to a directory, all the\n  # Sieve scripts contained therein (with the proper `.sieve' extension) are\n  # executed. The order of execution within that directory is determined by the\n  # file names, using a normal 8bit per-character comparison.\n  #\n  # Multiple script locations can be specified by appending an increasing number\n  # to the setting name. The Sieve scripts found from these locations are added\n  # to the script execution sequence in the specified order. Reading the\n  # numbered sieve_before settings stops at the first missing setting, so no\n  # numbers may be skipped.\n  #sieve_before = /var/lib/dovecot/sieve.d/\n  #sieve_before2 = ldap:/etc/sieve-ldap.conf;name=ldap-domain\n  #sieve_before3 = (etc...)\n\n  # Identical to sieve_before, only the specified scripts are executed after the\n  # user's script (only when keep is still in effect!). Multiple script\n  # locations can be specified by appending an increasing number.\n  #sieve_after =\n  #sieve_after2 =\n  #sieve_after2 = (etc...)\n\n  # Which Sieve language extensions are available to users. By default, all\n  # supported extensions are available, except for deprecated extensions or\n  # those that are still under development. Some system administrators may want\n  # to disable certain Sieve extensions or enable those that are not available\n  # by default. This setting can use '+' and '-' to specify differences relative\n  # to the default. For example `sieve_extensions = +imapflags' will enable the\n  # deprecated imapflags extension in addition to all extensions were already\n  # enabled by default.\n  #sieve_extensions = +notify +imapflags\n\n  # Which Sieve language extensions are ONLY available in global scripts. This\n  # can be used to restrict the use of certain Sieve extensions to administrator\n  # control, for instance when these extensions can cause security concerns.\n  # This setting has higher precedence than the `sieve_extensions' setting\n  # (above), meaning that the extensions enabled with this setting are never\n  # available to the user's personal script no matter what is specified for the\n  # `sieve_extensions' setting. The syntax of this setting is similar to the\n  # `sieve_extensions' setting, with the difference that extensions are\n  # enabled or disabled for exclusive use in global scripts. Currently, no\n  # extensions are marked as such by default.\n  #sieve_global_extensions =\n\n  # The Pigeonhole Sieve interpreter can have plugins of its own. Using this\n  # setting, the used plugins can be specified. Check the Dovecot wiki\n  # (wiki2.dovecot.org) or the pigeonhole website\n  # (http://pigeonhole.dovecot.org) for available plugins.\n  # The sieve_extprograms plugin is included in this release.\n  #sieve_plugins =\n\n  # The separator that is expected between the :user and :detail\n  # address parts introduced by the subaddress extension. This may\n  # also be a sequence of characters (e.g. '--'). The current\n  # implementation looks for the separator from the left of the\n  # localpart and uses the first one encountered. The :user part is\n  # left of the separator and the :detail part is right. This setting\n  # is also used by Dovecot's LMTP service.\n  #recipient_delimiter = +\n\n  # The maximum size of a Sieve script. The compiler will refuse to compile any\n  # script larger than this limit. If set to 0, no limit on the script size is\n  # enforced.\n  #sieve_max_script_size = 1M\n\n  # The maximum number of actions that can be performed during a single script\n  # execution. If set to 0, no limit on the total number of actions is enforced.\n  #sieve_max_actions = 32\n\n  # The maximum number of redirect actions that can be performed during a single\n  # script execution. If set to 0, no redirect actions are allowed.\n  #sieve_max_redirects = 4\n\n  # The maximum number of personal Sieve scripts a single user can have. If set\n  # to 0, no limit on the number of scripts is enforced.\n  # (Currently only relevant for ManageSieve)\n  #sieve_quota_max_scripts = 0\n\n  # The maximum amount of disk storage a single user's scripts may occupy. If\n  # set to 0, no limit on the used amount of disk storage is enforced.\n  # (Currently only relevant for ManageSieve)\n  #sieve_quota_max_storage = 0\n\n  # The primary e-mail address for the user. This is used as a default when no\n  # other appropriate address is available for sending messages. If this setting\n  # is not configured, either the postmaster or null \"<>\" address is used as a\n  # sender, depending on the action involved. This setting is important when\n  # there is no message envelope to extract addresses from, such as when the\n  # script is executed in IMAP.\n  #sieve_user_email =\n\n  # The path to the file where the user log is written. If not configured, a\n  # default location is used. If the main user's personal Sieve (as configured\n  # with sieve=) is a file, the logfile is set to <filename>.log by default. If\n  # it is not a file, the default user log file is ~/.dovecot.sieve.log.\n  #sieve_user_log =\n\n  # Specifies what envelope sender address is used for redirected messages.\n  # The following values are supported for this setting:\n  #\n  #   \"sender\"         - The sender address is used (default).\n  #   \"recipient\"      - The final recipient address is used.\n  #   \"orig_recipient\" - The original recipient is used.\n  #   \"user_email\"     - The user's primary address is used. This is\n  #                      configured with the \"sieve_user_email\" setting. If\n  #                      that setting is unconfigured, \"user_mail\" is equal to\n  #                      \"recipient\".\n  #   \"postmaster\"     - The postmaster_address configured for the LDA.\n  #   \"<user@domain>\"  - Redirected messages are always sent from user@domain.\n  #                      The angle brackets are mandatory. The null \"<>\" address\n  #                      is also supported.\n  #\n  # This setting is ignored when the envelope sender is \"<>\". In that case the\n  # sender of the redirected message is also always \"<>\".\n  #sieve_redirect_envelope_from = sender\n\n  ## TRACE DEBUGGING\n  # Trace debugging provides detailed insight in the operations performed by\n  # the Sieve script. These settings apply to both the LDA Sieve plugin and the\n  # IMAPSIEVE plugin.\n  #\n  # WARNING: On a busy server, this functionality can quickly fill up the trace\n  # directory with a lot of trace files. Enable this only temporarily and as\n  # selective as possible.\n\n  # The directory where trace files are written. Trace debugging is disabled if\n  # this setting is not configured or if the directory does not exist. If the\n  # path is relative or it starts with \"~/\" it is interpreted relative to the\n  # current user's home directory.\n  #sieve_trace_dir =\n\n  # The verbosity level of the trace messages. Trace debugging is disabled if\n  # this setting is not configured. Possible values are:\n  #\n  #   \"actions\"        - Only print executed action commands, like keep,\n  #                      fileinto, reject and redirect.\n  #   \"commands\"       - Print any executed command, excluding test commands.\n  #   \"tests\"          - Print all executed commands and performed tests.\n  #   \"matching\"       - Print all executed commands, performed tests and the\n  #                      values matched in those tests.\n  #sieve_trace_level =\n\n  # Enables highly verbose debugging messages that are usually only useful for\n  # developers.\n  #sieve_trace_debug = no\n\n  # Enables showing byte code addresses in the trace output, rather than only\n  # the source line numbers.\n  #sieve_trace_addresses = no\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/90-quota.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\nplugin {\n  quota = maildir:User quota\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[service dovecot restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- Dovecot with postfix -->\n\t\t\t\t<daemon name=\"dovecot_postfix\" version=\"2\"\n\t\t\t\t\ttitle=\"Dovecot with postfix\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='mail']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- Antispam services -->\n\t\t\t<service type=\"antispam\" title=\"Antispam\">\n\t\t\t\t<!-- general RSpamd commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install wget gnupg]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/apt/keyrings]]></command>\n\t\t\t\t\t\t<command><![CDATA[wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | tee /etc/apt/keyrings/rspamd.gpg > /dev/null]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ bullseye main\" > /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb-src [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ bullseye main\" >> /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[apt-get update]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install rspamd]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/local.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/override.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/rspamd/dkim/]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/actions.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# Set rewrite subject to this value (%s is replaced by the original subject)\nsubject = \"***SPAM*** %s\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/arc.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\ntry_fallback = true;\n### Enable DKIM signing for alias sender addresses\nallow_username_mismatch = true;\npath = \"/var/lib/rspamd/dkim/$domain.$selector.key\";\nselector_map = \"/etc/rspamd/dkim_selectors.map\";\nuse_esld = false;\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/milter_headers.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuse = [\"x-spamd-bar\", \"x-spam-level\", \"authentication-results\"];\nauthenticated_headers = [\"authentication-results\"];\nextended_spam_headers = true\nskip_local = false\nskip_authenticated = false\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/replies.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## If a user has replied to an email, don’t mark other emails in the same thread as spam\naction = \"no action\";\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/settings.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## Feel free to include your own settings or adjustments here, for example:\n#whitelist {\n#  priority = low;\n#  rcpt = \"postmaster@example.com\";\n#  want_spam = yes;\n#}\n\n## Include froxlor generated settings\n.include(try=true,priority=1,duplicate=merge) \"{{settings.antispam.config_file}}\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[cp /etc/rspamd/local.d/arc.conf /etc/rspamd/local.d/dkim_signing.conf]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_protocol = 6\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_default_action = accept\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"non_smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R _rspamd:_rspamd /var/lib/rspamd/dkim]]></command>\n\t\t\t\t\t\t<command><![CDATA[service rspamd restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- rspamd -->\n\t\t\t\t<daemon name=\"rspamd\" title=\"Rspamd\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- FTP services -->\n\t\t\t<service type=\"ftp\" title=\"{{lng.admin.configfiles.ftp}}\">\n\t\t\t\t<!-- Proftpd -->\n\t\t\t\t<daemon name=\"proftpd\" title=\"ProFTPd\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install proftpd-basic proftpd-mod-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/proftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/certs/proftpd.crt ] || openssl req -new -x509 -newkey rsa:4096 -days 3650 -nodes -out /etc/ssl/certs/proftpd.crt -keyout /etc/ssl/private/proftpd.key -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\n[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nchmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/proftpd/proftpd.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# /etc/proftpd/proftpd.conf -- This is a basic ProFTPD configuration file.\n# To really apply changes, reload proftpd after modifications, if\n# it runs in daemon mode. It is not required in inetd/xinetd mode.\n#\n\n# Includes DSO modules\nInclude /etc/proftpd/modules.conf\n\n# Set off to disable IPv6 support which is annoying on IPv4 only boxes.\nUseIPv6\t\t\t\ton\n# If set on you can experience a longer connection delay in many cases.\n<IfModule mod_ident.c>\n\tIdentLookups\t\t\t\toff\n</IfModule>\n\nServerName                     \"<SERVERNAME> FTP Server\"\nServerType\t\t\tstandalone\nDeferWelcome\t\t\toff\n\nDefaultServer\t\t\ton\nShowSymlinks\t\t\ton\n\nTimeoutNoTransfer\t\t600\nTimeoutStalled\t\t\t600\nTimeoutIdle\t\t\t1200\n\nDisplayLogin                    welcome.msg\nDisplayChdir               \t.message true\nListOptions                \t\"-l\"\n\nDenyFilter\t\t\t\\*.*/\n\n# Use this to jail all users in their homes\n# DefaultRoot\t\t\t~\n\n# Users require a valid shell listed in /etc/shells to login.\n# Use this directive to release that constrain.\n# RequireValidShell\t\toff\n\n# Port 21 is the standard FTP port.\nPort\t\t\t\t21\n\n# In some cases you have to specify passive ports range to by-pass\n# firewall limitations. Ephemeral ports can be used for that, but\n# feel free to use a more narrow range.\n# PassivePorts                  49152 65534\n\n# If your host was NATted, this option is useful in order to\n# allow passive transfers to work. You have to use your public\n# address and opening the passive ports used on your firewall as well.\n# MasqueradeAddress\t\t1.2.3.4\n\n# This is useful for masquerading address with dynamic IPs:\n# refresh any configured MasqueradeAddress directives every 8 hours\n<IfModule mod_dynmasq.c>\n# DynMasqRefresh 28800\n</IfModule>\n\n# To prevent DoS attacks, set the maximum number of child processes\n# to 30.  If you need to allow more than 30 concurrent connections\n# at once, simply increase this value.  Note that this ONLY works\n# in standalone mode, in inetd mode you should use an inetd server\n# that allows you to limit maximum number of processes per service\n# (such as xinetd)\nMaxInstances\t\t\t30\n\n# Set the user and group that the server normally runs at.\nUser\t\t\t\tproftpd\nGroup\t\t\t\tnogroup\n\n# Umask 022 is a good standard umask to prevent new files and dirs\n# (second parm) from being group and world writable.\nUmask\t\t\t\t022  022\n# Normally, we want files to be overwritable.\nAllowOverwrite\t\t\ton\n\n# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords:\n# PersistentPasswd\t\toff\n\n# This is required to use both PAM-based authentication and local passwords\n# AuthOrder\t\t\tmod_auth_pam.c* mod_auth_unix.c\n\n# Be warned: use of this directive impacts CPU average load!\n# Uncomment this if you like to see progress and transfer rate with ftpwho\n# in downloads. That is not needed for uploads rates.\n#\n# UseSendFile\t\t\toff\n\nTransferLog /var/log/proftpd/xferlog\nSystemLog   /var/log/proftpd/proftpd.log\n\n# Logging onto /var/log/lastlog is enabled but set to off by default\n#UseLastlog on\n\n# In order to keep log file dates consistent after chroot, use timezone info\n# from /etc/localtime.  If this is not set, and proftpd is configured to\n# chroot (e.g. DefaultRoot or <Anonymous>), it will use the non-daylight\n# savings timezone regardless of whether DST is in effect.\n#SetEnv TZ :/etc/localtime\n\n<IfModule mod_quotatab.c>\nQuotaEngine on\n</IfModule>\n\n<IfModule mod_ratio.c>\nRatios off\n</IfModule>\n\n\n# Delay engine reduces impact of the so-called Timing Attack described in\n# http://www.securityfocus.com/bid/11430/discuss\n# It is on by default.\n<IfModule mod_delay.c>\nDelayEngine on\n</IfModule>\n\n<IfModule mod_ctrls.c>\nControlsEngine        off\nControlsMaxClients    2\nControlsLog           /var/log/proftpd/controls.log\nControlsInterval      5\nControlsSocket        /var/run/proftpd/proftpd.sock\n</IfModule>\n\n<IfModule mod_ctrls_admin.c>\nAdminControlsEngine off\n</IfModule>\n\n#\n# Alternative authentication frameworks\n#\n#Include /etc/proftpd/ldap.conf\nInclude /etc/proftpd/sql.conf\n\n#\n# This is used for FTPS connections\n#\nInclude /etc/proftpd/tls.conf\n\n#\n# Useful to keep VirtualHost/VirtualRoot directives separated\n#\n#Include /etc/proftpd/virtuals.conf\n\n# A basic anonymous configuration, no upload directories.\n\n# <Anonymous ~ftp>\n#   User\t\t\t\tftp\n#   Group\t\t\t\tnogroup\n#   # We want clients to be able to login with \"anonymous\" as well as \"ftp\"\n#   UserAlias\t\t\tanonymous ftp\n#   # Cosmetic changes, all files belongs to ftp user\n#   DirFakeUser\ton ftp\n#   DirFakeGroup on ftp\n#\n#   RequireValidShell\t\toff\n#\n#   # Limit the maximum number of anonymous logins\n#   MaxClients\t\t\t10\n#\n#   # We want 'welcome.msg' displayed at login, and '.message' displayed\n#   # in each newly chdired directory.\n#   DisplayLogin\t\t\twelcome.msg\n#   DisplayChdir\t\t.message\n#\n#   # Limit WRITE everywhere in the anonymous chroot\n#   <Directory *>\n#     <Limit WRITE>\n#       DenyAll\n#     </Limit>\n#   </Directory>\n#\n#   # Uncomment this if you're brave.\n#   # <Directory incoming>\n#   #   # Umask 022 is a good standard umask to prevent new files and dirs\n#   #   # (second parm) from being group and world writable.\n#   #   Umask\t\t\t\t022  022\n#   #            <Limit READ WRITE>\n#   #            DenyAll\n#   #            </Limit>\n#   #            <Limit STOR>\n#   #            AllowAll\n#   #            </Limit>\n#   # </Directory>\n#\n# </Anonymous>\n\n# Include other custom configuration files\nInclude /etc/proftpd/conf.d/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/modules.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# This file is used to manage DSO modules and features.\n#\n\n# This is the directory where DSO modules reside\n\nModulePath /usr/lib/proftpd\n\n# Allow only user root to load and unload modules, but allow everyone\n# to see which modules have been loaded\n\nModuleControlsACLs insmod,rmmod allow user root\nModuleControlsACLs lsmod allow user *\n\nLoadModule mod_ctrls_admin.c\nLoadModule mod_tls.c\n\nLoadModule mod_ident.c\n\n# Install one of proftpd-mod-mysql, proftpd-mod-pgsql or any other\n# SQL backend engine to use this module and the required backend.\n# This module must be mandatory loaded before anyone of\n# the existent SQL backeds.\nLoadModule mod_sql.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_ldap.c\n\n#\n# 'SQLBackend mysql' or 'SQLBackend postgres' (or any other valid backend) directives\n# are required to have SQL authorization working. You can also comment out the\n# unused module here, in alternative.\n#\n\n# Install proftpd-mod-mysql and decomment the previous\n# mod_sql.c module to use this.\nLoadModule mod_sql_mysql.c\n\n# Install proftpd-mod-pgsql and decomment the previous\n# mod_sql.c module to use this.\n#LoadModule mod_sql_postgres.c\n\n# Install proftpd-mod-sqlite and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_sqlite.c\n\n# Install proftpd-mod-odbc and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_odbc.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sql_passwd.c\n\nLoadModule mod_radius.c\nLoadModule mod_quotatab.c\nLoadModule mod_quotatab_file.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_quotatab_ldap.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\nLoadModule mod_quotatab_sql.c\nLoadModule mod_quotatab_radius.c\nLoadModule mod_wrap.c\nLoadModule mod_rewrite.c\nLoadModule mod_load.c\nLoadModule mod_ban.c\nLoadModule mod_wrap2.c\nLoadModule mod_wrap2_file.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_wrap2_sql.c\nLoadModule mod_dynmasq.c\nLoadModule mod_exec.c\nLoadModule mod_shaper.c\nLoadModule mod_ratio.c\nLoadModule mod_site_misc.c\n\nLoadModule mod_sftp.c\nLoadModule mod_sftp_pam.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sftp_sql.c\n\nLoadModule mod_facl.c\nLoadModule mod_unique_id.c\nLoadModule mod_copy.c\nLoadModule mod_deflate.c\nLoadModule mod_ifversion.c\nLoadModule mod_tls_memcache.c\n\n# Install proftpd-mod-geoip to use the GeoIP feature\n#LoadModule mod_geoip.c\n\n# keep this module the last one\nLoadModule mod_ifsession.c\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/sql.conf\" chown=\"root:0\" chmod=\"0600\"\n\t\t\t\t\t\tbackup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Proftpd sample configuration for SQL-based authentication.\n#\n# (This is not to be used if you prefer a PAM-based SQL authentication)\n#\n\n<IfModule mod_sql.c>\n\nDefaultRoot ~\nRequireValidShell off\nAuthOrder mod_sql.c\n\n#\n# Choose a SQL backend among MySQL or PostgreSQL.\n# Both modules are loaded in default configuration, so you have to specify the backend\n# or comment out the unused module in /etc/proftpd/modules.conf.\n# Use 'mysql' or 'postgres' as possible values.\n#\nSQLBackend\tmysql\n#\nSQLEngine on\nSQLAuthenticate on\n#\n# Use both an encrypted or plaintext password\nSQLAuthTypes Crypt OpenSSL\n\nSQLAuthenticate users* groups*\n\n#\n# Connection\nSQLConnectInfo <SQL_DB>@<SQL_HOST> <SQL_UNPRIVILEGED_USER> <SQL_UNPRIVILEGED_PASSWORD>\n#\n# Describes both users/groups tables\n#\nSQLUserInfo ftp_users username password uid gid homedir shell\nSQLGroupInfo ftp_groups groupname gid members\n#\nSQLUserWhereClause \"login_enabled = 'y'\"\n\nSQLLog PASS login\nSQLNamedQuery login UPDATE \"last_login=now(), login_count=login_count+1 WHERE username='%u'\" ftp_users\n\nSQLLog RETR download\nSQLNamedQuery download UPDATE \"down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'\" ftp_users\n\nSQLLog STOR upload\nSQLNamedQuery upload UPDATE \"up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'\" ftp_users\n\nQuotaEngine on\nQuotaShowQuotas on\nQuotaDisplayUnits Mb\nQuotaLock /var/lock/ftpd.quotatab.lock\nQuotaLimitTable sql:/get-quota-limit\nQuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally\nSQLNamedQuery get-quota-limit SELECT \"ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'\"\nSQLNamedQuery get-quota-tally SELECT \"name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'\"\nSQLNamedQuery update-quota-tally UPDATE \"bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'\" ftp_quotatallies\nSQLNamedQuery insert-quota-tally INSERT \"%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}\" ftp_quotatallies\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/tls.conf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n<IfModule mod_tls.c>\nTLSEngine                               on\nTLSLog                                  /var/log/proftpd/tls.log\nTLSProtocol                             TLSv1.2 TLSv1.3\nTLSRSACertificateFile                   /etc/ssl/certs/proftpd.crt\nTLSRSACertificateKeyFile                /etc/ssl/private/proftpd.key\nTLSECCertificateFile                    /etc/ssl/certs/proftpd_ec.crt\nTLSECCertificateKeyFile                 /etc/ssl/private/proftpd_ec.key\n# TLSCACertificateFile\nTLSOptions                              NoSessionReuseRequired\nTLSVerifyClient                         off\n\n# Are clients required to use FTP over TLS when talking to this server?\nTLSRequired                             on\n\n# Allow SSL/TLS renegotiations when the client requests them, but\n# do not force the renegotiations.  Some clients do not support\n# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these\n# clients will close the data connection, or there will be a timeout\n# on an idle data connection.\n#\n#TLSRenegotiate                          required off\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/conf.d/99-froxlor-ratelimit.conf\" chown=\"root:0\"\n\t\t\t\t\t\t  chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n<Class whitelist>\nFrom 127.0.0.1\n</Class>\n\nMaxLoginAttempts 3\n<IfModule mod_ban.c>\n <IfClass whitelist>\n  BanEngine off\n </IfClass>\n <IfClass !whitelist>\n  BanEngine on\n </IfClass>\nBanLog /var/log/proftpd/ban.log\nBanTable /etc/proftpd/ban.tab\nBanMessage \"User %u was banned.\"\nBanOnEvent ClientConnectRate 10/00:00:02 02:00:00 \"Stop connecting frequently\"\nBanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00\nBanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99\nBanControlsACLs all allow user root\n</IfModule>\n\n<IfClass whitelist>\nBanEngine off\nDelayEngine off\n</IfClass>\n\t\t\t\t\t]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service proftpd restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Pureftpd -->\n\t\t\t\t<daemon name=\"pureftpd\" title=\"PureFTPd\">\n\t\t\t\t\t<install><![CDATA[apt-get install pure-ftpd-common pure-ftpd-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/private/pure-ftpd.pem ] || openssl req -x509 -nodes -days 7300 -newkey rsa:4096 -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nopenssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072\nchmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/TLS\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MinUID\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1000\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MySQLConfigFile\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n/etc/pure-ftpd/db/mysql.conf\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/NoAnonymous\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MaxIdleTime\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n15\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/ChrootEveryone\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/PAMAuthentication\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nno\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/db/mysql.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0640\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n##############################################\n#                                            #\n# Sample Pure-FTPd Mysql configuration file. #\n# See README.MySQL for explanations.         #\n#                                            #\n##############################################\n\n\n# Optional : MySQL server name or IP. Don't define this for unix sockets.\n\n# MYSQLServer     127.0.0.1\n\n\n# Optional : MySQL port. Don't define this if a local unix socket is used.\n\n# MYSQLPort       3306\n\n\n# Optional : define the location of mysql.sock if the server runs on this host.\n\nMYSQLSocket      /var/run/mysqld/mysqld.sock\n\n\n# Mandatory : user to bind the server as.\n\nMYSQLUser       <SQL_UNPRIVILEGED_USER>\n\n\n# Mandatory : user password. You must have a password.\n\nMYSQLPassword   <SQL_UNPRIVILEGED_PASSWORD>\n\n\n# Mandatory : database to open.\n\nMYSQLDatabase   <SQL_DB>\n\n\n# Mandatory : how passwords are stored\n# Valid values are : \"cleartext\", \"crypt\", \"sha1\", \"md5\" and \"password\"\n# (\"password\" = MySQL password() function)\n# You can also use \"any\" to try \"crypt\", \"sha1\", \"md5\" *and* \"password\"\n\nMYSQLCrypt      any\n\n\n# In the following directives, parts of the strings are replaced at\n# run-time before performing queries :\n#\n# \\L is replaced by the login of the user trying to authenticate.\n# \\I is replaced by the IP address the user connected to.\n# \\P is replaced by the port number the user connected to.\n# \\R is replaced by the IP address the user connected from.\n# \\D is replaced by the remote IP address, as a long decimal number.\n#\n# Very complex queries can be performed using these substitution strings,\n# especially for virtual hosting.\n\n\n# Query to execute in order to fetch the password\n\nMYSQLGetPW      SELECT password FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Query to execute in order to fetch the system user name or uid\n\nMYSQLGetUID     SELECT uid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default UID - if set this overrides MYSQLGetUID\n\n#MYSQLDefaultUID 1000\n\n\n# Query to execute in order to fetch the system user group or gid\n\nMYSQLGetGID     SELECT gid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default GID - if set this overrides MYSQLGetGID\n\n#MYSQLDefaultGID 1000\n\n\n# Query to execute in order to fetch the home directory\n\nMYSQLGetDir     SELECT homedir FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : query to get the maximal number of files\n# Pure-FTPd must have been compiled with virtual quotas support.\n\n# MySQLGetQTAFS  SELECT QuotaFiles FROM users WHERE User='\\L'\n\n\n# Optional : query to get the maximal disk usage (virtual quotas)\n# The number should be in Megabytes.\n# Pure-FTPd must have been compiled with virtual quotas support.\n\nMySQLGetQTASZ SELECT CASE WHEN panel_customers.diskspace = 0 THEN -1 WHEN panel_customers.diskspace <= -1 THEN 0 ELSE panel_customers.diskspace/1024 END AS QuotaSize FROM panel_customers, ftp_users WHERE username = \"\\L\" AND panel_customers.loginname = SUBSTRING_INDEX('\\L', 'ftp', 1)\n\n\n# Optional : ratios. The server has to be compiled with ratio support.\n\n# MySQLGetRatioUL SELECT ULRatio FROM users WHERE User='\\L'\n# MySQLGetRatioDL SELECT DLRatio FROM users WHERE User='\\L'\n\n\n# Optional : bandwidth throttling.\n# The server has to be compiled with throttling support.\n# Values are in KB/s .\n\n# MySQLGetBandwidthUL SELECT ULBandwidth FROM users WHERE User='\\L'\n# MySQLGetBandwidthDL SELECT DLBandwidth FROM users WHERE User='\\L'\n\n# Enable ~ expansion. NEVER ENABLE THIS BLINDLY UNLESS :\n# 1) You know what you are doing.\n# 2) Real and virtual users match.\n\n# MySQLForceTildeExpansion 1\n\n\n# If you're using a transactionnal storage engine, you can enable SQL\n# transactions to avoid races. Leave this commented if you are using the\n# traditional MyIsam engine.\n\n# MySQLTransactions On\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/CustomerProof\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/Bind\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n21\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/default/pure-ftpd-common\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Configuration for pure-ftpd\n# (this file is sourced by /bin/sh, edit accordingly)\n\n# STANDALONE_OR_INETD\n# valid values are \"standalone\" and \"inetd\".\n# Any change here overrides the setting in debconf.\nSTANDALONE_OR_INETD=standalone\n\n# VIRTUALCHROOT:\n# whether to use binary with virtualchroot support\n# valid values are \"true\" or \"false\"\n# Any change here overrides the setting in debconf.\nVIRTUALCHROOT=false\n\n# UPLOADSCRIPT: if this is set and the daemon is run in standalone mode,\n# pure-uploadscript will also be run to spawn the program given below\n# for handling uploads. see /usr/share/doc/pure-ftpd/README.gz or\n# pure-uploadscript(8)\n\n# example: UPLOADSCRIPT=/usr/local/sbin/uploadhandler.pl\nUPLOADSCRIPT=\n\n# if set, pure-uploadscript will spawn  running as the\n# given uid and gid\nUPLOADUID=\nUPLOADGID=\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pure-ftpd-mysql restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- System tools/services -->\n\t\t\t<service type=\"system\" title=\"{{lng.admin.configfiles.etc}}\">\n\t\t\t\t<!-- Webalizer -->\n\t\t\t\t<daemon name=\"webalizer\"\n\t\t\t\t\ttitle=\"Webalizer (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install webalizer]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- goaccess -->\n\t\t\t\t<daemon name=\"goaccess\"\n\t\t\t\t\ttitle=\"goaccess (traffic analyzer)\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install goaccess jq]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- AWstats -->\n\t\t\t\t<daemon name=\"awstats\"\n\t\t\t\t\ttitle=\"Awstats  (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install awstats]]></install>\n\t\t\t\t\t<command><![CDATA[mv {{settings.system.awstats_conf}}/awstats.conf {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's/^DirData/# DirData/' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's|^\\\\(DirIcons=\\\\).*$|\\\\1\\\\\"/awstats-icon\\\\\"|' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/cron.d/awstats]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/logrotate.d/httpd-prerotate/awstats]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- libnss-extrausers -->\n\t\t\t\t<daemon name=\"libnssextrausers\"\n\t\t\t\t\ttitle=\"libnss-extrausers\">\n\t\t\t\t\t<install><![CDATA[apt-get install libnss-extrausers]]></install>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/extrausers]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/passwd]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/group]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/shadow]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/nsswitch.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Make sure that `passwd`, `group` and `shadow` have extrausers in their lines\n# You should place extrausers at the end, so that it is queried after the other mechanisams\n#\npasswd:         compat extrausers\ngroup:          compat extrausers\nshadow:         compat extrausers\n\nhosts:       files dns\nnetworks:    files dns\n\nservices:    db files\nprotocols:   db files\nrpc:         db files\nethers:      db files\nnetmasks:    files\nnetgroup:    files\nbootparams:  files\n\nautomount:   files\naliases:     files\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Logrotate -->\n\t\t\t\t<daemon name=\"logrotate\" title=\"Logrotate\">\n\t\t\t\t\t<install><![CDATA[apt-get install logrotate]]></install>\n\t\t\t\t\t<file name=\"/etc/logrotate.d/froxlor\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Froxlor logrotate snippet\n#\n<CUSTOMER_LOGS>*.log {\n  missingok\n  daily\n  rotate 7\n  compress\n  delaycompress\n  notifempty\n  create\n  sharedscripts\n  postrotate\n  <WEBSERVER_RELOAD_CMD> > /dev/null 2>&1 || true\n  endscript\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- FCGID -->\n\t\t\t\t<daemon name=\"fcgid\" title=\"FCGID\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2-suexec-pristine libapache2-mod-fcgid php-cgi]]></install>\n\t\t\t\t\t<command><![CDATA[a2enmod suexec fcgid]]></command>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.mod_fcgid_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.system.mod_fcgid_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.system.mod_fcgid_httpgroup}} {{settings.system.mod_fcgid_httpuser}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_configdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 1777 {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php7.4]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- PHP-FPM -->\n\t\t\t\t<daemon name=\"php-fpm\"\n\t\t\t\t\ttitle=\"PHP-FPM\">\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install apache2-suexec-pristine]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<install><![CDATA[apt-get install php-fpm]]></install>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2enmod suexec proxy_fcgi actions]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"usernamenotexists\">{{settings.phpfpm.vhost_httpuser}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.phpfpm.vhost_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.phpfpm.vhost_httpgroup}} {{settings.phpfpm.vhost_httpuser}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"4\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php7.4]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Cronjob -->\n\t\t\t\t<daemon name=\"cron\" title=\"Cronjob for froxlor\"\n\t\t\t\t\tmandatory=\"true\">\n\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install cron gnupg]]></install>\n\t\t\t\t\t<command><![CDATA[[ ! -e /usr/local/bin/froxlor-cli ] && ln -s <BASE_PATH>bin/froxlor-cli /usr/local/bin/froxlor-cli]]></command>\n\t\t\t\t\t<command><![CDATA[/usr/bin/php <BASE_PATH>bin/froxlor-cli froxlor:cron --run-task 99]]></command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.crondreload}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t</services>\n\t</distribution>\n</froxlor>\n"
  },
  {
    "path": "lib/configfiles/focal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<froxlor>\n\t<distribution name=\"Ubuntu\" codename=\"Focal\"\n\t\tversion=\"20.04\" defaulteditor=\"/bin/nano\" deprecated=\"true\">\n\t\t<!-- OS defaults to be loaded on installation -->\n\t\t<defaults>\n\t\t\t<default settinggroup=\"system\" varname=\"nssextrausers\" value=\"1\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_vhost\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_diroptions\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_htpasswddir\" value=\"/etc/nginx/froxlor-htpasswd/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apachereload_command\" value=\"service nginx reload\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"letsencryptacmeconf\" value=\"/etc/nginx/acme.conf\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"phpfpm\" varname=\"fastcgi_ipcdir\" value=\"/var/run/php/\"></default>\n\t\t</defaults>\n\t\t<services>\n\t\t\t<!-- HTTP -->\n\t\t\t<service type=\"http\" title=\"{{lng.admin.configfiles.http}}\">\n\t\t\t\t<!-- general HTTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.documentroot_prefix}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.logfiles_directory}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"notempty\">{{settings.system.deactivateddocroot}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.deactivateddocroot}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- HTTP Apache -->\n\t\t\t\t<daemon name=\"apache\" version=\"2.4\" title=\"Apache 2.4\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2]]></install>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command><![CDATA[a2dismod userdir]]></command>\n\t\t\t\t\t<command><![CDATA[a2enmod headers]]></command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.use_ssl}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[a2enmod ssl]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n# Please remember to activate the use of mod_proxy / mod_proxy_fcgi in the PHP-FPM settings!!!\na2enmod proxy_fcgi\n]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nAlias \"/.well-known/acme-challenge\" \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\"\n<Directory \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\">\n\tRequire all granted\n</Directory>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- HTTP Nginx -->\n\t\t\t\t<daemon name=\"nginx\" title=\"nginx\">\n\t\t\t\t\t<install><![CDATA[apt-get install nginx]]></install>\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install php-cgi]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<file name=\"/etc/nginx/nginx.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nuser www-data;\nworker_processes auto;\npid /var/run/nginx.pid;\ninclude /etc/nginx/modules-enabled/*.conf;\n\nevents {\n\tworker_connections 768;\n\t# multi_accept on;\n}\n\nhttp {\n\n\t##\n\t# Basic Settings\n\t##\n\n\tsendfile on;\n\ttcp_nopush on;\n\ttcp_nodelay on;\n\tkeepalive_timeout 65;\n\ttypes_hash_max_size 2048;\n\t# server_tokens off;\n\n\t# server_names_hash_bucket_size 64;\n\t# server_name_in_redirect off;\n\n\tinclude /etc/nginx/mime.types;\n\tdefault_type application/octet-stream;\n\n\t##\n\t# Logging Settings\n\t##\n\n\taccess_log /var/log/nginx/access.log;\n\terror_log /var/log/nginx/error.log;\n\n\t##\n\t# Gzip Settings\n\t##\n\n\tgzip on;\n\tgzip_disable \"msie6\";\n\n\t# gzip_vary on;\n\t# gzip_proxied any;\n\t# gzip_comp_level 6;\n\t# gzip_buffers 16 8k;\n\t# gzip_http_version 1.1;\n\t# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;\n\n\t##\n\t# nginx-naxsi config\n\t##\n\t# Uncomment it if you installed nginx-naxsi\n\t##\n\n\t#include /etc/nginx/naxsi_core.rules;\n\n\t##\n\t# nginx-passenger config\n\t##\n\t# Uncomment it if you installed nginx-passenger\n\t##\n\n\t#passenger_root /usr;\n\t#passenger_ruby /usr/bin/ruby;\n\n\t##\n\t# Virtual Host Configs\n\t##\n\n\tinclude /etc/nginx/conf.d/*.conf;\n\tinclude /etc/nginx/sites-enabled/*;\n}\n\n\n#mail {\n#\t# See sample authentication script at:\n#\t# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript\n#\n#\t# auth_http localhost/auth.php;\n#\t# pop3_capabilities \"TOP\" \"USER\";\n#\t# imap_capabilities \"IMAP4rev1\" \"UIDPLUS\";\n#\n#\tserver {\n#\t\tlisten     localhost:110;\n#\t\tprotocol   pop3;\n#\t\tproxy      on;\n#\t}\n#\n#\tserver {\n#\t\tlisten     localhost:143;\n#\t\tprotocol   imap;\n#\t\tproxy      on;\n#\t}\n#}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/nginx/fastcgi_params\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nfastcgi_connect_timeout 65;\nfastcgi_send_timeout    180;\nfastcgi_read_timeout    180;\n\nfastcgi_param  QUERY_STRING       $query_string;\nfastcgi_param  REQUEST_METHOD     $request_method;\nfastcgi_param  CONTENT_TYPE       $content_type;\nfastcgi_param  CONTENT_LENGTH     $content_length;\n\nfastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\nfastcgi_param  REQUEST_URI        $request_uri;\nfastcgi_param  DOCUMENT_URI       $document_uri;\nfastcgi_param  DOCUMENT_ROOT      $document_root;\nfastcgi_param  SERVER_PROTOCOL    $server_protocol;\nfastcgi_param  HTTPS              $https if_not_empty;\n\nfastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\nfastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\nfastcgi_param  REMOTE_ADDR        $remote_addr;\nfastcgi_param  REMOTE_PORT        $remote_port;\nfastcgi_param  SERVER_ADDR        $server_addr;\nfastcgi_param  SERVER_PORT        $server_port;\nfastcgi_param  SERVER_NAME        $server_name;\n\n# PHP only, required if PHP was built with --enable-force-cgi-redirect\nfastcgi_param  REDIRECT_STATUS    200;\n\n# Fix for HTTP/3 ($http_host is not populated automatically)\nfastcgi_param  HTTP_HOST          $host;\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nlocation /.well-known/acme-challenge {\n\talias {{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge;\n\n\tlocation ~ /.well-known/acme-challenge/(.*) {\n\t\tdefault_type text/plain;\n\t}\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/init.d/php-fcgi\" backup=\"true\" chmod=\"u+x\">\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n#!/bin/bash\nBIND=\"127.0.0.1:8888\"\nUSER=\"www-data\"\nPHP_FCGI_CHILDREN=\"15\"\nPHP_FCGI_MAX_REQUESTS=\"1000\"\n\nPHP_CGI=\"/usr/bin/php-cgi\"\nPHP_CGI_NAME=\"$(basename ${PHP_CGI})\"\nPHP_CGI_ARGS=\"- USER=${USER} PATH=/usr/bin PHP_FCGI_CHILDREN=${PHP_FCGI_CHILDREN} PHP_FCGI_MAX_REQUESTS=${PHP_FCGI_MAX_REQUESTS} ${PHP_CGI} -b ${BIND}\"\nRETVAL=\"0\"\n\nstart() {\n      echo -n \"Starting PHP FastCGI: \"\n      start-stop-daemon --quiet --start --background --chuid \"$USER\" --exec /usr/bin/env -- $PHP_CGI_ARGS\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\nstop() {\n      echo -n \"Stopping PHP FastCGI: \"\n      killall -q -w -u ${USER} ${PHP_CGI}\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\n\ncase \"$1\" in\n    start)\n      start\n  ;;\n    stop)\n      stop\n  ;;\n    restart)\n      stop\n      start\n  ;;\n    *)\n      echo \"Usage: php-fastcgi {start|stop|restart}\"\n      exit 1\n  ;;\nesac\nexit \"$RETVAL\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[/etc/init.d/php-fcgi restart]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!--DNS -->\n\t\t\t<service type=\"dns\" title=\"{{lng.admin.configfiles.dns}}\">\n\t\t\t\t<!--Bind9 -->\n\t\t\t\t<daemon name=\"bind\" title=\"Bind9 nameserver\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install bind9]]></install>\n\t\t\t\t\t<command><![CDATA[echo \"include \\\"{{settings.system.bindconf_directory}}froxlor_bind.conf\\\";\" >> /etc/bind/named.conf.local]]></command>\n\t\t\t\t\t<command><![CDATA[touch {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chown bind:0 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chmod 0644 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[service bind9 restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns\" title=\"PowerDNS (standalone)\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server pdns-backend-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\nallow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# allow-recursion\tList of subnets that are allowed to recurse\n#\nallow-recursion=127.0.0.1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# recursive-cache-ttl\tSeconds to store packets for recursive queries in the PacketCache\n#\n# recursive-cache-ttl=10\n\n#################################\n# recursor\tIf recursion is desired, IP address of a recursing nameserver\n#\n# recursor=no\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# mysql-settings / you need to create the power-dns database for yourself!\nlaunch=gmysql\ngmysql-host=127.0.0.1\ngmysql-port=3306\ngmysql-dbname=pdns\ngmysql-user=powerdns\ngmysql-group=client\ngmysql-password=\n#gmysql-ssl-ca-file=\n#gmysql-ssl-verify-server-certificate=0\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns_bind\"\n\t\t\t\t\ttitle=\"PowerDNS via bind-backend\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\n# allow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# allow-recursion\tList of subnets that are allowed to recurse\n#\nallow-recursion=127.0.0.1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\nlaunch=bind\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# recursive-cache-ttl\tSeconds to store packets for recursive queries in the PacketCache\n#\n# recursive-cache-ttl=10\n\n#################################\n# recursor\tIf recursion is desired, IP address of a recursing nameserver\n#\n# recursor=no\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# Bind backend configuration\n\n# Location of the Bind configuration file to parse.\nbind-config=<BIND_CONFIG_PATH>named.conf\n\n# How often to check for zone changes. See 'Operation' section.\nbind-check-interval=180\n\n# Uncomment to enable Huffman compression on zone data.\n# Currently saves around 20% of memory actually used, but slows down operation.\n# bind-enable-huffman\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- SMTP services -->\n\t\t\t<service type=\"smtp\" title=\"{{lng.admin.configfiles.smtp}}\">\n\t\t\t\t<!-- general SMTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"groupnotexists\">{{settings.system.vmail_gid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[groupadd -g {{settings.system.vmail_gid}} vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"usernotexists\">{{settings.system.vmail_uid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[useradd -u {{settings.system.vmail_uid}} -g vmail vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install postfix postfix-mysql]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/etc/pam.d]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/var/run/mysqld]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R {{settings.system.vmail_uid}}:{{settings.system.vmail_gid}} {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0750  {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"0\">\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_alias_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT destination FROM mail_virtual AS v, panel_customers AS c WHERE c.customerid = v.customerid AND c.deactivated = 0 AND v.email = '%s' AND trim(v.destination) <> ''\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_domains.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' AND deactivated = 0\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_sender_permissions.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT GROUP_CONCAT(DISTINCT mu.username SEPARATOR ' ') AS sasl_users FROM mail_users mu WHERE mu.username = '%s' OR mu.email IN ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = CONCAT('@', SUBSTRING_INDEX('%s','@',-1))));\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_uid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT uid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_gid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT gid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/aliases\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# /etc/aliases\nmailer-daemon: postmaster\npostmaster: root\nnobody: root\nhostmaster: root\nusenet: root\nnews: root\nwebmaster: root\nwww: root\nftp: root\nabuse: root\nnoc: root\nsecurity: root\n\n# change this to a valid e-mail address you can access\nroot:               <ADMIN_MAIL>\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[newaliases]]></command>\n\t\t\t\t\t\t<command><![CDATA[service postfix restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- postfix with dovecot -->\n\t\t\t\t<daemon name=\"postfix_dovecot\" title=\"Postfix with dovecot\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<file name=\"/etc/postfix/main.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# See /usr/share/postfix/main.cf.dist for a commented, more complete version\n\n\n# Debian specific:  Specifying a file name will cause the first\n# line of that file to be used as the name.  The Debian default\n# is /etc/mailname.\n#myorigin = /etc/mailname\n\nsmtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)\nbiff = no\n\n# appending .domain is the MUA's job.\nappend_dot_mydomain = no\n\n# Uncomment the next line to generate \"delayed mail\" warnings\n#delay_warning_time = 4h\n\nreadme_directory = no\n\n# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on\n# fresh installs.\ncompatibility_level = 2\n\n# INTERNET HOST AND DOMAIN NAMES\n#\n# The myhostname parameter specifies the internet hostname of this\n# mail system. The default is to use the fully-qualified domain name\n# from gethostname(). $myhostname is used as a default value for many\n# other configuration parameters.\n#\n# Froxlor Note: $myhostname can and should be the same as $mydomain as long as\n# you don't intend to send mail to it (it will be considered local, not virtual)\n# for the case of a subdomain, $mydomain *must* be equal to $myhostname,\n# otherwise you cannot use the main domain for virtual transport.\n# also check the note about $mydomain below.\nmyhostname = $mydomain\n#myhostname = virtual.domain.tld\n\n# The mydomain parameter specifies the local internet domain name.\n# The default is to use $myhostname minus the first component.\n# $mydomain is used as a default value for many other configuration\n# parameters.\n#\n# Froxlor Note: We are using a default here but that may or may not make sense,\n# depending on your dns configuration, please check yourself.\n\n# FQDN from Froxlor\nmydomain = <SERVERNAME>\n\nmydestination = $myhostname, localhost.$mydomain, localhost\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain,\n#\tmail.$mydomain, www.$mydomain, ftp.$mydomain\n\n# The default setting is 550 (reject mail) but it is safer to start\n# with 450 (try again later) until you are certain that your\n# local_recipient_maps settings are OK.\n#\nunknown_local_recipient_reject_code = 550\n\n# The mailbox_command parameter specifies the optional external\n# command to use instead of mailbox delivery. The command is run as\n# the recipient with proper HOME, SHELL and LOGNAME environment settings.\n# Exception:  delivery for root is done as $default_user.\n#\n# Other environment variables of interest: USER (recipient username),\n# EXTENSION (address extension), DOMAIN (domain part of address),\n# and LOCAL (the address localpart).\n#\n# Unlike other Postfix configuration parameters, the mailbox_command\n# parameter is not subjected to $parameter substitutions. This is to\n# make it easier to specify shell syntax (see example below).\n#\n# Avoid shell meta characters because they will force Postfix to run\n# an expensive shell process. Procmail alone is expensive enough.\n#\n# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN\n# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER.\n#\nmailbox_command = /usr/lib/dovecot/deliver\n#mailbox_command = /usr/bin/procmail -a \"$EXTENSION\"\n\n# The debugger_command specifies the external command that is executed\n# when a Postfix daemon program is run with the -D option.\n#\n# Use \"command .. & sleep 5\" so that the debugger can attach before\n# the process marches on. If you use an X-based debugger, be sure to\n# set up your XAUTHORITY environment variable before starting Postfix.\n#\ndebugger_command =\n\t PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin\n\t ddd $daemon_directory/$process_name $process_id & sleep 5\n\ninet_protocols = ipv4\n\nsmtpd_helo_required = yes\nsmtpd_recipient_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unauth_destination,\n\treject_unauth_pipelining,\n\treject_non_fqdn_recipient\nsmtpd_sender_restrictions = permit_mynetworks,\n\treject_sender_login_mismatch,\n\tpermit_sasl_authenticated,\n\treject_unknown_helo_hostname,\n\treject_unknown_recipient_domain,\n\treject_unknown_sender_domain\nsmtpd_client_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unknown_client_hostname\n\n# Postfix 2.10 requires this option. Postfix < 2.10 ignores this.\n# The option is intentionally left empty.\nsmtpd_relay_restrictions =\n\n# Maximum size of Message in bytes (50MB)\nmessage_size_limit = 52428800\n\n## SASL Auth Settings\nsmtpd_sasl_auth_enable = yes\nsmtpd_sasl_local_domain = $myhostname\nbroken_sasl_auth_clients = yes\n## Dovecot Settings for deliver, SASL Auth and virtual transport\nsmtpd_sasl_type = dovecot\nvirtual_transport = dovecot\ndovecot_destination_recipient_limit = 1\nsmtpd_sasl_path = private/auth\n\n# Virtual delivery settings\nvirtual_mailbox_base = /\nvirtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf\nvirtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf\nvirtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_alias_maps.cf\nsmtpd_sender_login_maps = proxy:mysql:/etc/postfix/mysql-virtual_sender_permissions.cf\nvirtual_uid_maps = static:<VIRTUAL_UID_MAPS>\nvirtual_gid_maps = static:<VIRTUAL_GID_MAPS>\n\n# Local delivery settings\nlocal_transport = local\nalias_maps = $alias_database\n\n# Default Mailbox size, is set to 0 which means unlimited!\nmailbox_size_limit = 0\nvirtual_mailbox_limit = 0\n\n### TLS settings\n###\n## TLS for outgoing mails from the server to another server\nsmtp_tls_security_level = may\nsmtp_tls_note_starttls_offer = yes\n## TLS for incoming connections (clients or other mail servers)\nsmtpd_tls_security_level = may\nsmtpd_tls_cert_file = <SSL_CERT_FILE>\nsmtpd_tls_key_file = <SSL_KEY_FILE>\n#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt\nsmtpd_tls_loglevel = 1\nsmtpd_tls_received_header = yes\nsmtp_use_tls = yes\nsmtpd_use_tls = yes\nsmtpd_tls_session_cache_timeout = 3600s\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/files[@index=0]</include>\n\t\t\t\t\t<file name=\"/etc/postfix/master.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Postfix master process configuration file.  For details on the format\n# of the file, see the master(5) manual page (command: \"man 5 master\" or\n# on-line: http://www.postfix.org/master.5.html).\n#\n# Do not forget to execute \"postfix reload\" after editing this file.\n#\n# ==========================================================================\n# service type  private unpriv  chroot  wakeup  maxproc command + args\n#               (yes)   (yes)   (no)    (never) (100)\n# ==========================================================================\n#smtp      inet  n       -       y       -       -       smtpd\nsmtp      inet  n       -       y       -       1       postscreen\nsmtpd     pass  -       -       y       -       -       smtpd\ndnsblog   unix  -       -       y       -       0       dnsblog\ntlsproxy  unix  -       -       y       -       0       tlsproxy\nsubmission inet n       -       y       -       -       smtpd\n  -o syslog_name=postfix/submission\n  -o smtpd_tls_security_level=encrypt\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\nsmtps     inet  n       -       y       -       -       smtpd\n  -o syslog_name=postfix/smtps\n  -o smtpd_tls_wrappermode=yes\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\n#628       inet  n       -       y       -       -       qmqpd\npickup    unix  n       -       y       60      1       pickup\n         -o content_filter=\n         -o receive_override_options=no_header_body_checks\ncleanup   unix  n       -       n       -       0       cleanup\nqmgr      unix  n       -       n       300     1       qmgr\n#qmgr     unix  n       -       n       300     1       oqmgr\ntlsmgr    unix  -       -       n       1000?   1       tlsmgr\nrewrite   unix  -       -       n       -       -       trivial-rewrite\nbounce    unix  -       -       n       -       0       bounce\ndefer     unix  -       -       n       -       0       bounce\ntrace     unix  -       -       n       -       0       bounce\nverify    unix  -       -       n       -       1       verify\nflush     unix  n       -       n       1000?   0       flush\nproxymap  unix  -       -       n       -       -       proxymap\nproxywrite unix -       -       n       -       1       proxymap\nsmtp      unix  -       -       n       -       -       smtp\nrelay     unix  -       -       n       -       -       smtp\n        -o syslog_name=postfix/$service_name\n#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5\nshowq     unix  n       -       n       -       -       showq\nerror     unix  -       -       n       -       -       error\nretry     unix  -       -       n       -       -       error\ndiscard   unix  -       -       n       -       -       discard\nlocal     unix  -       n       n       -       -       local\nvirtual   unix  -       n       n       -       -       virtual\nlmtp      unix  -       -       n       -       -       lmtp\nanvil     unix  -       -       n       -       1       anvil\nscache    unix  -       -       n       -       1       scache\npostlog   unix-dgram n  -       n       -       1       postlogd\n#\n# ====================================================================\n# Interfaces to non-Postfix software. Be sure to examine the manual\n# pages of the non-Postfix software to find out what options it wants.\n#\n# Many of the following services use the Postfix pipe(8) delivery\n# agent.  See the pipe(8) man page for information about ${recipient}\n# and other message envelope options.\n# ====================================================================\n#\n# maildrop. See the Postfix MAILDROP_README file for details.\n# Also specify in main.cf: maildrop_destination_recipient_limit=1\n#\n#maildrop  unix  -       n       n       -       -       pipe\n#  flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}\n#\n# ====================================================================\n#\n# Recent Cyrus versions can use the existing \"lmtp\" master.cf entry.\n#\n# Specify in cyrus.conf:\n#   lmtp    cmd=\"lmtpd -a\" listen=\"localhost:lmtp\" proto=tcp4\n#\n# Specify in main.cf one or more of the following:\n#  mailbox_transport = lmtp:inet:localhost\n#  virtual_transport = lmtp:inet:localhost\n#\n# ====================================================================\n#\n# Cyrus 2.1.5 (Amos Gouaux)\n# Also specify in main.cf: cyrus_destination_recipient_limit=1\n#\n#cyrus     unix  -       n       n       -       -       pipe\n#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}\n#\n# ====================================================================\n#\n# Old example of delivery via Cyrus.\n#\n#old-cyrus unix  -       n       n       -       -       pipe\n#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}\n#\n# ====================================================================\n#\n# See the Postfix UUCP_README file for configuration details.\n#\n#uucp      unix  -       n       n       -       -       pipe\n#  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)\n#\n# ====================================================================\n#\n# Other external delivery methods.\n#\n#ifmail    unix  -       n       n       -       -       pipe\n#  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)\n#\n#bsmtp     unix  -       n       n       -       -       pipe\n#  flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient\n#\n#scalemail-backend unix -       n       n       -       2       pipe\n#  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store\n#  ${nexthop} ${user} ${extension}\n#\n#mailman   unix  -       n       n       -       -       pipe\n#  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py\n#  ${nexthop} ${user}\n#\n# Dovecot LDA\ndovecot\t  unix\t-\tn\tn\t-\t-\tpipe\n\tflags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ${recipient}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- IMAP/POP3 services -->\n\t\t\t<service type=\"mail\" title=\"{{lng.admin.configfiles.mail}}\">\n\t\t\t\t<!-- valid for both dovecots -->\n\t\t\t\t<general>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-managesieved dovecot-sieve]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/dovecot/dovecot.conf\" chown=\"root:root\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## Dovecot configuration file\n\n# If you're in a hurry, see http://wiki2.dovecot.org/QuickConfiguration\n\n# \"doveconf -n\" command gives a clean output of the changed settings. Use it\n# instead of copy&pasting files when posting to the Dovecot mailing list.\n\n# '#' character and everything after it is treated as comments. Extra spaces\n# and tabs are ignored. If you want to use either of these explicitly, put the\n# value inside quotes, eg.: key = \"# char and trailing whitespace  \"\n\n# Most (but not all) settings can be overridden by different protocols and/or\n# source/destination IPs by placing the settings inside sections, for example:\n# protocol imap { }, local 127.0.0.1 { }, remote 10.0.0.0/8 { }\n\n# Default values are shown for each setting, it's not required to uncomment\n# those. These are exceptions to this though: No sections (e.g. namespace {})\n# or plugin settings are added by default, they're listed only as examples.\n# Paths are also just examples with the real defaults being based on configure\n# options. The paths listed here are for configure --prefix=/usr\n# --sysconfdir=/etc --localstatedir=/var\n\n# Enable installed protocols\n!include_try /usr/share/dovecot/protocols.d/*.protocol\n\n# A comma separated list of IPs or hosts where to listen in for connections.\n# \"*\" listens in all IPv4 interfaces, \"::\" listens in all IPv6 interfaces.\n# If you want to specify non-default ports or anything more complex,\n# edit conf.d/master.conf.\n#listen = *, ::\n\n# Base directory where to store runtime data.\n#base_dir = /var/run/dovecot/\n\n# Name of this instance. In multi-instance setup doveadm and other commands\n# can use -i <instance_name> to select which instance is used (an alternative\n# to -c <config_path>). The instance name is also added to Dovecot processes\n# in ps output.\n#instance_name = dovecot\n\n# Greeting message for clients.\n#login_greeting = Dovecot ready.\n\n# Space separated list of trusted network ranges. Connections from these\n# IPs are allowed to override their IP addresses and ports (for logging and\n# for authentication checks). disable_plaintext_auth is also ignored for\n# these networks. Typically you'd specify your IMAP proxy servers here.\n#login_trusted_networks =\n\n# Space separated list of login access check sockets (e.g. tcpwrap)\n#login_access_sockets =\n\n# With proxy_maybe=yes if proxy destination matches any of these IPs, don't do\n# proxying. This isn't necessary normally, but may be useful if the destination\n# IP is e.g. a load balancer's IP.\n#auth_proxy_self =\n\n# Show more verbose process titles (in ps). Currently shows user name and\n# IP address. Useful for seeing who are actually using the IMAP processes\n# (eg. shared mailboxes or if same uid is used for multiple accounts).\n#verbose_proctitle = no\n\n# Should all processes be killed when Dovecot master process shuts down.\n# Setting this to \"no\" means that Dovecot can be upgraded without\n# forcing existing client connections to close (although that could also be\n# a problem if the upgrade is e.g. because of a security fix).\n#shutdown_clients = yes\n\n# If non-zero, run mail commands via this many connections to doveadm server,\n# instead of running them directly in the same process.\n#doveadm_worker_count = 0\n# UNIX socket or host:port used for connecting to doveadm server\n#doveadm_socket_path = doveadm-server\n\n# Space separated list of environment variables that are preserved on Dovecot\n# startup and passed down to all of its child processes. You can also give\n# key=value pairs to always set specific settings.\n#import_environment = TZ\n\n##\n## Dictionary server settings\n##\n\n# Dictionary can be used to store key=value lists. This is used by several\n# plugins. The dictionary can be accessed either directly or though a\n# dictionary server. The following dict block maps dictionary names to URIs\n# when the server is used. These can then be referenced using URIs in format\n# \"proxy::<name>\".\n\ndict {\n  #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext\n  #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext\n}\n\n# Most of the actual configuration gets included below. The filenames are\n# first sorted by their ASCII value and parsed in that order. The 00-prefixes\n# in filenames are intended to make it easier to understand the ordering.\n!include conf.d/*.conf\n\n# A config file can also tried to be included without giving an error if\n# it's not found:\n!include_try local.conf\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/dovecot-sql.conf.ext\"\n\t\t\t\t\t\t\tchown=\"root:root\" chmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# This file is commonly accessed via passdb {} or userdb {} section in\n# conf.d/auth-sql.conf.ext\n\n# This file is opened as root, so it should be owned by root and mode 0600.\n#\n# http://wiki2.dovecot.org/AuthDatabase/SQL\n#\n# For the sql passdb module, you'll need a database with a table that\n# contains fields for at least the username and password. If you want to\n# use the user@domain syntax, you might want to have a separate domain\n# field as well.\n#\n# If your users all have the same uig/gid, and have predictable home\n# directories, you can use the static userdb module to generate the home\n# dir based on the username and domain. In this case, you won't need fields\n# for home, uid, or gid in the database.\n#\n# If you prefer to use the sql userdb module, you'll want to add fields\n# for home, uid, and gid. Here is an example table:\n#\n# CREATE TABLE users (\n#     username VARCHAR(128) NOT NULL,\n#     domain VARCHAR(128) NOT NULL,\n#     password VARCHAR(64) NOT NULL,\n#     home VARCHAR(255) NOT NULL,\n#     uid INTEGER NOT NULL,\n#     gid INTEGER NOT NULL,\n#     active CHAR(1) DEFAULT 'Y' NOT NULL\n# );\n\n# Database driver: mysql, pgsql, sqlite\ndriver = mysql\n\n# Database connection string. This is driver-specific setting.\n#\n# HA / round-robin load-balancing is supported by giving multiple host\n# settings, like: host=sql1.host.org host=sql2.host.org\n#\n# pgsql:\n#   For available options, see the PostgreSQL documentation for the\n#   PQconnectdb function of libpq.\n#   Use maxconns=n (default 5) to change how many connections Dovecot can\n#   create to pgsql.\n#\n# mysql:\n#   Basic options emulate PostgreSQL option names:\n#     host, port, user, password, dbname\n#\n#   But also adds some new settings:\n#     client_flags           - See MySQL manual\n#     ssl_ca, ssl_ca_path    - Set either one or both to enable SSL\n#     ssl_cert, ssl_key      - For sending client-side certificates to server\n#     ssl_cipher             - Set minimum allowed cipher security (default: HIGH)\n#     ssl_verify_server_cert - Verify that the name in the server SSL certificate\n#                              matches the host (default: no)\n#     option_file            - Read options from the given file instead of\n#                              the default my.cnf location\n#     option_group           - Read options from the given group (default: client)\n#\n#   You can connect to UNIX sockets by using host: host=/var/run/mysql.sock\n#   Note that currently you can't use spaces in parameters.\n#\n# sqlite:\n#   The path to the database file.\n#\n# Examples:\n#   connect = host=192.168.1.1 dbname=users\n#   connect = host=sql.example.com dbname=virtual user=virtual password=blarg\n#   connect = /etc/dovecot/authdb.sqlite\n#\nconnect = \"host=<SQL_HOST> dbname=<SQL_DB> user=<SQL_UNPRIVILEGED_USER> password=<SQL_UNPRIVILEGED_PASSWORD>\"\n\n# Default password scheme.\n#\n# List of supported schemes is in\n# http://wiki2.dovecot.org/Authentication/PasswordSchemes\n#\n#default_pass_scheme = CRYPT\n\n# passdb query to retrieve the password. It can return fields:\n#   password - The user's password. This field must be returned.\n#   user - user@domain from the database. Needed with case-insensitive lookups.\n#   username and domain - An alternative way to represent the \"user\" field.\n#\n# The \"user\" field is often necessary with case-insensitive lookups to avoid\n# e.g. \"name\" and \"nAme\" logins creating two different mail directories. If\n# your user and domain names are in separate fields, you can return \"username\"\n# and \"domain\" fields instead of \"user\".\n#\n# The query can also return other fields which have a special meaning, see\n# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields\n#\n# Commonly used available substitutions (see http://wiki2.dovecot.org/Variables\n# for full list):\n#   %u = entire user@domain\n#   %n = user part of user@domain\n#   %d = domain part of user@domain\n#\n# Note that these can be used only as input to SQL query. If the query outputs\n# any of these substitutions, they're not touched. Otherwise it would be\n# difficult to have eg. usernames containing '%' characters.\n#\n# Example:\n#   password_query = SELECT userid AS user, pw AS password \\\n#     FROM users WHERE userid = '%u' AND active = 'Y'\n#\n#password_query = \\\n#  SELECT username, domain, password \\\n#  FROM users WHERE username = '%n' AND domain = '%d'\n\n# userdb query to retrieve the user information. It can return fields:\n#   uid - System UID (overrides mail_uid setting)\n#   gid - System GID (overrides mail_gid setting)\n#   home - Home directory\n#   mail - Mail location (overrides mail_location setting)\n#\n# None of these are strictly required. If you use a single UID and GID, and\n# home or mail directory fits to a template string, you could use userdb static\n# instead. For a list of all fields that can be returned, see\n# http://wiki2.dovecot.org/UserDatabase/ExtraFields\n#\n# Examples:\n#   user_query = SELECT home, uid, gid FROM users WHERE userid = '%u'\n#   user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u'\n#   user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u'\n#\n#user_query = \\\n#  SELECT home, uid, gid \\\n#  FROM users WHERE username = '%n' AND domain = '%d'\nuser_query = SELECT CONCAT(homedir, maildir) AS home, CONCAT('maildir:', homedir, maildir) AS mail, uid, gid, CONCAT('*:storage=', quota, 'M') as quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u')\n\n# If you wish to avoid two SQL lookups (passdb + userdb), you can use\n# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll\n# also have to return userdb fields in password_query prefixed with \"userdb_\"\n# string. For example:\n#password_query = \\\n#  SELECT userid AS user, password, \\\n#    home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \\\n#  FROM users WHERE userid = '%u'\npassword_query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid,  CONCAT('maildir:', homedir, maildir) AS userdb_mail, CONCAT('*:storage=', quota, 'M') as userdb_quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') AND ((imap = 1 AND '%Ls' = 'imap') OR (pop3 = 1 AND '%Ls' = 'pop3') OR ((postfix = 'Y' AND '%Ls' = 'smtp') OR (postfix = 'Y' AND '%Ls' = 'sieve')))\n\n# Query to get a list of all usernames.\niterate_query = \"SELECT username AS user FROM mail_users WHERE (imap = 1 OR pop3 = 1)\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-auth.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Authentication processes\n##\n\n# Disable LOGIN command and all other plaintext authentications unless\n# SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP\n# matches the local IP (ie. you're connecting from the same computer), the\n# connection is considered secure and plaintext authentication is allowed.\n# See also ssl=required setting.\ndisable_plaintext_auth = no\n\n# Authentication cache size (e.g. 10M). 0 means it's disabled. Note that\n# bsdauth, PAM and vpopmail require cache_key to be set for caching to be used.\n#auth_cache_size = 0\n# Time to live for cached data. After TTL expires the cached record is no\n# longer used, *except* if the main database lookup returns internal failure.\n# We also try to handle password changes automatically: If user's previous\n# authentication was successful, but this one wasn't, the cache isn't used.\n# For now this works only with plaintext authentication.\n#auth_cache_ttl = 1 hour\n# TTL for negative hits (user not found, password mismatch).\n# 0 disables caching them completely.\n#auth_cache_negative_ttl = 1 hour\n\n# Space separated list of realms for SASL authentication mechanisms that need\n# them. You can leave it empty if you don't want to support multiple realms.\n# Many clients simply use the first one listed here, so keep the default realm\n# first.\n#auth_realms =\n\n# Default realm/domain to use if none was specified. This is used for both\n# SASL realms and appending @domain to username in plaintext logins.\n#auth_default_realm =\n\n# List of allowed characters in username. If the user-given username contains\n# a character not listed in here, the login automatically fails. This is just\n# an extra check to make sure user can't exploit any potential quote escaping\n# vulnerabilities with SQL/LDAP databases. If you want to allow all characters,\n# set this value to empty.\n#auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@\n\n# Username character translations before it's looked up from databases. The\n# value contains series of from -> to characters. For example \"#@/@\" means\n# that '#' and '/' characters are translated to '@'.\n#auth_username_translation =\n\n# Username formatting before it's looked up from databases. You can use\n# the standard variables here, eg. %Lu would lowercase the username, %n would\n# drop away the domain if it was given, or \"%n-AT-%d\" would change the '@' into\n# \"-AT-\". This translation is done after auth_username_translation changes.\n#auth_username_format = %Lu\n\n# If you want to allow master users to log in by specifying the master\n# username within the normal username string (ie. not using SASL mechanism's\n# support for it), you can specify the separator character here. The format\n# is then <username><separator><master username>. UW-IMAP uses \"*\" as the\n# separator, so that could be a good choice.\n#auth_master_user_separator =\n\n# Username to use for users logging in with ANONYMOUS SASL mechanism\n#auth_anonymous_username = anonymous\n\n# Maximum number of dovecot-auth worker processes. They're used to execute\n# blocking passdb and userdb queries (eg. MySQL and PAM). They're\n# automatically created and destroyed as needed.\n#auth_worker_max_count = 30\n\n# Host name to use in GSSAPI principal names. The default is to use the\n# name returned by gethostname(). Use \"$ALL\" (with quotes) to allow all keytab\n# entries.\n#auth_gssapi_hostname =\n\n# Kerberos keytab to use for the GSSAPI mechanism. Will use the system\n# default (usually /etc/krb5.keytab) if not specified. You may need to change\n# the auth service to run as root to be able to read this file.\n#auth_krb5_keytab =\n\n# Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and\n# ntlm_auth helper. <doc/wiki/Authentication/Mechanisms/Winbind.txt>\n#auth_use_winbind = no\n\n# Path for Samba's ntlm_auth helper binary.\n#auth_winbind_helper_path = /usr/bin/ntlm_auth\n\n# Time to delay before replying to failed authentications.\n#auth_failure_delay = 2 secs\n\n# Require a valid SSL client certificate or the authentication fails.\n#auth_ssl_require_client_cert = no\n\n# Take the username from client's SSL certificate, using\n# X509_NAME_get_text_by_NID() which returns the subject's DN's\n# CommonName.\n#auth_ssl_username_from_cert = no\n\n# Space separated list of wanted authentication mechanisms:\n#   plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey\n#   gss-spnego\n# NOTE: See also disable_plaintext_auth setting.\nauth_mechanisms = plain login\n\n##\n## Password and user databases\n##\n\n#\n# Password database is used to verify user's password (and nothing more).\n# You can have multiple passdbs and userdbs. This is useful if you want to\n# allow both system users (/etc/passwd) and virtual users to login without\n# duplicating the system users into virtual database.\n#\n# <doc/wiki/PasswordDatabase.txt>\n#\n# User database specifies where mails are located and what user/group IDs\n# own them. For single-UID configuration use \"static\" userdb.\n#\n# <doc/wiki/UserDatabase.txt>\n\n#!include auth-deny.conf.ext\n#!include auth-master.conf.ext\n\n#!include auth-system.conf.ext\n!include auth-sql.conf.ext\n#!include auth-ldap.conf.ext\n#!include auth-passwdfile.conf.ext\n#!include auth-checkpassword.conf.ext\n#!include auth-vpopmail.conf.ext\n#!include auth-static.conf.ext\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-mail.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Mailbox locations and namespaces\n##\n\n# Location for users' mailboxes. The default is empty, which means that Dovecot\n# tries to find the mailboxes automatically. This won't work if the user\n# doesn't yet have any mail, so you should explicitly tell Dovecot the full\n# location.\n#\n# If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%u)\n# isn't enough. You'll also need to tell Dovecot where the other mailboxes are\n# kept. This is called the \"root mail directory\", and it must be the first\n# path given in the mail_location setting.\n#\n# There are a few special variables you can use, eg.:\n#\n#   %u - username\n#   %n - user part in user@domain, same as %u if there's no domain\n#   %d - domain part in user@domain, empty if there's no domain\n#   %h - home directory\n#\n# See doc/wiki/Variables.txt for full list. Some examples:\n#\n#   mail_location = maildir:~/Maildir\n#   mail_location = mbox:~/mail:INBOX=/var/mail/%u\n#   mail_location = mbox:/var/mail/%d/%1n/%n:INDEX=/var/indexes/%d/%1n/%n\n#\n# <doc/wiki/MailLocation.txt>\n#\nmail_location = mbox:~/mail:INBOX=/var/mail/%u\n\n# If you need to set multiple mailbox locations or want to change default\n# namespace settings, you can do it by defining namespace sections.\n#\n# You can have private, shared and public namespaces. Private namespaces\n# are for user's personal mails. Shared namespaces are for accessing other\n# users' mailboxes that have been shared. Public namespaces are for shared\n# mailboxes that are managed by sysadmin. If you create any shared or public\n# namespaces you'll typically want to enable ACL plugin also, otherwise all\n# users can access all the shared mailboxes, assuming they have permissions\n# on filesystem level to do so.\nnamespace inbox {\n  # Namespace type: private, shared or public\n  #type = private\n\n  # Hierarchy separator to use. You should use the same separator for all\n  # namespaces or some clients get confused. '/' is usually a good one.\n  # The default however depends on the underlying mail storage format.\n  #separator =\n\n  # Prefix required to access this namespace. This needs to be different for\n  # all namespaces. For example \"Public/\".\n  #prefix =\n\n  # Physical location of the mailbox. This is in same format as\n  # mail_location, which is also the default for it.\n  #location =\n\n  # There can be only one INBOX, and this setting defines which namespace\n  # has it.\n  inbox = yes\n\n  # If namespace is hidden, it's not advertised to clients via NAMESPACE\n  # extension. You'll most likely also want to set list=no. This is mostly\n  # useful when converting from another server with different namespaces which\n  # you want to deprecate but still keep working. For example you can create\n  # hidden namespaces with prefixes \"~/mail/\", \"~%u/mail/\" and \"mail/\".\n  #hidden = no\n\n  # Show the mailboxes under this namespace with LIST command. This makes the\n  # namespace visible for clients that don't support NAMESPACE extension.\n  # \"children\" value lists child mailboxes, but hides the namespace prefix.\n  #list = yes\n\n  # Namespace handles its own subscriptions. If set to \"no\", the parent\n  # namespace handles them (empty prefix should always have this as \"yes\")\n  #subscriptions = yes\n}\n\n# Example shared namespace configuration\n#namespace {\n  #type = shared\n  #separator = /\n\n  # Mailboxes are visible under \"shared/user@domain/\"\n  # %%n, %%d and %%u are expanded to the destination user.\n  #prefix = shared/%%u/\n\n  # Mail location for other users' mailboxes. Note that %variables and ~/\n  # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the\n  # destination user's data.\n  #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u\n\n  # Use the default namespace for saving subscriptions.\n  #subscriptions = no\n\n  # List the shared/ namespace only if there are visible shared mailboxes.\n  #list = children\n#}\n# Should shared INBOX be visible as \"shared/user\" or \"shared/user/INBOX\"?\n#mail_shared_explicit_inbox = no\n\n# System user and group used to access mails. If you use multiple, userdb\n# can override these by returning uid or gid fields. You can use either numbers\n# or names. <doc/wiki/UserIds.txt>\n#mail_uid =\n#mail_gid =\n\n# Group to enable temporarily for privileged operations. Currently this is\n# used only with INBOX when either its initial creation or dotlocking fails.\n# Typically this is set to \"mail\" to give access to /var/mail.\n#mail_privileged_group =\n\n# Grant access to these supplementary groups for mail processes. Typically\n# these are used to set up access to shared mailboxes. Note that it may be\n# dangerous to set these if users can create symlinks (e.g. if \"mail\" group is\n# set here, ln -s /var/mail ~/mail/var could allow a user to delete others'\n# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it).\nmail_access_groups = vmail\n\n# Allow full filesystem access to clients. There's no access checks other than\n# what the operating system does for the active UID/GID. It works with both\n# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/\n# or ~user/.\n#mail_full_filesystem_access = no\n\n# Dictionary for key=value mailbox attributes. Currently used by URLAUTH, but\n# soon intended to be used by METADATA as well.\n#mail_attribute_dict =\n\n##\n## Mail processes\n##\n\n# Don't use mmap() at all. This is required if you store indexes to shared\n# filesystems (NFS or clustered filesystem).\n#mmap_disable = no\n\n# Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL\n# since version 3, so this should be safe to use nowadays by default.\n#dotlock_use_excl = yes\n\n# When to use fsync() or fdatasync() calls:\n#   optimized (default): Whenever necessary to avoid losing important data\n#   always: Useful with e.g. NFS when write()s are delayed\n#   never: Never use it (best performance, but crashes can lose data)\n#mail_fsync = optimized\n\n# Locking method for index files. Alternatives are fcntl, flock and dotlock.\n# Dotlocking uses some tricks which may create more disk I/O than other locking\n# methods. NFS users: flock doesn't work, remember to change mmap_disable.\n#lock_method = fcntl\n\n# Directory in which LDA/LMTP temporarily stores incoming mails >128 kB.\n#mail_temp_dir = /tmp\n\n# Valid UID range for users, defaults to 500 and above. This is mostly\n# to make sure that users can't log in as daemons or other system users.\n# Note that denying root logins is hardcoded to dovecot binary and can't\n# be done even if first_valid_uid is set to 0.\n#first_valid_uid = 500\n#last_valid_uid = 0\n\n# Valid GID range for users, defaults to non-root/wheel. Users having\n# non-valid GID as primary group ID aren't allowed to log in. If user\n# belongs to supplementary groups with non-valid GIDs, those groups are\n# not set.\n#first_valid_gid = 1\n#last_valid_gid = 0\n\n# Maximum allowed length for mail keyword name. It's only forced when trying\n# to create new keywords.\n#mail_max_keyword_length = 50\n\n# ':' separated list of directories under which chrooting is allowed for mail\n# processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too).\n# This setting doesn't affect login_chroot, mail_chroot or auth chroot\n# settings. If this setting is empty, \"/./\" in home dirs are ignored.\n# WARNING: Never add directories here which local users can modify, that\n# may lead to root exploit. Usually this should be done only if you don't\n# allow shell access for users. <doc/wiki/Chrooting.txt>\n#valid_chroot_dirs =\n\n# Default chroot directory for mail processes. This can be overridden for\n# specific users in user database by giving /./ in user's home directory\n# (eg. /home/./user chroots into /home). Note that usually there is no real\n# need to do chrooting, Dovecot doesn't allow users to access files outside\n# their mail directory anyway. If your home directories are prefixed with\n# the chroot directory, append \"/.\" to mail_chroot. <doc/wiki/Chrooting.txt>\n#mail_chroot =\n\n# UNIX socket path to master authentication server to find users.\n# This is used by imap (for shared users) and lda.\n#auth_socket_path = /var/run/dovecot/auth-userdb\n\n# Directory where to look up mail plugins.\n#mail_plugin_dir = /usr/lib/dovecot/modules\n\n# Space separated list of plugins to load for all services. Plugins specific to\n# IMAP, LDA, etc. are added to this list in their own .conf files.\n#mail_plugins =\n\n##\n## Mailbox handling optimizations\n##\n\n# Mailbox list indexes can be used to optimize IMAP STATUS commands. They are\n# also required for IMAP NOTIFY extension to be enabled.\n#mailbox_list_index = no\n\n# The minimum number of mails in a mailbox before updates are done to cache\n# file. This allows optimizing Dovecot's behavior to do less disk writes at\n# the cost of more disk reads.\n#mail_cache_min_mail_count = 0\n\n# When IDLE command is running, mailbox is checked once in a while to see if\n# there are any new mails or other changes. This setting defines the minimum\n# time to wait between those checks. Dovecot can also use dnotify, inotify and\n# kqueue to find out immediately when changes occur.\n#mailbox_idle_check_interval = 30 secs\n\n# Save mails with CR+LF instead of plain LF. This makes sending those mails\n# take less CPU, especially with sendfile() syscall with Linux and FreeBSD.\n# But it also creates a bit more disk I/O which may just make it slower.\n# Also note that if other software reads the mboxes/maildirs, they may handle\n# the extra CRs wrong and cause problems.\n#mail_save_crlf = no\n\n# Max number of mails to keep open and prefetch to memory. This only works with\n# some mailbox formats and/or operating systems.\n#mail_prefetch_count = 0\n\n# How often to scan for stale temporary files and delete them (0 = never).\n# These should exist only after Dovecot dies in the middle of saving mails.\n#mail_temp_scan_interval = 1w\n\n##\n## Maildir-specific settings\n##\n\n# By default LIST command returns all entries in maildir beginning with a dot.\n# Enabling this option makes Dovecot return only entries which are directories.\n# This is done by stat()ing each entry, so it causes more disk I/O.\n# (For systems setting struct dirent->d_type, this check is free and it's\n# done always regardless of this setting)\n#maildir_stat_dirs = no\n\n# When copying a message, do it with hard links whenever possible. This makes\n# the performance much better, and it's unlikely to have any side effects.\n#maildir_copy_with_hardlinks = yes\n\n# Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only\n# when its mtime changes unexpectedly or when we can't find the mail otherwise.\n#maildir_very_dirty_syncs = no\n\n# If enabled, Dovecot doesn't use the S=<size> in the Maildir filenames for\n# getting the mail's physical size, except when recalculating Maildir++ quota.\n# This can be useful in systems where a lot of the Maildir filenames have a\n# broken size. The performance hit for enabling this is very small.\n#maildir_broken_filename_sizes = no\n\n# Always move mails from new/ directory to cur/, even when the \\Recent flags\n# aren't being reset.\n#maildir_empty_new = no\n\n##\n## mbox-specific settings\n##\n\n# Which locking methods to use for locking mbox. There are four available:\n#  dotlock: Create <mailbox>.lock file. This is the oldest and most NFS-safe\n#           solution. If you want to use /var/mail/ like directory, the users\n#           will need write access to that directory.\n#  dotlock_try: Same as dotlock, but if it fails because of permissions or\n#               because there isn't enough disk space, just skip it.\n#  fcntl  : Use this if possible. Works with NFS too if lockd is used.\n#  flock  : May not exist in all systems. Doesn't work with NFS.\n#  lockf  : May not exist in all systems. Doesn't work with NFS.\n#\n# You can use multiple locking methods; if you do the order they're declared\n# in is important to avoid deadlocks if other MTAs/MUAs are using multiple\n# locking methods as well. Some operating systems don't allow using some of\n# them simultaneously.\n#\n# The Debian value for mbox_write_locks differs from upstream Dovecot. It is\n# changed to be compliant with Debian Policy (section 11.6) for NFS safety.\n#       Dovecot: mbox_write_locks = dotlock fcntl\n#       Debian:  mbox_write_locks = fcntl dotlock\n#\n#mbox_read_locks = fcntl\n#mbox_write_locks = fcntl dotlock\n\n# Maximum time to wait for lock (all of them) before aborting.\n#mbox_lock_timeout = 5 mins\n\n# If dotlock exists but the mailbox isn't modified in any way, override the\n# lock file after this much time.\n#mbox_dotlock_change_timeout = 2 mins\n\n# When mbox changes unexpectedly we have to fully read it to find out what\n# changed. If the mbox is large this can take a long time. Since the change\n# is usually just a newly appended mail, it'd be faster to simply read the\n# new mails. If this setting is enabled, Dovecot does this but still safely\n# fallbacks to re-reading the whole mbox file whenever something in mbox isn't\n# how it's expected to be. The only real downside to this setting is that if\n# some other MUA changes message flags, Dovecot doesn't notice it immediately.\n# Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK\n# commands.\n#mbox_dirty_syncs = yes\n\n# Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE,\n# EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored.\n#mbox_very_dirty_syncs = no\n\n# Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK\n# commands and when closing the mailbox). This is especially useful for POP3\n# where clients often delete all mails. The downside is that our changes\n# aren't immediately visible to other MUAs.\n#mbox_lazy_writes = yes\n\n# If mbox size is smaller than this (e.g. 100k), don't write index files.\n# If an index file already exists it's still read, just not updated.\n#mbox_min_index_size = 0\n\n# Mail header selection algorithm to use for MD5 POP3 UIDLs when\n# pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired\n# algorithm, but it fails if the first Received: header isn't unique in all\n# mails. An alternative algorithm is \"all\" that selects all headers.\n#mbox_md5 = apop3d\n\n##\n## mdbox-specific settings\n##\n\n# Maximum dbox file size until it's rotated.\n#mdbox_rotate_size = 2M\n\n# Maximum dbox file age until it's rotated. Typically in days. Day begins\n# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled.\n#mdbox_rotate_interval = 0\n\n# When creating new mdbox files, immediately preallocate their size to\n# mdbox_rotate_size. This setting currently works only in Linux with some\n# filesystems (ext4, xfs).\n#mdbox_preallocate_space = no\n\n##\n## Mail attachments\n##\n\n# sdbox and mdbox support saving mail attachments to external files, which\n# also allows single instance storage for them. Other backends don't support\n# this for now.\n\n# Directory root where to store mail attachments. Disabled, if empty.\n#mail_attachment_dir =\n\n# Attachments smaller than this aren't saved externally. It's also possible to\n# write a plugin to disable saving specific attachments externally.\n#mail_attachment_min_size = 128k\n\n# Filesystem backend to use for saving attachments:\n#  posix : No SiS done by Dovecot (but this might help FS's own deduplication)\n#  sis posix : SiS with immediate byte-by-byte comparison during saving\n#  sis-queue posix : SiS with delayed comparison and deduplication\n#mail_attachment_fs = sis posix\n\n# Hash format to use in attachment filenames. You can add any text and\n# variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}.\n# Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits\n#mail_attachment_hash = %{sha1}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-master.conf\"\n\t\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n#default_process_limit = 100\n#default_client_limit = 1000\n\n# Default VSZ (virtual memory size) limit for service processes. This is mainly\n# intended to catch and kill processes that leak memory before they eat up\n# everything.\n#default_vsz_limit = 256M\n\n# Login user is internally used by login processes. This is the most untrusted\n# user in Dovecot system. It shouldn't have access to anything at all.\n#default_login_user = dovenull\n\n# Internal user is used by unprivileged processes. It should be separate from\n# login user, so that login processes can't disturb other processes.\n#default_internal_user = dovecot\n\nservice imap-login {\n  inet_listener imap {\n    #port = 143\n  }\n  inet_listener imaps {\n    #port = 993\n    #ssl = yes\n  }\n\n  # Number of connections to handle before starting a new process. Typically\n  # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0\n  # is faster. <doc/wiki/LoginProcess.txt>\n  #service_count = 1\n\n  # Number of processes to always keep waiting for more connections.\n  #process_min_avail = 0\n\n  # If you set service_count=0, you probably need to grow this.\n  #vsz_limit = $default_vsz_limit\n}\n\nservice pop3-login {\n  inet_listener pop3 {\n    #port = 110\n  }\n  inet_listener pop3s {\n    #port = 995\n    #ssl = yes\n  }\n}\n\nservice lmtp {\n  unix_listener lmtp {\n    #mode = 0666\n  }\n\n  # Create inet listener only if you can't use the above UNIX socket\n  #inet_listener lmtp {\n    # Avoid making LMTP visible for the entire internet\n    #address =\n    #port =\n  #}\n}\n\nservice imap {\n  # Most of the memory goes to mmap()ing files. You may need to increase this\n  # limit if you have huge mailboxes.\n  #vsz_limit = $default_vsz_limit\n\n  # Max. number of IMAP processes (connections)\n  #process_limit = 1024\n}\n\nservice pop3 {\n  # Max. number of POP3 processes (connections)\n  #process_limit = 1024\n}\n\nservice auth {\n  # auth_socket_path points to this userdb socket by default. It's typically\n  # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have\n  # full permissions to this socket are able to get a list of all usernames and\n  # get the results of everyone's userdb lookups.\n  #\n  # The default 0666 mode allows anyone to connect to the socket, but the\n  # userdb lookups will succeed only if the userdb returns an \"uid\" field that\n  # matches the caller process's UID. Also if caller's uid or gid matches the\n  # socket's uid or gid the lookup succeeds. Anything else causes a failure.\n  #\n  # To give the caller full permissions to lookup all users, set the mode to\n  # something else than 0666 and Dovecot lets the kernel enforce the\n  # permissions (e.g. 0777 allows everyone full permissions).\n  unix_listener auth-userdb {\n    #mode = 0666\n    #user =\n    #group =\n  }\n\n  # Postfix smtp-auth\n  unix_listener /var/spool/postfix/private/auth {\n    mode = 0660\n    user = postfix\n    group = postfix\n  }\n\n  # Exim4 smtp-auth\n  unix_listener auth-client {\n    mode = 0660\n    user = mail\n  #  group = Debian-exim\n  }\n\n  # Auth process is run as this user.\n  #user = $default_internal_user\n}\n\nservice auth-worker {\n  # Auth worker process is run as root by default, so that it can access\n  # /etc/shadow. If this isn't necessary, the user should be changed to\n  # $default_internal_user.\n  #user = root\n}\n\nservice dict {\n  # If dict proxy is used, mail processes should have access to its socket.\n  # For example: mode=0660, group=vmail and global mail_access_groups=vmail\n  unix_listener dict {\n    #mode = 0600\n    #user =\n    #group =\n  }\n}\n\nservice stats {\n  unix_listener stats-reader {\n    group = vmail\n    mode = 0666\n  }\n  unix_listener stats-writer {\n    group = vmail\n    mode = 0666\n  }\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-ssl.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## SSL settings\n##\n\n# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>\nssl = yes\n\n# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before\n# dropping root privileges, so keep the key file unreadable by anyone but\n# root. Included doc/mkcert.sh can be used to easily generate self-signed\n# certificate, just make sure to update the domains in dovecot-openssl.cnf\nssl_cert = <<SSL_CERT_FILE>\nssl_key = <<SSL_KEY_FILE>\n\n# If key file is password protected, give the password here. Alternatively\n# give it when starting dovecot with -p parameter. Since this file is often\n# world-readable, you may want to place this setting instead to a different\n# root owned 0600 file by using ssl_key_password = <path.\n#ssl_key_password =\n\n# PEM encoded trusted certificate authority. Set this only if you intend to use\n# ssl_verify_client_cert=yes. The file should contain the CA certificate(s)\n# followed by the matching CRL(s). (e.g. ssl_ca = </etc/ssl/certs/ca.pem)\n#ssl_ca =\n\n# Require that CRL check succeeds for client certificates.\n#ssl_require_crl = yes\n\n# Directory and/or file for trusted SSL CA certificates. These are used only\n# when Dovecot needs to act as an SSL client (e.g. imapc backend). The\n# directory is usually /etc/ssl/certs in Debian-based systems and the file is\n# /etc/pki/tls/cert.pem in RedHat-based systems.\n#ssl_client_ca_dir =\n#ssl_client_ca_file =\n\n# Request client to send a certificate. If you also want to require it, set\n# auth_ssl_require_client_cert=yes in auth section.\n#ssl_verify_client_cert = no\n\n# Which field from certificate to use for username. commonName and\n# x500UniqueIdentifier are the usual choices. You'll also need to set\n# auth_ssl_username_from_cert=yes.\n#ssl_cert_username_field = commonName\n\n# SSL DH parameters\n# Generate new params with `openssl dhparam -out /etc/dovecot/dh.pem 4096`\n# Or migrate from old ssl-parameters.dat file with the command dovecot\n# gives on startup when ssl_dh is unset.\nssl_dh = </usr/share/dovecot/dh.pem\n\n# SSL protocols to use\n#ssl_protocols = !SSLv3\n\n# SSL ciphers to use\n#ssl_cipher_list = ALL:!LOW:!SSLv2:!EXP:!aNULL\n\n# Prefer the server's order of ciphers over client's.\n#ssl_prefer_server_ciphers = no\n\n# SSL crypto device to use, for valid values run \"openssl engine\"\n#ssl_crypto_device =\n\n# SSL extra options. Currently supported options are:\n#   no_compression - Disable compression.\n#ssl_options =\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/15-lda.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## LDA specific settings (also used by LMTP)\n##\n\n# Address to use when sending rejection mails.\n# Default is postmaster@<your domain>. %d expands to recipient domain.\npostmaster_address = postmaster@<SERVERNAME>\n\n# Hostname to use in various parts of sent mails (e.g. in Message-Id) and\n# in LMTP replies. Default is the system's real hostname@domain.\n#hostname =\n\n# If user is over quota, return with temporary failure instead of\n# bouncing the mail.\n#quota_full_tempfail = no\n\n# Binary to use for sending mails.\n#sendmail_path = /usr/sbin/sendmail\n\n# If non-empty, send mails via this SMTP host[:port] instead of sendmail.\n#submission_host =\n\n# Subject: header to use for rejection mails. You can use the same variables\n# as for rejection_reason below.\n#rejection_subject = Rejected: %s\n\n# Human readable error message for rejection mails. You can use variables:\n#  %n = CRLF, %r = reason, %s = original subject, %t = recipient\n#rejection_reason = Your message to <%t> was automatically rejected:%n%r\n\n# Delimiter character between local-part and detail in email address.\n#recipient_delimiter = +\n\n# Header where the original recipient address (SMTP's RCPT TO: address) is taken\n# from if not available elsewhere. With dovecot-lda -a parameter overrides this.\n# A commonly used header for this is X-Original-To.\n#lda_original_recipient_header =\n\n# Should saving a mail to a nonexistent mailbox automatically create it?\n#lda_mailbox_autocreate = no\n\n# Should automatically created mailboxes be also automatically subscribed?\n#lda_mailbox_autosubscribe = no\n\nprotocol lda {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  mail_plugins = $mail_plugins quota sieve\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-imap.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## IMAP specific settings\n##\n\n# Maximum IMAP command line length. Some clients generate very long command\n# lines with huge mailboxes, so you may need to raise this if you get\n# \"Too long argument\" or \"IMAP command line too large\" errors often.\n#imap_max_line_length = 64k\n\n# IMAP logout format string:\n#  %i - total number of bytes read from client\n#  %o - total number of bytes sent to client\n#imap_logout_format = in=%i out=%o\n\n# Override the IMAP CAPABILITY response. If the value begins with '+',\n# add the given capabilities on top of the defaults (e.g. +XFOO XBAR).\n#imap_capability =\n\n# How long to wait between \"OK Still here\" notifications when client is\n# IDLEing.\n#imap_idle_notify_interval = 2 mins\n\n# ID field names and values to send to clients. Using * as the value makes\n# Dovecot use the default value. The following fields have default values\n# currently: name, version, os, os-version, support-url, support-email.\n#imap_id_send =\n\n# ID fields sent by client to log. * means everything.\n#imap_id_log =\n\n# Workarounds for various client bugs:\n#   delay-newmail:\n#     Send EXISTS/RECENT new mail notifications only when replying to NOOP\n#     and CHECK commands. Some clients ignore them otherwise, for example OSX\n#     Mail (<v2.1). Outlook Express breaks more badly though, without this it\n#     may show user \"Message no longer in server\" errors. Note that OE6 still\n#     breaks even with this workaround if synchronization is set to\n#     \"Headers Only\".\n#   tb-extra-mailbox-sep:\n#     Thunderbird gets somehow confused with LAYOUT=fs (mbox and dbox) and\n#     adds extra '/' suffixes to mailbox names. This option causes Dovecot to\n#     ignore the extra '/' instead of treating it as invalid mailbox name.\n#   tb-lsub-flags:\n#     Show \\Noselect flags for LSUB replies with LAYOUT=fs (e.g. mbox).\n#     This makes Thunderbird realize they aren't selectable and show them\n#     greyed out, instead of only later giving \"not selectable\" popup error.\n#\n# The list is space-separated.\n#imap_client_workarounds =\n\n# Host allowed in URLAUTH URLs sent by client. \"*\" allows all.\n#imap_urlauth_host =\n\nprotocol imap {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  mail_plugins = $mail_plugins quota imap_quota\n\n  # Maximum number of IMAP connections allowed for a user from each IP address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-managesieve.conf\"\n\t\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## ManageSieve specific settings\n##\n\n# Uncomment to enable managesieve protocol:\n#protocols = $protocols sieve\n\n# Service definitions\n\n#service managesieve-login {\n  #inet_listener sieve {\n  #  port = 4190\n  #}\n\n  #inet_listener sieve_deprecated {\n  #  port = 2000\n  #}\n\n  # Number of connections to handle before starting a new process. Typically\n  # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0\n  # is faster. <doc/wiki/LoginProcess.txt>\n  #service_count = 1\n\n  # Number of processes to always keep waiting for more connections.\n  #process_min_avail = 0\n\n  # If you set service_count=0, you probably need to grow this.\n  #vsz_limit = 64M\n#}\n\n#service managesieve {\n  # Max. number of ManageSieve processes (connections)\n  #process_limit = 1024\n#}\n\n# Service configuration\n\nprotocol sieve {\n  # Maximum ManageSieve command line length in bytes. ManageSieve usually does\n  # not involve overly long command lines, so this setting will not normally\n  # need adjustment\n  #managesieve_max_line_length = 65536\n\n  # Maximum number of ManageSieve connections allowed for a user from each IP\n  # address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n\n  # Space separated list of plugins to load (none known to be useful so far).\n  # Do NOT try to load IMAP plugins here.\n  #mail_plugins =\n\n  # MANAGESIEVE logout format string:\n  #  %i - total number of bytes read from client\n  #  %o - total number of bytes sent to client\n  #managesieve_logout_format = bytes=%i/%o\n\n  # To fool ManageSieve clients that are focused on CMU's timesieved you can\n  # specify the IMPLEMENTATION capability that Dovecot reports to clients.\n  # For example: 'Cyrus timsieved v2.2.13'\n  #managesieve_implementation_string = Dovecot Pigeonhole\n\n  # Explicitly specify the SIEVE and NOTIFY capability reported by the server\n  # before login. If left unassigned these will be reported dynamically\n  # according to what the Sieve interpreter supports by default (after login\n  # this may differ depending on the user).\n  #managesieve_sieve_capability =\n  #managesieve_notify_capability =\n\n  # The maximum number of compile errors that are returned to the client upon\n  # script upload or script verification.\n  #managesieve_max_compile_errors = 5\n\n  # Refer to 90-sieve.conf for script quota configuration and configuration of\n  # Sieve execution limits.\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-pop3.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## POP3 specific settings\n##\n\n# Don't try to set mails non-recent or seen with POP3 sessions. This is\n# mostly intended to reduce disk I/O. With maildir it doesn't move files\n# from new/ to cur/, with mbox it doesn't write Status-header.\n#pop3_no_flag_updates = no\n\n# Support LAST command which exists in old POP3 specs, but has been removed\n# from new ones. Some clients still wish to use this though. Enabling this\n# makes RSET command clear all \\Seen flags from messages.\n#pop3_enable_last = no\n\n# If mail has X-UIDL header, use it as the mail's UIDL.\n#pop3_reuse_xuidl = no\n\n# Allow only one POP3 session to run simultaneously for the same user.\n#pop3_lock_session = no\n\n# POP3 requires message sizes to be listed as if they had CR+LF linefeeds.\n# Many POP3 servers violate this by returning the sizes with LF linefeeds,\n# because it's faster to get. When this setting is enabled, Dovecot still\n# tries to do the right thing first, but if that requires opening the\n# message, it fallbacks to the easier (but incorrect) size.\n#pop3_fast_size_lookups = no\n\n# POP3 UIDL (unique mail identifier) format to use. You can use following\n# variables, along with the variable modifiers described in\n# doc/wiki/Variables.txt (e.g. %Uf for the filename in uppercase)\n#\n#  %v - Mailbox's IMAP UIDVALIDITY\n#  %u - Mail's IMAP UID\n#  %m - MD5 sum of the mailbox headers in hex (mbox only)\n#  %f - filename (maildir only)\n#  %g - Mail's GUID\n#\n# If you want UIDL compatibility with other POP3 servers, use:\n#  UW's ipop3d         : %08Xv%08Xu\n#  Courier             : %f or %v-%u (both might be used simultaneously)\n#  Cyrus (<= 2.1.3)    : %u\n#  Cyrus (>= 2.1.4)    : %v.%u\n#  Dovecot v0.99.x     : %v.%u\n#  tpop3d              : %Mf\n#\n# Note that Outlook 2003 seems to have problems with %v.%u format which was\n# Dovecot's default, so if you're building a new server it would be a good\n# idea to change this. %08Xu%08Xv should be pretty fail-safe.\n#\n#pop3_uidl_format = %08Xu%08Xv\n\n# Permanently save UIDLs sent to POP3 clients, so pop3_uidl_format changes\n# won't change those UIDLs. Currently this works only with Maildir.\n#pop3_save_uidl = no\n\n# What to do about duplicate UIDLs if they exist?\n#   allow: Show duplicates to clients.\n#   rename: Append a temporary -2, -3, etc. counter after the UIDL.\n#pop3_uidl_duplicates = allow\n\n# This option changes POP3 behavior so that it's not possible to actually\n# delete mails via POP3, only hide them from future POP3 sessions. The mails\n# will still be counted towards user's quota until actually deleted via IMAP.\n# Use e.g. \"$POP3Deleted\" as the value (it will be visible as IMAP keyword).\n# Make sure you can legally archive mails before enabling this setting.\n#pop3_deleted_flag =\n\n# POP3 logout format string:\n#  %i - total number of bytes read from client\n#  %o - total number of bytes sent to client\n#  %t - number of TOP commands\n#  %p - number of bytes sent to client as a result of TOP command\n#  %r - number of RETR commands\n#  %b - number of bytes sent to client as a result of RETR command\n#  %d - number of deleted messages\n#  %m - number of messages (before deletion)\n#  %s - mailbox size in bytes (before deletion)\n#  %u - old/new UIDL hash. may help finding out if UIDLs changed unexpectedly\npop3_logout_format = in=%i out=%o top=%t/%p, retr=%r/%b, del=%d/%m, size=%s\n\n# Workarounds for various client bugs:\n#   outlook-no-nuls:\n#     Outlook and Outlook Express hang if mails contain NUL characters.\n#     This setting replaces them with 0x80 character.\n#   oe-ns-eoh:\n#     Outlook Express and Netscape Mail breaks if end of headers-line is\n#     missing. This option simply sends it if it's missing.\n# The list is space-separated.\n#pop3_client_workarounds =\n\nprotocol pop3 {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  #mail_plugins = $mail_plugins\n\n  # Maximum number of POP3 connections allowed for a user from each IP address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/90-sieve.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Settings for the Sieve interpreter\n##\n\n# Do not forget to enable the Sieve plugin in 15-lda.conf and 20-lmtp.conf\n# by adding it to the respective mail_plugins= settings.\n\nplugin {\n  # The path to the user's main active script. If ManageSieve is used, this the\n  # location of the symbolic link controlled by ManageSieve.\n  sieve = ~/sieve/.dovecot.sieve\n\n  # The default Sieve script when the user has none. This is a path to a global\n  # sieve script file, which gets executed ONLY if user's private Sieve script\n  # doesn't exist. Be sure to pre-compile this script manually using the sievec\n  # command line tool.\n  # --> See sieve_before fore executing scripts before the user's personal\n  #     script.\n  #sieve_default = /var/lib/dovecot/sieve/default.sieve\n\n  # Directory for :personal include scripts for the include extension. This\n  # is also where the ManageSieve service stores the user's scripts.\n  sieve_dir = ~/sieve\n\n  # Directory for :global include scripts for the include extension.\n  #sieve_global_dir =\n\n  # Path to a script file or a directory containing script files that need to be\n  # executed before the user's script. If the path points to a directory, all\n  # the Sieve scripts contained therein (with the proper .sieve extension) are\n  # executed. The order of execution within a directory is determined by the\n  # file names, using a normal 8bit per-character comparison. Multiple script\n  # file or directory paths can be specified by appending an increasing number.\n  #sieve_before =\n  #sieve_before2 =\n  #sieve_before3 = (etc...)\n\n  # Identical to sieve_before, only the specified scripts are executed after the\n  # user's script (only when keep is still in effect!). Multiple script file or\n  # directory paths can be specified by appending an increasing number.\n  #sieve_after =\n  #sieve_after2 =\n  #sieve_after2 = (etc...)\n\n  # Which Sieve language extensions are available to users. By default, all\n  # supported extensions are available, except for deprecated extensions or\n  # those that are still under development. Some system administrators may want\n  # to disable certain Sieve extensions or enable those that are not available\n  # by default. This setting can use '+' and '-' to specify differences relative\n  # to the default. For example `sieve_extensions = +imapflags' will enable the\n\t# deprecated imapflags extension in addition to all extensions were already\n  # enabled by default.\n  #sieve_extensions = +notify +imapflags\n\n  # Which Sieve language extensions are ONLY available in global scripts. This\n  # can be used to restrict the use of certain Sieve extensions to administrator\n  # control, for instance when these extensions can cause security concerns.\n  # This setting has higher precedence than the `sieve_extensions' setting\n  # (above), meaning that the extensions enabled with this setting are never\n  # available to the user's personal script no matter what is specified for the\n  # `sieve_extensions' setting. The syntax of this setting is similar to the\n  # `sieve_extensions' setting, with the difference that extensions are\n  # enabled or disabled for exclusive use in global scripts. Currently, no\n  # extensions are marked as such by default.\n  #sieve_global_extensions =\n\n  # The Pigeonhole Sieve interpreter can have plugins of its own. Using this\n  # setting, the used plugins can be specified. Check the Dovecot wiki\n  # (wiki2.dovecot.org) or the pigeonhole website\n  # (http://pigeonhole.dovecot.org) for available plugins.\n\t# The sieve_extprograms plugin is included in this release.\n  #sieve_plugins =\n\n  # The separator that is expected between the :user and :detail\n  # address parts introduced by the subaddress extension. This may\n  # also be a sequence of characters (e.g. '--'). The current\n  # implementation looks for the separator from the left of the\n  # localpart and uses the first one encountered. The :user part is\n  # left of the separator and the :detail part is right. This setting\n  # is also used by Dovecot's LMTP service.\n  #recipient_delimiter = +\n\n  # The maximum size of a Sieve script. The compiler will refuse to compile any\n  # script larger than this limit. If set to 0, no limit on the script size is\n  # enforced.\n  #sieve_max_script_size = 1M\n\n  # The maximum number of actions that can be performed during a single script\n  # execution. If set to 0, no limit on the total number of actions is enforced.\n  #sieve_max_actions = 32\n\n  # The maximum number of redirect actions that can be performed during a single\n  # script execution. If set to 0, no redirect actions are allowed.\n  #sieve_max_redirects = 4\n\n  # The maximum number of personal Sieve scripts a single user can have. If set\n  # to 0, no limit on the number of scripts is enforced.\n  # (Currently only relevant for ManageSieve)\n  #sieve_quota_max_scripts = 0\n\n  # The maximum amount of disk storage a single user's scripts may occupy. If\n  # set to 0, no limit on the used amount of disk storage is enforced.\n  # (Currently only relevant for ManageSieve)\n  #sieve_quota_max_storage = 0\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/90-quota.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\nplugin {\n  quota = maildir:User quota\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[service dovecot restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- Dovecot with postfix -->\n\t\t\t\t<daemon name=\"dovecot_postfix\" version=\"2\"\n\t\t\t\t\ttitle=\"Dovecot with postfix\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='mail']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- Antispam services -->\n\t\t\t<service type=\"antispam\" title=\"Antispam\">\n\t\t\t\t<!-- general RSpamd commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install wget gnupg]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/apt/keyrings]]></command>\n\t\t\t\t\t\t<command><![CDATA[wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | tee /etc/apt/keyrings/rspamd.gpg > /dev/null]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ focal main\" > /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb-src [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ focal main\" >> /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[apt-get update]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install rspamd]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/local.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/override.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/rspamd/dkim/]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/actions.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# Set rewrite subject to this value (%s is replaced by the original subject)\nsubject = \"***SPAM*** %s\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/arc.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\ntry_fallback = true;\n### Enable DKIM signing for alias sender addresses\nallow_username_mismatch = true;\npath = \"/var/lib/rspamd/dkim/$domain.$selector.key\";\nselector_map = \"/etc/rspamd/dkim_selectors.map\";\nuse_esld = false;\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/milter_headers.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuse = [\"x-spamd-bar\", \"x-spam-level\", \"authentication-results\"];\nauthenticated_headers = [\"authentication-results\"];\nextended_spam_headers = true\nskip_local = false\nskip_authenticated = false\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/replies.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## If a user has replied to an email, don’t mark other emails in the same thread as spam\naction = \"no action\";\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/settings.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## Feel free to include your own settings or adjustments here, for example:\n#whitelist {\n#  priority = low;\n#  rcpt = \"postmaster@example.com\";\n#  want_spam = yes;\n#}\n\n## Include froxlor generated settings\n.include(try=true,priority=1,duplicate=merge) \"{{settings.antispam.config_file}}\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[cp /etc/rspamd/local.d/arc.conf /etc/rspamd/local.d/dkim_signing.conf]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_protocol = 6\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_default_action = accept\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"non_smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R _rspamd:_rspamd /var/lib/rspamd/dkim]]></command>\n\t\t\t\t\t\t<command><![CDATA[service rspamd restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- rspamd -->\n\t\t\t\t<daemon name=\"rspamd\" title=\"Rspamd\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- FTP services -->\n\t\t\t<service type=\"ftp\" title=\"{{lng.admin.configfiles.ftp}}\">\n\t\t\t\t<!-- Proftpd -->\n\t\t\t\t<daemon name=\"proftpd\" title=\"ProFTPd\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install proftpd-basic proftpd-mod-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/proftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/certs/proftpd.crt ] || openssl req -new -x509 -newkey rsa:4096 -days 3650 -nodes -out /etc/ssl/certs/proftpd.crt -keyout /etc/ssl/private/proftpd.key -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\n[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nchmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/proftpd/proftpd.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# /etc/proftpd/proftpd.conf -- This is a basic ProFTPD configuration file.\n# To really apply changes, reload proftpd after modifications, if\n# it runs in daemon mode. It is not required in inetd/xinetd mode.\n#\n\n# Includes DSO modules\nInclude /etc/proftpd/modules.conf\n\n# Set off to disable IPv6 support which is annoying on IPv4 only boxes.\nUseIPv6\t\t\t\ton\n# If set on you can experience a longer connection delay in many cases.\nIdentLookups\t\t\toff\n\nServerName                     \"<SERVERNAME> FTP Server\"\nServerType\t\t\tstandalone\nDeferWelcome\t\t\toff\n\nDefaultServer\t\t\ton\nShowSymlinks\t\t\ton\n\nTimeoutNoTransfer\t\t600\nTimeoutStalled\t\t\t600\nTimeoutIdle\t\t\t1200\n\nDisplayLogin                    welcome.msg\nDisplayChdir               \t.message true\nListOptions                \t\"-l\"\n\nDenyFilter\t\t\t\\*.*/\n\n# Use this to jail all users in their homes\n# DefaultRoot\t\t\t~\n\n# Users require a valid shell listed in /etc/shells to login.\n# Use this directive to release that constrain.\n# RequireValidShell\t\toff\n\n# Port 21 is the standard FTP port.\nPort\t\t\t\t21\n\n# In some cases you have to specify passive ports range to by-pass\n# firewall limitations. Ephemeral ports can be used for that, but\n# feel free to use a more narrow range.\n# PassivePorts                  49152 65534\n\n# If your host was NATted, this option is useful in order to\n# allow passive transfers to work. You have to use your public\n# address and opening the passive ports used on your firewall as well.\n# MasqueradeAddress\t\t1.2.3.4\n\n# This is useful for masquerading address with dynamic IPs:\n# refresh any configured MasqueradeAddress directives every 8 hours\n<IfModule mod_dynmasq.c>\n# DynMasqRefresh 28800\n</IfModule>\n\n# To prevent DoS attacks, set the maximum number of child processes\n# to 30.  If you need to allow more than 30 concurrent connections\n# at once, simply increase this value.  Note that this ONLY works\n# in standalone mode, in inetd mode you should use an inetd server\n# that allows you to limit maximum number of processes per service\n# (such as xinetd)\nMaxInstances\t\t\t30\n\n# Set the user and group that the server normally runs at.\nUser\t\t\t\tproftpd\nGroup\t\t\t\tnogroup\n\n# Umask 022 is a good standard umask to prevent new files and dirs\n# (second parm) from being group and world writable.\nUmask\t\t\t\t022  022\n# Normally, we want files to be overwritable.\nAllowOverwrite\t\t\ton\n\n# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords:\n# PersistentPasswd\t\toff\n\n# This is required to use both PAM-based authentication and local passwords\n# AuthOrder\t\t\tmod_auth_pam.c* mod_auth_unix.c\n\n# Be warned: use of this directive impacts CPU average load!\n# Uncomment this if you like to see progress and transfer rate with ftpwho\n# in downloads. That is not needed for uploads rates.\n#\n# UseSendFile\t\t\toff\n\nTransferLog /var/log/proftpd/xferlog\nSystemLog   /var/log/proftpd/proftpd.log\n\n# Logging onto /var/log/lastlog is enabled but set to off by default\n#UseLastlog on\n\n# In order to keep log file dates consistent after chroot, use timezone info\n# from /etc/localtime.  If this is not set, and proftpd is configured to\n# chroot (e.g. DefaultRoot or <Anonymous>), it will use the non-daylight\n# savings timezone regardless of whether DST is in effect.\n#SetEnv TZ :/etc/localtime\n\n<IfModule mod_quotatab.c>\nQuotaEngine on\n</IfModule>\n\n<IfModule mod_ratio.c>\nRatios off\n</IfModule>\n\n\n# Delay engine reduces impact of the so-called Timing Attack described in\n# http://www.securityfocus.com/bid/11430/discuss\n# It is on by default.\n<IfModule mod_delay.c>\nDelayEngine on\n</IfModule>\n\n<IfModule mod_ctrls.c>\nControlsEngine        off\nControlsMaxClients    2\nControlsLog           /var/log/proftpd/controls.log\nControlsInterval      5\nControlsSocket        /var/run/proftpd/proftpd.sock\n</IfModule>\n\n<IfModule mod_ctrls_admin.c>\nAdminControlsEngine off\n</IfModule>\n\n#\n# Alternative authentication frameworks\n#\n#Include /etc/proftpd/ldap.conf\nInclude /etc/proftpd/sql.conf\n\n#\n# This is used for FTPS connections\n#\nInclude /etc/proftpd/tls.conf\n\n#\n# Useful to keep VirtualHost/VirtualRoot directives separated\n#\n#Include /etc/proftpd/virtuals.conf\n\n# A basic anonymous configuration, no upload directories.\n\n# <Anonymous ~ftp>\n#   User\t\t\t\tftp\n#   Group\t\t\t\tnogroup\n#   # We want clients to be able to login with \"anonymous\" as well as \"ftp\"\n#   UserAlias\t\t\tanonymous ftp\n#   # Cosmetic changes, all files belongs to ftp user\n#   DirFakeUser\ton ftp\n#   DirFakeGroup on ftp\n#\n#   RequireValidShell\t\toff\n#\n#   # Limit the maximum number of anonymous logins\n#   MaxClients\t\t\t10\n#\n#   # We want 'welcome.msg' displayed at login, and '.message' displayed\n#   # in each newly chdired directory.\n#   DisplayLogin\t\t\twelcome.msg\n#   DisplayChdir\t\t.message\n#\n#   # Limit WRITE everywhere in the anonymous chroot\n#   <Directory *>\n#     <Limit WRITE>\n#       DenyAll\n#     </Limit>\n#   </Directory>\n#\n#   # Uncomment this if you're brave.\n#   # <Directory incoming>\n#   #   # Umask 022 is a good standard umask to prevent new files and dirs\n#   #   # (second parm) from being group and world writable.\n#   #   Umask\t\t\t\t022  022\n#   #            <Limit READ WRITE>\n#   #            DenyAll\n#   #            </Limit>\n#   #            <Limit STOR>\n#   #            AllowAll\n#   #            </Limit>\n#   # </Directory>\n#\n# </Anonymous>\n\n# Include other custom configuration files\nInclude /etc/proftpd/conf.d/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/modules.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# This file is used to manage DSO modules and features.\n#\n\n# This is the directory where DSO modules reside\n\nModulePath /usr/lib/proftpd\n\n# Allow only user root to load and unload modules, but allow everyone\n# to see which modules have been loaded\n\nModuleControlsACLs insmod,rmmod allow user root\nModuleControlsACLs lsmod allow user *\n\nLoadModule mod_ctrls_admin.c\nLoadModule mod_tls.c\n\n# Install one of proftpd-mod-mysql, proftpd-mod-pgsql or any other\n# SQL backend engine to use this module and the required backend.\n# This module must be mandatory loaded before anyone of\n# the existent SQL backeds.\nLoadModule mod_sql.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_ldap.c\n\n#\n# 'SQLBackend mysql' or 'SQLBackend postgres' (or any other valid backend) directives\n# are required to have SQL authorization working. You can also comment out the\n# unused module here, in alternative.\n#\n\n# Install proftpd-mod-mysql and decomment the previous\n# mod_sql.c module to use this.\nLoadModule mod_sql_mysql.c\n\n# Install proftpd-mod-pgsql and decomment the previous\n# mod_sql.c module to use this.\n#LoadModule mod_sql_postgres.c\n\n# Install proftpd-mod-sqlite and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_sqlite.c\n\n# Install proftpd-mod-odbc and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_odbc.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sql_passwd.c\n\nLoadModule mod_radius.c\nLoadModule mod_quotatab.c\nLoadModule mod_quotatab_file.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_quotatab_ldap.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\nLoadModule mod_quotatab_sql.c\nLoadModule mod_quotatab_radius.c\nLoadModule mod_wrap.c\nLoadModule mod_rewrite.c\nLoadModule mod_load.c\nLoadModule mod_ban.c\nLoadModule mod_wrap2.c\nLoadModule mod_wrap2_file.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_wrap2_sql.c\nLoadModule mod_dynmasq.c\nLoadModule mod_exec.c\nLoadModule mod_shaper.c\nLoadModule mod_ratio.c\nLoadModule mod_site_misc.c\n\nLoadModule mod_sftp.c\nLoadModule mod_sftp_pam.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sftp_sql.c\n\nLoadModule mod_facl.c\nLoadModule mod_unique_id.c\nLoadModule mod_copy.c\nLoadModule mod_deflate.c\nLoadModule mod_ifversion.c\nLoadModule mod_tls_memcache.c\n\n# Install proftpd-mod-geoip to use the GeoIP feature\n#LoadModule mod_geoip.c\n\n# keep this module the last one\nLoadModule mod_ifsession.c\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/sql.conf\" chown=\"root:0\" chmod=\"0600\"\n\t\t\t\t\t\tbackup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Proftpd sample configuration for SQL-based authentication.\n#\n# (This is not to be used if you prefer a PAM-based SQL authentication)\n#\n\n<IfModule mod_sql.c>\n\nDefaultRoot ~\nRequireValidShell off\nAuthOrder mod_sql.c\n\n#\n# Choose a SQL backend among MySQL or PostgreSQL.\n# Both modules are loaded in default configuration, so you have to specify the backend\n# or comment out the unused module in /etc/proftpd/modules.conf.\n# Use 'mysql' or 'postgres' as possible values.\n#\nSQLBackend\tmysql\n#\nSQLEngine on\nSQLAuthenticate on\n#\n# Use both an encrypted or plaintext password\nSQLAuthTypes Crypt OpenSSL\n\nSQLAuthenticate users* groups*\n\n#\n# Connection\nSQLConnectInfo <SQL_DB>@<SQL_HOST> <SQL_UNPRIVILEGED_USER> <SQL_UNPRIVILEGED_PASSWORD>\n#\n# Describes both users/groups tables\n#\nSQLUserInfo ftp_users username password uid gid homedir shell\nSQLGroupInfo ftp_groups groupname gid members\n#\nSQLUserWhereClause \"login_enabled = 'y'\"\n\nSQLLog PASS login\nSQLNamedQuery login UPDATE \"last_login=now(), login_count=login_count+1 WHERE username='%u'\" ftp_users\n\nSQLLog RETR download\nSQLNamedQuery download UPDATE \"down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'\" ftp_users\n\nSQLLog STOR upload\nSQLNamedQuery upload UPDATE \"up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'\" ftp_users\n\nQuotaEngine on\nQuotaShowQuotas on\nQuotaDisplayUnits Mb\nQuotaLock /var/lock/ftpd.quotatab.lock\nQuotaLimitTable sql:/get-quota-limit\nQuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally\nSQLNamedQuery get-quota-limit SELECT \"ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'\"\nSQLNamedQuery get-quota-tally SELECT \"name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'\"\nSQLNamedQuery update-quota-tally UPDATE \"bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'\" ftp_quotatallies\nSQLNamedQuery insert-quota-tally INSERT \"%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}\" ftp_quotatallies\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/tls.conf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n<IfModule mod_tls.c>\nTLSEngine                               on\nTLSLog                                  /var/log/proftpd/tls.log\nTLSProtocol                             TLSv1.2 TLSv1.3\nTLSRSACertificateFile                   /etc/ssl/certs/proftpd.crt\nTLSRSACertificateKeyFile                /etc/ssl/private/proftpd.key\nTLSECCertificateFile                    /etc/ssl/certs/proftpd_ec.crt\nTLSECCertificateKeyFile                 /etc/ssl/private/proftpd_ec.key\n# TLSCACertificateFile\nTLSOptions                              NoSessionReuseRequired\nTLSVerifyClient                         off\n\n# Are clients required to use FTP over TLS when talking to this server?\nTLSRequired                             on\n\n# Allow SSL/TLS renegotiations when the client requests them, but\n# do not force the renegotiations.  Some clients do not support\n# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these\n# clients will close the data connection, or there will be a timeout\n# on an idle data connection.\n#\n#TLSRenegotiate                          required off\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/conf.d/99-froxlor-ratelimit.conf\" chown=\"root:0\"\n\t\t\t\t\t\t  chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n<Class whitelist>\nFrom 127.0.0.1\n</Class>\n\nMaxLoginAttempts 3\n<IfModule mod_ban.c>\n <IfClass whitelist>\n  BanEngine off\n </IfClass>\n <IfClass !whitelist>\n  BanEngine on\n </IfClass>\nBanLog /var/log/proftpd/ban.log\nBanTable /etc/proftpd/ban.tab\nBanMessage \"User %u was banned.\"\nBanOnEvent ClientConnectRate 10/00:00:02 02:00:00 \"Stop connecting frequently\"\nBanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00\nBanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99\nBanControlsACLs all allow user root\n</IfModule>\n\n<IfClass whitelist>\nBanEngine off\nDelayEngine off\n</IfClass>\n\t\t\t\t\t]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service proftpd restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Pureftpd -->\n\t\t\t\t<daemon name=\"pureftpd\" title=\"PureFTPd\">\n\t\t\t\t\t<install><![CDATA[apt-get install pure-ftpd-common pure-ftpd-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/private/pure-ftpd.pem ] || openssl req -x509 -nodes -days 7300 -newkey rsa:4096 -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nopenssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072\nchmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/TLS\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MinUID\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1000\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MySQLConfigFile\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n/etc/pure-ftpd/db/mysql.conf\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/NoAnonymous\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MaxIdleTime\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n15\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/ChrootEveryone\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/PAMAuthentication\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nno\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/db/mysql.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0640\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n##############################################\n#                                            #\n# Sample Pure-FTPd Mysql configuration file. #\n# See README.MySQL for explanations.         #\n#                                            #\n##############################################\n\n\n# Optional : MySQL server name or IP. Don't define this for unix sockets.\n\n# MYSQLServer     127.0.0.1\n\n\n# Optional : MySQL port. Don't define this if a local unix socket is used.\n\n# MYSQLPort       3306\n\n\n# Optional : define the location of mysql.sock if the server runs on this host.\n\nMYSQLSocket      /var/run/mysqld/mysqld.sock\n\n\n# Mandatory : user to bind the server as.\n\nMYSQLUser       <SQL_UNPRIVILEGED_USER>\n\n\n# Mandatory : user password. You must have a password.\n\nMYSQLPassword   <SQL_UNPRIVILEGED_PASSWORD>\n\n\n# Mandatory : database to open.\n\nMYSQLDatabase   <SQL_DB>\n\n\n# Mandatory : how passwords are stored\n# Valid values are : \"cleartext\", \"crypt\", \"sha1\", \"md5\" and \"password\"\n# (\"password\" = MySQL password() function)\n# You can also use \"any\" to try \"crypt\", \"sha1\", \"md5\" *and* \"password\"\n\nMYSQLCrypt      any\n\n\n# In the following directives, parts of the strings are replaced at\n# run-time before performing queries :\n#\n# \\L is replaced by the login of the user trying to authenticate.\n# \\I is replaced by the IP address the user connected to.\n# \\P is replaced by the port number the user connected to.\n# \\R is replaced by the IP address the user connected from.\n# \\D is replaced by the remote IP address, as a long decimal number.\n#\n# Very complex queries can be performed using these substitution strings,\n# especially for virtual hosting.\n\n\n# Query to execute in order to fetch the password\n\nMYSQLGetPW      SELECT password FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Query to execute in order to fetch the system user name or uid\n\nMYSQLGetUID     SELECT uid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default UID - if set this overrides MYSQLGetUID\n\n#MYSQLDefaultUID 1000\n\n\n# Query to execute in order to fetch the system user group or gid\n\nMYSQLGetGID     SELECT gid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default GID - if set this overrides MYSQLGetGID\n\n#MYSQLDefaultGID 1000\n\n\n# Query to execute in order to fetch the home directory\n\nMYSQLGetDir     SELECT homedir FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : query to get the maximal number of files\n# Pure-FTPd must have been compiled with virtual quotas support.\n\n# MySQLGetQTAFS  SELECT QuotaFiles FROM users WHERE User='\\L'\n\n\n# Optional : query to get the maximal disk usage (virtual quotas)\n# The number should be in Megabytes.\n# Pure-FTPd must have been compiled with virtual quotas support.\n\nMySQLGetQTASZ SELECT CASE WHEN panel_customers.diskspace = 0 THEN -1 WHEN panel_customers.diskspace <= -1 THEN 0 ELSE panel_customers.diskspace/1024 END AS QuotaSize FROM panel_customers, ftp_users WHERE username = \"\\L\" AND panel_customers.loginname = SUBSTRING_INDEX('\\L', 'ftp', 1)\n\n\n# Optional : ratios. The server has to be compiled with ratio support.\n\n# MySQLGetRatioUL SELECT ULRatio FROM users WHERE User='\\L'\n# MySQLGetRatioDL SELECT DLRatio FROM users WHERE User='\\L'\n\n\n# Optional : bandwidth throttling.\n# The server has to be compiled with throttling support.\n# Values are in KB/s .\n\n# MySQLGetBandwidthUL SELECT ULBandwidth FROM users WHERE User='\\L'\n# MySQLGetBandwidthDL SELECT DLBandwidth FROM users WHERE User='\\L'\n\n# Enable ~ expansion. NEVER ENABLE THIS BLINDLY UNLESS :\n# 1) You know what you are doing.\n# 2) Real and virtual users match.\n\n# MySQLForceTildeExpansion 1\n\n\n# If you're using a transactionnal storage engine, you can enable SQL\n# transactions to avoid races. Leave this commented if you are using the\n# traditional MyIsam engine.\n\n# MySQLTransactions On\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/CustomerProof\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/Bind\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n21\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/default/pure-ftpd-common\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Configuration for pure-ftpd\n# (this file is sourced by /bin/sh, edit accordingly)\n\n# STANDALONE_OR_INETD\n# valid values are \"standalone\" and \"inetd\".\n# Any change here overrides the setting in debconf.\nSTANDALONE_OR_INETD=standalone\n\n# VIRTUALCHROOT:\n# whether to use binary with virtualchroot support\n# valid values are \"true\" or \"false\"\n# Any change here overrides the setting in debconf.\nVIRTUALCHROOT=false\n\n# UPLOADSCRIPT: if this is set and the daemon is run in standalone mode,\n# pure-uploadscript will also be run to spawn the program given below\n# for handling uploads. see /usr/share/doc/pure-ftpd/README.gz or\n# pure-uploadscript(8)\n\n# example: UPLOADSCRIPT=/usr/local/sbin/uploadhandler.pl\nUPLOADSCRIPT=\n\n# if set, pure-uploadscript will spawn  running as the\n# given uid and gid\nUPLOADUID=\nUPLOADGID=\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pure-ftpd-mysql restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- System tools/services -->\n\t\t\t<service type=\"system\" title=\"{{lng.admin.configfiles.etc}}\">\n\t\t\t\t<!-- Webalizer -->\n\t\t\t\t<daemon name=\"webalizer\"\n\t\t\t\t\ttitle=\"Webalizer (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install webalizer]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- goaccess -->\n\t\t\t\t<daemon name=\"goaccess\"\n\t\t\t\t\ttitle=\"goaccess (traffic analyzer)\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install goaccess jq]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- AWstats -->\n\t\t\t\t<daemon name=\"awstats\"\n\t\t\t\t\ttitle=\"Awstats  (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install awstats]]></install>\n\t\t\t\t\t<command><![CDATA[mv {{settings.system.awstats_conf}}/awstats.conf {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's/^DirData/# DirData/' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's|^\\\\(DirIcons=\\\\).*$|\\\\1\\\\\"/awstats-icon\\\\\"|' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/cron.d/awstats]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/logrotate.d/httpd-prerotate/awstats]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- libnss-extrausers -->\n\t\t\t\t<daemon name=\"libnssextrausers\"\n\t\t\t\t\ttitle=\"libnss-extrausers\">\n\t\t\t\t\t<install><![CDATA[apt-get install libnss-extrausers]]></install>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/extrausers]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/passwd]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/group]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/shadow]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/nsswitch.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Make sure that `passwd`, `group` and `shadow` have extrausers in their lines\n# You should place extrausers at the end, so that it is queried after the other mechanisams\n#\npasswd:         files systemd compat extrausers\ngroup:          files systemd compat extrausers\nshadow:         files compat extrausers\ngshadow:        files\n\nhosts:       files dns\nnetworks:    files dns\n\nprotocols:   db files\nservices:    db files\nethers:      db files\nrpc:         db files\n\nnetmasks:    files\nnetgroup:    files\nbootparams:  files\n\nautomount:   files\naliases:     files\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mv /var/spool/postfix/etc/nsswitch.conf /var/spool/postfix/etc/nsswitch.conf.frx.bak]]></command>\n\t\t\t\t\t\t<command><![CDATA[cp /etc/nsswitch.conf /var/spool/postfix/etc/nsswitch.conf]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Logrotate -->\n\t\t\t\t<daemon name=\"logrotate\" title=\"Logrotate\">\n\t\t\t\t\t<install><![CDATA[apt-get install logrotate]]></install>\n\t\t\t\t\t<file name=\"/etc/logrotate.d/froxlor\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Froxlor logrotate snippet\n#\n<CUSTOMER_LOGS>*.log {\n  missingok\n  daily\n  rotate 7\n  compress\n  delaycompress\n  notifempty\n  create\n  sharedscripts\n  postrotate\n  <WEBSERVER_RELOAD_CMD> > /dev/null 2>&1 || true\n  endscript\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- FCGID -->\n\t\t\t\t<daemon name=\"fcgid\" title=\"FCGID\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2-suexec-pristine libapache2-mod-fcgid php-cgi]]></install>\n\t\t\t\t\t<command><![CDATA[a2enmod suexec fcgid]]></command>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.mod_fcgid_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.system.mod_fcgid_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.system.mod_fcgid_httpgroup}} {{settings.system.mod_fcgid_httpuser}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_configdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 1777 {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php7.4]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- PHP-FPM -->\n\t\t\t\t<daemon name=\"php-fpm\"\n\t\t\t\t\ttitle=\"PHP-FPM\">\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install apache2-suexec-pristine]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<install><![CDATA[apt-get install php-fpm]]></install>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2enmod suexec proxy_fcgi actions]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"usernamenotexists\">{{settings.phpfpm.vhost_httpuser}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.phpfpm.vhost_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.phpfpm.vhost_httpgroup}} {{settings.phpfpm.vhost_httpuser}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"4\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php7.4]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Cronjob -->\n\t\t\t\t<daemon name=\"cron\" title=\"Cronjob for froxlor\"\n\t\t\t\t\tmandatory=\"true\">\n\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install cron gnupg]]></install>\n\t\t\t\t\t<command><![CDATA[[ ! -e /usr/local/bin/froxlor-cli ] && ln -s <BASE_PATH>bin/froxlor-cli /usr/local/bin/froxlor-cli]]></command>\n\t\t\t\t\t<command><![CDATA[/usr/bin/php <BASE_PATH>bin/froxlor-cli froxlor:cron --run-task 99]]></command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.crondreload}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t</services>\n\t</distribution>\n</froxlor>\n"
  },
  {
    "path": "lib/configfiles/index.html",
    "content": ""
  },
  {
    "path": "lib/configfiles/jammy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<froxlor>\n\t<distribution name=\"Ubuntu\" codename=\"Jammy\"\n\t\tversion=\"22.04\" defaulteditor=\"/bin/nano\">\n\t\t<!-- OS defaults to be loaded on installation -->\n\t\t<defaults>\n\t\t\t<default settinggroup=\"system\" varname=\"nssextrausers\" value=\"1\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_vhost\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_diroptions\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_htpasswddir\" value=\"/etc/nginx/froxlor-htpasswd/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apachereload_command\" value=\"service nginx reload\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"letsencryptacmeconf\" value=\"/etc/nginx/acme.conf\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"phpfpm\" varname=\"fastcgi_ipcdir\" value=\"/var/run/php/\"></default>\n\t\t</defaults>\n\t\t<services>\n\t\t\t<!-- HTTP -->\n\t\t\t<service type=\"http\" title=\"{{lng.admin.configfiles.http}}\">\n\t\t\t\t<!-- general HTTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.documentroot_prefix}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.logfiles_directory}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"notempty\">{{settings.system.deactivateddocroot}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.deactivateddocroot}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- HTTP Apache -->\n\t\t\t\t<daemon name=\"apache\" version=\"2.4\" title=\"Apache 2.4\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2]]></install>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command><![CDATA[a2dismod userdir]]></command>\n\t\t\t\t\t<command><![CDATA[a2enmod headers]]></command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.use_ssl}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[a2enmod ssl]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n# Please remember to activate the use of mod_proxy / mod_proxy_fcgi in the PHP-FPM settings!!!\na2enmod proxy_fcgi\n]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nAlias \"/.well-known/acme-challenge\" \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\"\n<Directory \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\">\n\tRequire all granted\n</Directory>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- HTTP Nginx -->\n\t\t\t\t<daemon name=\"nginx\" title=\"nginx\">\n\t\t\t\t\t<install><![CDATA[apt-get install nginx]]></install>\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install php-cgi]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<file name=\"/etc/nginx/nginx.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nuser www-data;\nworker_processes auto;\npid /var/run/nginx.pid;\ninclude /etc/nginx/modules-enabled/*.conf;\n\nevents {\n\tworker_connections 768;\n\t# multi_accept on;\n}\n\nhttp {\n\n\t##\n\t# Basic Settings\n\t##\n\n\tsendfile on;\n\ttcp_nopush on;\n\ttcp_nodelay on;\n\tkeepalive_timeout 65;\n\ttypes_hash_max_size 2048;\n\t# server_tokens off;\n\n\t# server_names_hash_bucket_size 64;\n\t# server_name_in_redirect off;\n\n\tinclude /etc/nginx/mime.types;\n\tdefault_type application/octet-stream;\n\n\t##\n\t# Logging Settings\n\t##\n\n\taccess_log /var/log/nginx/access.log;\n\terror_log /var/log/nginx/error.log;\n\n\t##\n\t# Gzip Settings\n\t##\n\n\tgzip on;\n\tgzip_disable \"msie6\";\n\n\t# gzip_vary on;\n\t# gzip_proxied any;\n\t# gzip_comp_level 6;\n\t# gzip_buffers 16 8k;\n\t# gzip_http_version 1.1;\n\t# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;\n\n\t##\n\t# nginx-naxsi config\n\t##\n\t# Uncomment it if you installed nginx-naxsi\n\t##\n\n\t#include /etc/nginx/naxsi_core.rules;\n\n\t##\n\t# nginx-passenger config\n\t##\n\t# Uncomment it if you installed nginx-passenger\n\t##\n\n\t#passenger_root /usr;\n\t#passenger_ruby /usr/bin/ruby;\n\n\t##\n\t# Virtual Host Configs\n\t##\n\n\tinclude /etc/nginx/conf.d/*.conf;\n\tinclude /etc/nginx/sites-enabled/*;\n}\n\n\n#mail {\n#\t# See sample authentication script at:\n#\t# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript\n#\n#\t# auth_http localhost/auth.php;\n#\t# pop3_capabilities \"TOP\" \"USER\";\n#\t# imap_capabilities \"IMAP4rev1\" \"UIDPLUS\";\n#\n#\tserver {\n#\t\tlisten     localhost:110;\n#\t\tprotocol   pop3;\n#\t\tproxy      on;\n#\t}\n#\n#\tserver {\n#\t\tlisten     localhost:143;\n#\t\tprotocol   imap;\n#\t\tproxy      on;\n#\t}\n#}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/nginx/fastcgi_params\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nfastcgi_connect_timeout 65;\nfastcgi_send_timeout    180;\nfastcgi_read_timeout    180;\n\nfastcgi_param  QUERY_STRING       $query_string;\nfastcgi_param  REQUEST_METHOD     $request_method;\nfastcgi_param  CONTENT_TYPE       $content_type;\nfastcgi_param  CONTENT_LENGTH     $content_length;\n\nfastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\nfastcgi_param  REQUEST_URI        $request_uri;\nfastcgi_param  DOCUMENT_URI       $document_uri;\nfastcgi_param  DOCUMENT_ROOT      $document_root;\nfastcgi_param  SERVER_PROTOCOL    $server_protocol;\nfastcgi_param  HTTPS              $https if_not_empty;\n\nfastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\nfastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\nfastcgi_param  REMOTE_ADDR        $remote_addr;\nfastcgi_param  REMOTE_PORT        $remote_port;\nfastcgi_param  SERVER_ADDR        $server_addr;\nfastcgi_param  SERVER_PORT        $server_port;\nfastcgi_param  SERVER_NAME        $server_name;\n\n# PHP only, required if PHP was built with --enable-force-cgi-redirect\nfastcgi_param  REDIRECT_STATUS    200;\n\n# Fix for HTTP/3 ($http_host is not populated automatically)\nfastcgi_param  HTTP_HOST          $host;\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nlocation /.well-known/acme-challenge {\n\talias {{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge;\n\n\tlocation ~ /.well-known/acme-challenge/(.*) {\n\t\tdefault_type text/plain;\n\t}\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/init.d/php-fcgi\" chmod=\"u+x\" backup=\"true\">\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n#!/bin/bash\nBIND=\"127.0.0.1:8888\"\nUSER=\"www-data\"\nPHP_FCGI_CHILDREN=\"15\"\nPHP_FCGI_MAX_REQUESTS=\"1000\"\n\nPHP_CGI=\"/usr/bin/php-cgi\"\nPHP_CGI_NAME=\"$(basename ${PHP_CGI})\"\nPHP_CGI_ARGS=\"- USER=${USER} PATH=/usr/bin PHP_FCGI_CHILDREN=${PHP_FCGI_CHILDREN} PHP_FCGI_MAX_REQUESTS=${PHP_FCGI_MAX_REQUESTS} ${PHP_CGI} -b ${BIND}\"\nRETVAL=\"0\"\n\nstart() {\n      echo -n \"Starting PHP FastCGI: \"\n      start-stop-daemon --quiet --start --background --chuid \"$USER\" --exec /usr/bin/env -- $PHP_CGI_ARGS\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\nstop() {\n      echo -n \"Stopping PHP FastCGI: \"\n      killall -q -w -u ${USER} ${PHP_CGI}\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\n\ncase \"$1\" in\n    start)\n      start\n  ;;\n    stop)\n      stop\n  ;;\n    restart)\n      stop\n      start\n  ;;\n    *)\n      echo \"Usage: php-fastcgi {start|stop|restart}\"\n      exit 1\n  ;;\nesac\nexit \"$RETVAL\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[/etc/init.d/php-fcgi restart]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!--DNS -->\n\t\t\t<service type=\"dns\" title=\"{{lng.admin.configfiles.dns}}\">\n\t\t\t\t<!--Bind9 -->\n\t\t\t\t<daemon name=\"bind\" title=\"Bind9 nameserver\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install bind9]]></install>\n\t\t\t\t\t<command><![CDATA[echo \"include \\\"{{settings.system.bindconf_directory}}froxlor_bind.conf\\\";\" >> /etc/bind/named.conf.local]]></command>\n\t\t\t\t\t<command><![CDATA[touch {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chown bind:0 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chmod 0644 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[service bind9 restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns\" title=\"PowerDNS (standalone)\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server pdns-backend-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\nallow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# allow-recursion\tList of subnets that are allowed to recurse\n#\nallow-recursion=127.0.0.1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# recursive-cache-ttl\tSeconds to store packets for recursive queries in the PacketCache\n#\n# recursive-cache-ttl=10\n\n#################################\n# recursor\tIf recursion is desired, IP address of a recursing nameserver\n#\n# recursor=no\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# mysql-settings / you need to create the power-dns database for yourself!\nlaunch=gmysql\ngmysql-host=127.0.0.1\ngmysql-port=3306\ngmysql-dbname=pdns\ngmysql-user=powerdns\ngmysql-group=client\ngmysql-password=\n#gmysql-ssl-ca-file=\n#gmysql-ssl-verify-server-certificate=0\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns_bind\"\n\t\t\t\t\ttitle=\"PowerDNS via bind-backend\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\n# allow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# allow-recursion\tList of subnets that are allowed to recurse\n#\nallow-recursion=127.0.0.1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\nlaunch=bind\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# recursive-cache-ttl\tSeconds to store packets for recursive queries in the PacketCache\n#\n# recursive-cache-ttl=10\n\n#################################\n# recursor\tIf recursion is desired, IP address of a recursing nameserver\n#\n# recursor=no\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# Bind backend configuration\n\n# Location of the Bind configuration file to parse.\nbind-config=<BIND_CONFIG_PATH>named.conf\n\n# How often to check for zone changes. See 'Operation' section.\nbind-check-interval=180\n\n# Uncomment to enable Huffman compression on zone data.\n# Currently saves around 20% of memory actually used, but slows down operation.\n# bind-enable-huffman\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- SMTP services -->\n\t\t\t<service type=\"smtp\" title=\"{{lng.admin.configfiles.smtp}}\">\n\t\t\t\t<!-- general SMTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"groupnotexists\">{{settings.system.vmail_gid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[groupadd -g {{settings.system.vmail_gid}} vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"usernotexists\">{{settings.system.vmail_uid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[useradd -u {{settings.system.vmail_uid}} -g vmail vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install postfix postfix-mysql]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/etc/pam.d]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/var/run/mysqld]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R {{settings.system.vmail_uid}}:{{settings.system.vmail_gid}} {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0750  {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"0\">\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_alias_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT destination FROM mail_virtual AS v, panel_customers AS c WHERE c.customerid = v.customerid AND c.deactivated = 0 AND v.email = '%s' AND trim(v.destination) <> ''\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_domains.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' AND deactivated = 0\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_sender_permissions.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT GROUP_CONCAT(DISTINCT mu.username SEPARATOR ' ') AS sasl_users FROM mail_users mu WHERE mu.username = '%s' OR mu.email IN ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = CONCAT('@', SUBSTRING_INDEX('%s','@',-1))));\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_uid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT uid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_gid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT gid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/aliases\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# /etc/aliases\nmailer-daemon: postmaster\npostmaster: root\nnobody: root\nhostmaster: root\nusenet: root\nnews: root\nwebmaster: root\nwww: root\nftp: root\nabuse: root\nnoc: root\nsecurity: root\n\n# change this to a valid e-mail address you can access\nroot:               <ADMIN_MAIL>\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[newaliases]]></command>\n\t\t\t\t\t\t<command><![CDATA[service postfix restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- postfix with dovecot -->\n\t\t\t\t<daemon name=\"postfix_dovecot\" title=\"Postfix with dovecot\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<file name=\"/etc/postfix/main.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# See /usr/share/postfix/main.cf.dist for a commented, more complete version\n\n\n# Debian specific:  Specifying a file name will cause the first\n# line of that file to be used as the name.  The Debian default\n# is /etc/mailname.\n#myorigin = /etc/mailname\n\nsmtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)\nbiff = no\n\n# appending .domain is the MUA's job.\nappend_dot_mydomain = no\n\n# Uncomment the next line to generate \"delayed mail\" warnings\n#delay_warning_time = 4h\n\nreadme_directory = no\n\n# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on\n# fresh installs.\ncompatibility_level = 2\n\n# INTERNET HOST AND DOMAIN NAMES\n#\n# The myhostname parameter specifies the internet hostname of this\n# mail system. The default is to use the fully-qualified domain name\n# from gethostname(). $myhostname is used as a default value for many\n# other configuration parameters.\n#\n# Froxlor Note: $myhostname can and should be the same as $mydomain as long as\n# you don't intend to send mail to it (it will be considered local, not virtual)\n# for the case of a subdomain, $mydomain *must* be equal to $myhostname,\n# otherwise you cannot use the main domain for virtual transport.\n# also check the note about $mydomain below.\nmyhostname = $mydomain\n#myhostname = virtual.domain.tld\n\n# The mydomain parameter specifies the local internet domain name.\n# The default is to use $myhostname minus the first component.\n# $mydomain is used as a default value for many other configuration\n# parameters.\n#\n# Froxlor Note: We are using a default here but that may or may not make sense,\n# depending on your dns configuration, please check yourself.\n\n# FQDN from Froxlor\nmydomain = <SERVERNAME>\n\nmydestination = $myhostname, localhost.$mydomain, localhost\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain,\n#\tmail.$mydomain, www.$mydomain, ftp.$mydomain\n\n# The default setting is 550 (reject mail) but it is safer to start\n# with 450 (try again later) until you are certain that your\n# local_recipient_maps settings are OK.\n#\nunknown_local_recipient_reject_code = 550\n\n# The mailbox_command parameter specifies the optional external\n# command to use instead of mailbox delivery. The command is run as\n# the recipient with proper HOME, SHELL and LOGNAME environment settings.\n# Exception:  delivery for root is done as $default_user.\n#\n# Other environment variables of interest: USER (recipient username),\n# EXTENSION (address extension), DOMAIN (domain part of address),\n# and LOCAL (the address localpart).\n#\n# Unlike other Postfix configuration parameters, the mailbox_command\n# parameter is not subjected to $parameter substitutions. This is to\n# make it easier to specify shell syntax (see example below).\n#\n# Avoid shell meta characters because they will force Postfix to run\n# an expensive shell process. Procmail alone is expensive enough.\n#\n# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN\n# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER.\n#\nmailbox_command = /usr/lib/dovecot/deliver\n#mailbox_command = /usr/bin/procmail -a \"$EXTENSION\"\n\n# The debugger_command specifies the external command that is executed\n# when a Postfix daemon program is run with the -D option.\n#\n# Use \"command .. & sleep 5\" so that the debugger can attach before\n# the process marches on. If you use an X-based debugger, be sure to\n# set up your XAUTHORITY environment variable before starting Postfix.\n#\ndebugger_command =\n\t PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin\n\t ddd $daemon_directory/$process_name $process_id & sleep 5\n\ninet_protocols = ipv4\n\nsmtpd_helo_required = yes\nsmtpd_recipient_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unauth_destination,\n\treject_unauth_pipelining,\n\treject_non_fqdn_recipient\nsmtpd_sender_restrictions = permit_mynetworks,\n\treject_sender_login_mismatch,\n\tpermit_sasl_authenticated,\n\treject_unknown_helo_hostname,\n\treject_unknown_recipient_domain,\n\treject_unknown_sender_domain\nsmtpd_client_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unknown_client_hostname\n\n# Postfix 2.10 requires this option. Postfix < 2.10 ignores this.\n# The option is intentionally left empty.\nsmtpd_relay_restrictions =\n\n# Maximum size of Message in bytes (50MB)\nmessage_size_limit = 52428800\n\n## SASL Auth Settings\nsmtpd_sasl_auth_enable = yes\nsmtpd_sasl_local_domain = $myhostname\nbroken_sasl_auth_clients = yes\n## Dovecot Settings for deliver, SASL Auth and virtual transport\nsmtpd_sasl_type = dovecot\nvirtual_transport = dovecot\ndovecot_destination_recipient_limit = 1\nsmtpd_sasl_path = private/auth\n\n# Virtual delivery settings\nvirtual_mailbox_base = /\nvirtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf\nvirtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf\nvirtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_alias_maps.cf\nsmtpd_sender_login_maps = proxy:mysql:/etc/postfix/mysql-virtual_sender_permissions.cf\nvirtual_uid_maps = static:<VIRTUAL_UID_MAPS>\nvirtual_gid_maps = static:<VIRTUAL_GID_MAPS>\n\n# Local delivery settings\nlocal_transport = local\nalias_maps = $alias_database\n\n# Default Mailbox size, is set to 0 which means unlimited!\nmailbox_size_limit = 0\nvirtual_mailbox_limit = 0\n\n### TLS settings\n###\n## TLS for outgoing mails from the server to another server\nsmtp_tls_security_level = may\nsmtp_tls_note_starttls_offer = yes\n## TLS for incoming connections (clients or other mail servers)\nsmtpd_tls_security_level = may\nsmtpd_tls_cert_file = <SSL_CERT_FILE>\nsmtpd_tls_key_file = <SSL_KEY_FILE>\n#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt\nsmtpd_tls_loglevel = 1\nsmtpd_tls_received_header = yes\nsmtp_use_tls = yes\nsmtpd_use_tls = yes\nsmtpd_tls_session_cache_timeout = 3600s\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/files[@index=0]</include>\n\t\t\t\t\t<file name=\"/etc/postfix/master.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Postfix master process configuration file.  For details on the format\n# of the file, see the master(5) manual page (command: \"man 5 master\" or\n# on-line: http://www.postfix.org/master.5.html).\n#\n# Do not forget to execute \"postfix reload\" after editing this file.\n#\n# ==========================================================================\n# service type  private unpriv  chroot  wakeup  maxproc command + args\n#               (yes)   (yes)   (no)    (never) (100)\n# ==========================================================================\n#smtp      inet  n       -       y       -       -       smtpd\nsmtp      inet  n       -       y       -       1       postscreen\nsmtpd     pass  -       -       y       -       -       smtpd\ndnsblog   unix  -       -       y       -       0       dnsblog\ntlsproxy  unix  -       -       y       -       0       tlsproxy\nsubmission inet n       -       y       -       -       smtpd\n  -o syslog_name=postfix/submission\n  -o smtpd_tls_security_level=encrypt\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\nsmtps     inet  n       -       y       -       -       smtpd\n  -o syslog_name=postfix/smtps\n  -o smtpd_tls_wrappermode=yes\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\n#628       inet  n       -       y       -       -       qmqpd\npickup    unix  n       -       y       60      1       pickup\n         -o content_filter=\n         -o receive_override_options=no_header_body_checks\ncleanup   unix  n       -       n       -       0       cleanup\nqmgr      unix  n       -       n       300     1       qmgr\n#qmgr     unix  n       -       n       300     1       oqmgr\ntlsmgr    unix  -       -       n       1000?   1       tlsmgr\nrewrite   unix  -       -       n       -       -       trivial-rewrite\nbounce    unix  -       -       n       -       0       bounce\ndefer     unix  -       -       n       -       0       bounce\ntrace     unix  -       -       n       -       0       bounce\nverify    unix  -       -       n       -       1       verify\nflush     unix  n       -       n       1000?   0       flush\nproxymap  unix  -       -       n       -       -       proxymap\nproxywrite unix -       -       n       -       1       proxymap\nsmtp      unix  -       -       n       -       -       smtp\nrelay     unix  -       -       n       -       -       smtp\n        -o syslog_name=postfix/$service_name\n#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5\nshowq     unix  n       -       n       -       -       showq\nerror     unix  -       -       n       -       -       error\nretry     unix  -       -       n       -       -       error\ndiscard   unix  -       -       n       -       -       discard\nlocal     unix  -       n       n       -       -       local\nvirtual   unix  -       n       n       -       -       virtual\nlmtp      unix  -       -       n       -       -       lmtp\nanvil     unix  -       -       n       -       1       anvil\nscache    unix  -       -       n       -       1       scache\npostlog   unix-dgram n  -       n       -       1       postlogd\n#\n# ====================================================================\n# Interfaces to non-Postfix software. Be sure to examine the manual\n# pages of the non-Postfix software to find out what options it wants.\n#\n# Many of the following services use the Postfix pipe(8) delivery\n# agent.  See the pipe(8) man page for information about ${recipient}\n# and other message envelope options.\n# ====================================================================\n#\n# maildrop. See the Postfix MAILDROP_README file for details.\n# Also specify in main.cf: maildrop_destination_recipient_limit=1\n#\n#maildrop  unix  -       n       n       -       -       pipe\n#  flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}\n#\n# ====================================================================\n#\n# Recent Cyrus versions can use the existing \"lmtp\" master.cf entry.\n#\n# Specify in cyrus.conf:\n#   lmtp    cmd=\"lmtpd -a\" listen=\"localhost:lmtp\" proto=tcp4\n#\n# Specify in main.cf one or more of the following:\n#  mailbox_transport = lmtp:inet:localhost\n#  virtual_transport = lmtp:inet:localhost\n#\n# ====================================================================\n#\n# Cyrus 2.1.5 (Amos Gouaux)\n# Also specify in main.cf: cyrus_destination_recipient_limit=1\n#\n#cyrus     unix  -       n       n       -       -       pipe\n#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}\n#\n# ====================================================================\n#\n# Old example of delivery via Cyrus.\n#\n#old-cyrus unix  -       n       n       -       -       pipe\n#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}\n#\n# ====================================================================\n#\n# See the Postfix UUCP_README file for configuration details.\n#\n#uucp      unix  -       n       n       -       -       pipe\n#  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)\n#\n# ====================================================================\n#\n# Other external delivery methods.\n#\n#ifmail    unix  -       n       n       -       -       pipe\n#  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)\n#\n#bsmtp     unix  -       n       n       -       -       pipe\n#  flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient\n#\n#scalemail-backend unix -       n       n       -       2       pipe\n#  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store\n#  ${nexthop} ${user} ${extension}\n#\n#mailman   unix  -       n       n       -       -       pipe\n#  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py\n#  ${nexthop} ${user}\n#\n# Dovecot LDA\ndovecot\t  unix\t-\tn\tn\t-\t-\tpipe\n\tflags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ${recipient}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- IMAP/POP3 services -->\n\t\t\t<service type=\"mail\" title=\"{{lng.admin.configfiles.mail}}\">\n\t\t\t\t<!-- valid for both dovecots -->\n\t\t\t\t<general>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-managesieved dovecot-sieve]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/dovecot/dovecot.conf\" chown=\"root:root\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## Dovecot configuration file\n\n# If you're in a hurry, see http://wiki2.dovecot.org/QuickConfiguration\n\n# \"doveconf -n\" command gives a clean output of the changed settings. Use it\n# instead of copy&pasting files when posting to the Dovecot mailing list.\n\n# '#' character and everything after it is treated as comments. Extra spaces\n# and tabs are ignored. If you want to use either of these explicitly, put the\n# value inside quotes, eg.: key = \"# char and trailing whitespace  \"\n\n# Most (but not all) settings can be overridden by different protocols and/or\n# source/destination IPs by placing the settings inside sections, for example:\n# protocol imap { }, local 127.0.0.1 { }, remote 10.0.0.0/8 { }\n\n# Default values are shown for each setting, it's not required to uncomment\n# those. These are exceptions to this though: No sections (e.g. namespace {})\n# or plugin settings are added by default, they're listed only as examples.\n# Paths are also just examples with the real defaults being based on configure\n# options. The paths listed here are for configure --prefix=/usr\n# --sysconfdir=/etc --localstatedir=/var\n\n# Enable installed protocols\n!include_try /usr/share/dovecot/protocols.d/*.protocol\n\n# A comma separated list of IPs or hosts where to listen in for connections.\n# \"*\" listens in all IPv4 interfaces, \"::\" listens in all IPv6 interfaces.\n# If you want to specify non-default ports or anything more complex,\n# edit conf.d/master.conf.\n#listen = *, ::\n\n# Base directory where to store runtime data.\n#base_dir = /var/run/dovecot/\n\n# Name of this instance. In multi-instance setup doveadm and other commands\n# can use -i <instance_name> to select which instance is used (an alternative\n# to -c <config_path>). The instance name is also added to Dovecot processes\n# in ps output.\n#instance_name = dovecot\n\n# Greeting message for clients.\n#login_greeting = Dovecot ready.\n\n# Space separated list of trusted network ranges. Connections from these\n# IPs are allowed to override their IP addresses and ports (for logging and\n# for authentication checks). disable_plaintext_auth is also ignored for\n# these networks. Typically you'd specify your IMAP proxy servers here.\n#login_trusted_networks =\n\n# Space separated list of login access check sockets (e.g. tcpwrap)\n#login_access_sockets =\n\n# With proxy_maybe=yes if proxy destination matches any of these IPs, don't do\n# proxying. This isn't necessary normally, but may be useful if the destination\n# IP is e.g. a load balancer's IP.\n#auth_proxy_self =\n\n# Show more verbose process titles (in ps). Currently shows user name and\n# IP address. Useful for seeing who are actually using the IMAP processes\n# (eg. shared mailboxes or if same uid is used for multiple accounts).\n#verbose_proctitle = no\n\n# Should all processes be killed when Dovecot master process shuts down.\n# Setting this to \"no\" means that Dovecot can be upgraded without\n# forcing existing client connections to close (although that could also be\n# a problem if the upgrade is e.g. because of a security fix).\n#shutdown_clients = yes\n\n# If non-zero, run mail commands via this many connections to doveadm server,\n# instead of running them directly in the same process.\n#doveadm_worker_count = 0\n# UNIX socket or host:port used for connecting to doveadm server\n#doveadm_socket_path = doveadm-server\n\n# Space separated list of environment variables that are preserved on Dovecot\n# startup and passed down to all of its child processes. You can also give\n# key=value pairs to always set specific settings.\n#import_environment = TZ\n\n##\n## Dictionary server settings\n##\n\n# Dictionary can be used to store key=value lists. This is used by several\n# plugins. The dictionary can be accessed either directly or though a\n# dictionary server. The following dict block maps dictionary names to URIs\n# when the server is used. These can then be referenced using URIs in format\n# \"proxy::<name>\".\n\ndict {\n  #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext\n  #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext\n}\n\n# Most of the actual configuration gets included below. The filenames are\n# first sorted by their ASCII value and parsed in that order. The 00-prefixes\n# in filenames are intended to make it easier to understand the ordering.\n!include conf.d/*.conf\n\n# A config file can also tried to be included without giving an error if\n# it's not found:\n!include_try local.conf\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/dovecot-sql.conf.ext\"\n\t\t\t\t\t\t\tchown=\"root:root\" chmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# This file is commonly accessed via passdb {} or userdb {} section in\n# conf.d/auth-sql.conf.ext\n\n# This file is opened as root, so it should be owned by root and mode 0600.\n#\n# http://wiki2.dovecot.org/AuthDatabase/SQL\n#\n# For the sql passdb module, you'll need a database with a table that\n# contains fields for at least the username and password. If you want to\n# use the user@domain syntax, you might want to have a separate domain\n# field as well.\n#\n# If your users all have the same uig/gid, and have predictable home\n# directories, you can use the static userdb module to generate the home\n# dir based on the username and domain. In this case, you won't need fields\n# for home, uid, or gid in the database.\n#\n# If you prefer to use the sql userdb module, you'll want to add fields\n# for home, uid, and gid. Here is an example table:\n#\n# CREATE TABLE users (\n#     username VARCHAR(128) NOT NULL,\n#     domain VARCHAR(128) NOT NULL,\n#     password VARCHAR(64) NOT NULL,\n#     home VARCHAR(255) NOT NULL,\n#     uid INTEGER NOT NULL,\n#     gid INTEGER NOT NULL,\n#     active CHAR(1) DEFAULT 'Y' NOT NULL\n# );\n\n# Database driver: mysql, pgsql, sqlite\ndriver = mysql\n\n# Database connection string. This is driver-specific setting.\n#\n# HA / round-robin load-balancing is supported by giving multiple host\n# settings, like: host=sql1.host.org host=sql2.host.org\n#\n# pgsql:\n#   For available options, see the PostgreSQL documentation for the\n#   PQconnectdb function of libpq.\n#   Use maxconns=n (default 5) to change how many connections Dovecot can\n#   create to pgsql.\n#\n# mysql:\n#   Basic options emulate PostgreSQL option names:\n#     host, port, user, password, dbname\n#\n#   But also adds some new settings:\n#     client_flags           - See MySQL manual\n#     ssl_ca, ssl_ca_path    - Set either one or both to enable SSL\n#     ssl_cert, ssl_key      - For sending client-side certificates to server\n#     ssl_cipher             - Set minimum allowed cipher security (default: HIGH)\n#     ssl_verify_server_cert - Verify that the name in the server SSL certificate\n#                              matches the host (default: no)\n#     option_file            - Read options from the given file instead of\n#                              the default my.cnf location\n#     option_group           - Read options from the given group (default: client)\n#\n#   You can connect to UNIX sockets by using host: host=/var/run/mysql.sock\n#   Note that currently you can't use spaces in parameters.\n#\n# sqlite:\n#   The path to the database file.\n#\n# Examples:\n#   connect = host=192.168.1.1 dbname=users\n#   connect = host=sql.example.com dbname=virtual user=virtual password=blarg\n#   connect = /etc/dovecot/authdb.sqlite\n#\nconnect = \"host=<SQL_HOST> dbname=<SQL_DB> user=<SQL_UNPRIVILEGED_USER> password=<SQL_UNPRIVILEGED_PASSWORD>\"\n\n# Default password scheme.\n#\n# List of supported schemes is in\n# http://wiki2.dovecot.org/Authentication/PasswordSchemes\n#\n#default_pass_scheme = CRYPT\n\n# passdb query to retrieve the password. It can return fields:\n#   password - The user's password. This field must be returned.\n#   user - user@domain from the database. Needed with case-insensitive lookups.\n#   username and domain - An alternative way to represent the \"user\" field.\n#\n# The \"user\" field is often necessary with case-insensitive lookups to avoid\n# e.g. \"name\" and \"nAme\" logins creating two different mail directories. If\n# your user and domain names are in separate fields, you can return \"username\"\n# and \"domain\" fields instead of \"user\".\n#\n# The query can also return other fields which have a special meaning, see\n# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields\n#\n# Commonly used available substitutions (see http://wiki2.dovecot.org/Variables\n# for full list):\n#   %u = entire user@domain\n#   %n = user part of user@domain\n#   %d = domain part of user@domain\n#\n# Note that these can be used only as input to SQL query. If the query outputs\n# any of these substitutions, they're not touched. Otherwise it would be\n# difficult to have eg. usernames containing '%' characters.\n#\n# Example:\n#   password_query = SELECT userid AS user, pw AS password \\\n#     FROM users WHERE userid = '%u' AND active = 'Y'\n#\n#password_query = \\\n#  SELECT username, domain, password \\\n#  FROM users WHERE username = '%n' AND domain = '%d'\n\n# userdb query to retrieve the user information. It can return fields:\n#   uid - System UID (overrides mail_uid setting)\n#   gid - System GID (overrides mail_gid setting)\n#   home - Home directory\n#   mail - Mail location (overrides mail_location setting)\n#\n# None of these are strictly required. If you use a single UID and GID, and\n# home or mail directory fits to a template string, you could use userdb static\n# instead. For a list of all fields that can be returned, see\n# http://wiki2.dovecot.org/UserDatabase/ExtraFields\n#\n# Examples:\n#   user_query = SELECT home, uid, gid FROM users WHERE userid = '%u'\n#   user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u'\n#   user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u'\n#\n#user_query = \\\n#  SELECT home, uid, gid \\\n#  FROM users WHERE username = '%n' AND domain = '%d'\nuser_query = SELECT CONCAT(homedir, maildir) AS home, CONCAT('maildir:', homedir, maildir) AS mail, uid, gid, CONCAT('*:storage=', quota, 'M') as quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u')\n\n# If you wish to avoid two SQL lookups (passdb + userdb), you can use\n# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll\n# also have to return userdb fields in password_query prefixed with \"userdb_\"\n# string. For example:\n#password_query = \\\n#  SELECT userid AS user, password, \\\n#    home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \\\n#  FROM users WHERE userid = '%u'\npassword_query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid,  CONCAT('maildir:', homedir, maildir) AS userdb_mail, CONCAT('*:storage=', quota, 'M') as userdb_quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') AND ((imap = 1 AND '%Ls' = 'imap') OR (pop3 = 1 AND '%Ls' = 'pop3') OR ((postfix = 'Y' AND '%Ls' = 'smtp') OR (postfix = 'Y' AND '%Ls' = 'sieve')))\n\n# Query to get a list of all usernames.\niterate_query = \"SELECT username AS user FROM mail_users WHERE (imap = 1 OR pop3 = 1)\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-auth.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Authentication processes\n##\n\n# Disable LOGIN command and all other plaintext authentications unless\n# SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP\n# matches the local IP (ie. you're connecting from the same computer), the\n# connection is considered secure and plaintext authentication is allowed.\n# See also ssl=required setting.\ndisable_plaintext_auth = no\n\n# Authentication cache size (e.g. 10M). 0 means it's disabled. Note that\n# bsdauth, PAM and vpopmail require cache_key to be set for caching to be used.\n#auth_cache_size = 0\n# Time to live for cached data. After TTL expires the cached record is no\n# longer used, *except* if the main database lookup returns internal failure.\n# We also try to handle password changes automatically: If user's previous\n# authentication was successful, but this one wasn't, the cache isn't used.\n# For now this works only with plaintext authentication.\n#auth_cache_ttl = 1 hour\n# TTL for negative hits (user not found, password mismatch).\n# 0 disables caching them completely.\n#auth_cache_negative_ttl = 1 hour\n\n# Space separated list of realms for SASL authentication mechanisms that need\n# them. You can leave it empty if you don't want to support multiple realms.\n# Many clients simply use the first one listed here, so keep the default realm\n# first.\n#auth_realms =\n\n# Default realm/domain to use if none was specified. This is used for both\n# SASL realms and appending @domain to username in plaintext logins.\n#auth_default_realm =\n\n# List of allowed characters in username. If the user-given username contains\n# a character not listed in here, the login automatically fails. This is just\n# an extra check to make sure user can't exploit any potential quote escaping\n# vulnerabilities with SQL/LDAP databases. If you want to allow all characters,\n# set this value to empty.\n#auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@\n\n# Username character translations before it's looked up from databases. The\n# value contains series of from -> to characters. For example \"#@/@\" means\n# that '#' and '/' characters are translated to '@'.\n#auth_username_translation =\n\n# Username formatting before it's looked up from databases. You can use\n# the standard variables here, eg. %Lu would lowercase the username, %n would\n# drop away the domain if it was given, or \"%n-AT-%d\" would change the '@' into\n# \"-AT-\". This translation is done after auth_username_translation changes.\n#auth_username_format = %Lu\n\n# If you want to allow master users to log in by specifying the master\n# username within the normal username string (ie. not using SASL mechanism's\n# support for it), you can specify the separator character here. The format\n# is then <username><separator><master username>. UW-IMAP uses \"*\" as the\n# separator, so that could be a good choice.\n#auth_master_user_separator =\n\n# Username to use for users logging in with ANONYMOUS SASL mechanism\n#auth_anonymous_username = anonymous\n\n# Maximum number of dovecot-auth worker processes. They're used to execute\n# blocking passdb and userdb queries (eg. MySQL and PAM). They're\n# automatically created and destroyed as needed.\n#auth_worker_max_count = 30\n\n# Host name to use in GSSAPI principal names. The default is to use the\n# name returned by gethostname(). Use \"$ALL\" (with quotes) to allow all keytab\n# entries.\n#auth_gssapi_hostname =\n\n# Kerberos keytab to use for the GSSAPI mechanism. Will use the system\n# default (usually /etc/krb5.keytab) if not specified. You may need to change\n# the auth service to run as root to be able to read this file.\n#auth_krb5_keytab =\n\n# Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and\n# ntlm_auth helper. <doc/wiki/Authentication/Mechanisms/Winbind.txt>\n#auth_use_winbind = no\n\n# Path for Samba's ntlm_auth helper binary.\n#auth_winbind_helper_path = /usr/bin/ntlm_auth\n\n# Time to delay before replying to failed authentications.\n#auth_failure_delay = 2 secs\n\n# Require a valid SSL client certificate or the authentication fails.\n#auth_ssl_require_client_cert = no\n\n# Take the username from client's SSL certificate, using\n# X509_NAME_get_text_by_NID() which returns the subject's DN's\n# CommonName.\n#auth_ssl_username_from_cert = no\n\n# Space separated list of wanted authentication mechanisms:\n#   plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey\n#   gss-spnego\n# NOTE: See also disable_plaintext_auth setting.\nauth_mechanisms = plain login\n\n##\n## Password and user databases\n##\n\n#\n# Password database is used to verify user's password (and nothing more).\n# You can have multiple passdbs and userdbs. This is useful if you want to\n# allow both system users (/etc/passwd) and virtual users to login without\n# duplicating the system users into virtual database.\n#\n# <doc/wiki/PasswordDatabase.txt>\n#\n# User database specifies where mails are located and what user/group IDs\n# own them. For single-UID configuration use \"static\" userdb.\n#\n# <doc/wiki/UserDatabase.txt>\n\n#!include auth-deny.conf.ext\n#!include auth-master.conf.ext\n\n#!include auth-system.conf.ext\n!include auth-sql.conf.ext\n#!include auth-ldap.conf.ext\n#!include auth-passwdfile.conf.ext\n#!include auth-checkpassword.conf.ext\n#!include auth-vpopmail.conf.ext\n#!include auth-static.conf.ext\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-mail.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Mailbox locations and namespaces\n##\n\n# Location for users' mailboxes. The default is empty, which means that Dovecot\n# tries to find the mailboxes automatically. This won't work if the user\n# doesn't yet have any mail, so you should explicitly tell Dovecot the full\n# location.\n#\n# If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%u)\n# isn't enough. You'll also need to tell Dovecot where the other mailboxes are\n# kept. This is called the \"root mail directory\", and it must be the first\n# path given in the mail_location setting.\n#\n# There are a few special variables you can use, eg.:\n#\n#   %u - username\n#   %n - user part in user@domain, same as %u if there's no domain\n#   %d - domain part in user@domain, empty if there's no domain\n#   %h - home directory\n#\n# See doc/wiki/Variables.txt for full list. Some examples:\n#\n#   mail_location = maildir:~/Maildir\n#   mail_location = mbox:~/mail:INBOX=/var/mail/%u\n#   mail_location = mbox:/var/mail/%d/%1n/%n:INDEX=/var/indexes/%d/%1n/%n\n#\n# <doc/wiki/MailLocation.txt>\n#\nmail_location = mbox:~/mail:INBOX=/var/mail/%u\n\n# If you need to set multiple mailbox locations or want to change default\n# namespace settings, you can do it by defining namespace sections.\n#\n# You can have private, shared and public namespaces. Private namespaces\n# are for user's personal mails. Shared namespaces are for accessing other\n# users' mailboxes that have been shared. Public namespaces are for shared\n# mailboxes that are managed by sysadmin. If you create any shared or public\n# namespaces you'll typically want to enable ACL plugin also, otherwise all\n# users can access all the shared mailboxes, assuming they have permissions\n# on filesystem level to do so.\nnamespace inbox {\n  # Namespace type: private, shared or public\n  #type = private\n\n  # Hierarchy separator to use. You should use the same separator for all\n  # namespaces or some clients get confused. '/' is usually a good one.\n  # The default however depends on the underlying mail storage format.\n  #separator =\n\n  # Prefix required to access this namespace. This needs to be different for\n  # all namespaces. For example \"Public/\".\n  #prefix =\n\n  # Physical location of the mailbox. This is in same format as\n  # mail_location, which is also the default for it.\n  #location =\n\n  # There can be only one INBOX, and this setting defines which namespace\n  # has it.\n  inbox = yes\n\n  # If namespace is hidden, it's not advertised to clients via NAMESPACE\n  # extension. You'll most likely also want to set list=no. This is mostly\n  # useful when converting from another server with different namespaces which\n  # you want to deprecate but still keep working. For example you can create\n  # hidden namespaces with prefixes \"~/mail/\", \"~%u/mail/\" and \"mail/\".\n  #hidden = no\n\n  # Show the mailboxes under this namespace with LIST command. This makes the\n  # namespace visible for clients that don't support NAMESPACE extension.\n  # \"children\" value lists child mailboxes, but hides the namespace prefix.\n  #list = yes\n\n  # Namespace handles its own subscriptions. If set to \"no\", the parent\n  # namespace handles them (empty prefix should always have this as \"yes\")\n  #subscriptions = yes\n}\n\n# Example shared namespace configuration\n#namespace {\n  #type = shared\n  #separator = /\n\n  # Mailboxes are visible under \"shared/user@domain/\"\n  # %%n, %%d and %%u are expanded to the destination user.\n  #prefix = shared/%%u/\n\n  # Mail location for other users' mailboxes. Note that %variables and ~/\n  # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the\n  # destination user's data.\n  #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u\n\n  # Use the default namespace for saving subscriptions.\n  #subscriptions = no\n\n  # List the shared/ namespace only if there are visible shared mailboxes.\n  #list = children\n#}\n# Should shared INBOX be visible as \"shared/user\" or \"shared/user/INBOX\"?\n#mail_shared_explicit_inbox = no\n\n# System user and group used to access mails. If you use multiple, userdb\n# can override these by returning uid or gid fields. You can use either numbers\n# or names. <doc/wiki/UserIds.txt>\n#mail_uid =\n#mail_gid =\n\n# Group to enable temporarily for privileged operations. Currently this is\n# used only with INBOX when either its initial creation or dotlocking fails.\n# Typically this is set to \"mail\" to give access to /var/mail.\n#mail_privileged_group =\n\n# Grant access to these supplementary groups for mail processes. Typically\n# these are used to set up access to shared mailboxes. Note that it may be\n# dangerous to set these if users can create symlinks (e.g. if \"mail\" group is\n# set here, ln -s /var/mail ~/mail/var could allow a user to delete others'\n# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it).\nmail_access_groups = vmail\n\n# Allow full filesystem access to clients. There's no access checks other than\n# what the operating system does for the active UID/GID. It works with both\n# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/\n# or ~user/.\n#mail_full_filesystem_access = no\n\n# Dictionary for key=value mailbox attributes. Currently used by URLAUTH, but\n# soon intended to be used by METADATA as well.\n#mail_attribute_dict =\n\n##\n## Mail processes\n##\n\n# Don't use mmap() at all. This is required if you store indexes to shared\n# filesystems (NFS or clustered filesystem).\n#mmap_disable = no\n\n# Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL\n# since version 3, so this should be safe to use nowadays by default.\n#dotlock_use_excl = yes\n\n# When to use fsync() or fdatasync() calls:\n#   optimized (default): Whenever necessary to avoid losing important data\n#   always: Useful with e.g. NFS when write()s are delayed\n#   never: Never use it (best performance, but crashes can lose data)\n#mail_fsync = optimized\n\n# Locking method for index files. Alternatives are fcntl, flock and dotlock.\n# Dotlocking uses some tricks which may create more disk I/O than other locking\n# methods. NFS users: flock doesn't work, remember to change mmap_disable.\n#lock_method = fcntl\n\n# Directory in which LDA/LMTP temporarily stores incoming mails >128 kB.\n#mail_temp_dir = /tmp\n\n# Valid UID range for users, defaults to 500 and above. This is mostly\n# to make sure that users can't log in as daemons or other system users.\n# Note that denying root logins is hardcoded to dovecot binary and can't\n# be done even if first_valid_uid is set to 0.\n#first_valid_uid = 500\n#last_valid_uid = 0\n\n# Valid GID range for users, defaults to non-root/wheel. Users having\n# non-valid GID as primary group ID aren't allowed to log in. If user\n# belongs to supplementary groups with non-valid GIDs, those groups are\n# not set.\n#first_valid_gid = 1\n#last_valid_gid = 0\n\n# Maximum allowed length for mail keyword name. It's only forced when trying\n# to create new keywords.\n#mail_max_keyword_length = 50\n\n# ':' separated list of directories under which chrooting is allowed for mail\n# processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too).\n# This setting doesn't affect login_chroot, mail_chroot or auth chroot\n# settings. If this setting is empty, \"/./\" in home dirs are ignored.\n# WARNING: Never add directories here which local users can modify, that\n# may lead to root exploit. Usually this should be done only if you don't\n# allow shell access for users. <doc/wiki/Chrooting.txt>\n#valid_chroot_dirs =\n\n# Default chroot directory for mail processes. This can be overridden for\n# specific users in user database by giving /./ in user's home directory\n# (eg. /home/./user chroots into /home). Note that usually there is no real\n# need to do chrooting, Dovecot doesn't allow users to access files outside\n# their mail directory anyway. If your home directories are prefixed with\n# the chroot directory, append \"/.\" to mail_chroot. <doc/wiki/Chrooting.txt>\n#mail_chroot =\n\n# UNIX socket path to master authentication server to find users.\n# This is used by imap (for shared users) and lda.\n#auth_socket_path = /var/run/dovecot/auth-userdb\n\n# Directory where to look up mail plugins.\n#mail_plugin_dir = /usr/lib/dovecot/modules\n\n# Space separated list of plugins to load for all services. Plugins specific to\n# IMAP, LDA, etc. are added to this list in their own .conf files.\n#mail_plugins =\n\n##\n## Mailbox handling optimizations\n##\n\n# Mailbox list indexes can be used to optimize IMAP STATUS commands. They are\n# also required for IMAP NOTIFY extension to be enabled.\n#mailbox_list_index = no\n\n# The minimum number of mails in a mailbox before updates are done to cache\n# file. This allows optimizing Dovecot's behavior to do less disk writes at\n# the cost of more disk reads.\n#mail_cache_min_mail_count = 0\n\n# When IDLE command is running, mailbox is checked once in a while to see if\n# there are any new mails or other changes. This setting defines the minimum\n# time to wait between those checks. Dovecot can also use dnotify, inotify and\n# kqueue to find out immediately when changes occur.\n#mailbox_idle_check_interval = 30 secs\n\n# Save mails with CR+LF instead of plain LF. This makes sending those mails\n# take less CPU, especially with sendfile() syscall with Linux and FreeBSD.\n# But it also creates a bit more disk I/O which may just make it slower.\n# Also note that if other software reads the mboxes/maildirs, they may handle\n# the extra CRs wrong and cause problems.\n#mail_save_crlf = no\n\n# Max number of mails to keep open and prefetch to memory. This only works with\n# some mailbox formats and/or operating systems.\n#mail_prefetch_count = 0\n\n# How often to scan for stale temporary files and delete them (0 = never).\n# These should exist only after Dovecot dies in the middle of saving mails.\n#mail_temp_scan_interval = 1w\n\n##\n## Maildir-specific settings\n##\n\n# By default LIST command returns all entries in maildir beginning with a dot.\n# Enabling this option makes Dovecot return only entries which are directories.\n# This is done by stat()ing each entry, so it causes more disk I/O.\n# (For systems setting struct dirent->d_type, this check is free and it's\n# done always regardless of this setting)\n#maildir_stat_dirs = no\n\n# When copying a message, do it with hard links whenever possible. This makes\n# the performance much better, and it's unlikely to have any side effects.\n#maildir_copy_with_hardlinks = yes\n\n# Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only\n# when its mtime changes unexpectedly or when we can't find the mail otherwise.\n#maildir_very_dirty_syncs = no\n\n# If enabled, Dovecot doesn't use the S=<size> in the Maildir filenames for\n# getting the mail's physical size, except when recalculating Maildir++ quota.\n# This can be useful in systems where a lot of the Maildir filenames have a\n# broken size. The performance hit for enabling this is very small.\n#maildir_broken_filename_sizes = no\n\n# Always move mails from new/ directory to cur/, even when the \\Recent flags\n# aren't being reset.\n#maildir_empty_new = no\n\n##\n## mbox-specific settings\n##\n\n# Which locking methods to use for locking mbox. There are four available:\n#  dotlock: Create <mailbox>.lock file. This is the oldest and most NFS-safe\n#           solution. If you want to use /var/mail/ like directory, the users\n#           will need write access to that directory.\n#  dotlock_try: Same as dotlock, but if it fails because of permissions or\n#               because there isn't enough disk space, just skip it.\n#  fcntl  : Use this if possible. Works with NFS too if lockd is used.\n#  flock  : May not exist in all systems. Doesn't work with NFS.\n#  lockf  : May not exist in all systems. Doesn't work with NFS.\n#\n# You can use multiple locking methods; if you do the order they're declared\n# in is important to avoid deadlocks if other MTAs/MUAs are using multiple\n# locking methods as well. Some operating systems don't allow using some of\n# them simultaneously.\n#\n# The Debian value for mbox_write_locks differs from upstream Dovecot. It is\n# changed to be compliant with Debian Policy (section 11.6) for NFS safety.\n#       Dovecot: mbox_write_locks = dotlock fcntl\n#       Debian:  mbox_write_locks = fcntl dotlock\n#\n#mbox_read_locks = fcntl\n#mbox_write_locks = fcntl dotlock\n\n# Maximum time to wait for lock (all of them) before aborting.\n#mbox_lock_timeout = 5 mins\n\n# If dotlock exists but the mailbox isn't modified in any way, override the\n# lock file after this much time.\n#mbox_dotlock_change_timeout = 2 mins\n\n# When mbox changes unexpectedly we have to fully read it to find out what\n# changed. If the mbox is large this can take a long time. Since the change\n# is usually just a newly appended mail, it'd be faster to simply read the\n# new mails. If this setting is enabled, Dovecot does this but still safely\n# fallbacks to re-reading the whole mbox file whenever something in mbox isn't\n# how it's expected to be. The only real downside to this setting is that if\n# some other MUA changes message flags, Dovecot doesn't notice it immediately.\n# Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK\n# commands.\n#mbox_dirty_syncs = yes\n\n# Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE,\n# EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored.\n#mbox_very_dirty_syncs = no\n\n# Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK\n# commands and when closing the mailbox). This is especially useful for POP3\n# where clients often delete all mails. The downside is that our changes\n# aren't immediately visible to other MUAs.\n#mbox_lazy_writes = yes\n\n# If mbox size is smaller than this (e.g. 100k), don't write index files.\n# If an index file already exists it's still read, just not updated.\n#mbox_min_index_size = 0\n\n# Mail header selection algorithm to use for MD5 POP3 UIDLs when\n# pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired\n# algorithm, but it fails if the first Received: header isn't unique in all\n# mails. An alternative algorithm is \"all\" that selects all headers.\n#mbox_md5 = apop3d\n\n##\n## mdbox-specific settings\n##\n\n# Maximum dbox file size until it's rotated.\n#mdbox_rotate_size = 2M\n\n# Maximum dbox file age until it's rotated. Typically in days. Day begins\n# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled.\n#mdbox_rotate_interval = 0\n\n# When creating new mdbox files, immediately preallocate their size to\n# mdbox_rotate_size. This setting currently works only in Linux with some\n# filesystems (ext4, xfs).\n#mdbox_preallocate_space = no\n\n##\n## Mail attachments\n##\n\n# sdbox and mdbox support saving mail attachments to external files, which\n# also allows single instance storage for them. Other backends don't support\n# this for now.\n\n# Directory root where to store mail attachments. Disabled, if empty.\n#mail_attachment_dir =\n\n# Attachments smaller than this aren't saved externally. It's also possible to\n# write a plugin to disable saving specific attachments externally.\n#mail_attachment_min_size = 128k\n\n# Filesystem backend to use for saving attachments:\n#  posix : No SiS done by Dovecot (but this might help FS's own deduplication)\n#  sis posix : SiS with immediate byte-by-byte comparison during saving\n#  sis-queue posix : SiS with delayed comparison and deduplication\n#mail_attachment_fs = sis posix\n\n# Hash format to use in attachment filenames. You can add any text and\n# variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}.\n# Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits\n#mail_attachment_hash = %{sha1}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-master.conf\"\n\t\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n#default_process_limit = 100\n#default_client_limit = 1000\n\n# Default VSZ (virtual memory size) limit for service processes. This is mainly\n# intended to catch and kill processes that leak memory before they eat up\n# everything.\n#default_vsz_limit = 256M\n\n# Login user is internally used by login processes. This is the most untrusted\n# user in Dovecot system. It shouldn't have access to anything at all.\n#default_login_user = dovenull\n\n# Internal user is used by unprivileged processes. It should be separate from\n# login user, so that login processes can't disturb other processes.\n#default_internal_user = dovecot\n\nservice imap-login {\n  inet_listener imap {\n    #port = 143\n  }\n  inet_listener imaps {\n    #port = 993\n    #ssl = yes\n  }\n\n  # Number of connections to handle before starting a new process. Typically\n  # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0\n  # is faster. <doc/wiki/LoginProcess.txt>\n  #service_count = 1\n\n  # Number of processes to always keep waiting for more connections.\n  #process_min_avail = 0\n\n  # If you set service_count=0, you probably need to grow this.\n  #vsz_limit = $default_vsz_limit\n}\n\nservice pop3-login {\n  inet_listener pop3 {\n    #port = 110\n  }\n  inet_listener pop3s {\n    #port = 995\n    #ssl = yes\n  }\n}\n\nservice lmtp {\n  unix_listener lmtp {\n    #mode = 0666\n  }\n\n  # Create inet listener only if you can't use the above UNIX socket\n  #inet_listener lmtp {\n    # Avoid making LMTP visible for the entire internet\n    #address =\n    #port =\n  #}\n}\n\nservice imap {\n  # Most of the memory goes to mmap()ing files. You may need to increase this\n  # limit if you have huge mailboxes.\n  #vsz_limit = $default_vsz_limit\n\n  # Max. number of IMAP processes (connections)\n  #process_limit = 1024\n}\n\nservice pop3 {\n  # Max. number of POP3 processes (connections)\n  #process_limit = 1024\n}\n\nservice auth {\n  # auth_socket_path points to this userdb socket by default. It's typically\n  # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have\n  # full permissions to this socket are able to get a list of all usernames and\n  # get the results of everyone's userdb lookups.\n  #\n  # The default 0666 mode allows anyone to connect to the socket, but the\n  # userdb lookups will succeed only if the userdb returns an \"uid\" field that\n  # matches the caller process's UID. Also if caller's uid or gid matches the\n  # socket's uid or gid the lookup succeeds. Anything else causes a failure.\n  #\n  # To give the caller full permissions to lookup all users, set the mode to\n  # something else than 0666 and Dovecot lets the kernel enforce the\n  # permissions (e.g. 0777 allows everyone full permissions).\n  unix_listener auth-userdb {\n    #mode = 0666\n    #user =\n    #group =\n  }\n\n  # Postfix smtp-auth\n  unix_listener /var/spool/postfix/private/auth {\n    mode = 0660\n    user = postfix\n    group = postfix\n  }\n\n  # Exim4 smtp-auth\n  unix_listener auth-client {\n    mode = 0660\n    user = mail\n  #  group = Debian-exim\n  }\n\n  # Auth process is run as this user.\n  #user = $default_internal_user\n}\n\nservice auth-worker {\n  # Auth worker process is run as root by default, so that it can access\n  # /etc/shadow. If this isn't necessary, the user should be changed to\n  # $default_internal_user.\n  #user = root\n}\n\nservice dict {\n  # If dict proxy is used, mail processes should have access to its socket.\n  # For example: mode=0660, group=vmail and global mail_access_groups=vmail\n  unix_listener dict {\n    #mode = 0600\n    #user =\n    #group =\n  }\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/10-ssl.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## SSL settings\n##\n\n# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>\nssl = yes\n\n# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before\n# dropping root privileges, so keep the key file unreadable by anyone but\n# root. Included doc/mkcert.sh can be used to easily generate self-signed\n# certificate, just make sure to update the domains in dovecot-openssl.cnf\nssl_cert = <<SSL_CERT_FILE>\nssl_key = <<SSL_KEY_FILE>\n\n# If key file is password protected, give the password here. Alternatively\n# give it when starting dovecot with -p parameter. Since this file is often\n# world-readable, you may want to place this setting instead to a different\n# root owned 0600 file by using ssl_key_password = <path.\n#ssl_key_password =\n\n# PEM encoded trusted certificate authority. Set this only if you intend to use\n# ssl_verify_client_cert=yes. The file should contain the CA certificate(s)\n# followed by the matching CRL(s). (e.g. ssl_ca = </etc/ssl/certs/ca.pem)\n#ssl_ca =\n\n# Require that CRL check succeeds for client certificates.\n#ssl_require_crl = yes\n\n# Directory and/or file for trusted SSL CA certificates. These are used only\n# when Dovecot needs to act as an SSL client (e.g. imapc backend). The\n# directory is usually /etc/ssl/certs in Debian-based systems and the file is\n# /etc/pki/tls/cert.pem in RedHat-based systems.\n#ssl_client_ca_dir =\n#ssl_client_ca_file =\n\n# Request client to send a certificate. If you also want to require it, set\n# auth_ssl_require_client_cert=yes in auth section.\n#ssl_verify_client_cert = no\n\n# Which field from certificate to use for username. commonName and\n# x500UniqueIdentifier are the usual choices. You'll also need to set\n# auth_ssl_username_from_cert=yes.\n#ssl_cert_username_field = commonName\n\n# SSL DH parameters\n# Generate new params with `openssl dhparam -out /etc/dovecot/dh.pem 4096`\n# Or migrate from old ssl-parameters.dat file with the command dovecot\n# gives on startup when ssl_dh is unset.\nssl_dh = </usr/share/dovecot/dh.pem\n\n\n# SSL protocols to use\n#ssl_protocols = !SSLv3\n\n# SSL ciphers to use\n#ssl_cipher_list = ALL:!LOW:!SSLv2:!EXP:!aNULL\n\n# Prefer the server's order of ciphers over client's.\n#ssl_prefer_server_ciphers = no\n\n# SSL crypto device to use, for valid values run \"openssl engine\"\n#ssl_crypto_device =\n\n# SSL extra options. Currently supported options are:\n#   no_compression - Disable compression.\n#ssl_options =\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/15-lda.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## LDA specific settings (also used by LMTP)\n##\n\n# Address to use when sending rejection mails.\n# Default is postmaster@<your domain>. %d expands to recipient domain.\npostmaster_address = postmaster@<SERVERNAME>\n\n# Hostname to use in various parts of sent mails (e.g. in Message-Id) and\n# in LMTP replies. Default is the system's real hostname@domain.\n#hostname =\n\n# If user is over quota, return with temporary failure instead of\n# bouncing the mail.\n#quota_full_tempfail = no\n\n# Binary to use for sending mails.\n#sendmail_path = /usr/sbin/sendmail\n\n# If non-empty, send mails via this SMTP host[:port] instead of sendmail.\n#submission_host =\n\n# Subject: header to use for rejection mails. You can use the same variables\n# as for rejection_reason below.\n#rejection_subject = Rejected: %s\n\n# Human readable error message for rejection mails. You can use variables:\n#  %n = CRLF, %r = reason, %s = original subject, %t = recipient\n#rejection_reason = Your message to <%t> was automatically rejected:%n%r\n\n# Delimiter character between local-part and detail in email address.\n#recipient_delimiter = +\n\n# Header where the original recipient address (SMTP's RCPT TO: address) is taken\n# from if not available elsewhere. With dovecot-lda -a parameter overrides this.\n# A commonly used header for this is X-Original-To.\n#lda_original_recipient_header =\n\n# Should saving a mail to a nonexistent mailbox automatically create it?\n#lda_mailbox_autocreate = no\n\n# Should automatically created mailboxes be also automatically subscribed?\n#lda_mailbox_autosubscribe = no\n\nprotocol lda {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  mail_plugins = $mail_plugins quota sieve\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-imap.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## IMAP specific settings\n##\n\n# Maximum IMAP command line length. Some clients generate very long command\n# lines with huge mailboxes, so you may need to raise this if you get\n# \"Too long argument\" or \"IMAP command line too large\" errors often.\n#imap_max_line_length = 64k\n\n# IMAP logout format string:\n#  %i - total number of bytes read from client\n#  %o - total number of bytes sent to client\n#imap_logout_format = in=%i out=%o\n\n# Override the IMAP CAPABILITY response. If the value begins with '+',\n# add the given capabilities on top of the defaults (e.g. +XFOO XBAR).\n#imap_capability =\n\n# How long to wait between \"OK Still here\" notifications when client is\n# IDLEing.\n#imap_idle_notify_interval = 2 mins\n\n# ID field names and values to send to clients. Using * as the value makes\n# Dovecot use the default value. The following fields have default values\n# currently: name, version, os, os-version, support-url, support-email.\n#imap_id_send =\n\n# ID fields sent by client to log. * means everything.\n#imap_id_log =\n\n# Workarounds for various client bugs:\n#   delay-newmail:\n#     Send EXISTS/RECENT new mail notifications only when replying to NOOP\n#     and CHECK commands. Some clients ignore them otherwise, for example OSX\n#     Mail (<v2.1). Outlook Express breaks more badly though, without this it\n#     may show user \"Message no longer in server\" errors. Note that OE6 still\n#     breaks even with this workaround if synchronization is set to\n#     \"Headers Only\".\n#   tb-extra-mailbox-sep:\n#     Thunderbird gets somehow confused with LAYOUT=fs (mbox and dbox) and\n#     adds extra '/' suffixes to mailbox names. This option causes Dovecot to\n#     ignore the extra '/' instead of treating it as invalid mailbox name.\n#   tb-lsub-flags:\n#     Show \\Noselect flags for LSUB replies with LAYOUT=fs (e.g. mbox).\n#     This makes Thunderbird realize they aren't selectable and show them\n#     greyed out, instead of only later giving \"not selectable\" popup error.\n#\n# The list is space-separated.\n#imap_client_workarounds =\n\n# Host allowed in URLAUTH URLs sent by client. \"*\" allows all.\n#imap_urlauth_host =\n\nprotocol imap {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  mail_plugins = $mail_plugins quota imap_quota\n\n  # Maximum number of IMAP connections allowed for a user from each IP address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-managesieve.conf\"\n\t\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## ManageSieve specific settings\n##\n\n# Uncomment to enable managesieve protocol:\n#protocols = $protocols sieve\n\n# Service definitions\n\n#service managesieve-login {\n  #inet_listener sieve {\n  #  port = 4190\n  #}\n\n  #inet_listener sieve_deprecated {\n  #  port = 2000\n  #}\n\n  # Number of connections to handle before starting a new process. Typically\n  # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0\n  # is faster. <doc/wiki/LoginProcess.txt>\n  #service_count = 1\n\n  # Number of processes to always keep waiting for more connections.\n  #process_min_avail = 0\n\n  # If you set service_count=0, you probably need to grow this.\n  #vsz_limit = 64M\n#}\n\n#service managesieve {\n  # Max. number of ManageSieve processes (connections)\n  #process_limit = 1024\n#}\n\n# Service configuration\n\nprotocol sieve {\n  # Maximum ManageSieve command line length in bytes. ManageSieve usually does\n  # not involve overly long command lines, so this setting will not normally\n  # need adjustment\n  #managesieve_max_line_length = 65536\n\n  # Maximum number of ManageSieve connections allowed for a user from each IP\n  # address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n\n  # Space separated list of plugins to load (none known to be useful so far).\n  # Do NOT try to load IMAP plugins here.\n  #mail_plugins =\n\n  # MANAGESIEVE logout format string:\n  #  %i - total number of bytes read from client\n  #  %o - total number of bytes sent to client\n  #managesieve_logout_format = bytes=%i/%o\n\n  # To fool ManageSieve clients that are focused on CMU's timesieved you can\n  # specify the IMPLEMENTATION capability that Dovecot reports to clients.\n  # For example: 'Cyrus timsieved v2.2.13'\n  #managesieve_implementation_string = Dovecot Pigeonhole\n\n  # Explicitly specify the SIEVE and NOTIFY capability reported by the server\n  # before login. If left unassigned these will be reported dynamically\n  # according to what the Sieve interpreter supports by default (after login\n  # this may differ depending on the user).\n  #managesieve_sieve_capability =\n  #managesieve_notify_capability =\n\n  # The maximum number of compile errors that are returned to the client upon\n  # script upload or script verification.\n  #managesieve_max_compile_errors = 5\n\n  # Refer to 90-sieve.conf for script quota configuration and configuration of\n  # Sieve execution limits.\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/20-pop3.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## POP3 specific settings\n##\n\n# Don't try to set mails non-recent or seen with POP3 sessions. This is\n# mostly intended to reduce disk I/O. With maildir it doesn't move files\n# from new/ to cur/, with mbox it doesn't write Status-header.\n#pop3_no_flag_updates = no\n\n# Support LAST command which exists in old POP3 specs, but has been removed\n# from new ones. Some clients still wish to use this though. Enabling this\n# makes RSET command clear all \\Seen flags from messages.\n#pop3_enable_last = no\n\n# If mail has X-UIDL header, use it as the mail's UIDL.\n#pop3_reuse_xuidl = no\n\n# Allow only one POP3 session to run simultaneously for the same user.\n#pop3_lock_session = no\n\n# POP3 requires message sizes to be listed as if they had CR+LF linefeeds.\n# Many POP3 servers violate this by returning the sizes with LF linefeeds,\n# because it's faster to get. When this setting is enabled, Dovecot still\n# tries to do the right thing first, but if that requires opening the\n# message, it fallbacks to the easier (but incorrect) size.\n#pop3_fast_size_lookups = no\n\n# POP3 UIDL (unique mail identifier) format to use. You can use following\n# variables, along with the variable modifiers described in\n# doc/wiki/Variables.txt (e.g. %Uf for the filename in uppercase)\n#\n#  %v - Mailbox's IMAP UIDVALIDITY\n#  %u - Mail's IMAP UID\n#  %m - MD5 sum of the mailbox headers in hex (mbox only)\n#  %f - filename (maildir only)\n#  %g - Mail's GUID\n#\n# If you want UIDL compatibility with other POP3 servers, use:\n#  UW's ipop3d         : %08Xv%08Xu\n#  Courier             : %f or %v-%u (both might be used simultaneously)\n#  Cyrus (<= 2.1.3)    : %u\n#  Cyrus (>= 2.1.4)    : %v.%u\n#  Dovecot v0.99.x     : %v.%u\n#  tpop3d              : %Mf\n#\n# Note that Outlook 2003 seems to have problems with %v.%u format which was\n# Dovecot's default, so if you're building a new server it would be a good\n# idea to change this. %08Xu%08Xv should be pretty fail-safe.\n#\n#pop3_uidl_format = %08Xu%08Xv\n\n# Permanently save UIDLs sent to POP3 clients, so pop3_uidl_format changes\n# won't change those UIDLs. Currently this works only with Maildir.\n#pop3_save_uidl = no\n\n# What to do about duplicate UIDLs if they exist?\n#   allow: Show duplicates to clients.\n#   rename: Append a temporary -2, -3, etc. counter after the UIDL.\n#pop3_uidl_duplicates = allow\n\n# This option changes POP3 behavior so that it's not possible to actually\n# delete mails via POP3, only hide them from future POP3 sessions. The mails\n# will still be counted towards user's quota until actually deleted via IMAP.\n# Use e.g. \"$POP3Deleted\" as the value (it will be visible as IMAP keyword).\n# Make sure you can legally archive mails before enabling this setting.\n#pop3_deleted_flag =\n\n# POP3 logout format string:\n#  %i - total number of bytes read from client\n#  %o - total number of bytes sent to client\n#  %t - number of TOP commands\n#  %p - number of bytes sent to client as a result of TOP command\n#  %r - number of RETR commands\n#  %b - number of bytes sent to client as a result of RETR command\n#  %d - number of deleted messages\n#  %m - number of messages (before deletion)\n#  %s - mailbox size in bytes (before deletion)\n#  %u - old/new UIDL hash. may help finding out if UIDLs changed unexpectedly\npop3_logout_format = in=%i out=%o top=%t/%p, retr=%r/%b, del=%d/%m, size=%s\n\n# Workarounds for various client bugs:\n#   outlook-no-nuls:\n#     Outlook and Outlook Express hang if mails contain NUL characters.\n#     This setting replaces them with 0x80 character.\n#   oe-ns-eoh:\n#     Outlook Express and Netscape Mail breaks if end of headers-line is\n#     missing. This option simply sends it if it's missing.\n# The list is space-separated.\n#pop3_client_workarounds =\n\nprotocol pop3 {\n  # Space separated list of plugins to load (default is global mail_plugins).\n  #mail_plugins = $mail_plugins\n\n  # Maximum number of POP3 connections allowed for a user from each IP address.\n  # NOTE: The username is compared case-sensitively.\n  #mail_max_userip_connections = 10\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/90-sieve.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n##\n## Settings for the Sieve interpreter\n##\n\n# Do not forget to enable the Sieve plugin in 15-lda.conf and 20-lmtp.conf\n# by adding it to the respective mail_plugins= settings.\n\nplugin {\n  # The path to the user's main active script. If ManageSieve is used, this the\n  # location of the symbolic link controlled by ManageSieve.\n  sieve = ~/sieve/.dovecot.sieve\n\n  # The default Sieve script when the user has none. This is a path to a global\n  # sieve script file, which gets executed ONLY if user's private Sieve script\n  # doesn't exist. Be sure to pre-compile this script manually using the sievec\n  # command line tool.\n  # --> See sieve_before fore executing scripts before the user's personal\n  #     script.\n  #sieve_default = /var/lib/dovecot/sieve/default.sieve\n\n  # Directory for :personal include scripts for the include extension. This\n  # is also where the ManageSieve service stores the user's scripts.\n  sieve_dir = ~/sieve\n\n  # Directory for :global include scripts for the include extension.\n  #sieve_global_dir =\n\n  # Path to a script file or a directory containing script files that need to be\n  # executed before the user's script. If the path points to a directory, all\n  # the Sieve scripts contained therein (with the proper .sieve extension) are\n  # executed. The order of execution within a directory is determined by the\n  # file names, using a normal 8bit per-character comparison. Multiple script\n  # file or directory paths can be specified by appending an increasing number.\n  #sieve_before =\n  #sieve_before2 =\n  #sieve_before3 = (etc...)\n\n  # Identical to sieve_before, only the specified scripts are executed after the\n  # user's script (only when keep is still in effect!). Multiple script file or\n  # directory paths can be specified by appending an increasing number.\n  #sieve_after =\n  #sieve_after2 =\n  #sieve_after2 = (etc...)\n\n  # Which Sieve language extensions are available to users. By default, all\n  # supported extensions are available, except for deprecated extensions or\n  # those that are still under development. Some system administrators may want\n  # to disable certain Sieve extensions or enable those that are not available\n  # by default. This setting can use '+' and '-' to specify differences relative\n  # to the default. For example `sieve_extensions = +imapflags' will enable the\n\t# deprecated imapflags extension in addition to all extensions were already\n  # enabled by default.\n  #sieve_extensions = +notify +imapflags\n\n  # Which Sieve language extensions are ONLY available in global scripts. This\n  # can be used to restrict the use of certain Sieve extensions to administrator\n  # control, for instance when these extensions can cause security concerns.\n  # This setting has higher precedence than the `sieve_extensions' setting\n  # (above), meaning that the extensions enabled with this setting are never\n  # available to the user's personal script no matter what is specified for the\n  # `sieve_extensions' setting. The syntax of this setting is similar to the\n  # `sieve_extensions' setting, with the difference that extensions are\n  # enabled or disabled for exclusive use in global scripts. Currently, no\n  # extensions are marked as such by default.\n  #sieve_global_extensions =\n\n  # The Pigeonhole Sieve interpreter can have plugins of its own. Using this\n  # setting, the used plugins can be specified. Check the Dovecot wiki\n  # (wiki2.dovecot.org) or the pigeonhole website\n  # (http://pigeonhole.dovecot.org) for available plugins.\n\t# The sieve_extprograms plugin is included in this release.\n  #sieve_plugins =\n\n  # The separator that is expected between the :user and :detail\n  # address parts introduced by the subaddress extension. This may\n  # also be a sequence of characters (e.g. '--'). The current\n  # implementation looks for the separator from the left of the\n  # localpart and uses the first one encountered. The :user part is\n  # left of the separator and the :detail part is right. This setting\n  # is also used by Dovecot's LMTP service.\n  #recipient_delimiter = +\n\n  # The maximum size of a Sieve script. The compiler will refuse to compile any\n  # script larger than this limit. If set to 0, no limit on the script size is\n  # enforced.\n  #sieve_max_script_size = 1M\n\n  # The maximum number of actions that can be performed during a single script\n  # execution. If set to 0, no limit on the total number of actions is enforced.\n  #sieve_max_actions = 32\n\n  # The maximum number of redirect actions that can be performed during a single\n  # script execution. If set to 0, no redirect actions are allowed.\n  #sieve_max_redirects = 4\n\n  # The maximum number of personal Sieve scripts a single user can have. If set\n  # to 0, no limit on the number of scripts is enforced.\n  # (Currently only relevant for ManageSieve)\n  #sieve_quota_max_scripts = 0\n\n  # The maximum amount of disk storage a single user's scripts may occupy. If\n  # set to 0, no limit on the used amount of disk storage is enforced.\n  # (Currently only relevant for ManageSieve)\n  #sieve_quota_max_storage = 0\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/90-quota.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\nplugin {\n  quota = maildir:User quota\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[service dovecot restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- Dovecot with postfix -->\n\t\t\t\t<daemon name=\"dovecot_postfix\" version=\"2\"\n\t\t\t\t\ttitle=\"Dovecot with postfix\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='mail']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- Antispam services -->\n\t\t\t<service type=\"antispam\" title=\"Antispam\">\n\t\t\t\t<!-- general RSpamd commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install wget gnupg]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/apt/keyrings]]></command>\n\t\t\t\t\t\t<command><![CDATA[wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | tee /etc/apt/keyrings/rspamd.gpg > /dev/null]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ jammy main\" > /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb-src [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ jammy main\" >> /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[apt-get update]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install rspamd]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/local.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/override.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/rspamd/dkim/]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/actions.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# Set rewrite subject to this value (%s is replaced by the original subject)\nsubject = \"***SPAM*** %s\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/arc.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\ntry_fallback = true;\n### Enable DKIM signing for alias sender addresses\nallow_username_mismatch = true;\npath = \"/var/lib/rspamd/dkim/$domain.$selector.key\";\nselector_map = \"/etc/rspamd/dkim_selectors.map\";\nuse_esld = false;\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/milter_headers.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuse = [\"x-spamd-bar\", \"x-spam-level\", \"authentication-results\"];\nauthenticated_headers = [\"authentication-results\"];\nextended_spam_headers = true\nskip_local = false\nskip_authenticated = false\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/replies.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## If a user has replied to an email, don’t mark other emails in the same thread as spam\naction = \"no action\";\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/settings.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## Feel free to include your own settings or adjustments here, for example:\n#whitelist {\n#  priority = low;\n#  rcpt = \"postmaster@example.com\";\n#  want_spam = yes;\n#}\n\n## Include froxlor generated settings\n.include(try=true,priority=1,duplicate=merge) \"{{settings.antispam.config_file}}\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[cp /etc/rspamd/local.d/arc.conf /etc/rspamd/local.d/dkim_signing.conf]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_protocol = 6\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_default_action = accept\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"non_smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R _rspamd:_rspamd /var/lib/rspamd/dkim]]></command>\n\t\t\t\t\t\t<command><![CDATA[service rspamd restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- rspamd -->\n\t\t\t\t<daemon name=\"rspamd\" title=\"Rspamd\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- FTP services -->\n\t\t\t<service type=\"ftp\" title=\"{{lng.admin.configfiles.ftp}}\">\n\t\t\t\t<!-- Proftpd -->\n\t\t\t\t<daemon name=\"proftpd\" title=\"ProFTPd\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install proftpd-basic proftpd-mod-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/proftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/certs/proftpd.crt ] || openssl req -new -x509 -newkey rsa:4096 -days 3650 -nodes -out /etc/ssl/certs/proftpd.crt -keyout /etc/ssl/private/proftpd.key -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\n[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nchmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/proftpd/proftpd.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# /etc/proftpd/proftpd.conf -- This is a basic ProFTPD configuration file.\n# To really apply changes, reload proftpd after modifications, if\n# it runs in daemon mode. It is not required in inetd/xinetd mode.\n#\n\n# Includes DSO modules\nInclude /etc/proftpd/modules.conf\n\n# Set off to disable IPv6 support which is annoying on IPv4 only boxes.\nUseIPv6\t\t\t\ton\n# If set on you can experience a longer connection delay in many cases.\n<IfModule mod_ident.c>\n\tIdentLookups\t\t\t\toff\n</IfModule>\n\nServerName                     \"<SERVERNAME> FTP Server\"\nServerType\t\t\tstandalone\nDeferWelcome\t\t\toff\n\nDefaultServer\t\t\ton\nShowSymlinks\t\t\ton\n\nTimeoutNoTransfer\t\t600\nTimeoutStalled\t\t\t600\nTimeoutIdle\t\t\t1200\n\nDisplayLogin                    welcome.msg\nDisplayChdir               \t.message true\nListOptions                \t\"-l\"\n\nDenyFilter\t\t\t\\*.*/\n\n# Use this to jail all users in their homes\n# DefaultRoot\t\t\t~\n\n# Users require a valid shell listed in /etc/shells to login.\n# Use this directive to release that constrain.\n# RequireValidShell\t\toff\n\n# Port 21 is the standard FTP port.\nPort\t\t\t\t21\n\n# In some cases you have to specify passive ports range to by-pass\n# firewall limitations. Ephemeral ports can be used for that, but\n# feel free to use a more narrow range.\n# PassivePorts                  49152 65534\n\n# If your host was NATted, this option is useful in order to\n# allow passive transfers to work. You have to use your public\n# address and opening the passive ports used on your firewall as well.\n# MasqueradeAddress\t\t1.2.3.4\n\n# This is useful for masquerading address with dynamic IPs:\n# refresh any configured MasqueradeAddress directives every 8 hours\n<IfModule mod_dynmasq.c>\n# DynMasqRefresh 28800\n</IfModule>\n\n# To prevent DoS attacks, set the maximum number of child processes\n# to 30.  If you need to allow more than 30 concurrent connections\n# at once, simply increase this value.  Note that this ONLY works\n# in standalone mode, in inetd mode you should use an inetd server\n# that allows you to limit maximum number of processes per service\n# (such as xinetd)\nMaxInstances\t\t\t30\n\n# Set the user and group that the server normally runs at.\nUser\t\t\t\tproftpd\nGroup\t\t\t\tnogroup\n\n# Umask 022 is a good standard umask to prevent new files and dirs\n# (second parm) from being group and world writable.\nUmask\t\t\t\t022  022\n# Normally, we want files to be overwritable.\nAllowOverwrite\t\t\ton\n\n# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords:\n# PersistentPasswd\t\toff\n\n# This is required to use both PAM-based authentication and local passwords\n# AuthOrder\t\t\tmod_auth_pam.c* mod_auth_unix.c\n\n# Be warned: use of this directive impacts CPU average load!\n# Uncomment this if you like to see progress and transfer rate with ftpwho\n# in downloads. That is not needed for uploads rates.\n#\n# UseSendFile\t\t\toff\n\nTransferLog /var/log/proftpd/xferlog\nSystemLog   /var/log/proftpd/proftpd.log\n\n# Logging onto /var/log/lastlog is enabled but set to off by default\n#UseLastlog on\n\n# In order to keep log file dates consistent after chroot, use timezone info\n# from /etc/localtime.  If this is not set, and proftpd is configured to\n# chroot (e.g. DefaultRoot or <Anonymous>), it will use the non-daylight\n# savings timezone regardless of whether DST is in effect.\n#SetEnv TZ :/etc/localtime\n\n<IfModule mod_quotatab.c>\nQuotaEngine on\n</IfModule>\n\n<IfModule mod_ratio.c>\nRatios off\n</IfModule>\n\n\n# Delay engine reduces impact of the so-called Timing Attack described in\n# http://www.securityfocus.com/bid/11430/discuss\n# It is on by default.\n<IfModule mod_delay.c>\nDelayEngine on\n</IfModule>\n\n<IfModule mod_ctrls.c>\nControlsEngine        off\nControlsMaxClients    2\nControlsLog           /var/log/proftpd/controls.log\nControlsInterval      5\nControlsSocket        /var/run/proftpd/proftpd.sock\n</IfModule>\n\n<IfModule mod_ctrls_admin.c>\nAdminControlsEngine off\n</IfModule>\n\n#\n# Alternative authentication frameworks\n#\n#Include /etc/proftpd/ldap.conf\nInclude /etc/proftpd/sql.conf\n\n#\n# This is used for FTPS connections\n#\nInclude /etc/proftpd/tls.conf\n\n#\n# Useful to keep VirtualHost/VirtualRoot directives separated\n#\n#Include /etc/proftpd/virtuals.conf\n\n# A basic anonymous configuration, no upload directories.\n\n# <Anonymous ~ftp>\n#   User\t\t\t\tftp\n#   Group\t\t\t\tnogroup\n#   # We want clients to be able to login with \"anonymous\" as well as \"ftp\"\n#   UserAlias\t\t\tanonymous ftp\n#   # Cosmetic changes, all files belongs to ftp user\n#   DirFakeUser\ton ftp\n#   DirFakeGroup on ftp\n#\n#   RequireValidShell\t\toff\n#\n#   # Limit the maximum number of anonymous logins\n#   MaxClients\t\t\t10\n#\n#   # We want 'welcome.msg' displayed at login, and '.message' displayed\n#   # in each newly chdired directory.\n#   DisplayLogin\t\t\twelcome.msg\n#   DisplayChdir\t\t.message\n#\n#   # Limit WRITE everywhere in the anonymous chroot\n#   <Directory *>\n#     <Limit WRITE>\n#       DenyAll\n#     </Limit>\n#   </Directory>\n#\n#   # Uncomment this if you're brave.\n#   # <Directory incoming>\n#   #   # Umask 022 is a good standard umask to prevent new files and dirs\n#   #   # (second parm) from being group and world writable.\n#   #   Umask\t\t\t\t022  022\n#   #            <Limit READ WRITE>\n#   #            DenyAll\n#   #            </Limit>\n#   #            <Limit STOR>\n#   #            AllowAll\n#   #            </Limit>\n#   # </Directory>\n#\n# </Anonymous>\n\n# Include other custom configuration files\nInclude /etc/proftpd/conf.d/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/modules.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# This file is used to manage DSO modules and features.\n#\n\n# This is the directory where DSO modules reside\n\nModulePath /usr/lib/proftpd\n\n# Allow only user root to load and unload modules, but allow everyone\n# to see which modules have been loaded\n\nModuleControlsACLs insmod,rmmod allow user root\nModuleControlsACLs lsmod allow user *\n\nLoadModule mod_ctrls_admin.c\nLoadModule mod_tls.c\n\n# Install one of proftpd-mod-mysql, proftpd-mod-pgsql or any other\n# SQL backend engine to use this module and the required backend.\n# This module must be mandatory loaded before anyone of\n# the existent SQL backeds.\nLoadModule mod_sql.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_ldap.c\n\n#\n# 'SQLBackend mysql' or 'SQLBackend postgres' (or any other valid backend) directives\n# are required to have SQL authorization working. You can also comment out the\n# unused module here, in alternative.\n#\n\n# Install proftpd-mod-mysql and decomment the previous\n# mod_sql.c module to use this.\nLoadModule mod_sql_mysql.c\n\n# Install proftpd-mod-pgsql and decomment the previous\n# mod_sql.c module to use this.\n#LoadModule mod_sql_postgres.c\n\n# Install proftpd-mod-sqlite and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_sqlite.c\n\n# Install proftpd-mod-odbc and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_odbc.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sql_passwd.c\n\nLoadModule mod_radius.c\nLoadModule mod_quotatab.c\nLoadModule mod_quotatab_file.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_quotatab_ldap.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\nLoadModule mod_quotatab_sql.c\nLoadModule mod_quotatab_radius.c\nLoadModule mod_wrap.c\nLoadModule mod_rewrite.c\nLoadModule mod_load.c\nLoadModule mod_ban.c\nLoadModule mod_wrap2.c\nLoadModule mod_wrap2_file.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_wrap2_sql.c\nLoadModule mod_dynmasq.c\nLoadModule mod_exec.c\nLoadModule mod_shaper.c\nLoadModule mod_ratio.c\nLoadModule mod_site_misc.c\n\nLoadModule mod_sftp.c\nLoadModule mod_sftp_pam.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sftp_sql.c\n\nLoadModule mod_facl.c\nLoadModule mod_unique_id.c\nLoadModule mod_copy.c\nLoadModule mod_deflate.c\nLoadModule mod_ifversion.c\nLoadModule mod_tls_memcache.c\n\n# Install proftpd-mod-geoip to use the GeoIP feature\n#LoadModule mod_geoip.c\n\n# keep this module the last one\nLoadModule mod_ifsession.c\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/sql.conf\" chown=\"root:0\" chmod=\"0600\"\n\t\t\t\t\t\tbackup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Proftpd sample configuration for SQL-based authentication.\n#\n# (This is not to be used if you prefer a PAM-based SQL authentication)\n#\n\n<IfModule mod_sql.c>\n\nDefaultRoot ~\nRequireValidShell off\nAuthOrder mod_sql.c\n\n#\n# Choose a SQL backend among MySQL or PostgreSQL.\n# Both modules are loaded in default configuration, so you have to specify the backend\n# or comment out the unused module in /etc/proftpd/modules.conf.\n# Use 'mysql' or 'postgres' as possible values.\n#\nSQLBackend\tmysql\n#\nSQLEngine on\nSQLAuthenticate on\n#\n# Use both an encrypted or plaintext password\nSQLAuthTypes Crypt OpenSSL\n\nSQLAuthenticate users* groups*\n\n#\n# Connection\nSQLConnectInfo <SQL_DB>@<SQL_HOST> <SQL_UNPRIVILEGED_USER> <SQL_UNPRIVILEGED_PASSWORD>\n#\n# Describes both users/groups tables\n#\nSQLUserInfo ftp_users username password uid gid homedir shell\nSQLGroupInfo ftp_groups groupname gid members\n#\nSQLUserWhereClause \"login_enabled = 'y'\"\n\nSQLLog PASS login\nSQLNamedQuery login UPDATE \"last_login=now(), login_count=login_count+1 WHERE username='%u'\" ftp_users\n\nSQLLog RETR download\nSQLNamedQuery download UPDATE \"down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'\" ftp_users\n\nSQLLog STOR upload\nSQLNamedQuery upload UPDATE \"up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'\" ftp_users\n\nQuotaEngine on\nQuotaShowQuotas on\nQuotaDisplayUnits Mb\nQuotaLock /var/lock/ftpd.quotatab.lock\nQuotaLimitTable sql:/get-quota-limit\nQuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally\nSQLNamedQuery get-quota-limit SELECT \"ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'\"\nSQLNamedQuery get-quota-tally SELECT \"name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'\"\nSQLNamedQuery update-quota-tally UPDATE \"bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'\" ftp_quotatallies\nSQLNamedQuery insert-quota-tally INSERT \"%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}\" ftp_quotatallies\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/tls.conf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n<IfModule mod_tls.c>\nTLSEngine                               on\nTLSLog                                  /var/log/proftpd/tls.log\nTLSProtocol                             TLSv1.2 TLSv1.3\nTLSRSACertificateFile                   /etc/ssl/certs/proftpd.crt\nTLSRSACertificateKeyFile                /etc/ssl/private/proftpd.key\nTLSECCertificateFile                    /etc/ssl/certs/proftpd_ec.crt\nTLSECCertificateKeyFile                 /etc/ssl/private/proftpd_ec.key\n# TLSCACertificateFile\nTLSOptions                              NoSessionReuseRequired\nTLSVerifyClient                         off\n\n# Are clients required to use FTP over TLS when talking to this server?\nTLSRequired                             on\n\n# Allow SSL/TLS renegotiations when the client requests them, but\n# do not force the renegotiations.  Some clients do not support\n# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these\n# clients will close the data connection, or there will be a timeout\n# on an idle data connection.\n#\n#TLSRenegotiate                          required off\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/conf.d/99-froxlor-ratelimit.conf\" chown=\"root:0\"\n\t\t\t\t\t\t  chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n<Class whitelist>\nFrom 127.0.0.1\n</Class>\n\nMaxLoginAttempts 3\n<IfModule mod_ban.c>\n <IfClass whitelist>\n  BanEngine off\n </IfClass>\n <IfClass !whitelist>\n  BanEngine on\n </IfClass>\nBanLog /var/log/proftpd/ban.log\nBanTable /etc/proftpd/ban.tab\nBanMessage \"User %u was banned.\"\nBanOnEvent ClientConnectRate 10/00:00:02 02:00:00 \"Stop connecting frequently\"\nBanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00\nBanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99\nBanControlsACLs all allow user root\n</IfModule>\n\n<IfClass whitelist>\nBanEngine off\nDelayEngine off\n</IfClass>\n\t\t\t\t\t]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service proftpd restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Pureftpd -->\n\t\t\t\t<daemon name=\"pureftpd\" title=\"PureFTPd\">\n\t\t\t\t\t<install><![CDATA[apt-get install pure-ftpd-common pure-ftpd-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/private/pure-ftpd.pem ] || openssl req -x509 -nodes -days 7300 -newkey rsa:4096 -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nopenssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072\nchmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/TLS\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MinUID\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1000\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MySQLConfigFile\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n/etc/pure-ftpd/db/mysql.conf\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/NoAnonymous\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MaxIdleTime\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n15\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/ChrootEveryone\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/PAMAuthentication\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nno\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/db/mysql.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0640\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n##############################################\n#                                            #\n# Sample Pure-FTPd Mysql configuration file. #\n# See README.MySQL for explanations.         #\n#                                            #\n##############################################\n\n\n# Optional : MySQL server name or IP. Don't define this for unix sockets.\n\n# MYSQLServer     127.0.0.1\n\n\n# Optional : MySQL port. Don't define this if a local unix socket is used.\n\n# MYSQLPort       3306\n\n\n# Optional : define the location of mysql.sock if the server runs on this host.\n\nMYSQLSocket      /var/run/mysqld/mysqld.sock\n\n\n# Mandatory : user to bind the server as.\n\nMYSQLUser       <SQL_UNPRIVILEGED_USER>\n\n\n# Mandatory : user password. You must have a password.\n\nMYSQLPassword   <SQL_UNPRIVILEGED_PASSWORD>\n\n\n# Mandatory : database to open.\n\nMYSQLDatabase   <SQL_DB>\n\n\n# Mandatory : how passwords are stored\n# Valid values are : \"cleartext\", \"crypt\", \"sha1\", \"md5\" and \"password\"\n# (\"password\" = MySQL password() function)\n# You can also use \"any\" to try \"crypt\", \"sha1\", \"md5\" *and* \"password\"\n\nMYSQLCrypt      any\n\n\n# In the following directives, parts of the strings are replaced at\n# run-time before performing queries :\n#\n# \\L is replaced by the login of the user trying to authenticate.\n# \\I is replaced by the IP address the user connected to.\n# \\P is replaced by the port number the user connected to.\n# \\R is replaced by the IP address the user connected from.\n# \\D is replaced by the remote IP address, as a long decimal number.\n#\n# Very complex queries can be performed using these substitution strings,\n# especially for virtual hosting.\n\n\n# Query to execute in order to fetch the password\n\nMYSQLGetPW      SELECT password FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Query to execute in order to fetch the system user name or uid\n\nMYSQLGetUID     SELECT uid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default UID - if set this overrides MYSQLGetUID\n\n#MYSQLDefaultUID 1000\n\n\n# Query to execute in order to fetch the system user group or gid\n\nMYSQLGetGID     SELECT gid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default GID - if set this overrides MYSQLGetGID\n\n#MYSQLDefaultGID 1000\n\n\n# Query to execute in order to fetch the home directory\n\nMYSQLGetDir     SELECT homedir FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : query to get the maximal number of files\n# Pure-FTPd must have been compiled with virtual quotas support.\n\n# MySQLGetQTAFS  SELECT QuotaFiles FROM users WHERE User='\\L'\n\n\n# Optional : query to get the maximal disk usage (virtual quotas)\n# The number should be in Megabytes.\n# Pure-FTPd must have been compiled with virtual quotas support.\n\nMySQLGetQTASZ SELECT CASE WHEN panel_customers.diskspace = 0 THEN -1 WHEN panel_customers.diskspace <= -1 THEN 0 ELSE panel_customers.diskspace/1024 END AS QuotaSize FROM panel_customers, ftp_users WHERE username = \"\\L\" AND panel_customers.loginname = SUBSTRING_INDEX('\\L', 'ftp', 1)\n\n\n# Optional : ratios. The server has to be compiled with ratio support.\n\n# MySQLGetRatioUL SELECT ULRatio FROM users WHERE User='\\L'\n# MySQLGetRatioDL SELECT DLRatio FROM users WHERE User='\\L'\n\n\n# Optional : bandwidth throttling.\n# The server has to be compiled with throttling support.\n# Values are in KB/s .\n\n# MySQLGetBandwidthUL SELECT ULBandwidth FROM users WHERE User='\\L'\n# MySQLGetBandwidthDL SELECT DLBandwidth FROM users WHERE User='\\L'\n\n# Enable ~ expansion. NEVER ENABLE THIS BLINDLY UNLESS :\n# 1) You know what you are doing.\n# 2) Real and virtual users match.\n\n# MySQLForceTildeExpansion 1\n\n\n# If you're using a transactionnal storage engine, you can enable SQL\n# transactions to avoid races. Leave this commented if you are using the\n# traditional MyIsam engine.\n\n# MySQLTransactions On\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/CustomerProof\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/Bind\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n21\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/default/pure-ftpd-common\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Configuration for pure-ftpd\n# (this file is sourced by /bin/sh, edit accordingly)\n\n# STANDALONE_OR_INETD\n# valid values are \"standalone\" and \"inetd\".\n# Any change here overrides the setting in debconf.\nSTANDALONE_OR_INETD=standalone\n\n# VIRTUALCHROOT:\n# whether to use binary with virtualchroot support\n# valid values are \"true\" or \"false\"\n# Any change here overrides the setting in debconf.\nVIRTUALCHROOT=false\n\n# UPLOADSCRIPT: if this is set and the daemon is run in standalone mode,\n# pure-uploadscript will also be run to spawn the program given below\n# for handling uploads. see /usr/share/doc/pure-ftpd/README.gz or\n# pure-uploadscript(8)\n\n# example: UPLOADSCRIPT=/usr/local/sbin/uploadhandler.pl\nUPLOADSCRIPT=\n\n# if set, pure-uploadscript will spawn  running as the\n# given uid and gid\nUPLOADUID=\nUPLOADGID=\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pure-ftpd-mysql restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- System tools/services -->\n\t\t\t<service type=\"system\" title=\"{{lng.admin.configfiles.etc}}\">\n\t\t\t\t<!-- Webalizer -->\n\t\t\t\t<daemon name=\"webalizer\"\n\t\t\t\t\ttitle=\"Webalizer (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install webalizer]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- goaccess -->\n\t\t\t\t<daemon name=\"goaccess\"\n\t\t\t\t\ttitle=\"goaccess (traffic analyzer)\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install goaccess jq]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- AWstats -->\n\t\t\t\t<daemon name=\"awstats\"\n\t\t\t\t\ttitle=\"Awstats  (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install awstats]]></install>\n\t\t\t\t\t<command><![CDATA[mv {{settings.system.awstats_conf}}/awstats.conf {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's/^DirData/# DirData/' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's|^\\\\(DirIcons=\\\\).*$|\\\\1\\\\\"/awstats-icon\\\\\"|' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/cron.d/awstats]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/logrotate.d/httpd-prerotate/awstats]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- libnss-extrausers -->\n\t\t\t\t<daemon name=\"libnssextrausers\"\n\t\t\t\t\ttitle=\"libnss-extrausers\">\n\t\t\t\t\t<install><![CDATA[apt-get install libnss-extrausers]]></install>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/extrausers]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/passwd]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/group]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/shadow]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/nsswitch.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Make sure that `passwd`, `group` and `shadow` have extrausers in their lines\n# You should place extrausers at the end, so that it is queried after the other mechanisams\n#\npasswd:         files systemd compat extrausers\ngroup:          files systemd compat extrausers\nshadow:         files compat extrausers\ngshadow:        files\n\nhosts:       files dns\nnetworks:    files dns\n\nprotocols:   db files\nservices:    db files\nethers:      db files\nrpc:         db files\n\nnetmasks:    files\nnetgroup:    files\nbootparams:  files\n\nautomount:   files\naliases:     files\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mv /var/spool/postfix/etc/nsswitch.conf /var/spool/postfix/etc/nsswitch.conf.frx.bak]]></command>\n\t\t\t\t\t\t<command><![CDATA[cp /etc/nsswitch.conf /var/spool/postfix/etc/nsswitch.conf]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Logrotate -->\n\t\t\t\t<daemon name=\"logrotate\" title=\"Logrotate\">\n\t\t\t\t\t<install><![CDATA[apt-get install logrotate]]></install>\n\t\t\t\t\t<file name=\"/etc/logrotate.d/froxlor\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Froxlor logrotate snippet\n#\n<CUSTOMER_LOGS>*.log {\n  missingok\n  daily\n  rotate 7\n  compress\n  delaycompress\n  notifempty\n  create\n  sharedscripts\n  postrotate\n  <WEBSERVER_RELOAD_CMD> > /dev/null 2>&1 || true\n  endscript\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- FCGID -->\n\t\t\t\t<daemon name=\"fcgid\" title=\"FCGID\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2-suexec-pristine libapache2-mod-fcgid php-cgi]]></install>\n\t\t\t\t\t<command><![CDATA[a2enmod suexec fcgid]]></command>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.mod_fcgid_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.system.mod_fcgid_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.system.mod_fcgid_httpgroup}} {{settings.system.mod_fcgid_httpuser}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_configdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 1777 {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php8.1]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- PHP-FPM -->\n\t\t\t\t<daemon name=\"php-fpm\"\n\t\t\t\t\ttitle=\"PHP-FPM\">\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install apache2-suexec-pristine]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<install><![CDATA[apt-get install php-fpm]]></install>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2enmod suexec proxy_fcgi actions]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"usernamenotexists\">{{settings.phpfpm.vhost_httpuser}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.phpfpm.vhost_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.phpfpm.vhost_httpgroup}} {{settings.phpfpm.vhost_httpuser}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"4\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php8.1]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"5\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2disconf php8.1-fpm]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Cronjob -->\n\t\t\t\t<daemon name=\"cron\" title=\"Cronjob for froxlor\"\n\t\t\t\t\tmandatory=\"true\">\n\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install cron gnupg]]></install>\n\t\t\t\t\t<command><![CDATA[[ ! -e /usr/local/bin/froxlor-cli ] && ln -s <BASE_PATH>bin/froxlor-cli /usr/local/bin/froxlor-cli]]></command>\n\t\t\t\t\t<command><![CDATA[/usr/bin/php <BASE_PATH>bin/froxlor-cli froxlor:cron --run-task 99]]></command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.crondreload}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t</services>\n\t</distribution>\n</froxlor>\n"
  },
  {
    "path": "lib/configfiles/noble.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<froxlor>\n\t<distribution name=\"Ubuntu\" codename=\"Noble\"\n\t\tversion=\"24.04\" defaulteditor=\"/bin/nano\">\n\t\t<!-- OS defaults to be loaded on installation -->\n\t\t<defaults>\n\t\t\t<default settinggroup=\"system\" varname=\"nssextrausers\" value=\"1\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_vhost\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_diroptions\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_htpasswddir\" value=\"/etc/nginx/froxlor-htpasswd/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apachereload_command\" value=\"service nginx reload\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"letsencryptacmeconf\" value=\"/etc/nginx/acme.conf\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"phpfpm\" varname=\"fastcgi_ipcdir\" value=\"/var/run/php/\"></default>\n\t\t</defaults>\n\t\t<services>\n\t\t\t<!-- HTTP -->\n\t\t\t<service type=\"http\" title=\"{{lng.admin.configfiles.http}}\">\n\t\t\t\t<!-- general HTTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.documentroot_prefix}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.logfiles_directory}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"notempty\">{{settings.system.deactivateddocroot}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.deactivateddocroot}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- HTTP Apache -->\n\t\t\t\t<daemon name=\"apache\" version=\"2.4\" title=\"Apache 2.4\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2]]></install>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command><![CDATA[a2dismod userdir]]></command>\n\t\t\t\t\t<command><![CDATA[a2enmod headers]]></command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.use_ssl}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[a2enmod ssl]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n# Please remember to activate the use of mod_proxy / mod_proxy_fcgi in the PHP-FPM settings!!!\na2enmod proxy_fcgi\n]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nAlias \"/.well-known/acme-challenge\" \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\"\n<Directory \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\">\n\tRequire all granted\n</Directory>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- HTTP Nginx -->\n\t\t\t\t<daemon name=\"nginx\" title=\"nginx\">\n\t\t\t\t\t<install><![CDATA[apt-get install nginx]]></install>\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install php-cgi]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<file name=\"/etc/nginx/nginx.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nuser www-data;\nworker_processes auto;\npid /var/run/nginx.pid;\ninclude /etc/nginx/modules-enabled/*.conf;\n\nevents {\n\tworker_connections 768;\n\t# multi_accept on;\n}\n\nhttp {\n\n\t##\n\t# Basic Settings\n\t##\n\n\tsendfile on;\n\ttcp_nopush on;\n\ttcp_nodelay on;\n\tkeepalive_timeout 65;\n\ttypes_hash_max_size 2048;\n\t# server_tokens off;\n\n\t# server_names_hash_bucket_size 64;\n\t# server_name_in_redirect off;\n\n\tinclude /etc/nginx/mime.types;\n\tdefault_type application/octet-stream;\n\n\t##\n\t# Logging Settings\n\t##\n\n\taccess_log /var/log/nginx/access.log;\n\terror_log /var/log/nginx/error.log;\n\n\t##\n\t# Gzip Settings\n\t##\n\n\tgzip on;\n\tgzip_disable \"msie6\";\n\n\t# gzip_vary on;\n\t# gzip_proxied any;\n\t# gzip_comp_level 6;\n\t# gzip_buffers 16 8k;\n\t# gzip_http_version 1.1;\n\t# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;\n\n\t##\n\t# nginx-naxsi config\n\t##\n\t# Uncomment it if you installed nginx-naxsi\n\t##\n\n\t#include /etc/nginx/naxsi_core.rules;\n\n\t##\n\t# nginx-passenger config\n\t##\n\t# Uncomment it if you installed nginx-passenger\n\t##\n\n\t#passenger_root /usr;\n\t#passenger_ruby /usr/bin/ruby;\n\n\t##\n\t# Virtual Host Configs\n\t##\n\n\tinclude /etc/nginx/conf.d/*.conf;\n\tinclude /etc/nginx/sites-enabled/*;\n}\n\n\n#mail {\n#\t# See sample authentication script at:\n#\t# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript\n#\n#\t# auth_http localhost/auth.php;\n#\t# pop3_capabilities \"TOP\" \"USER\";\n#\t# imap_capabilities \"IMAP4rev1\" \"UIDPLUS\";\n#\n#\tserver {\n#\t\tlisten     localhost:110;\n#\t\tprotocol   pop3;\n#\t\tproxy      on;\n#\t}\n#\n#\tserver {\n#\t\tlisten     localhost:143;\n#\t\tprotocol   imap;\n#\t\tproxy      on;\n#\t}\n#}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/nginx/fastcgi_params\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nfastcgi_connect_timeout 65;\nfastcgi_send_timeout    180;\nfastcgi_read_timeout    180;\n\nfastcgi_param  QUERY_STRING       $query_string;\nfastcgi_param  REQUEST_METHOD     $request_method;\nfastcgi_param  CONTENT_TYPE       $content_type;\nfastcgi_param  CONTENT_LENGTH     $content_length;\n\nfastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\nfastcgi_param  REQUEST_URI        $request_uri;\nfastcgi_param  DOCUMENT_URI       $document_uri;\nfastcgi_param  DOCUMENT_ROOT      $document_root;\nfastcgi_param  SERVER_PROTOCOL    $server_protocol;\nfastcgi_param  HTTPS              $https if_not_empty;\n\nfastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\nfastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\nfastcgi_param  REMOTE_ADDR        $remote_addr;\nfastcgi_param  REMOTE_PORT        $remote_port;\nfastcgi_param  SERVER_ADDR        $server_addr;\nfastcgi_param  SERVER_PORT        $server_port;\nfastcgi_param  SERVER_NAME        $server_name;\n\n# PHP only, required if PHP was built with --enable-force-cgi-redirect\nfastcgi_param  REDIRECT_STATUS    200;\n\n# Fix for HTTP/3 ($http_host is not populated automatically)\nfastcgi_param  HTTP_HOST          $host;\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nlocation /.well-known/acme-challenge {\n\talias {{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge;\n\n\tlocation ~ /.well-known/acme-challenge/(.*) {\n\t\tdefault_type text/plain;\n\t}\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/init.d/php-fcgi\" chmod=\"u+x\" backup=\"true\">\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n#!/bin/bash\nBIND=\"127.0.0.1:8888\"\nUSER=\"www-data\"\nPHP_FCGI_CHILDREN=\"15\"\nPHP_FCGI_MAX_REQUESTS=\"1000\"\n\nPHP_CGI=\"/usr/bin/php-cgi\"\nPHP_CGI_NAME=\"$(basename ${PHP_CGI})\"\nPHP_CGI_ARGS=\"- USER=${USER} PATH=/usr/bin PHP_FCGI_CHILDREN=${PHP_FCGI_CHILDREN} PHP_FCGI_MAX_REQUESTS=${PHP_FCGI_MAX_REQUESTS} ${PHP_CGI} -b ${BIND}\"\nRETVAL=\"0\"\n\nstart() {\n      echo -n \"Starting PHP FastCGI: \"\n      start-stop-daemon --quiet --start --background --chuid \"$USER\" --exec /usr/bin/env -- $PHP_CGI_ARGS\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\nstop() {\n      echo -n \"Stopping PHP FastCGI: \"\n      killall -q -w -u ${USER} ${PHP_CGI}\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\n\ncase \"$1\" in\n    start)\n      start\n  ;;\n    stop)\n      stop\n  ;;\n    restart)\n      stop\n      start\n  ;;\n    *)\n      echo \"Usage: php-fastcgi {start|stop|restart}\"\n      exit 1\n  ;;\nesac\nexit \"$RETVAL\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[/etc/init.d/php-fcgi restart]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!--DNS -->\n\t\t\t<service type=\"dns\" title=\"{{lng.admin.configfiles.dns}}\">\n\t\t\t\t<!--Bind9 -->\n\t\t\t\t<daemon name=\"bind\" title=\"Bind9 nameserver\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install bind9]]></install>\n\t\t\t\t\t<command><![CDATA[echo \"include \\\"{{settings.system.bindconf_directory}}froxlor_bind.conf\\\";\" >> /etc/bind/named.conf.local]]></command>\n\t\t\t\t\t<command><![CDATA[touch {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chown bind:0 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chmod 0644 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[service bind9 restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns\" title=\"PowerDNS (standalone)\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server pdns-backend-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\nallow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# allow-recursion\tList of subnets that are allowed to recurse\n#\nallow-recursion=127.0.0.1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# recursive-cache-ttl\tSeconds to store packets for recursive queries in the PacketCache\n#\n# recursive-cache-ttl=10\n\n#################################\n# recursor\tIf recursion is desired, IP address of a recursing nameserver\n#\n# recursor=no\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# mysql-settings / you need to create the power-dns database for yourself!\nlaunch=gmysql\ngmysql-host=127.0.0.1\ngmysql-port=3306\ngmysql-dbname=pdns\ngmysql-user=powerdns\ngmysql-group=client\ngmysql-password=\n#gmysql-ssl-ca-file=\n#gmysql-ssl-verify-server-certificate=0\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns_bind\"\n\t\t\t\t\ttitle=\"PowerDNS via bind-backend\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\n# allow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# allow-recursion\tList of subnets that are allowed to recurse\n#\nallow-recursion=127.0.0.1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\nlaunch=bind\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# recursive-cache-ttl\tSeconds to store packets for recursive queries in the PacketCache\n#\n# recursive-cache-ttl=10\n\n#################################\n# recursor\tIf recursion is desired, IP address of a recursing nameserver\n#\n# recursor=no\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# Bind backend configuration\n\n# Location of the Bind configuration file to parse.\nbind-config=<BIND_CONFIG_PATH>named.conf\n\n# How often to check for zone changes. See 'Operation' section.\nbind-check-interval=180\n\n# Uncomment to enable Huffman compression on zone data.\n# Currently saves around 20% of memory actually used, but slows down operation.\n# bind-enable-huffman\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- SMTP services -->\n\t\t\t<service type=\"smtp\" title=\"{{lng.admin.configfiles.smtp}}\">\n\t\t\t\t<!-- general SMTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"groupnotexists\">{{settings.system.vmail_gid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[groupadd -g {{settings.system.vmail_gid}} vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"usernotexists\">{{settings.system.vmail_uid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[useradd -u {{settings.system.vmail_uid}} -g vmail vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install postfix postfix-mysql]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/etc/pam.d]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/var/run/mysqld]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R {{settings.system.vmail_uid}}:{{settings.system.vmail_gid}} {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0750  {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"0\">\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_alias_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT destination FROM mail_virtual AS v, panel_customers AS c WHERE c.customerid = v.customerid AND c.deactivated = 0 AND v.email = '%s' AND trim(v.destination) <> ''\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_domains.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' AND deactivated = 0\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_sender_permissions.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT GROUP_CONCAT(DISTINCT mu.username SEPARATOR ' ') AS sasl_users FROM mail_users mu WHERE mu.username = '%s' OR mu.email IN ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = CONCAT('@', SUBSTRING_INDEX('%s','@',-1))));\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_uid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT uid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_gid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT gid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/aliases\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# /etc/aliases\nmailer-daemon: postmaster\npostmaster: root\nnobody: root\nhostmaster: root\nusenet: root\nnews: root\nwebmaster: root\nwww: root\nftp: root\nabuse: root\nnoc: root\nsecurity: root\n\n# change this to a valid e-mail address you can access\nroot:               <ADMIN_MAIL>\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[newaliases]]></command>\n\t\t\t\t\t\t<command><![CDATA[service postfix restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- postfix with dovecot -->\n\t\t\t\t<daemon name=\"postfix_dovecot\" title=\"Postfix with dovecot\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<file name=\"/etc/postfix/main.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# See /usr/share/postfix/main.cf.dist for a commented, more complete version\n\n\n# Debian specific:  Specifying a file name will cause the first\n# line of that file to be used as the name.  The Debian default\n# is /etc/mailname.\n#myorigin = /etc/mailname\n\nsmtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)\nbiff = no\n\n# appending .domain is the MUA's job.\nappend_dot_mydomain = no\n\n# Uncomment the next line to generate \"delayed mail\" warnings\n#delay_warning_time = 4h\n\nreadme_directory = no\n\n# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on\n# fresh installs.\ncompatibility_level = 2\n\n# INTERNET HOST AND DOMAIN NAMES\n#\n# The myhostname parameter specifies the internet hostname of this\n# mail system. The default is to use the fully-qualified domain name\n# from gethostname(). $myhostname is used as a default value for many\n# other configuration parameters.\n#\n# Froxlor Note: $myhostname can and should be the same as $mydomain as long as\n# you don't intend to send mail to it (it will be considered local, not virtual)\n# for the case of a subdomain, $mydomain *must* be equal to $myhostname,\n# otherwise you cannot use the main domain for virtual transport.\n# also check the note about $mydomain below.\nmyhostname = $mydomain\n#myhostname = virtual.domain.tld\n\n# The mydomain parameter specifies the local internet domain name.\n# The default is to use $myhostname minus the first component.\n# $mydomain is used as a default value for many other configuration\n# parameters.\n#\n# Froxlor Note: We are using a default here but that may or may not make sense,\n# depending on your dns configuration, please check yourself.\n\n# FQDN from Froxlor\nmydomain = <SERVERNAME>\n\nmydestination = $myhostname, localhost.$mydomain, localhost\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain\n#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain,\n#\tmail.$mydomain, www.$mydomain, ftp.$mydomain\n\n# The default setting is 550 (reject mail) but it is safer to start\n# with 450 (try again later) until you are certain that your\n# local_recipient_maps settings are OK.\n#\nunknown_local_recipient_reject_code = 550\n\n# The mailbox_command parameter specifies the optional external\n# command to use instead of mailbox delivery. The command is run as\n# the recipient with proper HOME, SHELL and LOGNAME environment settings.\n# Exception:  delivery for root is done as $default_user.\n#\n# Other environment variables of interest: USER (recipient username),\n# EXTENSION (address extension), DOMAIN (domain part of address),\n# and LOCAL (the address localpart).\n#\n# Unlike other Postfix configuration parameters, the mailbox_command\n# parameter is not subjected to $parameter substitutions. This is to\n# make it easier to specify shell syntax (see example below).\n#\n# Avoid shell meta characters because they will force Postfix to run\n# an expensive shell process. Procmail alone is expensive enough.\n#\n# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN\n# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER.\n#\nmailbox_command = /usr/lib/dovecot/deliver\n#mailbox_command = /usr/bin/procmail -a \"$EXTENSION\"\n\n# The debugger_command specifies the external command that is executed\n# when a Postfix daemon program is run with the -D option.\n#\n# Use \"command .. & sleep 5\" so that the debugger can attach before\n# the process marches on. If you use an X-based debugger, be sure to\n# set up your XAUTHORITY environment variable before starting Postfix.\n#\ndebugger_command =\n\t PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin\n\t ddd $daemon_directory/$process_name $process_id & sleep 5\n\ninet_protocols = ipv4\n\nsmtpd_helo_required = yes\nsmtpd_recipient_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unauth_destination,\n\treject_unauth_pipelining,\n\treject_non_fqdn_recipient\nsmtpd_sender_restrictions = permit_mynetworks,\n\treject_sender_login_mismatch,\n\tpermit_sasl_authenticated,\n\treject_unknown_helo_hostname,\n\treject_unknown_recipient_domain,\n\treject_unknown_sender_domain\nsmtpd_client_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unknown_client_hostname\n\n# Postfix 2.10 requires this option. Postfix < 2.10 ignores this.\n# The option is intentionally left empty.\nsmtpd_relay_restrictions =\n\n# Maximum size of Message in bytes (50MB)\nmessage_size_limit = 52428800\n\n## SASL Auth Settings\nsmtpd_sasl_auth_enable = yes\nsmtpd_sasl_local_domain = $myhostname\nbroken_sasl_auth_clients = yes\n## Dovecot Settings for deliver, SASL Auth and virtual transport\nsmtpd_sasl_type = dovecot\nvirtual_transport = dovecot\ndovecot_destination_recipient_limit = 1\nsmtpd_sasl_path = private/auth\n\n# Virtual delivery settings\nvirtual_mailbox_base = /\nvirtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf\nvirtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf\nvirtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_alias_maps.cf\nsmtpd_sender_login_maps = proxy:mysql:/etc/postfix/mysql-virtual_sender_permissions.cf\nvirtual_uid_maps = static:<VIRTUAL_UID_MAPS>\nvirtual_gid_maps = static:<VIRTUAL_GID_MAPS>\n\n# Local delivery settings\nlocal_transport = local\nalias_maps = $alias_database\n\n# Default Mailbox size, is set to 0 which means unlimited!\nmailbox_size_limit = 0\nvirtual_mailbox_limit = 0\n\n### TLS settings\n###\n## TLS for outgoing mails from the server to another server\nsmtp_tls_security_level = may\nsmtp_tls_note_starttls_offer = yes\n## TLS for incoming connections (clients or other mail servers)\nsmtpd_tls_security_level = may\nsmtpd_tls_cert_file = <SSL_CERT_FILE>\nsmtpd_tls_key_file = <SSL_KEY_FILE>\n#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt\nsmtpd_tls_loglevel = 1\nsmtpd_tls_received_header = yes\nsmtp_use_tls = yes\nsmtpd_use_tls = yes\nsmtpd_tls_session_cache_timeout = 3600s\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/files[@index=0]</include>\n\t\t\t\t\t<file name=\"/etc/postfix/master.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Postfix master process configuration file.  For details on the format\n# of the file, see the master(5) manual page (command: \"man 5 master\" or\n# on-line: http://www.postfix.org/master.5.html).\n#\n# Do not forget to execute \"postfix reload\" after editing this file.\n#\n# ==========================================================================\n# service type  private unpriv  chroot  wakeup  maxproc command + args\n#               (yes)   (yes)   (no)    (never) (100)\n# ==========================================================================\n#smtp      inet  n       -       y       -       -       smtpd\nsmtp      inet  n       -       y       -       1       postscreen\nsmtpd     pass  -       -       y       -       -       smtpd\ndnsblog   unix  -       -       y       -       0       dnsblog\ntlsproxy  unix  -       -       y       -       0       tlsproxy\nsubmission inet n       -       y       -       -       smtpd\n  -o syslog_name=postfix/submission\n  -o smtpd_tls_security_level=encrypt\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\nsmtps     inet  n       -       y       -       -       smtpd\n  -o syslog_name=postfix/smtps\n  -o smtpd_tls_wrappermode=yes\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\n#628       inet  n       -       y       -       -       qmqpd\npickup    unix  n       -       y       60      1       pickup\n         -o content_filter=\n         -o receive_override_options=no_header_body_checks\ncleanup   unix  n       -       n       -       0       cleanup\nqmgr      unix  n       -       n       300     1       qmgr\n#qmgr     unix  n       -       n       300     1       oqmgr\ntlsmgr    unix  -       -       n       1000?   1       tlsmgr\nrewrite   unix  -       -       n       -       -       trivial-rewrite\nbounce    unix  -       -       n       -       0       bounce\ndefer     unix  -       -       n       -       0       bounce\ntrace     unix  -       -       n       -       0       bounce\nverify    unix  -       -       n       -       1       verify\nflush     unix  n       -       n       1000?   0       flush\nproxymap  unix  -       -       n       -       -       proxymap\nproxywrite unix -       -       n       -       1       proxymap\nsmtp      unix  -       -       n       -       -       smtp\nrelay     unix  -       -       n       -       -       smtp\n        -o syslog_name=postfix/$service_name\n#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5\nshowq     unix  n       -       n       -       -       showq\nerror     unix  -       -       n       -       -       error\nretry     unix  -       -       n       -       -       error\ndiscard   unix  -       -       n       -       -       discard\nlocal     unix  -       n       n       -       -       local\nvirtual   unix  -       n       n       -       -       virtual\nlmtp      unix  -       -       n       -       -       lmtp\nanvil     unix  -       -       n       -       1       anvil\nscache    unix  -       -       n       -       1       scache\npostlog   unix-dgram n  -       n       -       1       postlogd\n#\n# ====================================================================\n# Interfaces to non-Postfix software. Be sure to examine the manual\n# pages of the non-Postfix software to find out what options it wants.\n#\n# Many of the following services use the Postfix pipe(8) delivery\n# agent.  See the pipe(8) man page for information about ${recipient}\n# and other message envelope options.\n# ====================================================================\n#\n# maildrop. See the Postfix MAILDROP_README file for details.\n# Also specify in main.cf: maildrop_destination_recipient_limit=1\n#\n#maildrop  unix  -       n       n       -       -       pipe\n#  flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}\n#\n# ====================================================================\n#\n# Recent Cyrus versions can use the existing \"lmtp\" master.cf entry.\n#\n# Specify in cyrus.conf:\n#   lmtp    cmd=\"lmtpd -a\" listen=\"localhost:lmtp\" proto=tcp4\n#\n# Specify in main.cf one or more of the following:\n#  mailbox_transport = lmtp:inet:localhost\n#  virtual_transport = lmtp:inet:localhost\n#\n# ====================================================================\n#\n# Cyrus 2.1.5 (Amos Gouaux)\n# Also specify in main.cf: cyrus_destination_recipient_limit=1\n#\n#cyrus     unix  -       n       n       -       -       pipe\n#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}\n#\n# ====================================================================\n#\n# Old example of delivery via Cyrus.\n#\n#old-cyrus unix  -       n       n       -       -       pipe\n#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}\n#\n# ====================================================================\n#\n# See the Postfix UUCP_README file for configuration details.\n#\n#uucp      unix  -       n       n       -       -       pipe\n#  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)\n#\n# ====================================================================\n#\n# Other external delivery methods.\n#\n#ifmail    unix  -       n       n       -       -       pipe\n#  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)\n#\n#bsmtp     unix  -       n       n       -       -       pipe\n#  flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient\n#\n#scalemail-backend unix -       n       n       -       2       pipe\n#  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store\n#  ${nexthop} ${user} ${extension}\n#\n#mailman   unix  -       n       n       -       -       pipe\n#  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py\n#  ${nexthop} ${user}\n#\n# Dovecot LDA\ndovecot\t  unix\t-\tn\tn\t-\t-\tpipe\n\tflags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ${recipient}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- IMAP/POP3 services -->\n\t\t\t<service type=\"mail\" title=\"{{lng.admin.configfiles.mail}}\">\n\t\t\t\t<!-- valid for both dovecots -->\n\t\t\t\t<general>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-managesieved dovecot-sieve]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/dovecot/dovecot-sql.conf.ext\"\n\t\t\t\t\t\t\tchown=\"root:root\" chmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\ndriver = mysql\nconnect = \"host=<SQL_HOST> dbname=<SQL_DB> user=<SQL_UNPRIVILEGED_USER> password=<SQL_UNPRIVILEGED_PASSWORD>\"\nuser_query = SELECT CONCAT(homedir, maildir) AS home, CONCAT('maildir:', homedir, maildir) AS mail, uid, gid, CONCAT('*:storage=', quota, 'M') as quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u')\npassword_query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid,  CONCAT('maildir:', homedir, maildir) AS userdb_mail, CONCAT('*:storage=', quota, 'M') as userdb_quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') AND ((imap = 1 AND '%Ls' = 'imap') OR (pop3 = 1 AND '%Ls' = 'pop3') OR ((postfix = 'Y' AND '%Ls' = 'smtp') OR (postfix = 'Y' AND '%Ls' = 'sieve')))\niterate_query = \"SELECT username AS user FROM mail_users WHERE (imap = 1 OR pop3 = 1)\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/99-froxlor.conf\" chown=\"root:0\"\n\t\t\t\t\t\t\t  chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\ndisable_plaintext_auth = no\nauth_mechanisms = plain login\n!include auth-sql.conf.ext\nmail_location = mbox:~/mail:INBOX=/var/mail/%u\n\nnamespace inbox {\n  inbox = yes\n}\nmail_privileged_group = mail\n\nservice auth {\n  # Postfix smtp-auth\n  unix_listener /var/spool/postfix/private/auth {\n    mode = 0660\n    user = postfix\n    group = postfix\n  }\n  # Exim4 smtp-auth\n  unix_listener auth-client {\n    mode = 0660\n    user = mail\n    #group = Debian-exim\n  }\n}\n\nservice stats {\n  unix_listener stats-reader {\n    group = vmail\n    mode = 0666\n  }\n  unix_listener stats-writer {\n    group = vmail\n    mode = 0666\n  }\n}\n\nssl = yes\nssl_cert = <<SSL_CERT_FILE>\nssl_key = <<SSL_KEY_FILE>\nssl_dh = </usr/share/dovecot/dh.pem\n\npostmaster_address = postmaster@<SERVERNAME>\n\nprotocol imap {\n  mail_plugins = $mail_plugins quota imap_quota\n}\n\npop3_logout_format = in=%i out=%o top=%t/%p, retr=%r/%b, del=%d/%m, size=%s\n\nplugin {\n  sieve = file:~/sieve;active=~/.dovecot.sieve\n  sieve_dir = ~/sieve\n}\n\nplugin {\n  quota = maildir:User quota\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[service dovecot restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- Dovecot with postfix -->\n\t\t\t\t<daemon name=\"dovecot_postfix\" version=\"2\"\n\t\t\t\t\ttitle=\"Dovecot with postfix\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='mail']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- Antispam services -->\n\t\t\t<service type=\"antispam\" title=\"Antispam\">\n\t\t\t\t<!-- general RSpamd commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install wget gnupg]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/apt/keyrings]]></command>\n\t\t\t\t\t\t<command><![CDATA[wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | tee /etc/apt/keyrings/rspamd.gpg > /dev/null]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ noble main\" > /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[echo \"deb-src [signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ noble main\" >> /etc/apt/sources.list.d/rspamd.list]]></command>\n\t\t\t\t\t\t<command><![CDATA[apt-get update]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install rspamd]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/local.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/override.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/rspamd/dkim/]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/actions.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# Set rewrite subject to this value (%s is replaced by the original subject)\nsubject = \"***SPAM*** %s\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/arc.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\ntry_fallback = true;\n### Enable DKIM signing for alias sender addresses\nallow_username_mismatch = true;\npath = \"/var/lib/rspamd/dkim/$domain.$selector.key\";\nselector_map = \"/etc/rspamd/dkim_selectors.map\";\nuse_esld = false;\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/milter_headers.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuse = [\"x-spamd-bar\", \"x-spam-level\", \"authentication-results\"];\nauthenticated_headers = [\"authentication-results\"];\nextended_spam_headers = true\nskip_local = false\nskip_authenticated = false\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/replies.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## If a user has replied to an email, don’t mark other emails in the same thread as spam\naction = \"no action\";\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/rspamd/local.d/settings.conf\"\n\t\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n## Feel free to include your own settings or adjustments here, for example:\n#whitelist {\n#  priority = low;\n#  rcpt = \"postmaster@example.com\";\n#  want_spam = yes;\n#}\n\n## Include froxlor generated settings\n.include(try=true,priority=1,duplicate=merge) \"{{settings.antispam.config_file}}\"\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[cp /etc/rspamd/local.d/arc.conf /etc/rspamd/local.d/dkim_signing.conf]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_protocol = 6\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_default_action = accept\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"non_smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R _rspamd:_rspamd /var/lib/rspamd/dkim]]></command>\n\t\t\t\t\t\t<command><![CDATA[service rspamd restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- rspamd -->\n\t\t\t\t<daemon name=\"rspamd\" title=\"Rspamd\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- FTP services -->\n\t\t\t<service type=\"ftp\" title=\"{{lng.admin.configfiles.ftp}}\">\n\t\t\t\t<!-- Proftpd -->\n\t\t\t\t<daemon name=\"proftpd\" title=\"ProFTPd\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install proftpd-basic proftpd-mod-mysql proftpd-mod-crypto proftpd-mod-wrap]]></install>\n\t\t\t\t\t<file name=\"/etc/proftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/certs/proftpd.crt ] || openssl req -new -x509 -newkey rsa:4096 -days 3650 -nodes -out /etc/ssl/certs/proftpd.crt -keyout /etc/ssl/private/proftpd.key -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\n[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nchmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/proftpd/proftpd.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# /etc/proftpd/proftpd.conf -- This is a basic ProFTPD configuration file.\n# To really apply changes, reload proftpd after modifications, if\n# it runs in daemon mode. It is not required in inetd/xinetd mode.\n#\n\n# Includes DSO modules\nInclude /etc/proftpd/modules.conf\n\n# Set off to disable IPv6 support which is annoying on IPv4 only boxes.\nUseIPv6\t\t\t\ton\n# If set on you can experience a longer connection delay in many cases.\n<IfModule mod_ident.c>\n\tIdentLookups\t\t\t\toff\n</IfModule>\n\nServerName                     \"<SERVERNAME> FTP Server\"\nServerType\t\t\tstandalone\nDeferWelcome\t\t\toff\n\nDefaultServer\t\t\ton\nShowSymlinks\t\t\ton\n\nTimeoutNoTransfer\t\t600\nTimeoutStalled\t\t\t600\nTimeoutIdle\t\t\t1200\n\nDisplayLogin                    welcome.msg\nDisplayChdir               \t.message true\nListOptions                \t\"-l\"\n\nDenyFilter\t\t\t\\*.*/\n\n# Use this to jail all users in their homes\n# DefaultRoot\t\t\t~\n\n# Users require a valid shell listed in /etc/shells to login.\n# Use this directive to release that constrain.\n# RequireValidShell\t\toff\n\n# Port 21 is the standard FTP port.\nPort\t\t\t\t21\n\n# In some cases you have to specify passive ports range to by-pass\n# firewall limitations. Ephemeral ports can be used for that, but\n# feel free to use a more narrow range.\n# PassivePorts                  49152 65534\n\n# If your host was NATted, this option is useful in order to\n# allow passive transfers to work. You have to use your public\n# address and opening the passive ports used on your firewall as well.\n# MasqueradeAddress\t\t1.2.3.4\n\n# This is useful for masquerading address with dynamic IPs:\n# refresh any configured MasqueradeAddress directives every 8 hours\n<IfModule mod_dynmasq.c>\n# DynMasqRefresh 28800\n</IfModule>\n\n# To prevent DoS attacks, set the maximum number of child processes\n# to 30.  If you need to allow more than 30 concurrent connections\n# at once, simply increase this value.  Note that this ONLY works\n# in standalone mode, in inetd mode you should use an inetd server\n# that allows you to limit maximum number of processes per service\n# (such as xinetd)\nMaxInstances\t\t\t30\n\n# Set the user and group that the server normally runs at.\nUser\t\t\t\tproftpd\nGroup\t\t\t\tnogroup\n\n# Umask 022 is a good standard umask to prevent new files and dirs\n# (second parm) from being group and world writable.\nUmask\t\t\t\t022  022\n# Normally, we want files to be overwritable.\nAllowOverwrite\t\t\ton\n\n# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords:\n# PersistentPasswd\t\toff\n\n# This is required to use both PAM-based authentication and local passwords\n# AuthOrder\t\t\tmod_auth_pam.c* mod_auth_unix.c\n\n# Be warned: use of this directive impacts CPU average load!\n# Uncomment this if you like to see progress and transfer rate with ftpwho\n# in downloads. That is not needed for uploads rates.\n#\n# UseSendFile\t\t\toff\n\nTransferLog /var/log/proftpd/xferlog\nSystemLog   /var/log/proftpd/proftpd.log\n\n# Logging onto /var/log/lastlog is enabled but set to off by default\n#UseLastlog on\n\n# In order to keep log file dates consistent after chroot, use timezone info\n# from /etc/localtime.  If this is not set, and proftpd is configured to\n# chroot (e.g. DefaultRoot or <Anonymous>), it will use the non-daylight\n# savings timezone regardless of whether DST is in effect.\n#SetEnv TZ :/etc/localtime\n\n<IfModule mod_quotatab.c>\nQuotaEngine on\n</IfModule>\n\n<IfModule mod_ratio.c>\nRatios off\n</IfModule>\n\n\n# Delay engine reduces impact of the so-called Timing Attack described in\n# http://www.securityfocus.com/bid/11430/discuss\n# It is on by default.\n<IfModule mod_delay.c>\nDelayEngine on\n</IfModule>\n\n<IfModule mod_ctrls.c>\nControlsEngine        off\nControlsMaxClients    2\nControlsLog           /var/log/proftpd/controls.log\nControlsInterval      5\nControlsSocket        /var/run/proftpd/proftpd.sock\n</IfModule>\n\n<IfModule mod_ctrls_admin.c>\nAdminControlsEngine off\n</IfModule>\n\n#\n# Alternative authentication frameworks\n#\n#Include /etc/proftpd/ldap.conf\nInclude /etc/proftpd/sql.conf\n\n#\n# This is used for FTPS connections\n#\nInclude /etc/proftpd/tls.conf\n\n#\n# Useful to keep VirtualHost/VirtualRoot directives separated\n#\n#Include /etc/proftpd/virtuals.conf\n\n# A basic anonymous configuration, no upload directories.\n\n# <Anonymous ~ftp>\n#   User\t\t\t\tftp\n#   Group\t\t\t\tnogroup\n#   # We want clients to be able to login with \"anonymous\" as well as \"ftp\"\n#   UserAlias\t\t\tanonymous ftp\n#   # Cosmetic changes, all files belongs to ftp user\n#   DirFakeUser\ton ftp\n#   DirFakeGroup on ftp\n#\n#   RequireValidShell\t\toff\n#\n#   # Limit the maximum number of anonymous logins\n#   MaxClients\t\t\t10\n#\n#   # We want 'welcome.msg' displayed at login, and '.message' displayed\n#   # in each newly chdired directory.\n#   DisplayLogin\t\t\twelcome.msg\n#   DisplayChdir\t\t.message\n#\n#   # Limit WRITE everywhere in the anonymous chroot\n#   <Directory *>\n#     <Limit WRITE>\n#       DenyAll\n#     </Limit>\n#   </Directory>\n#\n#   # Uncomment this if you're brave.\n#   # <Directory incoming>\n#   #   # Umask 022 is a good standard umask to prevent new files and dirs\n#   #   # (second parm) from being group and world writable.\n#   #   Umask\t\t\t\t022  022\n#   #            <Limit READ WRITE>\n#   #            DenyAll\n#   #            </Limit>\n#   #            <Limit STOR>\n#   #            AllowAll\n#   #            </Limit>\n#   # </Directory>\n#\n# </Anonymous>\n\n# Include other custom configuration files\nInclude /etc/proftpd/conf.d/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/modules.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# This file is used to manage DSO modules and features.\n#\n\n# This is the directory where DSO modules reside\n\nModulePath /usr/lib/proftpd\n\n# Allow only user root to load and unload modules, but allow everyone\n# to see which modules have been loaded\n\nModuleControlsACLs insmod,rmmod allow user root\nModuleControlsACLs lsmod allow user *\n\nLoadModule mod_ctrls_admin.c\nLoadModule mod_tls.c\n\nLoadModule mod_ident.c\n\n# Install one of proftpd-mod-mysql, proftpd-mod-pgsql or any other\n# SQL backend engine to use this module and the required backend.\n# This module must be mandatory loaded before anyone of\n# the existent SQL backeds.\nLoadModule mod_sql.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_ldap.c\n\n#\n# 'SQLBackend mysql' or 'SQLBackend postgres' (or any other valid backend) directives\n# are required to have SQL authorization working. You can also comment out the\n# unused module here, in alternative.\n#\n\n# Install proftpd-mod-mysql and decomment the previous\n# mod_sql.c module to use this.\nLoadModule mod_sql_mysql.c\n\n# Install proftpd-mod-pgsql and decomment the previous\n# mod_sql.c module to use this.\n#LoadModule mod_sql_postgres.c\n\n# Install proftpd-mod-sqlite and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_sqlite.c\n\n# Install proftpd-mod-odbc and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_odbc.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sql_passwd.c\n\nLoadModule mod_radius.c\nLoadModule mod_quotatab.c\nLoadModule mod_quotatab_file.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_quotatab_ldap.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\nLoadModule mod_quotatab_sql.c\nLoadModule mod_quotatab_radius.c\nLoadModule mod_wrap.c\nLoadModule mod_rewrite.c\nLoadModule mod_load.c\nLoadModule mod_ban.c\nLoadModule mod_wrap2.c\nLoadModule mod_wrap2_file.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_wrap2_sql.c\nLoadModule mod_dynmasq.c\nLoadModule mod_exec.c\nLoadModule mod_shaper.c\nLoadModule mod_ratio.c\nLoadModule mod_site_misc.c\n\nLoadModule mod_sftp.c\nLoadModule mod_sftp_pam.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sftp_sql.c\n\nLoadModule mod_facl.c\nLoadModule mod_unique_id.c\nLoadModule mod_copy.c\nLoadModule mod_deflate.c\nLoadModule mod_ifversion.c\nLoadModule mod_tls_memcache.c\n\n# Install proftpd-mod-geoip to use the GeoIP feature\n#LoadModule mod_geoip.c\n\n# keep this module the last one\nLoadModule mod_ifsession.c\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/sql.conf\" chown=\"root:0\" chmod=\"0600\"\n\t\t\t\t\t\tbackup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Proftpd sample configuration for SQL-based authentication.\n#\n# (This is not to be used if you prefer a PAM-based SQL authentication)\n#\n\n<IfModule mod_sql.c>\n\nDefaultRoot ~\nRequireValidShell off\nAuthOrder mod_sql.c\n\n#\n# Choose a SQL backend among MySQL or PostgreSQL.\n# Both modules are loaded in default configuration, so you have to specify the backend\n# or comment out the unused module in /etc/proftpd/modules.conf.\n# Use 'mysql' or 'postgres' as possible values.\n#\nSQLBackend\tmysql\n#\nSQLEngine on\nSQLAuthenticate on\n#\n# Use both an encrypted or plaintext password\nSQLAuthTypes Crypt OpenSSL\n\nSQLAuthenticate users* groups*\n\n#\n# Connection\nSQLConnectInfo <SQL_DB>@<SQL_HOST> <SQL_UNPRIVILEGED_USER> <SQL_UNPRIVILEGED_PASSWORD>\n#\n# Describes both users/groups tables\n#\nSQLUserInfo ftp_users username password uid gid homedir shell\nSQLGroupInfo ftp_groups groupname gid members\n#\nSQLUserWhereClause \"login_enabled = 'y'\"\n\nSQLLog PASS login\nSQLNamedQuery login UPDATE \"last_login=now(), login_count=login_count+1 WHERE username='%u'\" ftp_users\n\nSQLLog RETR download\nSQLNamedQuery download UPDATE \"down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'\" ftp_users\n\nSQLLog STOR upload\nSQLNamedQuery upload UPDATE \"up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'\" ftp_users\n\nQuotaEngine on\nQuotaShowQuotas on\nQuotaDisplayUnits Mb\nQuotaLock /var/lock/ftpd.quotatab.lock\nQuotaLimitTable sql:/get-quota-limit\nQuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally\nSQLNamedQuery get-quota-limit SELECT \"ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'\"\nSQLNamedQuery get-quota-tally SELECT \"name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'\"\nSQLNamedQuery update-quota-tally UPDATE \"bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'\" ftp_quotatallies\nSQLNamedQuery insert-quota-tally INSERT \"%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}\" ftp_quotatallies\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/tls.conf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n<IfModule mod_tls.c>\nTLSEngine                               on\nTLSLog                                  /var/log/proftpd/tls.log\nTLSProtocol                             TLSv1.2 TLSv1.3\nTLSRSACertificateFile                   /etc/ssl/certs/proftpd.crt\nTLSRSACertificateKeyFile                /etc/ssl/private/proftpd.key\nTLSECCertificateFile                    /etc/ssl/certs/proftpd_ec.crt\nTLSECCertificateKeyFile                 /etc/ssl/private/proftpd_ec.key\n# TLSCACertificateFile\nTLSOptions                              NoSessionReuseRequired\nTLSVerifyClient                         off\n\n# Are clients required to use FTP over TLS when talking to this server?\nTLSRequired                             on\n\n# Allow SSL/TLS renegotiations when the client requests them, but\n# do not force the renegotiations.  Some clients do not support\n# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these\n# clients will close the data connection, or there will be a timeout\n# on an idle data connection.\n#\n#TLSRenegotiate                          required off\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/conf.d/99-froxlor-ratelimit.conf\" chown=\"root:0\"\n\t\t\t\t\t\t  chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n<Class whitelist>\nFrom 127.0.0.1\n</Class>\n\nMaxLoginAttempts 3\n<IfModule mod_ban.c>\n <IfClass whitelist>\n  BanEngine off\n </IfClass>\n <IfClass !whitelist>\n  BanEngine on\n </IfClass>\nBanLog /var/log/proftpd/ban.log\nBanTable /etc/proftpd/ban.tab\nBanMessage \"User %u was banned.\"\nBanOnEvent ClientConnectRate 10/00:00:02 02:00:00 \"Stop connecting frequently\"\nBanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00\nBanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99\nBanControlsACLs all allow user root\n</IfModule>\n\n<IfClass whitelist>\nBanEngine off\nDelayEngine off\n</IfClass>\n\t\t\t\t\t]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service proftpd restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Pureftpd -->\n\t\t\t\t<daemon name=\"pureftpd\" title=\"PureFTPd\">\n\t\t\t\t\t<install><![CDATA[apt-get install pure-ftpd-common pure-ftpd-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/private/pure-ftpd.pem ] || openssl req -x509 -nodes -days 7300 -newkey rsa:4096 -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nopenssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072\nchmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/pure-ftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/TLS\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MinUID\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1000\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MySQLConfigFile\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n/etc/pure-ftpd/db/mysql.conf\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/NoAnonymous\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/MaxIdleTime\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n15\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/ChrootEveryone\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nyes\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/PAMAuthentication\"\n\t\t\t\t\t\tchown=\"root:0\" chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\nno\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/db/mysql.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0640\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n##############################################\n#                                            #\n# Sample Pure-FTPd Mysql configuration file. #\n# See README.MySQL for explanations.         #\n#                                            #\n##############################################\n\n\n# Optional : MySQL server name or IP. Don't define this for unix sockets.\n\n# MYSQLServer     127.0.0.1\n\n\n# Optional : MySQL port. Don't define this if a local unix socket is used.\n\n# MYSQLPort       3306\n\n\n# Optional : define the location of mysql.sock if the server runs on this host.\n\nMYSQLSocket      /var/run/mysqld/mysqld.sock\n\n\n# Mandatory : user to bind the server as.\n\nMYSQLUser       <SQL_UNPRIVILEGED_USER>\n\n\n# Mandatory : user password. You must have a password.\n\nMYSQLPassword   <SQL_UNPRIVILEGED_PASSWORD>\n\n\n# Mandatory : database to open.\n\nMYSQLDatabase   <SQL_DB>\n\n\n# Mandatory : how passwords are stored\n# Valid values are : \"cleartext\", \"crypt\", \"sha1\", \"md5\" and \"password\"\n# (\"password\" = MySQL password() function)\n# You can also use \"any\" to try \"crypt\", \"sha1\", \"md5\" *and* \"password\"\n\nMYSQLCrypt      any\n\n\n# In the following directives, parts of the strings are replaced at\n# run-time before performing queries :\n#\n# \\L is replaced by the login of the user trying to authenticate.\n# \\I is replaced by the IP address the user connected to.\n# \\P is replaced by the port number the user connected to.\n# \\R is replaced by the IP address the user connected from.\n# \\D is replaced by the remote IP address, as a long decimal number.\n#\n# Very complex queries can be performed using these substitution strings,\n# especially for virtual hosting.\n\n\n# Query to execute in order to fetch the password\n\nMYSQLGetPW      SELECT password FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Query to execute in order to fetch the system user name or uid\n\nMYSQLGetUID     SELECT uid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default UID - if set this overrides MYSQLGetUID\n\n#MYSQLDefaultUID 1000\n\n\n# Query to execute in order to fetch the system user group or gid\n\nMYSQLGetGID     SELECT gid FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : default GID - if set this overrides MYSQLGetGID\n\n#MYSQLDefaultGID 1000\n\n\n# Query to execute in order to fetch the home directory\n\nMYSQLGetDir     SELECT homedir FROM ftp_users WHERE username=\"\\L\" AND login_enabled=\"y\"\n\n\n# Optional : query to get the maximal number of files\n# Pure-FTPd must have been compiled with virtual quotas support.\n\n# MySQLGetQTAFS  SELECT QuotaFiles FROM users WHERE User='\\L'\n\n\n# Optional : query to get the maximal disk usage (virtual quotas)\n# The number should be in Megabytes.\n# Pure-FTPd must have been compiled with virtual quotas support.\n\nMySQLGetQTASZ SELECT CASE WHEN panel_customers.diskspace = 0 THEN -1 WHEN panel_customers.diskspace <= -1 THEN 0 ELSE panel_customers.diskspace/1024 END AS QuotaSize FROM panel_customers, ftp_users WHERE username = \"\\L\" AND panel_customers.loginname = SUBSTRING_INDEX('\\L', 'ftp', 1)\n\n\n# Optional : ratios. The server has to be compiled with ratio support.\n\n# MySQLGetRatioUL SELECT ULRatio FROM users WHERE User='\\L'\n# MySQLGetRatioDL SELECT DLRatio FROM users WHERE User='\\L'\n\n\n# Optional : bandwidth throttling.\n# The server has to be compiled with throttling support.\n# Values are in KB/s .\n\n# MySQLGetBandwidthUL SELECT ULBandwidth FROM users WHERE User='\\L'\n# MySQLGetBandwidthDL SELECT DLBandwidth FROM users WHERE User='\\L'\n\n# Enable ~ expansion. NEVER ENABLE THIS BLINDLY UNLESS :\n# 1) You know what you are doing.\n# 2) Real and virtual users match.\n\n# MySQLForceTildeExpansion 1\n\n\n# If you're using a transactionnal storage engine, you can enable SQL\n# transactions to avoid races. Leave this commented if you are using the\n# traditional MyIsam engine.\n\n# MySQLTransactions On\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/CustomerProof\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n1\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/pure-ftpd/conf/Bind\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n21\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/default/pure-ftpd-common\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Configuration for pure-ftpd\n# (this file is sourced by /bin/sh, edit accordingly)\n\n# STANDALONE_OR_INETD\n# valid values are \"standalone\" and \"inetd\".\n# Any change here overrides the setting in debconf.\nSTANDALONE_OR_INETD=standalone\n\n# VIRTUALCHROOT:\n# whether to use binary with virtualchroot support\n# valid values are \"true\" or \"false\"\n# Any change here overrides the setting in debconf.\nVIRTUALCHROOT=false\n\n# UPLOADSCRIPT: if this is set and the daemon is run in standalone mode,\n# pure-uploadscript will also be run to spawn the program given below\n# for handling uploads. see /usr/share/doc/pure-ftpd/README.gz or\n# pure-uploadscript(8)\n\n# example: UPLOADSCRIPT=/usr/local/sbin/uploadhandler.pl\nUPLOADSCRIPT=\n\n# if set, pure-uploadscript will spawn  running as the\n# given uid and gid\nUPLOADUID=\nUPLOADGID=\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pure-ftpd-mysql restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- System tools/services -->\n\t\t\t<service type=\"system\" title=\"{{lng.admin.configfiles.etc}}\">\n\t\t\t\t<!-- Webalizer -->\n\t\t\t\t<daemon name=\"webalizer\"\n\t\t\t\t\ttitle=\"Webalizer (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install webalizer]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- goaccess -->\n\t\t\t\t<daemon name=\"goaccess\"\n\t\t\t\t\ttitle=\"goaccess (traffic analyzer)\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install goaccess jq]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- AWstats -->\n\t\t\t\t<daemon name=\"awstats\"\n\t\t\t\t\ttitle=\"Awstats  (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install awstats]]></install>\n\t\t\t\t\t<command><![CDATA[mv {{settings.system.awstats_conf}}/awstats.conf {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's/^DirData/# DirData/' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's|^\\\\(DirIcons=\\\\).*$|\\\\1\\\\\"/awstats-icon\\\\\"|' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/cron.d/awstats]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/logrotate.d/httpd-prerotate/awstats]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- libnss-extrausers -->\n\t\t\t\t<daemon name=\"libnssextrausers\"\n\t\t\t\t\ttitle=\"libnss-extrausers\">\n\t\t\t\t\t<install><![CDATA[apt-get install libnss-extrausers]]></install>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/extrausers]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/passwd]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/group]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/shadow]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/nsswitch.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Make sure that `passwd`, `group` and `shadow` have extrausers in their lines\n# You should place extrausers at the end, so that it is queried after the other mechanisams\n#\npasswd:         files systemd compat extrausers\ngroup:          files systemd compat extrausers\nshadow:         files compat extrausers\ngshadow:        files\n\nhosts:       files dns\nnetworks:    files dns\n\nprotocols:   db files\nservices:    db files\nethers:      db files\nrpc:         db files\n\nnetmasks:    files\nnetgroup:    files\nbootparams:  files\n\nautomount:   files\naliases:     files\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mv /var/spool/postfix/etc/nsswitch.conf /var/spool/postfix/etc/nsswitch.conf.frx.bak]]></command>\n\t\t\t\t\t\t<command><![CDATA[cp /etc/nsswitch.conf /var/spool/postfix/etc/nsswitch.conf]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Logrotate -->\n\t\t\t\t<daemon name=\"logrotate\" title=\"Logrotate\">\n\t\t\t\t\t<install><![CDATA[apt-get install logrotate]]></install>\n\t\t\t\t\t<file name=\"/etc/logrotate.d/froxlor\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Froxlor logrotate snippet\n#\n<CUSTOMER_LOGS>*.log {\n  missingok\n  daily\n  rotate 7\n  compress\n  delaycompress\n  notifempty\n  create\n  sharedscripts\n  postrotate\n  <WEBSERVER_RELOAD_CMD> > /dev/null 2>&1 || true\n  endscript\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- FCGID -->\n\t\t\t\t<daemon name=\"fcgid\" title=\"FCGID\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2-suexec-pristine libapache2-mod-fcgid php-cgi]]></install>\n\t\t\t\t\t<command><![CDATA[a2enmod suexec fcgid]]></command>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.mod_fcgid_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.system.mod_fcgid_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.system.mod_fcgid_httpgroup}} {{settings.system.mod_fcgid_httpuser}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_configdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 1777 {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php8.3]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- PHP-FPM -->\n\t\t\t\t<daemon name=\"php-fpm\"\n\t\t\t\t\ttitle=\"PHP-FPM\">\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install apache2-suexec-pristine]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<install><![CDATA[apt-get install php-fpm]]></install>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2enmod suexec proxy_fcgi actions]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"usernamenotexists\">{{settings.phpfpm.vhost_httpuser}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.phpfpm.vhost_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.phpfpm.vhost_httpgroup}} {{settings.phpfpm.vhost_httpuser}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"4\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php8.3]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"5\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2disconf php8.3-fpm]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Cronjob -->\n\t\t\t\t<daemon name=\"cron\" title=\"Cronjob for froxlor\"\n\t\t\t\t\tmandatory=\"true\">\n\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install cron gnupg]]></install>\n\t\t\t\t\t<command><![CDATA[[ ! -e /usr/local/bin/froxlor-cli ] && ln -s <BASE_PATH>bin/froxlor-cli /usr/local/bin/froxlor-cli]]></command>\n\t\t\t\t\t<command><![CDATA[/usr/bin/php <BASE_PATH>bin/froxlor-cli froxlor:cron --run-task 99]]></command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.crondreload}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t</services>\n\t</distribution>\n</froxlor>\n"
  },
  {
    "path": "lib/configfiles/trixie.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<froxlor>\n\t<distribution name=\"Debian\" codename=\"Trixie\"\n\t\tversion=\"13.x\" defaulteditor=\"/bin/nano\">\n\t\t<!-- OS defaults to be loaded on installation -->\n\t\t<defaults>\n\t\t\t<default settinggroup=\"system\" varname=\"nssextrausers\" value=\"1\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_vhost\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_diroptions\" value=\"/etc/nginx/sites-enabled/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apacheconf_htpasswddir\" value=\"/etc/nginx/froxlor-htpasswd/\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"apachereload_command\" value=\"service nginx reload\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"system\" varname=\"letsencryptacmeconf\" value=\"/etc/nginx/acme.conf\"></default>\n\t\t\t<default for=\"nginx\" settinggroup=\"phpfpm\" varname=\"fastcgi_ipcdir\" value=\"/var/run/php/\"></default>\n\t\t</defaults>\n\t\t<services>\n\t\t\t<!-- HTTP -->\n\t\t\t<service type=\"http\" title=\"{{lng.admin.configfiles.http}}\">\n\t\t\t\t<!-- general HTTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_vhost}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_vhost}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_vhost}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isdir\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"isfile\">{{settings.system.apacheconf_diroptions}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[touch {{settings.system.apacheconf_diroptions}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command><![CDATA[chown root:0 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0600 {{settings.system.apacheconf_diroptions}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.documentroot_prefix}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.logfiles_directory}}]]></command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"notempty\">{{settings.system.deactivateddocroot}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[mkdir -p {{settings.system.deactivateddocroot}}]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- HTTP Apache -->\n\t\t\t\t<daemon name=\"apache\" version=\"2.4\" title=\"Apache 2.4\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2]]></install>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command><![CDATA[a2dismod userdir]]></command>\n\t\t\t\t\t<command><![CDATA[a2enmod headers]]></command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.use_ssl}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[a2enmod ssl]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n# Please remember to activate the use of mod_proxy / mod_proxy_fcgi in the PHP-FPM settings!!!\na2enmod proxy_fcgi\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nAlias \"/.well-known/acme-challenge\" \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\"\n<Directory \"{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge\">\n\tRequire all granted\n</Directory>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- HTTP Nginx -->\n\t\t\t\t<daemon name=\"nginx\" title=\"nginx\">\n\t\t\t\t\t<install><![CDATA[apt-get install nginx]]></install>\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install php-cgi]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<file name=\"/etc/nginx/nginx.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nuser www-data;\nworker_processes auto;\npid /var/run/nginx.pid;\ninclude /etc/nginx/modules-enabled/*.conf;\n\nevents {\n\tworker_connections 768;\n\t# multi_accept on;\n}\n\nhttp {\n\n\t##\n\t# Basic Settings\n\t##\n\n\tsendfile on;\n\ttcp_nopush on;\n\ttcp_nodelay on;\n\tkeepalive_timeout 65;\n\ttypes_hash_max_size 2048;\n\t# server_tokens off;\n\n\t# server_names_hash_bucket_size 64;\n\t# server_name_in_redirect off;\n\n\tinclude /etc/nginx/mime.types;\n\tdefault_type application/octet-stream;\n\n\t##\n\t# Logging Settings\n\t##\n\n\taccess_log /var/log/nginx/access.log;\n\terror_log /var/log/nginx/error.log;\n\n\t##\n\t# Gzip Settings\n\t##\n\n\tgzip on;\n\tgzip_disable \"msie6\";\n\n\t# gzip_vary on;\n\t# gzip_proxied any;\n\t# gzip_comp_level 6;\n\t# gzip_buffers 16 8k;\n\t# gzip_http_version 1.1;\n\t# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;\n\n\t##\n\t# nginx-naxsi config\n\t##\n\t# Uncomment it if you installed nginx-naxsi\n\t##\n\n\t#include /etc/nginx/naxsi_core.rules;\n\n\t##\n\t# nginx-passenger config\n\t##\n\t# Uncomment it if you installed nginx-passenger\n\t##\n\n\t#passenger_root /usr;\n\t#passenger_ruby /usr/bin/ruby;\n\n\t##\n\t# Virtual Host Configs\n\t##\n\n\tinclude /etc/nginx/conf.d/*.conf;\n\tinclude /etc/nginx/sites-enabled/*;\n}\n\n\n#mail {\n#\t# See sample authentication script at:\n#\t# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript\n#\n#\t# auth_http localhost/auth.php;\n#\t# pop3_capabilities \"TOP\" \"USER\";\n#\t# imap_capabilities \"IMAP4rev1\" \"UIDPLUS\";\n#\n#\tserver {\n#\t\tlisten     localhost:110;\n#\t\tprotocol   pop3;\n#\t\tproxy      on;\n#\t}\n#\n#\tserver {\n#\t\tlisten     localhost:143;\n#\t\tprotocol   imap;\n#\t\tproxy      on;\n#\t}\n#}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/nginx/fastcgi_params\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nfastcgi_connect_timeout 65;\nfastcgi_send_timeout    180;\nfastcgi_read_timeout    180;\n\nfastcgi_param  QUERY_STRING       $query_string;\nfastcgi_param  REQUEST_METHOD     $request_method;\nfastcgi_param  CONTENT_TYPE       $content_type;\nfastcgi_param  CONTENT_LENGTH     $content_length;\n\nfastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\nfastcgi_param  REQUEST_URI        $request_uri;\nfastcgi_param  DOCUMENT_URI       $document_uri;\nfastcgi_param  DOCUMENT_ROOT      $document_root;\nfastcgi_param  SERVER_PROTOCOL    $server_protocol;\nfastcgi_param  HTTPS              $https if_not_empty;\n\nfastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\nfastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\nfastcgi_param  REMOTE_ADDR        $remote_addr;\nfastcgi_param  REMOTE_PORT        $remote_port;\nfastcgi_param  SERVER_ADDR        $server_addr;\nfastcgi_param  SERVER_PORT        $server_port;\nfastcgi_param  SERVER_NAME        $server_name;\n\n# PHP only, required if PHP was built with --enable-force-cgi-redirect\nfastcgi_param  REDIRECT_STATUS    200;\n\n# Fix for HTTP/3 ($http_host is not populated automatically)\nfastcgi_param  HTTP_HOST          $host;\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"{{settings.system.letsencryptacmeconf}}\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.leenabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\nlocation /.well-known/acme-challenge {\n\talias {{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge;\n\n\tlocation ~ /.well-known/acme-challenge/(.*) {\n\t\tdefault_type text/plain;\n\t}\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/init.d/php-fcgi\" backup=\"true\" chmod=\"u+x\">\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[\n#!/bin/bash\nBIND=\"127.0.0.1:8888\"\nUSER=\"www-data\"\nPHP_FCGI_CHILDREN=\"15\"\nPHP_FCGI_MAX_REQUESTS=\"1000\"\n\nPHP_CGI=\"/usr/bin/php-cgi\"\nPHP_CGI_NAME=\"$(basename ${PHP_CGI})\"\nPHP_CGI_ARGS=\"- USER=${USER} PATH=/usr/bin PHP_FCGI_CHILDREN=${PHP_FCGI_CHILDREN} PHP_FCGI_MAX_REQUESTS=${PHP_FCGI_MAX_REQUESTS} ${PHP_CGI} -b ${BIND}\"\nRETVAL=\"0\"\n\nstart() {\n      echo -n \"Starting PHP FastCGI: \"\n      start-stop-daemon --quiet --start --background --chuid \"$USER\" --exec /usr/bin/env -- $PHP_CGI_ARGS\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\nstop() {\n      echo -n \"Stopping PHP FastCGI: \"\n      killall -q -w -u ${USER} ${PHP_CGI}\n      RETVAL=\"$?\"\n      echo \"${PHP_CGI_NAME}.\"\n}\n\ncase \"$1\" in\n    start)\n      start\n  ;;\n    stop)\n      stop\n  ;;\n    restart)\n      stop\n      start\n  ;;\n    *)\n      echo \"Usage: php-fastcgi {start|stop|restart}\"\n      exit 1\n  ;;\nesac\nexit \"$RETVAL\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='http']/general/commands</include>\n\t\t\t\t\t<command>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.phpfpm.enabled}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"false\">{{settings.system.mod_fcgid}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[/etc/init.d/php-fcgi restart]]></content>\n\t\t\t\t\t</command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.apachereload_command}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!--DNS -->\n\t\t\t<service type=\"dns\" title=\"{{lng.admin.configfiles.dns}}\">\n\t\t\t\t<!--Bind9 -->\n\t\t\t\t<daemon name=\"bind\" title=\"Bind9 nameserver\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install bind9]]></install>\n\t\t\t\t\t<command><![CDATA[echo \"include \\\"{{settings.system.bindconf_directory}}froxlor_bind.conf\\\";\" >> /etc/bind/named.conf.local]]></command>\n\t\t\t\t\t<command><![CDATA[touch {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chown bind:0 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[chmod 0644 {{settings.system.bindconf_directory}}froxlor_bind.conf]]></command>\n\t\t\t\t\t<command><![CDATA[service bind9 restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns\" title=\"PowerDNS (standalone)\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server pdns-backend-mysql]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\nallow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# mysql-settings / you need to create the power-dns database for yourself!\nlaunch=gmysql\ngmysql-host=127.0.0.1\ngmysql-port=3306\ngmysql-dbname=pdns\ngmysql-user=powerdns\ngmysql-group=client\ngmysql-password=\n#gmysql-ssl-ca-file=\n#gmysql-ssl-verify-server-certificate=0\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<daemon name=\"powerdns_bind\"\n\t\t\t\t\ttitle=\"PowerDNS via bind-backend\">\n\t\t\t\t\t<install><![CDATA[apt-get install pdns-server]]></install>\n\t\t\t\t\t<file name=\"/etc/powerdns/pdns.conf\" backup=\"true\" chown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n#################################\n# allow-axfr-ips\tAllow zonetransfers only to these subnets\n#\n# allow-axfr-ips=127.0.0.0/8,::1,<NAMESERVERS_IP>\n\n#################################\n# allow-dnsupdate-from\tA global setting to allow DNS updates from these IP ranges.\n#\n# allow-dnsupdate-from=127.0.0.0/8,::1\n\n#################################\n# also-notify\tWhen notifying a domain, also notify these nameservers\n#\n# also-notify=\n\n#################################\n# any-to-tcp\tAnswer ANY queries with tc=1, shunting to TCP\n#\n# any-to-tcp=no\n\n#################################\n# cache-ttl\tSeconds to store packets in the PacketCache\n#\n# cache-ttl=20\n\n#################################\n# carbon-interval\tNumber of seconds between carbon (graphite) updates\n#\n# carbon-interval=30\n\n#################################\n# carbon-ourname\tIf set, overrides our reported hostname for carbon stats\n#\n# carbon-ourname=\n\n#################################\n# carbon-server\tIf set, send metrics in carbon (graphite) format to this server\n#\n# carbon-server=\n\n#################################\n# chroot\tIf set, chroot to this directory for more security\n#\n# chroot=\n\n#################################\n# config-dir\tLocation of configuration directory (pdns.conf)\n#\nconfig-dir=/etc/powerdns\n\n#################################\n# config-name\tName of this virtual configuration - will rename the binary image\n#\n# config-name=\n\n#################################\n# control-console\tDebugging switch - don't use\n#\n# control-console=no\n\n#################################\n# daemon\tOperate as a daemon\n#\ndaemon=yes\n\n#################################\n# default-ksk-algorithms\tDefault KSK algorithms\n#\n# default-ksk-algorithms=rsasha256\n\n#################################\n# default-ksk-size\tDefault KSK size (0 means default)\n#\n# default-ksk-size=0\n\n#################################\n# default-soa-mail\tmail address to insert in the SOA record if none set in the backend\n#\n# default-soa-mail=\n\n#################################\n# default-soa-name\tname to insert in the SOA record if none set in the backend\n#\n# default-soa-name=a.misconfigured.powerdns.server\n\n#################################\n# default-ttl\tSeconds a result is valid if not set otherwise\n#\n# default-ttl=3600\n\n#################################\n# default-zsk-algorithms\tDefault ZSK algorithms\n#\n# default-zsk-algorithms=rsasha256\n\n#################################\n# default-zsk-size\tDefault ZSK size (0 means default)\n#\n# default-zsk-size=0\n\n#################################\n# direct-dnskey\tFetch DNSKEY RRs from backend during DNSKEY synthesis\n#\n# direct-dnskey=no\n\n#################################\n# disable-axfr\tDisable zonetransfers but do allow TCP queries\n#\n# disable-axfr=no\n\n#################################\n# disable-axfr-rectify\tDisable the rectify step during an outgoing AXFR. Only required for regression testing.\n#\n# disable-axfr-rectify=no\n\n#################################\n# disable-tcp\tDo not listen to TCP queries\n#\n# disable-tcp=no\n\n#################################\n# distributor-threads\tDefault number of Distributor (backend) threads to start\n#\n# distributor-threads=3\n\n#################################\n# do-ipv6-additional-processing\tDo AAAA additional processing\n#\n# do-ipv6-additional-processing=yes\n\n#################################\n# edns-subnet-processing\tIf we should act on EDNS Subnet options\n#\n# edns-subnet-processing=no\n\n#################################\n# entropy-source\tIf set, read entropy from this file\n#\n# entropy-source=/dev/urandom\n\n#################################\n# experimental-api-key\tREST API Static authentication key (required for API use)\n#\n# experimental-api-key=\n\n#################################\n# experimental-api-readonly\tIf the JSON API should disallow data modification\n#\n# experimental-api-readonly=no\n\n#################################\n# experimental-dname-processing\tIf we should support DNAME records\n#\n# experimental-dname-processing=no\n\n#################################\n# experimental-dnsupdate\tEnable/Disable DNS update (RFC2136) support. Default is no.\n#\n# experimental-dnsupdate=no\n\n#################################\n# experimental-json-interface\tIf the webserver should serve JSON data\n#\n# experimental-json-interface=no\n\n#################################\n# experimental-logfile\tFilename of the log file for JSON parser\n#\n# experimental-logfile=/var/log/pdns.log\n\n#################################\n# forward-dnsupdate\tA global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master.\n#\n# forward-dnsupdate=yes\n\n#################################\n# guardian\tRun within a guardian process\n#\nguardian=yes\n\n#################################\n# include-dir\tInclude *.conf files from this directory\n#\n# include-dir=\n\n#################################\n# launch\tWhich backends to launch and order to query them in\n#\n# launch=\nlaunch=bind\n\n#################################\n# load-modules\tLoad this module - supply absolute or relative path\n#\n# load-modules=\n\n#################################\n# local-address\tLocal IP addresses to which we bind\n#\nlocal-address=<SERVERIP>,127.0.0.1\n\n#################################\n# local-address-nonexist-fail\tFail to start if one or more of the local-address's do not exist on this server\n#\n# local-address-nonexist-fail=yes\n\n#################################\n# local-ipv6\tLocal IP address to which we bind\n#\n# local-ipv6=\n\n#################################\n# local-ipv6-nonexist-fail\tFail to start if one or more of the local-ipv6 addresses do not exist on this server\n#\n# local-ipv6-nonexist-fail=yes\n\n#################################\n# local-port\tThe port on which we listen\n#\n# local-port=53\n\n#################################\n# log-dns-details\tIf PDNS should log DNS non-erroneous details\n#\n# log-dns-details=no\n\n#################################\n# log-dns-queries\tIf PDNS should log all incoming DNS queries\n#\n# log-dns-queries=no\n\n#################################\n# logging-facility\tLog under a specific facility\n#\n# logging-facility=\n\n#################################\n# loglevel\tAmount of logging. Higher is more. Do not set below 3\n#\n# loglevel=4\n\n#################################\n# lua-prequery-script\tLua script with prequery handler\n#\n# lua-prequery-script=\n\n#################################\n# master\tAct as a master\n#\nmaster=yes\n\n#################################\n# max-cache-entries\tMaximum number of cache entries\n#\n# max-cache-entries=1000000\n\n#################################\n# max-ent-entries\tMaximum number of empty non-terminals in a zone\n#\n# max-ent-entries=100000\n\n#################################\n# max-nsec3-iterations\tLimit the number of NSEC3 hash iterations\n#\n# max-nsec3-iterations=500\n\n#################################\n# max-queue-length\tMaximum queuelength before considering situation lost\n#\n# max-queue-length=5000\n\n#################################\n# max-signature-cache-entries\tMaximum number of signatures cache entries\n#\n# max-signature-cache-entries=\n\n#################################\n# max-tcp-connections\tMaximum number of TCP connections\n#\n# max-tcp-connections=10\n\n#################################\n# module-dir\tDefault directory for modules\n#\n# module-dir=/usr/lib/TRIPLET/pdns\n\n#################################\n# negquery-cache-ttl\tSeconds to store negative query results in the QueryCache\n#\n# negquery-cache-ttl=60\n\n#################################\n# no-shuffle\tSet this to prevent random shuffling of answers - for regression testing\n#\n# no-shuffle=off\n\n#################################\n# only-notify\tOnly send AXFR NOTIFY to these IP addresses or netmasks\n#\n# only-notify=0.0.0.0/0,::/0\n\n#################################\n# out-of-zone-additional-processing\tDo out of zone additional processing\n#\n# out-of-zone-additional-processing=yes\n\n#################################\n# overload-queue-length\tMaximum queuelength moving to packetcache only\n#\n# overload-queue-length=0\n\n#################################\n# pipebackend-abi-version\tVersion of the pipe backend ABI\n#\n# pipebackend-abi-version=1\n\n#################################\n# prevent-self-notification\tDon't send notifications to what we think is ourself\n#\n# prevent-self-notification=yes\n\n#################################\n# query-cache-ttl\tSeconds to store query results in the QueryCache\n#\n# query-cache-ttl=20\n\n#################################\n# query-local-address\tSource IP address for sending queries\n#\n# query-local-address=0.0.0.0\n\n#################################\n# query-local-address6\tSource IPv6 address for sending queries\n#\n# query-local-address6=::\n\n#################################\n# query-logging\tHint backends that queries should be logged\n#\n# query-logging=no\n\n#################################\n# queue-limit\tMaximum number of milliseconds to queue a query\n#\n# queue-limit=1500\n\n#################################\n# receiver-threads\tDefault number of receiver threads to start\n#\n# receiver-threads=1\n\n#################################\n# retrieval-threads\tNumber of AXFR-retrieval threads for slave operation\n#\n# retrieval-threads=2\n\n#################################\n# reuseport\tEnable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket\n#\n# reuseport=no\n\n#################################\n# security-poll-suffix\tDomain name from which to query security update notifications\n#\n# security-poll-suffix=secpoll.powerdns.com.\n\n#################################\n# send-root-referral\tSend out old-fashioned root-referral instead of ServFail in case of no authority\n#\n# send-root-referral=no\n\n#################################\n# server-id\tReturned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom\n#\n# server-id=\n\n#################################\n# setgid\tIf set, change group id to this gid for more security\n#\nsetgid=pdns\n\n#################################\n# setuid\tIf set, change user id to this uid for more security\n#\nsetuid=pdns\n\n#################################\n# signing-threads\tDefault number of signer threads to start\n#\n# signing-threads=3\n\n#################################\n# slave\tAct as a slave\n#\n# slave=no\n\n#################################\n# slave-cycle-interval\tReschedule failed SOA serial checks once every .. seconds\n#\n# slave-cycle-interval=60\n\n#################################\n# slave-renotify\tIf we should send out notifications for slaved updates\n#\n# slave-renotify=no\n\n#################################\n# soa-expire-default\tDefault SOA expire\n#\n# soa-expire-default=604800\n\n#################################\n# soa-minimum-ttl\tDefault SOA minimum ttl\n#\n# soa-minimum-ttl=3600\n\n#################################\n# soa-refresh-default\tDefault SOA refresh\n#\n# soa-refresh-default=10800\n\n#################################\n# soa-retry-default\tDefault SOA retry\n#\n# soa-retry-default=3600\n\n#################################\n# socket-dir\tWhere the controlsocket will live\n#\n# socket-dir=/var/run\n\n#################################\n# tcp-control-address\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-address=\n\n#################################\n# tcp-control-port\tIf set, PowerDNS can be controlled over TCP on this address\n#\n# tcp-control-port=53000\n\n#################################\n# tcp-control-range\tIf set, remote control of PowerDNS is possible over these networks only\n#\n# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10\n\n#################################\n# tcp-control-secret\tIf set, PowerDNS can be controlled over TCP after passing this secret\n#\n# tcp-control-secret=\n\n#################################\n# traceback-handler\tEnable the traceback handler (Linux only)\n#\n# traceback-handler=yes\n\n#################################\n# trusted-notification-proxy\tIP address of incoming notification proxy\n#\n# trusted-notification-proxy=\n\n#################################\n# udp-truncation-threshold\tMaximum UDP response size before we truncate\n#\n# udp-truncation-threshold=1680\n\n#################################\n# version-string\tPowerDNS version in packets - full, anonymous, powerdns or custom\n#\n\nversion-string=powerdns\n#################################\n# webserver\tStart a webserver for monitoring\n#\n# webserver=no\n\n#################################\n# webserver-address\tIP Address of webserver to listen on\n#\n# webserver-address=127.0.0.1\n\n#################################\n# webserver-allow-from\tWebserver access is only allowed from these subnets\n#\n# webserver-allow-from=0.0.0.0/0,::/0\n\n#################################\n# webserver-password\tPassword required for accessing the webserver\n#\n# webserver-password=\n\n#################################\n# webserver-port\tPort of webserver to listen on\n#\n# webserver-port=8081\n\n#################################\n# webserver-print-arguments\tIf the webserver should print arguments\n#\n# webserver-print-arguments=no\n\n# include froxlor-bind-specific config\ninclude-dir=/etc/powerdns/froxlor/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[mkdir -p /etc/powerdns/froxlor/]]></command>\n\t\t\t\t\t<file name=\"/etc/powerdns/froxlor/pdns_froxlor.conf\"\n\t\t\t\t\t\tchown=\"root:pdns\" chmod=\"640\">\n\t\t\t\t\t\t<content><![CDATA[\n# Bind backend configuration\n\n# Location of the Bind configuration file to parse.\nbind-config=<BIND_CONFIG_PATH>named.conf\n\n# How often to check for zone changes. See 'Operation' section.\nbind-check-interval=180\n\n# Uncomment to enable Huffman compression on zone data.\n# Currently saves around 20% of memory actually used, but slows down operation.\n# bind-enable-huffman\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service pdns restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- SMTP services -->\n\t\t\t<service type=\"smtp\" title=\"{{lng.admin.configfiles.smtp}}\">\n\t\t\t\t<!-- general SMTP commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"groupnotexists\">{{settings.system.vmail_gid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[groupadd -g {{settings.system.vmail_gid}} vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t\t<command>\n\t\t\t\t\t\t\t<visibility mode=\"usernotexists\">{{settings.system.vmail_uid}}\n\t\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t\t<content><![CDATA[useradd -u {{settings.system.vmail_uid}} -g vmail vmail]]></content>\n\t\t\t\t\t\t</command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install postfix postfix-mysql]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/etc/pam.d]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/spool/postfix/var/run/mysqld]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R {{settings.system.vmail_uid}}:{{settings.system.vmail_gid}} {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 0750  {{settings.system.vmail_homedir}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"0\">\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_alias_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT destination FROM mail_virtual AS v, panel_customers AS c WHERE c.customerid = v.customerid AND c.deactivated = 0 AND v.email = '%s' AND trim(v.destination) <> ''\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_domains.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' AND deactivated = 0\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_mailbox_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_sender_permissions.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nhosts = <SQL_HOST>\nquery = SELECT GROUP_CONCAT(DISTINCT mu.username SEPARATOR ' ') AS sasl_users FROM mail_users mu WHERE mu.username = '%s' OR mu.email IN ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = '%s') UNION (SELECT mail_sender_aliases.email FROM mail_sender_aliases WHERE mail_sender_aliases.allowed_sender = CONCAT('@', SUBSTRING_INDEX('%s','@',-1))));\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_uid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT uid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/postfix/mysql-virtual_gid_maps.cf\"\n\t\t\t\t\t\t\tchown=\"root:postfix\" chmod=\"0640\">\n\t\t\t\t\t\t\t<content><![CDATA[\nuser = <SQL_UNPRIVILEGED_USER>\npassword = <SQL_UNPRIVILEGED_PASSWORD>\ndbname = <SQL_DB>\nexpansion_limit = 1\nhosts = <SQL_HOST>\nquery = SELECT gid FROM mail_users WHERE email = '%s'\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t\t<file name=\"/etc/aliases\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\n# /etc/aliases\nmailer-daemon: postmaster\npostmaster: root\nnobody: root\nhostmaster: root\nusenet: root\nnews: root\nwebmaster: root\nwww: root\nftp: root\nabuse: root\nnoc: root\nsecurity: root\n\n# change this to a valid e-mail address you can access\nroot:               <ADMIN_MAIL>\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<command><![CDATA[newaliases]]></command>\n\t\t\t\t\t\t<command><![CDATA[service postfix restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- postfix with dovecot -->\n\t\t\t\t<daemon name=\"postfix_dovecot\" title=\"Postfix with dovecot\"\n\t\t\t\t\tdefault=\"true\">\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t\t<file name=\"/etc/postfix/main.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# # See /usr/share/postfix/main.cf.dist for a commented, more complete version\n\n# FQDN from Froxlor\nmydomain = <SERVERNAME>\nmyhostname = $mydomain\ninet_interfaces = all\n\n# See http://www.postfix.org/COMPATIBILITY_README.html\ncompatibility_level = 3.9\n\n# Which domain that locally-originated mail appears to come from.\n# Debian policy suggests to read this value from /etc/mailname.\n#XX needs a review in postinst&config\n#myorigin = /etc/mailname\n#myorigin = $mydomain\nmyorigin = $myhostname\n\n# Text that follows the 220 code in the SMTP server's greeting banner.\n# You MUST specify $myhostname at the start due to an RFC requirement.\nsmtpd_banner = $myhostname ESMTP $mail_name (Debian)\n\n# IP protocols to use: ipv4, ipv6, or all\n# (set this explicitly so `post-install upgrade-configuration' won't complain)\ninet_protocols = all\n\n# List of \"trusted\" SMTP clients (maptype:mapname allowed) that have more\n# privileges than \"strangers\".  If mynetworks is not specified (the default),\n# mynetworks_style is used to compute its value.\n#mynetworks_style = class\n#mynetworks_style = subnet\nmynetworks_style = host\n#\nmynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128\n\n# Uncomment the next line to generate \"delayed mail\" warnings\n#delay_warning_time = 4h\n\n# List of domains (maptype:mapname allowed) that this machine considers\n# itself the final destination for.\nmydestination = $myhostname, localhost.$mydomain, localhost\n\n# Optional external command to use instead of mailbox delivery.  If set,\n# you must set up an alias to forward root mail to a real user.\n#mailbox_command = /usr/bin/procmail\n#mailbox_command = /usr/bin/procmail -a \"$EXTENSION\"\nmailbox_command = /usr/lib/dovecot/deliver\n\n# List of alias maps to use to lookup local addresses.\n# Per Debian Policy it should be /etc/aliases.\nalias_maps = hash:/etc/aliases\n\n# List of alias maps to make indexes on, when running newaliases.\nalias_database = hash:/etc/aliases\n\n# Notify (or not) local biff service when new mail arrives.\n# Rarely used these days.\nbiff = no\n\n# Separator between user name and address extension (user+foo@domain)\n#recipient_delimiter = +\nrecipient_delimiter = +\n\n# A host to send \"other\" mail to\n#relayhost = $mydomain\n#relayhost = [gateway.example.com]\n#relayhost = [ip.add.re.ss]:port\n#relayhost = uucphost\nrelayhost =\n\n# Where to look for Cyrus SASL configuration files.  Upstream default is unset\n# (use compiled-in SASL library default), Debian Policy says it should be\n# /etc/postfix/sasl.\ncyrus_sasl_config_path = /etc/postfix/sasl\n\n# SMTP Client TLS session cache\nsmtp_tls_session_cache_database = btree:${data_directory}/smtp_scache\nsmtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination\n\nappend_dot_mydomain = no\nbiff = no\nsmtpd_helo_required = yes\nsmtpd_recipient_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unauth_destination,\n\treject_unauth_pipelining,\n\treject_non_fqdn_recipient\nsmtpd_sender_restrictions = permit_mynetworks,\n\treject_sender_login_mismatch,\n\tpermit_sasl_authenticated,\n\treject_unknown_helo_hostname,\n\treject_unknown_recipient_domain,\n\treject_unknown_sender_domain\nsmtpd_client_restrictions = permit_mynetworks,\n\tpermit_sasl_authenticated,\n\treject_unknown_client_hostname\n\n# Maximum size of Message in bytes (50MB)\nmessage_size_limit = 52428800\n\n## SASL Auth Settings\nsmtpd_sasl_auth_enable = yes\nsmtpd_sasl_local_domain = $myhostname\nbroken_sasl_auth_clients = yes\n## Dovecot Settings for deliver, SASL Auth and virtual transport\nsmtpd_sasl_type = dovecot\nvirtual_transport = dovecot\ndovecot_destination_recipient_limit = 1\nsmtpd_sasl_path = private/auth\n\n# Virtual delivery settings\nvirtual_mailbox_base = /\nvirtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf\nvirtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf\nvirtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_alias_maps.cf\nsmtpd_sender_login_maps = proxy:mysql:/etc/postfix/mysql-virtual_sender_permissions.cf\nvirtual_uid_maps = static:<VIRTUAL_UID_MAPS>\nvirtual_gid_maps = static:<VIRTUAL_GID_MAPS>\n\n# Local delivery settings\nlocal_transport = local\n\n# Default Mailbox size, is set to 0 which means unlimited!\nmailbox_size_limit = 0\nvirtual_mailbox_limit = 0\n\n### TLS settings\n###\n## TLS for outgoing mails from the server to another server\nsmtp_tls_security_level = may\nsmtp_tls_note_starttls_offer = yes\n## TLS for incoming connections (clients or other mail servers)\nsmtpd_tls_security_level = may\nsmtpd_tls_cert_file = <SSL_CERT_FILE>\nsmtpd_tls_key_file = <SSL_KEY_FILE>\n#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt\nsmtpd_tls_loglevel = 1\nsmtpd_tls_received_header = yes\nsmtpd_tls_session_cache_timeout = 3600s\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/files[@index=0]</include>\n\t\t\t\t\t<file name=\"/etc/postfix/master.cf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Postfix master process configuration file.  For details on the format\n# of the file, see the master(5) manual page (command: \"man 5 master\" or\n# on-line: http://www.postfix.org/master.5.html).\n#\n# Do not forget to execute \"postfix reload\" after editing this file.\n#\n# ==========================================================================\n# service type  private unpriv  chroot  wakeup  maxproc command + args\n#               (yes)   (yes)   (yes)   (never) (100)\n# ==========================================================================\n#smtp      inet  n       -       y       -       -       smtpd\nsmtp      inet  n       -       y       -       1       postscreen\nsmtpd     pass  -       -       y       -       -       smtpd\ndnsblog   unix  -       -       y       -       0       dnsblog\ntlsproxy  unix  -       -       y       -       0       tlsproxy\nsubmission inet n       -       y       -       -       smtpd\n  -o syslog_name=postfix/submission\n  -o smtpd_tls_security_level=encrypt\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\nsmtps     inet  n       -       y       -       -       smtpd\n  -o syslog_name=postfix/smtps\n  -o smtpd_tls_wrappermode=yes\n  -o smtpd_sasl_auth_enable=yes\n  -o smtpd_reject_unlisted_recipient=no\n#  -o smtpd_client_restrictions=$mua_client_restrictions\n#  -o smtpd_helo_restrictions=$mua_helo_restrictions\n#  -o smtpd_sender_restrictions=$mua_sender_restrictions\n#  -o smtpd_recipient_restrictions=\n  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject\n  -o milter_macro_daemon_name=ORIGINATING\npickup    unix  n       -       y       60      1       pickup\n         -o content_filter=\n         -o receive_override_options=no_header_body_checks\ncleanup   unix  n       -       y       -       0       cleanup\nqmgr      unix  n       -       n       300     1       qmgr\n#qmgr     unix  n       -       n       300     1       oqmgr\ntlsmgr    unix  -       -       y       1000?   1       tlsmgr\nrewrite   unix  -       -       y       -       -       trivial-rewrite\nbounce    unix  -       -       y       -       0       bounce\ndefer     unix  -       -       y       -       0       bounce\ntrace     unix  -       -       y       -       0       bounce\nverify    unix  -       -       y       -       1       verify\nflush     unix  n       -       y       1000?   0       flush\nproxymap  unix  -       -       n       -       -       proxymap\nproxywrite unix -       -       n       -       1       proxymap\nsmtp      unix  -       -       y       -       -       smtp\nrelay     unix  -       -       y       -       -       smtp\n#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5\nshowq     unix  n       -       y       -       -       showq\nerror     unix  -       -       y       -       -       error\nretry     unix  -       -       y       -       -       error\ndiscard   unix  -       -       y       -       -       discard\nlocal     unix  -       n       n       -       -       local\nvirtual   unix  -       n       n       -       -       virtual\nlmtp      unix  -       -       y       -       -       lmtp\nanvil     unix  -       -       y       -       1       anvil\nscache    unix  -       -       y       -       1       scache\n#\n# ====================================================================\n# Interfaces to non-Postfix software. Be sure to examine the manual\n# pages of the non-Postfix software to find out what options it wants.\n#\n# Many of the following services use the Postfix pipe(8) delivery\n# agent.  See the pipe(8) man page for information about ${recipient}\n# and other message envelope options.\n# ====================================================================\n#\n# maildrop. See the Postfix MAILDROP_README file for details.\n# Also specify in main.cf: maildrop_destination_recipient_limit=1\n#\nmaildrop  unix  -       n       n       -       -       pipe\n  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}\n#\n# ====================================================================\n#\n# Recent Cyrus versions can use the existing \"lmtp\" master.cf entry.\n#\n# Specify in cyrus.conf:\n#   lmtp    cmd=\"lmtpd -a\" listen=\"localhost:lmtp\" proto=tcp4\n#\n# Specify in main.cf one or more of the following:\n#  mailbox_transport = lmtp:inet:localhost\n#  virtual_transport = lmtp:inet:localhost\n#\n# ====================================================================\n#\n# Cyrus 2.1.5 (Amos Gouaux)\n# Also specify in main.cf: cyrus_destination_recipient_limit=1\n#\n#cyrus     unix  -       n       n       -       -       pipe\n#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}\n#\n# ====================================================================\n# Old example of delivery via Cyrus.\n#\n#old-cyrus unix  -       n       n       -       -       pipe\n#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}\n#\n# ====================================================================\n#\n# See the Postfix UUCP_README file for configuration details.\n#\nuucp      unix  -       n       n       -       -       pipe\n  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)\n#\n# Other external delivery methods.\n#\nifmail    unix  -       n       n       -       -       pipe\n  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)\nbsmtp     unix  -       n       n       -       -       pipe\n  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient\nscalemail-backend unix  -       n       n       -       2       pipe\n  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}\nmailman   unix  -       n       n       -       -       pipe\n  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py\n  ${nexthop} ${user}\n# Dovecot LDA\ndovecot\t  unix\t-\tn\tn\t-\t-\tpipe\n\tflags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ${recipient}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<include>//service[@type='smtp']/general/commands[@index=3]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- IMAP/POP3 services -->\n\t\t\t<service type=\"mail\" title=\"{{lng.admin.configfiles.mail}}\">\n\t\t\t\t<!-- valid for both dovecots -->\n\t\t\t\t<general>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[apt-get install dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-managesieved dovecot-sieve]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t\t<file name=\"/etc/dovecot/conf.d/99-froxlor.conf\" chown=\"root:vmail\"\n\t\t\t\t\t\t\tchmod=\"0640\" backup=\"true\">\n\t\t\t\t\t\t\t<content><![CDATA[\nauth_allow_cleartext = yes\nauth_mechanisms = plain login\n\nsql_driver = mysql\nmysql <SQL_HOST> {\n  user = <SQL_UNPRIVILEGED_USER>\n  password = <SQL_UNPRIVILEGED_PASSWORD>\n  dbname = <SQL_DB>\n}\n\npassdb sql {\n  query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid, \"maildir\" as userdb_mail_driver, CONCAT(homedir, maildir) AS userdb_mail_path, CONCAT(quota, 'M') as userdb_quota_storage_size FROM mail_users WHERE (username = '%{user}' OR email = '%{user}') AND ((imap = 1 AND '%{protocol | lower}' = 'imap') OR (pop3 = 1 AND '%{protocol | lower}' = 'pop3') OR ((postfix = 'Y' AND '%{protocol | lower}' = 'smtp') OR (postfix = 'Y' AND '%{protocol | lower}' = 'sieve')))\n}\n\nuserdb sql {\n  query = SELECT CONCAT(homedir, maildir) AS home, \"maildir\" as mail_driver, CONCAT(homedir, maildir) AS mail_path, uid, gid, CONCAT(quota, 'M') as quota_storage_size FROM mail_users WHERE (username = '%{user}' OR email = '%{user}')\n}\n\nnamespace inbox {\n  inbox = yes\n}\nmail_privileged_group = mail\nmail_inbox_path = ~/\n\nservice auth {\n  # Postfix smtp-auth\n  unix_listener /var/spool/postfix/private/auth {\n    mode = 0660\n    user = postfix\n    group = postfix\n  }\n  # Exim4 smtp-auth\n  unix_listener auth-client {\n    mode = 0660\n    user = mail\n    #group = Debian-exim\n  }\n}\n\nservice stats {\n  unix_listener stats-reader {\n    group = vmail\n    mode = 0666\n  }\n  unix_listener stats-writer {\n    group = vmail\n    mode = 0666\n  }\n}\n\nssl = yes\nssl_server_cert_file = <SSL_CERT_FILE>\nssl_server_key_file = <SSL_KEY_FILE>\nssl_server_dh_file = /usr/share/dovecot/dh.pem\n\npostmaster_address = postmaster@<SERVERNAME>\n\nprotocol imap {\n  mail_plugins {\n    imap_quota = yes\n  }\n}\n\npop3_logout_format = \"in=%{input} out=%{output} top=%{top_count}/%{top_bytes}, retr=%{retr_count}/%{retr_bytes}, del=%{deleted_count}/%{deleted_bytes}, size=%{message_bytes}\"\n\nprotocol lda {\n\tmail_plugins {\n\t\tsieve = yes\n\t}\n}\n\nsieve_script personal {\n  driver = file\n  path = ~/sieve\n  active_path = ~/.dovecot.sieve\n}\n\nmail_plugins {\n  quota = yes\n}\n]]>\n\t\t\t\t\t\t\t</content>\n\t\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[sed -i.bak 's/^!include auth-system.conf.ext/#!include auth-system.conf.ext/' /etc/dovecot/conf.d/10-auth.conf]]></command>\n\t\t\t\t\t\t<command><![CDATA[service dovecot restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- Dovecot with postfix -->\n\t\t\t\t<daemon name=\"dovecot_postfix\" version=\"2\"\n\t\t\t\t\ttitle=\"Dovecot with postfix\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='mail']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='mail']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- Antispam services -->\n\t\t\t<service type=\"antispam\" title=\"Antispam\">\n\t\t\t\t<!-- general Rspamd commands -->\n\t\t\t\t<general>\n\t\t\t\t\t<installs index=\"1\">\n\t\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install rspamd]]></install>\n\t\t\t\t\t</installs>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/local.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /etc/rspamd/override.d/]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/rspamd/dkim/]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<files index=\"1\">\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/actions.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Set rewrite subject to this value (%s is replaced by the original subject)\nsubject = \"***SPAM*** %s\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/arc.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\ntry_fallback = true;\n### Enable DKIM signing for alias sender addresses\nallow_username_mismatch = true;\npath = \"/var/lib/rspamd/dkim/$domain.$selector.key\";\nselector_map = \"/etc/rspamd/dkim_selectors.map\";\nuse_esld = false;\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/milter_headers.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\nuse = [\"x-spamd-bar\", \"x-spam-level\", \"authentication-results\"];\nauthenticated_headers = [\"authentication-results\"];\nextended_spam_headers = true\nskip_local = false\nskip_authenticated = false\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/replies.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n## If a user has replied to an email, don’t mark other emails in the same thread as spam\naction = \"no action\";\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/rspamd/local.d/settings.conf\"\n\t\t\t\t\t\t  chown=\"root:root\" chmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n## Feel free to include your own settings or adjustments here, for example:\n#whitelist {\n#  priority = low;\n#  rcpt = \"postmaster@example.com\";\n#  want_spam = yes;\n#}\n\n## Include froxlor generated settings\n.include(try=true,priority=1,duplicate=merge) \"{{settings.antispam.config_file}}\"\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t</files>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<command><![CDATA[cp /etc/rspamd/local.d/arc.conf /etc/rspamd/local.d/dkim_signing.conf]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_protocol = 6\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"milter_default_action = accept\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[postconf -e \"non_smtpd_milters = inet:127.0.0.1:11332\"]]></command>\n\t\t\t\t\t\t<command><![CDATA[chown -R _rspamd:_rspamd /var/lib/rspamd/dkim]]></command>\n\t\t\t\t\t\t<command><![CDATA[service rspamd restart]]></command>\n\t\t\t\t\t\t<command><![CDATA[service postfix restart]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t</general>\n\t\t\t\t<!-- rspamd -->\n\t\t\t\t<daemon name=\"rspamd\" title=\"Rspamd\" default=\"true\">\n\t\t\t\t\t<include>//service[@type='antispam']/general/installs[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/files[@index=1]\n\t\t\t\t\t</include>\n\t\t\t\t\t<include>//service[@type='antispam']/general/commands[@index=2]\n\t\t\t\t\t</include>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- FTP services -->\n\t\t\t<service type=\"ftp\" title=\"{{lng.admin.configfiles.ftp}}\">\n\t\t\t\t<!-- Proftpd -->\n\t\t\t\t<daemon name=\"proftpd\" title=\"ProFTPd\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install proftpd-basic proftpd-mod-mysql proftpd-mod-crypto proftpd-mod-wrap]]></install>\n\t\t\t\t\t<file name=\"/etc/proftpd/create-cert.sh\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0700\">\n\t\t\t\t\t\t<content><![CDATA[#!/bin/bash\n[ -f /etc/ssl/certs/proftpd.crt ] || openssl req -new -x509 -newkey rsa:4096 -days 3650 -nodes -out /etc/ssl/certs/proftpd.crt -keyout /etc/ssl/private/proftpd.key -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\n[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj \"/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=<SERVERNAME>\"\nchmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[/etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t\t<command><![CDATA[rm -f /etc/proftpd/create-cert.sh]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/proftpd/proftpd.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0600\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# /etc/proftpd/proftpd.conf -- This is a basic ProFTPD configuration file.\n# To really apply changes, reload proftpd after modifications, if\n# it runs in daemon mode. It is not required in inetd/xinetd mode.\n#\n\n# Includes DSO modules\nInclude /etc/proftpd/modules.conf\n\n# Set off to disable IPv6 support which is annoying on IPv4 only boxes.\nUseIPv6 on\n# If set on you can experience a longer connection delay in many cases.\n<IfModule mod_ident.c>\n  IdentLookups off\n</IfModule>\n\nServerName \"<SERVERNAME> FTP Server\"\n# Set to inetd only if you would run proftpd by inetd/xinetd/socket.\n# Read README.Debian for more information on proper configuration.\nServerType\t\t\tstandalone\nDeferWelcome\t\t\toff\n\n# Disable MultilineRFC2228 per https://github.com/proftpd/proftpd/issues/1085\n# MultilineRFC2228on\nDefaultServer on\nShowSymlinks on\n\nTimeoutNoTransfer 600\nTimeoutStalled 600\nTimeoutIdle 1200\n\nDisplayLogin welcome.msg\nDisplayChdir .message true\nListOptions \"-l\"\n\nDenyFilter \\*.*/\n\n# Use this to jail all users in their homes\n# DefaultRoot ~\n\n# Users require a valid shell listed in /etc/shells to login.\n# Use this directive to release that constrain.\n# RequireValidShell off\n\n# Port 21 is the standard FTP port.\nPort 21\n\n# In some cases you have to specify passive ports range to by-pass\n# firewall limitations. Ephemeral ports can be used for that, but\n# feel free to use a more narrow range.\n# PassivePorts 49152 65534\n\n# If your host was NATted, this option is useful in order to\n# allow passive tranfers to work. You have to use your public\n# address and opening the passive ports used on your firewall as well.\n# MasqueradeAddress 1.2.3.4\n\n# This is useful for masquerading address with dynamic IPs:\n# refresh any configured MasqueradeAddress directives every 8 hours\n<IfModule mod_dynmasq.c>\n# DynMasqRefresh 28800\n</IfModule>\n\n# To prevent DoS attacks, set the maximum number of child processes\n# to 30.  If you need to allow more than 30 concurrent connections\n# at once, simply increase this value.  Note that this ONLY works\n# in standalone mode, in inetd mode you should use an inetd server\n# that allows you to limit maximum number of processes per service\n# (such as xinetd)\nMaxInstances 30\n\n# Set the user and group that the server normally runs at.\nUser proftpd\nGroup nogroup\n\n# Umask 022 is a good standard umask to prevent new files and dirs\n# (second parm) from being group and world writable.\nUmask 022 022\n# Normally, we want files to be overwriteable.\nAllowOverwrite on\n\n# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords:\n# PersistentPasswd off\n\n# This is required to use both PAM-based authentication and local passwords\n# AuthOrder mod_auth_pam.c* mod_auth_unix.c\n\n# Be warned: use of this directive impacts CPU average load!\n# Uncomment this if you like to see progress and transfer rate with ftpwho\n# in downloads. That is not needed for uploads rates.\n#\n# UseSendFile off\n\nTransferLog /var/log/proftpd/xferlog\nSystemLog /var/log/proftpd/proftpd.log\n\n# In order to keep log file dates consistent after chroot, use timezone info\n# from /etc/localtime.  If this is not set, and proftpd is configured to\n# chroot (e.g. DefaultRoot or <Anonymous>), it will use the non-daylight\n# savings timezone regardless of whether DST is in effect.\n#SetEnv TZ :/etc/localtime\n\n<IfModule mod_quotatab.c>\nQuotaEngine on\n</IfModule>\n\n<IfModule mod_ratio.c>\nRatios off\n</IfModule>\n\n\n# Delay engine reduces impact of the so-called Timing Attack described in\n# http://www.securityfocus.com/bid/11430/discuss\n# It is on by default.\n<IfModule mod_delay.c>\nDelayEngine on\n</IfModule>\n\n<IfModule mod_ctrls.c>\nControlsEngine off\nControlsMaxClients 2\nControlsLog /var/log/proftpd/controls.log\nControlsInterval 5\nControlsSocket /var/run/proftpd/proftpd.sock\n</IfModule>\n\n<IfModule mod_ctrls_admin.c>\nAdminControlsEngine off\n</IfModule>\n\n#\n# Alternative authentication frameworks\n#\n#Include /etc/proftpd/ldap.conf\nInclude /etc/proftpd/sql.conf\n\n#\n# This is used for FTPS connections\n#\nInclude /etc/proftpd/tls.conf\n\n#\n# Useful to keep VirtualHost/VirtualRoot directives separated\n#\n#Include /etc/proftpd/virtuals.conf\n\n#\n# This is used for SFTP connections\n#\n#Include /etc/proftpd/sftp.conf\n\n#\n# This is used for other add-on modules\n#\n#Include /etc/proftpd/dnsbl.conf\n#Include /etc/proftpd/geoip.conf\n#Include /etc/proftpd/snmp.conf\n\n#\n# Useful to keep VirtualHost/VirtualRoot directives separated\n#\n#Include /etc/proftpd/virtuals.conf\n\n# Include other custom configuration files\n# !! Please note, that this statement will read /all/ file from this subdir,\n# i.e. backup files created by your editor, too !!!\n# Eventually create file patterns like this: /etc/proftpd/conf.d/*.conf\n#\nInclude /etc/proftpd/conf.d/\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/modules.conf\" chown=\"root:0\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# This file is used to manage DSO modules and features.\n#\n\n# This is the directory where DSO modules reside\n\nModulePath /usr/lib/proftpd\n\n# Allow only user root to load and unload modules, but allow everyone\n# to see which modules have been loaded\n\nModuleControlsACLs insmod,rmmod allow user root\nModuleControlsACLs lsmod allow user *\n\n#This is required only if you need to set IdentLookups on\n#LoadModule mod_ident.c\n\nLoadModule mod_ctrls_admin.c\n\n# Debian #1103154: needed to make default config executable\nLoadModule mod_delay.c\nLoadModule mod_ls.c\nLoadModule mod_xfer.c\n\n# Install proftpd-mod-crypto to use this module for TLS/SSL support.\nLoadModule mod_tls.c\n# Even these modules depend on the previous one\nLoadModule mod_tls_fscache.c\nLoadModule mod_tls_shmcache.c\n\n# Install one of proftpd-mod-mysql, proftpd-mod-pgsql or any other\n# SQL backend engine to use this module and the required backend.\n# This module must be mandatory loaded before anyone of\n# the existent SQL backeds.\nLoadModule mod_sql.c\n\n# Install proftpd-mod-ldap to use this for LDAP support.\n#LoadModule mod_ldap.c\n\n#\n# 'SQLBackend mysql' or 'SQLBackend postgres' (or any other valid backend) directives\n# are required to have SQL authorization working. You can also comment out the\n# unused module here, in alternative.\n#\n\n# Install proftpd-mod-mysql and decomment the previous\n# mod_sql.c module to use this.\nLoadModule mod_sql_mysql.c\n\n# Install proftpd-mod-pgsql and decomment the previous\n# mod_sql.c module to use this.\n#LoadModule mod_sql_postgres.c\n\n# Install proftpd-mod-sqlite and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_sqlite.c\n\n# Install proftpd-mod-odbc and decomment the previous\n# mod_sql.c module to use this\n#LoadModule mod_sql_odbc.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sql_passwd.c\n\nLoadModule mod_radius.c\nLoadModule mod_quotatab.c\nLoadModule mod_quotatab_file.c\n\n# Install proftpd-mod-ldap to use this\n#LoadModule mod_quotatab_ldap.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\nLoadModule mod_quotatab_sql.c\nLoadModule mod_quotatab_radius.c\n# Install proftpd-mod-wrap module to use this\nLoadModule mod_wrap.c\nLoadModule mod_rewrite.c\nLoadModule mod_load.c\nLoadModule mod_ban.c\nLoadModule mod_wrap2.c\nLoadModule mod_wrap2_file.c\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_wrap2_sql.c\nLoadModule mod_dynmasq.c\nLoadModule mod_exec.c\nLoadModule mod_shaper.c\nLoadModule mod_ratio.c\nLoadModule mod_site_misc.c\n\n# Install proftpd-mod-crypto to use this module for SFTP support.\nLoadModule mod_sftp.c\nLoadModule mod_sftp_pam.c\n\n# Install one of the previous SQL backends and decomment\n# the previous mod_sql.c module to use this\n#LoadModule mod_sftp_sql.c\n\nLoadModule mod_facl.c\nLoadModule mod_unique_id.c\nLoadModule mod_copy.c\nLoadModule mod_deflate.c\nLoadModule mod_ifversion.c\nLoadModule mod_memcache.c\n# Install proftpd-mod-crypto to use this module for TLS/SSL support.\nLoadModule mod_tls_memcache.c\n\n#LoadModule mod_redis.c\n# Install proftpd-mod-crypto to use this module for TLS/SSL support.\n#LoadModule mod_tls_redis.c\n#LoadModule mod_wrap2_redis.c\n\n#LoadModule mod_auth_otp.c\n\nLoadModule mod_readme.c\n\n# Install proftpd-mod-geoip to use the GeoIP feature\n#LoadModule mod_geoip.c\n\n# Install proftpd-mod-snmp to use the SNMP feature\n#LoadModule mod_snmp.c\n\n# keep this module the last one\nLoadModule mod_ifsession.c\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/sql.conf\" chown=\"root:0\" chmod=\"0600\"\n\t\t\t\t\t\tbackup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Proftpd sample configuration for SQL-based authentication.\n#\n# (This is not to be used if you prefer a PAM-based SQL authentication)\n#\n\n<IfModule mod_sql.c>\n\nDefaultRoot ~\nRequireValidShell off\nAuthOrder mod_sql.c\n\n#\n# Choose a SQL backend among MySQL or PostgreSQL.\n# Both modules are loaded in default configuration, so you have to specify the backend\n# or comment out the unused module in /etc/proftpd/modules.conf.\n# Use 'mysql' or 'postgres' as possible values.\n#\nSQLBackend\tmysql\n#\nSQLEngine on\n\n#\n# Use both an encrypted or plaintext password\nSQLAuthTypes Crypt OpenSSL\n\n# Do user and (fast) group lookups\nSQLAuthenticate users groups groupsetfast\n\n#\n# Connection\nSQLConnectInfo <SQL_DB>@<SQL_HOST> <SQL_UNPRIVILEGED_USER> <SQL_UNPRIVILEGED_PASSWORD>\n#\n# Describes both users/groups tables\n#\nSQLUserInfo ftp_users username password uid gid homedir shell\nSQLGroupInfo ftp_groups groupname gid members\n#\nSQLUserWhereClause \"login_enabled = 'y'\"\n\nSQLLog PASS login\nSQLNamedQuery login UPDATE \"last_login=now(), login_count=login_count+1 WHERE username='%u'\" ftp_users\n\nSQLLog RETR download\nSQLNamedQuery download UPDATE \"down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'\" ftp_users\n\nSQLLog STOR upload\nSQLNamedQuery upload UPDATE \"up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'\" ftp_users\n\nQuotaEngine on\nQuotaShowQuotas on\nQuotaDisplayUnits Mb\nQuotaLock /var/lock/ftpd.quotatab.lock\nQuotaLimitTable sql:/get-quota-limit\nQuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally\nSQLNamedQuery get-quota-limit SELECT \"ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'\"\nSQLNamedQuery get-quota-tally SELECT \"name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'\"\nSQLNamedQuery update-quota-tally UPDATE \"bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'\" ftp_quotatallies\nSQLNamedQuery insert-quota-tally INSERT \"%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}\" ftp_quotatallies\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/tls.conf\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n<IfModule mod_tls.c>\nTLSEngine                               on\nTLSLog                                  /var/log/proftpd/tls.log\nTLSProtocol                             TLSv1.2 TLSv1.3\nTLSRSACertificateFile                   /etc/ssl/certs/proftpd.crt\nTLSRSACertificateKeyFile                /etc/ssl/private/proftpd.key\nTLSECCertificateFile                    /etc/ssl/certs/proftpd_ec.crt\nTLSECCertificateKeyFile                 /etc/ssl/private/proftpd_ec.key\n# TLSCACertificateFile\nTLSOptions                              NoSessionReuseRequired\nTLSVerifyClient                         off\n\n# Are clients required to use FTP over TLS when talking to this server?\nTLSRequired                             on\n\n# Allow SSL/TLS renegotiations when the client requests them, but\n# do not force the renegotiations.  Some clients do not support\n# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these\n# clients will close the data connection, or there will be a timeout\n# on an idle data connection.\n#\n#TLSRenegotiate                          required off\n</IfModule>\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<file name=\"/etc/proftpd/conf.d/99-froxlor-ratelimit.conf\" chown=\"root:0\"\n\t\t\t\t\t\t  chmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n<Class whitelist>\nFrom 127.0.0.1\n</Class>\n\nMaxLoginAttempts 3\n<IfModule mod_ban.c>\n <IfClass whitelist>\n  BanEngine off\n </IfClass>\n <IfClass !whitelist>\n  BanEngine on\n </IfClass>\nBanLog /var/log/proftpd/ban.log\nBanTable /etc/proftpd/ban.tab\nBanMessage \"User %u was banned.\"\nBanOnEvent ClientConnectRate 10/00:00:02 02:00:00 \"Stop connecting frequently\"\nBanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00\nBanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99\nBanControlsACLs all allow user root\n</IfModule>\n\n<IfClass whitelist>\nBanEngine off\nDelayEngine off\n</IfClass>\n\t\t\t\t\t]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t\t<command><![CDATA[service proftpd restart]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t\t<!-- System tools/services -->\n\t\t\t<service type=\"system\" title=\"{{lng.admin.configfiles.etc}}\">\n\t\t\t\t<!-- Webalizer -->\n\t\t\t\t<daemon name=\"webalizer\"\n\t\t\t\t\ttitle=\"Webalizer (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install webalizer]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- goaccess -->\n\t\t\t\t<daemon name=\"goaccess\"\n\t\t\t\t\ttitle=\"goaccess (traffic analyzer)\" default=\"true\">\n\t\t\t\t\t<install><![CDATA[apt-get install goaccess jq]]></install>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- AWstats -->\n\t\t\t\t<daemon name=\"awstats\"\n\t\t\t\t\ttitle=\"Awstats  (traffic analyzer)\">\n\t\t\t\t\t<install><![CDATA[apt-get install awstats]]></install>\n\t\t\t\t\t<command><![CDATA[mv {{settings.system.awstats_conf}}/awstats.conf {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's/^DirData/# DirData/' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[sed -i.bak 's|^\\\\(DirIcons=\\\\).*$|\\\\1\\\\\"/awstats-icon\\\\\"|' {{settings.system.awstats_conf}}/awstats.model.conf]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/cron.d/awstats]]></command>\n\t\t\t\t\t<command><![CDATA[rm /etc/logrotate.d/httpd-prerotate/awstats]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- libnss-extrausers -->\n\t\t\t\t<daemon name=\"libnssextrausers\"\n\t\t\t\t\ttitle=\"libnss-extrausers\">\n\t\t\t\t\t<install><![CDATA[apt-get install libnss-extrausers]]></install>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<command><![CDATA[mkdir -p /var/lib/extrausers]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/passwd]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/group]]></command>\n\t\t\t\t\t\t<command><![CDATA[touch /var/lib/extrausers/shadow]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<file name=\"/etc/nsswitch.conf\" backup=\"true\">\n\t\t\t\t\t\t<content><![CDATA[\n# Make sure that `passwd`, `group` and `shadow` have extrausers in their lines\n# You should place extrausers at the end, so that it is queried after the other mechanisams\n#\npasswd:         compat extrausers\ngroup:          compat extrausers\nshadow:         compat extrausers\n\nhosts:       files dns\nnetworks:    files dns\n\nservices:    db files\nprotocols:   db files\nrpc:         db files\nethers:      db files\nnetmasks:    files\nnetgroup:    files\nbootparams:  files\n\nautomount:   files\naliases:     files\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Logrotate -->\n\t\t\t\t<daemon name=\"logrotate\" title=\"Logrotate\">\n\t\t\t\t\t<install><![CDATA[apt-get install logrotate]]></install>\n\t\t\t\t\t<file name=\"/etc/logrotate.d/froxlor\" chown=\"root:root\"\n\t\t\t\t\t\tchmod=\"0644\">\n\t\t\t\t\t\t<content><![CDATA[\n#\n# Froxlor logrotate snippet\n#\n<CUSTOMER_LOGS>*.log {\n  missingok\n  daily\n  rotate 7\n  compress\n  delaycompress\n  notifempty\n  create\n  sharedscripts\n  postrotate\n  <WEBSERVER_RELOAD_CMD> > /dev/null 2>&1 || true\n  endscript\n}\n]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t</file>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- FCGID -->\n\t\t\t\t<daemon name=\"fcgid\" title=\"FCGID\">\n\t\t\t\t\t<install><![CDATA[apt-get install apache2-suexec-pristine libapache2-mod-fcgid php-cgi]]></install>\n\t\t\t\t\t<command><![CDATA[a2enmod suexec fcgid]]></command>\n\t\t\t\t\t<commands index=\"1\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.system.mod_fcgid_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.system.mod_fcgid_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.system.mod_fcgid_httpgroup}} {{settings.system.mod_fcgid_httpuser}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_configdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[chmod 1777 {{settings.system.mod_fcgid_tmpdir}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php8.4]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- PHP-FPM -->\n\t\t\t\t<daemon name=\"php-fpm\"\n\t\t\t\t\ttitle=\"PHP-FPM\">\n\t\t\t\t\t<install>\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<content><![CDATA[apt-get install apache2-suexec-pristine]]></content>\n\t\t\t\t\t</install>\n\t\t\t\t\t<install><![CDATA[apt-get install php-fpm]]></install>\n\t\t\t\t\t<commands index=\"2\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2enmod suexec proxy_fcgi actions]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"3\">\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"usernamenotexists\">{{settings.phpfpm.vhost_httpuser}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[groupadd -f {{settings.phpfpm.vhost_httpgroup}}]]></command>\n\t\t\t\t\t\t<command><![CDATA[useradd -s /bin/false -g {{settings.phpfpm.vhost_httpgroup}} {{settings.phpfpm.vhost_httpuser}}]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"4\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<visibility mode=\"true\">{{settings.phpfpm.enabled_ownvhost}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2dismod php8.4]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<commands index=\"5\">\n\t\t\t\t\t\t<visibility mode=\"equals\" value=\"apache2\">{{settings.system.webserver}}\n\t\t\t\t\t\t</visibility>\n\t\t\t\t\t\t<command><![CDATA[a2disconf php8.4-fpm]]></command>\n\t\t\t\t\t</commands>\n\t\t\t\t\t<!-- instead of just restarting apache, we let the cronjob do all the\n\t\t\t\t\t\tdirty work -->\n\t\t\t\t\t<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>\n\t\t\t\t</daemon>\n\t\t\t\t<!-- Cronjob -->\n\t\t\t\t<daemon name=\"cron\" title=\"Cronjob for froxlor\"\n\t\t\t\t\tmandatory=\"true\">\n\t\t\t\t\t<install><![CDATA[DEBIAN_FRONTEND=noninteractive apt-get -yq --no-install-recommends install cron gnupg]]></install>\n\t\t\t\t\t<command><![CDATA[[ ! -e /usr/local/bin/froxlor-cli ] && ln -s <BASE_PATH>bin/froxlor-cli /usr/local/bin/froxlor-cli]]></command>\n\t\t\t\t\t<command><![CDATA[/usr/bin/php <BASE_PATH>bin/froxlor-cli froxlor:cron --run-task 99]]></command>\n\t\t\t\t\t<command><![CDATA[{{settings.system.crondreload}}]]></command>\n\t\t\t\t</daemon>\n\t\t\t</service>\n\t\t</services>\n\t</distribution>\n</froxlor>\n"
  },
  {
    "path": "lib/formfields/admin/admin/formfield.admin_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Language;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'admin_add' => [\n\t\t'title' => lng('admin.admin_add'),\n\t\t'image' => 'fa-solid fa-user-plus',\n\t\t'self_overview' => ['section' => 'admins', 'page' => 'admins'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.accountdata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'new_loginname' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'admin_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'admin_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'def_language' => [\n\t\t\t\t\t\t'label' => lng('login.language'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => Language::getLanguages(),\n\t\t\t\t\t\t'selected' => $userinfo['language']\n\n\t\t\t\t\t],\n\t\t\t\t\t'gui_access' => [\n\t\t\t\t\t\t'label' => lng('usersettings.gui_access.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.gui_access.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => 1,\n\t\t\t\t\t],\n\t\t\t\t\t'api_allowed' => [\n\t\t\t\t\t\t'label' => lng('usersettings.api_allowed.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.api_allowed.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => Settings::Get('api.enabled') == '1',\n\t\t\t\t\t\t'visible' => Settings::Get('api.enabled') == '1'\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.contactdata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'name' => [\n\t\t\t\t\t\t'label' => lng('customer.name'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email' => [\n\t\t\t\t\t\t'label' => lng('customer.email'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'custom_notes' => [\n\t\t\t\t\t\t'label' => lng('usersettings.custom_notes.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.custom_notes.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'custom_notes_show' => [\n\t\t\t\t\t\t'label' => lng('usersettings.custom_notes.show'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_c' => [\n\t\t\t\t'title' => lng('admin.servicedata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'ipaddress' => [\n\t\t\t\t\t\t'label' => lng('serversettings.ipaddress.title'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $ipaddress\n\t\t\t\t\t],\n\t\t\t\t\t'change_serversettings' => [\n\t\t\t\t\t\t'label' => lng('admin.change_serversettings'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'customers' => [\n\t\t\t\t\t\t'label' => lng('admin.customers'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'customers_see_all' => [\n\t\t\t\t\t\t'label' => lng('admin.customers_see_all'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'domains' => [\n\t\t\t\t\t\t'label' => lng('admin.domains'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'caneditphpsettings' => [\n\t\t\t\t\t\t'label' => lng('admin.caneditphpsettings'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'diskspace' => [\n\t\t\t\t\t\t'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 6,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'traffic' => [\n\t\t\t\t\t\t'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 4,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'subdomains' => [\n\t\t\t\t\t\t'label' => lng('customer.subdomains'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'emails' => [\n\t\t\t\t\t\t'label' => lng('customer.emails'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_accounts' => [\n\t\t\t\t\t\t'label' => lng('customer.accounts'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_forwarders' => [\n\t\t\t\t\t\t'label' => lng('customer.forwarders'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_quota' => [\n\t\t\t\t\t\t'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'visible' => Settings::Get('system.mail_quota_enabled') == '1',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ftps' => [\n\t\t\t\t\t\t'label' => lng('customer.ftps'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9\n\t\t\t\t\t],\n\t\t\t\t\t'mysqls' => [\n\t\t\t\t\t\t'label' => lng('customer.mysqls'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/admin/formfield.admin_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Language;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'admin_edit' => [\n\t\t'title' => lng('admin.admin_edit'),\n\t\t'image' => 'fa-solid fa-user-pen',\n\t\t'self_overview' => ['section' => 'admins', 'page' => 'admins'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.accountdata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'loginname' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['loginname']\n\t\t\t\t\t],\n\t\t\t\t\t'deactivated' => [\n\t\t\t\t\t\t'label' => lng('admin.deactivated_user'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['deactivated'],\n\t\t\t\t\t\t'visible' => $result['adminid'] != $userinfo['userid']\n\t\t\t\t\t],\n\t\t\t\t\t'admin_password' => [\n\t\t\t\t\t\t'label' => lng('login.password') . '&nbsp;(' . lng('panel.emptyfornochanges') . ')',\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'visible' => $result['adminid'] != $userinfo['userid'],\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'admin_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == '' && !($result['adminid'] == $userinfo['userid'])),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'def_language' => [\n\t\t\t\t\t\t'label' => lng('login.language'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => Language::getLanguages(),\n\t\t\t\t\t\t'selected' => $result['def_language'],\n\t\t\t\t\t\t'visible' => $result['adminid'] != $userinfo['userid']\n\t\t\t\t\t],\n\t\t\t\t\t'gui_access' => [\n\t\t\t\t\t\t'label' => lng('usersettings.gui_access.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.gui_access.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['gui_access'],\n\t\t\t\t\t\t'visible' => $result['adminid'] != $userinfo['userid']\n\t\t\t\t\t],\n\t\t\t\t\t'api_allowed' => [\n\t\t\t\t\t\t'label' => lng('usersettings.api_allowed.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.api_allowed.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['api_allowed'],\n\t\t\t\t\t\t'visible' => Settings::Get('api.enabled') == '1'\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.contactdata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'name' => [\n\t\t\t\t\t\t'label' => lng('customer.name'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => $result['name']\n\t\t\t\t\t],\n\t\t\t\t\t'email' => [\n\t\t\t\t\t\t'label' => lng('customer.email'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => $result['email']\n\t\t\t\t\t],\n\t\t\t\t\t'custom_notes' => [\n\t\t\t\t\t\t'label' => lng('usersettings.custom_notes.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.custom_notes.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $result['custom_notes']\n\t\t\t\t\t],\n\t\t\t\t\t'custom_notes_show' => [\n\t\t\t\t\t\t'label' => lng('usersettings.custom_notes.show'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['custom_notes_show']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_c' => [\n\t\t\t\t'title' => lng('admin.servicedata'),\n\t\t\t\t'visible' => $result['adminid'] != $userinfo['userid'],\n\t\t\t\t'fields' => [\n\t\t\t\t\t'ipaddress' => [\n\t\t\t\t\t\t'label' => lng('serversettings.ipaddress.title'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $ipaddress,\n\t\t\t\t\t\t'selected' => $result['ip']\n\t\t\t\t\t],\n\t\t\t\t\t'change_serversettings' => [\n\t\t\t\t\t\t'label' => lng('admin.change_serversettings'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['change_serversettings']\n\t\t\t\t\t],\n\t\t\t\t\t'customers' => [\n\t\t\t\t\t\t'label' => lng('admin.customers'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['customers']) ? '0' : $result['customers'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'customers_see_all' => [\n\t\t\t\t\t\t'label' => lng('admin.customers_see_all'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['customers_see_all']\n\t\t\t\t\t],\n\t\t\t\t\t'domains' => [\n\t\t\t\t\t\t'label' => lng('admin.domains'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['domains']) ? '0' : $result['domains'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'caneditphpsettings' => [\n\t\t\t\t\t\t'label' => lng('admin.caneditphpsettings'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['caneditphpsettings']\n\t\t\t\t\t],\n\t\t\t\t\t'diskspace' => [\n\t\t\t\t\t\t'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['diskspace']) ? '0' : $result['diskspace'],\n\t\t\t\t\t\t'maxlength' => 6,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'traffic' => [\n\t\t\t\t\t\t'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['traffic']) ? '0' : $result['traffic'],\n\t\t\t\t\t\t'maxlength' => 4,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'subdomains' => [\n\t\t\t\t\t\t'label' => lng('customer.subdomains'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['subdomains']) ? '0' : $result['subdomains'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'emails' => [\n\t\t\t\t\t\t'label' => lng('customer.emails'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['emails']) ? '0' : $result['emails'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_accounts' => [\n\t\t\t\t\t\t'label' => lng('customer.accounts'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['email_accounts']) ? '0' : $result['email_accounts'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_forwarders' => [\n\t\t\t\t\t\t'label' => lng('customer.forwarders'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['email_forwarders']) ? '0' : $result['email_forwarders'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_quota' => [\n\t\t\t\t\t\t'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['email_quota']) ? '0' : $result['email_quota'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'visible' => Settings::Get('system.mail_quota_enabled') == '1',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ftps' => [\n\t\t\t\t\t\t'label' => lng('customer.ftps'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['ftps']) ? '0' : $result['ftps'],\n\t\t\t\t\t\t'maxlength' => 9\n\t\t\t\t\t],\n\t\t\t\t\t'mysqls' => [\n\t\t\t\t\t\t'label' => lng('customer.mysqls'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['mysqls']) ? '0' : $result['mysqls'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/admin/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/cronjobs/formfield.cronjobs_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'cronjobs_edit' => [\n\t\t'title' => lng('admin.cronjob_edit'),\n\t\t'image' => 'fa-solid fa-clock-rotate-left',\n\t\t'self_overview' => ['section' => 'cronjobs', 'page' => 'overview'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('cronjob.cronjobsettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'cronfile' => [\n\t\t\t\t\t\t'label' => 'Cronjob',\n\t\t\t\t\t\t'type' => (substr($result['module'], 0, strpos($result['module'], '/')) != 'froxlor' ? 'text' : 'label'),\n\t\t\t\t\t\t'value' => $result['cronfile']\n\t\t\t\t\t],\n\t\t\t\t\t'isactive' => [\n\t\t\t\t\t\t'label' => lng('admin.activated'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['isactive']\n\t\t\t\t\t],\n\t\t\t\t\t'interval_value' => [\n\t\t\t\t\t\t'label' => lng('cronjob.cronjobintervalv'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => explode(' ', $result['interval'] ?? \"5 MINUTE\")[0] ?? \"\"\n\t\t\t\t\t],\n\t\t\t\t\t'interval_interval' => [\n\t\t\t\t\t\t'label' => lng('cronjob.cronjobinterval'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t'MINUTE' => lng('cronmgmt.minutes'),\n\t\t\t\t\t\t\t'HOUR' => lng('cronmgmt.hours'),\n\t\t\t\t\t\t\t'DAY' => lng('cronmgmt.days'),\n\t\t\t\t\t\t\t'WEEK' => lng('cronmgmt.weeks'),\n\t\t\t\t\t\t\t'MONTH' => lng('cronmgmt.months')\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'selected' => explode(' ', $result['interval'] ?? \"5 MINUTE\")[1] ?? null\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/cronjobs/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/customer/formfield.customer_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Language;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'customer_add' => [\n\t\t'title' => lng('admin.customer_add'),\n\t\t'image' => 'fa-solid fa-user-plus',\n\t\t'self_overview' => ['section' => 'customers', 'page' => 'customers'],\n\t\t'id' => 'customer_add',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.accountdata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'new_loginname' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'placeholder' => lng('admin.username_default_msg')\n\t\t\t\t\t],\n\t\t\t\t\t'createstdsubdomain' => [\n\t\t\t\t\t\t'label' => lng('admin.stdsubdomain_add') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => Settings::Get('system.createstdsubdom_default')\n\t\t\t\t\t],\n\t\t\t\t\t'store_defaultindex' => [\n\t\t\t\t\t\t'label' => lng('admin.store_defaultindex') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'new_customer_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'placeholder' => lng('admin.password_default_msg'),\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'new_customer_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'sendpassword' => [\n\t\t\t\t\t\t'label' => lng('admin.sendpassword'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'def_language' => [\n\t\t\t\t\t\t'label' => lng('login.language'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => Language::getLanguages(),\n\t\t\t\t\t\t'selected' => Settings::Get('panel.standardlanguage')\n\t\t\t\t\t],\n\t\t\t\t\t'gui_access' => [\n\t\t\t\t\t\t'label' => lng('usersettings.gui_access.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.gui_access.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'api_allowed' => [\n\t\t\t\t\t\t'label' => lng('usersettings.api_allowed.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.api_allowed.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => Settings::Get('api.enabled') == '1' && Settings::Get('api.customer_default'),\n\t\t\t\t\t\t'visible' => Settings::Get('api.enabled') == '1'\n\t\t\t\t\t],\n\t\t\t\t\t'shell_allowed' => [\n\t\t\t\t\t\t'label' => lng('usersettings.shell_allowed.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.shell_allowed.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'visible' => Settings::Get('system.allow_customer_shell') == '1',\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.contactdata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'gender' => [\n\t\t\t\t\t\t'label' => lng('gender.title'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t0 => lng('gender.undef'),\n\t\t\t\t\t\t\t1 => lng('gender.male'),\n\t\t\t\t\t\t\t2 => lng('gender.female')\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'firstname' => [\n\t\t\t\t\t\t'label' => lng('customer.firstname'),\n\t\t\t\t\t\t'desc' => lng('customer.nameorcompany_desc'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'name' => [\n\t\t\t\t\t\t'label' => lng('customer.lastname'),\n\t\t\t\t\t\t'desc' => lng('customer.nameorcompany_desc'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'company' => [\n\t\t\t\t\t\t'label' => lng('customer.company'),\n\t\t\t\t\t\t'desc' => lng('customer.nameorcompany_desc'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'street' => [\n\t\t\t\t\t\t'label' => lng('customer.street'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'zipcode' => [\n\t\t\t\t\t\t'label' => lng('customer.zipcode') . ' / ' . lng('customer.city'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'city' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => ' / ',\n\t\t\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'phone' => [\n\t\t\t\t\t\t'label' => lng('customer.phone'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'fax' => [\n\t\t\t\t\t\t'label' => lng('customer.fax'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'email' => [\n\t\t\t\t\t\t'label' => lng('customer.email'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'customernumber' => [\n\t\t\t\t\t\t'label' => lng('customer.customernumber'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'custom_notes' => [\n\t\t\t\t\t\t'label' => lng('usersettings.custom_notes.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.custom_notes.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'custom_notes_show' => [\n\t\t\t\t\t\t'label' => lng('usersettings.custom_notes.show'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_cpre' => [\n\t\t\t\t'visible' => !empty($hosting_plans),\n\t\t\t\t'title' => lng('admin.plans.use_plan'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'use_plan' => [\n\t\t\t\t\t\t'label' => lng('admin.plans.use_plan'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $hosting_plans\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_c' => [\n\t\t\t\t'title' => lng('admin.servicedata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'diskspace' => [\n\t\t\t\t\t\t'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 16,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'traffic' => [\n\t\t\t\t\t\t'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 14,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'subdomains' => [\n\t\t\t\t\t\t'label' => lng('customer.subdomains'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'emails' => [\n\t\t\t\t\t\t'label' => lng('customer.emails'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_accounts' => [\n\t\t\t\t\t\t'label' => lng('customer.accounts'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_forwarders' => [\n\t\t\t\t\t\t'label' => lng('customer.forwarders'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_quota' => [\n\t\t\t\t\t\t'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'visible' => Settings::Get('system.mail_quota_enabled') == '1',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_imap' => [\n\t\t\t\t\t\t'label' => lng('customer.email_imap'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_pop3' => [\n\t\t\t\t\t\t'label' => lng('customer.email_pop3'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ftps' => [\n\t\t\t\t\t\t'label' => lng('customer.ftps'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9\n\t\t\t\t\t],\n\t\t\t\t\t'mysqls' => [\n\t\t\t\t\t\t'label' => lng('customer.mysqls'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => 0,\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'allowed_mysqlserver' => [\n\t\t\t\t\t\t'visible' => count($mysql_servers) > 1,\n\t\t\t\t\t\t'label' => lng('customer.mysqlserver'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'values' => $mysql_servers,\n\t\t\t\t\t\t'value' => [0],\n\t\t\t\t\t\t'is_array' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'phpenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.phpenabled') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'allowed_phpconfigs' => [\n\t\t\t\t\t\t'visible' => (((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1)),\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.title'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'values' => $phpconfigs,\n\t\t\t\t\t\t'value' => ((int)Settings::Get('system.mod_fcgid') == 1 ?\n\t\t\t\t\t\t\t[Settings::Get('system.mod_fcgid_defaultini')]\n\t\t\t\t\t\t\t: ((int)Settings::Get('phpfpm.enabled') == 1 ?\n\t\t\t\t\t\t\t\t[Settings::Get('phpfpm.defaultini')]\n\t\t\t\t\t\t\t\t: null)),\n\t\t\t\t\t\t'is_array' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'perlenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.perlenabled') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'dnsenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.dnsenabled') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => Settings::Get('system.dnsenabled') == '1',\n\t\t\t\t\t\t'visible' => Settings::Get('system.dnsenabled') == '1'\n\t\t\t\t\t],\n\t\t\t\t\t'logviewenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.logviewenabled') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/customer/formfield.customer_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Language;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'customer_edit' => [\n\t\t'title' => lng('admin.customer_edit'),\n\t\t'image' => 'fa-solid fa-user-pen',\n\t\t'self_overview' => ['section' => 'customers', 'page' => 'customers'],\n\t\t'id' => 'customer_edit',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.accountdata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'loginname' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['loginname']\n\t\t\t\t\t],\n\t\t\t\t\t'documentroot' => [\n\t\t\t\t\t\t'label' => lng('customer.documentroot'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['documentroot']\n\t\t\t\t\t],\n\t\t\t\t\t'createstdsubdomain' => [\n\t\t\t\t\t\t'label' => lng('admin.stdsubdomain_add') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => (bool)$result['standardsubdomain']\n\t\t\t\t\t],\n\t\t\t\t\t'deactivated' => [\n\t\t\t\t\t\t'label' => lng('admin.deactivated_user'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['deactivated']\n\t\t\t\t\t],\n\t\t\t\t\t'new_customer_password' => [\n\t\t\t\t\t\t'label' => lng('login.password') . '&nbsp;(' . lng('panel.emptyfornochanges') . ')',\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'new_customer_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'def_language' => [\n\t\t\t\t\t\t'label' => lng('login.language'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => Language::getLanguages(),\n\t\t\t\t\t\t'selected' => $result['def_language']\n\t\t\t\t\t],\n\t\t\t\t\t'gui_access' => [\n\t\t\t\t\t\t'label' => lng('usersettings.gui_access.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.gui_access.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['gui_access'],\n\t\t\t\t\t],\n\t\t\t\t\t'api_allowed' => [\n\t\t\t\t\t\t'label' => lng('usersettings.api_allowed.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.api_allowed.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['api_allowed'],\n\t\t\t\t\t\t'visible' => Settings::Get('api.enabled') == '1'\n\t\t\t\t\t],\n\t\t\t\t\t'shell_allowed' => [\n\t\t\t\t\t\t'label' => lng('usersettings.shell_allowed.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.shell_allowed.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['shell_allowed'],\n\t\t\t\t\t\t'visible' => Settings::Get('system.allow_customer_shell') == '1',\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.contactdata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'gender' => [\n\t\t\t\t\t\t'label' => lng('gender.title'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t0 => lng('gender.undef'),\n\t\t\t\t\t\t\t1 => lng('gender.male'),\n\t\t\t\t\t\t\t2 => lng('gender.female')\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'selected' => $result['gender']\n\t\t\t\t\t],\n\t\t\t\t\t'firstname' => [\n\t\t\t\t\t\t'label' => lng('customer.firstname'),\n\t\t\t\t\t\t'desc' => lng('customer.nameorcompany_desc'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => $result['firstname']\n\t\t\t\t\t],\n\t\t\t\t\t'name' => [\n\t\t\t\t\t\t'label' => lng('customer.lastname'),\n\t\t\t\t\t\t'desc' => lng('customer.nameorcompany_desc'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => $result['name']\n\t\t\t\t\t],\n\t\t\t\t\t'company' => [\n\t\t\t\t\t\t'label' => lng('customer.company'),\n\t\t\t\t\t\t'desc' => lng('customer.nameorcompany_desc'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => $result['company']\n\t\t\t\t\t],\n\t\t\t\t\t'street' => [\n\t\t\t\t\t\t'label' => lng('customer.street'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['street']\n\t\t\t\t\t],\n\t\t\t\t\t'zipcode' => [\n\t\t\t\t\t\t'label' => lng('customer.zipcode') . ' / ' . lng('customer.city'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['zipcode'],\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'city' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => ' / ',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'value' => $result['city']\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'phone' => [\n\t\t\t\t\t\t'label' => lng('customer.phone'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['phone']\n\t\t\t\t\t],\n\t\t\t\t\t'fax' => [\n\t\t\t\t\t\t'label' => lng('customer.fax'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['fax']\n\t\t\t\t\t],\n\t\t\t\t\t'email' => [\n\t\t\t\t\t\t'label' => lng('customer.email'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => $result['email']\n\t\t\t\t\t],\n\t\t\t\t\t'customernumber' => [\n\t\t\t\t\t\t'label' => lng('customer.customernumber'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['customernumber']\n\t\t\t\t\t],\n\t\t\t\t\t'custom_notes' => [\n\t\t\t\t\t\t'style' => 'align-top',\n\t\t\t\t\t\t'label' => lng('usersettings.custom_notes.title'),\n\t\t\t\t\t\t'desc' => lng('usersettings.custom_notes.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $result['custom_notes']\n\t\t\t\t\t],\n\t\t\t\t\t'custom_notes_show' => [\n\t\t\t\t\t\t'label' => lng('usersettings.custom_notes.show'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['custom_notes_show']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_cpre' => [\n\t\t\t\t'visible' => !empty($hosting_plans),\n\t\t\t\t'title' => lng('admin.plans.use_plan'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'use_plan' => [\n\t\t\t\t\t\t'label' => lng('admin.plans.use_plan'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $hosting_plans\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_c' => [\n\t\t\t\t'title' => lng('admin.servicedata'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'diskspace' => [\n\t\t\t\t\t\t'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['diskspace']) ? '0' : $result['diskspace'],\n\t\t\t\t\t\t'maxlength' => 16,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'traffic' => [\n\t\t\t\t\t\t'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['traffic']) ? '0' : $result['traffic'],\n\t\t\t\t\t\t'maxlength' => 14,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'subdomains' => [\n\t\t\t\t\t\t'label' => lng('customer.subdomains'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['subdomains']) ? '0' : $result['subdomains'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'emails' => [\n\t\t\t\t\t\t'label' => lng('customer.emails'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['emails']) ? '0' : $result['emails'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_accounts' => [\n\t\t\t\t\t\t'label' => lng('customer.accounts'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['email_accounts']) ? '0' : $result['email_accounts'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_forwarders' => [\n\t\t\t\t\t\t'label' => lng('customer.forwarders'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['email_forwarders']) ? '0' : $result['email_forwarders'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_quota' => [\n\t\t\t\t\t\t'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['email_quota']) ? '0' : $result['email_quota'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'visible' => Settings::Get('system.mail_quota_enabled') == '1',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_imap' => [\n\t\t\t\t\t\t'label' => lng('customer.email_imap'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['imap'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_pop3' => [\n\t\t\t\t\t\t'label' => lng('customer.email_pop3'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['pop3'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ftps' => [\n\t\t\t\t\t\t'label' => lng('customer.ftps'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['ftps']) ? '0' : $result['ftps'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'mysqls' => [\n\t\t\t\t\t\t'label' => lng('customer.mysqls'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_for_unlimited'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'value' => empty($result['mysqls']) ? '0' : $result['mysqls'],\n\t\t\t\t\t\t'maxlength' => 9,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'allowed_mysqlserver' => [\n\t\t\t\t\t\t'visible' => count($mysql_servers) > 1,\n\t\t\t\t\t\t'label' => lng('customer.mysqlserver'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'values' => $mysql_servers,\n\t\t\t\t\t\t'value' => isset($result['allowed_mysqlserver']) && !empty($result['allowed_mysqlserver']) ? json_decode($result['allowed_mysqlserver'], JSON_OBJECT_AS_ARRAY) : [],\n\t\t\t\t\t\t'is_array' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'phpenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.phpenabled') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['phpenabled']\n\t\t\t\t\t],\n\t\t\t\t\t'allowed_phpconfigs' => [\n\t\t\t\t\t\t'visible' => (((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1)),\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.title'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'values' => $phpconfigs,\n\t\t\t\t\t\t'value' => isset($result['allowed_phpconfigs']) && !empty($result['allowed_phpconfigs']) ? json_decode($result['allowed_phpconfigs'], JSON_OBJECT_AS_ARRAY) : [],\n\t\t\t\t\t\t'is_array' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'perlenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.perlenabled') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['perlenabled']\n\t\t\t\t\t],\n\t\t\t\t\t'dnsenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.dnsenabled') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['dnsenabled'],\n\t\t\t\t\t\t'visible' => Settings::Get('system.dnsenabled') == '1'\n\t\t\t\t\t],\n\t\t\t\t\t'logviewenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.logviewenabled') . '?',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['logviewenabled']\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_d' => [\n\t\t\t\t'title' => lng('admin.movetoadmin'),\n\t\t\t\t'visible' => count($admin_select) > 0,\n\t\t\t\t'fields' => [\n\t\t\t\t\t'move_to_admin' => [\n\t\t\t\t\t\t'label' => lng('admin.movecustomertoadmin'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $admin_select\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/customer/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/domains/formfield.domains_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'domain_add' => [\n\t\t'title' => lng('admin.domain_add'),\n\t\t'image' => 'fa-solid fa-globe',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domains'],\n\t\t'id' => 'domain_add',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('domains.domainsettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'domain' => [\n\t\t\t\t\t\t'label' => 'Domain',\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'customerid' => [\n\t\t\t\t\t\t'label' => lng('admin.customer'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $customers,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'adminid' => [\n\t\t\t\t\t\t'visible' => $userinfo['customers_see_all'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.admin'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $admins,\n\t\t\t\t\t\t'selected' => $userinfo['adminid'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'alias' => [\n\t\t\t\t\t\t'label' => lng('domains.aliasdomain'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $domains\n\t\t\t\t\t],\n\t\t\t\t\t'caneditdomain' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_editable.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_editable.desc'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'add_date' => [\n\t\t\t\t\t\t'label' => lng('domains.add_date'),\n\t\t\t\t\t\t'desc' => lng('panel.dateformat'),\n\t\t\t\t\t\t'type' => 'date',\n\t\t\t\t\t\t'readonly' => true,\n\t\t\t\t\t\t'value' => date('Y-m-d')\n\t\t\t\t\t],\n\t\t\t\t\t'registration_date' => [\n\t\t\t\t\t\t'label' => lng('domains.registration_date'),\n\t\t\t\t\t\t'desc' => lng('panel.dateformat'),\n\t\t\t\t\t\t'type' => 'date',\n\t\t\t\t\t\t'size' => 10\n\t\t\t\t\t],\n\t\t\t\t\t'termination_date' => [\n\t\t\t\t\t\t'label' => lng('domains.termination_date'),\n\t\t\t\t\t\t'desc' => lng('panel.dateformat'),\n\t\t\t\t\t\t'type' => 'date',\n\t\t\t\t\t\t'size' => 10\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_e' => [\n\t\t\t\t'title' => lng('admin.mailserversettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'isemaildomain' => [\n\t\t\t\t\t\t'label' => lng('admin.emaildomain'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'email_only' => [\n\t\t\t\t\t\t'label' => lng('admin.email_only'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'subcanemaildomain' => [\n\t\t\t\t\t\t'label' => lng('admin.subdomainforemail'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $subcanemaildomain,\n\t\t\t\t\t\t'selected' => 0\n\t\t\t\t\t],\n\t\t\t\t\t'dkim' => [\n\t\t\t\t\t\t'visible' => Settings::Get('antispam.activated') == '1',\n\t\t\t\t\t\t'label' => 'DomainKeys',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.webserversettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'documentroot' => [\n\t\t\t\t\t\t'label' => 'DocumentRoot',\n\t\t\t\t\t\t'desc' => lng('panel.emptyfordefault'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'ipandport' => [\n\t\t\t\t\t\t'label' => lng('domains.ipandport_multi.title'),\n\t\t\t\t\t\t'desc' => lng('domains.ipandport_multi.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'values' => $ipsandports,\n\t\t\t\t\t\t'value' => explode(',', Settings::Get('system.defaultip')),\n\t\t\t\t\t\t'is_array' => 1,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'selectserveralias' => [\n\t\t\t\t\t\t'label' => lng('admin.selectserveralias'),\n\t\t\t\t\t\t'desc' => lng('admin.selectserveralias_desc'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $serveraliasoptions,\n\t\t\t\t\t\t'selected' => Settings::Get('system.domaindefaultalias')\n\t\t\t\t\t],\n\t\t\t\t\t'specialsettings' => [\n\t\t\t\t\t\t'visible' => $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.ownvhostsettings'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'notryfiles' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.webserver') == 'nginx' && $userinfo['change_serversettings'] == '1'),\n\t\t\t\t\t\t'label' => lng('admin.notryfiles.title'),\n\t\t\t\t\t\t'desc' => lng('admin.notryfiles.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'writeaccesslog' => [\n\t\t\t\t\t\t'label' => lng('admin.writeaccesslog.title'),\n\t\t\t\t\t\t'desc' => lng('admin.writeaccesslog.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'writeerrorlog' => [\n\t\t\t\t\t\t'label' => lng('admin.writeerrorlog.title'),\n\t\t\t\t\t\t'desc' => lng('admin.writeerrorlog.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'speciallogfile' => [\n\t\t\t\t\t\t'label' => lng('admin.speciallogfile.title'),\n\t\t\t\t\t\t'desc' => lng('admin.speciallogfile.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_bssl' => [\n\t\t\t\t'title' => lng('admin.webserversettings_ssl'),\n\t\t\t\t'visible' => Settings::Get('system.use_ssl') == '1',\n\t\t\t\t'fields' => [\n\t\t\t\t\t'sslenabled' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_sslenabled'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => !empty(Settings::Get('system.defaultsslip'))\n\t\t\t\t\t],\n\t\t\t\t\t'no_ssl_available_info' => [\n\t\t\t\t\t\t'visible' => empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => 'SSL',\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => lng('panel.nosslipsavailable')\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_ipandport' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('domains.ipandport_ssl_multi.title'),\n\t\t\t\t\t\t'desc' => lng('domains.ipandport_multi.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'values' => $ssl_ipsandports,\n\t\t\t\t\t\t'value' => explode(',', Settings::Get('system.defaultsslip')),\n\t\t\t\t\t\t'is_array' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_redirect' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('domains.ssl_redirect.title'),\n\t\t\t\t\t\t'desc' => lng('domains.ssl_redirect.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'letsencrypt' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.leenabled') == '1' && !empty($ssl_ipsandports)),\n\t\t\t\t\t\t'label' => lng('admin.letsencrypt.title'),\n\t\t\t\t\t\t'desc' => lng('admin.letsencrypt.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'http2' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && Settings::Get('system.http2_support') == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_http2.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_http2.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'http3' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && Settings::Get('system.webserver') == 'nginx' && Settings::Get('system.http3_support') == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_http3.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_http3.description') . lng('admin.domain_http3.nginx_version_warning'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'override_tls' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_override_tls'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_protocols' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_protocols.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.ssl.ssl_protocols.description').lng('admin.domain_override_tls_addinfo'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => explode(\",\", Settings::Get('system.ssl_protocols') ?? 'TLSv1.2'),\n\t\t\t\t\t\t'values' => [\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'value' => 'TLSv1',\n\t\t\t\t\t\t\t\t'label' => 'TLSv1'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'value' => 'TLSv1.1',\n\t\t\t\t\t\t\t\t'label' => 'TLSv1.1'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'value' => 'TLSv1.2',\n\t\t\t\t\t\t\t\t'label' => 'TLSv1.2'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'value' => 'TLSv1.3',\n\t\t\t\t\t\t\t\t'label' => 'TLSv1.3'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'is_array' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_cipher_list' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_cipher_list.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.ssl.ssl_cipher_list.description').lng('admin.domain_override_tls_addinfo'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => Settings::Get('system.ssl_cipher_list')\n\t\t\t\t\t],\n\t\t\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1' && Settings::Get('system.webserver') == \"apache2\" && Settings::Get('system.apache24') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.ssl.tlsv13_cipher_list.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.ssl.tlsv13_cipher_list.description').lng('admin.domain_override_tls_addinfo'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => Settings::Get('system.tlsv13_cipher_list')\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_specialsettings' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.ownsslvhostsettings'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'include_specialsettings' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_maxage' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_maxage.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_maxage.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 0,\n\t\t\t\t\t\t'max' => 94608000, // 3-years\n\t\t\t\t\t\t'value' => 0\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_sub' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_incsub.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_incsub.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_preload' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_preload.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_preload.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'ocsp_stapling' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_ocsp_stapling.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_ocsp_stapling.description') . (Settings::Get('system.webserver') == 'nginx' ? lng('admin.domain_ocsp_stapling.nginx_version_warning') : \"\"),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'honorcipherorder' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_honorcipherorder'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'sessiontickets' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && Settings::Get('system.sessionticketsenabled' != '1'),\n\t\t\t\t\t\t'label' => lng('admin.domain_sessiontickets'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_c' => [\n\t\t\t\t'title' => lng('admin.phpserversettings'),\n\t\t\t\t'visible' => $userinfo['change_serversettings'] == '1' || $userinfo['caneditphpsettings'] == '1',\n\t\t\t\t'fields' => [\n\t\t\t\t\t'openbasedir' => [\n\t\t\t\t\t\t'label' => 'OpenBasedir',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'openbasedir_path' => [\n\t\t\t\t\t\t'label' => lng('domain.openbasedirpath'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $openbasedir,\n\t\t\t\t\t\t'selected' => 0\n\t\t\t\t\t],\n\t\t\t\t\t'phpenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.phpenabled'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'phpsettingid' => [\n\t\t\t\t\t\t'visible' => (int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.title'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $phpconfigs,\n\t\t\t\t\t\t'selected' => (int)Settings::Get('phpfpm.enabled') == 1 ? Settings::Get('phpfpm.defaultini') : Settings::Get('system.mod_fcgid_defaultini')\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_starter' => [\n\t\t\t\t\t\t'visible' => (int)Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_starter.title'),\n\t\t\t\t\t\t'type' => 'number'\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_maxrequests' => [\n\t\t\t\t\t\t'visible' => (int)Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_maxrequests.title'),\n\t\t\t\t\t\t'type' => 'number'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_d' => [\n\t\t\t\t'title' => lng('admin.nameserversettings'),\n\t\t\t\t'visible' => Settings::Get('system.bind_enable') == '1' && $userinfo['change_serversettings'] == '1',\n\t\t\t\t'fields' => [\n\t\t\t\t\t'isbinddomain' => [\n\t\t\t\t\t\t'label' => lng('admin.createzonefile'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'zonefile' => [\n\t\t\t\t\t\t'label' => lng('admin.custombindzone'),\n\t\t\t\t\t\t'desc' => lng('admin.bindzonewarning'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/domains/formfield.domains_duplicate.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'domain_duplicate' => [\n\t\t'title' => lng('admin.domain_duplicate'),\n\t\t'image' => 'fa-solid fa-globe',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domains'],\n\t\t'id' => 'domain_add',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('domains.domainsettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'domain' => [\n\t\t\t\t\t\t'label' => 'Domain',\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'customerid' => [\n\t\t\t\t\t\t'label' => lng('admin.customer'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $customers,\n\t\t\t\t\t\t'selected' => $result['customerid'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'buttons' => [\n\t\t\t[\n\t\t\t\t'label' => lng('admin.domain_duplicate')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/domains/formfield.domains_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'domain_edit' => [\n\t\t'title' => lng('admin.domain_edit'),\n\t\t'image' => 'fa-solid fa-globe',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domains'],\n\t\t'id' => 'domain_edit',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('domains.domainsettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'domain' => [\n\t\t\t\t\t\t'label' => 'Domain',\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['domain_ace']\n\t\t\t\t\t],\n\t\t\t\t\t'customerid' => [\n\t\t\t\t\t\t'label' => lng('admin.customer'),\n\t\t\t\t\t\t'type' => (Settings::Get('panel.allow_domain_change_customer') == '1' ? 'select' : 'infotext'),\n\t\t\t\t\t\t'select_var' => (isset($customers) ? $customers : null),\n\t\t\t\t\t\t'selected' => $result['customerid'],\n\t\t\t\t\t\t'value' => (isset($result['customername']) ? $result['customername'] : null),\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'adminid' => [\n\t\t\t\t\t\t'visible' => $userinfo['customers_see_all'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.admin'),\n\t\t\t\t\t\t'type' => (Settings::Get('panel.allow_domain_change_admin') == '1' ? 'select' : 'infotext'),\n\t\t\t\t\t\t'select_var' => (!empty($admins) ? $admins : null),\n\t\t\t\t\t\t'selected' => (isset($result['adminid']) ? $result['adminid'] : $userinfo['adminid']),\n\t\t\t\t\t\t'value' => (isset($result['adminname']) ? $result['adminname'] : null),\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'alias' => [\n\t\t\t\t\t\t'visible' => $alias_check == '0',\n\t\t\t\t\t\t'label' => lng('domains.aliasdomain'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $domains,\n\t\t\t\t\t\t'selected' => $result['aliasdomain']\n\t\t\t\t\t],\n\t\t\t\t\t'associated_info' => [\n\t\t\t\t\t\t'label' => lng('domains.associated_with_domain'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $subdomains . ' ' . lng('customer.subdomains') . ', ' . $alias_check . ' ' . lng('domains.aliasdomains') . ', ' . $emails . ' ' . lng('customer.emails') . ', ' . $email_accounts . ' ' . lng('customer.accounts') . ', ' . $email_forwarders . ' ' . lng('customer.forwarders')\n\t\t\t\t\t],\n\t\t\t\t\t'caneditdomain' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_editable.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_editable.desc'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['caneditdomain']\n\t\t\t\t\t],\n\t\t\t\t\t'add_date' => [\n\t\t\t\t\t\t'label' => lng('domains.add_date'),\n\t\t\t\t\t\t'desc' => lng('panel.dateformat'),\n\t\t\t\t\t\t'type' => 'date',\n\t\t\t\t\t\t'readonly' => true,\n\t\t\t\t\t\t'value' => date('Y-m-d', (int)$result['add_date'])\n\t\t\t\t\t],\n\t\t\t\t\t'registration_date' => [\n\t\t\t\t\t\t'label' => lng('domains.registration_date'),\n\t\t\t\t\t\t'desc' => lng('panel.dateformat'),\n\t\t\t\t\t\t'type' => 'date',\n\t\t\t\t\t\t'value' => $result['registration_date'],\n\t\t\t\t\t\t'size' => 10\n\t\t\t\t\t],\n\t\t\t\t\t'termination_date' => [\n\t\t\t\t\t\t'label' => lng('domains.termination_date'),\n\t\t\t\t\t\t'desc' => lng('panel.dateformat'),\n\t\t\t\t\t\t'type' => 'date',\n\t\t\t\t\t\t'value' => $result['termination_date'],\n\t\t\t\t\t\t'size' => 10\n\t\t\t\t\t],\n\t\t\t\t\t'deactivated' => [\n\t\t\t\t\t\t'label' => lng('admin.deactivated'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['deactivated']\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_e' => [\n\t\t\t\t'title' => lng('admin.mailserversettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'isemaildomain' => [\n\t\t\t\t\t\t'label' => lng('admin.emaildomain'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['isemaildomain']\n\t\t\t\t\t],\n\t\t\t\t\t'email_only' => [\n\t\t\t\t\t\t'label' => lng('admin.email_only'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['email_only']\n\t\t\t\t\t],\n\t\t\t\t\t'subcanemaildomain' => [\n\t\t\t\t\t\t'label' => lng('admin.subdomainforemail'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $subcanemaildomain,\n\t\t\t\t\t\t'selected' => $result['subcanemaildomain']\n\t\t\t\t\t],\n\t\t\t\t\t'dkim' => [\n\t\t\t\t\t\t'visible' => Settings::Get('antispam.activated') == '1',\n\t\t\t\t\t\t'label' => 'DomainKeys',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['dkim']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.webserversettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'documentroot' => [\n\t\t\t\t\t\t'label' => 'DocumentRoot',\n\t\t\t\t\t\t'desc' => lng('panel.emptyfordefault'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['documentroot']\n\t\t\t\t\t],\n\t\t\t\t\t'ipandport' => [\n\t\t\t\t\t\t'label' => lng('domains.ipandport_multi.title'),\n\t\t\t\t\t\t'desc' => lng('domains.ipandport_multi.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'values' => $ipsandports,\n\t\t\t\t\t\t'value' => $usedips,\n\t\t\t\t\t\t'is_array' => 1,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'selectserveralias' => [\n\t\t\t\t\t\t'label' => lng('admin.selectserveralias'),\n\t\t\t\t\t\t'desc' => lng('admin.selectserveralias_desc'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $serveraliasoptions,\n\t\t\t\t\t\t'selected' => $result['iswildcarddomain'] == '1' ? 0 : ($result['wwwserveralias'] == '1' ? 1 : 2)\n\t\t\t\t\t],\n\t\t\t\t\t'specialsettings' => [\n\t\t\t\t\t\t'visible' => $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.ownvhostsettings'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'value' => $result['specialsettings'],\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'specialsettingsforsubdomains' => [\n\t\t\t\t\t\t'visible' => $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.specialsettingsforsubdomains'),\n\t\t\t\t\t\t'desc' => lng('serversettings.specialsettingsforsubdomains.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => Settings::Get('system.apply_specialsettings_default') == 1 ? '1' : '0'\n\t\t\t\t\t],\n\t\t\t\t\t'notryfiles' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.webserver') == 'nginx' && $userinfo['change_serversettings'] == '1'),\n\t\t\t\t\t\t'label' => lng('admin.notryfiles.title'),\n\t\t\t\t\t\t'desc' => lng('admin.notryfiles.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['notryfiles']\n\t\t\t\t\t],\n\t\t\t\t\t'writeaccesslog' => [\n\t\t\t\t\t\t'label' => lng('admin.writeaccesslog.title'),\n\t\t\t\t\t\t'desc' => lng('admin.writeaccesslog.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['writeaccesslog']\n\t\t\t\t\t],\n\t\t\t\t\t'writeerrorlog' => [\n\t\t\t\t\t\t'label' => lng('admin.writeerrorlog.title'),\n\t\t\t\t\t\t'desc' => lng('admin.writeerrorlog.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['writeerrorlog']\n\t\t\t\t\t],\n\t\t\t\t\t'speciallogfile' => [\n\t\t\t\t\t\t'label' => lng('admin.speciallogfile.title'),\n\t\t\t\t\t\t'desc' => lng('admin.speciallogfile.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['speciallogfile']\n\t\t\t\t\t],\n\t\t\t\t\t'speciallogverified' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => '0'\n\t\t\t\t\t],\n\t\t\t\t\t'emaildomainverified' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => '0'\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_bssl' => [\n\t\t\t\t'title' => lng('admin.webserversettings_ssl'),\n\t\t\t\t'visible' => Settings::Get('system.use_ssl') == '1',\n\t\t\t\t'fields' => [\n\t\t\t\t\t'sslenabled' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_sslenabled'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ssl_enabled']\n\t\t\t\t\t],\n\t\t\t\t\t'no_ssl_available_info' => [\n\t\t\t\t\t\t'visible' => empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => 'SSL',\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => lng('panel.nosslipsavailable')\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_ipandport' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('domains.ipandport_ssl_multi.title'),\n\t\t\t\t\t\t'desc' => lng('domains.ipandport_multi.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'values' => $ssl_ipsandports,\n\t\t\t\t\t\t'value' => $usedips,\n\t\t\t\t\t\t'is_array' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_redirect' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('domains.ssl_redirect.title'),\n\t\t\t\t\t\t'desc' => lng('domains.ssl_redirect.description') . ($result['temporary_ssl_redirect'] > 1 ? lng('domains.ssl_redirect_temporarilydisabled') : ''),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ssl_redirect']\n\t\t\t\t\t],\n\t\t\t\t\t'letsencrypt' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.leenabled') == '1' && !empty($ssl_ipsandports)),\n\t\t\t\t\t\t'label' => lng('admin.letsencrypt.title'),\n\t\t\t\t\t\t'desc' => lng('admin.letsencrypt.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['letsencrypt']\n\t\t\t\t\t],\n\t\t\t\t\t'http2' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && Settings::Get('system.http2_support') == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_http2.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_http2.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['http2']\n\t\t\t\t\t],\n\t\t\t\t\t'http3' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && Settings::Get('system.webserver') == 'nginx' && Settings::Get('system.http3_support') == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_http3.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_http3.description') . lng('admin.domain_http3.nginx_version_warning'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['http3']\n\t\t\t\t\t],\n\t\t\t\t\t'override_tls' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_override_tls'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['override_tls']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_protocols' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_protocols.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.ssl.ssl_protocols.description').lng('admin.domain_override_tls_addinfo'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => !empty($result['ssl_protocols']) ? explode(\",\", $result['ssl_protocols']) : explode(\",\", Settings::Get('system.ssl_protocols')),\n\t\t\t\t\t\t'values' => [\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'value' => 'TLSv1',\n\t\t\t\t\t\t\t\t'label' => 'TLSv1'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'value' => 'TLSv1.1',\n\t\t\t\t\t\t\t\t'label' => 'TLSv1.1'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'value' => 'TLSv1.2',\n\t\t\t\t\t\t\t\t'label' => 'TLSv1.2'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'value' => 'TLSv1.3',\n\t\t\t\t\t\t\t\t'label' => 'TLSv1.3'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'is_array' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_cipher_list' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('serversettings.ssl.ssl_cipher_list.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.ssl.ssl_cipher_list.description').lng('admin.domain_override_tls_addinfo'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => !empty($result['ssl_cipher_list']) ? $result['ssl_cipher_list'] : Settings::Get('system.ssl_cipher_list')\n\t\t\t\t\t],\n\t\t\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1' && Settings::Get('system.webserver') == \"apache2\" && Settings::Get('system.apache24') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.ssl.tlsv13_cipher_list.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.ssl.tlsv13_cipher_list.description').lng('admin.domain_override_tls_addinfo'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => !empty($result['tlsv13_cipher_list']) ? $result['tlsv13_cipher_list'] : Settings::Get('system.tlsv13_cipher_list')\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_specialsettings' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.ownsslvhostsettings'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $result['ssl_specialsettings']\n\t\t\t\t\t],\n\t\t\t\t\t'include_specialsettings' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['include_specialsettings']\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_maxage' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_maxage.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_maxage.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 0,\n\t\t\t\t\t\t'max' => 94608000, // 3-years\n\t\t\t\t\t\t'value' => $result['hsts']\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_sub' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_incsub.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_incsub.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['hsts_sub']\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_preload' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_preload.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_preload.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['hsts_preload']\n\t\t\t\t\t],\n\t\t\t\t\t'ocsp_stapling' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_ocsp_stapling.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_ocsp_stapling.description') . (Settings::Get('system.webserver') == 'nginx' ? lng('admin.domain_ocsp_stapling.nginx_version_warning') : \"\"),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ocsp_stapling']\n\t\t\t\t\t],\n\t\t\t\t\t'honorcipherorder' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports),\n\t\t\t\t\t\t'label' => lng('admin.domain_honorcipherorder'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ssl_honorcipherorder']\n\t\t\t\t\t],\n\t\t\t\t\t'sessiontickets' => [\n\t\t\t\t\t\t'visible' => !empty($ssl_ipsandports) && Settings::Get('system.sessionticketsenabled' != '1'),\n\t\t\t\t\t\t'label' => lng('admin.domain_sessiontickets'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ssl_sessiontickets']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_c' => [\n\t\t\t\t'title' => lng('admin.phpserversettings'),\n\t\t\t\t'visible' => $userinfo['change_serversettings'] == '1' || $userinfo['caneditphpsettings'] == '1',\n\t\t\t\t'fields' => [\n\t\t\t\t\t'openbasedir' => [\n\t\t\t\t\t\t'label' => 'OpenBasedir',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['openbasedir']\n\t\t\t\t\t],\n\t\t\t\t\t'openbasedir_path' => [\n\t\t\t\t\t\t'label' => lng('domain.openbasedirpath'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $openbasedir,\n\t\t\t\t\t\t'selected' => $result['openbasedir_path']\n\t\t\t\t\t],\n\t\t\t\t\t'phpenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.phpenabled'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['phpenabled']\n\t\t\t\t\t],\n\t\t\t\t\t'phpsettingid' => [\n\t\t\t\t\t\t'visible' => (((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1)),\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.title'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $phpconfigs,\n\t\t\t\t\t\t'selected' => $result['phpsettingid']\n\t\t\t\t\t],\n\t\t\t\t\t'phpsettingsforsubdomains' => [\n\t\t\t\t\t\t'visible' => $userinfo['change_serversettings'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.phpsettingsforsubdomains'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpsettingsforsubdomains.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => Settings::Get('system.apply_phpconfigs_default') == 1 ? '1' : '0'\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_starter' => [\n\t\t\t\t\t\t'visible' => (int)Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_starter.title'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => ((int)$result['mod_fcgid_starter'] != -1 ? $result['mod_fcgid_starter'] : '')\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_maxrequests' => [\n\t\t\t\t\t\t'visible' => (int)Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_maxrequests.title'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => ((int)$result['mod_fcgid_maxrequests'] != -1 ? $result['mod_fcgid_maxrequests'] : '')\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_d' => [\n\t\t\t\t'title' => lng('admin.nameserversettings'),\n\t\t\t\t'visible' => ($userinfo['change_serversettings'] == '1' && Settings::Get('system.bind_enable') == '1') || ($result['isemaildomain'] == '1' && (Settings::Get('spf.use_spf') == '1' || Settings::Get('dmarc.use_dmarc') == '1') || Settings::Get('antispam.activated') == '1' && $result['dkim'] == '1' && $result['dkim_pubkey'] != ''),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'isbinddomain' => [\n\t\t\t\t\t\t'visible' => $userinfo['change_serversettings'] == '1' && Settings::Get('system.bind_enable') == '1',\n\t\t\t\t\t\t'label' => lng('admin.createzonefile'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['isbinddomain']\n\t\t\t\t\t],\n\t\t\t\t\t'zonefile' => [\n\t\t\t\t\t\t'visible' => $userinfo['change_serversettings'] == '1' && Settings::Get('system.bind_enable') == '1',\n\t\t\t\t\t\t'label' => lng('admin.custombindzone'),\n\t\t\t\t\t\t'desc' => lng('admin.bindzonewarning'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['zonefile']\n\t\t\t\t\t],\n\t\t\t\t\t'spf_entry' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('spf.use_spf') == '1' && $result['isemaildomain'] == '1'),\n\t\t\t\t\t\t'label' => lng('antispam.required_spf_dns'),\n\t\t\t\t\t\t'type' => 'longtext',\n\t\t\t\t\t\t'value' => (string)(new \\Froxlor\\Dns\\DnsEntry('@', 'TXT', \\Froxlor\\Dns\\Dns::encloseTXTContent(Settings::Get('spf.spf_entry'))))\n\t\t\t\t\t],\n\t\t\t\t\t'dmarc_entry' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('dmarc.use_dmarc') == '1' && $result['isemaildomain'] == '1'),\n\t\t\t\t\t\t'label' => lng('antispam.required_dmarc_dns'),\n\t\t\t\t\t\t'type' => 'longtext',\n\t\t\t\t\t\t'value' => (string)(new \\Froxlor\\Dns\\DnsEntry('_dmarc', 'TXT', \\Froxlor\\Dns\\Dns::encloseTXTContent(Settings::Get('dmarc.dmarc_entry'))))\n\t\t\t\t\t],\n\t\t\t\t\t'dkim_entry' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('antispam.activated') == '1' && $result['dkim'] == '1' && $result['dkim_pubkey'] != ''),\n\t\t\t\t\t\t'label' => lng('antispam.required_dkim_dns'),\n\t\t\t\t\t\t'type' => 'longtext',\n\t\t\t\t\t\t'value' => (string)(new \\Froxlor\\Dns\\DnsEntry('dkim' . $result['dkim_id'] . '._domainkey', 'TXT', \\Froxlor\\Dns\\Dns::encloseTXTContent('v=DKIM1; k=rsa; p='.trim($result['dkim_pubkey']))))\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/domains/formfield.domains_import.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'domain_import' => [\n\t\t'title' => lng('domains.domain_import'),\n\t\t'image' => 'fa-solid fa-file-import',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domains'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('domains.domain_import'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'separator' => [\n\t\t\t\t\t\t'label' => lng('domains.import_separator'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'size' => 5,\n\t\t\t\t\t\t'value' => ';'\n\t\t\t\t\t],\n\t\t\t\t\t'offset' => [\n\t\t\t\t\t\t'label' => lng('domains.import_offset'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'size' => 10,\n\t\t\t\t\t\t'min' => 0,\n\t\t\t\t\t\t'value' => '0'\n\t\t\t\t\t],\n\t\t\t\t\t'file' => [\n\t\t\t\t\t\t'label' => lng('domains.import_file'),\n\t\t\t\t\t\t'type' => 'file',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'buttons' => [\n\t\t\t[\n\t\t\t\t'label' => lng('domains.domain_import')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/domains/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/ipsandports/formfield.ipsandports_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'ipsandports_add' => [\n\t\t'title' => lng('admin.ipsandports.add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'ipsandports', 'page' => 'ipsandports'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.ipsandports.ipandport'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'ip' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ip'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'port' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.port'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 1,\n\t\t\t\t\t\t'max' => 65535,\n\t\t\t\t\t\t'value' => 80,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.ipsandports.webserverdefaultconfig'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'listen_statement' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.webserver') != 'nginx',\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.create_listen_statement'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'namevirtualhost_statement' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.webserver') == 'apache2' && (int)Settings::Get('system.apache24') == 0,\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.create_namevirtualhost_statement'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => Settings::Get('system.webserver') == 'apache2' && (int)Settings::Get('system.apache24') == 0\n\t\t\t\t\t],\n\t\t\t\t\t'vhostcontainer' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.create_vhostcontainer'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'docroot' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.docroot.title'),\n\t\t\t\t\t\t'desc' => lng('admin.ipsandports.docroot.description'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'specialsettings' => [\n\t\t\t\t\t\t'label' => lng('admin.ownvhostsettings'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'vhostcontainer_servername_statement' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.webserver') == 'apache2',\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.create_vhostcontainer_servername_statement'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_c' => [\n\t\t\t\t'title' => lng('admin.ipsandports.webserverdomainconfig'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'default_vhostconf_domain' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.default_vhostconf_domain'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf_domain.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_default_vhostconf_domain' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') == 1,\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_default_vhostconf_domain'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf_domain.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'include_default_vhostconf_domain' => [\n\t\t\t\t\t\t'label' => lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_d' => [\n\t\t\t\t'title' => lng('admin.ipsandports.webserverssldomainconfig'),\n\t\t\t\t'visible' => Settings::Get('system.use_ssl') == 1,\n\t\t\t\t'fields' => [\n\t\t\t\t\t'ssl' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.enable_ssl'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_cert_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_file'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_key_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_key_file'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_ca_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_ca_file'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_chainfile.title'),\n\t\t\t\t\t\t'desc' => lng('admin.ipsandports.ssl_cert_chainfile.description'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_specialsettings' => [\n\t\t\t\t\t\t'label' => lng('admin.ownsslvhostsettings'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t],\n\t\t\t\t\t'include_specialsettings' => [\n\t\t\t\t\t\t'label' => lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'ipsandports_edit' => [\n\t\t'title' => lng('admin.ipsandports.edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'ipsandports', 'page' => 'ipsandports'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.ipsandports.ipandport'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'ip' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ip'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['ip'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'port' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.port'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['port'],\n\t\t\t\t\t\t'min' => 1,\n\t\t\t\t\t\t'max' => 65535,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.ipsandports.webserverdefaultconfig'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'listen_statement' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.webserver') != 'nginx',\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.create_listen_statement'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['listen_statement']\n\t\t\t\t\t],\n\t\t\t\t\t'namevirtualhost_statement' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.webserver') == 'apache2' && (int)Settings::Get('system.apache24') == 0,\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.create_namevirtualhost_statement'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['namevirtualhost_statement']\n\t\t\t\t\t],\n\t\t\t\t\t'vhostcontainer' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.create_vhostcontainer'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['vhostcontainer']\n\t\t\t\t\t],\n\t\t\t\t\t'docroot' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.docroot.title'),\n\t\t\t\t\t\t'desc' => lng('admin.ipsandports.docroot.description'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['docroot']\n\t\t\t\t\t],\n\t\t\t\t\t'specialsettings' => [\n\t\t\t\t\t\t'label' => lng('admin.ownvhostsettings'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $result['specialsettings']\n\t\t\t\t\t],\n\t\t\t\t\t'vhostcontainer_servername_statement' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.webserver') == 'apache2',\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.create_vhostcontainer_servername_statement'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['vhostcontainer_servername_statement']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_c' => [\n\t\t\t\t'title' => lng('admin.ipsandports.webserverdomainconfig'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'default_vhostconf_domain' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.default_vhostconf_domain'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf_domain.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $result['default_vhostconf_domain']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_default_vhostconf_domain' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.use_ssl') == 1,\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_default_vhostconf_domain'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf_domain.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $result['ssl_default_vhostconf_domain']\n\t\t\t\t\t],\n\t\t\t\t\t'include_default_vhostconf_domain' => [\n\t\t\t\t\t\t'label' => lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['include_default_vhostconf_domain']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_d' => [\n\t\t\t\t'title' => lng('admin.ipsandports.webserverssldomainconfig'),\n\t\t\t\t'visible' => Settings::Get('system.use_ssl') == 1,\n\t\t\t\t'fields' => [\n\t\t\t\t\t'ssl' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.enable_ssl'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ssl']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_cert_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_file'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['ssl_cert_file']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_key_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_key_file'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['ssl_key_file']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_ca_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_ca_file'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['ssl_ca_file']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_chainfile.title'),\n\t\t\t\t\t\t'desc' => lng('admin.ipsandports.ssl_cert_chainfile.description'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['ssl_cert_chainfile']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_specialsettings' => [\n\t\t\t\t\t\t'label' => lng('admin.ownsslvhostsettings'),\n\t\t\t\t\t\t'desc' => lng('serversettings.default_vhostconf.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $result['ssl_specialsettings']\n\t\t\t\t\t],\n\t\t\t\t\t'include_specialsettings' => [\n\t\t\t\t\t\t'label' => lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['include_specialsettings']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/ipsandports/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/messages/formfield.messages_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'messages_add' => [\n\t\t'title' => lng('admin.message'),\n\t\t'image' => 'fa-solid fa-paper-plane',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'fields' => [\n\t\t\t\t\t'recipient' => [\n\t\t\t\t\t\t'label' => lng('admin.recipient'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $recipients,\n\t\t\t\t\t\t'selected' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'subject' => [\n\t\t\t\t\t\t'label' => lng('admin.subject'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => lng('admin.nosubject'),\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'message' => [\n\t\t\t\t\t\t'label' => lng('admin.text'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'buttons' => [\n\t\t\t[\n\t\t\t\t'label' => lng('admin.message')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/messages/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/mysqlserver/formfield.mysqlserver_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'mysqlserver_add' => [\n\t\t'title' => lng('admin.mysqlserver.add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'mysqlserver', 'page' => 'mysqlserver'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.mysqlserver.mysqlserver'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'mysql_host' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.host'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_port' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.port'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 1,\n\t\t\t\t\t\t'max' => 65535,\n\t\t\t\t\t\t'value' => 3306,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.caption'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t],\n\t\t\t\t\t'privileged_user' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.user'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'privileged_password' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'allow_all_customers' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.allowall'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'test_connection' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.testconn'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.mysqlserver.ssl'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'mysql_ca' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.ssl_cert_file'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_verifycert' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.verify_ca'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/mysqlserver/formfield.mysqlserver_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\n\nreturn [\n\t'mysqlserver_edit' => [\n\t\t'title' => lng('admin.mysqlserver.edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'mysqlserver', 'page' => 'mysqlserver'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.mysqlserver.mysqlserver'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'mysql_host' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.host'),\n\t\t\t\t\t\t'type' => empty($result['id']) ? 'label' : 'text',\n\t\t\t\t\t\t'value' => $result['host'],\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_port' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.port'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 1,\n\t\t\t\t\t\t'max' => 65535,\n\t\t\t\t\t\t'value' => $result['port'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.caption'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['caption'],\n\t\t\t\t\t],\n\t\t\t\t\t'privileged_user' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.user'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['user'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'privileged_password' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.password_emptynochange'),\n\t\t\t\t\t\t'type' => 'password'\n\t\t\t\t\t],\n\t\t\t\t\t'allow_all_customers' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.allowall'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'test_connection' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.testconn'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_b' => [\n\t\t\t\t'title' => lng('admin.mysqlserver.ssl'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'mysql_ca' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.ssl_cert_file'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['ssl']['caFile'] ?? \"\",\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_verifycert' => [\n\t\t\t\t\t\t'label' => lng('admin.mysqlserver.verify_ca'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ssl']['verifyServerCertificate'] ?? false,\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/mysqlserver/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'fpmconfig_add' => [\n\t\t'title' => lng('admin.fpmsettings.addnew'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'phpsettings', 'page' => 'fpmdaemons'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.fpmsettings.addnew'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.description'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 50,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'reload_cmd' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.reload'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 255,\n\t\t\t\t\t\t'value' => 'service php7.4-fpm restart',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'required_otp' => true\n\t\t\t\t\t],\n\t\t\t\t\t'config_dir' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.configdir'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 255,\n\t\t\t\t\t\t'value' => '/etc/php/7.4/fpm/pool.d/',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'pm' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.pm'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t'static' => 'static',\n\t\t\t\t\t\t\t'dynamic' => 'dynamic',\n\t\t\t\t\t\t\t'ondemand' => 'ondemand'\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'max_children' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_children.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_children.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 5,\n\t\t\t\t\t\t'min' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'start_servers' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.start_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.start_servers.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 2,\n\t\t\t\t\t\t'min' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'min_spare_servers' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.min_spare_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.min_spare_servers.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'max_spare_servers' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_spare_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_spare_servers.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 3\n\t\t\t\t\t],\n\t\t\t\t\t'max_requests' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_requests.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_requests.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 0\n\t\t\t\t\t],\n\t\t\t\t\t'idle_timeout' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.idle_timeout.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.idle_timeout.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 10\n\t\t\t\t\t],\n\t\t\t\t\t'limit_extensions' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.limit_extensions.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.limit_extensions.description'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => '.php'\n\t\t\t\t\t],\n\t\t\t\t\t'custom_config' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.custom_config.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.custom_config.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 50,\n\t\t\t\t\t\t'rows' => 7\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'fpmconfig_replacers' => [\n\t\t'replacers' => [\n\t\t\t[\n\t\t\t\t'var' => 'PEAR_DIR',\n\t\t\t\t'description' => lng('admin.phpconfig.pear_dir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR_C',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir_c')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR_GLOBAL',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir_global')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'TMP_DIR',\n\t\t\t\t'description' => lng('admin.phpconfig.tmp_dir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_EMAIL',\n\t\t\t\t'description' => lng('admin.phpconfig.customer_email')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN_EMAIL',\n\t\t\t\t'description' => lng('admin.phpconfig.admin_email')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'DOMAIN',\n\t\t\t\t'description' => lng('admin.phpconfig.domain')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER',\n\t\t\t\t'description' => lng('admin.phpconfig.customer')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN',\n\t\t\t\t'description' => lng('admin.phpconfig.admin')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'DOCUMENT_ROOT',\n\t\t\t\t'description' => lng('admin.phpconfig.docroot')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_HOMEDIR',\n\t\t\t\t'description' => lng('admin.phpconfig.homedir')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'fpmconfig_edit' => [\n\t\t'title' => lng('admin.fpmsettings.edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'phpsettings', 'page' => 'fpmdaemons'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.fpmsettings.edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.description'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 50,\n\t\t\t\t\t\t'value' => $result['description'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'reload_cmd' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.reload'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 255,\n\t\t\t\t\t\t'value' => $result['reload_cmd'],\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'required_otp' => true\n\t\t\t\t\t],\n\t\t\t\t\t'config_dir' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.configdir'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 255,\n\t\t\t\t\t\t'value' => $result['config_dir'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'pm' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.pm'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t'static' => 'static',\n\t\t\t\t\t\t\t'dynamic' => 'dynamic',\n\t\t\t\t\t\t\t'ondemand' => 'ondemand'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'selected' => $result['pm']\n\t\t\t\t\t],\n\t\t\t\t\t'max_children' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_children.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_children.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['max_children'],\n\t\t\t\t\t\t'min' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'start_servers' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.start_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.start_servers.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['start_servers'],\n\t\t\t\t\t\t'min' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'min_spare_servers' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.min_spare_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.min_spare_servers.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['min_spare_servers']\n\t\t\t\t\t],\n\t\t\t\t\t'max_spare_servers' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_spare_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_spare_servers.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['max_spare_servers']\n\t\t\t\t\t],\n\t\t\t\t\t'max_requests' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_requests.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_requests.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['max_requests']\n\t\t\t\t\t],\n\t\t\t\t\t'idle_timeout' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.idle_timeout.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.idle_timeout.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['idle_timeout']\n\t\t\t\t\t],\n\t\t\t\t\t'limit_extensions' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.limit_extensions.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.limit_extensions.description'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['limit_extensions']\n\t\t\t\t\t],\n\t\t\t\t\t'custom_config' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.custom_config.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.custom_config.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 50,\n\t\t\t\t\t\t'rows' => 7,\n\t\t\t\t\t\t'value' => $result['custom_config']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'fpmconfig_replacers' => [\n\t\t'replacers' => [\n\t\t\t[\n\t\t\t\t'var' => 'PEAR_DIR',\n\t\t\t\t'description' => lng('admin.phpconfig.pear_dir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR_C',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir_c')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR_GLOBAL',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir_global')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'TMP_DIR',\n\t\t\t\t'description' => lng('admin.phpconfig.tmp_dir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_EMAIL',\n\t\t\t\t'description' => lng('admin.phpconfig.customer_email')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN_EMAIL',\n\t\t\t\t'description' => lng('admin.phpconfig.admin_email')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'DOMAIN',\n\t\t\t\t'description' => lng('admin.phpconfig.domain')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER',\n\t\t\t\t'description' => lng('admin.phpconfig.customer')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN',\n\t\t\t\t'description' => lng('admin.phpconfig.admin')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'DOCUMENT_ROOT',\n\t\t\t\t'description' => lng('admin.phpconfig.docroot')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_HOMEDIR',\n\t\t\t\t'description' => lng('admin.phpconfig.homedir')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/phpconfig/formfield.phpconfig_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'phpconfig_add' => [\n\t\t'title' => lng('admin.phpsettings.addsettings'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'phpsettings', 'page' => 'overview'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.phpsettings.addsettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.description'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 50,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'binary' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.binary'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 255,\n\t\t\t\t\t\t'value' => '/usr/bin/php-cgi',\n\t\t\t\t\t\t'required_otp' => true\n\t\t\t\t\t],\n\t\t\t\t\t'fpmconfig' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.fpmdesc'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $fpmconfigs,\n\t\t\t\t\t\t'selected' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'file_extensions' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.file_extensions'),\n\t\t\t\t\t\t'desc' => lng('admin.phpsettings.file_extensions_note'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 255,\n\t\t\t\t\t\t'value' => 'php',\n\t\t\t\t\t\t'required_otp' => true\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_starter' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_starter.title'),\n\t\t\t\t\t\t'type' => 'number'\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_maxrequests' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_maxrequests.title'),\n\t\t\t\t\t\t'type' => 'number'\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_umask' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_umask.title'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 3,\n\t\t\t\t\t\t'value' => '022'\n\t\t\t\t\t],\n\t\t\t\t\t'phpfpm_enable_slowlog' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.enable_slowlog'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'phpfpm_reqtermtimeout' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.request_terminate_timeout'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 10,\n\t\t\t\t\t\t'value' => '60s'\n\t\t\t\t\t],\n\t\t\t\t\t'phpfpm_reqslowtimeout' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.request_slowlog_timeout'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 10,\n\t\t\t\t\t\t'value' => '5s'\n\t\t\t\t\t],\n\t\t\t\t\t'pass_authorizationheader' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.webserver') == \"apache2\",\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.pass_authorizationheader'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'override_fpmconfig' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.override_fpmconfig'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'pm' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.pm'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t'static' => 'static',\n\t\t\t\t\t\t\t'dynamic' => 'dynamic',\n\t\t\t\t\t\t\t'ondemand' => 'ondemand'\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'max_children' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_children.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_children.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 1\n\t\t\t\t\t],\n\t\t\t\t\t'start_servers' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.start_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.start_servers.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 20\n\t\t\t\t\t],\n\t\t\t\t\t'min_spare_servers' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.min_spare_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.min_spare_servers.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 5\n\t\t\t\t\t],\n\t\t\t\t\t'max_spare_servers' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_spare_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_spare_servers.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 35\n\t\t\t\t\t],\n\t\t\t\t\t'max_requests' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_requests.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_requests.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 0\n\t\t\t\t\t],\n\t\t\t\t\t'idle_timeout' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.idle_timeout.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.idle_timeout.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => 10\n\t\t\t\t\t],\n\t\t\t\t\t'limit_extensions' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.limit_extensions.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.limit_extensions.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => '.php'\n\t\t\t\t\t],\n\t\t\t\t\t'phpsettings' => [\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.phpinisettings'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 80,\n\t\t\t\t\t\t'rows' => 20,\n\t\t\t\t\t\t'value' => $result['phpsettings'],\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'required_otp' => true\n\t\t\t\t\t],\n\t\t\t\t\t'allow_all_customers' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.allow_all_customers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.allow_all_customers.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'phpconfig_replacers' => [\n\t\t'replacers' => [\n\t\t\t[\n\t\t\t\t'var' => 'PEAR_DIR',\n\t\t\t\t'description' => lng('admin.phpconfig.pear_dir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR_C',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir_c')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR_GLOBAL',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir_global')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'TMP_DIR',\n\t\t\t\t'description' => lng('admin.phpconfig.tmp_dir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_EMAIL',\n\t\t\t\t'description' => lng('admin.phpconfig.customer_email')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN_EMAIL',\n\t\t\t\t'description' => lng('admin.phpconfig.admin_email')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'DOMAIN',\n\t\t\t\t'description' => lng('admin.phpconfig.domain')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER',\n\t\t\t\t'description' => lng('admin.phpconfig.customer')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN',\n\t\t\t\t'description' => lng('admin.phpconfig.admin')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'DOCUMENT_ROOT',\n\t\t\t\t'description' => lng('admin.phpconfig.docroot')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_HOMEDIR',\n\t\t\t\t'description' => lng('admin.phpconfig.homedir')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'phpconfig_edit' => [\n\t\t'title' => lng('admin.phpsettings.editsettings'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'phpsettings', 'page' => 'overview'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.phpsettings.editsettings'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.description'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 50,\n\t\t\t\t\t\t'value' => $result['description'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'binary' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.binary'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 255,\n\t\t\t\t\t\t'value' => $result['binary'],\n\t\t\t\t\t\t'required_otp' => true\n\t\t\t\t\t],\n\t\t\t\t\t'fpmconfig' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.fpmdesc'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $fpmconfigs,\n\t\t\t\t\t\t'selected' => $result['fpmsettingid']\n\t\t\t\t\t],\n\t\t\t\t\t'file_extensions' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.file_extensions'),\n\t\t\t\t\t\t'desc' => lng('admin.phpsettings.file_extensions_note'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 255,\n\t\t\t\t\t\t'value' => $result['file_extensions'],\n\t\t\t\t\t\t'required_otp' => true\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_starter' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_starter.title'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => ((int)$result['mod_fcgid_starter'] != -1 ? $result['mod_fcgid_starter'] : '')\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_maxrequests' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_maxrequests.title'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => ((int)$result['mod_fcgid_maxrequests'] != -1 ? $result['mod_fcgid_maxrequests'] : '')\n\t\t\t\t\t],\n\t\t\t\t\t'mod_fcgid_umask' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mod_fcgid') == 1,\n\t\t\t\t\t\t'label' => lng('admin.mod_fcgid_umask.title'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 3,\n\t\t\t\t\t\t'value' => $result['mod_fcgid_umask']\n\t\t\t\t\t],\n\t\t\t\t\t'phpfpm_enable_slowlog' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.enable_slowlog'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['fpm_slowlog']\n\t\t\t\t\t],\n\t\t\t\t\t'phpfpm_reqtermtimeout' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.request_terminate_timeout'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 10,\n\t\t\t\t\t\t'value' => $result['fpm_reqterm']\n\t\t\t\t\t],\n\t\t\t\t\t'phpfpm_reqslowtimeout' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.request_slowlog_timeout'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'maxlength' => 10,\n\t\t\t\t\t\t'value' => $result['fpm_reqslow']\n\t\t\t\t\t],\n\t\t\t\t\t'pass_authorizationheader' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.webserver') == \"apache2\",\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.pass_authorizationheader'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['pass_authorizationheader']\n\t\t\t\t\t],\n\t\t\t\t\t'override_fpmconfig' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.override_fpmconfig'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['override_fpmconfig']\n\t\t\t\t\t],\n\t\t\t\t\t'pm' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.pm'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t'static' => 'static',\n\t\t\t\t\t\t\t'dynamic' => 'dynamic',\n\t\t\t\t\t\t\t'ondemand' => 'ondemand'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'selected' => $result['pm']\n\t\t\t\t\t],\n\t\t\t\t\t'max_children' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_children.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_children.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['max_children']\n\t\t\t\t\t],\n\t\t\t\t\t'start_servers' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.start_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.start_servers.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['start_servers']\n\t\t\t\t\t],\n\t\t\t\t\t'min_spare_servers' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.min_spare_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.min_spare_servers.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['min_spare_servers']\n\t\t\t\t\t],\n\t\t\t\t\t'max_spare_servers' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_spare_servers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_spare_servers.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['max_spare_servers']\n\t\t\t\t\t],\n\t\t\t\t\t'max_requests' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.max_requests.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.max_requests.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['max_requests']\n\t\t\t\t\t],\n\t\t\t\t\t'idle_timeout' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.idle_timeout.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.idle_timeout.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $result['idle_timeout']\n\t\t\t\t\t],\n\t\t\t\t\t'limit_extensions' => [\n\t\t\t\t\t\t'visible' => Settings::Get('phpfpm.enabled') == 1,\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.limit_extensions.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.limit_extensions.description') . lng('serversettings.phpfpm_settings.override_fpmconfig_addinfo'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['limit_extensions']\n\t\t\t\t\t],\n\t\t\t\t\t'phpsettings' => [\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.phpinisettings'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 80,\n\t\t\t\t\t\t'rows' => 20,\n\t\t\t\t\t\t'value' => $result['phpsettings'],\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'required_otp' => true\n\t\t\t\t\t],\n\t\t\t\t\t'allow_all_customers' => [\n\t\t\t\t\t\t'label' => lng('serversettings.phpfpm_settings.allow_all_customers.title'),\n\t\t\t\t\t\t'desc' => lng('serversettings.phpfpm_settings.allow_all_customers.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'phpconfig_replacers' => [\n\t\t'replacers' => [\n\t\t\t[\n\t\t\t\t'var' => 'PEAR_DIR',\n\t\t\t\t'description' => lng('admin.phpconfig.pear_dir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR_C',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir_c')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'OPEN_BASEDIR_GLOBAL',\n\t\t\t\t'description' => lng('admin.phpconfig.open_basedir_global')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'TMP_DIR',\n\t\t\t\t'description' => lng('admin.phpconfig.tmp_dir')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_EMAIL',\n\t\t\t\t'description' => lng('admin.phpconfig.customer_email')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN_EMAIL',\n\t\t\t\t'description' => lng('admin.phpconfig.admin_email')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'DOMAIN',\n\t\t\t\t'description' => lng('admin.phpconfig.domain')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER',\n\t\t\t\t'description' => lng('admin.phpconfig.customer')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN',\n\t\t\t\t'description' => lng('admin.phpconfig.admin')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'DOCUMENT_ROOT',\n\t\t\t\t'description' => lng('admin.phpconfig.docroot')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_HOMEDIR',\n\t\t\t\t'description' => lng('admin.phpconfig.homedir')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/phpconfig/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/plans/formfield.plans_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'plans_add' => [\n\t\t'title' => lng('admin.plans.add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'plans', 'page' => 'overview'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.plans.plan_details'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'name' => [\n\t\t\t\t\t\t'label' => lng('admin.plans.name'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('admin.plans.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/plans/formfield.plans_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'plans_edit' => [\n\t\t'title' => lng('admin.plans.edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'plans', 'page' => 'overview'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.plans.plan_details'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'name' => [\n\t\t\t\t\t\t'label' => lng('admin.plans.name'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['name'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('admin.plans.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $result['description']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/plans/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/settings/formfield.settings_import.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'settings_import' => [\n\t\t'title' => lng('admin.configfiles.importexport'),\n\t\t'image' => 'fa-solid fa-file-import',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'fields' => [\n\t\t\t\t\t'import_file' => [\n\t\t\t\t\t\t'label' => lng('admin.settings_importfile'),\n\t\t\t\t\t\t'type' => 'file',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'buttons' => [\n\t\t\t[\n\t\t\t\t'label' => lng('panel.upload_import')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/settings/formfield.settings_mailtest.php",
    "content": "<?php\n\nuse Froxlor\\Settings;\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'mailtest' => [\n\t\t'title' => lng('admin.testmail'),\n\t\t'image' => 'fa-solid fa-paper-plane',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'fields' => [\n\t\t\t\t\t'smtp_user' => [\n\t\t\t\t\t\t'label' => lng('serversettings.mail_smtp_user'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => (empty(Settings::Get('system.mail_smtp_user')) ? lng('panel.unspecified') : Settings::Get('system.mail_smtp_user'))\n\t\t\t\t\t],\n\t\t\t\t\t'smtp_host' => [\n\t\t\t\t\t\t'label' => lng('serversettings.mail_smtp_host'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => (empty(Settings::Get('system.mail_smtp_host')) ? lng('panel.unspecified') : Settings::Get('system.mail_smtp_host'))\n\t\t\t\t\t],\n\t\t\t\t\t'smtp_port' => [\n\t\t\t\t\t\t'label' => lng('serversettings.mail_smtp_port'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => (empty(Settings::Get('system.mail_smtp_port')) ? lng('panel.unspecified') : Settings::Get('system.mail_smtp_port'))\n\t\t\t\t\t],\n\t\t\t\t\t'smtp_auth' => [\n\t\t\t\t\t\t'label' => lng('serversettings.mail_smtp_auth'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => 1,\n\t\t\t\t\t\t'checked' => (bool)Settings::Get('system.mail_use_smtp'),\n\t\t\t\t\t\t'disabled' => true\n\t\t\t\t\t],\n\t\t\t\t\t'smtp_tls' => [\n\t\t\t\t\t\t'label' => lng('serversettings.mail_smtp_usetls'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => 1,\n\t\t\t\t\t\t'checked' => (bool)Settings::Get('system.mail_smtp_usetls'),\n\t\t\t\t\t\t'disabled' => true\n\t\t\t\t\t],\n\t\t\t\t\t'test_addr' => [\n\t\t\t\t\t\t'label' => lng('admin.smtptestaddr'),\n\t\t\t\t\t\t'type' => 'email',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'buttons' => [\n\t\t\t[\n\t\t\t\t'label' => lng('admin.smtptestsend')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/settings/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/templates/formfield.filetemplate_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'filetemplate_add' => [\n\t\t'title' => lng('admin.templates.template_add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'templates', 'page' => 'email'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.templates.template_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'template' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.action'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $free_templates\n\t\t\t\t\t],\n\t\t\t\t\t'file_extension' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.file_extension'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'string_regexp' => '/^[a-zA-Z0-9]{1,6}$/',\n\t\t\t\t\t\t'default' => 'html',\n\t\t\t\t\t\t'value' => 'html',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'filecontent' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.filecontent'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'filesend' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => 'filesend'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'filetemplate_replacers' => [\n\t\t'replacers' => [\n\t\t\t[\n\t\t\t\t'var' => 'SERVERNAME',\n\t\t\t\t'description' => lng('admin.templates.SERVERNAME')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER',\n\t\t\t\t'description' => lng('admin.templates.CUSTOMER')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN',\n\t\t\t\t'description' => lng('admin.templates.ADMIN')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_EMAIL',\n\t\t\t\t'description' => lng('admin.templates.CUSTOMER_EMAIL')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN_EMAIL',\n\t\t\t\t'description' => lng('admin.templates.ADMIN_EMAIL')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/templates/formfield.filetemplate_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'filetemplate_edit' => [\n\t\t'title' => lng('admin.templates.template_edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'templates', 'page' => 'email'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.templates.template_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'template' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.action'),\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => lng('admin.templates.' . $row['varname']),\n\t\t\t\t\t\t'display' => lng('admin.templates.' . $row['varname'])\n\t\t\t\t\t],\n\t\t\t\t\t'file_extension' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.file_extension'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'string_regexp' => '/^[a-zA-Z0-9]{1,6}$/',\n\t\t\t\t\t\t'value' => $row['file_extension'],\n\t\t\t\t\t\t'default' => 'html',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'filecontent' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.filecontent'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $row['value'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'filesend' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => 'filesend'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'filetemplate_replacers' => [\n\t\t'replacers' => [\n\t\t\t[\n\t\t\t\t'var' => 'SERVERNAME',\n\t\t\t\t'description' => lng('admin.templates.SERVERNAME')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER',\n\t\t\t\t'description' => lng('admin.templates.CUSTOMER')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN',\n\t\t\t\t'description' => lng('admin.templates.ADMIN')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'CUSTOMER_EMAIL',\n\t\t\t\t'description' => lng('admin.templates.CUSTOMER_EMAIL')\n\t\t\t],\n\t\t\t[\n\t\t\t\t'var' => 'ADMIN_EMAIL',\n\t\t\t\t'description' => lng('admin.templates.ADMIN_EMAIL')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/admin/templates/formfield.template_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'template_add' => [\n\t\t'title' => lng('admin.templates.template_add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'templates', 'page' => 'email', 'action' => 'add'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.templates.template_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'language' => [\n\t\t\t\t\t\t'label' => lng('login.language'),\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => $language,\n\t\t\t\t\t\t'display' => $language\n\t\t\t\t\t],\n\t\t\t\t\t'template' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.action'),\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => $template,\n\t\t\t\t\t\t'display' => lng('admin.templates.' . $template)\n\t\t\t\t\t],\n\t\t\t\t\t'subject' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.subject'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $subject\n\t\t\t\t\t],\n\t\t\t\t\t'mailbody' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.mailbody'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'value' => $body,\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'template_replacers' => include __DIR__ . '/template.replacers.php'\n];\n"
  },
  {
    "path": "lib/formfields/admin/templates/formfield.template_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'template_edit' => [\n\t\t'title' => lng('admin.templates.template_edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'templates', 'page' => 'email'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('admin.templates.template_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'language' => [\n\t\t\t\t\t\t'label' => lng('login.language'),\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => $language,\n\t\t\t\t\t\t'display' => $language\n\t\t\t\t\t],\n\t\t\t\t\t'template' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.action'),\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => $template_name,\n\t\t\t\t\t\t'display' => $template_name\n\t\t\t\t\t],\n\t\t\t\t\t'subject' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.subject'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $subject\n\t\t\t\t\t],\n\t\t\t\t\t'mailbody' => [\n\t\t\t\t\t\t'label' => lng('admin.templates.mailbody'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'value' => $mailbody\n\t\t\t\t\t],\n\t\t\t\t\t'subjectid' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => $subjectid\n\t\t\t\t\t],\n\t\t\t\t\t'mailbodyid' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => $mailbodyid\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'template_replacers' => include __DIR__ . '/template.replacers.php'\n];\n"
  },
  {
    "path": "lib/formfields/admin/templates/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/admin/templates/template.replacers.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\nreturn [\n\t'replacers' => [\n\t\t[\n\t\t\t'var' => 'SALUTATION',\n\t\t\t'description' => lng('admin.templates.SALUTATION')\n\t\t],\n\t\t[\n\t\t\t'var' => 'FIRSTNAME',\n\t\t\t'description' => lng('admin.templates.FIRSTNAME')\n\t\t],\n\t\t[\n\t\t\t'var' => 'NAME',\n\t\t\t'description' => lng('admin.templates.NAME')\n\t\t],\n\t\t[\n\t\t\t'var' => 'COMPANY',\n\t\t\t'description' => lng('admin.templates.COMPANY')\n\t\t],\n\t\t[\n\t\t\t'var' => 'CUSTOMER_NO',\n\t\t\t'description' => lng('admin.templates.CUSTOMER_NO')\n\t\t],\n\t\t[\n\t\t\t'var' => 'USERNAME',\n\t\t\t'description' => lng('admin.templates.USERNAME')\n\t\t],\n\t\t[\n\t\t\t'var' => 'PASSWORD',\n\t\t\t'description' => lng('admin.templates.PASSWORD'),\n\t\t\t'visible' => $template == 'createcustomer'\n\t\t],\n\t\t[\n\t\t\t'var' => 'EMAIL',\n\t\t\t'description' => lng('admin.templates.EMAIL'),\n\t\t\t'visible' => $template == 'pop_success'\n\t\t],\n\t\t[\n\t\t\t'var' => 'PASSWORD',\n\t\t\t'description' => lng('admin.templates.EMAIL_PASSWORD'),\n\t\t\t'visible' => $template == 'pop_success'\n\t\t],\n\t\t[\n\t\t\t'var' => 'LINK',\n\t\t\t'description' => lng('admin.templates.LINK'),\n\t\t\t'visible' => $template == 'password_reset'\n\t\t],\n\t\t[\n\t\t\t'var' => 'TRAFFIC',\n\t\t\t'description' => lng('admin.templates.TRAFFIC'),\n\t\t\t'visible' => $template == 'trafficmaxpercent'\n\t\t],\n\t\t[\n\t\t\t'var' => 'TRAFFICUSED',\n\t\t\t'description' => lng('admin.templates.TRAFFICUSED'),\n\t\t\t'visible' => $template == 'trafficmaxpercent'\n\t\t],\n\t\t[\n\t\t\t'var' => 'DISKAVAILABLE',\n\t\t\t'description' => lng('admin.templates.DISKAVAILABLE'),\n\t\t\t'visible' => $template == 'diskmaxpercent'\n\t\t],\n\t\t[\n\t\t\t'var' => 'DISKUSED',\n\t\t\t'description' => lng('admin.templates.DISKUSED'),\n\t\t\t'visible' => $template == 'diskmaxpercent'\n\t\t],\n\t\t[\n\t\t\t'var' => 'MAX_PERCENT',\n\t\t\t'description' => lng('admin.templates.MAX_PERCENT'),\n\t\t\t'visible' => $template == 'trafficmaxpercent' || $template == 'diskmaxpercent'\n\t\t],\n\t\t[\n\t\t\t'var' => 'USAGE_PERCENT',\n\t\t\t'description' => lng('admin.templates.USAGE_PERCENT'),\n\t\t\t'visible' => $template == 'trafficmaxpercent' || $template == 'diskmaxpercent'\n\t\t],\n\t\t[\n\t\t\t'var' => 'DB_NAME',\n\t\t\t'description' => lng('admin.templates.DB_NAME'),\n\t\t\t'visible' => $template == 'new_database_by_customer'\n\t\t],\n\t\t[\n\t\t\t'var' => 'DB_PASS',\n\t\t\t'description' => lng('admin.templates.DB_PASS'),\n\t\t\t'visible' => $template == 'new_database_by_customer'\n\t\t],\n\t\t[\n\t\t\t'var' => 'DB_DESC',\n\t\t\t'description' => lng('admin.templates.DB_DESC'),\n\t\t\t'visible' => $template == 'new_database_by_customer'\n\t\t],\n\t\t[\n\t\t\t'var' => 'DB_SRV',\n\t\t\t'description' => lng('admin.templates.DB_SRV'),\n\t\t\t'visible' => $template == 'new_database_by_customer'\n\t\t],\n\t\t[\n\t\t\t'var' => 'PMA_URI',\n\t\t\t'description' => lng('admin.templates.PMA_URI'),\n\t\t\t'visible' => $template == 'new_database_by_customer'\n\t\t],\n\t\t[\n\t\t\t'var' => 'USR_NAME',\n\t\t\t'description' => lng('admin.templates.USR_NAME'),\n\t\t\t'visible' => $template == 'new_ftpaccount_by_customer'\n\t\t],\n\t\t[\n\t\t\t'var' => 'USR_PASS',\n\t\t\t'description' => lng('admin.templates.USR_PASS'),\n\t\t\t'visible' => $template == 'new_ftpaccount_by_customer'\n\t\t],\n\t\t[\n\t\t\t'var' => 'USR_PATH',\n\t\t\t'description' => lng('admin.templates.USR_PATH'),\n\t\t\t'visible' => $template == 'new_ftpaccount_by_customer'\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/domains/formfield.domains_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'domain_add' => [\n\t\t'title' => lng('domains.subdomain_add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domains'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('domains.subdomain_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'subdomain' => [\n\t\t\t\t\t\t'label' => lng('domains.domainname'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'domain' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => '.',\n\t\t\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t\t\t'select_var' => $domains\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'alias' => [\n\t\t\t\t\t\t'label' => lng('domains.aliasdomain'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $aliasdomains\n\t\t\t\t\t],\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t\t\t'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescriptionSubdomain').(Settings::Get('system.documentroot_use_default_value') == 1 ? lng('panel.pathDescriptionEx') : '') : null),\n\t\t\t\t\t\t'type' => $pathSelect['type'],\n\t\t\t\t\t\t'select_var' => $pathSelect['select_var'] ?? '',\n\t\t\t\t\t\t'selected' => $pathSelect['value'],\n\t\t\t\t\t\t'value' => $pathSelect['value'],\n\t\t\t\t\t\t'note' => $pathSelect['note'] ?? '',\n\t\t\t\t\t],\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'visible' => Settings::Get('panel.pathedit') == 'Dropdown',\n\t\t\t\t\t\t'label' => lng('panel.urloverridespath'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'redirectcode' => [\n\t\t\t\t\t\t'visible' => Settings::Get('customredirect.enabled') == '1',\n\t\t\t\t\t\t'label' => lng('domains.redirectifpathisurl'),\n\t\t\t\t\t\t'desc' => lng('domains.redirectifpathisurlinfo'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => isset($redirectcode) ? $redirectcode : null\n\t\t\t\t\t],\n\t\t\t\t\t'selectserveralias' => [\n\t\t\t\t\t\t'label' => lng('admin.selectserveralias'),\n\t\t\t\t\t\t'desc' => lng('admin.selectserveralias_desc'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => lng('customer.selectserveralias_addinfo')\n\t\t\t\t\t],\n\t\t\t\t\t'openbasedir_path' => [\n\t\t\t\t\t\t'label' => lng('domain.openbasedirpath'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $openbasedir\n\t\t\t\t\t],\n\t\t\t\t\t'phpsettingid' => [\n\t\t\t\t\t\t'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && count($phpconfigs) > 0 && $userinfo['phpenabled'] == '1',\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.title'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $phpconfigs,\n\t\t\t\t\t\t'selected' => (int)Settings::Get('phpfpm.enabled') == 1 ? Settings::Get('phpfpm.defaultini') : Settings::Get('system.mod_fcgid_defaultini')\n\t\t\t\t\t],\n\t\t\t\t\t'speciallogfile' => [\n\t\t\t\t\t\t'label' => lng('admin.speciallogfile.title'),\n\t\t\t\t\t\t'desc' => lng('admin.speciallogfile.description'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t0 => lng('panel.no'),\n\t\t\t\t\t\t\t1 => lng('panel.yes'),\n\t\t\t\t\t\t\t2 => lng('domain.inherited')\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'selected' => 2\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_bssl' => [\n\t\t\t\t'title' => lng('admin.webserversettings_ssl'),\n\t\t\t\t'visible' => Settings::Get('system.use_ssl') == '1' && $ssl_ipsandports,\n\t\t\t\t'fields' => [\n\t\t\t\t\t'sslenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_sslenabled'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_redirect' => [\n\t\t\t\t\t\t'label' => lng('domains.ssl_redirect.title'),\n\t\t\t\t\t\t'desc' => lng('domains.ssl_redirect.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'letsencrypt' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.leenabled') == '1',\n\t\t\t\t\t\t'label' => lng('customer.letsencrypt.title'),\n\t\t\t\t\t\t'desc' => lng('customer.letsencrypt.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'http2' => [\n\t\t\t\t\t\t'visible' => $ssl_ipsandports && Settings::Get('system.http2_support') == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_http2.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_http2.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'http3' => [\n\t\t\t\t\t\t'visible' => $ssl_ipsandports && Settings::Get('system.webserver') == 'nginx' && Settings::Get('system.http3_support') == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_http3.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_http3.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_maxage' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_maxage.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_maxage.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 0,\n\t\t\t\t\t\t'max' => 94608000, // 3-years\n\t\t\t\t\t\t'value' => 0\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_sub' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_incsub.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_incsub.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_preload' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_preload.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_preload.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/domains/formfield.domains_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Domain\\Domain;\nuse Froxlor\\Settings;\n\nreturn [\n\t'domain_edit' => [\n\t\t'title' => lng('domains.subdomain_edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domains'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('domains.subdomain_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'domain' => [\n\t\t\t\t\t\t'label' => lng('domains.domainname'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['domain']\n\t\t\t\t\t],\n\t\t\t\t\t'dns' => [\n\t\t\t\t\t\t'label' => lng('dns.destinationip'),\n\t\t\t\t\t\t'type' => 'itemlist',\n\t\t\t\t\t\t'values' => $domainips\n\t\t\t\t\t],\n\t\t\t\t\t'alias' => [\n\t\t\t\t\t\t'visible' => $alias_check == '0' && (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => lng('domains.aliasdomain'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $domains,\n\t\t\t\t\t\t'selected' => $result['aliasdomain']\n\t\t\t\t\t],\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'visible' => (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t\t\t'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescriptionSubdomain').(Settings::Get('system.documentroot_use_default_value') == 1 ? lng('panel.pathDescriptionEx') : '') : null),\n\t\t\t\t\t\t'type' => $pathSelect['type'],\n\t\t\t\t\t\t'select_var' => $pathSelect['select_var'] ?? '',\n\t\t\t\t\t\t'selected' => $pathSelect['value'],\n\t\t\t\t\t\t'value' => $pathSelect['value'],\n\t\t\t\t\t\t'note' => $pathSelect['note'] ?? '',\n\t\t\t\t\t],\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'visible' => Settings::Get('panel.pathedit') == 'Dropdown' && (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => lng('panel.urloverridespath'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $urlvalue\n\t\t\t\t\t],\n\t\t\t\t\t'redirectcode' => [\n\t\t\t\t\t\t'visible' => Settings::Get('customredirect.enabled') == '1' && (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => lng('domains.redirectifpathisurl'),\n\t\t\t\t\t\t'desc' => lng('domains.redirectifpathisurlinfo'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $redirectcode,\n\t\t\t\t\t\t'selected' => $def_code\n\t\t\t\t\t],\n\t\t\t\t\t'selectserveralias' => [\n\t\t\t\t\t\t'visible' => (($result['parentdomainid'] == '0' && $userinfo['subdomains'] != '0') || $result['parentdomainid'] != '0') && (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => lng('admin.selectserveralias'),\n\t\t\t\t\t\t'desc' => lng('admin.selectserveralias_desc'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $serveraliasoptions,\n\t\t\t\t\t\t'selected' => $serveraliasoptions_selected\n\t\t\t\t\t],\n\t\t\t\t\t'isemaildomain' => [\n\t\t\t\t\t\t'visible' => (($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2') && $result['parentdomainid'] != '0') && (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => 'Emaildomain',\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['isemaildomain']\n\t\t\t\t\t],\n\t\t\t\t\t'openbasedir_path' => [\n\t\t\t\t\t\t'visible' => $result['openbasedir'] == '1' && (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => lng('domain.openbasedirpath'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $openbasedir,\n\t\t\t\t\t\t'selected' => $result['openbasedir_path']\n\t\t\t\t\t],\n\t\t\t\t\t'phpsettingid' => [\n\t\t\t\t\t\t'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && count($phpconfigs) > 0 && $userinfo['phpenabled'] == '1' && $result['phpenabled'] == '1' && (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => lng('admin.phpsettings.title'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $phpconfigs,\n\t\t\t\t\t\t'selected' => $result['phpsettingid']\n\t\t\t\t\t],\n\t\t\t\t\t'speciallogfile' => [\n\t\t\t\t\t\t'visible' =>  (int)$result['email_only'] == 0,\n\t\t\t\t\t\t'label' => lng('admin.speciallogfile.title'),\n\t\t\t\t\t\t'desc' => lng('admin.speciallogfile.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['speciallogfile']\n\t\t\t\t\t],\n\t\t\t\t\t'speciallogverified' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => '0'\n\t\t\t\t\t],\n\t\t\t\t\t'spf_entry' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.bind_enable') == '0' && Settings::Get('spf.use_spf') == '1' && $result['isemaildomain'] == '1'),\n\t\t\t\t\t\t'label' => lng('antispam.required_spf_dns'),\n\t\t\t\t\t\t'type' => 'longtext',\n\t\t\t\t\t\t'value' => (string)(new \\Froxlor\\Dns\\DnsEntry('@', 'TXT', \\Froxlor\\Dns\\Dns::encloseTXTContent(Settings::Get('spf.spf_entry'))))\n\t\t\t\t\t],\n\t\t\t\t\t'dmarc_entry' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.bind_enable') == '0' && Settings::Get('dmarc.use_dmarc') == '1' && $result['isemaildomain'] == '1'),\n\t\t\t\t\t\t'label' => lng('antispam.required_dmarc_dns'),\n\t\t\t\t\t\t'type' => 'longtext',\n\t\t\t\t\t\t'value' => (string)(new \\Froxlor\\Dns\\DnsEntry('_dmarc', 'TXT', \\Froxlor\\Dns\\Dns::encloseTXTContent(Settings::Get('dmarc.dmarc_entry'))))\n\t\t\t\t\t],\n\t\t\t\t\t'dkim_entry' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.bind_enable') == '0' && Settings::Get('antispam.activated') == '1' && $result['dkim'] == '1' && $result['dkim_pubkey'] != ''),\n\t\t\t\t\t\t'label' => lng('antispam.required_dkim_dns'),\n\t\t\t\t\t\t'type' => 'longtext',\n\t\t\t\t\t\t'value' => (string)(new \\Froxlor\\Dns\\DnsEntry('dkim' . $result['dkim_id'] . '._domainkey', 'TXT', '\"v=DKIM1; k=rsa; p='.trim($result['dkim_pubkey']).'\"'))\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'section_bssl' => [\n\t\t\t\t'title' => lng('admin.webserversettings_ssl'),\n\t\t\t\t'visible' => Settings::Get('system.use_ssl') == '1' && $ssl_ipsandports && Domain::domainHasSslIpPort($result['id']) && (int)$result['email_only'] == 0,\n\t\t\t\t'fields' => [\n\t\t\t\t\t'sslenabled' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_sslenabled'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ssl_enabled']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_redirect' => [\n\t\t\t\t\t\t'label' => lng('domains.ssl_redirect.title'),\n\t\t\t\t\t\t'desc' => lng('domains.ssl_redirect.description') . ($result['temporary_ssl_redirect'] > 1 ? lng('domains.ssl_redirect_temporarilydisabled') : ''),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['ssl_redirect']\n\t\t\t\t\t],\n\t\t\t\t\t'letsencrypt' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.leenabled') == '1',\n\t\t\t\t\t\t'label' => lng('customer.letsencrypt.title'),\n\t\t\t\t\t\t'desc' => lng('customer.letsencrypt.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['letsencrypt']\n\t\t\t\t\t],\n\t\t\t\t\t'http2' => [\n\t\t\t\t\t\t'visible' => $ssl_ipsandports && Settings::Get('system.http2_support') == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_http2.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_http2.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['http2']\n\t\t\t\t\t],\n\t\t\t\t\t'http3' => [\n\t\t\t\t\t\t'visible' => $ssl_ipsandports && Settings::Get('system.webserver') == 'nginx' && Settings::Get('system.http3_support') == '1',\n\t\t\t\t\t\t'label' => lng('admin.domain_http3.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_http3.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['http3']\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_maxage' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_maxage.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_maxage.description'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 0,\n\t\t\t\t\t\t'max' => 94608000, // 3-years\n\t\t\t\t\t\t'value' => $result['hsts']\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_sub' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_incsub.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_incsub.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['hsts_sub']\n\t\t\t\t\t],\n\t\t\t\t\t'hsts_preload' => [\n\t\t\t\t\t\t'label' => lng('admin.domain_hsts_preload.title'),\n\t\t\t\t\t\t'desc' => lng('admin.domain_hsts_preload.description'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['hsts_preload']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'buttons' => ((int)$result['email_only'] == 1) ? [] : null\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/domains/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/customer/email/formfield.emails_accountchangepasswd.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'emails_accountchangepasswd' => [\n\t\t'title' => lng('menue.main.changepassword'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('menue.main.changepassword'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'emailaddr' => [\n\t\t\t\t\t\t'label' => lng('emails.emailaddress'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['email_full']\n\t\t\t\t\t],\n\t\t\t\t\t'email_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'email_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/email/formfield.emails_accountchangequota.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'emails_accountchangequota' => [\n\t\t'title' => lng('emails.quota_edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('emails.quota_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'emailaddr' => [\n\t\t\t\t\t\t'label' => lng('emails.emailaddress'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['email_full']\n\t\t\t\t\t],\n\t\t\t\t\t'email_quota' => [\n\t\t\t\t\t\t'label' => lng('emails.quota') . ' (MiB)',\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['quota']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/email/formfield.emails_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\n$email_domainid ?: 0;\n\nreturn [\n\t'emails_add' => [\n\t\t'title' => lng('emails.emails_add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'email', 'page' => $email_domainid != 0 ? 'email_domain' : 'emails', 'domainid' => $email_domainid],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('emails.emails_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'email_part' => [\n\t\t\t\t\t\t'label' => lng('emails.emailaddress'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'domain' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => '@',\n\t\t\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t\t\t'select_var' => $domains,\n\t\t\t\t\t\t\t\t'selected' => $selected_domain\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'iscatchall' => [\n\t\t\t\t\t\t'label' => lng('emails.iscatchall'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/email/formfield.emails_addaccount.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'emails_addaccount' => [\n\t\t'title' => lng('emails.account_add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('emails.account_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'emailaddr' => [\n\t\t\t\t\t\t'label' => lng('emails.emailaddress'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['email_full']\n\t\t\t\t\t],\n\t\t\t\t\t'email_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'email_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'email_quota' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.mail_quota_enabled') == '1',\n\t\t\t\t\t\t'label' => lng('emails.quota'),\n\t\t\t\t\t\t'desc' => \"MiB\",\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $quota\n\t\t\t\t\t],\n\t\t\t\t\t'alternative_email' => [\n\t\t\t\t\t\t'visible' => Settings::Get('panel.sendalternativemail') == '1',\n\t\t\t\t\t\t'label' => lng('emails.alternative_emailaddress'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/email/formfield.emails_addforwarder.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'emails_addforwarder' => [\n\t\t'title' => lng('emails.forwarder_add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('emails.forwarder_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'emailaddr' => [\n\t\t\t\t\t\t'label' => lng('emails.from'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['email_full']\n\t\t\t\t\t],\n\t\t\t\t\t'destination' => [\n\t\t\t\t\t\t'label' => lng('emails.to'),\n\t\t\t\t\t\t'type' => 'email',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/email/formfield.emails_addsender.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'emails_addsender' => [\n\t\t'title' => lng('emails.sender_add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('emails.sender_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'emailaddr' => [\n\t\t\t\t\t\t'label' => lng('emails.account'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['email_full']\n\t\t\t\t\t],\n\t\t\t\t\t'allowed_sender' => [\n\t\t\t\t\t\t'label' => lng('emails.foreign_sender').'<span class=\"text-danger\">*</span>',\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'string_regex' => '/^[A-Za-z0-9._+-]+$/',\n\t\t\t\t\t\t'placeholder' => '(all)',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'allowed_domain' => (Settings::Get('mail.allow_external_domains') == '0' ?\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t'next_to_prefix' => '@',\n\t\t\t\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t\t\t\t'select_var' => $domains,\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'next_to_prefix' => '@',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'placeholder' => 'domain.tld',\n\t\t\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t\t\t])\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/email/formfield.emails_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'emails_edit' => [\n\t\t'title' => lng('emails.emails_edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'email', 'page' => 'email_domain', 'domainid' => $result['domainid']],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('emails.emails_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'email_full' => [\n\t\t\t\t\t\t'label' => lng('emails.emailaddress'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['email_full']\n\t\t\t\t\t],\n\t\t\t\t\t'account_yes' => [\n\t\t\t\t\t\t'visible' => (int)$result['popaccountid'] != 0,\n\t\t\t\t\t\t'label' => lng('emails.account'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => lng('panel.yes'),\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'edit_link' => [\n\t\t\t\t\t\t\t\t'type' => 'link',\n\t\t\t\t\t\t\t\t'href' => $filename . '?page=accounts&amp;domainid=' . $result['domainid'] . '&amp;action=changepw&amp;id=' . $result['id'],\n\t\t\t\t\t\t\t\t'label' => lng('menue.main.changepassword'),\n\t\t\t\t\t\t\t\t'classes' => 'btn btn-sm btn-secondary'\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t'del_link' => [\n\t\t\t\t\t\t\t\t'type' => 'link',\n\t\t\t\t\t\t\t\t'href' => $filename . '?page=accounts&amp;domainid=' . $result['domainid'] . '&amp;action=delete&amp;id=' . $result['id'],\n\t\t\t\t\t\t\t\t'label' => lng('emails.account_delete'),\n\t\t\t\t\t\t\t\t'classes' => 'btn btn-sm btn-danger'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'account_no' => [\n\t\t\t\t\t\t'visible' => (int)$result['popaccountid'] == 0,\n\t\t\t\t\t\t'label' => lng('emails.account'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => lng('panel.no'),\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'add_link' => [\n\t\t\t\t\t\t\t\t'type' => 'link',\n\t\t\t\t\t\t\t\t'href' => $filename . '?page=accounts&amp;domainid=' . $result['domainid'] . '&amp;action=add&amp;id=' . $result['id'],\n\t\t\t\t\t\t\t\t'label' => lng('emails.account_add'),\n\t\t\t\t\t\t\t\t'classes' => 'btn btn-sm btn-primary'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'mail_quota' => [\n\t\t\t\t\t\t'visible' => ((int)$result['popaccountid'] != 0 && Settings::Get('system.mail_quota_enabled')),\n\t\t\t\t\t\t'label' => lng('customer.email_quota'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['quota'] . ' MiB',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'add_link' => [\n\t\t\t\t\t\t\t\t'visible' => ((int)$result['popaccountid'] != 0 && Settings::Get('system.mail_quota_enabled')),\n\t\t\t\t\t\t\t\t'type' => 'link',\n\t\t\t\t\t\t\t\t'href' => $filename . '?page=accounts&amp;domainid=' . $result['domainid'] . '&amp;action=changequota&amp;id=' . $result['id'],\n\t\t\t\t\t\t\t\t'label' => lng('emails.quota_edit'),\n\t\t\t\t\t\t\t\t'classes' => 'btn btn-sm btn-secondary'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'iscatchall' => [\n\t\t\t\t\t\t'visible' => Settings::Get('catchall.catchall_enabled') == '1',\n\t\t\t\t\t\t'label' => lng('emails.catchall'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => (int)$result['iscatchall'],\n\t\t\t\t\t],\n\t\t\t\t\t'bypass_spam' => [\n\t\t\t\t\t\t'visible' => Settings::Get('antispam.activated') == '1' && (int)Settings::Get('antispam.default_bypass_spam') <= 2,\n\t\t\t\t\t\t'label' => lng('antispam.bypass_spam'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => (int)$result['bypass_spam'],\n\t\t\t\t\t],\n\t\t\t\t\t'spam_tag_level' => [\n\t\t\t\t\t\t'visible' => Settings::Get('antispam.activated') == '1',\n\t\t\t\t\t\t'label' => lng('antispam.spam_tag_level'),\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 0,\n\t\t\t\t\t\t'step' => 0.1,\n\t\t\t\t\t\t'value' => $result['spam_tag_level'],\n\t\t\t\t\t],\n\t\t\t\t\t'rewrite_subject' => [\n\t\t\t\t\t\t'visible' => Settings::Get('antispam.activated') == '1' && (int)Settings::Get('antispam.default_spam_rewrite_subject') <= 2,\n\t\t\t\t\t\t'label' => lng('antispam.rewrite_subject'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => (int)$result['rewrite_subject'],\n\t\t\t\t\t],\n\t\t\t\t\t'spam_kill_level' => [\n\t\t\t\t\t\t'visible' => Settings::Get('antispam.activated') == '1',\n\t\t\t\t\t\t'label' => lng('antispam.spam_kill_level'),\n\t\t\t\t\t\t'desc' => lng('panel.use_checkbox_to_disable'),\n\t\t\t\t\t\t'type' => 'textul',\n\t\t\t\t\t\t'step' => 0.1,\n\t\t\t\t\t\t'value' => $result['spam_kill_level']\n\t\t\t\t\t],\n\t\t\t\t\t'policy_greylist' => [\n\t\t\t\t\t\t'visible' => Settings::Get('antispam.activated') == '1' && (int)Settings::Get('antispam.default_policy_greylist') <= 2,\n\t\t\t\t\t\t'label' => lng('antispam.policy_greylist'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => (int)$result['policy_greylist'],\n\t\t\t\t\t],\n\t\t\t\t\t'mail_fwds' => [\n\t\t\t\t\t\t'label' => lng('emails.forwarders') . ' (' . $forwarders_count . ')',\n\t\t\t\t\t\t'type' => 'itemlist',\n\t\t\t\t\t\t'values' => $forwarders,\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'add_link' => [\n\t\t\t\t\t\t\t\t'type' => 'link',\n\t\t\t\t\t\t\t\t'href' => $filename . '?page=forwarders&amp;domainid=' . $result['domainid'] . '&amp;action=add&amp;id=' . $result['id'],\n\t\t\t\t\t\t\t\t'label' => lng('emails.forwarder_add'),\n\t\t\t\t\t\t\t\t'classes' => 'btn btn-sm btn-primary'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'mail_senders' => [\n\t\t\t\t\t\t'visible' => ((int)$result['popaccountid'] != 0 && Settings::Get('mail.enable_allow_sender') == '1'),\n\t\t\t\t\t\t'label' => lng('emails.senders') . ' (' . $senders_count . ')',\n\t\t\t\t\t\t'type' => 'itemlist',\n\t\t\t\t\t\t'values' => $senders,\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'add_link' => [\n\t\t\t\t\t\t\t\t'type' => 'link',\n\t\t\t\t\t\t\t\t'href' => $filename . '?page=senders&amp;domainid=' . $result['domainid'] . '&amp;action=add&amp;id=' . $result['id'],\n\t\t\t\t\t\t\t\t'label' => lng('emails.sender_add'),\n\t\t\t\t\t\t\t\t'classes' => 'btn btn-sm btn-primary'\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'buttons' => [\n\t\t\t[\n\t\t\t\t'label' => lng('panel.save')\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/email/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/customer/extras/formfield.export.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2016 the froxlor Team (see authors).\n *\n * For the full copyright and license information, please view the COPYING\n * file that was distributed with this source code. You can also view the\n * COPYING file online at https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  (c) the authors\n * @author         froxlor team <team@froxlor.org> (2010-)\n * @license        GPLv2 https://files.froxlor.org/misc/COPYING.txt\n * @package        Formfields\n *\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'export' => [\n\t\t'title' => lng('extras.export'),\n\t\t'image' => 'fa-solid fa-server',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('extras.export'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'label' => lng('panel.exportpath.title'),\n\t\t\t\t\t\t'desc' => lng('panel.exportpath.description') . '<br>' . (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescription') : null),\n\t\t\t\t\t\t'type' => $pathSelect['type'],\n\t\t\t\t\t\t'select_var' => $pathSelect['select_var'] ?? '',\n\t\t\t\t\t\t'selected' => $pathSelect['value'],\n\t\t\t\t\t\t'value' => $pathSelect['value'],\n\t\t\t\t\t\t'note' => $pathSelect['note'] ?? '',\n\t\t\t\t\t],\n\t\t\t\t\t'pgp_public_key' => [\n\t\t\t\t\t\t'label' => lng('panel.export_pgp_public_key.title'),\n\t\t\t\t\t\t'desc' => lng('panel.export_pgp_public_key.description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t],\n\t\t\t\t\t'path_protection_info' => [\n\t\t\t\t\t\t'label' => lng('extras.path_protection_label'),\n\t\t\t\t\t\t'type' => 'infotext',\n\t\t\t\t\t\t'value' => lng('extras.path_protection_info'),\n\t\t\t\t\t\t'classes' => 'fw-bold text-danger'\n\t\t\t\t\t],\n\t\t\t\t\t'dump_web' => [\n\t\t\t\t\t\t'label' => lng('extras.dump_web'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'dump_mail' => [\n\t\t\t\t\t\t'label' => lng('extras.dump_mail'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t\t'dump_dbs' => [\n\t\t\t\t\t\t'label' => lng('extras.dump_dbs'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/extras/formfield.htaccess_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'htaccess_add' => [\n\t\t'title' => lng('extras.pathoptions_add'),\n\t\t'image' => 'fa-solid fa-folder',\n\t\t'self_overview' => ['section' => 'extras', 'page' => 'htaccess'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('extras.pathoptions_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t\t\t'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescription') : null),\n\t\t\t\t\t\t'type' => $pathSelect['type'],\n\t\t\t\t\t\t'select_var' => $pathSelect['select_var'] ?? '',\n\t\t\t\t\t\t'selected' => $pathSelect['value'],\n\t\t\t\t\t\t'value' => $pathSelect['value'],\n\t\t\t\t\t\t'note' => $pathSelect['note'] ?? '',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'options_indexes' => [\n\t\t\t\t\t\t'label' => lng('extras.directory_browsing'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'error404path' => [\n\t\t\t\t\t\t'label' => lng('extras.errordocument404path'),\n\t\t\t\t\t\t'desc' => lng('panel.descriptionerrordocument'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'error403path' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.webserver') == 'apache2'),\n\t\t\t\t\t\t'label' => lng('extras.errordocument403path'),\n\t\t\t\t\t\t'desc' => lng('panel.descriptionerrordocument'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'error500path' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.webserver') == 'apache2'),\n\t\t\t\t\t\t'label' => lng('extras.errordocument500path'),\n\t\t\t\t\t\t'desc' => lng('panel.descriptionerrordocument'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'options_cgi' => [\n\t\t\t\t\t\t'visible' => ($cperlenabled == 1),\n\t\t\t\t\t\t'label' => lng('extras.execute_perl'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/extras/formfield.htaccess_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\n\nreturn [\n\t'htaccess_edit' => [\n\t\t'title' => lng('extras.pathoptions_edit'),\n\t\t'image' => 'fa-solid fa-folder',\n\t\t'self_overview' => ['section' => 'extras', 'page' => 'htaccess'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('extras.pathoptions_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['path']\n\t\t\t\t\t],\n\t\t\t\t\t'options_indexes' => [\n\t\t\t\t\t\t'label' => lng('extras.directory_browsing'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['options_indexes']\n\t\t\t\t\t],\n\t\t\t\t\t'error404path' => [\n\t\t\t\t\t\t'label' => lng('extras.errordocument404path'),\n\t\t\t\t\t\t'desc' => lng('panel.descriptionerrordocument'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['error404path']\n\t\t\t\t\t],\n\t\t\t\t\t'error403path' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.webserver') == 'apache2'),\n\t\t\t\t\t\t'label' => lng('extras.errordocument403path'),\n\t\t\t\t\t\t'desc' => lng('panel.descriptionerrordocument'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['error403path']\n\t\t\t\t\t],\n\t\t\t\t\t'error500path' => [\n\t\t\t\t\t\t'visible' => (Settings::Get('system.webserver') == 'apache2'),\n\t\t\t\t\t\t'label' => lng('extras.errordocument500path'),\n\t\t\t\t\t\t'desc' => lng('panel.descriptionerrordocument'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['error500path']\n\t\t\t\t\t],\n\t\t\t\t\t'options_cgi' => [\n\t\t\t\t\t\t'visible' => ($cperlenabled == 1),\n\t\t\t\t\t\t'label' => lng('extras.execute_perl'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['options_cgi']\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/extras/formfield.htpasswd_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'htpasswd_add' => [\n\t\t'title' => lng('extras.directoryprotection_add'),\n\t\t'image' => 'fa-solid fa-lock',\n\t\t'self_overview' => ['section' => 'extras', 'page' => 'htpasswds'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('extras.directoryprotection_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t\t\t'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescription') : null),\n\t\t\t\t\t\t'type' => $pathSelect['type'],\n\t\t\t\t\t\t'select_var' => $pathSelect['select_var'] ?? '',\n\t\t\t\t\t\t'selected' => $pathSelect['value'],\n\t\t\t\t\t\t'value' => $pathSelect['value'],\n\t\t\t\t\t\t'note' => $pathSelect['note'] ?? '',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'username' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'directory_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'directory_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'directory_authname' => [\n\t\t\t\t\t\t'label' => lng('extras.htpasswdauthname'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/extras/formfield.htpasswd_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'htpasswd_edit' => [\n\t\t'title' => lng('extras.directoryprotection_edit'),\n\t\t'image' => 'fa-solid fa-lock',\n\t\t'self_overview' => ['section' => 'extras', 'page' => 'htpasswds'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('extras.directoryprotection_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['path']\n\t\t\t\t\t],\n\t\t\t\t\t'username' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['username']\n\t\t\t\t\t],\n\t\t\t\t\t'directory_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'directory_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'directory_authname' => [\n\t\t\t\t\t\t'label' => lng('extras.htpasswdauthname'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['authname'],\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/extras/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/customer/ftp/formfield.ftp_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * For the full copyright and license information, please view the COPYING\n * file that was distributed with this source code. You can also view the\n * COPYING file online at https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  (c) the authors\n * @author         froxlor team <team@froxlor.org> (2010-)\n * @license        GPLv2 https://files.froxlor.org/misc/COPYING.txt\n * @package        Formfields\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'ftp_add' => [\n\t\t'title' => lng('ftp.account_add'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'ftp', 'page' => 'accounts'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('ftp.account_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'ftp_username' => [\n\t\t\t\t\t\t'visible' => Settings::Get('customer.ftpatdomain') == '1',\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'next_to' => (Settings::Get('customer.ftpatdomain') == '1' && count($domainlist) > 0 ? [\n\t\t\t\t\t\t\t'ftp_domain' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => '@',\n\t\t\t\t\t\t\t\t'label' => lng('domains.domainname'),\n\t\t\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t\t\t'select_var' => $domainlist\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t]\n\t\t\t\t\t\t\t: [])\n\t\t\t\t\t],\n\t\t\t\t\t'ftp_description' => [\n\t\t\t\t\t\t'label' => lng('panel.ftpdesc'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t\t\t'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescription') : null),\n\t\t\t\t\t\t'type' => $pathSelect['type'],\n\t\t\t\t\t\t'select_var' => $pathSelect['select_var'] ?? '',\n\t\t\t\t\t\t'selected' => $pathSelect['value'],\n\t\t\t\t\t\t'value' => $pathSelect['value'],\n\t\t\t\t\t\t'note' => $pathSelect['note'] ?? '',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ftp_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'ftp_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'sendinfomail' => [\n\t\t\t\t\t\t'label' => lng('customer.sendinfomail'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t],\n\t\t\t\t\t'shell' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.allow_customer_shell') == '1' && $user_shell_allowed,\n\t\t\t\t\t\t'label' => lng('panel.shell'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $shells,\n\t\t\t\t\t\t'selected' => '/bin/false'\n\t\t\t\t\t],\n\t\t\t\t\t'login_enabled' => [\n\t\t\t\t\t\t'label' => lng('panel.active'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => true\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/ftp/formfield.ftp_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * For the full copyright and license information, please view the COPYING\n * file that was distributed with this source code. You can also view the\n * COPYING file online at https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  (c) the authors\n * @author         froxlor team <team@froxlor.org> (2010-)\n * @license        GPLv2 https://files.froxlor.org/misc/COPYING.txt\n * @package        Formfields\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'ftp_edit' => [\n\t\t'title' => lng('ftp.account_edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'ftp', 'page' => 'accounts'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('ftp.account_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'username' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['username']\n\t\t\t\t\t],\n\t\t\t\t\t'ftp_description' => [\n\t\t\t\t\t\t'label' => lng('panel.ftpdesc'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['description']\n\t\t\t\t\t],\n\t\t\t\t\t'path' => [\n\t\t\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t\t\t'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescription') : null),\n\t\t\t\t\t\t'type' => $pathSelect['type'],\n\t\t\t\t\t\t'select_var' => $pathSelect['select_var'] ?? '',\n\t\t\t\t\t\t'selected' => $pathSelect['value'],\n\t\t\t\t\t\t'value' => $pathSelect['value'],\n\t\t\t\t\t\t'note' => $pathSelect['note'] ?? '',\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ftp_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'desc' => lng('ftp.editpassdescription'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'ftp_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'shell' => [\n\t\t\t\t\t\t'visible' => Settings::Get('system.allow_customer_shell') == '1' && $user_shell_allowed,\n\t\t\t\t\t\t'label' => lng('panel.shell'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $shells,\n\t\t\t\t\t\t'selected' => $result['shell'] ?? '/bin/false'\n\t\t\t\t\t],\n\t\t\t\t\t'login_enabled' => [\n\t\t\t\t\t\t'label' => lng('panel.active'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => $result['login_enabled'] == 'Y',\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/ftp/formfield.ftp_ssh_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * For the full copyright and license information, please view the COPYING\n * file that was distributed with this source code. You can also view the\n * COPYING file online at https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  (c) the authors\n * @author         froxlor team <team@froxlor.org> (2010-)\n * @license        GPLv2 https://files.froxlor.org/misc/COPYING.txt\n * @package        Formfields\n */\n\nreturn [\n\t'sshkey_add' => [\n\t\t'title' => lng('ftp.sshkey_add'),\n\t\t'image' => 'fa-solid fa-key',\n\t\t'self_overview' => ['section' => 'ftp', 'page' => 'sshkeys'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('ftp.sshkey_add'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('panel.sshkeydesc'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'ftpuser' => [\n\t\t\t\t\t\t'label' => lng('panel.ftpuser'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $userList,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ssh_pubkey' => [\n\t\t\t\t\t\t'label' => lng('panel.sshpubkey'),\n\t\t\t\t\t\t'placeholder' => lng('panel.sshpubkeyph'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 60,\n\t\t\t\t\t\t'rows' => 12,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/ftp/formfield.ftp_ssh_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * For the full copyright and license information, please view the COPYING\n * file that was distributed with this source code. You can also view the\n * COPYING file online at https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  (c) the authors\n * @author         froxlor team <team@froxlor.org> (2010-)\n * @license        GPLv2 https://files.froxlor.org/misc/COPYING.txt\n * @package        Formfields\n */\n\nreturn [\n\t'sshkey_edit' => [\n\t\t'title' => lng('ftp.sshkey_edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'ftp', 'page' => 'sshkeys'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('ftp.sshkey_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'username' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['username']\n\t\t\t\t\t],\n\t\t\t\t\t'fingerprint' => [\n\t\t\t\t\t\t'label' => lng('panel.sshfingerprint'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['fingerprint']\n\t\t\t\t\t],\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('panel.sshkeydesc'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['description']\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/ftp/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/customer/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/customer/mysql/formfield.mysql_add.php",
    "content": "<?php\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * For the full copyright and license information, please view the COPYING\n * file that was distributed with this source code. You can also view the\n * COPYING file online at https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  (c) the authors\n * @author         froxlor team <team@froxlor.org> (2010-)\n * @license        GPLv2 https://files.froxlor.org/misc/COPYING.txt\n * @package        Formfields\n */\n\nreturn [\n\t'mysql_add' => [\n\t\t'title' => lng('mysql.database_create'),\n\t\t'image' => 'fa-solid fa-plus',\n\t\t'self_overview' => ['section' => 'mysql', 'page' => 'mysqls'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('mysql.database_create'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'custom_suffix' => [\n\t\t\t\t\t\t'visible' => strtoupper(Settings::Get('customer.mysqlprefix')) == 'DBNAME',\n\t\t\t\t\t\t'label' => lng('mysql.databasename'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('mysql.databasedescription'),\n\t\t\t\t\t\t'type' => 'text'\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_server' => [\n\t\t\t\t\t\t'visible' => count($mysql_servers) > 1,\n\t\t\t\t\t\t'label' => lng('mysql.mysql_server'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => $mysql_servers\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'mysql_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\t'sendinfomail' => [\n\t\t\t\t\t\t'label' => lng('customer.sendinfomail'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => false\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/mysql/formfield.mysql_edit.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * For the full copyright and license information, please view the COPYING\n * file that was distributed with this source code. You can also view the\n * COPYING file online at https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  (c) the authors\n * @author         froxlor team <team@froxlor.org> (2010-)\n * @license        GPLv2 https://files.froxlor.org/misc/COPYING.txt\n * @package        Formfields\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\nreturn [\n\t'mysql_edit' => [\n\t\t'title' => lng('mysql.database_edit'),\n\t\t'image' => 'fa-solid fa-pen',\n\t\t'self_overview' => ['section' => 'mysql', 'page' => 'mysqls'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('mysql.database_edit'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'databasename' => [\n\t\t\t\t\t\t'label' => lng('mysql.databasename'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['databasename']\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_server' => [\n\t\t\t\t\t\t'visible' => count($mysql_servers) > 1,\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => $result['dbserver'] ?? 0,\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_server_info' => [\n\t\t\t\t\t\t'visible' => count($mysql_servers) > 1,\n\t\t\t\t\t\t'label' => lng('mysql.mysql_server'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'disabled' => true,\n\t\t\t\t\t\t'value' => $mysql_servers[$result['dbserver']] ?? 'unknown db server',\n\t\t\t\t\t],\n\t\t\t\t\t'description' => [\n\t\t\t\t\t\t'label' => lng('mysql.databasedescription'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['description']\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_password' => [\n\t\t\t\t\t\t'label' => lng('changepassword.new_password_ifnotempty'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'mysql_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/mysql/formfield.mysql_global_user.php",
    "content": "<?php\n\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Crypt;\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * For the full copyright and license information, please view the COPYING\n * file that was distributed with this source code. You can also view the\n * COPYING file online at https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  (c) the authors\n * @author         froxlor team <team@froxlor.org> (2010-)\n * @license        GPLv2 https://files.froxlor.org/misc/COPYING.txt\n * @package        Formfields\n */\n\nreturn [\n\t'mysql_global_user' => [\n\t\t'title' => lng('mysql.edit_global_user'),\n\t\t'self_overview' => ['section' => 'mysql', 'page' => 'mysqls'],\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => lng('mysql.edit_global_user'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'username' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'value' => $userinfo['loginname'],\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_password' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'autocomplete' => 'new-password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'next_to' => [\n\t\t\t\t\t\t\t'mysql_password_suggestion' => [\n\t\t\t\t\t\t\t\t'next_to_prefix' => lng('customer.generated_pwd') . ':',\n\t\t\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t\t\t'visible' => (Settings::Get('panel.password_regex') == ''),\n\t\t\t\t\t\t\t\t'value' => Crypt::generatePassword(),\n\t\t\t\t\t\t\t\t'readonly' => true\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/customer/mysql/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/formfield.api_key.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\nreturn [\n\t'apikey' => [\n\t\t'title' => lng('menue.main.apikeys'),\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'fields' => [\n\t\t\t\t\t'loginname' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result['loginname'] ?? $result['adminname']\n\t\t\t\t\t],\n\t\t\t\t\t'apikey' => [\n\t\t\t\t\t\t'label' => 'API key',\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'readonly' => true,\n\t\t\t\t\t\t'value' => $result['apikey']\n\t\t\t\t\t],\n\t\t\t\t\t'secret' => [\n\t\t\t\t\t\t'label' => 'Secret',\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'readonly' => true,\n\t\t\t\t\t\t'value' => $result['secret']\n\t\t\t\t\t],\n\t\t\t\t\t'allowed_from' => [\n\t\t\t\t\t\t'label' => [\n\t\t\t\t\t\t\t'title' => lng('apikeys.allowed_from'),\n\t\t\t\t\t\t\t'description' => lng('apikeys.allowed_from_help')\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $result['allowed_from'],\n\t\t\t\t\t],\n\t\t\t\t\t'valid_until' => [\n\t\t\t\t\t\t'label' => [\n\t\t\t\t\t\t\t'title' => lng('apikeys.valid_until'),\n\t\t\t\t\t\t\t'description' => lng('apikeys.valid_until_help')\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'type' => 'datetime-local',\n\t\t\t\t\t\t'value' => $result['valid_until'] < 0 ? \"\" : date('Y-m-d\\TH:i', $result['valid_until'])\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/formfield.dns_add.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'dns_add' => [\n\t\t'title' => 'DNS Editor',\n\t\t'image' => 'fa-solid fa-globe',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'fields' => [\n\t\t\t\t\t'dns_record' => [\n\t\t\t\t\t\t'label' => 'Record',\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $record,\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'dns_type' => [\n\t\t\t\t\t\t'label' => 'Type',\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'select_var' => [\n\t\t\t\t\t\t\t'A' => 'A',\n\t\t\t\t\t\t\t'AAAA' => 'AAAA',\n\t\t\t\t\t\t\t'CAA' => 'CAA',\n\t\t\t\t\t\t\t'CNAME' => 'CNAME',\n\t\t\t\t\t\t\t'DNAME' => 'DNAME',\n\t\t\t\t\t\t\t'LOC' => 'LOC',\n\t\t\t\t\t\t\t'MX' => 'MX',\n\t\t\t\t\t\t\t'NS' => 'NS',\n\t\t\t\t\t\t\t'RP' => 'RP',\n\t\t\t\t\t\t\t'SRV' => 'SRV',\n\t\t\t\t\t\t\t'SSHFP' => 'SSHFP',\n\t\t\t\t\t\t\t'TLSA' => 'TLSA',\n\t\t\t\t\t\t\t'TXT' => 'TXT'\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'selected' => $type\n\t\t\t\t\t],\n\t\t\t\t\t'dns_mxp' => [\n\t\t\t\t\t\t'label' => 'Priority',\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'value' => $prio\n\t\t\t\t\t],\n\t\t\t\t\t'dns_content' => [\n\t\t\t\t\t\t'label' => 'Content',\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => $content,\n\t\t\t\t\t\t'note' => lng('dnseditor.notes.A')\n\t\t\t\t\t],\n\t\t\t\t\t'dns_ttl' => [\n\t\t\t\t\t\t'label' => 'TTL',\n\t\t\t\t\t\t'type' => 'number',\n\t\t\t\t\t\t'min' => 30,\n\t\t\t\t\t\t'value' => $ttl\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/formfield.domain_ssleditor.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'domain_ssleditor' => [\n\t\t'title' => lng('panel.ssleditor'),\n\t\t'image' => 'fa-solid fa-lock',\n\t\t'sections' => [\n\t\t\t'section_a' => [\n\t\t\t\t'title' => 'SSL certificates',\n\t\t\t\t'fields' => [\n\t\t\t\t\t'domainname' => [\n\t\t\t\t\t\t'label' => lng('domains.domainname'),\n\t\t\t\t\t\t'type' => 'label',\n\t\t\t\t\t\t'value' => $result_domain['domain']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_cert_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_file_content'),\n\t\t\t\t\t\t'desc' => lng('admin.ipsandports.ssl_paste_description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 100,\n\t\t\t\t\t\t'rows' => 15,\n\t\t\t\t\t\t'value' => $result['ssl_cert_file'],\n\t\t\t\t\t\t'placeholder' => lng('domain.ssl_certificate_placeholder'),\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_key_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_key_file_content'),\n\t\t\t\t\t\t'desc' => lng('admin.ipsandports.ssl_paste_description'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 100,\n\t\t\t\t\t\t'rows' => 15,\n\t\t\t\t\t\t'value' => $result['ssl_key_file'],\n\t\t\t\t\t\t'placeholder' => lng('domain.ssl_key_placeholder'),\n\t\t\t\t\t\t'mandatory' => true\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_chainfile_content'),\n\t\t\t\t\t\t'desc' => lng('admin.ipsandports.ssl_paste_description') . lng('admin.ipsandports.ssl_cert_chainfile_content_desc'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 100,\n\t\t\t\t\t\t'rows' => 15,\n\t\t\t\t\t\t'value' => $result['ssl_cert_chainfile']\n\t\t\t\t\t],\n\t\t\t\t\t'ssl_ca_file' => [\n\t\t\t\t\t\t'label' => lng('admin.ipsandports.ssl_ca_file_content'),\n\t\t\t\t\t\t'desc' => lng('admin.ipsandports.ssl_paste_description') . lng('admin.ipsandports.ssl_ca_file_content_desc'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'cols' => 100,\n\t\t\t\t\t\t'rows' => 15,\n\t\t\t\t\t\t'value' => $result['ssl_ca_file']\n\t\t\t\t\t],\n\t\t\t\t\t'do_insert' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'visible' => empty($result['ssl_cert_file'])\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/index.html",
    "content": ""
  },
  {
    "path": "lib/formfields/install/formfield.install.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Froxlor;\n\n$httpuser = '';\n$httpgroup = '';\nif (extension_loaded('posix')) {\n\t$httpuser = posix_getpwuid(posix_getuid())['name'] ?? '';\n\t$httpgroup = posix_getgrgid(posix_getgid())['name'] ?? '';\n}\n\nreturn [\n\t'install' => [\n\t\t'title' => 'install',\n\t\t'sections' => [\n\t\t\t'step1' => [\n\t\t\t\t'title' => lng('install.database.title'),\n\t\t\t\t'description' => lng('install.database.description'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'mysql_host' => [\n\t\t\t\t\t\t'label' => lng('mysql.mysql_server'),\n\t\t\t\t\t\t'placeholder' => lng('mysql.mysql_server'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('mysql_host', 'localhost', 'installation')\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_ssl_ca_file' => [\n\t\t\t\t\t\t'label' => lng('mysql.mysql_ssl_ca_file'),\n\t\t\t\t\t\t'placeholder' => lng('mysql.mysql_ssl_ca_file'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => old('mysql_ssl_ca_file', null, 'installation'),\n\t\t\t\t\t\t'advanced' => true,\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_ssl_verify_server_certificate' => [\n\t\t\t\t\t\t'label' => lng('mysql.mysql_ssl_verify_server_certificate'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => old('mysql_ssl_verify_server_certificate', '0', 'installation'),\n\t\t\t\t\t\t'advanced' => true,\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_root_user' => [\n\t\t\t\t\t\t'label' => lng('mysql.privileged_user'),\n\t\t\t\t\t\t'placeholder' => lng('mysql.privileged_user'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('mysql_root_user', 'froxroot', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_root_pass' => [\n\t\t\t\t\t\t'label' => lng('mysql.privileged_passwd'),\n\t\t\t\t\t\t'placeholder' => lng('mysql.privileged_passwd'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('mysql_root_pass', null, 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_unprivileged_user' => [\n\t\t\t\t\t\t'label' => lng('install.database.user'),\n\t\t\t\t\t\t'placeholder' => lng('install.database.user'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('mysql_unprivileged_user', 'froxlor', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_unprivileged_pass' => [\n\t\t\t\t\t\t'label' => lng('mysql.unprivileged_passwd'),\n\t\t\t\t\t\t'placeholder' => lng('mysql.unprivileged_passwd'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('mysql_unprivileged_pass', null, 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_database' => [\n\t\t\t\t\t\t'label' => lng('install.database.dbname'),\n\t\t\t\t\t\t'placeholder' => lng('install.database.dbname'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('mysql_database', 'froxlor', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'mysql_force_create' => [\n\t\t\t\t\t\t'label' => lng('install.database.force_create'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => old('mysql_force_create', '0', 'installation')\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'step2' => [\n\t\t\t\t'title' => lng('install.admin.title'),\n\t\t\t\t'description' => lng('install.admin.description'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'admin_name' => [\n\t\t\t\t\t\t'label' => lng('customer.name'),\n\t\t\t\t\t\t'placeholder' => lng('customer.name'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('admin_name', 'Administrator', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'admin_user' => [\n\t\t\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t\t\t'placeholder' => lng('login.username'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('admin_user', 'admin', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'admin_pass' => [\n\t\t\t\t\t\t'label' => lng('login.password'),\n\t\t\t\t\t\t'placeholder' => lng('login.password'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('admin_pass', null, 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'admin_pass_confirm' => [\n\t\t\t\t\t\t'label' => lng('changepassword.new_password_confirm'),\n\t\t\t\t\t\t'placeholder' => lng('changepassword.new_password_confirm'),\n\t\t\t\t\t\t'type' => 'password',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('admin_pass_confirm', null, 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'admin_email' => [\n\t\t\t\t\t\t'label' => lng('customer.email'),\n\t\t\t\t\t\t'placeholder' => lng('customer.email'),\n\t\t\t\t\t\t'type' => 'email',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('admin_email', null, 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'use_admin_email_as_sender' => [\n\t\t\t\t\t\t'label' => lng('install.admin.use_admin_email_as_sender'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => old('use_admin_email_as_sender', '1', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'sender_email' => [\n\t\t\t\t\t\t'label' => lng('serversettings.adminmail.title'),\n\t\t\t\t\t\t'placeholder' => lng('install.admin.use_autogenerated_email_as_sender'),\n\t\t\t\t\t\t'type' => 'email',\n\t\t\t\t\t\t'value' => old('sender_email', null, 'installation'),\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'step3' => [\n\t\t\t\t'title' => lng('install.system.title'),\n\t\t\t\t'description' => lng('install.system.description'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'distribution' => [\n\t\t\t\t\t\t'label' => lng('admin.configfiles.distribution'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'select_var' => $supportedOS,\n\t\t\t\t\t\t'selected' => $guessedDistribution\n\t\t\t\t\t],\n\t\t\t\t\t'serveripv4' => [\n\t\t\t\t\t\t'label' => lng('install.system.ipv4'),\n\t\t\t\t\t\t'placeholder' => lng('install.system.ipv4'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => old('serveripv4', filter_var($_SERVER['SERVER_ADDR'] ?? \"\", FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? ($_SERVER['SERVER_ADDR'] ?? \"\") : \"\", 'installation'),\n\n\t\t\t\t\t],\n\t\t\t\t\t'serveripv6' => [\n\t\t\t\t\t\t'label' => lng('install.system.ipv6'),\n\t\t\t\t\t\t'placeholder' => lng('install.system.ipv6'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'value' => old('serveripv6', filter_var($_SERVER['SERVER_ADDR'] ?? \"\", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? ($_SERVER['SERVER_ADDR'] ?? \"\") : \"\", 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'servername' => [\n\t\t\t\t\t\t'label' => lng('install.system.servername'),\n\t\t\t\t\t\t'placeholder' => lng('install.system.servername'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('servername', filter_var($_SERVER['SERVER_NAME'] ?? \"\", FILTER_VALIDATE_IP) ? null : $_SERVER['SERVER_NAME'], 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'use_ssl' => [\n\t\t\t\t\t\t'label' => lng('serversettings.ssl.use_ssl.title'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => old('use_ssl', '1', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'webserver' => [\n\t\t\t\t\t\t'label' => lng('admin.webserver'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'select_var' => ['apache24' => 'Apache 2.4', 'nginx' => 'Nginx'],\n\t\t\t\t\t\t'selected' => old('webserver', $guessedWebserver, 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'webserver_backend' => [\n\t\t\t\t\t\t'label' => lng('install.system.phpbackend'),\n\t\t\t\t\t\t'type' => 'select',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'select_var' => $webserverBackend,\n\t\t\t\t\t\t'selected' => old('webserver_backend', 'php-fpm', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'httpuser' => [\n\t\t\t\t\t\t'label' => lng('admin.webserver_user'),\n\t\t\t\t\t\t'placeholder' => lng('admin.webserver_user'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('httpuser', $httpuser, 'installation'),\n\t\t\t\t\t\t'advanced' => true,\n\t\t\t\t\t],\n\t\t\t\t\t'httpgroup' => [\n\t\t\t\t\t\t'label' => lng('admin.webserver_group'),\n\t\t\t\t\t\t'placeholder' => lng('admin.webserver_group'),\n\t\t\t\t\t\t'type' => 'text',\n\t\t\t\t\t\t'mandatory' => true,\n\t\t\t\t\t\t'value' => old('httpgroup', $httpgroup, 'installation'),\n\t\t\t\t\t\t'advanced' => true,\n\t\t\t\t\t],\n\t\t\t\t\t'activate_newsfeed' => [\n\t\t\t\t\t\t'label' => lng('install.system.activate_newsfeed'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => old('activate_newsfeed', '0', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t],\n\t\t\t'step4' => [\n\t\t\t\t'title' => lng('install.install.title'),\n\t\t\t\t'description' => lng('install.install.description'),\n\t\t\t\t'fields' => [\n\t\t\t\t\t'system' => [\n\t\t\t\t\t\t'label' => lng('install.install.runcmd'),\n\t\t\t\t\t\t'type' => 'textarea',\n\t\t\t\t\t\t'value' => (!empty($_SESSION['installation']['ud_str']) ? Froxlor::getInstallDir() . \"bin/froxlor-cli froxlor:install -c '\" . $_SESSION['installation']['ud_str'] . \"'\\n\" : \"\") .\n\t\t\t\t\t\t\t(!empty($_SESSION['installation']['json_params']) ? Froxlor::getInstallDir() . \"bin/froxlor-cli froxlor:config-services -a '\" . $_SESSION['installation']['json_params'] . \"' --yes-to-all\" : \"something went wrong...\"),\n\t\t\t\t\t\t'readonly' => true,\n\t\t\t\t\t\t'rows' => 10,\n\t\t\t\t\t\t'style' => 'min-height:16rem;'\n\t\t\t\t\t],\n\t\t\t\t\t'manual_config' => [\n\t\t\t\t\t\t'label' => lng('install.install.manual_config'),\n\t\t\t\t\t\t'type' => 'checkbox',\n\t\t\t\t\t\t'value' => '1',\n\t\t\t\t\t\t'checked' => old('manual_config', '0', 'installation'),\n\t\t\t\t\t],\n\t\t\t\t\t'target_servername' => [\n\t\t\t\t\t\t'type' => 'hidden',\n\t\t\t\t\t\t'value' => $_SESSION['installation']['servername'] ?? \"\",\n\t\t\t\t\t],\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/formfields/install/index.html",
    "content": ""
  },
  {
    "path": "lib/functions.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Language;\nuse Froxlor\\UI\\Request;\n\n/**\n * Render a template with the given data.\n * Mostly used if we have no template-engine (twig).\n *\n * @param $template\n * @param $attributes\n * @return array|false|string|string[]\n */\nfunction view($template, $attributes)\n{\n\t$view = file_get_contents(dirname(__DIR__) . '/templates/' . $template);\n\n\treturn str_replace(array_keys($attributes), array_values($attributes), $view);\n}\n\n/**\n * Get the current translation for a given string.\n *\n * @param string $identifier\n * @param array $arguments\n * @return array|string\n */\nfunction lng(string $identifier, array $arguments = [])\n{\n\treturn Language::getTranslation($identifier, $arguments);\n}\n\n/**\n * Get the value of a request variable.\n *\n * @param string $identifier\n * @param string|null $default\n * @param string|null $session\n * @return mixed|string|null\n */\nfunction old(string $identifier, ?string $default, ?string $session = null)\n{\n\tif ($session && isset($_SESSION[$session])) {\n\t\treturn $_SESSION[$session][$identifier] ?? $default;\n\t}\n\treturn Request::any($identifier, $default);\n}\n\n/**\n * Loading the mix manifest file from given theme.\n * This file contains the hashed filenames of the assets.\n * It must be always placed in the theme assets folder.\n *\n * @deprecated since 2.1.x no longer in use\n * @param $filename\n * @return mixed|string\n */\nfunction mix($filename)\n{\n\tif (preg_match('/templates\\/(.+)\\/assets\\/(.+)\\/(.+)/', $filename, $matches)) {\n\t\t$mixManifest = dirname(__DIR__) . '/templates/' . $matches[1] . '/assets/mix-manifest.json';\n\t\tif (file_exists($mixManifest)) {\n\t\t\t$manifest = json_decode(file_get_contents($mixManifest), true);\n\t\t\t$key = '/' . $matches[2] . '/' . $matches[3];\n\t\t\tif ($manifest && !empty($manifest[$key])) {\n\t\t\t\t$filename = 'templates/' . $matches[1] . '/assets' . $manifest[$key];\n\t\t\t}\n\t\t}\n\t}\n\treturn $filename;\n}\n\n/**\n * Loading the vite manifest file from given theme.\n * This file contains the hashed filenames of the assets.\n * It must be always placed in the theme assets folder.\n *\n * @param string|null $basehref\n * @param array $filenames\n * @return string\n * @throws Exception\n */\nfunction vite($basehref, array $filenames): string\n{\n\t// Get the hashed filenames from the manifest file\n\t$links = [];\n\tforeach ($filenames as $filename) {\n\t\tif (preg_match(\"/templates\\/([^\\/]+)(.*)/\", $filename, $matches)) {\n\t\t\t$assetDirectory = '/templates/' . $matches[1] . '/build/';\n\t\t\t$viteManifest = dirname(__DIR__) . $assetDirectory . '/manifest.json';\n\t\t\t$manifest = json_decode(file_get_contents($viteManifest), true);\n\t\t\tif (!empty($manifest[$filename]['file'])) {\n\t\t\t\t$links[] = $basehref . ltrim($assetDirectory, '/') . $manifest[$filename]['file'];\n\t\t\t} else {\n\t\t\t\t// additional asset from config.json that was not prebuilt on release (e.g. custom.css)\n\t\t\t\t$links[] = $filename;\n\t\t\t}\n\t\t} else {\n\t\t\t$links[] = $filename;\n\t\t}\n\t}\n\n\t// Update the links to the correct html tags\n\tforeach ($links as $key => $link) {\n\t\tswitch (pathinfo($link, PATHINFO_EXTENSION)) {\n\t\t\tcase 'css':\n\t\t\t\t$links[$key] = '<link rel=\"stylesheet\" href=\"'. $link . '\">';\n\t\t\t\tbreak;\n\t\t\tcase 'js':\n\t\t\t\t$links[$key] = '<script src=\"' . $link . '\" type=\"module\"></script>';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new Exception('Unknown file extension for file '. $link .' from manifest.json');\n\t\t}\n\t}\n\n\treturn implode(\"\\n\", $links);\n}\n"
  },
  {
    "path": "lib/index.html",
    "content": ""
  },
  {
    "path": "lib/init.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\n// define default theme for configurehint, etc.\n$_deftheme = 'Froxlor';\n\nrequire dirname(__DIR__) . '/lib/functions.php';\n\n// validate correct php version\nif (version_compare(\"7.4.0\", PHP_VERSION, \">=\")) {\n\tdie(view($_deftheme . '/misc/phprequirementfailed.html.twig', [\n\t\t'{{ basehref }}' => '',\n\t\t'{{ froxlor_min_version }}' => '7.4.0',\n\t\t'{{ current_version }}' => PHP_VERSION,\n\t\t'{{ current_year }}' => date('Y', time()),\n\t]));\n}\n\n// validate vendor autoloader\nif (!file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {\n\tdie(view($_deftheme . '/misc/vendormissinghint.html.twig', [\n\t\t'{{ basehref }}' => '',\n\t\t'{{ froxlor_install_dir }}' => dirname(__DIR__),\n\t\t'{{ current_year }}' => date('Y', time()),\n\t]));\n}\n\nrequire dirname(__DIR__) . '/vendor/autoload.php';\n\nuse Froxlor\\CurrentUser;\nuse Froxlor\\FileDir;\nuse Froxlor\\Froxlor;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\Http\\RateLimiter;\nuse Froxlor\\Idna\\IdnaWrapper;\nuse Froxlor\\Install\\Update;\nuse Froxlor\\Language;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\Settings;\nuse Froxlor\\System\\Mailer;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Linker;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\nuse Froxlor\\Install\\Requirements;\n\n// include MySQL-tabledefinitions\nrequire Froxlor::getInstallDir() . '/lib/tables.inc.php';\n\nUI::sendHeaders();\nUI::initTwig();\n\n/**\n * Register Globals Security Fix\n */\nRequest::cleanAll();\n\nunset($_);\nunset($key);\n\n$filename = htmlentities(basename($_SERVER['SCRIPT_NAME']));\n\n// check whether the userdata file exists\nif (!file_exists(Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {\n\tUI::twig()->addGlobal('install_mode', '1');\n\techo UI::twig()->render($_deftheme . '/misc/configurehint.html.twig');\n\tdie();\n}\n\n// check whether we can read the userdata file\nif (!is_readable(Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {\n\t// get possible owner\n\t$posixusername = posix_getpwuid(posix_getuid());\n\t$posixgroup = posix_getgrgid(posix_getgid());\n\tUI::twig()->addGlobal('install_mode', '1');\n\techo UI::twig()->render($_deftheme . '/misc/ownershiphint.html.twig', [\n\t\t'user' => $posixusername['name'],\n\t\t'group' => $posixgroup['name'],\n\t\t'installdir' => Froxlor::getInstallDir()\n\t]);\n\tdie();\n}\n\n// include MySQL-Username/Passwort etc.\nrequire Froxlor::getInstallDir() . '/lib/userdata.inc.php';\nif (!isset($sql) || !is_array($sql)) {\n\tUI::twig()->addGlobal('install_mode', '1');\n\techo UI::twig()->render($_deftheme . '/misc/configurehint.html.twig');\n\tdie();\n}\n\n/**\n * Show nice note if requested domain is \"unknown\" to froxlor and thus is being lead to its vhost\n */\n$req_host = UI::getCookieHost();\nif ($req_host != Settings::Get('system.hostname') &&\n\t    Settings::Get('panel.is_configured') == 1 &&\n\t\t!filter_var($req_host, FILTER_VALIDATE_IP) && (\n\t\tempty(Settings::Get('system.froxloraliases')) ||\n\t\t(!empty(Settings::Get('system.froxloraliases')) && !in_array($req_host, array_map('trim', explode(',', Settings::Get('system.froxloraliases')))))\n)) {\n\t// not the froxlor system-hostname, show info page for domains not configured in froxlor\n\t$redirect_file = FileDir::getUnknownDomainTemplate($req_host ?? \"non-detectable http-host\");\n\theader('Location: '.$redirect_file);\n\tdie();\n}\n\n// validate php-extensions requirements\n$loadedExtensions = get_loaded_extensions();\n$missingExtensions = [];\nforeach (Requirements::REQUIRED_EXTENSIONS as $requiredExtension) {\n\tif (in_array($requiredExtension, $loadedExtensions)) {\n\t\tcontinue;\n\t}\n\t$missingExtensions[] = $requiredExtension;\n}\nif (!empty($missingExtensions)) {\n\tUI::twig()->addGlobal('install_mode', '1');\n\techo UI::twig()->render($_deftheme . '/misc/missingextensionhint.html.twig', [\n\t\t'phpversion' => phpversion(),\n\t\t'missing_extensions' => implode(\", \", $missingExtensions),\n\t]);\n\tdie();\n}\n\n// set error-handler\n@set_error_handler([\n\t'\\\\Froxlor\\\\PhpHelper',\n\t'phpErrHandler'\n]);\n@set_exception_handler([\n\t'\\\\Froxlor\\\\PhpHelper',\n\t'phpExceptionHandler'\n]);\n\n// send ssl-related headers (later than the others because we need a working database-connection and installation)\nUI::sendSslHeaders();\nRateLimiter::run();\n\n// create a new idna converter\n$idna_convert = new IdnaWrapper();\n\n// re-read user data if logged in\nif (CurrentUser::hasSession()) {\n\tCurrentUser::reReadUserData();\n}\n\n/**\n * Language management\n */\n\n// set default language before anything else to\n// ensure that we can display messages\nLanguage::setLanguage(Settings::Get('panel.standardlanguage'));\n\n// set language by given user\nif (CurrentUser::hasSession()) {\n\tif (!empty(CurrentUser::getField('language')) && isset(Language::getLanguages()[CurrentUser::getField('language')])) {\n\t\tLanguage::setLanguage(CurrentUser::getField('language'));\n\t} else {\n\t\tLanguage::setLanguage(CurrentUser::getField('def_language'));\n\t}\n}\n\n// Initialize our link - class\n$linker = new Linker('index.php');\nUI::setLinker($linker);\n\n/**\n * Global Theme-variable\n */\nif (Update::versionInUpdate(Settings::Get('panel.version'), '2.0.0-beta1')) {\n\t$theme = $_deftheme;\n} else {\n\t$theme = (Settings::Get('panel.default_theme') !== null) ? Settings::Get('panel.default_theme') : $_deftheme;\n\t// Overwrite with customer/admin theme if defined\n\tif (CurrentUser::hasSession() && CurrentUser::getField('theme') != $theme) {\n\t\t$theme = CurrentUser::getField('theme');\n\t}\n}\n\n// Check if a different variant of the theme is used\n$themevariant = \"default\";\nif (preg_match(\"/([a-z0-9.\\-]+)_([a-z0-9.\\-]+)/i\", $theme, $matches)) {\n\t$theme = $matches[1];\n\t$themevariant = $matches[2];\n}\n\n// check for existence of the theme\nif (@file_exists('templates/' . $theme . '/config.json')) {\n\t$_themeoptions = json_decode(file_get_contents('templates/' . $theme . '/config.json'), true);\n} else {\n\t$_themeoptions = null;\n}\n\n// check for existence of variant in theme\nif (is_array($_themeoptions) && (!array_key_exists('variants', $_themeoptions) || !array_key_exists(\n\t\t\t$themevariant,\n\t\t\t$_themeoptions['variants']\n\t\t))) {\n\t$themevariant = \"default\";\n}\n\nif (array_key_exists('global', $_themeoptions)) {\n\t$_themeoptions['variants'][$themevariant] = PhpHelper::array_merge_recursive_distinct($_themeoptions['global'], $_themeoptions['variants'][$themevariant]);\n}\n\n// check for custom header-graphic\n$hl_path = 'templates/' . $theme . '/assets/img';\n\n// default is theme-image\n$header_logo = $hl_path . '/' . ($_themeoptions['variants'][$themevariant]['img']['ui'] ?? 'logo_white.png');\n$header_logo_login = $hl_path . '/' . ($_themeoptions['variants'][$themevariant]['img']['login'] ?? 'logo_white.png');\n\nif (Settings::Get('panel.logo_overridetheme') == 1 || Settings::Get('panel.logo_overridecustom') == 1) {\n\t// logo settings shall overwrite theme logo and possible custom logo\n\t$header_logo = Settings::Get('panel.logo_image_header') ?: $header_logo;\n\t$header_logo_login = Settings::Get('panel.logo_image_login') ?: $header_logo_login;\n}\nif (Settings::Get('panel.logo_overridecustom') == 0 && file_exists($hl_path . '/logo_custom.png')) {\n\t// custom theme image (logo_custom.png) is not being overwritten by logo_image_* setting\n\t$header_logo = $hl_path . '/logo_custom.png';\n\t$header_logo_login = $hl_path . '/logo_custom.png';\n\tif (file_exists($hl_path . '/logo_custom_login.png')) {\n\t\t$header_logo_login = $hl_path . '/logo_custom_login.png';\n\t}\n}\n\n$color_scheme = $_themeoptions['variants'][$themevariant]['color-scheme'] ?? 'auto';\n\nUI::twig()->addGlobal('header_logo_login', $header_logo_login);\nUI::twig()->addGlobal('header_logo', $header_logo);\nUI::twig()->addGlobal('color_scheme', $color_scheme);\n\n/**\n * Redirects to index.php (login page) if no session exists\n */\nif (!CurrentUser::hasSession() && AREA != 'login') {\n\tunset($_SESSION['userinfo']);\n\tCurrentUser::setData();\n\t$_SESSION = [\n\t\t\"lastscript\" => basename($_SERVER[\"SCRIPT_NAME\"]),\n\t\t\"lastqrystr\" => $_SERVER[\"QUERY_STRING\"]\n\t];\n\tResponse::redirectTo('index.php');\n\texit();\n}\n\n$userinfo = CurrentUser::getData();\nUI::twig()->addGlobal('userinfo', $userinfo);\nUI::setCurrentUser($userinfo);\n// Initialize logger\nif (CurrentUser::hasSession()) {\n\t// Initialize logging\n\t$log = FroxlorLogger::getInstanceOf($userinfo);\n\tif ((CurrentUser::isAdmin() && AREA != 'admin') || (!CurrentUser::isAdmin() && AREA != 'customer')) {\n\t\t// user tries to access an area not meant for him -> redirect to corresponding index\n\t\tResponse::redirectTo((CurrentUser::isAdmin() ? 'admin' : 'customer') . '_index.php');\n\t\texit();\n\t}\n}\n\n/**\n * Fills variables for navigation, header and footer\n */\n$navigation = [];\nif (AREA == 'admin' || AREA == 'customer') {\n\tif (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {\n\t\t/*\n\t\t * if froxlor-files have been updated\n\t\t * but not yet configured by the admin\n\t\t * we only show logout and the update-page\n\t\t */\n\t\t$navigation_data = [\n\t\t\t'admin' => [\n\t\t\t\t'server' => [\n\t\t\t\t\t'label' => lng('admin.server'),\n\t\t\t\t\t'required_resources' => 'change_serversettings',\n\t\t\t\t\t'elements' => [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'url' => 'admin_updates.php?page=overview',\n\t\t\t\t\t\t\t'label' => lng('update.update'),\n\t\t\t\t\t\t\t'required_resources' => 'change_serversettings'\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\t\t$navigation = HTML::buildNavigation($navigation_data['admin'], CurrentUser::getData());\n\t} else {\n\t\t$navigation_data = PhpHelper::loadConfigArrayDir('lib/navigation/');\n\t\t$navigation = HTML::buildNavigation($navigation_data[AREA], CurrentUser::getData());\n\t}\n}\nUI::twig()->addGlobal('nav_entries', $navigation);\n\n$theme_assets = [];\nforeach (['css', 'js'] as $asset) {\n\tif (is_array($_themeoptions) && array_key_exists($asset, $_themeoptions['variants'][$themevariant])) {\n\t\tif (is_array($_themeoptions['variants'][$themevariant][$asset])) {\n\t\t\tforeach ($_themeoptions['variants'][$themevariant][$asset] as $assetfile) {\n\t\t\t\tif (file_exists('templates/' . $theme . '/' . $assetfile)) {\n\t\t\t\t\t$theme_assets[] .= 'templates/' . $theme . '/' . $assetfile;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nUI::twig()->addGlobal('theme_assets', $theme_assets);\nunset($theme_assets);\n\n$action = Request::any('action');\n$page = Request::any('page', 'overview');\n$gSearchText = Request::any('searchtext');\n\n// clear request data\nif (!$action && isset($_SESSION)) {\n\tunset($_SESSION['requestData']);\n}\n\nUI::twig()->addGlobal('action', $action);\nUI::twig()->addGlobal('page', $page);\nUI::twig()->addGlobal('area', AREA);\nUI::twig()->addGlobal('gSearchText', $gSearchText);\n\n// Initialize the mailingsystem\n$mail = new Mailer(true);\n\n// initialize csrf\nif (CurrentUser::hasSession()) {\n\t// create new csrf token if not set\n\tif (!$csrf_token = CurrentUser::getField('csrf_token')) {\n\t\t$csrf_token = Froxlor::genSessionId(20);\n\t\tCurrentUser::setField('csrf_token', $csrf_token);\n\t}\n\t// set csrf token for twig\n\tUI::twig()->addGlobal('csrf_token', $csrf_token);\n\t// check if csrf token is valid\n\tif (in_array($_SERVER['REQUEST_METHOD'], ['POST', 'PUT', 'PATCH', 'DELETE'])) {\n\t\t$current_token = Request::post('csrf_token', $_SERVER['HTTP_X_CSRF_TOKEN'] ?? null);\n\t\tif ($current_token != CurrentUser::getField('csrf_token')) {\n\t\t\thttp_response_code(403);\n\t\t\tResponse::dynamicError('CSRF validation failed');\n\t\t}\n\t}\n\t// update cookie lifetime\n\t$cookie_params = [\n\t\t'expires' => time() + min(Settings::Get('session.sessiontimeout'), 31536000),\n\t\t'path' => '/',\n\t\t'domain' => UI::getCookieHost(),\n\t\t'secure' => UI::requestIsHttps(),\n\t\t'httponly' => true,\n\t\t'samesite' => 'Lax'\n\t];\n\tsetcookie(session_name(), $_COOKIE[session_name()], $cookie_params);\n} else {\n\tUI::twig()->addGlobal('csrf_token', Froxlor::genSessionId(20));\n}\n"
  },
  {
    "path": "lib/navigation/00.froxlor.main.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\CurrentUser;\n\nreturn [\n\t'customer' => [\n\t\t'email' => [\n\t\t\t'url' => 'customer_email.php',\n\t\t\t'label' => lng('menue.email.email'),\n\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'email')),\n\t\t\t'icon' => 'fa-solid fa-envelope',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_email.php?page=emails',\n\t\t\t\t\t'label' => lng('menue.email.emails'),\n\t\t\t\t\t'required_resources' => 'emails',\n\t\t\t\t\t'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('emails') ? 'customer_email.php?page=email_domain&action=add' : null,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => Settings::Get('panel.webmail_url'),\n\t\t\t\t\t'new_window' => true,\n\t\t\t\t\t'label' => lng('menue.email.webmail'),\n\t\t\t\t\t'required_resources' => 'emails_used',\n\t\t\t\t\t'show_element' => (Settings::Get('panel.webmail_url') != ''),\n\t\t\t\t\t'is_external' => true,\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'mysql' => [\n\t\t\t'url' => 'customer_mysql.php',\n\t\t\t'label' => lng('menue.mysql.mysql'),\n\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'mysql')),\n\t\t\t'icon' => 'fa-solid fa-database',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_mysql.php?page=mysqls',\n\t\t\t\t\t'label' => lng('menue.mysql.databases'),\n\t\t\t\t\t'required_resources' => 'mysqls',\n\t\t\t\t\t'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('mysqls')? 'customer_mysql.php?page=mysqls&action=add' : null,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => Settings::Get('panel.phpmyadmin_url'),\n\t\t\t\t\t'new_window' => true,\n\t\t\t\t\t'label' => lng('menue.mysql.phpmyadmin'),\n\t\t\t\t\t'required_resources' => 'mysqls_used',\n\t\t\t\t\t'show_element' => (Settings::Get('panel.phpmyadmin_url') != ''),\n\t\t\t\t\t'is_external' => true,\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'domains' => [\n\t\t\t'url' => 'customer_domains.php',\n\t\t\t'label' => lng('menue.domains.domains'),\n\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'domains')),\n\t\t\t'icon' => 'fa-solid fa-globe',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_domains.php?page=domains',\n\t\t\t\t\t'label' => lng('menue.domains.settings'),\n\t\t\t\t\t'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('subdomains') ? 'customer_domains.php?page=domains&action=add' : null,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_domains.php?page=sslcertificates',\n\t\t\t\t\t'label' => lng('domains.ssl_certificates')\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'ftp' => [\n\t\t\t'url' => 'customer_ftp.php',\n\t\t\t'label' => lng('menue.ftp.ftp'),\n\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'ftp')),\n\t\t\t'icon' => 'fa-solid fa-users',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_ftp.php?page=accounts',\n\t\t\t\t\t'label' => lng('menue.ftp.accounts'),\n\t\t\t\t\t'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('ftps') ? 'customer_ftp.php?page=accounts&action=add' : null,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_ftp.php?page=sshkeys',\n\t\t\t\t\t'label' => lng('menue.ftp.sshkeys'),\n\t\t\t\t\t'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('ftps') ? 'customer_ftp.php?page=sshkeys&action=add' : null,\n\t\t\t\t\t'show_element' => intval(Settings::Get('system.allow_customer_shell')) == 1 && intval(CurrentUser::getField('shell_allowed')) == 1\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => Settings::Get('panel.webftp_url'),\n\t\t\t\t\t'new_window' => true,\n\t\t\t\t\t'label' => lng('menue.ftp.webftp'),\n\t\t\t\t\t'show_element' => (Settings::Get('panel.webftp_url') != ''),\n\t\t\t\t\t'is_external' => true,\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'extras' => [\n\t\t\t'url' => 'customer_extras.php',\n\t\t\t'label' => lng('menue.extras.extras'),\n\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'extras')),\n\t\t\t'icon' => 'fa-solid fa-wrench',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_extras.php?page=htpasswds',\n\t\t\t\t\t'label' => lng('menue.extras.directoryprotection'),\n\t\t\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'extras.directoryprotection')),\n\t\t\t\t\t'add_shortlink' => 'customer_extras.php?page=htpasswds&action=add',\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_extras.php?page=htaccess',\n\t\t\t\t\t'label' => lng('menue.extras.pathoptions'),\n\t\t\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'extras.pathoptions')),\n\t\t\t\t\t'add_shortlink' => 'customer_extras.php?page=htaccess&action=add',\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_logger.php?page=log',\n\t\t\t\t\t'label' => lng('menue.logger.logger'),\n\t\t\t\t\t'show_element' => (Settings::Get('logger.enabled') == true) && (!Settings::IsInList('panel.customer_hide_options', 'extras.logger'))\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_extras.php?page=export',\n\t\t\t\t\t'label' => lng('menue.extras.export'),\n\t\t\t\t\t'show_element' => (Settings::Get('system.exportenabled') == true) && (!Settings::IsInList('panel.customer_hide_options', 'extras.export'))\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'traffic' => [\n\t\t\t'url' => 'customer_traffic.php',\n\t\t\t'label' => lng('menue.traffic.traffic'),\n\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'traffic')),\n\t\t\t'icon' => 'fa-solid fa-area-chart',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_traffic.php?page=current',\n\t\t\t\t\t'label' => lng('menue.traffic.current')\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'customer_traffic.php',\n\t\t\t\t\t'label' => lng('menue.traffic.overview')\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'docs' => [\n\t\t\t'label' => lng('admin.documentation'),\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'misc.documentation')),\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => \\Froxlor\\Froxlor::getDocsUrl() . 'user-guide/',\n\t\t\t\t\t'label' => lng('admin.userguide'),\n\t\t\t\t\t'new_window' => true,\n\t\t\t\t\t'is_external' => true,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => \\Froxlor\\Froxlor::getDocsUrl() . 'api-guide/',\n\t\t\t\t\t'label' => lng('admin.apiguide'),\n\t\t\t\t\t'new_window' => true,\n\t\t\t\t\t'show_element' => Settings::Get('api.enabled') == 1 && CurrentUser::getField('api_allowed') == 1,\n\t\t\t\t\t'is_external' => true,\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'admin' => [\n\t\t'resources' => [\n\t\t\t'label' => lng('admin.resources'),\n\t\t\t'required_resources' => 'customers',\n\t\t\t'icon' => 'fa-solid fa-box',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_customers.php?page=customers',\n\t\t\t\t\t'label' => lng('admin.customers'),\n\t\t\t\t\t'required_resources' => 'customers',\n\t\t\t\t\t'add_shortlink' => CurrentUser::isAdmin() && CurrentUser::canAddResource('customers') ? 'admin_customers.php?page=customers&action=add' : null,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_admins.php?page=admins',\n\t\t\t\t\t'label' => lng('admin.admins'),\n\t\t\t\t\t'required_resources' => 'change_serversettings',\n\t\t\t\t\t'add_shortlink' => 'admin_admins.php?page=admins&action=add'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_domains.php?page=domains',\n\t\t\t\t\t'label' => lng('admin.domains'),\n\t\t\t\t\t'required_resources' => 'domains',\n\t\t\t\t\t'add_shortlink' => CurrentUser::isAdmin() && CurrentUser::canAddResource('domains') ? 'admin_domains.php?page=domains&action=add' : null,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_domains.php?page=sslcertificates',\n\t\t\t\t\t'label' => lng('domains.ssl_certificates'),\n\t\t\t\t\t'required_resources' => 'domains'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_ipsandports.php?page=ipsandports',\n\t\t\t\t\t'label' => lng('admin.ipsandports.ipsandports'),\n\t\t\t\t\t'required_resources' => 'change_serversettings',\n\t\t\t\t\t'add_shortlink' => 'admin_ipsandports.php?page=ipsandports&action=add'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_mysqlserver.php?page=mysqlserver',\n\t\t\t\t\t'label' => lng('admin.mysqlserver.mysqlserver'),\n\t\t\t\t\t'required_resources' => 'change_serversettings',\n\t\t\t\t\t'add_shortlink' => 'admin_mysqlserver.php?page=mysqlserver&action=add'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_plans.php?page=overview',\n\t\t\t\t\t'label' => lng('admin.plans.plans'),\n\t\t\t\t\t'required_resources' => 'customers',\n\t\t\t\t\t'add_shortlink' => 'admin_plans.php?page=overview&action=add'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_settings.php?page=updatecounters',\n\t\t\t\t\t'label' => lng('admin.updatecounters'),\n\t\t\t\t\t'required_resources' => 'change_serversettings'\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'traffic' => [\n\t\t\t'label' => lng('admin.traffic'),\n\t\t\t'required_resources' => 'customers',\n\t\t\t'icon' => 'fa-solid fa-area-chart',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_traffic.php?page=customers',\n\t\t\t\t\t'label' => lng('admin.customertraffic'),\n\t\t\t\t\t'required_resources' => 'customers'\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'server' => [\n\t\t\t'label' => lng('admin.server'),\n\t\t\t'icon' => 'fa-solid fa-server',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_configfiles.php?page=configfiles',\n\t\t\t\t\t'label' => lng('admin.configfiles.serverconfiguration'),\n\t\t\t\t\t'required_resources' => 'change_serversettings'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_settings.php?page=overview',\n\t\t\t\t\t'label' => lng('admin.serversettings'),\n\t\t\t\t\t'required_resources' => 'change_serversettings'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_cronjobs.php?page=overview',\n\t\t\t\t\t'label' => lng('admin.cron.cronsettings'),\n\t\t\t\t\t'required_resources' => 'change_serversettings'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_logger.php?page=log',\n\t\t\t\t\t'label' => lng('menue.logger.logger'),\n\t\t\t\t\t'show_element' => (Settings::Get('logger.enabled') == true)\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_settings.php?page=rebuildconfigs',\n\t\t\t\t\t'label' => lng('admin.rebuildconf'),\n\t\t\t\t\t'required_resources' => 'change_serversettings'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_autoupdate.php?page=overview',\n\t\t\t\t\t'label' => lng('admin.autoupdate'),\n\t\t\t\t\t'required_resources' => 'change_serversettings',\n\t\t\t\t\t'show_element' => extension_loaded('zip') && Settings::Config('enable_webupdate')\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_settings.php?page=wipecleartextmailpws',\n\t\t\t\t\t'label' => lng('admin.wipecleartextmailpwd'),\n\t\t\t\t\t'required_resources' => 'change_serversettings',\n\t\t\t\t\t'show_element' => (Settings::Get('system.mailpwcleartext') == true)\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'server_php' => [\n\t\t\t'label' => lng('admin.server_php'),\n\t\t\t'icon' => 'fab fa-php',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_phpsettings.php?page=overview',\n\t\t\t\t\t'label' => lng('menue.phpsettings.maintitle'),\n\t\t\t\t\t'show_element' => (Settings::Get('system.mod_fcgid') == true || Settings::Get('phpfpm.enabled') == true),\n\t\t\t\t\t'add_shortlink' => 'admin_phpsettings.php?page=overview&action=add'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_phpsettings.php?page=fpmdaemons',\n\t\t\t\t\t'label' => lng('menue.phpsettings.fpmdaemons'),\n\t\t\t\t\t'show_element' => Settings::Get('phpfpm.enabled') == true,\n\t\t\t\t\t'add_shortlink' => 'admin_phpsettings.php?page=fpmdaemons&action=add'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_settings.php?page=phpinfo',\n\t\t\t\t\t'label' => lng('admin.phpinfo'),\n\t\t\t\t\t'required_resources' => 'change_serversettings'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_apcuinfo.php?page=showinfo',\n\t\t\t\t\t'label' => lng('admin.apcuinfo'),\n\t\t\t\t\t'required_resources' => 'change_serversettings',\n\t\t\t\t\t'show_element' => (function_exists('apcu_cache_info') === true)\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_opcacheinfo.php?page=showinfo',\n\t\t\t\t\t'label' => lng('admin.opcacheinfo'),\n\t\t\t\t\t'required_resources' => 'change_serversettings',\n\t\t\t\t\t'show_element' => (function_exists('opcache_get_configuration') === true)\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'misc' => [\n\t\t\t'label' => lng('admin.misc'),\n\t\t\t'icon' => 'fa-solid fa-wrench',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_settings.php?page=integritycheck',\n\t\t\t\t\t'label' => lng('admin.integritycheck'),\n\t\t\t\t\t'required_resources' => 'change_serversettings'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_templates.php?page=email',\n\t\t\t\t\t'label' => lng('admin.templates.email')\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_message.php?page=message',\n\t\t\t\t\t'label' => lng('admin.message')\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => 'admin_settings.php?page=testmail',\n\t\t\t\t\t'label' => lng('admin.testmail')\n\t\t\t\t]\n\t\t\t]\n\t\t],\n\t\t'docs' => [\n\t\t\t'label' => lng('admin.documentation'),\n\t\t\t'icon' => 'fa-solid fa-circle-info',\n\t\t\t'elements' => [\n\t\t\t\t[\n\t\t\t\t\t'url' => \\Froxlor\\Froxlor::getDocsUrl() . 'admin-guide/',\n\t\t\t\t\t'label' => lng('admin.adminguide'),\n\t\t\t\t\t'new_window' => true,\n\t\t\t\t\t'is_external' => true,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'url' => \\Froxlor\\Froxlor::getDocsUrl() . 'api-guide/',\n\t\t\t\t\t'label' => lng('admin.apiguide'),\n\t\t\t\t\t'new_window' => true,\n\t\t\t\t\t'show_element' => Settings::Get('api.enabled') == 1,\n\t\t\t\t\t'is_external' => true,\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/navigation/index.html",
    "content": ""
  },
  {
    "path": "lib/tablelisting/admin/index.html",
    "content": ""
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.admins.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Admin;\nuse Froxlor\\UI\\Callbacks\\Customer;\nuse Froxlor\\UI\\Callbacks\\Impersonate;\nuse Froxlor\\UI\\Callbacks\\ProgressBar;\nuse Froxlor\\UI\\Callbacks\\Style;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'admin_list' => [\n\t\t'title' => lng('admin.admin'),\n\t\t'icon' => 'fa-solid fa-user',\n\t\t'self_overview' => ['section' => 'admins', 'page' => 'admins'],\n\t\t'default_sorting' => ['loginname' => 'asc'],\n\t\t'columns' => [\n\t\t\t'adminid' => [\n\t\t\t\t'label' => 'ID',\n\t\t\t\t'field' => 'adminid',\n\t\t\t\t'sortable' => true,\n\t\t\t],\n\t\t\t'loginname' => [\n\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t'field' => 'loginname',\n\t\t\t\t'callback' => [Impersonate::class, 'admin'],\n\t\t\t\t'sortable' => true,\n\t\t\t\t'isdefaultsearchfield' => true,\n\t\t\t],\n\t\t\t'name' => [\n\t\t\t\t'label' => lng('customer.name'),\n\t\t\t\t'field' => 'name',\n\t\t\t],\n\t\t\t'email' => [\n\t\t\t\t'label' => lng('login.email'),\n\t\t\t\t'field' => 'email',\n\t\t\t],\n\t\t\t'def_language' => [\n\t\t\t\t'label' => lng('login.profile_lng'),\n\t\t\t\t'field' => 'def_language',\n\t\t\t],\n\t\t\t'customers_used' => [\n\t\t\t\t'label' => lng('admin.customers'),\n\t\t\t\t'field' => 'customers_used',\n\t\t\t\t'class' => 'text-center',\n\t\t\t],\n\t\t\t'diskspace' => [\n\t\t\t\t'label' => lng('customer.diskspace'),\n\t\t\t\t'field' => 'diskspace',\n\t\t\t\t'callback' => [ProgressBar::class, 'diskspace'],\n\t\t\t],\n\t\t\t'traffic' => [\n\t\t\t\t'label' => lng('customer.traffic'),\n\t\t\t\t'field' => 'traffic',\n\t\t\t\t'callback' => [ProgressBar::class, 'traffic_admins'],\n\t\t\t],\n\t\t\t'caneditphpsettings' => [\n\t\t\t\t'label' => lng('admin.caneditphpsettings'),\n\t\t\t\t'field' => 'caneditphpsettings',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'change_serversettings' => [\n\t\t\t\t'label' => lng('admin.change_serversettings'),\n\t\t\t\t'field' => 'change_serversettings',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'deactivated' => [\n\t\t\t\t'label' => lng('admin.deactivated'),\n\t\t\t\t'field' => 'deactivated',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'lastlogin_succ' => [\n\t\t\t\t'label' => lng('admin.lastlogin_succ'),\n\t\t\t\t'field' => 'lastlogin_succ',\n\t\t\t\t'callback' => [Text::class, 'timestamp'],\n\t\t\t],\n\t\t\t'theme' => [\n\t\t\t\t'label' => lng('panel.theme'),\n\t\t\t\t'field' => 'theme',\n\t\t\t],\n\t\t\t'api_allowed' => [\n\t\t\t\t'label' => lng('usersettings.api_allowed.title'),\n\t\t\t\t'field' => 'api_allowed',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'type_2fa' => [\n\t\t\t\t'label' => lng('2fa.type_2fa'),\n\t\t\t\t'field' => 'type_2fa',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'type2fa'],\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('admin_list', [\n\t\t\t'loginname',\n\t\t\t'name',\n\t\t\t'customers_used',\n\t\t\t'diskspace',\n\t\t\t'traffic',\n\t\t\t'deactivated',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'show' => [\n\t\t\t\t'icon' => 'fa-solid fa-eye',\n\t\t\t\t'title' => lng('usersettings.custom_notes.title'),\n\t\t\t\t'modal' => [Text::class, 'customerNoteDetailModal'],\n\t\t\t\t'visible' => [Customer::class, 'hasNote']\n\t\t\t],\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'admins',\n\t\t\t\t\t'page' => 'admins',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':adminid'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'admins',\n\t\t\t\t\t'page' => 'admins',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':adminid'\n\t\t\t\t],\n\t\t\t\t'visible' => [Admin::class, 'isNotMe']\n\t\t\t],\n\t\t],\n\t\t'format_callback' => [\n\t\t\t[Style::class, 'deactivated'],\n\t\t\t[Style::class, 'diskspaceWarning'],\n\t\t\t[Style::class, 'trafficWarning']\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.cronjobs.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'cron_list' => [\n\t\t'title' => lng('admin.cron.cronsettings'),\n\t\t'icon' => 'fa-solid fa-clock-rotate-left',\n\t\t'default_sorting' => ['c.id' => 'asc'],\n\t\t'no_search' => true,\n\t\t'columns' => [\n\t\t\t'c.desc_lng_key' => [\n\t\t\t\t'label' => lng('cron.description'),\n\t\t\t\t'field' => 'desc_lng_key',\n\t\t\t\t'callback' => [Text::class, 'crondesc']\n\t\t\t],\n\t\t\t'c.lastrun' => [\n\t\t\t\t'label' => lng('cron.lastrun'),\n\t\t\t\t'field' => 'lastrun',\n\t\t\t\t'callback' => [Text::class, 'timestamp']\n\t\t\t],\n\t\t\t'c.interval' => [\n\t\t\t\t'label' => lng('cron.interval'),\n\t\t\t\t'field' => 'interval'\n\t\t\t],\n\t\t\t'c.isactive' => [\n\t\t\t\t'label' => lng('cron.isactive'),\n\t\t\t\t'field' => 'isactive',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('cron_list', [\n\t\t\t'c.desc_lng_key',\n\t\t\t'c.lastrun',\n\t\t\t'c.interval',\n\t\t\t'c.isactive',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'cronjobs',\n\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.customers.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Customer;\nuse Froxlor\\UI\\Callbacks\\Impersonate;\nuse Froxlor\\UI\\Callbacks\\ProgressBar;\nuse Froxlor\\UI\\Callbacks\\Style;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'customer_list' => [\n\t\t'title' => lng('admin.customers'),\n\t\t'description' => lng('admin.customers_list_desc'),\n\t\t'icon' => 'fa-solid fa-user',\n\t\t'self_overview' => ['section' => 'customers', 'page' => 'customers'],\n\t\t'default_sorting' => ['c.name' => 'asc'],\n\t\t'columns' => [\n\t\t\t'c.customerid' => [\n\t\t\t\t'label' => 'ID',\n\t\t\t\t'field' => 'customerid',\n\t\t\t\t'sortable' => true,\n\t\t\t],\n\t\t\t'c.name' => [\n\t\t\t\t'label' => lng('customer.name'),\n\t\t\t\t'field' => 'name',\n\t\t\t\t'callback' => [Text::class, 'customerfullname'],\n\t\t\t],\n\t\t\t'c.loginname' => [\n\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t'field' => 'loginname',\n\t\t\t\t'callback' => [Impersonate::class, 'customer'],\n\t\t\t],\n\t\t\t'a.loginname' => [\n\t\t\t\t'label' => lng('admin.admin'),\n\t\t\t\t'field' => 'adminname',\n\t\t\t\t'callback' => [Impersonate::class, 'admin'],\n\t\t\t],\n\t\t\t'c.email' => [\n\t\t\t\t'label' => lng('customer.email'),\n\t\t\t\t'field' => 'email',\n\t\t\t],\n\t\t\t'c.street' => [\n\t\t\t\t'label' => lng('customer.street'),\n\t\t\t\t'field' => 'street',\n\t\t\t],\n\t\t\t'c.zipcode' => [\n\t\t\t\t'label' => lng('customer.zipcode'),\n\t\t\t\t'field' => 'zipcode',\n\t\t\t],\n\t\t\t'c.city' => [\n\t\t\t\t'label' => lng('customer.city'),\n\t\t\t\t'field' => 'city',\n\t\t\t],\n\t\t\t'c.phone' => [\n\t\t\t\t'label' => lng('customer.phone'),\n\t\t\t\t'field' => 'phone',\n\t\t\t],\n\t\t\t'c.fax' => [\n\t\t\t\t'label' => lng('customer.fax'),\n\t\t\t\t'field' => 'fax',\n\t\t\t],\n\t\t\t'c.customernumber' => [\n\t\t\t\t'label' => lng('customer.customernumber'),\n\t\t\t\t'field' => 'customernumber',\n\t\t\t],\n\t\t\t'c.def_language' => [\n\t\t\t\t'label' => lng('login.profile_lng'),\n\t\t\t\t'field' => 'def_language',\n\t\t\t],\n\t\t\t'c.guid' => [\n\t\t\t\t'label' => 'GUID',\n\t\t\t\t'field' => 'guid',\n\t\t\t],\n\t\t\t'c.diskspace' => [\n\t\t\t\t'label' => lng('customer.diskspace'),\n\t\t\t\t'field' => 'diskspace',\n\t\t\t\t'callback' => [ProgressBar::class, 'diskspace'],\n\t\t\t],\n\t\t\t'c.traffic' => [\n\t\t\t\t'label' => lng('customer.traffic'),\n\t\t\t\t'field' => 'traffic',\n\t\t\t\t'callback' => [ProgressBar::class, 'traffic'],\n\t\t\t],\n\t\t\t'c.deactivated' => [\n\t\t\t\t'label' => lng('admin.deactivated'),\n\t\t\t\t'field' => 'deactivated',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'c.lastlogin_succ' => [\n\t\t\t\t'label' => lng('admin.lastlogin_succ'),\n\t\t\t\t'field' => 'lastlogin_succ',\n\t\t\t\t'callback' => [Text::class, 'timestamp'],\n\t\t\t],\n\t\t\t'c.phpenabled' => [\n\t\t\t\t'label' => lng('admin.phpenabled'),\n\t\t\t\t'field' => 'phpenabled',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'c.perlenabled' => [\n\t\t\t\t'label' => lng('admin.perlenabled'),\n\t\t\t\t'field' => 'perlenabled',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'c.dnsenabled' => [\n\t\t\t\t'label' => lng('admin.dnsenabled'),\n\t\t\t\t'field' => 'dnsenabled',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'c.theme' => [\n\t\t\t\t'label' => lng('panel.theme'),\n\t\t\t\t'field' => 'theme',\n\t\t\t],\n\t\t\t'c.logviewenabled' => [\n\t\t\t\t'label' => lng('admin.logviewenabled'),\n\t\t\t\t'field' => 'logviewenabled',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'c.api_allowed' => [\n\t\t\t\t'label' => lng('usersettings.api_allowed.title'),\n\t\t\t\t'field' => 'api_allowed',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'c.type_2fa' => [\n\t\t\t\t'label' => lng('2fa.type_2fa'),\n\t\t\t\t'field' => 'type_2fa',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'type2fa'],\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('customer_list', [\n\t\t\t'c.name',\n\t\t\t'c.loginname',\n\t\t\t'a.loginname',\n\t\t\t'c.email',\n\t\t\t'c.diskspace',\n\t\t\t'c.traffic',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'show' => [\n\t\t\t\t'icon' => 'fa-solid fa-eye',\n\t\t\t\t'title' => lng('usersettings.custom_notes.title'),\n\t\t\t\t'modal' => [Text::class, 'customerNoteDetailModal'],\n\t\t\t\t'visible' => [Customer::class, 'hasNote']\n\t\t\t],\n\t\t\t'unlock' => [\n\t\t\t\t'icon' => 'fa-solid fa-unlock',\n\t\t\t\t'title' => lng('panel.unlock'),\n\t\t\t\t'class' => 'btn-outline-secondary',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'customers',\n\t\t\t\t\t'page' => 'customers',\n\t\t\t\t\t'action' => 'unlock',\n\t\t\t\t\t'id' => ':customerid'\n\t\t\t\t],\n\t\t\t\t'visible' => [Customer::class, 'isLocked']\n\t\t\t],\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'customers',\n\t\t\t\t\t'page' => 'customers',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':customerid'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'customers',\n\t\t\t\t\t'page' => 'customers',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':customerid'\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t\t'format_callback' => [\n\t\t\t[Style::class, 'resultCustomerLockedOrDeactivated']\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.domains.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Callbacks\\Domain;\nuse Froxlor\\UI\\Callbacks\\Impersonate;\nuse Froxlor\\UI\\Callbacks\\Style;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\n// used outside scope variables\n$customerCollectionCount = !is_null($customerCollection ?? null) ? $customerCollection->count() : 0;\n\nreturn [\n\t'domain_list' => [\n\t\t'title' => lng('admin.domains'),\n\t\t'icon' => 'fa-solid fa-globe',\n\t\t'empty_msg' => $customerCollectionCount == 0 ? lng('admin.domain_nocustomeraddingavailable') : '',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domains'],\n\t\t'default_sorting' => ['d.domain_ace' => 'asc'],\n\t\t'columns' => [\n\t\t\t'd.id' => [\n\t\t\t\t'label' => 'ID',\n\t\t\t\t'field' => 'id',\n\t\t\t\t'sortable' => true,\n\t\t\t],\n\t\t\t'd.domain_ace' => [\n\t\t\t\t'label' => lng('domains.domainname'),\n\t\t\t\t'field' => 'domain_ace',\n\t\t\t\t'isdefaultsearchfield' => true,\n\t\t\t\t'callback' => [Domain::class, 'domainEditLink'],\n\t\t\t],\n\t\t\t'ipsandports' => [\n\t\t\t\t'label' => lng('admin.ipsandports.ipsandports'),\n\t\t\t\t'field' => 'ipsandports',\n\t\t\t\t'sortable' => false,\n\t\t\t\t'callback' => [Domain::class, 'listIPs'],\n\t\t\t],\n\t\t\t'c.name' => [\n\t\t\t\t'label' => lng('customer.name'),\n\t\t\t\t'field' => 'customer.name',\n\t\t\t\t'callback' => [Text::class, 'customerfullname'],\n\t\t\t],\n\t\t\t'c.loginname' => [\n\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t'field' => 'customer.loginname',\n\t\t\t\t'callback' => [Impersonate::class, 'customer'],\n\t\t\t],\n\t\t\t'd.aliasdomain' => [\n\t\t\t\t'label' => lng('domains.aliasdomain'),\n\t\t\t\t'field' => 'aliasdomain',\n\t\t\t],\n\t\t\t'd.documentroot' => [\n\t\t\t\t'label' => lng('customer.documentroot'),\n\t\t\t\t'field' => 'documentroot',\n\t\t\t],\n\t\t\t'd.isbinddomain' => [\n\t\t\t\t'label' => lng('domains.isbinddomain'),\n\t\t\t\t'field' => 'isbinddomain',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.isemaildomain' => [\n\t\t\t\t'label' => lng('admin.emaildomain'),\n\t\t\t\t'field' => 'isemaildomain',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.email_only' => [\n\t\t\t\t'label' => lng('admin.email_only'),\n\t\t\t\t'field' => 'email_only',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.iswildcarddomain' => [\n\t\t\t\t'label' => lng('domains.serveraliasoption_wildcard'),\n\t\t\t\t'field' => 'iswildcarddomain',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.subcanemaildomain' => [\n\t\t\t\t'label' => lng('admin.subdomainforemail'),\n\t\t\t\t'field' => 'subcanemaildomain',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.caneditdomain' => [\n\t\t\t\t'label' => lng('admin.domain_editable.title'),\n\t\t\t\t'field' => 'caneditdomain',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.dkim' => [\n\t\t\t\t'label' => lng('domains.dkimenabled'),\n\t\t\t\t'field' => 'dkim',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.phpenabled' => [\n\t\t\t\t'label' => lng('admin.phpenabled'),\n\t\t\t\t'field' => 'phpenabled',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.phpsettingid' => [\n\t\t\t\t'label' => lng('admin.phpsettings.title'),\n\t\t\t\t'field' => 'phpsettingid',\n\t\t\t\t'searchable' => false,\n\t\t\t\t'callback' => [Domain::class, 'getPhpConfigName'],\n\t\t\t\t'visible' => (int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1\n\t\t\t],\n\t\t\t'd.openbasedir' => [\n\t\t\t\t'label' => lng('domains.openbasedirenabled'),\n\t\t\t\t'field' => 'openbasedir',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.speciallogfile' => [\n\t\t\t\t'label' => lng('admin.speciallogfile.title'),\n\t\t\t\t'field' => 'speciallogfile',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.hsts' => [\n\t\t\t\t'label' => lng('domains.hsts'),\n\t\t\t\t'field' => 'hsts',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.http2' => [\n\t\t\t\t'label' => lng('admin.domain_http2.title'),\n\t\t\t\t'field' => 'http2',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.http3' => [\n\t\t\t\t'label' => lng('admin.domain_http3.title'),\n\t\t\t\t'field' => 'http3',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.letsencrypt' => [\n\t\t\t\t'label' => lng('panel.letsencrypt'),\n\t\t\t\t'field' => 'letsencrypt',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.deactivated' => [\n\t\t\t\t'label' => lng('admin.deactivated'),\n\t\t\t\t'field' => 'deactivated',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('domain_list', [\n\t\t\t'd.domain_ace',\n\t\t\t'c.name',\n\t\t\t'c.loginname',\n\t\t\t'd.aliasdomain',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domains',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'duplicate' => [\n\t\t\t\t'icon' => 'fa-solid fa-clone',\n\t\t\t\t'title' => lng('admin.domain_duplicate'),\n\t\t\t\t'modal' => [Text::class, 'domainDuplicateModal'],\n\t\t\t],\n\t\t\t'logfiles' => [\n\t\t\t\t'icon' => 'fa-solid fa-file',\n\t\t\t\t'title' => lng('panel.viewlogs'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'logfiles',\n\t\t\t\t\t'domain_id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Domain::class, 'canViewLogs']\n\t\t\t],\n\t\t\t'domaindnseditor' => [\n\t\t\t\t'icon' => 'fa-solid fa-globe',\n\t\t\t\t'title' => lng('dnseditor.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domaindnseditor',\n\t\t\t\t\t'domain_id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Domain::class, 'adminCanEditDNS']\n\t\t\t],\n\t\t\t'domainssleditor' => [\n\t\t\t\t'callback' => [Domain::class, 'editSSLButtons'],\n\t\t\t],\n\t\t\t'letsencrypt' => [\n\t\t\t\t'icon' => 'fa-solid fa-shield',\n\t\t\t\t'title' => lng('panel.letsencrypt'),\n\t\t\t\t'visible' => [Domain::class, 'hasLetsEncryptActivated']\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domains',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Domain::class, 'adminCanDelete']\n\t\t\t]\n\t\t],\n\t\t'format_callback' => [\n\t\t\t[Style::class, 'resultDomainTerminatedOrDeactivated']\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.filetemplates.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'filetpl_list' => [\n\t\t'title' => lng('admin.templates.filetemplates'),\n\t\t'icon' => 'fa-solid fa-file-lines',\n\t\t'self_overview' => ['section' => 'templates', 'page' => 'email'],\n\t\t'default_sorting' => ['template' => 'asc'],\n\t\t'no_search' => true,\n\t\t'columns' => [\n\t\t\t'template' => [\n\t\t\t\t'label' => lng('admin.templates.action'),\n\t\t\t\t'field' => 'template'\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('filetpl_list', [\n\t\t\t'template'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'templates',\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => 'editf',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'templates',\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => 'deletef',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.fpmconfigs.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Admin;\nuse Froxlor\\UI\\Callbacks\\PHPConf;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'fpmconf_list' => [\n\t\t'title' => lng('menue.phpsettings.fpmdaemons'),\n\t\t'icon' => 'fa-brands fa-php',\n\t\t'self_overview' => ['section' => 'phpsettings', 'page' => 'fpmdaemons'],\n\t\t'default_sorting' => ['description' => 'asc'],\n\t\t'columns' => [\n\t\t\t'id' => [\n\t\t\t\t'label' => 'ID',\n\t\t\t\t'field' => 'id'\n\t\t\t],\n\t\t\t'description' => [\n\t\t\t\t'label' => lng('admin.phpsettings.description'),\n\t\t\t\t'field' => 'description',\n\t\t\t\t'isdefaultsearchfield' => true,\n\t\t\t],\n\t\t\t'configs' => [\n\t\t\t\t'label' => lng('admin.phpsettings.activephpconfigs'),\n\t\t\t\t'callback' => [PHPConf::class, 'configsList'],\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t\t'reload_cmd' => [\n\t\t\t\t'label' => lng('serversettings.phpfpm_settings.reload'),\n\t\t\t\t'field' => 'reload_cmd'\n\t\t\t],\n\t\t\t'config_dir' => [\n\t\t\t\t'label' => lng('serversettings.phpfpm_settings.configdir'),\n\t\t\t\t'field' => 'config_dir'\n\t\t\t],\n\t\t\t'pm' => [\n\t\t\t\t'label' => lng('serversettings.phpfpm_settings.pm'),\n\t\t\t\t'field' => 'pm',\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('fpmconf_list', [\n\t\t\t'description',\n\t\t\t'configs',\n\t\t\t'reload_cmd',\n\t\t\t'config_dir',\n\t\t\t'pm'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'phpsettings',\n\t\t\t\t\t'page' => 'fpmdaemons',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Admin::class, 'canChangeServerSettings']\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'phpsettings',\n\t\t\t\t\t'page' => 'fpmdaemons',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [PHPConf::class, 'isNotDefault']\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.integrity.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Style;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'integrity_list' => [\n\t\t'title' => lng('admin.integritycheck'),\n\t\t'icon' => 'fa-solid fa-circle-check',\n\t\t'self_overview' => ['section' => 'settings', 'page' => 'integritycheck'],\n\t\t'default_sorting' => ['displayid' => 'asc'],\n\t\t'no_search' => true,\n\t\t'columns' => [\n\t\t\t'displayid' => [\n\t\t\t\t'label' => 'ID',\n\t\t\t\t'field' => 'displayid'\n\t\t\t],\n\t\t\t'checkdesc' => [\n\t\t\t\t'label' => lng('admin.integrityname'),\n\t\t\t\t'field' => 'checkdesc'\n\t\t\t],\n\t\t\t'result' => [\n\t\t\t\t'label' => lng('admin.integrityresult'),\n\t\t\t\t'field' => 'result',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t\t'searchable' => false,\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('integrity_list', [\n\t\t\t'displayid',\n\t\t\t'checkdesc',\n\t\t\t'result'\n\t\t]),\n\t\t'format_callback' => [\n\t\t\t[Style::class, 'resultIntegrityBad']\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.ipsandports.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'ipsandports_list' => [\n\t\t'title' => lng('admin.ipsandports.ipsandports'),\n\t\t'icon' => 'fa-solid fa-ethernet',\n\t\t'self_overview' => ['section' => 'ipsandports', 'page' => 'ipsandports'],\n\t\t'default_sorting' => ['ip' => 'asc'],\n\t\t'columns' => [\n\t\t\t'ip' => [\n\t\t\t\t'label' => lng('admin.ipsandports.ip'),\n\t\t\t\t'field' => 'ip',\n\t\t\t\t'isdefaultsearchfield' => true,\n\t\t\t],\n\t\t\t'port' => [\n\t\t\t\t'label' => lng('admin.ipsandports.port'),\n\t\t\t\t'field' => 'port',\n\t\t\t\t'class' => 'text-center',\n\t\t\t],\n\t\t\t'listen' => [\n\t\t\t\t'label' => 'Listen',\n\t\t\t\t'field' => 'listen_statement',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t\t'visible' => Settings::Get('system.webserver') != 'nginx'\n\t\t\t],\n\t\t\t'namevirtualhost' => [\n\t\t\t\t'label' => 'NameVirtualHost',\n\t\t\t\t'field' => 'namevirtualhost_statement',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t\t'visible' => Settings::Get('system.webserver') == 'apache2' && (int)Settings::Get('system.apache24') == 0\n\t\t\t],\n\t\t\t'vhostcontainer' => [\n\t\t\t\t'label' => 'vHost-Container',\n\t\t\t\t'field' => 'vhostcontainer',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t\t'servername' => [\n\t\t\t\t'label' => lng('admin.ipsandports.create_vhostcontainer_servername_statement'),\n\t\t\t\t'field' => 'vhostcontainer_servername_statement',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t\t'visible' => Settings::Get('system.webserver') == 'apache2'\n\t\t\t],\n\t\t\t'specialsettings' => [\n\t\t\t\t'label' => 'Specialsettings',\n\t\t\t\t'field' => 'specialsettings',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t\t'ssl' => [\n\t\t\t\t'label' => 'SSL',\n\t\t\t\t'field' => 'ssl',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_file'),\n\t\t\t\t'field' => 'ssl_cert_file',\n\t\t\t\t'class' => 'text-center',\n\t\t\t],\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'label' => lng('admin.ipsandports.ssl_key_file'),\n\t\t\t\t'field' => 'ssl_key_file',\n\t\t\t\t'class' => 'text-center',\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'label' => lng('admin.ipsandports.ssl_ca_file'),\n\t\t\t\t'field' => 'ssl_ca_file',\n\t\t\t\t'class' => 'text-center',\n\t\t\t],\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'label' => lng('admin.ipsandports.ssl_cert_chainfile.title'),\n\t\t\t\t'field' => 'ssl_cert_chainfile',\n\t\t\t\t'class' => 'text-center',\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'label' => lng('admin.ipsandports.docroot.title'),\n\t\t\t\t'field' => 'docroot',\n\t\t\t\t'class' => 'text-center',\n\t\t\t],\n\t\t\t'ssl_specialsettings' => [\n\t\t\t\t'label' => 'SSL Specialsettings',\n\t\t\t\t'field' => 'ssl_specialsettings',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t\t'include_specialsettings' => [\n\t\t\t\t'label' => lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t'field' => 'include_specialsettings',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t\t'ssl_default_vhostconf_domain' => [\n\t\t\t\t'label' => lng('admin.ipsandports.ssl_default_vhostconf_domain'),\n\t\t\t\t'field' => 'ssl_default_vhostconf_domain',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t\t'include_default_vhostconf_domain' => [\n\t\t\t\t'label' => '[Domains] '. lng('serversettings.includedefault_sslvhostconf'),\n\t\t\t\t'field' => 'include_default_vhostconf_domain',\n\t\t\t\t'class' => 'text-center',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('ipsandports_list', [\n\t\t\t'ip',\n\t\t\t'port',\n\t\t\t'vhostcontainer',\n\t\t\t'specialsettings',\n\t\t\t'servername',\n\t\t\t'ssl'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'ipsandports',\n\t\t\t\t\t'page' => 'ipsandports',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'ipsandports',\n\t\t\t\t\t'page' => 'ipsandports',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.mailtemplates.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'mailtpl_list' => [\n\t\t'title' => lng('admin.templates.templates'),\n\t\t'icon' => 'fa-solid fa-envelope',\n\t\t'self_overview' => ['section' => 'templates', 'page' => 'email'],\n\t\t'default_sorting' => ['template' => 'asc'],\n\t\t'no_search' => true,\n\t\t'columns' => [\n\t\t\t'language' => [\n\t\t\t\t'label' => lng('login.language'),\n\t\t\t\t'field' => 'language'\n\t\t\t],\n\t\t\t'template' => [\n\t\t\t\t'label' => lng('admin.templates.action'),\n\t\t\t\t'field' => 'template'\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('mailtpl_list', [\n\t\t\t'language',\n\t\t\t'template'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'templates',\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'subjectid' => ':subjectid',\n\t\t\t\t\t'mailbodyid' => ':mailbodyid'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'templates',\n\t\t\t\t\t'page' => $page,\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'subjectid' => ':subjectid',\n\t\t\t\t\t'mailbodyid' => ':mailbodyid'\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.mysqlserver.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Admin;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'mysqlserver_list' => [\n\t\t'title' => lng('admin.mysqlserver.mysqlserver'),\n\t\t'icon' => 'fa-solid fa-server',\n\t\t'self_overview' => ['section' => 'mysqlserver', 'page' => 'mysqlserver'],\n\t\t'default_sorting' => ['caption' => 'asc'],\n\t\t'columns' => [\n\t\t\t'id' => [\n\t\t\t\t'label' => lng('admin.mysqlserver.dbserver'),\n\t\t\t\t'field' => 'id',\n\t\t\t],\n\t\t\t'caption' => [\n\t\t\t\t'label' => lng('admin.mysqlserver.caption'),\n\t\t\t\t'field' => 'caption',\n\t\t\t\t'isdefaultsearchfield' => true,\n\t\t\t],\n\t\t\t'host' => [\n\t\t\t\t'label' => lng('admin.mysqlserver.host'),\n\t\t\t\t'field' => 'host',\n\t\t\t],\n\t\t\t'port' => [\n\t\t\t\t'label' => lng('admin.mysqlserver.port'),\n\t\t\t\t'field' => 'port',\n\t\t\t\t'class' => 'text-center',\n\t\t\t],\n\t\t\t'user' => [\n\t\t\t\t'label' => lng('admin.mysqlserver.user'),\n\t\t\t\t'field' => 'user',\n\t\t\t\t'visible' => [Admin::class, 'canChangeServerSettings']\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('mysqlserver_list', [\n\t\t\t'caption',\n\t\t\t'host',\n\t\t\t'port',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'mysqlserver',\n\t\t\t\t\t'page' => 'mysqlserver',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Admin::class, 'canChangeServerSettings']\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'mysqlserver',\n\t\t\t\t\t'page' => 'mysqlserver',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Admin::class, 'canChangeServerSettings']\n\t\t\t],\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.phpconfigs.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Callbacks\\Admin;\nuse Froxlor\\UI\\Callbacks\\PHPConf;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'phpconf_list' => [\n\t\t'title' => lng('menue.phpsettings.maintitle'),\n\t\t'icon' => 'fa-brands fa-php',\n\t\t'self_overview' => ['section' => 'phpsettings', 'page' => 'overview'],\n\t\t'default_sorting' => ['c.description' => 'asc'],\n\t\t'columns' => [\n\t\t\t'c.id' => [\n\t\t\t\t'label' => 'ID',\n\t\t\t\t'field' => 'id',\n\t\t\t],\n\t\t\t'c.description' => [\n\t\t\t\t'label' => lng('admin.phpsettings.description'),\n\t\t\t\t'field' => 'description',\n\t\t\t\t'isdefaultsearchfield' => true,\n\t\t\t],\n\t\t\t'domains' => [\n\t\t\t\t'label' => lng('admin.phpsettings.activedomains'),\n\t\t\t\t'field' => 'domains',\n\t\t\t\t'callback' => [PHPConf::class, 'domainList'],\n\t\t\t\t'searchable' => false,\n\t\t\t\t'sortable' => false,\n\t\t\t],\n\t\t\t'fpmdesc' => [\n\t\t\t\t'label' => lng('admin.phpsettings.fpmdesc'),\n\t\t\t\t'field' => 'fpmdesc',\n\t\t\t\t'visible' => (bool)Settings::Get('phpfpm.enabled'),\n\t\t\t\t'callback' => [PHPConf::class, 'fpmConfLink']\n\t\t\t],\n\t\t\t'c.binary' => [\n\t\t\t\t'label' => lng('admin.phpsettings.binary'),\n\t\t\t\t'field' => 'binary',\n\t\t\t\t'visible' => !(bool)Settings::Get('phpfpm.enabled')\n\t\t\t],\n\t\t\t'c.file_extensions' => [\n\t\t\t\t'label' => lng('admin.phpsettings.file_extensions'),\n\t\t\t\t'field' => 'file_extensions',\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('phpconf_list', [\n\t\t\t'c.description',\n\t\t\t'domains',\n\t\t\t'fpmdesc',\n\t\t\t'c.binary',\n\t\t\t'c.file_extensions'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'phpsettings',\n\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Admin::class, 'canChangeServerSettings']\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'phpsettings',\n\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [PHPConf::class, 'isNotDefault']\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/admin/tablelisting.plans.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'plan_list' => [\n\t\t'title' => lng('admin.plans.plans'),\n\t\t'icon' => 'fa-solid fa-clipboard-list',\n\t\t'self_overview' => ['section' => 'plans', 'page' => 'overview'],\n\t\t'default_sorting' => ['p.name' => 'asc'],\n\t\t'columns' => [\n\t\t\t'p.id' => [\n\t\t\t\t'label' => 'ID',\n\t\t\t\t'field' => 'id',\n\t\t\t],\n\t\t\t'p.name' => [\n\t\t\t\t'label' => lng('admin.plans.name'),\n\t\t\t\t'field' => 'name',\n\t\t\t\t'isdefaultsearchfield' => true,\n\t\t\t],\n\t\t\t'p.description' => [\n\t\t\t\t'label' => lng('admin.plans.description'),\n\t\t\t\t'field' => 'description',\n\t\t\t],\n\t\t\t'p.adminname' => [\n\t\t\t\t'label' => lng('admin.admin'),\n\t\t\t\t'field' => 'adminname',\n\t\t\t],\n\t\t\t'p.ts' => [\n\t\t\t\t'label' => lng('admin.plans.last_update'),\n\t\t\t\t'field' => 'ts',\n\t\t\t\t'callback' => [Text::class, 'timestamp'],\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('plan_list', [\n\t\t\t'p.name',\n\t\t\t'p.description',\n\t\t\t'p.adminname',\n\t\t\t'p.ts',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'plans',\n\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'plans',\n\t\t\t\t\t'page' => 'overview',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/index.html",
    "content": ""
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.domains.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Domain;\nuse Froxlor\\UI\\Callbacks\\Style;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'domain_list' => [\n\t\t'title' => lng('admin.domains'),\n\t\t'icon' => 'fa-solid fa-globe',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domains'],\n\t\t'default_sorting' => ['d.domain_ace' => 'asc'],\n\t\t'columns' => [\n\t\t\t'd.domain_ace' => [\n\t\t\t\t'label' => lng('domains.domainname'),\n\t\t\t\t'field' => 'domain_ace',\n\t\t\t\t'callback' => [Domain::class, 'domainExternalLinkInfo'],\n\t\t\t],\n\t\t\t'ipsandports' => [\n\t\t\t\t'label' => lng('admin.ipsandports.ipsandports'),\n\t\t\t\t'field' => 'ipsandports',\n\t\t\t\t'sortable' => false,\n\t\t\t\t'callback' => [Domain::class, 'listIPs'],\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t\t'd.documentroot' => [\n\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t'field' => 'documentroot',\n\t\t\t\t'callback' => [Domain::class, 'domainTarget'],\n\t\t\t],\n\t\t\t'd.isbinddomain' => [\n\t\t\t\t'label' => lng('domains.isbinddomain'),\n\t\t\t\t'field' => 'isbinddomain',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.isemaildomain' => [\n\t\t\t\t'label' => lng('admin.emaildomain'),\n\t\t\t\t'field' => 'isemaildomain',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.email_only' => [\n\t\t\t\t'label' => lng('admin.email_only'),\n\t\t\t\t'field' => 'email_only',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.iswildcarddomain' => [\n\t\t\t\t'label' => lng('domains.serveraliasoption_wildcard'),\n\t\t\t\t'field' => 'iswildcarddomain',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'd.letsencrypt' => [\n\t\t\t\t'label' => lng('panel.letsencrypt'),\n\t\t\t\t'field' => 'letsencrypt',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'ad.id' => [\n\t\t\t\t'label' => lng('domains.aliasdomainid'),\n\t\t\t\t'field' => 'aliasdomainid'\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('domain_list', [\n\t\t\t'd.domain_ace',\n\t\t\t'd.documentroot'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domains',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Domain::class, 'canEdit']\n\t\t\t],\n\t\t\t'logfiles' => [\n\t\t\t\t'icon' => 'fa-solid fa-file',\n\t\t\t\t'title' => lng('panel.viewlogs'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'logfiles',\n\t\t\t\t\t'domain_id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Domain::class, 'canViewLogs']\n\t\t\t],\n\t\t\t'domaindnseditor' => [\n\t\t\t\t'icon' => 'fa-solid fa-globe',\n\t\t\t\t'title' => lng('dnseditor.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domaindnseditor',\n\t\t\t\t\t'domain_id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Domain::class, 'canEditDNS']\n\t\t\t],\n\t\t\t'domainssleditor' => [\n\t\t\t\t'callback' => [Domain::class, 'editSSLButtons'],\n\t\t\t],\n\t\t\t'letsencrypt' => [\n\t\t\t\t'icon' => 'fa-solid fa-shield',\n\t\t\t\t'title' => lng('panel.letsencrypt'),\n\t\t\t\t'visible' => [Domain::class, 'hasLetsEncryptActivated']\n\t\t\t],\n\t\t\t'haslias' => [\n\t\t\t\t'icon' => 'fa-solid fa-arrow-up-right-from-square',\n\t\t\t\t'title' => lng('domains.hasaliasdomains'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domains',\n\t\t\t\t\t'searchfield' => 'ad.id',\n\t\t\t\t\t'searchtext' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Domain::class, 'canEditAlias']\n\t\t\t],\n\t\t\t'isassigned' => [\n\t\t\t\t'icon' => 'fa-solid fa-check-to-slot',\n\t\t\t\t'title' => lng('domains.isassigneddomain'),\n\t\t\t\t'visible' => [Domain::class, 'isAssigned']\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domains',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t'visible' => [Domain::class, 'canDelete']\n\t\t\t]\n\t\t],\n\t\t'format_callback' => [\n\t\t\t[Style::class, 'resultDomainTerminatedOrDeactivated']\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.emails.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Callbacks\\Email;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'email_list' => [\n\t\t'title' => lng('menue.email.emails'),\n\t\t'icon' => 'fa-solid fa-envelope',\n\t\t'self_overview' => ['section' => 'email', 'page' => 'email_domain'],\n\t\t'default_sorting' => ['m.email_full' => 'asc'],\n\t\t'columns' => [\n\t\t\t'm.email_full' => [\n\t\t\t\t'label' => lng('emails.emailaddress'),\n\t\t\t\t'field' => 'email_full',\n\t\t\t],\n\t\t\t'm.destination' => [\n\t\t\t\t'label' => lng('emails.forwarders'),\n\t\t\t\t'field' => 'destination',\n\t\t\t\t'callback' => [Email::class, 'forwarderList'],\n\t\t\t],\n\t\t\t'm.popaccountid' => [\n\t\t\t\t'label' => lng('emails.account'),\n\t\t\t\t'field' => 'popaccountid',\n\t\t\t\t'callback' => [Email::class, 'account'],\n\t\t\t],\n\t\t\t'm.spam_tag_level' => [\n\t\t\t\t'label' => lng('antispam.spam_tag_level.title'),\n\t\t\t\t'field' => 'spam_tag_level',\n\t\t\t],\n\t\t\t'm.spam_kill_level' => [\n\t\t\t\t'label' => lng('antispam.spam_kill_level.title'),\n\t\t\t\t'field' => 'spam_kill_level',\n\t\t\t],\n\t\t\t'm.bypass_spam' => [\n\t\t\t\t'label' => lng('antispam.bypass_spam.title'),\n\t\t\t\t'field' => 'bypass_spam',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'm.policy_greylist' => [\n\t\t\t\t'label' => lng('antispam.policy_greylist.title'),\n\t\t\t\t'field' => 'policy_greylist',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'm.iscatchall' => [\n\t\t\t\t'label' => lng('emails.catchall'),\n\t\t\t\t'field' => 'iscatchall',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t\t'visible' => Settings::Get('catchall.catchall_enabled') == '1'\n\t\t\t],\n\t\t\t'u.quota' => [\n\t\t\t\t'label' => lng('emails.quota'),\n\t\t\t\t'field' => 'quota',\n\t\t\t\t'visible' => Settings::Get('system.mail_quota_enabled') == '1'\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('email_list', [\n\t\t\t'm.email_full',\n\t\t\t'm.destination',\n\t\t\t'm.popaccountid',\n\t\t\t'm.iscatchall',\n\t\t\t'u.quota'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => ':domainid',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => ':domainid',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.emails_overview.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Impersonate;\nuse Froxlor\\UI\\Callbacks\\Style;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'emaildomain_list' => [\n\t\t'title' => lng('menue.email.emailsoverview'),\n\t\t'icon' => 'fa-solid fa-envelope',\n\t\t'self_overview' => ['section' => 'email', 'page' => 'overview'],\n\t\t'default_sorting' => ['d.domain_ace' => 'asc'],\n\t\t'columns' => [\n\t\t\t'd.domain_ace' => [\n\t\t\t\t'label' => 'Domain',\n\t\t\t\t'field' => 'domain',\n\t\t\t],\n\t\t\t'addresses' => [\n\t\t\t\t'label' => '# ' . lng('emails.emails'),\n\t\t\t\t'field' => 'addresses',\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t\t'accounts' => [\n\t\t\t\t'label' => '# ' . lng('emails.accounts'),\n\t\t\t\t'field' => 'accounts',\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t\t'forwarder' => [\n\t\t\t\t'label' => '# ' . lng('emails.forwarders'),\n\t\t\t\t'field' => 'forwarder',\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('emaildomain_list', [\n\t\t\t'd.domain_ace',\n\t\t\t'addresses',\n\t\t\t'accounts',\n\t\t\t'forwarder',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'show' => [\n\t\t\t\t'icon' => 'fa-solid fa-eye',\n\t\t\t\t'title' => lng('apikeys.clicktoview'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'email',\n\t\t\t\t\t'page' => 'email_domain',\n\t\t\t\t\t'domainid' => ':domainid'\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.export.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Ftp;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'export_list' => [\n\t\t'title' => lng('error.customerhasongoingexportjob'),\n\t\t'icon' => 'fa-solid fa-server',\n\t\t'self_overview' => ['section' => 'extras', 'page' => 'export'],\n\t\t'default_sorting' => ['destdir' => 'asc'],\n\t\t'columns' => [\n\t\t\t'destdir' => [\n\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t'field' => 'data.destdir',\n\t\t\t\t'callback' => [Ftp::class, 'pathRelative']\n\t\t\t],\n\t\t\t'pgp_public_key' => [\n\t\t\t\t'label' => lng('panel.pgp_public_key'),\n\t\t\t\t'field' => 'data.pgp_public_key',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t\t'dump_web' => [\n\t\t\t\t'label' => lng('extras.dump_web'),\n\t\t\t\t'field' => 'data.dump_web',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'dump_mail' => [\n\t\t\t\t'label' => lng('extras.dump_mail'),\n\t\t\t\t'field' => 'data.dump_mail',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t],\n\t\t\t'dump_dbs' => [\n\t\t\t\t'label' => lng('extras.dump_dbs'),\n\t\t\t\t'field' => 'data.dump_dbs',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('export_list', [\n\t\t\t'destdir',\n\t\t\t'pgp_public_key',\n\t\t\t'dump_web',\n\t\t\t'dump_mail',\n\t\t\t'dump_dbs'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.abort'),\n\t\t\t\t'class' => 'btn-warning',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'extras',\n\t\t\t\t\t'page' => 'export',\n\t\t\t\t\t'action' => 'abort',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.ftps.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Callbacks\\Ftp;\nuse Froxlor\\UI\\Callbacks\\Style;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'ftp_list' => [\n\t\t'title' => lng('menue.ftp.accounts'),\n\t\t'icon' => 'fa-solid fa-users',\n\t\t'self_overview' => ['section' => 'ftp', 'page' => 'accounts'],\n\t\t'default_sorting' => ['username' => 'asc'],\n\t\t'columns' => [\n\t\t\t'username' => [\n\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t'field' => 'username',\n\t\t\t],\n\t\t\t'description' => [\n\t\t\t\t'label' => lng('panel.ftpdesc'),\n\t\t\t\t'field' => 'description'\n\t\t\t],\n\t\t\t'homedir' => [\n\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t'field' => 'homedir',\n\t\t\t\t'callback' => [Ftp::class, 'pathRelative']\n\t\t\t],\n\t\t\t'shell' => [\n\t\t\t\t'label' => lng('panel.shell'),\n\t\t\t\t'field' => 'shell',\n\t\t\t\t'visible' => Settings::Get('system.allow_customer_shell') == '1'\n\t\t\t],\n\t\t\t'login_enabled' => [\n\t\t\t\t'label' => lng('panel.active'),\n\t\t\t\t'field' => 'login_enabled',\n\t\t\t\t'callback' => [Text::class, 'yesno'],\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('ftp_list', [\n\t\t\t'username',\n\t\t\t'description',\n\t\t\t'homedir',\n\t\t\t'shell',\n\t\t\t'login_enabled',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'ftp',\n\t\t\t\t\t'page' => 'accounts',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'ftp',\n\t\t\t\t\t'page' => 'accounts',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t]\n\t\t],\n\t\t'format_callback' => [\n\t\t\t[Style::class, 'loginDisabled']\n\t\t],\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.htaccess.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Ftp;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\n// used outside scope variables\n$cperlenabled = $cperlenabled ?? false;\n\nreturn [\n\t'htaccess_list' => [\n\t\t'title' => lng('menue.extras.pathoptions'),\n\t\t'icon' => 'fa-solid fa-folder',\n\t\t'self_overview' => ['section' => 'extras', 'page' => 'htaccess'],\n\t\t'default_sorting' => ['path' => 'asc'],\n\t\t'columns' => [\n\t\t\t'path' => [\n\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t'field' => 'path',\n\t\t\t\t'callback' => [Ftp::class, 'pathRelative']\n\t\t\t],\n\t\t\t'options_indexes' => [\n\t\t\t\t'label' => lng('extras.view_directory'),\n\t\t\t\t'field' => 'options_indexes',\n\t\t\t\t'callback' => [Text::class, 'boolean']\n\t\t\t],\n\t\t\t'error404path' => [\n\t\t\t\t'label' => lng('extras.error404path'),\n\t\t\t\t'field' => 'error404path'\n\t\t\t],\n\t\t\t'error403path' => [\n\t\t\t\t'label' => lng('extras.error403path'),\n\t\t\t\t'field' => 'error403path'\n\t\t\t],\n\t\t\t'error500path' => [\n\t\t\t\t'label' => lng('extras.error500path'),\n\t\t\t\t'field' => 'error500path'\n\t\t\t],\n\t\t\t'options_cgi' => [\n\t\t\t\t'label' => lng('extras.execute_perl'),\n\t\t\t\t'field' => 'options_cgi',\n\t\t\t\t'callback' => [Text::class, 'boolean'],\n\t\t\t\t'visible' => $cperlenabled\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('htaccess_list', [\n\t\t\t'path',\n\t\t\t'options_indexes',\n\t\t\t'error404path',\n\t\t\t'error403path',\n\t\t\t'error500path',\n\t\t\t'options_cgi'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'extras',\n\t\t\t\t\t'page' => 'htaccess',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'extras',\n\t\t\t\t\t'page' => 'htaccess',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.htpasswd.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Ftp;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'htpasswd_list' => [\n\t\t'title' => lng('menue.extras.directoryprotection'),\n\t\t'icon' => 'fa-solid fa-lock',\n\t\t'self_overview' => ['section' => 'extras', 'page' => 'htpasswds'],\n\t\t'default_sorting' => ['path' => 'asc'],\n\t\t'columns' => [\n\t\t\t'username' => [\n\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t'field' => 'username'\n\t\t\t],\n\t\t\t'path' => [\n\t\t\t\t'label' => lng('panel.path'),\n\t\t\t\t'field' => 'path',\n\t\t\t\t'callback' => [Ftp::class, 'pathRelative']\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('htpasswd_list', [\n\t\t\t'username',\n\t\t\t'path'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'extras',\n\t\t\t\t\t'page' => 'htpasswds',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'extras',\n\t\t\t\t\t'page' => 'htpasswds',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.mysqls.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Mysql;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\n// used outside scope variables\n$multiple_mysqlservers = $multiple_mysqlservers ?? false;\n\nreturn [\n\t'mysql_list' => [\n\t\t'title' => lng('menue.mysql.databases'),\n\t\t'icon' => 'fa-solid fa-database',\n\t\t'self_overview' => ['section' => 'mysql', 'page' => 'mysqls'],\n\t\t'default_sorting' => ['databasename' => 'asc'],\n\t\t'columns' => [\n\t\t\t'databasename' => [\n\t\t\t\t'label' => lng('mysql.databasename'),\n\t\t\t\t'field' => 'databasename',\n\t\t\t],\n\t\t\t'description' => [\n\t\t\t\t'label' => lng('mysql.databasedescription'),\n\t\t\t\t'field' => 'description'\n\t\t\t],\n\t\t\t'size' => [\n\t\t\t\t'label' => lng('mysql.size'),\n\t\t\t\t'field' => 'size',\n\t\t\t\t'callback' => [Text::class, 'size'],\n\t\t\t\t'searchable' => false\n\t\t\t],\n\t\t\t'dbserver' => [\n\t\t\t\t'label' => lng('mysql.mysql_server'),\n\t\t\t\t'field' => 'dbserver',\n\t\t\t\t'callback' => [Mysql::class, 'dbserver'],\n\t\t\t\t'visible' => $multiple_mysqlservers\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('mysql_list', [\n\t\t\t'databasename',\n\t\t\t'description',\n\t\t\t'size',\n\t\t\t'dbserver'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'mysql',\n\t\t\t\t\t'page' => 'mysqls',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'mysql',\n\t\t\t\t\t'page' => 'mysqls',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t]\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/customer/tablelisting.sshkeys.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'sshkeys_list' => [\n\t\t'title' => lng('menue.ftp.sshkeys'),\n\t\t'icon' => 'fa-solid fa-key',\n\t\t'self_overview' => ['section' => 'ftp', 'page' => 'sshkeys'],\n\t\t'default_sorting' => ['f.username' => 'asc'],\n\t\t'columns' => [\n\t\t\t'username' => [\n\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t'field' => 'username',\n\t\t\t],\n\t\t\t'description' => [\n\t\t\t\t'label' => lng('panel.sshkeydesc'),\n\t\t\t\t'field' => 'description'\n\t\t\t],\n\t\t\t'fingerprint' => [\n\t\t\t\t'label' => lng('panel.sshfingerprint'),\n\t\t\t\t'field' => 'fingerprint',\n\t\t\t\t'sortable' => false,\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('sshkeys_list', [\n\t\t\t'username',\n\t\t\t'description',\n\t\t\t'fingerprint',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'ftp',\n\t\t\t\t\t'page' => 'sshkeys',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'ftp',\n\t\t\t\t\t'page' => 'sshkeys',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t]\n\t\t],\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/index.html",
    "content": ""
  },
  {
    "path": "lib/tablelisting/tablelisting.apikeys.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Impersonate;\nuse Froxlor\\UI\\Callbacks\\Style;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'apikeys_list' => [\n\t\t'title' => lng('menue.main.apikeys'),\n\t\t'icon' => 'fa-solid fa-key',\n\t\t'self_overview' => ['section' => 'index', 'page' => 'apikeys'],\n\t\t'no_search' => true,\n\t\t'columns' => [\n\t\t\t'a.loginname' => [\n\t\t\t\t'label' => lng('login.username'),\n\t\t\t\t'field' => 'loginname',\n\t\t\t\t'callback' => [Impersonate::class, 'apiAdminCustomerLink']\n\t\t\t],\n\t\t\t'ak.apikey' => [\n\t\t\t\t'label' => 'API-key',\n\t\t\t\t'field' => 'apikey',\n\t\t\t\t'callback' => [Text::class, 'shorten'],\n\t\t\t],\n\t\t\t'ak.secret' => [\n\t\t\t\t'label' => 'Secret',\n\t\t\t\t'field' => 'secret',\n\t\t\t\t'callback' => [Text::class, 'shorten'],\n\t\t\t],\n\t\t\t'ak.allowed_from' => [\n\t\t\t\t'label' => lng('apikeys.allowed_from'),\n\t\t\t\t'field' => 'allowed_from',\n\t\t\t],\n\t\t\t'ak.valid_until' => [\n\t\t\t\t'label' => lng('apikeys.valid_until'),\n\t\t\t\t'field' => 'valid_until',\n\t\t\t\t'callback' => [Text::class, 'timestampUntil'],\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('apikeys_list', [\n\t\t\t'a.loginname',\n\t\t\t'ak.apikey',\n\t\t\t'ak.secret',\n\t\t\t'ak.allowed_from',\n\t\t\t'ak.valid_until'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'show' => [\n\t\t\t\t'icon' => 'fa-solid fa-eye',\n\t\t\t\t'title' => lng('apikeys.clicktoview'),\n\t\t\t\t'modal' => [Text::class, 'apikeyDetailModal'],\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'index',\n\t\t\t\t\t'page' => 'apikeys',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t\t'format_callback' => [\n\t\t\t[Style::class, 'invalidApiKey']\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/tablelisting.dns.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Dns;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\n// used outside scope variables\n$domain = $domain ?? '';\n$domain_id = $domain_id ?? '';\n\nreturn [\n\t'dns_list' => [\n\t\t'title' => 'DNS Entries',\n\t\t'description' => $domain,\n\t\t'icon' => 'fa-solid fa-globe',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'domaindnseditor'],\n\t\t'default_sorting' => ['record' => 'asc'],\n\t\t'columns' => [\n\t\t\t'record' => [\n\t\t\t\t'label' => 'Record',\n\t\t\t\t'field' => 'record'\n\t\t\t],\n\t\t\t'type' => [\n\t\t\t\t'label' => 'Type',\n\t\t\t\t'field' => 'type'\n\t\t\t],\n\t\t\t'prio' => [\n\t\t\t\t'label' => 'Priority',\n\t\t\t\t'field' => 'prio',\n\t\t\t\t'callback' => [Dns::class, 'prio'],\n\t\t\t],\n\t\t\t'content' => [\n\t\t\t\t'label' => 'Content',\n\t\t\t\t'field' => 'content',\n\t\t\t\t'callback' => [Text::class, 'wordwrap'],\n\t\t\t],\n\t\t\t'ttl' => [\n\t\t\t\t'label' => 'TTL',\n\t\t\t\t'field' => 'ttl'\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('dns_list', [\n\t\t\t'record',\n\t\t\t'type',\n\t\t\t'prio',\n\t\t\t'content',\n\t\t\t'ttl'\n\t\t]),\n\t\t'actions' => [\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'text-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domaindnseditor',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'domain_id' => $domain_id,\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t],\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/tablelisting.sslcertificates.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\Domain;\nuse Froxlor\\UI\\Callbacks\\SSLCertificate;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'sslcertificates_list' => [\n\t\t'title' => lng('domains.ssl_certificates'),\n\t\t'icon' => 'fa-solid fa-shield',\n\t\t'self_overview' => ['section' => 'domains', 'page' => 'sslcertificates'],\n\t\t'default_sorting' => ['d.domain' => 'asc'],\n\t\t'columns' => [\n\t\t\t'd.domain' => [\n\t\t\t\t'label' => lng('domains.domainname'),\n\t\t\t\t'field' => 'domain',\n\t\t\t\t'callback' => [Domain::class, 'domainWithCustomerLink'],\n\t\t\t],\n\t\t\t'c.domain' => [\n\t\t\t\t'label' => lng('ssl_certificates.certificate_for'),\n\t\t\t\t'field' => 'domain',\n\t\t\t\t'callback' => [SSLCertificate::class, 'domainWithSan'],\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t\t's.issuer' => [\n\t\t\t\t'label' => lng('ssl_certificates.issuer'),\n\t\t\t\t'field' => 'issuer',\n\t\t\t],\n\t\t\t's.validfromdate' => [\n\t\t\t\t'label' => lng('ssl_certificates.valid_from'),\n\t\t\t\t'field' => 'validfromdate',\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t\t's.validtodate' => [\n\t\t\t\t'label' => lng('ssl_certificates.valid_until'),\n\t\t\t\t'field' => 'validtodate',\n\t\t\t\t'searchable' => false,\n\t\t\t],\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('sslcertificates_list', [\n\t\t\t'd.domain',\n\t\t\t'c.domain',\n\t\t\t's.issuer',\n\t\t\t's.validfromdate',\n\t\t\t's.validtodate',\n\t\t]),\n\t\t'actions' => [\n\t\t\t'edit' => [\n\t\t\t\t'icon' => 'fa-solid fa-edit',\n\t\t\t\t'title' => lng('panel.edit'),\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domainssleditor',\n\t\t\t\t\t'action' => 'view',\n\t\t\t\t\t'id' => ':domainid'\n\t\t\t\t],\n\t\t\t\t'visible' => [SSLCertificate::class, 'canEditSSL']\n\t\t\t],\n\t\t\t'delete' => [\n\t\t\t\t'icon' => 'fa-solid fa-trash',\n\t\t\t\t'title' => lng('panel.delete'),\n\t\t\t\t'class' => 'btn-danger',\n\t\t\t\t'href' => [\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'sslcertificates',\n\t\t\t\t\t'action' => 'delete',\n\t\t\t\t\t'id' => ':id'\n\t\t\t\t],\n\t\t\t\t// Let's Encrypt certificates can be removed 'correctly'\n\t\t\t\t// by disabling let's encrypt for the domain\n\t\t\t\t'visible' => [SSLCertificate::class, 'isNotLetsEncrypt']\n\t\t\t],\n\t\t]\n\t]\n];\n"
  },
  {
    "path": "lib/tablelisting/tablelisting.syslog.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nuse Froxlor\\UI\\Callbacks\\SysLog;\nuse Froxlor\\UI\\Callbacks\\Text;\nuse Froxlor\\UI\\Listing;\n\nreturn [\n\t'syslog_list' => [\n\t\t'title' => lng('menue.logger.logger'),\n\t\t'icon' => 'fa-solid fa-file-lines',\n\t\t'self_overview' => ['section' => 'logger', 'page' => 'log'],\n\t\t'default_sorting' => ['date' => 'desc'],\n\t\t'columns' => [\n\t\t\t'date' => [\n\t\t\t\t'label' => lng('logger.date'),\n\t\t\t\t'field' => 'date',\n\t\t\t\t'callback' => [Text::class, 'timestamp'],\n\t\t\t],\n\t\t\t'type' => [\n\t\t\t\t'label' => lng('logger.type'),\n\t\t\t\t'field' => 'type',\n\t\t\t\t'callback' => [SysLog::class, 'typeDescription'],\n\t\t\t],\n\t\t\t'user' => [\n\t\t\t\t'label' => lng('logger.user'),\n\t\t\t\t'field' => 'user',\n\t\t\t],\n\t\t\t'text' => [\n\t\t\t\t'label' => lng('logger.action'),\n\t\t\t\t'field' => 'text',\n\t\t\t]\n\t\t],\n\t\t'visible_columns' => Listing::getVisibleColumnsForListing('syslog_list', [\n\t\t\t'date',\n\t\t\t'type',\n\t\t\t'user',\n\t\t\t'text'\n\t\t])\n\t]\n];\n"
  },
  {
    "path": "lib/tables.inc.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nconst TABLE_FTP_GROUPS = 'ftp_groups';\nconst TABLE_FTP_USERS = 'ftp_users';\nconst TABLE_FTP_QUOTALIMITS = 'ftp_quotalimits';\nconst TABLE_FTP_QUOTATALLIES = 'ftp_quotatallies';\nconst TABLE_MAIL_USERS = 'mail_users';\nconst TABLE_MAIL_VIRTUAL = 'mail_virtual';\nconst TABLE_MAIL_SENDER_ALIAS = 'mail_sender_aliases';\nconst TABLE_PANEL_ACTIVATION = 'panel_activation';\nconst TABLE_PANEL_ADMINS = 'panel_admins';\nconst TABLE_PANEL_CUSTOMERS = 'panel_customers';\nconst TABLE_PANEL_DATABASES = 'panel_databases';\nconst TABLE_PANEL_DOMAINS = 'panel_domains';\nconst TABLE_PANEL_HTACCESS = 'panel_htaccess';\nconst TABLE_PANEL_HTPASSWDS = 'panel_htpasswds';\nconst TABLE_PANEL_SETTINGS = 'panel_settings';\nconst TABLE_PANEL_TASKS = 'panel_tasks';\nconst TABLE_PANEL_TEMPLATES = 'panel_templates';\nconst TABLE_PANEL_TRAFFIC = 'panel_traffic';\nconst TABLE_PANEL_TRAFFIC_ADMINS = 'panel_traffic_admins';\nconst TABLE_PANEL_DISKSPACE = 'panel_diskspace';\nconst TABLE_PANEL_IPSANDPORTS = 'panel_ipsandports';\nconst TABLE_PANEL_LOG = 'panel_syslog';\nconst TABLE_PANEL_PHPCONFIGS = 'panel_phpconfigs';\nconst TABLE_PANEL_CRONRUNS = 'cronjobs_run';\nconst TABLE_PANEL_REDIRECTCODES = 'redirect_codes';\nconst TABLE_PANEL_DOMAINREDIRECTS = 'domain_redirect_codes';\nconst TABLE_PANEL_DOMAIN_SSL_SETTINGS = 'domain_ssl_settings';\nconst TABLE_DOMAINTOIP = 'panel_domaintoip';\nconst TABLE_DOMAIN_DNS = 'domain_dns_entries';\nconst TABLE_PANEL_FPMDAEMONS = 'panel_fpmdaemons';\nconst TABLE_PANEL_PLANS = 'panel_plans';\nconst TABLE_API_KEYS = 'api_keys';\nconst TABLE_PANEL_USERCOLUMNS = 'panel_usercolumns';\nconst TABLE_PANEL_LOGINLINKS = 'panel_loginlinks';\nconst TABLE_PANEL_2FA_TOKENS = 'panel_2fa_tokens';\nconst TABLE_PANEL_USER_SSHKEYS = 'panel_sshkeys';\n"
  },
  {
    "path": "lng/ca.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'languages' => [\n\t\t'cz' => 'Txec',\n\t\t'de' => 'Alemany',\n\t\t'en' => 'Angles',\n\t\t'fr' => 'Frances',\n\t\t'it' => 'Italia',\n\t\t'nl' => 'Holandes',\n\t\t'pt' => 'Portugues',\n\t\t'se' => 'Suec',\n\t\t'sk' => 'Eslovac',\n\t\t'es' => 'Espanyol',\n\t\t'ca' => 'Catala',\n\t\t'zh_CN' => 'Xines (Simplificat)',\n\t],\n\t'2fa' => [\n\t\t'2fa' => 'Opcions 2FA',\n\t\t'2fa_enabled' => 'Activar l\\'autenticació de dos factors (2FA)',\n\t\t'2fa_removed' => '2FA eliminat correctament',\n\t\t'2fa_added' => '2FA activat correctament<br /><a class=\"alert-link\" href=\"%s?page=2fa\">Veure detalls de 2FA</a>',\n\t\t'2fa_add' => 'Activar 2FA',\n\t\t'2fa_delete' => 'Desactivar 2FA',\n\t\t'2fa_verify' => 'Verificar codi',\n\t\t'2fa_overview_desc' => 'Aquí podeu activar una autenticació de dos factors per al vostre compte.<br/><br/>Podeu utilitzar una aplicació d\\'autenticació (contrasenya d\\'un sol ús basada en el temps / TOTP) o deixar que froxlor us enviï un correu electrònic a l\\'adreça del vostre compte després de cada inici de sessió correcte amb una contrasenya d\\'un sol ús.',\n\t\t'2fa_email_desc' => 'El vostre compte està configurat per utilitzar contrasenyes d\\'un sol ús per a cada correu electrònic. Per desactivar-la, feu clic a \"Desactivar 2FA\".',\n\t\t'2fa_ga_desc' => 'El vostre compte està configurat per utilitzar contrasenyes d\\'un sol ús basades en el temps mitjançant una aplicació d\\'autenticació. Escanegeu el codi QR que apareix a continuació amb l\\'aplicació d\\'autenticació que vulgueu per generar els codis. Per desactivar, feu clic a \"Desactivar 2FA\".'\n\t],\n\t'admin' => [\n\t\t'overview' => 'Visió general',\n\t\t'ressourcedetails' => 'Recursos utilitzats',\n\t\t'systemdetails' => 'Detalls del sistema',\n\t\t'froxlordetails' => 'Detalls de froxlor',\n\t\t'installedversion' => 'Versió instal·lada',\n\t\t'latestversion' => 'Última versió',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'Cerca mitjançant el servei web',\n\t\t\t'error' => 'Error de lectura'\n\t\t],\n\t\t'resources' => 'Recursos',\n\t\t'customer' => 'Client',\n\t\t'customers' => 'Clients',\n\t\t'customers_list_desc' => 'Gestioni els seus clients',\n\t\t'customer_add' => 'Crear client',\n\t\t'customer_edit' => 'Editar client',\n\t\t'username_default_msg' => 'Deixar buit per al valor autogenerat',\n\t\t'domains' => 'Dominis',\n\t\t'domain_add' => 'Crear domini',\n\t\t'domain_edit' => 'Editar domini',\n\t\t'subdomainforemail' => 'Subdominis com dominis de correu electrònic',\n\t\t'admin' => 'Administrador',\n\t\t'admins' => 'Administradors',\n\t\t'admin_add' => 'Crear administrador',\n\t\t'admin_edit' => 'Editar administrador',\n\t\t'customers_see_all' => 'Pot accedir als recursos d\\'altres administradors/revenedors?',\n\t\t'change_serversettings' => 'Pot canviar la configuració del servidor?',\n\t\t'server' => 'Sistema',\n\t\t'serversettings' => 'Ajustos',\n\t\t'serversettings_desc' => 'Administrar el sistema froxlor',\n\t\t'rebuildconf' => 'Reconstruir arxius de configuració',\n\t\t'stdsubdomain' => 'Subdomini estàndard',\n\t\t'stdsubdomain_add' => 'Crear subdomini estàndard',\n\t\t'phpenabled' => 'PHP habilitat',\n\t\t'deactivated' => 'Desactivat',\n\t\t'deactivated_user' => 'Desactivar usuari',\n\t\t'sendpassword' => 'Enviar contrasenya',\n\t\t'ownvhostsettings' => 'Configuració vHost pròpia',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Configuració',\n\t\t\t'overview' => 'Visió general',\n\t\t\t'wizard' => 'Assistent',\n\t\t\t'distribution' => 'Distribució',\n\t\t\t'service' => 'Servei',\n\t\t\t'daemon' => 'Dimoni',\n\t\t\t'http' => 'Servidor web (HTTP)',\n\t\t\t'dns' => 'Servidor de noms (DNS)',\n\t\t\t'mail' => 'Servidor de correu (IMAP/POP3)',\n\t\t\t'smtp' => 'Servidor de correu (SMTP)',\n\t\t\t'ftp' => 'Servidor FTP',\n\t\t\t'etc' => 'Altres (Sistema)',\n\t\t\t'choosedistribution' => '-- Esculli una distribució --',\n\t\t\t'chooseservice' => '-- Esculli un servei --',\n\t\t\t'choosedaemon' => '-- Esculli un dimoni --',\n\t\t\t'statistics' => 'Estadístiques',\n\t\t\t'compactoverview' => 'Vista general compacta',\n\t\t\t'legend' => '<h3>Està a punt de configurar un servei/dimoni</h3>',\n\t\t\t'commands' => '<span class=\"text-danger\">Ordres:</span> Aquestes ordres han de ser executades línia per línia com a usuari root en una shell. És segur copiar tot el bloc i enganxar-lo a l\\'intèrpret d\\'ordres.',\n\t\t\t'files' => '<span class=\"text-danger\">Fitxers de configuració:</span> Les ordres abans dels camps de text han d\\'obrir un editor amb el fitxer de destí. Només has de copiar i enganxar el contingut a l\\'editor i desar el fitxer.<br /><span class=\"text-danger\">Nota:</span> La contrasenya MySQL no ha estat reemplaçada per raons de seguretat. Si us plau, reemplaça \"FROXLOR_MYSQL_PASSWORD\" pel teu compte o fes servir el formulari javascript de sota per reemplaçar-la in situ. Si has oblidat la teva contrasenya MySQL la trobaràs a \"lib/userdata.inc.php\".',\n\t\t\t'importexport' => 'Importar/Exportar',\n\t\t\t'finishnote' => 'Fitxer de paràmetres generat correctament. Ara executeu la següent ordre com a root:',\n\t\t\t'description' => 'Configurar els serveis del sistema',\n\t\t\t'minihowto' => 'En aquesta pàgina podeu veure les diferents plantilles de configuració per a cada servei, (re)configurar serveis específics si cal o exportar la selecció actual a un fitxer JSON per utilitzar-lo als scripts CLI o en un altre servidor.<br/><br/><b>Tingui en compte</b> que els serveis ressaltats no reflecteixen la configuració actual, sinó que mostren requisits/recomanacions dels seus valors de configuració actuals.',\n\t\t\t'skipconfig' => 'No (re)configurar',\n\t\t\t'recommendednote' => 'Serveis recomanats/requerits segons la configuració actual del sistema',\n\t\t\t'selectrecommended' => 'Seleccionar recomanats',\n\t\t\t'downloadselected' => 'Exportar seleccionat'\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Plantilles de correu electrònic',\n\t\t\t'template_add' => 'Afegir plantilla',\n\t\t\t'template_fileadd' => 'Afegir plantilla de fitxer',\n\t\t\t'template_edit' => 'Editar plantilla',\n\t\t\t'action' => 'Acció',\n\t\t\t'email' => 'Plantilles de correu electrònic i fitxers',\n\t\t\t'subject' => 'Assumpte',\n\t\t\t'mailbody' => 'Cos del correu',\n\t\t\t'createcustomer' => 'Correu de benvinguda per a nous clients',\n\t\t\t'pop_success' => 'Correu de benvinguda per a nous comptes de correu electrònic',\n\t\t\t'template_replace_vars' => 'Variables a substituir a la plantilla:',\n\t\t\t'SALUTATION' => 'Substituït per una correcta salutació (nom o empresa)',\n\t\t\t'FIRSTNAME' => 'Substituït pel nom del client.',\n\t\t\t'NAME' => 'Substituït pel nom complet del client.',\n\t\t\t'COMPANY' => 'Substituït pel nom de l\\'empresa del client.',\n\t\t\t'USERNAME' => 'Substituït pel nom d\\'usuari del compte del client.',\n\t\t\t'PASSWORD' => 'Substituït per la contrasenya del compte del client.',\n\t\t\t'EMAIL' => 'Substituït per l\\'adreça de correu electrònic del compte POP3/IMAP.',\n\t\t\t'CUSTOMER_NO' => 'Substituït pel número de client.',\n\t\t\t'TRAFFIC' => 'Substituït pel trànsit assignat al client.',\n\t\t\t'TRAFFICUSED' => 'Substituït pel trànsit, que ha sigut esgotat pel client.',\n\t\t\t'pop_success_alternative' => 'Correu de benvinguda per a nous comptes de correu electrònic enviats a l\\'adreça alternativa.',\n\t\t\t'EMAIL_PASSWORD' => 'Substituït per la contrasenya del compte POP3/IMAP.',\n\t\t\t'index_html' => 'Arxiu d\\'índex per a directoris de clients acabats de crear',\n\t\t\t'SERVERNAME' => 'Substituït pel nom del servidor.',\n\t\t\t'CUSTOMER' => 'Substituït pel nom d\\'usuari del client.',\n\t\t\t'ADMIN' => 'Substituït pel nom d\\'usuari de l\\'administrador.',\n\t\t\t'CUSTOMER_EMAIL' => 'Substituït per l\\'adreça de correu electrònic del client.',\n\t\t\t'ADMIN_EMAIL' => 'Substituït per l\\'adreça de correu electrònic de l\\'administrador.',\n\t\t\t'filetemplates' => 'Plantilles de fitxers',\n\t\t\t'filecontent' => 'Contingut del fitxer',\n\t\t\t'new_database_by_customer' => 'Notificació al client de la creació d\\'una base de dades',\n\t\t\t'new_ftpaccount_by_customer' => 'Notificació al client de la creació d\\'un usuari ftp',\n\t\t\t'newdatabase' => 'Correus de notificació per a noves bases de dades',\n\t\t\t'newftpuser' => 'Correus de notificació per a nous usuaris ftp',\n\t\t\t'CUST_NAME' => 'Nom del client',\n\t\t\t'DB_NAME' => 'Nom de la base de dades',\n\t\t\t'DB_PASS' => 'Contrasenya de la base de dades',\n\t\t\t'DB_DESC' => 'Descripció de la base de dades',\n\t\t\t'DB_SRV' => 'Servidor de base de dades',\n\t\t\t'PMA_URI' => 'URL de phpMyAdmin (si s\\'indica)',\n\t\t\t'USR_NAME' => 'Nom d\\'usuari FTP',\n\t\t\t'USR_PASS' => 'Contrasenya FTP',\n\t\t\t'USR_PATH' => 'Directori HOME de l\\'FTP (relatiu a customer-docroot)',\n\t\t\t'forgotpwd' => 'Correus de notificació de restabliment de contrasenya',\n\t\t\t'password_reset' => 'Notificació al client de restabliment de contrasenya',\n\t\t\t'trafficmaxpercent' => 'Correu de notificació per als clients quan s\\'esgota un determinat percentatge màxim de trànsit',\n\t\t\t'MAX_PERCENT' => 'Substituït pel límit de l\\'ús del disc/trànsit per a l\\'enviament d\\'informes en percentatge.',\n\t\t\t'USAGE_PERCENT' => 'Substituït amb l\\'ús del disc/trànsit esgotat pel client en percentatge.',\n\t\t\t'diskmaxpercent' => 'Correu de notificació als clients quan s\\'esgota el percentatge màxim d\\'espai al disc.',\n\t\t\t'DISKAVAILABLE' => 'Substituït per l\\'ús de disc assignat al client.',\n\t\t\t'DISKUSED' => 'Substituït per l\\'ús de disc, que ha sigut esgotat pel client.',\n\t\t\t'LINK' => 'Substituït per l\\'enllaç de restabliment de contrasenya del client.',\n\t\t\t'SERVER_HOSTNAME' => 'Reemplaça el nom del sistema (URL a froxlor)',\n\t\t\t'SERVER_IP' => 'Reemplaça l\\'adreça IP per defecte del servidor ',\n\t\t\t'SERVER_PORT' => 'Reemplaça el port per defecte del servidor',\n\t\t\t'DOMAINNAME' => 'Reemplaça el subdomini estàndard del client (pot estar buit si no se\\'n genera cap)'\n\t\t],\n\t\t'webserver' => 'Servidor web',\n\t\t'createzonefile' => 'Crear zona dns pel domini',\n\t\t'custombindzone' => 'Arxiu de zona personalitzatada / no gestionada',\n\t\t'bindzonewarning' => 'buit per defecte<br /><strong class=\"text-danger\">ATENCIÓ:</strong> Si utilitza un fitxer de zones, haurà de gestionar també manualment tots els registres necessaris per a totes les subzones.',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IPs i Ports',\n\t\t\t'add' => 'Afegir IP/Port',\n\t\t\t'edit' => 'Editar IP/Port',\n\t\t\t'ipandport' => 'IP/Port',\n\t\t\t'ip' => 'IP',\n\t\t\t'ipnote' => '<div id=\"ipnote\" class=\"invalid-feedback\">Nota: Tot i que les adreces IP privades estan permeses, algunes característiques com DNS podrien no comportar-se correctament.<br />Només utilitzi adreces IP privades si està segur.</div>',\n\t\t\t'port' => 'Port',\n\t\t\t'create_listen_statement' => 'Crear sentència Listen',\n\t\t\t'create_namevirtualhost_statement' => 'Crear sentència NameVirtualHost',\n\t\t\t'create_vhostcontainer' => 'Crear vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Crear sentència ServerName a vHost-Container',\n\t\t\t'enable_ssl' => 'Es tracta d\\'un port SSL?',\n\t\t\t'ssl_cert_file' => 'Ruta del certificat SSL',\n\t\t\t'webserverdefaultconfig' => 'Configuració per defecte del servidor web',\n\t\t\t'webserverdomainconfig' => 'Configuració de domini del servidor web',\n\t\t\t'webserverssldomainconfig' => 'Configuració SSL del servidor web',\n\t\t\t'ssl_key_file' => 'Ruta del fitxer de claus SSL',\n\t\t\t'ssl_ca_file' => 'Ruta del certificat SSL CA',\n\t\t\t'default_vhostconf_domain' => 'Configuració vHost per defecte per a cada contenidor de domini',\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'Ruta del fitxer SSL CertificateChainFile',\n\t\t\t\t'description' => 'Normalment CA_Bundle, o similar, probablement vols configurar això si has comprat un certificat SSL.'\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Docroot personalizat (buit = apunta a froxlor)',\n\t\t\t\t'description' => 'Aquí podeu definir un document-root personalitzat (el destí d\\'una petició) per a aquesta combinació ip/port.<br/><strong>ATENCIÓ:</strong> Si us plau, vés amb compte amb el que introdueixes aquí!'\n\t\t\t],\n\t\t\t'ssl_paste_description' => 'Enganxi el contingut complet del vostre certificat al quadre de text',\n\t\t\t'ssl_cert_file_content' => 'Contingut del certificat ssl',\n\t\t\t'ssl_key_file_content' => 'Contingut del fitxer de la clau ssl (privada)',\n\t\t\t'ssl_ca_file_content' => 'Contingut del fitxer ssl CA (opcional)',\n\t\t\t'ssl_ca_file_content_desc' => '<br/><br/>Autenticació del client, configuri això només si sap el que és.',\n\t\t\t'ssl_cert_chainfile_content' => 'Contingut del fitxer de cadena de certificats (opcional)',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br/><br/>Normalment CA_Bundle, o similar, probablement vols configurar això si has comprat un certificat SSL.',\n\t\t\t'ssl_default_vhostconf_domain' => 'Configuració SSL vHost per defecte per a cada contenidor de domini'\n\t\t],\n\t\t'memorylimitdisabled' => 'Desactivat',\n\t\t'valuemandatory' => 'Aquest valor és obligatori',\n\t\t'valuemandatorycompany' => 'O bé \"nom\" i \"nom complet\" o \"empresa\" ha de ser omplert',\n\t\t'serversoftware' => 'Programari del servidor',\n\t\t'phpversion' => 'Versió PHP',\n\t\t'mysqlserverversion' => 'Versió del servidor MySQL',\n\t\t'webserverinterface' => 'Interfície del servidor web',\n\t\t'accountsettings' => 'Configuració del compte',\n\t\t'panelsettings' => 'Configuració del panell',\n\t\t'systemsettings' => 'Configuració del sistema',\n\t\t'webserversettings' => 'Configuració del servidor web',\n\t\t'mailserversettings' => 'Configuració del servidor de correu',\n\t\t'nameserversettings' => 'Configuració del servidor de noms',\n\t\t'updatecounters' => 'Recalcular l\\'ús de recursos',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Mai',\n\t\t\t'choosableno' => 'Seleccionable, per defecte no',\n\t\t\t'choosableyes' => 'Seleccionable, per defecte sí',\n\t\t\t'always' => 'Sempre'\n\t\t],\n\t\t'wipecleartextmailpwd' => 'Esborrar contrasenyes en text pla',\n\t\t'webalizersettings' => 'Configuració de Webalizer',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normal',\n\t\t\t'quiet' => 'Silenciós',\n\t\t\t'veryquiet' => 'No mostra res'\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Actualment no és possible afegir un domini. Primer ha d\\'afegir almenys un client.',\n\t\t'loggersettings' => 'Configuració del registre',\n\t\t'logger' => [\n\t\t\t'normal' => 'normal',\n\t\t\t'paranoid' => 'paranoic'\n\t\t],\n\t\t'emaildomain' => 'Correu de domini',\n\t\t'email_only' => 'Només correu electrònic?',\n\t\t'wwwserveralias' => 'Afageix un \"www.\" ServerAlias',\n\t\t'subject' => 'Assumpte',\n\t\t'recipient' => 'Destinatari',\n\t\t'message' => 'Escriure un missatge',\n\t\t'text' => 'Missatge',\n\t\t'sslsettings' => 'Configuració SSL',\n\t\t'specialsettings_replacements' => 'Pot utilitzar les següents variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (si escau)<br/>',\n\t\t'dkimsettings' => 'Configuració de DKIM',\n\t\t'caneditphpsettings' => 'Pot canviar els paràmetres de domini relacionats amb php?',\n\t\t'allips' => 'Totes les IP',\n\t\t'awstatssettings' => 'Configuració de AWstats',\n\t\t'domain_dns_settings' => 'Configuració DNS del domini',\n\t\t'activated' => 'Activat',\n\t\t'statisticsettings' => 'Configuració d\\'estadístiques',\n\t\t'or' => 'o',\n\t\t'sysload' => 'Càrrega del sistema',\n\t\t'noloadavailable' => 'no disponible',\n\t\t'nouptimeavailable' => 'no disponible',\n\t\t'nosubject' => '(Sense assumpte)',\n\t\t'security_settings' => 'Opcions de seguretat',\n\t\t'know_what_youre_doing' => 'Canviar només si sap el que fa!',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Mostra la versió de froxlor a l\\'inici de sessió',\n\t\t\t'description' => 'Mostrar la versió de froxlor al peu de pàgina a la pàgina d\\'inici de sessió'\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Mostrar la versió de froxlor al peu de pàgina',\n\t\t\t'description' => 'Mostrar la versió de froxlor al peu de pàgina de la resta de pàgines'\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Gràfic de capçalera per a froxlor',\n\t\t\t'description' => 'Quin gràfic s\\'ha de mostrar a la capçalera'\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'Configuració PHP',\n\t\t\t'description' => 'Breu descripció',\n\t\t\t'actions' => 'Accions',\n\t\t\t'activedomains' => 'En ús per domini(s)',\n\t\t\t'notused' => 'Configuració no en ús',\n\t\t\t'editsettings' => 'Canviar configuració PHP',\n\t\t\t'addsettings' => 'Crear nova configuració PHP',\n\t\t\t'viewsettings' => 'Veure la configuració PHP',\n\t\t\t'phpinisettings' => 'Configuració php.ini',\n\t\t\t'addnew' => 'Crear nova configuració PHP',\n\t\t\t'binary' => 'PHP Binari',\n\t\t\t'fpmdesc' => 'Configuració PHP-FPM',\n\t\t\t'file_extensions' => 'Extensions de fitxers',\n\t\t\t'file_extensions_note' => '(sense punt, separades per espais)',\n\t\t\t'enable_slowlog' => 'Activar slowlog (per domini)',\n\t\t\t'request_terminate_timeout' => 'Sol·licitar terminate-timeout',\n\t\t\t'request_slowlog_timeout' => 'Sol·licitar slowlog-timeout',\n\t\t\t'activephpconfigs' => 'En ús per php-config(s)',\n\t\t\t'pass_authorizationheader' => 'Afegeix les capçaleres HTTP AUTH BASIC/DIGEST de l\\'Apache cap al PHP'\n\t\t],\n\t\t'misc' => 'Varis',\n\t\t'fpmsettings' => [\n\t\t\t'addnew' => 'Crear nova versió PHP',\n\t\t\t'edit' => 'Canviar versió PHP'\n\t\t],\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'Variables que seran reemplaçades a les configs',\n\t\t\t'pear_dir' => 'Serà reemplaçada amb la configuració global per al directori pear.',\n\t\t\t'open_basedir_c' => 'S\\'inserirà un ; (punt i coma) per comentar/desactivar open_basedir quan s\\'estableixi',\n\t\t\t'open_basedir' => 'Es substituirà per la configuració open_basedir del domini.',\n\t\t\t'tmp_dir' => 'Serà reemplaçat pel directori temporal del domini.',\n\t\t\t'open_basedir_global' => 'Es substituirà pel valor global de la ruta que s\\'adjuntarà a open_basedir (veure configuració del servidor web).',\n\t\t\t'customer_email' => 'Es substituirà per l\\'adreça de correu electrònic del client propietari del domini.',\n\t\t\t'admin_email' => 'Es substituirà per l\\'adreça de correu electrònic de l\\'administrador propietari del domini.',\n\t\t\t'domain' => 'Es substituirà pel domini.',\n\t\t\t'customer' => 'Es substituirà pel nom d\\'usuari del client propietari del domini.',\n\t\t\t'admin' => 'Es substituirà pel nom d\\'usuari de l\\'administrador propietari del domini.',\n\t\t\t'docroot' => 'Es substituirà per l\\'arrel del document del domini.',\n\t\t\t'homedir' => 'Es substituirà pel directori arrel del client.'\n\t\t],\n\t\t'expert_settings' => 'Configuració experta!',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'Processos PHP per a aquest domini (buit per defecte)'\n\t\t],\n\t\t'phpserversettings' => 'Configuracions de PHP',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Peticions php màximes per a aquest domini (buit per defecte)'\n\t\t],\n\t\t'spfsettings' => 'Configuració SPF del domini',\n\t\t'specialsettingsforsubdomains' => 'Aplicar configuració especial a tots els subdominis (*.exemple.com)',\n\t\t'accountdata' => 'Dades del compte',\n\t\t'contactdata' => 'Dades de contacte',\n\t\t'servicedata' => 'Dades de servei',\n\t\t'newerversionavailable' => 'Hi ha una nova versió de froxlor disponible.',\n\t\t'newerversiondetails' => 'Actualitzi ara a la versió <b>%s</b>?<br/>(La seva versió actual és: %s)',\n\t\t'extractdownloadedzip' => 'Extreure el fitxer descarregat \"%s\"?',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Configuració de tasques de Cron',\n\t\t\t'add' => 'Afegir tasca cron'\n\t\t],\n\t\t'cronjob_edit' => 'Editar tasques de cron',\n\t\t'warning' => 'AVÍS - Tingueu en compte!',\n\t\t'lastlogin_succ' => 'Darrer inici de sessió',\n\t\t'ftpserver' => 'Servidor FTP',\n\t\t'ftpserversettings' => 'Configuració del servidor FTP',\n\t\t'webserver_user' => 'Nom d\\'usuari del servidor web',\n\t\t'webserver_group' => 'Nom de grup del servidor web',\n\t\t'perlenabled' => 'Perl activat',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Usuari local a utilitzar per a FCGID (froxlor vHost)',\n\t\t'mod_fcgid_group' => 'Grup local a utilitzar per a FCGID (froxlor vHost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[No indicat]',\n\t\t'store_defaultindex' => 'Emmagatzemar el fitxer d\\'índex per defecte al docroot del client',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => 'Trànsit',\n\t\t'traffic_sub' => 'Detalls sobre l\\'ús del trànsit',\n\t\t'domaintraffic' => 'Dominis',\n\t\t'customertraffic' => 'Clients',\n\t\t'assignedmax' => 'Assignat / Màxim',\n\t\t'usedmax' => 'Utilitzat / Màxim',\n\t\t'used' => 'Utilitzat',\n\t\t'speciallogwarning' => '<div id=\"speciallogfilenote\" class=\"invalid-feedback\">AVÍS: En canviar aquesta configuració perdrà totes les antigues estadístiques per a aquest domini.</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => 'Fitxer de registre separat',\n\t\t\t'description' => 'Activi aquesta opció per obtenir un fitxer de registre d\\'accés independent per a aquest domini.'\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => 'Permetre editar el domini',\n\t\t\t'desc' => 'Si s\\'estableix en \"si\", el client pot canviar diversos paràmetres del domini.<br/>Si s\\'estableix en \"no\", el client no pot canviar res.'\n\t\t],\n\t\t'writeaccesslog' => [\n\t\t\t'title' => 'Escriure un registre d\\'accés',\n\t\t\t'description' => 'Activi aquesta opció per obtenir un fitxer de registre d\\'accés per a aquest domini.'\n\t\t],\n\t\t'writeerrorlog' => [\n\t\t\t'title' => 'Escriure un registre d\\'errors',\n\t\t\t'description' => 'Activi aquesta opció per obtenir un fitxer de registre d \\'errors per a aquest domini.'\n\t\t],\n\t\t'phpfpm.ininote' => 'No tots els valors que voleu definir poden ser utilitzats en la configuració del pool php-fpm',\n\t\t'phpinfo' => 'PHPinfo()',\n\t\t'selectserveralias' => 'Valor ServerAlias per al domini',\n\t\t'selectserveralias_desc' => 'Triï si froxlor ha de crear una entrada comodí (*.domini.tld), un àlies WWW (www.domini.tld) o cap àlies.',\n\t\t'show_news_feed' => [\n\t\t\t'title' => 'Mostrar notícies al panell d\\'administració',\n\t\t\t'description' => 'Activi aquesta opció per mostrar les notícies oficials de froxlor (https://inside.froxlor.org/news/) al teu tauler de control i no perdre\\'t mai informació important o anuncis de llançaments.'\n\t\t],\n\t\t'cronsettings' => 'Configuració de tasques de Cron',\n\t\t'integritycheck' => 'Validació de la base de dades',\n\t\t'integrityname' => 'Nom',\n\t\t'integrityresult' => 'Resultat',\n\t\t'integrityfix' => 'Solució automàtica de problemes',\n\t\t'customer_show_news_feed' => 'Mostrar notícies al panell del client',\n\t\t'customer_news_feed_url' => [\n\t\t\t'title' => 'Utilitzar un canal RSS personalitzat',\n\t\t\t'description' => 'Especifiqui un canal RSS personalitzat que es mostrarà als vostres clients en el vostre tauler de control.<br/><small>Deixi aquest camp buit per utilitzar el canal de notícies oficial de froxlor (https://inside.froxlor.org/news/).</small>'\n\t\t],\n\t\t'movetoadmin' => 'Moure client',\n\t\t'movecustomertoadmin' => 'Mou el client a l\\'administrador/revenedor seleccionat<br/><small>Deixa això buit per no fer canvis.<br/>Si l\\'administrador desitjat no apareix a la llista, el seu límit de clients ha estat assolit.</small>',\n\t\t'note' => 'Nota',\n\t\t'mod_fcgid_umask' => [\n\t\t\t'title' => 'Màscara (per defecte: 022)'\n\t\t],\n\t\t'apcuinfo' => 'Informació APCu',\n\t\t'opcacheinfo' => 'Informació OPcache',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Utilitzar Let\\'s Encrypt',\n\t\t\t'description' => 'Obtingui un certificat gratuït de <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. El certificat es crearà i renovarà automàticament.<br /><strong class=\"text-danger\">ATENCIÓ:</strong> Si els comodins estan activats, aquesta opció es desactivarà automàticament.'\n\t\t],\n\t\t'autoupdate' => 'Actualitza automàticament',\n\t\t'server_php' => 'PHP',\n\t\t'dnsenabled' => 'Habilitar editor DNS',\n\t\t'froxlorvhost' => 'Configuració VirtualHost de froxlor ',\n\t\t'hostname' => 'Nom de host',\n\t\t'memory' => 'Memòria en ús',\n\t\t'webserversettings_ssl' => 'Configuració SSL del servidor web',\n\t\t'domain_hsts_maxage' => [\n\t\t\t'title' => 'Seguretat estricta de transport HTTP (HSTS)',\n\t\t\t'description' => 'Especifiqui el valor d\\'edat màxima per a la capçalera Strict-Transport-Security (STS)<br/>El valor <i>0</i> desactivarà HSTS per al domini. La majoria dels usuaris estableixen un valor de <i>31536000</i> (un any).'\n\t\t],\n\t\t'domain_hsts_incsub' => [\n\t\t\t'title' => 'Incloure HSTS per a qualsevol subdomini',\n\t\t\t'description' => 'La directiva opcional \"includeSubDomains\", si és present, indica a la UA que la Política HSTS s\\'aplica a aquest Host HSTS així com a qualsevol subdomini del nom de domini del host.'\n\t\t],\n\t\t'domain_hsts_preload' => [\n\t\t\t'title' => 'Incloure domini a la <a href=\"https://hstspreload.org/\" target=\"_blank\">llista de precàrrega HSTS</a>',\n\t\t\t'description' => 'Si vol que aquest domini s\\'inclogui a la llista de precàrrega HSTS mantinguda per Chrome (i utilitzada per Firefox i Safari), activeu aquesta opció.<br />L\\'enviament de la directiva de precàrrega des del vostre lloc pot tenir CONSEQÜÈNCIES PERMANENTS i impedir que els usuaris accedeixin al seu lloc i a qualsevol dels seus subdominis.<br />Si us plau, llegiu els detalls a <a href=\"https://hstspreload.org/#removal\" target=\"_blank\">https://hstspreload .org/#removal</a> abans d\\'enviar la capçalera amb \"preload\".'\n\t\t],\n\t\t'domain_ocsp_stapling' => [\n\t\t\t'title' => 'OCSP stapling',\n\t\t\t'description' => 'Consulti <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/OCSP_stapling\">Wikipedia</a> per a una explicació detallada del grapat OCSP',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">AVÍS:</strong> Es requereix la versió 1.3.7 o superior de Nginx per al OCSP stapling. Si la vostra versió és anterior, el servidor web NO s\\'iniciarà correctament mentre el grapat OCSP estigui activat!'\n\t\t],\n\t\t'domain_http2' => [\n\t\t\t'title' => 'Suport HTTP2',\n\t\t\t'description' => 'Consulti <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/2\">Wikipedia</a> per a una explicació detallada de HTTP2'\n\t\t],\n\t\t'testmail' => 'Prova SMTP',\n\t\t'phpsettingsforsubdomains' => 'Aplicar php-config a tots els subdominis:',\n\t\t'plans' => [\n\t\t\t'name' => 'Nom del pla',\n\t\t\t'description' => 'Descripció',\n\t\t\t'last_update' => 'Última actualització',\n\t\t\t'plans' => 'Plans d\\'allotjament',\n\t\t\t'plan_details' => 'Detalls del pla',\n\t\t\t'add' => 'Afegir nou pla',\n\t\t\t'edit' => 'Editar pla',\n\t\t\t'use_plan' => 'Aplicar pla'\n\t\t],\n\t\t'notryfiles' => [\n\t\t\t'title' => 'No s\\'autogeneren try_files',\n\t\t\t'description' => 'Digui \"sí\" aquí si vol especificar una directiva try_files personalitzada en specialsettings (necessària per a alguns plugins de wordpress).'\n\t\t],\n\t\t'logviewenabled' => 'Habilitar accés als logs d\\'accéss/error',\n\t\t'novhostcontainer' => '<br /><br /><small class=\"text-danger\">Cap de les IPs i ports té activada l\\'opció \"Crear vHost-Container\", molts ajustaments aquí no estaran disponibles</small>',\n\t\t'ownsslvhostsettings' => 'Configuració pròpia de SSL vHost',\n\t\t'domain_override_tls' => 'Anul·lar la configuració TLS del sistema',\n\t\t'domain_override_tls_addinfo' => '<br /><span class=\"text-danger\">Només s\\'utilitza si l\\'opció \"Override system TLS settings\" està configurada com a \"Sí\".</span>',\n\t\t'domain_sslenabled' => 'Habilitar l\\'ús de SSL',\n\t\t'domain_honorcipherorder' => 'Respecteu l\\'ordre de xifrat (servidor), per defecte <strong>no</strong>',\n\t\t'domain_sessiontickets' => 'Habilitar TLS sessiontickets (RFC 5077), per defecte <strong>sí</strong>',\n\t\t'domain_sessionticketsenabled' => [\n\t\t\t'title' => 'Habilitar l\\'ús de TLS sessiontickets globalment',\n\t\t\t'description' => 'Per defecte <strong>sí</strong><br/>Requereix apache-2.4.11+ o nginx-1.5.9+'\n\t\t],\n\t\t'domaindefaultalias' => 'Valor per defecte de ServerAlias per a nous dominis',\n\t\t'smtpsettings' => 'Configuració SMTP',\n\t\t'smtptestaddr' => 'Enviar correu de prova a',\n\t\t'smtptestnote' => 'Tingueu en compte que els valors següents reflecteixen la configuració actual i només es poden ajustar allà (vegeu l\\'enllaç a la cantonada superior dreta)',\n\t\t'smtptestsend' => 'Enviar correu de prova',\n\t\t'mysqlserver' => [\n\t\t\t'mysqlserver' => 'Servidor MySQL',\n\t\t\t'dbserver' => 'Servidor #',\n\t\t\t'caption' => 'Descripció',\n\t\t\t'host' => 'Nom de host / IP',\n\t\t\t'port' => 'Port',\n\t\t\t'user' => 'Usuari privilegiat',\n\t\t\t'add' => 'Afegir nou servidor MySQL',\n\t\t\t'edit' => 'Editar servidor MySQL',\n\t\t\t'password' => 'Contrasenya d\\'usuari privilegiat',\n\t\t\t'password_emptynochange' => 'Nova contrasenya, deixar buit per a cap canvi',\n\t\t\t'allowall' => [\n\t\t\t\t'title' => 'Permetre l\\'ús d\\'aquest servidor a tots els clients existents actualment',\n\t\t\t\t'description' => 'Estableix aquesta opció a \"Cert\" si voleu permetre l\\'ús d\\'aquest servidor de base de dades a tots els clients existents perquè hi puguin afegir bases de dades. Aquesta configuració no és permanent, però es pot executar diverses vegades.'\n\t\t\t],\n\t\t\t'testconn' => 'Provar la connexió en desar',\n\t\t\t'ssl' => 'Utilitzar SSL per a la connexió al servidor de base de dades',\n\t\t\t'ssl_cert_file' => 'La ruta del fitxer a l\\'autoritat del certificat SSL',\n\t\t\t'verify_ca' => 'Habilitar la verificació del certificat SSL del servidor'\n\t\t],\n\t\t'settings_importfile' => 'Escollir fitxer d\\'importació',\n\t\t'documentation' => 'Documentació',\n\t\t'adminguide' => 'Guia de l\\'administrador',\n\t\t'userguide' => 'Guia de l\\'usuari',\n\t\t'apiguide' => 'Guia de la API'\n\t],\n\t'apcuinfo' => [\n\t\t'clearcache' => 'Esborrar memòria cau d\\'APCu',\n\t\t'generaltitle' => 'Informació general sobre la memòria cau',\n\t\t'version' => 'Versió d\\'APCu',\n\t\t'phpversion' => 'Versió PHP',\n\t\t'host' => 'Host APCu',\n\t\t'sharedmem' => 'Memòria compartida',\n\t\t'sharedmemval' => '%d Segment(s) amb %s (%s memòria)',\n\t\t'start' => 'Hora d\\'inici',\n\t\t'uptime' => 'Temps d\\'activitat',\n\t\t'upload' => 'Suport de càrrega de fitxers',\n\t\t'cachetitle' => 'Informació de la memòria cau',\n\t\t'cvar' => 'Variables en memòria cau',\n\t\t'hit' => 'Encerts',\n\t\t'miss' => 'Errors',\n\t\t'reqrate' => 'Taxa de peticions (encerts, errors)',\n\t\t'creqsec' => 'Peticions de memòria cau/segon',\n\t\t'hitrate' => 'Taxa d\\'encerts',\n\t\t'missrate' => 'Percentatge d\\'errors',\n\t\t'insrate' => 'Taxa d\\'insercions',\n\t\t'cachefull' => 'Recompte de memòria cau plena',\n\t\t'runtime' => 'Configuració de temps d\\'execució',\n\t\t'memnote' => 'Ús de memòria',\n\t\t'total' => 'Total',\n\t\t'free' => 'Lliure',\n\t\t'used' => 'Utilitzat',\n\t\t'hitmiss' => 'Encerts i errors',\n\t\t'detailmem' => 'Ús detallat de memòria i fragmentació',\n\t\t'fragment' => 'Fragmentació',\n\t\t'nofragment' => 'Sense Fragmentació',\n\t\t'fragments' => 'Fragments'\n\t],\n\t'apikeys' => [\n\t\t'no_api_keys' => 'No s\\'han trobat claus API',\n\t\t'key_add' => 'Afegir una nova clau',\n\t\t'apikey_removed' => 'La clau api amb l\\'id #%s ha estat eliminada amb èxit',\n\t\t'apikey_added' => 'S\\'ha generat correctament una nova clau api',\n\t\t'clicktoview' => 'Faci clic per veure',\n\t\t'allowed_from' => 'Permès des de',\n\t\t'allowed_from_help' => 'Llista separada per comes d\\'adreces IP / xarxes.<br />Per defecte és buit (permetre des de tots).',\n\t\t'valid_until' => 'Vàlid fins',\n\t\t'valid_until_help' => 'Data fins a la qual és vàlid, format AAAA-MM-DDThh:mm'\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Contrasenya antiga',\n\t\t'new_password' => 'Nova contrasenya',\n\t\t'new_password_confirm' => 'Confirmi la contrasenya',\n\t\t'new_password_ifnotempty' => 'Nova contrasenya (buida = sense canvis)',\n\t\t'also_change_ftp' => ' canviï també la contrasenya del compte FTP principal',\n\t\t'also_change_stats' => ' canviï també la contrasenya de la pàgina d\\'estadístiques'\n\t],\n\t'cron' => [\n\t\t'cronname' => 'Nom de la tasca cron',\n\t\t'lastrun' => 'última execució',\n\t\t'interval' => 'interval',\n\t\t'isactive' => 'activat',\n\t\t'description' => 'descripció',\n\t\t'changewarning' => 'Canviar aquests valors pot tenir una causa negativa en el comportament de froxlor i les tasques automatitzades.<br/>Si us plau, només canviï els valors aquí, si està segur del que està fent.'\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'cap descripció especificada',\n\t\t'cron_tasks' => 'Generació d\\'arxius de configuració',\n\t\t'cron_legacy' => 'Tasca cron heretadada (antic)',\n\t\t'cron_traffic' => 'Càlcul de trànsit',\n\t\t'cron_usage_report' => 'Informes web i de trànsit',\n\t\t'cron_mailboxsize' => 'Càlcul de la mida de la bústia',\n\t\t'cron_letsencrypt' => 'Actualització de certificats Let\\'s Encrypt',\n\t\t'cron_backup' => 'Processar tasques de còpia de seguretat'\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'Configuració de Tasques Cron',\n\t\t'cronjobintervalv' => 'Valor de l\\'interval de temps d\\'execució',\n\t\t'cronjobinterval' => 'Interval d\\'execució'\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Encara no executat'\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => 'minuts',\n\t\t'hours' => 'hores',\n\t\t'days' => 'dies',\n\t\t'weeks' => 'setmanes',\n\t\t'months' => 'mesos'\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Directori d\\'inici',\n\t\t'name' => 'Nom complet',\n\t\t'firstname' => 'Nom',\n\t\t'lastname' => 'Cognoms',\n\t\t'company' => 'Empresa',\n\t\t'nameorcompany_desc' => 'Es requereix nom/cognom o empresa',\n\t\t'street' => 'Carrer',\n\t\t'zipcode' => 'Codi postal',\n\t\t'city' => 'Ciutat',\n\t\t'phone' => 'Telèfon',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'Correu electrònic',\n\t\t'customernumber' => 'ID de client',\n\t\t'diskspace' => 'Espai web',\n\t\t'traffic' => 'Trànsit',\n\t\t'mysqls' => 'Bases de dades MySQL',\n\t\t'emails' => 'Adreces de correu electrònic',\n\t\t'accounts' => 'Comptes de correu electrònic',\n\t\t'forwarders' => 'Remitents de correu electrònic',\n\t\t'ftps' => 'Comptes FTP',\n\t\t'subdomains' => 'Subdominis',\n\t\t'domains' => 'Dominis',\n\t\t'mib' => 'MiB',\n\t\t'gib' => 'GiB',\n\t\t'title' => 'Títol',\n\t\t'country' => 'País',\n\t\t'email_quota' => 'Quota de correu electrònic',\n\t\t'email_imap' => 'Correu IMAP',\n\t\t'email_pop3' => 'Correu electrònic POP3',\n\t\t'sendinfomail' => 'Enviar dades per correu electrònic',\n\t\t'generated_pwd' => 'Suggeriment de contrasenya',\n\t\t'usedmax' => 'Utilitzat / Màxim',\n\t\t'services' => 'Serveis',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Utilitzar Let\\'s Encrypt',\n\t\t\t'description' => 'Obtingui un certificat gratuït de <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. El certificat es crearà i renovarà automàticament.'\n\t\t],\n\t\t'selectserveralias_addinfo' => 'Aquesta opció es pot configurar en editar el domini. El seu valor inicial s\\'hereta del domini pare.',\n\t\t'total_diskspace' => 'Espai total en disc',\n\t\t'mysqlserver' => 'Servidor mysql utilitzable'\n\t],\n\t'diskquota' => 'Quota',\n\t'dns' => [\n\t\t'destinationip' => 'IP(s) del domini',\n\t\t'standardip' => 'IP estàndard del servidor',\n\t\t'a_record' => 'Registre A (IPv6 opcional)',\n\t\t'cname_record' => 'Registre CNAME',\n\t\t'mxrecords' => 'Definir registres MX',\n\t\t'standardmx' => 'Registre MX estàndard del servidor',\n\t\t'mxconfig' => 'Registres MX personalitzats',\n\t\t'priority10' => 'Prioritat 10',\n\t\t'priority20' => 'Prioritat 20',\n\t\t'txtrecords' => 'Definir registres TXT',\n\t\t'txtexample' => 'Exemple (entrada SPF):<br/>v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'howitworks' => 'Aquí podeu gestionar les entrades DNS per al vostre domini. Tingueu en compte que froxlor generarà automàticament els registres NS/MX/A/AAAA per tu. Les entrades personalitzades són preferibles, només les entrades que faltin seran generades automàticament.'\n\t],\n\t'dnseditor' => [\n\t\t'edit' => 'editar DNS',\n\t\t'records' => 'Registres',\n\t\t'notes' => [\n\t\t\t'A' => 'Adreça IPv4 de 32 bits, utilitzada per mapejar noms de host a una adreça IP del host.',\n\t\t\t'AAAA' => 'Adreça IPv6 de 128 bits, utilitzada per mapejar noms de host a una adreça IP del host.',\n\t\t\t'CAA' => 'El registre de recursos CAA permet al titular d\\'un nom de domini DNS especificar una o diverses Autoritats de Certificació (CA) autoritzades a emetre certificats per a aquest domini.<br/>Estructura: <code>flag tag[issue|issuewild|iodef|contactmail|contactphone] value</code><br/>Exemple: <code>0 issue \"ca.example.net\"<br/></code> 0 <code>iodef \"mailto:security@example.com\"< /code>',\n\t\t\t'CNAME' => 'Àlies del nom de domini, la cerca DNS continuarà reintentant la cerca amb el nou nom. Només és possible per a subdominis.',\n\t\t\t'DNAME' => 'Crea un àlies per a tot un subarbre de l\\'arbre de noms de domini',\n\t\t\t'LOC' => 'Informació sobre la ubicació geogràfica d\\'un nom de domini.<br/>Estructura: <code>( d1 [m1 [s1]] { d2 [m2 [s2]] { alt[\"m\"] [siz[\"m\"] [hp[\"m\"] [vp[\"m\"]]]] )</code><br/>Descripció: <code>d1</code>: [0 . <code>. 90] (graus de latitud)\n\t\t\td2: [0 .. 180] (graus de longitud)\n\t\t\tm1, m2: [0 .. 59] (minuts latitud/longitud)\n\t\t\ts1, s2: [0 .. 59,999] (segons latitud/longitud)\n\t\t\talt: [-100000.00 .. 42849672.95] POR .01 (altitud en metres)\n\t\t\tsiz, hp, vp: [0 .. 90000000.00] (mida/precisió en metres)</code><br/>Exemple: <code>52 22 23.000 N 4 53 32.000 E -2.00m 0.00m 10000m 10m</code>',\n\t\t\t'MX' => 'Registre d\\'intercanvi de correu, assigna un nom de domini a un servidor de correu per a aquest domini.<br/>Exemple: <code>10 mail.example.com</code><br/>Nota : Per a la prioritat, utilitzeu el camp anterior.',\n\t\t\t'NS' => 'Delega una zona DNS perquè utilitzi els servidors de noms autoritatius indicats.',\n\t\t\t'RP' => 'Registre de persona responsable<br/>Estructura: <code>mailbox[substituir @ per un punt] txt-record-name</code><br/>Exemple: <code>team.froxlor.org . froxlor.org.</code>',\n\t\t\t'SRV' => 'Registre d\\'ubicació de servei, utilitzat per a protocols més recents en lloc de crear registres específics de protocol com MX.<br/>Estructura: <code>priority weight port target</code><br/>Exemple : <code>0 5 5060 sipserver.example.com.</code><br/>Nota: Per a la prioritat, utilitzeu el camp anterior.',\n\t\t\t'SSHFP' => 'El registre de recursos SSHFP s\\'utilitza per publicar empremtes digitals de claus d\\'intèrpret d\\'ordres segur (SSH) al DNS.<br/>Estructura: <code>tipus d\\'algorisme empremta digital</code><br/ >Algorismes: 0: reservat <code>, 1: RSA, 2: DSA, 3: ECDSA, 4: Ed25519, 6: Ed448</code><br/>Tipus: 0 <code>: reservat, 1: SHA- 1, 2: SHA-256</code><br/>Exemple: <code>2 1 123456789abcdef67890123456789abcdef67890</code>',\n\t\t\t'TXT' => 'Text descriptiu de lliure definició.'\n\t\t]\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'Ruta OpenBasedir',\n\t\t'docroot' => 'Ruta del camp anterior',\n\t\t'homedir' => 'Directori d\\'inici',\n\t\t'docparent' => 'Directori pare de la ruta del camp anterior',\n    'ssl_certificate_placeholder' => '---- BEGIN CERTIFICATE---' . PHP_EOL . '[...]' . PHP_EOL . '----END CERTIFICATE----',\n    'ssl_key_placeholder' => '---- BEGIN RSA PRIVATE KEY-----' . PHP_EOL . '[...]' . PHP_EOL . '-----END RSA PRIVATE KEY-----',\n\t],\n\t'domains' => [\n\t\t'description' => 'Aquí pot crear (sub)dominis i canviar les seves rutes.<br/>El sistema necessitarà algun temps per aplicar la nova configuració després de cada canvi.',\n\t\t'domainsettings' => 'Configuració del domini',\n\t\t'domainname' => 'Nom de domini',\n\t\t'subdomain_add' => 'Crear subdomini',\n\t\t'subdomain_edit' => 'Editar (sub)domini',\n\t\t'wildcarddomain' => 'Crear com a domini comodí?',\n\t\t'aliasdomain' => 'Àlies per a domini',\n\t\t'noaliasdomain' => 'Sense àlies de domini',\n\t\t'hasaliasdomains' => 'Té domini(s) àlies',\n\t\t'statstics' => 'Estadístiques d\\'ús',\n\t\t'isassigneddomain' => 'Domini assignat',\n\t\t'add_date' => 'Afegit a froxlor',\n\t\t'registration_date' => 'Afegit al registre',\n\t\t'topleveldomain' => 'Domini Top-Level',\n\t\t'associated_with_domain' => 'Associat',\n\t\t'aliasdomains' => 'Dominis d\\'àlies',\n\t\t'redirectifpathisurl' => 'Codi de redirecció (per defecte: buit)',\n\t\t'redirectifpathisurlinfo' => 'Només ha de seleccionar una d\\'aquestes opcions si heu introduït un URL com a ruta<br/><strong class=\"text-danger\">NOTA:</strong> Els canvis només s\\'apliquen si la ruta indicada és una URL.',\n\t\t'issubof' => 'Aquest domini és un subdomini d\\'un altre domini',\n\t\t'issubofinfo' => 'Si vol afegir un subdomini com a domini complet, haureu d\\'establir-lo al domini correcte (per exemple, si voleu afegir \"www.domini.tld\", haureu de seleccionar \"domini.tld\").',\n\t\t'nosubtomaindomain' => 'No és subdomini d\\'un domini complet',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'Adreces IP',\n\t\t\t'description' => 'Especifica una o més adreces IP per al domini.<br/><br/><div class=\"text-danger\">NOTA: Les adreces IP no es poden canviar quan el domini està configurat com a <strong>àlies-domini</ strong> d\\'un altre domini.</div>'\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'Adreça(es) IP SSL'\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'Redirecció SSL',\n\t\t\t'description' => 'Aquesta opció crea redireccionaments per a vhosts no SSL de manera que totes les peticions són redirigides al vhost SSL.<br/><br/>p.e. una petició a <strong>http://domini.tld/</strong> us redirigirà a <strong>https://domini.tld/</strong>'\n\t\t],\n\t\t'serveraliasoption_wildcard' => 'Comodí (*.domini.tld)',\n\t\t'serveraliasoption_www' => 'WWW (www.domini.tld)',\n\t\t'serveraliasoption_none' => 'Sense àlies',\n\t\t'domain_import' => 'Importar dominis',\n\t\t'import_separator' => 'Separador',\n\t\t'import_offset' => 'Desplaçament',\n\t\t'import_file' => 'Arxiu CSV',\n\t\t'import_description' => 'Per obtenir informació detallada sobre l\\'estructura del fitxer d\\'importació i sobre com fer la importació correctament, visiti <a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\" class=\"alert-link\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a>.',\n\t\t'ssl_redirect_temporarilydisabled' => '<br/>La redirecció SSL es desactiva temporalment mentre es genera un certificat Let\\'s Encrypt nou. Es tornarà a activar un cop generat el certificat.',\n\t\t'termination_date' => 'Data de terminació',\n\t\t'termination_date_overview' => 'acabat a partir de ',\n\t\t'ssl_certificates' => 'Certificats SSL',\n\t\t'ssl_certificate_removed' => 'El certificat amb l\\'id #%s s\\'ha eliminat amb èxit',\n\t\t'ssl_certificate_error' => 'Error en llegir el certificat per al domini: %s',\n\t\t'no_ssl_certificates' => 'No hi ha dominis amb certificat SSL',\n\t\t'isaliasdomainof' => 'És àlies de domini per a %s',\n\t\t'isbinddomain' => 'Crear zona DNS',\n\t\t'dkimenabled' => 'DKIM activat',\n\t\t'openbasedirenabled' => 'Restricció de Openbasedir',\n\t\t'hsts' => 'HSTS habilitat',\n\t\t'aliasdomainid' => 'ID de àlies de domini'\n\t],\n\t'emails' => [\n\t\t'description' => 'Aquí pots crear i modificar les teves adreces de correu electrònic.<br/>Un compte és com la bústia que tens davant de casa. Si algú us envia un correu electrònic, aquest caurà al compte.<br/><br/>Per descarregar els vostres correus electrònics utilitzeu la següent configuració al vostre programa de correu: (Les dades en <i>cursiva</i> s\\'han de canviar pels equivalents que heu escrit!)<br/>Hostname: <b><i>nom del domini</i></b><br/>Username: <b><i>nom del compte / adreça de correu electrònic</i></b><br/>password: <b><i>la contrasenya que heu triat</i></b>',\n\t\t'emailaddress' => 'Adreça de correu electrònic',\n\t\t'emails_add' => 'Crear adreça de correu electrònic',\n\t\t'emails_edit' => 'Editar adreça de correu electrònic',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => 'Definir com a adreça \"catchall\"?',\n\t\t'account' => 'Compte',\n\t\t'account_add' => 'Crear compte',\n\t\t'account_delete' => 'Eliminar compte',\n\t\t'from' => 'Origen',\n\t\t'to' => 'Destí',\n\t\t'forwarders' => 'Transitaris',\n\t\t'forwarder_add' => 'Crear expedidor',\n\t\t'alternative_emailaddress' => 'Adreça de correu electrònic alternativa',\n\t\t'quota' => 'Quota',\n\t\t'noquota' => 'Sense quota',\n\t\t'updatequota' => 'Actualitzar quota',\n\t\t'quota_edit' => 'Modificar quota de correu electrònic',\n\t\t'noemaildomainaddedyet' => 'Encara no té un domini (de correu electrònic) al vostre compte.',\n\t\t'back_to_overview' => 'Tornar a la vista general de dominis',\n\t\t'accounts' => 'Comptes',\n\t\t'emails' => 'Adreces'\n\t],\n\t'error' => [\n\t\t'error' => 'Error',\n\t\t'directorymustexist' => 'El directori %s ha d\\'existir. Si us plau, crea\\'l amb el teu client FTP.',\n\t\t'filemustexist' => 'El fitxer %s ha d\\'existir.',\n\t\t'allresourcesused' => 'Ja ha utilitzat tots els seus recursos.',\n\t\t'domains_cantdeletemaindomain' => 'No pot suprimir un domini assignat.',\n\t\t'domains_canteditdomain' => 'No pot editar aquest domini. Ha estat desactivat per l\\'administrador.',\n\t\t'domains_cantdeletedomainwithemail' => 'No pot suprimir un domini que s\\'utilitza com a domini de correu electrònic. Elimineu primer totes les adreces de correu electrònic.',\n\t\t'firstdeleteallsubdomains' => 'Abans de crear un domini comodí, heu d\\'eliminar tots els subdominis.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Ja ha definit un domini comodí per a aquest domini.',\n\t\t'ftp_cantdeletemainaccount' => 'No pot suprimir el vostre compte FTP principal',\n\t\t'login' => 'El nom d\\'usuari o la contrasenya que heu introduït són incorrectes. Intenta-ho de nou.',\n\t\t'login_blocked' => 'Aquest compte ha estat suspès a causa de masses errors d\\'inici de sessió. <br/>Intenteu-ho de nou en %s segons.',\n\t\t'notallreqfieldsorerrors' => 'No ha emplenat tots els camps o n\\'ha omplert alguns incorrectament.',\n\t\t'oldpasswordnotcorrect' => 'La contrasenya antiga no és correcta.',\n\t\t'youcantallocatemorethanyouhave' => 'No es pot assignar més recursos dels que té.',\n\t\t'mustbeurl' => 'No heu escrit una URL vàlida o completa (per exemple http://algundomini.com/error404.htm)',\n\t\t'invalidpath' => 'No ha triat una URL vàlida (potser té problemes amb la llista d\\'adreces?)',\n\t\t'stringisempty' => 'Falta informació al camp',\n\t\t'stringiswrong' => 'Camp incorrecte',\n\t\t'newpasswordconfirmerror' => 'La nova contrasenya i la de confirmació no coincideixen',\n\t\t'mydomain' => 'Domini',\n\t\t'mydocumentroot' => 'Documentroot',\n\t\t'loginnameexists' => 'El nom d\\'usuari %s ja existeix',\n\t\t'emailiswrong' => 'L\\'adreça de correu %s conté caràcters no vàlids o està incomplet',\n\t\t'alternativeemailiswrong' => 'Les %s adreces de correu electrònic alternatives donades per enviar les credencials semblen no ser vàlides',\n\t\t'loginnameiswrong' => 'El nom d\\'usuari \"%s\" conté caràcters il·legals.',\n\t\t'loginnameiswrong2' => 'El nom d\\'usuari conté massa caràcters. Només es permeten %s caràcters.',\n\t\t'userpathcombinationdupe' => 'La combinació de nom d\\'usuari i ruta ja existeix',\n\t\t'patherror' => 'Error general. La ruta no pot estar buida',\n\t\t'errordocpathdupe' => 'L\\'opció per a la ruta %s ja existeix',\n\t\t'adduserfirst' => 'Si us plau, creï primer un client',\n\t\t'domainalreadyexists' => 'El domini %s ja està assignat a un client',\n\t\t'nolanguageselect' => 'No s\\'ha seleccionat cap idioma.',\n\t\t'nosubjectcreate' => 'Ha de definir un tema per a aquesta plantilla de correu.',\n\t\t'nomailbodycreate' => 'Ha de definir un text de correu per a aquesta plantilla de correu.',\n\t\t'templatenotfound' => 'La plantilla no s\\'ha trobat.',\n\t\t'alltemplatesdefined' => 'No pot definir més plantilles, tots els idiomes ja estan suportats.',\n\t\t'wwwnotallowed' => 'www no està permès per a subdominis.',\n\t\t'subdomainiswrong' => 'El subdomini %s conté caràcters no vàlids.',\n\t\t'domaincantbeempty' => 'El nom de domini no pot estar buit.',\n\t\t'domainexistalready' => 'El domini %s ja existeix.',\n\t\t'domainisaliasorothercustomer' => 'L\\'àlies de domini seleccionat és en ell mateix un àlies de domini, té una combinació ip/port diferent o pertany a un altre client.',\n\t\t'emailexistalready' => 'L\\'adreça de correu electrònic %s ja existeix.',\n\t\t'maindomainnonexist' => 'El domini principal %s no existeix.',\n\t\t'destinationnonexist' => 'Si us plau, creeu el vostre redireccionador al camp \"Destí\".',\n\t\t'destinationalreadyexistasmail' => 'El remitent a %s ja existeix com a adreça de correu electrònic activa.',\n\t\t'destinationalreadyexist' => 'Ja ha definit un reenviador per \"%s\".',\n\t\t'destinationiswrong' => 'La redirecció %s conté caràcters no vàlids o està incompleta.',\n\t\t'backupfoldercannotbedocroot' => 'La carpeta per a les còpies de seguretat no pot ser la vostra carpeta d\\'inici, escolliu una carpeta dins de la vostra carpeta d\\'inici, per exemple, /backups.',\n\t\t'templatelanguagecombodefined' => 'La combinació idioma/plantilla seleccionada ja ha estat definida.',\n\t\t'templatelanguageinvalid' => 'L\\'idioma seleccionat no existeix.',\n\t\t'ipstillhasdomains' => 'La combinació IP/Port que vol eliminar encara té dominis assignats, si us plau reassigneu-los a altres combinacions IP/Port abans d\\'eliminar aquesta combinació IP/Port.',\n\t\t'cantdeletedefaultip' => 'No pot esborrar la combinació IP/Port per defecte, si us plau fes una altra combinació IP/Port per defecte abans d\\'esborrar aquesta combinació IP/Port.',\n\t\t'cantdeletesystemip' => 'No pot suprimir la última IP del sistema, creeu una nova combinació IP/Port per a la IP del sistema o canvieu la IP del sistema.',\n\t\t'myipaddress' => 'IP',\n\t\t'myport' => 'Port',\n\t\t'myipdefault' => 'Ha de seleccionar una combinació IP/Port que es converteixi en predeterminada.',\n\t\t'myipnotdouble' => 'Aquesta combinació IP/Port ja existeix.',\n\t\t'admin_domain_emailsystemhostname' => 'El nom de host del servidor no es pot utilitzar com a domini del client.',\n\t\t'cantchangesystemip' => 'No pot canviar la última IP del sistema, creeu una nova combinació IP/Port per a la IP del sistema o canvieu la IP del sistema.',\n\t\t'sessiontimeoutiswrong' => 'Només es permet un temps d\\'espera de sessió numèric.',\n\t\t'maxloginattemptsiswrong' => 'Només es permeten \"intents d\\'inici de sessió màxims\" numèrics.',\n\t\t'deactivatetimiswrong' => 'Només es permet \"temps de desactivació\" numèric.',\n\t\t'accountprefixiswrong' => 'El \"prefix del client\" és incorrecte.',\n\t\t'mysqlprefixiswrong' => 'El \"prefix SQL\" és incorrecte.',\n\t\t'ftpprefixiswrong' => 'El \"prefix FTP\" és incorrecte.',\n\t\t'ipiswrong' => 'L\\'\"adreça IP\" és incorrecte. Només es permet una adreça IP vàlida.',\n\t\t'vmailuidiswrong' => 'El \"mails-uid\" és incorrecte. Només es permet un UID numèric.',\n\t\t'vmailgidiswrong' => 'El \"mails-gid\" és incorrecte. Només es permet un GID numèric.',\n\t\t'adminmailiswrong' => 'La direcció del remitent és incorrecte. Només es permet una adreça de correu electrònic vàlida.',\n\t\t'pagingiswrong' => 'El valor \"entries per page\" és incorrecte. Només es permeten caràcters numèrics.',\n\t\t'phpmyadminiswrong' => 'L\\'enllaç phpMyAdmin no és un enllaç vàlid.',\n\t\t'webmailiswrong' => 'L\\'enllaç webmail no és un enllaç vàlid.',\n\t\t'webftpiswrong' => 'L\\'enllaç WebFTP no és un enllaç vàlid.',\n\t\t'stringformaterror' => 'El valor del camp \"%s\" no té el format esperat.',\n\t\t'loginnameisusingprefix' => 'No pot crear comptes que comencen per \"%s\", ja que aquest prefix està configurat per ser utilitzat a l\\'assignació automàtica de noms de compte. Introduïu un altre nom de compte.',\n\t\t'loginnameissystemaccount' => 'El compte \"%s\" ja existeix al sistema i no es pot utilitzar. Si us plau, introduïu un altre nom de compte.',\n\t\t'youcantdeleteyourself' => 'No es pot esborrar per raons de seguretat.',\n\t\t'youcanteditallfieldsofyourself' => 'Nota: No pot editar tots els camps del vostre compte per raons de seguretat.',\n\t\t'documentrootexists' => 'El directori \"%s\" ja existeix per a aquest client. Si us plau, elimineu-lo abans d\\'afegir el client de nou.',\n\t\t'norepymailiswrong' => 'L\\'adreça \"Noreply-address\" és incorrecta. Només es permet una adreça de correu electrònic vàlida.',\n\t\t'logerror' => 'Registre d\\'error: %s',\n\t\t'nomessagetosend' => 'No ha introduït cap missatge.',\n\t\t'norecipientsgiven' => 'No ha especificat cap destinatari',\n\t\t'errorsendingmail' => 'El missatge a \"%s\" ha fallat',\n\t\t'errorsendingmailpub' => 'El missatge a l\\'adreça de correu electrònic indicada ha fallat',\n\t\t'cannotreaddir' => 'No s\\'ha pogut llegir el directori \"%s\".',\n\t\t'invalidip' => 'Adreça IP no vàlida: %s',\n\t\t'invalidmysqlhost' => 'Adreça de host MySQL no vàlida: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'No pot activar Webalizer i AWstats al mateix temps, si us plau escolliu un d\\'ells.',\n\t\t'cannotwritetologfile' => 'No es pot obrir el fitxer de registre %s per escriure',\n\t\t'vmailquotawrong' => 'La mida de la quota ha de ser un número positiu.',\n\t\t'allocatetoomuchquota' => 'Ha intentat assignar %s MB de quota, però no en té suficients.',\n\t\t'missingfields' => 'No s\\'han omplert tots els camps obligatoris.',\n\t\t'requiredfield' => 'Aquest camp és obligatori.',\n\t\t'accountnotexisting' => 'El compte de correu electrònic indicat no existeix.',\n\t\t'nopermissionsorinvalidid' => 'No té permisos suficients per canviar aquesta configuració o s\\'ha introduït un identificador no vàlid.',\n\t\t'phpsettingidwrong' => 'No hi ha una configuració PHP amb aquest id.',\n\t\t'descriptioninvalid' => 'La descripció és massa curta, massa llarga o conté caràcters il·legals.',\n\t\t'info' => 'Informació',\n\t\t'filecontentnotset' => 'El fitxer no pot estar buit.',\n\t\t'index_file_extension' => 'L\\'extensió del fitxer índex ha de tenir entre 1 i 6 caràcters. L\\'extensió només pot contenir caràcters com a-z, A-Z y 0-9',\n\t\t'customerdoesntexist' => 'El client seleccionat no existeix.',\n\t\t'admindoesntexist' => 'L\\'administrador escollit no existeix.',\n\t\t'ipportdoesntexist' => 'La combinació ip/port que ha triat no existeix.',\n\t\t'usernamealreadyexists' => 'El nom d\\'usuari %s ja existeix.',\n\t\t'plausibilitychecknotunderstood' => 'No s\\'ha entès la resposta de la comprovació de plausibilitat.',\n\t\t'errorwhensaving' => 'S\\'ha produït un error en desar els %s camps',\n\t\t'hiddenfieldvaluechanged' => 'El valor del camp ocult \"%s\" ha canviat en editar la configuració.<br/><br/>Això no sol ser un gran problema, però la configuració no s\\'ha pogut desar per aquest motiu.',\n\t\t'notrequiredpasswordlength' => 'La contrasenya introduïda és massa curta. Si us plau, introduïu almenys %s caràcters.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Ups, un camp que s\\'hauria de mostrar com una opció a la vista de configuració no és un tipus exceptuat. Pots culpar els desenvolupadors per això. Això no hauria de passar.',\n\t\t'pathmaynotcontaincolon' => 'La ruta introduïda no ha de contenir dos punts (\":\"). Introduïu un valor de ruta correcte.',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => 'La complexitat de la contrasenya especificada no s\\'ha complert.<br/>Poseu-vos en contacte amb el vostre administrador si teniu algun dubte sobre la complexitat especificada.',\n\t\t'invaliderrordocumentvalue' => 'El valor donat com a ErrorDocument no sembla ser un fitxer, URL o cadena vàlids.',\n\t\t'intvaluetoolow' => 'El número donat és massa baix (camp %s))',\n\t\t'intvaluetoohigh' => 'El número donat és massa alt (camp %s)',\n\t\t'phpfpmstillenabled' => 'PHP-FPM està actualment actiu. Desactiveu-lo abans d\\'activar FCGID.',\n\t\t'fcgidstillenabled' => 'FCGID està actualment actiu. Desactiveu-lo abans d\\'activar PHP-FPM.',\n\t\t'domains_cantdeletedomainwithaliases' => 'No es pot suprimir un domini que s\\'utilitza per a àlies. Primer heu d\\'eliminar els àlies.',\n\t\t'user_banned' => 'El vostre compte ha estat bloquejat. Si us plau, contacteu amb el vostre administrador per a més informació.',\n\t\t'session_timeout' => 'Valor massa baix',\n\t\t'session_timeout_desc' => 'El temps d\\'espera de la sessió no ha de ser inferior a 1 minut.',\n\t\t'invalidhostname' => 'El nom de host ha de ser un domini vàlid. No pot estar buit ni estar format només per espais en blanc.',\n\t\t'operationnotpermitted' => 'Operació no permesa',\n\t\t'featureisdisabled' => 'La funció %s està desactivada. Poseu-vos en contacte amb el vostre proveïdor de serveis.',\n\t\t'usercurrentlydeactivated' => 'L\\'usuari %s està desactivat actualment',\n\t\t'setlessthanalreadyused' => 'No pot establir menys recursos de \\'%s\\' dels que aquest usuari ja ha utilitzat<br/>',\n\t\t'stringmustntbeempty' => 'El valor del camp %s no ha d\\'estar buit',\n\t\t'sslcertificateismissingprivatekey' => 'Necessita especificar una clau privada per al vostre certificat',\n\t\t'sslcertificatewrongdomain' => 'El certificat indicat no pertany a aquest domini',\n\t\t'sslcertificateinvalidcert' => 'El contingut del certificat indicat no sembla un certificat vàlid.',\n\t\t'sslcertificateinvalidcertkeypair' => 'La clau privada indicada no pertany al certificat en qüestió.',\n\t\t'sslcertificateinvalidca' => 'Les dades del certificat de la CA no semblen ser un certificat vàlid.',\n\t\t'sslcertificateinvalidchain' => 'Les dades de la cadena del certificat no semblen ser un certificat vàlid.',\n\t\t'givendirnotallowed' => 'El directori indicat al camp %s no està permès.',\n\t\t'sslredirectonlypossiblewithsslipport' => 'L\\'ús de Let\\'s Encrypt només és possible quan el domini té assignada almenys una combinació IP/port habilitada per a ssl.',\n\t\t'fcgidstillenableddeadlock' => 'FCGID està actualment actiu.<br/>Si us plau, desactiveu-lo abans de canviar a un altre servidor web que no sigui Apache2',\n\t\t'send_report_title' => 'Enviar informe d\\'error',\n\t\t'send_report_desc' => 'Gràcies per informar d\\'aquest error i ajudar-nos a millorar froxlor.<br/>Aquest és el correu electrònic que s\\'enviarà a l\\'equip de desenvolupadors de froxlor:',\n\t\t'send_report' => 'Enviar informe',\n\t\t'send_report_error' => 'Error en enviar l\\'informe: <br/>%s',\n\t\t'notallowedtouseaccounts' => 'El vostre compte no permet utilitzar IMAP/POP3. No podeu afegir comptes de correu.',\n\t\t'cannotdeletehostnamephpconfig' => 'Aquesta configuració PHP és utilitzada pel froxlor-vhost i no pot ser esborrada.',\n\t\t'cannotdeletedefaultphpconfig' => 'Aquesta configuració PHP està establerta per defecte i no es pot esborrar.',\n\t\t'passwordshouldnotbeusername' => 'La contrasenya no pot ser la mateixa que el nom dusuari.',\n\t\t'no_phpinfo' => 'Ho sentim, no puc llegir phpinfo()',\n\t\t'moveofcustomerfailed' => 'El canvi del client a l\\'administrador/revenedor seleccionat ha fallat. Tingui en compte que tots els altres canvis en el client s\\'han aplicat amb èxit en aquesta etapa.<br/><br/>Missatge d\\'error: %s',\n\t\t'domain_import_error' => 'S\\'ha produït el següent error en importar dominis: %s',\n\t\t'fcgidandphpfpmnogoodtogether' => 'FCGID i PHP-FPM no poden estar activats alhora',\n\t\t'no_apcuinfo' => 'No hi ha informació de memòria cau disponible. APCu no sembla que s\\'està executant.',\n\t\t'no_opcacheinfo' => 'No hi ha informació de memòria cau disponible. OPCache no sembla que s\\'està executant.',\n\t\t'nowildcardwithletsencrypt' => 'Let\\'s Encrypt no pot manejar dominis comodí usant ACME a froxlor (requereix dns-challenge), ho sento. Si us plau, establiu el ServerAlias a WWW o desactiveu-lo completament.',\n\t\t'customized_version' => 'Sembla que la teva instal·lació de froxlor ha estat modificada, no hi ha suport, ho sentim.',\n\t\t'autoupdate_0' => 'Error desconegut',\n\t\t'autoupdate_1' => 'El paràmetre de PHP allow_url_fopen està desactivat. Autoupdate necessita que aquest paràmetre estigui habilitat a php.ini',\n\t\t'autoupdate_2' => 'Extensió PHP zip no trobada, si us plau assegureu-vos que està instal·lada i activada',\n\t\t'autoupdate_4' => 'El fitxer froxlor no ha pogut ser emmagatzemat al disc :(',\n\t\t'autoupdate_5' => 'version.froxlor.org ha tornat valors inacceptables :(',\n\t\t'autoupdate_6' => 'Whoops, no hi havia una versió (vàlida) donada per descarregar :(',\n\t\t'autoupdate_7' => 'No s\\'ha pogut trobar el fitxer descarregat :(',\n\t\t'autoupdate_8' => 'No s\\'ha pogut extreure el fitxer :(',\n\t\t'autoupdate_9' => 'El fitxer descarregat no ha passat la comprovació de la integritat. Si us plau, intenteu tornar a actualitzar.',\n\t\t'autoupdate_10' => 'La versió mínima suportada de PHP és 7.4.0',\n\t\t'autoupdate_11' => 'Webupdate està desactivat',\n\t\t'mailaccistobedeleted' => 'Un altre compte amb el mateix nom (%s) està sent eliminat i per tant no es pot afegir en aquest moment.',\n\t\t'customerhasongoingbackupjob' => 'Ja hi ha un treball de còpia de seguretat esperant a ser processat, si us plau sigui pacient.',\n\t\t'backupfunctionnotenabled' => 'La funció de còpia de seguretat no està habilitada',\n\t\t'dns_domain_nodns' => 'DNS no està habilitat per a aquest domini',\n\t\t'dns_content_empty' => 'No hi ha contingut',\n\t\t'dns_content_invalid' => 'El contingut DNS no és vàlid',\n\t\t'dns_arec_noipv4' => 'No s\\'ha proporcionat cap adreça IP vàlida per al registre A.',\n\t\t'dns_aaaarec_noipv6' => 'No s\\'ha indicat una adreça IP vàlida per al registre AAAA',\n\t\t'dns_mx_prioempty' => 'Prioritat MX no vàlida',\n\t\t'dns_mx_needdom' => 'El valor de contingut MX ha de ser un nom de domini vàlid.',\n\t\t'dns_mx_noalias' => 'El valor de contingut MX no pot ser una entrada CNAME.',\n\t\t'dns_cname_invaliddom' => 'Nom de domini no vàlid per al registre CNAME',\n\t\t'dns_cname_nomorerr' => 'Ja hi ha un resource-record amb el mateix nom de registre. No es pot utilitzar com a CNAME.',\n\t\t'dns_other_nomorerr' => 'Ja hi ha un registre CNAME amb el mateix nom de registre. No es pot utilitzar per a cap altre tipus.',\n\t\t'dns_ns_invaliddom' => 'Nom de domini no vàlid per al registre NS',\n\t\t'dns_srv_prioempty' => 'Prioritat SRV no vàlida',\n\t\t'dns_srv_invalidcontent' => 'Contingut SRV no vàlid, ha de contenir els camps weight, port i target, p.ex: 5 5060 servidorsip.exemple.com.',\n\t\t'dns_srv_needdom' => 'El valor SRV target ha de ser un nom de domini vàlid',\n\t\t'dns_srv_noalias' => 'El valor SRV-target no pot ser una entrada CNAME.',\n\t\t'dns_duplicate_entry' => 'El registre ja existeix',\n\t\t'dns_notfoundorallowed' => 'Domini no trobat o sense permís',\n\t\t'domain_nopunycode' => 'No heu d\\'especificar punycode (IDNA). El domini es convertirà automàticament',\n\t\t'dns_record_toolong' => 'Els registres/etiquetes només poden tenir un màxim de 63 caràcters',\n\t\t'noipportgiven' => 'No s\\'ha especificat IP/port',\n\t\t'jsonextensionnotfound' => 'Aquesta funció requereix una extensió php json.',\n\t\t'cannotdeletesuperadmin' => 'El primer administrador no es pot eliminar.',\n\t\t'no_wwwcnamae_ifwwwalias' => 'No es pot establir un registre CNAME per a \"www\" perquè el domini està configurat per generar un àlies www. Canvieu la configuració a \"Sense àlies\" o \"Àlies comodí\".',\n\t\t'local_group_exists' => 'El grup indicat ja existeix al sistema.',\n\t\t'local_group_invalid' => 'El nom del grup no és vàlid',\n\t\t'invaliddnsforletsencrypt' => 'El DNS del domini no inclou cap de les adreces IP seleccionades. La generació del certificat Let\\'s Encrypt no és possible.',\n\t\t'notallowedphpconfigused' => 'Intentant utilitzar php-config que no està assignat al client',\n\t\t'pathmustberelative' => 'L\\'usuari no té permís per especificar directoris fora del directori personal del client. Si us plau, especifiqueu una ruta relativa (sense /).',\n\t\t'mysqlserverstillhasdbs' => 'No es pot eliminar el servidor de base de dades de la llista de clients permesos, ja que encara hi ha bases de dades.',\n\t\t'domaincannotbeedited' => 'No se us permet editar la %s domini.',\n\t\t'invalidcronjobintervalvalue' => 'L\\'interval de la tasca Cron ha de ser un dels següents: %s'\n\t],\n\t'extras' => [\n\t\t'description' => 'Aquí podeu afegir alguns extres, per exemple protecció de directoris.<br/>El sistema necessitarà algun temps per aplicar la nova configuració després de cada canvi.',\n\t\t'directoryprotection_add' => 'Afegir protecció de directori',\n\t\t'view_directory' => 'Mostra el contingut del directori',\n\t\t'pathoptions_add' => 'Afegir opcions de ruta',\n\t\t'directory_browsing' => 'Exploració del contingut del directori',\n\t\t'pathoptions_edit' => 'Editar opcions de ruta',\n\t\t'error404path' => '404',\n\t\t'error403path' => '403',\n\t\t'error500path' => '500',\n\t\t'error401path' => '401',\n\t\t'errordocument404path' => 'DocumentError 404',\n\t\t'errordocument403path' => 'DocumentError 403',\n\t\t'errordocument500path' => 'DocumentError 500',\n\t\t'errordocument401path' => 'DocumentError 401',\n\t\t'execute_perl' => 'Executar perl/CGI',\n\t\t'htpasswdauthname' => 'Raó d\\'autenticació (AuthName)',\n\t\t'directoryprotection_edit' => 'Editar protecció de directori',\n\t\t'backup' => 'Crear còpia de seguretat',\n\t\t'backup_web' => 'Còpia de seguretat de dades web',\n\t\t'backup_mail' => 'Còpia de seguretat de les dades de correu',\n\t\t'backup_dbs' => 'Còpia de seguretat de bases de dades',\n\t\t'path_protection_label' => '<strong class=\"text-danger\">Important</strong>',\n\t\t'path_protection_info' => 'Us recomanem encaridament que protegeixi la ruta indicada, consulteu \"Extres\" -> \"Protecció de directoris\".'\n\t],\n\t'ftp' => [\n\t\t'description' => 'Aquí podeu crear i modificar els vostres comptes FTP.<br/>Els canvis es fan a l\\'instant i els comptes es poden utilitzar immediatament.',\n\t\t'account_add' => 'Crear compte',\n\t\t'account_edit' => 'Editar compte ftp',\n\t\t'editpassdescription' => 'Estableixi una nova contrasenya o deixeu-la en blanc per no canviar-la.'\n\t],\n\t'gender' => [\n\t\t'title' => 'Títol',\n\t\t'male' => 'Sr.',\n\t\t'female' => 'Sra.',\n\t\t'undef' => ''\n\t],\n\t'imprint' => 'Avís legal',\n\t'index' => [\n\t\t'customerdetails' => 'Dades del client',\n\t\t'accountdetails' => 'Dades del compte'\n\t],\n\t'integrity_check' => [\n\t\t'databaseCharset' => 'Joc de caràcters de la base de dades (ha de ser UTF-8)',\n\t\t'domainIpTable' => 'Referències IP <-> domini',\n\t\t'subdomainSslRedirect' => 'Bandera falsa SSL-redirect per a dominis no SSL',\n\t\t'froxlorLocalGroupMemberForFcgidPhpFpm' => 'Usuari froxlor als grups de clients (per a FCGID/php-fpm)',\n\t\t'webserverGroupMemberForFcgidPhpFpm' => 'Usuari Webserver als grups de clients (per a FCGID/php-fpm)',\n\t\t'subdomainLetsencrypt' => 'Els dominis principals sense port SSL assignat no tenen subdominis amb redirecció SSL activa'\n\t],\n\t'logger' => [\n\t\t'date' => 'Data',\n\t\t'type' => 'Tipus',\n\t\t'action' => 'Acció',\n\t\t'user' => 'Usuari',\n\t\t'truncate' => 'Registre buit',\n\t\t'reseller' => 'Revenedor',\n\t\t'admin' => 'Administrador',\n\t\t'cron' => 'Tasca Cron',\n\t\t'login' => 'Inici de sessió',\n\t\t'intern' => 'Intern',\n\t\t'unknown' => 'Desconegut'\n\t],\n\t'login' => [\n\t\t'username' => 'Nom d\\'usuari',\n\t\t'password' => 'Contrasenya',\n\t\t'language' => 'Idioma',\n\t\t'login' => 'Inici de sessió',\n\t\t'logout' => 'Finalitzar sessió',\n\t\t'profile_lng' => 'Idioma del perfil',\n\t\t'welcomemsg' => 'Iniciï sessió per accedir al vostre compte.',\n\t\t'forgotpwd' => 'Heu oblidat la contrasenya?',\n\t\t'presend' => 'Restablir contrasenya',\n\t\t'email' => 'Correu electrònic',\n\t\t'remind' => 'Restablir la contrasenya',\n\t\t'usernotfound' => 'No s\\'ha trobat l\\'usuari',\n\t\t'backtologin' => 'Tornar a l\\'inici de sessió',\n\t\t'combination_not_found' => 'No s\\'ha trobat cap combinació d\\'usuari i adreça electrònica.',\n\t\t'2fa' => 'Autenticació de dos factors (2FA)',\n\t\t'2facode' => 'Introdueixi el codi 2FA'\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Hola,\\\\nel vostre compte de correu {EMAIL} s\\'ha configurat correctament.\\\\nAquest és un correu creat \\\\nautomàticament, si us plau no contesteu!\\\\nAtentament, el vostre administrador',\n\t\t\t'subject' => 'Compte de correu configurat correctament'\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Hola {SALUTATION}, aquí hi ha les dades del vostre compte: Nom d\\'usuari: {USERNAME}: {PASSWORD}, el seu administrador.',\n\t\t\t'subject' => 'Informació del compte'\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Hola {SALUTATION}, el vostre compte de correu {EMAIL} s\\'ha configurat correctament. La contrasenya és {PASSWORD}. Aquest és un correu creat automàticament, si us plau no el contesteu. Atentament, el vostre administrador.',\n\t\t\t'subject' => 'Compte de correu configurat correctament'\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Restablir contrasenya',\n\t\t\t'mailbody' => 'Hola {SALUTATION}, aquí està el vostre enllaç per establir una nova contrasenya. Aquest enllaç és vàlid durant les següents 24 hores. {LINK}, el vostre administrador.'\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] Nova base de dades creada',\n\t\t\t'mailbody' => 'Hola {CUST_NAME},\n\nacabes d\\'afegir una nova base de dades. Aquí hi ha la informació introduïda:\n\nNom de la base de dades: {DB_NAME}\nContrasenya: {DB_PASS}\nDescripció: {DB_DESC}\nNom de host de la base de dades: {DB_SRV}\nphpMyAdmin: {PMA_URI}\nAtentament, el vostre administrador'\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'Nou usuari ftp creat',\n\t\t\t'mailbody' => 'Hola {CUST_NAME}\n\nacabes d\\'afegir un nou usuari ftp. Aquí hi ha la informació introduïda:\n\nNom d\\'usuari: {USR_NAME}\nContrasenya: {USR_PASS}\nRuta: {USR_PATH}\n\nAtentament, el vostre administrador'\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => 'Estimat {SALUTATION}, ha utilitzat el {TRAFFICUSED} del seu {TRAFFIC} disponible de trànsit. Això és més del {MAX_PERCENT}%%',\n\t\t\t'subject' => 'Arribant al límit de trànsit'\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => 'Estimat {SALUTATION}, ha utilitzat el {DISKUSED} de su {DISKAVAILABLE} disponible d\\'espai en disc. Això és més del {MAX_PERCENT}.',\n\t\t\t'subject' => 'Arribant al límit d\\'espai de disc'\n\t\t],\n\t\t'2fa' => [\n\t\t\t'mailbody' => 'Hola, el vostre codi d\\'accés 2FA és..: {CODE}. Aquest és un correu creat automàticament, si us plau no el respongui. Atentament, el vostre administrador.',\n\t\t\t'subject' => 'froxlor - Codi 2FA'\n\t\t]\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Principal',\n\t\t\t'changepassword' => 'Canviar contrasenya',\n\t\t\t'changelanguage' => 'Canviar idioma',\n\t\t\t'username' => 'Iniciar sessió com ',\n\t\t\t'changetheme' => 'Canviar tema',\n\t\t\t'apihelp' => 'Ajuda d\\'API',\n\t\t\t'apikeys' => 'Claus d\\'API'\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'Correu electrònic',\n\t\t\t'emails' => 'Adreces',\n\t\t\t'webmail' => 'Correu web',\n\t\t\t'emailsoverview' => 'Vista general de dominis de correu electrònic'\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Bases de dades',\n\t\t\t'phpmyadmin' => 'phpMyAdmin'\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Dominis',\n\t\t\t'settings' => 'Vista general de dominis'\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Comptes',\n\t\t\t'webftp' => 'WebFTP'\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extres',\n\t\t\t'directoryprotection' => 'Protecció de directoris',\n\t\t\t'pathoptions' => 'Opcions de ruta',\n\t\t\t'backup' => 'Còpia de seguretat'\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Trànsit',\n\t\t\t'current' => 'Mes en curs',\n\t\t\t'overview' => 'Trànsit total'\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'Configuracions PHP',\n\t\t\t'fpmdaemons' => 'Versions de PHP-FPM'\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'Registre del sistema'\n\t\t]\n\t],\n\t'message' => [\n\t\t'norecipients' => 'No s\\'ha enviat cap correu electrònic perquè no hi ha destinataris a la base de dades',\n\t\t'success' => 'Missatge enviat correctament als destinataris de %s',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'Usuari/Nom de la base de dades',\n\t\t'databasedescription' => 'Descripció de la base de dades',\n\t\t'database_create' => 'Crear base de dades',\n\t\t'description' => 'Aquí podeu crear i modificar les vostres bases de dades MySQL.<br/>Els canvis es realitzen a l\\'instant i la base de dades es pot utilitzar immediatament.<br/>Al menú de l\\'esquerra trobareu l\\'eina phpMyAdmin amb la qual podeu administrar fàcilment la vostra base de dades.<br/><br/>Per utilitzar les vostres bases de dades en els vostres propis scripts php utilitzeu els següents ajustaments: (Les dades en <i>cursiva</i> s\\'han de canviar pels equivalents que hagi escrit!)<br/>Nom de host: <b><sql_host/></b><br/>Nom d\\'usuari: <b><i>databasename</i></b><br/> Contrasenya: <b><i>la contrasenya que heu triat</i></b><br/>Base de dades: <b><i>databasename</i></b>',\n\t\t'mysql_server' => 'Servidor MySQL',\n\t\t'database_edit' => 'Editar base de dades',\n\t\t'size' => 'Mida',\n\t\t'privileged_user' => 'Usuari privilegiat de la base de dades',\n\t\t'privileged_passwd' => 'Contrasenya per a usuari privilegiat',\n\t\t'unprivileged_passwd' => 'Contrasenya per a usuari sense privilegis',\n\t\t'mysql_ssl_ca_file' => 'Certificat del servidor SSL',\n\t\t'mysql_ssl_verify_server_certificate' => 'Verificar certificat de servidor SSL'\n\t],\n\t'opcacheinfo' => [\n\t\t'generaltitle' => 'Informació general',\n\t\t'resetcache' => 'Restablir OPcache',\n\t\t'version' => 'Versió de OPCache',\n\t\t'phpversion' => 'Versió de PHP',\n\t\t'runtimeconf' => 'Configuració de temps d\\'execució',\n\t\t'start' => 'Hora d\\'inici',\n\t\t'lastreset' => 'Últim reinici',\n\t\t'oomrestarts' => 'Recompte de reinicis OOM',\n\t\t'hashrestarts' => 'Recompte de reinicis Hash',\n\t\t'manualrestarts' => 'Recompte de reinicis manuals',\n\t\t'hitsc' => 'Nombre d\\'encerts',\n\t\t'missc' => 'Nombre de faltes',\n\t\t'blmissc' => 'Llista negra del nombre de faltes',\n\t\t'status' => 'Estat',\n\t\t'never' => 'mai',\n\t\t'enabled' => 'OPcache activat',\n\t\t'cachefull' => 'Memòria cau plena',\n\t\t'restartpending' => 'Reinici pendent',\n\t\t'restartinprogress' => 'Reinici en curs',\n\t\t'cachedscripts' => 'Recompte d\\'scripts en memòria cau',\n\t\t'memusage' => 'Ús de memòria',\n\t\t'totalmem' => 'Memòria total',\n\t\t'usedmem' => 'Memòria utilitzada',\n\t\t'freemem' => 'Memòria lliure',\n\t\t'wastedmem' => 'Memòria desaprofitada',\n\t\t'maxkey' => 'Tecles màximes',\n\t\t'usedkey' => 'Tecles utilitzades',\n\t\t'wastedkey' => 'Tecles desaprofitades',\n\t\t'strinterning' => 'Intercalació de cadenes',\n\t\t'strcount' => 'Recompte de cadenes',\n\t\t'keystat' => 'Estadística de claus en memòria cau',\n\t\t'used' => 'Utilitzat',\n\t\t'free' => 'Lliure',\n\t\t'blacklist' => 'Llista negra',\n\t\t'novalue' => '<i>sense valor</i>',\n\t\t'true' => '<i>cert</i>',\n\t\t'false' => '<i>fals</i>',\n\t\t'funcsavail' => 'Funcions disponibles'\n\t],\n\t'panel' => [\n\t\t'edit' => 'Editar',\n\t\t'delete' => 'Eliminar',\n\t\t'create' => 'Crear',\n\t\t'save' => 'Desar',\n\t\t'yes' => 'Sí',\n\t\t'no' => 'No',\n\t\t'emptyfornochanges' => 'buit per a cap canvi',\n\t\t'emptyfordefault' => 'buit per a valors per defecte',\n\t\t'path' => 'Ruta',\n\t\t'toggle' => 'Commutar',\n\t\t'next' => 'Següent',\n\t\t'dirsmissing' => 'No es pot trobar o llegir el directori!',\n\t\t'unlimited' => '∞',\n\t\t'urloverridespath' => 'URL (anul·la la ruta)',\n\t\t'pathorurl' => 'Ruta o URL',\n\t\t'ascending' => 'ascendent',\n\t\t'descending' => 'descendent',\n\t\t'search' => 'Cercar',\n\t\t'used' => 'utilitzada',\n\t\t'translator' => 'Traductor',\n\t\t'reset' => 'Descartar canvis',\n\t\t'pathDescription' => 'Si el directori no existeix, es crearà automàticament.',\n\t\t'pathDescriptionEx' => '<br/><br/><span class=\"text-danger\">Nota:</span> La ruta <code>/</code> no està permesa a causa de la configuració administrativa, s\\'establirà automàticament a <code>/elegit.subdomini.tld/</code> si no s\\'estableix en un altre directori.',\n\t\t'pathDescriptionSubdomain' => 'Si el directori no existeix, es crearà automàticament.<br/><br/>Si voleu una redirecció a un altre domini, aquesta entrada ha de començar per http:// o https://.<br/><br/>Si la URL acaba en / es considera una carpeta, si no, es tracta com a fitxer.',\n\t\t'back' => 'Tornar',\n\t\t'reseller' => 'revenedor',\n\t\t'admin' => 'administrador',\n\t\t'customer' => 'client/s',\n\t\t'send' => 'enviar',\n\t\t'nosslipsavailable' => 'Actualment no hi ha combinacions ip/port ssl per a aquest servidor',\n\t\t'backtooverview' => 'Tornar a la vista general',\n\t\t'dateformat' => 'AAAA-MM-DD',\n\t\t'dateformat_function' => 'A-m-d',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Per defecte',\n\t\t'never' => 'Mai',\n\t\t'active' => 'Actiu',\n\t\t'please_choose' => 'Seleccioni una opció',\n\t\t'allow_modifications' => 'Permetre modificacions',\n\t\t'megabyte' => 'MegaByte',\n\t\t'not_supported' => 'No suportat en: ',\n\t\t'view' => 'veure',\n\t\t'toomanydirs' => 'Massa subdirectoris. Tornar a la selecció manual de ruta.',\n\t\t'abort' => 'Avortar',\n\t\t'not_activated' => 'no activat',\n\t\t'off' => 'desactivat',\n\t\t'options' => 'Opcions',\n\t\t'neverloggedin' => 'Encara no s\\'ha iniciat sessió',\n\t\t'descriptionerrordocument' => 'Pot ser una URL, la ruta a un fitxer o simplement una cadena envoltada de \"\"<br/>Deixeu-lo buit per utilitzar el valor predeterminat del servidor.',\n\t\t'unlock' => 'Desbloquejar',\n\t\t'theme' => 'Tema',\n\t\t'variable' => 'Variable',\n\t\t'description' => 'Descripció',\n\t\t'cancel' => 'Cancel·lar',\n\t\t'ssleditor' => 'Configuració SSL per a aquest domini',\n\t\t'ssleditor_infoshared' => 'Actualment utilitzant el certificat del domini pare',\n\t\t'ssleditor_infoglobal' => 'Actualment utilitzant el certificat global',\n\t\t'dashboard' => 'Panell de control',\n\t\t'assigned' => 'Assignat',\n\t\t'available' => 'Disponible',\n\t\t'news' => 'Notícies',\n\t\t'newsfeed_disabled' => 'La font de notícies està desactivada. Feu clic a la icona d\\'edició per anar a la configuració.',\n\t\t'ftpdesc' => 'Descripció d\\'FTP',\n\t\t'letsencrypt' => 'Utilitzant Let\\'s encrypt',\n\t\t'set' => 'Aplicar',\n\t\t'shell' => 'Shell',\n\t\t'backuppath' => [\n\t\t\t'title' => 'Ruta de destí de la còpia de seguretat',\n\t\t\t'description' => 'Aquesta és la ruta on s\\'emmagatzemaran les còpies de seguretat. Si seleccioneu la còpia de seguretat de les dades web, tots els fitxers de la carpeta d\\'inici s\\'emmagatzemen excloent-hi la carpeta de còpia de seguretat especificada aquí.'\n\t\t],\n\t\t'none_value' => 'Cap',\n\t\t'viewlogs' => 'Veure fitxers de registre',\n\t\t'not_configured' => 'El sistema encara no està configurat. Feu clic aquí per anar a la configuració.',\n\t\t'ihave_configured' => 'He configurat els serveis',\n\t\t'system_is_configured' => '<i class=\"fa-solid fa-circle-exclamation me-1\"/>El sistema ja està configurat',\n\t\t'settings_before_configuration' => 'Assegureu-vos d\\'ajustar la configuració abans de configurar els serveis aquí',\n\t\t'image_field_delete' => 'Esborrar la imatge actual existent',\n\t\t'usage_statistics' => 'Ús de recursos',\n\t\t'security_question' => 'Pregunta de seguretat',\n\t\t'listing_empty' => 'No s\\'han trobat entrades',\n\t\t'unspecified' => 'sense especificar',\n\t\t'settingsmode' => 'Mode',\n\t\t'settingsmodebasic' => 'Bàsic',\n\t\t'settingsmodeadvanced' => 'Avançat',\n\t\t'settingsmodetoggle' => 'Faci clic per canviar el mode',\n\t\t'modalclose' => 'Tancar',\n\t\t'managetablecolumnsmodal' => [\n\t\t\t'title' => 'Administrar columnes de la taula',\n\t\t\t'description' => 'Aquí pot personalitzar les columnes visibles'\n\t\t],\n\t\t'mandatoryfield' => 'Camp obligatori',\n\t\t'select_all' => 'Selecciona-ho tot',\n\t\t'unselect_all' => 'Desselecciona-ho tot',\n\t\t'searchtablecolumnsmodal' => [\n\t\t\t'title' => 'Cerca en els camps',\n\t\t\t'description' => 'Seleccioni el camp on vol cercar'\n\t\t],\n\t\t'upload_import' => 'Carregar e importar'\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => 'Usuari local a utilitzar per PHP-FPM (froxlor vHost)',\n\t\t'vhost_httpgroup' => 'Grup local a utilitzar per PHP-FPM (froxlor vHost)',\n\t\t'ownvhost' => [\n\t\t\t'title' => 'Habilitar PHP-FPM per al vHost de froxlor',\n\t\t\t'description' => 'Si està habilitat, froxlor també s\\'executarà sota un usuari local'\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => 'Utilitzar mod_proxy / mod_proxy_fcgi',\n\t\t\t'description' => '<strong class=\"text-danger\">S\\'ha d\\'activar quan utilitzeu Debian 9.x (Stretch) o posterior</strong>. Activar per utilitzar php-fpm via mod_proxy_fcgi. Com a mínim es requereix apache-2.4.9'\n\t\t],\n\t\t'ini_flags' => 'Introdueixi possibles <strong>php_flags</strong>per a php.ini. Una entrada per línia',\n\t\t'ini_values' => 'Introdueixi possibles <strong>php_values</strong>per a  php.ini. Una entrada per línia',\n\t\t'ini_admin_flags' => 'Introdueixi possibles <strong>php_admin_flags</strong>per a  php.ini. Una entrada per línia',\n\t\t'ini_admin_values' => 'Introdueixi possibles <strong>php_admin_values</strong>per a  php.ini. Una entrada per línia'\n\t],\n\t'privacy' => 'Política de privadesa',\n\t'pwdreminder' => [\n\t\t'success' => 'Restabliment de contrasenya sol·licitat amb èxit. Seguiu les instruccions del correu electrònic que heu rebut.',\n\t\t'notallowed' => 'Usuari desconegut o el restabliment de contrasenya està desactivat',\n\t\t'changed' => 'La vostra contrasenya s\\'ha actualitzat correctament. Ja podeu iniciar sessió amb la nova contrasenya.',\n\t\t'wrongcode' => 'Ho sentim, el vostre codi d\\'activació no existeix o ja ha caducat.',\n\t\t'choosenew' => 'Establir nova contrasenya'\n\t],\n\t'question' => [\n\t\t'question' => 'Pregunta de seguretat',\n\t\t'admin_customer_reallydelete' => 'Realment vol eliminar el client %s? No es podrà desfer.',\n\t\t'admin_domain_reallydelete' => 'Realment vol esborrar el domini %s?',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Realment vol desactivar aquesta configuració de seguretat OpenBasedir?',\n\t\t'admin_admin_reallydelete' => 'Realment vol esborrar l\\'admin %s? Tots els clients i dominis seran reassignats al vostre compte.',\n\t\t'admin_template_reallydelete' => 'Realment vol esborrar la plantilla \\'%s\\'?',\n\t\t'domains_reallydelete' => 'Realment vol esborrar el domini %s?',\n\t\t'email_reallydelete' => 'Realment vol esborrar l\\'adreça de correu electrònic %s?',\n\t\t'email_reallydelete_account' => 'Realment vol esborrar el compte de correu de %s?',\n\t\t'email_reallydelete_forwarder' => 'Realment vol esborrar el forwarder %s?',\n\t\t'extras_reallydelete' => 'Realment vol esborrar la protecció de directori de %s?',\n\t\t'extras_reallydelete_pathoptions' => 'Realment vol esborrar les opcions de ruta de %s?',\n\t\t'extras_reallydelete_backup' => 'Realment vol avortar el treball de la còpia de seguretat planificat?',\n\t\t'ftp_reallydelete' => 'Realment vol esborrar el compte FTP %s?',\n\t\t'mysql_reallydelete' => 'Realment vol esborrar la base de dades %s? No es podrà desfer.',\n\t\t'admin_configs_reallyrebuild' => 'Realment vol reconstruir tots els fitxers de configuració?',\n\t\t'admin_customer_alsoremovefiles' => 'Treure també els fitxers d\\'usuari?',\n\t\t'admin_customer_alsoremovemail' => 'Eliminar completament les dades de correu electrònic del sistema de fitxers?',\n\t\t'admin_customer_alsoremoveftphomedir' => 'Treure també el directori de l\\'usuari FTP?',\n\t\t'admin_ip_reallydelete' => 'Realment vol esborrar l\\'adreça IP %s?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Estàs segur que vol que l\\'arrel del document per a aquest domini no estigui dins l\\'arrel del client?',\n\t\t'admin_counters_reallyupdate' => 'Realment vol recalcular l\\'ús de recursos?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Realment vol esborrar totes les contrasenyes no encriptades dels comptes de correu de la taula mail_users? Això no es pot revertir. L\\'opció d\\'emmagatzemar les contrasenyes de correu electrònic sense xifrar també es desactivarà.',\n\t\t'logger_reallytruncate' => 'Realment vol truncar la taula \"%s\"?',\n\t\t'admin_quotas_reallywipe' => 'Realment vol esborrar totes les quotes de la taula mail_users? Això no es pot revertir.',\n\t\t'admin_quotas_reallyenforce' => 'Realment vol aplicar la quota per defecte a tots els usuaris? Això no es pot revertir.',\n\t\t'phpsetting_reallydelete' => 'Realment vol suprimir aquesta configuració? Tots els dominis que utilitzen aquesta configuració seran canviats a la configuració per defecte.',\n\t\t'fpmsetting_reallydelete' => 'Realment vol eliminar aquesta configuració de php-fpm? Totes les configuracions de php que utilitzin aquests paràmetres es canviaran a la configuració per defecte.',\n\t\t'remove_subbutmain_domains' => 'Treure també els dominis que s\\'afegeixen com a dominis complets però que són subdominis d\\'aquest domini?',\n\t\t'customer_reallyunlock' => 'Realment vol desbloquejar el client %s?',\n\t\t'admin_integritycheck_reallyfix' => 'Realment vol intentar arreglar tots els problemes d\\'integritat de la base de dades automàticament?',\n\t\t'plan_reallydelete' => 'Realment vol eliminar el pla d\\'allotjament %s?',\n\t\t'apikey_reallydelete' => 'Realment vol esborrar aquesta api-key?',\n\t\t'apikey_reallyadd' => 'Realment vol crear una nova api-key?',\n\t\t'dnsentry_reallydelete' => 'Realment vol suprimir aquesta entrada dns?',\n\t\t'certificate_reallydelete' => 'Realment vol esborrar aquest certificat?',\n\t\t'cache_reallydelete' => 'Realment vol esborrar la memòria cau?'\n\t],\n\t'redirect_desc' => [\n\t\t'rc_default' => 'per defecte',\n\t\t'rc_movedperm' => 'mogut permanentment',\n\t\t'rc_found' => 'trobat',\n\t\t'rc_seeother' => 'veure\\'n un altre',\n\t\t'rc_tempred' => 'redirecció temporal'\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Temps d\\'espera de la sessió',\n\t\t\t'description' => 'Quant de temps ha d\\'estar inactiu un usuari perquè s\\'invalidi la sessió (segons)?'\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Prefix de client',\n\t\t\t'description' => 'Quin prefix han de tenir els comptes de client?'\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'Prefix SQL',\n\t\t\t'description' => 'Quin prefix han de tenir els comptes MySQL?<br/>Utilitzeu \"RANDOM\" com a valor per obtenir un prefix aleatori de 3 dígits<br/>Utilitzeu \"DBNAME\" com a valor, s\\'utilitza un camp de nom de base de dades juntament amb el nom del client com a prefix.'\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'Prefix FTP',\n\t\t\t'description' => 'Quin prefix han de tenir els comptes ftp?<br/><b>Si canvies això, també hauràs de canviar la consulta SQL Quota al fitxer de configuració del servidor FTP en cas que l\\'utilitzis</b>. '\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Directori d\\'inici',\n\t\t\t'description' => 'On s\\'han d\\'emmagatzemar tots els directoris d\\'inici?'\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Directori Logfiles',\n\t\t\t'description' => 'On s\\'han d\\'emmagatzemar tots els fitxers de registre?'\n\t\t],\n\t\t'logfiles_script' => [\n\t\t\t'title' => 'Script personalitzat per enviar els fitxers de registre',\n\t\t\t'description' => 'Pot especificar un script aquí i utilitzar els marcadors de posició <strong>{LOGFILE}, {DOMAIN} i {CUSTOMER}</strong> si cal. En cas que vulgui utilitzar-lo, ha d\\'activar també l\\'opció <strong>Pipe webserver logfiles</strong>. No cal el prefix pipe.'\n\t\t],\n\t\t'logfiles_format' => [\n\t\t\t'title' => 'Format de registre d\\'accés',\n\t\t\t'description' => 'Introduïu aquí un format de registre personalitzat d\\'acord amb les especificacions del vostre servidor web, deixi buit per defecte. Depenent del seu format, la cadena ha d\\'estar entre cometes.<br/>Si s\\'utilitza amb nginx, es veurà com a <i>log_format</i> <i>frx_custom</i> <i> {CONFIGURED_VALUE}</i >.<br/>Si s\\'utilitza amb Apache, es veurà com a <i>LogFormat {CONFIGURED_VALUE} frx_custom</i>.<br/><strong>Atenció</strong>: No es comprovarà si el codi conté errors. Si conté errors, el servidor web podria no tornar a arrencar!'\n\t\t],\n\t\t'logfiles_type' => [\n\t\t\t'title' => 'Tipus de registre d\\'accés',\n\t\t\t'description' => 'Trieu aquí entre <strong>combinat</strong> o <strong>vhost_combinat</strong>.'\n\t\t],\n\t\t'logfiles_piped' => [\n\t\t\t'title' => 'Canalitzar els fitxers de registre del servidor web a l\\'script especificat (veure a dalt)',\n\t\t\t'description' => 'Si utilitzeu un script personalitzat per als fitxers de registre, haureu d\\'activar-lo perquè s\\'executi.'\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'Adreça IP',\n\t\t\t'description' => 'Quina és l\\'adreça IP principal d\\'aquest servidor?'\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Nom de host',\n\t\t\t'description' => 'Quin és el nom de host d\\'aquest servidor?'\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Ordre de recàrrega del servidor web',\n\t\t\t'description' => 'Quina és l\\'ordre del servidor web per recarregar els fitxers de configuració?'\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => 'Habilitar servidor de noms',\n\t\t\t'description' => 'Aquí es pot habilitar i deshabilitar globalment el servidor de noms.'\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Directori de configuració del servidor DNS',\n\t\t\t'description' => 'On s\\'han de desar els fitxers de configuració del servidor DNS?'\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Ordre de recàrrega del servidor DNS',\n\t\t\t'description' => 'Quina és la comanda per recarregar el dimoni del servidor DNS?'\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'Mails-UID',\n\t\t\t'description' => 'Quin UserID han de tenir els correus?'\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mails-GID',\n\t\t\t'description' => 'Quin GroupID ha de tenir Mails?'\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Mails-homedir',\n\t\t\t'description' => 'On s\\'haurien d\\'emmagatzemar tots els correus?'\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Remitent',\n\t\t\t'description' => 'Quina és l\\'adreça del remitent per als correus electrònics enviats des del Panell?'\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'URL de phpMyAdmin',\n\t\t\t'description' => 'Quina és la URL de phpMyAdmin? (ha de començar per http(s)://)'\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'URL de Webmail',\n\t\t\t'description' => 'Quina és la URL de webmail? (ha de començar per http(s)://)'\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'URL de WebFTP',\n\t\t\t'description' => 'Quina és la URL de WebFTP? (ha de començar per http(s)://)'\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Quin és el llenguatge estàndard del vostre servidor?'\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Nombre màxim d\\'intents d\\'inici de sessió',\n\t\t\t'description' => 'Nombre màxim d\\'intents d\\'inici de sessió després dels quals es desactiva el compte.'\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Temps de desactivació',\n\t\t\t'description' => 'Temps (segons) que es desactiva un compte després de massa intents d\\'inici de sessió.'\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Tipus d\\'entrada de ruta',\n\t\t\t'description' => 'S\\'ha de seleccionar una ruta amb un menú desplegable o amb un camp d\\'entrada?'\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Servidors de noms',\n\t\t\t'description' => 'Una llista separada per comes que conté els noms de host de tots els servidors de noms. El primer serà el principal.'\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'Servidors MX',\n\t\t\t'description' => 'Una llista separada per comes que conté un parell d\\'un nombre i un nom de sistema principal separats per espais en blanc (per exemple, \\'10 mx.exemple.com\\') que conté els servidors mx.'\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Entrades per pàgina',\n\t\t\t'description' => 'Quantes entrades es mostraran a una pàgina? (0 = desactivar la paginació)'\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'IP/Port per defecte',\n\t\t\t'description' => 'Seleccioni totes les adreces IP que voleu utilitzar com a predeterminades per als nous dominis.'\n\t\t],\n\t\t'defaultsslip' => [\n\t\t\t'title' => 'IP/Port SSL per defecte',\n\t\t\t'description' => 'Seleccioni totes les adreces IP amb ssl habilitat que voleu utilitzar per defecte per als nous dominis'\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Rutes a afegir a OpenBasedir',\n\t\t\t'description' => 'Aquestes rutes (separades per dos punts) s\\'afegiran a la declaració OpenBasedir a cada contenidor vHost.'\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Utilitzar ordenació humana natural a la vista de llista',\n\t\t\t'description' => 'Ordena les llistes com a web1 -> web2 -> web11 en lloc de web1 -> web11 -> web2.'\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Docroot per a usuaris desactivats',\n\t\t\t'description' => 'Quan un usuari és desactivat aquesta ruta és utilitzada com el seu docroot. Deixar buit per no crear cap vHost.'\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Desar també les contrasenyes dels comptes de correu sense xifrar a la base de dades',\n\t\t\t'description' => 'Si aquesta opció està activada, totes les contrasenyes seran guardades sense encriptar (text clar, llegible per a qualsevol amb accés a la base de dades) a la taula mail_users. Activeu aquesta opció només si voleu utilitzar SASL.'\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'Comptes FTP @domini',\n\t\t\t'description' => 'Els clients poden crear comptes FTP user@customerdomain?'\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Activar FCGID',\n\t\t\t'description' => 'Utilitzi això per executar PHP amb el compte d\\'usuari corresponent.<br/><br/><b>Això necessita una configuració especial del servidor web per a Apache, vegeu <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - manual</a></b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Directori de configuració',\n\t\t\t\t'description' => 'On s\\'han de desar tots els fitxers de configuració fcgid? Si no utilitzeu un binari suexec autocompilat, que és la situació normal, aquesta ruta ha d\\'estar sota /var/www/<br/><br/><div class=\"text-danger\">NOTA: El contingut d\\'aquesta carpeta s\\'esborra regularment, així que eviteu emmagatzemar dades allà manualment.</div>'\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Directori temporal',\n\t\t\t\t'description' => 'On s\\'han d\\'emmagatzemar els directoris temporals'\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Processos per domini',\n\t\t\t\t'description' => 'Quants processos s\\'haurien d\\'iniciar/permetre per domini? Es recomana el valor 0 perquè PHP gestionarà llavors la quantitat de processos per si mateix de forma molt eficient.'\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper en Vhosts',\n\t\t\t\t'description' => 'Com s\\'ha d\\'incloure el wrapper als Vhosts'\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Directoris globals de PEAR',\n\t\t\t\t'description' => 'Quins directoris globals de PEAR han de ser reemplaçats a cada configuració php.ini? Els directoris diferents han d\\'estar separats per dos punts.'\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Nombre màxim de peticions per domini',\n\t\t\t\t'description' => 'Quantes peticions cal permetre per domini?'\n\t\t\t],\n\t\t\t'defaultini' => 'Configuració PHP per defecte per a nous dominis',\n\t\t\t'defaultini_ownvhost' => 'Configuració PHP per defecte per a froxlor-vHost',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Temps d\\'espera',\n\t\t\t\t'description' => 'Configuració de temps d\\'espera per a FastCGI Mod.'\n\t\t\t]\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Utilitzar una adreça de correu electrònic alternativa',\n\t\t\t'description' => 'Enviar la contrasenya a una adreça diferent durant la creació del compte de correu electrònic.'\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Fitxer/nom de directori de configuració del servidor web vHost',\n\t\t\t'description' => 'On s\\'ha de desar la configuració del vHost? Podeu especificar aquí un fitxer (tots els vHosts en un fitxer) o un directori (cada vHost al vostre propi fitxer).'\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Fitxer/nom de directori de configuració de diroptions del servidor web',\n\t\t\t'description' => 'On s\\'ha d\\'emmagatzemar la configuració de diroptions? Podeu especificar un fitxer (totes les diroptions en un fitxer) o directori (cada diroption en el vostre propi fitxer) aquí.'\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Nom de directori per a htpasswd Webserver',\n\t\t\t'description' => 'On s\\'han de desar els fitxers htpasswd per a la protecció de directoris?'\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'MySQL-Access-Hosts',\n\t\t\t'description' => 'Una llista separada per comes dels hosts des dels quals s\\'ha de permetre als usuaris connectar-se al servidor MySQL. Per permetre una subxarxa és vàlida la sintaxi netmask o cidr.'\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Sortida de Webalizer',\n\t\t\t'description' => 'Verbositat del programa webalizer'\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Registre activat/desactivat',\n\t\t\t'severity' => 'Nivell de registre',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Tipus de registre',\n\t\t\t\t'description' => 'Especifiqueu els tipus de registre. Per seleccionar diversos tipus, mantingueu premuda la tecla CTRL mentre selecciona.<br/>Els tipus de registre disponibles són: syslog, file, mysql'\n\t\t\t],\n\t\t\t'logfile' => [\n\t\t\t\t'title' => 'Nom del fitxer de registre',\n\t\t\t\t'description' => 'Només s\\'utilitza si el log-type inclou \"file\". Aquest fitxer es crearà a froxlor/logs/. Aquesta carpeta està protegida contra l\\'accés públic.'\n\t\t\t],\n\t\t\t'logcron' => 'Log cronjobs',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Mai',\n\t\t\t\t'once' => 'Un cop',\n\t\t\t\t'always' => 'Sempre'\n\t\t\t]\n\t\t],\n\t\t'ssl' => [\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => 'Activar ús de SSL',\n\t\t\t\t'description' => 'Marqui aquesta opció si vol utilitzar SSL per al seu servidor web'\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'Ruta al certificat SSL',\n\t\t\t\t'description' => 'Especifiqui la ruta incloent el nom del fitxer .crt o .pem (certificat principal)'\n\t\t\t],\n\t\t\t'openssl_cnf' => 'Valors predeterminats per crear el fitxer Cert',\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'Ruta al fitxer de claus SSL',\n\t\t\t\t'description' => 'Especifiqui la ruta incloent el nom de fitxer per al fitxer de clau privada (.key principalment)'\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'Ruta al certificat SSL CA (opcional)',\n\t\t\t\t'description' => 'Autenticació del client, configuri això només si sap el que és.'\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => 'Configuri els xifrats SSL permesos',\n\t\t\t\t'description' => 'Aquesta és una llista de xifradors que vol (o no vol) utilitzar quan parli SSL. Per a una llista de xifradors i com incloure\\'ls/excloure\\'ls, vegeu les seccions \"CIPHER LIST FORMAT\" i \"CIPHER STRINGS\" a <a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">la pàgina man de xifradors</a>.<br/><br/><b>El valor per defecte és:</b><pre>ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305</pre>'\n\t\t\t],\n\t\t\t'apache24_ocsp_cache_path' => [\n\t\t\t\t'title' => 'Apache 2.4: ruta a la memòria cau de grapat OCSP',\n\t\t\t\t'description' => 'Configura la memòria cau utilitzada per emmagatzemar les respostes OCSP que s\\'inclouen als handshakes TLS.'\n\t\t\t],\n\t\t\t'ssl_protocols' => [\n\t\t\t\t'title' => 'Configurar la versió del protocol TLS',\n\t\t\t\t'description' => 'Aquesta és una llista de protocols ssl que vol (o no vol) utilitzar quan utilitzeu SSL. <b>Nota:</b> És possible que alguns navegadors antics no admetin les versions de protocol més recents.<br/><br/><b>El valor per defecte és:</b><pre>TLSv1.2</pre>'\n\t\t\t],\n\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t'title' => 'Configurar xifrats explícits TLSv1.3 si es fan servir',\n\t\t\t\t'description' => 'Aquesta és una llista de xifradors que voleu (o no voleu) utilitzar quan parleu TLSv1.3. Per a una llista de xifradors i com incloure\\'ls/excloure\\'ls, vegeu <a href=\"https://wiki.openssl.org/index.php/TLS1.3\">els documents per a TLSv1.3</a>.<br/ ><br/><b>El valor per defecte és buit</b>'\n\t\t\t]\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Configuració vHost per defecte',\n\t\t\t'description' => 'El contingut d\\'aquest camp s\\'inclourà directament en aquest contenidor ip/port vHost. Podeu utilitzar les variables següents:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}< /code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (si escau)<br/> Atenció: No es comprovarà si el codi conté errors. Si conté errors, el servidor web podria no tornar a arrencar!'\n\t\t],\n\t\t'apache_globaldiropt' => [\n\t\t\t'title' => 'Opcions de directori per a customer-prefix',\n\t\t\t'description' => 'El contingut d\\'aquest camp s\\'inclourà a la configuració d\\'apache 05_froxlor_dirfix_nofcgid.conf. Si està buit, es farà servir el valor per defecte:<br/><br/>apache >=2.4<br/><code>Require all granted<br/>AllowOverride All</code><br/><br/>apache <=2.2<br/><code>Order allow,deny<br/>allow from all</code>'\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => 'El contingut d\\'aquest camp s\\'inclou directament al contenidor vHost del domini. Podeu utilitzar les variables següents:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}< /code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (si escau)<br/> Atenció: No es comprovarà si el codi conté errors. Si conté errors, el servidor web podria no tornar a arrencar!'\n\t\t],\n\t\t'decimal_places' => 'Nombre de decimals a la sortida de trànsit/espai web',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Configuració dns del domini del client'\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Permetre als clients editar la configuració dns del domini'\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Utilitzar noms d\\'usuari compatibles amb UNIX',\n\t\t\t'description' => 'Permet utilitzar <strong>-</strong> i <strong>_</strong> als noms d\\'usuari si <strong>No</strong>'\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Permetre que els clients restableixin la contrasenya',\n\t\t\t'description' => 'Els clients poden restablir la contrasenya i se\\'ls enviarà un enllaç d\\'activació a la seva adreça de correu electrònic.'\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Permetre que els administradors restableixin la contrasenya',\n\t\t\t'description' => 'Els administradors/revenedors poden restablir la contrasenya i se\\'ls enviarà un enllaç d\\'activació a la seva adreça de correu electrònic.'\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Quota de bústia',\n\t\t\t'description' => 'La quota per defecte per a les noves bústies creades (MegaByte).'\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Utilitzar mailbox-quota per a clients',\n\t\t\t'description' => 'Activar per utilitzar quotes a les bústies de correu. Per defecte és <b>No</b> ja que això requereix una configuració especial.',\n\t\t\t'removelink' => 'Faci clic aquí per esborrar totes les quotes dels comptes de correu.',\n\t\t\t'enforcelink' => 'Faci clic aquí per aplicar la quota per defecte a tots els comptes de correu dels usuaris.'\n\t\t],\n\t\t'index_file_extension' => [\n\t\t\t'description' => 'Quina extensió de fitxer s\\'ha d\\'utilitzar per al fitxer d\\'índex als directoris de clients acabats de crear? Aquesta extensió de fitxer s\\'utilitzarà si vosté o un dels seus administradors ha creat la seva pròpia plantilla de fitxer d\\'índex.',\n\t\t\t'title' => 'Extensió de fitxer per al fitxer d\\'índex en directoris de clients acabats de crear'\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Permetre l\\'inici de sessió múltiple',\n\t\t\t'description' => 'Si s\\'activa, un usuari pot iniciar sessió diverses vegades.'\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Permetre moure dominis entre administradors',\n\t\t\t'description' => 'Si està activat pot canviar l\\'admin d\\'un domini a domainsettings.<br/><b>Atenció:</b> Si un client no està assignat al mateix administrador que el domini, l\\'administrador pot veure tots els altres dominis d\\'aquest client!'\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Permetre moure dominis entre clients',\n\t\t\t'description' => 'Si està activat pots canviar el client d\\'un domini a domainsettings.<br/><b>Atenció:</b> froxlor canvia el documentroot al homedir per defecte del nou client (+ carpeta de domini si està activat)'\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'En cas afirmatiu, aquests paràmetres personalitzats de vHost s\\'afegiran a tots els subdominis; en cas negatiu, s\\'eliminaran els ajustaments especials de subdomini.'\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Longitud mínima de contrasenya',\n\t\t\t'description' => 'Aquí pot establir una longitud mínima per a les contrasenyes. 0\\' vol dir que no es requereix longitud mínima.'\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Emmagatzemar el fitxer índex per defecte també a les noves subcarpetes',\n\t\t\t'description' => 'Si s\\'activa, el fitxer d\\'índex per defecte s\\'emmagatzema a cada ruta de subdomini creada recentment (no si la carpeta ja existeix).'\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Adreça de resposta',\n\t\t\t'description' => 'Defineix una adreça de correu electrònic com a adreça de resposta per als correus electrònics enviats pel panell.'\n\t\t],\n\t\t'adminmail_defname' => 'Nom del remitent del correu electrònic del panell',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Subdomini estàndard del client',\n\t\t\t'description' => 'Quin nom de host s\\'ha de fer servir per crear subdominis estàndard per al client. Si està buit, s\\'utilitza el nom de sistema principal del sistema.'\n\t\t],\n\t\t'awstats_path' => 'Ruta a AWStats \\'awstats_buildstaticpages.pl\\'',\n\t\t'awstats_conf' => 'Ruta de configuració d\\'AWStats',\n\t\t'defaultttl' => 'TTL de domini per a bind en segons (per defecte \\'604800\\' = 1 setmana)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Habilitar documents d\\'error per defecte per a tots els clients',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'Fitxer/URL per a l\\'error 401',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'Fitxer/URL per a l\\'error 403',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'Fitxer/URL per a l\\'error 404',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'Fitxer/URL per a l\\'error 500',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'Si seleccioneu pureftpd, els fitxers .ftpquota per a quotes d\\'usuari es creen i s\\'actualitzen diàriament'\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Permetre redireccionaments de clients',\n\t\t\t'description' => 'Permetre als clients triar el codi http-status per a les redireccions que es faran servir'\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Redirecció per defecte',\n\t\t\t'description' => 'Estableix el codi de redirecció per defecte que s\\'utilitzarà si el client no ho estableix per si mateix'\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Crear mail-, imap-, pop3- y smtp-\"A record\" també amb MX-Servers set',\n\t\t'froxlordirectlyviahostname' => 'Accedir a froxlor directament a través del nom de host',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Expressió regular per a contrasenyes',\n\t\t\t'description' => 'Aquí pot establir una expressió regular per a la complexitat de les contrasenyes.<br/>Buit = cap requeriment'\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'Habilita FCGID per al vHost de froxlor',\n\t\t\t'description' => 'Si està habilitat, froxlor també s\\'executarà sota un usuari local'\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'Habilitar solució SuExec',\n\t\t\t\t'description' => 'Habilitar només si els directoris del client no estan dins de la ruta apache suexec.<br/>Si està habilitat, froxlor generarà un enllaç simbòlic des del directori del client habilitat per a perl + /cgi-bin/ a la ruta donada.<br />Tingueu en compte que perl només funcionarà al subdirectori de carpetes /cgi-bin/ i no a la carpeta en si (com ho fa sense aquesta solució!)'\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Ruta per als enllaços simbòlics de directori habilitats per a perl del client',\n\t\t\t\t'description' => 'Només heu de configurar això si la solució de SuExec està activada.<br/>ATENCIÓ: Assegureu-vos que aquesta ruta està dins de la ruta de suexec o en cas contrari aquesta solució és inútil.'\n\t\t\t]\n\t\t],\n\t\t'awstats_awstatspath' => 'Ruta a AWStats \\'awstats.pl\\'.',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'Ruta a la carpeta d\\'icones d\\'AWstats',\n\t\t\t'description' => 'Exemple: /usr/share/awstats/htdocs/icon/'\n\t\t],\n\t\t'login_domain_login' => 'Permetre login amb dominis',\n\t\t'perl_server' => [\n\t\t\t'title' => 'Ubicació del socket del servidor Perl',\n\t\t\t'description' => 'Pot trobar una guia senzilla a: <a target=\"blank\" href=\"https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/\">nginx.com</a>'\n\t\t],\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP backend',\n\t\t\t'description' => 'aquí és on el procés PHP està escoltant peticions de nginx, pot ser un socket unix de combinació ip:port<br/>*NO s\\'utilitza amb php-fpm'\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'Ordre de reinici de PHP',\n\t\t\t'description' => 's\\'utilitza per recarregar el backend PHP si se n\\'utilitza algun<br/>Per defecte: en blanc<br/>*NO s\\'utilitza amb php-fpm'\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => 'Habilitar php-fpm',\n\t\t\t'description' => '<b>Això necessita una configuració especial del servidor web veure <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">manual PHP-FPM</a></b>'\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'Directori de configuració de php-fpm',\n\t\t\t'aliasconfigdir' => 'Directori Àlies de configuració de php-fpm',\n\t\t\t'reload' => 'Ordre de reinici de php-fpm',\n\t\t\t'pm' => 'Control del gestor de processos (pm)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'Nombre de processos fill',\n\t\t\t\t'description' => 'El nombre de processos fill a ser creats quan pm està en \\'static\\' i el nombre màxim de processos fill a ser creats quan pm està en \\'dynamic/ondemand\\'<br/>Equivalent a PHP_FCGI_CHILDREN'\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'El nombre de processos fill creats a l\\'inici',\n\t\t\t\t'description' => 'Nota: Només es fa servir quan pm està configurat com a \\'dynamic'\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'El nombre mínim desitjat de processos de servidor inactius',\n\t\t\t\t'description' => 'Nota: Només es fa servir quan pm és \\'dynamic\\'<br/>Nota: Obligatori quan pm és \\'dynamic'\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'El nombre màxim desitjat de processos inactius de servidor',\n\t\t\t\t'description' => 'Nota: només s\\'utilitza quan pm té el valor \"dynamic\".<br/>Nota: obligatori quan pm té el valor \"dynamic\".'\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Peticions per fill abans de respawning',\n\t\t\t\t'description' => 'Per a un processament infinit de peticions especifiqueu \\'0\\'. Equivalent a PHP_FCGI_MAX_REQUESTS.'\n\t\t\t],\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Temps d\\'espera',\n\t\t\t\t'description' => 'Configuració de temps d\\'espera per a PHP FPM FastCGI.'\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'Directori IPC FastCGI',\n\t\t\t\t'description' => 'El directori on els sockets php-fpm seran emmagatzemats pel servidor web.<br/>Aquest directori ha de ser llegible per al servidor web.'\n\t\t\t],\n\t\t\t'limit_extensions' => [\n\t\t\t\t'title' => 'Extensions permeses',\n\t\t\t\t'description' => 'Limita les extensions de l\\'script principal que FPM permetrà analitzar. Això pot evitar errors de configuració al servidor web. Només heu de limitar FPM a extensions .php per evitar que usuaris maliciosos utilitzin altres extensions per executar codi php. Valor per defecte: .php'\n\t\t\t],\n\t\t\t'envpath' => 'Rutes per afegir a l\\'entorn PATH. Deixeu-lo buit per a cap variable d\\'entorn PATH',\n\t\t\t'override_fpmconfig' => 'Anul·lar la configuració de FPM-daemon (pm, max_children, etc.)',\n\t\t\t'override_fpmconfig_addinfo' => '<br/><span class=\"text-danger\">Només es fa servir si \"Override FPM-daemon settings\" està en \"Sí\"</span>',\n\t\t\t'restart_note' => 'Atenció: La configuració no serà revisada per errors. Si conté errors, PHP-FPM podria no tornar a arrencar.',\n\t\t\t'custom_config' => [\n\t\t\t\t'title' => 'Configuració personalitzada',\n\t\t\t\t'description' => 'Afegeix configuració personalitzada a cada instància de versió de PHP-FPM, per exemple <i>pm.status_path = /status</i> per a monitoratge. Les variables de sota poden ser utilitzades aquí. <strong>Atenció: La configuració no serà revisada per errors. Si conté errors, PHP-FPM podria no tornar a arrencar!</strong>'\n\t\t\t],\n\t\t\t'allow_all_customers' => [\n\t\t\t\t'title' => 'Assigneu aquesta configuració a tots els clients existents',\n\t\t\t\t'description' => 'Establiu-lo a \"cert\" si voleu assignar aquesta configuració a tots els clients existents perquè puguin utilitzar-la. Aquesta configuració no és permanent, però es pot executar diverses vegades.'\n\t\t\t]\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => 'Habilitar l\\'enviament d\\'informes sobre l\\'ús de la web i el trànsit',\n\t\t\t'webmax' => [\n\t\t\t\t'title' => 'Nivell d\\'advertència en percentatge per a l\\'espai web',\n\t\t\t\t'description' => 'Els valors vàlids són 0-150. Establir aquest valor a 0 desactiva aquest informe.'\n\t\t\t],\n\t\t\t'trafficmax' => [\n\t\t\t\t'title' => 'Nivell d\\'advertència en percentatge per al trànsit',\n\t\t\t\t'description' => 'Els valors vàlids són de 0 a 150. El valor 0 desactiva aquest informe.'\n\t\t\t]\n\t\t],\n\t\t'dropdown' => 'Desplegable',\n\t\t'manual' => 'Manual',\n\t\t'default_theme' => 'Tema per defecte',\n\t\t'validate_domain' => 'Validar noms de domini',\n\t\t'diskquota_enabled' => 'Quota activada?',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'Ruta a repquota'\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'Ruta a quotatool'\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => 'Partició en què s\\'emmagatzemen els fitxers del client'\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'Nom del maildir',\n\t\t\t'description' => 'Directori maildir al compte de l\\'usuari. Normalment \\'Maildir\\', en algunes implementacions \\'.maildir\\', i directament al directori de l\\'usuari si es deixa en blanc.'\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => 'Utilitzar Catchall',\n\t\t\t'description' => 'Vol proporcionar als seus clients la funció catchall?'\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => 'Utilitzi les modificacions per Apache 2.4',\n\t\t\t'description' => '<strong class=\"text-danger\">ATENCIÓ:</strong> utilitzeu-lo només si teniu instal·lada la versió 2.4 o superior d\\'apache<br/>en cas contrari el vostre servidor web no podrà arrencar'\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'Ruta al fitxer fastcgi_params',\n\t\t\t'description' => 'Especifiqui la ruta al fitxer fastcgi_params de nginx incloent el nom de fitxer'\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => 'Utilitzar el nom de domini com a valor per defecte per a la ruta DocumentRoot',\n\t\t\t'description' => 'Si està habilitat i la ruta DocumentRoot és buida, el valor per defecte serà el (sub)nom de domini.<br/><br/>Exemples: <br/>/var/clients/nom_client/exemple.com/<br />/var/clients/nom_client/subdomini.exemple.com/'\n\t\t],\n\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t'title' => 'Amagar subdominis a la vista general de configuració PHP',\n\t\t\t'description' => 'Si s\\'activa, els subdominis dels clients no es mostraran a la vista general de configuracions PHP, només es mostrarà el número de subdominis.<br/><br/>Nota: Això només serà visible si heu activat FCGID o PHP-FPM .'\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => 'Amagar subdominis estàndard a la vista general de configuracions PHP',\n\t\t\t'description' => 'Si s\\'activa, els subdominis estàndard dels clients no es mostraran a la vista general de configuracions php<br/><br/>Nota: Això només serà visible si heu activat FCGID o PHP-FPM.'\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => 'Triï el mètode de xifratge de contrasenyes a utilitzar'\n\t\t],\n\t\t'systemdefault' => 'Sistema per defecte',\n\t\t'panel_allow_theme_change_admin' => 'Permetre als administradors canviar el tema',\n\t\t'panel_allow_theme_change_customer' => 'Permetre als clients canviar el tema',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'Servidors AXFR',\n\t\t\t'description' => 'Una llista separada per comes d\\'adreces IP permeses per transferir (AXFR) zones dns.'\n\t\t],\n\t\t'powerdns_mode' => [\n\t\t\t'title' => 'Mode de funcionament PowerDNS',\n\t\t\t'description' => 'Seleccioni el mode PoweDNS: Natiu si no es necessita replicació DNS (Predeterminat) / Mestre si es necessita replicació DNS.'\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Webserver customer-ssl certificates-directory',\n\t\t\t'description' => 'On s\\'han de crear els certificats ssl especificats pel client?<br/><br/><div class=\"text-danger\">NOTA: El contingut d\\'aquesta carpeta s\\'esborra amb regularitat, així que eviteu emmagatzemar dades allà manualment. </div>'\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => 'Permetre als administradors/revenedors informar d\\'errors a la base de dades a froxlor',\n\t\t\t'description' => 'Nota: Mai ens enviïs dades personals (de clients)!'\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => 'Permetre als clients informar d\\'errors a la base de dades a froxlor',\n\t\t\t'description' => 'Nota: Mai ens enviïs dades personals (de clients)!'\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => 'Analitzar el trànsit de correu',\n\t\t\t'description' => 'Permetre l\\'anàlisi dels registres del servidor de correu per calcular el trànsit'\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'Tipus de MDA',\n\t\t\t'description' => 'Tipus de servidor de lliurament de correu'\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'Registre MDA',\n\t\t\t'description' => 'Fitxer de registre del Mail Delivery Server'\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'Tipus de MTA',\n\t\t\t'description' => 'Tipus d\\'agent de transferència de correu'\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'Registre MTA',\n\t\t\t'description' => 'Fitxer de registre de l\\'agent de transferència de correu'\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'Fitxer de configuració de cron',\n\t\t\t'description' => 'Ruta al fitxer de configuració del servei cron. Aquest fitxer serà actualitzat regularment i automàticament per froxlor.<br/>Nota: Si us plau <b>assegureu-vos</b> d\\'utilitzar el mateix nom de fitxer que per al froxlor cronjob principal (per defecte: /etc/cron.d/froxlor)!<br/><br/>Si utilitzeu <b>FreeBSD</b>, si us plau especifiqueu <i>/etc/crontab</i> aquí!'\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Ordre de recàrrega pel domini Cron',\n\t\t\t'description' => 'Especifiqui l\\'ordre a executar per recarregar el dimoni cron del vostre sistema'\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Ordre d\\'execució de cron (php-binari)',\n\t\t\t'description' => 'Ordre per executar les nostres tasques cron. Canvieu-ho només si sabeu el que esteu fent (per defecte: \"/usr/bin/nice -n 5 /usr/bin/php -q\").'\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => 'Permetre actualitzar automàticament la base de dades',\n\t\t\t'description' => '<div class=\"text-danger\"><b>ATENCIÓ:</b></div> Aquesta configuració permet al cronjob saltar-se la comprovació de versió dels fitxers froxlors i la base de dades i executa l\\'actualització de la base de dades en cas que passi un desajust de versió.<br/><br/><div class= \"text-danger\">Auto-update sempre establirà valors per defecte per a noves configuracions o canvis. Això pot no ser sempre adequat per al vostre sistema. Si us plau, penseu-ho dues vegades abans d\\'activar aquesta opció</div>'\n\t\t],\n\t\t'dns_createhostnameentry' => 'Crear bind-zone/config per al nom de host del sistema',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => 'Minúscules',\n\t\t\t'description' => 'La contrasenya ha de contenir com a mínim una lletra minúscula (a-z).'\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => 'Majúscules',\n\t\t\t'description' => 'La contrasenya ha de contenir com a mínim una lletra majúscula (A-Z).'\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => 'Números',\n\t\t\t'description' => 'La contrasenya ha de contenir almenys un número (0-9).'\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => 'Caràcter especial',\n\t\t\t'description' => 'La contrasenya ha de contenir com a mínim un dels caràcters definits a continuació.'\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => 'Llista de caràcters especials',\n\t\t\t'description' => 'Es requereix un d\\'aquests caràcters si s\\'estableix l\\'opció anterior.'\n\t\t],\n\t\t'apache_itksupport' => [\n\t\t\t'title' => 'Modificacions d\\'ús per Apache ITK-MPM',\n\t\t\t'description' => '<strong class=\"text-danger\">ATENCIÓ:</strong> utilitzeu-la només si teniu apache itk-mpm habilitat<br/>, altrament el vostre servidor web no es podrà iniciar.'\n\t\t],\n\t\t'letsencryptca' => [\n\t\t\t'title' => 'Entorn ACME',\n\t\t\t'description' => 'Entorn que s\\'utilitzarà per als certificats Let\\'s Encrypt / ZeroSSL.'\n\t\t],\n\t\t'letsencryptchallengepath' => [\n\t\t\t'title' => 'Ruta per als desafiaments Let\\'s Encrypt',\n\t\t\t'description' => 'Directori des del qual s\\'oferiran els reptes Let\\'s Encrypt a través d\\'un àlies global.'\n\t\t],\n\t\t'letsencryptkeysize' => [\n\t\t\t'title' => 'Mida de la clau per a nous certificats Let\\'s Encrypt',\n\t\t\t'description' => 'Mida de la clau en Bits per a nous certificats Let\\'s Encrypt.'\n\t\t],\n\t\t'letsencryptreuseold' => [\n\t\t\t'title' => 'Reutilitzar clau Let\\'s Encrypt',\n\t\t\t'description' => 'Si s\\'activa, s\\'utilitzarà la mateixa clau per a cada renovació, altrament es generarà una clau nova cada vegada.'\n\t\t],\n\t\t'leenabled' => [\n\t\t\t'title' => 'Activar Let\\'s Encrypt',\n\t\t\t'description' => 'Si s\\'activa, els clients poden deixar que automàticament froxlor generi i renovi certificats ssl Let\\'s Encrypt per a dominis amb una IP/port ssl.<br/><br/>Si us plau recorda que necessites anar a través de la configuració del servidor web quan s\\'activa perquè aquesta característica necessita una configuració especial.'\n\t\t],\n\t\t'caa_entry' => [\n\t\t\t'title' => 'Generar registres DNS CAA',\n\t\t\t'description' => 'Genera automàticament registres CAA per a dominis habilitats per a SSL que utilitzen Let\\'s Encrypt.'\n\t\t],\n\t\t'caa_entry_custom' => [\n\t\t\t'title' => 'Registres DNS CAA addicionals',\n\t\t\t'description' => 'DNS Certification Authority Authorization (CAA) és un mecanisme de política de seguretat a Internet que permet als titulars de noms de domini indicar a les autoritats de certificació<br/>si estan autoritzades a emetre certificats digitals per a un nom de domini concret. Ho fa mitjançant un nou registre de recursos del sistema de noms de domini (DNS) \"CAA\".<br/><br/>El contingut d\\'aquest camp s\\'inclourà a la zona DNS directament (cada línia dóna lloc a un registre CAA ).<br/>Si Let\\'s Encrypt està habilitat per a aquest domini, aquesta entrada sempre s\\'afegirà automàticament i no caldrà afegir-la manualment:<br/> 0<code>issue \"letsencrypt.org\"</code> ( Si el domini és un domini comodí, s\\'utilitzarà issuewild al seu lloc).<br/>Per habilitar l\\'informe d\\'incidents, podeu afegir un registre <code>iodef</code>. Un exemple per enviar aquest informe a <code>me@example.com</code> seria:<br/> 0<code>iodef \"mailto:me@example.com\"</code><br/><br/ ><strong>Atenció:</strong> No es comprovarà si el codi conté errors. Si conté errors, és possible que els vostres registres CAA no funcionin!'\n\t\t],\n\t\t'backupenabled' => [\n  \t\t\t'title' => 'Activar còpia de seguretat per a clients',\n\t\t\t'description' => 'Si s\\'activa, el client podrà programar treballs de còpia de seguretat (cron-backup) que generen un fitxer dins del seu docroot (subdirectori a elecció del client)'\n\t\t],\n\t\t'dnseditorenable' => [\n\t\t\t'title' => 'Habilitar editor DNS',\n\t\t\t'description' => 'Permet als administradors i als clients gestionar les entrades DNS del domini'\n\t\t],\n\t\t'dns_server' => [\n\t\t\t'title' => 'Dimoni del servidor DNS',\n\t\t\t'description' => 'Recordi que els dimonis han de ser configurats usant les plantilles de configuració de froxlors'\n\t\t],\n\t\t'panel_customer_hide_options' => [\n\t\t\t'title' => 'Amagar elements de menú i gràfics de trànsit al panell de client',\n\t\t\t'description' => 'Seleccioni els elements que vol amagar al panell de client. Per seleccionar diverses opcions, mantingueu premuda la tecla CTRL mentre seleccioneu.'\n\t\t],\n\t\t'allow_allow_customer_shell' => [\n\t\t\t'title' => 'Permetre als clients habilitar l\\'accés shell per a usuaris ftp',\n\t\t\t'description' => '<strong class=\"text-danger\">Atenció: L\\'accés Shell permet a l\\'usuari executar diversos binaris al vostre sistema. Utilitzeu-lo amb extrema precaució. Si us plau, activeu això només si REALMENT sap el que està fent!!!</strong>'\n\t\t],\n\t\t'available_shells' => [\n\t\t\t'title' => 'Llista de shells disponibles',\n\t\t\t'description' => 'Llista separada per comes dels intèrprets d\\'ordres disponibles perquè el client esculli per als seus usuaris ftp.<br/><br/>Tingueu en compte que l\\'intèrpret d\\'ordres predeterminat <strong>/bin/false</strong> sempre serà una opció (si està habilitat), fins i tot si aquesta configuració és buida. És el valor per defecte per als usuaris ftp en qualsevol cas.'\n\t\t],\n\t\t'le_froxlor_enabled' => [\n\t\t\t'title' => 'Activar Let\\'s Encrypt per al froxlor vhost',\n\t\t\t'description' => 'Si s\\'activa, el froxlor vhost s\\'assegurarà automàticament utilitzant un certificat Let\\'s Encrypt.'\n\t\t],\n\t\t'le_froxlor_redirect' => [\n\t\t\t'title' => 'Activar SSL-redirect per a froxlor vhost',\n\t\t\t'description' => 'Si s\\'activa, totes les peticions http al vostre froxlor seran redirigides al lloc SSL corresponent.'\n\t\t],\n\t\t'option_unavailable_websrv' => '<br/><em class=\"text-danger\">Disponible només per a: %s</em>',\n\t\t'option_unavailable' => '<br/><em class=\"text-danger\">Opció no disponible degut a altres paràmetres.</em>',\n\t\t'letsencryptacmeconf' => [\n\t\t\t'title' => 'Ruta al fragment acme.conf',\n\t\t\t'description' => 'Nom de fitxer del fragment de configuració que permet al servidor web utilitzar el desafiament acme.'\n\t\t],\n\t\t'mail_use_smtp' => 'Configurar mailer per utilitzar SMTP',\n\t\t'mail_smtp_host' => 'Especifiqui el servidor SMTP',\n\t\t'mail_smtp_usetls' => 'Activar el xifratge TLS',\n\t\t'mail_smtp_auth' => 'Activar l\\'autenticació SMTP',\n\t\t'mail_smtp_port' => 'Port TCP al qual connectar-se',\n\t\t'mail_smtp_user' => 'Nom d\\'usuari SMTP',\n\t\t'mail_smtp_passwd' => 'Contrasenya SMTP',\n\t\t'http2_support' => [\n\t\t\t'title' => 'Suport HTTP2',\n\t\t\t'description' => 'habiliti el suport HTTP2 per a ssl.<br/><em class=\"text-danger\">HABILITAR NOMÉS SI EL SEU WEBSERVER SUPORTA AQUESTA CARACTERÍSTICA (nginx versió 1.9.5+, apache2 versió 2.4.17+)</em>'\n\t\t],\n\t\t'nssextrausers' => [\n\t\t\t'title' => 'Utilitzar libnss-extrausers en lloc de libnss-mysql',\n\t\t\t'description' => 'No llegir usuaris de la base de dades sinó de fitxers. Si us plau, activa-ho només si ja has realitzat els passos de configuració necessaris (system -><strong class=\"text-danger\">libnss-extrausers).</strong><br/><strong class=\"text-danger\" >Només per a Debian/Ubuntu (o si ha compilat libnss-extrausers vostè mateix!)</strong>'\n\t\t],\n\t\t'le_domain_dnscheck' => [\n\t\t\t'title' => 'Validar DNS de dominis en utilitzar Let\\'s Encrypt',\n\t\t\t'description' => 'Si està activat, froxlor validarà si el domini que sol·licita un certificat Let\\'s Encrypt resol almenys una de les adreces ip del sistema.'\n\t\t],\n\t\t'le_domain_dnscheck_resolver' => [\n\t\t\t'title' => 'Utilitzar un servidor de noms extern per a la validació DNS',\n\t\t\t'description' => 'Si s\\'estableix, froxlor utilitzarà aquest DNS per validar els DNS dels dominis quan utilitzeu Let\\'s Encrypt. Si està buit, s\\'utilitzarà el solucionador DNS per defecte del sistema.'\n\t\t],\n\t\t'phpsettingsforsubdomains' => [\n\t\t\t'description' => 'En cas afirmatiu s\\'actualitzarà el php-config triat a tots els subdominis'\n\t\t],\n\t\t'leapiversion' => [\n\t\t\t'title' => 'Escollir la implementació ACME de Let\\'s Encrypt',\n\t\t\t'description' => 'Actualment només es suporta la implementació ACME v2 per a Let\\'s Encrypt.'\n\t\t],\n\t\t'enable_api' => [\n\t\t\t'title' => 'Habilitar ús d\\'API externa',\n\t\t\t'description' => 'Per utilitzar la API froxlor cal activar aquesta opció. Per obtenir informació més detallada, vegeu <a href=\"https://docs.froxlor.org/latest/api-guide/\" target=\"_new\">https://docs.froxlor.org/</a>'\n\t\t],\n\t\t'api_customer_default' => '\"Permetre accés a la API\" valor per defecte per a nous clients',\n\t\t'dhparams_file' => [\n\t\t\t'title' => 'Fitxer DHParams (intercanvi de claus Diffie-Hellman)',\n\t\t\t'description' => 'Si s\\'especifica aquí un fitxer dhparams.pem, s\\'inclourà a la configuració del servidor web. Deixeu-lo buit per desactivar-lo.<br/>Exemple: <code>/etc/ssl/webserver/dhparams</code>.pem<br/><br/>Si el fitxer no existeix, es crearà automàticament amb la següent ordre: <code>openssl dhparam -out /etc/ssl/webserver/dhparams.pem 4096</code>. Es recomana crear el fitxer abans d\\'especificar-lo aquí, ja que la creació triga força i bloqueja el cronjob.'\n\t\t],\n\t\t'errorlog_level' => [\n\t\t\t'title' => 'Nivell de registre d\\'errors',\n\t\t\t'description' => 'Especifiqui el nivell de registre d\\'errors. Per defecte és \"warn\" per a usuaris apache i \"error\" per a usuaris nginx.'\n\t\t],\n\t\t'letsencryptecc' => [\n\t\t\t'title' => 'Emetre certificat ECC / ECDSA',\n\t\t\t'description' => 'Si s\\'estableix una mida de clau vàlida, el certificat emès utilitzarà ECC/ECDSA.'\n\t\t],\n\t\t'froxloraliases' => [\n\t\t\t'title' => 'Àlies de domini per a froxlor vhost',\n\t\t\t'description' => 'Llista separada per comes de dominis per afegir com a àlies de servidor al froxlor vhost'\n\t\t],\n\t\t'default_sslvhostconf' => [\n\t\t\t'title' => 'SSL per defecte vHost-settings',\n\t\t\t'description' => 'El contingut d\\'aquest camp s\\'inclourà directament en aquest contenidor ip/port vHost. Podeu utilitzar les variables següents:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}< /code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (si escau)<br/> Atenció: No es comprovarà si el codi conté errors. Si conté errors, el servidor web podria no tornar a arrencar!'\n\t\t],\n\t\t'includedefault_sslvhostconf' => 'Incloure configuració de vHost no SSL a SSL-vHost',\n\t\t'apply_specialsettings_default' => [\n\t\t\t'title' => 'Valor per defecte per a \"Aplicar configuracions especials a tots els subdominis (*.exemple.com)\\' en editar un domini'\n\t\t],\n\t\t'apply_phpconfigs_default' => [\n\t\t\t'title' => 'Valor per defecte per a \"Aplicar php-config a tots els subdominis:\\' en editar un domini'\n\t\t],\n\t\t'awstats' => [\n\t\t\t'logformat' => [\n\t\t\t\t'title' => 'Configuració de LogFormat',\n\t\t\t\t'description' => 'Si utilitzeu un format de registre personalitzat per al vostre servidor web, necessiteu canviar també l\\'awstats LogFormat.<br/>Per defecte és 1. Per a més informació consulta la documentació <a target=\"_blank\" href=\"https://awstats.sourceforge.io/docs/awstats_config.html#LogFormat\">aquí</a>.'\n\t\t\t]\n\t\t],\n\t\t'hide_incompatible_settings' => 'Amagar configuracions incompatibles',\n\t\t'soaemail' => 'Adreça de correu a utilitzar en registres SOA (per defecte l\\'adreça del remitent de la configuració del panell si és buida)',\n\t\t'imprint_url' => [\n\t\t\t'title' => 'URL a notes legals / impressió',\n\t\t\t'description' => 'Especifiqui una URL al vostre lloc de notes legals / impressió. L\\'enllaç serà visible a la pantalla d\\'inici de sessió i al peu de pàgina un cop iniciada la sessió.'\n\t\t],\n\t\t'terms_url' => [\n\t\t\t'title' => 'URL a les condicions d\\'ús',\n\t\t\t'description' => 'Especifiqui una URL al vostre lloc de condicions d\\'ús. L\\'enllaç serà visible a la pantalla d\\'inici de sessió i al peu de pàgina en iniciar sessió.'\n\t\t],\n\t\t'privacy_url' => [\n\t\t\t'title' => 'URL a la política de privadesa',\n\t\t\t'description' => 'Especifiqui una URL al vostre lloc de política de privadesa / lloc d\\'impressió. L\\'enllaç serà visible a la pantalla d\\'inici de sessió i al peu de pàgina en iniciar sessió.'\n\t\t],\n\t\t'logo_image_header' => [\n\t\t\t'title' => 'Imatge de logotip (capçalera)',\n\t\t\t'description' => 'Carregui la vostra pròpia imatge de logotip perquè es mostri a la capçalera després de l\\'inici de sessió (alçada recomanada 30px)'\n\t\t],\n\t\t'logo_image_login' => [\n\t\t\t'title' => 'Imatge del logotip (inici de sessió)',\n\t\t\t'description' => 'Carregui la vostra pròpia imatge de logotip perquè es mostri durant l\\'inici de sessió'\n\t\t],\n\t\t'logo_overridetheme' => [\n\t\t\t'title' => 'Sobreescriu el logotip definit en el tema per \"Logo Image\" (Capçalera i Login, veure més a avall)',\n\t\t\t'description' => 'Aquesta opció s\\'ha d\\'establir com a \"cert\" si vol utilitzar el logotip que ha carregat; com a alternativa, pot continuar utilitzant les possibilitats basades en el tema \"logo_custom.png\" i \"logo_custom_login.png\".'\n\t\t],\n\t\t'logo_overridecustom' => [\n\t\t\t'title' => 'Sobreescriure el logotip personalitzat (logo_custom.png i logo_custom_login.png) definit al tema per \"Imatge del logotip\" (Capçalera i Inici de sessió, veure més avall).',\n\t\t\t'description' => 'Estableixi aquest valor a \"cert\" si vol ignorar els logotips personalitzats específics del tema per a la capçalera i l\\'inici de sessió i utilitzar \"Logo Image\".'\n\t\t],\n\t\t'createstdsubdom_default' => [\n\t\t\t'title' => 'Valor preseleccionat per a \"Crear subdomini estàndard\" en crear un client',\n\t\t\t'description' => ''\n\t\t],\n\t\t'froxlorusergroup' => [\n\t\t\t'title' => 'Grup de sistema personalitzat per a tots els usuaris del client',\n\t\t\t'description' => 'Cal utilitzar libnss-extrausers (system-settings) perquè això tingui efecte. Un valor buit omet la creació o elimina el grup existent.'\n\t\t],\n\t\t'acmeshpath' => [\n\t\t\t'title' => 'Ruta a acme.sh',\n\t\t\t'description' => 'Establiu-lo on s\\'instal·la acme.sh, incloent l\\'script acme.sh<br/>Per defecte és <b>/root/.acme.sh/acme.sh</b>'\n\t\t],\n\t\t'update_channel' => [\n\t\t\t'title' => 'Actualitzar canal froxlor',\n\t\t\t'description' => 'Seleccioni el canal d\\'actualització de froxlor. Per defecte és \"estable\"'\n\t\t],\n\t\t'uc_stable' => 'estable',\n\t\t'uc_testing' => 'tests',\n\t\t'traffictool' => [\n\t\t\t'toolselect' => 'Analitzador de trànsit',\n\t\t\t'webalizer' => 'Webalizer',\n\t\t\t'awstats' => 'AWStats',\n\t\t\t'goaccess' => 'goacccess'\n\t\t],\n\t\t'requires_reconfiguration' => 'El canvi d\\'aquesta configuració podria requerir una reconfiguració dels serveis següents:<br/><strong>%s</strong>'\n\t],\n\t'spf' => [\n\t\t'use_spf' => 'Activar SPF per a dominis?',\n\t\t'spf_entry' => 'Entrada SPF per a tots els dominis'\n\t],\n\t'ssl_certificates' => [\n\t\t'certificate_for' => 'Certificat per a',\n\t\t'valid_from' => 'Vàlid des de',\n\t\t'valid_until' => 'Vàlid fins a',\n\t\t'issuer' => 'Emissor'\n\t],\n\t'success' => [\n\t\t'success' => 'Informació',\n\t\t'clickheretocontinue' => 'Feu clic aquí per a continuar',\n\t\t'settingssaved' => 'La configuració s\\'ha guardat correctament.',\n\t\t'rebuildingconfigs' => 'Tasques inserides amb èxit per reconstruir fitxers de configuració',\n\t\t'domain_import_successfully' => 'S\\'han importat correctament els dominis %s.',\n\t\t'backupscheduled' => 'S\\'ha programat la vostra tasca de còpia de seguretat. Espereu que es processi.',\n\t\t'backupaborted' => 'La seva còpia de seguretat programada ha estat cancel·lada',\n\t\t'dns_record_added' => 'Registre DNS afegit correctament',\n\t\t'dns_record_deleted' => 'Registre DNS eliminat correctament',\n\t\t'testmailsent' => 'Correu de prova enviat correctament',\n\t\t'settingsimported' => 'Configuració importada correctament',\n\t\t'sent_error_report' => 'Informe d\\'error enviat correctament. Gràcies per la vostra contribució.'\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Tasques cron pendents',\n\t\t'REBUILD_VHOST' => 'Reconstrucció de la configuració del servidor web',\n\t\t'CREATE_HOME' => 'Afegeix nova %s client',\n\t\t'REBUILD_DNS' => 'Reconstrucció de la configuració bind',\n\t\t'CREATE_FTP' => 'Crear directori per a un nou usuari ftp',\n\t\t'DELETE_CUSTOMER_FILES' => 'Esborrar fitxers de client %s',\n\t\t'noneoutstanding' => 'Actualment no hi ha tasques pendents per a froxlor',\n\t\t'CREATE_QUOTA' => 'Establir quota al sistema de fitxers',\n\t\t'DELETE_EMAIL_DATA' => 'Esborrar dades de correu electrònic del client.',\n\t\t'DELETE_FTP_DATA' => 'Esborrar les dades del compte ftp del client.',\n\t\t'REBUILD_CRON' => 'Reconstruir el fitxer cron.d',\n\t\t'CREATE_CUSTOMER_BACKUP' => 'Treball de còpia de seguretat per al client %s',\n\t\t'DELETE_DOMAIN_PDNS' => 'Esborrar domini %s de la base de dades PowerDNS',\n\t\t'DELETE_DOMAIN_SSL' => 'Esborrar fitxers ssl de domini %s'\n\t],\n\t'terms' => 'Condicions d\\'ús',\n\t'traffic' => [\n\t\t'month' => 'Mes',\n\t\t'day' => 'Dia',\n\t\t'months' => [\n\t\t\t'1' => 'Gener',\n\t\t\t'2' => 'Febrer',\n\t\t\t'3' => 'Març',\n\t\t\t'4' => 'Abril',\n\t\t\t'5' => 'Maig',\n\t\t\t'6' => 'Juny',\n\t\t\t'7' => 'Juliol',\n\t\t\t'8' => 'Agost',\n\t\t\t'9' => 'Setembre',\n\t\t\t'10' => 'Octubre',\n\t\t\t'11' => 'Novembre',\n\t\t\t'12' => 'Desembre',\n\t\t\t'jan' => 'Gener',\n\t\t\t'feb' => 'Febrer',\n\t\t\t'mar' => 'Març',\n\t\t\t'apr' => 'Abril',\n\t\t\t'may' => 'Maig',\n\t\t\t'jun' => 'Juny',\n\t\t\t'jul' => 'Juliol',\n\t\t\t'aug' => 'Agost',\n\t\t\t'sep' => 'Setembre',\n\t\t\t'oct' => 'Octubre',\n\t\t\t'nov' => 'Novembre',\n\t\t\t'dec' => 'Desembre',\n\t\t\t'total' => 'Total'\n\t\t],\n\t\t'mb' => 'Trànsit',\n\t\t'sumtotal' => 'Trànsit total',\n\t\t'sumhttp' => 'Trànsit HTTP',\n\t\t'sumftp' => 'Trànsit FTP',\n\t\t'summail' => 'Trànsit de correu',\n\t\t'customer' => 'Client',\n\t\t'domain' => 'Domini',\n\t\t'trafficoverview' => 'Resum del trànsit',\n\t\t'bycustomers' => 'Trànsit per clients',\n\t\t'details' => 'Detalls',\n\t\t'http' => 'HTTP',\n\t\t'ftp' => 'FTP',\n\t\t'mail' => 'Correu',\n\t\t'nocustomers' => 'Necessita com a mínim un client per veure els informes de trànsit.',\n\t\t'top5customers' => 'Top 5 clients',\n\t\t'nodata' => 'No s\\'han trobat dades per a l\\'interval donat.',\n\t\t'ranges' => [\n\t\t\t'last24h' => 'últimes 24 hores',\n\t\t\t'last7d' => 'últims 7 dies',\n\t\t\t'last30d' => 'últims 30 dies',\n\t\t\t'cm' => 'Mes actual',\n\t\t\t'last3m' => 'últims 3 mesos',\n\t\t\t'last6m' => 'últims 6 mesos',\n\t\t\t'last12m' => 'últims 12 meses',\n\t\t\t'cy' => 'Any en curs'\n\t\t],\n\t\t'byrange' => 'Especificat per rang'\n\t],\n\t'translator' => '',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'S\\'ha instal·lat una versió més recent del froxlor però encara no s\\'ha configurat.<br/>Només l\\'administrador pot iniciar sessió i finalitzar l\\'actualització.',\n\t\t'update' => 'Actualització de froxlor',\n\t\t'proceed' => 'Procedir',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'Els fitxers froxlor han estat actualitzats a la versió <strong>%s</strong>. La versió instal·lada és <strong>%s</strong>.',\n\t\t\t'part_b' => '<br/><br/>Els clients no es poden connectar fins que l\\'actualització hagi finalitzat.<br/><strong>Procedir?</strong>'\n\t\t],\n\t\t'noupdatesavail' => 'Ja té instal·lada la darrera %sversion de froxlor.',\n\t\t'description' => 'Executant actualitzacions de la base de dades per a la instal·lació de froxlor',\n\t\t'uc_newinfo' => 'Hi ha una versió més recent disponible: \"%s\" (La vostra versió actual és: %s)',\n\t\t'notify_subject' => 'Nova actualització disponible'\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => 'Notes personalitzades',\n\t\t\t'description' => 'Sigues lliure de posar les notes que vulguis/necessitis aquí. Es mostraran a la vista general de l\\'administrador/client per a l\\'usuari corresponent.',\n\t\t\t'show' => 'Mostrar les teves notes al tauler de control de l\\'usuari'\n\t\t],\n\t\t'api_allowed' => [\n\t\t\t'title' => 'Permet accedir a la API',\n\t\t\t'description' => 'Quan està habilitat a la configuració, aquest usuari pot crear claus API i accedir a la API froxlor',\n\t\t\t'notice' => 'L\\'accés a la API no està permès per al vostre compte.'\n\t\t]\n\t],\n\t'install' => [\n\t\t'slogan' => 'Panell de gestió del servidor froxlor',\n\t\t'preflight' => 'Comprovació del sistema',\n\t\t'critical_error' => 'Error crític',\n\t\t'suggestions' => 'No requerit però recomanat',\n\t\t'phpinfosuccess' => 'El seu sistema funciona amb PHP %s',\n\t\t'phpinfowarn' => 'El seu sistema està executant una versió inferior a PHP %s',\n\t\t'phpinfoupdate' => 'Actualitzi la seva versió PHP actual de %s a %s o superior',\n\t\t'start_installation' => 'Inicieu la instal·lació',\n\t\t'check_again' => 'Recarregar per comprovar de nou',\n\t\t'switchmode_advanced' => 'Mostrar opcions avançades',\n\t\t'switchmode_basic' => 'Ocultar opcions avançades',\n\t\t'dependency_check' => [\n\t\t\t'title' => 'Benvingut a froxlor',\n\t\t\t'description' => 'Comprovem les dependències del sistema per assegurar-nos que totes les extensions i mòduls php necessaris estan habilitats perquè froxlor funcioni correctament.'\n\t\t],\n\t\t'database' => [\n\t\t\t'top' => 'Base de dades',\n\t\t\t'title' => 'Crear base de dades i usuari',\n\t\t\t'description' => 'froxlor requereix una base de dades i addicionalment un usuari privilegiat per poder crear usuaris i bases de dades (opció GRANT). La base de dades i l\\'usuari no privilegiat es crearan en aquest procés. L\\'usuari privilegiat ha d\\'existir.',\n\t\t\t'user' => 'Usuari no privilegiat de la base de dades',\n\t\t\t'dbname' => 'Nom de la base de dades',\n\t\t\t'force_create' => 'Fer còpia de seguretat i sobreescriure la base de dades si existeix?'\n\t\t],\n\t\t'admin' => [\n\t\t\t'top' => 'Usuari administrador',\n\t\t\t'title' => 'Crearem l\\'usuari administrador principal.',\n\t\t\t'description' => 'Aquest usuari tindrà tots els privilegis per ajustar la configuració i afegir/actualitzar/eliminar recursos com a clients, dominis, etc.'\n\t\t],\n\t\t'system' => [\n\t\t\t'top' => 'Configuració del sistema',\n\t\t\t'title' => 'Detalls sobre el vostre servidor',\n\t\t\t'description' => 'Establiu el vostre entorn així com les dades i opcions rellevants del servidor aquí perquè froxlor conegui el vostre sistema. Aquests valors són crucials per a la configuració i el funcionament del sistema.',\n\t\t\t'ipv4' => 'Adreça IPv4 primària (si escau)',\n\t\t\t'ipv6' => 'Adreça IPv6 primària (si escau)',\n\t\t\t'servername' => 'Nom del servidor (FQDN, sense adreça IP)',\n\t\t\t'phpbackend' => 'PHP backend',\n\t\t\t'activate_newsfeed' => 'Habilitar la font oficial de notícies<br/><small>(font externa: https://inside.froxlor.org/news/)</small>'\n\t\t],\n\t\t'install' => [\n\t\t\t'top' => 'Finalitzar la configuració',\n\t\t\t'title' => 'Un últim pas...',\n\t\t\t'description' => 'L\\'ordre següent descarregarà, instal·larà i configurarà els serveis necessaris al vostre sistema d\\'acord amb les dades que heu facilitat en aquest procés d\\'instal·lació.',\n\t\t\t'runcmd' => 'Executeu la següent ordre com a usuari root al vostre intèrpret d\\'ordres en aquest servidor:',\n\t\t\t'manual_config' => 'Configuraré manualment els serveis, porta\\'m a l\\'inici de sessió',\n\t\t\t'waitforconfig' => 'Esperant que es configurin els serveis...'\n\t\t],\n\t\t'errors' => [\n\t\t\t'wrong_ownership' => 'Assegureu-vos que els fitxers froxlor són propietat de %s:%s',\n\t\t\t'missing_extensions' => 'Les següents extensions de php són necessàries i no estan instal·lades',\n\t\t\t'suggestedextensions' => 'No s\\'han trobat les següents extensions de php però es recomanen',\n\t\t\t'databaseexists' => 'La base de dades ja existeix, si us plau establiu l\\'opció override per reconstruir o trieu un altre nom',\n\t\t\t'unabletocreatedb' => 'No s\\'ha pogut crear la base de dades de prova',\n\t\t\t'unabletodropdb' => 'No s\\'ha pogut suprimir la base de dades de prova',\n\t\t\t'mysqlusernameexists' => 'L\\'usuari especificat per a l\\'usuari sense privilegis ja existeix. Si us plau, utilitzeu un altre nom d\\'usuari o elimineu-lo primer.',\n\t\t\t'unabletocreateuser' => 'No s\\'ha pogut crear l\\'usuari de prova',\n\t\t\t'unabletodropuser' => 'L\\'usuari de prova no s\\'ha pogut eliminar.',\n\t\t\t'unabletoflushprivs' => 'L\\'usuari privilegiat no pot eliminar privilegis.',\n\t\t\t'nov4andnov6ip' => 'Cal indicar una adreça IPv4 o IPv6.',\n\t\t\t'servernameneedstobevalid' => 'El nom de servidor indicat no sembla que sigui un FQDN o un nom de host.',\n\t\t\t'websrvuserdoesnotexist' => 'Sembla que l\\'usuari del servidor web no existeix al sistema.',\n\t\t\t'websrvgrpdoesnotexist' => 'El webserver-group indicat no sembla existir al sistema.',\n\t\t\t'notyetconfigured' => 'Els serveis encara no s\\'han configurat (correctament). Si us plau, executeu l\\'ordre que es mostra a continuació o marqueu la casella per fer-ho més tard.',\n\t\t\t'mandatory_field_not_set' => 'El camp obligatori \"%s\" no està configurat.',\n\t\t\t'unexpected_database_error' => 'S\\'ha produït una excepció inesperada a la base de dades. %s',\n\t\t\t'sql_import_failed' => 'Error en importar dades SQL.',\n\t\t\t'unprivileged_sql_connection_failed' => 'Error en inicialitzar una connexió SQL sense privilegis.',\n\t\t\t'privileged_sql_connection_failed' => 'Error en inicialitzar la connexió SQL privilegiada.',\n\t\t\t'mysqldump_backup_failed' => 'No es pot crear una còpia de seguretat de la base de dades, tenim un error de mysqldump.',\n\t\t\t'sql_backup_file_missing' => 'No es pot crear una còpia de seguretat de la base de dades, el fitxer de còpia de seguretat no existeix.',\n\t\t\t'backup_binary_missing' => 'No es pot crear una còpia de seguretat de la base de dades, assegureu-vos que heu instal·lat mysqldump.',\n\t\t\t'creating_configfile_failed' => 'No es poden crear fitxers de configuració, no es pot escriure el fitxer.',\n\t\t\t'database_already_exiting' => 'Hem trobat una base de dades i no se\\'ns ha permès sobreescriure-la!'\n\t\t]\n\t],\n\t'welcome' => [\n\t\t'title' => 'Benvingut a froxlor!',\n\t\t'config_note' => 'Perquè froxlor pugui comunicar-se correctament amb el backend, has de configurar-lo.',\n\t\t'config_now' => 'Configurar ara'\n\t],\n];\n"
  },
  {
    "path": "lng/cz.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'languages' => [\n\t\t'cz' => 'Čeština',\n\t\t'de' => 'Němčina',\n\t\t'en' => 'Angličtina',\n\t\t'fr' => 'Francouzština',\n\t\t'hu' => 'Maďarština',\n\t\t'it' => 'Italština',\n\t\t'nl' => 'Holandština',\n\t\t'pt' => 'Portugalština',\n\t\t'se' => 'Švědština',\n\t\t'sk' => 'Slovenština',\n\t\t'es' => 'Španělština',\n\t\t'ca' => 'Katalánština',\n\t\t'zh_CN' => 'Čínština (Zjednodušená)',\n\t],\n\t'2fa' => [\n\t\t'2fa' => 'Možnosti 2FA',\n\t\t'2fa_enabled' => 'Aktivovat dvoufázové ověření (2FA)',\n\t\t'2fa_removed' => '2FA úspěšně odebráno',\n\t\t'2fa_added' => '2FA aktivováno úspěšně<br><a class=\"alert-link\" href=\"%s?page=2fa\">Zobrazit 2FA podrobnosti</a>',\n\t\t'2fa_add' => 'Aktivovat 2FA',\n\t\t'2fa_delete' => 'Deaktivovat 2FA',\n\t\t'2fa_verify' => 'Ověřit kód',\n\t\t'2fa_overview_desc' => 'Zde můžete aktivovat dvoufaktorové ověřování vašeho účtu.<br><br>Můžete buď použít autentifikátor-aplikaci (jednorázové heslo / TOTP) nebo vám nechat froxlor po každém úspěšném přihlášení pomocí jednorázového hesla poslat e-mail na vaši e-mailovou adresu.',\n\t\t'2fa_email_desc' => 'Váš účet je nastaven pro použití jednorázových hesel e-mailem. Chcete-li deaktivovat, klikněte na \"Deaktivovat 2FA\"',\n\t\t'2fa_ga_desc' => 'Váš účet je nastaven tak, aby používal jednorázová hesla založená na čase prostřednictvím autentizační aplikace. Naskenujte níže uvedený QR kód pomocí požadované autentizační aplikace pro vygenerování kódů. Chcete-li deaktivovat, klikněte na \"Deaktivovat 2FA\"',\n\t\t'2fa_not_activated' => 'Dvoufázové ověřování není povoleno',\n\t\t'2fa_not_activated_for_user' => 'Dvoufaktorové ověření není pro aktuálního uživatele povoleno',\n\t\t'type_2fa' => 'Stav 2FA',\n\t],\n\t'admin' => [\n\t\t'overview' => 'Přehled',\n\t\t'ressourcedetails' => 'Využijte zdroje',\n\t\t'systemdetails' => 'Podrobnosti o systému',\n\t\t'froxlordetails' => 'Podrobnosti o froxloru',\n\t\t'installedversion' => 'Nainstalovaná verze',\n\t\t'latestversion' => 'Nejnovější verze',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'Hledat prostřednictvím webové služby',\n\t\t\t'error' => 'Při čtení došlo k chybě',\n\t\t],\n\t\t'resources' => 'Zdroje',\n\t\t'customer' => 'Zákazník',\n\t\t'customers' => 'Zákazníci',\n\t\t'customers_list_desc' => 'Spravovat své zákazníky',\n\t\t'customer_add' => 'Vytvořit zákazníka',\n\t\t'customer_edit' => 'Upravit zákazníka',\n\t\t'username_default_msg' => 'Ponechte prázdné pro automaticky generovanou hodnotu',\n\t\t'password_default_msg' => 'Automaticky vygenerováno, pokud je prázdné',\n\t\t'domains' => 'Domény',\n\t\t'domain_add' => 'Vytvořit doménu',\n\t\t'domain_edit' => 'Upravit doménu',\n\t\t'subdomainforemail' => 'Subdomény jako e-mailové domény',\n\t\t'admin' => 'Admin',\n\t\t'admins' => 'Administrátoři',\n\t\t'admin_add' => 'Vytvořit administrátora',\n\t\t'admin_edit' => 'Upravit administrátora',\n\t\t'customers_see_all' => 'Můžete přistupovat k dalším správcům/prodejcům?',\n\t\t'change_serversettings' => 'Může změnit nastavení serveru?',\n\t\t'server' => 'Systém',\n\t\t'serversettings' => 'Nastavení',\n\t\t'serversettings_desc' => 'Spravujte svůj systém froxlor',\n\t\t'rebuildconf' => 'Znovu vytvořit konfigurační soubory',\n\t\t'stdsubdomain' => 'Standardní subdoména',\n\t\t'stdsubdomain_add' => 'Vytvořit standardní subdoménu',\n\t\t'phpenabled' => 'PHP povoleno',\n\t\t'deactivated' => 'Deaktivováno',\n\t\t'deactivated_user' => 'Deaktivovat uživatele',\n\t\t'sendpassword' => 'Poslat heslo',\n\t\t'ownvhostsettings' => 'Vlastní nastavení vHost-serveru',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Konfigurace',\n\t\t\t'overview' => 'Přehled',\n\t\t\t'wizard' => 'Průvodce',\n\t\t\t'distribution' => 'Distribuce',\n\t\t\t'service' => 'Služba',\n\t\t\t'daemon' => 'Daemon',\n\t\t\t'http' => 'Webový server (HTTP)',\n\t\t\t'dns' => 'Nameserver (DNS)',\n\t\t\t'mail' => 'Poštovní server (IMAP/POP3)',\n\t\t\t'smtp' => 'Mailový server (SMTP)',\n\t\t\t'ftp' => 'FTP-server',\n\t\t\t'etc' => 'Ostatní (Systém)',\n\t\t\t'choosedistribution' => '-- Vyberte distribuci --',\n\t\t\t'chooseservice' => '-- Vyberte službu --',\n\t\t\t'choosedaemon' => '-- Zvolte daemon --',\n\t\t\t'statistics' => 'Statistiky',\n\t\t\t'compactoverview' => 'Kompaktní přehled',\n\t\t\t'legend' => '<h3>Chystáte se nakonfigurovat službu/daemon</h3>',\n\t\t\t'commands' => '<span class=\"text-danger\">Příkazy:</span> Tyto příkazy mají být spuštěny po řádku jako uživatel rootu v shellu. Je bezpečné zkopírovat celý blok a vložit ho do konzole.',\n\t\t\t'files' => '<span class=\"text-danger\">Konfigurační soubory:</span> Příkazy před textovými poli by měly otevřít editor s cílovým souborem. Stačí jen zkopírovat a vložit obsah do editoru a uložit soubor.<br><span class=\"text-danger\">Upozornění:</span> MySQL heslo nebylo z bezpečnostních důvodů nahrazeno. Prosím nahraďte \"FROXLOR_MYSQL_PASSWORD\" samostatně nebo použijte formulář javascript níže k nahrazení na webu. Pokud jste zapomněli heslo k MySQL, najdete ho v \"lib/userdata.inc.php\"',\n\t\t\t'importexport' => 'Importovat/Exportovat',\n\t\t\t'finishnote' => 'Soubor parametru byl úspěšně vygenerován. Nyní spusťte následující příkaz jako root:',\n\t\t\t'description' => 'Konfigurace systémových služeb',\n\t\t\t'minihowto' => 'Na této stránce můžete zobrazit různé konfigurační šablony pro každou službu, (opětovně)konfigurovat specifické služby v případě potřeby nebo exportovat aktuální výběr do JSON souboru pro použití ve skriptech CLI nebo na jiném serveru.<br><br><b>Poznámka</b>, že zvýrazněné služby neodrážejí aktuální nastavení, ale ukazují požadavky/doporučení z vašich aktuálních hodnot nastavení.',\n\t\t\t'skipconfig' => 'Ne(re)konfigurovat',\n\t\t\t'recommendednote' => 'Doporučené/požadované služby založené na aktuálním nastavení systému',\n\t\t\t'selectrecommended' => 'Vybrat doporučené',\n\t\t\t'downloadselected' => 'Exportovat vybrané',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Šablony e-mailů',\n\t\t\t'template_add' => 'Přidat šablonu',\n\t\t\t'template_fileadd' => 'Přidat šablonu souboru',\n\t\t\t'template_edit' => 'Upravit šablonu',\n\t\t\t'action' => 'Akce',\n\t\t\t'email' => 'E-mailové a souborové šablony',\n\t\t\t'subject' => 'Předmět',\n\t\t\t'mailbody' => 'Text e-mailu',\n\t\t\t'createcustomer' => 'Uvítací e-mail pro nové zákazníky',\n\t\t\t'pop_success' => 'Uvítací e-mail pro nové e-mailové účty',\n\t\t\t'template_replace_vars' => 'Proměnné, které mají být nahrazeny v šabloně:',\n\t\t\t'SALUTATION' => 'Nahrazeno správným oslovením (jméno nebo název společnosti)',\n\t\t\t'FIRSTNAME' => 'Nahrazeno křestním jménem zákazníka.',\n\t\t\t'NAME' => 'Nahrazeno jménem zákazníka.',\n\t\t\t'COMPANY' => 'Nahrazuje název firmy zákazníka',\n\t\t\t'USERNAME' => 'Nahradí uživatelským jménem zákazníka.',\n\t\t\t'PASSWORD' => 'Nahradí heslem zákazníka.',\n\t\t\t'EMAIL' => 'Nahrazeno adresou účtu POP3/IMAP.',\n\t\t\t'CUSTOMER_NO' => 'Nahradí číslo zákazníka',\n\t\t\t'TRAFFIC' => 'Nahrazeno provozem, který byl přiřazen zákazníkovi.',\n\t\t\t'TRAFFICUSED' => 'Nahrazeno provozem, který byl zákazníkem vyčerpán.',\n\t\t\t'pop_success_alternative' => 'Uvítací e-mail pro nové e-mailové účty odeslaný na alternativní adresu',\n\t\t\t'EMAIL_PASSWORD' => 'Nahradí heslem účtu POP3/IMAP.',\n\t\t\t'index_html' => 'indexový soubor pro nově vytvořené adresáře zákazníků',\n\t\t\t'unconfigured_html' => 'soubor indexu pro nenastavené/neznámé domény',\n\t\t\t'unconfigured_content_fallback' => 'Tato doména vyžaduje konfiguraci prostřednictvím panelu pro správu serveru froxlor, protože v současné době není přiřazena žádnému zákazníkovi.',\n\t\t\t'file_extension' => [\n\t\t\t\t'description' => 'Přípona souboru pro index musí být v rozmezí 1 až 6 znaků. Rozšíření může obsahovat pouze znaky jako a-z, A-Z a 0-9<br><br>Výchozí: html',\n\t\t\t\t'title' => 'Přípona souboru pro šablonu souboru',\n\t\t\t],\n\t\t\t'SERVERNAME' => 'Nahrazeno jménem serveru.',\n\t\t\t'CUSTOMER' => 'Nahrazeno přihlašovacím jménem zákazníka. Pouze pro \"indexový soubor pro nově vytvořené adresáře zákazníků\"',\n\t\t\t'ADMIN' => 'Nahrazeno přihlašovacím jménem správce. Pouze pro \"indexový soubor pro nově vytvořené adresáře zákazníků\"',\n\t\t\t'CUSTOMER_EMAIL' => 'Nahrazeno e-mailovou adresou zákazníka. Pouze pro \"indexový soubor pro nově vytvořené adresáře zákazníků\"',\n\t\t\t'ADMIN_EMAIL' => 'Nahrazeno e-mailovou adresou správce. Pouze pro \"indexový soubor pro nově vytvořené adresáře zákazníků\"',\n\t\t\t'filetemplates' => 'Šablony souborů',\n\t\t\t'filecontent' => 'Obsah souboru',\n\t\t\t'new_database_by_customer' => 'Upozornění zákazníka na vytvoření databáze',\n\t\t\t'new_ftpaccount_by_customer' => 'Upozornění zákazníka na vytvoření ftp uživatele',\n\t\t\t'newdatabase' => 'Notifikační-maily o nových databázích',\n\t\t\t'newftpuser' => 'Notifikační-maily o nových ftp uživatelích',\n\t\t\t'CUST_NAME' => 'Jméno zákazníka',\n\t\t\t'DB_NAME' => 'Jméno databáze',\n\t\t\t'DB_PASS' => 'Heslo databáze',\n\t\t\t'DB_DESC' => 'Popis databáze',\n\t\t\t'DB_SRV' => 'Databázový server',\n\t\t\t'PMA_URI' => 'URL na phpMyAdmin (pokud byl poskytnut)',\n\t\t\t'USR_NAME' => 'FTP uživatelské jméno',\n\t\t\t'USR_PASS' => 'FTP heslo',\n\t\t\t'USR_PATH' => 'Domovský adresář FTP (relativní k adresáři customer-docroot)',\n\t\t\t'forgotpwd' => 'Notifikační-maily o resetu hesla',\n\t\t\t'password_reset' => 'Upozornění pro zákazníka na resetování hesla',\n\t\t\t'trafficmaxpercent' => 'Upozornění pro zákazníky při vyčerpání daného maximálního procenta provozu',\n\t\t\t'MAX_PERCENT' => 'Nahrazeno limitem využití disku/limitu provozu pro odesílání zpráv v procentech.',\n\t\t\t'USAGE_PERCENT' => 'Nahrazeno využitím disku / provozem, který byl zákazníkem vyčerpán v procentech.',\n\t\t\t'diskmaxpercent' => 'Oznamovací e-mail pro zákazníky, pokud je vyčerpáno maximum procent z disku',\n\t\t\t'DISKAVAILABLE' => 'Nahrazeno využitím disku, který byl přiřazen zákazníkovi.',\n\t\t\t'DISKUSED' => 'Nahrazeno využitím disku, který byl zákazníkem vyčerpán.',\n\t\t\t'LINK' => 'Nahrazeno odkazem na obnovení hesla zákazníka.',\n\t\t\t'SERVER_HOSTNAME' => 'Nahradí název systémového hostitele (URL do froxlor)',\n\t\t\t'SERVER_IP' => 'Nahradí výchozí Ip adresu serveru',\n\t\t\t'SERVER_PORT' => 'Nahradí výchozí port serveru',\n\t\t\t'DOMAINNAME' => 'Nahrazuje standardní subdoménu zákazníka (může být prázdná, pokud není vygenerována žádná)',\n\t\t],\n\t\t'webserver' => 'Webový server',\n\t\t'createzonefile' => 'Vytvořit zónu dns pro doménu',\n\t\t'custombindzone' => 'Vlastní / nespravovaný soubor zóny',\n\t\t'bindzonewarning' => 'pro výchozí nastavení<br /><strong class=\"text-danger\">POZOR:</strong> Pokud používáte zonefile, budete muset ručně spravovat všechny požadované záznamy i pro všechny podzóny.',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IP aresy a porty',\n\t\t\t'add' => 'Přidat IP/port',\n\t\t\t'edit' => 'Upravit IP/port',\n\t\t\t'ipandport' => 'IP/Port',\n\t\t\t'ip' => 'IP',\n\t\t\t'ipnote' => '<div id=\"ipnote\" class=\"invalid-feedback\">Poznámka: Ačkoli jsou soukromé IP adresy povoleny, některé funkce jako DNS se nemusí chovat správně.<br>Používejte pouze soukromé IP adresy, pokud jste si jisti.</div>',\n\t\t\t'port' => 'Port',\n\t\t\t'create_listen_statement' => 'Vytvořit Listen statement',\n\t\t\t'create_namevirtualhost_statement' => 'Vytvořit výpis VirtualHost',\n\t\t\t'create_vhostcontainer' => 'Vytvořit vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Vytvořit výpis ServerName v kontejneru vHost-Container',\n\t\t\t'enable_ssl' => 'Je tohle SSL port?',\n\t\t\t'ssl_cert_file' => 'Cesta k certifikátu SSL',\n\t\t\t'webserverdefaultconfig' => 'Výchozí nastavení webserveru',\n\t\t\t'webserverdomainconfig' => 'Konfigurace domény Webserveru',\n\t\t\t'webserverssldomainconfig' => 'Konfigurace Webserveru SSL',\n\t\t\t'ssl_key_file' => 'Cesta k souboru klíče SSL',\n\t\t\t'ssl_ca_file' => 'Cesta k SSL CA certifikátu',\n\t\t\t'default_vhostconf_domain' => 'Výchozí nastavení vHost pro každý kontejner domény',\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'Cesta k souboru SSL CertificateChainFile',\n\t\t\t\t'description' => 'Většinou CA_Bundle, nebo podobně, to pravděpodobně chcete nastavit, pokud jste si koupili SSL certifikát.',\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Vlastní docroot (prázdný = ukazuje na froxlor)',\n\t\t\t\t'description' => 'Zde můžete definovat vlastní kořenový adresář dokumentu (cíl požadavku) pro tuto kombinaci IP/port.<br /><strong>POZOR:</strong> Dávejte pozor, co zde zadáváte!',\n\t\t\t],\n\t\t\t'ssl_paste_description' => 'Vložte svůj úplný obsah certifikátu do textového pole',\n\t\t\t'ssl_cert_file_content' => 'Obsah certifikátu ssl',\n\t\t\t'ssl_key_file_content' => 'Obsah souboru ssl (soukromého) klíče',\n\t\t\t'ssl_ca_file_content' => 'Obsah souboru ssl CA (nepovinné)',\n\t\t\t'ssl_ca_file_content_desc' => '<br /><br />Ověřování klienta, nastavte to pouze pokud víte, co je.',\n\t\t\t'ssl_cert_chainfile_content' => 'Obsah souboru certifikačního řetězce (nepovinné)',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br /><br />Nejčastěji CA_Bundle nebo podobný, pravděpodobně jej budete chtít nastavit, pokud jste si zakoupili SSL certifikát.',\n\t\t\t'ssl_default_vhostconf_domain' => 'Výchozí nastavení SSL vHost pro každý kontejner domény',\n\t\t],\n\t\t'memorylimitdisabled' => 'Zakázáno',\n\t\t'valuemandatory' => 'Tato hodnota je povinná',\n\t\t'valuemandatorycompany' => 'Musí být vyplněno buď \"jméno\" a \"křestní jméno\" nebo \"název společnosti\"',\n\t\t'serversoftware' => 'Software serveru',\n\t\t'phpversion' => 'Verze PHP',\n\t\t'mysqlserverversion' => 'Verze MySQL serveru',\n\t\t'webserverinterface' => 'Rozhraní Webserveru',\n\t\t'accountsettings' => 'Nastavení účtu',\n\t\t'panelsettings' => 'Nastavení panelu',\n\t\t'systemsettings' => 'Systemová nastavení',\n\t\t'webserversettings' => 'Nastavení webserveru',\n\t\t'mailserversettings' => 'Nastavení webserveru',\n\t\t'nameserversettings' => 'Nastavení nameserveru',\n\t\t'updatecounters' => 'Přepočítat využití zdrojů',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Nikdy',\n\t\t\t'choosableno' => 'Volitelný, výchozí ne',\n\t\t\t'choosableyes' => 'Volitelné, výchozí ano',\n\t\t\t'always' => 'Vždy',\n\t\t],\n\t\t'wipecleartextmailpwd' => 'Vymazat prostý text hesla',\n\t\t'webalizersettings' => 'Nastavení Webalizéru',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normální',\n\t\t\t'quiet' => 'Tiché',\n\t\t\t'veryquiet' => 'Žádný výstup',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'V současné době není možné přidat doménu. Nejprve musíte přidat alespoň jednoho zákazníka.',\n\t\t'loggersettings' => 'Nastavení logování',\n\t\t'logger' => [\n\t\t\t'normal' => 'normální',\n\t\t\t'paranoid' => 'paranoid',\n\t\t],\n\t\t'emaildomain' => 'E-mailová doména',\n\t\t'email_only' => 'Pouze e-mail?',\n\t\t'wwwserveralias' => 'Přidat \"www.\" ServerAlias',\n\t\t'subject' => 'Předmět',\n\t\t'recipient' => 'Příjemce',\n\t\t'message' => 'Napsat zprávu',\n\t\t'text' => 'Zpráva',\n\t\t'sslsettings' => 'Nastavení SSL',\n\t\t'specialsettings_replacements' => 'Můžete použít následující proměnné:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code> <code>{IP}</code>, <code>{PORT}</code> <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (připadá-li v úvahu)<br/>',\n\t\t'antispam_settings' => 'Nastavení Antispamu',\n\t\t'caneditphpsettings' => 'Může změnit php nastavení domény?',\n\t\t'allips' => 'Všechny IP adresy',\n\t\t'awstatssettings' => 'Nastavení AWstatu',\n\t\t'domain_dns_settings' => 'Nastavení doménové dns',\n\t\t'activated' => 'Aktivováno',\n\t\t'statisticsettings' => 'Nastavení statistik',\n\t\t'or' => 'nebo',\n\t\t'sysload' => 'Systémové zatížení',\n\t\t'noloadavailable' => 'neni k dispozici',\n\t\t'nouptimeavailable' => 'neni k dispozici',\n\t\t'nosubject' => '(Bez předmětu)',\n\t\t'security_settings' => 'Možnosti zabezpečení',\n\t\t'know_what_youre_doing' => 'Změňte pouze, pokud víte, co děláte!',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Zobrazit froxlor verzi při přihlášení',\n\t\t\t'description' => 'Zobrazit froxlor verzi v zápatí na přihlašovací stránce',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Zobrazit froxlor verzi v zápatí',\n\t\t\t'description' => 'Zobrazit froxlor verzi v zápatí na ostatních stránkách',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Grafické záhlaví pro froxlor',\n\t\t\t'description' => 'Jaká grafika by měla být zobrazena v záhlaví',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'PHP konfigurace',\n\t\t\t'description' => 'Stručný popis',\n\t\t\t'actions' => 'Akce',\n\t\t\t'activedomains' => 'Používá se pro doménu/y',\n\t\t\t'notused' => 'Konfigurace se nepoužívá',\n\t\t\t'editsettings' => 'Změnit nastavení PHP',\n\t\t\t'addsettings' => 'Vytvořit nové nastavení PHP',\n\t\t\t'viewsettings' => 'Zobrazit nastavení PHP',\n\t\t\t'phpinisettings' => 'php.ini nastavení',\n\t\t\t'addnew' => 'Vytvořit novou konfiguraci PHP',\n\t\t\t'binary' => 'PHP Binary',\n\t\t\t'fpmdesc' => 'PHP-FPM konfigurace',\n\t\t\t'file_extensions' => 'Přípony souborů',\n\t\t\t'file_extensions_note' => '(bez tečky, oddělených mezerami)',\n\t\t\t'enable_slowlog' => 'Povolit slowlog (pro každou doménu)',\n\t\t\t'request_terminate_timeout' => 'Časový limit ukončení požadavku',\n\t\t\t'request_slowlog_timeout' => 'Časový limit ukončení požadavku slowlogu',\n\t\t\t'activephpconfigs' => 'Používá se pro php-config(y)',\n\t\t\t'pass_authorizationheader' => 'Předávání hlaviček HTTP AUTH BASIC/DIGEST z Apache do PHP',\n\t\t],\n\t\t'misc' => 'Ostatní',\n\t\t'fpmsettings' => [\n\t\t\t'addnew' => 'Vytvořit novou verzi PHP',\n\t\t\t'edit' => 'Změnit verzi PHP'\n\t\t],\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'Proměnné, které budou nahrazeny v konfiguraci',\n\t\t\t'pear_dir' => 'Bude nahrazeno globálním nastavením pro pearl adresář.',\n\t\t\t'open_basedir_c' => 'Vloží ; (středník) do komentáře/vypne open_basedir po nastavení',\n\t\t\t'open_basedir' => 'Bude nahrazeno nastavením domény open_basedir.',\n\t\t\t'tmp_dir' => 'Bude nahrazeno dočasným adresářem domény.',\n\t\t\t'open_basedir_global' => 'Bude nahrazena globální hodnotou cesty, která bude připojena k open_basedir (viz nastavení webserveru).',\n\t\t\t'customer_email' => 'Bude nahrazeno e-mailovou adresou zákazníka, který tuto doménu vlastní.',\n\t\t\t'admin_email' => 'Bude nahrazeno e-mailovou adresou správce, který tuto doménu vlastní.',\n\t\t\t'domain' => 'Bude nahrazeno doménou.',\n\t\t\t'customer' => 'Bude nahrazeno přihlašovacím jménem zákazníka, který vlastní tuto doménu.',\n\t\t\t'admin' => 'Bude nahrazeno přihlašovacím jménem správce, který tuto doménu vlastní.',\n\t\t\t'docroot' => 'Bude nahrazeno kořenovým textem domény.',\n\t\t\t'homedir' => 'Bude nahrazeno domovským adresářem zákazníka.',\n\t\t],\n\t\t'expert_settings' => 'Pokročilá nastavení!',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'PHP procesy pro tuto doménu (prázdné pro výchozí hodnotu)',\n\t\t],\n\t\t'phpserversettings' => 'Nastavení PHP',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Maximální počet php požadavků pro tuto doménu (ponechte prázdné pro výchozí hodnotu)',\n\t\t],\n\t\t'spfsettings' => 'Nastavení SPF domén',\n\t\t'specialsettingsforsubdomains' => 'Použít speciální nastavení pro všechny subdomény (*.example.com)',\n\t\t'accountdata' => 'Údaje o účtu',\n\t\t'contactdata' => 'Kontaktní údaje',\n\t\t'servicedata' => 'Údaje o službě',\n\t\t'newerversionavailable' => 'K dispozici je novější verze froxloru.',\n\t\t'newerversiondetails' => 'Aktualizovat na verzi <b>%s</b> nyní?<br/>(Vaše aktuální verze je: %s)',\n\t\t'extractdownloadedzip' => 'Extrahovat stažený archiv \"%s\"?',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Nastavení Cronjobu',\n\t\t\t'add' => 'Přidat cronjob',\n\t\t],\n\t\t'cronjob_edit' => 'Upravit cronjob',\n\t\t'warning' => 'VAROVÁNÍ - Vezměte prosím na vědomí!',\n\t\t'lastlogin_succ' => 'Poslední přihlášení',\n\t\t'ftpserver' => 'FTP Server',\n\t\t'ftpserversettings' => 'Nastavení FTP serveru',\n\t\t'webserver_user' => 'Uživatelské jméno webserveru',\n\t\t'webserver_group' => 'Název skupiny webserveru',\n\t\t'perlenabled' => 'Perl povolen',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Místní uživatel pro FCGID (froxlor vHost)',\n\t\t'mod_fcgid_group' => 'Místní skupina k použití pro FCGID (froxlor vHost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[neposkytnuto]',\n\t\t'store_defaultindex' => 'Uložit výchozí indexový soubor zákazníkům docroot',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => 'Provoz',\n\t\t'traffic_sub' => 'Podrobnosti o využití provozu',\n\t\t'domaintraffic' => 'Domény',\n\t\t'customertraffic' => 'Zákazníci',\n\t\t'assignedmax' => 'Přiřazeno / Max',\n\t\t'usedmax' => 'Použito / Max',\n\t\t'used' => 'Využito',\n\t\t'speciallogwarning' => '<div id=„speciallogfilenote“ class=„invalid-feedback“>Pozor: Změnou tohoto nastavení ztratíte všechny staré statistiky pro tuto doménu.</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => 'Oddělit soubor logu',\n\t\t\t'description' => 'Povolením této možnosti získáte pro tuto doménu samostatný soubor přístupového protokolu',\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => 'Povolit úpravy domény',\n\t\t\t'desc' => 'Pokud je nastaveno ano, může zákazník změnit několik nastavení domény.<br />Pokud je nastaveno ne, nemůže zákazník nic měnit.',\n\t\t],\n\t\t'writeaccesslog' => [\n\t\t\t'title' => 'Zapisovat přístupový protokol',\n\t\t\t'description' => 'Povolením této možnosti získáte soubor protokolu přístupu pro tuto doménu',\n\t\t],\n\t\t'writeerrorlog' => [\n\t\t\t'title' => 'Zapsat protokol chyb',\n\t\t\t'description' => 'Povolením této funkce získáte soubor s chybovým protokolem pro tuto doménu',\n\t\t],\n\t\t'phpfpm.ininote' => 'Ne všechny hodnoty, které chcete definovat, mohou být použity v konfiguraci php-fpm poolu',\n\t\t'phpinfo' => 'PHPinfo()',\n\t\t'selectserveralias' => 'Hodnota ServerAlias pro doménu',\n\t\t'selectserveralias_desc' => 'Vyberte, zda by froxlor měl vytvořit wildcard-entry (*.domain.tld), WWW-alias (www.domain.tld) nebo vůbec žádný alias',\n\t\t'show_news_feed' => [\n\t\t\t'title' => 'Zobrazit novinky na admin nástěnce',\n\t\t\t'description' => 'Povolením zobrazíte oficiální novinky froxlor (https://inside.froxlor.org/news/) na vaší nástěnce a nikdy nezmeškejte důležité informace nebo oznámení o vydání.',\n\t\t],\n\t\t'cronsettings' => 'Nastavení Cronjobu',\n\t\t'integritycheck' => 'Ověření databáze',\n\t\t'integrityname' => 'Název',\n\t\t'integrityresult' => 'Výsledek',\n\t\t'integrityfix' => 'Automaticky opravit problémy',\n\t\t'customer_show_news_feed' => 'Zobrazit novinky na řídicím panelu zákazníka',\n\t\t'customer_news_feed_url' => [\n\t\t\t'title' => 'Použít vlastní RSS-feed',\n\t\t\t'description' => 'Zadejte vlastní RSS kanál, který bude zobrazen vašim zákazníkům na jejich nástěnce.<br /><small>Ponechte prázdné pro použití oficiálního froxlor newsfeed (https://inside.froxlor.org/news/).</small>',\n\t\t],\n\t\t'movetoadmin' => 'Přesunout zákazníka',\n\t\t'movecustomertoadmin' => [\n\t\t\t'title' => 'Přesunout zákazníka na vybraného administrátora/prodejce',\n\t\t\t'description' => 'Ponechte prázdné pro žádnou změnu.<br />Pokud se požadovaný administrátor nezobrazí v seznamu, jeho zákaznický limit byl dosažen.',\n\t\t],\n\t\t'note' => 'Poznámka',\n\t\t'mod_fcgid_umask' => [\n\t\t\t'title' => 'Umask (výchozí: 022)',\n\t\t],\n\t\t'apcuinfo' => 'APCu info',\n\t\t'opcacheinfo' => 'OPcache Info',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Použít Let\\'s Encrypt',\n\t\t\t'description' => 'Získejte zdarma certifikát od <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. Certifikát bude vytvořen a obnoven automaticky.<br><strong class=\"text-danger\">POZNÁMKA:</strong> Pokud jsou povoleny wildcards, tato možnost bude automaticky zakázána.',\n\t\t],\n\t\t'autoupdate' => 'Automatická aktualizace',\n\t\t'server_php' => 'PHP',\n\t\t'dnsenabled' => 'Povolit DNS editor',\n\t\t'froxlorvhost' => 'froxlor VirtualHost nastavení',\n\t\t'hostname' => 'Název serveru',\n\t\t'memory' => 'Využití paměti',\n\t\t'webserversettings_ssl' => 'Nastavení SSL Webserveru',\n\t\t'domain_hsts_maxage' => [\n\t\t\t'title' => 'HTTP Strict Transport Security (HSTS)',\n\t\t\t'description' => 'Zadejte hodnotu maximálního věku pro záhlaví Strict-Transport-Security<br>Hodnota <i>0</i> zakáže HSTS pro doménu. Většina uživatelů nastavila hodnotu <i>31536000</i> (jeden rok).',\n\t\t],\n\t\t'domain_hsts_incsub' => [\n\t\t\t'title' => 'Zahrnout HSTS pro jakoukoliv subdoménu',\n\t\t\t'description' => 'Volitelná direktiva „includeSubDomains“, pokud je přítomna, signalizuje UA, že zásady HSTS se vztahují na tohoto hostitele HSTS i na všechny subdomény názvu domény hostitele.',\n\t\t],\n\t\t'domain_hsts_preload' => [\n\t\t\t'title' => 'Zahrnout doménu do seznamu přednačtených HSTS',\n\t\t\t'description' => 'Pokud chcete, aby byla tato doména zahrnuta do <a href=\"https://hstspreload.org/\" target=\"_blank\">HSTS preload listu</a> spravovaného Chrome (a používaného Firefoxem a Safari), poté použijte tuto aktivaci.<br>Odeslání preload direktivy z vašich stránek může mít TRVALÉ NÁSLEDKY a zabránit uživatelům v přístupu na vaše stránky a všechny jejich poddomény.<br>Před odesláním hlavičky s \"preload\" si prosím přečtěte podrobnosti na <a href=\"https://hstspreload.org/#removal\" target=\"_blank\">https://hstspreload.org/#removal</a>.',\n\t\t],\n\t\t'domain_ocsp_stapling' => [\n\t\t\t'title' => 'Stapling OCSP',\n\t\t\t'description' => 'Podrobné vysvětlení stapling OCSP viz <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/OCSP_stapling\">Wikipedia</a>',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">VAROVÁNÍ:</strong> Nginx verze 1.3.7 nebo vyšší je vyžadována pro OCSP stapling. Pokud je vaše verze starší, webový server NEBUDE správně spuštěn, dokud je povoleno OCSP stapling!',\n\t\t],\n\t\t'domain_http2' => [\n\t\t\t'title' => 'HTTP2 podpora',\n\t\t\t'description' => 'Navštivte <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/2\">Wikipedia</a> pro podrobné vysvětlení HTTP3 protokolu',\n\t\t],\n\t\t'domain_http3' => [\n\t\t\t'title' => 'HTTP3 podpora',\n\t\t\t'description' => 'Navštivte <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/3\">Wikipedia</a> pro podrobné vysvětlení HTTP3 protokolu',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">VAROVÁNÍ:</strong> Je vyžadována Nginx verze 1.25.0 nebo vyšší a pro HTTP/3 je vyžadován ssl-protokol TLSv1.3. Pokud je vaše verze starší, tak se webový server při zapnutém HTTP/3 správně nespustí!',\n\t\t],\n\t\t'testmail' => 'Test SMTP',\n\t\t'phpsettingsforsubdomains' => 'Použít php konfiguraci na všechny subdomény:',\n\t\t'plans' => [\n\t\t\t'name' => 'Název plánu',\n\t\t\t'description' => 'Popis',\n\t\t\t'last_update' => 'Naposledy aktualizováno',\n\t\t\t'plans' => 'Plány hostingu',\n\t\t\t'plan_details' => 'Detaily plánu',\n\t\t\t'add' => 'Přidat nový plán',\n\t\t\t'edit' => 'Upravit plán',\n\t\t\t'use_plan' => 'Použít plán',\n\t\t],\n\t\t'notryfiles' => [\n\t\t\t'title' => 'Žádné automatické generování try_souborů',\n\t\t\t'description' => 'Zde zvolte ano, pokud chcete ve sepciálním nastavcení zadat vlastní direktivu try_files (potřebnou například pro některé pluginy WordPressu).',\n\t\t],\n\t\t'logviewenabled' => 'Povolit přístup k logům přístupu/chyb',\n\t\t'novhostcontainer' => '<br><br><small class=\"text-danger\">Žádná z IP a portů nemá povolenou možnost \"Vytvořit vHost-Container\", mnoho nastavení zde nebude k dispozici</small>',\n\t\t'ownsslvhostsettings' => 'Vlastní SSL vHost-nastavení',\n\t\t'domain_override_tls' => 'Přepsat nastavení systémového TLS',\n\t\t'domain_override_tls_addinfo' => '<br /><span class=\"text-danger\">Používá se pouze v případě, že \"Přepsat nastavení TLS systému\" je nastaveno na \"Ano\"</span>',\n\t\t'domain_sslenabled' => 'Povolit použití SSL',\n\t\t'domain_honorcipherorder' => 'Dodržovat pořadí šifry (serveru), výchozí <strong>ne</strong>',\n\t\t'domain_sessiontickets' => 'Povolit TLS sessiontickety (RFC 5077), výchozí <strong>ano</strong>',\n\t\t'domain_sessionticketsenabled' => [\n\t\t\t'title' => 'Povolit použití tiketů TLS globálně',\n\t\t\t'description' => 'Výchozí <strong>ano</strong><br>Vyžaduje apache-2.4.11+ nebo nginx-1.5.9+',\n\t\t],\n\t\t'domaindefaultalias' => 'Výchozí hodnota serverAlias pro nové domény',\n\t\t'smtpsettings' => 'Nastavení SMTP',\n\t\t'smtptestaddr' => 'Odeslat testovací email na',\n\t\t'smtptestnote' => 'Níže uvedené hodnoty odrážejí vaše aktuální nastavení a lze je upravit pouze zde (viz odkaz v pravém horním rohu)',\n\t\t'smtptestsend' => 'Odeslat testovací e-mail',\n\t\t'mysqlserver' => [\n\t\t\t'mysqlserver' => 'MySQL Server',\n\t\t\t'dbserver' => 'Server #',\n\t\t\t'caption' => 'Popis',\n\t\t\t'host' => 'Název serveru / IP',\n\t\t\t'port' => 'Port',\n\t\t\t'user' => 'Oprávněný uživatel',\n\t\t\t'add' => 'Přidat nový MySQL server',\n\t\t\t'edit' => 'Upravit MySQL server',\n\t\t\t'password' => 'Heslo oprávněného uživatele',\n\t\t\t'password_emptynochange' => 'Nové heslo, ponechte prázdné pro ponechání aktuálního',\n\t\t\t'allowall' => [\n\t\t\t\t'title' => 'Povolit použití tohoto serveru všem aktuálně existujícím zákazníkům',\n\t\t\t\t'description' => 'Nastavte tuto hodnotu na „true“, pokud chcete povolit používání tohoto databázového serveru všem stávajícím zákazníkům, aby na něj mohli přidávat databáze. Toto nastavení není trvalé, ale lze jej spustit vícekrát.',\n\t\t\t],\n\t\t\t'testconn' => 'Otestovat připojení při ukládání',\n\t\t\t'ssl' => 'Použít SSL pro připojení k databázovému serveru',\n\t\t\t'ssl_cert_file' => 'Cesta k souboru k SSL Certifikační autoritě',\n\t\t\t'verify_ca' => 'Povolit ověření serverového SSL certifikátu',\n\t\t],\n\t\t'settings_importfile' => 'Zvolit importovaný soubor',\n\t\t'documentation' => 'Dokumentace',\n\t\t'adminguide' => 'Admin průvodce',\n\t\t'userguide' => 'Uživatelský manuál',\n\t\t'apiguide' => 'Průvodce API',\n\t\t'domain_duplicate' => 'Duplikovat doménu',\n\t\t'domain_duplicate_named' => 'Duplikovat %s',\n\t\t'backups' => [\n\t\t\t'backups' => 'Zálohy',\n\t\t],\n\t\t'emaildomainwarning' => '<div id=\"emaildomainnote\" class=\"invalid-feedback\">VAROVÁNÍ: Změnou tohoto nastavení trvale smažete všechny existující e-mailové adresy a účty.</div>',\n\t],\n\t'apcuinfo' => [\n\t\t'clearcache' => 'Vymazat APCu mezipaměť',\n\t\t'generaltitle' => 'Obecné informace o cache',\n\t\t'version' => 'APCu verze',\n\t\t'phpversion' => 'Verze PHP',\n\t\t'host' => 'APCu hostitel',\n\t\t'sharedmem' => 'Sdílená paměť',\n\t\t'sharedmemval' => '%d Segment(y) s %s (%s paměti)',\n\t\t'start' => 'Čas spuštění',\n\t\t'uptime' => 'Doba provozu',\n\t\t'upload' => 'Podpora nahrávání souborů',\n\t\t'cachetitle' => 'Informace o cache',\n\t\t'cvar' => 'Proměnné v mezipaměti',\n\t\t'hit' => 'Zásahy',\n\t\t'miss' => 'Nevyužito',\n\t\t'reqrate' => 'Míra požadavku (zásahy, nevyužité)',\n\t\t'creqsec' => 'požadavky mezipaměti za sekundu',\n\t\t'hitrate' => 'Rychlost zásahu',\n\t\t'missrate' => 'Nevyužitá míra',\n\t\t'insrate' => 'Insert Rate',\n\t\t'cachefull' => 'Plný počet vyrovnávací paměti',\n\t\t'runtime' => 'Nastavení spuštění',\n\t\t'memnote' => 'Využití paměti',\n\t\t'total' => 'Celkem',\n\t\t'free' => 'Volné',\n\t\t'used' => 'Využito',\n\t\t'hitmiss' => 'Zásahy & Nevyužití',\n\t\t'detailmem' => 'Podrobné využití paměti a fragmentace',\n\t\t'fragment' => 'Fragmentace',\n\t\t'nofragment' => 'Bez fragmentace',\n\t\t'fragments' => 'Fragmentace',\n\t],\n\t'apikeys' => [\n\t\t'no_api_keys' => 'Nebyly nalezeny žádné API klíče',\n\t\t'key_add' => 'Přidat nový klíč',\n\t\t'apikey_removed' => 'Api klíč s Id #%s byl úspěšně odstraněn',\n\t\t'apikey_added' => 'Byl úspěšně vygenerován nový api klíč',\n\t\t'clicktoview' => 'Klikněte pro zobrazení',\n\t\t'allowed_from' => 'Povoleno od',\n\t\t'allowed_from_help' => 'Čárkami oddělený seznam Ip adres / sítí.<br>Výchozí nastavení je prázdné (povolit od všech).',\n\t\t'valid_until' => 'Platné do',\n\t\t'valid_until_help' => 'Datum do platnosti, formát RRRR-MM-DDTh:mm',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Staré heslo',\n\t\t'new_password' => 'Nové heslo',\n\t\t'new_password_confirm' => 'Potvrdit heslo',\n\t\t'new_password_ifnotempty' => 'Nové heslo (prázdné = žádná změna)',\n\t\t'also_change_ftp' => ' také změnit heslo hlavního FTP účtu',\n\t\t'also_change_stats' => ' také změnit heslo pro stránku se statistikou',\n\t\t'also_change_global_mysql' => 'také změnit heslo pro globální MySQL účet',\n\t],\n\t'cron' => [\n\t\t'cronname' => 'název cronjobu',\n\t\t'lastrun' => 'poslední spuštění',\n\t\t'interval' => 'interval',\n\t\t'isactive' => 'povoleno',\n\t\t'description' => 'popis',\n\t\t'changewarning' => 'Změna těchto hodnot může mít negativní příčinu chování froxlor a jeho automatických úloh.<br />Změňte hodnoty zde pouze pokud jste si jisti, že víte, co děláte.',\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'nebyl zadán žádný popis',\n\t\t'cron_tasks' => 'Generování konfiguračních souborů',\n\t\t'cron_legacy' => 'starší (starý) cronjob',\n\t\t'cron_traffic' => 'Výpočet datového provozu',\n\t\t'cron_usage_report' => 'Hlášení o provozu a webu',\n\t\t'cron_mailboxsize' => 'Výpočet velikosti schránky',\n\t\t'cron_letsencrypt' => 'Aktualizace certifikátu Let\\'s Encrypt',\n\t\t'cron_export' => 'Zpracování úloh exportu dat',\n\t\t'cron_backup' => 'Zpracování úloh zálohování systému a zákazníků',\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'Nastavení Cronjobu',\n\t\t'cronjobintervalv' => 'Hodnota intervalu běhu',\n\t\t'cronjobinterval' => 'Interval běhu',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Ještě nespuštěno',\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => 'minut',\n\t\t'hours' => 'hodin',\n\t\t'days' => 'dní',\n\t\t'weeks' => 'týdnů',\n\t\t'months' => 'měsíců',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Domovský adresář',\n\t\t'name' => 'Název',\n\t\t'firstname' => 'Křestní jméno',\n\t\t'lastname' => 'Příjmení',\n\t\t'company' => 'Firma',\n\t\t'nameorcompany_desc' => 'Křestní jméno/příjmení nebo firma je povinná',\n\t\t'street' => 'Ulice',\n\t\t'zipcode' => 'Psč',\n\t\t'city' => 'Město',\n\t\t'phone' => 'Mobil',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'Email',\n\t\t'customernumber' => 'ID zákazníka',\n\t\t'diskspace' => 'Webový prostor',\n\t\t'traffic' => 'Provoz',\n\t\t'mysqls' => 'Databáze MySQL',\n\t\t'emails' => 'E-mailové adresy',\n\t\t'accounts' => 'E-mailové účty',\n\t\t'forwarders' => 'E-mailoví přeposílači',\n\t\t'ftps' => 'FTP účty',\n\t\t'subdomains' => 'Subdomény',\n\t\t'domains' => 'Domény',\n\t\t'mib' => 'MiB',\n\t\t'gib' => 'GiB',\n\t\t'title' => 'Název',\n\t\t'country' => 'Země',\n\t\t'email_quota' => 'Kvóta e-mailu',\n\t\t'email_imap' => 'E-mail IMAP',\n\t\t'email_pop3' => 'E-mail POP3',\n\t\t'sendinfomail' => 'Poslat mi data e-mailem',\n\t\t'generated_pwd' => 'Návrh hesla',\n\t\t'usedmax' => 'Použito / Max',\n\t\t'services' => 'Služby',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Použít Let\\'s Encrypt',\n\t\t\t'description' => 'Získejte zdarma certifikát od <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. Certifikát bude vytvořen a obnoven automaticky.',\n\t\t],\n\t\t'selectserveralias_addinfo' => 'Tuto možnost lze nastavit při úpravě domény. Její počáteční hodnota je zděděna z rodičovské domény.',\n\t\t'total_diskspace' => 'Celkový prostor na disku',\n\t\t'mysqlserver' => 'Použitelný mysql-server',\n\t],\n\t'diskquota' => 'Kvóta',\n\t'antispam' => [\n\t\t'config_file' => [\n\t\t\t'title' => 'Soubor s nastavením Antispamu',\n\t\t\t'description' => 'Zadejte název souboru pro pravidla e-mail-antispamu',\n\t\t],\n\t\t'reload_command' => [\n\t\t\t'title' => 'Příkaz k restartu systému Milter',\n\t\t\t'description' => 'Zadejte příkaz restartu pro službu rspamd',\n\t\t],\n\t\t'activated' => [\n\t\t\t'title' => 'Aktivovat antispam?',\n\t\t\t'description' => 'Chcete použít rspamd jako antispam službu?',\n\t\t],\n\t\t'dkim_keylength' => [\n\t\t\t'title' => 'DKIM délka klíče',\n\t\t\t'description' => 'Upozornění: Změny se budou vztahovat pouze na nové klíče<br/><br/>Vyžaduje určitou položku dns pro doménu. Pokud nepoužíváte funkci nameserver, budete muset tyto položky ručně spravovat.',\n\t\t],\n\t\t'spam_tag_level' => [\n\t\t\t'title' => 'Úroveň Spam tagu',\n\t\t\t'description' => 'Počet bodů, který je nutný k označení e-mailu jako spam<br/>Výchozí: 7.0'\n\t\t],\n\t\t'rewrite_subject' => [\n\t\t\t'title' => 'Přepisovat předmět',\n\t\t\t'description' => 'Zda přidat <strong>***SPAM***</strong> do předmětu e-mailu, pokud je to vhodné',\n\t\t],\n\t\t'spam_kill_level' => [\n\t\t\t'title' => 'Úroveň likvidace spamu',\n\t\t\t'description' => 'Počet bodů, který je nutný k úplnému vyřazení e-mailu<br/>Výchozí: 14.0'\n\t\t],\n\t\t'bypass_spam' => [\n\t\t\t'title' => 'Obejít spamfiltr',\n\t\t\t'description' => 'Aktivací obejdete/zakážete filtrování spamu pro tuto adresu.<br/>Výchozí: ne'\n\t\t],\n\t\t'policy_greylist' => [\n\t\t\t'title' => 'Použít greylisting',\n\t\t\t'description' => 'Příchozí e-maily budou chráněny <a href=\"https://en.wikipedia.org/wiki/Greylisting_(email)\" target=\"_blank\">greylisting</a>.<br/>Výchozí: ano'\n\t\t],\n\t\t'required_spf_dns' => 'Požadovaný SPF DNS záznam',\n\t\t'required_dmarc_dns' => 'Požadovaný DMARC DNS záznam',\n\t\t'required_dkim_dns' => 'Požadovaný DKIM DNS záznam',\n\t\t'default_select' => [\n\t\t\t'on_changeable' => 'Aktivováno, nastavitelné',\n\t\t\t'off_changeable' => 'Deaktivováno, nastavitelné',\n\t\t\t'on_unchangeable' => 'Aktivováno, nelze nastavit',\n\t\t\t'off_unchangeable' => 'Deaktivováno, nelze nastavit',\n\t\t],\n\t\t'default_bypass_spam' => [\n\t\t\t'title' => 'Obcházet výchozí hodnotu emailového spam filtru',\n\t\t\t'description' => 'Zda mají nové e-mailové účty ve výchozím nastavení aktivovanou funkci „Obejít spamfiltr“ a zda je toto nastavení zákazníkem nastavitelné. <br/>Výchozí nastavení: Deaktivováno, nastavitelné'\n\t\t],\n\t\t'default_spam_rewrite_subject' => [\n\t\t\t'title' => 'Přepisovat výchozí hodnotu předmětu',\n\t\t\t'description' => 'Zda mají nové e-mailové účty ve výchozím nastavení aktivovanou funkci „Přepisovat předmět“ a zda je toto nastavení zákazníkem nastavitelné. <br/>Výchozí nastavení: Aktivováno, nastavitelné'\n\t\t],\n\t\t'default_policy_greylist' => [\n\t\t\t'title' => 'Použít výchozí hodnotu greylistingu',\n\t\t\t'description' => 'Zda mají nové e-mailové účty ve výchozím nastavení aktivovanou funkci „Použít greylisting“ a zda je toto nastavení zákazníkem nastavitelné. <br/>Výchozí nastavení: Aktivováno, nastavitelné'\n\t\t],\n\t],\n\t'dns' => [\n\t\t'destinationip' => 'IP domény(y)',\n\t\t'standardip' => 'Standardní IP adresa serveru',\n\t\t'a_record' => 'A-záznam (volitelně IPv6)',\n\t\t'cname_record' => 'Záznam CNAME',\n\t\t'mxrecords' => 'Definovat MX záznamy',\n\t\t'standardmx' => 'Standardní MX záznam serveru',\n\t\t'mxconfig' => 'Vlastní MX záznamy',\n\t\t'priority10' => 'Priorita 10',\n\t\t'priority20' => 'Priorita 20',\n\t\t'txtrecords' => 'Definovat TXT záznamy',\n\t\t'txtexample' => 'Příklad (SPF-entry):<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'howitworks' => 'Zde můžete spravovat DNS položky pro vaši doménu. Pamatujte, že froxlor automaticky vygeneruje NS/MX/A/AAAA záznamy pro vás. Vlastní položky jsou upřednostňovány, budou automaticky vygenerovány pouze chybějící položky.',\n\t\t'nis2note' => [\n\t\t\t'title' => 'NIS2 info',\n\t\t\t'content' => 'DNS hosting/autoritativní DNS služby mohou být považovány za digitální služby s vyššími bezpečnostními a oznamovacími povinnostmi podle <strong>EU-NIS2</strong>. Zkontrolujte, zda se na vaše nastavení vztahuje NIS2 a jaká opatření jsou vyžadována.'\n\t\t],\n\t],\n\t'dnseditor' => [\n\t\t'edit' => 'upravit DNS',\n\t\t'records' => 'záznamy',\n\t\t'notes' => [\n\t\t\t'A' => '32bitová adresa IPv4 používaná k mapování názvů hostitelů na IP adresu hostitele.',\n\t\t\t'AAAA' => '128-bitová IPv6 adresa, používaná k mapování jmen na IP adresu hostitele.',\n\t\t\t'CAA' => 'Záznam zdroje CAA umožňuje držiteli jména domény DNS zadat jednu nebo více certifikačních autorit (CA), které jsou oprávněny vydávat certifikáty pro tuto doménu.<br>Struktura: <code>značka tag[issue|issuewild|iodef|contactmail|contactphone] hodnota</code><br>Příklad: <code>0 issue \"ca.example.net\"<br>0 iodef \"mailto:security@example.com\"</code>',\n\t\t\t'CNAME' => 'Alias názvu domény, hledání DNS bude pokračovat opakováním hledání s novým názvem. Možné pouze pro subdomény!',\n\t\t\t'DNAME' => 'Vytvoří alias pro celý podstrom stromu domény',\n\t\t\t'LOC' => 'Informace o geografickém umístění pro název domény.<br>Struktura: <code>( d1 [m1 [s1]] {\"N\"|\"S\"} d2 [m2 [s2]] {\"E\"|\"W\"} alt[\"m\"] [siz[\"m\"] [hp[\"] [vp[\"m\"]]]] )</code><br> <code>d1: [0 . 90] (stupně zeměpisné šířky)\n\t\t\td2: [0 .. 180] (stupnice zeměpisné délky)\n\t\t\tm1, m2: [0 .. 59] (minuty zeměpisné šířky/délky)\n\t\t\ts1, s2: [0 .. 59. 99] (sekundy zeměpisné šířky/délky)\n\t\t\tt [-100000.00 .. 42849672.95] BY . 1 (výška v metrech)\n\t\t\tvelikost, hp, vp: [0 .. 90000000. 0] (velikost/přesnost v metrech)</code><br>Příklad: <code>52 22 23.000 N 4 53 32.000 E -2,00m 000m 10000m</code>',\n\t\t\t'MX' => 'Záznam o výměně e-mailů, namapuje doménu na mailserver pro tuto doménu.<br>Příklad: <code>10 mail.example.com</code><br>Poznámka: Pro prioritu použijte pole výše',\n\t\t\t'NS' => 'Deleguje zónu DNS, aby použil dané autoritativní jmenné servery.',\n\t\t\t'RP' => 'Záznam odpovědné osoby<br>Struktura: <code>mailbox[nahraďte @ tečkou] txt-record-name</code><br>Příklad: <code>team.froxlor.org. froxlor.org.</code>',\n\t\t\t'SRV' => 'Záznam polohy služby, použitý pro novější protokoly namísto vytváření záznamů specifických pro protokol, jako je MX.<br>Struktura: <code>prioritní váhový port cíl</code><br>Příklad: <code>0 5 5060 sipserver. xample.com.</code><br>Poznámka: Pro prioritu použijte pole výše',\n\t\t\t'SSHFP' => 'Záznam zdroje SSHFP se používá ke zveřejnění otisků prstů klíče bezpečného shell (SSH) v DNS.<br>Struktura: <code>typ algoritmu</code><br>Algoritmy: <code>0: vyhrazeno, 1: RSA, 2: DSA, 3: ECDSA, 4: Ed25519, 6: Typy Ed448</code><br> <code>0: vyhrazené, 1: SHA-1, 2: SHA-256</code><br>Příklad: <code>2 1 123456789abcdef67890123456789abcdef67890</code>',\n\t\t\t'TLSA' => 'TLSA (TLS Authentication) záznam slouží ke zveřejnění otisku prstu certifikátu TLS/SSL. Běžně se používá pro DANE.<br>TLSA záznamy mohou být důvěryhodné, pouze pokud je DNSSEC povolena na vaší doméně.<br>Struktura: <code>typ otisku prstu</code><br>využití certifikátu: <code>0: PKIX-T, 1: PKIX-EE, 2: DANE-TA, 3: DANE-EE</code><br>selektor: <code>0: Použijte celý certifikát 1: Používejte podřízený veřejný klíč</code><br>Odpovídající typ: <code>0: Bez Hash, 1: SHA-256 Hash, 2:SHA-512 Hash</code><br>Příklad: <code>3 1 123456789abcdef67890123456789abcdef123456789abcdef123456789abcde</code>',\n\t\t\t'TXT' => 'Volně definovaný, popisný text.'\n\t\t]\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'OpenBasedir-cesta',\n\t\t'inherited' => 'Stejné jako rodičovská doména',\n\t\t'docroot' => 'Cesta z pole výše',\n\t\t'homedir' => 'Domovský adresář',\n\t\t'docparent' => 'Nadřazený adresář cesty z výše uvedeného pole',\n\t\t'ssl_certificate_placeholder' => '---- BEGIN CERTIFICATE---' . PHP_EOL . '[...]' . PHP_EOL . '----END CERTIFICATE----',\n\t\t'ssl_key_placeholder' => '---- BEGIN RSA PRIVATE KEY-----' . PHP_EOL . '[...]' . PHP_EOL . '-----END RSA PRIVATE KEY-----',\n\t],\n\t'domains' => [\n\t\t'description' => 'Zde můžete vytvořit (sub)domény a změnit jejich cesty.<br />Systém bude potřebovat nějaký čas, aby mohl po každé změně použít nové nastavení.',\n\t\t'domainsettings' => 'Nastavení domény',\n\t\t'domainname' => 'Název domény',\n\t\t'subdomain_add' => 'Vytvořit subdoménu',\n\t\t'subdomain_edit' => 'Upravit (sub)doménu',\n\t\t'wildcarddomain' => 'Vytvořit jako wildcarddomain?',\n\t\t'aliasdomain' => 'Alias pro doménu',\n\t\t'noaliasdomain' => 'Žádný alias domény',\n\t\t'hasaliasdomains' => 'Má alias doménu(y)',\n\t\t'statstics' => 'Statistiky využití',\n\t\t'isassigneddomain' => 'Je přiřazena doména',\n\t\t'add_date' => 'Přidáno do froxloru',\n\t\t'registration_date' => 'Přidáno do registru',\n\t\t'topleveldomain' => 'Top-Level-Doména',\n\t\t'associated_with_domain' => 'Přidružené',\n\t\t'aliasdomains' => 'Alias domén',\n\t\t'redirectifpathisurl' => 'Přesměrovací kód (výchozí: prázdný)',\n\t\t'redirectifpathisurlinfo' => 'Musíte vybrat pouze jednu z těchto položek, pokud jste zadali adresu URL jako cestu<br/><strong class=\"text-danger\">POZNÁMKA:</strong> Změny jsou aplikovány pouze v případě, že daná cesta je URL adresa.',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'IP adresa (adresy)',\n\t\t\t'description' => 'Zadejte jednu nebo více IP adres domény.<br /><br /><div class=\"text-danger\">POZNÁMKA: IP adresy nelze změnit, pokud je doména nakonfigurována jako <strong>alias-doména</strong> jiné domény.</div>',\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'SSL IP adresa/y',\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'Přesměrování SSL',\n\t\t\t'description' => 'Tato volba vytvoří přesměrování pro ne ssl vhosty, takže všechny požadavky jsou přesměrovány na SSL-vhost.<br /><br />e.. požadavek na <strong>http</strong>://domain.tld/ přesměruje vás na <strong>https</strong>://domain.tld/',\n\t\t],\n\t\t'serveraliasoption_wildcard' => 'Wildcard (*.domain.tld)',\n\t\t'serveraliasoption_www' => 'WWW (www.domain.tld)',\n\t\t'serveraliasoption_none' => 'Bez aliasu',\n\t\t'domain_import' => 'Importovat domény',\n\t\t'import_separator' => 'Oddělovač',\n\t\t'import_offset' => 'Odsazení',\n\t\t'import_file' => 'Soubor CSV',\n\t\t'import_description' => 'Podrobné informace o struktuře importního souboru a o úspěšném importu, navštivte prosím <a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\" class=\"alert-link\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a>',\n\t\t'ssl_redirect_temporarilydisabled' => '<br>Přesměrování protokolu SSL je dočasně deaktivováno, zatímco je generován nový Let\\'s Encrypt certifikát. Bude znovu aktivováno po vygenerování certifikátu.',\n\t\t'termination_date' => 'Datum ukončení',\n\t\t'termination_date_overview' => 'ukončeno dne ',\n\t\t'ssl_certificates' => 'SSL certifikáty',\n\t\t'ssl_certificate_removed' => 'Certifikát s Id #%s byl úspěšně odstraněn',\n\t\t'ssl_certificate_error' => 'Chyba při čtení certifikátu pro doménu: %s',\n\t\t'no_ssl_certificates' => 'Neexistují žádné domény s SSL certifikátem',\n\t\t'isaliasdomainof' => 'Je aliasdoména pro %s',\n\t\t'isbinddomain' => 'Vytvořit zónu DNS',\n\t\t'dkimenabled' => 'DKIM povoleno',\n\t\t'openbasedirenabled' => 'Openbasedir restrikce',\n\t\t'hsts' => 'HSTS povoleno',\n\t\t'aliasdomainid' => 'ID aliasové domény',\n\t\t'nodomainsassignedbyadmin' => 'Váš účet nemá v současné době přiřazeny žádné (aktivní) domény. Pokud si myslíte, že je to špatné, kontaktujte svého správce.',\n\t\t'email_only' => 'Jen emaily',\n\t],\n\t'emails' => [\n\t\t'description' => 'Zde můžete vytvářet a měnit své e-mailové adresy.<br />Účet je jako poštovní schránka před vaším domem. Pokud vám někdo pošle e-mail, bude vypuštěn na účet.<br /><br />Chcete-li stáhnout své e-maily, použijte následující nastavení ve vašem poštovním programu: (Data v <i>kurzívě</i> musí být změněna na ekvivalenty, které jste zadali!<br />Hostitelské jméno: <b><i>doménové jméno</i></b><br />Uživatelské jméno: <b><i>jméno účtu / e-mailová adresa</i></b><br />heslo: <b><i>heslo, které jste zvolili</i></b>',\n\t\t'emailaddress' => 'E-mailová adresa',\n\t\t'emails_add' => 'Vytvořit e-mailovou adresu',\n\t\t'emails_edit' => 'Upravit e-mailovou adresu',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => 'Definovat jako catchall-adresu?',\n\t\t'account' => 'Účet',\n\t\t'account_add' => 'Vytvořit účet',\n\t\t'account_delete' => 'Odstranit účet',\n\t\t'from' => 'Zdroj',\n\t\t'to' => 'Destinace',\n\t\t'forwarders' => 'Přesměrování',\n\t\t'forwarder_add' => 'Vytvořit přeposílatele',\n\t\t'alternative_emailaddress' => 'Alternativní e-mailová adresa',\n\t\t'quota' => 'Kvóta',\n\t\t'noquota' => 'Bez kvóty',\n\t\t'updatequota' => 'Aktualizovat kvótu',\n\t\t'quota_edit' => 'Změnit E-Mail kvótu',\n\t\t'noemaildomainaddedyet' => 'Ve svém účtu ještě nemáte (e-mailovou) doménu.',\n\t\t'back_to_overview' => 'Zpět na přehled domény',\n\t\t'accounts' => 'Účty',\n\t\t'emails' => 'Adresy',\n\t\t'senders' => 'Povolený odesílatel',\n\t\t'sender_add' => 'Přidat povoleného odesílatele',\n\t\t'foreign_sender' => 'Povolený (externí) odesílatel',\n\t\t'allowed_sender_info' => 'Pomocí nastavení <strong>Povolený odesílatel</strong> povolíte existujícímu e-mailovému účtu odesílání e-mailů s jinou adresou odesílatele.<br><strong>Důležité:</strong> Adresa/wildcard-doména zadaná zde se automaticky nestane poštovní schránkou – slouží pouze jako další povolený identifikátor odesílatele.',\n\t],\n\t'error' => [\n\t\t'error' => 'Chyba',\n\t\t'directorymustexist' => 'Adresář %s musí existovat. Vytvořte jej prosím pomocí Vašeho FTP klienta.',\n\t\t'filemustexist' => 'Soubor %s musí existovat.',\n\t\t'allresourcesused' => 'Všechny své zdroje jste již využili.',\n\t\t'domains_cantdeletemaindomain' => 'Nemůžete odstranit přiřazenou doménu.',\n\t\t'domains_canteditdomain' => 'Tuto doménu nelze upravovat. Byla zakázána administrátorem.',\n\t\t'domains_cantdeletedomainwithemail' => 'Nemůžete odstranit doménu, která se používá jako e-mailová doména. Nejprve odstraňte všechny e-mailové adresy.',\n\t\t'firstdeleteallsubdomains' => 'Nejdříve musíte odstranit všechny subdomény, než budete moci vytvořit wildcard doménu.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Pro tuto doménu jste již definovali catchall.',\n\t\t'ftp_cantdeletemainaccount' => 'Nemůžete odstranit svůj hlavní FTP účet',\n\t\t'login' => 'Zadané uživatelské jméno nebo heslo je špatné. Zkuste to prosím znovu!',\n\t\t'login_blocked' => 'Tento účet byl pozastaven kvůli příliš mnoha chybám přihlášení. <br />Zkuste to prosím znovu za %s sekund.',\n\t\t'notallreqfieldsorerrors' => 'Nevyplnili jste všechna nebo jste nesprávně vyplnili některá pole.',\n\t\t'oldpasswordnotcorrect' => 'Staré heslo není správné.',\n\t\t'youcantallocatemorethanyouhave' => 'Nemůžete přidělit více zdrojů než pro sebe.',\n\t\t'mustbeurl' => 'Nezadal jsi platnou nebo úplnou Url (např. http://somedomain.com/error404.htm)',\n\t\t'invalidpath' => 'Nezvolili jste platnou adresu URL (možná problémy se seznamem adres?)',\n\t\t'stringisempty' => 'Chybějící vstup v poli',\n\t\t'stringiswrong' => 'Nesprávný vstup v poli',\n\t\t'newpasswordconfirmerror' => 'Nové heslo a potvrzení hesla se neshodují',\n\t\t'mydomain' => '\\'Doména\\'',\n\t\t'mydocumentroot' => '\\'Documentroot\\'',\n\t\t'loginnameexists' => 'Přihlašovací jméno %s již existuje',\n\t\t'emailiswrong' => 'Emailová adresa %s obsahuje neplatné znaky nebo je nekompletní',\n\t\t'emailexists' => 'E-mailová adresa %s je již používána jiným správcem.',\n\t\t'emailexistsanon' => 'E-mailová adresa %s je již používána.',\n\t\t'alternativeemailiswrong' => 'Zadaná alternativní e-mailová adresa %s pro odeslání přihlašovacích údajů se zdá být neplatná',\n\t\t'loginnameiswrong' => 'Přihlašovací jméno \"%s\" obsahuje nepovolené znaky.',\n\t\t'loginnameiswrong2' => 'Přihlašovací jméno obsahuje příliš mnoho znaků. Pouze %s znaky jsou povoleny.',\n\t\t'userpathcombinationdupe' => 'Kombinace uživatelského jména a cesty již existuje',\n\t\t'patherror' => 'Obecná chyba! Cesta nemůže být prázdná',\n\t\t'errordocpathdupe' => 'Možnost pro cestu %s již existují',\n\t\t'adduserfirst' => 'Nejprve prosím vytvořte zákazníka',\n\t\t'domainalreadyexists' => 'Doména %s je již přiřazena zákazníkovi',\n\t\t'nolanguageselect' => 'Nebyl vybrán žádný jazyk.',\n\t\t'nosubjectcreate' => 'Musíte definovat téma pro tuto šablonu e-mailu.',\n\t\t'nomailbodycreate' => 'Musíte definovat text e-mailu pro tuto šablonu e-mailu.',\n\t\t'templatenotfound' => 'Šablona nebyla nalezena.',\n\t\t'alltemplatesdefined' => 'Nemůžete definovat více šablon, všechny jazyky jsou již podporovány.',\n\t\t'wwwnotallowed' => 'www není povoleno pro subdomény.',\n\t\t'subdomainiswrong' => 'Subdoména %s obsahuje neplatné znaky.',\n\t\t'domaincantbeempty' => 'Název domény nemůže být prázdný.',\n\t\t'domainexistalready' => 'Doména %s již existuje.',\n\t\t'domainisaliasorothercustomer' => 'Vybraná doména aliasu je sama o sobě doménou, má jinou kombinaci ip/port nebo patří jinému zákazníkovi.',\n\t\t'emailexistalready' => 'E-mailová adresa %s již existuje.',\n\t\t'maindomainnonexist' => 'Hlavní doména %s neexistuje.',\n\t\t'maindomaindeactivated' => 'Hlavní doména %s je deaktivována.',\n\t\t'destinationnonexist' => 'Prosím vytvořte předavatele v poli \"Destination\".',\n\t\t'destinationalreadyexistasmail' => 'Přeposílatel do %s již existuje jako aktivní e-mailová adresa.',\n\t\t'destinationalreadyexist' => 'Již jste definovali přesměrování na \"%s\"',\n\t\t'destinationiswrong' => 'Přeposílatel(é) %s obsahuje neplatné znaky nebo je neúplný.',\n\t\t'dumpfoldercannotbedocroot' => 'Složka pro datové výpisy nemůže být váš domovský adresář, zvolte prosím složku ve svém domovském adresáři, např. /dumps',\n\t\t'templatelanguagecombodefined' => 'Kombinace vybraného jazyka/šablony již byla definována.',\n\t\t'templatelanguageinvalid' => 'Vybraný jazyk neexistuje',\n\t\t'ipstillhasdomains' => 'Kombinace IP/portu, kterou chcete odstranit, stále obsahuje přiřazené domény, před smazáním této kombinace IP/portu je prosím přiřaďte k jiným kombinacím IP/portu.',\n\t\t'cantdeletedefaultip' => 'Nelze odstranit výchozí kombinaci IP/portu, před smazáním této kombinace IP/portu prosím nastavte výchozí nastavení jiné kombinace IP/portu.',\n\t\t'cantdeletesystemip' => 'Nelze odstranit poslední systémovou IP adresu ani vytvořit novou kombinaci IP/Port pro systémovou IP adresu nebo změnit systémovou IP adresu.',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'Port\\'',\n\t\t'myipdefault' => 'Musíte vybrat kombinaci IP/port, která by měla být výchozí.',\n\t\t'myipnotdouble' => 'Tato kombinace IP/portu již existuje.',\n\t\t'admin_domain_emailsystemhostname' => 'Jméno serveru nelze použít jako doménu zákazníka.',\n\t\t'cantchangesystemip' => 'Nelze změnit poslední systémovou IP adresu ani vytvořit novou kombinaci IP/Port pro systémovou IP adresu nebo změnit systémovou IP adresu.',\n\t\t'sessiontimeoutiswrong' => 'Je povolen pouze číselný \"časový limit relace\".',\n\t\t'maxloginattemptsiswrong' => 'Jsou povoleny pouze číselné \"maximální pokusy o přihlášení\".',\n\t\t'deactivatetimiswrong' => 'Jsou povoleny pouze číselné \"doby deaktivace\".',\n\t\t'accountprefixiswrong' => '\"Zákaznický prefix\" je nesprávný.',\n\t\t'mysqlprefixiswrong' => '\"SQL prefix\" je nesprávný.',\n\t\t'ftpprefixiswrong' => 'Prefix FTP je špatný.',\n\t\t'ipiswrong' => '\"IP-adresa\" je špatně. Je povoleno pouze platná IP adresa.',\n\t\t'vmailuidiswrong' => '\"UID-Mailů\" je nesprávné. Je povoleno pouze číselné UID.',\n\t\t'vmailgidiswrong' => 'Položka „mails-gid“ je chybná. Povoleno je pouze číselné GID.',\n\t\t'adminmailiswrong' => '\"Odesílatelská adresa\" je chybná. Povolena je pouze platná e-mailová adresa.',\n\t\t'pagingiswrong' => 'Hodnota \"Položky na stránku\" je špatná. Povoleny jsou pouze číselné znaky.',\n\t\t'phpmyadminiswrong' => 'PhpMyAdmin-link není platný odkaz.',\n\t\t'webmailiswrong' => 'Odkaz na webmail není platný odkaz.',\n\t\t'webftpiswrong' => 'WebFTP odkaz není platným odkazem.',\n\t\t'stringformaterror' => 'Hodnota pro pole \"%s\" není v očekávaném formátu.',\n\t\t'loginnameisusingprefix' => 'Nemůžete vytvořit účty, které začínají znakem „%s“, protože tento prefix je nastaven na automatické pojmenování účtu. Zadejte prosím jiný název účtu.',\n\t\t'loginnameissystemaccount' => 'Účet \"%s\" již v systému existuje a nelze jej použít. Zadejte jiný název účtu.',\n\t\t'loginnameisreservedname' => 'Název účtu „%s“ je vyhrazen pro vnitřní systém a nelze jej použít.',\n\t\t'youcantdeleteyourself' => 'Z bezpečnostních důvodů nelze odstranit sám sebe.',\n\t\t'youcanteditallfieldsofyourself' => 'Poznámka: Z bezpečnostních důvodů nelze upravovat všechna pole vlastního účtu.',\n\t\t'documentrootexists' => 'Adresář \"%s\" již pro tohoto zákazníka existuje. Před přidáním zákazníka jej odstraňte.',\n\t\t'norepymailiswrong' => 'Položka „Noreply-address“ je chybná. Povolena je pouze platná e-mailová adresa.',\n\t\t'logerror' => 'Chyba logu: %s',\n\t\t'nomessagetosend' => 'Nezadali jste zprávu.',\n\t\t'norecipientsgiven' => 'Nezadali jste žádného příjemce',\n\t\t'errorsendingmail' => 'Zpráva pro \"%s\" se nezdařila',\n\t\t'errorsendingmailpub' => 'Zpráva na danou e-mailovou adresu se nezdařila',\n\t\t'cannotreaddir' => 'Nelze přečíst adresář \"%s\"',\n\t\t'invalidip' => 'Neplatná IP adresa: %s',\n\t\t'invalidmysqlhost' => 'Neplatná adresa MySQL hostitele: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'Nemůžete povolit Webalizer a AWstats zároveň, zvolte si jeden z nich',\n\t\t'cannotwritetologfile' => 'Nelze otevřít soubor protokolu %s pro zápis',\n\t\t'vmailquotawrong' => 'Kvóta musí být kladné číslo.',\n\t\t'allocatetoomuchquota' => 'Pokusili jste se přidělit %s MB kvótu, ale již vám nezbývá dostatek.',\n\t\t'missingfields' => 'Ne všechna povinná pole byla vyplněna.',\n\t\t'requiredfield' => 'Toto pole je povinné.',\n\t\t'accountnotexisting' => 'Zadaný e-mailový účet neexistuje.',\n\t\t'nopermissionsorinvalidid' => 'Nemáte dostatečná oprávnění ke změně tohoto nastavení nebo bylo uděleno neplatné Id.',\n\t\t'phpsettingidwrong' => 'PHP konfigurace s tímto ID neexistuje',\n\t\t'descriptioninvalid' => 'Popis je příliš krátký/příliš dlouhý nebo obsahuje nepovolené znaky.',\n\t\t'info' => 'Info',\n\t\t'filecontentnotset' => 'Soubor nemůže být prázdný!',\n\t\t'customerdoesntexist' => 'Zvolený zákazník neexistuje.',\n\t\t'admindoesntexist' => 'Zvolený administrátor neexistuje.',\n\t\t'ipportdoesntexist' => 'Kombinace ip/portu, kterou jste zvolili, neexistuje.',\n\t\t'usernamealreadyexists' => 'Uživatelské jméno %s již existuje.',\n\t\t'plausibilitychecknotunderstood' => 'Odpověď kontroly věrohodnosti není srozumitelná.',\n\t\t'errorwhensaving' => 'Došlo k chybě při ukládání pole %s',\n\t\t'hiddenfieldvaluechanged' => 'Hodnota skrytého pole \"%s\" se změnila při úpravách nastavení.<br /><br />Toto obvykle není velký problém, ale z toho důvodu nelze uložit nastavení.',\n\t\t'notrequiredpasswordlength' => 'Zadané heslo je příliš krátké. Zadejte prosím alespoň %s znaky.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Jejda, pole, které by mělo být zobrazeno jako volba v přehledu nastavení, není výjimečně povolený typ. Z toho můžete vinit vývojáře. Nemělo by se to stát!',\n\t\t'pathmaynotcontaincolon' => 'Cesta, kterou jste zadali, by neměla obsahovat dvojtečku (\":\"). Zadejte správnou hodnotu cesty.',\n\t\t'invaliddocumentrooturl' => 'URL adresa, kterou jste zadali pro kořenový adresář, není platná. Zadejte prosím správnou URL adresu nebo unixovou cestu.',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => 'Složitost zadaného hesla nebyla dostatečná.<br />Pokud máte nějaké dotazy ohledně složitosti',\n\t\t'invaliderrordocumentvalue' => 'Hodnota udaná jako chybový dokument se nezdá být platným souborem, URL nebo řetězcem.',\n\t\t'intvaluetoolow' => 'Zadané číslo je příliš nízké (pole %s)',\n\t\t'intvaluetoohigh' => 'Zadané číslo je příliš vysoké (pole %s)',\n\t\t'phpfpmstillenabled' => 'PHP-FPM je aktuálně aktivní. Před aktivací FCGID jej deaktivujte',\n\t\t'fcgidstillenabled' => 'FCGID je aktuálně aktivní. Prosím deaktivujte jej před aktivací PHP-FPM',\n\t\t'domains_cantdeletedomainwithaliases' => 'Nemůžete odstranit doménu, která se používá pro alias-domény. Nejdříve musíte aliasy odstranit.',\n\t\t'user_banned' => 'Váš účet byl uzamčen. Pro další informace kontaktujte svého správce.',\n\t\t'session_timeout' => 'Hodnota je příliš nízká',\n\t\t'session_timeout_desc' => 'Časový limit relace by neměl být nižší než 1 minuta.',\n\t\t'invalidhostname' => 'Název hostitele musí být platná doména. Nemůže být prázdný ani nesmí obsahovat pouze mezery',\n\t\t'operationnotpermitted' => 'Operace není povolena!',\n\t\t'featureisdisabled' => 'Funkce %s je zakázána. Obraťte se na svého poskytovatele služeb.',\n\t\t'usercurrentlydeactivated' => 'Uživatel %s je momentálně deaktivován',\n\t\t'setlessthanalreadyused' => 'Nelze nastavit méně prostředků \\'%s\\', než tento uživatel již použil<br />',\n\t\t'stringmustntbeempty' => 'Hodnota pole %s nesmí být prázdná',\n\t\t'sslcertificateismissingprivatekey' => 'Musíte zadat soukromý klíč k vašemu certifikátu',\n\t\t'sslcertificatewrongdomain' => 'Daný certifikát nepatří k této doméně',\n\t\t'sslcertificateinvalidcert' => 'Daný obsah certifikátu se nezdá být platným certifikátem',\n\t\t'sslcertificateinvalidcertkeypair' => 'Zadaný soukromý klíč nepatří k danému certifikátu',\n\t\t'sslcertificateinvalidca' => 'Dané údaje certifikátů CA se nezdají být platným certifikátem',\n\t\t'sslcertificateinvalidchain' => 'Údaje daného řetězce certifikátů se nezdají být platným certifikátem',\n\t\t'givendirnotallowed' => 'Zadaný adresář v poli %s není povolen.',\n\t\t'sslredirectonlypossiblewithsslipport' => 'Použití Let\\'s Encryptí je možné pouze v případě, že doména má přiřazenou alespoň jednu ssl-povolenou kombinaci IP/port.',\n\t\t'fcgidstillenableddeadlock' => 'FCGID je aktuálně aktivní.<br />Prosím deaktivujte ho před přepnutím na jiný webový server než Apache2',\n\t\t'send_report_title' => 'Odeslat zprávu o chybě',\n\t\t'send_report_desc' => 'Děkujeme, že jste nahlásili tuto chybu a pomohli nám vylepšit froxlor.<br />Toto je e-mail, který bude odeslán froxlor vývojáři:',\n\t\t'send_report' => 'Odeslat hlášení',\n\t\t'send_report_error' => 'Chyba při odesílání hlášení: <br />%s',\n\t\t'notallowedtouseaccounts' => 'Váš účet neumožňuje používat IMAP/POP3. E-mailové účty nelze přidat.',\n\t\t'cannotdeletehostnamephpconfig' => 'Tato konfigurace PHP je používána ve froxlor-vhost a nelze ji odstranit.',\n\t\t'cannotdeletedefaultphpconfig' => 'Tato konfigurace PHP je nastavena jako výchozí a nelze ji odstranit.',\n\t\t'passwordshouldnotbeusername' => 'Heslo by nemělo být stejné jako uživatelské jméno.',\n\t\t'no_phpinfo' => 'Je nám líto, phpinfo() nelze přečíst',\n\t\t'moveofcustomerfailed' => 'Přesun zákazníka do vybraného administrátora/prodejce selhal. Mějte na paměti, že všechny ostatní změny zákazníka byly úspěšně aplikovány v této fázi.<br><br>Zpráva o chybě: %s',\n\t\t'domain_import_error' => 'Při importu domén došlo k chybě: %s',\n\t\t'fcgidandphpfpmnogoodtogether' => 'FCGID a PHP-FPM nelze současně aktivovat',\n\t\t'no_apcuinfo' => 'Žádné informace o cache nejsou k dispozici. APCu se nezdá být spuštěna.',\n\t\t'no_opcacheinfo' => 'Žádné informace o OPCache nejsou k dispozici. OPCache se nezdá být načtena.',\n\t\t'inactive_opcacheinfo' => 'OPCache se zdá být nainstalována, ale není aktivováno.',\n\t\t'nowildcardwithletsencrypt' => 'Let\\'s Encrypt neumí zpracovávat wildcard domény pomocí ACME ve froxlor (vyžaduje dns-challenge), omlouváme se. Nastavte prosím ServerAlias na WWW nebo jej zcela zakažte',\n\t\t'customized_version' => 'Vypadá to, že vaše instalace froxlor byla upravena, na úpravy neposkytuijeme podporu, je nám líto.',\n\t\t'autoupdate_0' => 'Neznámá chyba',\n\t\t'autoupdate_1' => 'Nastavení PHP allow_url_fopen je zakázáno. Autoupdate musí být povolen v php.ini',\n\t\t'autoupdate_2' => 'PHP zip rozšíření nebylo nalezeno, ujistěte se, že je nainstalováno a aktivováno',\n\t\t'autoupdate_4' => 'Archiv froxlor nemohl být uložen na disk :(',\n\t\t'autoupdate_5' => 'version.froxlor.org vrátil nepřijatelné hodnoty :(',\n\t\t'autoupdate_6' => 'Jejda, ke stažení nebyla zadána žádná (platná) verze :(',\n\t\t'autoupdate_7' => 'Stažený archiv nebyl nalezen :(',\n\t\t'autoupdate_8' => 'Archiv nelze extrahovat :(',\n\t\t'autoupdate_9' => 'Stažený soubor neprošel kontrolou integrity. Zkuste prosím aktualizovat znovu.',\n\t\t'autoupdate_10' => 'Minimální podporovaná verze PHP je 7.4.0',\n\t\t'autoupdate_11' => 'Webupdate je zakázán',\n\t\t'mailaccistobedeleted' => 'Jiný účet se stejným názvem (%s) je v současné době smazán, a proto jej nelze v tuto chvíli přidat.',\n\t\t'customerhasongoingexportjob' => 'Na zpracování již čeká úloha exportu dat, buďte prosím trpěliví.',\n\t\t'exportfunctionnotenabled' => 'Funkce exportu není povolena',\n\t\t'dns_domain_nodns' => 'DNS není pro tuto doménu povoleno',\n\t\t'dns_content_empty' => 'Nebyl zadán žádný obsah',\n\t\t'dns_content_invalid' => 'Neplatný obsah DNS',\n\t\t'dns_arec_noipv4' => 'Neplatná IP adresa pro A-záznam',\n\t\t'dns_aaaarec_noipv6' => 'Neplatná IP adresa pro zadaný záznam AAAA',\n\t\t'dns_mx_prioempty' => 'Byla zadána neplatná MX priorita',\n\t\t'dns_mx_needdom' => 'Hodnota MX obsahu musí být platný název domény',\n\t\t'dns_mx_noalias' => 'Hodnota MX nemůže být položka CNAME.',\n\t\t'dns_cname_invaliddom' => 'Neplatný název domény pro záznam CNAME',\n\t\t'dns_cname_nomorerr' => 'Záznam zdroje se stejným názvem záznamu již existuje. Nelze jej použít jako CNAME.',\n\t\t'dns_other_nomorerr' => 'Záznam CNAME se stejným názvem záznamu již existuje. Nelze jej použít pro jiný typ.',\n\t\t'dns_ns_invaliddom' => 'Neplatný název domény pro NS záznam',\n\t\t'dns_srv_prioempty' => 'Byla zadána neplatná SRV priorita',\n\t\t'dns_srv_invalidcontent' => 'Neplatný obsah SRV musí obsahovat váhu, port a cíl, např.: 5 5060 sipserver.example.com.',\n\t\t'dns_srv_needdom' => 'Hodnota SRV cíle musí být platný název domény',\n\t\t'dns_srv_noalias' => 'Hodnota SRV cíle nemůže být položka CNAME.',\n\t\t'dns_duplicate_entry' => 'Záznam již existuje',\n\t\t'dns_notfoundorallowed' => 'Doména nebyla nalezena nebo nemáte oprávnění',\n\t\t'domain_nopunycode' => 'Nesmíte specifikovat punycode (IDNA). Doména bude automaticky převedena',\n\t\t'domain_noipaddress' => 'Nelze přidat IP adresu jako doménu',\n\t\t'dns_record_toolong' => 'Záznamy/popisky mohou mít pouze 63 znaků',\n\t\t'noipportgiven' => 'Není zadán žádný IP/port',\n\t\t'nosslippportgiven' => 'Při povolení SSL je třeba zvolit SSL IP/port',\n\t\t'jsonextensionnotfound' => 'Tato funkce vyžaduje rozšíření php-json.',\n\t\t'cannotdeletesuperadmin' => 'Prvního správce nelze odstranit.',\n\t\t'no_wwwcnamae_ifwwwalias' => 'Nelze nastavit CNAME záznam pro \"www\" jako doménu pro generování www-alias. Změňte prosím nastavení buď na \"No alias\" nebo \"Wildcard alias\"',\n\t\t'local_group_exists' => 'Tato skupina již v systému existuje.',\n\t\t'local_group_invalid' => 'Zadaný název skupiny je neplatný',\n\t\t'local_user_invalid' => 'Zadané uživatelské jméno je neplatné nebo neexistuje',\n\t\t'local_user_isfroxloruser' => 'Zadané uživatelské jméno je spravováno froxlorem a nelze jej v tomto kontextu použít',\n\t\t'invaliddnsforletsencrypt' => 'DNS domén neobsahuje žádnou z vybraných IP adres. Vytvoření Let\\'s Encryp certifikátu není možné.',\n\t\t'notallowedphpconfigused' => 'Pokus o použití php konfigurace, která není přiřazena zákazníkovi',\n\t\t'pathmustberelative' => 'Uživatel nemá oprávnění specifikovat adresáře mimo domovský adresář zákazníka. Zadejte relativní cestu (bez úvodního /).',\n\t\t'mysqlserverstillhasdbs' => 'Databázový server nelze odstranit ze seznamu povolených zákazníků, protože na něm stále existují databáze.',\n\t\t'domaincannotbeedited' => 'Nemáte oprávnění upravovat doménu %s',\n\t\t'invalidcronjobintervalvalue' => 'Interval Cronjobu musí být jeden z: %s',\n\t\t'phpgdextensionnotavailable' => 'Rozšíření PHP GD není dostupné. Nelze ověřit data obrázků',\n\t\t'2fa_wrongcode' => 'Zadaný kód není platný',\n\t\t'gnupgextensionnotavailable' => 'Rozšíření PHP GnuPG není dostupné. Nelze ověřit PGP veřejný klíč',\n\t\t'invalidpgppublickey' => 'PGP veřejný klíč není platný',\n\t\t'invalid_validtime' => 'Platný čas v sekundách může být pouze mezi 10 a 120',\n\t\t'customerphpenabledbutnoconfig' => 'Zákazník má PHP aktivován, ale nebyla vybrána žádná konfigurace PHP.',\n\t\t'emaildomainstillhasaddresses' => 'Nelze deaktivovat flag poštovní domény, protože pro tuto doménu stále existují e-mailové adresy.',\n\t\t'tls13requiredforhttp3' => 'Flag domény http3 je povolen, ale protokoly SSL nezahrnují TLSv1.3.',\n\t\t'senderdomainnotowned' => 'Zadaná doména „%s“ nemůže být použita.',\n\t\t'emailhasnoaccount' => 'Zadaná e-mailová adresa „%s“ nemá žádný účet, nelze přidat adresu odesílatele.',\n\t],\n\t'extras' => [\n\t\t'description' => 'Zde můžete přidat některé doplňky, například ochranu adresářů.<br />Po každé změně bude systém potřebovat určitý čas, aby aplikoval nová nastavení.',\n\t\t'directoryprotection_add' => 'Přidat ochranu adresáře',\n\t\t'view_directory' => 'Zobrazit obsah adresáře',\n\t\t'pathoptions_add' => 'Přidat možnosti cesty',\n\t\t'directory_browsing' => 'Prohlížení obsahu adresáře',\n\t\t'pathoptions_edit' => 'Upravit možnosti cesty',\n\t\t'error404path' => '404',\n\t\t'error403path' => '403',\n\t\t'error500path' => '500',\n\t\t'error401path' => '401',\n\t\t'errordocument404path' => 'ErrorDocument 404',\n\t\t'errordocument403path' => 'ErrorDocument 403',\n\t\t'errordocument500path' => 'ErrorDocument 500',\n\t\t'errordocument401path' => 'ErrorDocument 401',\n\t\t'execute_perl' => 'Spustit perl/CGI',\n\t\t'htpasswdauthname' => 'Důvod ověření (AuthName)',\n\t\t'directoryprotection_edit' => 'Upravit ochranu adresáře',\n\t\t'export' => 'Vytvořit výpis dat',\n\t\t'dump_web' => 'Zahrnout webová data',\n\t\t'dump_mail' => 'Zahrnout e-mailová data',\n\t\t'dump_dbs' => 'Zahrnout databáze',\n\t\t'path_protection_label' => '<strong class=\"text-danger\">Důležité</strong>',\n\t\t'path_protection_info' => 'Důrazně doporučujeme chránit danou cestu, viz \"Extra\" -> \"Ochrana adresářů\"',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Zde můžete vytvořit a změnit své FTP účty.<br />Změny jsou provedeny okamžitě a účty mohou být použity okamžitě.',\n\t\t'account_add' => 'Vytvořit účet',\n\t\t'account_edit' => 'Upravit ftp účet',\n\t\t'editpassdescription' => 'Nastavte nové heslo nebo ponechte prázdné pro zanechání stávajícího.',\n\t\t'sshkey_add' => 'Přidat ssh klíč',\n\t\t'sshkey_edit' => 'Upravit ssh klíč',\n\t],\n\t'gender' => [\n\t\t'title' => 'Název',\n\t\t'male' => 'Pan',\n\t\t'female' => 'Paní',\n\t\t'undef' => '',\n\t],\n\t'imprint' => 'Právní ustanovení',\n\t'index' => [\n\t\t'customerdetails' => 'Podrobnosti o zákazníkovi',\n\t\t'accountdetails' => 'Nastavení účtu',\n\t],\n\t'integrity_check' => [\n\t\t'databaseCharset' => 'Sada znaků databáze (měla by být UTF-8)',\n\t\t'domainIpTable' => 'IP &lt;&dash;&gt; reference domén',\n\t\t'subdomainSslRedirect' => 'Falešný příznak přesměrování SSL pro non-ssl domény',\n\t\t'froxlorLocalGroupMemberForFcgidPhpFpm' => 'froxlor-uživatelve skupinách zákazníků (pro FCGID/php-fpm)',\n\t\t'webserverGroupMemberForFcgidPhpFpm' => 'Webový uživatel ve skupině zákazníků (pro FCGID/php-fpm)',\n\t\t'subdomainLetsencrypt' => 'Hlavní domény bez SSL portu nemají žádné subdomény s aktivním přesměrováním SSL',\n\t],\n\t'logger' => [\n\t\t'date' => 'Datum',\n\t\t'type' => 'Typ',\n\t\t'action' => 'Akce',\n\t\t'user' => 'Uživatel',\n\t\t'truncate' => 'Vyprázdnit protokol',\n\t\t'reseller' => 'Překupník',\n\t\t'admin' => 'Administrátor',\n\t\t'cron' => 'Cronjob',\n\t\t'login' => 'Přihlášení',\n\t\t'intern' => 'Interní',\n\t\t'unknown' => 'Neznámý',\n\t],\n\t'login' => [\n\t\t'username' => 'Uživatelské jméno',\n\t\t'password' => 'Heslo',\n\t\t'language' => 'Jazyk',\n\t\t'login' => 'Přihlásit se',\n\t\t'logout' => 'Odhlásit se',\n\t\t'profile_lng' => 'Jazyk profilu',\n\t\t'welcomemsg' => 'Pro přístup k vašemu účtu se přihlaste.',\n\t\t'forgotpwd' => 'Zapomněli jste heslo?',\n\t\t'presend' => 'Obnovit heslo',\n\t\t'email' => 'E-mailová adresa',\n\t\t'remind' => 'Obnovit heslo',\n\t\t'usernotfound' => 'Uživatel nebyl nalezen!',\n\t\t'backtologin' => 'Zpět na přihlášení',\n\t\t'combination_not_found' => 'Kombinace uživatele a e-mailové adresy nenalezena.',\n\t\t'2fa' => 'Dvoufázové ověření (2FA)',\n\t\t'2facode' => 'Zadejte prosím 2FA kód',\n\t\t'2faremember' => 'Důvěřovat prohlížeči',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Dobrý den,\\\\n\\\\nváš e-mailový účet {EMAIL}\\\\nbyl úspěšně vytvořen.\\\\n\\\\nToto je automaticky vytvořený\\\\ne-mail, prosím neodpovídejte na něj!\\\\n\\\\nVáš správce',\n\t\t\t'subject' => 'E-mailový účet byl úspěšně nastaven',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Dobrýž den {SALUTATION},\\\\n\\\\nzde je váš účet:\\\\n\\\\nUživatelské jméno: {USERNAME}\\\\nHeslo: {PASSWORD}\\\\n\\\\nDěkujeme,\\\\nváš správce',\n\t\t\t'subject' => 'Informace o účtu',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Dobrý den {SALUTATION},\\\\n\\\\nVáš e-mailový účet {EMAIL}\\\\nbyl úspěšně vytvořen.\\\\nVaše heslo je {PASSWORD}.\\\\n\\\\nToto je automaticky vytvořený e-mail\\\\n, prosím neodpovídejte na to!\\\\n\\\\nYours upřímně, váš správce',\n\t\t\t'subject' => 'E-mailový účet byl úspěšně nastaven',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Obnovení hesla',\n\t\t\t'mailbody' => 'Dobrý den, {SALUTATION},\\\\n\\\\nje zde váš odkaz pro nastavení nového hesla. Tento odkaz je platný po dobu následujících 24 hodin.\\\\n\\\\n{LINK}\\\\n\\\\nDěkujeme,\\\\nváš správce',\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] Byla vytvořena nová databáze',\n\t\t\t'mailbody' => 'Dobrý den {CUST_NAME},\n\njste právě přidali novou databázi. Zde jsou zadané informace:\n\nNázev databáze: {DB_NAME}\nHeslo: {DB_PASS}\nPopis: {DB_DESC}\nHostitel databáze: {DB_SRV}\nphpMyAdmin: {PMA_URI}\nS pozdravem Váš správce',\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'Nový ftp uživatel vytvořen',\n\t\t\t'mailbody' => 'Dobrý den {CUST_NAME},\n\njste právě přidali nového ftp uživatele. Zde jsou zadané informace:\n\nUživatelské jméno: {USR_NAME}\nHeslo: {USR_PASS}\nCesta: {USR_PATH}\n\nAch upřímně, váš správce',\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => 'Vážený/á {SALUTATION},\\\\n\\\\njste použili {TRAFFICUSED} z dostupného provozu {TRAFFIC}.\\\\nToto je více než {MAX_PERCENT}%%.\\\\n\\\\nVaše upřímně správce',\n\t\t\t'subject' => 'Dosažení limitu provozu',\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => 'Vážený/á {SALUTATION},\\\\n\\\\njste použili {DISKUSED} ze svého dostupného {DISKAVAILABLE} disku.\\\\nToto je více než {MAX_PERCENT}%%.\\\\n\\\\nVaše upřímně správce',\n\t\t\t'subject' => 'Dosažení limitu na disku',\n\t\t],\n\t\t'2fa' => [\n\t\t\t'mailbody' => 'Dobrý den,\\\\n\\\\nváš 2FA přihlašovací kód je: {CODE}.\\\\n\\\\nToto je automaticky vytvořený\\\\ne-mail, prosím neodpovídejte na to!\\\\n\\\\nVáš správce',\n\t\t\t'subject' => 'froxlor - 2FA kód',\n\t\t],\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Hlavní',\n\t\t\t'changepassword' => 'Změnit heslo',\n\t\t\t'changelanguage' => 'Změnit jazyk',\n\t\t\t'username' => 'Přihlášen jako: ',\n\t\t\t'changetheme' => 'Změnit motiv',\n\t\t\t'apihelp' => 'Nápověda API',\n\t\t\t'apikeys' => 'API klíče',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'Email',\n\t\t\t'emails' => 'Adresy',\n\t\t\t'webmail' => 'Webmail',\n\t\t\t'emailsoverview' => 'Přehled e-mailových domén',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Databáze',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domény',\n\t\t\t'settings' => 'Přehled domén',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Účty',\n\t\t\t'webftp' => 'WebFTP',\n\t\t\t'sshkeys' => 'SSH klíče',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extra',\n\t\t\t'directoryprotection' => 'Ochrana adresáře',\n\t\t\t'pathoptions' => 'Možnosti cesty',\n\t\t\t'export' => 'Export dat',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Provoz',\n\t\t\t'current' => 'Aktuální měsíc',\n\t\t\t'overview' => 'Celkový provoz',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'PHP konfigurace',\n\t\t\t'fpmdaemons' => 'Verze PHP-FPM',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'Systémový protokol',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'Nebyl odeslán žádný e-mail, protože v databázi nejsou žádní příjemci',\n\t\t'success' => 'Zpráva byla úspěšně odeslána příjemcům %s',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'Uživatel/Jméno databáze',\n\t\t'databasedescription' => 'Popis databáze',\n\t\t'database_create' => 'Vytvořit databázi',\n\t\t'description' => 'Zde můžete vytvořit a změnit databáze MySQL.<br />Změny jsou provedeny okamžitě a databáze může být použita okamžitě.<br />V menu na levé straně najdete nástroj phpMyAdmin, pomocí kterého můžete snadno spravovat svou databázi.<br /><br />Pro použití Vašich databází ve svých vlastních php-skriptech použijte následující nastavení: (Data <i>kurzívou</i> je třeba změnit na ekvivalenty, které jste zadali!<br />Název hostitele: <b><SQL_HOST></b><br />Uživatelské jméno: <b><i>název databáze</i></b><br />Heslo: <b><i>heslo, které jste si vybrali</i></b><br />databáze: <b><i>databázový název</i></b>',\n\t\t'mysql_server' => 'Server MySQL',\n\t\t'database_edit' => 'Upravit databázi',\n\t\t'size' => 'Velikost',\n\t\t'privileged_user' => 'Oprávněný databázový uživatel',\n\t\t'privileged_passwd' => 'Heslo pro oprávněného uživatele',\n\t\t'unprivileged_passwd' => 'Heslo pro neoprávněného uživatele',\n\t\t'mysql_ssl_ca_file' => 'Certifikát SSL serveru',\n\t\t'mysql_ssl_verify_server_certificate' => 'Ověřit certifikát SSL serveru',\n\t\t'globaluserinfo' => 'Pro přístup k databázím můžete navíc použít vaše froxlor přihlášení (uživatel: %s), které má automaticky přístup ke všem databázím.<br />Doporučujeme <b>ne</b> použít pro aplikace, pouze pro správu (např. přes phpMyAdmin).',\n\t\t'edit_global_user' => 'Upravit administrátora',\n\t],\n\t'opcacheinfo' => [\n\t\t'generaltitle' => 'Obecné informace',\n\t\t'resetcache' => 'Resetovat OPcache',\n\t\t'version' => 'Verze OPCache',\n\t\t'phpversion' => 'Verze PHP',\n\t\t'runtimeconf' => 'Spustitelná konfigurace',\n\t\t'start' => 'Čas spuštění',\n\t\t'lastreset' => 'Poslední restart',\n\t\t'oomrestarts' => 'Počet restartů OOM',\n\t\t'hashrestarts' => 'Počet restartů hash',\n\t\t'manualrestarts' => 'Manuální počet restartů',\n\t\t'hitsc' => 'Počet zásahů',\n\t\t'missc' => 'Nevyužitý počet',\n\t\t'blmissc' => 'Počet zmeškaných záznamů na černé listině',\n\t\t'status' => 'Stav',\n\t\t'never' => 'nikdy',\n\t\t'enabled' => 'OPcache povoleno',\n\t\t'cachefull' => 'Mezipaměť je plná',\n\t\t'restartpending' => 'Čeká na restart',\n\t\t'restartinprogress' => 'Probíhá restart',\n\t\t'cachedscripts' => 'Počet skriptů v mezipaměti',\n\t\t'memusage' => 'Využití paměti',\n\t\t'totalmem' => 'Celková paměť',\n\t\t'usedmem' => 'Využitá paměť',\n\t\t'freemem' => 'Volná paměť',\n\t\t'wastedmem' => 'Promarněná paměť',\n\t\t'maxkey' => 'Maximální počet klíčů',\n\t\t'usedkey' => 'Použité klíče',\n\t\t'wastedkey' => 'Promarněné klíče',\n\t\t'strinterning' => 'String interning',\n\t\t'strcount' => 'Počet řetězců',\n\t\t'keystat' => 'Statistika klíčů v mezipaměti',\n\t\t'used' => 'Využito',\n\t\t'free' => 'Volné',\n\t\t'blacklist' => 'Černá listina',\n\t\t'novalue' => '<i>žádná hodnota</i>',\n\t\t'true' => '<i>true</i>',\n\t\t'false' => '<i>false</i>',\n\t\t'funcsavail' => 'Dostupné funkce',\n\t],\n\t'panel' => [\n\t\t'edit' => 'Upravit',\n\t\t'delete' => 'Smazat',\n\t\t'create' => 'Vytvořit',\n\t\t'save' => 'Uložit',\n\t\t'yes' => 'Ano',\n\t\t'no' => 'Ne',\n\t\t'emptyfornochanges' => 'prázdné pro žádné změny',\n\t\t'emptyfordefault' => 'prázdné pro výchozí nastavení',\n\t\t'path' => 'Cesta',\n\t\t'toggle' => 'Přepínač',\n\t\t'next' => 'Další',\n\t\t'dirsmissing' => 'Nelze najít nebo přečíst adresář!',\n\t\t'unlimited' => '∞',\n\t\t'urloverridespath' => 'URL (přepsání cesty)',\n\t\t'pathorurl' => 'Cesta nebo URL',\n\t\t'ascending' => 'vzestupně',\n\t\t'descending' => 'sestupně',\n\t\t'search' => 'Vyhledat',\n\t\t'used' => 'využito',\n\t\t'translator' => 'Překladač',\n\t\t'reset' => 'Zahodit změny',\n\t\t'pathDescription' => 'Pokud složka neexistuje, bude vytvořena automaticky.',\n\t\t'pathDescriptionEx' => '<br /><br /><span class=\"text-danger\">Upozornění:</span> Cesta <code>/</code> není povolena z důvodu administrativního nastavení, bude automaticky nastaveno na <code>/chosen.subdomain.tld/</code> pokud není nastaveno na jiný adresář.',\n\t\t'pathDescriptionSubdomain' => 'Pokud složka neexistuje, bude vytvořena automaticky.<br /><br />Pokud chcete přesměrovat na jinou doménu, musí tento záznam začít http:// nebo https://.<br /><br />Pokud URL končí / je považována za složku, pokud ne, je považována za soubor.',\n\t\t'back' => 'Zpět',\n\t\t'reseller' => 'překupník',\n\t\t'admin' => 'admin',\n\t\t'customer' => 'zákazník/zákazníci',\n\t\t'send' => 'odeslat',\n\t\t'nosslipsavailable' => 'V současné době neexistují žádné kombinace ssl ip/port pro tento server',\n\t\t'backtooverview' => 'Zpět na přehled',\n\t\t'dateformat' => 'RRRR-MM-DD',\n\t\t'dateformat_function' => 'R-m-d',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Výchozí',\n\t\t'never' => 'Nikdy',\n\t\t'active' => 'Aktivní',\n\t\t'please_choose' => 'Vyberte prosím',\n\t\t'allow_modifications' => 'Povolit změny',\n\t\t'megabyte' => 'MegaByte',\n\t\t'not_supported' => 'Nepodporováno v: ',\n\t\t'view' => 'zobrazení',\n\t\t'toomanydirs' => 'Příliš mnoho podadresářů. Vraťte se zpět na manuální výběr cesty.',\n\t\t'abort' => 'Přerušit',\n\t\t'not_activated' => 'není aktivováno',\n\t\t'off' => 'vyp.',\n\t\t'options' => 'Možnosti',\n\t\t'neverloggedin' => 'Dosud žádné přihlášení',\n\t\t'descriptionerrordocument' => 'Může být adresa URL, cesta k souboru nebo jen řetězec zabalený kolem \" \"<br />Ponechte prázdné pro použití výchozí hodnoty serveru.',\n\t\t'unlock' => 'Odemknout',\n\t\t'theme' => 'Motiv',\n\t\t'variable' => 'Proměnná',\n\t\t'description' => 'Popis',\n\t\t'cancel' => 'Zrušit',\n\t\t'ssleditor' => 'Nastavení SSL pro tuto doménu',\n\t\t'ssleditor_infoshared' => 'V současné době používá certifikát rodičovské domény',\n\t\t'ssleditor_infoglobal' => 'V současné době používá globální certifikát',\n\t\t'dashboard' => 'Nástěnka',\n\t\t'assigned' => 'Přiřazeno',\n\t\t'available' => 'K dispozici',\n\t\t'news' => 'Novinky',\n\t\t'newsfeed_disabled' => 'Novinky jsou zakázány. Klepnutím na ikonu úpravy přejdete do nastavení.',\n\t\t'ftpdesc' => 'Popis FTP',\n\t\t'letsencrypt' => 'Používá Let\\'s Encrypt',\n\t\t'set' => 'Aplikovat',\n\t\t'shell' => 'Konzole',\n\t\t'sshkeydesc' => 'Popis klíče SSH',\n\t\t'ftpuser' => 'FTP uživatel',\n\t\t'sshpubkey' => 'Věřejný SSH klíč',\n\t\t'sshpubkeyph' => \"Začíná na „ssh-ed25519“, „ssh-rsa“, „ecdsa-sha2-nistp256“, „ecdsa-sha2-nistp384“, „ecdsa-sha2-nistp521“, „sk-ecdsa-sha2-nistp256@openssh.com“ nebo „sk-ssh-ed25519@openssh.com'\",\n\t\t'sshfingerprint' => 'Otisk',\n\t\t'exportpath' => [\n\t\t\t'title' => 'Cílová cesta pro exportovaná data',\n\t\t\t'description' => 'Toto je cesta, kde bude exporotovaný archiv uložen. Pokud jsou webová data zahrnuta, všechny soubory z domovského adresáře jsou uloženy mimo složku zadanou zde.',\n\t\t],\n\t\t'export_pgp_public_key' => [\n\t\t\t'title' => 'Veřejný PGP klíč pro šifrování',\n\t\t\t'description' => 'Toto je veřejný PGP klíč, který bude použit k zašifrování exportu. Pokud necháte toto pole prázdné, export nebude zašifrován.',\n\t\t],\n\t\t'pgp_public_key' => 'Veřejný PGP klíč',\n\t\t'none_value' => 'Žádná',\n\t\t'viewlogs' => 'Zobrazit protokoly',\n\t\t'not_configured' => 'Systém ještě není nakonfigurován. Klikněte zde pro přechod do konfigurace.',\n\t\t'start_setup' => 'Spustit nastavení',\n\t\t'ihave_configured' => 'Konfiguroval jsem služby',\n\t\t'system_is_configured' => '<i class=\"fa-solid fa-circle-exclamation me-1\"></i>Systém je již nastaven jako konfigurovaný',\n\t\t'settings_before_configuration' => 'Ujistěte se, že jste upravili nastavení před konfigurací služeb zde',\n\t\t'image_field_delete' => 'Odstranit stávající obrázek',\n\t\t'usage_statistics' => 'Využití zdrojů',\n\t\t'security_question' => 'Bezpečnostní otázka',\n\t\t'listing_empty' => 'Nebyly nalezeny žádné záznamy',\n\t\t'unspecified' => 'nespecifikováno',\n\t\t'settingsmode' => 'Režim',\n\t\t'settingsmodebasic' => 'Základní',\n\t\t'settingsmodeadvanced' => 'Rozšířený',\n\t\t'settingsmodetoggle' => 'Režim přepínání',\n\t\t'modalclose' => 'Zavřít',\n\t\t'managetablecolumnsmodal' => [\n\t\t\t'title' => 'Správa sloupců tabulky',\n\t\t\t'description' => 'Zde si můžete přizpůsobit viditelné sloupce',\n\t\t],\n\t\t'mandatoryfield' => 'Pole je povinné',\n\t\t'select_all' => 'Vybrat vše',\n\t\t'unselect_all' => 'Odznačit vše',\n\t\t'searchtablecolumnsmodal' => [\n\t\t\t'title' => 'Hledat v polích',\n\t\t\t'description' => 'Vyberte pole, ve kterém chcete vyhledat'\n\t\t],\n\t\t'upload_import' => 'Nahrát a importovat',\n\t\t'profile' => 'Můj profil',\n\t\t'use_checkbox_for_unlimited' => 'Hodnota „0“ deaktivuje tento prostředek. Zaškrtávací políčko vpravo umožňuje „neomezené“ použití.',\n\t\t'use_checkbox_to_disable' => 'Chcete-li tuto funkci deaktivovat, zaškrtněte políčko napravo od textového pole.',\n\t\t'distro_mismatch' => 'Zdá se, že jste provedli upgrade na novou distribuci. Nezapomeňte prosím odpovídajícím způsobem překonfigurovat služby.',\n\t\t'set_new_distro' => 'Nastavit distribuci',\n\t\t'dismiss' => 'Zavřít',\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => 'Místní uživatel pro PHP-FPM (froxlor vHost)',\n\t\t'vhost_httpgroup' => 'Místní skupina pro PHP-FPM (froxlor vHost)',\n\t\t'ownvhost' => [\n\t\t\t'title' => 'Povolit PHP-FPM pro froxlor vHost',\n\t\t\t'description' => 'Pokud je povoleno, bude froxlor spuštěn také pod místním uživatelem',\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => 'Použít mod_proxy / mod_proxy_fcgi',\n\t\t\t'description' => '<strong class=\"text-danger\">Při používání Debianu 9.x (Stretch) nebo novějšího</strong>musí být povoleno použití php-fpm přes mod_proxy_fcgi. Vyžaduje alespoň apache-2.4.9',\n\t\t],\n\t\t'ini_flags' => 'Zadejte možný <strong>php_flag</strong> pro php.ini. Jeden záznam na řádek',\n\t\t'ini_values' => 'Zadejte možnou <strong>php_value</strong> pro php.ini. Jeden záznam na řádek',\n\t\t'ini_admin_flags' => 'Zadejte možný <strong>php_admin_flag</strong> pro php.ini. Jeden záznam na řádek',\n\t\t'ini_admin_values' => 'Zadejte možnou <strong>php_value</strong> pro php.ini. Jeden záznam na řádek',\n\t],\n\t'privacy' => 'Zásady ochrany soukromí',\n\t'pwdreminder' => [\n\t\t'success' => 'Obnovení hesla bylo úspěšně požadováno. Postupujte podle pokynů v e-mailu, který jste obdrželi.',\n\t\t'notallowed' => 'Neznámý uživatel nebo obnovení hesla je zakázáno',\n\t\t'changed' => 'Vaše heslo bylo úspěšně aktualizováno. Nyní se můžete přihlásit pomocí nového hesla.',\n\t\t'wrongcode' => 'Je nám líto, váš aktivační kód neexistuje nebo již vypršel.',\n\t\t'choosenew' => 'Nastavit nové heslo',\n\t],\n\t'question' => [\n\t\t'question' => 'Bezpečnostní otázka',\n\t\t'admin_customer_reallydelete' => 'Opravdu chcete odstranit zákazníka %s? Tuto akci nelze vrátit zpět!',\n\t\t'admin_domain_reallydelete' => 'Opravdu chcete odstranit doménu %s?<br><span class=\"text-danger\"><strong>POZNÁMKA:</strong> Všechny subdomény, ftp-účty a e-mailové adresy/účty spojené s touto doménou budou odstraněny!</span>',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Opravdu chcete zakázat toto nastavení zabezpečení OpenBasedir?',\n\t\t'admin_admin_reallydelete' => 'Opravdu chcete odstranit správce %s? Každý zákazník a doména budou znovu přiřazeny k vašemu účtu.',\n\t\t'admin_template_reallydelete' => 'Opravdu chcete odstranit šablonu\\'%s\\'?',\n\t\t'domains_reallydelete' => 'Opravdu chcete odstranit doménu %s?',\n\t\t'email_reallydelete' => 'Opravdu chcete odstranit e-mailovou adresu %s?',\n\t\t'email_reallydelete_account' => 'Opravdu chcete smazat e-mailový účet %s?',\n\t\t'email_reallydelete_forwarder' => 'Opravdu chcete odstranit přeposílatele %s?',\n\t\t'email_reallydelete_sender' => 'Opravdu chcete odstranit povoleného odesílatele %s?',\n\t\t'extras_reallydelete' => 'Opravdu chcete odstranit ochranu adresáře pro %s?',\n\t\t'extras_reallydelete_pathoptions' => 'Opravdu chcete odstranit možnosti cesty pro %s?',\n\t\t'extras_reallydelete_export' => 'Opravdu chcete přerušit plánovanou práci na exportu?',\n\t\t'ftp_reallydelete' => 'Opravdu chcete odstranit FTP účet %s?',\n\t\t'sshkey_reallydelete' => 'Opravdu chcete smazat ssh-klíč %s?',\n\t\t'mysql_reallydelete' => 'Opravdu chcete odstranit databázi %s? Tuto akci nelze vrátit zpět!',\n\t\t'admin_configs_reallyrebuild' => 'Opravdu chcete znovu sestavit všechny konfigurační soubory?',\n\t\t'admin_customer_alsoremovefiles' => 'Odstranit také uživatelské soubory?',\n\t\t'admin_customer_alsoremovemail' => 'Zcela odstranit e-mailová data ze souborového systému?',\n\t\t'admin_customer_alsoremoveftphomedir' => 'Odebrat také domovský adresář FTP uživatele?',\n\t\t'admin_ip_reallydelete' => 'Opravdu chcete odstranit IP adresu %s?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Jste si jisti, že chcete, aby kořen dokumentu pro tuto doménu nebyl v rámci kořene zákazníka?',\n\t\t'admin_counters_reallyupdate' => 'Opravdu chcete přepočítat využití prostředků?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Opravdu chcete vymazat všechna nešifrovaná hesla e-mailového účtu z tabulky mail_users? Tuto akci nelze vrátit zpět! Nastavení nešifrovaných e-mailových hesel bude také nastaveno na vypnutí',\n\t\t'logger_reallytruncate' => 'Opravdu chcete zredukovat tabulku \"%s\"?',\n\t\t'admin_quotas_reallywipe' => 'Opravdu chcete vymazat všechny kvóty v tabulce mail_users? Toto nelze vrátit zpět!',\n\t\t'admin_quotas_reallyenforce' => 'Opravdu chcete vynutit výchozí kvótu pro všechny uživatele? Toto nelze vrátit zpět!',\n\t\t'phpsetting_reallydelete' => 'Opravdu chcete odstranit tato nastavení? Všechny domény, které v současné době používají tato nastavení, budou změněny na výchozí nastavení.',\n\t\t'fpmsetting_reallydelete' => 'Opravdu chcete smazat tato nastavení php-fpm? Všechny php konfigurace, které v současné době používají tato nastavení, budou změněny na výchozí nastavení.',\n\t\t'customer_reallyunlock' => 'Opravdu chcete odemknout zákazníka %s?',\n\t\t'admin_integritycheck_reallyfix' => 'Opravdu chcete zkusit automaticky opravit všechny problémy s integritou databáze?',\n\t\t'plan_reallydelete' => 'Opravdu chcete odstranit plán hostování %s?',\n\t\t'apikey_reallydelete' => 'Opravdu chcete odstranit tento api klíč?',\n\t\t'apikey_reallyadd' => 'Opravdu chcete vytvořit nový api klíč?',\n\t\t'dnsentry_reallydelete' => 'Opravdu chcete odstranit tento záznam zóny?',\n\t\t'certificate_reallydelete' => 'Opravdu chcete odstranit tento certifikát?',\n\t\t'cache_reallydelete' => 'Opravdu chcete vymazat mezipaměť?',\n\t\t'please_enter_otp' => 'Zadejte prosím 2FA kód',\n\t\t'admin_mysqlserver_reallydelete' => 'Opravdu chcete smazat tento MySQL-server?',\n\t],\n\t'redirect_desc' => [\n\t\t'rc_default' => 'výchozí',\n\t\t'rc_movedperm' => 'trvale přesunuto',\n\t\t'rc_found' => 'nalezeno',\n\t\t'rc_seeother' => 'viz ostatní',\n\t\t'rc_tempred' => 'dočasné přesměrování',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Vypršení platnosti relace',\n\t\t\t'description' => 'Jak dlouho musí být uživatel neaktivní, než bude relace neplatná (sekundy)?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Prefix zákazníka',\n\t\t\t'description' => 'Jaký prefix by měly mít zákaznické účty?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'SQL Prefix',\n\t\t\t'description' => 'Jaký prefix by měl mít MySQL účty?</br>Použijte \"RANDOM\" jako hodnotu k získání trojmístného náhodného prefixu</br>Použijte \"DBNAME\" jako hodnotu, pole názvu databáze je použito spolu s názvem zákazníka jako prefix.',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP Prefix',\n\t\t\t'description' => 'Jaký prefix by měly mít ftp účty?<br/><b>Pokud toto změníte, musíte také změnit Quota SQL Query v konfiguračním souboru FTP serveru, pokud jej používáte!</b> ',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Domovský adresář',\n\t\t\t'description' => 'Kde by měly být uloženy všechny domovské adresáře?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Adresář souborů protokolu',\n\t\t\t'description' => 'Kde mají být uloženy všechny soubory protokolů?',\n\t\t],\n\t\t'logfiles_script' => [\n\t\t\t'title' => 'Vlastní skript pro odesílání souborů protokolu do',\n\t\t\t'description' => 'Zde můžete zadat skript a použít zástupné symboly <strong>{LOGFILE}, {DOMAIN} a {CUSTOMER}</strong> v případě potřeby. Pokud jej chcete použít, budete muset aktivovat také <strong>nastavení logů webserveru</strong>. Není zapotřebí žádný předem stanovený pipe-znak.',\n\t\t],\n\t\t'logfiles_format' => [\n\t\t\t'title' => 'Formát přístupového protokolu',\n\t\t\t'description' => 'Zde zadejte vlastní formát logu podle specifikací webových serverů, ponechte prázdný pro výchozí. V závislosti na vašem formátu musí být řetězec uveden.<br/>Pokud je použito s nginx, bude vypadat jako <i>log_format frx_custom {CONFIGURED_VALUE}</i>.<br/>Pokud je použito s Apache, bude vypadat jako <i>LogFormat {CONFIGURED_VALUE} frx_custom</i>.<br/><strong>Pozornost</strong>: Kód nebude zkontrolován pro žádné chyby. Pokud obsahuje chyby, webový server nemusí znovu spustit!',\n\t\t],\n\t\t'logfiles_type' => [\n\t\t\t'title' => 'Typ přístupového protokolu',\n\t\t\t'description' => 'Zde si vyberte mezi <strong>combined</strong> nebo <strong>vhost_combined</strong>.',\n\t\t],\n\t\t'logfiles_piped' => [\n\t\t\t'title' => 'Přesměrování souborů protokolu webového serveru do zadaného skriptu (viz výše)',\n\t\t\t'description' => 'Používáte-li vlastní skript pro logy, musíte jej aktivovat, aby mohl být spuštěn',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'IP adresa',\n\t\t\t'description' => 'Jaká je hlavní IP adresa tohoto serveru?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Název serveru',\n\t\t\t'description' => 'Jaký je název hostitele tohoto serveru?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Příkaz pro opětovné načtení webového serveru',\n\t\t\t'description' => 'Jaký je příkaz webového serveru pro opětovné načtení konfiguračních souborů?',\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => 'Povolit nameserver',\n\t\t\t'description' => 'Zde lze globálně povolit a zakázat nameserver.',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Konfigurační adresář serveru Dns',\n\t\t\t'description' => 'Kde by měly být uloženy konfigurační soubory dns-serveru?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Příkaz pro opětovné načtení serveru DNS',\n\t\t\t'description' => 'Jaký je příkaz pro znovunačtení dns serveru daemon?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'UID-Mailů',\n\t\t\t'description' => 'Které uživatelské Id by měly mít e-maily?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mails-GID',\n\t\t\t'description' => 'Jaké GroupID by měly mít e-maily?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Domovský adresář pro emaily',\n\t\t\t'description' => 'Kde by měly být uloženy všechny emaily?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Odesílatel',\n\t\t\t'description' => 'Jaká je adresa odesílatele pro e-maily odeslané z panelu?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin URL',\n\t\t\t'description' => 'Jaká je adresa URL na phpMyAdmin? (musí začít s http(s)://)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'Webmail URL',\n\t\t\t'description' => 'Jaká je URL adresa webové pošty? (musí začít http(s)://)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP URL',\n\t\t\t'description' => 'Jaká je URL adresa na WebFTP? (musí začít http(s)://)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Jaký je váš standardní jazyk serveru?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Maximální počet pokusů o přihlášení',\n\t\t\t'description' => 'Maximální počet pokusů o přihlášení, po kterých je účet zakázán.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Čas deaktivace',\n\t\t\t'description' => 'Čas (sek.) je po příliš mnoha pokusech o přihlášení deaktivován účet.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Typ vstupu cesty',\n\t\t\t'description' => 'Měla by být cesta vybrána v rozbalovacím menu nebo ve vstupním poli?',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Nameservery',\n\t\t\t'description' => 'Čárkou oddělený seznam obsahující jména všech nameserverů. První bude primární.',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'MX servery',\n\t\t\t'description' => 'Čárkou oddělený seznam obsahující dvojici čísel a názvu hostitele oddělených mezerou (např. \\'10 mx.example.com\\') obsahující mx servery.',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Počet položek na stránku',\n\t\t\t'description' => 'Kolik záznamů se zobrazí na jedné stránce? (0 = zakázat stránkování)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'Výchozí IP/port',\n\t\t\t'description' => 'Vyberte všechny IP adresy, které chcete použít jako výchozí pro nové domény',\n\t\t],\n\t\t'defaultsslip' => [\n\t\t\t'title' => 'Výchozí SSL IP/port',\n\t\t\t'description' => 'Vyberte všechny ssl IP adresy, které chcete použít jako výchozí pro nové domény',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Cesty, které se připojí do souboru OpenBasedir',\n\t\t\t'description' => 'Tyto cesty (oddělené dvojtečkami) budou přidány do příkazu OpenBasedir v každém kontejneru vHost.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Použít přirozené lidské třídění v zobrazení seznamu',\n\t\t\t'description' => 'Seřadí seznamy jako web1 -> web2 -> web11 místo web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Docroot pro deaktivované uživatele',\n\t\t\t'description' => 'Pokud je uživatel deaktivován, použije se tato cesta jako docroot. Ponechte prázdné, pokud vůbec nechcete vytvářet vHost hostitele.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Také ukládat nešifrované hesla e-mailových účtů do databáze',\n\t\t\t'description' => 'Pokud je toto nastaveno na Ano, všechna hesla budou také uložena v tabulce mail_users-table (nešifrovaný text, jednoduše čitelný pro všechny s přístupem k databázi). Aktivujte pouze pokud chcete použít SASL!',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'FTP účty @doména',\n\t\t\t'description' => 'Zákazníci mohou vytvořit FTP účty uživatel@doménauživatele?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Povolit FCGID',\n\t\t\t'description' => 'Použijte pro spuštění PHP s odpovídajícím uživatelským účtem.<br /><br /><b>Toto vyžaduje speciální konfiguraci webového serveru pro Apache, viz <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - příručka</a></b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Adresář konfigurace',\n\t\t\t\t'description' => 'Kde by měly být uloženy všechny fcgid konfigurační soubory? Pokud nepoužíváte vlastní kompilovaný suexec binární, což je normální situace, tato cesta musí být pod /var/www/<br /><br /><div class=\"text-danger\">POZNÁMKA: Obsah této složky je pravidelně smazán, aby se zabránilo ukládání dat v ní manuálně.</div>',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Dočasný adresář',\n\t\t\t\t'description' => 'Kde by měly být uloženy dočasné adresáře',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Procesy na doménu',\n\t\t\t\t'description' => 'Kolik procesů by mělo být spuštěno/povoleno v dané doméně? Hodnota 0 je doporučená příčinou PHP pak bude velmi efektivně spravovat samotný počet procesů.',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper v hostitelích Vhosts',\n\t\t\t\t'description' => 'Jak by měl být wrapper zahrnut do Vhosts',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Globální adresáře PEAR',\n\t\t\t\t'description' => 'Které globální adresáře PEAR by měly být nahrazeny v každém konfiguračním souboru php.ini? Různé adresáře musí být odděleny dvojtečkou.',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Maximální počet požadavků na doménu',\n\t\t\t\t'description' => 'Kolik požadavků by mělo být povoleno na doménu?',\n\t\t\t],\n\t\t\t'defaultini' => 'Výchozí konfigurace PHP pro nové domény',\n\t\t\t'defaultini_ownvhost' => 'Výchozí konfigurace PHP pro froxlor-vHost',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Časový limit nečinnosti',\n\t\t\t\t'description' => 'Časový limit nastavení Mod FastCGI.',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Použít alternativní e-mailovou adresu',\n\t\t\t'description' => 'Poslat heslo na jinou adresu při vytváření e-mailu',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Konfigurační soubor/adresář webserveru',\n\t\t\t'description' => 'Kde má být uložena konfigurace vHost? Zde můžete zadat buď soubor (všechny vHosty v jednom souboru), nebo adresář (každý vHost ve vlastním souboru).',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Konfigurační soubor/název adresáře webového serveru diroptions',\n\t\t\t'description' => 'Kde má být uložena konfigurace diroptions? Zde můžete zadat buď soubor (všechny dioptrie v jednom souboru), nebo adresář (každá dioptrie ve vlastním souboru).',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Webserver htpasswd dirname',\n\t\t\t'description' => 'Kde mají být soubory htpasswd pro ochranu adresáře uloženy?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'MySQL-Access-Hosts',\n\t\t\t'description' => 'Čárkami oddělený seznam hostitelů, od kterých by měli mít uživatelé možnost se připojit k serveru MySQL-Server. Pro povolení podsítě je platná síťová maska nebo cidr.',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Výstup Webalizátoru',\n\t\t\t'description' => 'Verbosita webalizéru',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Logování povoleno/zakázáno',\n\t\t\t'severity' => 'Úroveň protokolování',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Typ (typy) logů',\n\t\t\t\t'description' => 'Zadejte typy logů. Chcete-li vybrat více typů, podržte CTRL při výběru.<br />Dostupné typy logů jsou: syslog, soubor, mysql',\n\t\t\t],\n\t\t\t'logfile' => [\n\t\t\t\t'title' => 'Název souboru pro log',\n\t\t\t\t'description' => 'Používá se pouze v případě, že typ logu obsahuje \"soubor\". Tento soubor bude vytvořen v froxlor/logs/. Tato složka je chráněna před veřejným přístupem.',\n\t\t\t],\n\t\t\t'logcron' => 'Zaznamenávat cronjoby',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Nikdy',\n\t\t\t\t'once' => 'Jednou',\n\t\t\t\t'always' => 'Vždy',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => 'Povolit využití SSL',\n\t\t\t\t'description' => 'Zaškrtněte, pokud chcete použít SSL pro váš webový server',\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'Cesta k SSL certifikátu',\n\t\t\t\t'description' => 'Zadejte cestu včetně názvu souboru .crt nebo .pem (hlavní certifikát)',\n\t\t\t],\n\t\t\t'openssl_cnf' => 'Výchozí nastavení pro vytvoření Cert souboru',\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'Cesta k souboru klíče SSL',\n\t\t\t\t'description' => 'Zadejte cestu včetně názvu souboru pro soukromý klíč (z většiny.key)',\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'Cesta k SSL CA certifikátu (volitelné)',\n\t\t\t\t'description' => 'Ověřování klienta, nastavte to pouze pokud víte, co je.',\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => 'Konfigurovat povolené SSL šifry',\n\t\t\t\t'description' => 'Toto je seznam šifer, které chcete (nebo nechcete) použít při komunikaci SSL. Pro seznam šifer a způsob, jak je zahrnout/vyloučit viz oddíly \"CIPHER LIST FORMAT\" a \"CIPHER STRINGS\" na <a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">man-stránce pro šifry</a>.<br /><br /><b>Výchozí hodnota je:</b><pre>ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305</pre>',\n\t\t\t],\n\t\t\t'apache24_ocsp_cache_path' => [\n\t\t\t\t'title' => 'Apache 2.4: cesta k OCSP stapling cache',\n\t\t\t\t'description' => 'Konfiguruje mezipaměť použitou k ukládání odpovědí OCSP, které jsou zahrnuty do TLS handshakes.',\n\t\t\t],\n\t\t\t'ssl_protocols' => [\n\t\t\t\t'title' => 'Konfigurace verze protokolu TLS',\n\t\t\t\t'description' => 'Toto je seznam ssl protokolů, které chcete (nebo nechcete) použít při použití SSL. <b>Upozornění:</b> Některé starší prohlížeče nemusí podporovat nejnovější verze protokolu.<br /><br /><b>Výchozí hodnota je:</b><pre>TLSv1.2</pre>',\n\t\t\t],\n\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t'title' => 'Nakonfigurujte explicitní TLSv1.3 šifry, pokud jsou použity',\n\t\t\t\t'description' => 'Toto je seznam šifrů, které chcete (nebo nechcete) použít při komunikaci TLSv1.3. Seznam šifr a jak je zahrnovat/vyloučit, viz <a href=\"https://wiki.openssl.org/index.php/TLS1.3\">dokumentace pro TLSv1.</a>.<br /><br /><b>Výchozí hodnota je prázdná</b>',\n\t\t\t],\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Výchozí nastavení vHost-serveru',\n\t\t\t'description' => 'Obsah tohoto pole bude přímo zahrnut do tohoto kontejneru s ip/portem. Můžete použít následující proměnné:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code> <code>{FPMSOCKET}</code> (pokud existuje)<br/> Upozornění: Kód nebude zkontrolován na výskyt chyb. Pokud obsahuje chyby, webový server nemusí znovu spustit!',\n\t\t],\n\t\t'apache_globaldiropt' => [\n\t\t\t'title' => 'Možnosti adresáře pro prefix zákazníka',\n\t\t\t'description' => 'Obsah tohoto pole bude zahrnut do 05_froxlor_dirfix_nofcgid.conf apache config. Je-li prázdné, použije se výchozí hodnota:<br><br>apache >=2.<br><code>Require all granted<br>AllowOverride All</code><br><br>apache <=2.<br><code>Order allow,deny<br>allow from all</code>',\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => 'Obsah tohoto pole bude přímo zahrnut do vHhost kontejneru. Můžete použít následující proměnné:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code> <code>{FPMSOCKET}</code> (pokud existuje)<br/> Upozornění: Kód nebude zkontrolován na obsah chyb. Pokud obsahuje chyby, webový server nemusí znovu spustit!',\n\t\t],\n\t\t'decimal_places' => 'Počet desetinných míst ve výstupu provozu/webového prostoru',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Nastavení dns domény zákazníka',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Umožnit zákazníkům upravit nastavení dns domény',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Použít uživatelská jména kompatibilní s UNIX',\n\t\t\t'description' => 'Umožňuje používat <strong>-</strong> a <strong>_</strong> v uživatelských jménech, pokud <strong>Ne</strong>',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Povolit obnovení hesla zákazníkem',\n\t\t\t'description' => 'Zákazníci mohou obnovit své heslo a aktivační odkaz bude odeslán na jejich e-mailovou adresu',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Povolit obnovení hesla administrátorem',\n\t\t\t'description' => 'Správci/prodejci mohou obnovit své heslo a na jejich e-mailovou adresu bude zaslán aktivační odkaz',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Kvóta pro poštovní schránku',\n\t\t\t'description' => 'Výchozí kvóta pro nové vytvořené poštovní schránky (MegaByte).',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Použít kvóty pro poštovní schránku pro zákazníky',\n\t\t\t'description' => 'Aktivujte pro použití kvót na mailboxech. Výchozí hodnota je <b>Ne</b>, protože to vyžaduje speciální nastavení.',\n\t\t\t'removelink' => 'Klikněte zde pro vymazání všech kvót pro e-mailové účty.',\n\t\t\t'enforcelink' => 'Klikněte zde pro vynucení výchozí kvóty na všechny uživatelské e-mailové účty.',\n\t\t],\n\t\t'mail_enable_allow_sender' => [\n\t\t\t'title' => 'Povolit zákazníkům používání „povoleného odesílatele“',\n\t\t\t'description' => 'Pokud je tato funkce povolena, mohou zákazníci určit „povoleného odesílatele“ pro e-mailové účty, ze kterých budou odesílat zprávy. <br>Výchozí nastavení: vypnuto',\n\t\t],\n\t\t'mail_allow_external_domains' => [\n\t\t\t'title' => 'Povolit externí domény pro „povolené odesílatele“\"',\n\t\t\t'description' => 'Pokud je tato možnost povolena, může zákazník zadat jako „povoleného odesílatele“ pro e-mailové účty libovolnou doménu (kromě domén, které tento systém nevlastní).<br>Výchozí: vypnuto',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Povolit vícenásobné přihlášení',\n\t\t\t'description' => 'Pokud je uživatel aktivován, může se přihlásit vícekrát.',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Povolit přesouvání domén mezi správci',\n\t\t\t'description' => 'Pokud je aktivováno, můžete změnit administrátora domény v nastavení domény.<br /><b>Upozornění:</b> Pokud zákazník není přiřazen stejnému správci jako doména, administrátor může vidět všechny ostatní domény tohoto zákazníka!',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Povolit přesouvání domén mezi zákazníky',\n\t\t\t'description' => 'Pokud je aktivováno, můžete změnit zákazníka domény v nastavení domény.<br /><b>Upozornění:</b> froxlor změní kořenový adresář dokumentu na výchozí domovský adresář nového zákazníka (+ doménová složka, pokud je aktivována)',\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'Pokud je zvoleno ano, budou tato vlastní nastavení vHost přidána ke všem subdoménám; pokud ne, budou zvláštní nastavení subdomény odstraněna.',\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Minimální délka hesla',\n\t\t\t'description' => 'Zde můžete nastavit minimální délku hesla. \"0\" znamená, že není vyžadována žádná minimální délka.',\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Uložit výchozí soubor indexu také do nových podsložek',\n\t\t\t'description' => 'Pokud je povoleno, výchozí indexový soubor se ukládá do každé nově vytvořené subdoménové cesty (ne pokud složka již existuje!)',\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Odpovědět na adresu',\n\t\t\t'description' => 'Definujte e-mailovou adresu jako odpověď na adresu pro emaily odeslané panelem.',\n\t\t],\n\t\t'adminmail_defname' => 'Jméno odesílatele e-mailu v panelu',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Zákaznická standardní subdoména',\n\t\t\t'description' => 'Jaký název hostitele by měl být použit pro vytvoření standardních subdomén pro zákazníka. Pokud je prázdný, použije se systémový název hostitele.',\n\t\t],\n\t\t'awstats_path' => 'Cesta k AWStats \\'awstats_buildstaticpages.pl\\'',\n\t\t'awstats_conf' => 'Cesta k konfiguraci AWStats',\n\t\t'defaultttl' => 'TTL domény pro navázání v sekundách (výchozí \\'604800\\' = 1 týden)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Povolit výchozí chybové dokumenty pro všechny zákazníky',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'Soubor/URL pro chybu 401',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'Soubor/URL pro chybu 403',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'Soubor/URL pro chybu 404',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'Soubor/URL pro chybu 500',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'Pokud je zvolen pureftpd, soubory .ftpquota pro uživatelské kvóty jsou vytvářeny a denně aktualizovány',\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Povolit přesměrování zákazníků',\n\t\t\t'description' => 'Umožnit zákazníkům vybrat kód pro http-status přesměrování, které budou použity',\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Výchozí přesměrování',\n\t\t\t'description' => 'Nastavte výchozí kód přesměrování, který by se měl použít, pokud jej zákazník nenastaví sám',\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Vytvořit mail-, imap-, pop3- a smtp-\"A record\" také s nastavením MX-serverů',\n\t\t'froxlordirectlyviahostname' => 'Přístup k froxlor přímo prostřednictvím názvu hostitele',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Regulární výraz pro hesla',\n\t\t\t'description' => 'Zde můžete nastavit regulární výraz pro složitost hesel.<br />Prázdné = žádné specifické požadavky',\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'Povolit FCGID pro froxlor vHost',\n\t\t\t'description' => 'Pokud je povoleno, bude froxlor spuštěn také pod místním uživatelem',\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'Povolit SuExec workaround',\n\t\t\t\t'description' => 'Povolit pouze v případě, že zákaznické docrooty nejsou v apache suexec cestě.<br />Pokud je povoleno, froxlor vygeneruje symbolický odkaz od zákazníků perl-enabled adresáře + /cgi-bin/ k dané cestě.<br />Všimněte si, že perl bude fungovat pouze v podadresáři složek /cgi-bin/ a ne ve složce samotné (jako to dělá bez této opravy!)',\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Cesta pro symlinky adresářů zákazníka s povoleným perlem',\n\t\t\t\t'description' => 'Toto je třeba nastavit pouze pokud je povolena SuExec-workaround.<br />POZOR: Ujistěte se, že tato cesta je v rámci hlavní cesty, nebo jinak je toto řešení zbytečné',\n\t\t\t],\n\t\t],\n\t\t'awstats_awstatspath' => 'Cesta k AWStats \\'awstats.pl\\'',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'Cesta ke složce ikon AWstats',\n\t\t\t'description' => 'např. /usr/share/awstats/htdocs/icon/',\n\t\t],\n\t\t'login_domain_login' => 'Povolit přihlášení pomocí domén',\n\t\t'perl_server' => [\n\t\t\t'title' => 'Umístění soketu perl serveru',\n\t\t\t'description' => 'Jednoduchý návod najdete na: <a target=\"blank\" href=\"https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/\">nginx.com</a>',\n\t\t],\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP backend',\n\t\t\t'description' => 'zde proces PHP naslouchá požadavkům z nginxu, může to být unixový soket s kombinací ip:port<br />*Nepoužívá se s php-fpm',\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'Příkaz pro opětovné načtení PHP',\n\t\t\t'description' => 'toto se používá k obnovení PHP backendu, pokud je použit<br />Výchozí: prázdný<br />*NENÍ použito s php-fpm',\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => 'Povolit php-fpm',\n\t\t\t'description' => '<b>To vyžaduje speciální konfiguraci webserveru viz <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">PHP-FPM příručka</a></b>',\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'Adresář konfigurace php-fpm',\n\t\t\t'aliasconfigdir' => 'Konfigurace adresáře aliasu php-fpm',\n\t\t\t'reload' => 'php-fpm restart příkaz',\n\t\t\t'pm' => 'Řízení správce procesu (pm)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'Počet podřízených procesů',\n\t\t\t\t'description' => 'Počet podřízených procesů, které mají být vytvořeny, když je hodnota pm nastavena na \\'static\\' a maximální počet podřízených procesů, které mají být vytvořeny, když je hodnota pm nastavena na \\'dynamic/ondemand\\'<br />ekvivalent hodnoty PHP_FCGI_CHILDREN',\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'Počet podřízených procesů vytvořených při spuštění',\n\t\t\t\t'description' => 'Poznámka: Používá se pouze v případě, že je pm nastaven na \\'dynamické\\'',\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'Požadovaný minimální počet procesů serveru v nečinnosti',\n\t\t\t\t'description' => 'Poznámka: Používá se pouze při nastavení pm na \\'dynamic\\'<br />Poznámka: Povinné při nastavení pm na \\'dynamic\\'',\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'Požadovaný maximální počet procesů serveru v nečinnosti',\n\t\t\t\t'description' => 'Poznámka: Používá se pouze v případě, že je pm nastaven na \\'dynamické\\'<br />Poznámka: Povinné, když je pm nastaven na \\'dynamické\\'',\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Požadavky na proces před znovu objevením',\n\t\t\t\t'description' => 'Pro nekonečné zpracování požadavků zadejte \\'0\\'. Ekvivalentní do PHP_FCGI_MAX_REQUESTS.',\n\t\t\t],\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Časový limit nečinnosti',\n\t\t\t\t'description' => 'Nastavení časového limitu pro PHP FastCGI.',\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'FastCGI adresář IPC',\n\t\t\t\t'description' => 'Adresář, kam bude webový server ukládat sockety php-fpm.<br />Tento adresář musí být pro webový server čitelný',\n\t\t\t],\n\t\t\t'limit_extensions' => [\n\t\t\t\t'title' => 'Povolené přípony',\n\t\t\t\t'description' => 'Omezuje rozšíření hlavního skriptu, které FPM dovolí analyzovat. To může zabránit chybám v konfiguraci na straně webového serveru. Měli byste omezit FPM pouze na přípony .php, abyste zabránili škodlivým uživatelům používat jiné přípony ke spuštění kódu php. Výchozí hodnota: .php',\n\t\t\t],\n\t\t\t'envpath' => 'Cesty, které se přidají do prostředí PATH. Nechte prázdné, pokud proměnná prostředí PATH neexistuje',\n\t\t\t'override_fpmconfig' => 'Přepsat nastavení FPM-daemon (pm, max_children atd.)',\n\t\t\t'override_fpmconfig_addinfo' => '<br /><span class=\"text-danger\">Používá se pouze v případě, že \"Přepsat nastavení FPM-daemon\" je nastaveno na \"Ano\"</span>',\n\t\t\t'restart_note' => 'Upozornění: Konfigurace nebude zkontrolována na výskyt chyb. Pokud obsahuje chyby, PHP-FPM nemusí znovu spustit!',\n\t\t\t'custom_config' => [\n\t\t\t\t'title' => 'Vlastní konfigurace',\n\t\t\t\t'description' => 'Přidejte vlastní konfiguraci pro každou instanci PHP-FPM verze, například <i>pm.status_path = /status</i> pro monitorování. Proměnné níže lze použít zde.  <strong>Upozornění: Konfigurace nebude zkontrolována pro žádné chyby. Pokud obsahuje chyby, PHP-FPM nemusí znovu spustit!</strong>',\n\t\t\t],\n\t\t\t'allow_all_customers' => [\n\t\t\t\t'title' => 'Přiřadit tuto konfiguraci všem aktuálně existujícím zákazníkům',\n\t\t\t\t'description' => 'Nastavte tuto hodnotu na „true“, pokud chcete tuto konfiguraci přiřadit všem aktuálně existujícím zákazníkům, aby ji mohli používat. Toto nastavení není trvalé, ale lze jej spustit vícekrát.',\n\t\t\t],\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => 'Povolit odesílání hlášení o používání webu a provozu',\n\t\t\t'webmax' => [\n\t\t\t\t'title' => 'Úroveň varování v procentech pro webový prostor',\n\t\t\t\t'description' => 'Platné hodnoty jsou 0 až 150. Nastavením této hodnoty na 0 zakáže tuto zprávu.',\n\t\t\t],\n\t\t\t'trafficmax' => [\n\t\t\t\t'title' => 'Úroveň varování v procentech pro provoz',\n\t\t\t\t'description' => 'Platné hodnoty jsou 0 až 150. Nastavením této hodnoty na 0 zakáže tuto zprávu.',\n\t\t\t],\n\t\t\t'report_web_bccadmin' => [\n\t\t\t\t'title' => 'BCC e-mail pro oznámení o webovém využití správci',\n\t\t\t\t'description' => 'Pokud je tato funkce povolena, varování o využití místa na disku zasílané zákazníkovi se zasílá také příslušnému správci/prodejci (BCC).'\n\t\t\t],\n\t\t],\n\t\t'dropdown' => 'Rozevírací nabídka',\n\t\t'manual' => 'Manuální',\n\t\t'default_theme' => 'Výchozí šablona',\n\t\t'validate_domain' => 'Ověřit názvy domén',\n\t\t'diskquota_enabled' => 'Kvóta aktivována?',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'Cesta k repquota',\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'Cesta k quotatool',\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => 'Oddíl, na kterém jsou uloženy zákaznické soubory',\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'Název maildir',\n\t\t\t'description' => 'Maildir adresář do uživatelského účtu. Obvykle \\'Maildir\\', v některých implementacích \\'.maildir\\' a přímo do adresáře uživatele, pokud zůstane prázdný.',\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => 'Použít Catchall',\n\t\t\t'description' => 'Chcete svým zákazníkům poskytnout funkci catchall?',\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => 'Použít úpravy pro Apache 2.4',\n\t\t\t'description' => '<strong class=\"text-danger\">POZOR:</strong> použijte, pouze pokud máte nainstalovanou apache verzi 2.4 nebo vyšší<br />jinak nebude váš webserver moci spustit',\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'Cesta k souboru fastcgi_params',\n\t\t\t'description' => 'Zadejte cestu k souboru fastcgi_params nginx včetně názvu souboru',\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => 'Použít název domény jako výchozí hodnotu pro cestu kořenového adresáře dokumentu',\n\t\t\t'description' => 'Pokud je povoleno a cesta DocumentRoot je prázdná, výchozí hodnotou bude název (sub)domény.<br /><br />Příklady: <br />/var/customers/webs/customer_name/example.com/<br />/var/customers/webs/customer_name/subdomain.example.com/',\n\t\t],\n\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t'title' => 'Skrýt subdomény v PHP-konfiguračním přehledu',\n\t\t\t'description' => 'Pokud je aktivováno, subdomény zákazníků nebudou zobrazeny v přehledu php-konfigurací, zobrazí se pouze počet subdomén.<br /><br />Poznámka: Toto je viditelné pouze v případě, že jste povolili FCGID nebo PHP-FPM',\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => 'Skrýt standardní subdomény v přehledu konfigurace PHP-',\n\t\t\t'description' => 'Pokud je aktivováno, standardní subdomény pro zákazníky se nebudou zobrazovat v přehledu php-konfigurací<br /><br />Poznámka: Toto je viditelné pouze v případě, že jste povolili FCGID nebo PHP-FPM',\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => 'Vyberte, která metoda šifrování hesla má být použita',\n\t\t\t'description' => 'Vyberte, která metoda šifrování heslem má být použita. Pokud toto nastavení změníte, budou šifrována pouze nová hesla s novou metodou. Stávající hesla se nezmění.',\n\t\t],\n\t\t'systemdefault' => 'Výchozí systémové nastavení',\n\t\t'panel_allow_theme_change_admin' => 'Povolit administrátorům změnit motiv',\n\t\t'panel_allow_theme_change_customer' => 'Umožnit zákazníkům změnu motivu',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'Servery AXFR',\n\t\t\t'description' => 'Čárkami oddělený seznam IP adres, které mohou přenášet (AXFR) zóny.',\n\t\t],\n\t\t'powerdns_mode' => [\n\t\t\t'title' => 'Operační režim PowerDNS',\n\t\t\t'description' => 'Vyberte režim PoweDNS: Nativní pro žádnou replikaci (výchozí) / Master, pokud je potřeba DNS replikace.',\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Adresář zákaznických certifikátů webových serverů',\n\t\t\t'description' => 'Kde by měly být vytvořeny ssl-certifikáty zadané zákazníkem?<br /><br /><div class=\"text-danger\">POZNÁMKA: Obsah této složky je pravidelně smazán, aby se zabránilo ukládání dat do této složky ručně.</div>',\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => 'Povolit správcům/prodejcům nahlásit chyby databáze froxlor',\n\t\t\t'description' => 'Upozornění: Nikdy nám neposílejte žádné osobní (zákaznické)údaje!',\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => 'Umožnit zákazníkům nahlásit chyby databáze froxlor',\n\t\t\t'description' => 'Upozornění: Nikdy nám neposílejte žádné osobní (zákaznické)údaje!',\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => 'Analyzovat přenos pošty',\n\t\t\t'description' => 'Povolit analýzu protokolů mailserveru pro výpočet provozu',\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'MDA typ',\n\t\t\t'description' => 'Typ poštovního serveru',\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'Protokol MDA',\n\t\t\t'description' => 'Soubor protokolu serveru pro doručování pošty',\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'MTA typ',\n\t\t\t'description' => 'Typ zprostředkovatele pro přenos pošty',\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'Protokol MTA',\n\t\t\t'description' => 'Soubor protokolu agenta pro přenos pošty',\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'Konfigurační soubor cronu',\n\t\t\t'description' => 'Cesta ke konfiguračnímu souboru cron-service. Tento soubor bude pravidelně a automaticky aktualizován froxlorem.<br />Poznámka: <b>Ujistěte se</b>, že používáte stejný název souboru jako pro hlavní froxlor cronjob (výchozí: /etc/cron.d/froxlor)!<br><br>Pokud používáte <b>FreeBSD</b>, zadejte zde <i>/etc/crontab</i>!',\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Příkaz pro opětovné načtení Cron-daemona',\n\t\t\t'description' => 'Určete příkaz, který má být proveden, aby bylo možné znovu načíst systémy cron-daemon',\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Příkaz pro provedení cronu (php-binary)',\n\t\t\t'description' => 'Příkaz k provedení našich cronjobů. Změňte to pouze pokud víte, co děláte (výchozí: \"/usr/bin/nice -n 5 /usr/bin/php -q\")!',\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => 'Povolit automatické aktualizace databáze',\n\t\t\t'description' => '<div class=\"text-danger\"><b>POZOR:</b></div> Toto nastavení umožňuje cronjob obejít kontrolu verzí froxlor souborů a databáze a spustit aktualizace databáze v případě, že dojde k nesouladu verzí.<br><br><div class=\"text-danger\">Automatická aktualizace vždy nastaví výchozí hodnoty pro nová nastavení nebo změny. Toto nemusí vždy vyhovovat vašemu systému. Před aktivací této možnosti</div> prosím dvakrát rozmyslete',\n\t\t],\n\t\t'dns_createhostnameentry' => 'Vytvořte bind-zone/config pro název hostitele systému',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => 'Malé písmo',\n\t\t\t'description' => 'Heslo musí obsahovat alespoň jedno malé písmeno (a-z).',\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => 'Velké písmeno',\n\t\t\t'description' => 'Heslo musí obsahovat alespoň jedno velké písmeno (A-Z).',\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => 'Čísla',\n\t\t\t'description' => 'Heslo musí obsahovat alespoň jedno číslo (0-9).',\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => 'Speciální znak',\n\t\t\t'description' => 'Heslo musí obsahovat alespoň jeden z níže uvedených znaků.',\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => 'Seznam speciálních znaků',\n\t\t\t'description' => 'Jeden z těchto znaků je vyžadován, pokud je nastavena výše uvedená volba.',\n\t\t],\n\t\t'apache_itksupport' => [\n\t\t\t'title' => 'Použít úpravy ITK-MPM Apache',\n\t\t\t'description' => '<strong class=\"text-danger\">POZOR:</strong> použijte, pouze pokud máte opravdu povoleno apache itk-mpm<br />jinak váš webserver nebude moci spustit',\n\t\t],\n\t\t'letsencryptca' => [\n\t\t\t'title' => 'ACME prostředí',\n\t\t\t'description' => 'Prostředí, které se má použít pro certifikáty Let\\'s Encrypt / ZeroSSL.',\n\t\t],\n\t\t'letsencryptchallengepath' => [\n\t\t\t'title' => 'Cesta k výzvám Let\\'s Encrypt',\n\t\t\t'description' => 'Adresář, ze kterého by měly být nabízeny výzvy Let\\'s Encrypt prostřednictvím globálního aliasu.',\n\t\t],\n\t\t'letsencryptkeysize' => [\n\t\t\t'title' => 'Velikost klíče pro nové Let\\'s Encrypt certifikáty',\n\t\t\t'description' => 'Velikost klíče v Bitech pro nové Let\\'s Encrypt certifikáty.',\n\t\t],\n\t\t'letsencryptreuseold' => [\n\t\t\t'title' => 'Znovu použít Let\\'s Encrypt klíč',\n\t\t\t'description' => 'Pokud je aktivován, bude při každém obnovení použit stejný klíč, jinak bude pokaždé vygenerován nový klíč.',\n\t\t],\n\t\t'leenabled' => [\n\t\t\t'title' => 'Použít Let\\'s Encrypt',\n\t\t\t'description' => 'Pokud je aktivováno, zákazníci mohou nechat froxlor automaticky generovat a obnovit šifrovací certifikáty domén s ssl IP/portem.<br /><br />Nezapomeňte, že musíte procházet konfigurací webserveru, pokud je povoleno, protože tato funkce vyžaduje speciální konfiguraci.',\n\t\t],\n\t\t'caa_entry' => [\n\t\t\t'title' => 'Generovat CAA DNS záznamy',\n\t\t\t'description' => 'Automaticky generuje CAA záznamy pro domény s podporou SSL, které používají Let\\'s Encrypt',\n\t\t],\n\t\t'caa_entry_custom' => [\n\t\t\t'title' => 'Další záznamy CAA DNS',\n\t\t\t'description' => 'DNS Certification Authority autorizace (CAA) je mechanismus politiky bezpečnosti internetu, který držitelům doménových jmen umožňuje certifikovat autoritě<br>, zda jsou oprávněni vydávat digitální certifikáty pro určitý název domény. To dělá pomocí nového zdrojového záznamu doménového systému \"CAA\" (DNS).<br><br>Obsah tohoto pole bude zahrnut do DNS zóny přímo (každý řádek vyústí v CAA záznam).<br>Pokud je pro tuto doménu povoleno šifrování, tato položka bude vždy přidána automaticky a nemusí být přidána ručně:<br><code>0 problém \"letsencrypt\". rg\"</code> (Pokud doména je doména zástupných znaků, bude místo toho použita vydání).<br>Chcete-li povolit hlášení incidentů, můžete přidat záznam <code>iodef</code>. Příklad pro odeslání zprávy na <code>me@example. om</code> bude:<br><code>0 iodef \"mailto:me@example. om\"</code><br><br><strong>Upozornění:</strong> Kód nebude zkontrolován z důvodu chyb. Pokud obsahuje chyby, vaše CAA záznamy nemusí fungovat!',\n\t\t],\n\t\t'exportenabled' => [\n\t\t\t'title' => 'Povolit export dat pro zákazníky',\n\t\t\t'description' => 'Pokud je aktivováno, zákazník bude moci naplánovat úlohy pro export dat (cron-export), které generují archiv ve svém docrootu (podadresář vybraný zákazníkem)',\n\t\t],\n\t\t'dnseditorenable' => [\n\t\t\t'title' => 'Povolit DNS editor',\n\t\t\t'description' => 'Umožňuje administrátorům a zákazníkovi spravovat dns záznamy domén',\n\t\t],\n\t\t'dns_server' => [\n\t\t\t'title' => 'Daemon DNS serveru',\n\t\t\t'description' => 'Nezapomeňte, že \"daemons\" je třeba konfigurovat pomocí konfiguračních šablon froxloru',\n\t\t],\n\t\t'panel_customer_hide_options' => [\n\t\t\t'title' => 'Skrytí položek nabídek a grafů návštěvnosti v zákaznickém panelu',\n\t\t\t'description' => 'Vyberte položky, které chcete skrýt v panelu zákazníka. Chcete-li vybrat více možností, podržte CTRL při výběru.',\n\t\t],\n\t\t'allow_allow_customer_shell' => [\n\t\t\t'title' => 'Umožnit zákazníkům povolit přístup přes shell pro uživatele ftp',\n\t\t\t'description' => '<strong class=\"text-danger\">Upozornění: Přístup k Shell umožňuje uživateli spustit různé binární soubory ve vašem systému. Používejte s extrémní opatrností. Aktivujte prosím pouze pokud víte, co děláte!!!</strong>',\n\t\t],\n\t\t'available_shells' => [\n\t\t\t'title' => 'Seznam dostupných příkazů',\n\t\t\t'description' => 'Čárkami oddělený seznam shellů, z nichž si může zákazník vybrat pro své ftp uživatele.<br><br>Všimněte si, že výchozí shell <strong>/bin/false</strong> bude vždy na výběr (pokud je povolen), i když je toto nastavení prázdné. V každém případě je to výchozí hodnota proftp uživatele',\n\t\t],\n\t\t'le_froxlor_enabled' => [\n\t\t\t'title' => 'Povolit Let\\'s Encrypt pro hostitele froxlor',\n\t\t\t'description' => 'Pokud je aktivováno, bude vhost froxloru automaticky zabezpečen pomocí Let\\'s Encrypt certifikátu.',\n\t\t],\n\t\t'le_froxlor_redirect' => [\n\t\t\t'title' => 'Povolit přesměrování SSL pro vhost froxloru',\n\t\t\t'description' => 'Pokud je aktivováno, všechny požadavky http na váš froxlor budou přesměrovány na příslušný server SSL.',\n\t\t],\n\t\t'option_unavailable_websrv' => '<br><em class=\"text-danger\">K dispozici pouze pro: %s</em>',\n\t\t'option_unavailable' => '<br><em class=\"text-danger\">Možnost není dostupná kvůli jiným nastavením.</em>',\n\t\t'letsencryptacmeconf' => [\n\t\t\t'title' => 'Cesta k snippetu acme.conf',\n\t\t\t'description' => 'Název souboru v konfiguračním textovém bloku, který umožňuje webovému serveru obsluhovat výzvu acme.',\n\t\t],\n\t\t'mail_use_smtp' => 'Nastavte e-mail pro použití SMTP',\n\t\t'mail_smtp_host' => 'Zadejte SMTP server',\n\t\t'mail_smtp_usetls' => 'Povolit šifrování TLS',\n\t\t'mail_smtp_auth' => 'Povolit ověřování SMTP',\n\t\t'mail_smtp_port' => 'TCP port pro připojení k',\n\t\t'mail_smtp_user' => 'SMTP uživatelské jméno',\n\t\t'mail_smtp_passwd' => 'SMTP heslo',\n\t\t'http2_support' => [\n\t\t\t'title' => 'HTTP2 podpora',\n\t\t\t'description' => 'povolit podporu HTTP2 pro ssl.<br><em class=\"text-danger\">POVOLTE POUZE POKUD VÁŠ SERVER TUTO FUNKCI PODPORUJE (nginx verze 1.9.5+, apache2 verze 2.4.17+)</em>',\n\t\t],\n\t\t'http3_support' => [\n\t\t\t'title' => 'HTTP3 podpora',\n\t\t\t'description' => 'povolit podporu HTTP3 pro ssl.<br><em class=\"text-danger\">POVOLTE POUZE V PŘÍPADĚ, ŽE VÁŠ WEBOVÝ SERVER TUTO FUNKCI PODPORUJE (nginx verze 1.25.0+)</em>',\n\t\t],\n\t\t'nssextrausers' => [\n\t\t\t'title' => 'Použít libnss-extrauser místo libnss-mysql',\n\t\t\t'description' => 'Nečíst uživatele z databáze, ale ze souborů. Aktivujte se, prosím, pouze pokud jste již prošli požadovanými kroky konfigurace (system -> libnss-extrausers).<br><strong class=\"text-danger\">Pouze pro Debian/Ubuntu (nebo pokud jste sami zkompilovali libnss-extrauser!)</strong>',\n\t\t],\n\t\t'le_domain_dnscheck' => [\n\t\t\t'title' => 'Ověřit DNS domén při použití Let\\'s Encrypt',\n\t\t\t'description' => 'Pokud je aktivováno, froxlor ověří, zda se doména, která žádá o certifikát, směřuje alespoň na jednu ze Ip adres systému.',\n\t\t],\n\t\t'le_domain_dnscheck_resolver' => [\n\t\t\t'title' => 'Použít externí nameserver pro ověření DNS',\n\t\t\t'description' => 'Je-li nastaveno, froxlor použije tuto DNS k ověření DNS domén při použití Let\\'s Encrypt. Pokud je prázdné, bude použit výchozí překladač DNS systému.',\n\t\t],\n\t\t'phpsettingsforsubdomains' => [\n\t\t\t'description' => 'Pokud ano, zvolený php-config bude aktualizován na všechny subdomény',\n\t\t],\n\t\t'leapiversion' => [\n\t\t\t'title' => 'Zvolte si implementaci ACME pro Let\\'s Encrypt',\n\t\t\t'description' => 'V současné době je podporována pouze implementace ACME v2 pro Let\\'s Encrypt.',\n\t\t],\n\t\t'enable_api' => [\n\t\t\t'title' => 'Povolit externí použití API',\n\t\t\t'description' => 'Chcete-li používat froxlor API, musíte aktivovat tuto možnost. Podrobnější informace naleznete v <a href=\"https://docs.froxlor.org/latest/api-guide/\" target=\"_new\">https://docs.froxlor.org/</a>',\n\t\t],\n\t\t'api_customer_default' => '\"Povolit přístup k API\" pro nové zákazníky',\n\t\t'dhparams_file' => [\n\t\t\t'title' => 'Soubor DHParams (výměna klíčů Diffie-Hellman)',\n\t\t\t'description' => 'Pokud je zde zadán soubor dhparams.pem, bude zahrnut do konfigurace webserveru. Ponechte prázdné pro vypnutí.<br>Příklad: /etc/ssl/webserver/dhparams.<br><br>Pokud soubor neexistuje, bude vytvořen automaticky s následujícím příkazem: <code>openssl dhparam -out /etc/ssl/webserver/dhparams. em 4096</code>. Před zadáním souboru je doporučeno vytvořit soubor, protože vytvoření trvá docela dlouho a blokuje cronjob.',\n\t\t],\n\t\t'errorlog_level' => [\n\t\t\t'title' => 'Úroveň protokolů chyb',\n\t\t\t'description' => 'Zadejte úroveň protokolu chyb. Výchozí hodnota je \"warn\" pro apache-user a \"error\" pro nginx-usery.',\n\t\t],\n\t\t'letsencryptecc' => [\n\t\t\t'title' => 'Vystavit certifikát ESC / certifikát ECDSA',\n\t\t\t'description' => 'Je-li nastaveno na platnou velikost klíče, vystavený certifikát bude používat ECC / ECDSA',\n\t\t],\n\t\t'froxloraliases' => [\n\t\t\t'title' => 'Aliasy domén pro froxlor vhost',\n\t\t\t'description' => 'Čárkami oddělený seznam domén, které mají být přidány jako serverový alias do froxlor vhost',\n\t\t],\n\t\t'default_sslvhostconf' => [\n\t\t\t'title' => 'Výchozí nastavení SSL vHost-serveru',\n\t\t\t'description' => 'Obsah tohoto pole bude přímo zahrnut do tohoto kontejneru s ip/portem. Můžete použít následující proměnné:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code> <code>{FPMSOCKET}</code> (pokud existuje)<br/> Upozornění: Kód nebude zkontrolován na výskyt chyb. Pokud obsahuje chyby, webový server nemusí znovu spustit!',\n\t\t],\n\t\t'includedefault_sslvhostconf' => 'Zahrnout ne-SSL vHost-nastavení v SSL-vHost',\n\t\t'apply_specialsettings_default' => 'Výchozí hodnota pro \"Použít speciální nastavení pro všechny subdomény (*.example.com)\" při úpravě domény',\n\t\t'apply_phpconfigs_default' => 'Výchozí hodnota pro nastavení \"Použít php konfiguraci na všechny subdomény\" při úpravě domény',\n\t\t'awstats' => [\n\t\t\t'logformat' => [\n\t\t\t\t'title' => 'Nastavení LogFormat',\n\t\t\t\t'description' => 'Pokud používáte vlastní logformat pro svůj webový server, musíte také změnit awstats LogFormat.<br/>Výchozí je 1. Pro více informací zkontrolujte dokumentaci <a target=\"_blank\" href=\"https://awstats.sourceforge.io/docs/awstats_config.html#LogFormat\">zde</a>.',\n\t\t\t],\n\t\t],\n\t\t'hide_incompatible_settings' => 'Skrýt nekompatibilní nastavení',\n\t\t'soaemail' => 'E-mailová adresa pro použití v SOA záznamech (výchozí adresa odesílatele z nastavení panelu, pokud je prázdná)',\n\t\t'imprint_url' => [\n\t\t\t'title' => 'Adresa URL k právním poznámkám / otisku',\n\t\t\t'description' => 'Zadejte adresu URL svých právních poznámek / stránek s otisky. Odkaz bude viditelný na přihlašovací obrazovce a v zápatí po přihlášení.',\n\t\t],\n\t\t'terms_url' => [\n\t\t\t'title' => 'URL k podmínkám použití',\n\t\t\t'description' => 'Zadejte adresu URL k podmínkám používání. Odkaz bude viditelný na přihlašovací obrazovce a v zápatí při přihlášení.',\n\t\t],\n\t\t'privacy_url' => [\n\t\t\t'title' => 'URL k zásadám ochrany osobních údajů',\n\t\t\t'description' => 'Zadejte adresu URL své stránky se zásadami ochrany osobních údajů / stránky s otisky. Odkaz bude viditelný na přihlašovací obrazovce a v zápatí po přihlášení.',\n\t\t],\n\t\t'logo_image_header' => [\n\t\t\t'title' => 'Obrázek loga (záhlaví)',\n\t\t\t'description' => 'Nahrajte vlastní obrázek loga, který se zobrazí v záhlaví po přihlášení (doporučená výška 30px)',\n\t\t],\n\t\t'logo_image_login' => [\n\t\t\t'title' => 'Obrázek loga (Přihlašovací obrazovka)',\n\t\t\t'description' => 'Nahrajte vlastní obrázek loga, který se zobrazí během přihlášení',\n\t\t],\n\t\t'logo_overridetheme' => [\n\t\t\t'title' => 'Přepíše logo definované v motivu pomocí \"Logo Image\" (hlavička a přihlašovací stránka, viz níže)',\n\t\t\t'description' => 'Toto musí být nastaveno na \"true\", pokud máte v úmyslu použít nahrané logo_custom. Alternativně můžete stále používat \"Logo custom. ng\" a \"logo_custom_login.png\".',\n\t\t],\n\t\t'logo_overridecustom' => [\n\t\t\t'title' => 'Přepsat vlastní logo (logo_custom.png a logo_custom_login.png) definované v šabloně \"Obrázek loga\" (hlavička a přihlášení, viz níže)',\n\t\t\t'description' => 'Nastavte na \"true\" pokud chcete ignorovat vlastní loga pro záhlaví a přihlášení a použít \"Logo Image\"',\n\t\t],\n\t\t'createstdsubdom_default' => [\n\t\t\t'title' => 'Předvolená hodnota pro \"Vytvořit standardní subdoménu\" při vytváření zákazníka',\n\t\t\t'description' => '',\n\t\t],\n\t\t'froxlorusergroup' => [\n\t\t\t'title' => 'Vlastní systémová skupina pro všechny zákazníky',\n\t\t\t'description' => 'Použití libnss-extrauser (systémového nastavení) je vyžadováno. Vytváření prázdných hodnot přeskočí nebo odstraní existující skupinu.',\n\t\t],\n\t\t'acmeshpath' => [\n\t\t\t'title' => 'Cesta k acme.sh',\n\t\t\t'description' => 'Nastavte na místo, kde je acme.sh nainstalován, včetně skriptu acme.sh<br>Výchozí je <b>/root/.acme.sh/acme.sh</b>',\n\t\t],\n\t\t'update_channel' => [\n\t\t\t'title' => 'froxlor aktualizační-kanál',\n\t\t\t'description' => 'Vyberte aktualizační kanál froxlor. Výchozí hodnota je \"stabilní\"',\n\t\t],\n\t\t'uc_stable' => 'stabilní',\n\t\t'uc_testing' => 'testovací',\n\t\t'uc_nightly' => 'nightly',\n\t\t'traffictool' => [\n\t\t\t'toolselect' => 'Analyzátor provozu',\n\t\t\t'webalizer' => 'Webalizer',\n\t\t\t'awstats' => 'AWStats',\n\t\t\t'goaccess' => 'goaccess'\n\t\t],\n\t\t'requires_reconfiguration' => 'Změna tohoto nastavení může vyžadovat změnu konfigurace následujících služeb:<br><strong>%s</strong>',\n\t\t'req_limit_per_interval' => [\n\t\t\t'title' => 'Počet HTTP požadavků na interval',\n\t\t\t'description' => 'Omezit počet HTTP požadavků na interval (viz níže) pro froxlor, výchozí hodnota je \"60\"',\n\t\t],\n\t\t'req_limit_interval' => [\n\t\t\t'title' => 'Interval omezení rychlosti',\n\t\t\t'description' => 'Zadejte čas v sekundách pro počet HTTP požadavků. Výchozí hodnota je \"60\"',\n\t\t],\n\t\t'option_requires_otp' => 'Toto nastavení vyžaduje ověření přes OTP',\n\t\t'panel_menu_collapsed' => [\n\t\t\t'title' => 'Sbalit sekce menu',\n\t\t\t'description' => 'Pokud dojde k deaktivaci, levé části menu budou vždy rozšířeny.',\n\t\t],\n\t\t'le_renew_services' => [\n\t\t\t'title' => 'Pro tyto služby použijte certifikát froxloru Let\\'s Encrypt',\n\t\t\t'description' => 'Pokud je nastavena hodnota none (nebo je níže uvedený příkaz renew-hook prázdný), nebudou u vybraných služeb provedeny žádné úpravy konfigurace týkající se ssl.<br><br>Příkaz reload-command pro vybrané služby by měl být přidán do příkazu renew-hook, jinak se změny konfigurace nebo obnovené certifikáty nemusí správně použít.',\n\t\t],\n\t\t'le_renew_hook' => [\n\t\t\t'title' => 'Příkaz renew-hook programu Let\\'s Encrypt',\n\t\t\t'description' => 'Nastavte příkaz, který restartuje výše vybrané služby, aby služba mohla obnovené certifikáty správně používat.',\n\t\t],\n\t],\n\t'spf' => [\n\t\t'use_spf' => [\n\t\t\t'title' => 'Aktivovat SPF pro domény?',\n\t\t\t'description' => 'Vyžaduje pro doménu určitou položku dns. Pokud nepoužíváte funkci nameserver, budete muset tyto položky ručně spravovat.',\n\t\t],\n\t\t'spf_entry' => 'SPF položka pro všechny domény',\n\t],\n\t'dmarc' => [\n\t\t'use_dmarc' => [\n\t\t\t'title' => 'Aktivovat DMARC pro domény?',\n\t\t\t'description' => 'Vyžaduje pro doménu určitou položku dns. Pokud nepoužíváte funkci nameserver, budete muset tyto položky ručně spravovat.',\n\t\t],\n\t\t'dmarc_entry' => 'DMARC záznam pro všechny domény',\n\t],\n\t'ssl_certificates' => [\n\t\t'certificate_for' => 'Certifikát pro',\n\t\t'valid_from' => 'Platné od',\n\t\t'valid_until' => 'Platné do',\n\t\t'issuer' => 'Vydavatel',\n\t],\n\t'success' => [\n\t\t'success' => 'Informace',\n\t\t'clickheretocontinue' => 'Klikněte zde pro pokračování',\n\t\t'settingssaved' => 'Nastavení bylo úspěšně uloženo.',\n\t\t'rebuildingconfigs' => 'Úspěšně vložené úkoly pro obnovení konfiguračních souborů',\n\t\t'domain_import_successfully' => 'Úspěšně importováno %s domén.',\n\t\t'exportscheduled' => 'Vaše exportní úloha byla naplánována. Počkejte prosím na její zpracování',\n\t\t'exportaborted' => 'Váš plánovaný export byl zrušen',\n\t\t'dns_record_added' => 'Záznam byl úspěšně přidán',\n\t\t'dns_record_deleted' => 'Záznam byl úspěšně odstraněn',\n\t\t'testmailsent' => 'Testovací e-mail byl úspěšně odeslán',\n\t\t'settingsimported' => 'Nastavení bylo úspěšně importováno',\n\t\t'sent_error_report' => 'Hlášení o chybách bylo úspěšně odesláno. Děkujeme za váš příspěvek.',\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Nevyřízené úlohy',\n\t\t'REBUILD_VHOST' => 'Obnovení konfigurace webserveru',\n\t\t'CREATE_HOME' => 'Přidávání nového zákazníka %s',\n\t\t'REBUILD_DNS' => 'Obnovení bind konfigurace',\n\t\t'CREATE_FTP' => 'Vytváření adresáře pro nového uživatele ftp-user',\n\t\t'DELETE_CUSTOMER_FILES' => 'Mazání zákaznických souborů %s',\n\t\t'noneoutstanding' => 'V současné době nejsou žádné nevyřízené úkoly pro froxlor',\n\t\t'DELETE_EMAIL_DATA' => 'Odstranit e-mailová data zákazníka.',\n\t\t'DELETE_FTP_DATA' => 'Odstranit data ftp účtu.',\n\t\t'REBUILD_RSPAMD' => 'Obnovení konfigurace antispamu.',\n\t\t'CREATE_QUOTA' => 'Nastavit kvótu na souborovém systému',\n\t\t'REBUILD_CRON' => 'Obnovení cron.d-souboru',\n\t\t'CREATE_CUSTOMER_DATADUMP' => 'Úloha pro export dat pro zákazníka %s',\n\t\t'DELETE_DOMAIN_PDNS' => 'Odstranit doménu %s z databáze PowerDNS',\n\t\t'DELETE_DOMAIN_SSL' => 'Odstranit ssl soubory domény %s',\n\t\t'UPDATE_LE_SERVICES' => 'Aktualizace systémových služeb pro Let\\'s Encrypt',\n\t],\n\t'terms' => 'Podmínky použití',\n\t'traffic' => [\n\t\t'month' => 'Měsíc',\n\t\t'day' => 'Den',\n\t\t'months' => [\n\t\t\t1 => 'Leden',\n\t\t\t2 => 'Únor',\n\t\t\t3 => 'Březen',\n\t\t\t4 => 'Duben',\n\t\t\t5 => 'Květen',\n\t\t\t6 => 'Červen',\n\t\t\t7 => 'Červenec',\n\t\t\t8 => 'Srpen',\n\t\t\t9 => 'Září',\n\t\t\t10 => 'Říjen',\n\t\t\t11 => 'Listopad',\n\t\t\t12 => 'Prosinec',\n\t\t\t'jan' => 'Led',\n\t\t\t'feb' => 'Ún',\n\t\t\t'mar' => 'Bře',\n\t\t\t'apr' => 'Dub',\n\t\t\t'may' => 'Kvě',\n\t\t\t'jun' => 'Čvn',\n\t\t\t'jul' => 'Čvc',\n\t\t\t'aug' => 'Srp',\n\t\t\t'sep' => 'Zář',\n\t\t\t'oct' => 'Říj',\n\t\t\t'nov' => 'Lis',\n\t\t\t'dec' => 'Pro',\n\t\t\t'total' => 'Celkem',\n\t\t],\n\t\t'mb' => 'Provoz',\n\t\t'sumtotal' => 'Celkový provoz',\n\t\t'sumhttp' => 'HTTP provoz',\n\t\t'sumftp' => 'FTP provoz',\n\t\t'summail' => 'Přenos e-mailů',\n\t\t'customer' => 'Zákazník',\n\t\t'domain' => 'Doména',\n\t\t'trafficoverview' => 'Shrnutí provozu',\n\t\t'bycustomers' => 'Provoz podle zákazníků',\n\t\t'details' => 'Podrobnosti',\n\t\t'http' => 'HTTP',\n\t\t'ftp' => 'FTP',\n\t\t'mail' => 'Mail',\n\t\t'nocustomers' => 'Potřebujete alespoň jednoho zákazníka pro zobrazení statistik provozu.',\n\t\t'top5customers' => 'Top 5 zákazníků',\n\t\t'nodata' => 'Pro daný rozsah nebyla nalezena žádná data.',\n\t\t'ranges' => [\n\t\t\t'last24h' => 'posledních 24 hodin',\n\t\t\t'last7d' => 'posledních 7 dní',\n\t\t\t'last30d' => 'posledních 30 dní',\n\t\t\t'cm' => 'Aktuální měsíc',\n\t\t\t'last3m' => 'poslední 3 měsíce',\n\t\t\t'last6m' => 'posledních 6 měsíců',\n\t\t\t'last12m' => 'posledních 12 měsíců',\n\t\t\t'cy' => 'Aktuální rok',\n\t\t],\n\t\t'byrange' => 'Určeno podle rozsahu',\n\t],\n\t'translator' => '',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'Byla nainstalována novější verze froxloru, ale ještě nebyla nastavena.<br />Pouze správce se může přihlásit a dokončit aktualizaci.',\n\t\t'update' => 'Aktualizace froxloru',\n\t\t'proceed' => 'Pokračovat',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'froxlor soubory byly aktualizovány na verzi <strong>%s</strong>. Nainstalovaná verze je <strong>%s</strong>.',\n\t\t\t'part_b' => '<br /><br />Zákazníci se nebudou moci přihlásit, dokud nebude aktualizace dokončena.<br /><strong>Pokračovat?</strong>',\n\t\t],\n\t\t'noupdatesavail' => 'Již máte nejnovější verzi %sfroxlor nainstalovanou.',\n\t\t'description' => 'Probíhá aktualizace databáze pro vaši instalaci froxlor',\n\t\t'uc_newinfo' => 'K dispozici je novější verze %s: \"%s\" (Vaše aktuální verze je: %s)',\n\t\t'notify_subject' => 'K dispozici je nová aktualizace',\n\t\t'dbupdate_required' => 'froxlor soubory byly aktualizovány, je vyžadována aktualizace databáze',\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => 'Vlastní poznámky',\n\t\t\t'description' => 'Nebojte se zde vložit jakékoliv poznámky, které chcete/potřebujete. Zobrazí se v přehledu správce/zákazníka pro příslušného uživatele.<br>Markdown je podporován, HTML bude odstraněno.',\n\t\t\t'show' => 'Zobrazit své poznámky na nástěnce uživatele',\n\t\t],\n\t\t'api_allowed' => [\n\t\t\t'title' => 'Povolit přístup k API',\n\t\t\t'description' => 'Pokud je povoleno v nastavení, může tento uživatel vytvořit API klíče a přistupovat k froxlor API',\n\t\t\t'notice' => 'Přístup k API není povolen pro váš účet.',\n\t\t],\n\t\t'shell_allowed' => [\n\t\t\t'title' => 'Povolit přístup k shellu',\n\t\t\t'description' => 'Pokud je tato možnost povolena v nastavení, může tento uživatel přiřadit přístup k shellu uživatelům ftp.',\n\t\t],\n\t\t'gui_access' => [\n\t\t\t'title' => 'Povolit přihlášení do WebUI',\n\t\t\t'description' => 'Pokud je zakázáno, uživatel se nemůže přihlásit do froxlor web-ui, ale všechny služby (web, ftp, mail, databáze, api-přístup atd.) budou fungovat normálně.',\n\t\t],\n\t],\n\t'install' => [\n\t\t'slogan' => 'panel pro správu serveru froxlor',\n\t\t'preflight' => 'Kontrola systému',\n\t\t'critical_error' => 'Kritická chyba',\n\t\t'suggestions' => 'Není vyžadováno, ale doporučuje se',\n\t\t'phpinfosuccess' => 'Váš systém běží s PHP %s',\n\t\t'suggestionsnote' => 'Neexistují žádné kritické chyby, které by bránily instalaci, ale pro optimální fungování prosím postupujte podle níže uvedených doporučení.',\n\t\t'phpinfowarn' => 'Váš systém běží na nižší verzi než PHP %s',\n\t\t'phpinfoupdate' => 'Aktualizujte vaši aktuální verzi PHP z %s na %s nebo vyšší',\n\t\t'start_installation' => 'Spustit instalaci',\n\t\t'check_again' => 'Znovu načtěte a zkontrolujte',\n\t\t'switchmode_advanced' => 'Zobrazit rozšířené možnosti',\n\t\t'switchmode_basic' => 'Skrýt rozšířené možnosti',\n\t\t'dependency_check' => [\n\t\t\t'title' => 'Vítejte ve froxloru',\n\t\t\t'description' => 'Zkontrolujeme závislost systému, abychom zajistili, že budou povolena všechna požadovaná php rozšíření a moduly, aby froxlor běžel správně.',\n\t\t],\n\t\t'database' => [\n\t\t\t'top' => 'Databáze',\n\t\t\t'title' => 'Vytvořit databázi a uživatele',\n\t\t\t'description' => 'froxlor vyžaduje databázi a navíc <a href=\"https://docs.froxlor.org/latest/general/installation/tarball.html#_3-create-privileged-database-user\" target=\"_blank\">privilegovaného uživatele</a>, aby mohl vytvářet uživatele a databáze (volba GRANT). Daná databáze a neprivilegovaný databázový uživatel bude vytvořen v tomto procesu. Oprávněný uživatel musí existovat.',\n\t\t\t'user' => 'Neoprávněný databázový uživatel',\n\t\t\t'dbname' => 'Název databáze',\n\t\t\t'force_create' => 'Zálohovat a přepsat databázi, pokud existuje?',\n\t\t],\n\t\t'admin' => [\n\t\t\t'top' => 'Admin uživatel',\n\t\t\t'title' => 'Pojďme vytvořit hlavního administrátora.',\n\t\t\t'description' => 'Tomuto uživateli budou udělena všechna oprávnění pro úpravu nastavení a přidávání/aktualizace/mazání zdrojů, jako jsou zákazníci, domény, atd.',\n\t\t\t'use_admin_email_as_sender' => 'Použijte výše uvedenou e-mailovou adresu jako adresu odesílatele. Pokud není zaškrtnuto, zadejte prosím níže uvedenou adresu odesílatele.',\n\t\t\t'use_autogenerated_email_as_sender' => 'Ponechte prázdné pro výchozí: admin@názevserveru',\n\t\t],\n\t\t'system' => [\n\t\t\t'top' => 'Systémové nastavení',\n\t\t\t'title' => 'Podrobnosti o Vašem serveru',\n\t\t\t'description' => 'Nastavte si své prostředí stejně jako data a možnosti související se serverem, aby se froxlor mohl dozvědět o vašem systému. Tyto hodnoty jsou klíčové pro konfiguraci a fungování systému.',\n\t\t\t'ipv4' => 'Primární IPv4 adresa (použije-li se)',\n\t\t\t'ipv6' => 'Primární IPv6 adresa (použije-li se)',\n\t\t\t'servername' => 'Název serveru (FQDN, žádná ip-adresa)',\n\t\t\t'phpbackend' => 'PHP backend',\n\t\t\t'activate_newsfeed' => 'Povolit oficiální novinky<br><small>(externí zdroj: https://inside.froxlor.org/news/)</small>',\n\t\t],\n\t\t'install' => [\n\t\t\t'top' => 'Dokončit nastavení',\n\t\t\t'title' => 'Poslední krok...',\n\t\t\t'description' => 'Níže uvedený příkaz stáhne, nainstaluje a nakonfiguruje požadované služby ve vašem systému podle údajů, které jste zadali v tomto instalačním procesu.<br><br><span class=\"text-danger\">Nezapomeňte spustit následující příkaz jako <b>root</b> na shell/terminál serveru a <b>uvědomte si</b>, že tento příkaz <b>přepíše</b> jakoukoli existující konfiguraci pro použité služby (budou vytvořeny zálohy)!.<br>Pokud nechcete přepsat žádné konfigurace, vyberte možnost <i>Nakonfiguruji služby ručně</i> v dolní části této stránky!</span>',\n\t\t\t'runcmd' => 'Spusťte následující příkaz pro dokončení instalace:',\n\t\t\t'manual_config' => 'Služby nakonfiguruji ručně, stačí mě přesměrovat k přihlášení',\n\t\t\t'waitforconfig' => 'Čekání na konfiguraci služeb...',\n\t\t],\n\t\t'errors' => [\n\t\t\t'wrong_ownership' => 'Ujistěte se, že soubory froxlor jsou vlastněny %s:%s',\n\t\t\t'missing_extensions' => 'Následující php rozšíření jsou vyžadována a nejsou nainstalována',\n\t\t\t'suggestedextensions' => 'Následující php rozšíření se nepodařilo najít, ale jsou doporučeny',\n\t\t\t'databaseexists' => 'Databáze již existuje, prosím nastavte možnost přepsání pro obnovu nebo zvolte jiný název',\n\t\t\t'unabletocreatedb' => 'Nelze vytvořit testovací databázi',\n\t\t\t'unabletodropdb' => 'Nelze zrušit zkušební databázi',\n\t\t\t'mysqlusernameexists' => 'Uživatel určený pro uživatele bez oprávnění již existuje. Použijte prosím jiné uživatelské jméno, nebo jej odstraňte.',\n\t\t\t'unabletocreateuser' => 'Nelze vytvořit testovacího uživatele',\n\t\t\t'unabletodropuser' => 'Nelze zrušit testovací uživatele',\n\t\t\t'unabletoflushprivs' => 'Zadaný privilegovaný uživatel nemůže vymazat oprávnění',\n\t\t\t'nov4andnov6ip' => 'Musí být zadána adresa IPv4 nebo IPv6',\n\t\t\t'servernameneedstobevalid' => 'Zadaný název serveru se nezdá být FQDN nebo hostname',\n\t\t\t'websrvuserdoesnotexist' => 'Zdá se, že uživatel webového serveru v systému neexistuje',\n\t\t\t'websrvgrpdoesnotexist' => 'Zdá se, že daná webserverová skupina v systému neexistuje',\n\t\t\t'notyetconfigured' => 'Zdá se, že služby ještě nebyly nakonfigurovány (úspěšně). Proveďte prosím příkaz níže nebo zaškrtněte políčko pro pozdější zpracování.',\n\t\t\t'mandatory_field_not_set' => 'Povinné pole \"%s\" není nastaveno!',\n\t\t\t'unexpected_database_error' => 'Došlo k neočekávané výjimce databáze. %s',\n\t\t\t'sql_import_failed' => 'Nepodařilo se importovat SQL data!',\n\t\t\t'unprivileged_sql_connection_failed' => 'Nepodařilo se inicializovat neprivilegované SQL připojení!',\n\t\t\t'privileged_sql_connection_failed' => 'Nepodařilo se inicializovat privilegované SQL připojení!',\n\t\t\t'mysqldump_backup_failed' => 'Nelze vytvořit zálohu databáze, došlo k chybě mysqldump.',\n\t\t\t'sql_backup_file_missing' => 'Nelze vytvořit zálohu databáze, záložní soubor neexistuje.',\n\t\t\t'backup_binary_missing' => 'Nelze vytvořit zálohu databáze, ujistěte se, že jste nainstalovali mysqldump.',\n\t\t\t'creating_configfile_failed' => 'Nelze vytvořit konfigurační soubory, nelze zapisovat do souboru.',\n\t\t\t'database_already_exiting' => 'Našli jsme databázi a nebylo možné ji přepsat!'\n\t\t]\n\t],\n\t'welcome' => [\n\t\t'title' => 'Vítejte ve froxloru!',\n\t\t'config_note' => 'Aby mohl froxlor správně komunikovat se zálohou, musíte ji nakonfigurovat.',\n\t\t'config_now' => 'Nastavit nyní'\n\t],\n];\n"
  },
  {
    "path": "lng/de.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'languages' => [\n\t\t'cz' => 'Tschechisch',\n\t\t'de' => 'Deutsch',\n\t\t'en' => 'Englisch',\n\t\t'fr' => 'Französisch',\n\t\t'hu' => 'Ungarisch',\n\t\t'it' => 'Italienisch',\n\t\t'nl' => 'Niederländisch',\n\t\t'pt' => 'Portugiesisch',\n\t\t'se' => 'Schwedisch',\n\t\t'sk' => 'Slowakisch',\n\t\t'es' => 'Spanisch',\n\t\t'ca' => 'Katalanisch',\n\t\t'zh_CN' => 'Chinesisch (Vereinfacht)',\n\t],\n\t'2fa' => [\n\t\t'2fa' => '2FA Optionen',\n\t\t'2fa_enabled' => 'Aktiviere Zwei-Faktor Authentifizierung (2FA)',\n\t\t'2fa_removed' => '2FA erfolgreich gelöscht',\n\t\t'2fa_added' => '2FA erfolgreich aktiviert<br><a class=\"alert-link\" href=\"%s?page=2fa\">2FA Details öffnen</a>',\n\t\t'2fa_add' => '2FA aktivieren',\n\t\t'2fa_delete' => '2FA deaktivieren',\n\t\t'2fa_verify' => 'Code verifizieren',\n\t\t'2fa_overview_desc' => 'Hier kann für das Konto eine Zwei-Faktor-Authentisierung aktiviert werden.<br><br>Es kann entweder eine Authenticator-App (time-based one-time password / TOTP) genutzt werden oder ein Einmalpasswort, welches nach erfolgreichem Login an die hinterlegte E-Mail Adresse gesendet wird.',\n\t\t'2fa_email_desc' => 'Das Konto ist eingerichtet, um Einmalpasswörter per E-Mail zu erhalten. Zum Deaktivieren, klicke auf \"2FA deaktivieren\"',\n\t\t'2fa_ga_desc' => 'Das Konto ist eingerichtet, um zeitbasierte Einmalpasswörter via Authenticator-App zu erhalten. Um die gewünschte Authenticator-App einzurichten, scanne bitte den untenstehenden QR-Code. Zum Deaktivieren, klicke auf \"2FA deaktivieren\"',\n\t\t'2fa_not_activated' => 'Zwei-Faktor Authentifizierung ist nicht aktiviert',\n\t\t'2fa_not_activated_for_user' => 'Zwei-Faktor Authentifizierung ist für den aktuellen Benutzer nicht aktiviert',\n\t\t'type_2fa' => '2FA Status',\n\t],\n\t'admin' => [\n\t\t'overview' => 'Übersicht',\n\t\t'ressourcedetails' => 'Verbrauchte Ressourcen',\n\t\t'systemdetails' => 'Systemdetails',\n\t\t'froxlordetails' => 'froxlor-Details',\n\t\t'installedversion' => 'Installierte Version',\n\t\t'latestversion' => 'Neueste Version',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'per Webservice abfragen - Hier klicken',\n\t\t\t'error' => 'Fehler bei Abfrage',\n\t\t],\n\t\t'resources' => 'Ressourcen',\n\t\t'customer' => 'Kunde',\n\t\t'customers' => 'Kunden',\n\t\t'customers_list_desc' => 'Kundenverwaltung',\n\t\t'customer_add' => 'Kunden anlegen',\n\t\t'customer_edit' => 'Kunden bearbeiten',\n\t\t'username_default_msg' => 'Leer lassen für automatische Benutzername-Vergabe',\n\t\t'password_default_msg' => 'Leer lassen für Passwortgenerierung',\n\t\t'domains' => 'Domains',\n\t\t'domain_add' => 'Domain anlegen',\n\t\t'domain_edit' => 'Domain bearbeiten',\n\t\t'subdomainforemail' => 'Subdomains als E-Mail-Domains erlauben',\n\t\t'admin' => 'Admin',\n\t\t'admins' => 'Admins',\n\t\t'admin_add' => 'Admin anlegen',\n\t\t'admin_edit' => 'Admin bearbeiten',\n\t\t'customers_see_all' => 'Zugriff auf Resourcen anderer Admins/Reseller?',\n\t\t'change_serversettings' => 'Kann Servereinstellungen bearbeiten?',\n\t\t'serversettings' => 'Einstellungen',\n\t\t'serversettings_desc' => 'Verwalte dein froxlor System',\n\t\t'rebuildconf' => 'Configs neu schreiben',\n\t\t'stdsubdomain' => 'Standardsubdomain',\n\t\t'stdsubdomain_add' => 'Standardsubdomain anlegen',\n\t\t'phpenabled' => 'PHP verfügbar',\n\t\t'deactivated' => 'Gesperrt',\n\t\t'deactivated_user' => 'Benutzer sperren',\n\t\t'sendpassword' => 'Passwort zusenden',\n\t\t'ownvhostsettings' => 'Eigene vHost-Einstellungen',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Konfiguration',\n\t\t\t'overview' => 'Übersicht',\n\t\t\t'wizard' => 'Assistent',\n\t\t\t'distribution' => 'Distribution',\n\t\t\t'service' => 'Service',\n\t\t\t'daemon' => 'Daemon',\n\t\t\t'http' => 'Webserver (HTTP)',\n\t\t\t'dns' => 'Nameserver (DNS)',\n\t\t\t'mail' => 'Mailserver (IMAP/POP3)',\n\t\t\t'smtp' => 'Mailserver (SMTP)',\n\t\t\t'ftp' => 'FTP-Server',\n\t\t\t'etc' => 'Sonstige (System)',\n\t\t\t'choosedistribution' => '-- Distribution wählen --',\n\t\t\t'chooseservice' => '-- Service wählen --',\n\t\t\t'choosedaemon' => '-- Daemon wählen --',\n\t\t\t'statistics' => 'Statistik',\n\t\t\t'compactoverview' => 'Kompakt-Übersicht',\n\t\t\t'legend' => '<h3>Sie konfigurieren nun einen Service/Daemon.</h3>',\n\t\t\t'commands' => '<span class=\"text-danger\">Kommandos:</span> Die angezeigten Befehle müssen als Benutzer root in einer Shell ausgeführt werden. Es kann auch problemlos der ganze Block kopiert und in die Shell eingefügt werden.',\n\t\t\t'files' => '<span class=\"text-danger\">Konfigurationsdateien:</span> Der Befehl direkt vor dem Textfeld sollte einen Editor mit der Zieldatei öffnen. Der Inhalt kann nun einfach kopiert und in den Editor eingefügt und die Datei gespeichert werden.<br><span class=\"text-danger\">Bitte beachten:</span> Das MySQL-Passwort wurde aus Sicherheitsgründen nicht ersetzt. Bitte ersetzen Sie \"FROXLOR_MYSQL_PASSWORD\" manuell oder nutzen Sie das folgende Formular, um es temporär auf dieser Seite zu setzen. Falls das Passwort vergessen wurde, findet es sich in der Datei \"lib/userdata.inc.php\".',\n\t\t\t'finishnote' => 'Parameter Datei erfolgreich erstellt. Folgende Befehle müssen als root ausgeführt werden:',\n\t\t\t'description' => 'System-Dienste konfigurieren',\n\t\t\t'minihowto' => 'Auf dieser Seite können die verschiedenen Konfigurationsvorlagen für jeden Dienst angezeigt werden, bestimmte Dienste bei Bedarf (erneut) konfiguriert oder die aktuelle Auswahl in eine JSON-Datei exportiert werden, um sie in den CLI-Skripten oder auf einem anderen Server zu verwenden.<br><br><b>Beachte</b>, dass die hervorgehobenen Dienste nicht die auf dem Server genutzten Dienste widerspiegeln, sondern Anforderungen/Empfehlungen aus den aktuellen Einstellungswerten zeigen.',\n\t\t\t'skipconfig' => 'Nicht (erneut) konfigurieren',\n\t\t\t'recommendednote' => 'Empfohlene/benötigte Dienste anhand der aktuellen Systemeinstellungen',\n\t\t\t'selectrecommended' => 'Empfohlene wählen',\n\t\t\t'downloadselected' => 'Auswahl exportieren',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'E-Mail-Vorlagen',\n\t\t\t'template_add' => 'Vorlage hinzufügen',\n\t\t\t'template_fileadd' => 'Dateivorlage hinzufügen',\n\t\t\t'template_edit' => 'Vorlage bearbeiten',\n\t\t\t'action' => 'Aktion',\n\t\t\t'email' => 'E-Mail- & Dateivorlagen',\n\t\t\t'subject' => 'Betreff',\n\t\t\t'mailbody' => 'Mailtext',\n\t\t\t'createcustomer' => 'Willkommensmail für neue Kunden',\n\t\t\t'pop_success' => 'Willkommensmail für neue E-Mail-Konten',\n\t\t\t'template_replace_vars' => 'Variablen, die in den Vorlagen ersetzt werden:',\n\t\t\t'SALUTATION' => 'Wird mit einer korrekten Anrede des Kunden ersetzt.',\n\t\t\t'FIRSTNAME' => 'Wird mit dem Vornamen des Kunden ersetzt.',\n\t\t\t'NAME' => 'Wird mit dem Namen des Kunden ersetzt.',\n\t\t\t'COMPANY' => 'Wird mit dem Firmennamen des Kunden ersetzt.',\n\t\t\t'USERNAME' => 'Wird mit dem Benutzernamen des neuen Kundenkontos ersetzt.',\n\t\t\t'PASSWORD' => 'Wird mit dem Passwort des neuen Kundenkontos ersetzt.',\n\t\t\t'EMAIL' => 'Wird mit der Adresse des neuen E-Mail-Kontos ersetzt.',\n\t\t\t'CUSTOMER_NO' => 'Wir mit der Kunden-Nummer ersetzt',\n\t\t\t'TRAFFIC' => 'Wird mit Traffic, der dem Kunden zugewiesen wurde, ersetzt.',\n\t\t\t'TRAFFICUSED' => 'Wird mit Traffic, der vom Kunden bereits verbraucht wurde, ersetzt.',\n\t\t\t'pop_success_alternative' => 'Willkommensmail für neue E-Mail-Konten für die alternative E-Mail-Adresse',\n\t\t\t'EMAIL_PASSWORD' => 'Wird mit dem Passwort des neuen POP3/IMAP Kontos ersetzt.',\n\t\t\t'index_html' => 'index Datei für neu erzeugte Kundenverzeichnisse',\n\t\t\t'unconfigured_html' => 'index Datei für unkonfigurierte/unbekannte Domains',\n\t\t\t'unconfigured_content_fallback' => 'Diese Domain muss über das froxlor-Serververwaltungspanel konfiguriert werden, da sie derzeit keinem Kunden zugewiesen ist.',\n\t\t\t'file_extension' => [\n\t\t\t\t'description' => 'Die Dateiendung für die index Datei muss zwischen 1 und 6 Zeichen lang sein und darf nur aus den Zeichen a-z, A-Z und 0-9 bestehen<br><br>Standard: html',\n\t\t\t\t'title' => 'Dateiendung für Datei Vorlage',\n\t\t\t],\n\t\t\t'SERVERNAME' => 'Wird mit dem Servernamen ersetzt.',\n\t\t\t'CUSTOMER' => 'Wird mit dem Loginnamen des Kunden ersetzt. Nur für \"index Datei für neu erzeugte Kundenverzeichnisse\".',\n\t\t\t'ADMIN' => 'Wird mit dem Loginnamen des Admins ersetzt. Nur für \"index Datei für neu erzeugte Kundenverzeichnisse\".',\n\t\t\t'CUSTOMER_EMAIL' => 'Wird mit der E-Mail-Adresse des Kunden ersetzt. Nur für \"index Datei für neu erzeugte Kundenverzeichnisse\".',\n\t\t\t'ADMIN_EMAIL' => 'Wird mit der E-Mail-Adresse des Admin ersetzt. Nur für \"index Datei für neu erzeugte Kundenverzeichnisse\".',\n\t\t\t'filetemplates' => 'Dateivorlagen',\n\t\t\t'filecontent' => 'Dateiinhalt',\n\t\t\t'new_database_by_customer' => 'Kunden-Benachrichtigungs nach Erstellung einer neuen Datenbank',\n\t\t\t'new_ftpaccount_by_customer' => 'Kunden-Benachrichtigung nach Erstellung eines neuen FTP-Benutzers',\n\t\t\t'newdatabase' => 'Benachrichtigungs-Mails für neue Datenbank',\n\t\t\t'newftpuser' => 'Benachrichtigungs-Mails für neuen FTP-Benutzer',\n\t\t\t'CUST_NAME' => 'Kundenname',\n\t\t\t'DB_NAME' => 'Datenbankname',\n\t\t\t'DB_PASS' => 'Datenbankpasswort',\n\t\t\t'DB_DESC' => 'Datenbankbeschreibung',\n\t\t\t'DB_SRV' => 'Datenbankserver',\n\t\t\t'PMA_URI' => 'URL zu phpMyAdmin (wenn angegeben)',\n\t\t\t'USR_NAME' => 'FTP-Benutzername',\n\t\t\t'USR_PASS' => 'FTP-Passwort',\n\t\t\t'USR_PATH' => 'FTP-Heimatverzeichnis (relativ zum Kunden-Heimatverzeichnis)',\n\t\t\t'forgotpwd' => 'Benachrichtigungs-Mails bei Zurücksetzen des Passworts',\n\t\t\t'password_reset' => 'Kunden-Benachrichtigung nach Zurücksetzen des Passworts',\n\t\t\t'trafficmaxpercent' => 'Hinweismail für Kunden, wenn sie die angegebenen Prozent des Traffics verbraucht haben',\n\t\t\t'MAX_PERCENT' => 'Wird mit dem Webspace/Traffic-Limit, welches dem Kunden zugewiesen wurde, ersetzt.',\n\t\t\t'USAGE_PERCENT' => 'Wird mit dem Webspace/Traffic, welcher vom Kunden bereits verbraucht wurde, ersetzt.',\n\t\t\t'diskmaxpercent' => 'Hinweismail für Kunden, wenn sie die angegebenen Prozent des Webspaces verbraucht haben',\n\t\t\t'DISKAVAILABLE' => 'Wird mit dem Webspace, der dem Kunden zugewiesen wurde, ersetzt.',\n\t\t\t'DISKUSED' => 'Wird mit dem Webspace, welcher vom Kunden bereits verbraucht wurde, ersetzt.',\n\t\t\t'LINK' => 'Wird mit dem Link zum Zurücksetzen des Passworts ersetzt.',\n\t\t\t'SERVER_HOSTNAME' => 'Wird mit dem System-Hostname (URL zu froxlor) ersetzt',\n\t\t\t'SERVER_IP' => 'Wird mit der Standard-System-IP-Adresse ersetzt',\n\t\t\t'SERVER_PORT' => 'Wird mit dem Standard-Port ersetzt',\n\t\t\t'DOMAINNAME' => 'Wird mit der Standardsubdomain des Kunden ersetzt (kann leer sein, wenn keine erstellt werden soll)',\n\t\t],\n\t\t'createzonefile' => 'DNS Zone für Domain erstellen',\n\t\t'custombindzone' => 'Eigene / manuelle Zone',\n\t\t'bindzonewarning' => 'Leer für Standardeinstellung.<br /><strong class=\"text-danger\">WARNUNG:</strong> Bei der Verwendung einer Zonendatei müssen alle benötigten Records aller Subdomains ebenfalls manuell verwaltet werden.',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IPs und Ports',\n\t\t\t'add' => 'IP-Adresse/Port hinzufügen',\n\t\t\t'edit' => 'IP-Adresse/Port bearbeiten',\n\t\t\t'ipandport' => 'IP-Adresse/Port',\n\t\t\t'ip' => 'IP-Adresse',\n\t\t\t'ipnote' => '<div id=\"ipnote\" class=\"invalid-feedback\">Hinweis: Obwohl private IP Adressen erlaubt sind, kann es bei manchen Features wie DNS zu ungewolltem Verhalten kommen.<br>Verwende private Adressen nur wenn du sicher bist.</div>',\n\t\t\t'port' => 'Port',\n\t\t\t'create_listen_statement' => 'Erstelle Listen-Eintrag',\n\t\t\t'create_namevirtualhost_statement' => 'Erstelle NameVirtualHost-Eintrag',\n\t\t\t'create_vhostcontainer' => 'Erstelle vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Erstelle ServerName-Eintrag im vHost-Container',\n\t\t\t'enable_ssl' => 'Ist dies ein SSL-Port?',\n\t\t\t'ssl_cert_file' => 'Pfad zum Zertifikat',\n\t\t\t'webserverdefaultconfig' => 'Webserver-Standard-Konfiguration',\n\t\t\t'webserverdomainconfig' => 'Webserver-Domain-Konfiguration',\n\t\t\t'webserverssldomainconfig' => 'Webserver-SSL-Konfiguration',\n\t\t\t'ssl_key_file' => 'Pfad zum SSL-Private-Key',\n\t\t\t'ssl_ca_file' => 'Pfad zum SSL-CA-Zertifikat (optional)',\n\t\t\t'default_vhostconf_domain' => 'Standard vHost-Einstellungen für jeden Domain-Container',\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'Pfad zu dem SSL-CertificateChainFile (optional)',\n\t\t\t\t'description' => 'Meist CA_Bundle, o. Ä. Dies ist das Feld, das gesetzt werden sollte, wenn ein gekauftes SSL-Zertifikat vorliegt.',\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Benutzerdefinierter Docroot (wenn leer, zeige auf froxlor)',\n\t\t\t\t'description' => 'Hier kann ein benutzerdefinierter Document-Root (der Zielordner für einen Zugriff) für diese IP/Port Kombination gesetzt werden.<br /><strong>ACHTUNG:</strong> Bitte überlege vorher, welchen Pfad du hier angibst!',\n\t\t\t],\n\t\t\t'ssl_paste_description' => 'Bitte den Inhalt der Zertifikatsdatei in das Textfeld kopieren.',\n\t\t\t'ssl_cert_file_content' => 'Inhalt des SSL-Zertifikats (Certificate)',\n\t\t\t'ssl_key_file_content' => 'Inhalt der Key-Datei (Private-Key)',\n\t\t\t'ssl_ca_file_content' => 'Inhalt der SSL-CA-Datei (optional)',\n\t\t\t'ssl_ca_file_content_desc' => '<br /><br />Client Authentifizierung, dieses Feld sollte nur gesetzt werden, wenn es wirklich gebraucht wird.',\n\t\t\t'ssl_cert_chainfile_content' => 'Inhalt des SSL-CertificateChainFile (optional)',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br /><br />Meist CA_Bundle, o. Ä. Dies ist das Feld, das gesetzt werden sollte, wenn ein gekauftes SSL-Zertifikat vorliegt.',\n\t\t\t'ssl_default_vhostconf_domain' => 'Standard SSL vHost-Einstellungen für jeden Domain-Container',\n\t\t],\n\t\t'memorylimitdisabled' => 'Deaktiviert',\n\t\t'valuemandatory' => 'Dieses Feld muss ausgefüllt werden.',\n\t\t'valuemandatorycompany' => 'Entweder \"Name\" und \"Vorname\" oder \"Firma\" muss ausgefüllt werden.',\n\t\t'serversoftware' => 'Webserver',\n\t\t'phpversion' => 'PHP-Version',\n\t\t'mysqlserverversion' => 'MySQL-Server-Version',\n\t\t'webserverinterface' => 'Webserver-Interface',\n\t\t'accountsettings' => 'Konteneinstellungen',\n\t\t'panelsettings' => 'Panel-Einstellungen',\n\t\t'systemsettings' => 'Systemeinstellungen',\n\t\t'webserversettings' => 'Webserver-Einstellungen',\n\t\t'mailserversettings' => 'Mailserver-Einstellungen',\n\t\t'nameserversettings' => 'Nameserver-Einstellungen',\n\t\t'updatecounters' => 'Ressourcenverbrauch',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Nie',\n\t\t\t'choosableno' => 'Wählbar, Standardwert: Nein',\n\t\t\t'choosableyes' => 'Wählbar, Standardwert: Ja',\n\t\t\t'always' => 'Immer',\n\t\t],\n\t\t'wipecleartextmailpwd' => 'Klartext-Passwörter leeren',\n\t\t'webalizersettings' => 'Webalizereinstellungen',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normal',\n\t\t\t'quiet' => 'Leise',\n\t\t\t'veryquiet' => 'Keine Ausgaben',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Es können derzeit keine Domains angelegt werden. Sie müssen zuerst einen Kunden anlegen',\n\t\t'loggersettings' => 'Log-Einstellungen',\n\t\t'logger' => [\n\t\t\t'normal' => 'Normal',\n\t\t\t'paranoid' => 'Paranoid',\n\t\t],\n\t\t'emaildomain' => 'E-Mail-Domain',\n\t\t'email_only' => 'Nur als E-Mail-Domain verwenden?',\n\t\t'wwwserveralias' => 'Einen \"www.\" ServerAlias hinzufügen',\n\t\t'subject' => 'Betreff',\n\t\t'recipient' => 'Empfänger',\n\t\t'message' => 'Rundmail senden',\n\t\t'text' => 'Nachricht',\n\t\t'sslsettings' => 'SSL-Einstellungen',\n\t\t'specialsettings_replacements' => 'Die folgenden Variablen können verwendet werden:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (wenn zutreffend)<br/>',\n\t\t'antispam_settings' => 'Antispam-Einstellungen',\n\t\t'caneditphpsettings' => 'Kann PHP-bezogene Domaineinstellungen vornehmen?',\n\t\t'allips' => 'Alle IP-Adressen',\n\t\t'awstatssettings' => 'AWstats-Einstellungen',\n\t\t'domain_dns_settings' => 'Domain-DNS-Einstellungen',\n\t\t'activated' => 'Aktiviert',\n\t\t'statisticsettings' => 'Statistik-Einstellungen',\n\t\t'or' => 'oder',\n\t\t'sysload' => 'System-Auslastung',\n\t\t'noloadavailable' => 'nicht verfügbar',\n\t\t'nouptimeavailable' => 'nicht verfügbar',\n\t\t'nosubject' => '(Kein Betreff)',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Zeige froxlor Version beim Login',\n\t\t\t'description' => 'Zeige froxlor Version in der Fußzeile der Loginseite',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Zeige froxlor Version in Fußzeile',\n\t\t\t'description' => 'Zeige froxlor Version in der Fußzeile aller anderen Seiten',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Grafik im Kopfbereich des Panels',\n\t\t\t'description' => 'Welche Grafik soll im Kopfbereich des Panels anstatt des froxlor Logos angezeigt werden?',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'PHP-Konfiguration',\n\t\t\t'description' => 'Kurzbeschreibung',\n\t\t\t'actions' => 'Aktionen',\n\t\t\t'activedomains' => 'In Verwendung für Domain(s)',\n\t\t\t'notused' => 'Konfiguration wird nicht verwendet',\n\t\t\t'editsettings' => 'PHP-Konfiguration bearbeiten',\n\t\t\t'addsettings' => 'PHP-Konfiguration erstellen',\n\t\t\t'viewsettings' => 'PHP-Konfiguration ansehen',\n\t\t\t'phpinisettings' => 'php.ini-Einstellungen',\n\t\t\t'addnew' => 'Neue PHP Konfiguration erstellen',\n\t\t\t'binary' => 'PHP-Binary',\n\t\t\t'fpmdesc' => 'PHP-FPM Config',\n\t\t\t'file_extensions' => 'Dateiendungen',\n\t\t\t'file_extensions_note' => '(ohne Punkt, durch Leerzeichen getrennt)',\n\t\t\t'enable_slowlog' => 'FPM-slowlog pro Domain aktivieren',\n\t\t\t'request_terminate_timeout' => 'request_terminate_timeout',\n\t\t\t'request_slowlog_timeout' => 'request_slowlog_timeout',\n\t\t\t'activephpconfigs' => 'In Verwendung für PHP-Konfiguration(en)',\n\t\t\t'pass_authorizationheader' => 'Übergeben von HTTP AUTH BASIC/DIGEST-Headern von Apache an PHP',\n\t\t],\n\t\t'misc' => 'Sonstiges',\n\t\t'fpmsettings' => [\n\t\t\t'addnew' => 'Neue PHP Version erstellen',\n\t\t\t'edit' => 'PHP version bearbeiten',\n\t\t],\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'Variablen, die in den Konfigurationen ersetzt werden',\n\t\t\t'pear_dir' => 'Wird mit dem globalen Wert für das Include Verzeichnis ersetzt.',\n\t\t\t'open_basedir_c' => 'Wird mit einem ; (Semikolon) ersetzt, um open_basedir auszukommentieren/deaktivieren, wenn eingestellt.',\n\t\t\t'open_basedir' => 'Wird mit der open_basedir-Einstellung der Domain ersetzt.',\n\t\t\t'tmp_dir' => 'Wird mit der Einstellung für das temporäre Verzeichnis der Domain ersetzt.',\n\t\t\t'open_basedir_global' => 'Wird mit der globalen Einstellung des Pfades ersetzt, der dem open_basedir hinzugefügt wird (siehe Webserver Einstellungen).',\n\t\t\t'customer_email' => 'Wird mit der E-Mail-Adresse des Kunden ersetzt, dem die Domain gehört.',\n\t\t\t'admin_email' => 'Wird mit der E-Mail-Adresse des Admins ersetzt, dem die Domain gehört.',\n\t\t\t'domain' => 'Wird mit der Domain ersetzt.',\n\t\t\t'customer' => 'Wird mit dem Loginnamen des Kunden ersetzt, dem die Domain gehört.',\n\t\t\t'admin' => 'Wird mit dem Loginnamen des Admins ersetzt, dem die Domain gehört.',\n\t\t\t'docroot' => 'Wird mit dem Heimatverzeichnis der Domain ersetzt.',\n\t\t\t'homedir' => 'Wird mit dem Heimatverzeichnis des Kunden ersetzt.',\n\t\t],\n\t\t'know_what_youre_doing' => 'Ändern Sie diese Einstellungen nur, wenn Sie wissen was Sie tun!',\n\t\t'security_settings' => 'Sicherheitseinstellungen',\n\t\t'expert_settings' => 'Experteneinstellungen!',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'PHP-Prozesse für diese Domain (leer für Standardwert)',\n\t\t],\n\t\t'phpserversettings' => 'PHP-Einstellungen',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Maximale PHP-Requests für diese Domain (leer für Standardwert)',\n\t\t],\n\t\t'webserver' => 'Webserver',\n\t\t'spfsettings' => 'Domain-SPF-Einstellungen',\n\t\t'specialsettingsforsubdomains' => 'Übernehme Einstellungen für alle Subdomains (*.beispiel.de)',\n\t\t'accountdata' => 'Benutzerdaten',\n\t\t'contactdata' => 'Kontaktdaten',\n\t\t'servicedata' => 'Dienstleistungsdaten',\n\t\t'newerversionavailable' => 'Eine neuere Version von froxlor wurde veröffentlicht.',\n\t\t'newerversiondetails' => 'Jetzt auf Version <b>%s</b> aktualisieren?<br/>(Aktuelle Version ist: %s)',\n\t\t'extractdownloadedzip' => 'Heruntergeladenes Archiv \"%s\" entpacken?',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Cronjob-Einstellungen',\n\t\t\t'add' => 'Cronjob hinzufügen',\n\t\t],\n\t\t'cronjob_edit' => 'Cronjob bearbeiten',\n\t\t'warning' => 'ACHTUNG - Wichtiger Hinweis!',\n\t\t'lastlogin_succ' => 'Letzte Anmeldung',\n\t\t'ftpserver' => 'FTP-Server',\n\t\t'ftpserversettings' => 'FTP-Server-Einstellungen',\n\t\t'webserver_user' => 'Benutzername Webserver',\n\t\t'webserver_group' => 'Gruppenname Webserver',\n\t\t'perlenabled' => 'Perl verfügbar',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Lokaler Benutzer für FCGID (froxlor Vhost)',\n\t\t'mod_fcgid_group' => 'Lokale Gruppe für FCGID (froxlor Vhost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[nicht angegeben]',\n\t\t'store_defaultindex' => 'Standard-Index-Datei im Kundenordner erstellen',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => 'Traffic',\n\t\t'traffic_sub' => 'Details der Traffic-Nutzung',\n\t\t'customertraffic' => 'Kunden',\n\t\t'assignedmax' => 'Zugewiesen / Max.',\n\t\t'usedmax' => 'Benutzt / Max.',\n\t\t'used' => 'Benutzt',\n\t\t'speciallogwarning' => '<div id=\"speciallogfilenote\" class=\"invalid-feedback\">ACHTUNG: Durch diese Einstellungen werden Sie alle bisherige Statistiken dieser Domain verlieren.</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => 'Eigene Log-Datei',\n\t\t\t'description' => 'Aktivieren Sie diese Option, um für diese Domain eine eigene Access-Log Datei zu erhalten',\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => 'Erlaube Bearbeiten der Domain',\n\t\t\t'desc' => 'Wenn ja, darf der Kunde verschiedene Einstellungen anpassen.<br />Wenn nein, darf nichts durch den Kunden geändert werden.',\n\t\t],\n\t\t'writeaccesslog' => [\n\t\t\t'title' => 'Zugriffslog schreiben',\n\t\t\t'description' => 'Aktiviere diese Option, um für diese Domain eine Access-Log Datei zu erhalten',\n\t\t],\n\t\t'writeerrorlog' => [\n\t\t\t'title' => 'Fehlerlog schreiben',\n\t\t\t'description' => 'Aktiviere diese Option, um für diese Domain eine Error-Log Datei zu erhalten',\n\t\t],\n\t\t'phpfpm.ininote' => 'Nicht alle gewünschten Werte können in der php-fpm-pool-Konfiguration verwendet werden.',\n\t\t'selectserveralias' => 'ServerAlias-Angabe für Domain',\n\t\t'selectserveralias_desc' => 'Wählen Sie hier, ob für diese Domain ein Wildcard-Eintrag (*.domain.tld), ein www-Alias (www.domain.tld) oder gar kein Alias angelegt werden soll.',\n\t\t'show_news_feed' => [\n\t\t\t'title' => 'Zeige Newsfeed im Admin-Dashboard',\n\t\t\t'description' => 'Aktivieren Sie diese Option, um das offizielle froxlor newsfeed (https://inside.froxlor.org/news/) auf deinem Dashboard anzuzeigen und verpasse keine wichtigen Informationen oder Release-Announcements.',\n\t\t],\n\t\t'cronsettings' => 'Cronjob-Einstellungen',\n\t\t'integritycheck' => 'Datenbankprüfung',\n\t\t'integrityname' => 'Name',\n\t\t'integrityresult' => 'Ergebnis',\n\t\t'integrityfix' => 'Probleme automatisch beheben',\n\t\t'customer_show_news_feed' => 'Zeige Newsfeed im Kunden-Dashboard',\n\t\t'customer_news_feed_url' => [\n\t\t\t'title' => 'Benutzerdefiniertes RSS-Feed',\n\t\t\t'description' => 'Hier kann ein eigenes RSS-Feed angegeben werden, welches den Kunden auf dem Dashboard angezeigt wird.<br /><small>Leerlassen um das offizielle froxlor Newsfeed (https://inside.froxlor.org/news/) zu verwenden.</small>',\n\t\t],\n\t\t'movetoadmin' => 'Kunde verschieben',\n\t\t'movecustomertoadmin' => [\n\t\t\t'title' => 'Verschiebe den Kunden zum angegebenen Admin/Reseller',\n\t\t\t'description' => 'Leerlassen für keine Änderung.<br />Wird der gewünschte Admin/Reseller hier nicht aufgelistet, hat er sein Kunden-Kontigent erreicht.',\n\t\t],\n\t\t'note' => 'Hinweis',\n\t\t'mod_fcgid_umask' => [\n\t\t\t'title' => 'Umask (Standard: 022)',\n\t\t],\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'SSL Zertifikat erstellen (Let\\'s Encrypt)',\n\t\t\t'description' => 'Holt ein kostenloses Zertifikat von <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. Das Zertifikat wird automatisch erstellt und verlängert.<br><strong class=\"text-danger\">ACHTUNG:</strong> Wenn Wildcards aktiviert sind, wird diese Option automatisch deaktiviert.',\n\t\t],\n\t\t'autoupdate' => 'Auto-Update',\n\t\t'dnsenabled' => 'Zugriff auf DNS Editor',\n\t\t'froxlorvhost' => 'froxlor VirtualHost Einstellungen',\n\t\t'hostname' => 'Hostname',\n\t\t'memory' => 'Speicherauslastung',\n\t\t'webserversettings_ssl' => 'Webserver SSL-Einstellungen',\n\t\t'domain_hsts_maxage' => [\n\t\t\t'title' => 'HTTP Strict Transport Security (HSTS)',\n\t\t\t'description' => '\"max-age\" Wert für den Strict-Transport-Security Header<br>Der Wert <i>0</i> deaktiviert HSTS für diese Domain. Meist wird der Wert <i>31536000</i> gerne genutzt (ein Jahr).',\n\t\t],\n\t\t'domain_hsts_incsub' => [\n\t\t\t'title' => 'Inkludiere HSTS für jede Subdomain',\n\t\t\t'description' => 'Die optionale \"includeSubDomains\" Direktive, wenn vorhanden, signalisiert dem UA, dass die HSTS Regel für diese Domain und auch jede Subdomain dieser gilt.',\n\t\t],\n\t\t'domain_hsts_preload' => [\n\t\t\t'title' => 'Füge Domain in die HSTS preload Liste hinzu',\n\t\t\t'description' => 'Wenn die Domain in die <a href=\"https://hstspreload.org/\" target=\"_blank\">HSTS preload Liste</a>, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktivieren Sie diese Einstellung.<br>Die preload-Direktive zu senden kann PERMANTENTE KONSEQUENZEN haben und dazu führen, dass Benutzer auf diese Domain und auch Subdomains nicht zugreifen können.<br>Beachten Sie die Details unter <a href=\"https://hstspreload.org/#removal\" target=\"_blank\">https://hstspreload.org/#removal</a> bevor ein Header mit \"preload\" gesendet wird.',\n\t\t],\n\t\t'domain_ocsp_stapling' => [\n\t\t\t'title' => 'OCSP stapling',\n\t\t\t'description' => 'Siehe <a target=\"_blank\" href=\"https://de.wikipedia.org/wiki/Online_Certificate_Status_Protocol_stapling\">Wikipedia</a> für eine ausführliche Beschreibung von OCSP-Stapling',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">WARNUNG:</strong> Nginx unterstützt OCSP-Stapling erst ab Version 1.3.7. Wenn Ihre Version älter ist, wird der Webserver bei aktiviertem OCSP-Stapling NICHT korrekt starten.',\n\t\t],\n\t\t'domain_http2' => [\n\t\t\t'title' => 'HTTP2 Unterstützung',\n\t\t\t'description' => 'Siehe <a target=\"_blank\" href=\"https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol#HTTP.2F2\">Wikipedia</a> für eine ausführliche Beschreibung von HTTP2',\n\t\t],\n\t\t'domain_http3' => [\n\t\t\t'title' => 'HTTP3 Unterstützung',\n\t\t\t'description' => 'Siehe <a target=\"_blank\" href=\"https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol#HTTP.2F3\">Wikipedia</a> für eine ausführliche Beschreibung von HTTP3',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">WARNUNG:</strong> Nginx unterstützt HTTP/3 erst ab Version 1.25.0 und dem SSL-Protokoll TLSv1.3. Wenn Ihre Version älter ist, wird der Webserver bei aktiviertem HTTP/3 NICHT korrekt starten.',\n\t\t],\n\t\t'testmail' => 'SMTP Test',\n\t\t'phpsettingsforsubdomains' => 'PHP-Config für alle Subdomains übernehmen:',\n\t\t'plans' => [\n\t\t\t'name' => 'Plan Name',\n\t\t\t'description' => 'Beschreibung',\n\t\t\t'last_update' => 'Zuletzt aktualisiert',\n\t\t\t'plans' => 'Hosting Pläne',\n\t\t\t'plan_details' => 'Plan Details',\n\t\t\t'add' => 'Neuen Plan anlegen',\n\t\t\t'edit' => 'Plan editieren',\n\t\t\t'use_plan' => 'Plan übernehmen',\n\t\t],\n\t\t'notryfiles' => [\n\t\t\t'title' => 'Keine generierte try_files Anweisung',\n\t\t\t'description' => 'Wählen Sie \"Ja\", wenn eine eigene try_files Direktive in den \"eigenen Vhost Einstellungen\" angegeben werden soll (z.B. nötig für manche Wordpress Plugins).',\n\t\t],\n\t\t'logviewenabled' => 'Zugriff auf access/error-Logdateien',\n\t\t'novhostcontainer' => '<br><br><small class=\"text-danger\">Keine der IPs und Ports hat die Option \"Erstelle vHost-Container\" aktiviert, einige Einstellungen sind daher nicht verfügbar.</small>',\n\t\t'ownsslvhostsettings' => 'Eigene SSL vHost-Einstellungen',\n\t\t'domain_override_tls' => 'Überschreibe System TLS Einstellungen',\n\t\t'domain_override_tls_addinfo' => '<br /><span class=\"text-danger\">Nur verwendet wenn \"Überschreibe System TLS Einstellungen\" auf \"Ja\" gestellt ist</span>',\n\t\t'domain_sslenabled' => 'Aktiviere Nutzung von SSL',\n\t\t'domain_honorcipherorder' => 'Bevorzuge die serverseitige Cipher Reihenfolge, Standardwert <strong>nein</strong>',\n\t\t'domain_sessiontickets' => 'Aktiviere TLS Sessiontickets (RFC 5077), Standardwert <strong>ja</strong>',\n\t\t'domain_sessionticketsenabled' => [\n\t\t\t'title' => 'Aktiviere Nutzung von TLS Sessiontickets systemweit',\n\t\t\t'description' => 'Standardwert <strong>yes</strong><br>Erfordert apache-2.4.11+ oder nginx-1.5.9+',\n\t\t],\n\t\t'domaindefaultalias' => 'Standard ServerAlias-Angabe für neue Domains',\n\t\t'smtpsettings' => 'SMTP Einstellungen',\n\t\t'smtptestaddr' => 'Test-Email senden an',\n\t\t'smtptestnote' => 'Bitte beachten: Die untenstehenden Werte reflektieren die aktuellen Einstellungen und können auch nur dort angepasst werden (siehe Link in der oberen rechten Ecke)',\n\t\t'smtptestsend' => 'Test-Email senden',\n\t\t'mysqlserver' => [\n\t\t\t'caption' => 'Beschreibung',\n\t\t\t'user' => 'Privilegierter Benutzer',\n\t\t\t'add' => 'MySQL Server hinzufügen',\n\t\t\t'edit' => 'MySQL Server bearbeiten',\n\t\t\t'password' => 'Passwort privilegierter Benutzer',\n\t\t\t'password_emptynochange' => 'Neues Passwort, leer für keine Änderung',\n\t\t\t'allowall' => [\n\t\t\t\t'title' => 'Nutzung für aktuelle Kunden automatisch erlauben',\n\t\t\t\t'description' => 'Ist diese Einstellung aktiv, wird die Verwendung dieses Datenbank-Servers automatisch allen aktuell existierenden Kunden-Accounts erlaubt. Diese Einstellung ist nicht permanent, kann aber mehrfach / nach Bedarf ausgeführt werden.',\n\t\t\t],\n\t\t\t'testconn' => 'Teste Verbindung beim Speichern',\n\t\t\t'ssl' => 'Verwende SSL für die Verbindung zum Datenbank-Server',\n\t\t\t'ssl_cert_file' => 'Dateipfad zur SSL certificate authority',\n\t\t\t'verify_ca' => 'Aktiviere SSL Zertifikats-Verifikation',\n\t\t],\n\t\t'settings_importfile' => 'Wähle Import-Datei',\n\t\t'documentation' => 'Dokumentation',\n\t\t'adminguide' => 'Admin Guide',\n\t\t'userguide' => 'User Guide',\n\t\t'apiguide' => 'API Guide',\n\t\t'domain_duplicate' => 'Domain duplizieren',\n\t\t'domain_duplicate_named' => '%s duplizieren',\n\t\t'emaildomainwarning' => '<div id=\"emaildomainnote\" class=\"invalid-feedback\">ACHTUNG: Durch die Änderung dieser Einstellung löschen Sie alle bestehenden E-Mail-Adressen und -Konten unwiderruflich.</div>',\n\t\t'webserver_serveradmin' => [\n\t\t\t'setting' => 'ServerAdmin Angabe',\n\t\t\t'customer' => 'Kunden E-Mail Adresse (standard)',\n\t\t\t'admin' => 'Admin E-Mail Adresse',\n\t\t\t'global' => 'Panel-Admin E-Mail Adresse',\n\t\t\t'none' => 'Keine ServerAdmin Angabe'\n\t\t]\n\t],\n\t'apikeys' => [\n\t\t'no_api_keys' => 'Keine API Keys gefunden',\n\t\t'key_add' => 'API Key hinzufügen',\n\t\t'apikey_removed' => 'Der API Key mit der ID #%s wurde erfolgreich gelöscht.',\n\t\t'apikey_added' => 'Der neue API Key wurde erfolgreich angelegt.',\n\t\t'clicktoview' => 'Details anzeigen',\n\t\t'allowed_from' => 'Erlaube Zugriff von',\n\t\t'allowed_from_help' => 'Komma getrennte Liste von IPs oder Netzen.<br>Standard ist leer (von überall erlaubt).',\n\t\t'valid_until' => 'Gültig bis',\n\t\t'valid_until_help' => 'Datum Gültigkeitsende, Format YYYY-MM-DDThh:mm',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Altes Passwort',\n\t\t'new_password' => 'Neues Passwort',\n\t\t'new_password_confirm' => 'Neues Passwort bestätigen',\n\t\t'new_password_ifnotempty' => 'Neues Passwort (leer für keine Änderung)',\n\t\t'also_change_ftp' => 'Auch Passwort des Haupt-FTP-Zugangs ändern',\n\t\t'also_change_stats' => ' Auch Passwort der Statistikseite ändern',\n\t\t'also_change_global_mysql' => 'Auch Passwort des globalen MySQL-Zugangs ändern',\n\t],\n\t'cron' => [\n\t\t'cronname' => 'Cronjob-Name',\n\t\t'lastrun' => 'Zuletzt gestartet',\n\t\t'interval' => 'Intervall',\n\t\t'isactive' => 'Aktiv',\n\t\t'description' => 'Beschreibung',\n\t\t'changewarning' => 'Änderungen an diesen Werten können einen negativen Effekt auf das Verhalten von froxlor und seinen automatisierten Aufgaben haben.<br />Ändern Sie hier bitte nur etwas, wenn Sie sich über die Folgen im Klaren sind.',\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'Keine Beschreibung angegeben',\n\t\t'cron_tasks' => 'Erstellen von Konfigurationsdateien',\n\t\t'cron_legacy' => 'Legacy (alter) Cronjob',\n\t\t'cron_traffic' => 'Traffic-Berechnung',\n\t\t'cron_usage_report' => 'Webspace- und Trafficreport',\n\t\t'cron_mailboxsize' => 'Berechnung der Mailbox-Größen',\n\t\t'cron_letsencrypt' => 'Aktualisierung der Let\\'s Encrypt Zertifikate',\n\t\t'cron_export' => 'Ausstehende Datenexporte erstellen',\n\t\t'cron_backup' => 'System- und Kunden-Sicherungen erstellen',\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'Cronjob-Einstellungen',\n\t\t'cronjobintervalv' => 'Laufzeit-Intervall Wert',\n\t\t'cronjobinterval' => 'Laufzeit-Intervall',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Bisher nicht gestartet',\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => 'Minuten',\n\t\t'hours' => 'Stunden',\n\t\t'days' => 'Tage',\n\t\t'weeks' => 'Wochen',\n\t\t'months' => 'Monate',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Heimatverzeichnis',\n\t\t'name' => 'Name',\n\t\t'firstname' => 'Vorname',\n\t\t'lastname' => 'Nachname',\n\t\t'company' => 'Firma',\n\t\t'nameorcompany_desc' => 'Entweder Vorname/Name oder Firma ist erforderlich',\n\t\t'street' => 'Straße',\n\t\t'zipcode' => 'PLZ',\n\t\t'city' => 'Ort',\n\t\t'phone' => 'Telefon',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'E-Mail-Adresse',\n\t\t'customernumber' => 'Kundennummer',\n\t\t'diskspace' => 'Webspace',\n\t\t'traffic' => 'Traffic',\n\t\t'mysqls' => 'MySQL-Datenbanken',\n\t\t'emails' => 'E-Mail-Adressen',\n\t\t'accounts' => 'E-Mail-Konten',\n\t\t'forwarders' => 'E-Mail-Weiterleitungen',\n\t\t'ftps' => 'FTP-Konten',\n\t\t'subdomains' => 'Subdomain(s)',\n\t\t'domains' => 'Domain(s)',\n\t\t'title' => 'Titel',\n\t\t'country' => 'Land',\n\t\t'email_quota' => 'E-Mail-Kontingent',\n\t\t'email_imap' => 'IMAP',\n\t\t'email_pop3' => 'POP3',\n\t\t'sendinfomail' => 'Daten per E-Mail an mich senden',\n\t\t'generated_pwd' => 'Passwortvorschlag',\n\t\t'usedmax' => 'Benutzt / Max.',\n\t\t'services' => 'Dienste',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'SSL Zertifikat erstellen (Let\\'s Encrypt)',\n\t\t\t'description' => 'Holt ein kostenloses Zertifikat von <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. Das Zertifikat wird automatisch erstellt und verlängert.',\n\t\t],\n\t\t'selectserveralias_addinfo' => 'Diese Option steht beim Bearbeiten der Domain zur Verfügung. Als Initial-Wert wird die Einstellung der Hauptdomain vererbt.',\n\t\t'total_diskspace' => 'Gesamtspeicherplatz',\n\t\t'mysqlserver' => 'Erlaubte MySQL-Server',\n\t],\n\t'diskquota' => 'Quota',\n\t'antispam' => [\n\t\t'config_file' => [\n\t\t\t'title' => 'Antispam Konfigurationsdatei',\n\t\t\t'description' => 'Pfad + Dateiname der Antispam-Regel Konfigurationsdatei',\n\t\t],\n\t\t'reload_command' => [\n\t\t\t'title' => 'Milter-Restart-Befehl',\n\t\t\t'description' => 'Wie lautet der Befehl zum Neustarten des rspamd-Dienstes?',\n\t\t],\n\t\t'activated' => [\n\t\t\t'title' => 'Antispam aktivieren?',\n\t\t\t'description' => 'Aktivieren, um rspamd als Antispam Dienst zu verwenden.',\n\t\t],\n\t\t'dkim_keylength' => [\n\t\t\t'title' => 'DKIM Schlüssel-Länge',\n\t\t\t'description' => 'Achtung: Änderungen sind nur für neue Schlüssel gültig.<br/><br/>Erfordert einen speziellen DNS Eintrag für die Domain. Wenn das Nameserver-Feature nicht genutzt wird, muss dieser Eintrag manuell verwaltet werden.',\n\t\t],\n\t\t'spam_tag_level' => [\n\t\t\t'title' => 'Spam Level',\n\t\t\t'description' => 'Erforderliche Punktzahl zum Markieren einer E-Mail als Spam<br/>Standard: 7.0'\n\t\t],\n\t\t'rewrite_subject' => [\n\t\t\t'title' => 'Betreff ändern',\n\t\t\t'description' => 'Dem E-Mail Betreff <strong>***SPAM***</strong> hinzufügen, sofern zutreffend',\n\t\t],\n\t\t'spam_kill_level' => [\n\t\t\t'title' => 'Ablehnungs Level',\n\t\t\t'description' => 'Erforderliche Punktzahl für das Ablehnen einer E-Mail<br/>Standard: 14.0'\n\t\t],\n\t\t'bypass_spam' => [\n\t\t\t'title' => 'Spamfilter umgehen',\n\t\t\t'description' => 'Aktivieren, um den Spamfilter für diese Adresse zu umgehen/deaktivieren.<br/>Standard: Nein'\n\t\t],\n\t\t'policy_greylist' => [\n\t\t\t'title' => 'Verwende greylisting',\n\t\t\t'description' => 'Eingehende E-Mails mittels <a href=\"https://de.wikipedia.org/wiki/Greylisting\" target=\"_blank\">Greylisting</a> schützen.<br/>Standard: Ja'\n\t\t],\n\t\t'required_spf_dns' => 'Erforderlicher SPF DNS Eintrag',\n\t\t'required_dmarc_dns' => 'Erforderlicher DMARC DNS Eintrag',\n\t\t'required_dkim_dns' => 'Erforderlicher DKIM DNS Eintrag',\n\t\t'default_select' => [\n\t\t\t'on_changeable' => 'Aktiviert, einstellbar',\n\t\t\t'off_changeable' => 'Deaktiviert, einstellbar',\n\t\t\t'on_unchangeable' => 'Aktiviert, nicht einstellbar',\n\t\t\t'off_unchangeable' => 'Deaktiviert, nicht einstellbar',\n\t\t],\n\t\t'default_bypass_spam' => [\n\t\t\t'title' => 'Standardwert: Spamfilter umgehen',\n\t\t\t'description' => 'Wählen, ob bei neuen E-Mail-Konten \"Spamfilter umgehen\" standardmäßig aktiviert ist und ob diese Einstellung vom Kunden angepasst werden kann.<br/>Standard: Deaktiviert, einstellbar'\n\t\t],\n\t\t'default_spam_rewrite_subject' => [\n\t\t\t'title' => 'Standardwert: Betreff ändern',\n\t\t\t'description' => 'Wählen, ob bei neuen E-Mail-Konten \"Betreff ändern\" standardmäßig aktiviert ist und ob diese Einstellung vom Kunden angepasst werden kann.<br/>Standard: Aktiviert, einstellbar'\n\t\t],\n\t\t'default_policy_greylist' => [\n\t\t\t'title' => 'Standardwert: Verwende greylisting',\n\t\t\t'description' => 'Wählen, ob bei neuen E-Mail-Konten \"Verwende greylisting\" standardmäßig aktiviert ist und ob diese Einstellung vom Kunden angepasst werden kann.<br/>Standard: Aktiviert, einstellbar'\n\t\t],\n\t],\n\t'dns' => [\n\t\t'destinationip' => 'Domain-IP-Adresse(n)',\n\t\t'standardip' => 'Server-Standard-IP-Adresse',\n\t\t'a_record' => 'A-Eintrag (IPv6 optional)',\n\t\t'cname_record' => 'CNAME-Eintrag',\n\t\t'mxrecords' => 'MX Einträge definieren',\n\t\t'standardmx' => 'Server Standard MX Eintrag',\n\t\t'mxconfig' => 'Eigene MX Einträge',\n\t\t'priority10' => 'Priorität 10',\n\t\t'priority20' => 'Priorität 20',\n\t\t'txtrecords' => 'TXT-Einträge definieren',\n\t\t'txtexample' => 'Beispiel (SPF-Eintrag):<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'howitworks' => 'Hier können DNS Einträge für die Domain verwaltet werden. Beachten Sie, dass froxlor automatisch NS/MX/A/AAAA Einträge generiert. Die benutzerdefinierten Einträge werden bevorzugt, nur fehlende Einträge werden automatisch generiert.',\n\t\t'nis2note' => [\n\t\t\t'title' => 'NIS2 Info',\n\t\t\t'content' => 'DNS-Hosting/Authoritative DNS Services können nach <strong>EU-NIS2</strong> als digitale Dienstleistung mit erhöhten Sicherheits- und Meldepflichten gelten. Bitte prüfe, ob dein Setup NIS2-betroffen ist und welche Maßnahmen erforderlich sind.'\n\t\t],\n\t],\n\t'dnseditor' => [\n\t\t'edit' => 'DNS editieren',\n\t\t'records' => 'Einträge',\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'OpenBasedir-Pfad',\n\t\t'inherited' => 'Gleich wie Elterndomain',\n\t\t'docroot' => 'Oben angegebener Pfad',\n\t\t'homedir' => 'Heimverzeichnis',\n\t\t'docparent' => 'Elternverzeichnis des oben angegebenen Pfads',\n\t],\n\t'domains' => [\n\t\t'description' => 'Hier können Sie (Sub-)Domains erstellen und deren Pfade ändern.<br />Nach jeder Änderung braucht das System etwas Zeit, um die Konfiguration neu einzulesen.',\n\t\t'domainsettings' => 'Domaineinstellungen',\n\t\t'domainname' => 'Domainname',\n\t\t'subdomain_add' => 'Subdomain anlegen',\n\t\t'subdomain_edit' => '(Sub-)Domain bearbeiten',\n\t\t'wildcarddomain' => 'Als Wildcarddomain eintragen?',\n\t\t'aliasdomain' => 'Alias für Domain',\n\t\t'noaliasdomain' => 'Keine Aliasdomain',\n\t\t'hasaliasdomains' => 'Hat Aliasdomain(s)',\n\t\t'statstics' => 'Statistiken',\n\t\t'isassigneddomain' => 'zugewiesene Domain',\n\t\t'add_date' => 'Zu froxlor hinzugefügt',\n\t\t'registration_date' => 'Registriert am',\n\t\t'topleveldomain' => 'Top-Level-Domain',\n\t\t'associated_with_domain' => 'Verbunden mit',\n\t\t'aliasdomains' => 'Aliasdomains',\n\t\t'redirectifpathisurl' => 'Redirect-Code (Standard: leer)',\n\t\t'redirectifpathisurlinfo' => 'Der Redirect-Code kann gewählt werden, wenn der eingegebene Pfad eine URL ist.<br/><strong class=\"text-danger\">HINWEIS:</strong> Änderungen werden nur wirksam wenn der Pfad eine URL ist.',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'IP-Adresse(n)',\n\t\t\t'description' => 'Definieren Sie eine oder mehrere IP-Adresse(n) für diese Domain.<br /><br /><div class=\"text-danger\">Hinweis: Die IP-Adressen können nicht geändert werden, sollte die Domain als <strong>Alias-Domain</strong> für eine andere Domain konfiguriert worden sein.</div>',\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'SSL-IP-Adresse(n)',\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'SSL-Weiterleitung',\n\t\t\t'description' => 'Diese Option erstellt für alle Nicht-SSL-vHosts eine Weiterleitung (Redirect), so dass alle Anfragen an den SSL-vHost übermittelt werden (z. B. würde eine Anfrage an <strong>http</strong>://domain.tld/ weitergeleitet werden zu <strong>https</strong>://domain.tld/).',\n\t\t],\n\t\t'serveraliasoption_wildcard' => 'Wildcard (*.domain.tld)',\n\t\t'serveraliasoption_www' => 'www (www.domain.tld)',\n\t\t'serveraliasoption_none' => 'Kein Alias',\n\t\t'domain_import' => 'Domains importieren',\n\t\t'import_separator' => 'Trennzeichen',\n\t\t'import_offset' => 'Versatz (offset)',\n\t\t'import_file' => 'CSV-Datei',\n\t\t'import_description' => 'Detaillierte Informationen über den Aufbau der Importdatei und einen erfolgreichen Import gibt es hier: <a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\" class=\"alert-link\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a> (englisch)',\n\t\t'ssl_redirect_temporarilydisabled' => '<br>Die SSL-Umleitung ist, während ein neues Let\\'s Encrypt - Zertifikat erstellt wird, temporär deaktiviert. Die Umleitung wird nach der Zertifikatserstellung wieder aktiviert.',\n\t\t'termination_date' => 'Kündigungsdatum',\n\t\t'termination_date_overview' => 'gekündigt zum ',\n\t\t'ssl_certificates' => 'SSL Zertifikate',\n\t\t'ssl_certificate_removed' => 'Das Zertifikat mit der ID #%s wurde erfolgreich gelöscht.',\n\t\t'ssl_certificate_error' => 'Fehler beim Lesen des Zertifikats für die Domain: %s',\n\t\t'no_ssl_certificates' => 'Es wurden keine SSL-Zertifikate gefunden',\n\t\t'isaliasdomainof' => 'Ist Aliasdomain für %s',\n\t\t'isbinddomain' => 'Erstelle DNS-Zone',\n\t\t'dkimenabled' => 'DKIM aktiviert',\n\t\t'openbasedirenabled' => 'Openbasedir Einschränkung',\n\t\t'hsts' => 'HSTS aktiviert',\n\t\t'aliasdomainid' => 'ID der Alias-Domain',\n\t\t'nodomainsassignedbyadmin' => 'Diesem Account wurde noch keine (aktive) Domain zugewiesen. Bitte kontaktiere deinen Administrator, wenn du der Meinung bist, das ist nicht korrekt.',\n\t\t'email_only' => 'Nur E-Mail',\n\t],\n\t'emails' => [\n\t\t'description' => 'Hier können Sie Ihre E-Mail-Adressen einrichten.<br />Ein Konto ist wie Ihr Briefkasten vor der Haustür. Wenn jemand eine E-Mail an Sie schreibt, wird diese in dieses Konto gelegt.<br /><br />Die Zugangsdaten lauten wie folgt: (Die Angaben in <i>kursiver</i> Schrift sind durch die jeweiligen Einträge zu ersetzen)<br /><br />Hostname: <b><i>Domainname</i></b><br />Benutzername: <b><i>Kontoname / E-Mail-Adresse</i></b><br />Passwort: <b><i>das gewählte Passwort</i></b>',\n\t\t'emailaddress' => 'E-Mail-Adresse',\n\t\t'emails_add' => 'E-Mail-Adresse anlegen',\n\t\t'emails_edit' => 'E-Mail-Adresse ändern',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => 'Als Catchall-Adresse definieren?',\n\t\t'account' => 'Konto',\n\t\t'account_add' => 'Konto anlegen',\n\t\t'account_delete' => 'Konto löschen',\n\t\t'from' => 'Von',\n\t\t'to' => 'Nach',\n\t\t'forwarders' => 'Weiterleitungen',\n\t\t'forwarder_add' => 'Weiterleitung hinzufügen',\n\t\t'alternative_emailaddress' => 'Alternative E-Mail-Adresse',\n\t\t'quota' => 'Kontingent',\n\t\t'noquota' => 'Kein Kontingent',\n\t\t'updatequota' => 'Update Kontingent',\n\t\t'quota_edit' => 'E-Mail-Kontingent ändern',\n\t\t'noemaildomainaddedyet' => 'Sie haben bisher noch keine (E-Mail-)Domain in Ihrem Konto.',\n\t\t'back_to_overview' => 'Zurück zur Domain-Übersicht',\n\t\t'accounts' => 'Konten',\n\t\t'emails' => 'Adressen',\n\t\t'senders' => 'Erlaubte Absendeadressen',\n\t\t'sender_add' => 'Absendeadressen hinzufügen',\n\t\t'foreign_sender' => 'Erlaubte (externe) Sender-Adresse',\n\t\t'allowed_sender_info' => 'Mit einer <strong>Erlaubten Absendeadresse</strong> erlauben Sie einem bestehenden Mailkonto, zusätzlich mit einer anderen Absenderadresse E-Mails zu versenden.<br><strong>Wichtig:</strong> Die hier eingetragene Adresse/Wildcard-Domain wird nicht automatisch ein Postfach – sie dient nur als zusätzliche, erlaubte Absenderkennungen.',\n\t],\n\t'error' => [\n\t\t'error' => 'Fehlermeldung',\n\t\t'directorymustexist' => 'Das Verzeichnis \"%s\" muss existieren. Legen Sie es bitte mit Ihrem FTP-Programm an.',\n\t\t'filemustexist' => 'Die Datei \"%s\" muss existieren.',\n\t\t'allresourcesused' => 'Sie haben bereits alle Ihnen zur Verfügung stehenden Ressourcen verbraucht.',\n\t\t'domains_cantdeletemaindomain' => 'Sie können keine zugewiesene Domain löschen. ',\n\t\t'domains_canteditdomain' => 'Sie können diese Domain nicht bearbeiten. Dies wurde durch den Admin verweigert.',\n\t\t'domains_cantdeletedomainwithemail' => 'Sie können keine Domain löschen, die noch als E-Mail-Domain verwendet wird. Löschen Sie zuerst alle E-Mail-Adressen dieser Domain.',\n\t\t'firstdeleteallsubdomains' => 'Sie müssen zuerst alle Subdomains löschen, bevor Sie eine Wildcarddomain anlegen können.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Sie haben bereits eine E-Mail-Adresse als Catchall für diese Domain definiert.',\n\t\t'ftp_cantdeletemainaccount' => 'Sie können Ihren Hauptaccount nicht löschen.',\n\t\t'login' => 'Die Kombination aus Benutzername und Passwort ist ungültig.',\n\t\t'login_blocked' => 'Dieser Account wurde aufgrund zu vieler Fehlversuche vorübergehend geschlossen.<br />Bitte versuchen Sie es in \"%s\" Sekunden erneut.',\n\t\t'notallreqfieldsorerrors' => 'Sie haben nicht alle Felder bzw. ein Feld mit fehlerhaften Angaben ausgefüllt.',\n\t\t'oldpasswordnotcorrect' => 'Das alte Passwort ist nicht korrekt.',\n\t\t'youcantallocatemorethanyouhave' => 'Sie können nicht mehr Ressourcen verteilen als Ihnen noch zur Verfügung stehen.',\n\t\t'mustbeurl' => 'Sie müssen eine vollständige URL angeben (z. B. http://domain.de/error404.htm).',\n\t\t'invalidpath' => 'Sie haben keine gültige URL angegeben (evtl. Probleme beim Verzeichnislisting?).',\n\t\t'stringisempty' => 'Fehlende Eingabe im Feld',\n\t\t'stringiswrong' => 'Falsche Eingabe im Feld',\n\t\t'newpasswordconfirmerror' => 'Das neue Passwort und dessen Bestätigung sind nicht identisch.',\n\t\t'mydomain' => '\\'Domain\\'',\n\t\t'mydocumentroot' => '\\'Documentroot\\'',\n\t\t'loginnameexists' => 'Der Login-Name \"%s\" existiert bereits.',\n\t\t'emailiswrong' => 'Die E-Mail-Adresse \"%s\" enthält ungültige Zeichen oder ist nicht vollständig.',\n\t\t'emailexists' => 'Die E-Mail-Adresse \"%s\" wird bereits von einem anderen Admin verwendet',\n\t\t'emailexistsanon' => 'Die E-Mail-Adresse \"%s\" wird bereits verwendet',\n\t\t'alternativeemailiswrong' => 'Die angegebene alternative E-Mail Adresse \"%s\", an welche die Zugangsdaten geschickt werden soll, scheint ungültig zu sein.',\n\t\t'loginnameiswrong' => 'Der Login-Name \"%s\" enthält ungültige Zeichen.',\n\t\t'loginnameiswrong2' => 'Der Login-Name enthält zu viele Zeichen, es sind maximal %s Zeichen erlaubt.',\n\t\t'userpathcombinationdupe' => 'Die Kombination aus Benutzername und Pfad existiert bereits.',\n\t\t'patherror' => 'Allgemeiner Fehler! Pfad darf nicht leer sein.',\n\t\t'errordocpathdupe' => 'Option für Pfad \"%s\" existiert bereits.',\n\t\t'adduserfirst' => 'Sie müssen zuerst einen Kunden anlegen.',\n\t\t'domainalreadyexists' => 'Die Domain \"%s\" wurde bereits einem Kunden zugeordnet.',\n\t\t'nolanguageselect' => 'Es wurde keine Sprache ausgewählt.',\n\t\t'nosubjectcreate' => 'Sie müssen einen Betreff angeben.',\n\t\t'nomailbodycreate' => 'Sie müssen einen E-Mail-Text eingeben.',\n\t\t'templatenotfound' => 'Vorlage wurde nicht gefunden.',\n\t\t'alltemplatesdefined' => 'Sie können keine weiteren Vorlagen anlegen, da bereits für alle Sprachen eine Vorlage existiert.',\n\t\t'wwwnotallowed' => 'Ihre Subdomain darf nicht \\'www\\' heißen.',\n\t\t'subdomainiswrong' => 'Die Subdomain \"%s\" enthält ungültige Zeichen.',\n\t\t'domaincantbeempty' => 'Der Domainname darf nicht leer sein.',\n\t\t'domainexistalready' => 'Die Domain \"%s\" existiert bereits.',\n\t\t'domainisaliasorothercustomer' => 'Die ausgewählte Aliasdomain ist entweder selbst eine Aliasdomain, hat nicht die gleiche IP/Port-Kombination oder gehört einem anderen Kunden.',\n\t\t'emailexistalready' => 'Die E-Mail-Adresse \"%s\" existiert bereits.',\n\t\t'maindomainnonexist' => 'Die Hauptdomain \"%s\" existiert nicht.',\n\t\t'maindomaindeactivated' => 'Die Hauptdomain \"%s\" ist deaktiviert.',\n\t\t'destinationnonexist' => 'Bitte geben Sie Ihre Weiterleitungsadresse im Feld \\'Nach\\' ein.',\n\t\t'destinationalreadyexistasmail' => 'Die Weiterleitung zu \"%s\" existiert bereits als aktive E-Mail-Adresse.',\n\t\t'destinationalreadyexist' => 'Es existiert bereits eine Weiterleitung nach \"%s\".',\n\t\t'destinationiswrong' => 'Die Weiterleitungsadresse \"%s\" enthält ungültige Zeichen oder ist nicht vollständig.',\n\t\t'dumpfoldercannotbedocroot' => 'Der Ordner für Daten-Export darf nicht das Heimatverzeichnis sein, wählen Sie einen Ordner unterhalb des Heimatverzeichnisses, z.B. /dumps',\n\t\t'templatelanguagecombodefined' => 'Die gewählte Kombination aus Sprache und Vorlage ist bereits definiert.',\n\t\t'templatelanguageinvalid' => 'Die gewählte Sprache existiert nicht',\n\t\t'ipstillhasdomains' => 'Die IP/Port-Kombination, die Sie löschen wollen, ist noch bei einer oder mehreren Domains eingetragen. Bitte ändern Sie die Domains vorher auf eine andere IP/Port-Kombination, um diese löschen zu können.',\n\t\t'cantdeletedefaultip' => 'Sie können die Standard-IP/Port-Kombination für Reseller nicht löschen. Bitte setzen Sie eine andere IP/Port-Kombination als Standard, um diese löschen zu können.',\n\t\t'cantdeletesystemip' => 'Sie können die letzte System-IP-Adresse nicht löschen. Entweder legen Sie eine neue IP/Port-Kombination an oder Sie ändern die System-IP-Adresse.',\n\t\t'myipaddress' => '\\'IP-Adresse\\'',\n\t\t'myport' => '\\'Port\\'',\n\t\t'myipdefault' => 'Sie müssen eine IP/Port-Kombination auswählen, die den Standard definieren soll.',\n\t\t'myipnotdouble' => 'Diese Kombination aus IP-Adresse und Port existiert bereits.',\n\t\t'cantchangesystemip' => 'Sie können die letzte System-IP-Adresse nicht löschen. Entweder legen Sie eine neue IP/Port-Kombination an oder Sie ändern die System-IP-Adresse.',\n\t\t'sessiontimeoutiswrong' => '\"Session-Timeout\" muss ein numerischer Wert sein.',\n\t\t'maxloginattemptsiswrong' => '\"Maximale Loginversuche\" muss ein numerischer Wert sein.',\n\t\t'deactivatetimiswrong' => '\"Länge der Deaktivierung\" muss numerisch sein.',\n\t\t'accountprefixiswrong' => 'Das \"Kundenpräfix\" ist falsch.',\n\t\t'mysqlprefixiswrong' => 'Das \"MySQL-Präfix\" ist falsch.',\n\t\t'ftpprefixiswrong' => 'Das \"FTP-Präfix\" ist falsch.',\n\t\t'ipiswrong' => 'Die \"IP-Adresse\" ist falsch. Bitte geben Sie eine gültige IP-Adresse an.',\n\t\t'vmailuidiswrong' => 'Die \"Mail-UID\" ist falsch. Es ist nur eine numerische UID erlaubt.',\n\t\t'vmailgidiswrong' => 'Die \"Mail-GID\" ist falsch. Es ist nur eine numerische GID erlaubt.',\n\t\t'adminmailiswrong' => 'Die \"Absenderadresse\" ist fehlerhaft. Bitte geben Sie eine gültige E-Mail-Adresse an.',\n\t\t'pagingiswrong' => 'Die \"Einträge pro Seite\"-Einstellung ist falsch. Es sind nur numerische Zeichen erlaubt.',\n\t\t'phpmyadminiswrong' => 'Die \"phpMyAdmin-URL\" ist keine gültige URL.',\n\t\t'webmailiswrong' => 'Die \"Webmail-URL\" ist keine gültige URL.',\n\t\t'webftpiswrong' => 'Die \"WebFTP-URL\" ist keine gültige URL.',\n\t\t'stringformaterror' => 'Der Wert des Feldes \"%s\" hat nicht das erwartete Format.',\n\t\t'loginnameisusingprefix' => 'Sie können keinen Account anlegen, der mit \"%s\" beginnt, da dieser Prefix für die automatische Namensvergabe eingestellt ist. Bitte wählen Sie einen anderen Accountnamen.',\n\t\t'loginnameissystemaccount' => 'Der Account \"%s\" existiert bereits auf dem System und kann daher nicht verwendet werden. Bitte wählen Sie einen anderen Accountnamen.',\n\t\t'loginnameisreservedname' => 'Der Account-Name \"%s\" ist systemseitig reserviert und kann nicht verwenden werden.',\n\t\t'youcantdeleteyourself' => 'Aus Sicherheitsgründen können Sie sich nicht selbst löschen.',\n\t\t'youcanteditallfieldsofyourself' => 'Hinweis: Aus Sicherheitsgründen können Sie nicht alle Felder Ihres eigenen Accounts bearbeiten.',\n\t\t'documentrootexists' => 'Es existiert noch ein Verzeichnis \"%s\" für diesen Kunden. Bitte löschen Sie dieses vorher.',\n\t\t'norepymailiswrong' => 'Die \"Noreply-Adresse\" ist ungültig. Nur eine valide E-Mail Adresse kann akzeptiert werden.',\n\t\t'logerror' => 'Log-Fehler: \"%s\"',\n\t\t'nomessagetosend' => 'Keine Nachricht angegeben',\n\t\t'norecipientsgiven' => 'Keine Empfänger angegeben',\n\t\t'errorsendingmail' => 'Das Versenden der Nachricht an \"%s\" schlug fehl.',\n\t\t'errorsendingmailpub' => 'Das Versenden der Nachricht an die angegebene E-Mail Adresse schlug fehl.',\n\t\t'cannotreaddir' => 'Der Ordner \"%s\" kann nicht gelesen werden',\n\t\t'invalidip' => 'Ungültige IP-Adresse: \"%s\"',\n\t\t'invalidmysqlhost' => 'Ungültige MySQL-Host-Adresse: \"%s\"',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'Webalizer und AWstats können nicht zur gleichen Zeit aktiviert werden, bitte wählen Sie eines aus.',\n\t\t'cannotwritetologfile' => 'Logdatei \"%s\" konnte nicht für Schreiboperationen geöffnet werden.',\n\t\t'vmailquotawrong' => 'Die Kontingent-Größe muss positiv sein.',\n\t\t'allocatetoomuchquota' => 'Sie versuchen \"%s\" MB Kontingent zu zuweisen, haben aber nicht genug übrig.',\n\t\t'missingfields' => 'Es wurden nicht alle Felder augefüllt.',\n\t\t'requiredfield' => 'Dieses Feld ist ein Pflichtfeld.',\n\t\t'accountnotexisting' => 'Der angegebene E-Mail-Account existiert nicht.',\n\t\t'nopermissionsorinvalidid' => 'Entweder fehlen Ihnen die nötigen Rechte diese Einstellung zu ändern oder es wurde eine ungültige ID übergeben',\n\t\t'phpsettingidwrong' => 'Eine PHP-Konfiguration mit dieser ID existiert nicht',\n\t\t'descriptioninvalid' => 'Der Beschreibungstext ist zu kurz, zu lang oder enthält ungültige Zeichen',\n\t\t'info' => 'Info',\n\t\t'filecontentnotset' => 'Diese Datei darf nicht leer sein!',\n\t\t'customerdoesntexist' => 'Der ausgewählte Kunde existiert nicht.',\n\t\t'admindoesntexist' => 'Der ausgewählte Admin existiert nicht.',\n\t\t'ipportdoesntexist' => 'Die gewählte IP/Port-Kombination existiert nicht.',\n\t\t'hiddenfieldvaluechanged' => 'Der Wert des verborgenen Feldes \"%s\" hat sich während dem Ändern der Einstellungen geändert.<br /><br />Dies ist im Grunde kein schwerwiegendes Problem, allerdings konnten so die Einstellungen nicht gespeichert werden.',\n\t\t'notrequiredpasswordlength' => 'Das Passwort ist zu kurz. Bitte geben Sie mindestens \"%s\" Zeichen an.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Hoppla, ein Feld, das als Option in der Konfigurationsübersicht angezeigt werden soll, hat nicht den erwarteten Wert. Sie können den Entwicklern die Schuld geben. Dies sollte nicht passieren!',\n\t\t'usernamealreadyexists' => 'Der Benutzername \"%s\" existiert bereits.',\n\t\t'plausibilitychecknotunderstood' => 'Die Antwort des Plausibilitätschecks wurde nicht verstanden',\n\t\t'errorwhensaving' => 'Bei dem Speichern des Feldes \"%s\" trat ein Fehler auf',\n\t\t'pathmaynotcontaincolon' => 'Der eingegebene Pfad sollte keinen Doppelpunkt (\":\") enthalten. Bitte geben Sie einen korrekten Wert für den Pfad ein.',\n\t\t'invaliddocumentrooturl' => 'Die URL, die Sie für den Pfad eingegeben haben, ist ungültig. Bitte geben Sie eine korrekte URL oder einen Unix-Pfad ein.',\n\t\t'notrequiredpasswordcomplexity' => 'Die vorgegebene Passwort-Komplexität wurde nicht erfüllt.<br />Bitte kontaktieren Sie Ihren Administrator, wenn Sie Fragen zur Komplexitäts-Vorgabe haben.',\n\t\t'invaliderrordocumentvalue' => 'Der angegebene Wert für das Fehlederdokument ist keine gültige Datei, URL oder Text-Zeile.',\n\t\t'intvaluetoolow' => 'Die angegebene Zahl ist zu klein (Feld \"%s\")',\n\t\t'intvaluetoohigh' => 'Die angegebene Zahl ist zu groß (Feld \"%s\")',\n\t\t'phpfpmstillenabled' => 'PHP-FPM ist derzeit aktiviert. Bitte deaktivieren Sie es, um FCGID zu aktivieren',\n\t\t'fcgidstillenabled' => 'FCGID ist derzeit aktiviert. Bitte deaktivieren Sie es, um PHP-FPM zu aktivieren',\n\t\t'domains_cantdeletedomainwithaliases' => 'Sie können keine Domain löschen, die noch von Alias-Domains verwendet wird. Löschen Sie zuerst alle Alias-Domains dieser Domain.',\n\t\t'user_banned' => 'Ihr Benutzerkonto wurde gesperrt. Bitte kontaktieren Sie Ihren Administrator für weitere Informationen.',\n\t\t'admin_domain_emailsystemhostname' => 'Der System-Hostname kann nicht als Kundendomain verwendet werden.',\n\t\t'session_timeout' => 'Wert zu niedrig',\n\t\t'session_timeout_desc' => 'Der Wert der Session-Timeout sollte nicht unter einer Minute liegen.',\n\t\t'invalidhostname' => 'Hostname muss eine gültige Domain sein. Er darf weder leer sein noch nur aus Leerzeichen bestehen',\n\t\t'operationnotpermitted' => 'Diese Aktion ist nicht erlaubt!',\n\t\t'featureisdisabled' => 'Die Funktion \"%s\" wurde deaktiviert. Kontaktieren Sie bitte Ihren Dienstleister.',\n\t\t'usercurrentlydeactivated' => 'Der Benutzer \"%s\" ist derzeit deaktiviert',\n\t\t'setlessthanalreadyused' => 'Es können nicht weniger Resourcen von \"%s\" gesetzt werden, als der Benutzer bereits vergeben hat<br />',\n\t\t'stringmustntbeempty' => 'Der Wert für das Feld \"%s\" darf nicht leer sein',\n\t\t'sslcertificateismissingprivatekey' => 'Für das Zertifikat muss eine Key-Datei (Private-Key) angegeben werden.',\n\t\t'sslcertificatewrongdomain' => 'Das angegebene Zertifikat gilt nicht für die gewählte Domain.',\n\t\t'sslcertificateinvalidcert' => 'Der angegebene Zertifikatsinhalt scheint kein gültiges Zertifikat zu sein.',\n\t\t'sslcertificateinvalidcertkeypair' => 'Der angegebene Key (Private-Key) gehört nicht zum angegebenen Zertifikat.',\n\t\t'sslcertificateinvalidca' => 'Das angegebene CA-Zertifikat scheint nicht gültig zu sein.',\n\t\t'sslcertificateinvalidchain' => 'Das angegebene CertificateChainFile scheint nicht gültig zu sein.',\n\t\t'givendirnotallowed' => 'Das angegebene Verzeichnis im Feld %s ist nicht erlaubt.',\n\t\t'sslredirectonlypossiblewithsslipport' => 'Die Nutzung von Let\\'s Encrypt ist nur möglich, wenn die Domain mindestens eine IP/Port - Kombination mit aktiviertem SSL zugewiesen hat.',\n\t\t'fcgidstillenableddeadlock' => 'FCGID ist derzeit aktiviert.<br />Bitte deaktiviere es, um einen anderen Webserver als Apache2 auswählen zu können.',\n\t\t'send_report_title' => 'Fehler melden',\n\t\t'send_report_desc' => 'Danke, dass Sie uns diesen Fehler melden und damit helfen froxlor zu verbessern.<br />Folgender Bericht wird per Mail an das froxlor Entwickler Team gesendet.',\n\t\t'send_report' => 'Fehlerbericht senden',\n\t\t'send_report_error' => 'Fehler beim Senden des Berichts: <br />%s',\n\t\t'notallowedtouseaccounts' => 'Ihrem Konto ist die Nutzung von IMAP/POP3 nicht erlaubt, daher können keine E-Mail-Konten angelegt werden',\n\t\t'cannotdeletehostnamephpconfig' => 'Diese PHP-Konfiguration ist dem froxlor-Vhost zugewiesen und kann daher nicht gelöscht werden.',\n\t\t'cannotdeletedefaultphpconfig' => 'Diese PHP-Konfiguration ist als Standard hinterlegt und kann daher nicht gelöscht werden.',\n\t\t'passwordshouldnotbeusername' => 'Das Passwort sollte nicht mit dem Benutzernamen übereinstimmen.',\n\t\t'no_phpinfo' => 'Entschuldigung, es ist nicht möglich die phpinfo() auszulesen.',\n\t\t'moveofcustomerfailed' => 'Das Verschieben des Kunden ist fehlgeschlagen. Alle übrigen Änderungen wurden durchgeführt und gespeichert.<br><br>Fehlermeldung: %s',\n\t\t'domain_import_error' => 'Der folgende Fehler trat beim Importieren der Domains auf: %s',\n\t\t'fcgidandphpfpmnogoodtogether' => 'FCGID und PHP-FPM können nicht gleichzeitig aktiviert werden.',\n\t\t'no_apcuinfo' => 'Keine APCu Cache Informationen verfügbar. APCu scheint nicht installiert zu sein.',\n\t\t'no_opcacheinfo' => 'Keine OPCache Informationen verfügbar. OPCache scheint nicht installiert zu sein.',\n\t\t'inactive_opcacheinfo' => 'OPCache ist installiert, aber nicht aktiviert.',\n\t\t'nowildcardwithletsencrypt' => 'Let\\'s Encrypt kann mittels ACME Wildcard-Domains nur via DNS validieren, sorry. Bitte den ServerAlias auf WWW setzen oder deaktivieren',\n\t\t'customized_version' => 'Es scheint als wäre die froxlor Installation angepasst worden. Kein Support, sorry.',\n\t\t'autoupdate_0' => 'Unbekannter Fehler',\n\t\t'autoupdate_1' => 'PHP Einstellung allow_url_fopen ist deaktiviert. Autoupdate benötigt diese Option, bitte in der php.ini aktivieren.',\n\t\t'autoupdate_2' => 'PHP zip Erweiterung nicht gefunden, bitte prüfen, ob diese installiert und aktiviert ist.',\n\t\t'autoupdate_4' => 'Das froxlor Archiv konnte nicht auf der Festplatte gespeichert werden :(',\n\t\t'autoupdate_5' => 'version.froxlor.org gab ungültige Werte zurück :(',\n\t\t'autoupdate_6' => 'Woops, keine (gültige) Version angegeben für den Download :(',\n\t\t'autoupdate_7' => 'Das heruntergeladene Archiv konnte nicht gefunden werden :(',\n\t\t'autoupdate_8' => 'Das Archiv konnte nicht entpackt werden :(',\n\t\t'autoupdate_9' => 'Die heruntergeladene Datei konnte nicht verifiziert werden. Bitte erneut versuchen zu aktualisieren.',\n\t\t'autoupdate_10' => 'Minimum unterstützte Version von PHP ist 7.4.0',\n\t\t'autoupdate_11' => 'Webupdate ist deaktiviert',\n\t\t'mailaccistobedeleted' => 'Ein vorheriges Konto mit dem gleichen Namen (%s) wird aktuell noch gelöscht und kann daher derzeit nicht angelegt werden',\n\t\t'customerhasongoingexportjob' => 'Es gibt noch einen austehenden Daten-Export. Bitte haben Sie etwas Geduld.',\n\t\t'exportfunctionnotenabled' => 'Die Datenexport-Funktion is nicht aktiviert',\n\t\t'dns_domain_nodns' => 'DNS ist für diese Domain nicht aktiviert',\n\t\t'dns_content_empty' => 'Keinen Inhalt angegeben',\n\t\t'dns_content_invalid' => 'DNS Eintrag ungültig',\n\t\t'dns_arec_noipv4' => 'Keine gültige IP-Adresse für A-Eintrag angegeben',\n\t\t'dns_aaaarec_noipv6' => 'Keine gültige IP-Adresse für AAAA-Eintrag angegeben',\n\t\t'dns_mx_prioempty' => 'Ungültige MX Priorität angegeben',\n\t\t'dns_mx_needdom' => 'Der Wert des MX Eintrags muss ein gültiger Domainname sein',\n\t\t'dns_mx_noalias' => 'Der MX Eintrag darf kein CNAME Eintrag sein.',\n\t\t'dns_cname_invaliddom' => 'Ungültiger Domain-Name für CNAME Eintrag',\n\t\t'dns_cname_nomorerr' => 'Es existiert bereits ein Eintrag mit dem gleichen Namen. Dieser Eintrag kann daher nicht für CNAME genutzt werden.',\n\t\t'dns_other_nomorerr' => 'Es existiert bereits ein CNAME Eintrag mit dem gleichen Namen. Dieser Eintrag kann daher nicht für einen anderen genutzt werden.',\n\t\t'dns_ns_invaliddom' => 'Ungültiger Domain-Name für NS Eintrag',\n\t\t'dns_srv_prioempty' => 'Ungültige SRV Priorität angegeben',\n\t\t'dns_srv_invalidcontent' => 'Ungültiger Wert des SRV Eintrags, dieser muss aus den Feldern weight, port und target, bestehen. Bsp.: 5 5060 sipserver.example.com.',\n\t\t'dns_srv_needdom' => 'Der Wert des SRV Eintrags muss ein gültiger Domainname sein',\n\t\t'dns_srv_noalias' => 'Der SRV Eintrag darf kein CNAME Eintrag sein.',\n\t\t'dns_duplicate_entry' => 'Eintrag existiert bereits',\n\t\t'dns_notfoundorallowed' => 'Domain nicht gefunden oder keine Berechtigung',\n\t\t'dns_loc_invalid' => 'Ungültiger LOC Eintrag',\n\t\t'dns_rp_invalid' => 'Ungültiger RP Eintrag',\n\t\t'dns_sshfp_invalid' => 'Ungültiger SSHFP Eintrag',\n\t\t'dns_tlsa_invalid' => 'Ungültiger TLSA Eintrag',\n\t\t'dns_naptr_invalid' => 'Ungültiger NAPTR Eintrag',\n\t\t'domain_nopunycode' => 'Die Eingabe von Punycode (IDNA) ist nicht notwendig. Die Domain wird automatisch konvertiert.',\n\t\t'domain_noipaddress' => 'Eine IP-Adresse kann nicht als Domain angelegt werden',\n\t\t'dns_record_toolong' => 'Records/Labels können maximal 63 Zeichen lang sein',\n\t\t'noipportgiven' => 'Keine IP/Port angegeben',\n\t\t'nosslippportgiven' => 'Wenn SSL aktiviert ist, muss eine SSL IP/Port angegeben werden',\n\t\t'jsonextensionnotfound' => 'Diese Funktion benötigt die PHP json-Erweiterung.',\n\t\t'cannotdeletesuperadmin' => 'Der erste Administrator kann nicht gelöscht werden.',\n\t\t'no_wwwcnamae_ifwwwalias' => 'Es kann kein CNAME Eintrag für \"www\" angelegt werden, da die Domain einen www-Alias aktiviert hat. Ändere diese Einstellung auf \"Kein Alias\" oder \"Wildcard Alias\"',\n\t\t'local_group_exists' => 'Die angegebene Gruppe existiert bereits auf dem System',\n\t\t'local_group_invalid' => 'Der angegebene Gruppen-Name ist nicht gültig',\n\t\t'local_user_invalid' => 'Der angegebene Benutzer-Name ist nicht gültig oder existiert nicht',\n\t\t'local_user_isfroxloruser' => 'Der angegebene Benutzer-Name ist ein von froxlor verwalteter Benutzer und kann in diesem Kontext nicht verwendet werden.',\n\t\t'invaliddnsforletsencrypt' => 'Die DNS-Einträge der Domain enthalten keine der gewählten IP Adressen. Let\\'s Encrypt Zertifikats-Erstellung ist nicht möglich.',\n\t\t'notallowedphpconfigused' => 'Nutzung einer PHP-Konfiguration welche nicht dem Kunden zugeordnet ist',\n\t\t'pathmustberelative' => 'Der Benutzer hat nicht die benötigten Berechtigungen, um Pfade außerhalb des Kunden-Heimatverzeichnisses anzugeben. Bitte einen relativen Pfad angeben (kein führendes /).',\n\t\t'mysqlserverstillhasdbs' => 'Datenbank-Server kann für den Kunden nicht entfernt werden, da sich dort noch Datenbanken befinden.',\n\t\t'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten',\n\t\t'invalidcronjobintervalvalue' => 'Cronjob Intervall muss einer der folgenden Werte sein: %s',\n\t\t'phpgdextensionnotavailable' => 'Die PHP GD Extension ist nicht verfügbar. Bild-Daten können nicht validiert werden.',\n\t\t'2fa_wrongcode' => 'Der angegebene Code ist nicht korrekt',\n\t\t'gnupgextensionnotavailable' => 'Die PHP GnuPG Extension ist nicht verfügbar. PGP Schlüssel können nicht validiert werden.',\n\t\t'invalidpgppublickey' => 'Der angegebene PGP Public Key ist ungültig',\n\t\t'invalid_validtime' => 'Wert der valid_time in Sekunden muss zwischen 10 und 120 liegen.',\n\t\t'customerphpenabledbutnoconfig' => 'Kunde hat PHP aktiviert aber keine PHP-Konfiguration wurde gewählt.',\n\t\t'emaildomainstillhasaddresses' => 'Maildomain-Flag kann nicht deaktiviert werden, da für diese Domain noch E-Mail-Adressen vorhanden sind.',\n\t\t'tls13requiredforhttp3' => 'Domain hat http3 Option aktiviert, aber SSL-Protokoll enthält nicht TLSv1.3',\n\t\t'senderdomainnotowned' => 'Die angegebene Domain \"%s\" kann nicht genutzt werden.',\n\t\t'emailhasnoaccount' => 'Die angegebene E-Mail-Adresse \"%s\" hat kein Konto. Eine Absenderadresse kann nicht hinzugefügt werden.',\n\t],\n\t'extras' => [\n\t\t'description' => 'Hier können Sie zusätzliche Extras einrichten, wie zum Beispiel einen Verzeichnisschutz.<br />Die Änderungen sind erst nach einer kurzen Zeit wirksam.',\n\t\t'directoryprotection_add' => 'Verzeichnisschutz anlegen',\n\t\t'view_directory' => 'Verzeichnis anzeigen',\n\t\t'pathoptions_add' => 'Pfadoptionen hinzufügen',\n\t\t'directory_browsing' => 'Verzeichnisinhalt anzeigen',\n\t\t'pathoptions_edit' => 'Pfadoptionen bearbeiten',\n\t\t'error404path' => '404',\n\t\t'error403path' => '403',\n\t\t'error500path' => '500',\n\t\t'error401path' => '401',\n\t\t'errordocument404path' => 'Fehlerdokument 404',\n\t\t'errordocument403path' => 'Fehlerdokument 403',\n\t\t'errordocument500path' => 'Fehlerdokument 500',\n\t\t'errordocument401path' => 'Fehlerdokument 401',\n\t\t'execute_perl' => 'Perl/CGI ausführen',\n\t\t'htpasswdauthname' => 'Grund der Authentifizierung (AuthName)',\n\t\t'directoryprotection_edit' => 'Verzeichnisschutz bearbeiten',\n\t\t'export' => 'Datenexport erstellen',\n\t\t'dump_web' => 'Web-Daten hinzufügen',\n\t\t'dump_mail' => 'E-Mail Daten hinzufügen',\n\t\t'dump_dbs' => 'Datenbanken hinzufügen',\n\t\t'path_protection_label' => '<strong class=\"text-danger\">Wichtig</strong>',\n\t\t'path_protection_info' => 'Wir raten dringend dazu den angegebenen Pfad zu schützen, siehe \"Extras\" -> \"Verzeichnisschutz\"',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Hier können Sie zusätzliche FTP-Konten einrichten.<br />Die Änderungen sind sofort wirksam und die FTP-Konten sofort benutzbar.',\n\t\t'account_add' => 'Benutzerkonto anlegen',\n\t\t'account_edit' => 'FTP-Konto bearbeiten',\n\t\t'editpassdescription' => 'Neues Passwort setzen oder leer für keine Änderung.',\n\t\t'sshkey_add' => 'SSH-Key anlegen',\n\t\t'sshkey_edit' => 'SSH-Key bearbeiten',\n\t],\n\t'gender' => [\n\t\t'title' => 'Anrede',\n\t\t'male' => 'Herr',\n\t\t'female' => 'Frau',\n\t\t'undef' => '',\n\t],\n\t'imprint' => 'Impressum',\n\t'index' => [\n\t\t'customerdetails' => 'Kundendaten',\n\t\t'accountdetails' => 'Kontodaten',\n\t],\n\t'integrity_check' => [\n\t\t'databaseCharset' => 'Characterset der Datenbank (sollte UTF-8 sein)',\n\t\t'domainIpTable' => 'IP &lt;&dash;&gt; Domain Verkn&uuml;pfung',\n\t\t'subdomainSslRedirect' => 'Falsches SSL-redirect Flag bei nicht-SSL Domains',\n\t\t'froxlorLocalGroupMemberForFcgidPhpFpm' => 'froxlor-Benutzer in Kunden-Gruppen (f&uuml;r FCGID/php-fpm)',\n\t\t'webserverGroupMemberForFcgidPhpFpm' => 'Webserver-Benutzer in Kunden-Gruppen (f&uuml;r FCGID/php-fpm)',\n\t\t'subdomainLetsencrypt' => 'Hauptdomains ohne zugewiesenen SSL-Port haben keine Subdomain mit aktiviertem SSL-Redirect',\n\t],\n\t'logger' => [\n\t\t'date' => 'Datum',\n\t\t'type' => 'Typ',\n\t\t'action' => 'Aktion',\n\t\t'user' => 'Benutzer',\n\t\t'truncate' => 'Log leeren',\n\t\t'reseller' => 'Reseller',\n\t\t'admin' => 'Administrator',\n\t\t'cron' => 'Cronjob',\n\t\t'login' => 'Login',\n\t\t'intern' => 'Intern',\n\t\t'unknown' => 'Unbekannt',\n\t],\n\t'login' => [\n\t\t'username' => 'Benutzername',\n\t\t'password' => 'Passwort',\n\t\t'language' => 'Sprache',\n\t\t'login' => 'Anmelden',\n\t\t'logout' => 'Abmelden',\n\t\t'profile_lng' => 'Profilsprache',\n\t\t'welcomemsg' => 'Bitte melden Sie sich an, um auf Ihr Konto zuzugreifen.',\n\t\t'forgotpwd' => 'Passwort vergessen?',\n\t\t'presend' => 'Passwort zurücksetzen',\n\t\t'email' => 'E-Mail-Adresse',\n\t\t'remind' => 'Passwort zurücksetzen',\n\t\t'usernotfound' => 'Fehler: Unbekannter Benutzer!',\n\t\t'backtologin' => 'Zurück zum Login',\n\t\t'combination_not_found' => 'Kombination aus Benutzername und E-Mail Adresse stimmen nicht überein.',\n\t\t'2fa' => 'Zwei-Faktor Authentifizierung (2FA)',\n\t\t'2facode' => 'Bitte 2FA Code angeben',\n\t\t'2faremember' => 'Browser vertrauen',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Hallo,\\\\n\\\\nIhr E-Mail-Konto {USERNAME}\\\\nwurde erfolgreich eingerichtet.\\\\n\\\\nDies ist eine automatisch generierte\\\\nE-Mail, bitte antworten Sie nicht auf\\\\ndiese Mitteilung.\\\\n\\\\nIhr Administrator',\n\t\t\t'subject' => 'E-Mail-Konto erfolgreich eingerichtet',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Hallo {SALUTATION},\\\\n\\\\nhier Ihre Accountinformationen:\\\\n\\\\nBenutzername: {USERNAME}\\\\nPasswort: {PASSWORD}\\\\n\\\\nVielen Dank,\\\\nIhr Administrator',\n\t\t\t'subject' => 'Kontoinformationen',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Hallo {SALUTATION},\\\\n\\\\nihr E-Mail-Konto {USERNAME}\\\\nwurde erfolgreich eingerichtet.\\\\nIhr Passwort lautet {PASSWORD}.\\\\n\\\\nDies ist eine automatisch generierte\\\\neMail, bitte antworten Sie nicht auf\\\\ndiese Mitteilung.\\\\n\\\\nIhr Administrator',\n\t\t\t'subject' => 'E-Mail-Konto erfolgreich eingerichtet',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Passwort zurückgesetzt',\n\t\t\t'mailbody' => 'Hallo {SALUTATION},\\\\n\\\\nhiermit erhalten Sie den Link, um ein neues Passwort zu setzen. Dieser Link ist für die nächsten 24 Stunden gültig.\\\\n\\\\n{LINK}\\\\n\\\\nVielen Dank,\\\\nIhr Administrator',\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] Neue Datenbank erstellt',\n\t\t\t'mailbody' => 'Hallo {CUST_NAME},\n\ndu hast gerade eine neue Datenbank angelegt. Hier die angegebenen Informationen:\n\nDatenbankname: {DB_NAME}\nPasswort: {DB_PASS}\nBeschreibung: {DB_DESC}\nDatenbank-Server: {DB_SRV}\nphpMyAdmin: {PMA_URI}\nVielen Dank, Ihr Administrator',\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'Neuer FTP-Benutzer erstellt',\n\t\t\t'mailbody' => 'Hallo {CUST_NAME},\n\ndu hast gerade einen neuen FTP-Benutzer angelegt. Hier die angegebenen Informationen:\n\nBenutzername: {USR_NAME}\nPasswort: {USR_PASS}\nPfad: {USR_PATH}\n\nVielen Dank, Ihr Administrator',\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => 'Hallo {SALUTATION},\\\\n\\\\nSie haben bereits {TRAFFICUSED} von Ihren insgesamt {TRAFFIC} Traffic verbraucht.\\\\nDies sind mehr als {MAX_PERCENT}%%.\\\\n\\\\nVielen Dank,\\\\nIhr Administrator',\n\t\t\t'subject' => 'Sie erreichen bald Ihr Traffic-Limit',\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => 'Hallo {SALUTATION},\\\\n\\\\nSie haben bereits {DISKUSED} von Ihren insgesamt {DISKAVAILABLE} Speicherplatz verbraucht.\\\\nDies sind mehr als {MAX_PERCENT}%%.\\\\n\\\\nVielen Dank,\\\\nIhr Administrator',\n\t\t\t'subject' => 'Sie erreichen bald Ihr Speicherplatz-Limit',\n\t\t],\n\t\t'2fa' => [\n\t\t\t'mailbody' => 'Hallo,\\\\n\\\\nihr 2FA-Login Code lautet: {CODE}\\\\n\\\\nDies ist eine automatisch generierte\\\\neMail, bitte antworten Sie nicht auf\\\\ndiese Mitteilung.\\\\n\\\\nIhr Administrator',\n\t\t\t'subject' => 'froxlor - 2FA Code',\n\t\t],\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Allgemein',\n\t\t\t'changepassword' => 'Passwort ändern',\n\t\t\t'changelanguage' => 'Sprache ändern',\n\t\t\t'username' => 'Angemeldet als ',\n\t\t\t'changetheme' => 'Theme wechseln',\n\t\t\t'apihelp' => 'API Hilfe',\n\t\t\t'apikeys' => 'API Keys',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'E-Mail',\n\t\t\t'emails' => 'Adressen',\n\t\t\t'webmail' => 'Webmail',\n\t\t\t'emailsoverview' => 'E-Mail Domain Übersicht',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Datenbanken',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domains',\n\t\t\t'settings' => 'Übersicht Domains',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Benutzerkonten',\n\t\t\t'webftp' => 'WebFTP',\n\t\t\t'sshkeys' => 'SSH Keys',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extras',\n\t\t\t'directoryprotection' => 'Verzeichnisschutz',\n\t\t\t'pathoptions' => 'Pfadoptionen',\n\t\t\t'export' => 'Datenexport',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Traffic',\n\t\t\t'current' => 'Aktueller Monat',\n\t\t\t'overview' => 'Übersicht',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'PHP-Konfigurationen',\n\t\t\t'fpmdaemons' => 'PHP-FPM Versionen',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'System-Log',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'Es wurde keine E-Mail versendet, da sich keine Empfänger in der Datenbank befinden',\n\t\t'success' => 'Nachricht erfolgreich an \"%s\" Empfänger gesendet',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'Benutzer-/Datenbankname',\n\t\t'databasedescription' => 'Datenbankbeschreibung',\n\t\t'database_create' => 'Datenbank anlegen',\n\t\t'description' => 'Hier können Sie MySQL-Datenbanken anlegen und löschen.<br />Die Änderungen werden sofort wirksam und die Datenbanken sind sofort benutzbar.<br />Im Menü finden Sie einen Link zu phpMyAdmin, mit dem Sie Ihre Datenbankinhalte komfortabel bearbeiten können.<br /><br />Die Zugangsdaten sind wie folgt: (Die Angaben in <i>kursiver</i> Schrift sind durch die jeweiligen Einträge zu ersetzen)<br />Hostname: <b><SQL_HOST></b><br />Benutzername: <b><i>Datenbankname</i></b><br />Passwort: <b><i>das gewählte Passwort</i></b><br />Datenbank: <b><i>Datenbankname</i></b>',\n\t\t'mysql_server' => 'MySQL-Server',\n\t\t'database_edit' => 'Datenbank bearbeiten',\n\t\t'size' => 'Datenbankgröße',\n\t\t'privileged_user' => 'Privilegierter Datenbankbenutzer',\n\t\t'privileged_passwd' => 'Passwort für privilegierten Benutzer',\n\t\t'unprivileged_passwd' => 'Passwort für nicht privilegierten Benutzer',\n\t\t'mysql_ssl_ca_file' => 'SSL-Serverzertifikat',\n\t\t'mysql_ssl_verify_server_certificate' => 'Verifizieren des SSL-Serverzertifikats',\n\t\t'globaluserinfo' => 'Um auf Datenbanken zuzugreifen, kann zusätzlich der froxlor-Login (Benutzer: %s) verwendet werden, dieser hat automatisch Zugriff auf alle Datenbanken.<br />Es wird empfohlen diesen <b>nicht</b> für Applikationen zu nutzen, lediglich zur Administration (z.B. via phpMyAdmin).',\n\t\t'edit_global_user' => 'Admin Benutzer bearbeiten',\n\t],\n\t'panel' => [\n\t\t'edit' => 'bearbeiten',\n\t\t'delete' => 'löschen',\n\t\t'create' => 'anlegen',\n\t\t'save' => 'Speichern',\n\t\t'yes' => 'Ja',\n\t\t'no' => 'Nein',\n\t\t'emptyfornochanges' => 'leer für keine Änderung',\n\t\t'emptyfordefault' => 'Leer für Standardeinstellung.',\n\t\t'path' => 'Pfad',\n\t\t'toggle' => 'Umschalten',\n\t\t'next' => 'Weiter',\n\t\t'dirsmissing' => 'Das angegebene Verzeichnis konnte nicht gefunden werden.',\n\t\t'urloverridespath' => 'URL (überschreibt Pfad)',\n\t\t'pathorurl' => 'Pfad oder URL',\n\t\t'ascending' => 'aufsteigend',\n\t\t'descending' => 'absteigend',\n\t\t'search' => 'Suche',\n\t\t'used' => 'genutzt',\n\t\t'translator' => 'Übersetzung',\n\t\t'reset' => 'Änderungen verwerfen',\n\t\t'pathDescription' => 'Sollte das Verzeichnis nicht existieren, wird es automatisch erstellt.',\n\t\t'pathDescriptionEx' => '<br /><br /><span class=\"text-danger\">Bitte beachten:</span> Der Pfad <code>/</code> ist nicht erlaubt aufgrund administrativer Einstellungen, er wird automatisch auf <code>/gewaehlte.subdomain.tld/</code> gesetzt, sofern nicht ein anderer Ordner angegeben wird.',\n\t\t'pathDescriptionSubdomain' => 'Sollte das Verzeichnis nicht existieren, wird es automatisch erstellt.<br />Sollte eine Weiterleitung auf eine andere Domain gewünscht sein, muss der Eintrag mit http:// oder https:// beginnen.<br />Endet die URL mit einem / (Slash) geht froxlor von einem Ordner aus, wenn nicht, wird es wie eine Datei behandelt.',\n\t\t'back' => 'Zurück',\n\t\t'reseller' => 'Reseller',\n\t\t'admin' => 'Administrator',\n\t\t'customer' => 'Kunde/n',\n\t\t'send' => 'Versenden',\n\t\t'nosslipsavailable' => 'Für diesen Server wurden noch keine SSL IP/Port Kombinationen eingetragen',\n\t\t'backtooverview' => 'Zurück zur Übersicht',\n\t\t'dateformat' => 'TT.MM.JJJJ',\n\t\t'dateformat_function' => 'd.m.Y',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Standard',\n\t\t'never' => 'Nie',\n\t\t'active' => 'Aktiv',\n\t\t'please_choose' => 'Bitte auswählen',\n\t\t'allow_modifications' => 'Änderungen zulassen',\n\t\t'megabyte' => 'Megabyte',\n\t\t'not_supported' => 'Nicht unterstützt in: ',\n\t\t'view' => 'ansehen',\n\t\t'toomanydirs' => 'Zu viele Unterverzeichnisse. Weiche auf manuelle Verzeichniseingabe aus.',\n\t\t'abort' => 'Abbrechen',\n\t\t'not_activated' => 'Nicht aktiviert',\n\t\t'off' => 'aus',\n\t\t'options' => 'Optionen',\n\t\t'neverloggedin' => 'Keine Anmeldung bisher',\n\t\t'descriptionerrordocument' => 'Mögliche Werte sind: URL, Pfad zu einer Datei oder ein Text, umgeben von Anführungszeichen (\" \").<br />Leer für Server-Standardwert.',\n\t\t'unlock' => 'entsperren',\n\t\t'theme' => 'Theme',\n\t\t'variable' => 'Variable',\n\t\t'description' => 'Beschreibung',\n\t\t'cancel' => 'Abbrechen',\n\t\t'ssleditor' => 'SSL-Einstellungen für diese Domain',\n\t\t'ssleditor_infoshared' => 'Aktuell Zertifikat der Elterndomain genutzt',\n\t\t'ssleditor_infoglobal' => 'Aktuell globales Zertifikat genutzt',\n\t\t'dashboard' => 'Dashboard',\n\t\t'assigned' => 'zugewiesen',\n\t\t'available' => 'verfügbar',\n\t\t'news' => 'Neuigkeiten',\n\t\t'newsfeed_disabled' => 'Das Newsfeed ist deaktiviert. Klicken Sie auf das Editier-Icon, um zu den Einstellungen zu gelangen.',\n\t\t'ftpdesc' => 'FTP-Beschreibung',\n\t\t'letsencrypt' => 'Benutzt Let\\'s encrypt',\n\t\t'set' => 'Setzen',\n\t\t'sshkeydesc' => 'SSH-Key Beschreibung',\n\t\t'ftpuser' => 'FTP Benutzer',\n\t\t'sshpubkey' => 'Öffentlicher SSH Schlüssel',\n\t\t'sshpubkeyph' => \"Startet mit 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', oder 'sk-ssh-ed25519@openssh.com'\",\n\t\t'exportpath' => [\n\t\t\t'title' => 'Pfad zur Ablage des Exports',\n\t\t\t'description' => 'In diesem Ordner werden die Export-Archive abgelegt. Wenn Web-Daten exportiert werden, werden alle Dateien aus dem Heimatverzeichnis gesichert, exklusive des hier angegebenen Ordners.',\n\t\t],\n\t\t'export_pgp_public_key' => [\n\t\t\t'title' => 'Öffentlicher PGP-Schlüssel',\n\t\t\t'description' => 'Der öffentliche PGP-Schlüssel, mit dem die Exporte verschlüsselt werden sollen. Wenn kein Schlüssel angegeben ist, werden die Exporte nicht verschlüsselt.',\n\t\t],\n\t\t'pgp_public_key' => 'Öffentlicher PGP-Schlüssel',\n\t\t'none_value' => 'Keine',\n\t\t'viewlogs' => 'Logdateien einsehen',\n\t\t'not_configured' => 'Das System wurde noch nicht konfiguriert. Klicke auf den Button um die Installation zu starten.',\n\t\t'start_setup' => 'Setup starten',\n\t\t'ihave_configured' => 'Ich habe die Dienste konfiguriert',\n\t\t'system_is_configured' => '<i class=\"fa-solid fa-circle-exclamation me-1\"></i>Das System ist bereits konfiguriert',\n\t\t'settings_before_configuration' => 'Stelle sicher, dass die Einstellungen angepasst wurden bevor die Dienste konfiguriert werden.',\n\t\t'image_field_delete' => 'Das momentan vorhandene Bild löschen',\n\t\t'usage_statistics' => 'Resourcen-Verbrauch',\n\t\t'security_question' => 'Sicherheitsabfrage',\n\t\t'listing_empty' => 'Keine Einträge gefunden',\n\t\t'unspecified' => 'keine Angabe',\n\t\t'settingsmode' => 'Modus',\n\t\t'settingsmodebasic' => 'Einfach',\n\t\t'settingsmodeadvanced' => 'Erweitert',\n\t\t'settingsmodetoggle' => 'Modus umschalten',\n\t\t'modalclose' => 'Schließen',\n\t\t'managetablecolumnsmodal' => [\n\t\t\t'title' => 'Tabellenspalten verwalten',\n\t\t\t'description' => 'Hier können die angezeigten Tabellenspalten angepasst werden',\n\t\t],\n\t\t'mandatoryfield' => 'Pflichtfeld',\n\t\t'select_all' => 'Alle auswählen',\n\t\t'unselect_all' => 'Alle abwählen',\n\t\t'searchtablecolumnsmodal' => [\n\t\t\t'title' => 'Feld-Suche',\n\t\t\t'description' => 'Wähle das zu durchsuchende Feld aus'\n\t\t],\n\t\t'upload_import' => 'Hochladen und importieren',\n\t\t'profile' => 'Mein Profil',\n\t\t'use_checkbox_for_unlimited' => 'Der Wert \"0\" deaktiviert die Resource. Die Checkbox rechts erlaubt \"unlimitierte\" Nutzung.',\n\t\t'use_checkbox_to_disable' => 'Zum Deaktivieren, klicke die Checkbox auf der rechten Seite des Eingabefeldes',\n\t\t'distro_mismatch' => 'Anscheinend wurde auf eine neue Distribution aktualisiert. Bitte die Dienste entsprechend neu konfigurieren.',\n\t\t'set_new_distro' => 'Distribution setzen',\n\t\t'dismiss' => 'Ignorieren',\n\t\t'confirmaction' => 'Vorgang bestätigen',\n\t\t'confirmactiondesc' => 'Der Vorgang muss durch Angabe des aktuellen Benutzerpassworts bestätigt werden',\n\t\t'authenticationfailed' => 'Authentifizierung fehlgeschlagen',\n\t\t'noauthentication' => 'Fehlende Authentifizierung',\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => 'Lokaler Benutzer für PHP-FPM (froxlor-Vhost)',\n\t\t'vhost_httpgroup' => 'Lokale Gruppe für PHP-FPM (froxlor-Vhost)',\n\t\t'ownvhost' => [\n\t\t\t'title' => 'Verwende PHP-FPM im froxlor-Vhost',\n\t\t\t'description' => 'Wenn verwendet, wird froxlor selbst unter einem lokalen Benutzer ausgeführt',\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => 'Verwende mod_proxy / mod_proxy_fcgi',\n\t\t\t'description' => '<strong class=\"text-danger\">Muss gesetzt sein bei Debian 9.x (Stretch) oder neuer</strong>. Diese Option kann aktiviert werden, um php-fpm via mod_proxy_fcgi einzubinden. Dies setzt mindestens apache-2.4.9 voraus',\n\t\t],\n\t\t'ini_flags' => 'Mögliche <strong>php_flag</strong>s für die php.ini. Pro Zeile eine Direktive',\n\t\t'ini_values' => 'Mögliche <strong>php_value</strong>s für die php.ini. Pro Zeile eine Direktive',\n\t\t'ini_admin_flags' => 'Mögliche <strong>php_admin_flag</strong>s für die php.ini. Pro Zeile eine Direktive',\n\t\t'ini_admin_values' => 'Mögliche <strong>php_admin_value</strong>s für die php.ini. Pro Zeile eine Direktive',\n\t],\n\t'privacy' => 'Datenschutzerklärung',\n\t'pwdreminder' => [\n\t\t'success' => 'Das Zurücksetzen des Passworts wurde erfolgreich angefordert. Sie sollten nun eine E-Mail mit weiteren Anweisungen erhalten.',\n\t\t'notallowed' => 'Unbekannter Benutzer oder Zurücksetzen des Passworts ist deaktiviert.',\n\t\t'changed' => 'Ihr Passwort wurde erfolgreich geändert. Sie können sich nun damit anmelden.',\n\t\t'wrongcode' => 'Der verwendete Aktivierungscode ist entweder nicht gültig oder bereits abgelaufen.',\n\t\t'choosenew' => 'Neues Passwort auswählen',\n\t],\n\t'question' => [\n\t\t'question' => 'Sicherheitsabfrage',\n\t\t'admin_customer_reallydelete' => 'Wollen Sie den Kunden \"%s\" wirklich löschen?<br />ACHTUNG! Alle Daten gehen unwiderruflich verloren! Nach dem Vorgang müssen die Daten manuell aus dem Dateisystem entfernt werden.',\n\t\t'admin_domain_reallydelete' => 'Wollen Sie die Domain \"%s\" wirklich löschen?<br><span class=\"text-danger\"><strong>ACHTUNG:</strong> Alle Subdomains, FTP-Konten und E-Mail Adressen/Konten, welche mit dieser Domain verbunden sind, werden gelöscht!</span>',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Wollen Sie die wichtige Sicherheitseinstellung \\'OpenBasedir\\' wirklich deaktivieren?',\n\t\t'admin_admin_reallydelete' => 'Wollen Sie den Admin \"%s\" wirklich löschen?<br />Alle Kunden und Domains dieses Admins werden Ihnen zugeteilt.',\n\t\t'admin_template_reallydelete' => 'Wollen Sie die Vorlage \"%s\" wirklich löschen?',\n\t\t'domains_reallydelete' => 'Wollen Sie die Domain \"%s\" wirklich löschen?',\n\t\t'email_reallydelete' => 'Wollen Sie die E-Mail-Adresse \"%s\" wirklich löschen?',\n\t\t'email_reallydelete_account' => 'Wollen Sie das Konto von \"%s\" wirklich löschen?',\n\t\t'email_reallydelete_forwarder' => 'Wollen Sie die Weiterleitung \"%s\" wirklich löschen?',\n\t\t'email_reallydelete_sender' => 'Wollen Sie die Absendeadresse \"%s\" wirklich löschen?',\n\t\t'extras_reallydelete' => 'Wollen Sie den Verzeichnisschutz für \"%s\" wirklich löschen?',\n\t\t'extras_reallydelete_pathoptions' => 'Wollen Sie die Optionen für den Pfad \"%s\" wirklich löschen?',\n\t\t'extras_reallydelete_export' => 'Wollen Sie den geplanten Daten-Export wirklich löschen?',\n\t\t'ftp_reallydelete' => 'Wollen Sie das FTP-Benutzerkonto \"%s\" wirklich löschen?',\n\t\t'sshkey_reallydelete' => 'Wollen Sie den SSH-Schlüssel \"%s\" wirklich löschen?',\n\t\t'mysql_reallydelete' => 'Wollen Sie die Datenbank \"%s\" wirklich löschen?<br />ACHTUNG! Alle Daten gehen unwiderruflich verloren!',\n\t\t'admin_configs_reallyrebuild' => 'Wollen Sie wirklich alle Konfigurationsdateien neu erstellen lassen?',\n\t\t'admin_customer_alsoremovefiles' => 'Kundendaten löschen?',\n\t\t'admin_customer_alsoremovemail' => 'E-Mail-Daten auf dem Dateisystem löschen?',\n\t\t'admin_customer_alsoremoveftphomedir' => 'Heimatverzeichnis des FTP-Benutzers löschen?',\n\t\t'admin_ip_reallydelete' => 'Wollen Sie wirklich die IP-Adresse \"%s\" löschen?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Sind Sie sicher, dass der DocumentRoot dieser Domain außerhalb des Heimatverzeichnisses des Kunden liegen soll?',\n\t\t'admin_counters_reallyupdate' => 'Wollen Sie den Ressourcenverbrauch neu berechnen?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Wollen Sie wirklich alle unverschlüsselten Passwörter aus der Tabelle mail_users entfernen? Dieser Schritt kann nicht rückgängig gemacht werden! Die Einstellung für das Speichern der E-Mail-Konten-Passwörter in Klartext wird hierbei ebenfalls deaktiviert.',\n\t\t'logger_reallytruncate' => 'Wollen Sie die Tabelle \"%s\" wirklich leeren?',\n\t\t'admin_quotas_reallywipe' => 'Sind Sie sicher, dass alle E-Mail-Kontingente aus der Tabelle mail_users entfernt werden sollen? Dieser Schritt kann nicht rückgängig gemacht werden!',\n\t\t'admin_quotas_reallyenforce' => 'Sind Sie sicher, dass Sie allen Benutzern das Default-Quota zuweisen wollen? Dies kann nicht rückgängig gemacht werden!',\n\t\t'phpsetting_reallydelete' => 'Wollen Sie diese PHP-Einstellungen wirklich löschen? Alle Domains die diese Einstellungen bis jetzt verwendet haben, werden dann auf die Standardeinstellungen umgestellt.',\n\t\t'fpmsetting_reallydelete' => 'Wollen Sie diese PHP-FPM Einstellungen wirklich löschen? Alle PHP Konfigurationen die diese Einstellungen bis jetzt verwendet haben, werden dann auf die Standardeinstellungen umgestellt.',\n\t\t'customer_reallyunlock' => 'Wollen Sie den Kunden \"%s\" wirklich entsperren?',\n\t\t'admin_integritycheck_reallyfix' => 'M&ouml;chten Sie wirklich versuchen s&auml;mtliche Datenbank-Integrit&auml;tsprobleme automatisch zu beheben?',\n\t\t'plan_reallydelete' => 'Wollen Sie den Hostingplan %s wirklich löschen?',\n\t\t'apikey_reallydelete' => 'Wollen Sie den Api-Key wirklich löschen?',\n\t\t'apikey_reallyadd' => 'Einen neuen Api-Key erstellen?',\n\t\t'dnsentry_reallydelete' => 'Wollen Sie den DNS-Eintrag wirklich löschen?',\n\t\t'certificate_reallydelete' => 'Wollen Sie diese Zertifikat wirklich löschen?',\n\t\t'cache_reallydelete' => 'Wollen Sie den Cache wirklich leeren?',\n\t\t'please_enter_otp' => 'Bitte 2FA Code eingeben',\n\t\t'admin_mysqlserver_reallydelete' => 'Wollen Sie wirklich diesen MySQL-Server löschen?',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Session-Timeout',\n\t\t\t'description' => 'Wie lange muss ein Benutzer inaktiv sein, damit die Session ungültig wird? (in Sekunden)',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Kundenpräfix',\n\t\t\t'description' => 'Welchen Präfix sollen die Kundenaccounts haben?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'MySQL-Präfix',\n\t\t\t'description' => 'Welchen Präfix sollen die MySQL-Benutzerkonten haben?</br>Mit \"RANDOM\" als Wert wird ein 3-stelliger Zufallswert als Präfix verwendet.</br>Mit \"DBNAME\" als Wert wird ein Feld Databankname zusammen mit dem Kundennamen als Präfix genutzt.',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP-Präfix',\n\t\t\t'description' => 'Welchen Präfix sollen die FTP-Benutzerkonten haben?<br/><b>Falls FTP-Quoatas verwendet werden, ist es notwendig das Quota-SQL-Query in der FTP-Server-Config ebenfalls zu ändern!</b>',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Heimatverzeichnis',\n\t\t\t'description' => 'Wo sollen die Heimatverzeichnisse der Kunden liegen?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Webserver-Logdateien-Verzeichnis',\n\t\t\t'description' => 'Wo sollen die Logdateien des Webservers liegen?',\n\t\t],\n\t\t'logfiles_script' => [\n\t\t\t'title' => 'Eigenes Script zu dem Log-Files übergeben werden',\n\t\t\t'description' => 'Hier kann ein Script an das die Loginhalte übergeben werden hinterlegt und die Platzhalter <strong>{LOGFILE}, {DOMAIN} und {CUSTOMER}</strong> genutzt werden, sofern nötig. Falls ein Script angegeben wird, muss die Option <strong>Webserver Logdateien umleiten</strong> gesetzt werden',\n\t\t],\n\t\t'logfiles_format' => [\n\t\t\t'title' => 'Access-Log Format',\n\t\t\t'description' => 'Hier kann ein angepasstes Log-format entsprechend der Webserver-Dokumentation angegeben werden, leer lassen für Standard. Abhängig vom LogFormat muss die Angabe unter Anführungszeichen stehen.<br/>Wenn verwendet mit nginx, so kann es wie folgt aussehen: <i>log_format frx_custom {EINGESTELLTES_FORMAT}</i>.<br/>Wenn verwendet mit Apache, so kann es wie folgt aussehen: <i>LogFormat {EINGESTELLTES_FORMAT} frx_custom</i>.<br /><strong>ACHTUNG:</strong> Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden auch übernommen und der Webserver könnte nicht mehr starten!',\n\t\t],\n\t\t'logfiles_type' => [\n\t\t\t'title' => 'Access-Log Typ',\n\t\t\t'description' => 'Wähle zwischen <strong>combined</strong> oder <strong>vhost_combined</strong>.',\n\t\t],\n\t\t'logfiles_piped' => [\n\t\t\t'title' => 'Webserver Logdateien zu eigenem Script umleiten (siehe oben)',\n\t\t\t'description' => 'Wenn ein Script für die Logdateien verwendet wird, muss diese Option aktiviert werden, damit der Webserver die Ausgabe an das Script weitergibt.',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'IP-Adresse',\n\t\t\t'description' => 'Welche Haupt-IP-Adresse hat der Server?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Hostname',\n\t\t\t'description' => 'Welchen Hostnamen hat der Server?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Webserver-Reload-Befehl',\n\t\t\t'description' => 'Wie heißt das Skript zum Neuladen der Webserver-Konfigurationsdateien?',\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => 'Nameserver aktivieren',\n\t\t\t'description' => 'Hier können Sie den Nameserver global aktivieren bzw. deaktivieren.',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'DNS-Server Konfigurationsordner',\n\t\t\t'description' => 'Wo liegen die DNS-Server Konfigurationsdateien?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'DNS-Server Reload-Befehl',\n\t\t\t'description' => 'Wie heißt das Skript zum Neuladen der DNS-Server Konfigurationsdateien?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'Mail-UID',\n\t\t\t'description' => 'Welche UID sollen die E-Mails haben?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mail-GID',\n\t\t\t'description' => 'Welche GID sollen die E-Mails haben?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Mail-Homedir',\n\t\t\t'description' => 'Wo sollen die E-Mails liegen?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Absenderadresse',\n\t\t\t'description' => 'Wie lautet die Absenderadresse für E-Mails aus dem Panel?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin-URL',\n\t\t\t'description' => 'Wo liegt phpMyAdmin? (muss mit http(s):// beginnen)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'Webmail-URL',\n\t\t\t'description' => 'Wo liegt der Webmail-Client? (muss mit http(s):// beginnen)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP-URL',\n\t\t\t'description' => 'Wo liegt der WebFTP-Client? (muss mit http(s):// beginnen)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Welche Sprache ist Ihre Standardsprache?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Maximale Loginversuche',\n\t\t\t'description' => 'Maximale Anzahl an Loginversuchen bis der Account deaktiviert wird.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Länge der Deaktivierung',\n\t\t\t'description' => 'Zeitraum (in Sekunden) für den der Account deaktiviert ist.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Pfad-Eingabemethode',\n\t\t\t'description' => 'Soll ein Pfad via Auswahlliste ausgewählt oder manuell eingegeben werden können?',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Nameserver',\n\t\t\t'description' => 'Eine durch Komma getrennte Liste mit den Hostnamen aller Nameserver. Der Erste ist der Primäre.',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'MX-Server',\n\t\t\t'description' => 'Eine durch Komma getrenne Liste, die ein Paar mit einer Nummer und den Hostnamen einen MX-Servers, getrennt durch ein Leerzeichen, enthält (z. B. \\'10 mx.example.tld\\').',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Einträge pro Seite',\n\t\t\t'description' => 'Wie viele Einträge sollen auf einer Seite angezeigt werden? (0 = Paging deaktivieren)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'Standard-IP/Port-Kombination',\n\t\t\t'description' => 'Welche IP/Port-Kombination sollen standardmäßig verwendet werden?',\n\t\t],\n\t\t'defaultsslip' => [\n\t\t\t'title' => 'Standard SSL IP/Port-Kombination',\n\t\t\t'description' => 'Welche ssl-fähigen IP/Port-Kombination sollen standardmäßig verwendet werden?',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Anzuhängende Pfade bei OpenBasedir',\n\t\t\t'description' => 'Diese (durch Doppelpunkte getrennten) Pfade werden dem OpenBasedir-Statement in jedem vHost-Container angehängt.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Natürliche Sortierung in der Listenansicht nutzen',\n\t\t\t'description' => 'Sortiert die Liste in der Reihenfolge web1 -> web2 -> web11 statt web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Docroot für deaktivierte Benutzer',\n\t\t\t'description' => 'Dieser Pfad wird als Docroot für deaktivierte Benutzer verwendet. Ist das Feld leer, wird kein vHost erstellt.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Passwörter der Mail-Konten auch im Klartext in der Datenbank speichern',\n\t\t\t'description' => 'Wenn diese Einstellung auf Ja gesetzt wird, werden alle Passwörter auch unverschlüsselt (also im Klartext, für jeden mit Zugriff auf die froxlor-Datenbank sofort lesbar) in der mail_users-Tabelle gespeichert. Aktivieren Sie diese Option nur dann, wenn Sie SASL nutzen!',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'FTP-Benutzerkonten @domain',\n\t\t\t'description' => 'Können Kunden FTP-Benutzerkonten user@domain anlegen?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'PHP über mod_fcgid/suexec einbinden',\n\t\t\t'description' => 'PHP wird unter dem Benutzer des Kunden ausgeführt.<br /><br /><b>Dies benötigt eine spezielle Webserver-Konfiguration für Apache, siehe <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID-Handbuch</a>.</b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Konfigurations-Verzeichnis',\n\t\t\t\t'description' => 'Wo sollen alle Konfigurationsdateien von fcgid liegen? Wenn Sie keine selbst kompilierte suexec Binary benutzen, was in der Regel der Fall ist, muss dieser Pfad unter /var/www/ liegen.<br /><br /><div class=\"text-danger\">ACHTUNG: Der Inhalt dieses Ordners wird regelmäßig geleert, daher sollten dort keinerlei Daten manuell abgelegt werden.</div>',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Temporäres Verzeichnis',\n\t\t\t\t'description' => 'Wo sollen die temporären Verzeichnisse erstellt werden',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Prozesse je Domain',\n\t\t\t\t'description' => 'Wieviele PHP-Prozesse pro Domain sollen gestartet/erlaubt werden. Der Wert 0 wird empfohlen, da PHP die Anzahl dann selbst effizient verwaltet.',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrappereinbindung in Vhosts',\n\t\t\t\t'description' => 'Wie sollen die Wrapper in den Vhosts eingebunden werden',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Globale PEAR Verzeichnisse',\n\t\t\t\t'description' => 'Welche globalen PEAR Verzeichnisse sollen in den php.ini-Einstellungen ersetzt werden? Einzelne Verzeichnisse sind mit einem Doppelpunkt zu trennen.',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Maximale Requests pro Domain',\n\t\t\t\t'description' => 'Wieviele PHP-Requests pro Domain sollen erlaubt werden?',\n\t\t\t],\n\t\t\t'defaultini' => 'Voreingestellte PHP-Konfiguration für neue Domains',\n\t\t\t'defaultini_ownvhost' => 'Voreingestellte PHP-Konfiguration für den froxlor-Vhost',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Idle-Timeout',\n\t\t\t\t'description' => 'Timeout-Einstellung für mod_FastCGI.',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Alternative E-Mail-Adresse benutzen',\n\t\t\t'description' => 'Während des Erstellens eines Accounts das Passwort an eine andere E-Mail-Adresse senden',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Webserver vHost-Konfigurations-Datei/Verzeichnis-Name',\n\t\t\t'description' => 'Wo sollen die vHost-Konfigurationen abgelegt werden? Sie können entweder eine Datei (also mit allen vHosts) oder einen Ordner (mit einer Datei pro vHost) angeben.',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Webserver Verzeichnisoption-Konfigurations-Datei/Verzeichnis-Name',\n\t\t\t'description' => 'Wo sollen die Verzeichnisoption-Konfigurationen abgelegt werden? Sie können entweder eine Datei (also mit allen vHosts) oder einen Ordner (mit einer Datei pro vHost) angeben.',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Webserver htpasswd Verzeichnisname',\n\t\t\t'description' => 'Wo sollen die htpasswd-Dateien für den Verzeichnisschutz abgelegt werden?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'MySQL-Access-Hosts',\n\t\t\t'description' => 'Eine durch Komma getrennte Liste mit den Hostnamen aller Hostnames/IP-Adressen, von denen sich die Benutzer einloggen dürfen. Um ein Subnetz zu erlauben ist die Netzmaske oder CIDR Syntax erlaubt.',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Webalizerausgabe',\n\t\t\t'description' => 'Ausgabefreudigkeit des Webalizer-Programms',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Logging ja/nein',\n\t\t\t'severity' => 'Logging Level',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Log-Art(en)',\n\t\t\t\t'description' => 'Wählen Sie hier die gewünschten Logtypen. Für Mehrfachauswahl, halten Sie während der Auswahl STRG gedrückt<br />Mögliche Logtypen sind: syslog, file, mysql',\n\t\t\t],\n\t\t\t'logfile' => [\n\t\t\t\t'title' => 'Dateiname der Logdatei',\n\t\t\t\t'description' => 'Wird nur verwendet, wenn die Log-Art \"file\" ausgewählt ist. Diese Datei wird unter froxlor/logs/ geschrieben. Dieser Ordner ist vor Webzugriff geschützt.',\n\t\t\t],\n\t\t\t'logcron' => 'Logge Cronjobs',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Nie',\n\t\t\t\t'once' => 'Einmalig',\n\t\t\t\t'always' => 'Immer',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => 'Aktiviere SSL',\n\t\t\t\t'description' => 'Erlaubt die Nutzung von SSL für den Webserver',\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'Pfad zum SSL-Zertifikat',\n\t\t\t\t'description' => 'Geben Sie den Pfad inklusive Dateinamen des Zertifikats an (meist .crt or .pem).',\n\t\t\t],\n\t\t\t'openssl_cnf' => 'Standardwerte zum Erstellen eines Zertifikats',\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'Pfad zum SSL Private-Key',\n\t\t\t\t'description' => 'Geben Sie den Pfad inklusive Dateinamen der Schlüssel-Datei an (der private-key, meist .key).',\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'Pfad zum SSL-CA-Zertifikat (optional)',\n\t\t\t\t'description' => 'Client Authentifizierung, dieses Feld sollte nur gesetzt werden, wenn es wirklich gebraucht wird.',\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => 'Erlaubte SSL Ciphers festlegen',\n\t\t\t\t'description' => 'Dies ist eine Liste von Ciphers, die genutzt werden sollen (oder auch nicht genutzt werden sollen), wenn eine SSL Verbindung besteht. Eine Liste aller Ciphers und wie diese hinzugefügt/ausgeschlossen werden ist in den Abschnitten \"CIPHER LIST FORMAT\" und \"CIPHER STRINGS\" in <a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">der man-page für Ciphers</a> zu finden.<br /><br /><b>Standard-Wert ist:</b><pre>ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305</pre>',\n\t\t\t],\n\t\t\t'apache24_ocsp_cache_path' => [\n\t\t\t\t'title' => 'Apache 2.4: Pfad zum OCSP-Stapling-Cache',\n\t\t\t\t'description' => 'Konfiguriert den Cache-Pfad zum Zwischenspeichern der OCSP-Antworten,<br />die an TLS-Handshakes angehängt werden.',\n\t\t\t],\n\t\t\t'ssl_protocols' => [\n\t\t\t\t'title' => 'SSL Protokollversion festlegen',\n\t\t\t\t'description' => 'Dies ist eine Liste von SSL/TLS Protokollversionen die genutzt werden sollen (oder auch nicht genutzt werden sollen), wenn SSL verwendet wird. <b>Hinweis:</b> Ältere Browser sind möglicherweise nicht vollständig zum neusten Protokoll kompatibel.<br /><br /><b>Standard-Wert ist:</b><pre>TLSv1.2</pre>',\n\t\t\t],\n\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t'title' => 'Explizite TLSv1.3 Ciphers, wenn genutzt',\n\t\t\t\t'description' => 'Dies ist eine Liste von Ciphers, die genutzt werden sollen (oder auch nicht genutzt werden sollen), wenn eine TLSv1.3 Verbindung hergestellt werden soll. Eine Liste aller Ciphers und wie diese hinzugefügt/ausgeschlossen werden ist <a href=\"https://wiki.openssl.org/index.php/TLS1.3\">der Dokumentation für TLSv1.3</a> zu entnehmen.<br /><br /><b>Standard-Wert ist leer</b>',\n\t\t\t],\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Standard vHost-Einstellungen',\n\t\t\t'description' => 'Der Inhalt dieses Feldes wird direkt in den IP/Port-vHost-Container übernommen. Die folgenden Variablen können verwendet werden:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (wenn zutreffend)<br/><br /><strong>ACHTUNG:</strong> Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!',\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => 'Der Inhalt dieses Feldes wird direkt in jeden Domain-vHost-Container übernommen. Die folgenden Variablen können verwendet werden:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (wenn zutreffend)<br/><strong>ACHTUNG:</strong> Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!',\n\t\t],\n\t\t'apache_globaldiropt' => [\n\t\t\t'title' => 'Kunden-Prefix Ordner-Optionen',\n\t\t\t'description' => 'Der Inhalt dieses Feldes wird in die 05_froxlor_dirfix_nofcgid.conf Apache Konfigurationsdatei eingefügt. Wenn leer werden folgende Standardwerte verwendet:<br><br>apache >=2.4<br><code>Require all granted<br>AllowOverride All</code><br><br>apache <=2.2<br><code>Order allow,deny<br>allow from all</code>',\n\t\t],\n\t\t'decimal_places' => 'Nachkommastellen bei der Ausgabe von Traffic/Webspace',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Manuelle DNS-Einstellungen für Domains',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Erlaube Kunden eigene DNS-Einstellungen vorzunehmen.',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Benutze UNIX-kompatible Benutzernamen.',\n\t\t\t'description' => 'Erlaubt die Nutzung von <strong>-</strong> und <strong>_</strong> in Benutzernamen wenn <strong>Nein</strong>.',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Erlaube das Zurücksetzen des Kundenpassworts.',\n\t\t\t'description' => 'Kunden können ihr Passwort zurücksetzen und bekommen einen Aktivierungs-Link per E-Mail zugesandt',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Erlaube das Zurücksetzen von Admin-/Reseller-Passwörtern.',\n\t\t\t'description' => 'Admins/Reseller können ihr Passwort zurücksetzen und bekommen einen Aktivierungs-Link per E-Mail zugesandt',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Mailbox-Kontingent',\n\t\t\t'description' => 'Standard-Kontingent für neu erstellte E-Mail-Benutzerkonten (Megabyte).',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Nutze E-Mail-Kontingent für Kunden',\n\t\t\t'description' => 'Aktiviere Kontingent für E-Mail-Konten. Standard ist <b>Nein</b>, da dies eine spezielle Konfiguration voraussetzt.',\n\t\t\t'removelink' => 'Hier klicken, um alle E-Mail-Kontingente zu entfernen',\n\t\t\t'enforcelink' => 'Hier klicken, um allen Benutzern das Standard-Kontingent zu zuweisen.',\n\t\t],\n\t\t'mail_enable_allow_sender' => [\n\t\t\t'title' => 'Aktiviere \"Erlaubte Absendeadressen\" Nutzung für Kunden',\n\t\t\t'description' => 'Wenn aktiviert können Kunden für E-Mail-Konten alternative Absendeadressen festlegen.<br>Standard: aus',\n\t\t],\n\t\t'mail_allow_external_domains' => [\n\t\t\t'title' => 'Erlaube externe Domains für \"Erlaubte Absendeadressen\"',\n\t\t\t'description' => 'Wenn aktiviert können Kunden beliebige Domains (außer diese auf diesem System, die nicht dem Kunden gehören) als \"Erlaubte Absendeadressen\" nutzen.<br>Standard: aus',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Erlaube gleichzeitigen Login',\n\t\t\t'description' => 'Wenn diese Option aktiviert ist, können sich Nutzer mehrmals gleichzeitig anmelden.',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Erlaube Verschieben von Domains unter Admins',\n\t\t\t'description' => 'Wenn diese Option aktiviert ist, kann unter Domaineinstellungen die Domain einem anderen Admin zugewiesen werden.<br /><b>Achtung:</b> Wenn der Kunde einer Domain nicht dem gleichen Admin zugeordnet ist wie die Domain selbst, kann dieser Admin alle anderen Domains des Kunden sehen!',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Erlaube Verschieben von Domains unter Kunden',\n\t\t\t'description' => 'Wenn diese Option aktiviert ist, kann unter Domaineinstellungen die Domain einem anderen Kunden zugewiesen werden.<br /><b>Achtung:</b> Der Dokumenten-Pfad der Domain wird auf den Heimatpfad (+ Domain-Ordner, sofern aktiviert) des neuen Kunden gesetzt.',\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'Wenn ja, werden die individuellen Einstellungen für alle Subdomains übernommen.<br />Wenn nein, werden Subdomain-Specialsettings entfernt.',\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Mindestlänge von Passwörtern',\n\t\t\t'description' => 'Hier können Sie die Mindestlänge für Passwörter festlegen. \\'0\\' bedeutet: Keine Mindestlänge',\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Erstelle Index-Datei auch in neuen Unterordnern',\n\t\t\t'description' => 'Wenn aktiviert, wird für jede Subdomain mit neuem Unterordner die Standard-Index Datei angelegt.',\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Antwort-Adresse',\n\t\t\t'description' => 'Standard-Antwort-Adresse für E-Mails aus dem Panel.',\n\t\t],\n\t\t'adminmail_defname' => 'Panel-Absender-Name',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Kunden Standard-Subdomain',\n\t\t\t'description' => 'Welcher Hostname soll für das Erstellen der Kunden-Standard-Subdomain verwendet werden? Falls leer wird der System-Hostname verwendet.',\n\t\t],\n\t\t'awstats_path' => 'Pfad zu AWStats \\'awstats_buildstaticpages.pl\\'',\n\t\t'awstats_conf' => 'AWStats Konfigurations-Pfad',\n\t\t'defaultttl' => 'Domain TTL für Bind in Sekunden (default \\'604800\\' = 1 Woche)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Verwende Standard-Fehlerdokumente für alle Kunden',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'Datei/URL für Fehler 401',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'Datei/URL für Fehler 403',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'Datei/URL für Fehler 404',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'Datei/URL für Fehler 500',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'Wenn pureftpd ausgewählt ist, werden die .ftpquota Dateien für das Quota erstellt und täglich aktualisiert.',\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Erlaube Kunden-Redirect',\n\t\t\t'description' => 'Erlaubt es Kunden den HTTP-Status Code für einen Redirect zu wählen',\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Standard-Redirect',\n\t\t\t'description' => 'Dieser Redirect wird immer genutzt, sofern der Kunde keinen anderen auswählt.',\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Erstelle mail-, imap-, pop3- and smtp-\"A Record\" auch wenn MX-Server angegeben sind',\n\t\t'froxlordirectlyviahostname' => 'froxlor direkt über den Hostnamen erreichbar machen',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Regulärer Ausdruck für Passwörter',\n\t\t\t'description' => 'Hier können Sie einen regulären Ausdruck für Passwort-Komplexität festlegen.<br />Leer = keine bestimmten Anforderungen',\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'Verwende FCGID im froxlor-Vhost',\n\t\t\t'description' => 'Wenn verwendet, wird froxlor selbst unter einem lokalen Benutzer ausgeführt',\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'Aktiviere SuExec-Workaround',\n\t\t\t\t'description' => 'Aktivieren Sie den Workaround nur, wenn die Kunden-Heimatverzeichnisse sich nicht unterhalb des suexec-Pfades liegen.<br />Wenn aktiviert erstellt froxlor eine Verknüpfung des vom Kunden für Perl aktiviertem Pfad + /cgi-bin/ im angegebenen suexec-Pfad.<br />Bitte beachten Sie, dass Perl dann nur im Unterordner /cgi-bin/ des Kunden-Ordners funktioniert und nicht direkt in diesem Ordner (wie es ohne den Workaround wäre!)',\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Pfad für Verknüpfungen zu Kunden-Perl-Verzeichnis',\n\t\t\t\t'description' => 'Diese Einstellung wird nur benötigt, wenn der SuExec-Workaround aktiviert ist.<br />ACHTUNG: Stellen Sie sicher, dass sich der angegebene Pfad innerhalb des Suexec-Pfades befindet ansonsten ist der Workaround nutzlos',\n\t\t\t],\n\t\t],\n\t\t'awstats_awstatspath' => 'Pfad zu AWStats \\'awstats.pl\\'',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'Pfad zum AWstats-Icon-Ordner',\n\t\t\t'description' => 'z. B. /usr/share/awstats/htdocs/icon/',\n\t\t],\n\t\t'login_domain_login' => 'Erlaube Anmeldung mit Domains',\n\t\t'perl_server' => [\n\t\t\t'title' => 'Perl Server-Socket',\n\t\t\t'description' => 'Eine einfache Anleitung hier zu findet man unter <a target=\"blank\" href=\"http://wiki.nginx.org/SimpleCGIhttps://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/\">nginx.com</a>',\n\t\t],\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx-PHP-Backend',\n\t\t\t'description' => 'Dies ist das Backend, auf dem PHP auf Anfragen von Nginx hört. Kann ein UNIX Socket oder eine IP:Port Kombination sein<br />*NICHT relevant bei php-fpm',\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'PHP-Reload-Befehl',\n\t\t\t'description' => 'Dieser wird benötigt, um das PHP-Backend bei Bedarf durch den Cronjob neu zu laden. (Standard: leer)<br />*NICHT relevant bei php-fpm',\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => 'Aktiviere PHP-FPM',\n\t\t\t'description' => '<b>Dies benötigt eine spezielle Webserver-Konfiguration, siehe <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">PHP-FPM Handbuch</a></b>',\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'Pfad zu php-fpm-Konfigurationen',\n\t\t\t'aliasconfigdir' => 'Alias-Ordner der php-fpm Konfiguration',\n\t\t\t'reload' => 'Kommando zum Neustarten von php-fpm',\n\t\t\t'pm' => 'Prozess Manager Control (PM)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'Anzahl der Kind-Prozesse',\n\t\t\t\t'description' => 'Die Anzahl der zu startenden Kind-Prozesse wenn PM auf \\'static\\' steht und die maximale Anzahl der Prozesse wenn PM auf \\'dynamic/ondemand\\' steht.<br />Equivalent zu PHP_FCGI_CHILDREN',\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'Anzahl der beim Starten zu erstellenden Kind-Prozesse',\n\t\t\t\t'description' => 'Hinweis: Nur wenn PM auf \\'dynamic\\' steht',\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'Mindestanzahl der Idle-Prozesse',\n\t\t\t\t'description' => 'Hinweis: Nur wenn PM auf \\'dynamic\\' steht<br />Wichtig: Pflichtangabe wenn PM auf \\'dynamic\\' steht',\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'Maximale Anzahl der Idle-Prozesse',\n\t\t\t\t'description' => 'Hinweis: Nur wenn PM auf \\'dynamic\\' steht<br />Wichtig: Pflichtangabe wenn PM auf \\'dynamic\\' steht',\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Requests pro Kindprozess bevor Neuerstellung (respawning)',\n\t\t\t\t'description' => 'Für keine Begrenzung \\'0\\' angeben. Equivalent zu PHP_FCGI_MAX_REQUESTS.',\n\t\t\t],\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Idle-Timeout',\n\t\t\t\t'description' => 'Timeout-Einstellung für PHP-FPM FastCGI.',\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'FastCGI IPC Verzeichnis',\n\t\t\t\t'description' => 'In dieses Verzeichnis werden die php-fpm Sockets vom Webserver abgelegt.<br />Das Verzeichnis muss für den Webserver lesbar sein.',\n\t\t\t],\n\t\t\t'limit_extensions' => [\n\t\t\t\t'title' => 'Erlaubte Dateiendungen',\n\t\t\t\t'description' => 'Beschränkt die Dateierweiterungen des Haupt-Skripts, das FPM zu parsen erlaubt. Dies kann Konfigurationsfehler auf der Webserverseite verhindern. Sie sollten FPM nur auf .php Erweiterungen beschränken, um zu verhindern, dass bösartige Nutzter andere Erweiterungen verwenden, um PHP Code auszuführen. Standardwert: .php',\n\t\t\t],\n\t\t\t'envpath' => 'Pfade für die PATH Umgebungsvariable. Leerlassen, um keine PATH Umgebungsvariable zu setzen.',\n\t\t\t'override_fpmconfig' => 'Überschreibe FPM-Daemon Einstellungen (pm, max_children, etc.)',\n\t\t\t'override_fpmconfig_addinfo' => '<br /><span class=\"text-danger\">Nur verwendet wenn \"Überschreibe FPM-Daemon Einstellungen\" auf \"Ja\" gestellt ist</span>',\n\t\t\t'restart_note' => 'Achtung: Der Code wird nicht auf Fehler geprüft. Bei etwaigen Fehlern könnte der PHP-FPM-Prozess nicht mehr starten!',\n\t\t\t'custom_config' => [\n\t\t\t\t'title' => 'Benutzerdefinierte Konfiguration',\n\t\t\t\t'description' => 'Füge eine benutzerdefinierte Einstellungen zur PHP-FPM Instanz hinzu, beispielsweise <i>pm.status_path = /status</i> für Monitoring. Unten ersichtliche Variablen können verwendet werden. <strong>Achtung: Der Code wird nicht auf Fehler geprüft. Bei etwaigen Fehlern könnte der PHP-FPM-Prozess nicht mehr starten!</strong>',\n\t\t\t],\n\t\t\t'allow_all_customers' => [\n\t\t\t\t'title' => 'Für aktuelle Kunden automatisch hinzufügen',\n\t\t\t\t'description' => 'Ist diese Einstellung aktiv, wird die Konfiguration automatisch allen aktuell existierenden Kunden-Accounts zugewiesen. Diese Einstellung ist nicht permanent, kann aber mehrfach / nach Bedarf ausgeführt werden.',\n\t\t\t],\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => 'Aktiviere das Senden von Reports über Webspace- und Trafficverbrauch',\n\t\t\t'webmax' => [\n\t\t\t\t'title' => 'Warn-Level in Prozent für Webspace',\n\t\t\t\t'description' => 'Gültige Werte sind von 0 bis 150. Der Wert 0 deaktiviert diesen Report.',\n\t\t\t],\n\t\t\t'trafficmax' => [\n\t\t\t\t'title' => 'Warn-Level in Prozent für Traffic',\n\t\t\t\t'description' => 'Gültige Werte sind von 0 bis 150. Der Wert 0 deaktiviert diesen Report.',\n\t\t\t],\n\t\t\t'report_web_bccadmin' => [\n\t\t\t\t'title' => 'BCC-E-Mail für Benachrichtigungen zur Webnutzung an den Administrator',\n\t\t\t\t'description' => 'Wenn diese Option aktiviert ist, wird die an den Kunden gesendete Warnung bezüglich der Speicherplatznutzung auch an den entsprechenden Administrator/Reseller (BCC) gesendet.'\n\t\t\t],\n\t\t],\n\t\t'dropdown' => 'Auswahlliste',\n\t\t'manual' => 'Manuelle Eingabe',\n\t\t'default_theme' => 'Standard-Theme',\n\t\t'validate_domain' => 'Validiere Domainnamen',\n\t\t'diskquota_enabled' => 'Quota aktiviert?',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'Pfad zu repquota',\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'Pfad zu quotatool',\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => 'Partition, auf welcher die Kundendaten liegen',\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'Maildir-(Unter-)Ordner',\n\t\t\t'description' => 'Der Maildir-Ordner innerhalb des Kontos des Benutzers (normalerweise \\'Maildir\\', in manchen Fällen auch \\'.maildir\\'). Sollen die E-Mails direkt in das Verzeichnis, diese Option leer lassen.',\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => 'Catchall verwenden',\n\t\t\t'description' => 'Möchten Sie Ihren Kunden die Funktion Catchall zur Verfügung stellen?',\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => 'Anpassungen für Apache 2.4 verwenden',\n\t\t\t'description' => '<div class=\"text-danger\">Achtung: Bitte nur verwenden, wenn wirklich Apache mit Version 2.4 oder höher installiert ist, ansonsten wird der Webserver nicht starten.</div>',\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'Pfad zur fastcgi_params Datei',\n\t\t\t'description' => 'Geben Sie den Pfad zu nginx\\'s fastcgi_params Datei an. Inklusive Dateiname!',\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => 'Verwende Domainnamen im Documentroot',\n\t\t\t'description' => 'Wenn aktiviert wird dem standard Documentroot zusätzlich der Domain-Name angehängt.<br /><br />Beispiel:<br />/var/customers/webs/customer_name/example.tld/<br />/var/customers/webs/customer_name/subdomain.example.tld/',\n\t\t],\n\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t'title' => 'Verstecke Subdomains in PHP-Konfigurations-Übersicht',\n\t\t\t'description' => 'Wenn aktiviert, werden die Subdomains der Kunden nicht in der PHP-Konfigurations-Übersicht angezeigt, lediglich die Anzahl.<br /><br />Hinweis: Nur relevant, wenn FCGID oder PHP-FPM aktiviert ist.',\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => 'Verstecke Standard-Subdomains in PHP-Konfigurations-Übersicht',\n\t\t\t'description' => 'Wenn aktiviert, werden die Standard-Subdomains der Kunden nicht mehr in der PHP-Konfigurations-Übersicht angezeigt.<br /><br />Hinweis: Nur relevant, wenn FCGID oder PHP-FPM aktiviert ist.',\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => 'Wählen Sie die zu verwendende Passwort-Verschlüsselungsmethode',\n\t\t\t'description' => 'Wählen Sie, welche Methode zur Verschlüsselung von Kennwörtern verwendet werden soll. Wenn Sie diese Einstellung ändern, werden nur neue Kennwörter mit der neuen Methode verschlüsselt. Bestehende Passwörter werden nicht geändert.'\n\t\t],\n\t\t'systemdefault' => 'Systemstandard',\n\t\t'panel_allow_theme_change_admin' => 'Erlaube Admins das Theme zu wechseln',\n\t\t'panel_allow_theme_change_customer' => 'Erlaube Kunden das Theme zu wechseln',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'AXFR Server',\n\t\t\t'description' => 'Eine durch Kommas getrennte Liste von IP Adressen, die DNS-Zonen transferieren dürfen (AXFR).',\n\t\t],\n\t\t'powerdns_mode' => [\n\t\t\t'title' => 'PowerDNS Operation Mode',\n\t\t\t'description' => 'Wählen Sie den PowerDNS-Modus: Native für keine DNS-Replikation (Standard) / Master wenn eine DNS-Replikation benötigt wird.',\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Webserver-Kunden-SSL-Zertifikatsverzeichnis',\n\t\t\t'description' => 'Wo sollen kundenspezifizierte SSL-Zertifikate erstellt werden?<br /><br /><div class=\"text-danger\">ACHTUNG: Der Inhalt dieses Ordners wird regelmäßig geleert, daher sollten dort keinerlei Daten manuell abgelegt werden.</div>',\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => 'Erlaube Administrator/Reseller das Melden von Datenbankfehlern an froxlor',\n\t\t\t'description' => 'Bitte beachten: Senden Sie zu keiner Zeit irgendwelche datenschutzrelevanten/persönlichen (Kunden-)Daten an uns!',\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => 'Erlaube Kunden das Melden von Datenbankfehlern an froxlor',\n\t\t\t'description' => 'Bitte beachten: Senden Sie zu keiner Zeit irgendwelche datenschutzrelevanten/persönlichen (Kunden-)Daten an uns!',\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => 'Analysiere Mailtraffic',\n\t\t\t'description' => 'Aktiviere das Analysieren der Logdateien des Mailsystems, um den verbrauchten Traffic zu berechnen.',\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'Typ des MDA',\n\t\t\t'description' => 'Der eingesetzte Mail Delivery Server',\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'Logdatei des MDA',\n\t\t\t'description' => 'Die Logdatei des Mail Delivery Server',\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'Typ des MTA',\n\t\t\t'description' => 'Der eingesetzte Mail Transfer Agent',\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'Logdatei des MTA',\n\t\t\t'description' => 'Die Logdatei des Mail Transfer Agent',\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'Cron-Konfigurationsdatei',\n\t\t\t'description' => 'Pfad zur Konfigurationsdatei des Cron-Dienstes. Diese Datei wird von froxlor automatisch aktualisiert.<br />Hinweis: Bitte verwenden Sie <strong>exakt</strong> die gleiche Datei wie für den froxlor-Haupt-Cronjob (Standard: /etc/cron.d/froxlor)!<br><br>Wird <b>FreeBSD</b> verwendet, sollte hier <i>/etc/crontab</i> angegeben werden!',\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Cron-Daemon reload Befehl',\n\t\t\t'description' => 'Geben Sie hier den Befehl zum Neuladen des Cron-Daemons an',\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Cron Startbefehl (php Programm)',\n\t\t\t'description' => 'Befehl zum Ausführen des Cronjobs. Ändern dieser Einstellung nur wenn nötig (Standard: \"/usr/bin/nice -n 5 /usr/bin/php -q\")!',\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => 'Erlaube automatische Datenbank-Aktualisierungen',\n\t\t\t'description' => '<strong class=\"text-danger\">WARNUNG:</strong> Diese Einstellung erlaubt es dem Cronjob die Prüfung der Dateien- und Datenbank-Version zu umgehen und bei einem Versions-Unterschied die Datenbank-Aktualisierungen automatisiert auszuführen.<br /><br/><div class=\"text-danger\">Das automatische Update setzt für neue Einstellungen und Änderungen immer die default-Werte. Diese müssen nicht zwingend zu dem genutzten System passen. Bitte zwei mal nachdenken, bevor diese Option aktiviert wird.</div>',\n\t\t],\n\t\t'dns_createhostnameentry' => 'Erstelle bind-Zone/Konfiguration für den System-Hostnamen',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => 'Kleinbuchstaben',\n\t\t\t'description' => 'Das Passwort muss mindestens einen Kleinbuchstaben (a-z) enthalten.',\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => 'Großbuchstaben',\n\t\t\t'description' => 'Das Passwort muss mindestens einen Großbuchstaben (A-Z) enthalten.',\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => 'Zahlen',\n\t\t\t'description' => 'Das Passwort muss mindestens eine Zahl (0-9) enthalten.',\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => 'Sonderzeichen',\n\t\t\t'description' => 'Das Passwort muss mindestens eines der untenstehenden Sonderzeichen enthalten',\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => 'Sonderzeichen-Liste',\n\t\t\t'description' => 'Mindestens eines dieser Sonderzeichen muss in dem Passwort vorkommen, sofern die Sonderzeichen-Option aktiviert ist.',\n\t\t],\n\t\t'apache_itksupport' => [\n\t\t\t'title' => 'Anpassungen für Apache ITK-MPM verwenden',\n\t\t\t'description' => '<div class=\"text-danger\">Achtung: Bitte nur verwenden, wenn wirklich Apache itk-mpm verwendet wird, ansonsten wird der Webserver nicht starten.</div>',\n\t\t],\n\t\t'letsencryptca' => [\n\t\t\t'title' => 'ACME Umgebung',\n\t\t\t'description' => 'Umgebung, welche genutzt wird um Zertifikate zu bestellen.',\n\t\t],\n\t\t'letsencryptchallengepath' => [\n\t\t\t'title' => 'Verzeichnis für Let\\'s Encrypt challenges',\n\t\t\t'description' => 'Let\\'s Encrypt challenges werden aus diesem Verzeichnis über einen globalen Alias ausgeliefert.',\n\t\t],\n\t\t'letsencryptkeysize' => [\n\t\t\t'title' => 'Schlüsselgröße für neue Let\\'s Encrypt Zertifikate',\n\t\t\t'description' => 'Größe des Schlüssels in Bit für neue Let\\'s Encrypt Zertifikate.',\n\t\t],\n\t\t'letsencryptreuseold' => [\n\t\t\t'title' => 'Let\\'s Encrypt Schlüssel wiederverwenden',\n\t\t\t'description' => 'Wenn dies aktiviert ist, wird der alte Schlüssel bei jeder Verlängerung verwendet, andernfalls wird ein neues Paar generiert.',\n\t\t],\n\t\t'leenabled' => [\n\t\t\t'title' => 'Let\\'s Encrypt verwenden',\n\t\t\t'description' => 'Wenn dies aktiviert ist, können Kunden durch froxlor automatisch generierte und verlängerbare Let\\'s Encrypt SSL-Zertifikate für Domains mit SSL IP/Port nutzen.<br /><br />Bitte die Webserver-Konfiguration beachten wenn aktiviert, da dieses Feature eine spezielle Konfiguration benötigt.',\n\t\t],\n\t\t'caa_entry' => [\n\t\t\t'title' => 'CAA DNS Einträge generieren',\n\t\t\t'description' => 'Generiert CAA Einträge automatisch für alle Domains mit aktiviertem SSL und Let\\'s Encrypt',\n\t\t],\n\t\t'caa_entry_custom' => [\n\t\t\t'title' => 'Zusätzliche CAA DNS Einträge',\n\t\t\t'description' => 'DNS Certification Authority Authorization (CAA) verwendet das Domain Name System, um dem Besitzer einer Domain die Möglichkeit zu bieten, gewisse Zertifizierungsstellen (CAs) dazu zu berechtigen,<br>ein Zertifikat für die betroffene Domain auszustellen. CAA Records sollen verhindern, dass Zertifikate fälschlicherweise für eine Domain ausgestellt werden.<br><br>Der Inhalt dieses Feldes wird direkt in die DNS Zone übernommen (eine Zeile pro CAA Record). Wenn Let\\'s Encrypt für eine Domain aktiviert wurde und die obige Option aktiviert wurde, wird immer automatisch dieser Eintrag angefügt und muss nicht selber angegeben werden:<br><code>0 issue \"letsencrypt.org\"</code> (Wenn wildcard aktiviert ist, wird statdessen issuewild benutzt).<br>Um Incident Reporting per Mail zu aktivieren, muss eine <code>iodef</code> Zeile angefügt werden. Ein Beispiel für einen Report an <code>me@example.com</code> wäre:<br><code>0 iodef \"mailto:me@example.com\"</code><br><br><strong>ACHTUNG:</strong> Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Die CAA finalen Einträge könnten daher falsch sein!',\n\t\t],\n\t\t'exportenabled' => [\n\t\t\t'title' => 'Daten-Export für Kunden aktivieren',\n\t\t\t'description' => 'Wenn dies aktiviert ist, kann der Kunde Daten-Exporte planen (cron-export) welche ein Archiv in sein Heimatverzeichnis ablegen (Unterordner vom Kunden wählbar)',\n\t\t],\n\t\t'dnseditorenable' => [\n\t\t\t'title' => 'DNS Editor aktivieren',\n\t\t\t'description' => 'Erlaubt es Admins und Kunden die DNS Einträge ihrer Domains zu verwalten.',\n\t\t],\n\t\t'dns_server' => [\n\t\t\t'title' => 'DNS Server Dienst',\n\t\t\t'description' => 'Dienste müssen mit den froxlor Konfigurationstemplates konfiguriert werden',\n\t\t],\n\t\t'panel_customer_hide_options' => [\n\t\t\t'title' => 'Menüpunkte und Traffic-Charts im Kundenbereich ausblenden',\n\t\t\t'description' => 'Wählen Sie hier die gewünschten Menüpunkte und Traffic-Charts aus, welche im Kundenbereich ausgeblendet werden sollen. Für Mehrfachauswahl, halten Sie während der Auswahl STRG gedrückt.',\n\t\t],\n\t\t'allow_allow_customer_shell' => [\n\t\t\t'title' => 'Erlaube Kunden für FTP Benutzer eine Shell auszuwählen',\n\t\t\t'description' => '<strong class=\"text-danger\">Bitte beachten: Shell Zugriff gestattet dem Benutzer verschiedene Programme auf Ihrem System auszuführen. Mit großer Vorsicht verwenden. Bitte aktiviere dies nur wenn WIRKLICH bekannt ist, was das bedeutet!!!</strong>',\n\t\t],\n\t\t'available_shells' => [\n\t\t\t'title' => 'Liste der verfügbaren Shells',\n\t\t\t'description' => 'Komma-getrennte Liste von Shells, die der Kunde für seine FTP-Konten wählen kann.<br><br>Hinweis: Die Standard-Shell <strong>/bin/false</strong> wird immer eine Auswahlmöglichkeit sein (wenn aktiviert), auch wenn diese Einstellung leer ist. Sie ist in jedem Fall der Standardwert für alle FTP-Konten.',\n\t\t],\n\t\t'le_froxlor_enabled' => [\n\t\t\t'title' => 'Let\\'s Encrypt für den froxlor Vhost verwenden',\n\t\t\t'description' => 'Wenn dies aktiviert ist, erstellt froxlor für seinen vhost automatisch ein Let\\'s Encrypt Zertifikat.',\n\t\t],\n\t\t'le_froxlor_redirect' => [\n\t\t\t'title' => 'SSL-Weiterleitung für den froxlor Vhost aktivieren',\n\t\t\t'description' => 'Wenn dies aktiviert ist, werden alle HTTP Anfragen an die entsprechende SSL Seite weitergeleitet.',\n\t\t],\n\t\t'option_unavailable_websrv' => '<br><em class=\"text-danger\">Nur verfügbar für: %s</em>',\n\t\t'option_unavailable' => '<br><em class=\"text-danger\">Option aufgrund anderer Einstellungen nicht verfügbar.</em>',\n\t\t'letsencryptacmeconf' => [\n\t\t\t'title' => 'Pfad zu acme.conf',\n\t\t\t'description' => 'Dateiname der Konfiguration, die dem Webserver erlaubt, die ACME-Challenges zu bedienen.',\n\t\t],\n\t\t'mail_use_smtp' => 'Nutze SMTP für das Senden von E-Mails',\n\t\t'mail_smtp_host' => 'SMTP Server',\n\t\t'mail_smtp_usetls' => 'Aktiviere TLS Verschlüsselung',\n\t\t'mail_smtp_auth' => 'Nutze SMTP Authentifizierung',\n\t\t'mail_smtp_port' => 'TCP Port für SMTP',\n\t\t'mail_smtp_user' => 'SMTP Benutzer',\n\t\t'mail_smtp_passwd' => 'SMTP Passwort',\n\t\t'http2_support' => [\n\t\t\t'title' => 'HTTP2 Unterstützung',\n\t\t\t'description' => 'Aktiviere HTTP2 Unterstützung für SSL.<br><em class=\"text-danger\">NUR AKTIVIEREN, WENN DER WEBSERVER DIESE FUNKTION UNTERSTÜTZT (nginx version 1.9.5+, apache2 version 2.4.17+)</em>',\n\t\t],\n\t\t'http3_support' => [\n\t\t\t'title' => 'HTTP3 Unterstützung',\n\t\t\t'description' => 'Aktiviere HTTP3 Unterstützung für SSL.<br><em class=\"text-danger\">NUR AKTIVIEREN, WENN DER WEBSERVER DIESE FUNKTION UNTERSTÜTZT (nginx version 1.25.0+)</em>',\n\t\t],\n\t\t'nssextrausers' => [\n\t\t\t'title' => 'Verwende libnss-extrausers anstatt libnss-mysql',\n\t\t\t'description' => 'Lese Benutzer nicht direkt aus der Datenbank sondern über Dateien. Bitte nur aktivieren, wenn die entsprechende Konfiguration vorgenommen wurde (System -> libnss-extrausers).<br><strong class=\"text-danger\">Nur für Debian/Ubuntu (oder wenn libnss-extrausers manuell kompiliert wurde!)</strong>',\n\t\t],\n\t\t'le_domain_dnscheck' => [\n\t\t\t'title' => 'Validiere DNS der Domains wenn Let\\'s Encrypt genutzt wird',\n\t\t\t'description' => 'Wenn aktiviert wird froxlor überprüfen ob die DNS Einträge der Domains, welche ein Let\\'s Encrypt Zertifikat beantragt, mindestens auf eine der System IP Adressen auflöst.',\n\t\t],\n\t\t'le_domain_dnscheck_resolver' => [\n\t\t\t'title' => 'DNS Resolver für die DNS Überprüfung',\n\t\t\t'description' => 'IP Adresse des DNS Servers, welcher für die DNS Überprüfung genutzt werden soll. Wenn leer, wird der Standard DNS Resolver des Systems genutzt.',\n\t\t],\n\t\t'phpsettingsforsubdomains' => [\n\t\t\t'description' => 'Wenn ja, wird die gewählte PHP-Config für alle Subdomains übernommen',\n\t\t],\n\t\t'leapiversion' => [\n\t\t\t'title' => 'Wählen Sie die Let\\'s Encrypt ACME Implementierung',\n\t\t\t'description' => 'Aktuell unterstützt froxlor lediglich die ACME v2 Implementierung von Let\\'s Encrypt.',\n\t\t],\n\t\t'enable_api' => [\n\t\t\t'title' => 'Aktiviere externe API Nutzung',\n\t\t\t'description' => 'Um die froxlor API nutzen zu können, muss diese Option aktiviert sein. Für detaillierte Informationen siehe <a href=\"https://docs.froxlor.org/latest/api-guide/\" target=\"_new\">https://docs.froxlor.org/</a>',\n\t\t],\n\t\t'api_customer_default' => '\"Erlaube API Nutzung\" Vorbelegung für neue Kunden',\n\t\t'dhparams_file' => [\n\t\t\t'title' => 'DHParams Datei (Diffie–Hellman key exchange)',\n\t\t\t'description' => 'Wird eine dhparams.pem Datei hier angegeben, wir sie in die Webserver Konfiguration mit eingefügt.<br>Beispiel: /etc/ssl/webserver/dhparams.pem<br><br>Existiert die Datei nicht, wird sie wie folgt erstellt: <code>openssl dhparam -out /etc/ssl/webserver/dhparams.pem 4096</code>. Es wird empfohlen die Datei zu erstellen, bevor sie hier angegeben wird, da die Erstellung längere Zeit in Anspruch nimmt und den Cronjob blockiert.',\n\t\t],\n\t\t'errorlog_level' => [\n\t\t\t'title' => 'Ausführlichkeit des Fehlerprotokolls',\n\t\t\t'description' => 'Steuert die Ausführlichkeit des Fehlerprotokolls. Voreinstellung ist \"warn\" bei Apache und \"error\" bei Nginx.',\n\t\t],\n\t\t'letsencryptecc' => [\n\t\t\t'title' => 'ECC / ECDSA Zertifikate ausstellen',\n\t\t\t'description' => 'Wenn eine Schlüsselgröße ausgewählt wird, werden ECC / ECDSA Zertifikate erstellt',\n\t\t],\n\t\t'froxloraliases' => [\n\t\t\t'title' => 'Domain Aliase für froxlor Vhost',\n\t\t\t'description' => 'Komma getrennte Liste von Domains, welche als Server Alias zum froxlor Vhost hinzugefügt werden',\n\t\t],\n\t\t'default_sslvhostconf' => [\n\t\t\t'title' => 'Standard SSL vHost-Einstellungen',\n\t\t\t'description' => 'Der Inhalt dieses Feldes wird direkt in den IP/Port-vHost-Container übernommen. Die folgenden Variablen können verwendet werden:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (wenn zutreffend)<br/><br /><strong>ACHTUNG:</strong> Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!',\n\t\t],\n\t\t'includedefault_sslvhostconf' => 'Nicht-SSL vHost-Einstellungen in SSL-vHost inkludieren',\n\t\t'apply_specialsettings_default' => 'Standardwert für \"Übernehme Einstellungen für alle Subdomains (*.beispiel.de)\" Einstellung beim Bearbeiten einer Domain',\n\t\t'apply_phpconfigs_default' => 'Standardwert für \"PHP-Config für alle Subdomains übernehmen:\" Einstellung beim Bearbeiten einer Domain',\n\t\t'awstats' => [\n\t\t\t'logformat' => [\n\t\t\t\t'title' => 'LogFormat Einstellung',\n\t\t\t\t'description' => 'Wenn ein benutzerdefiniertes LogFormat beim Webserver verwendet wird, muss LogFormat von awstats ebenso angepasst werden.<br/>Standard ist 1. Für weitere Informationen siehe Dokumentation unter <a target=\"_blank\" href=\"https://awstats.sourceforge.io/docs/awstats_config.html#LogFormat\">hier</a>.',\n\t\t\t],\n\t\t],\n\t\t'hide_incompatible_settings' => 'Inkompatible Einstellungen ausblenden',\n\t\t'soaemail' => 'Mail-Adresse für SOA-Einträge (verwendet Panel-Absender-Name der Panel-Einstellungen falls leer)',\n\t\t'imprint_url' => [\n\t\t\t'title' => 'URL zum Impressum',\n\t\t\t'description' => 'Die URL zur Impressums-Seite. Der Link ist auf der Login-Seite und wenn eingeloggt, in der Fußzeile sichtbar.',\n\t\t],\n\t\t'terms_url' => [\n\t\t\t'title' => 'URL zu den AGB',\n\t\t\t'description' => 'Die URL zur AGB-Seite. Der Link ist auf der Login-Seite und wenn eingeloggt, in der Fußzeile sichtbar.',\n\t\t],\n\t\t'privacy_url' => [\n\t\t\t'title' => 'URL zur Datenschutzerklärung',\n\t\t\t'description' => 'Die URL zur Datenschutzerklärungs-Seite. Der Link ist auf der Login-Seite und wenn eingeloggt, in der Fußzeile sichtbar.',\n\t\t],\n\t\t'logo_image_header' => [\n\t\t\t'title' => 'Logo Bild (Header)',\n\t\t\t'description' => 'Das hochgeladene Bild wird als Logo oben links nach dem Login angezeigt (empfohlene Höhe sind 30px)',\n\t\t],\n\t\t'logo_image_login' => [\n\t\t\t'title' => 'Logo Bild (Login)',\n\t\t\t'description' => 'Das hochgeladene Bild wird als Logo während des Logins angezeigt',\n\t\t],\n\t\t'logo_overridetheme' => [\n\t\t\t'title' => 'Überschreibe Theme-Logo mit \"Logo Bild\" (Header und Login, siehe unten)',\n\t\t\t'description' => 'Ist die Nutzung eines hochgeladenen Logos gewünscht, muss diese Einstellung auf \"Ja\" gesetzt werden. Alternativ kann weiterhin das Theme-basierte Überschreiben via \"logo_custom.png\" und \"logo_custom_login.png\" genutzt werden.',\n\t\t],\n\t\t'logo_overridecustom' => [\n\t\t\t'title' => 'Überschreibe benutzerdefinierte Theme-Logos (logo_custom.png und logo_custom_login.png) mit \"Logo Bild\" (Header und Login, siehe unten)',\n\t\t\t'description' => 'Ist diese Einstellung aktiv, werden benutzerdefinierte Logos im Theme-Ordner mit dem \"Logo Bild\" ersetzt',\n\t\t],\n\t\t'createstdsubdom_default' => [\n\t\t\t'title' => 'Standardwert für \"Standardsubdomain anlegen\" bei Erstellung eines Kunden',\n\t\t\t'description' => '',\n\t\t],\n\t\t'froxlorusergroup' => [\n\t\t\t'title' => 'Benutzerdefinierte Gruppe für alle Kunden-Benutzer',\n\t\t\t'description' => 'Voraussetzung hierfür ist die Nutzung von libnss-extrausers (System-Einstellungen). Ein leerer Wert bedeutet, es wird keine Gruppe erstellt, bzw. vorhandene Gruppe wird entfernt.',\n\t\t],\n\t\t'acmeshpath' => [\n\t\t\t'title' => 'Pfad zu acme.sh',\n\t\t\t'description' => 'Installationspfad zu acme.sh, inklusive acme.sh Script<br>Standard ist <b>/root/.acme.sh/acme.sh</b>',\n\t\t],\n\t\t'update_channel' => [\n\t\t\t'title' => 'froxlor Update Kanal',\n\t\t\t'description' => 'Wähle den bevorzugten Update Kanal. Standard ist \"stable\"',\n\t\t],\n\t\t'traffictool' => [\n\t\t\t'toolselect' => 'Traffic Analyzer',\n\t\t],\n\t\t'requires_reconfiguration' => 'Änderungen an dieser Einstellungen benötigen unter Umständen eine erneute Konfiguration der folgenden Dienste:<br><strong>%s</strong>',\n\t\t'req_limit_per_interval' => [\n\t\t\t'title' => 'Anzahl der HTTP-Anfragen pro Intervall',\n\t\t\t'description' => 'Erlaubte Anzahl von HTTP-Anfragen pro Intervall (siehe unten) auf froxlor, Standard ist \"60\"',\n\t\t],\n\t\t'req_limit_interval' => [\n\t\t\t'title' => 'Rate-Limit-Intervall',\n\t\t\t'description' => 'Zeit in Sekunden für die maximale Anzahl von HTTP-Anfragen, Standard ist \"60\".',\n\t\t],\n\t\t'option_requires_otp' => 'Das Ändern dieser Einstellung erfordert OTP Validierung',\n\t\t'panel_menu_collapsed' => [\n\t\t\t'title' => 'Menüabschnitte einklappen',\n\t\t\t'description' => 'Bei Deaktivierung werden die Menübereiche auf der linken Seite immer aufgeklappt angezeigt.',\n\t\t],\n\t\t'le_renew_services' => [\n\t\t\t'title' => 'Verwende das froxlor Let\\'s Encrypt Zertifikat für folgende Dienste',\n\t\t\t'description' => 'Wenn auf \"Keine\" gesetzt (oder der Renew-Hook-Befehl unten leer ist), werden keine Konfigurationsanpassungen bezüglich SSL an den ausgewählten Diensten vorgenommen.<br><br>Der Reload-Befehl für die ausgewählten Dienste sollte im Renew-Hook-Befehl hinzugefügt werden, da sonst die Konfigurationsänderungen oder erneuerten Zertifikate möglicherweise nicht korrekt angewendet werden.',\n\t\t],\n\t\t'le_renew_hook' => [\n\t\t\t'title' => 'Let\\'s Encrypt Renew-Hook Befehl',\n\t\t\t'description' => 'Lege den Befehl fest, der die oben ausgewählten Dienste neu startet, damit erneuerte Zertifikate vom Dienst ordnungsgemäß verwendet werden.',\n\t\t],\n\t],\n\t'spf' => [\n\t\t'use_spf' => [\n\t\t\t'title' => 'Aktiviere SPF für Domains?',\n\t\t\t'description' => 'Erfordert einen speziellen DNS Eintrag für die Domain. Wenn das Nameserver-Feature nicht genutzt wird, muss dieser Eintrag manuell verwaltet werden.',\n\t\t],\n\t\t'spf_entry' => 'SPF-Eintrag für alle Domains',\n\t],\n\t'dmarc' => [\n\t\t'use_dmarc' => [\n\t\t\t'title' => 'Aktiviere DMARC für Domains?',\n\t\t\t'description' => 'Erfordert einen speziellen DNS Eintrag für die Domain. Wenn das Nameserver-Feature nicht genutzt wird, muss dieser Eintrag manuell verwaltet werden.',\n\t\t],\n\t\t'dmarc_entry' => 'DMARC-Eintrag für alle Domains',\n\t],\n\t'ssl_certificates' => [\n\t\t'certificate_for' => 'Zertifikat für',\n\t\t'valid_from' => 'Gültig ab',\n\t\t'valid_until' => 'Gültig bis',\n\t\t'issuer' => 'Aussteller',\n\t],\n\t'success' => [\n\t\t'success' => 'Information',\n\t\t'clickheretocontinue' => 'Hier klicken, um fortzufahren',\n\t\t'settingssaved' => 'Die Einstellungen wurden erfolgreich gespeichert.',\n\t\t'rebuildingconfigs' => 'Task für Neuerstellung der Konfigurationen wurde erfolgreich eingetragen',\n\t\t'domain_import_successfully' => 'Erfolgreich %s Domains importiert.',\n\t\t'exportscheduled' => 'Ihr Daten-Export wurde erfolgreich geplant. Bitte warten Sie nun, bis dieser bearbeitet wurde.',\n\t\t'exportaborted' => 'Der geplante Daten-Export wurde abgebrochen',\n\t\t'dns_record_added' => 'Eintrag erfolgreich hinzugefügt',\n\t\t'dns_record_deleted' => 'Eintrag erfolgreich entfernt',\n\t\t'testmailsent' => 'Test E-Mail erfolgreich gesendet',\n\t\t'settingsimported' => 'Einstellungnen erfolgreich importiert',\n\t\t'sent_error_report' => 'Fehlerbericht erfolgreich gesendet. Danke für die Unterstützung.',\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Ausstehende Cron-Aufgaben',\n\t\t'REBUILD_VHOST' => 'Neuerstellung der Webserver-Konfiguration',\n\t\t'CREATE_HOME' => 'Erstelle neuen Kunden %s',\n\t\t'REBUILD_DNS' => 'Neuerstellung der Bind-Konfiguration',\n\t\t'CREATE_FTP' => 'Erstelle Verzeichnis für neuen FTP-Benutzer',\n\t\t'DELETE_CUSTOMER_FILES' => 'Löschen von Kunden-Dateien %s',\n\t\t'noneoutstanding' => 'Zur Zeit gibt es keine ausstehenden Aufgaben für froxlor',\n\t\t'DELETE_EMAIL_DATA' => 'E-Mail-Dateien des Kunden löschen',\n\t\t'DELETE_FTP_DATA' => 'Kunden FTP-Konto Dateien löschen',\n\t\t'REBUILD_RSPAMD' => 'Neuerstellung der Antispam-Konfiguration',\n\t\t'CREATE_QUOTA' => 'Quota auf dem Dateisystem setzen',\n\t\t'REBUILD_CRON' => 'Neuerstellung der cron.d-Datei',\n\t\t'CREATE_CUSTOMER_DATADUMP' => 'Daten-Export für Kunde %s',\n\t\t'DELETE_DOMAIN_PDNS' => 'Lösche Domain %s von PowerDNS Datenbank',\n\t\t'DELETE_DOMAIN_SSL' => 'Lösche SSL Dateien von Domain %s',\n\t\t'UPDATE_LE_SERVICES' => 'Aktualisiere Systemdienste für Let\\'s Encrypt',\n\t\t'REBUILD_NSSUSERS' => 'Neuerstellung der libnss-extrausers Dateien',\n\t],\n\t'terms' => 'AGB',\n\t'traffic' => [\n\t\t'month' => 'Monat',\n\t\t'months' => [\n\t\t\t1 => 'Januar',\n\t\t\t2 => 'Februar',\n\t\t\t3 => 'März',\n\t\t\t4 => 'April',\n\t\t\t5 => 'Mai',\n\t\t\t6 => 'Juni',\n\t\t\t7 => 'Juli',\n\t\t\t8 => 'August',\n\t\t\t9 => 'September',\n\t\t\t10 => 'Oktober',\n\t\t\t11 => 'November',\n\t\t\t12 => 'Dezember',\n\t\t\t'jan' => 'Jan',\n\t\t\t'feb' => 'Feb',\n\t\t\t'mar' => 'Mär',\n\t\t\t'apr' => 'Apr',\n\t\t\t'may' => 'Mai',\n\t\t\t'jun' => 'Jun',\n\t\t\t'jul' => 'Jul',\n\t\t\t'aug' => 'Aug',\n\t\t\t'sep' => 'Sep',\n\t\t\t'oct' => 'Okt',\n\t\t\t'nov' => 'Nov',\n\t\t\t'dec' => 'Dez',\n\t\t\t'total' => 'Gesamt',\n\t\t],\n\t\t'mb' => 'Traffic',\n\t\t'day' => 'Tag',\n\t\t'sumtotal' => 'Gesamt Traffic',\n\t\t'sumhttp' => 'HTTP-Traffic',\n\t\t'sumftp' => 'FTP-Traffic',\n\t\t'summail' => 'Mail-Traffic',\n\t\t'customer' => 'Kunde',\n\t\t'trafficoverview' => 'Übersicht Traffic',\n\t\t'bycustomers' => 'Traffic nach Kunden',\n\t\t'details' => 'Details',\n\t\t'http' => 'HTTP',\n\t\t'ftp' => 'FTP',\n\t\t'mail' => 'Mail',\n\t\t'nocustomers' => 'Es wird mindestens ein Kunde benötigt um die Traffic Statistiken anzuzeigen.',\n\t\t'top5customers' => 'Top 5 Kunden',\n\t\t'nodata' => 'Keine Daten im angegebenen Zeitraum.',\n\t\t'ranges' => [\n\t\t\t'last24h' => 'die letzten 24 Std',\n\t\t\t'last7d' => 'die letzten 7 Tage',\n\t\t\t'last30d' => 'die letzten  30 Tage',\n\t\t\t'cm' => 'Aktueller Monat',\n\t\t\t'last3m' => 'die letzten 3 Monate',\n\t\t\t'last6m' => 'die letzten 6 Monate',\n\t\t\t'last12m' => 'die letzten 12 Monate',\n\t\t\t'cy' => 'Aktuelles Jahr',\n\t\t],\n\t\t'byrange' => 'Nach angegebenem Zeitraum',\n\t],\n\t'translator' => '',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'Eine neuere Version von froxlor wurde installiert, aber noch nicht eingerichtet.<br />Nur der Administrator kann sich anmelden und die Aktualisierung abschließen.',\n\t\t'update' => 'froxlor Aktualisierung',\n\t\t'proceed' => 'Ausführen',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'Die froxlor-Dateien wurden aktualisiert. Neue Version ist <strong>%s</strong>. Die bisher installierte Version ist <strong>%s</strong>',\n\t\t\t'part_b' => '<br /><br />Ein Kunden-Login ist vor Abschluss des Aktualisierungsvorganges nicht möglich.<br /><strong>Aktualisierung ausführen?</strong>',\n\t\t],\n\t\t'noupdatesavail' => 'Die genutzte %sVersion von froxlor ist aktuell.',\n\t\t'description' => 'Aktualisierung der froxlor Datenbank',\n\t\t'uc_newinfo' => 'Eine neuere %sVersion ist verfügbar: \"%s\" (Aktuell installierte Version: %s)',\n\t\t'notify_subject' => 'Neues Update verfügbar',\n\t\t'dbupdate_required' => 'froxlor-Dateien wurden aktualisiert, Datenbank-Aktualisierung notwendig',\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => 'Eigene Notizen',\n\t\t\t'description' => 'Hier können Notizen je nach Lust und Laune eingetragen werden. Diese werden in der Administrator/Kunden-Übersicht bei dem jeweiligen Benutzer angezeigt.<br>Markdown ist unterstützt, HTML wird entfernt.',\n\t\t\t'show' => 'Zeige die Notizen auf dem Dashboard des Benutzers',\n\t\t],\n\t\t'api_allowed' => [\n\t\t\t'title' => 'Erlaube API Zugriff',\n\t\t\t'description' => 'Wenn in den Einstellungen aktiviert, kann der Benutzer API Schlüssel erstellen und auf die froxlor API Zugreifen',\n\t\t\t'notice' => 'API Zugriff ist für dieses Konto deaktiviert.',\n\t\t],\n\t\t'shell_allowed' => [\n\t\t\t'title' => 'Erlaube Shell Zugriff',\n\t\t\t'description' => 'Wenn in den Einstellungen aktiviert, kann der Benutzer seinen FTP-Konten Shell Zugriff erlauben',\n\t\t],\n\t\t'gui_access' => [\n\t\t\t'title' => 'WebUI-Anmeldung zulassen',\n\t\t\t'description' => 'Wenn diese Option deaktiviert ist, kann sich der Benutzer nicht bei der froxlor-Weboberfläche anmelden, aber alle Dienste (Web, FTP, E-Mail, Datenbanken, API-Zugriff, usw.) funktionieren normal.',\n\t\t],\n\t],\n\t'install' => [\n\t\t'preflight' => 'System-Prüfung',\n\t\t'critical_error' => 'Kritischer Fehler',\n\t\t'suggestions' => 'Nicht notwendig, aber emfohlen',\n\t\t'phpinfosuccess' => 'Auf dem System ist PHP %s installiert',\n\t\t'suggestionsnote' => 'Es liegen keine kritische Fehler vor, die eine Installation verhindern, allerdings beachten Sie bitte die Empfehlungen weiter unten für ein optimales Erlebnis.',\n\t\t'phpinfowarn' => 'Die genutzte PHP Version ist kleiner als die geforderte Version %s',\n\t\t'phpinfoupdate' => 'Aktualisierung von PHP Version %s auf %s oder höher notwendig',\n\t\t'start_installation' => 'Installation starten',\n\t\t'check_again' => 'Erneut prüfen...',\n\t\t'switchmode_advanced' => 'Erweiterte Optionen anzeigen',\n\t\t'switchmode_basic' => 'Erweiterte Optionen ausblenden',\n\t\t'dependency_check' => [\n\t\t\t'title' => 'Willkommen bei froxlor',\n\t\t\t'description' => 'Das System wird auf Abhängigkeiten überprüft, um sicher zu stellen das alle erforderlichen PHP Erweiterungen vorhanden und aktiviert sind, so dass froxlor einwandfrei funktioniert.',\n\t\t],\n\t\t'database' => [\n\t\t\t'top' => 'Datenbank',\n\t\t\t'title' => 'Datenbank und Benutzer erstellen',\n\t\t\t'description' => 'froxlor benötigt eine Datenbank und zusätzlich <a href=\"https://docs.froxlor.org/latest/general/installation/tarball.html#_3-create-privileged-database-user\" target=\"_blank\">einen Benutzer mit privilegierten Rechten</a>, welcher Benutzer und Datenbanken erstellen darf (GRANT Option). Die angegebene Datenbank und der unprivilegierte Benutzer werden automatisch in diesem Prozess erstellt. Der privilegierte Benutzer muss existieren.',\n\t\t\t'user' => 'Unprivilegierter Datenbank Benutzer',\n\t\t\t'dbname' => 'Datenbank Name',\n\t\t\t'force_create' => 'Sichern und überschreiben, sofern Datenbank existiert?',\n\t\t],\n\t\t'admin' => [\n\t\t\t'top' => 'Admin Konto',\n\t\t\t'title' => 'Erstellen des Haupt-Administrators.',\n\t\t\t'description' => 'Dieser Benutzer erhält alle Berechtigungen zur Anpassungen von Einstellungen und Erstellen/Bearbeiten/Löschen von Resourcen wie Kunden, Domains, etc.',\n\t\t\t'use_admin_email_as_sender' => 'Verwende die oben angegebene E-Mail-Adresse als Absenderadresse. Wenn die Option deaktiviert ist, geben Sie unten bitte eine Absenderadresse an.',\n\t\t\t'use_autogenerated_email_as_sender' => 'Leer lassen für Standard: admin@servername',\n\t\t],\n\t\t'system' => [\n\t\t\t'top' => 'System Setup',\n\t\t\t'title' => 'Detailangaben zum Server',\n\t\t\t'description' => 'Gebe hier Informationen und relevante Daten zu dem Server an damit froxlor dein System kennt. Diese Angaben sind entscheidend für die Konfiguration und den Betrieb der Dienste.',\n\t\t\t'ipv4' => 'Primäre IPv4 Adresse (sofern zutreffend)',\n\t\t\t'ipv6' => 'Primäre IPv6 Adresse (sofern zutreffend)',\n\t\t\t'servername' => 'Server Name (FQDN, keine IP Adresse)',\n\t\t\t'phpbackend' => 'PHP Backend',\n\t\t\t'activate_newsfeed' => 'Offizielles Newsfeed aktivieren<br><small>(externe Quelle: https://inside.froxlor.org/news/)</small>',\n\t\t],\n\t\t'install' => [\n\t\t\t'top' => 'Abschluss',\n\t\t\t'title' => 'Ein letzter Schritt...',\n\t\t\t'description' => 'Der untenstehende Befehl lädt, installiert und konfiguriert die benötigten Dienste auf dem System aufgrund der Angaben die während des Installationsprozessen gesammelt wurden.<br><br><span class=\"text-danger\">Führe die gezeigten Befehle als <b>root</b> in der Shell/Konsole des Servers aus. <b>Beachte bitte</b> das dieser Befehl vorhandene Konfigurationen <b>überschreibt</b> (Sicherungsdateien werden erstellt)!<br>Sollte dies nicht gewünscht sein, wähle <i>Ich werden die Dienste manuell konfigurieren</i> am Ende dieser Seite.</span>',\n\t\t\t'runcmd' => 'Folgende Befehle ausführen, um die Installation abzuschließen:',\n\t\t\t'manual_config' => 'Ich werden die Dienste manuell konfigurieren, direkt zum Login umleiten',\n\t\t\t'waitforconfig' => 'Warte auf Abschluss der Dienstkonfiguration...',\n\t\t],\n\t\t'errors' => [\n\t\t\t'wrong_ownership' => 'Die froxlor Dateien gehören nicht vollständig dem Benutzer %s:%s',\n\t\t\t'missing_extensions' => 'Folgende PHP Erweiterungen werden zusätzlich benötigt',\n\t\t\t'suggestedextensions' => 'Folgende PHP Erweiterungen werden empfohlen',\n\t\t\t'databaseexists' => 'Datenbank existiert bereits, Uberschreiben-Option oder anderen Namen wählen.',\n\t\t\t'unabletocreatedb' => 'Test-Datenbank konnte nicht erstellt werden',\n\t\t\t'unabletodropdb' => 'Test-Datenbank konnte nicht gelöscht werden',\n\t\t\t'mysqlusernameexists' => 'Der unprivilegierte Datenbank-Benutzer existiert bereits. Anderen Benutzername angeben oder vorhandenen Benutzer löschen.',\n\t\t\t'unabletocreateuser' => 'Test-Benutzer konnte nicht erstellt werden',\n\t\t\t'unabletodropuser' => 'Test-Benutzer konnte nicht gelöscht werden',\n\t\t\t'unabletoflushprivs' => 'Der privilegierte Benutzer kann Berechtigungen nicht übernehmen (FLUSH PRIVILEGES)',\n\t\t\t'nov4andnov6ip' => 'Es muss mindestens eine IPv4- oder IPv6-Adresse angegeben werden',\n\t\t\t'servernameneedstobevalid' => 'Der angegebene Server-Name scheint kein gültiger FQDN oder Hostname zu sein',\n\t\t\t'websrvuserdoesnotexist' => 'Der angegebene Webserver-Benutzer scheint auf dem System nicht zu existieren',\n\t\t\t'websrvgrpdoesnotexist' => 'Die angegebene Webserver-Gruppe scheint auf dem System nicht zu existieren',\n\t\t\t'notyetconfigured' => 'Es scheint als wären die Dienste (noch) nicht erfolgreich konfiguriert worden. Bitte den angezeigten Befehl ausführen oder überspringen (direkt zum Login)',\n\t\t\t'mandatory_field_not_set' => 'Pflichtfeld \"%s\" ist nicht gesetzt!',\n\t\t\t'unexpected_database_error' => 'Eine unerwarteter Datenbankfehler ist aufgetreten. %s',\n\t\t\t'sql_import_failed' => 'Der Import von SQL-Daten ist fehlgeschlagen!',\n\t\t\t'unprivileged_sql_connection_failed' => 'Unprivilegierte SQL-Verbindung konnte nicht initialisiert werden!',\n\t\t\t'privileged_sql_connection_failed' => 'Initialisierung der privilegierten SQL-Verbindung fehlgeschlagen!',\n\t\t\t'mysqldump_backup_failed' => 'Es kann keine Datenbanksicherung erstellt werden, wir haben einen Fehler von mysqldump erhalten.',\n\t\t\t'sql_backup_file_missing' => 'Es kann keine Datenbanksicherung erstellt werden, die Sicherungsdatei ist nicht vorhanden.',\n\t\t\t'backup_binary_missing' => 'Es kann keine Datenbanksicherung erstellen werden, stelle sicher, dass mysqldump installiert wurde.',\n\t\t\t'creating_configfile_failed' => 'Konfigurationsdateien können nicht erstellt werden, Datei kann nicht geschrieben werden.',\n\t\t\t'database_already_exiting' => 'Es existiert bereits eine Datenbank, diese darf aber nicht überschreiben werden!'\n\t\t]\n\t],\n\t'welcome' => [\n\t\t'title' => 'Willkommen bei froxlor!',\n\t\t'config_note' => 'Damit froxlor mit dem Backend vernünftig kommunizieren kann, musst du dieses noch konfigurieren.',\n\t\t'config_now' => 'Jetzt konfigurieren'\n\t],\n];\n"
  },
  {
    "path": "lng/en.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'languages' => [\n\t\t'cz' => 'Czech',\n\t\t'de' => 'German',\n\t\t'en' => 'English',\n\t\t'fr' => 'French',\n\t\t'hu' => 'Hungarian',\n\t\t'it' => 'Italian',\n\t\t'nl' => 'Dutch',\n\t\t'pt' => 'Portuguese',\n\t\t'se' => 'Swedish',\n\t\t'sk' => 'Slovak',\n\t\t'es' => 'Spanish',\n\t\t'ca' => 'Catalan',\n\t\t'zh_CN' => 'Chinese (Simplified)',\n\t],\n\t'2fa' => [\n\t\t'2fa' => '2FA options',\n\t\t'2fa_enabled' => 'Activate Two-factor authentication (2FA)',\n\t\t'2fa_removed' => '2FA removed successfully',\n\t\t'2fa_added' => '2FA activated successfully<br><a class=\"alert-link\" href=\"%s?page=2fa\">View 2FA details</a>',\n\t\t'2fa_add' => 'Activate 2FA',\n\t\t'2fa_delete' => 'Deactivate 2FA',\n\t\t'2fa_verify' => 'Verify code',\n\t\t'2fa_overview_desc' => 'Here you can activate a two-factor authentication for your account.<br><br>You can either use an authenticator-app (time-based one-time password / TOTP) or let froxlor send you an email to your account-address after each successful login with a one-time password.',\n\t\t'2fa_email_desc' => 'Your account is set up to use one-time passwords via e-mail. To deactivate, click on \"Deactivate 2FA\"',\n\t\t'2fa_ga_desc' => 'Your account is set up to use time-based one-time passwords via authenticator-app. Please scan the QR code below with your desired authenticator app to generate the codes. To deactivate, click on \"Deactivate 2FA\"',\n\t\t'2fa_not_activated' => 'Two-factor authentication is not enabled',\n\t\t'2fa_not_activated_for_user' => 'Two-factor authentication is not enabled for the current user',\n\t\t'type_2fa' => '2FA status',\n\t],\n\t'admin' => [\n\t\t'overview' => 'Overview',\n\t\t'ressourcedetails' => 'Used resources',\n\t\t'systemdetails' => 'System details',\n\t\t'froxlordetails' => 'froxlor details',\n\t\t'installedversion' => 'Installed version',\n\t\t'latestversion' => 'Latest version',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'Search via webservice',\n\t\t\t'error' => 'Error while reading',\n\t\t],\n\t\t'resources' => 'Resources',\n\t\t'customer' => 'Customer',\n\t\t'customers' => 'Customers',\n\t\t'customers_list_desc' => 'Manage your customers',\n\t\t'customer_add' => 'Create customer',\n\t\t'customer_edit' => 'Edit customer',\n\t\t'username_default_msg' => 'Leave empty for autogenerated value',\n\t\t'password_default_msg' => 'Autogenerated if empty',\n\t\t'domains' => 'Domains',\n\t\t'domain_add' => 'Create domain',\n\t\t'domain_edit' => 'Edit domain',\n\t\t'subdomainforemail' => 'Subdomains as email-domains',\n\t\t'admin' => 'Admin',\n\t\t'admins' => 'Admins',\n\t\t'admin_add' => 'Create admin',\n\t\t'admin_edit' => 'Edit admin',\n\t\t'customers_see_all' => 'Can access other admins/resellers resources?',\n\t\t'change_serversettings' => 'Can change server settings?',\n\t\t'server' => 'System',\n\t\t'serversettings' => 'Settings',\n\t\t'serversettings_desc' => 'Manage your froxlor system',\n\t\t'rebuildconf' => 'Rebuild config files',\n\t\t'stdsubdomain' => 'Standard subdomain',\n\t\t'stdsubdomain_add' => 'Create standard subdomain',\n\t\t'phpenabled' => 'PHP enabled',\n\t\t'deactivated' => 'Deactivated',\n\t\t'deactivated_user' => 'Deactivate user',\n\t\t'sendpassword' => 'Send password',\n\t\t'ownvhostsettings' => 'Own vHost-settings',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Configuration',\n\t\t\t'overview' => 'Overview',\n\t\t\t'wizard' => 'Wizard',\n\t\t\t'distribution' => 'Distribution',\n\t\t\t'service' => 'Service',\n\t\t\t'daemon' => 'Daemon',\n\t\t\t'http' => 'Webserver (HTTP)',\n\t\t\t'dns' => 'Nameserver (DNS)',\n\t\t\t'mail' => 'Mailserver (IMAP/POP3)',\n\t\t\t'smtp' => 'Mailserver (SMTP)',\n\t\t\t'ftp' => 'FTP-server',\n\t\t\t'etc' => 'Others (System)',\n\t\t\t'choosedistribution' => '-- Choose a distribution --',\n\t\t\t'chooseservice' => '-- Choose a service --',\n\t\t\t'choosedaemon' => '-- Choose a daemon --',\n\t\t\t'statistics' => 'Statistics',\n\t\t\t'compactoverview' => 'Compact-overview',\n\t\t\t'legend' => '<h3>You are about to configure a service/daemon</h3>',\n\t\t\t'commands' => '<span class=\"text-danger\">Commands:</span> These commands are to be executed line by line as root-user in a shell. It is safe to copy the whole block and paste it into the shell.',\n\t\t\t'files' => '<span class=\"text-danger\">Config files:</span> The commands before the textfields should open an editor with the target file. Just copy and paste the contents into the editor and save the file.<br><span class=\"text-danger\">Please note:</span> The MySQL-password has not been replaced for security reasons. Please replace \"FROXLOR_MYSQL_PASSWORD\" on your own or use the javascript form below to replace it on-site. If you forgot your MySQL-password you\\'ll find it in \"lib/userdata.inc.php\"',\n\t\t\t'importexport' => 'Import/Export',\n\t\t\t'finishnote' => 'Parameter file generated successfully. Now run the following command as root:',\n\t\t\t'description' => 'Configure the system services',\n\t\t\t'minihowto' => 'On this page you can view the different configuration templates for each service, (re-)configure specific services if needed or export the current selection to a JSON file to use in the CLI scripts or on another server.<br><br><b>Note</b> that the services highlighted do not reflect your current setup but show requirements/recommendations from your current setting values.',\n\t\t\t'skipconfig' => 'Don\\'t (re)configure',\n\t\t\t'recommendednote' => 'Recommended/required services based on current system settings',\n\t\t\t'selectrecommended' => 'Select recommended',\n\t\t\t'downloadselected' => 'Export selected',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Email-templates',\n\t\t\t'template_add' => 'Add template',\n\t\t\t'template_fileadd' => 'Add file template',\n\t\t\t'template_edit' => 'Edit template',\n\t\t\t'action' => 'Action',\n\t\t\t'email' => 'Email & file templates',\n\t\t\t'subject' => 'Subject',\n\t\t\t'mailbody' => 'Mail body',\n\t\t\t'createcustomer' => 'Welcome mail for new customers',\n\t\t\t'pop_success' => 'Welcome mail for new email accounts',\n\t\t\t'template_replace_vars' => 'Variables to be replaced in the template:',\n\t\t\t'SALUTATION' => 'Replaced with a correct salutation (name or company)',\n\t\t\t'FIRSTNAME' => 'Replaced with the customer\\'s first name.',\n\t\t\t'NAME' => 'Replaced with the customers name.',\n\t\t\t'COMPANY' => 'Replaces with the customer\\'s company name',\n\t\t\t'USERNAME' => 'Replaced with the customer\\'s account username.',\n\t\t\t'PASSWORD' => 'Replaced with the customer\\'s account password.',\n\t\t\t'EMAIL' => 'Replaced with the address of the POP3/IMAP account.',\n\t\t\t'CUSTOMER_NO' => 'Replaces with the customer number',\n\t\t\t'TRAFFIC' => 'Replaced with the traffic, which was assigned to the customer.',\n\t\t\t'TRAFFICUSED' => 'Replaced with the traffic, which was exhausted by the customer.',\n\t\t\t'pop_success_alternative' => 'Welcome mail for new email accounts sent to alternative address',\n\t\t\t'EMAIL_PASSWORD' => 'Replaced with the POP3/IMAP account password.',\n\t\t\t'index_html' => 'index file for newly created customer directories',\n\t\t\t'unconfigured_html' => 'index file for unconfigured/unknown domains',\n\t\t\t'unconfigured_content_fallback' => 'This domain requires configuration via the froxlor server management panel, as it is currently not assigned to any customer.',\n\t\t\t'file_extension' => [\n\t\t\t\t'description' => 'The file extension for the index file must be between 1 and 6 characters long. The extension can only contain characters like a-z, A-Z and 0-9<br><br>Default: html',\n\t\t\t\t'title' => 'File extension for the file template',\n\t\t\t],\n\t\t\t'SERVERNAME' => 'Replaced with the servername.',\n\t\t\t'CUSTOMER' => 'Replaced with the loginname of the customer. Only for \"index file for newly created customer directories\"',\n\t\t\t'ADMIN' => 'Replaced with the loginname of the admin. Only for \"index file for newly created customer directories\"',\n\t\t\t'CUSTOMER_EMAIL' => 'Replaced with the e-mail address of the customer. Only for \"index file for newly created customer directories\"',\n\t\t\t'ADMIN_EMAIL' => 'Replaced with the e-mail address of the admin. Only for \"index file for newly created customer directories\"',\n\t\t\t'filetemplates' => 'File templates',\n\t\t\t'filecontent' => 'File content',\n\t\t\t'new_database_by_customer' => 'Customer-notification when a database has been created',\n\t\t\t'new_ftpaccount_by_customer' => 'Customer-notification when a ftp-user has been created',\n\t\t\t'newdatabase' => 'Notification-mails for new databases',\n\t\t\t'newftpuser' => 'Notification-mails for new ftp-user',\n\t\t\t'CUST_NAME' => 'Customer name',\n\t\t\t'DB_NAME' => 'Database name',\n\t\t\t'DB_PASS' => 'Database password',\n\t\t\t'DB_DESC' => 'Database description',\n\t\t\t'DB_SRV' => 'Database server',\n\t\t\t'PMA_URI' => 'URL to phpMyAdmin (if given)',\n\t\t\t'USR_NAME' => 'FTP username',\n\t\t\t'USR_PASS' => 'FTP password',\n\t\t\t'USR_PATH' => 'FTP homedir (relative to customer-docroot)',\n\t\t\t'forgotpwd' => 'Notification-mails for password-reset',\n\t\t\t'password_reset' => 'Customer-notification for password-reset',\n\t\t\t'trafficmaxpercent' => 'Notification mail for customers when given maximum of percent of traffic is exhausted',\n\t\t\t'MAX_PERCENT' => 'Replaced with the diskusage/traffic limit for sending reports in percent.',\n\t\t\t'USAGE_PERCENT' => 'Replaced with the diskusage/traffic, which was exhausted by the customer in percent.',\n\t\t\t'diskmaxpercent' => 'Notification mail for customers when given maximum of percent of diskspace is exhausted',\n\t\t\t'DISKAVAILABLE' => 'Replaced with the diskusage, which was assigned to the customer.',\n\t\t\t'DISKUSED' => 'Replaced with the diskusage, which was exhausted by the customer.',\n\t\t\t'LINK' => 'Replaced with the customers password reset link.',\n\t\t\t'SERVER_HOSTNAME' => 'Replaces the system-hostname (URL to froxlor)',\n\t\t\t'SERVER_IP' => 'Replaces the default server ip-address',\n\t\t\t'SERVER_PORT' => 'Replaces the default server port',\n\t\t\t'DOMAINNAME' => 'Replaces the customers standard-subdomain (can be empty if none is generated)',\n\t\t],\n\t\t'webserver' => 'Webserver',\n\t\t'createzonefile' => 'Create dns zone for domain',\n\t\t'custombindzone' => 'Custom / unmanaged zone file',\n\t\t'bindzonewarning' => 'empty for defaults<br /><strong class=\"text-danger\">ATTENTION:</strong> If you use a zonefile you will have to manage all required records for all sub-zones manually as well.',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IPs and Ports',\n\t\t\t'add' => 'Add IP/Port',\n\t\t\t'edit' => 'Edit IP/Port',\n\t\t\t'ipandport' => 'IP/Port',\n\t\t\t'ip' => 'IP',\n\t\t\t'ipnote' => '<div id=\"ipnote\" class=\"invalid-feedback\">Note: Although private ip addresses are allowed, some features like DNS might not behave correctly.<br>Only use private ip addresses if you are sure.</div>',\n\t\t\t'port' => 'Port',\n\t\t\t'create_listen_statement' => 'Create Listen statement',\n\t\t\t'create_namevirtualhost_statement' => 'Create NameVirtualHost statement',\n\t\t\t'create_vhostcontainer' => 'Create vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Create ServerName statement in vHost-Container',\n\t\t\t'enable_ssl' => 'Is this an SSL Port?',\n\t\t\t'ssl_cert_file' => 'Path to the SSL Certificate',\n\t\t\t'webserverdefaultconfig' => 'Webserver default config',\n\t\t\t'webserverdomainconfig' => 'Webserver domain config',\n\t\t\t'webserverssldomainconfig' => 'Webserver SSL config',\n\t\t\t'ssl_key_file' => 'Path to the SSL Keyfile',\n\t\t\t'ssl_ca_file' => 'Path to the SSL CA certificate',\n\t\t\t'default_vhostconf_domain' => 'Default vHost-settings for every domain container',\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'Path to the SSL CertificateChainFile',\n\t\t\t\t'description' => 'Mostly CA_Bundle, or similar, you probably want to set this if you bought a SSL certificate.',\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Custom docroot (empty = point to froxlor)',\n\t\t\t\t'description' => 'You can define a custom document-root (the destination for a request) for this ip/port combination here.<br /><strong>ATTENTION:</strong> Please be careful with what you enter here!',\n\t\t\t],\n\t\t\t'ssl_paste_description' => 'Paste your complete certificate content in the textbox',\n\t\t\t'ssl_cert_file_content' => 'Content of the ssl certificate',\n\t\t\t'ssl_key_file_content' => 'Content of the ssl (private-) key file',\n\t\t\t'ssl_ca_file_content' => 'Content of the ssl CA file (optional)',\n\t\t\t'ssl_ca_file_content_desc' => '<br /><br />Client authentication, set this only if you know what it is.',\n\t\t\t'ssl_cert_chainfile_content' => 'Content of the certificate chain file (optional)',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br /><br />Mostly CA_Bundle, or similar, you probably want to set this if you bought a SSL certificate.',\n\t\t\t'ssl_default_vhostconf_domain' => 'Default SSL vHost-settings for every domain container',\n\t\t],\n\t\t'memorylimitdisabled' => 'Disabled',\n\t\t'valuemandatory' => 'This value is mandatory',\n\t\t'valuemandatorycompany' => 'Either \"name\" and \"firstname\" or \"company\" must be filled',\n\t\t'serversoftware' => 'Serversoftware',\n\t\t'phpversion' => 'PHP-Version',\n\t\t'mysqlserverversion' => 'MySQL server version',\n\t\t'webserverinterface' => 'Webserver interface',\n\t\t'accountsettings' => 'Account settings',\n\t\t'panelsettings' => 'Panel settings',\n\t\t'systemsettings' => 'System settings',\n\t\t'webserversettings' => 'Webserver settings',\n\t\t'mailserversettings' => 'Mailserver settings',\n\t\t'nameserversettings' => 'Nameserver settings',\n\t\t'updatecounters' => 'Recalculate resource usage',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Never',\n\t\t\t'choosableno' => 'Choosable, default no',\n\t\t\t'choosableyes' => 'Choosable, default yes',\n\t\t\t'always' => 'Always',\n\t\t],\n\t\t'wipecleartextmailpwd' => 'Clear plaintext passwords',\n\t\t'webalizersettings' => 'Webalizer settings',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normal',\n\t\t\t'quiet' => 'Quiet',\n\t\t\t'veryquiet' => 'No output',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'It\\'s not possible to add a domain currently. You first need to add at least one customer.',\n\t\t'loggersettings' => 'Log settings',\n\t\t'logger' => [\n\t\t\t'normal' => 'normal',\n\t\t\t'paranoid' => 'paranoid',\n\t\t],\n\t\t'emaildomain' => 'Emaildomain',\n\t\t'email_only' => 'Only email?',\n\t\t'wwwserveralias' => 'Add a \"www.\" ServerAlias',\n\t\t'subject' => 'Subject',\n\t\t'recipient' => 'Recipient',\n\t\t'message' => 'Write a Message',\n\t\t'text' => 'Message',\n\t\t'sslsettings' => 'SSL settings',\n\t\t'specialsettings_replacements' => 'You can use the following variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (if applicable)<br/>',\n\t\t'antispam_settings' => 'Antispam settings',\n\t\t'caneditphpsettings' => 'Can change php-related domain settings?',\n\t\t'allips' => 'All IP\\'s',\n\t\t'awstatssettings' => 'AWstats settings',\n\t\t'domain_dns_settings' => 'Domain dns settings',\n\t\t'activated' => 'Activated',\n\t\t'statisticsettings' => 'Statistic settings',\n\t\t'or' => 'or',\n\t\t'sysload' => 'System load',\n\t\t'noloadavailable' => 'not available',\n\t\t'nouptimeavailable' => 'not available',\n\t\t'nosubject' => '(No Subject)',\n\t\t'security_settings' => 'Security Options',\n\t\t'know_what_youre_doing' => 'Change only, if you know what you\\'re doing!',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Show froxlor version on login',\n\t\t\t'description' => 'Show the froxlor version in the footer on the login page',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Show froxlor version in footer',\n\t\t\t'description' => 'Show the froxlor version in the footer on the rest of the pages',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Header graphic for froxlor',\n\t\t\t'description' => 'What graphic should be shown in the header',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'PHP Configuration',\n\t\t\t'description' => 'Short description',\n\t\t\t'actions' => 'Actions',\n\t\t\t'activedomains' => 'In use for domain(s)',\n\t\t\t'notused' => 'Configuration not in use',\n\t\t\t'editsettings' => 'Change PHP settings',\n\t\t\t'addsettings' => 'Create new PHP settings',\n\t\t\t'viewsettings' => 'View PHP settings',\n\t\t\t'phpinisettings' => 'php.ini settings',\n\t\t\t'addnew' => 'Create new PHP configuration',\n\t\t\t'binary' => 'PHP Binary',\n\t\t\t'fpmdesc' => 'PHP-FPM config',\n\t\t\t'file_extensions' => 'File extensions',\n\t\t\t'file_extensions_note' => '(without dot, separated by spaces)',\n\t\t\t'enable_slowlog' => 'Enable slowlog (per domain)',\n\t\t\t'request_terminate_timeout' => 'Request terminate-timeout',\n\t\t\t'request_slowlog_timeout' => 'Request slowlog-timeout',\n\t\t\t'activephpconfigs' => 'In use for php-config(s)',\n\t\t\t'pass_authorizationheader' => 'Passing HTTP AUTH BASIC/DIGEST headers from Apache to PHP',\n\t\t],\n\t\t'misc' => 'Miscellaneous',\n\t\t'fpmsettings' => [\n\t\t\t'addnew' => 'Create new PHP version',\n\t\t\t'edit' => 'Change PHP version'\n\t\t],\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'Variables that will be replaced in the configs',\n\t\t\t'pear_dir' => 'Will be replaced with the global setting for the pear directory.',\n\t\t\t'open_basedir_c' => 'Will insert a ; (semicolon) to comment-out/disable open_basedir when set',\n\t\t\t'open_basedir' => 'Will be replaced with the open_basedir setting of the domain.',\n\t\t\t'tmp_dir' => 'Will be replaced with the temporary directory of the domain.',\n\t\t\t'open_basedir_global' => 'Will be replaced with the global value of the path which will be attached to the open_basedir (see webserver settings).',\n\t\t\t'customer_email' => 'Will be replaced with the e-mail address of the customer who owns this domain.',\n\t\t\t'admin_email' => 'Will be replaced with e-mail address of the admin who owns this domain.',\n\t\t\t'domain' => 'Will be replaced with the domain.',\n\t\t\t'customer' => 'Will be replaced with the loginname of the customer who owns this domain.',\n\t\t\t'admin' => 'Will be replaced with the loginname of the admin who owns this domain.',\n\t\t\t'docroot' => 'Will be replaced with the domain\\'s document-root.',\n\t\t\t'homedir' => 'Will be replaced with the customer\\'s home-directory.',\n\t\t],\n\t\t'expert_settings' => 'Expert settings!',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'PHP Processes for this domain (empty for default value)',\n\t\t],\n\t\t'phpserversettings' => 'PHP Settings',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Maximum php requests for this domain (empty for default value)',\n\t\t],\n\t\t'spfsettings' => 'Domain SPF settings',\n\t\t'specialsettingsforsubdomains' => 'Apply specialsettings to all subdomains (*.example.com)',\n\t\t'accountdata' => 'Account Data',\n\t\t'contactdata' => 'Contact Data',\n\t\t'servicedata' => 'Service Data',\n\t\t'newerversionavailable' => 'There is a newer version of froxlor available.',\n\t\t'newerversiondetails' => 'Update to version <b>%s</b> now?<br/>(Your current version is: %s)',\n\t\t'extractdownloadedzip' => 'Extract downloaded archive \"%s\"?',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Cronjob settings',\n\t\t\t'add' => 'Add cronjob',\n\t\t],\n\t\t'cronjob_edit' => 'Edit cronjob',\n\t\t'warning' => 'WARNING - Please note!',\n\t\t'lastlogin_succ' => 'Last login',\n\t\t'ftpserver' => 'FTP Server',\n\t\t'ftpserversettings' => 'FTP Server settings',\n\t\t'webserver_user' => 'Webserver user-name',\n\t\t'webserver_group' => 'Webserver group-name',\n\t\t'perlenabled' => 'Perl enabled',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Local user to use for FCGID (froxlor vHost)',\n\t\t'mod_fcgid_group' => 'Local group to use for FCGID (froxlor vHost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[not given]',\n\t\t'store_defaultindex' => 'Store default index-file to customers docroot',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => 'Traffic',\n\t\t'traffic_sub' => 'Details on traffic usage',\n\t\t'domaintraffic' => 'Domains',\n\t\t'customertraffic' => 'Customers',\n\t\t'assignedmax' => 'Assigned / Max',\n\t\t'usedmax' => 'Used / Max',\n\t\t'used' => 'Used',\n\t\t'speciallogwarning' => '<div id=\"speciallogfilenote\" class=\"invalid-feedback\">WARNING: By changing this setting you will lose all your old statistics for this domain.</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => 'Separate logfile',\n\t\t\t'description' => 'Enable this to get a separate access-log file for this domain',\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => 'Allow editing of domain',\n\t\t\t'desc' => 'If set to yes, the customer is allowed to change several domain-settings.<br />If set to no, nothing can be changed by the customer.',\n\t\t],\n\t\t'writeaccesslog' => [\n\t\t\t'title' => 'Write an access log',\n\t\t\t'description' => 'Enable this to get an access-log file for this domain',\n\t\t],\n\t\t'writeerrorlog' => [\n\t\t\t'title' => 'Write an error log',\n\t\t\t'description' => 'Enable this to get an error-log file for this domain',\n\t\t],\n\t\t'phpfpm.ininote' => 'Not all values you may want to define can be used in the php-fpm pool configuration',\n\t\t'phpinfo' => 'PHPinfo()',\n\t\t'selectserveralias' => 'ServerAlias value for the domain',\n\t\t'selectserveralias_desc' => 'Choose whether froxlor should create a wildcard-entry (*.domain.tld), a WWW-alias (www.domain.tld) or no alias at all',\n\t\t'show_news_feed' => [\n\t\t\t'title' => 'Show news-feed on admin-dashboard',\n\t\t\t'description' => 'Enable this to show the official froxlor newsfeed (https://inside.froxlor.org/news/) on your dashboard and never miss important information or release-announcements.',\n\t\t],\n\t\t'cronsettings' => 'Cronjob settings',\n\t\t'integritycheck' => 'Database validation',\n\t\t'integrityname' => 'Name',\n\t\t'integrityresult' => 'Result',\n\t\t'integrityfix' => 'Fix problems automatically',\n\t\t'customer_show_news_feed' => 'Show newsfeed on customer-dashboard',\n\t\t'customer_news_feed_url' => [\n\t\t\t'title' => 'Use custom RSS-feed',\n\t\t\t'description' => 'Specify a custom RSS-feed that will be shown to your customers on their dashboard.<br /><small>Leave this empty to use the official froxlor newsfeed (https://inside.froxlor.org/news/).</small>',\n\t\t],\n\t\t'movetoadmin' => 'Move customer',\n\t\t'movecustomertoadmin' => [\n\t\t\t'title' => 'Move customer to the selected admin/reseller',\n\t\t\t'description' => 'Leave this empty for no change.<br />If the desired admin does not show up in the list, his customer-limit has been reached.',\n\t\t],\n\t\t'note' => 'Note',\n\t\t'mod_fcgid_umask' => [\n\t\t\t'title' => 'Umask (default: 022)',\n\t\t],\n\t\t'apcuinfo' => 'APCu info',\n\t\t'opcacheinfo' => 'OPcache Info',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Use Let\\'s Encrypt',\n\t\t\t'description' => 'Get a free certificate from <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. The certificate will be created and renewed automatically.<br><strong class=\"text-danger\">ATTENTION:</strong> If wildcards are enabled, this option will automatically be disabled.',\n\t\t],\n\t\t'autoupdate' => 'Auto-Update',\n\t\t'server_php' => 'PHP',\n\t\t'dnsenabled' => 'Enable DNS editor',\n\t\t'froxlorvhost' => 'froxlor VirtualHost settings',\n\t\t'hostname' => 'Hostname',\n\t\t'memory' => 'Memory usage',\n\t\t'webserversettings_ssl' => 'Webserver SSL settings',\n\t\t'domain_hsts_maxage' => [\n\t\t\t'title' => 'HTTP Strict Transport Security (HSTS)',\n\t\t\t'description' => 'Specify the max-age value for the Strict-Transport-Security header<br>The value <i>0</i> will disable HSTS for the domain. Most user set a value of <i>31536000</i> (one year).',\n\t\t],\n\t\t'domain_hsts_incsub' => [\n\t\t\t'title' => 'Include HSTS for any subdomain',\n\t\t\t'description' => 'The optional \"includeSubDomains\" directive, if present, signals the UA that the HSTS Policy applies to this HSTS Host as well as any subdomains of the host\\'s domain name.',\n\t\t],\n\t\t'domain_hsts_preload' => [\n\t\t\t'title' => 'Include domain in HSTS preload list',\n\t\t\t'description' => 'If you would like this domain to be included in the <a href=\"https://hstspreload.org/\" target=\"_blank\">HSTS preload list</a> maintained by Chrome (and used by Firefox and Safari), then use activate this.<br>Sending the preload directive from your site can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains.<br>Please read the details at <a href=\"https://hstspreload.org/#removal\" target=\"_blank\">https://hstspreload.org/#removal</a> before sending the header with \"preload\".',\n\t\t],\n\t\t'domain_ocsp_stapling' => [\n\t\t\t'title' => 'OCSP stapling',\n\t\t\t'description' => 'See <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/OCSP_stapling\">Wikipedia</a> for a detailed explanation of OCSP stapling',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">WARNING:</strong> Nginx version 1.3.7 or above is required for OCSP stapling. If your version is older, the webserver will NOT start correctly while OCSP stapling is enabled!',\n\t\t],\n\t\t'domain_http2' => [\n\t\t\t'title' => 'HTTP2 support',\n\t\t\t'description' => 'See <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/2\">Wikipedia</a> for a detailed explanation of HTTP2',\n\t\t],\n\t\t'domain_http3' => [\n\t\t\t'title' => 'HTTP3 support',\n\t\t\t'description' => 'See <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/3\">Wikipedia</a> for a detailed explanation of HTTP3',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">WARNING:</strong> Nginx version 1.25.0 or above and ssl-protocol TLSv1.3 is required for HTTP/3. If your version is older, the webserver will NOT start correctly while HTTP/3 is enabled!',\n\t\t],\n\t\t'testmail' => 'SMTP test',\n\t\t'phpsettingsforsubdomains' => 'Apply php-config to all subdomains:',\n\t\t'plans' => [\n\t\t\t'name' => 'Plan name',\n\t\t\t'description' => 'Description',\n\t\t\t'last_update' => 'Last updated',\n\t\t\t'plans' => 'Hosting plans',\n\t\t\t'plan_details' => 'Plan details',\n\t\t\t'add' => 'Add new plan',\n\t\t\t'edit' => 'Edit plan',\n\t\t\t'use_plan' => 'Apply plan',\n\t\t],\n\t\t'notryfiles' => [\n\t\t\t'title' => 'No autogenerated try_files',\n\t\t\t'description' => 'Say yes here if you want to specify a custom try_files directive in specialsettings (needed for some wordpress plugins for example).',\n\t\t],\n\t\t'logviewenabled' => 'Enable access to access/error-logs',\n\t\t'novhostcontainer' => '<br><br><small class=\"text-danger\">None of the IPs and ports has the \"Create vHost-Container\" option enabled, many settings here will not be available</small>',\n\t\t'ownsslvhostsettings' => 'Own SSL vHost-settings',\n\t\t'domain_override_tls' => 'Override system TLS settings',\n\t\t'domain_override_tls_addinfo' => '<br /><span class=\"text-danger\">Only used if \"Override system TLS settings\" is set to \"Yes\"</span>',\n\t\t'domain_sslenabled' => 'Enable usage of SSL',\n\t\t'domain_honorcipherorder' => 'Honor the (server) cipher order, default <strong>no</strong>',\n\t\t'domain_sessiontickets' => 'Enable TLS sessiontickets (RFC 5077), default <strong>yes</strong>',\n\t\t'domain_sessionticketsenabled' => [\n\t\t\t'title' => 'Enable usage of TLS sessiontickets globally',\n\t\t\t'description' => 'Default <strong>yes</strong><br>Requires apache-2.4.11+ or nginx-1.5.9+',\n\t\t],\n\t\t'domaindefaultalias' => 'Default ServerAlias value for new domains',\n\t\t'smtpsettings' => 'SMTP settings',\n\t\t'smtptestaddr' => 'Send test-mail to',\n\t\t'smtptestnote' => 'Note that the values below reflect your current settings and can only be adjusted there (see link in top right corner)',\n\t\t'smtptestsend' => 'Send test-mail',\n\t\t'mysqlserver' => [\n\t\t\t'mysqlserver' => 'MySQL Server',\n\t\t\t'dbserver' => 'Server #',\n\t\t\t'caption' => 'Description',\n\t\t\t'host' => 'Hostname / IP',\n\t\t\t'port' => 'Port',\n\t\t\t'user' => 'Privileged user',\n\t\t\t'add' => 'Add new MySQL server',\n\t\t\t'edit' => 'Edit MySQL server',\n\t\t\t'password' => 'Privileged user password',\n\t\t\t'password_emptynochange' => 'New password, leave empty for no change',\n\t\t\t'allowall' => [\n\t\t\t\t'title' => 'Allow use of this server to all currently existing customers',\n\t\t\t\t'description' => 'Set this to \"true\" if you want to allow use of this database-server to all currently existing customers so they can add databases on it. This setting is not permanent but can be run multiple times.',\n\t\t\t],\n\t\t\t'testconn' => 'Test connection when saving',\n\t\t\t'ssl' => 'Use SSL for connection to database-server',\n\t\t\t'ssl_cert_file' => 'The file path to the SSL certificate authority',\n\t\t\t'verify_ca' => 'Enable verification of the server SSL certificate',\n\t\t],\n\t\t'settings_importfile' => 'Chose import file',\n\t\t'documentation' => 'Documentation',\n\t\t'adminguide' => 'Admin guide',\n\t\t'userguide' => 'User guide',\n\t\t'apiguide' => 'API guide',\n\t\t'domain_duplicate' => 'Duplicate domain',\n\t\t'domain_duplicate_named' => 'Duplicate %s',\n\t\t'backups' => [\n\t\t\t'backups' => 'Backups',\n\t\t],\n\t\t'emaildomainwarning' => '<div id=\"emaildomainnote\" class=\"invalid-feedback\">WARNING: By changing this setting you will delete all existing e-mail addresses and -accounts permanently.</div>',\n\t\t'webserver_serveradmin' => [\n\t\t\t'setting' => 'ServerAdmin directive value',\n\t\t\t'customer' => 'Customer email address (default)',\n\t\t\t'admin' => 'Admin email address',\n\t\t\t'global' => 'Panel admin email address',\n\t\t\t'none' => 'No ServerAdmin'\n\t\t]\n\t],\n\t'apcuinfo' => [\n\t\t'clearcache' => 'Clear APCu cache',\n\t\t'generaltitle' => 'General Cache Information',\n\t\t'version' => 'APCu Version',\n\t\t'phpversion' => 'PHP Version',\n\t\t'host' => 'APCu Host',\n\t\t'sharedmem' => 'Shared Memory',\n\t\t'sharedmemval' => '%d Segment(s) with %s (%s memory)',\n\t\t'start' => 'Start Time',\n\t\t'uptime' => 'Uptime',\n\t\t'upload' => 'File Upload Support',\n\t\t'cachetitle' => 'Cache Information',\n\t\t'cvar' => 'Cached Variables',\n\t\t'hit' => 'Hits',\n\t\t'miss' => 'Misses',\n\t\t'reqrate' => 'Request Rate (hits, misses)',\n\t\t'creqsec' => 'cache requests/second',\n\t\t'hitrate' => 'Hit Rate',\n\t\t'missrate' => 'Miss Rate',\n\t\t'insrate' => 'Insert Rate',\n\t\t'cachefull' => 'Cache full count',\n\t\t'runtime' => 'Runtime Settings',\n\t\t'memnote' => 'Memory Usage',\n\t\t'total' => 'Total',\n\t\t'free' => 'Free',\n\t\t'used' => 'Used',\n\t\t'hitmiss' => 'Hits & Misses',\n\t\t'detailmem' => 'Detailed Memory Usage and Fragmentation',\n\t\t'fragment' => 'Fragmentation',\n\t\t'nofragment' => 'No fragmentation',\n\t\t'fragments' => 'Fragments',\n\t],\n\t'apikeys' => [\n\t\t'no_api_keys' => 'No API keys found',\n\t\t'key_add' => 'Add new key',\n\t\t'apikey_removed' => 'The api key with the id #%s has been removed successfully',\n\t\t'apikey_added' => 'A new api key has been generated successfully',\n\t\t'clicktoview' => 'Click to view',\n\t\t'allowed_from' => 'Allowed from',\n\t\t'allowed_from_help' => 'Comma separated list of ip addresses / networks.<br>Default is empty (allow from all).',\n\t\t'valid_until' => 'Valid until',\n\t\t'valid_until_help' => 'Date until valid, format YYYY-MM-DDThh:mm',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Old password',\n\t\t'new_password' => 'New password',\n\t\t'new_password_confirm' => 'Confirm password',\n\t\t'new_password_ifnotempty' => 'New password (empty = no change)',\n\t\t'also_change_ftp' => ' also change password of the main FTP account',\n\t\t'also_change_stats' => ' also change password for the statistics page',\n\t\t'also_change_global_mysql' => 'also change password for global MySQL account',\n\t],\n\t'cron' => [\n\t\t'cronname' => 'cronjob-name',\n\t\t'lastrun' => 'last run',\n\t\t'interval' => 'interval',\n\t\t'isactive' => 'enabled',\n\t\t'description' => 'description',\n\t\t'changewarning' => 'Changing these values can have a negative cause to the behavior of froxlor and its automated tasks.<br />Please only change values here, if you are sure you know what you are doing.',\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'no description given',\n\t\t'cron_tasks' => 'Generating of configfiles',\n\t\t'cron_legacy' => 'legacy (old) cronjob',\n\t\t'cron_traffic' => 'Traffic calculation',\n\t\t'cron_usage_report' => 'Web- and traffic-reports',\n\t\t'cron_mailboxsize' => 'Mailbox-size calculation',\n\t\t'cron_letsencrypt' => 'Let\\'s Encrypt certificate updates',\n\t\t'cron_export' => 'Process data-export jobs',\n\t\t'cron_backup' => 'Process system- and customer backup jobs',\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'Cronjob settings',\n\t\t'cronjobintervalv' => 'Runtime interval value',\n\t\t'cronjobinterval' => 'Runtime interval',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Not yet run',\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => 'minutes',\n\t\t'hours' => 'hours',\n\t\t'days' => 'days',\n\t\t'weeks' => 'weeks',\n\t\t'months' => 'months',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Home directory',\n\t\t'name' => 'Name',\n\t\t'firstname' => 'First name',\n\t\t'lastname' => 'Last name',\n\t\t'company' => 'Company',\n\t\t'nameorcompany_desc' => 'Either firstname/lastname or company is required',\n\t\t'street' => 'Street',\n\t\t'zipcode' => 'Zipcode',\n\t\t'city' => 'City',\n\t\t'phone' => 'Phone',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'Email',\n\t\t'customernumber' => 'Customer ID',\n\t\t'diskspace' => 'Webspace',\n\t\t'traffic' => 'Traffic',\n\t\t'mysqls' => 'MySQL-databases',\n\t\t'emails' => 'Email-addresses',\n\t\t'accounts' => 'Email-accounts',\n\t\t'forwarders' => 'Email-forwarders',\n\t\t'ftps' => 'FTP-accounts',\n\t\t'subdomains' => 'Subdomains',\n\t\t'domains' => 'Domains',\n\t\t'mib' => 'MiB',\n\t\t'gib' => 'GiB',\n\t\t'title' => 'Title',\n\t\t'country' => 'Country',\n\t\t'email_quota' => 'E-mail quota',\n\t\t'email_imap' => 'E-mail IMAP',\n\t\t'email_pop3' => 'E-mail POP3',\n\t\t'sendinfomail' => 'Send data via email to me',\n\t\t'generated_pwd' => 'Password suggestion',\n\t\t'usedmax' => 'Used / Max',\n\t\t'services' => 'Services',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Use Let\\'s Encrypt',\n\t\t\t'description' => 'Get a free certificate from <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. The certificate will be created and renewed automatically.',\n\t\t],\n\t\t'selectserveralias_addinfo' => 'This option can be set when editing the domain. Its initial value is inherited from the parent-domain.',\n\t\t'total_diskspace' => 'Total diskspace',\n\t\t'mysqlserver' => 'Usable mysql-server',\n\t],\n\t'diskquota' => 'Quota',\n\t'antispam' => [\n\t\t'config_file' => [\n\t\t\t'title' => 'Antispam settings file',\n\t\t\t'description' => 'Please specify the filename for the email-antispam rules',\n\t\t],\n\t\t'reload_command' => [\n\t\t\t'title' => 'Milter restart command',\n\t\t\t'description' => 'Please specify the restart command for the rspamd service',\n\t\t],\n\t\t'activated' => [\n\t\t\t'title' => 'Activate antispam?',\n\t\t\t'description' => 'Would you like to use rspamd as antispam service?',\n\t\t],\n\t\t'dkim_keylength' => [\n\t\t\t'title' => 'DKIM Key-length',\n\t\t\t'description' => 'Attention: Changes will only apply for new keys<br/><br/>Requires a specific dns entry for the domain. If you are not using the nameserver feature, you will have to manually manage these entries.',\n\t\t],\n\t\t'spam_tag_level' => [\n\t\t\t'title' => 'Spam tag level',\n\t\t\t'description' => 'Score that is required to mark an email as spam<br/>Default: 7.0'\n\t\t],\n\t\t'rewrite_subject' => [\n\t\t\t'title' => 'Rewrite subject',\n\t\t\t'description' => 'Whether to add <strong>***SPAM***</strong> to the email subject if applicable',\n\t\t],\n\t\t'spam_kill_level' => [\n\t\t\t'title' => 'Spam kill level',\n\t\t\t'description' => 'Score that is required to discard an email entirely<br/>Default: 14.0'\n\t\t],\n\t\t'bypass_spam' => [\n\t\t\t'title' => 'Bypass spamfilter',\n\t\t\t'description' => 'Activate to bypass/disable spamfiltering for this address.<br/>Default: no'\n\t\t],\n\t\t'policy_greylist' => [\n\t\t\t'title' => 'Use greylisting',\n\t\t\t'description' => 'Incoming emails will be protected by <a href=\"https://en.wikipedia.org/wiki/Greylisting_(email)\" target=\"_blank\">greylisting</a>.<br/>Default: yes'\n\t\t],\n\t\t'required_spf_dns' => 'Required SPF DNS entry',\n\t\t'required_dmarc_dns' => 'Required DMARC DNS entry',\n\t\t'required_dkim_dns' => 'Required DKIM DNS entry',\n\t\t'default_select' => [\n\t\t\t'on_changeable' => 'Activated, adjustable',\n\t\t\t'off_changeable' => 'Deactivated, adjustable',\n\t\t\t'on_unchangeable' => 'Activated, not adjustable',\n\t\t\t'off_unchangeable' => 'Deactivated, not adjustable',\n\t\t],\n\t\t'default_bypass_spam' => [\n\t\t\t'title' => 'Bypass spamfilter default value',\n\t\t\t'description' => 'Whether new email accounts have \"Bypass spamfilter\" activated by default and whether this setting is adjustable by the customer.<br/>Default: Deactivated, adjustable'\n\t\t],\n\t\t'default_spam_rewrite_subject' => [\n\t\t\t'title' => 'Rewrite subject default value',\n\t\t\t'description' => 'Whether new email accounts have \"Rewrite subject\" activated by default and whether this setting is adjustable by the customer.<br/>Default: Activated, adjustable'\n\t\t],\n\t\t'default_policy_greylist' => [\n\t\t\t'title' => 'Use greylisting default value',\n\t\t\t'description' => 'Whether new email accounts have \"Use greylisting\" activated by default and whether this setting is adjustable by the customer.<br/>Default: Activated, adjustable'\n\t\t],\n\t],\n\t'dns' => [\n\t\t'destinationip' => 'Domain IP(s)',\n\t\t'standardip' => 'Server standard IP',\n\t\t'a_record' => 'A-Record (IPv6 optional)',\n\t\t'cname_record' => 'CNAME-Record',\n\t\t'mxrecords' => 'Define MX records',\n\t\t'standardmx' => 'Server standard MX record',\n\t\t'mxconfig' => 'Custom MX records',\n\t\t'priority10' => 'Priority 10',\n\t\t'priority20' => 'Priority 20',\n\t\t'txtrecords' => 'Define TXT records',\n\t\t'txtexample' => 'Example (SPF-entry):<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'howitworks' => 'Here you can manage DNS entries for your domain. Note that froxlor will automatically generate NS/MX/A/AAAA records for you. The custom entries are preferred, only missing entries will be automatically generated.',\n\t\t'nis2note' => [\n\t\t\t'title' => 'NIS2 info',\n\t\t\t'content' => 'DNS hosting/authoritative DNS services may be considered a digital service with increased security and reporting obligations under <strong>EU-NIS2</strong>. Please check whether your setup is affected by NIS2 and what measures are required.'\n\t\t],\n\t],\n\t'dnseditor' => [\n\t\t'edit' => 'edit DNS',\n\t\t'records' => 'records',\n\t\t'notes' => [\n\t\t\t'A' => '32-bit IPv4 address, used to map hostnames to an IP address of the host.',\n\t\t\t'AAAA' => '128-bit IPv6 address, used to map hostnames to an IP address of the host.',\n\t\t\t'CAA' => 'The CAA resource record allows a DNS domain name holder to specify one or more Certification Authorities (CAs) authorized to issue certificates for that domain.<br>Structure: <code>flag tag[issue|issuewild|iodef|contactmail|contactphone] value</code><br>Example: <code>0 issue \"ca.example.net\"<br>0 iodef \"mailto:security@example.com\"</code>',\n\t\t\t'CNAME' => 'Alias of the domain name, the DNS lookup will continue by retrying the lookup with the new name. Only possible for subdomains!',\n\t\t\t'DNAME' => 'Creates an alias for an entire subtree of the domain name tree',\n\t\t\t'LOC' => 'Geographic location information for a domain name.<br>Structure: <code>( d1 [m1 [s1]] {\"N\"|\"S\"} d2 [m2 [s2]] {\"E\"|\"W\"} alt[\"m\"] [siz[\"m\"] [hp[\"m\"] [vp[\"m\"]]]] )</code><br>Description: <code>d1:     [0 .. 90]            (degrees latitude)\n\t\t\td2:     [0 .. 180]           (degrees longitude)\n\t\t\tm1, m2: [0 .. 59]            (minutes latitude/longitude)\n\t\t\ts1, s2: [0 .. 59.999]        (seconds latitude/longitude)\n\t\t\talt:    [-100000.00 .. 42849672.95] BY .01 (altitude in meters)\n\t\t\tsiz, hp, vp: [0 .. 90000000.00] (size/precision in meters)</code><br>Example: <code>52 22 23.000 N 4 53 32.000 E -2.00m 0.00m 10000m 10m</code>',\n\t\t\t'MX' => 'Mail exchange record, maps a domain name to a mailserver for that domain.<br>Example: <code>10 mail.example.com</code><br>Note: For priority, use field above',\n\t\t\t'NS' => 'Delegates a DNS zone to use the given authoritative name servers.',\n\t\t\t'RP' => 'Responsible Person record<br>Structure: <code>mailbox[replace @ with a dot] txt-record-name</code><br>Example: <code>team.froxlor.org. froxlor.org.</code>',\n\t\t\t'SRV' => 'Service location record, used for newer protocols instead of creating protocol-specific records such as MX.<br>Structure: <code>priority weight port target</code><br>Example: <code>0 5 5060 sipserver.example.com.</code><br>Note: For priority, use field above',\n\t\t\t'SSHFP' => 'The SSHFP resource record is used to publish secure shell (SSH) key fingerprints in the DNS.<br>Structure: <code>algorithm type fingerprint</code><br>Algorithms: <code>0: reserved, 1: RSA, 2: DSA, 3: ECDSA, 4: Ed25519, 6: Ed448</code><br>Types: <code>0: reserved, 1: SHA-1, 2: SHA-256</code><br>Example: <code>2 1 123456789abcdef67890123456789abcdef67890</code>',\n\t\t\t'TLSA' => 'TLSA (TLS Authentication) record is used to publish fingerprint of a TLS/SSL certificate. It is commonly used for DANE.<br>TLSA records can only be trusted if DNSSEC is enabled on your domain.<br>Structure: <code>usage selector type fingerprint</code><br>Certificate usage: <code>0: PKIX-T, 1: PKIX-EE, 2: DANE-TA, 3: DANE-EE</code><br>Selector: <code>0: Use full certificate, 1: Use subject public key</code><br>Matching type: <code>0: Full: No Hash, 1: SHA-256 Hash, 2:SHA-512 Hash</code><br>Example: <code>3 1 1 123456789abcdef67890123456789abcdef123456789abcdef123456789abcde</code>',\n\t\t\t'TXT' => 'Free definable, descriptive text.'\n\t\t]\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'OpenBasedir-path',\n\t\t'inherited' => 'Same as parent-domain',\n\t\t'docroot' => 'Path from field above',\n\t\t'homedir' => 'Home directory',\n\t\t'docparent' => 'Parent-directory of path from field above',\n\t\t'ssl_certificate_placeholder' => '---- BEGIN CERTIFICATE---' . PHP_EOL . '[...]' . PHP_EOL . '----END CERTIFICATE----',\n\t\t'ssl_key_placeholder' => '---- BEGIN RSA PRIVATE KEY-----' . PHP_EOL . '[...]' . PHP_EOL . '-----END RSA PRIVATE KEY-----',\n\t],\n\t'domains' => [\n\t\t'description' => 'Here you can create (sub-)domains and change their paths.<br />The system will need some time to apply the new settings after every change.',\n\t\t'domainsettings' => 'Domain settings',\n\t\t'domainname' => 'Domain name',\n\t\t'subdomain_add' => 'Create subdomain',\n\t\t'subdomain_edit' => 'Edit (sub)domain',\n\t\t'wildcarddomain' => 'Create as wildcarddomain?',\n\t\t'aliasdomain' => 'Alias for domain',\n\t\t'noaliasdomain' => 'No alias domain',\n\t\t'hasaliasdomains' => 'Has alias domain(s)',\n\t\t'statstics' => 'Usage Statistics',\n\t\t'isassigneddomain' => 'Is assigned domain',\n\t\t'add_date' => 'Added to froxlor',\n\t\t'registration_date' => 'Added to registry',\n\t\t'topleveldomain' => 'Top-Level-Domain',\n\t\t'associated_with_domain' => 'Associated',\n\t\t'aliasdomains' => 'Alias domains',\n\t\t'redirectifpathisurl' => 'Redirect code (default: empty)',\n\t\t'redirectifpathisurlinfo' => 'You only need to select one of these if you entered an URL as path<br/><strong class=\"text-danger\">NOTE:</strong> Changes are only applied if the given path is an URL.',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'IP address(es)',\n\t\t\t'description' => 'Specify one or more IP address for the domain.<br /><br /><div class=\"text-danger\">NOTE: IP addresses cannot be changed when the domain is configured as <strong>alias-domain</strong> of another domain.</div>',\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'SSL IP address(es)',\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'SSL redirect',\n\t\t\t'description' => 'This option creates redirects for non-ssl vhosts so that all requests are redirected to the SSL-vhost.<br /><br />e.g. a request to <strong>http</strong>://domain.tld/ will redirect you to <strong>https</strong>://domain.tld/',\n\t\t],\n\t\t'serveraliasoption_wildcard' => 'Wildcard (*.domain.tld)',\n\t\t'serveraliasoption_www' => 'WWW (www.domain.tld)',\n\t\t'serveraliasoption_none' => 'No alias',\n\t\t'domain_import' => 'Import Domains',\n\t\t'import_separator' => 'Separator',\n\t\t'import_offset' => 'Offset',\n\t\t'import_file' => 'CSV-File',\n\t\t'import_description' => 'Detailed information about the structure of the import-file and how to import successfully, please visit <a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\" class=\"alert-link\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a>',\n\t\t'ssl_redirect_temporarilydisabled' => '<br>The SSL redirect is temporarily deactivated while a new Let\\'s Encrypt certificate is generated. It will be activated again after the certificate was generated.',\n\t\t'termination_date' => 'Date of termination',\n\t\t'termination_date_overview' => 'terminated as of ',\n\t\t'ssl_certificates' => 'SSL certificates',\n\t\t'ssl_certificate_removed' => 'The certificate with the id #%s has been removed successfully',\n\t\t'ssl_certificate_error' => 'Error reading certificate for domain: %s',\n\t\t'no_ssl_certificates' => 'There are no domains with SSL certificate',\n\t\t'isaliasdomainof' => 'Is aliasdomain for %s',\n\t\t'isbinddomain' => 'Create DNS zone',\n\t\t'dkimenabled' => 'DKIM enabled',\n\t\t'openbasedirenabled' => 'Openbasedir restiction',\n\t\t'hsts' => 'HSTS enabled',\n\t\t'aliasdomainid' => 'ID of alias domain',\n\t\t'nodomainsassignedbyadmin' => 'Your account has currently no (active) domains assigned to it. Please contact your administrator if you think this is wrong.',\n\t\t'email_only' => 'Email only',\n\t],\n\t'emails' => [\n\t\t'description' => 'Here you can create and change your email addresses.<br />An account is like your letterbox in front of your house. If someone sends you an email, it will be dropped into the account.<br /><br />To download your emails use the following settings in your mailprogram: (The data in <i>italics</i> has to be changed to the equivalents you typed in!)<br />Hostname: <b><i>domainname</i></b><br />Username: <b><i>account name / e-mail address</i></b><br />password: <b><i>the password you\\'ve chosen</i></b>',\n\t\t'emailaddress' => 'Email-address',\n\t\t'emails_add' => 'Create email-address',\n\t\t'emails_edit' => 'Edit email-address',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => 'Define as catchall-address?',\n\t\t'account' => 'Account',\n\t\t'account_add' => 'Create account',\n\t\t'account_delete' => 'Delete account',\n\t\t'from' => 'Source',\n\t\t'to' => 'Destination',\n\t\t'forwarders' => 'Forwarders',\n\t\t'forwarder_add' => 'Create forwarder',\n\t\t'alternative_emailaddress' => 'Alternative e-mail-address',\n\t\t'quota' => 'Quota',\n\t\t'noquota' => 'No quota',\n\t\t'updatequota' => 'Update Quota',\n\t\t'quota_edit' => 'Change E-Mail Quota',\n\t\t'noemaildomainaddedyet' => 'You do not have a (email-)domain in your account yet.',\n\t\t'back_to_overview' => 'Back to domain overview',\n\t\t'accounts' => 'Accounts',\n\t\t'emails' => 'Addresses',\n\t\t'senders' => 'Allowed sender',\n\t\t'sender_add' => 'Add allowed sender',\n\t\t'foreign_sender' => 'Allowed (external) sender',\n\t\t'allowed_sender_info' => 'With an <strong>allowed sender</strong>, you allow an existing email account to send emails with a different sender address.<br><strong>Important:</strong> The address/wildcard-domain entered here does not automatically become a mailbox – it only serves as additional, permitted sender identifier.',\n\t],\n\t'error' => [\n\t\t'error' => 'Error',\n\t\t'directorymustexist' => 'The directory %s must exist. Please create it with your FTP client.',\n\t\t'filemustexist' => 'The file %s must exist.',\n\t\t'allresourcesused' => 'You have already used all of your resources.',\n\t\t'domains_cantdeletemaindomain' => 'You cannot delete an assigned domain.',\n\t\t'domains_canteditdomain' => 'You cannot edit this domain. It has been disabled by the admin.',\n\t\t'domains_cantdeletedomainwithemail' => 'You cannot delete a domain which is used as an email-domain. Delete all email addresses first.',\n\t\t'firstdeleteallsubdomains' => 'You have to delete all subdomains first before you can create a wildcard domain.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'You have already defined a catchall for this domain.',\n\t\t'ftp_cantdeletemainaccount' => 'You cannot delete your main FTP account',\n\t\t'login' => 'The username or password you typed in is wrong. Please try it again!',\n\t\t'login_blocked' => 'This account has been suspended because of too many login errors. <br />Please try again in %s seconds.',\n\t\t'notallreqfieldsorerrors' => 'You have not filled in all or filled in some fields incorrectly.',\n\t\t'oldpasswordnotcorrect' => 'The old password is not correct.',\n\t\t'youcantallocatemorethanyouhave' => 'You cannot allocate more resources than you own for yourself.',\n\t\t'mustbeurl' => 'You have not typed a valid or complete url (e.g. http://somedomain.com/error404.htm)',\n\t\t'invalidpath' => 'You have not chosen a valid URL (maybe problems with the dirlisting?)',\n\t\t'stringisempty' => 'Missing Input in Field',\n\t\t'stringiswrong' => 'Wrong Input in Field',\n\t\t'newpasswordconfirmerror' => 'New password and confirmation does not match',\n\t\t'mydomain' => '\\'Domain\\'',\n\t\t'mydocumentroot' => '\\'Documentroot\\'',\n\t\t'loginnameexists' => 'Loginname %s already exists',\n\t\t'emailiswrong' => 'Email-address %s contains invalid characters or is incomplete',\n\t\t'emailexists' => 'Email-address %s already in use by another admin',\n\t\t'emailexistsanon' => 'Email-address %s already in use.',\n\t\t'alternativeemailiswrong' => 'The given alternative email address %s to send the credentials to seems to be invalid',\n\t\t'loginnameiswrong' => 'Loginname \"%s\" contains illegal characters.',\n\t\t'loginnameiswrong2' => 'Loginname contains too many characters. Only %s characters are allowed.',\n\t\t'userpathcombinationdupe' => 'Combination of username and path already exists',\n\t\t'patherror' => 'General Error! Path cannot be empty',\n\t\t'errordocpathdupe' => 'Option for path %s already exists',\n\t\t'adduserfirst' => 'Please create a customer first',\n\t\t'domainalreadyexists' => 'The domain %s is already assigned to a customer',\n\t\t'nolanguageselect' => 'No language selected.',\n\t\t'nosubjectcreate' => 'You must define a topic for this mail template.',\n\t\t'nomailbodycreate' => 'You must define a mail-text for this mail template.',\n\t\t'templatenotfound' => 'Template was not found.',\n\t\t'alltemplatesdefined' => 'You can\\'t define more templates, all languages are supported already.',\n\t\t'wwwnotallowed' => 'www is not allowed for subdomains.',\n\t\t'subdomainiswrong' => 'The subdomain %s contains invalid characters.',\n\t\t'domaincantbeempty' => 'The domain-name can not be empty.',\n\t\t'domainexistalready' => 'The domain %s already exists.',\n\t\t'domainisaliasorothercustomer' => 'The selected alias domain is either itself an alias domain, has a different ip/port combination or belongs to another customer.',\n\t\t'emailexistalready' => 'The email-address %s already exists.',\n\t\t'maindomainnonexist' => 'The main-domain %s does not exist.',\n\t\t'maindomaindeactivated' => 'The main-domain %s is deactivated.',\n\t\t'destinationnonexist' => 'Please create your forwarder in the field \\'Destination\\'.',\n\t\t'destinationalreadyexistasmail' => 'The forwarder to %s already exists as active email-address.',\n\t\t'destinationalreadyexist' => 'You have already defined a forwarder to \"%s\"',\n\t\t'destinationiswrong' => 'The forwarder %s contains invalid character(s) or is incomplete.',\n\t\t'dumpfoldercannotbedocroot' => 'The folder for data-dumps cannot be your homedir, please chose a folder within your homedir, e.g. /dumps',\n\t\t'templatelanguagecombodefined' => 'The selected language/template combination has already been defined.',\n\t\t'templatelanguageinvalid' => 'The selected language does not exist',\n\t\t'ipstillhasdomains' => 'The IP/Port combination you want to delete still has domains assigned to it, please reassign those to other IP/Port combinations before deleting this IP/Port combination.',\n\t\t'cantdeletedefaultip' => 'You cannot delete the default IP/Port combination, please make another IP/Port combination default for before deleting this IP/Port combination.',\n\t\t'cantdeletesystemip' => 'You cannot delete the last system IP, either create a new IP/Port combination for the system IP or change the system IP.',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'Port\\'',\n\t\t'myipdefault' => 'You need to select an IP/Port combination that should become default.',\n\t\t'myipnotdouble' => 'This IP/Port combination already exists.',\n\t\t'admin_domain_emailsystemhostname' => 'The server-hostname cannot be used as customer-domain.',\n\t\t'cantchangesystemip' => 'You cannot change the last system IP, either create another new IP/Port combination for the system IP or change the system IP.',\n\t\t'sessiontimeoutiswrong' => 'Only numerical \"session timeout\" is allowed.',\n\t\t'maxloginattemptsiswrong' => 'Only numerical \"max login attempts\" are allowed.',\n\t\t'deactivatetimiswrong' => 'Only numerical \"deactivation time\" is allowed.',\n\t\t'accountprefixiswrong' => 'The \"customerprefix\" is wrong.',\n\t\t'mysqlprefixiswrong' => 'The \"SQL prefix\" is wrong.',\n\t\t'ftpprefixiswrong' => 'The \"FTP prefix\" is wrong.',\n\t\t'ipiswrong' => 'The \"IP-address\" is wrong. Only a valid IP-address is allowed.',\n\t\t'vmailuidiswrong' => 'The \"mails-uid\" is wrong. Only a numerical UID is allowed.',\n\t\t'vmailgidiswrong' => 'The \"mails-gid\" is wrong. Only a numerical GID is allowed.',\n\t\t'adminmailiswrong' => 'The \"sender-address\" is wrong. Only a valid email-address is allowed.',\n\t\t'pagingiswrong' => 'The \"entries per page\"-value is wrong. Only numerical characters are allowed.',\n\t\t'phpmyadminiswrong' => 'The phpMyAdmin-link is not a valid link.',\n\t\t'webmailiswrong' => 'The webmail-link is not a valid link.',\n\t\t'webftpiswrong' => 'The WebFTP-link is not a valid link.',\n\t\t'stringformaterror' => 'The value for the field \"%s\" is not in the expected format.',\n\t\t'loginnameisusingprefix' => 'You cannot create accounts that begin with \"%s\", as this prefix is set to be used for the automatic account-naming. Please enter another account name.',\n\t\t'loginnameissystemaccount' => 'The account \"%s\" already exists on the system and cannot be used. Please enter another account name.',\n\t\t'loginnameisreservedname' => 'The account-name \"%s\" is reserved for system internals and cannot be used.',\n\t\t'youcantdeleteyourself' => 'You cannot delete yourself for security reasons.',\n\t\t'youcanteditallfieldsofyourself' => 'Note: You cannot edit all fields of your own account for security reasons.',\n\t\t'documentrootexists' => 'The directory \"%s\" already exists for this customer. Please remove this before adding the customer again.',\n\t\t'norepymailiswrong' => 'The \"Noreply-address\" is wrong. Only a valid email-address is allowed.',\n\t\t'logerror' => 'Log-Error: %s',\n\t\t'nomessagetosend' => 'You did not enter a message.',\n\t\t'norecipientsgiven' => 'You did not specify any recipient',\n\t\t'errorsendingmail' => 'The message to \"%s\" failed',\n\t\t'errorsendingmailpub' => 'The message to the given email-address failed',\n\t\t'cannotreaddir' => 'Unable to read directory \"%s\"',\n\t\t'invalidip' => 'Invalid IP address: %s',\n\t\t'invalidmysqlhost' => 'Invalid MySQL host address: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'You cannot enable Webalizer and AWstats at the same time, please chose one of them',\n\t\t'cannotwritetologfile' => 'Cannot open logfile %s for writing',\n\t\t'vmailquotawrong' => 'The quotasize must be positive number.',\n\t\t'allocatetoomuchquota' => 'You tried to allocate %s MB Quota, but you do not have enough left.',\n\t\t'missingfields' => 'Not all required fields were filled out.',\n\t\t'requiredfield' => 'This field is required.',\n\t\t'accountnotexisting' => 'The given email account doesn\\'t exist.',\n\t\t'nopermissionsorinvalidid' => 'You don\\'t have enough permissions to change these settings or an invalid id was given.',\n\t\t'phpsettingidwrong' => 'A PHP Configuration with this id doesn\\'t exist',\n\t\t'descriptioninvalid' => 'The description is too short, too long or contains illegal characters.',\n\t\t'info' => 'Info',\n\t\t'filecontentnotset' => 'The file cannot be empty!',\n\t\t'customerdoesntexist' => 'The customer you have chosen doesn\\'t exist.',\n\t\t'admindoesntexist' => 'The admin you have chosen doesn\\'t exist.',\n\t\t'ipportdoesntexist' => 'The ip/port combination you have chosen doesn\\'t exist.',\n\t\t'usernamealreadyexists' => 'The username %s already exists.',\n\t\t'plausibilitychecknotunderstood' => 'Answer of plausibility check not understood.',\n\t\t'errorwhensaving' => 'An error occurred when saving the field %s',\n\t\t'hiddenfieldvaluechanged' => 'The value for the hidden field \"%s\" changed while editing the settings.<br /><br />This is usually not a big problem but the settings could not be saved because of this.',\n\t\t'notrequiredpasswordlength' => 'The given password is too short. Please enter at least %s characters.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Whoops, a field that should be displayed as an option in the settings-overview is not an excepted type. You can blame the developers for this. This should not happen!',\n\t\t'pathmaynotcontaincolon' => 'The path you have entered should not contain a colon (\":\"). Please enter a correct path value.',\n\t\t'invaliddocumentrooturl' => 'The URL you have entered for the documentroot is not valid. Please enter a correct URL or a unix-path.',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => 'The specified password-complexity was not satisfied.<br />Please contact your administrator if you have any questions about the complexity-specification',\n\t\t'invaliderrordocumentvalue' => 'The value given as ErrorDocument does not seem to be a valid file, URL or string.',\n\t\t'intvaluetoolow' => 'The given number is too low (field %s)',\n\t\t'intvaluetoohigh' => 'The given number is too high (field %s)',\n\t\t'phpfpmstillenabled' => 'PHP-FPM is currently active. Please deactivate it before activating FCGID',\n\t\t'fcgidstillenabled' => 'FCGID is currently active. Please deactivate it before activating PHP-FPM',\n\t\t'domains_cantdeletedomainwithaliases' => 'You cannot delete a domain which is used for alias-domains. You have to delete the aliases first.',\n\t\t'user_banned' => 'Your account has been locked. Please contact your administrator for further information.',\n\t\t'session_timeout' => 'Value too low',\n\t\t'session_timeout_desc' => 'You should not set the session timeout lower than 1 minute.',\n\t\t'invalidhostname' => 'Hostname needs to be a valid domain. It can\\'t be empty nor can it consist only of whitespaces',\n\t\t'operationnotpermitted' => 'Operation not permitted!',\n\t\t'featureisdisabled' => 'Feature %s is disabled. Please contact your service provider.',\n\t\t'usercurrentlydeactivated' => 'The user %s is currently deactivated',\n\t\t'setlessthanalreadyused' => 'You cannot set less resources of \\'%s\\' than this user already used<br />',\n\t\t'stringmustntbeempty' => 'The value for the field %s must not be empty',\n\t\t'sslcertificateismissingprivatekey' => 'You need to specify a private key for your certificate',\n\t\t'sslcertificatewrongdomain' => 'The given certificate does not belong to this domain',\n\t\t'sslcertificateinvalidcert' => 'The given certificate-content does not seem to be a valid certificate',\n\t\t'sslcertificateinvalidcertkeypair' => 'The given private-key does not belong to the given certificate',\n\t\t'sslcertificateinvalidca' => 'The given CA certificate data does not seem to be a valid certificate',\n\t\t'sslcertificateinvalidchain' => 'The given certificate chain data does not seem to be a valid certificate',\n\t\t'givendirnotallowed' => 'The given directory in field %s is not allowed.',\n\t\t'sslredirectonlypossiblewithsslipport' => 'Using Let\\'s Encrypt is only possible when the domain has at least one ssl-enabled IP/port combination assigned.',\n\t\t'fcgidstillenableddeadlock' => 'FCGID is currently active.<br />Please deactivate it before switching to another webserver than Apache2',\n\t\t'send_report_title' => 'Send error report',\n\t\t'send_report_desc' => 'Thank you for reporting this error and helping us to improve froxlor.<br />This is the email which will be sent to the froxlor developer team:',\n\t\t'send_report' => 'Send report',\n\t\t'send_report_error' => 'Error when sending report: <br />%s',\n\t\t'notallowedtouseaccounts' => 'Your account does not allow using IMAP/POP3. You cannot add email accounts.',\n\t\t'cannotdeletehostnamephpconfig' => 'This PHP-configuration is used by the froxlor-vhost and cannot be deleted.',\n\t\t'cannotdeletedefaultphpconfig' => 'This PHP-configuration is set as default and cannot be deleted.',\n\t\t'passwordshouldnotbeusername' => 'The password should not be the same as the username.',\n\t\t'no_phpinfo' => 'Sorry, unable to read phpinfo()',\n\t\t'moveofcustomerfailed' => 'Moving the customer to the selected admin/reseller failed. Keep in mind that all other changes to the customer were applied successfully at this stage.<br><br>Error-message: %s',\n\t\t'domain_import_error' => 'Following error occurred while importing domains: %s',\n\t\t'fcgidandphpfpmnogoodtogether' => 'FCGID and PHP-FPM cannot be activated at the same time',\n\t\t'no_apcuinfo' => 'No cache info available. APCu does not appear to be running.',\n\t\t'no_opcacheinfo' => 'No OPCache info available. OPCache does not appear to be loaded.',\n\t\t'inactive_opcacheinfo' => 'OPCache seems to be installed but not activated.',\n\t\t'nowildcardwithletsencrypt' => 'Let\\'s Encrypt cannot handle wildcard-domains using ACME in froxlor (requires dns-challenge), sorry. Please set the ServerAlias to WWW or disable it completely',\n\t\t'customized_version' => 'It looks like your froxlor installation has been modified, no support sorry.',\n\t\t'autoupdate_0' => 'Unknown error',\n\t\t'autoupdate_1' => 'PHP setting allow_url_fopen is disabled. Autoupdate needs this setting to be enabled in php.ini',\n\t\t'autoupdate_2' => 'PHP zip extension not found, please ensure it is installed and activated',\n\t\t'autoupdate_4' => 'The froxlor archive could not be stored to the disk :(',\n\t\t'autoupdate_5' => 'version.froxlor.org returned unacceptable values :(',\n\t\t'autoupdate_6' => 'Whoops, there was no (valid) version given to download :(',\n\t\t'autoupdate_7' => 'The downloaded archive could not be found :(',\n\t\t'autoupdate_8' => 'The archive could not be extracted :(',\n\t\t'autoupdate_9' => 'The downloaded file did not pass the integrity check. Please try to update again.',\n\t\t'autoupdate_10' => 'Minimum supported version of PHP is 7.4.0',\n\t\t'autoupdate_11' => 'Webupdate is disabled',\n\t\t'mailaccistobedeleted' => 'Another account with the same name (%s) is currently being deleted and can therefore not be added at this moment.',\n\t\t'customerhasongoingexportjob' => 'There is already a data export job waiting to be processed, please be patient.',\n\t\t'exportfunctionnotenabled' => 'The export function is not enabled',\n\t\t'dns_domain_nodns' => 'DNS is not enabled for this domain',\n\t\t'dns_content_empty' => 'No content given',\n\t\t'dns_content_invalid' => 'DNS content invalid',\n\t\t'dns_arec_noipv4' => 'No valid IP address for A-record given',\n\t\t'dns_aaaarec_noipv6' => 'No valid IP address for AAAA-record given',\n\t\t'dns_mx_prioempty' => 'Invalid MX priority given',\n\t\t'dns_mx_needdom' => 'The MX content value must be a valid domain-name',\n\t\t'dns_mx_noalias' => 'The MX-content value cannot be an CNAME entry.',\n\t\t'dns_cname_invaliddom' => 'Invalid domain-name for CNAME record',\n\t\t'dns_cname_nomorerr' => 'There already exists a resource-record with the same record-name. It can not be used as CNAME.',\n\t\t'dns_other_nomorerr' => 'There already exists a CNAME record with the same record-name. It can not be used for another type.',\n\t\t'dns_ns_invaliddom' => 'Invalid domain-name for NS record',\n\t\t'dns_srv_prioempty' => 'Invalid SRV priority given',\n\t\t'dns_srv_invalidcontent' => 'Invalid SRV content, must contain of fields weight, port and target, e.g.: 5 5060 sipserver.example.com.',\n\t\t'dns_srv_needdom' => 'The SRV target value must be a valid domain-name',\n\t\t'dns_srv_noalias' => 'The SRV-target value cannot be an CNAME entry.',\n\t\t'dns_duplicate_entry' => 'Record already exists',\n\t\t'dns_notfoundorallowed' => 'Domain not found or no permission',\n\t\t'dns_loc_invalid' => 'The LOC record has invalid content',\n\t\t'dns_rp_invalid' => 'The RP record has invalid content',\n\t\t'dns_sshfp_invalid' => 'The SSHFP record has invalid content',\n\t\t'dns_tlsa_invalid' => 'The TLSA record has invalid content',\n\t\t'dns_naptr_invalid' => 'The NAPTR record has invalid content',\n\t\t'domain_nopunycode' => 'You must not specify punycode (IDNA). The domain will automatically be converted',\n\t\t'domain_noipaddress' => 'Cannot add an IP address as domain',\n\t\t'dns_record_toolong' => 'Records/labels can only be up to 63 characters',\n\t\t'noipportgiven' => 'No IP/port given',\n\t\t'nosslippportgiven' => 'When enabling SSL you need to select a SSL IP/port',\n\t\t'jsonextensionnotfound' => 'This feature requires the php json-extension.',\n\t\t'cannotdeletesuperadmin' => 'The first admin cannot be deleted.',\n\t\t'no_wwwcnamae_ifwwwalias' => 'Cannot set CNAME record for \"www\" as domain is set to generate a www-alias. Please change settings to either \"No alias\" or \"Wildcard alias\"',\n\t\t'local_group_exists' => 'The given group already exists on the system.',\n\t\t'local_group_invalid' => 'The given group name is invalid',\n\t\t'local_user_invalid' => 'The given user name is invalid or does not exist',\n\t\t'local_user_isfroxloruser' => 'The given user name is a froxlor managed username and cannot be used in this context',\n\t\t'invaliddnsforletsencrypt' => 'The domains DNS does not include any of the chosen IP addresses. Let\\'s Encrypt certificate generation not possible.',\n\t\t'notallowedphpconfigused' => 'Trying to use php-config which is not assigned to customer',\n\t\t'pathmustberelative' => 'The user does not have the permission to specify directories outside the customers home-directory. Please specify a relative path (no leading /).',\n\t\t'mysqlserverstillhasdbs' => 'Cannot remove database server from customers allow-list as there are still databases on it.',\n\t\t'domaincannotbeedited' => 'You are not permitted to edit the domain %s',\n\t\t'invalidcronjobintervalvalue' => 'Cronjob interval must be one of: %s',\n\t\t'phpgdextensionnotavailable' => 'The PHP GD extension is not available. Unable to validate image-data',\n\t\t'2fa_wrongcode' => 'The code entered is not valid',\n\t\t'gnupgextensionnotavailable' => 'The PHP GnuPG extension is not available. Unable to validate PGP Public Key',\n\t\t'invalidpgppublickey' => 'The PGP Public Key is not valid',\n\t\t'invalid_validtime' => 'Valid time in seconds can only be between 10 and 120',\n\t\t'customerphpenabledbutnoconfig' => 'Customer has PHP activated but no PHP-configuration was selected.',\n\t\t'emaildomainstillhasaddresses' => 'Cannot deactivate mail-domain flag, as there are still email-addresses for this domain.',\n\t\t'tls13requiredforhttp3' => 'Domain http3 flag enabled but ssl-protocols does not include TLSv1.3',\n\t\t'senderdomainnotowned' => 'Given domain \"%s\" cannot be used.',\n\t\t'emailhasnoaccount' => 'Given email address \"%s\" has no account, cannot add sender address.',\n\t],\n\t'extras' => [\n\t\t'description' => 'Here you can add some extras, for example directory protection.<br />The system will need some time to apply the new settings after every change.',\n\t\t'directoryprotection_add' => 'Add directory protection',\n\t\t'view_directory' => 'Display directory content',\n\t\t'pathoptions_add' => 'Add path options',\n\t\t'directory_browsing' => 'Directory content browsing',\n\t\t'pathoptions_edit' => 'Edit path options',\n\t\t'error404path' => '404',\n\t\t'error403path' => '403',\n\t\t'error500path' => '500',\n\t\t'error401path' => '401',\n\t\t'errordocument404path' => 'ErrorDocument 404',\n\t\t'errordocument403path' => 'ErrorDocument 403',\n\t\t'errordocument500path' => 'ErrorDocument 500',\n\t\t'errordocument401path' => 'ErrorDocument 401',\n\t\t'execute_perl' => 'Execute perl/CGI',\n\t\t'htpasswdauthname' => 'Authentication reason (AuthName)',\n\t\t'directoryprotection_edit' => 'Edit directory protection',\n\t\t'export' => 'Create data dump',\n\t\t'dump_web' => 'Include web-data',\n\t\t'dump_mail' => 'Include mail-data',\n\t\t'dump_dbs' => 'Include databases',\n\t\t'path_protection_label' => '<strong class=\"text-danger\">Important</strong>',\n\t\t'path_protection_info' => 'We strongly recommend protecting the given path, see \"Extras\" -> \"Directory protection\"',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Here you can create and change your FTP-accounts.<br />The changes are made instantly and the accounts can be used immediately.',\n\t\t'account_add' => 'Create account',\n\t\t'account_edit' => 'Edit ftp account',\n\t\t'editpassdescription' => 'Set new password or leave blank for no change.',\n\t\t'sshkey_add' => 'Add ssh-key',\n\t\t'sshkey_edit' => 'Edit ssh-key',\n\t],\n\t'gender' => [\n\t\t'title' => 'Title',\n\t\t'male' => 'Mr.',\n\t\t'female' => 'Mrs.',\n\t\t'undef' => '',\n\t],\n\t'imprint' => 'Legal notes',\n\t'index' => [\n\t\t'customerdetails' => 'Customer details',\n\t\t'accountdetails' => 'Account details',\n\t],\n\t'integrity_check' => [\n\t\t'databaseCharset' => 'Character set of database (should be UTF-8)',\n\t\t'domainIpTable' => 'IP &lt;&dash;&gt; domain references',\n\t\t'subdomainSslRedirect' => 'False SSL-redirect flag for non-ssl domains',\n\t\t'froxlorLocalGroupMemberForFcgidPhpFpm' => 'froxlor-user in the customer groups (for FCGID/php-fpm)',\n\t\t'webserverGroupMemberForFcgidPhpFpm' => 'Webserver-user in the customer groups (for FCGID/php-fpm)',\n\t\t'subdomainLetsencrypt' => 'Main domains with no SSL-Port assigned don\\'t have any subdomains with active SSL redirect',\n\t],\n\t'logger' => [\n\t\t'date' => 'Date',\n\t\t'type' => 'Type',\n\t\t'action' => 'Action',\n\t\t'user' => 'User',\n\t\t'truncate' => 'Empty log',\n\t\t'reseller' => 'Reseller',\n\t\t'admin' => 'Administrator',\n\t\t'cron' => 'Cronjob',\n\t\t'login' => 'Login',\n\t\t'intern' => 'Internal',\n\t\t'unknown' => 'Unknown',\n\t],\n\t'login' => [\n\t\t'username' => 'Username',\n\t\t'password' => 'Password',\n\t\t'language' => 'Language',\n\t\t'login' => 'Login',\n\t\t'logout' => 'Logout',\n\t\t'profile_lng' => 'Profile language',\n\t\t'welcomemsg' => 'Please log in to access your account.',\n\t\t'forgotpwd' => 'Forgot your password?',\n\t\t'presend' => 'Reset password',\n\t\t'email' => 'E-mail address',\n\t\t'remind' => 'Reset my password',\n\t\t'usernotfound' => 'User not found!',\n\t\t'backtologin' => 'Back to login',\n\t\t'combination_not_found' => 'Combination of user and email address not found.',\n\t\t'2fa' => 'Two-factor authentication (2FA)',\n\t\t'2facode' => 'Please enter 2FA code',\n\t\t'2faremember' => 'Trust browser'\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Hello,\\\\n\\\\nyour mail account {EMAIL}\\\\nwas set up successfully.\\\\n\\\\nThis is an automatically created\\\\ne-mail, please do not answer!\\\\n\\\\nYours sincerely, your administrator',\n\t\t\t'subject' => 'Mail account set up successfully',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Hello {SALUTATION},\\\\n\\\\nhere is your account information:\\\\n\\\\nUsername: {USERNAME}\\\\nPassword: {PASSWORD}\\\\n\\\\nThank you,\\\\nyour administrator',\n\t\t\t'subject' => 'Account information',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Hello {SALUTATION},\\\\n\\\\nyour Mail account {EMAIL}\\\\nwas set up successfully.\\\\nYour password is {PASSWORD}.\\\\n\\\\nThis is an automatically created\\\\ne-mail, please do not answer!\\\\n\\\\nYours sincerely, your administrator',\n\t\t\t'subject' => 'Mail account set up successfully',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Password reset',\n\t\t\t'mailbody' => 'Hello {SALUTATION},\\\\n\\\\nhere is your link for setting a new password. This link is valid for the next 24 hours.\\\\n\\\\n{LINK}\\\\n\\\\nThank you,\\\\nyour administrator',\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] New database created',\n\t\t\t'mailbody' => 'Hello {CUST_NAME},\n\nyou have just added a new database. Here is the entered information:\n\nDatabasename: {DB_NAME}\nPassword: {DB_PASS}\nDescription: {DB_DESC}\nDB-Hostname: {DB_SRV}\nphpMyAdmin: {PMA_URI}\nYours sincerely, your administrator',\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'New ftp-user created',\n\t\t\t'mailbody' => 'Hello {CUST_NAME},\n\nyou have just added a new ftp-user. Here is the entered information:\n\nUsername: {USR_NAME}\nPassword: {USR_PASS}\nPath: {USR_PATH}\n\nYours sincerely, your administrator',\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => 'Dear {SALUTATION},\\\\n\\\\nyou used {TRAFFICUSED} of your available {TRAFFIC} of traffic.\\\\nThis is more than {MAX_PERCENT}%%.\\\\n\\\\nYours sincerely, your administrator',\n\t\t\t'subject' => 'Reaching your traffic limit',\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => 'Dear {SALUTATION},\\\\n\\\\nyou used {DISKUSED} of your available {DISKAVAILABLE} of diskspace.\\\\nThis is more than {MAX_PERCENT}%%.\\\\n\\\\nYours sincerely, your administrator',\n\t\t\t'subject' => 'Reaching your diskspace limit',\n\t\t],\n\t\t'2fa' => [\n\t\t\t'mailbody' => 'Hello,\\\\n\\\\nyour 2FA login-code is: {CODE}.\\\\n\\\\nThis is an automatically created\\\\ne-mail, please do not answer!\\\\n\\\\nYours sincerely, your administrator',\n\t\t\t'subject' => 'froxlor - 2FA Code',\n\t\t],\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Main',\n\t\t\t'changepassword' => 'Change password',\n\t\t\t'changelanguage' => 'Change language',\n\t\t\t'username' => 'Logged in as: ',\n\t\t\t'changetheme' => 'Change theme',\n\t\t\t'apihelp' => 'API help',\n\t\t\t'apikeys' => 'API keys',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'Email',\n\t\t\t'emails' => 'Addresses',\n\t\t\t'webmail' => 'Webmail',\n\t\t\t'emailsoverview' => 'Email domains overview',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Databases',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domains',\n\t\t\t'settings' => 'Domains overview',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Accounts',\n\t\t\t'webftp' => 'WebFTP',\n\t\t\t'sshkeys' => 'SSH keys',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extras',\n\t\t\t'directoryprotection' => 'Directory protection',\n\t\t\t'pathoptions' => 'Path options',\n\t\t\t'export' => 'Data export',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Traffic',\n\t\t\t'current' => 'Current Month',\n\t\t\t'overview' => 'Total traffic',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'PHP Configurations',\n\t\t\t'fpmdaemons' => 'PHP-FPM versions',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'System log',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'No e-mail has been sent because there are no recipients in the database',\n\t\t'success' => 'Successfully sent message to %s recipients',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'User/Database name',\n\t\t'databasedescription' => 'Database description',\n\t\t'database_create' => 'Create database',\n\t\t'description' => 'Here you can create and change your MySQL-databases.<br />The changes are made instantly and the database can be used immediately.<br />At the menu on the left side you find the tool phpMyAdmin with which you can easily administer your database.<br /><br />To use your databases in your own php-scripts use the following settings: (The data in <i>italics</i> have to be changed into the equivalents you typed in!)<br />Hostname: <b><SQL_HOST></b><br />Username: <b><i>databasename</i></b><br />Password: <b><i>the password you\\'ve chosen</i></b><br />Database: <b><i>databasename</i></b>',\n\t\t'mysql_server' => 'MySQL-Server',\n\t\t'database_edit' => 'Edit database',\n\t\t'size' => 'Size',\n\t\t'privileged_user' => 'Privileged database user',\n\t\t'privileged_passwd' => 'Password for privileged user',\n\t\t'unprivileged_passwd' => 'Password for unprivileged user',\n\t\t'mysql_ssl_ca_file' => 'SSL server certificate',\n\t\t'mysql_ssl_verify_server_certificate' => 'Verify SSL server certificate',\n\t\t'globaluserinfo' => 'To access your databases, you can additionally use your froxlor login (user: %s) which automatically has access to all your databases.<br />It is recommended <b>not</b> to use this for applications, only for administration (e.g. via phpMyAdmin).',\n\t\t'edit_global_user' => 'Edit admin user',\n\t],\n\t'opcacheinfo' => [\n\t\t'generaltitle' => 'General Information',\n\t\t'resetcache' => 'Reset OPcache',\n\t\t'version' => 'OPCache version',\n\t\t'phpversion' => 'PHP version',\n\t\t'runtimeconf' => 'Runtime Configuration',\n\t\t'start' => 'Start time',\n\t\t'lastreset' => 'Last restart',\n\t\t'oomrestarts' => 'OOM restart count',\n\t\t'hashrestarts' => 'Hash restart count',\n\t\t'manualrestarts' => 'Manual restart count',\n\t\t'hitsc' => 'Hits count',\n\t\t'missc' => 'Miss count',\n\t\t'blmissc' => 'Blacklist miss count',\n\t\t'status' => 'Status',\n\t\t'never' => 'never',\n\t\t'enabled' => 'OPcache Enabled',\n\t\t'cachefull' => 'Cache full',\n\t\t'restartpending' => 'Pending restart',\n\t\t'restartinprogress' => 'Restart in progress',\n\t\t'cachedscripts' => 'Cached scripts count',\n\t\t'memusage' => 'Memory usage',\n\t\t'totalmem' => 'Total memory',\n\t\t'usedmem' => 'Used memory',\n\t\t'freemem' => 'Free memory',\n\t\t'wastedmem' => 'Wasted memory',\n\t\t'maxkey' => 'Maximum keys',\n\t\t'usedkey' => 'Used keys',\n\t\t'wastedkey' => 'Wasted keys',\n\t\t'strinterning' => 'String interning',\n\t\t'strcount' => 'String count',\n\t\t'keystat' => 'Cached keys statistic',\n\t\t'used' => 'Used',\n\t\t'free' => 'Free',\n\t\t'blacklist' => 'Blacklist',\n\t\t'novalue' => '<i>no value</i>',\n\t\t'true' => '<i>true</i>',\n\t\t'false' => '<i>false</i>',\n\t\t'funcsavail' => 'Available functions',\n\t],\n\t'panel' => [\n\t\t'edit' => 'Edit',\n\t\t'delete' => 'Delete',\n\t\t'create' => 'Create',\n\t\t'save' => 'Save',\n\t\t'yes' => 'Yes',\n\t\t'no' => 'No',\n\t\t'emptyfornochanges' => 'empty for no changes',\n\t\t'emptyfordefault' => 'empty for defaults',\n\t\t'path' => 'Path',\n\t\t'toggle' => 'Toggle',\n\t\t'next' => 'Next',\n\t\t'dirsmissing' => 'Can not find or read the directory!',\n\t\t'unlimited' => '∞',\n\t\t'urloverridespath' => 'URL (overrides path)',\n\t\t'pathorurl' => 'Path or URL',\n\t\t'ascending' => 'ascending',\n\t\t'descending' => 'descending',\n\t\t'search' => 'Search',\n\t\t'used' => 'used',\n\t\t'translator' => 'Translator',\n\t\t'reset' => 'Discard changes',\n\t\t'pathDescription' => 'If the directory doesn\\'t exist, it will be created automatically.',\n\t\t'pathDescriptionEx' => '<br /><br /><span class=\"text-danger\">Please note:</span> The path <code>/</code> is not allowed due to administrative settings, it will automatically be set to <code>/chosen.subdomain.tld/</code> if not set to another directory.',\n\t\t'pathDescriptionSubdomain' => 'If the directory doesn\\'t exist, it will be created automatically.<br /><br />If you want a redirect to another domain then this entry has to start with http:// or https://.<br /><br />If the URL ends with / it is considered a folder, if not, it is treated as file.',\n\t\t'back' => 'Back',\n\t\t'reseller' => 'reseller',\n\t\t'admin' => 'admin',\n\t\t'customer' => 'customer/s',\n\t\t'send' => 'send',\n\t\t'nosslipsavailable' => 'There are currently no ssl ip/port combinations for this server',\n\t\t'backtooverview' => 'Back to overview',\n\t\t'dateformat' => 'YYYY-MM-DD',\n\t\t'dateformat_function' => 'Y-m-d',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Default',\n\t\t'never' => 'Never',\n\t\t'active' => 'Active',\n\t\t'please_choose' => 'Please choose',\n\t\t'allow_modifications' => 'Allow modifications',\n\t\t'megabyte' => 'MegaByte',\n\t\t'not_supported' => 'Not supported in: ',\n\t\t'view' => 'view',\n\t\t'toomanydirs' => 'Too many subdirectories. Falling back to manual path-select.',\n\t\t'abort' => 'Abort',\n\t\t'not_activated' => 'not activated',\n\t\t'off' => 'off',\n\t\t'options' => 'Options',\n\t\t'neverloggedin' => 'No login yet',\n\t\t'descriptionerrordocument' => 'Can be an URL, path to a file or just a string wrapped around \" \"<br />Leave empty to use server default value.',\n\t\t'unlock' => 'Unlock',\n\t\t'theme' => 'Theme',\n\t\t'variable' => 'Variable',\n\t\t'description' => 'Description',\n\t\t'cancel' => 'Cancel',\n\t\t'ssleditor' => 'SSL settings for this domain',\n\t\t'ssleditor_infoshared' => 'Currently using certificate of parentdomain',\n\t\t'ssleditor_infoglobal' => 'Currently using global certificate',\n\t\t'dashboard' => 'Dashboard',\n\t\t'assigned' => 'Assigned',\n\t\t'available' => 'Available',\n\t\t'news' => 'News',\n\t\t'newsfeed_disabled' => 'The newsfeed is disabled. Click the edit icon to go to the settings.',\n\t\t'ftpdesc' => 'FTP description',\n\t\t'letsencrypt' => 'Using Let\\'s encrypt',\n\t\t'set' => 'Apply',\n\t\t'shell' => 'Shell',\n\t\t'sshkeydesc' => 'SSH-key description',\n\t\t'ftpuser' => 'FTP user',\n\t\t'sshpubkey' => 'SSH public key',\n\t\t'sshpubkeyph' => \"Starts with 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com'\",\n\t\t'sshfingerprint' => 'Fingerprint',\n\t\t'exportpath' => [\n\t\t\t'title' => 'Destination path for the exported data',\n\t\t\t'description' => 'This is the path where the export-archive will be stored. If web-data is being included, all files from the homedir are stored excluding the folder specified here.',\n\t\t],\n\t\t'export_pgp_public_key' => [\n\t\t\t'title' => 'Public PGP key for encryption',\n\t\t\t'description' => 'This is the public PGP key which will be used to encrypt the export. If you leave this field empty, the export will not be encrypted.',\n\t\t],\n\t\t'pgp_public_key' => 'Public PGP key',\n\t\t'none_value' => 'None',\n\t\t'viewlogs' => 'View logfiles',\n\t\t'not_configured' => 'System not configured yet. Click here to go to configurations.',\n\t\t'start_setup' => 'Start setup',\n\t\t'ihave_configured' => 'I have configured the services',\n\t\t'system_is_configured' => '<i class=\"fa-solid fa-circle-exclamation me-1\"></i>System is already set as configured',\n\t\t'settings_before_configuration' => 'Please be sure you adjusted the settings prior to configuring the services here',\n\t\t'image_field_delete' => 'Delete the existing current image',\n\t\t'usage_statistics' => 'Resource usage',\n\t\t'security_question' => 'Security question',\n\t\t'listing_empty' => 'No entries found',\n\t\t'unspecified' => 'unspecified',\n\t\t'settingsmode' => 'Mode',\n\t\t'settingsmodebasic' => 'Basic',\n\t\t'settingsmodeadvanced' => 'Advanced',\n\t\t'settingsmodetoggle' => 'Click to toggle mode',\n\t\t'modalclose' => 'Close',\n\t\t'managetablecolumnsmodal' => [\n\t\t\t'title' => 'Manage table columns',\n\t\t\t'description' => 'Here you can customize the visible columns',\n\t\t],\n\t\t'mandatoryfield' => 'Field is mandatory',\n\t\t'select_all' => 'Select all',\n\t\t'unselect_all' => 'Unselect all',\n\t\t'searchtablecolumnsmodal' => [\n\t\t\t'title' => 'Search in fields',\n\t\t\t'description' => 'Select the field you want to search in'\n\t\t],\n\t\t'upload_import' => 'Upload and import',\n\t\t'profile' => 'My profile',\n\t\t'use_checkbox_for_unlimited' => 'The value \"0\" deactivates this resource. The checkbox on the right allows \"unlimited\" usage.',\n\t\t'use_checkbox_to_disable' => 'To disable, activate the checkbox on the right of the input field',\n\t\t'distro_mismatch' => 'It seems that you have upgraded to a new distribution. Please remember to reconfigure services accordingly.',\n\t\t'set_new_distro' => 'Set distribution',\n\t\t'dismiss' => 'Dismiss',\n\t\t'confirmaction' => 'Confirm action',\n\t\t'confirmactiondesc' => 'Please confirm this action by entering your current account password',\n\t\t'authenticationfailed' => 'Authentication failed',\n\t\t'noauthentication' => 'Missing authentication',\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => 'Local user to use for PHP-FPM (froxlor vHost)',\n\t\t'vhost_httpgroup' => 'Local group to use for PHP-FPM (froxlor vHost)',\n\t\t'ownvhost' => [\n\t\t\t'title' => 'Enable PHP-FPM for the froxlor vHost',\n\t\t\t'description' => 'If enabled, froxlor will also be running under a local user',\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => 'Use mod_proxy / mod_proxy_fcgi',\n\t\t\t'description' => '<strong class=\"text-danger\">Must be enabled when using Debian 9.x (Stretch) or newer</strong>. Activate to use php-fpm via mod_proxy_fcgi. Requires at least apache-2.4.9',\n\t\t],\n\t\t'ini_flags' => 'Enter possible <strong>php_flag</strong>s for php.ini. One entry per line',\n\t\t'ini_values' => 'Enter possible <strong>php_value</strong>s for php.ini. One entry per line',\n\t\t'ini_admin_flags' => 'Enter possible <strong>php_admin_flag</strong>s for php.ini. One entry per line',\n\t\t'ini_admin_values' => 'Enter possible <strong>php_admin_value</strong>s for php.ini. One entry per line',\n\t],\n\t'privacy' => 'Privacy policy',\n\t'pwdreminder' => [\n\t\t'success' => 'Password reset successfully requested. Please follow the instructions in the email you received.',\n\t\t'notallowed' => 'Unknown user or password reset is disabled',\n\t\t'changed' => 'Your password has been updated successfully. You can now login with your new password.',\n\t\t'wrongcode' => 'Sorry, your activation-code does not exist or has already expired.',\n\t\t'choosenew' => 'Set new password',\n\t],\n\t'question' => [\n\t\t'question' => 'Security question',\n\t\t'admin_customer_reallydelete' => 'Do you really want to delete the customer %s? This cannot be undone!',\n\t\t'admin_domain_reallydelete' => 'Do you really want to delete the domain %s?<br><span class=\"text-danger\"><strong>NOTE:</strong> All subdomains, ftp-accounts and email-addresses/accounts connected to this domain will be removed!</span>',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Do you really want to disable this security setting OpenBasedir?',\n\t\t'admin_admin_reallydelete' => 'Do you really want to delete the admin %s? Every customer and domain will be reassigned to your account.',\n\t\t'admin_template_reallydelete' => 'Do you really want to delete the template \\'%s\\'?',\n\t\t'domains_reallydelete' => 'Do you really want to delete the domain %s?',\n\t\t'email_reallydelete' => 'Do you really want to delete the email-address %s?',\n\t\t'email_reallydelete_account' => 'Do you really want to delete the email-account of %s?',\n\t\t'email_reallydelete_forwarder' => 'Do you really want to delete the forwarder %s?',\n\t\t'email_reallydelete_sender' => 'Do you really want to delete the allowed sender %s?',\n\t\t'extras_reallydelete' => 'Do you really want to delete the directory protection for %s?',\n\t\t'extras_reallydelete_pathoptions' => 'Do you really want to delete the path options for %s?',\n\t\t'extras_reallydelete_export' => 'Do you really want to abort the planned export job?',\n\t\t'ftp_reallydelete' => 'Do you really want to delete the FTP-account %s?',\n\t\t'sshkey_reallydelete' => 'Do you really want to delete the ssh-key %s?',\n\t\t'mysql_reallydelete' => 'Do you really want to delete the database %s? This cannot be undone!',\n\t\t'admin_configs_reallyrebuild' => 'Do you really want to rebuild all config files?',\n\t\t'admin_customer_alsoremovefiles' => 'Remove user files too?',\n\t\t'admin_customer_alsoremovemail' => 'Completely remove email data from filesystem?',\n\t\t'admin_customer_alsoremoveftphomedir' => 'Also remove FTP-user homedir?',\n\t\t'admin_ip_reallydelete' => 'Do you really want to delete the IP address %s?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Are you sure, you want the document root for this domain, not being within the customer root of the customer?',\n\t\t'admin_counters_reallyupdate' => 'Do you really want to recalculate resource usage?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Do you really want to wipe all unencrypted mail account passwords from the table mail_users? This cannot be reverted! The setting to store email passwords unencrypted will also be set to OFF',\n\t\t'logger_reallytruncate' => 'Do you really want to truncate the table \"%s\"?',\n\t\t'admin_quotas_reallywipe' => 'Do you really want to wipe all quotas on table mail_users? This cannot be reverted!',\n\t\t'admin_quotas_reallyenforce' => 'Do you really want to enforce the default quota to all Users? This cannot be reverted!',\n\t\t'phpsetting_reallydelete' => 'Do you really want to delete these settings? All domains which use these settings currently will be changed to the default config.',\n\t\t'fpmsetting_reallydelete' => 'Do you really want to delete these php-fpm settings? All php configurations which use these settings currently will be changed to the default config.',\n\t\t'customer_reallyunlock' => 'Do you really want to unlock customer %s?',\n\t\t'admin_integritycheck_reallyfix' => 'Do you really want to try fixing all database integrity problems automatically?',\n\t\t'plan_reallydelete' => 'Do you really want to delete the hosting plan %s?',\n\t\t'apikey_reallydelete' => 'Do you really want to delete this api-key?',\n\t\t'apikey_reallyadd' => 'Do you really want to create a new api-key?',\n\t\t'dnsentry_reallydelete' => 'Do you really want to delete this zone entry?',\n\t\t'certificate_reallydelete' => 'Do you really want to delete this certificate?',\n\t\t'cache_reallydelete' => 'Do you really want to clear the cache?',\n\t\t'please_enter_otp' => 'Please enter 2FA code',\n\t\t'admin_mysqlserver_reallydelete' => 'Do you really want to delete this MySQL-server?',\n\t],\n\t'redirect_desc' => [\n\t\t'rc_default' => 'default',\n\t\t'rc_movedperm' => 'moved permanently',\n\t\t'rc_found' => 'found',\n\t\t'rc_seeother' => 'see other',\n\t\t'rc_tempred' => 'temporary redirect',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Session Timeout',\n\t\t\t'description' => 'How long does a user have to be inactive before a session gets invalid (seconds)?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Customer prefix',\n\t\t\t'description' => 'Which prefix should customer accounts have?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'SQL Prefix',\n\t\t\t'description' => 'Which prefix should MySQL accounts have?</br>Use \"RANDOM\" as value to get a 3-digit random prefix</br>Use \"DBNAME\" as the value, a database name field is used together with the customer name as a prefix.',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP Prefix',\n\t\t\t'description' => 'Which prefix should ftp accounts have?<br/><b>If you change this you also have to change the Quota SQL Query in your FTP Server config file in case you use it!</b> ',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Home directory',\n\t\t\t'description' => 'Where should all home directories be stored?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Logfiles directory',\n\t\t\t'description' => 'Where should all log files be stored?',\n\t\t],\n\t\t'logfiles_script' => [\n\t\t\t'title' => 'Custom script to pipe log-files to',\n\t\t\t'description' => 'You can specify a script here and use the placeholders <strong>{LOGFILE}, {DOMAIN} and {CUSTOMER}</strong> if needed. In case you want to use it you will need to activate the <strong>Pipe webserver logfiles</strong> option too. No prefixed pipe-character is needed.',\n\t\t],\n\t\t'logfiles_format' => [\n\t\t\t'title' => 'Access-log format',\n\t\t\t'description' => 'Enter a custom log-format here according to your webservers specifications, leave empty for default. Depending on your format the string must be quoted.<br/>If used with nginx, it will look like <i>log_format frx_custom {CONFIGURED_VALUE}</i>.<br/>If used with Apache, it will look like <i>LogFormat {CONFIGURED_VALUE} frx_custom</i>.<br/><strong>Attention</strong>: The code won\\'t be checked for any errors. If it contains errors, webserver might not start again!',\n\t\t],\n\t\t'logfiles_type' => [\n\t\t\t'title' => 'Access-log type',\n\t\t\t'description' => 'Choose between <strong>combined</strong> or <strong>vhost_combined</strong> here.',\n\t\t],\n\t\t'logfiles_piped' => [\n\t\t\t'title' => 'Pipe webserver logfiles to specified script (see above)',\n\t\t\t'description' => 'When using a custom script for the logfiles you need to activate this in order for it to be executed',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'IP-address',\n\t\t\t'description' => 'What\\'s the main IP-address of this server?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Hostname',\n\t\t\t'description' => 'What\\'s the Hostname of this server?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Webserver reload command',\n\t\t\t'description' => 'What\\'s the webserver command to reload configfiles?',\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => 'Enable Nameserver',\n\t\t\t'description' => 'Here the Nameserver can be enabled and disabled globally.',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Dns server config directory',\n\t\t\t'description' => 'Where should dns-server config files be saved?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'DNS server reload command',\n\t\t\t'description' => 'What\\'s the command to reload the dns server daemon?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'Mails-UID',\n\t\t\t'description' => 'Which UserID should mails have?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mails-GID',\n\t\t\t'description' => 'Which GroupID should mails have?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Mails-homedir',\n\t\t\t'description' => 'Where should all mails be stored?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Sender',\n\t\t\t'description' => 'What\\'s the sender address for emails sent from the Panel?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin URL',\n\t\t\t'description' => 'What\\'s the URL to phpMyAdmin? (has to start with http(s)://)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'Webmail URL',\n\t\t\t'description' => 'What\\'s the URL to webmail? (has to start with http(s)://)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP URL',\n\t\t\t'description' => 'What\\'s the URL to  WebFTP? (has to start with http(s)://)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'What\\'s your standard server language?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Max Login Attempts',\n\t\t\t'description' => 'Maximum login attempts after which the account gets disabled.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Deactivation Time',\n\t\t\t'description' => 'Time (sec.) an account gets disabled after too many login tries.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Type of path input',\n\t\t\t'description' => 'Should a path be selected by a dropdown menu or by an input field?',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Nameservers',\n\t\t\t'description' => 'A comma separated list containing the hostnames of all nameservers. The first one will be the primary one.',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'MX servers',\n\t\t\t'description' => 'A comma separated list containing a pair of a number and a hostname separated by whitespace (e.g. \\'10 mx.example.com\\') containing the mx servers.',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Entries per page',\n\t\t\t'description' => 'How many entries shall be shown on one page? (0 = disable paging)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'Default IP/Port',\n\t\t\t'description' => 'Select all IP-addresses you want to use as default for new domains',\n\t\t],\n\t\t'defaultsslip' => [\n\t\t\t'title' => 'Default SSL IP/Port',\n\t\t\t'description' => 'Select all ssl-enabled IP-addresses you want to use as default for new domains',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Paths to append to OpenBasedir',\n\t\t\t'description' => 'These paths (separated by colons) will be added to the OpenBasedir-statement in every vHost-container.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Use natural human sorting in list view',\n\t\t\t'description' => 'Sorts lists as web1 -> web2 -> web11 instead of web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Docroot for deactivated users',\n\t\t\t'description' => 'When a user is deactivated this path is used as his docroot. Leave empty for not creating a vHost at all.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Also save passwords of mail accounts unencrypted in database',\n\t\t\t'description' => 'If this is set to yes, all passwords will also be saved unencrypted (clear text, plain readable for everyone with database access) in the mail_users-table. Only activate this if you intend to use SASL!',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'FTP accounts @domain',\n\t\t\t'description' => 'Customers can create FTP accounts user@customerdomain?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Enable FCGID',\n\t\t\t'description' => 'Use this to run PHP with the corresponding user account.<br /><br /><b>This needs a special webserver configuration for Apache, see <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - handbook</a></b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Configuration directory',\n\t\t\t\t'description' => 'Where should all fcgid-configuration files be stored? If you don\\'t use a self compiled suexec binary, which is the normal situation, this path must be under /var/www/<br /><br /><div class=\"text-danger\">NOTE: This folder\\'s content gets deleted regularly so avoid storing data in there manually.</div>',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Temp directory',\n\t\t\t\t'description' => 'Where should the temp directories be stored',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Processes per domain',\n\t\t\t\t'description' => 'How many processes should be started/allowed per domain? The value 0 is recommended cause PHP will then manage the amount of processes itself very efficiently.',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper in Vhosts',\n\t\t\t\t'description' => 'How should the wrapper be included in the Vhosts',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Global PEAR directories',\n\t\t\t\t'description' => 'Which global PEAR directories should be replaced in every php.ini config? Different directories must be separated by a colon.',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Maximum Requests per domain',\n\t\t\t\t'description' => 'How many requests should be allowed per domain?',\n\t\t\t],\n\t\t\t'defaultini' => 'Default PHP configuration for new domains',\n\t\t\t'defaultini_ownvhost' => 'Default PHP configuration for froxlor-vHost',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Idle Timeout',\n\t\t\t\t'description' => 'Timeout setting for Mod FastCGI.',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Use alternative email-address',\n\t\t\t'description' => 'Send the password-email to a different address during email-account-creation',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Webserver vHost configuration file/dirname',\n\t\t\t'description' => 'Where should the vHost configuration be stored? You could either specify a file (all vHosts in one file) or directory (each vHost in his own file) here.',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Webserver diroptions configuration file/dirname',\n\t\t\t'description' => 'Where should the diroptions configuration be stored? You could either specify a file (all diroptions in one file) or directory (each diroption in his own file) here.',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Webserver htpasswd dirname',\n\t\t\t'description' => 'Where should the htpasswd files for directory protection be stored?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'MySQL-Access-Hosts',\n\t\t\t'description' => 'A comma separated list of hosts from which users should be allowed to connect to the MySQL-Server. To allow a subnet the netmask or cidr syntax is valid.',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Webalizer output',\n\t\t\t'description' => 'Verbosity of the webalizer-program',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Logging enabled/disabled',\n\t\t\t'severity' => 'Logging level',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Log-type(s)',\n\t\t\t\t'description' => 'Specify logtypes. To select multiple types, hold down CTRL while selecting.<br />Available logtypes are: syslog, file, mysql',\n\t\t\t],\n\t\t\t'logfile' => [\n\t\t\t\t'title' => 'Filename for log',\n\t\t\t\t'description' => 'Only used if log-type includes \"file\". This file will be created in froxlor/logs/. This folder is protected against public access.',\n\t\t\t],\n\t\t\t'logcron' => 'Log cronjobs',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Never',\n\t\t\t\t'once' => 'Once',\n\t\t\t\t'always' => 'Always',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => 'Enable SSL usage',\n\t\t\t\t'description' => 'Check this if you want to use SSL for your webserver',\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'Path to the SSL certificate',\n\t\t\t\t'description' => 'Specify the path including the filename of the .crt or .pem file (main certificate)',\n\t\t\t],\n\t\t\t'openssl_cnf' => 'Defaults for creating the Cert file',\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'Path to the SSL Keyfile',\n\t\t\t\t'description' => 'Specify the path including the filename for the private-key file (.key mostly)',\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'Path to the SSL CA certificate (optional)',\n\t\t\t\t'description' => 'Client authentication, set this only if you know what it is.',\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => 'Configure the allowed SSL ciphers',\n\t\t\t\t'description' => 'This is a list of ciphers that you want (or don\\'t want) to use when talking SSL. For a list of ciphers and how to include/exclude them, see sections \"CIPHER LIST FORMAT\" and \"CIPHER STRINGS\" on <a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">the man-page for ciphers</a>.<br /><br /><b>Default value is:</b><pre>ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305</pre>',\n\t\t\t],\n\t\t\t'apache24_ocsp_cache_path' => [\n\t\t\t\t'title' => 'Apache 2.4: path to the OCSP stapling cache',\n\t\t\t\t'description' => 'Configures the cache used to store OCSP responses which get included in TLS handshakes.',\n\t\t\t],\n\t\t\t'ssl_protocols' => [\n\t\t\t\t'title' => 'Configure the TLS protocol version',\n\t\t\t\t'description' => 'This is a list of ssl protocols that you want (or don\\'t want) to use when using SSL. <b>Notice:</b> Some older browsers may not support the newest protocol versions.<br /><br /><b>Default value is:</b><pre>TLSv1.2</pre>',\n\t\t\t],\n\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t'title' => 'Configure explicit TLSv1.3 ciphers if used',\n\t\t\t\t'description' => 'This is a list of ciphers that you want (or don\\'t want) to use when talking TLSv1.3. For a list of ciphers and how to include/exclude them, see <a href=\"https://wiki.openssl.org/index.php/TLS1.3\">the docs for TLSv1.3</a>.<br /><br /><b>Default value is empty</b>',\n\t\t\t],\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Default vHost-settings',\n\t\t\t'description' => 'The content of this field will be included into this ip/port vHost container directly. You can use the following variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (if applicable)<br/> Attention: The code won\\'t be checked for any errors. If it contains errors, webserver might not start again!',\n\t\t],\n\t\t'apache_globaldiropt' => [\n\t\t\t'title' => 'Directory options for customer-prefix',\n\t\t\t'description' => 'The content of this field will be included into the 05_froxlor_dirfix_nofcgid.conf apache config. If empty, the default value is used:<br><br>apache >=2.4<br><code>Require all granted<br>AllowOverride All</code><br><br>apache <=2.2<br><code>Order allow,deny<br>allow from all</code>',\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => 'The content of this field will be included into the domain vHost container directly. You can use the following variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (if applicable)<br/> Attention: The code won\\'t be checked for any errors. If it contains errors, webserver might not start again!',\n\t\t],\n\t\t'decimal_places' => 'Number of decimal places in traffic/webspace output',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Customer domain dns settings',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Allow customers to edit domain dns settings',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Use UNIX compatible usernames',\n\t\t\t'description' => 'Allows you to use <strong>-</strong> and <strong>_</strong> in usernames if <strong>No</strong>',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Allow password reset by customers',\n\t\t\t'description' => 'Customers can reset their password and an activation link will be sent to their e-mail address',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Allow password reset by admins',\n\t\t\t'description' => 'Admins/reseller can reset their password and an activation link will be sent to their e-mail address',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Mailbox-quota',\n\t\t\t'description' => 'The default quota for a new created mailboxes (MegaByte).',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Use mailbox-quota for customers',\n\t\t\t'description' => 'Activate to use quotas on mailboxes. Default is <b>No</b> since this requires a special setup.',\n\t\t\t'removelink' => 'Click here to wipe all quotas for mail accounts.',\n\t\t\t'enforcelink' => 'Click here to enforce default quota to all User mail accounts.',\n\t\t],\n\t\t'mail_enable_allow_sender' => [\n\t\t\t'title' => 'Allow \"allowed sender\" to be used by customers',\n\t\t\t'description' => 'If enabled, customers can specify \"allowed sender\" for email accounts to send with.<br>Default: off',\n\t\t],\n\t\t'mail_allow_external_domains' => [\n\t\t\t'title' => 'Allow external domains for \"allowed sender\"',\n\t\t\t'description' => 'If enabled, customer can enter any domain (except not owned domains on this system) as \"allowed sender\" for email accounts.<br>Default: off',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Allow multiple login',\n\t\t\t'description' => 'If activated a user could login multiple times.',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Allow moving domains between admins',\n\t\t\t'description' => 'If activated you can change the admin of a domain at domainsettings.<br /><b>Attention:</b> If a customer isn\\'t assigned to the same admin as the domain, the admin can see every other domain of that customer!',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Allow moving domains between customers',\n\t\t\t'description' => 'If activated you can change the customer of a domain at domainsettings.<br /><b>Attention:</b> froxlor changes the documentroot to the new customer\\'s default homedir (+ domain-folder if activated)',\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'If yes these custom vHost-settings will be added to all subdomains; if no subdomain-specialsettings are being removed.',\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Minimum password length',\n\t\t\t'description' => 'Here you can set a minimum length for passwords. \\'0\\' means: no minimum length required.',\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Store default index file also to new subfolders',\n\t\t\t'description' => 'If enabled, the default index-file is being stored to every subdomain-path newly created (not if the folder already exists!)',\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Reply-To address',\n\t\t\t'description' => 'Define an e-mail address as reply-to-address for mails sent by the panel.',\n\t\t],\n\t\t'adminmail_defname' => 'Panel e-mail sender name',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Customer standard subdomain',\n\t\t\t'description' => 'What hostname should be used to create standard subdomains for customer. If empty, the system-hostname is used.',\n\t\t],\n\t\t'awstats_path' => 'Path to AWStats \\'awstats_buildstaticpages.pl\\'',\n\t\t'awstats_conf' => 'AWStats configuration path',\n\t\t'defaultttl' => 'Domain TTL for bind in seconds (default \\'604800\\' = 1 week)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Enable default errordocuments for all customers',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'File/URL for error 401',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'File/URL for error 403',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'File/URL for error 404',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'File/URL for error 500',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'If pureftpd is selected the .ftpquota files for user quotas are created and updated daily',\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Allow customer redirects',\n\t\t\t'description' => 'Allow customers to choose the http-status code for redirects which will be used',\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Default redirect',\n\t\t\t'description' => 'Set the default redirect-code which should be used if the customer does not set it himself',\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Create mail-, imap-, pop3- and smtp-\"A record\" also with MX-Servers set',\n\t\t'froxlordirectlyviahostname' => 'Access froxlor directly via the hostname',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Regular expression for passwords',\n\t\t\t'description' => 'Here you can set a regular expression for passwords-complexity.<br />Empty = no specific requirement',\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'Enable FCGID for the froxlor vHost',\n\t\t\t'description' => 'If enabled, froxlor will also be running under a local user',\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'Enable SuExec workaround',\n\t\t\t\t'description' => 'Enable only if customer docroots are not within the apache suexec path.<br />If enabled, froxlor will generate a symlink from the customers perl-enabled directory + /cgi-bin/ to the given path.<br />Note that perl will then only work in the folders subdirectory /cgi-bin/ and not in the folder itself (as it does without this fix!)',\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Path for customer perl-enabled directory symlinks',\n\t\t\t\t'description' => 'You only need to set this if the SuExec-workaround is enabled.<br />ATTENTION: Be sure this path is within the suexec path or else this workaround is useless',\n\t\t\t],\n\t\t],\n\t\t'awstats_awstatspath' => 'Path to AWStats \\'awstats.pl\\'',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'Path to AWstats icons folder',\n\t\t\t'description' => 'e.g. /usr/share/awstats/htdocs/icon/',\n\t\t],\n\t\t'login_domain_login' => 'Allow login with domains',\n\t\t'perl_server' => [\n\t\t\t'title' => 'Perl server socket location',\n\t\t\t'description' => 'A simple guide can be found at: <a target=\"blank\" href=\"https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/\">nginx.com</a>',\n\t\t],\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP backend',\n\t\t\t'description' => 'this is where the PHP process is listening for requests from nginx, can be a unix socket of ip:port combination<br />*NOT used with php-fpm',\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'PHP reload command',\n\t\t\t'description' => 'this is used to reload the PHP backend if any is used<br />Default: blank<br />*NOT used with php-fpm',\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => 'Enable php-fpm',\n\t\t\t'description' => '<b>This needs a special webserver configuration see <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">PHP-FPM handbook</a></b>',\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'Configuration directory of php-fpm',\n\t\t\t'aliasconfigdir' => 'Configuration Alias-directory of php-fpm',\n\t\t\t'reload' => 'php-fpm restart command',\n\t\t\t'pm' => 'Process manager control (pm)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'The number of child processes',\n\t\t\t\t'description' => 'The number of child processes to be created when pm is set to \\'static\\' and the maximum number of child processes to be created when pm is set to \\'dynamic/ondemand\\'<br />Equivalent to the PHP_FCGI_CHILDREN',\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'The number of child processes created on startup',\n\t\t\t\t'description' => 'Note: Used only when pm is set to \\'dynamic\\'',\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'The desired minimum number of idle server processes',\n\t\t\t\t'description' => 'Note: Used only when pm is set to \\'dynamic\\'<br />Note: Mandatory when pm is set to \\'dynamic\\'',\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'The desired maximum number of idle server processes',\n\t\t\t\t'description' => 'Note: Used only when pm is set to \\'dynamic\\'<br />Note: Mandatory when pm is set to \\'dynamic\\'',\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Requests per child before respawning',\n\t\t\t\t'description' => 'For endless request processing specify \\'0\\'. Equivalent to PHP_FCGI_MAX_REQUESTS.',\n\t\t\t],\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Idle Timeout',\n\t\t\t\t'description' => 'Timeout setting for PHP FPM FastCGI.',\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'FastCGI IPC directory',\n\t\t\t\t'description' => 'The directory where the php-fpm sockets will be stored by the webserver.<br />This directory has to be readable for the webserver',\n\t\t\t],\n\t\t\t'limit_extensions' => [\n\t\t\t\t'title' => 'Allowed extensions',\n\t\t\t\t'description' => 'Limits the extensions of the main script FPM will allow to parse. This can prevent configuration mistakes on the web server side. You should only limit FPM to .php extensions to prevent malicious users to use other extensions to execute php code. Default value: .php',\n\t\t\t],\n\t\t\t'envpath' => 'Paths to add to the PATH environment. Leave empty for no PATH environment variable',\n\t\t\t'override_fpmconfig' => 'Override FPM-daemon settings (pm, max_children, etc.)',\n\t\t\t'override_fpmconfig_addinfo' => '<br /><span class=\"text-danger\">Only used if \"Override FPM-daemon settings\" is set to \"Yes\"</span>',\n\t\t\t'restart_note' => 'Attention: The config won\\'t be checked for any errors. If it contains errors, PHP-FPM might not start again!',\n\t\t\t'custom_config' => [\n\t\t\t\t'title' => 'Custom configuration',\n\t\t\t\t'description' => 'Add custom configuration to each PHP-FPM version instance, for example <i>pm.status_path = /status</i> for monitoring. Variables below can be used here.  <strong>Attention: The config won\\'t be checked for any errors. If it contains errors, PHP-FPM might not start again!</strong>',\n\t\t\t],\n\t\t\t'allow_all_customers' => [\n\t\t\t\t'title' => 'Assign this configuration to all currently existing customers',\n\t\t\t\t'description' => 'Set this to \"true\" if you want to assign this configuration to all currently existing customers so it can be used by them. This setting is not permanent but can be run multiple times.',\n\t\t\t],\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => 'Enable sending of reports about web- and traffic-usage',\n\t\t\t'webmax' => [\n\t\t\t\t'title' => 'Warning-level in percent for webspace',\n\t\t\t\t'description' => 'Valid values are 0 up to 150. Setting this value to 0 disables this report.',\n\t\t\t],\n\t\t\t'trafficmax' => [\n\t\t\t\t'title' => 'Warning-level in percent for traffic',\n\t\t\t\t'description' => 'Valid values are 0 up to 150. Setting this value to 0 disables this report.',\n\t\t\t],\n\t\t\t'report_web_bccadmin' => [\n\t\t\t\t'title' => 'BCC email for web-usage notification to admin',\n\t\t\t\t'description' => 'If enabled, the warning about disk-space usage sent to the customer is also being sent to the corresponding admin/reseller (BCC)'\n\t\t\t],\n\t\t],\n\t\t'dropdown' => 'Dropdown',\n\t\t'manual' => 'Manual',\n\t\t'default_theme' => 'Default theme',\n\t\t'validate_domain' => 'Validate domain names',\n\t\t'diskquota_enabled' => 'Quota activated?',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'Path to repquota',\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'Path to quotatool',\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => 'Partition, on which the customer files are stored',\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'Maildir name',\n\t\t\t'description' => 'Maildir directory into user\\'s account. Normally \\'Maildir\\', in some implementations \\'.maildir\\', and directly into user\\'s directory if left blank.',\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => 'Use Catchall',\n\t\t\t'description' => 'Do you want to provide your customers the catchall-feature?',\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => 'Use modifications for Apache 2.4',\n\t\t\t'description' => '<strong class=\"text-danger\">ATTENTION:</strong> use only if you actually have apache version 2.4 or higher installed<br />otherwise your webserver will not be able to start',\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'Path to fastcgi_params file',\n\t\t\t'description' => 'Specify the path to nginx\\'s fastcgi_params file including filename',\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => 'Use domain name as default value for DocumentRoot path',\n\t\t\t'description' => 'If enabled and DocumentRoot path is empty, default value will be the (sub)domain name.<br /><br />Examples: <br />/var/customers/webs/customer_name/example.com/<br />/var/customers/webs/customer_name/subdomain.example.com/',\n\t\t],\n\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t'title' => 'Hide subdomains in PHP-configuration overview',\n\t\t\t'description' => 'If activated the subdomains of customers will not be listed in the php-configurations overview, only the number of subdomains is shown.<br /><br />Note: This is only visible if you have enabled FCGID or PHP-FPM',\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => 'Hide standard-subdomains in PHP-configuration overview',\n\t\t\t'description' => 'If activated the standard-subdomains for customers will not be displayed in the php-configurations overview<br /><br />Note: This is only visible if you have enabled FCGID or PHP-FPM',\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => 'Choose which password-crypt method is to be used',\n\t\t\t'description' => 'Choose which password-crypt method is to be used. If you change this setting, only new passwords will be encrypted with the new method. Existing passwords will not be changed.',\n\t\t],\n\t\t'systemdefault' => 'System default',\n\t\t'panel_allow_theme_change_admin' => 'Allow admins to change the theme',\n\t\t'panel_allow_theme_change_customer' => 'Allow customers to change the theme',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'AXFR servers',\n\t\t\t'description' => 'A comma separated list of IP addresses allowed to transfer (AXFR) dns zones.',\n\t\t],\n\t\t'powerdns_mode' => [\n\t\t\t'title' => 'PowerDNS Operation Mode',\n\t\t\t'description' => 'Select the PoweDNS mode: Native for no replication (Default) / Master if DNS replication is needed.',\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Webserver customer-ssl certificates-directory',\n\t\t\t'description' => 'Where should customer-specified ssl-certificates be created?<br /><br /><div class=\"text-danger\">NOTE: This folder\\'s content gets deleted regularly so avoid storing data in there manually.</div>',\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => 'Allow administrators/resellers to report database-errors to froxlor',\n\t\t\t'description' => 'Please note: Never send any personal (customer-)data to us!',\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => 'Allow customers to report database-errors to froxlor',\n\t\t\t'description' => 'Please note: Never send any personal (customer-)data to us!',\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => 'Analyse mail traffic',\n\t\t\t'description' => 'Enable analysing of mailserver logs to calculate the traffic',\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'MDA type',\n\t\t\t'description' => 'Type of the Mail Delivery Server',\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'MDA log',\n\t\t\t'description' => 'Logfile of the Mail Delivery Server',\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'MTA type',\n\t\t\t'description' => 'Type of the Mail Transfer Agent',\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'MTA log',\n\t\t\t'description' => 'Logfile of the Mail Transfer Agent',\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'Cron configuration file',\n\t\t\t'description' => 'Path to the cron-service configuration-file. This file will be updated regularly and automatically by froxlor.<br />Note: Please <b>be sure</b> to use the same filename as for the main froxlor cronjob (default: /etc/cron.d/froxlor)!<br><br>If you are using <b>FreeBSD</b>, please specify <i>/etc/crontab</i> here!',\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Cron-daemon reload command',\n\t\t\t'description' => 'Specify the command to execute in order to reload your systems cron-daemon',\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Cron execution command (php-binary)',\n\t\t\t'description' => 'Command to execute our cronjobs. Change this only if you know what you are doing (default: \"/usr/bin/nice -n 5 /usr/bin/php -q\")!',\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => 'Allow automatic database updates',\n\t\t\t'description' => '<div class=\"text-danger\"><b>ATTENTION:</b></div> This settings allows the cronjob to bypass the version-check of froxlors files and database and runs the database-updates in case a version-mismatch occurs.<br><br><div class=\"text-danger\">Auto-update will always set default values for new settings or changes. This might not always suite your system. Please think twice before activating this option</div>',\n\t\t],\n\t\t'dns_createhostnameentry' => 'Create bind-zone/config for system hostname',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => 'Lowercase character',\n\t\t\t'description' => 'Password must contain at least one lowercase letter (a-z).',\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => 'Uppercase character',\n\t\t\t'description' => 'Password must contain at least one  uppercase letter (A-Z).',\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => 'Numbers',\n\t\t\t'description' => 'Password must contain at least one number (0-9).',\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => 'Special character',\n\t\t\t'description' => 'Password must contain at least one of the characters defined below.',\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => 'Special characters list',\n\t\t\t'description' => 'One of these characters is required if the above option is set.',\n\t\t],\n\t\t'apache_itksupport' => [\n\t\t\t'title' => 'Use modifications for Apache ITK-MPM',\n\t\t\t'description' => '<strong class=\"text-danger\">ATTENTION:</strong> use only if you actually have apache itk-mpm enabled<br />otherwise your webserver will not be able to start',\n\t\t],\n\t\t'letsencryptca' => [\n\t\t\t'title' => 'ACME environment',\n\t\t\t'description' => 'Environment to be used for Let\\'s Encrypt / ZeroSSL certificates.',\n\t\t],\n\t\t'letsencryptchallengepath' => [\n\t\t\t'title' => 'Path for Let\\'s Encrypt challenges',\n\t\t\t'description' => 'Directory where the Let\\'s Encrypt challenges should be offered from via a global alias.',\n\t\t],\n\t\t'letsencryptkeysize' => [\n\t\t\t'title' => 'Key size for new Let\\'s Encrypt certificates',\n\t\t\t'description' => 'Size of the key in Bits for new Let\\'s Encrypt certificates.',\n\t\t],\n\t\t'letsencryptreuseold' => [\n\t\t\t'title' => 'Re-use Let\\'s Encrypt key',\n\t\t\t'description' => 'If activated, the same key will be used for every renew, otherwise a new key will be generated every time.',\n\t\t],\n\t\t'leenabled' => [\n\t\t\t'title' => 'Enable Let\\'s Encrypt',\n\t\t\t'description' => 'If activated, customers are able to let froxlor automatically generate and renew Let\\'s Encrypt ssl-certificates for domains with a ssl IP/port.<br /><br />Please remember that you need to go through the webserver-configuration when enabled because this feature needs a special configuration.',\n\t\t],\n\t\t'caa_entry' => [\n\t\t\t'title' => 'Generate CAA DNS records',\n\t\t\t'description' => 'Automatically generates CAA records for SSL-enabled domains that are using Let\\'s Encrypt',\n\t\t],\n\t\t'caa_entry_custom' => [\n\t\t\t'title' => 'Additional CAA DNS records',\n\t\t\t'description' => 'DNS Certification Authority Authorization (CAA) is an Internet security policy mechanism which allows domain name holders to indicate to certificate authorities<br>whether they are authorized to issue digital certificates for a particular domain name. It does this by means of a new \"CAA\" Domain Name System (DNS) resource record.<br><br>The content of this field will be included into the DNS zone directly (each line results in a CAA record).<br>If Let\\'s Encrypt is enabled for this domain, this entry will always be added automatically and does not need to be added manually:<br><code>0 issue \"letsencrypt.org\"</code> (If domain is a wildcard domain, issuewild will be used instead).<br>To enable Incident Reporting, you can add an <code>iodef</code> record. An example for sending such report to <code>me@example.com</code> would be:<br><code>0 iodef \"mailto:me@example.com\"</code><br><br><strong>Attention:</strong> The code won\\'t be checked for any errors. If it contains errors, your CAA records might not work!',\n\t\t],\n\t\t'exportenabled' => [\n\t\t\t'title' => 'Enable data export for customers',\n\t\t\t'description' => 'If activated, the customer will be able to schedule data export jobs (cron-export) which generates an archive within his docroot (subdirectory chosable by customer)',\n\t\t],\n\t\t'dnseditorenable' => [\n\t\t\t'title' => 'Enable DNS editor',\n\t\t\t'description' => 'Allows admins and customer to manage domain dns entries',\n\t\t],\n\t\t'dns_server' => [\n\t\t\t'title' => 'DNS server daemon',\n\t\t\t'description' => 'Remember that daemons have to be configured using froxlors configuration templates',\n\t\t],\n\t\t'panel_customer_hide_options' => [\n\t\t\t'title' => 'Hide menu items and traffic charts in customer panel',\n\t\t\t'description' => 'Select items to hide in customer panel. To select multiple options, hold down CTRL while selecting.',\n\t\t],\n\t\t'allow_allow_customer_shell' => [\n\t\t\t'title' => 'Allow customers to enable shell access for ftp-users',\n\t\t\t'description' => '<strong class=\"text-danger\">Please note: Shell access allows the user to execute various binaries on your system. Use with extrem caution. Please only activate this if you REALLY know what you are doing!!!</strong>',\n\t\t],\n\t\t'available_shells' => [\n\t\t\t'title' => 'List of available shells',\n\t\t\t'description' => 'Comma separated list of shells that are available for the customer to chose from for their ftp-users.<br><br>Note that the default shell <strong>/bin/false</strong> will always be a choice (if enabled), even if this setting is empty. It is the default value for ftp-users in any case.',\n\t\t],\n\t\t'le_froxlor_enabled' => [\n\t\t\t'title' => 'Enable Let\\'s Encrypt for the froxlor vhost',\n\t\t\t'description' => 'If activated, the froxlor vhost will automatically be secured using a Let\\'s Encrypt certificate.',\n\t\t],\n\t\t'le_froxlor_redirect' => [\n\t\t\t'title' => 'Enable SSL-redirect for the froxlor vhost',\n\t\t\t'description' => 'If activated, all http requests to your froxlor will be redirected to the corresponding SSL site.',\n\t\t],\n\t\t'option_unavailable_websrv' => '<br><em class=\"text-danger\">Available only for: %s</em>',\n\t\t'option_unavailable' => '<br><em class=\"text-danger\">Option not available due to other settings.</em>',\n\t\t'letsencryptacmeconf' => [\n\t\t\t'title' => 'Path to the acme.conf snippet',\n\t\t\t'description' => 'File name of the config snippet which allows the web server to serve the acme challenge.',\n\t\t],\n\t\t'mail_use_smtp' => 'Set mailer to use SMTP',\n\t\t'mail_smtp_host' => 'Specify SMTP server',\n\t\t'mail_smtp_usetls' => 'Enable TLS encryption',\n\t\t'mail_smtp_auth' => 'Enable SMTP authentication',\n\t\t'mail_smtp_port' => 'TCP port to connect to',\n\t\t'mail_smtp_user' => 'SMTP username',\n\t\t'mail_smtp_passwd' => 'SMTP password',\n\t\t'http2_support' => [\n\t\t\t'title' => 'HTTP2 Support',\n\t\t\t'description' => 'enable HTTP2 support for ssl.<br><em class=\"text-danger\">ENABLE ONLY IF YOUR WEBSERVER SUPPORTS THIS FEATURE (nginx version 1.9.5+, apache2 version 2.4.17+)</em>',\n\t\t],\n\t\t'http3_support' => [\n\t\t\t'title' => 'HTTP3 Support',\n\t\t\t'description' => 'enable HTTP3 support for ssl.<br><em class=\"text-danger\">ENABLE ONLY IF YOUR WEBSERVER SUPPORTS THIS FEATURE (nginx version 1.25.0+)</em>',\n\t\t],\n\t\t'nssextrausers' => [\n\t\t\t'title' => 'Use libnss-extrausers instead of libnss-mysql',\n\t\t\t'description' => 'Do not read users from the database but from files. Please only activate if you have already gone through the required configuration steps (system -> libnss-extrausers).<br><strong class=\"text-danger\">For Debian/Ubuntu only (or if you have compiled libnss-extrausers yourself!)</strong>',\n\t\t],\n\t\t'le_domain_dnscheck' => [\n\t\t\t'title' => 'Validate DNS of domains when using Let\\'s Encrypt',\n\t\t\t'description' => 'If activated, froxlor will validate whether the domain which requests a Let\\'s Encrypt certificate resolves to at least one of the system ip addresses.',\n\t\t],\n\t\t'le_domain_dnscheck_resolver' => [\n\t\t\t'title' => 'Use a external nameserver for DNS validation',\n\t\t\t'description' => 'If set, froxlor will use this DNS to validate the DNS of domains when using Let\\'s Encrypt. If empty, the system\\'s default DNS resolver will be used.',\n\t\t],\n\t\t'phpsettingsforsubdomains' => [\n\t\t\t'description' => 'If yes the chosen php-config will be updated to all subdomains',\n\t\t],\n\t\t'leapiversion' => [\n\t\t\t'title' => 'Choose Let\\'s Encrypt ACME implementation',\n\t\t\t'description' => 'Currently only ACME v2 implementation for Let\\'s Encrypt is supported.',\n\t\t],\n\t\t'enable_api' => [\n\t\t\t'title' => 'Enable external API usage',\n\t\t\t'description' => 'In order to use the froxlor API you need to activate this option. For more detailed information see <a href=\"https://docs.froxlor.org/latest/api-guide/\" target=\"_new\">https://docs.froxlor.org/</a>',\n\t\t],\n\t\t'api_customer_default' => '\"Allow API access\" default value for new customers',\n\t\t'dhparams_file' => [\n\t\t\t'title' => 'DHParams file (Diffie–Hellman key exchange)',\n\t\t\t'description' => 'If a dhparams.pem file is specified here it will be included in the webserver configuration. Leave empty to disable.<br>Example: /etc/ssl/webserver/dhparams.pem<br><br>If the file does not exist, it will be created automatically with the following command: <code>openssl dhparam -out /etc/ssl/webserver/dhparams.pem 4096</code>. It is recommended to create the file prior to specifying it here as the creation takes quite a while and blocks the cronjob.',\n\t\t],\n\t\t'errorlog_level' => [\n\t\t\t'title' => 'Error log-level',\n\t\t\t'description' => 'Specify the error log level. Default is \"warn\" for apache-users and \"error\" for nginx-users.',\n\t\t],\n\t\t'letsencryptecc' => [\n\t\t\t'title' => 'Issue ECC / ECDSA certificate',\n\t\t\t'description' => 'If set to a valid key-size the certificate issued will use ECC / ECDSA',\n\t\t],\n\t\t'froxloraliases' => [\n\t\t\t'title' => 'Domain aliases for froxlor vhost',\n\t\t\t'description' => 'Comma separated list of domains to add as server alias to the froxlor vhost',\n\t\t],\n\t\t'default_sslvhostconf' => [\n\t\t\t'title' => 'Default SSL vHost-settings',\n\t\t\t'description' => 'The content of this field will be included into this ip/port vHost container directly. You can use the following variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (if applicable)<br/> Attention: The code won\\'t be checked for any errors. If it contains errors, webserver might not start again!',\n\t\t],\n\t\t'includedefault_sslvhostconf' => 'Include non-SSL vHost-settings in SSL-vHost',\n\t\t'apply_specialsettings_default' => 'Default value for \"Apply specialsettings to all subdomains (*.example.com)\" setting when editing a domain',\n\t\t'apply_phpconfigs_default' => 'Default value for \"Apply php-config to all subdomains\" setting when editing a domain',\n\t\t'awstats' => [\n\t\t\t'logformat' => [\n\t\t\t\t'title' => 'LogFormat setting',\n\t\t\t\t'description' => 'If you use customized logformat for your webserver, you need change the awstats LogFormat too.<br/>Default is 1. For more information check documentation <a target=\"_blank\" href=\"https://awstats.sourceforge.io/docs/awstats_config.html#LogFormat\">here</a>.',\n\t\t\t],\n\t\t],\n\t\t'hide_incompatible_settings' => 'Hide incompatible settings',\n\t\t'soaemail' => 'Mail address to use in SOA records (defaults to sender address from panel settings if empty)',\n\t\t'imprint_url' => [\n\t\t\t'title' => 'URL to legal notes / imprint',\n\t\t\t'description' => 'Specify an URL to your legal notes / imprint site. The link will be visible on the login screen and on the footer when logged in.',\n\t\t],\n\t\t'terms_url' => [\n\t\t\t'title' => 'URL to terms of use',\n\t\t\t'description' => 'Specify an URL to your terms of use site. The link will be visible on the login screen and on the footer when logged in.',\n\t\t],\n\t\t'privacy_url' => [\n\t\t\t'title' => 'URL to privacy policy',\n\t\t\t'description' => 'Specify an URL to your privacy policy site / imprint site. The link will be visible on the login screen and on the footer when logged in.',\n\t\t],\n\t\t'logo_image_header' => [\n\t\t\t'title' => 'Logo Image (Header)',\n\t\t\t'description' => 'Upload your own logo image to be shown in the header after login (recommended height 30px)',\n\t\t],\n\t\t'logo_image_login' => [\n\t\t\t'title' => 'Logo Image (Login)',\n\t\t\t'description' => 'Upload your own logo image to be shown during login',\n\t\t],\n\t\t'logo_overridetheme' => [\n\t\t\t'title' => 'Overwrites logo defined in theme by \"Logo Image\" (Header and Login, see below)',\n\t\t\t'description' => 'This needs to be set to \"true\" if you intend to use your uploaded logo; alternatively you can still use the theme-based \"logo_custom.png\" and \"logo_custom_login.png\" possibility.',\n\t\t],\n\t\t'logo_overridecustom' => [\n\t\t\t'title' => 'Overwrite custom logo (logo_custom.png and logo_custom_login.png) defined in theme by \"Logo Image\" (Header and Login, see below)',\n\t\t\t'description' => 'Set this to \"true\" if you want to ignore theme-specific custom logos for header and login and use \"Logo Image\"',\n\t\t],\n\t\t'createstdsubdom_default' => [\n\t\t\t'title' => 'Preselected value for \"Create standard subdomain\" when creating a customer',\n\t\t\t'description' => '',\n\t\t],\n\t\t'froxlorusergroup' => [\n\t\t\t'title' => 'Custom system group for all customer users',\n\t\t\t'description' => 'Usage of libnss-extrausers (system-settings) is required for this to take effect. An empty value skips creation or removes existing group.',\n\t\t],\n\t\t'acmeshpath' => [\n\t\t\t'title' => 'Path to acme.sh',\n\t\t\t'description' => 'Set this to where acme.sh is installed to, including the acme.sh script<br>Default is <b>/root/.acme.sh/acme.sh</b>',\n\t\t],\n\t\t'update_channel' => [\n\t\t\t'title' => 'froxlor update-channel',\n\t\t\t'description' => 'Select the update channel of froxlor. Default is \"stable\"',\n\t\t],\n\t\t'uc_stable' => 'stable',\n\t\t'uc_testing' => 'testing',\n\t\t'uc_nightly' => 'nightly',\n\t\t'traffictool' => [\n\t\t\t'toolselect' => 'Traffic analyzer',\n\t\t\t'webalizer' => 'Webalizer',\n\t\t\t'awstats' => 'AWStats',\n\t\t\t'goaccess' => 'goaccess'\n\t\t],\n\t\t'requires_reconfiguration' => 'Changing this settings might require a reconfiguration of the following services:<br><strong>%s</strong>',\n\t\t'req_limit_per_interval' => [\n\t\t\t'title' => 'Number of HTTP requests per interval',\n\t\t\t'description' => 'Limit the number of HTTP requests per interval (see below) to froxlor, default is \"60\"',\n\t\t],\n\t\t'req_limit_interval' => [\n\t\t\t'title' => 'Rate-limit interval',\n\t\t\t'description' => 'Specify the time in seconds for the number of HTTP requests, default is \"60\"',\n\t\t],\n\t\t'option_requires_otp' => 'This setting requires an OTP validation',\n\t\t'panel_menu_collapsed' => [\n\t\t\t'title' => 'Collapse menu-sections',\n\t\t\t'description' => 'If deactivated, the left-side menu sections will always be expanded.',\n\t\t],\n\t\t'le_renew_services' => [\n\t\t\t'title' => 'Use froxlor Let\\'s Encrypt certificate for these services',\n\t\t\t'description' => 'If set to none (or the renew-hook command below is empty), no configuration adjustments regarding ssl will be made to the selected services.<br><br>The reload-command for the services selected should be added in the renew-hook command or the configuration changes or renewed certificates may not be applied correctly.',\n\t\t],\n\t\t'le_renew_hook' => [\n\t\t\t'title' => 'Let\\'s Encrypt renew-hook command',\n\t\t\t'description' => 'Set this to a command that restarts the services selected above in order for renewed certificates to be used correctly by the service.',\n\t\t],\n\t],\n\t'spf' => [\n\t\t'use_spf' => [\n\t\t\t'title' => 'Activate SPF for domains?',\n\t\t\t'description' => 'Requires a specific dns entry for the domain. If you are not using the nameserver feature, you will have to manually manage these entries.',\n\t\t],\n\t\t'spf_entry' => 'SPF entry for all domains',\n\t],\n\t'dmarc' => [\n\t\t'use_dmarc' => [\n\t\t\t'title' => 'Activate DMARC for domains?',\n\t\t\t'description' => 'Requires a specific dns entry for the domain. If you are not using the nameserver feature, you will have to manually manage these entries.',\n\t\t],\n\t\t'dmarc_entry' => 'DMARC entry for all domains',\n\t],\n\t'ssl_certificates' => [\n\t\t'certificate_for' => 'Certificate for',\n\t\t'valid_from' => 'Valid from',\n\t\t'valid_until' => 'Valid until',\n\t\t'issuer' => 'Issuer',\n\t],\n\t'success' => [\n\t\t'success' => 'Information',\n\t\t'clickheretocontinue' => 'Click here to continue',\n\t\t'settingssaved' => 'The settings have been successfully saved.',\n\t\t'rebuildingconfigs' => 'Successfully inserted tasks for rebuild configfiles',\n\t\t'domain_import_successfully' => 'Successfully imported %s domains.',\n\t\t'exportscheduled' => 'Your export job has been scheduled. Please wait for it to be processed',\n\t\t'exportaborted' => 'Your scheduled export has been cancelled',\n\t\t'dns_record_added' => 'Record added successfully',\n\t\t'dns_record_deleted' => 'Record deleted successfully',\n\t\t'testmailsent' => 'Test mail sent successfully',\n\t\t'settingsimported' => 'Settings imported successfully',\n\t\t'sent_error_report' => 'Succesfully sent error report. Thank you for your contribution.',\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Pending cron-tasks',\n\t\t'REBUILD_VHOST' => 'Rebuilding webserver-configuration',\n\t\t'CREATE_HOME' => 'Adding new customer %s',\n\t\t'REBUILD_DNS' => 'Rebuilding bind-configuration',\n\t\t'CREATE_FTP' => 'Creating directory for new ftp-user',\n\t\t'DELETE_CUSTOMER_FILES' => 'Deleting customer-files %s',\n\t\t'noneoutstanding' => 'There are currently no outstanding tasks for froxlor',\n\t\t'DELETE_EMAIL_DATA' => 'Delete customer e-mail data.',\n\t\t'DELETE_FTP_DATA' => 'Delete customer ftp-account data.',\n\t\t'REBUILD_RSPAMD' => 'Rebuilding antispam-configuration.',\n\t\t'CREATE_QUOTA' => 'Set quota on filesystem',\n\t\t'REBUILD_CRON' => 'Rebuilding the cron.d-file',\n\t\t'CREATE_CUSTOMER_DATADUMP' => 'Data export job for customer %s',\n\t\t'DELETE_DOMAIN_PDNS' => 'Delete domain %s from PowerDNS database',\n\t\t'DELETE_DOMAIN_SSL' => 'Delete ssl files of domain %s',\n\t\t'UPDATE_LE_SERVICES' => 'Updating system services for Let\\'s Encrypt',\n\t\t'REBUILD_NSSUSERS' => 'Rebuilding libnss-extrausers files',\n\t],\n\t'terms' => 'Terms of use',\n\t'traffic' => [\n\t\t'month' => 'Month',\n\t\t'day' => 'Day',\n\t\t'months' => [\n\t\t\t1 => 'January',\n\t\t\t2 => 'February',\n\t\t\t3 => 'March',\n\t\t\t4 => 'April',\n\t\t\t5 => 'May',\n\t\t\t6 => 'June',\n\t\t\t7 => 'July',\n\t\t\t8 => 'August',\n\t\t\t9 => 'September',\n\t\t\t10 => 'October',\n\t\t\t11 => 'November',\n\t\t\t12 => 'December',\n\t\t\t'jan' => 'Jan',\n\t\t\t'feb' => 'Feb',\n\t\t\t'mar' => 'Mar',\n\t\t\t'apr' => 'Apr',\n\t\t\t'may' => 'May',\n\t\t\t'jun' => 'Jun',\n\t\t\t'jul' => 'Jul',\n\t\t\t'aug' => 'Aug',\n\t\t\t'sep' => 'Sep',\n\t\t\t'oct' => 'Oct',\n\t\t\t'nov' => 'Nov',\n\t\t\t'dec' => 'Dec',\n\t\t\t'total' => 'Total',\n\t\t],\n\t\t'mb' => 'Traffic',\n\t\t'sumtotal' => 'Total traffic',\n\t\t'sumhttp' => 'HTTP traffic',\n\t\t'sumftp' => 'FTP traffic',\n\t\t'summail' => 'Mail traffic',\n\t\t'customer' => 'Customer',\n\t\t'domain' => 'Domain',\n\t\t'trafficoverview' => 'Traffic summary',\n\t\t'bycustomers' => 'Traffic by customers',\n\t\t'details' => 'Details',\n\t\t'http' => 'HTTP',\n\t\t'ftp' => 'FTP',\n\t\t'mail' => 'Mail',\n\t\t'nocustomers' => 'You need at least one customer to view the traffic reports.',\n\t\t'top5customers' => 'Top 5 customers',\n\t\t'nodata' => 'No data for given range found.',\n\t\t'ranges' => [\n\t\t\t'last24h' => 'last 24 hours',\n\t\t\t'last7d' => 'last 7 days',\n\t\t\t'last30d' => 'last 30 days',\n\t\t\t'cm' => 'Current month',\n\t\t\t'last3m' => 'last 3 months',\n\t\t\t'last6m' => 'last 6 months',\n\t\t\t'last12m' => 'last 12 months',\n\t\t\t'cy' => 'Current year',\n\t\t],\n\t\t'byrange' => 'Specified by range',\n\t],\n\t'translator' => '',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'A newer version of froxlor has been installed but not yet set up.<br />Only the administrator can log in and finish the update.',\n\t\t'update' => 'froxlor update',\n\t\t'proceed' => 'Proceed',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'The froxlor files have been updated to version <strong>%s</strong>. The installed version is <strong>%s</strong>.',\n\t\t\t'part_b' => '<br /><br />Customers will not be able to log in until the update has been finished.<br /><strong>Proceed?</strong>',\n\t\t],\n\t\t'noupdatesavail' => 'You already have the latest %sversion of froxlor installed.',\n\t\t'description' => 'Running database updates for your froxlor installation',\n\t\t'uc_newinfo' => 'There is a newer %sversion available: \"%s\" (Your current version is: %s)',\n\t\t'notify_subject' => 'New update available',\n\t\t'dbupdate_required' => 'froxlor files have been updated, database update required',\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => 'Custom notes',\n\t\t\t'description' => 'Feel free to put any notes you want/need in here. They will show up in the admin/customer overview for the corresponding user.<br>Markdown is supported, HTML will be removed.',\n\t\t\t'show' => 'Show your notes on the dashboard of the user',\n\t\t],\n\t\t'api_allowed' => [\n\t\t\t'title' => 'Allow API access',\n\t\t\t'description' => 'When enabled in the settings, this user can create API keys and access the froxlor API',\n\t\t\t'notice' => 'API access is not allowed for your account.',\n\t\t],\n\t\t'shell_allowed' => [\n\t\t\t'title' => 'Allow shell access',\n\t\t\t'description' => 'When enabled in the settings, this user can assign shell access to ftp users',\n\t\t],\n\t\t'gui_access' => [\n\t\t\t'title' => 'Allow WebUI login',\n\t\t\t'description' => 'When disabled, the user cannot log in to the froxlor web-ui but all the services (web, ftp, mail, databases, api-access, etc.) will work normally.',\n\t\t],\n\t],\n\t'install' => [\n\t\t'slogan' => 'froxlor Server Management Panel',\n\t\t'preflight' => 'System check',\n\t\t'critical_error' => 'Critical error',\n\t\t'suggestions' => 'Not required but recommended',\n\t\t'phpinfosuccess' => 'Your system is running with PHP %s',\n\t\t'suggestionsnote' => 'There are no critical errors that prevent installation, but please follow the recommendations below for an optimal experience.',\n\t\t'phpinfowarn' => 'Your system is running a lower version than PHP %s',\n\t\t'phpinfoupdate' => 'Update your current PHP version from %s to %s or higher',\n\t\t'start_installation' => 'Start installation',\n\t\t'check_again' => 'Reload to check again',\n\t\t'switchmode_advanced' => 'Show advanced options',\n\t\t'switchmode_basic' => 'Hide advanced options',\n\t\t'dependency_check' => [\n\t\t\t'title' => 'Welcome to froxlor',\n\t\t\t'description' => 'We check the system for dependencies to ensure that all required php extensions and modules are enabled so that froxlor runs properly.',\n\t\t],\n\t\t'database' => [\n\t\t\t'top' => 'Database',\n\t\t\t'title' => 'Create database and user',\n\t\t\t'description' => 'froxlor requires a database and additionally <a href=\"https://docs.froxlor.org/latest/general/installation/tarball.html#_3-create-privileged-database-user\" target=\"_blank\">a privileged user</a> to be able to create users and databases (GRANT option). The given database and unprivileged database-user will be created in this process. The privileged user must exist.',\n\t\t\t'user' => 'Unprivileged database user',\n\t\t\t'dbname' => 'Database name',\n\t\t\t'force_create' => 'Backup and overwrite database if exists?',\n\t\t],\n\t\t'admin' => [\n\t\t\t'top' => 'Admin user',\n\t\t\t'title' => 'Let\\'s create the main administrator user.',\n\t\t\t'description' => 'This user will be granted all privileges to adjust settings and add/update/delete resources like customers, domains, etc.',\n\t\t\t'use_admin_email_as_sender' => 'Use the email address above as sender address. If unchecked, please specify a sender address below.',\n\t\t\t'use_autogenerated_email_as_sender' => 'Leave empty for default: admin@servername',\n\t\t],\n\t\t'system' => [\n\t\t\t'top' => 'System setup',\n\t\t\t'title' => 'Details about your server',\n\t\t\t'description' => 'Set your environment as well as server relevant data and options here to let froxlor know about your system. These values are crucial for the system configuration and operating.',\n\t\t\t'ipv4' => 'Primary IPv4 address (if applicable)',\n\t\t\t'ipv6' => 'Primary IPv6 address (if applicable)',\n\t\t\t'servername' => 'Server name (FQDN, no ip-address)',\n\t\t\t'phpbackend' => 'PHP backend',\n\t\t\t'activate_newsfeed' => 'Enable the official newsfeed<br><small>(external source: https://inside.froxlor.org/news/)</small>',\n\t\t],\n\t\t'install' => [\n\t\t\t'top' => 'Finish setup',\n\t\t\t'title' => 'One last step...',\n\t\t\t'description' => 'The command below will download, install and configure required services on your system according to the data you have given in this installation process.<br><br><span class=\"text-danger\">Be sure to run the following command as <b>root</b> on the server\\'s shell/terminal and <b>be aware</b> that this command will <b>overwrite</b> any existing configuration for the used services (backups will be created)!.<br>If you do not want to overwrite any configurations, select <i>I will manually configure the services</i> at the bottom of this page!</span>',\n\t\t\t'runcmd' => 'Run the following command to finish the installation:',\n\t\t\t'manual_config' => 'I will manually configure the services, just take me to the login',\n\t\t\t'waitforconfig' => 'Waiting for services to be configured...',\n\t\t],\n\t\t'errors' => [\n\t\t\t'wrong_ownership' => 'Make sure the froxlor files are owned by %s:%s',\n\t\t\t'missing_extensions' => 'The following php extensions are required and not installed',\n\t\t\t'suggestedextensions' => 'The following php extensions could not be found but are recommended',\n\t\t\t'databaseexists' => 'Database already exist, please set override option to rebuild or chose another name',\n\t\t\t'unabletocreatedb' => 'Test database could not be created',\n\t\t\t'unabletodropdb' => 'Test database could not be dropped',\n\t\t\t'mysqlusernameexists' => 'The user specified for unprivileged user already exist. Please use another username or delete it first.',\n\t\t\t'unabletocreateuser' => 'Test user could not be created',\n\t\t\t'unabletodropuser' => 'Test user could not be dropped',\n\t\t\t'unabletoflushprivs' => 'Given privileged user is unable to flush privileges',\n\t\t\t'nov4andnov6ip' => 'Either IPv4- or IPv6-address must be given',\n\t\t\t'servernameneedstobevalid' => 'Given servername does not seem to be a FQDN or hostname',\n\t\t\t'websrvuserdoesnotexist' => 'Given webserver-user does not seem to exist on the system',\n\t\t\t'websrvgrpdoesnotexist' => 'Given webserver-group does not seem to exist on the system',\n\t\t\t'notyetconfigured' => 'It seems that the services were not yet configured (successfully). Please either run the command shown below or check the box to do it later.',\n\t\t\t'mandatory_field_not_set' => 'Mandatory field \"%s\" is not set!',\n\t\t\t'unexpected_database_error' => 'Unexpected database exception occurred. %s',\n\t\t\t'sql_import_failed' => 'Failed to import SQL data!',\n\t\t\t'unprivileged_sql_connection_failed' => 'Failed to initialize unprivileged SQL connection!',\n\t\t\t'privileged_sql_connection_failed' => 'Failed to initialize privileged SQL connection!',\n\t\t\t'mysqldump_backup_failed' => 'Cannot create a database backup, we got an error from mysqldump.',\n\t\t\t'sql_backup_file_missing' => 'Cannot create a database backup, the backup file does not exist.',\n\t\t\t'backup_binary_missing' => 'Cannot create a database backup, make sure you installed mysqldump.',\n\t\t\t'creating_configfile_failed' => 'Cannot create config files, unable to write file.',\n\t\t\t'database_already_exiting' => 'We found a database and were not allow to override it!'\n\t\t]\n\t],\n\t'welcome' => [\n\t\t'title' => 'Welcome to froxlor!',\n\t\t'config_note' => 'In order for froxlor to be able to communicate properly with the backend, you have to configure it.',\n\t\t'config_now' => 'Configure now'\n\t],\n];\n"
  },
  {
    "path": "lng/es.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'languages' => [\n\t\t'cz' => 'Checo',\n\t\t'de' => 'Alemán',\n\t\t'en' => 'Inglés',\n\t\t'fr' => 'Francés',\n\t\t'it' => 'Italia',\n\t\t'nl' => 'Holandés',\n\t\t'pt' => 'Portugués',\n\t\t'se' => 'Sueco',\n\t\t'sk' => 'Eslovaco',\n\t\t'es' => 'Español',\n\t\t'zh_CN' => 'Chino (Simplificado)',\n\t],\n\t'2fa' => [\n\t\t'2fa' => 'Opciones 2FA',\n\t\t'2fa_enabled' => 'Activar la autenticación de dos factores (2FA)',\n\t\t'2fa_removed' => '2FA eliminado correctamente',\n\t\t'2fa_added' => '2FA activado correctamente<br /><a class=\"alert-link\" href=\"%s?page=2fa\">Ver detalles de 2FA</a>',\n\t\t'2fa_add' => 'Activar 2FA',\n\t\t'2fa_delete' => 'Desactivar 2FA',\n\t\t'2fa_verify' => 'Verificar código',\n\t\t'2fa_overview_desc' => 'Aquí puede activar una autenticación de dos factores para su cuenta.<br /><br />Puede utilizar una aplicación de autenticación (contraseña de un solo uso basada en el tiempo / TOTP) o dejar que froxlor le envíe un correo electrónico a la dirección de su cuenta después de cada inicio de sesión correcto con una contraseña de un solo uso.',\n\t\t'2fa_email_desc' => 'Su cuenta está configurada para utilizar contraseñas de un solo uso por correo electrónico. Para desactivarla, haga clic en \"Desactivar 2FA\".',\n\t\t'2fa_ga_desc' => 'Su cuenta está configurada para utilizar contraseñas de un solo uso basadas en el tiempo a través de una aplicación de autenticación. Escanee el código QR que aparece a continuación con la aplicación de autenticación que desee para generar los códigos. Para desactivar, haga clic en \"Desactivar 2FA\".'\n\t],\n\t'admin' => [\n\t\t'overview' => 'Visión general',\n\t\t'ressourcedetails' => 'Recursos usados',\n\t\t'systemdetails' => 'Detalles del sistema',\n\t\t'froxlordetails' => 'Detalles de froxlor',\n\t\t'installedversion' => 'Versión instalada',\n\t\t'latestversion' => 'Última versión',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'Búsqueda mediante webservice',\n\t\t\t'error' => 'Error de lectura'\n\t\t],\n\t\t'resources' => 'Recursos',\n\t\t'customer' => 'Cliente',\n\t\t'customers' => 'Clientes',\n\t\t'customers_list_desc' => 'Gestione sus clientes',\n\t\t'customer_add' => 'Crear cliente',\n\t\t'customer_edit' => 'Editar cliente',\n\t\t'username_default_msg' => 'Dejar vacío para el valor autogenerado',\n\t\t'domains' => 'Dominios',\n\t\t'domain_add' => 'Crear dominio',\n\t\t'domain_edit' => 'Editar dominio',\n\t\t'subdomainforemail' => 'Subdominios como dominios de correo electrónico',\n\t\t'admin' => 'Administrador',\n\t\t'admins' => 'Admins',\n\t\t'admin_add' => 'Crear administrador',\n\t\t'admin_edit' => 'Editar admin',\n\t\t'customers_see_all' => '¿Puede acceder a los recursos de otros administradores/revendedores?',\n\t\t'change_serversettings' => '¿Puede cambiar la configuración del servidor?',\n\t\t'server' => 'Sistema',\n\t\t'serversettings' => 'Ajustes',\n\t\t'serversettings_desc' => 'Administrar su sistema froxlor',\n\t\t'rebuildconf' => 'Reconstruir archivos de configuración',\n\t\t'stdsubdomain' => 'Subdominio estándar',\n\t\t'stdsubdomain_add' => 'Crear subdominio estándar',\n\t\t'phpenabled' => 'PHP habilitado',\n\t\t'deactivated' => 'Desactivado',\n\t\t'deactivated_user' => 'Desactivar usuario',\n\t\t'sendpassword' => 'Enviar contraseña',\n\t\t'ownvhostsettings' => 'Configuración vHost propia',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Configuración',\n\t\t\t'overview' => 'Visión general',\n\t\t\t'wizard' => 'Asistente',\n\t\t\t'distribution' => 'Distribución',\n\t\t\t'service' => 'Servicio',\n\t\t\t'daemon' => 'Daemon',\n\t\t\t'http' => 'Servidor web (HTTP)',\n\t\t\t'dns' => 'Servidor de nombres (DNS)',\n\t\t\t'mail' => 'Servidor de correo (IMAP/POP3)',\n\t\t\t'smtp' => 'Servidor de correo (SMTP)',\n\t\t\t'ftp' => 'Servidor FTP',\n\t\t\t'etc' => 'Otros (Sistema)',\n\t\t\t'choosedistribution' => '-- Elija una distribución --',\n\t\t\t'chooseservice' => '-- Elija un servicio --',\n\t\t\t'choosedaemon' => '-- Elija un demonio --',\n\t\t\t'statistics' => 'Estadísticas',\n\t\t\t'compactoverview' => 'Vista general compacta',\n\t\t\t'legend' => '<h3>Está a punto de configurar un servicio/demonio</h3>',\n\t\t\t'commands' => '<span class=\"text-danger\">Comandos:</span> Estos comandos deben ser ejecutados línea por línea como usuario root en un shell. Es seguro copiar todo el bloque y pegarlo en el shell.',\n\t\t\t'files' => '<span class=\"text-danger\">Archivos de configuración:</span> Los comandos antes de los campos de texto deben abrir un editor con el archivo de destino. Sólo tienes que copiar y pegar el contenido en el editor y guardar el archivo.<br /><span class=\"text-danger\">Nota:</span> La contraseña MySQL no ha sido reemplazada por razones de seguridad. Por favor, reemplaza \"FROXLOR_MYSQL_PASSWORD\" por tu cuenta o utiliza el formulario javascript de abajo para reemplazarla in situ. Si olvidaste tu contraseña MySQL la encontrarás en \"lib/userdata.inc.php\".',\n\t\t\t'importexport' => 'Importar/Exportar',\n\t\t\t'finishnote' => 'Archivo de parámetros generado correctamente. Ahora ejecute el siguiente comando como root:',\n\t\t\t'description' => 'Configurar los servicios del sistema',\n\t\t\t'minihowto' => 'En esta página puede ver las diferentes plantillas de configuración para cada servicio, (re)configurar servicios específicos si es necesario o exportar la selección actual a un archivo JSON para utilizarlo en los scripts CLI o en otro servidor.<br/><br/><b>Tenga en cuenta</b> que los servicios resaltados no reflejan su configuración actual, sino que muestran requisitos/recomendaciones de sus valores de configuración actuales.',\n\t\t\t'skipconfig' => 'No (re)configurar',\n\t\t\t'recommendednote' => 'Servicios recomendados/requeridos según la configuración actual del sistema',\n\t\t\t'selectrecommended' => 'Seleccionar recomendados',\n\t\t\t'downloadselected' => 'Exportar seleccionado'\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Plantillas de correo electrónico',\n\t\t\t'template_add' => 'Añadir plantilla',\n\t\t\t'template_fileadd' => 'Añadir plantilla de archivo',\n\t\t\t'template_edit' => 'Editar plantilla',\n\t\t\t'action' => 'Acción',\n\t\t\t'email' => 'Plantillas de correo electrónico y archivos',\n\t\t\t'subject' => 'Asunto',\n\t\t\t'mailbody' => 'Cuerpo del correo',\n\t\t\t'createcustomer' => 'Correo de bienvenida para nuevos clientes',\n\t\t\t'pop_success' => 'Correo de bienvenida para nuevas cuentas de correo electrónico',\n\t\t\t'template_replace_vars' => 'Variables a sustituir en la plantilla:',\n\t\t\t'SALUTATION' => 'Sustituido por un saludo correcto (nombre o empresa)',\n\t\t\t'FIRSTNAME' => 'Sustituido por el nombre del cliente.',\n\t\t\t'NAME' => 'Se sustituye por el nombre del cliente.',\n\t\t\t'COMPANY' => 'Se sustituye por el nombre de la empresa del cliente.',\n\t\t\t'USERNAME' => 'Se sustituye por el nombre de usuario de la cuenta del cliente.',\n\t\t\t'PASSWORD' => 'Se sustituye por la contraseña de la cuenta del cliente.',\n\t\t\t'EMAIL' => 'Se sustituye por la dirección de la cuenta POP3/IMAP.',\n\t\t\t'CUSTOMER_NO' => 'Sustituido por el número de cliente',\n\t\t\t'TRAFFIC' => 'Se sustituye por el tráfico asignado al cliente.',\n\t\t\t'TRAFFICUSED' => 'Sustituido por el tráfico, que fue agotado por el cliente.',\n\t\t\t'pop_success_alternative' => 'Correo de bienvenida para nuevas cuentas de correo electrónico enviado a la dirección alternativa',\n\t\t\t'EMAIL_PASSWORD' => 'Sustituido por la contraseña de la cuenta POP3/IMAP.',\n\t\t\t'index_html' => 'Archivo de índice para directorios de clientes recién creados',\n\t\t\t'SERVERNAME' => 'Sustituido por el nombre del servidor.',\n\t\t\t'CUSTOMER' => 'Sustituido por el nombre de usuario del cliente.',\n\t\t\t'ADMIN' => 'Se sustituye por el nombre de usuario del administrador.',\n\t\t\t'CUSTOMER_EMAIL' => 'Se sustituye por la dirección de correo electrónico del cliente.',\n\t\t\t'ADMIN_EMAIL' => 'Se sustituye por la dirección de correo electrónico del administrador.',\n\t\t\t'filetemplates' => 'Plantillas de archivos',\n\t\t\t'filecontent' => 'Contenido del fichero',\n\t\t\t'new_database_by_customer' => 'Notificación al cliente de la creación de una base de datos',\n\t\t\t'new_ftpaccount_by_customer' => 'Notificación al cliente de la creación de un usuario ftp',\n\t\t\t'newdatabase' => 'Correos de notificación para nuevas bases de datos',\n\t\t\t'newftpuser' => 'Correos de notificación para nuevos usuarios ftp',\n\t\t\t'CUST_NAME' => 'Nombre del cliente',\n\t\t\t'DB_NAME' => 'Nombre de la base de datos',\n\t\t\t'DB_PASS' => 'Contraseña de la base de datos',\n\t\t\t'DB_DESC' => 'Descripción de la base de datos',\n\t\t\t'DB_SRV' => 'Servidor de base de datos',\n\t\t\t'PMA_URI' => 'URL de phpMyAdmin (si se indica)',\n\t\t\t'USR_NAME' => 'Nombre de usuario FTP',\n\t\t\t'USR_PASS' => 'Contraseña FTP',\n\t\t\t'USR_PATH' => 'FTP homedir (relativo a customer-docroot)',\n\t\t\t'forgotpwd' => 'Correos de notificación de restablecimiento de contraseña',\n\t\t\t'password_reset' => 'Notificación al cliente de restablecimiento de contraseña',\n\t\t\t'trafficmaxpercent' => 'Correo de notificación para los clientes cuando se agota un determinado porcentaje máximo de tráfico',\n\t\t\t'MAX_PERCENT' => 'Sustituido por el límite de diskusage/tráfico para el envío de informes en porcentaje.',\n\t\t\t'USAGE_PERCENT' => 'Reemplazado con el diskusage/tráfico agotado por el cliente en porcentaje.',\n\t\t\t'diskmaxpercent' => 'Correo de notificación a los clientes cuando se agota el porcentaje máximo de espacio en disco.',\n\t\t\t'DISKAVAILABLE' => 'Sustituido por el uso de disco asignado al cliente.',\n\t\t\t'DISKUSED' => 'Reemplazado por el uso de disco, que fue agotado por el cliente.',\n\t\t\t'LINK' => 'Sustituido por el enlace de restablecimiento de contraseña del cliente.',\n\t\t\t'SERVER_HOSTNAME' => 'Reemplaza el system-hostname (URL a froxlor)',\n\t\t\t'SERVER_IP' => 'Reemplaza la dirección IP del servidor por defecto',\n\t\t\t'SERVER_PORT' => 'Reemplaza el puerto por defecto del servidor',\n\t\t\t'DOMAINNAME' => 'Reemplaza el subdominio estándar del cliente (puede estar vacío si no se genera ninguno)'\n\t\t],\n\t\t'webserver' => 'Servidor web',\n\t\t'createzonefile' => 'Crear zona dns para el dominio',\n\t\t'custombindzone' => 'Archivo de zona personalizado / no gestionado',\n\t\t'bindzonewarning' => 'vacío por defecto<br /><strong class=\"text-danger\">ATENCIÓN:</strong> Si utiliza un archivo de zonas, tendrá que gestionar también manualmente todos los registros necesarios para todas las subzonas.',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IPs y Puertos',\n\t\t\t'add' => 'Añadir IP/Puerto',\n\t\t\t'edit' => 'Editar IP/Puerto',\n\t\t\t'ipandport' => 'IP/Puerto',\n\t\t\t'ip' => 'IP',\n\t\t\t'ipnote' => '<div id=\"ipnote\" class=\"invalid-feedback\">Nota: Aunque las direcciones IP privadas están permitidas, algunas características como DNS podrían no comportarse correctamente.<br />Sólo use direcciones IP privadas si está seguro.</div>',\n\t\t\t'port' => 'Puerto',\n\t\t\t'create_listen_statement' => 'Crear sentencia Listen',\n\t\t\t'create_namevirtualhost_statement' => 'Crear sentencia NameVirtualHost',\n\t\t\t'create_vhostcontainer' => 'Crear vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Crear sentencia ServerName en vHost-Container',\n\t\t\t'enable_ssl' => '¿Se trata de un puerto SSL?',\n\t\t\t'ssl_cert_file' => 'Ruta al certificado SSL',\n\t\t\t'webserverdefaultconfig' => 'Configuración por defecto del servidor web',\n\t\t\t'webserverdomainconfig' => 'Configuración de dominio del servidor web',\n\t\t\t'webserverssldomainconfig' => 'Configuración SSL del servidor web',\n\t\t\t'ssl_key_file' => 'Ruta al archivo de claves SSL',\n\t\t\t'ssl_ca_file' => 'Ruta al certificado SSL CA',\n\t\t\t'default_vhostconf_domain' => 'Configuración vHost por defecto para cada contenedor de dominio',\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'Ruta al archivo SSL CertificateChainFile',\n\t\t\t\t'description' => 'Mayormente CA_Bundle, o similar, probablemente quieras configurar esto si has comprado un certificado SSL.'\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Docroot personalizado (vacío = apunta a froxlor)',\n\t\t\t\t'description' => 'Aquí puedes definir un document-root personalizado (el destino de una petición) para esta combinación ip/puerto.<br/><strong>ATENCIÓN:</strong> ¡Por favor, ten cuidado con lo que introduces aquí!'\n\t\t\t],\n\t\t\t'ssl_paste_description' => 'Pegue el contenido completo de su certificado en el cuadro de texto',\n\t\t\t'ssl_cert_file_content' => 'Contenido del certificado ssl',\n\t\t\t'ssl_key_file_content' => 'Contenido del archivo de clave ssl (privada)',\n\t\t\t'ssl_ca_file_content' => 'Contenido del archivo ssl CA (opcional)',\n\t\t\t'ssl_ca_file_content_desc' => '<br/><br/>Autenticación del cliente, configure esto sólo si sabe lo que es.',\n\t\t\t'ssl_cert_chainfile_content' => 'Contenido del archivo de cadena de certificados (opcional)',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br/><br/>Principalmente CA_Bundle, o similar, probablemente quiera establecer esto si compró un certificado SSL.',\n\t\t\t'ssl_default_vhostconf_domain' => 'Configuración SSL vHost por defecto para cada contenedor de dominio'\n\t\t],\n\t\t'memorylimitdisabled' => 'Desactivado',\n\t\t'valuemandatory' => 'Este valor es obligatorio',\n\t\t'valuemandatorycompany' => 'O bien \"nombre\" y \"nombre\" o \"empresa\" debe ser llenado',\n\t\t'serversoftware' => 'Serversoftware',\n\t\t'phpversion' => 'Versión PHP',\n\t\t'mysqlserverversion' => 'Versión del servidor MySQL',\n\t\t'webserverinterface' => 'Interfaz del servidor web',\n\t\t'accountsettings' => 'Configuración de la cuenta',\n\t\t'panelsettings' => 'Configuración del panel',\n\t\t'systemsettings' => 'Configuración del sistema',\n\t\t'webserversettings' => 'Configuración del servidor web',\n\t\t'mailserversettings' => 'Configuración del servidor de correo',\n\t\t'nameserversettings' => 'Configuración del servidor de nombres',\n\t\t'updatecounters' => 'Recalcular el uso de recursos',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Nunca',\n\t\t\t'choosableno' => 'Seleccionable, por defecto no',\n\t\t\t'choosableyes' => 'Seleccionable, por defecto sí',\n\t\t\t'always' => 'Siempre'\n\t\t],\n\t\t'wipecleartextmailpwd' => 'Borrar contraseñas en texto plano',\n\t\t'webalizersettings' => 'Configuración de Webalizer',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normal',\n\t\t\t'quiet' => 'Silencioso',\n\t\t\t'veryquiet' => 'Sin salida'\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Actualmente no es posible añadir un dominio. Primero debe añadir al menos un cliente.',\n\t\t'loggersettings' => 'Configuración del registro',\n\t\t'logger' => [\n\t\t\t'normal' => 'normal',\n\t\t\t'paranoid' => 'paranoico'\n\t\t],\n\t\t'emaildomain' => 'Emaildomain',\n\t\t'email_only' => '¿Sólo correo electrónico?',\n\t\t'wwwserveralias' => 'Añada un \"www.\" ServerAlias',\n\t\t'subject' => 'Asunto',\n\t\t'recipient' => 'Destinatario',\n\t\t'message' => 'Escribir un mensaje',\n\t\t'text' => 'Mensaje',\n\t\t'sslsettings' => 'Configuración SSL',\n\t\t'specialsettings_replacements' => 'Puede utilizar las siguientes variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (si procede)<br/>',\n\t\t'dkimsettings' => 'Configuración de DomainKey',\n\t\t'caneditphpsettings' => '¿Puede cambiar los ajustes de dominio relacionados con php?',\n\t\t'allips' => 'Todas las IP',\n\t\t'awstatssettings' => 'Configuración de AWstats',\n\t\t'domain_dns_settings' => 'Configuración dns del dominio',\n\t\t'activated' => 'Activado',\n\t\t'statisticsettings' => 'Configuración de estadísticas',\n\t\t'or' => 'o',\n\t\t'sysload' => 'Carga del sistema',\n\t\t'noloadavailable' => 'no disponible',\n\t\t'nouptimeavailable' => 'no disponible',\n\t\t'nosubject' => '(Sin asunto)',\n\t\t'security_settings' => 'Opciones de seguridad',\n\t\t'know_what_youre_doing' => 'Cambiar sólo, ¡si sabes lo que haces!',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Mostrar la versión de froxlor en el login',\n\t\t\t'description' => 'Mostrar la versión de froxlor en el pie de página en la página de inicio de sesión'\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Mostrar la versión de froxlor en el pie de página',\n\t\t\t'description' => 'Mostrar la versión de froxlor en el pie de página del resto de páginas'\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Gráfico de cabecera para froxlor',\n\t\t\t'description' => 'Qué gráfico debe mostrarse en la cabecera'\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'Configuración PHP',\n\t\t\t'description' => 'Breve descripción',\n\t\t\t'actions' => 'Acciones',\n\t\t\t'activedomains' => 'En uso para dominio(s)',\n\t\t\t'notused' => 'Configuración no en uso',\n\t\t\t'editsettings' => 'Cambiar configuración PHP',\n\t\t\t'addsettings' => 'Crear nueva configuración PHP',\n\t\t\t'viewsettings' => 'Ver configuración PHP',\n\t\t\t'phpinisettings' => 'Configuración php.ini',\n\t\t\t'addnew' => 'Crear nueva configuración PHP',\n\t\t\t'binary' => 'PHP Binario',\n\t\t\t'fpmdesc' => 'Configuración PHP-FPM',\n\t\t\t'file_extensions' => 'Extensiones de archivos',\n\t\t\t'file_extensions_note' => '(sin punto, separadas por espacios)',\n\t\t\t'enable_slowlog' => 'Activar slowlog (por dominio)',\n\t\t\t'request_terminate_timeout' => 'Solicitar terminate-timeout',\n\t\t\t'request_slowlog_timeout' => 'Solicitar slowlog-timeout',\n\t\t\t'activephpconfigs' => 'En uso para php-config(s)',\n\t\t\t'pass_authorizationheader' => 'Añade \"-pass-header Authorization\" / \"CGIPassAuth On\" a vhosts'\n\t\t],\n\t\t'misc' => 'Varios',\n\t\t'fpmsettings' => [\n\t\t\t'addnew' => 'Crear nueva versión PHP',\n\t\t\t'edit' => 'Cambiar versión PHP'\n\t\t],\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'Variables que serán reemplazadas en las configs',\n\t\t\t'pear_dir' => 'Será reemplazada con la configuración global para el directorio pear.',\n\t\t\t'open_basedir_c' => 'Se insertará un ; (punto y coma) para comentar/desactivar open_basedir cuando se establezca',\n\t\t\t'open_basedir' => 'Se sustituirá por la configuración open_basedir del dominio.',\n\t\t\t'tmp_dir' => 'Será reemplazado por el directorio temporal del dominio.',\n\t\t\t'open_basedir_global' => 'Se sustituirá por el valor global de la ruta que se adjuntará a open_basedir (ver configuración del servidor web).',\n\t\t\t'customer_email' => 'Se sustituirá por la dirección de correo electrónico del cliente propietario del dominio.',\n\t\t\t'admin_email' => 'Se sustituirá por la dirección de correo electrónico del administrador propietario del dominio.',\n\t\t\t'domain' => 'Se sustituirá por el dominio.',\n\t\t\t'customer' => 'Se sustituirá por el nombre de usuario del cliente propietario del dominio.',\n\t\t\t'admin' => 'Se sustituirá por el nombre de usuario del administrador propietario del dominio.',\n\t\t\t'docroot' => 'Se sustituirá por la raíz del documento del dominio.',\n\t\t\t'homedir' => 'Se sustituirá por el directorio raíz del cliente.'\n\t\t],\n\t\t'expert_settings' => 'Configuración experta',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'Procesos PHP para este dominio (vacío por defecto)'\n\t\t],\n\t\t'phpserversettings' => 'Configuración PHP',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Peticiones php máximas para este dominio (vacío por defecto)'\n\t\t],\n\t\t'spfsettings' => 'Configuración SPF del dominio',\n\t\t'specialsettingsforsubdomains' => 'Aplicar configuración especial a todos los subdominios (*.ejemplo.com)',\n\t\t'accountdata' => 'Datos de la cuenta',\n\t\t'contactdata' => 'Datos de contacto',\n\t\t'servicedata' => 'Datos de servicio',\n\t\t'newerversionavailable' => 'Hay una nueva versión de froxlor disponible.',\n\t\t'newerversiondetails' => '¿Actualice ahora a la versión <b>%s</b>?<br/>(Su versión actual es: %s)',\n\t\t'extractdownloadedzip' => '¿Extraer el archivo descargado \"%s\"?',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Configuración de Cronjob',\n\t\t\t'add' => 'Añadir cronjob'\n\t\t],\n\t\t'cronjob_edit' => 'Editar cronjob',\n\t\t'warning' => 'ADVERTENCIA - ¡Tenga en cuenta!',\n\t\t'lastlogin_succ' => 'Último acceso',\n\t\t'ftpserver' => 'Servidor FTP',\n\t\t'ftpserversettings' => 'Configuración del servidor FTP',\n\t\t'webserver_user' => 'Nombre de usuario del servidor web',\n\t\t'webserver_group' => 'Nombre de grupo del servidor web',\n\t\t'perlenabled' => 'Perl activado',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Usuario local a utilizar para FCGID (froxlor vHost)',\n\t\t'mod_fcgid_group' => 'Grupo local a utilizar para FCGID (froxlor vHost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[No indicado]',\n\t\t'store_defaultindex' => 'Almacenar el archivo de índice por defecto en el docroot del cliente',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => 'Tráfico',\n\t\t'traffic_sub' => 'Detalles sobre el uso del tráfico',\n\t\t'domaintraffic' => 'Dominios',\n\t\t'customertraffic' => 'Clientes',\n\t\t'assignedmax' => 'Asignado / Max',\n\t\t'usedmax' => 'Usado / Max',\n\t\t'used' => 'Usado',\n\t\t'speciallogwarning' => '<div id=\"speciallogfilenote\" class=\"invalid-feedback\">ADVERTENCIA: Al cambiar esta configuración perderá todas sus antiguas estadísticas para este dominio.</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => 'Archivo de registro separado',\n\t\t\t'description' => 'Active esta opción para obtener un archivo de registro de acceso independiente para este dominio.'\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => 'Permitir editar el dominio',\n\t\t\t'desc' => 'Si se establece en sí, el cliente puede cambiar varios ajustes del dominio.<br/>Si se establece en no, el cliente no puede cambiar nada.'\n\t\t],\n\t\t'writeaccesslog' => [\n\t\t\t'title' => 'Escribir un registro de acceso',\n\t\t\t'description' => 'Active esta opción para obtener un archivo de registro de acceso para este dominio.'\n\t\t],\n\t\t'writeerrorlog' => [\n\t\t\t'title' => 'Escribir un registro de errores',\n\t\t\t'description' => 'Active esta opción para obtener un archivo de registro de errores para este dominio.'\n\t\t],\n\t\t'phpfpm.ininote' => 'No todos los valores que quiera definir pueden ser usados en la configuración del pool php-fpm',\n\t\t'phpinfo' => 'PHPinfo()',\n\t\t'selectserveralias' => 'Valor ServerAlias para el dominio',\n\t\t'selectserveralias_desc' => 'Elija si froxlor debe crear una entrada comodín (*.dominio.tld), un alias WWW (www.domain.tld) o ningún alias.',\n\t\t'show_news_feed' => [\n\t\t\t'title' => 'Mostrar noticias en el panel de administración',\n\t\t\t'description' => 'Activa esta opción para mostrar las noticias oficiales de froxlor (https://inside.froxlor.org/news/) en tu panel de control y no perderte nunca información importante o anuncios de lanzamientos.'\n\t\t],\n\t\t'cronsettings' => 'Configuración de Cronjob',\n\t\t'integritycheck' => 'Validación de la base de datos',\n\t\t'integrityname' => 'Nombre',\n\t\t'integrityresult' => 'Resultado',\n\t\t'integrityfix' => 'Solución automática de problemas',\n\t\t'customer_show_news_feed' => 'Mostrar noticias en el panel del cliente',\n\t\t'customer_news_feed_url' => [\n\t\t\t'title' => 'Utilizar un canal RSS personalizado',\n\t\t\t'description' => 'Especifique un canal RSS personalizado que se mostrará a sus clientes en su panel de control.<br/><small>Deje este campo vacío para utilizar el canal de noticias oficial de froxlor (https://inside.froxlor.org/news/).</small>'\n\t\t],\n\t\t'movetoadmin' => 'Mover cliente',\n\t\t'movecustomertoadmin' => 'Mueve el cliente al administrador/revendedor seleccionado<br/><small>Deja esto vacío para no hacer cambios.<br/>Si el administrador deseado no aparece en la lista, su límite de clientes ha sido alcanzado.</small>',\n\t\t'note' => 'Nota',\n\t\t'mod_fcgid_umask' => [\n\t\t\t'title' => 'Máscara (por defecto: 022)'\n\t\t],\n\t\t'apcuinfo' => 'Información APCu',\n\t\t'opcacheinfo' => 'Información OPcache',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Usar Let\\'s Encrypt',\n\t\t\t'description' => 'Obtenga un certificado gratuito de <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. El certificado se creará y renovará automáticamente.<br /><strong class=\"text-danger\">ATENCIÓN:</strong> Si los comodines están activados, esta opción se desactivará automáticamente.'\n\t\t],\n\t\t'autoupdate' => 'Actualizar automáticamente',\n\t\t'server_php' => 'PHP',\n\t\t'dnsenabled' => 'Habilitar editor DNS',\n\t\t'froxlorvhost' => 'Configuración de froxlor VirtualHost',\n\t\t'hostname' => 'Nombre de host',\n\t\t'memory' => 'Uso de memoria',\n\t\t'webserversettings_ssl' => 'Configuración SSL del servidor web',\n\t\t'domain_hsts_maxage' => [\n\t\t\t'title' => 'Seguridad estricta de transporte HTTP (HSTS)',\n\t\t\t'description' => 'Especifique el valor max-age para la cabecera Strict-Transport-Security<br/>El valor <i>0</i> desactivará HSTS para el dominio. La mayoría de los usuarios establecen un valor de <i>31536000</i> (un año).'\n\t\t],\n\t\t'domain_hsts_incsub' => [\n\t\t\t'title' => 'Incluir HSTS para cualquier subdominio',\n\t\t\t'description' => 'La directiva opcional \"includeSubDomains\", si está presente, indica a la UA que la Política HSTS se aplica a este Host HSTS así como a cualquier subdominio del nombre de dominio del host.'\n\t\t],\n\t\t'domain_hsts_preload' => [\n\t\t\t'title' => 'Incluir dominio en la <a href=\"https://hstspreload.org/\" target=\"_blank\">lista de precarga</a> HSTS',\n\t\t\t'description' => 'Si desea que este dominio se incluya en la lista de precarga HSTS mantenida por Chrome (y utilizada por Firefox y Safari), active esta opción.<br />El envío de la directiva de precarga desde su sitio puede tener CONSECUENCIAS PERMANENTES e impedir que los usuarios accedan a su sitio y a cualquiera de sus subdominios.<br />Por favor, lea los detalles en <a href=\"https://hstspreload.org/#removal\" target=\"_blank\">https://hstspreload.org/#removal</a> antes de enviar la cabecera con \"preload\".'\n\t\t],\n\t\t'domain_ocsp_stapling' => [\n\t\t\t'title' => 'Engrapado OCSP',\n\t\t\t'description' => 'Consulte <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/OCSP_stapling\">Wikipedia</a> para una explicación detallada del grapado OCSP',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">ADVERTENCIA:</strong> Se requiere la versión 1.3.7 o superior de Nginx para el grapado OCSP. Si su versión es anterior, ¡el servidor web NO se iniciará correctamente mientras el grapado OCSP esté activado!'\n\t\t],\n\t\t'domain_http2' => [\n\t\t\t'title' => 'Soporte HTTP2',\n\t\t\t'description' => 'Consulte <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/2\">Wikipedia</a> para una explicación detallada de HTTP2'\n\t\t],\n\t\t'testmail' => 'Prueba SMTP',\n\t\t'phpsettingsforsubdomains' => 'Aplicar php-config a todos los subdominios:',\n\t\t'plans' => [\n\t\t\t'name' => 'Nombre del plan',\n\t\t\t'description' => 'Descripción',\n\t\t\t'last_update' => 'Última actualización',\n\t\t\t'plans' => 'Planes de alojamiento',\n\t\t\t'plan_details' => 'Detalles del plan',\n\t\t\t'add' => 'Añadir nuevo plan',\n\t\t\t'edit' => 'Editar plan',\n\t\t\t'use_plan' => 'Aplicar plan'\n\t\t],\n\t\t'notryfiles' => [\n\t\t\t'title' => 'No autogenerated try_files',\n\t\t\t'description' => 'Diga sí aquí si desea especificar una directiva try_files personalizada en specialsettings (necesaria para algunos plugins de wordpress, por ejemplo).'\n\t\t],\n\t\t'logviewenabled' => 'Habilitar acceso a access/error-logs',\n\t\t'novhostcontainer' => '<br /><br /><small class=\"text-danger\">Ninguna de las IPs y puertos tiene activada la opción \"Crear vHost-Container\", muchos ajustes aquí no estarán disponibles</small>',\n\t\t'ownsslvhostsettings' => 'Configuración propia de SSL vHost',\n\t\t'domain_override_tls' => 'Anular la configuración TLS del sistema',\n\t\t'domain_override_tls_addinfo' => '<br /><span class=\"text-danger\">Sólo se utiliza si la opción \"Override system TLS settings\" está configurada como \"Yes\".</span>',\n\t\t'domain_sslenabled' => 'Habilitar el uso de SSL',\n\t\t'domain_honorcipherorder' => 'Respetar el orden de cifrado (servidor), por defecto <strong>no</strong>',\n\t\t'domain_sessiontickets' => 'Habilitar TLS sessiontickets (RFC 5077), por defecto <strong>sí</strong>',\n\t\t'domain_sessionticketsenabled' => [\n\t\t\t'title' => 'Habilitar el uso de TLS sessiontickets globalmente',\n\t\t\t'description' => 'Por defecto <strong>sí</strong><br/>Requiere apache-2.4.11+ o nginx-1.5.9+'\n\t\t],\n\t\t'domaindefaultalias' => 'Valor por defecto de ServerAlias para nuevos dominios',\n\t\t'smtpsettings' => 'Configuración SMTP',\n\t\t'smtptestaddr' => 'Enviar correo de prueba a',\n\t\t'smtptestnote' => 'Tenga en cuenta que los valores siguientes reflejan su configuración actual y sólo pueden ajustarse allí (véase el enlace en la esquina superior derecha)',\n\t\t'smtptestsend' => 'Enviar correo de prueba',\n\t\t'mysqlserver' => [\n\t\t\t'mysqlserver' => 'Servidor MySQL',\n\t\t\t'dbserver' => 'Servidor',\n\t\t\t'caption' => 'Descripción',\n\t\t\t'host' => 'Nombre de host / IP',\n\t\t\t'port' => 'Puerto',\n\t\t\t'user' => 'Usuario privilegiado',\n\t\t\t'add' => 'Añadir nuevo servidor MySQL',\n\t\t\t'edit' => 'Editar servidor MySQL',\n\t\t\t'password' => 'Contraseña de usuario privilegiado',\n\t\t\t'password_emptynochange' => 'Nueva contraseña, dejar vacío para ningún cambio',\n\t\t\t'allowall' => [\n\t\t\t\t'title' => 'Permitir el uso de este servidor a todos los clientes existentes actualmente',\n\t\t\t\t'description' => 'Establezca esta opción en \"true\" si desea permitir el uso de este servidor de base de datos a todos los clientes existentes para que puedan añadir bases de datos en él. Esta configuración no es permanente, pero se puede ejecutar varias veces.'\n\t\t\t],\n\t\t\t'testconn' => 'Probar la conexión al guardar',\n\t\t\t'ssl' => 'Utilizar SSL para la conexión al servidor de base de datos',\n\t\t\t'ssl_cert_file' => 'La ruta del archivo a la autoridad del certificado SSL',\n\t\t\t'verify_ca' => 'Habilitar la verificación del certificado SSL del servidor'\n\t\t],\n\t\t'settings_importfile' => 'Elegir archivo de importación',\n\t\t'documentation' => 'Documentación',\n\t\t'adminguide' => 'Guía del administrador',\n\t\t'userguide' => 'Guía del usuario',\n\t\t'apiguide' => 'Guía de la API'\n\t],\n\t'apcuinfo' => [\n\t\t'clearcache' => 'Borrar caché de APCu',\n\t\t'generaltitle' => 'Información general sobre la caché',\n\t\t'version' => 'Versión de APCu',\n\t\t'phpversion' => 'Versión PHP',\n\t\t'host' => 'Host APCu',\n\t\t'sharedmem' => 'Memoria compartida',\n\t\t'sharedmemval' => '%d Segmento(s) con %s (%s memoria)',\n\t\t'start' => 'Hora de inicio',\n\t\t'uptime' => 'Tiempo de actividad',\n\t\t'upload' => 'Soporte de carga de archivos',\n\t\t'cachetitle' => 'Información de la caché',\n\t\t'cvar' => 'Variables en caché',\n\t\t'hit' => 'Aciertos',\n\t\t'miss' => 'Fallos',\n\t\t'reqrate' => 'Tasa de peticiones (aciertos, fallos)',\n\t\t'creqsec' => 'Peticiones de caché/segundo',\n\t\t'hitrate' => 'Tasa de aciertos',\n\t\t'missrate' => 'Porcentaje de fallos',\n\t\t'insrate' => 'Tasa de inserciones',\n\t\t'cachefull' => 'Recuento de caché llena',\n\t\t'runtime' => 'Configuración de tiempo de ejecución',\n\t\t'memnote' => 'Uso de memoria',\n\t\t'total' => 'Total',\n\t\t'free' => 'Libre',\n\t\t'used' => 'Usado',\n\t\t'hitmiss' => 'Aciertos y errores',\n\t\t'detailmem' => 'Uso detallado de memoria y fragmentación',\n\t\t'fragment' => 'Fragmentación',\n\t\t'nofragment' => 'Sin fragmentación',\n\t\t'fragments' => 'Fragmentos'\n\t],\n\t'apikeys' => [\n\t\t'no_api_keys' => 'No se han encontrado claves API',\n\t\t'key_add' => 'Añadir nueva clave',\n\t\t'apikey_removed' => 'La clave api con el id #%s ha sido eliminada con éxito',\n\t\t'apikey_added' => 'Se ha generado correctamente una nueva clave api',\n\t\t'clicktoview' => 'Haga clic para ver',\n\t\t'allowed_from' => 'Permitido desde',\n\t\t'allowed_from_help' => 'Lista separada por comas de direcciones IP / redes.<br />Por defecto está vacío (permitir desde todos).',\n\t\t'valid_until' => 'Válido hasta',\n\t\t'valid_until_help' => 'Fecha hasta la que es válido, formato AAAA-MM-DDThh:mm'\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Contraseña antigua',\n\t\t'new_password' => 'Nueva contraseña',\n\t\t'new_password_confirm' => 'Confirme la contraseña',\n\t\t'new_password_ifnotempty' => 'Nueva contraseña (vacía = sin cambios)',\n\t\t'also_change_ftp' => ' cambie también la contraseña de la cuenta FTP principal',\n\t\t'also_change_stats' => ' cambie también la contraseña de la página de estadísticas'\n\t],\n\t'cron' => [\n\t\t'cronname' => 'cronjob-name',\n\t\t'lastrun' => 'última ejecución',\n\t\t'interval' => 'intervalo',\n\t\t'isactive' => 'activado',\n\t\t'description' => 'descripción',\n\t\t'changewarning' => 'Cambiar estos valores puede tener una causa negativa en el comportamiento de froxlor y sus tareas automatizadas.<br/>Por favor, sólo cambie los valores aquí, si está seguro de lo que está haciendo.'\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'no description given',\n\t\t'cron_tasks' => 'Generación de archivos de configuración',\n\t\t'cron_legacy' => 'cronjob heredado (antiguo)',\n\t\t'cron_traffic' => 'Cálculo de tráfico',\n\t\t'cron_usage_report' => 'Informes web y de tráfico',\n\t\t'cron_mailboxsize' => 'Cálculo del tamaño del buzón',\n\t\t'cron_letsencrypt' => 'Actualización de certificados Let\\'s Encrypt',\n\t\t'cron_backup' => 'Procesar tareas de copia de seguridad'\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'Configuración de Cronjob',\n\t\t'cronjobintervalv' => 'Valor del intervalo de tiempo de ejecución',\n\t\t'cronjobinterval' => 'Intervalo de ejecución'\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Aún no ejecutado'\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => 'minutos',\n\t\t'hours' => 'horas',\n\t\t'days' => 'días',\n\t\t'weeks' => 'semanas',\n\t\t'months' => 'meses'\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Directorio de inicio',\n\t\t'name' => 'Nombre',\n\t\t'firstname' => 'Nombre de pila',\n\t\t'lastname' => 'Apellidos',\n\t\t'company' => 'Empresa',\n\t\t'nameorcompany_desc' => 'Se requiere nombre/apellido o empresa',\n\t\t'street' => 'Calle',\n\t\t'zipcode' => 'Código postal',\n\t\t'city' => 'Ciudad',\n\t\t'phone' => 'Teléfono',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'Correo electrónico',\n\t\t'customernumber' => 'ID de cliente',\n\t\t'diskspace' => 'Espacio web',\n\t\t'traffic' => 'Tráfico',\n\t\t'mysqls' => 'Bases de datos MySQL',\n\t\t'emails' => 'Direcciones de correo electrónico',\n\t\t'accounts' => 'Cuentas de correo electrónico',\n\t\t'forwarders' => 'Remitentes de correo electrónico',\n\t\t'ftps' => 'Cuentas FTP',\n\t\t'subdomains' => 'Subdominios',\n\t\t'domains' => 'Dominios',\n\t\t'mib' => 'MiB',\n\t\t'gib' => 'GiB',\n\t\t'title' => 'Título',\n\t\t'country' => 'País',\n\t\t'email_quota' => 'Cuota de correo electrónico',\n\t\t'email_imap' => 'Correo IMAP',\n\t\t'email_pop3' => 'Correo electrónico POP3',\n\t\t'sendinfomail' => 'Enviarme datos por correo electrónico',\n\t\t'generated_pwd' => 'Sugerencia de contraseña',\n\t\t'usedmax' => 'Usado / Max',\n\t\t'services' => 'Servicios',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Usar Let\\'s Encrypt',\n\t\t\t'description' => 'Obtenga un certificado gratuito de <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. El certificado se creará y renovará automáticamente.'\n\t\t],\n\t\t'selectserveralias_addinfo' => 'Esta opción se puede configurar al editar el dominio. Su valor inicial se hereda del dominio padre.',\n\t\t'total_diskspace' => 'Espacio total en disco',\n\t\t'mysqlserver' => 'Servidor mysql utilizable'\n\t],\n\t'diskquota' => 'Cuota',\n\t'dns' => [\n\t\t'destinationip' => 'IP(s) del dominio',\n\t\t'standardip' => 'IP estándar del servidor',\n\t\t'a_record' => 'Registro A (IPv6 opcional)',\n\t\t'cname_record' => 'Registro CNAME',\n\t\t'mxrecords' => 'Definir registros MX',\n\t\t'standardmx' => 'Registro MX estándar del servidor',\n\t\t'mxconfig' => 'Registros MX personalizados',\n\t\t'priority10' => 'Prioridad 10',\n\t\t'priority20' => 'Prioridad 20',\n\t\t'txtrecords' => 'Definir registros TXT',\n\t\t'txtexample' => 'Ejemplo (SPF-entry):<br/>v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'howitworks' => 'Aquí puedes gestionar las entradas DNS para tu dominio. Ten en cuenta que froxlor generará automáticamente los registros NS/MX/A/AAAA por ti. Las entradas personalizadas son preferibles, sólo las entradas que falten serán generadas automáticamente.'\n\t],\n\t'dnseditor' => [\n\t\t'edit' => 'editar DNS',\n\t\t'records' => 'Registros',\n\t\t'notes' => [\n\t\t\t'A' => 'Dirección IPv4 de 32 bits, usada para mapear nombres de host a una dirección IP del host.',\n\t\t\t'AAAA' => 'Dirección IPv6 de 128 bits, utilizada para asignar nombres de host a una dirección IP del host.',\n\t\t\t'CAA' => 'El registro de recursos CAA permite al titular de un nombre de dominio DNS especificar una o varias Autoridades de Certificación (CA) autorizadas a emitir certificados para ese dominio.<br/>Estructura: <code>flag tag[issue|issuewild|iodef|contactmail|contactphone] value</code><br/>Ejemplo: <code>0 issue \"ca.example.net\"<br/></code> 0 <code>iodef \"mailto:security@example.com\"</code>',\n\t\t\t'CNAME' => 'Alias del nombre de dominio, la búsqueda DNS continuará reintentando la búsqueda con el nuevo nombre. Sólo es posible para subdominios.',\n\t\t\t'DNAME' => 'Crea un alias para todo un subárbol del árbol de nombres de dominio',\n\t\t\t'LOC' => 'Información sobre la ubicación geográfica de un nombre de dominio.<br/>Estructura: <code>( d1 [m1 [s1]] { d2 [m2 [s2]] { alt[\"m\"] [siz[\"m\"] [hp[\"m\"] [vp[\"m\"]]]] )</code><br/>Descripción: <code>d1</code>: [0 . <code>. 90] (grados de latitud)\n\t\t\td2: [0 .. 180] (grados de longitud)\n\t\t\tm1, m2: [0 .. 59] (minutos latitud/longitud)\n\t\t\ts1, s2: [0 .. 59,999] (segundos latitud/longitud)\n\t\t\talt: [-100000.00 .. 42849672.95] POR .01 (altitud en metros)\n\t\t\tsiz, hp, vp: [0 .. 90000000.00] (tamaño/precisión en metros)</code><br/>Ejemplo: <code>52 22 23.000 N 4 53 32.000 E -2.00m 0.00m 10000m 10m</code>',\n\t\t\t'MX' => 'Registro de intercambio de correo, asigna un nombre de dominio a un servidor de correo para ese dominio.<br/>Ejemplo: <code>10 mail.example.com</code><br/>Nota: Para la prioridad, utilice el campo anterior.',\n\t\t\t'NS' => 'Delega una zona DNS para que utilice los servidores de nombres autoritativos indicados.',\n\t\t\t'RP' => 'Registro de persona responsable<br/>Estructura: <code>mailbox[sustituir @ por un punto] txt-record-name</code><br/>Ejemplo: <code>team.froxlor.org. froxlor.org.</code>',\n\t\t\t'SRV' => 'Registro de ubicación de servicio, utilizado para protocolos más recientes en lugar de crear registros específicos de protocolo como MX.<br/>Estructura: <code>priority weight port target</code><br/>Ejemplo: <code>0 5 5060 sipserver.example.com.</code><br/>Nota: Para la prioridad, utilice el campo anterior.',\n\t\t\t'SSHFP' => 'El registro de recursos SSHFP se utiliza para publicar huellas digitales de claves de shell seguro (SSH) en el DNS.<br/>Estructura: <code>tipo de algoritmo huella digital</code><br/>Algoritmos: 0: reservado <code>, 1: RSA, 2: DSA, 3: ECDSA, 4: Ed25519, 6: Ed448</code><br/>Tipos: 0 <code>: reservado, 1: SHA-1, 2: SHA-256</code><br/>Ejemplo: <code>2 1 123456789abcdef67890123456789abcdef67890</code>',\n\t\t\t'TXT' => 'Texto descriptivo de libre definición.'\n\t\t]\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'Ruta OpenBasedir',\n\t\t'docroot' => 'Ruta del campo anterior',\n\t\t'homedir' => 'Directorio de inicio',\n\t\t'docparent' => 'Directorio padre de la ruta del campo anterior',\n    'ssl_certificate_placeholder' => '---- BEGIN CERTIFICATE---' . PHP_EOL . '[...]' . PHP_EOL . '----END CERTIFICATE----',\n    'ssl_key_placeholder' => '---- BEGIN RSA PRIVATE KEY-----' . PHP_EOL . '[...]' . PHP_EOL . '-----END RSA PRIVATE KEY-----',\n\t],\n\t'domains' => [\n\t\t'description' => 'Aquí puede crear (sub)dominios y cambiar sus rutas.<br/>El sistema necesitará algún tiempo para aplicar la nueva configuración después de cada cambio.',\n\t\t'domainsettings' => 'Configuración del dominio',\n\t\t'domainname' => 'Nombre de dominio',\n\t\t'subdomain_add' => 'Crear subdominio',\n\t\t'subdomain_edit' => 'Editar (sub)dominio',\n\t\t'wildcarddomain' => '¿Crear como dominio comodín?',\n\t\t'aliasdomain' => 'Alias para dominio',\n\t\t'noaliasdomain' => 'Sin alias de dominio',\n\t\t'hasaliasdomains' => 'Tiene dominio(s) alias',\n\t\t'statstics' => 'Estadísticas de uso',\n\t\t'isassigneddomain' => 'Dominio asignado',\n\t\t'add_date' => 'Añadido a froxlor',\n\t\t'registration_date' => 'Añadido al registro',\n\t\t'topleveldomain' => 'Top-Level-Dominio',\n\t\t'associated_with_domain' => 'Asociado',\n\t\t'aliasdomains' => 'Alias dominios',\n\t\t'redirectifpathisurl' => 'Código de redirección (por defecto: vacío)',\n\t\t'redirectifpathisurlinfo' => 'Sólo tiene que seleccionar una de estas opciones si ha introducido una URL como ruta<br/><strong class=\"text-danger\">NOTA:</strong> Los cambios sólo se aplican si la ruta indicada es una URL.',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'Direcciones IP',\n\t\t\t'description' => 'Especifique una o más direcciones IP para el dominio.<br/><br/><div class=\"text-danger\">NOTA: Las direcciones IP no pueden cambiarse cuando el dominio está configurado como <strong>alias-dominio</strong> de otro dominio.</div>'\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'Dirección(es) IP SSL'\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'Redirección SSL',\n\t\t\t'description' => 'Esta opción crea redireccionamientos para vhosts no SSL de forma que todas las peticiones son redirigidas al vhost SSL.<br/><br/>p.e. una petición a <strong>http://domain.tld/</strong> le redirigirá a <strong>https://domain.tld/</strong>'\n\t\t],\n\t\t'serveraliasoption_wildcard' => 'Comodín (*.dominio.tld)',\n\t\t'serveraliasoption_www' => 'WWW (www.domain.tld)',\n\t\t'serveraliasoption_none' => 'Sin alias',\n\t\t'domain_import' => 'Importar dominios',\n\t\t'import_separator' => 'Separador',\n\t\t'import_offset' => 'Desplazamiento',\n\t\t'import_file' => 'Archivo CSV',\n\t\t'import_description' => 'Para obtener información detallada sobre la estructura del archivo de importación y sobre cómo realizar la importación correctamente, visite <a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\" class=\"alert-link\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a>.',\n\t\t'ssl_redirect_temporarilydisabled' => '<br/>La redirección SSL se desactiva temporalmente mientras se genera un nuevo certificado Let\\'s Encrypt. Se volverá a activar una vez generado el certificado.',\n\t\t'termination_date' => 'Fecha de terminación',\n\t\t'termination_date_overview' => 'terminado a partir de ',\n\t\t'ssl_certificates' => 'Certificados SSL',\n\t\t'ssl_certificate_removed' => 'El certificado con el id #%s ha sido eliminado con éxito',\n\t\t'ssl_certificate_error' => 'Error al leer el certificado para el dominio: %s',\n\t\t'no_ssl_certificates' => 'No hay dominios con certificado SSL',\n\t\t'isaliasdomainof' => 'Es aliasdomain para %s',\n\t\t'isbinddomain' => 'Crear zona DNS',\n\t\t'dkimenabled' => 'DKIM activado',\n\t\t'openbasedirenabled' => 'Restricción de Openbasedir',\n\t\t'hsts' => 'HSTS habilitado',\n\t\t'aliasdomainid' => 'ID de aliasdominio'\n\t],\n\t'emails' => [\n\t\t'description' => 'Aquí puedes crear y modificar tus direcciones de correo electrónico.<br/>Una cuenta es como el buzón que tienes delante de casa. Si alguien le envía un correo electrónico, éste caerá en la cuenta.<br/><br/>Para descargar sus correos electrónicos utilice la siguiente configuración en su programa de correo: (¡Los datos en <i>cursiva</i> deben cambiarse por los equivalentes que haya escrito!)<br/>Hostname: <b><i>nombre del dominio</i></b><br/>Username: <b><i>nombre de la cuenta / dirección de correo electrónico</i></b><br/>password: <b><i>la contraseña que haya elegido</i></b>',\n\t\t'emailaddress' => 'Dirección de correo electrónico',\n\t\t'emails_add' => 'Crear dirección de correo electrónico',\n\t\t'emails_edit' => 'Editar dirección de correo electrónico',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => '¿Definir como dirección \"catchall\"?',\n\t\t'account' => 'Cuenta',\n\t\t'account_add' => 'Crear cuenta',\n\t\t'account_delete' => 'Eliminar cuenta',\n\t\t'from' => 'Fuente',\n\t\t'to' => 'Destino',\n\t\t'forwarders' => 'Transitarios',\n\t\t'forwarder_add' => 'Crear expedidor',\n\t\t'alternative_emailaddress' => 'Dirección de correo electrónico alternativa',\n\t\t'quota' => 'Cuota',\n\t\t'noquota' => 'Sin cuota',\n\t\t'updatequota' => 'Actualizar cuota',\n\t\t'quota_edit' => 'Modificar cuota de correo electrónico',\n\t\t'noemaildomainaddedyet' => 'Aún no tiene un dominio (de correo electrónico) en su cuenta.',\n\t\t'back_to_overview' => 'Volver a la vista general de dominios',\n\t\t'accounts' => 'Cuentas',\n\t\t'emails' => 'Direcciones'\n\t],\n\t'error' => [\n\t\t'error' => 'Error',\n\t\t'directorymustexist' => 'El directorio %s debe existir. Por favor, créalo con tu cliente FTP.',\n\t\t'filemustexist' => 'El archivo %s debe existir.',\n\t\t'allresourcesused' => 'Ya ha utilizado todos sus recursos.',\n\t\t'domains_cantdeletemaindomain' => 'No puede eliminar un dominio asignado.',\n\t\t'domains_canteditdomain' => 'No puede editar este dominio. Ha sido desactivado por el administrador.',\n\t\t'domains_cantdeletedomainwithemail' => 'No puede eliminar un dominio que se utiliza como dominio de correo electrónico. Elimine primero todas las direcciones de correo electrónico.',\n\t\t'firstdeleteallsubdomains' => 'Antes de crear un dominio comodín, debe eliminar todos los subdominios.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Ya ha definido un dominio comodín para este dominio.',\n\t\t'ftp_cantdeletemainaccount' => 'No puede eliminar su cuenta FTP principal',\n\t\t'login' => 'El nombre de usuario o la contraseña que has introducido son incorrectos. Inténtalo de nuevo.',\n\t\t'login_blocked' => 'Esta cuenta ha sido suspendida debido a demasiados errores de inicio de sesión. <br/>Por favor, inténtelo %s nuevo en segundos.',\n\t\t'notallreqfieldsorerrors' => 'No ha rellenado todos los campos o ha rellenado algunos incorrectamente.',\n\t\t'oldpasswordnotcorrect' => 'La contraseña antigua no es correcta.',\n\t\t'youcantallocatemorethanyouhave' => 'No puede asignarse más recursos de los que posee.',\n\t\t'mustbeurl' => 'No has escrito una url válida o completa (por ejemplo http://somedomain.com/error404.htm)',\n\t\t'invalidpath' => 'No ha elegido una URL válida (¿quizás problemas con la lista de direcciones?)',\n\t\t'stringisempty' => 'Falta información en el campo',\n\t\t'stringiswrong' => 'Campo incorrecto',\n\t\t'newpasswordconfirmerror' => 'La nueva contraseña y la confirmación no coinciden',\n\t\t'mydomain' => 'Dominio',\n\t\t'mydocumentroot' => 'Documentroot',\n\t\t'loginnameexists' => 'Loginname %s ya existe',\n\t\t'emailiswrong' => 'Email-address %s contiene caracteres no válidos o está incompleto',\n\t\t'alternativeemailiswrong' => 'Las %s dirección de correo electrónico alternativas dadas para enviar las credenciales parecen no ser válidas',\n\t\t'loginnameiswrong' => 'Loginname \"%s\" contiene caracteres ilegales.',\n\t\t'loginnameiswrong2' => 'Loginname contiene demasiados caracteres. Sólo se permiten caracteres de %s.',\n\t\t'userpathcombinationdupe' => 'La combinación de nombre de usuario y ruta ya existe',\n\t\t'patherror' => 'Error general La ruta no puede estar vacía',\n\t\t'errordocpathdupe' => 'La opción para la %s la ruta ya existe',\n\t\t'adduserfirst' => 'Por favor, cree primero un cliente',\n\t\t'domainalreadyexists' => 'El %s dominio ya está asignado a un cliente',\n\t\t'nolanguageselect' => 'No se ha seleccionado ningún idioma.',\n\t\t'nosubjectcreate' => 'Debe definir un asunto para esta plantilla de correo.',\n\t\t'nomailbodycreate' => 'Debe definir un texto de correo para esta plantilla de correo.',\n\t\t'templatenotfound' => 'No se ha encontrado la plantilla.',\n\t\t'alltemplatesdefined' => 'No puede definir más plantillas, todos los idiomas ya están soportados.',\n\t\t'wwwnotallowed' => 'www no está permitido para subdominios.',\n\t\t'subdomainiswrong' => 'La %s del subdominio contiene caracteres no válidos.',\n\t\t'domaincantbeempty' => 'El nombre de dominio no puede estar vacío.',\n\t\t'domainexistalready' => 'El dominio %s ya existe.',\n\t\t'domainisaliasorothercustomer' => 'El dominio alias seleccionado es en sí mismo un dominio alias, tiene una combinación ip/puerto diferente o pertenece a otro cliente.',\n\t\t'emailexistalready' => 'La dirección de correo electrónico %s ya existe.',\n\t\t'maindomainnonexist' => 'El dominio principal %s no existe.',\n\t\t'destinationnonexist' => 'Por favor, cree su redireccionador en el campo \"Destino\".',\n\t\t'destinationalreadyexistasmail' => 'El remitente a %s ya existe como dirección de correo electrónico activa.',\n\t\t'destinationalreadyexist' => 'Ya ha definido un reenviador para \"%s\".',\n\t\t'destinationiswrong' => 'La %s la redirección contiene caracteres no válidos o está incompleta.',\n\t\t'templatelanguagecombodefined' => 'La combinación idioma/plantilla seleccionada ya ha sido definida.',\n\t\t'templatelanguageinvalid' => 'El idioma seleccionado no existe.',\n\t\t'ipstillhasdomains' => 'La combinación IP/Puerto que desea eliminar todavía tiene dominios asignados, por favor reasígnelos a otras combinaciones IP/Puerto antes de eliminar esta combinación IP/Puerto.',\n\t\t'cantdeletedefaultip' => 'No puede borrar la combinación IP/Puerto por defecto, por favor haga otra combinación IP/Puerto por defecto para antes de borrar esta combinación IP/Puerto.',\n\t\t'cantdeletesystemip' => 'No puede borrar la última IP del sistema, cree una nueva combinación IP/Puerto para la IP del sistema o cambie la IP del sistema.',\n\t\t'myipaddress' => 'IP',\n\t\t'myport' => 'Puerto',\n\t\t'myipdefault' => 'Debe seleccionar una combinación IP/Puerto que se convierta en predeterminada.',\n\t\t'myipnotdouble' => 'Esta combinación IP/Puerto ya existe.',\n\t\t'admin_domain_emailsystemhostname' => 'El nombre de host del servidor no se puede utilizar como dominio del cliente.',\n\t\t'cantchangesystemip' => 'No puede cambiar la última IP del sistema, cree otra nueva combinación IP/Puerto para la IP del sistema o cambie la IP del sistema.',\n\t\t'sessiontimeoutiswrong' => 'Sólo se permite un \"tiempo de espera de sesión\" numérico.',\n\t\t'maxloginattemptsiswrong' => 'Sólo se permiten \"intentos de inicio de sesión máximos\" numéricos.',\n\t\t'deactivatetimiswrong' => 'Sólo se permite \"tiempo de desactivación\" numérico.',\n\t\t'accountprefixiswrong' => 'El \"customerprefix\" es incorrecto.',\n\t\t'mysqlprefixiswrong' => 'El \"prefijo SQL\" es incorrecto.',\n\t\t'ftpprefixiswrong' => 'El \"prefijo FTP\" es incorrecto.',\n\t\t'ipiswrong' => 'La \"dirección IP\" es incorrecta. Sólo se permite una dirección IP válida.',\n\t\t'vmailuidiswrong' => 'El \"mails-uid\" es incorrecto. Sólo se permite un UID numérico.',\n\t\t'vmailgidiswrong' => 'El \"mails-gid\" es incorrecto. Sólo se permite un GID numérico.',\n\t\t'adminmailiswrong' => 'La dirección del remitente es incorrecta. Sólo se permite una dirección de correo electrónico válida.',\n\t\t'pagingiswrong' => 'El valor \"entries per page\" es incorrecto. Sólo se permiten caracteres numéricos.',\n\t\t'phpmyadminiswrong' => 'El enlace phpMyAdmin no es un enlace válido.',\n\t\t'webmailiswrong' => 'El enlace webmail no es un enlace válido.',\n\t\t'webftpiswrong' => 'El enlace WebFTP no es un enlace válido.',\n\t\t'stringformaterror' => 'El valor del campo \"%s\" no tiene el formato esperado.',\n\t\t'loginnameisusingprefix' => 'No puede crear cuentas que empiecen por \"%s\", ya que este prefijo está configurado para ser utilizado en la asignación automática de nombres de cuenta. Por favor, introduzca otro nombre de cuenta.',\n\t\t'loginnameissystemaccount' => 'La cuenta \"%s\" ya existe en el sistema y no se puede utilizar. Por favor, introduzca otro nombre de cuenta.',\n\t\t'youcantdeleteyourself' => 'No puede borrarse por razones de seguridad.',\n\t\t'youcanteditallfieldsofyourself' => 'Nota: No puede editar todos los campos de su propia cuenta por razones de seguridad.',\n\t\t'documentrootexists' => 'El directorio \"%s\" ya existe para este cliente. Por favor, elimínelo antes de añadir el cliente de nuevo.',\n\t\t'norepymailiswrong' => 'La dirección \"Noreply-address\" es incorrecta. Sólo se permite una dirección de correo electrónico válida.',\n\t\t'logerror' => 'Log-Error: %s',\n\t\t'nomessagetosend' => 'No ha introducido ningún mensaje.',\n\t\t'norecipientsgiven' => 'No ha especificado ningún destinatario',\n\t\t'errorsendingmail' => 'El mensaje a \"%s\" ha fallado',\n\t\t'errorsendingmailpub' => 'El mensaje a la dirección de correo electrónico indicada ha fallado',\n\t\t'cannotreaddir' => 'No se ha podido leer el directorio \"%s\".',\n\t\t'invalidip' => 'Dirección IP no válida: %s',\n\t\t'invalidmysqlhost' => 'Dirección de host MySQL no válida: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'No puede activar Webalizer y AWstats al mismo tiempo, por favor elija uno de ellos.',\n\t\t'cannotwritetologfile' => 'No se puede abrir el archivo de registro %s para escribir',\n\t\t'vmailquotawrong' => 'El quotasize debe ser un número positivo.',\n\t\t'allocatetoomuchquota' => 'Ha intentado asignar la cuota %s MB, pero no tiene suficiente.',\n\t\t'missingfields' => 'No se han rellenado todos los campos obligatorios.',\n\t\t'requiredfield' => 'Este campo es obligatorio.',\n\t\t'accountnotexisting' => 'La cuenta de correo electrónico indicada no existe.',\n\t\t'nopermissionsorinvalidid' => 'No tiene permisos suficientes para cambiar esta configuración o se ha introducido un identificador no válido.',\n\t\t'phpsettingidwrong' => 'No existe una configuración PHP con este id.',\n\t\t'descriptioninvalid' => 'La descripción es demasiado corta, demasiado larga o contiene caracteres ilegales.',\n\t\t'info' => 'Información',\n\t\t'filecontentnotset' => 'El archivo no puede estar vacío.',\n\t\t'index_file_extension' => 'La extensión del fichero índice debe tener entre 1 y 6 caracteres. La extensión sólo puede contener caracteres como a-z, A-Z y 0-9',\n\t\t'customerdoesntexist' => 'El cliente seleccionado no existe.',\n\t\t'admindoesntexist' => 'El administrador elegido no existe.',\n\t\t'ipportdoesntexist' => 'La combinación ip/puerto que ha elegido no existe.',\n\t\t'usernamealreadyexists' => 'El nombre de usuario %s ya existe.',\n\t\t'plausibilitychecknotunderstood' => 'No se ha entendido la respuesta de la comprobación de plausibilidad.',\n\t\t'errorwhensaving' => 'Se ha producido un error al guardar la %s campos',\n\t\t'hiddenfieldvaluechanged' => 'El valor del campo oculto \"%s\" ha cambiado al editar la configuración.<br/><br/>Esto no suele ser un gran problema, pero la configuración no se ha podido guardar por este motivo.',\n\t\t'notrequiredpasswordlength' => 'La contraseña introducida es demasiado corta. Por favor, introduzca al menos caracteres de %s.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Ups, un campo que debería mostrarse como una opción en la vista de configuración no es un tipo exceptuado. Puedes culpar a los desarrolladores por esto. Esto no debería ocurrir.',\n\t\t'pathmaynotcontaincolon' => 'La ruta introducida no debe contener dos puntos (\":\"). Por favor, introduzca un valor de ruta correcto.',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => 'La complejidad de la contraseña especificada no se ha cumplido.<br/>Póngase en contacto con su administrador si tiene alguna duda sobre la complejidad especificada.',\n\t\t'invaliderrordocumentvalue' => 'El valor dado como ErrorDocument no parece ser un archivo, URL o cadena válidos.',\n\t\t'intvaluetoolow' => 'El número dado es demasiado bajo (campo %s))',\n\t\t'intvaluetoohigh' => 'El número dado es demasiado alto (campo %s)',\n\t\t'phpfpmstillenabled' => 'PHP-FPM está actualmente activo. Desactívelo antes de activar FCGID.',\n\t\t'fcgidstillenabled' => 'FCGID está actualmente activo. Desactívelo antes de activar PHP-FPM.',\n\t\t'domains_cantdeletedomainwithaliases' => 'No se puede eliminar un dominio que se utiliza para alias. Primero debe eliminar los alias.',\n\t\t'user_banned' => 'Su cuenta ha sido bloqueada. Por favor, contacte con su administrador para más información.',\n\t\t'session_timeout' => 'Valor demasiado bajo',\n\t\t'session_timeout_desc' => 'El tiempo de espera de la sesión no debe ser inferior a 1 minuto.',\n\t\t'invalidhostname' => 'El nombre de host debe ser un dominio válido. No puede estar vacío ni estar formado sólo por espacios en blanco.',\n\t\t'operationnotpermitted' => 'Operación no permitida',\n\t\t'featureisdisabled' => 'La función de %s está desactivada. Póngase en contacto con su proveedor de servicios.',\n\t\t'usercurrentlydeactivated' => 'El usuario %s está actualmente desactivado',\n\t\t'setlessthanalreadyused' => 'No puede establecer menos recursos de \\'%s\\' de los que este usuario ya ha utilizado<br/>',\n\t\t'stringmustntbeempty' => 'El valor del campo %s no debe estar vacío',\n\t\t'sslcertificateismissingprivatekey' => 'Necesita especificar una clave privada para su certificado',\n\t\t'sslcertificatewrongdomain' => 'El certificado indicado no pertenece a este dominio',\n\t\t'sslcertificateinvalidcert' => 'El contenido del certificado indicado no parece ser un certificado válido.',\n\t\t'sslcertificateinvalidcertkeypair' => 'La clave privada indicada no pertenece al certificado en cuestión.',\n\t\t'sslcertificateinvalidca' => 'Los datos del certificado de la CA no parecen ser un certificado válido.',\n\t\t'sslcertificateinvalidchain' => 'Los datos de la cadena del certificado no parecen ser un certificado válido.',\n\t\t'givendirnotallowed' => 'El directorio indicado en el campo %s no está permitido.',\n\t\t'sslredirectonlypossiblewithsslipport' => 'El uso de Let\\'s Encrypt sólo es posible cuando el dominio tiene asignada al menos una combinación IP/puerto habilitada para ssl.',\n\t\t'fcgidstillenableddeadlock' => 'FCGID está actualmente activo.<br/>Por favor, desactívelo antes de cambiar a otro servidor web que no sea Apache2',\n\t\t'send_report_title' => 'Enviar informe de error',\n\t\t'send_report_desc' => 'Gracias por informar de este error y ayudarnos a mejorar froxlor.<br/>Este es el correo electrónico que se enviará al equipo de desarrolladores de froxlor:',\n\t\t'send_report' => 'Enviar informe',\n\t\t'send_report_error' => 'Error al enviar el informe: <br/>%s',\n\t\t'notallowedtouseaccounts' => 'Tu cuenta no permite usar IMAP/POP3. No puedes añadir cuentas de correo.',\n\t\t'cannotdeletehostnamephpconfig' => 'Esta configuración PHP es usada por el froxlor-vhost y no puede ser borrada.',\n\t\t'cannotdeletedefaultphpconfig' => 'Esta configuración PHP está establecida por defecto y no puede ser borrada.',\n\t\t'passwordshouldnotbeusername' => 'La contraseña no debe ser la misma que el nombre de usuario.',\n\t\t'no_phpinfo' => 'Lo sentimos, no puedo leer phpinfo()',\n\t\t'moveofcustomerfailed' => 'El cambio del cliente al administrador/revendedor seleccionado ha fallado. Tenga en cuenta que todos los demás cambios en el cliente se aplicaron con éxito en esta etapa.<br/><br/>Mensaje de error: %s',\n\t\t'domain_import_error' => 'Se ha producido el siguiente error al importar dominios: %s',\n\t\t'fcgidandphpfpmnogoodtogether' => 'FCGID y PHP-FPM no pueden ser activados al mismo tiempo',\n\t\t'no_apcuinfo' => 'No hay información de caché disponible. APCu no parece estar ejecutándose.',\n\t\t'no_opcacheinfo' => 'No hay información de caché disponible. OPCache no parece estar ejecutándose.',\n\t\t'nowildcardwithletsencrypt' => 'Let\\'s Encrypt no puede manejar dominios comodín usando ACME en froxlor (requiere dns-challenge), lo siento. Por favor, establezca el ServerAlias a WWW o desactívelo completamente.',\n\t\t'customized_version' => 'Parece que tu instalación de froxlor ha sido modificada, no hay soporte, lo sentimos.',\n\t\t'autoupdate_0' => 'Error desconocido',\n\t\t'autoupdate_1' => 'El ajuste de PHP allow_url_fopen está desactivado. Autoupdate necesita que este ajuste esté habilitado en php.ini',\n\t\t'autoupdate_2' => 'Extensión PHP zip no encontrada, por favor asegúrese de que está instalada y activada',\n\t\t'autoupdate_4' => 'El archivo froxlor no pudo ser almacenado en el disco :(',\n\t\t'autoupdate_5' => 'version.froxlor.org devolvió valores inaceptables :(',\n\t\t'autoupdate_6' => 'Whoops, no había una versión (válida) dada para descargar :(',\n\t\t'autoupdate_7' => 'No se pudo encontrar el archivo descargado :(',\n\t\t'autoupdate_8' => 'No se ha podido extraer el archivo :(',\n\t\t'autoupdate_9' => 'El archivo descargado no ha pasado la comprobación de integridad. Por favor, intente actualizar de nuevo.',\n\t\t'autoupdate_10' => 'La versión mínima soportada de PHP es 7.4.0',\n\t\t'autoupdate_11' => 'Webupdate está desactivado',\n\t\t'mailaccistobedeleted' => 'Otra cuenta con el mismo nombre (%s) está siendo eliminada y por lo tanto no puede ser añadida en este momento.',\n\t\t'dns_domain_nodns' => 'DNS no está habilitado para este dominio',\n\t\t'dns_content_empty' => 'No hay contenido',\n\t\t'dns_content_invalid' => 'El contenido DNS no es válido',\n\t\t'dns_arec_noipv4' => 'No se ha proporcionado una dirección IP válida para el registro A.',\n\t\t'dns_aaaarec_noipv6' => 'No se ha indicado una dirección IP válida para el registro AAAA',\n\t\t'dns_mx_prioempty' => 'Prioridad MX no válida',\n\t\t'dns_mx_needdom' => 'El valor de contenido MX debe ser un nombre de dominio válido.',\n\t\t'dns_mx_noalias' => 'El valor de contenido MX no puede ser una entrada CNAME.',\n\t\t'dns_cname_invaliddom' => 'Nombre de dominio no válido para el registro CNAME',\n\t\t'dns_cname_nomorerr' => 'Ya existe un resource-record con el mismo nombre de registro. No se puede utilizar como CNAME.',\n\t\t'dns_other_nomorerr' => 'Ya existe un registro CNAME con el mismo nombre de registro. No se puede utilizar para otro tipo.',\n\t\t'dns_ns_invaliddom' => 'Nombre de dominio no válido para el registro NS',\n\t\t'dns_srv_prioempty' => 'Prioridad SRV no válida',\n\t\t'dns_srv_invalidcontent' => 'Contenido SRV no válido, debe contener los campos weight, port y target, p.ej: 5 5060 sipserver.ejemplo.com.',\n\t\t'dns_srv_needdom' => 'El valor SRV target debe ser un nombre de dominio válido',\n\t\t'dns_srv_noalias' => 'El valor SRV-target no puede ser una entrada CNAME.',\n\t\t'dns_duplicate_entry' => 'El registro ya existe',\n\t\t'dns_notfoundorallowed' => 'Dominio no encontrado o sin permiso',\n\t\t'domain_nopunycode' => 'No debe especificar punycode (IDNA). El dominio se convertirá automáticamente',\n\t\t'dns_record_toolong' => 'Los registros/etiquetas sólo pueden tener un máximo de 63 caracteres',\n\t\t'noipportgiven' => 'No se ha especificado IP/puerto',\n\t\t'jsonextensionnotfound' => 'Esta función requiere la extensión php json.',\n\t\t'cannotdeletesuperadmin' => 'El primer administrador no puede ser eliminado.',\n\t\t'no_wwwcnamae_ifwwwalias' => 'No se puede establecer un registro CNAME para \"www\" porque el dominio está configurado para generar un alias www. Cambie la configuración a \"Sin alias\" o \"Alias comodín\".',\n\t\t'local_group_exists' => 'El grupo indicado ya existe en el sistema.',\n\t\t'local_group_invalid' => 'El nombre del grupo no es válido',\n\t\t'invaliddnsforletsencrypt' => 'El DNS del dominio no incluye ninguna de las direcciones IP seleccionadas. La generación del certificado Let\\'s Encrypt no es posible.',\n\t\t'notallowedphpconfigused' => 'Intentando usar php-config que no está asignado al cliente',\n\t\t'pathmustberelative' => 'El usuario no tiene permiso para especificar directorios fuera del directorio personal del cliente. Por favor, especifique una ruta relativa (sin /).',\n\t\t'mysqlserverstillhasdbs' => 'No se puede eliminar el servidor de base de datos de la lista de clientes permitidos, ya que todavía hay bases de datos en él.',\n\t\t'domaincannotbeedited' => 'No se le permite editar la %s dominio.',\n\t\t'invalidcronjobintervalvalue' => 'El intervalo de Cronjob debe ser uno de los siguientes: %s'\n\t],\n\t'extras' => [\n\t\t'description' => 'Aquí puede añadir algunos extras, por ejemplo protección de directorios.<br/>El sistema necesitará algún tiempo para aplicar la nueva configuración después de cada cambio.',\n\t\t'directoryprotection_add' => 'Añadir protección de directorio',\n\t\t'view_directory' => 'Mostrar el contenido del directorio',\n\t\t'pathoptions_add' => 'Añadir opciones de ruta',\n\t\t'directory_browsing' => 'Exploración del contenido del directorio',\n\t\t'pathoptions_edit' => 'Editar opciones de ruta',\n\t\t'error404path' => '404',\n\t\t'error403path' => '403',\n\t\t'error500path' => '500',\n\t\t'error401path' => '401',\n\t\t'errordocument404path' => 'DocumentoError 404',\n\t\t'errordocument403path' => 'DocumentoError 403',\n\t\t'errordocument500path' => 'DocumentoError 500',\n\t\t'errordocument401path' => 'DocumentoError 401',\n\t\t'execute_perl' => 'Ejecutar perl/CGI',\n\t\t'htpasswdauthname' => 'Razón de autenticación (AuthName)',\n\t\t'directoryprotection_edit' => 'Editar protección de directorio',\n\t\t'path_protection_label' => '<strong class=\"text-danger\">Importante</strong>',\n\t\t'path_protection_info' => 'Le recomendamos encarecidamente que proteja la ruta indicada, consulte \"Extras\" -> \"Protección de directorios\".'\n\t],\n\t'ftp' => [\n\t\t'description' => 'Aquí puede crear y modificar sus cuentas FTP.<br/>Los cambios se realizan al instante y las cuentas pueden utilizarse inmediatamente.',\n\t\t'account_add' => 'Crear cuenta',\n\t\t'account_edit' => 'Editar cuenta ftp',\n\t\t'editpassdescription' => 'Establezca una nueva contraseña o déjela en blanco para no cambiarla.'\n\t],\n\t'gender' => [\n\t\t'title' => 'Título',\n\t\t'male' => 'Sr.',\n\t\t'female' => 'Sra.',\n\t\t'undef' => ''\n\t],\n\t'imprint' => 'Aviso legal',\n\t'index' => [\n\t\t'customerdetails' => 'Datos del cliente',\n\t\t'accountdetails' => 'Datos de la cuenta'\n\t],\n\t'integrity_check' => [\n\t\t'databaseCharset' => 'Juego de caracteres de la base de datos (debe ser UTF-8)',\n\t\t'domainIpTable' => 'Referencias IP <-> dominio',\n\t\t'subdomainSslRedirect' => 'Bandera falsa SSL-redirect para dominios no SSL',\n\t\t'froxlorLocalGroupMemberForFcgidPhpFpm' => 'froxlor-usuario en los grupos de clientes (para FCGID/php-fpm)',\n\t\t'webserverGroupMemberForFcgidPhpFpm' => 'Webserver-user en los grupos de clientes (para FCGID/php-fpm)',\n\t\t'subdomainLetsencrypt' => 'Los dominios principales sin puerto SSL asignado no tienen subdominios con redirección SSL activa'\n\t],\n\t'logger' => [\n\t\t'date' => 'Fecha',\n\t\t'type' => 'Tipo',\n\t\t'action' => 'Acción',\n\t\t'user' => 'Usuario',\n\t\t'truncate' => 'Registro vacío',\n\t\t'reseller' => 'Revendedor',\n\t\t'admin' => 'Administrador',\n\t\t'cron' => 'Cronjob',\n\t\t'login' => 'Inicio de sesión',\n\t\t'intern' => 'Interno',\n\t\t'unknown' => 'Desconocido'\n\t],\n\t'login' => [\n\t\t'username' => 'Nombre de usuario',\n\t\t'password' => 'Contraseña',\n\t\t'language' => 'Idioma',\n\t\t'login' => 'Inicio de sesión',\n\t\t'logout' => 'Cierre de sesión',\n\t\t'profile_lng' => 'Idioma del perfil',\n\t\t'welcomemsg' => 'Inicie sesión para acceder a su cuenta.',\n\t\t'forgotpwd' => '¿Ha olvidado su contraseña?',\n\t\t'presend' => 'Restablecer contraseña',\n\t\t'email' => 'Correo electrónico',\n\t\t'remind' => 'Restablecer mi contraseña',\n\t\t'usernotfound' => 'Usuario no encontrado',\n\t\t'backtologin' => 'Volver al inicio de sesión',\n\t\t'combination_not_found' => 'No se ha encontrado la combinación de usuario y dirección de correo electrónico.',\n\t\t'2fa' => 'Autenticación de dos factores (2FA)',\n\t\t'2facode' => 'Introduzca el código 2FA'\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Hola,\\\\nsu cuenta de correo {EMAIL} configurado correctamente.\\\\nEste es un correo creado\\\\nautomáticamente, ¡por favor no conteste!\\\\nSu atentamente, su administrador',\n\t\t\t'subject' => 'Cuenta de correo configurada correctamente'\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Hola {SALUTATION}, aquí están los datos de su cuenta: Nombre de usuario: {USERNAME}: {PASSWORD}, su administrador.',\n\t\t\t'subject' => 'Información de la cuenta'\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Hola {SALUTATION}, su cuenta de correo {EMAIL} se ha configurado correctamente. Su contraseña es {PASSWORD}. Este es un correo creado automáticamente, por favor no conteste. Atentamente, su administrador.',\n\t\t\t'subject' => 'Cuenta de correo configurada correctamente'\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Restablecer contraseña',\n\t\t\t'mailbody' => 'Hola {SALUTATION}, aquí está su enlace para establecer una nueva contraseña. Este enlace es válido durante las próximas 24 horas. {LINK}, su administrador.'\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[Nueva base de datos creada',\n\t\t\t'mailbody' => 'Hola {CUST_NAME},\n\nacabas de añadir una nueva base de datos. Aquí está la información introducida:\n\nNombre de base de datos: {DB_NAME}\nContraseña: {DB_PASS}\nDescripción: {DB_DESC}\nNombre de host de base de datos: {DB_SRV}\nphpMyAdmin: {PMA_URI}\nAtentamente, su administrador'\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'Nuevo usuario ftp creado',\n\t\t\t'mailbody' => 'Hola {CUST_NAME}\n\nacabas de añadir un nuevo usuario ftp. Aquí está la información introducida:\n\nNombre de usuario: {USR_NAME}\nContraseña: {USR_PASS}\nRuta: {USR_PATH}\n\nAtentamente, su administrador'\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => 'Estimado {SALUTATION}, ha utilizado {TRAFFICUSED} del {MAX_PERCENT} de su {TRAFFIC} disponible de tráfico.',\n\t\t\t'subject' => 'Alcanzando su límite de tráfico'\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => 'Estimado {SALUTATION}, ha utilizado el {DISKUSED} de su {DISKAVAILABLE} disponible de espacio en disco. Esto es más del {MAX_PERCENT}.',\n\t\t\t'subject' => 'Alcanzando el límite de espacio en disco'\n\t\t],\n\t\t'2fa' => [\n\t\t\t'mailbody' => 'Hola, su código de acceso 2FA es..: {CODE}. Este es un correo creado automáticamente, por favor no responda. Atentamente, su administrador.',\n\t\t\t'subject' => 'froxlor - Código 2FA'\n\t\t]\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Principal',\n\t\t\t'changepassword' => 'Cambiar contraseña',\n\t\t\t'changelanguage' => 'Cambiar idioma',\n\t\t\t'username' => 'Iniciar sesión como ',\n\t\t\t'changetheme' => 'Cambiar tema',\n\t\t\t'apihelp' => 'Ayuda API',\n\t\t\t'apikeys' => 'Claves de la API'\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'Correo electrónico',\n\t\t\t'emails' => 'Direcciones',\n\t\t\t'webmail' => 'Correo web',\n\t\t\t'emailsoverview' => 'Vista general de dominios de correo electrónico'\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Bases de datos',\n\t\t\t'phpmyadmin' => 'phpMyAdmin'\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Dominios',\n\t\t\t'settings' => 'Vista general de dominios'\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Cuentas',\n\t\t\t'webftp' => 'WebFTP'\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extras',\n\t\t\t'directoryprotection' => 'Protección de directorios',\n\t\t\t'pathoptions' => 'Opciones de ruta',\n\t\t\t'export' => 'Exportación de datos'\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Tráfico',\n\t\t\t'current' => 'Mes en curso',\n\t\t\t'overview' => 'Tráfico total'\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'Configuraciones PHP',\n\t\t\t'fpmdaemons' => 'Versiones de PHP-FPM'\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'Registro del sistema'\n\t\t]\n\t],\n\t'message' => [\n\t\t'norecipients' => 'No se ha enviado ningún correo electrónico porque no hay destinatarios en la base de datos',\n\t\t'success' => 'Mensaje enviado correctamente a los destinatarios de %s',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'Usuario/Nombre de la base de datos',\n\t\t'databasedescription' => 'Descripción de la base de datos',\n\t\t'database_create' => 'Crear base de datos',\n\t\t'description' => 'Aquí puede crear y modificar sus bases de datos MySQL.<br/>Los cambios se realizan al instante y la base de datos se puede utilizar inmediatamente.<br/>En el menú de la izquierda encontrará la herramienta phpMyAdmin con la que puede administrar fácilmente su base de datos.<br/><br/>Para utilizar sus bases de datos en sus propios scripts php utilice los siguientes ajustes: (¡Los datos en <i>cursiva</i> tienen que cambiarse por los equivalentes que haya escrito!)<br/>Nombre de host: <b><sql_host/></b><br/>Nombre de usuario: <b><i>databasename</i></b><br/>Contraseña: <b><i>la contraseña que hayas elegido</i></b><br/>Base de datos: <b><i>databasename</i></b>',\n\t\t'mysql_server' => 'Servidor MySQL',\n\t\t'database_edit' => 'Editar base de datos',\n\t\t'size' => 'Tamaño',\n\t\t'privileged_user' => 'Usuario privilegiado de la base de datos',\n\t\t'privileged_passwd' => 'Contraseña para usuario privilegiado',\n\t\t'unprivileged_passwd' => 'Contraseña para usuario sin privilegios',\n\t\t'mysql_ssl_ca_file' => 'Certificado del servidor SSL',\n\t\t'mysql_ssl_verify_server_certificate' => 'Verificar certificado de servidor SSL'\n\t],\n\t'opcacheinfo' => [\n\t\t'generaltitle' => 'Información general',\n\t\t'resetcache' => 'Restablecer OPcache',\n\t\t'version' => 'Versión de OPCache',\n\t\t'phpversion' => 'Versión PHP',\n\t\t'runtimeconf' => 'Configuración de tiempo de ejecución',\n\t\t'start' => 'Hora de inicio',\n\t\t'lastreset' => 'Último reinicio',\n\t\t'oomrestarts' => 'Recuento de reinicios OOM',\n\t\t'hashrestarts' => 'Recuento de reinicios Hash',\n\t\t'manualrestarts' => 'Recuento de reinicios manuales',\n\t\t'hitsc' => 'Número de aciertos',\n\t\t'missc' => 'Faltas',\n\t\t'blmissc' => 'Lista negra',\n\t\t'status' => 'Estado',\n\t\t'never' => 'nunca',\n\t\t'enabled' => 'OPcache activado',\n\t\t'cachefull' => 'Caché llena',\n\t\t'restartpending' => 'Reinicio pendiente',\n\t\t'restartinprogress' => 'Reinicio en curso',\n\t\t'cachedscripts' => 'Recuento de scripts en caché',\n\t\t'memusage' => 'Uso de memoria',\n\t\t'totalmem' => 'Memoria total',\n\t\t'usedmem' => 'Memoria utilizada',\n\t\t'freemem' => 'Memoria libre',\n\t\t'wastedmem' => 'Memoria desperdiciada',\n\t\t'maxkey' => 'Teclas máximas',\n\t\t'usedkey' => 'Teclas usadas',\n\t\t'wastedkey' => 'Teclas desperdiciadas',\n\t\t'strinterning' => 'Intercalación de cadenas',\n\t\t'strcount' => 'Recuento de cadenas',\n\t\t'keystat' => 'Estadística de claves en caché',\n\t\t'used' => 'Usado',\n\t\t'free' => 'Libre',\n\t\t'blacklist' => 'Lista negra',\n\t\t'novalue' => '<i>sin valor</i>',\n\t\t'true' => '<i>verdadero</i>',\n\t\t'false' => '<i>falso</i>',\n\t\t'funcsavail' => 'Funciones disponibles'\n\t],\n\t'panel' => [\n\t\t'edit' => 'Editar',\n\t\t'delete' => 'Eliminar',\n\t\t'create' => 'Crear',\n\t\t'save' => 'Guardar',\n\t\t'yes' => 'Sí',\n\t\t'no' => 'No',\n\t\t'emptyfornochanges' => 'vacío para ningún cambio',\n\t\t'emptyfordefault' => 'vacío para valores por defecto',\n\t\t'path' => 'Ruta',\n\t\t'toggle' => 'Conmutar',\n\t\t'next' => 'Siguiente',\n\t\t'dirsmissing' => '¡No se puede encontrar o leer el directorio!',\n\t\t'unlimited' => '∞',\n\t\t'urloverridespath' => 'URL (anula la ruta)',\n\t\t'pathorurl' => 'Ruta o URL',\n\t\t'ascending' => 'ascendente',\n\t\t'descending' => 'descendente',\n\t\t'search' => 'Buscar en',\n\t\t'used' => 'utilizada',\n\t\t'translator' => 'Traductor',\n\t\t'reset' => 'Descartar cambios',\n\t\t'pathDescription' => 'Si el directorio no existe, se creará automáticamente.',\n\t\t'pathDescriptionEx' => '<br/><br/><span class=\"text-danger\">Nota:</span> La ruta <code>/</code> no está permitida debido a la configuración administrativa, se establecerá automáticamente en <code>/elegido.subdominio.tld/</code> si no se establece en otro directorio.',\n\t\t'pathDescriptionSubdomain' => 'Si el directorio no existe, se creará automáticamente.<br/><br/>Si desea una redirección a otro dominio, esta entrada debe empezar por http:// o https://.<br/><br/>Si la URL termina en / se considera una carpeta, si no, se trata como archivo.',\n\t\t'back' => 'Volver',\n\t\t'reseller' => 'revendedor',\n\t\t'admin' => 'admin',\n\t\t'customer' => 'cliente/s',\n\t\t'send' => 'enviar',\n\t\t'nosslipsavailable' => 'Actualmente no hay combinaciones ip/puerto ssl para este servidor',\n\t\t'backtooverview' => 'Volver a la vista general',\n\t\t'dateformat' => 'AAAA-MM-DD',\n\t\t'dateformat_function' => 'Y-m-d',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Por defecto',\n\t\t'never' => 'Nunca',\n\t\t'active' => 'Activo',\n\t\t'please_choose' => 'Seleccione una opción',\n\t\t'allow_modifications' => 'Permitir modificaciones',\n\t\t'megabyte' => 'MegaByte',\n\t\t'not_supported' => 'No soportado en: ',\n\t\t'view' => 'ver',\n\t\t'toomanydirs' => 'Demasiados subdirectorios. Volver a la selección manual de ruta.',\n\t\t'abort' => 'Abortar',\n\t\t'not_activated' => 'no activado',\n\t\t'off' => 'desactivado',\n\t\t'options' => 'Opciones',\n\t\t'neverloggedin' => 'Aún no se ha iniciado sesión',\n\t\t'descriptionerrordocument' => 'Puede ser una URL, la ruta a un archivo o simplemente una cadena rodeada de \" \"<br/>Déjelo vacío para utilizar el valor predeterminado del servidor.',\n\t\t'unlock' => 'Desbloquear',\n\t\t'theme' => 'Tema',\n\t\t'variable' => 'Variable',\n\t\t'description' => 'Descripción',\n\t\t'cancel' => 'Cancelar',\n\t\t'ssleditor' => 'Configuración SSL para este dominio',\n\t\t'ssleditor_infoshared' => 'Actualmente usando el certificado del dominio padre',\n\t\t'ssleditor_infoglobal' => 'Actualmente usando certificado global',\n\t\t'dashboard' => 'Panel de control',\n\t\t'assigned' => 'Asignado',\n\t\t'available' => 'Disponible',\n\t\t'news' => 'Noticias',\n\t\t'newsfeed_disabled' => 'La fuente de noticias está desactivada. Haga clic en el icono de edición para ir a la configuración.',\n\t\t'ftpdesc' => 'Descripción de FTP',\n\t\t'letsencrypt' => 'Usando Let\\'s encrypt',\n\t\t'set' => 'Aplicar',\n\t\t'shell' => 'Shell',\n\t\t'backuppath' => [\n\t\t\t'title' => 'Ruta de destino de la copia de seguridad',\n\t\t\t'description' => 'Esta es la ruta donde se almacenarán las copias de seguridad. Si se selecciona la copia de seguridad de los datos web, todos los archivos de la carpeta de inicio se almacenan excluyendo la carpeta de copia de seguridad especificada aquí.'\n\t\t],\n\t\t'none_value' => 'Ninguno',\n\t\t'viewlogs' => 'Ver archivos de registro',\n\t\t'not_configured' => 'El sistema aún no está configurado. Pulse aquí para ir a la configuración.',\n\t\t'ihave_configured' => 'He configurado los servicios',\n\t\t'system_is_configured' => '<i class=\"fa-solid fa-circle-exclamation me-1\"/>El sistema ya está configurado',\n\t\t'settings_before_configuration' => 'Por favor, asegúrese de ajustar la configuración antes de configurar los servicios aquí',\n\t\t'image_field_delete' => 'Borrar la imagen actual existente',\n\t\t'usage_statistics' => 'Uso de recursos',\n\t\t'security_question' => 'Cuestión de seguridad',\n\t\t'listing_empty' => 'No se han encontrado entradas',\n\t\t'unspecified' => 'sin especificar',\n\t\t'settingsmode' => 'Modo',\n\t\t'settingsmodebasic' => 'Básico',\n\t\t'settingsmodeadvanced' => 'Avanzado',\n\t\t'settingsmodetoggle' => 'Haga clic para cambiar el modo',\n\t\t'modalclose' => 'Cerrar',\n\t\t'managetablecolumnsmodal' => [\n\t\t\t'title' => 'Administrar columnas de la tabla',\n\t\t\t'description' => 'Aquí puede personalizar las columnas visibles'\n\t\t],\n\t\t'mandatoryfield' => 'Campo obligatorio',\n\t\t'select_all' => 'Seleccionar todo',\n\t\t'unselect_all' => 'Deseleccionar todo',\n\t\t'searchtablecolumnsmodal' => [\n\t\t\t'title' => 'Buscar en campos',\n\t\t\t'description' => 'Seleccione el campo en el que desea buscar'\n\t\t],\n\t\t'upload_import' => 'Cargar e importar'\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => 'Usuario local a usar para PHP-FPM (froxlor vHost)',\n\t\t'vhost_httpgroup' => 'Grupo local a usar para PHP-FPM (froxlor vHost)',\n\t\t'ownvhost' => [\n\t\t\t'title' => 'Habilitar PHP-FPM para el vHost de froxlor',\n\t\t\t'description' => 'Si está habilitado, froxlor también se ejecutará bajo un usuario local'\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => 'Usar mod_proxy / mod_proxy_fcgi',\n\t\t\t'description' => '<strong class=\"text-danger\">Debe activarse cuando se use Debian 9.x (Stretch) o posterior</strong>. Activar para usar php-fpm via mod_proxy_fcgi. Requiere al menos apache-2.4.9'\n\t\t],\n\t\t'ini_flags' => 'Introduzca posibles <strong>php_flags</strong>para php.ini. Una entrada por línea',\n\t\t'ini_values' => 'Introduzca posibles <strong>php_values</strong>para php.ini. Una entrada por línea',\n\t\t'ini_admin_flags' => 'Introduzca posibles <strong>php_admin_flags</strong>para php.ini. Una entrada por línea',\n\t\t'ini_admin_values' => 'Introduzca posibles <strong>php_admin_values</strong>para php.ini. Una entrada por línea'\n\t],\n\t'privacy' => 'Política de privacidad',\n\t'pwdreminder' => [\n\t\t'success' => 'Restablecimiento de contraseña solicitado con éxito. Por favor, siga las instrucciones del correo electrónico que ha recibido.',\n\t\t'notallowed' => 'Usuario desconocido o el restablecimiento de contraseña está desactivado',\n\t\t'changed' => 'Su contraseña se ha actualizado correctamente. Ya puede iniciar sesión con su nueva contraseña.',\n\t\t'wrongcode' => 'Lo sentimos, su código de activación no existe o ya ha caducado.',\n\t\t'choosenew' => 'Establecer nueva contraseña'\n\t],\n\t'question' => [\n\t\t'question' => 'Cuestión de seguridad',\n\t\t'admin_customer_reallydelete' => '¿Realmente desea eliminar el cliente %s? No se puede deshacer.',\n\t\t'admin_domain_reallydelete' => '¿Realmente quieres borrar el dominio %s?',\n\t\t'admin_domain_reallydisablesecuritysetting' => '¿Realmente quieres desactivar esta configuración de seguridad OpenBasedir?',\n\t\t'admin_admin_reallydelete' => '¿Realmente quieres borrar el admin %s? Todos los clientes y dominios serán reasignados a tu cuenta.',\n\t\t'admin_template_reallydelete' => '¿Realmente quieres borrar la plantilla \\'%s\\'?',\n\t\t'domains_reallydelete' => '¿Realmente quieres borrar el dominio %s?',\n\t\t'email_reallydelete' => '¿Realmente quieres borrar la dirección de email %s?',\n\t\t'email_reallydelete_account' => '¿Realmente quieres borrar la cuenta de correo de %s?',\n\t\t'email_reallydelete_forwarder' => '¿Realmente quieres borrar el forwarder %s?',\n\t\t'extras_reallydelete' => '¿Realmente quieres borrar la protección de directorio de %s?',\n\t\t'extras_reallydelete_pathoptions' => '¿Realmente quieres borrar las opciones de ruta de %s?',\n\t\t'ftp_reallydelete' => '¿Realmente quieres borrar la cuenta FTP %s?',\n\t\t'mysql_reallydelete' => '¿Realmente quieres borrar la base de datos %s? Esto no se puede deshacer.',\n\t\t'admin_configs_reallyrebuild' => '¿Realmente quieres reconstruir todos los archivos de configuración?',\n\t\t'admin_customer_alsoremovefiles' => '¿Quitar también los archivos de usuario?',\n\t\t'admin_customer_alsoremovemail' => '¿Eliminar completamente los datos de correo electrónico del sistema de archivos?',\n\t\t'admin_customer_alsoremoveftphomedir' => '¿Quitar también el directorio del usuario FTP?',\n\t\t'admin_ip_reallydelete' => '¿Realmente quieres borrar la dirección IP %s?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => '¿Estás seguro de que quieres que la raíz del documento para este dominio no esté dentro de la raíz del cliente?',\n\t\t'admin_counters_reallyupdate' => '¿Realmente quiere recalcular el uso de recursos?',\n\t\t'admin_cleartextmailpws_reallywipe' => '¿Realmente quiere borrar todas las contraseñas no encriptadas de las cuentas de correo de la tabla mail_users? Esto no se puede revertir. La opción de almacenar las contraseñas de correo electrónico sin cifrar también se desactivará.',\n\t\t'logger_reallytruncate' => '¿Realmente quieres truncar la tabla \"%s\"?',\n\t\t'admin_quotas_reallywipe' => '¿Realmente desea borrar todas las cuotas de la tabla mail_users? Esto no se puede revertir.',\n\t\t'admin_quotas_reallyenforce' => '¿Realmente desea aplicar la cuota por defecto a todos los usuarios? Esto no se puede revertir.',\n\t\t'phpsetting_reallydelete' => '¿Realmente desea eliminar esta configuración? Todos los dominios que usen esta configuración serán cambiados a la configuración por defecto.',\n\t\t'fpmsetting_reallydelete' => '¿Realmente desea eliminar esta configuración de php-fpm? Todas las configuraciones de php que utilicen estos ajustes se cambiarán a la configuración por defecto.',\n\t\t'customer_reallyunlock' => '¿Realmente quieres desbloquear al cliente %s?',\n\t\t'admin_integritycheck_reallyfix' => '¿Realmente quieres intentar arreglar todos los problemas de integridad de la base de datos automáticamente?',\n\t\t'plan_reallydelete' => '¿De verdad quieres eliminar el plan de alojamiento %s?',\n\t\t'apikey_reallydelete' => '¿Realmente quieres borrar esta api-key?',\n\t\t'apikey_reallyadd' => '¿Realmente quieres crear una nueva api-key?',\n\t\t'dnsentry_reallydelete' => '¿Realmente desea eliminar esta entrada de zona?',\n\t\t'certificate_reallydelete' => '¿Realmente quieres borrar este certificado?',\n\t\t'cache_reallydelete' => '¿Realmente quieres borrar la caché?'\n\t],\n\t'redirect_desc' => [\n\t\t'rc_default' => 'por defecto',\n\t\t'rc_movedperm' => 'movido permanentemente',\n\t\t'rc_found' => 'encontrado',\n\t\t'rc_seeother' => 'ver otro',\n\t\t'rc_tempred' => 'redirección temporal'\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Tiempo de espera de la sesión',\n\t\t\t'description' => '¿Cuánto tiempo tiene que estar inactivo un usuario para que se invalide la sesión (segundos)?'\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Prefijo de cliente',\n\t\t\t'description' => '¿Qué prefijo deben tener las cuentas de cliente?'\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'Prefijo SQL',\n\t\t\t'description' => '¿Qué prefijo deben tener las cuentas MySQL?<br/>Utilice \"RANDOM\" como valor para obtener un prefijo aleatorio de 3 dígitos<br/>Utilice \"DBNAME\" como valor, se utiliza un campo de nombre de base de datos junto con el nombre del cliente como prefijo.'\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'Prefijo FTP',\n\t\t\t'description' => '¿Qué prefijo deben tener las cuentas ftp?<br/><b>Si cambias esto, también tendrás que cambiar la consulta SQL Quota en el archivo de configuración del servidor FTP en caso de que lo utilices</b>. '\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Directorio de inicio',\n\t\t\t'description' => '¿Dónde deben almacenarse todos los directorios de inicio?'\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Directorio Logfiles',\n\t\t\t'description' => '¿Dónde deben almacenarse todos los archivos de registro?'\n\t\t],\n\t\t'logfiles_script' => [\n\t\t\t'title' => 'Script personalizado al que enviar los archivos de registro',\n\t\t\t'description' => 'Puede especificar un script aquí y utilizar los marcadores de posición <strong>{LOGFILE}, {DOMAIN} y {CUSTOMER}</strong> si es necesario. En caso de que desee utilizarlo, deberá activar también la opción <strong>Pipe webserver logfiles</strong>. No es necesario el prefijo pipe.'\n\t\t],\n\t\t'logfiles_format' => [\n\t\t\t'title' => 'Formato de registro de acceso',\n\t\t\t'description' => 'Introduzca aquí un formato de registro personalizado de acuerdo con las especificaciones de su servidor web, deje vacío por defecto. Dependiendo de su formato, la cadena debe estar entre comillas.<br/>Si se utiliza con nginx, se verá como <i>log_format</i> <i>frx_custom</i> <i> {CONFIGURED_VALUE}</i>.<br/>Si se utiliza con Apache, se verá como <i>LogFormat {CONFIGURED_VALUE} frx_custom</i>.<br/><strong>Atención</strong>: No se comprobará si el código contiene errores. Si contiene errores, ¡el servidor web podría no volver a arrancar!'\n\t\t],\n\t\t'logfiles_type' => [\n\t\t\t'title' => 'Tipo de registro de acceso',\n\t\t\t'description' => 'Elija aquí entre <strong>combinado</strong> o <strong>vhost_combinado</strong>.'\n\t\t],\n\t\t'logfiles_piped' => [\n\t\t\t'title' => 'Canalizar los archivos de registro del servidor web al script especificado (ver arriba)',\n\t\t\t'description' => 'Si utiliza un script personalizado para los archivos de registro, deberá activarlo para que se ejecute.'\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'Dirección IP',\n\t\t\t'description' => '¿Cuál es la dirección IP principal de este servidor?'\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Nombre de host',\n\t\t\t'description' => '¿Cuál es el nombre de host de este servidor?'\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Comando de recarga del servidor web',\n\t\t\t'description' => '¿Cuál es el comando del servidor web para recargar los archivos de configuración?'\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => 'Habilitar servidor de nombres',\n\t\t\t'description' => 'Aquí se puede habilitar y deshabilitar globalmente el servidor de nombres.'\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Directorio de configuración del servidor DNS',\n\t\t\t'description' => '¿Dónde deben guardarse los archivos de configuración del servidor DNS?'\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Comando de recarga del servidor DNS',\n\t\t\t'description' => '¿Cuál es el comando para recargar el demonio del servidor DNS?'\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'Mails-UID',\n\t\t\t'description' => '¿Qué UserID deben tener los correos?'\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mails-GID',\n\t\t\t'description' => '¿Qué GroupID debe tener Mails?'\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Mails-homedir',\n\t\t\t'description' => '¿Dónde deberían almacenarse todos los correos?'\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Remitente',\n\t\t\t'description' => '¿Cuál es la dirección del remitente para los emails enviados desde el Panel?'\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'URL de phpMyAdmin',\n\t\t\t'description' => '¿Cuál es la URL de phpMyAdmin? (debe empezar por http(s)://)'\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'URL de Webmail',\n\t\t\t'description' => '¿Cuál es la URL de webmail? (debe empezar por http(s)://)'\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'URL de WebFTP',\n\t\t\t'description' => '¿Cuál es la URL de WebFTP? (debe empezar por http(s)://)'\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => '¿Cuál es el lenguaje estándar de su servidor?'\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Número máximo de intentos de inicio de sesión',\n\t\t\t'description' => 'Número máximo de intentos de inicio de sesión tras los cuales se desactiva la cuenta.'\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Tiempo de desactivación',\n\t\t\t'description' => 'Tiempo (seg.) que se desactiva una cuenta después de demasiados intentos de inicio de sesión.'\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Tipo de entrada de ruta',\n\t\t\t'description' => '¿Debe seleccionarse una ruta mediante un menú desplegable o mediante un campo de entrada?'\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Servidores de nombres',\n\t\t\t'description' => 'Una lista separada por comas que contiene los nombres de host de todos los servidores de nombres. El primero será el principal.'\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'Servidores MX',\n\t\t\t'description' => 'Una lista separada por comas que contiene un par de un número y un nombre de host separados por espacios en blanco (por ejemplo, \\'10 mx.ejemplo.com\\') que contiene los servidores mx.'\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Entradas por página',\n\t\t\t'description' => '¿Cuántas entradas se mostrarán en una página? (0 = desactivar la paginación)'\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'IP/Puerto por defecto',\n\t\t\t'description' => 'Seleccione todas las direcciones IP que desee utilizar como predeterminadas para los nuevos dominios.'\n\t\t],\n\t\t'defaultsslip' => [\n\t\t\t'title' => 'IP/Puerto SSL por defecto',\n\t\t\t'description' => 'Seleccione todas las direcciones IP con ssl habilitado que desee utilizar por defecto para los nuevos dominios'\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Rutas a añadir a OpenBasedir',\n\t\t\t'description' => 'Estas rutas (separadas por dos puntos) se añadirán a la declaración OpenBasedir en cada contenedor vHost.'\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Usar ordenación humana natural en la vista de lista',\n\t\t\t'description' => 'Ordena las listas como web1 -> web2 -> web11 en lugar de web1 -> web11 -> web2.'\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Docroot para usuarios desactivados',\n\t\t\t'description' => 'Cuando un usuario es desactivado esta ruta es usada como su docroot. Dejar vacío para no crear ningún vHost.'\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Guardar también las contraseñas de las cuentas de correo sin cifrar en la base de datos',\n\t\t\t'description' => 'Si esta opción está activada, todas las contraseñas serán guardadas sin encriptar (texto claro, legible para cualquiera con acceso a la base de datos) en la tabla mail_users. Active esta opción sólo si desea utilizar SASL.'\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'Cuentas FTP @dominio',\n\t\t\t'description' => 'Los clientes pueden crear cuentas FTP user@customerdomain?'\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Activar FCGID',\n\t\t\t'description' => 'Use esto para ejecutar PHP con la cuenta de usuario correspondiente.<br/><br/><b>Esto necesita una configuración especial del servidor web para Apache, vea <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - manual</a></b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Directorio de configuración',\n\t\t\t\t'description' => '¿Dónde deben guardarse todos los archivos de configuración fcgid? Si no utiliza un binario suexec autocompilado, que es la situación normal, esta ruta debe estar bajo /var/www/<br/><br/><div class=\"text-danger\">NOTA: El contenido de esta carpeta se borra regularmente, así que evite almacenar datos allí manualmente.</div>'\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Directorio temporal',\n\t\t\t\t'description' => 'Dónde deben almacenarse los directorios temporales'\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Procesos por dominio',\n\t\t\t\t'description' => '¿Cuántos procesos deberían iniciarse/permitirse por dominio? Se recomienda el valor 0 porque PHP gestionará entonces la cantidad de procesos por sí mismo de forma muy eficiente.'\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper en Vhosts',\n\t\t\t\t'description' => 'Cómo debe incluirse el wrapper en los Vhosts'\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Directorios globales de PEAR',\n\t\t\t\t'description' => '¿Qué directorios globales de PEAR deben ser reemplazados en cada configuración php.ini? Los diferentes directorios deben estar separados por dos puntos.'\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Número máximo de peticiones por dominio',\n\t\t\t\t'description' => '¿Cuántas peticiones deben permitirse por dominio?'\n\t\t\t],\n\t\t\t'defaultini' => 'Configuración PHP por defecto para nuevos dominios',\n\t\t\t'defaultini_ownvhost' => 'Configuración PHP por defecto para froxlor-vHost',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Tiempo de espera',\n\t\t\t\t'description' => 'Configuración de tiempo de espera para Mod FastCGI.'\n\t\t\t]\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Utilizar una dirección de correo electrónico alternativa',\n\t\t\t'description' => 'Enviar la contraseña a una dirección diferente durante la creación de la cuenta de correo electrónico.'\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Archivo/nombre de directorio de configuración del servidor web vHost',\n\t\t\t'description' => '¿Dónde debe guardarse la configuración del vHost? Puede especificar aquí un archivo (todos los vHosts en un archivo) o un directorio (cada vHost en su propio archivo).'\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Archivo/nombre de directorio de configuración de diropciones del servidor web',\n\t\t\t'description' => '¿Dónde debe almacenarse la configuración de diroptions? Puede especificar un archivo (todas las diropciones en un archivo) o directorio (cada diropción en su propio archivo) aquí.'\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Webserver htpasswd dirname',\n\t\t\t'description' => '¿Dónde deben guardarse los archivos htpasswd para la protección de directorios?'\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'MySQL-Access-Hosts',\n\t\t\t'description' => 'Una lista separada por comas de los hosts desde los que se debe permitir a los usuarios conectarse al servidor MySQL. Para permitir una subred es válida la sintaxis netmask o cidr.'\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Salida de Webalizer',\n\t\t\t'description' => 'Verbosidad del programa webalizer'\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Registro activado/desactivado',\n\t\t\t'severity' => 'Nivel de registro',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Tipo(s) de registro',\n\t\t\t\t'description' => 'Especifique los tipos de registro. Para seleccionar varios tipos, mantenga pulsada la tecla CTRL mientras selecciona.<br/>Los tipos de registro disponibles son: syslog, file, mysql'\n\t\t\t],\n\t\t\t'logfile' => [\n\t\t\t\t'title' => 'Nombre del archivo de registro',\n\t\t\t\t'description' => 'Sólo se utiliza si log-type incluye \"file\". Este archivo se creará en froxlor/logs/. Esta carpeta está protegida contra el acceso público.'\n\t\t\t],\n\t\t\t'logcron' => 'Log cronjobs',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Nunca',\n\t\t\t\t'once' => 'Una vez',\n\t\t\t\t'always' => 'Siempre'\n\t\t\t]\n\t\t],\n\t\t'ssl' => [\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => 'Activar uso de SSL',\n\t\t\t\t'description' => 'Marque esta opción si desea utilizar SSL para su servidor web'\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'Ruta al certificado SSL',\n\t\t\t\t'description' => 'Especifique la ruta incluyendo el nombre del archivo .crt o .pem (certificado principal)'\n\t\t\t],\n\t\t\t'openssl_cnf' => 'Valores predeterminados para crear el archivo Cert',\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'Ruta al archivo de claves SSL',\n\t\t\t\t'description' => 'Especifique la ruta incluyendo el nombre de archivo para el archivo de clave privada (.key principalmente)'\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'Ruta al certificado SSL CA (opcional)',\n\t\t\t\t'description' => 'Autenticación del cliente, configure esto sólo si sabe lo que es.'\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => 'Configure los cifrados SSL permitidos',\n\t\t\t\t'description' => 'Esta es una lista de cifradores que quiere (o no quiere) usar cuando hable SSL. Para una lista de cifradores y cómo incluirlos/excluirlos, vea las secciones \"CIPHER LIST FORMAT\" y \"CIPHER STRINGS\" en <a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">la página man de cifradores</a>.<br/><br/><b>El valor por defecto es:</b><pre>ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305</pre>'\n\t\t\t],\n\t\t\t'apache24_ocsp_cache_path' => [\n\t\t\t\t'title' => 'Apache 2.4: ruta a la caché de grapado OCSP',\n\t\t\t\t'description' => 'Configura la caché utilizada para almacenar las respuestas OCSP que se incluyen en los handshakes TLS.'\n\t\t\t],\n\t\t\t'ssl_protocols' => [\n\t\t\t\t'title' => 'Configurar la versión del protocolo TLS',\n\t\t\t\t'description' => 'Esta es una lista de protocolos ssl que quiere (o no quiere) usar cuando use SSL. <b>Nota:</b> Es posible que algunos navegadores antiguos no admitan las versiones de protocolo más recientes.<br/><br/><b>El valor predeterminado es:</b><pre>TLSv1.2</pre>'\n\t\t\t],\n\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t'title' => 'Configurar cifrados explícitos TLSv1.3 si se utilizan',\n\t\t\t\t'description' => 'Esta es una lista de cifradores que desea (o no desea) utilizar cuando hable TLSv1.3. Para una lista de cifradores y como incluirlos/excluirlos, vea <a href=\"https://wiki.openssl.org/index.php/TLS1.3\">los documentos para TLSv1.3</a>.<br/><br/><b>El valor por defecto es vacío</b>'\n\t\t\t]\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Configuración vHost por defecto',\n\t\t\t'description' => 'El contenido de este campo se incluirá en este contenedor ip/port vHost directamente. Puede utilizar las siguientes variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (si procede)<br/> Atención: No se comprobará si el código contiene errores. Si contiene errores, ¡el servidor web podría no volver a arrancar!'\n\t\t],\n\t\t'apache_globaldiropt' => [\n\t\t\t'title' => 'Opciones de directorio para customer-prefix',\n\t\t\t'description' => 'El contenido de este campo se incluirá en la configuración de apache 05_froxlor_dirfix_nofcgid.conf. Si está vacío, se usará el valor por defecto:<br/><br/>apache >=2.4<br/><code>Require all granted<br/>AllowOverride All</code><br/><br/>apache <=2.2<br/><code>Order allow,deny<br/>allow from all</code>'\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => 'El contenido de este campo se incluirá directamente en el contenedor vHost del dominio. Puede utilizar las siguientes variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (si procede)<br/> Atención: No se comprobará si el código contiene errores. Si contiene errores, ¡el servidor web podría no volver a arrancar!'\n\t\t],\n\t\t'decimal_places' => 'Número de decimales en la salida de tráfico/espacio web',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Configuración dns del dominio del cliente'\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Permitir a los clientes editar la configuración dns del dominio'\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Utilizar nombres de usuario compatibles con UNIX',\n\t\t\t'description' => 'Permite utilizar <strong>-</strong> y <strong>_</strong> en los nombres de usuario si <strong>No</strong>'\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Permitir que los clientes restablezcan la contraseña',\n\t\t\t'description' => 'Los clientes pueden restablecer su contraseña y se les enviará un enlace de activación a su dirección de correo electrónico.'\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Permitir que los administradores restablezcan la contraseña',\n\t\t\t'description' => 'Los administradores/revendedores pueden restablecer su contraseña y se les enviará un enlace de activación a su dirección de correo electrónico.'\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Cuota de buzón',\n\t\t\t'description' => 'La cuota por defecto para los nuevos buzones creados (MegaByte).'\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Usar mailbox-quota para clientes',\n\t\t\t'description' => 'Activar para utilizar cuotas en los buzones de correo. Por defecto es <b>No</b> ya que esto requiere una configuración especial.',\n\t\t\t'removelink' => 'Haga clic aquí para borrar todas las cuotas de las cuentas de correo.',\n\t\t\t'enforcelink' => 'Haga clic aquí para aplicar la cuota por defecto a todas las cuentas de correo de los usuarios.'\n\t\t],\n\t\t'index_file_extension' => [\n\t\t\t'description' => '¿Qué extensión de archivo debe utilizarse para el archivo de índice en los directorios de clientes recién creados? Esta extensión de archivo se utilizará si usted o uno de sus administradores ha creado su propia plantilla de archivo de índice.',\n\t\t\t'title' => 'Extensión de archivo para el archivo de índice en directorios de clientes recién creados'\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Permitir inicio de sesión múltiple',\n\t\t\t'description' => 'Si se activa, un usuario puede iniciar sesión varias veces.'\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Permitir mover dominios entre administradores',\n\t\t\t'description' => 'Si está activado puede cambiar el admin de un dominio en domainsettings.<br/><b>Atención:</b> Si un cliente no está asignado al mismo administrador que el dominio, ¡el administrador puede ver todos los demás dominios de ese cliente!'\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Permitir mover dominios entre clientes',\n\t\t\t'description' => 'Si está activado puedes cambiar el cliente de un dominio en domainsettings.<br/><b>Atención:</b> froxlor cambia el documentroot al homedir por defecto del nuevo cliente (+ carpeta de dominio si está activado)'\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'En caso afirmativo, estos ajustes personalizados de vHost se añadirán a todos los subdominios; en caso negativo, se eliminarán los ajustes especiales de subdominio.'\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Longitud mínima de contraseña',\n\t\t\t'description' => 'Aquí puede establecer una longitud mínima para las contraseñas. 0\\' significa que no se requiere longitud mínima.'\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Almacenar el fichero índice por defecto también en las nuevas subcarpetas',\n\t\t\t'description' => 'Si se activa, el archivo de índice por defecto se almacena en cada ruta de subdominio recién creada (no si la carpeta ya existe).'\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Dirección de respuesta',\n\t\t\t'description' => 'Defina una dirección de email como dirección de respuesta para los emails enviados por el panel.'\n\t\t],\n\t\t'adminmail_defname' => 'Nombre del remitente del e-mail del panel',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Subdominio estándar del cliente',\n\t\t\t'description' => 'Qué nombre de host debe usarse para crear subdominios estándar para el cliente. Si está vacío, se utiliza el nombre de host del sistema.'\n\t\t],\n\t\t'awstats_path' => 'Ruta a AWStats \\'awstats_buildstaticpages.pl\\'',\n\t\t'awstats_conf' => 'Ruta de configuración de AWStats',\n\t\t'defaultttl' => 'TTL de dominio para bind en segundos (por defecto \\'604800\\' = 1 semana)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Habilitar documentos de error por defecto para todos los clientes',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'Archivo/URL para error 401',\n\t\t\t'description' => ''\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'Archivo/URL para error 403',\n\t\t\t'description' => ''\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'Archivo/URL para error 404',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'Archivo/URL para error 500',\n\t\t\t'description' => ''\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'Si se selecciona pureftpd, los archivos .ftpquota para cuotas de usuario se crean y actualizan diariamente'\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Permitir redireccionamientos de clientes',\n\t\t\t'description' => 'Permitir a los clientes elegir el código http-status para las redirecciones que se utilizarán'\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Redirección por defecto',\n\t\t\t'description' => 'Establece el código de redirección por defecto que se utilizará si el cliente no lo establece por sí mismo'\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Crear mail-, imap-, pop3- y smtp-\"A record\" también con MX-Servers set',\n\t\t'froxlordirectlyviahostname' => 'Acceder a froxlor directamente a través del nombre de host',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Expresión regular para contraseñas',\n\t\t\t'description' => 'Aquí puede establecer una expresión regular para la complejidad de las contraseñas.<br/>Empty = no specific requirement'\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'Habilitar FCGID para el vHost de froxlor',\n\t\t\t'description' => 'Si está habilitado, froxlor también se ejecutará bajo un usuario local'\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'Habilitar solución SuExec',\n\t\t\t\t'description' => 'Habilitar sólo si los directorios del cliente no están dentro de la ruta apache suexec.<br/>Si está habilitado, froxlor generará un enlace simbólico desde el directorio del cliente habilitado para perl + /cgi-bin/ a la ruta dada.<br/>Tenga en cuenta que perl sólo funcionará en el subdirectorio de carpetas /cgi-bin/ y no en la carpeta en sí (¡como lo hace sin esta solución!)'\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Ruta para los enlaces simbólicos de directorio habilitados para perl del cliente',\n\t\t\t\t'description' => 'Sólo necesita configurar esto si la solución de SuExec está activada.<br/>ATENCIÓN: Asegúrese de que esta ruta está dentro de la ruta de suexec o de lo contrario esta solución es inútil.'\n\t\t\t]\n\t\t],\n\t\t'awstats_awstatspath' => 'Ruta a AWStats \\'awstats.pl\\'.',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'Ruta a la carpeta de iconos de AWstats',\n\t\t\t'description' => 'e.g. /usr/share/awstats/htdocs/icon/'\n\t\t],\n\t\t'login_domain_login' => 'Permitir login con dominios',\n\t\t'perl_server' => [\n\t\t\t'title' => 'Ubicación del socket del servidor Perl',\n\t\t\t'description' => 'Puede encontrar una guía sencilla en: <a target=\"blank\" href=\"https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/\">nginx.com</a>'\n\t\t],\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP backend',\n\t\t\t'description' => 'aquí es donde el proceso PHP está escuchando peticiones de nginx, puede ser un socket unix de combinación ip:port<br/>*NO se usa con php-fpm'\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'Comando PHP reload',\n\t\t\t'description' => 'se usa para recargar el backend PHP si se usa alguno<br/>Por defecto: en blanco<br/>*NO se usa con php-fpm'\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => 'Habilitar php-fpm',\n\t\t\t'description' => '<b>Esto necesita una configuración especial del servidor web ver <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">manual PHP-FPM</a></b>'\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'Directorio de configuración de php-fpm',\n\t\t\t'aliasconfigdir' => 'Directorio Alias de configuración de php-fpm',\n\t\t\t'reload' => 'Comando de reinicio de php-fpm',\n\t\t\t'pm' => 'Control del gestor de procesos (pm)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'Número de procesos hijo',\n\t\t\t\t'description' => 'El número de procesos hijo a ser creados cuando pm está en \\'static\\' y el número máximo de procesos hijo a ser creados cuando pm está en \\'dynamic/ondemand\\'<br/>Equivalente a PHP_FCGI_CHILDREN'\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'El número de procesos hijo creados al inicio',\n\t\t\t\t'description' => 'Nota: Sólo se usa cuando pm está configurado como \\'dynamic'\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'El número mínimo deseado de procesos de servidor inactivos',\n\t\t\t\t'description' => 'Nota: Sólo se usa cuando pm es \\'dynamic\\'<br/>Nota: Obligatorio cuando pm es \\'dynamic'\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'El número máximo deseado de procesos de servidor inactivos',\n\t\t\t\t'description' => 'Nota: sólo se utiliza cuando pm tiene el valor \"dynamic\".<br/>Nota: obligatorio cuando pm tiene el valor \"dynamic\".'\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Peticiones por hijo antes de respawning',\n\t\t\t\t'description' => 'Para un procesamiento infinito de peticiones especifique \\'0\\'. Equivalente a PHP_FCGI_MAX_REQUESTS.'\n\t\t\t],\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Tiempo de espera',\n\t\t\t\t'description' => 'Configuración de tiempo de espera para PHP FPM FastCGI.'\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'Directorio IPC FastCGI',\n\t\t\t\t'description' => 'El directorio donde los sockets php-fpm serán almacenados por el servidor web.<br/>Este directorio debe ser legible para el servidor web.'\n\t\t\t],\n\t\t\t'limit_extensions' => [\n\t\t\t\t'title' => 'Extensiones permitidas',\n\t\t\t\t'description' => 'Limita las extensiones del script principal que FPM permitirá analizar. Esto puede evitar errores de configuración en el servidor web. Sólo debe limitar FPM a extensiones .php para evitar que usuarios maliciosos utilicen otras extensiones para ejecutar código php. Valor por defecto: .php'\n\t\t\t],\n\t\t\t'envpath' => 'Rutas a añadir al entorno PATH. Dejar vacío para ninguna variable de entorno PATH',\n\t\t\t'override_fpmconfig' => 'Anular la configuración de FPM-daemon (pm, max_children, etc.)',\n\t\t\t'override_fpmconfig_addinfo' => '<br/><span class=\"text-danger\">Sólo se usa si \"Override FPM-daemon settings\" está en \"Yes\"</span>',\n\t\t\t'restart_note' => 'Atención: La configuración no será revisada por errores. Si contiene errores, PHP-FPM podría no arrancar de nuevo.',\n\t\t\t'custom_config' => [\n\t\t\t\t'title' => 'Configuración personalizada',\n\t\t\t\t'description' => 'Agregue configuración personalizada a cada instancia de versión de PHP-FPM, por ejemplo <i>pm.status_path = /status</i> para monitoreo. Las variables de abajo pueden ser usadas aquí. <strong>Atención: La configuración no será revisada por errores. Si contiene errores, ¡PHP-FPM podría no arrancar de nuevo!</strong>'\n\t\t\t],\n\t\t\t'allow_all_customers' => [\n\t\t\t\t'title' => 'Asigne esta configuración a todos los clientes existentes',\n\t\t\t\t'description' => 'Establézcalo en \"true\" si desea asignar esta configuración a todos los clientes existentes para que puedan utilizarla. Esta configuración no es permanente, pero puede ejecutarse varias veces.'\n\t\t\t]\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => 'Habilitar el envío de informes sobre el uso de la web y el tráfico',\n\t\t\t'webmax' => [\n\t\t\t\t'title' => 'Nivel de advertencia en porcentaje para el espacio web',\n\t\t\t\t'description' => 'Los valores válidos son 0 hasta 150. Establecer este valor a 0 desactiva este informe.'\n\t\t\t],\n\t\t\t'trafficmax' => [\n\t\t\t\t'title' => 'Nivel de advertencia en porcentaje para el tráfico',\n\t\t\t\t'description' => 'Los valores válidos son de 0 a 150. El valor 0 desactiva este informe.'\n\t\t\t]\n\t\t],\n\t\t'dropdown' => 'Desplegable',\n\t\t'manual' => 'Manual',\n\t\t'default_theme' => 'Tema por defecto',\n\t\t'validate_domain' => 'Validar nombres de dominio',\n\t\t'diskquota_enabled' => '¿Cuota activada?',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'Ruta a repquota'\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'Ruta a quotatool'\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => 'Partición en la que se almacenan los archivos del cliente'\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'Nombre del maildir',\n\t\t\t'description' => 'Directorio maildir en la cuenta del usuario. Normalmente \\'Maildir\\', en algunas implementaciones \\'.maildir\\', y directamente en el directorio del usuario si se deja en blanco.'\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => 'Utilizar Catchall',\n\t\t\t'description' => '¿Quiere proporcionar a sus clientes la función catchall?'\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => 'Utilice las modificaciones para Apache 2.4',\n\t\t\t'description' => '<strong class=\"text-danger\">ATENCIÓN:</strong> utilícelo sólo si tiene instalada la versión 2.4 o superior de apache<br/>de lo contrario su servidor web no podrá arrancar'\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'Ruta al archivo fastcgi_params',\n\t\t\t'description' => 'Especifique la ruta al archivo fastcgi_params de nginx incluyendo el nombre de archivo'\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => 'Usar el nombre de dominio como valor por defecto para la ruta DocumentRoot',\n\t\t\t'description' => 'Si está habilitado y la ruta DocumentRoot está vacía, el valor por defecto será el (sub)nombre de dominio.<br/><br/>Ejemplos: <br/>/var/clientes/nombre_cliente/ejemplo.com/<br/>/var/clientes/nombre_cliente/subdominio.ejemplo.com/'\n\t\t],\n\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t'title' => 'Ocultar subdominios en la vista general de configuración PHP',\n\t\t\t'description' => 'Si se activa, los subdominios de los clientes no se mostrarán en la vista general de configuraciones PHP, sólo se mostrará el número de subdominios.<br/><br/>Nota: Esto sólo es visible si ha activado FCGID o PHP-FPM.'\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => 'Ocultar subdominios estándar en la vista general de configuraciones PHP',\n\t\t\t'description' => 'Si se activa, los subdominios estándar de los clientes no se mostrarán en la vista general de configuraciones php<br/><br/>Nota: Esto sólo es visible si ha activado FCGID o PHP-FPM.'\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => 'Elija el método de cifrado de contraseñas a utilizar'\n\t\t],\n\t\t'systemdefault' => 'Sistema por defecto',\n\t\t'panel_allow_theme_change_admin' => 'Permitir a los administradores cambiar el tema',\n\t\t'panel_allow_theme_change_customer' => 'Permitir a los clientes cambiar el tema',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'Servidores AXFR',\n\t\t\t'description' => 'Una lista separada por comas de direcciones IP permitidas para transferir (AXFR) zonas dns.'\n\t\t],\n\t\t'powerdns_mode' => [\n\t\t\t'title' => 'Modo de funcionamiento PowerDNS',\n\t\t\t'description' => 'Seleccione el modo PoweDNS: Nativo para no replicación (Predeterminado) / Maestro si se necesita replicación DNS.'\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Webserver customer-ssl certificates-directory',\n\t\t\t'description' => '¿Dónde deben crearse los certificados ssl especificados por el cliente?<br/><br/><div class=\"text-danger\">NOTA: El contenido de esta carpeta se borra con regularidad, así que evite almacenar datos allí manualmente.</div>'\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => 'Permitir a los administradores/revendedores informar de errores en la base de datos a froxlor',\n\t\t\t'description' => 'Nota: ¡Nunca nos envíes datos personales (de clientes)!'\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => 'Permitir a los clientes informar de errores en la base de datos a froxlor',\n\t\t\t'description' => 'Nota: ¡No nos envíe nunca datos personales (de clientes)!'\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => 'Analizar el tráfico de correo',\n\t\t\t'description' => 'Permitir el análisis de los registros del servidor de correo para calcular el tráfico'\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'Tipo de MDA',\n\t\t\t'description' => 'Tipo de servidor de entrega de correo'\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'Registro MDA',\n\t\t\t'description' => 'Archivo de registro del Mail Delivery Server'\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'Tipo de MTA',\n\t\t\t'description' => 'Tipo de agente de transferencia de correo'\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'Registro MTA',\n\t\t\t'description' => 'Archivo de registro del Agente de Transferencia de Correo'\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'Archivo de configuración de cron',\n\t\t\t'description' => 'Ruta al archivo de configuración del servicio cron. Este archivo será actualizado regular y automáticamente por froxlor.<br/>Nota: ¡Por favor <b>asegúrese</b> de usar el mismo nombre de archivo que para el froxlor cronjob principal (por defecto: /etc/cron.d/froxlor)!<br/><br/>¡Si está usando <b>FreeBSD</b>, por favor especifique <i>/etc/crontab</i> aquí!'\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Comando Cron-daemon reload',\n\t\t\t'description' => 'Especifique el comando a ejecutar para recargar el cron-daemon de su sistema'\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Comando de ejecución de cron (php-binario)',\n\t\t\t'description' => 'Comando para ejecutar nuestros cronjobs. Cámbielo sólo si sabe lo que está haciendo (por defecto: \"/usr/bin/nice -n 5 /usr/bin/php -q\").'\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => 'Permitir la actualización automática de la base de datos',\n\t\t\t'description' => '<div class=\"text-danger\"><b>ATENCIÓN:</b></div> Esta configuración permite al cronjob saltarse la comprobación de versión de los archivos froxlors y la base de datos y ejecuta la actualización de la base de datos en caso de que ocurra un desajuste de versión.<br/><br/><div class=\"text-danger\">Auto-update siempre establecerá valores por defecto para nuevas configuraciones o cambios. Esto puede no ser siempre adecuado para su sistema. Por favor, piénselo dos veces antes de activar esta opción</div>'\n\t\t],\n\t\t'dns_createhostnameentry' => 'Crear bind-zone/config para el nombre de host del sistema',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => 'Carácter en minúsculas',\n\t\t\t'description' => 'La contraseña debe contener al menos una letra minúscula (a-z).'\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => 'Mayúsculas',\n\t\t\t'description' => 'La contraseña debe contener al menos una letra mayúscula (A-Z).'\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => 'Números',\n\t\t\t'description' => 'La contraseña debe contener al menos un número (0-9).'\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => 'Carácter especial',\n\t\t\t'description' => 'La contraseña debe contener al menos uno de los caracteres definidos a continuación.'\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => 'Lista de caracteres especiales',\n\t\t\t'description' => 'Se requiere uno de estos caracteres si se establece la opción anterior.'\n\t\t],\n\t\t'apache_itksupport' => [\n\t\t\t'title' => 'Modificaciones de uso para Apache ITK-MPM',\n\t\t\t'description' => '<strong class=\"text-danger\">ATENCIÓN:</strong> utilícela sólo si tiene apache itk-mpm habilitado<br/>, de lo contrario su servidor web no podrá iniciarse.'\n\t\t],\n\t\t'letsencryptca' => [\n\t\t\t'title' => 'Entorno ACME',\n\t\t\t'description' => 'Entorno que se utilizará para los certificados Let\\'s Encrypt / ZeroSSL.'\n\t\t],\n\t\t'letsencryptchallengepath' => [\n\t\t\t'title' => 'Ruta para los desafíos Let\\'s Encrypt',\n\t\t\t'description' => 'Directorio desde el que se ofrecerán los retos Let\\'s Encrypt a través de un alias global.'\n\t\t],\n\t\t'letsencryptkeysize' => [\n\t\t\t'title' => 'Tamaño de la clave para nuevos certificados Let\\'s Encrypt',\n\t\t\t'description' => 'Tamaño de la clave en Bits para nuevos certificados Let\\'s Encrypt.'\n\t\t],\n\t\t'letsencryptreuseold' => [\n\t\t\t'title' => 'Reutilizar clave Let\\'s Encrypt',\n\t\t\t'description' => 'Si se activa, se utilizará la misma clave para cada renovación, de lo contrario se generará una nueva clave cada vez.'\n\t\t],\n\t\t'leenabled' => [\n\t\t\t'title' => 'Activar Let\\'s Encrypt',\n\t\t\t'description' => 'Si se activa, los clientes pueden dejar que froxlor automáticamente genere y renueve certificados ssl Let\\'s Encrypt para dominios con una IP/puerto ssl.<br/><br/>Por favor recuerda que necesitas ir a través de la configuración del servidor web cuando se activa porque esta característica necesita una configuración especial.'\n\t\t],\n\t\t'caa_entry' => [\n\t\t\t'title' => 'Generar registros DNS CAA',\n\t\t\t'description' => 'Genera automáticamente registros CAA para dominios habilitados para SSL que utilizan Let\\'s Encrypt.'\n\t\t],\n\t\t'caa_entry_custom' => [\n\t\t\t'title' => 'Registros DNS CAA adicionales',\n\t\t\t'description' => 'DNS Certification Authority Authorization (CAA) es un mecanismo de política de seguridad en Internet que permite a los titulares de nombres de dominio indicar a las autoridades de certificación<br/>si están autorizadas a emitir certificados digitales para un nombre de dominio concreto. Lo hace mediante un nuevo registro de recursos del Sistema de Nombres de Dominio (DNS) \"CAA\".<br/><br/>El contenido de este campo se incluirá en la zona DNS directamente (cada línea da lugar a un registro CAA).<br/>Si Let\\'s Encrypt está habilitado para este dominio, esta entrada siempre se añadirá automáticamente y no es necesario añadirla manualmente:<br/> 0<code>issue \"letsencrypt.org\"</code> (Si el dominio es un dominio comodín, se utilizará issuewild en su lugar).<br/>Para habilitar el informe de incidentes, puede añadir un registro <code>iodef</code>. Un ejemplo para enviar dicho informe a <code>me@example.com</code> sería:<br/> 0<code>iodef \"mailto:me@example.com\"</code><br/><br/><strong>Atención:</strong> No se comprobará si el código contiene errores. Si contiene errores, ¡es posible que sus registros CAA no funcionen!'\n\t\t],\n\t\t'dnseditorenable' => [\n\t\t\t'title' => 'Habilitar editor DNS',\n\t\t\t'description' => 'Permite a los administradores y a los clientes gestionar las entradas DNS del dominio'\n\t\t],\n\t\t'dns_server' => [\n\t\t\t'title' => 'Demonio del servidor DNS',\n\t\t\t'description' => 'Recuerde que los daemons tienen que ser configurados usando las plantillas de configuración de froxlors'\n\t\t],\n\t\t'panel_customer_hide_options' => [\n\t\t\t'title' => 'Ocultar elementos de menú y gráficos de tráfico en el panel de cliente',\n\t\t\t'description' => 'Seleccione los elementos que desea ocultar en el panel de cliente. Para seleccionar varias opciones, mantenga pulsada la tecla CTRL mientras selecciona.'\n\t\t],\n\t\t'allow_allow_customer_shell' => [\n\t\t\t'title' => 'Permitir a los clientes habilitar el acceso shell para usuarios ftp',\n\t\t\t'description' => '<strong class=\"text-danger\">Atención: El acceso Shell permite al usuario ejecutar varios binarios en su sistema. Utilícelo con extrema precaución. ¡¡¡Por favor, active esto sólo si REALMENTE sabe lo que está haciendo!!!</strong>'\n\t\t],\n\t\t'available_shells' => [\n\t\t\t'title' => 'Lista de shells disponibles',\n\t\t\t'description' => 'Lista separada por comas de los intérpretes de comandos que están disponibles para que el cliente elija para sus usuarios ftp.<br/><br/>Tenga en cuenta que el intérprete de comandos predeterminado <strong>/bin/false</strong> siempre será una opción (si está habilitado), incluso si esta configuración está vacía. Es el valor por defecto para los usuarios ftp en cualquier caso.'\n\t\t],\n\t\t'le_froxlor_enabled' => [\n\t\t\t'title' => 'Activar Let\\'s Encrypt para el froxlor vhost',\n\t\t\t'description' => 'Si se activa, el froxlor vhost se asegurará automáticamente usando un certificado Let\\'s Encrypt.'\n\t\t],\n\t\t'le_froxlor_redirect' => [\n\t\t\t'title' => 'Activar SSL-redirect para froxlor vhost',\n\t\t\t'description' => 'Si se activa, todas las peticiones http a su froxlor serán redirigidas al sitio SSL correspondiente.'\n\t\t],\n\t\t'option_unavailable_websrv' => '<br/><em class=\"text-danger\">Disponible sólo para: %s</em>',\n\t\t'option_unavailable' => '<br/><em class=\"text-danger\">Opción no disponible debido a otros ajustes.</em>',\n\t\t'letsencryptacmeconf' => [\n\t\t\t'title' => 'Ruta al fragmento acme.conf',\n\t\t\t'description' => 'Nombre de archivo del fragmento de configuración que permite al servidor web servir el desafío acme.'\n\t\t],\n\t\t'mail_use_smtp' => 'Configurar mailer para usar SMTP',\n\t\t'mail_smtp_host' => 'Especifique el servidor SMTP',\n\t\t'mail_smtp_usetls' => 'Activar el cifrado TLS',\n\t\t'mail_smtp_auth' => 'Activar la autenticación SMTP',\n\t\t'mail_smtp_port' => 'Puerto TCP al que conectarse',\n\t\t'mail_smtp_user' => 'Nombre de usuario SMTP',\n\t\t'mail_smtp_passwd' => 'Contraseña SMTP',\n\t\t'http2_support' => [\n\t\t\t'title' => 'Soporte HTTP2',\n\t\t\t'description' => 'habilite el soporte HTTP2 para ssl.<br/><em class=\"text-danger\">HABILITAR SÓLO SI SU WEBSERVER SOPORTA ESTA CARACTERÍSTICA (nginx versión 1.9.5+, apache2 versión 2.4.17+)</em>'\n\t\t],\n\t\t'nssextrausers' => [\n\t\t\t'title' => 'Usar libnss-extrausers en lugar de libnss-mysql',\n\t\t\t'description' => 'No leer usuarios de la base de datos sino de ficheros. Por favor, actívelo sólo si ya ha realizado los pasos de configuración necesarios (system -><strong class=\"text-danger\">libnss-extrausers).</strong><br/><strong class=\"text-danger\">Sólo para Debian/Ubuntu (¡o si ha compilado libnss-extrausers usted mismo!)</strong>'\n\t\t],\n\t\t'le_domain_dnscheck' => [\n\t\t\t'title' => 'Validar DNS de dominios al usar Let\\'s Encrypt',\n\t\t\t'description' => 'Si está activado, froxlor validará si el dominio que solicita un certificado Let\\'s Encrypt resuelve al menos a una de las direcciones ip del sistema.'\n\t\t],\n\t\t'le_domain_dnscheck_resolver' => [\n\t\t\t'title' => 'Usar un servidor de nombres externo para validación DNS',\n\t\t\t'description' => 'Si se establece, froxlor usará este DNS para validar los DNS de los dominios cuando use Let\\'s Encrypt. Si está vacío, se usará el resolver DNS por defecto del sistema.'\n\t\t],\n\t\t'phpsettingsforsubdomains' => [\n\t\t\t'description' => 'En caso afirmativo se actualizará el php-config elegido a todos los subdominios'\n\t\t],\n\t\t'leapiversion' => [\n\t\t\t'title' => 'Elegir la implementación ACME de Let\\'s Encrypt',\n\t\t\t'description' => 'Actualmente sólo se soporta la implementación ACME v2 para Let\\'s Encrypt.'\n\t\t],\n\t\t'enable_api' => [\n\t\t\t'title' => 'Habilitar uso de API externa',\n\t\t\t'description' => 'Para utilizar la API froxlor es necesario activar esta opción. Para obtener información más detallada, consulte <a href=\"https://docs.froxlor.org/latest/api-guide/\" target=\"_new\">https://docs.froxlor.org/</a>'\n\t\t],\n\t\t'api_customer_default' => '\"Permitir acceso a la API\" valor por defecto para nuevos clientes',\n\t\t'dhparams_file' => [\n\t\t\t'title' => 'Archivo DHParams (intercambio de claves Diffie-Hellman)',\n\t\t\t'description' => 'Si se especifica aquí un archivo dhparams.pem, se incluirá en la configuración del servidor web. Déjelo vacío para desactivarlo.<br/>Ejemplo: <code>/etc/ssl/webserver/dhparams</code>.pem<br/><br/>Si el archivo no existe, se creará automáticamente con el siguiente comando: <code>openssl dhparam -out /etc/ssl/webserver/dhparams.pem 4096</code>. Se recomienda crear el archivo antes de especificarlo aquí, ya que la creación tarda bastante y bloquea el cronjob.'\n\t\t],\n\t\t'errorlog_level' => [\n\t\t\t'title' => 'Nivel de registro de errores',\n\t\t\t'description' => 'Especifique el nivel de registro de errores. Por defecto es \"warn\" para usuarios apache y \"error\" para usuarios nginx.'\n\t\t],\n\t\t'letsencryptecc' => [\n\t\t\t'title' => 'Emitir certificado ECC / ECDSA',\n\t\t\t'description' => 'Si se establece un tamaño de clave válido, el certificado emitido utilizará ECC / ECDSA.'\n\t\t],\n\t\t'froxloraliases' => [\n\t\t\t'title' => 'Alias de dominio para froxlor vhost',\n\t\t\t'description' => 'Lista separada por comas de dominios para añadir como alias de servidor al froxlor vhost'\n\t\t],\n\t\t'default_sslvhostconf' => [\n\t\t\t'title' => 'SSL por defecto vHost-settings',\n\t\t\t'description' => 'El contenido de este campo se incluirá en este contenedor ip/port vHost directamente. Puede utilizar las siguientes variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (si procede)<br/> Atención: No se comprobará si el código contiene errores. Si contiene errores, ¡el servidor web podría no volver a arrancar!'\n\t\t],\n\t\t'includedefault_sslvhostconf' => 'Incluir configuración de vHost no SSL en SSL-vHost',\n\t\t'apply_specialsettings_default' => [\n\t\t\t'title' => 'Valor por defecto para \"Apply specialsettings to all subdomains (*.example.com)\\' setting when editing a domain'\n\t\t],\n\t\t'apply_phpconfigs_default' => [\n\t\t\t'title' => 'Valor por defecto para \"Aplicar php-config a todos los subdominios:\\' al editar un dominio'\n\t\t],\n\t\t'awstats' => [\n\t\t\t'logformat' => [\n\t\t\t\t'title' => 'Configuración de LogFormat',\n\t\t\t\t'description' => 'Si utilizas un formato de registro personalizado para tu servidor web, necesitas cambiar también el awstats LogFormat.<br/>Por defecto es 1. Para más información consulta la documentación <a target=\"_blank\" href=\"https://awstats.sourceforge.io/docs/awstats_config.html#LogFormat\">aquí</a>.'\n\t\t\t]\n\t\t],\n\t\t'hide_incompatible_settings' => 'Ocultar configuraciones incompatibles',\n\t\t'soaemail' => 'Dirección de correo a usar en registros SOA (por defecto la dirección del remitente de la configuración del panel si está vacía)',\n\t\t'imprint_url' => [\n\t\t\t'title' => 'URL a notas legales / impresión',\n\t\t\t'description' => 'Especifique una URL a su sitio de notas legales / impresión. El enlace será visible en la pantalla de inicio de sesión y en el pie de página una vez iniciada la sesión.'\n\t\t],\n\t\t'terms_url' => [\n\t\t\t'title' => 'URL a las condiciones de uso',\n\t\t\t'description' => 'Especifique una URL a su sitio de condiciones de uso. El enlace será visible en la pantalla de inicio de sesión y en el pie de página al iniciar sesión.'\n\t\t],\n\t\t'privacy_url' => [\n\t\t\t'title' => 'URL a la política de privacidad',\n\t\t\t'description' => 'Especifique una URL a su sitio de política de privacidad / sitio de impresión. El enlace será visible en la pantalla de inicio de sesión y en el pie de página al iniciar sesión.'\n\t\t],\n\t\t'logo_image_header' => [\n\t\t\t'title' => 'Imagen de logotipo (cabecera)',\n\t\t\t'description' => 'Cargue su propia imagen de logotipo para que se muestre en el encabezado después del inicio de sesión (altura recomendada 30px)'\n\t\t],\n\t\t'logo_image_login' => [\n\t\t\t'title' => 'Imagen del logotipo (inicio de sesión)',\n\t\t\t'description' => 'Cargue su propia imagen de logotipo para que se muestre durante el inicio de sesión'\n\t\t],\n\t\t'logo_overridetheme' => [\n\t\t\t'title' => 'Sobrescribe el logo definido en el tema por \"Logo Image\" (Cabecera y Login, ver más abajo)',\n\t\t\t'description' => 'Esta opción debe establecerse como \"true\" si desea utilizar el logotipo que ha cargado; como alternativa, puede seguir utilizando las posibilidades basadas en el tema \"logo_custom.png\" y \"logo_custom_login.png\".'\n\t\t],\n\t\t'logo_overridecustom' => [\n\t\t\t'title' => 'Sobrescribir el logotipo personalizado (logo_custom.png y logo_custom_login.png) definido en el tema por \"Imagen del logotipo\" (Cabecera e Inicio de sesión, ver más abajo).',\n\t\t\t'description' => 'Establezca este valor en \"true\" si desea ignorar los logotipos personalizados específicos del tema para el encabezado y el inicio de sesión y utilizar \"Logo Image\".'\n\t\t],\n\t\t'createstdsubdom_default' => [\n\t\t\t'title' => 'Valor preseleccionado para \"Crear subdominio estándar\" al crear un cliente',\n\t\t\t'description' => ''\n\t\t],\n\t\t'froxlorusergroup' => [\n\t\t\t'title' => 'Grupo de sistema personalizado para todos los usuarios del cliente',\n\t\t\t'description' => 'Se requiere el uso de libnss-extrausers (system-settings) para que esto tenga efecto. Un valor vacío omite la creación o elimina el grupo existente.'\n\t\t],\n\t\t'acmeshpath' => [\n\t\t\t'title' => 'Ruta a acme.sh',\n\t\t\t'description' => 'Establézcalo donde se instala acme.sh, incluyendo el script acme.sh<br/>Por defecto es <b>/root/.acme.sh/acme.sh</b>'\n\t\t],\n\t\t'update_channel' => [\n\t\t\t'title' => 'froxlor actualizar-canal',\n\t\t\t'description' => 'Seleccione el canal de actualización de froxlor. Por defecto es \"estable\"'\n\t\t],\n\t\t'uc_stable' => 'estable',\n\t\t'uc_testing' => 'testing',\n\t\t'traffictool' => [\n\t\t\t'toolselect' => 'Analizador de tráfico',\n\t\t\t'webalizer' => 'Webalizer',\n\t\t\t'awstats' => 'AWStats',\n\t\t\t'goaccess' => 'goaccess'\n\t\t],\n\t\t'requires_reconfiguration' => 'El cambio de esta configuración podría requerir una reconfiguración de los siguientes servicios:<br/><strong>%s</strong>'\n\t],\n\t'spf' => [\n\t\t'use_spf' => '¿Activar SPF para dominios?',\n\t\t'spf_entry' => 'Entrada SPF para todos los dominios'\n\t],\n\t'ssl_certificates' => [\n\t\t'certificate_for' => 'Certificado para',\n\t\t'valid_from' => 'Válido de',\n\t\t'valid_until' => 'Válido hasta',\n\t\t'issuer' => 'Emisor'\n\t],\n\t'success' => [\n\t\t'success' => 'Información',\n\t\t'clickheretocontinue' => 'Haga clic aquí para continuar',\n\t\t'settingssaved' => 'La configuración se ha guardado correctamente.',\n\t\t'rebuildingconfigs' => 'Tareas insertadas con éxito para reconstruir archivos de configuración',\n\t\t'domain_import_successfully' => 'Se han importado correctamente los dominios %s.',\n\t\t'dns_record_added' => 'Registro añadido correctamente',\n\t\t'dns_record_deleted' => 'Registro eliminado correctamente',\n\t\t'testmailsent' => 'Correo de prueba enviado correctamente',\n\t\t'settingsimported' => 'Ajustes importados correctamente',\n\t\t'sent_error_report' => 'Informe de error enviado correctamente. Gracias por su contribución.'\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Tareas cron pendientes',\n\t\t'REBUILD_VHOST' => 'Reconstrucción de la configuración del servidor web',\n\t\t'CREATE_HOME' => 'Añadir nueva %s cliente',\n\t\t'REBUILD_DNS' => 'Reconstrucción de la configuración bind',\n\t\t'CREATE_FTP' => 'Crear directorio para nuevo usuario ftp',\n\t\t'DELETE_CUSTOMER_FILES' => 'Borrar archivos de cliente %s',\n\t\t'noneoutstanding' => 'Actualmente no hay tareas pendientes para froxlor',\n\t\t'CREATE_QUOTA' => 'Establecer cuota en el sistema de archivos',\n\t\t'DELETE_EMAIL_DATA' => 'Borrar datos de e-mail del cliente.',\n\t\t'DELETE_FTP_DATA' => 'Borrar los datos de la cuenta ftp del cliente.',\n\t\t'REBUILD_CRON' => 'Reconstruir el archivo cron.d',\n\t\t'DELETE_DOMAIN_PDNS' => 'Borrar dominio %s de la base de datos PowerDNS',\n\t\t'DELETE_DOMAIN_SSL' => 'Borrar archivos ssl de dominio %s'\n\t],\n\t'terms' => 'Condiciones de uso',\n\t'traffic' => [\n\t\t'month' => 'Mes',\n\t\t'day' => 'Día',\n\t\t'months' => [\n\t\t\t'1' => 'Enero',\n\t\t\t'2' => 'Febrero',\n\t\t\t'3' => 'Marzo',\n\t\t\t'4' => 'Abril',\n\t\t\t'5' => 'Mayo',\n\t\t\t'6' => 'Junio',\n\t\t\t'7' => 'Julio',\n\t\t\t'8' => 'Agosto',\n\t\t\t'9' => 'Septiembre',\n\t\t\t'10' => 'Octubre',\n\t\t\t'11' => 'Noviembre',\n\t\t\t'12' => 'Diciembre',\n\t\t\t'jan' => 'Enero',\n\t\t\t'feb' => 'Febrero',\n\t\t\t'mar' => 'Mar',\n\t\t\t'apr' => 'Abr',\n\t\t\t'may' => 'Mayo',\n\t\t\t'jun' => 'Jun',\n\t\t\t'jul' => 'Jul',\n\t\t\t'aug' => 'Ago',\n\t\t\t'sep' => 'Sep',\n\t\t\t'oct' => 'Oct',\n\t\t\t'nov' => 'Nov',\n\t\t\t'dec' => 'Diciembre',\n\t\t\t'total' => 'Total'\n\t\t],\n\t\t'mb' => 'Tráfico',\n\t\t'sumtotal' => 'Tráfico total',\n\t\t'sumhttp' => 'Tráfico HTTP',\n\t\t'sumftp' => 'Tráfico FTP',\n\t\t'summail' => 'Tráfico de correo',\n\t\t'customer' => 'Cliente',\n\t\t'domain' => 'Dominio',\n\t\t'trafficoverview' => 'Resumen del tráfico',\n\t\t'bycustomers' => 'Tráfico por clientes',\n\t\t'details' => 'Detalles',\n\t\t'http' => 'HTTP',\n\t\t'ftp' => 'FTP',\n\t\t'mail' => 'Correo',\n\t\t'nocustomers' => 'Necesita al menos un cliente para ver los informes de tráfico.',\n\t\t'top5customers' => 'Top 5 clientes',\n\t\t'nodata' => 'No se han encontrado datos para el intervalo dado.',\n\t\t'ranges' => [\n\t\t\t'last24h' => 'últimas 24 horas',\n\t\t\t'last7d' => 'últimos 7 días',\n\t\t\t'last30d' => 'últimos 30 días',\n\t\t\t'cm' => 'Mes actual',\n\t\t\t'last3m' => 'últimos 3 meses',\n\t\t\t'last6m' => 'últimos 6 meses',\n\t\t\t'last12m' => 'últimos 12 meses',\n\t\t\t'cy' => 'Año en curso'\n\t\t],\n\t\t'byrange' => 'Especificado por rango'\n\t],\n\t'translator' => '',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'Se ha instalado una versión más reciente de froxlor pero aún no se ha configurado.<br/>Sólo el administrador puede iniciar sesión y finalizar la actualización.',\n\t\t'update' => 'Actualización de froxlor',\n\t\t'proceed' => 'Proceder',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'Los archivos froxlor han sido actualizados a la versión <strong>%s</strong>. La versión instalada es <strong>%s</strong>.',\n\t\t\t'part_b' => '<br/><br/>Los clientes no podrán conectarse hasta que la actualización haya finalizado.<br/><strong>¿Proceder?</strong>'\n\t\t],\n\t\t'noupdatesavail' => 'Ya tiene instalada la última %sversion de froxlor.',\n\t\t'description' => 'Ejecutando actualizaciones de la base de datos para su instalación de froxlor',\n\t\t'uc_newinfo' => 'Hay un %sversion más reciente disponible: \"%s\" (Su versión actual es: %s)',\n\t\t'notify_subject' => 'Nueva actualización disponible'\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => 'Notas personalizadas',\n\t\t\t'description' => 'Siéntete libre de poner las notas que quieras/necesites aquí. Se mostrarán en la vista general del administrador/cliente para el usuario correspondiente.',\n\t\t\t'show' => 'Mostrar tus notas en el panel de control del usuario'\n\t\t],\n\t\t'api_allowed' => [\n\t\t\t'title' => 'Permitir el acceso a la API',\n\t\t\t'description' => 'Cuando está habilitado en la configuración, este usuario puede crear claves API y acceder a la API froxlor',\n\t\t\t'notice' => 'El acceso a la API no está permitido para su cuenta.'\n\t\t]\n\t],\n\t'install' => [\n\t\t'slogan' => 'Panel de gestión del servidor froxlor',\n\t\t'preflight' => 'Comprobación del sistema',\n\t\t'critical_error' => 'Error crítico',\n\t\t'suggestions' => 'No requerido pero recomendado',\n\t\t'phpinfosuccess' => 'Su sistema funciona con PHP %s',\n\t\t'phpinfowarn' => 'Su sistema está ejecutando una versión inferior a PHP %s',\n\t\t'phpinfoupdate' => 'Actualice su versión PHP actual de %s a %s o superior',\n\t\t'start_installation' => 'Inicie la instalación',\n\t\t'check_again' => 'Recargar para comprobar de nuevo',\n\t\t'switchmode_advanced' => 'Mostrar opciones avanzadas',\n\t\t'switchmode_basic' => 'Ocultar opciones avanzadas',\n\t\t'dependency_check' => [\n\t\t\t'title' => 'Bienvenido a froxlor',\n\t\t\t'description' => 'Comprobamos las dependencias del sistema para asegurarnos de que todas las extensiones y módulos php necesarios están habilitados para que froxlor funcione correctamente.'\n\t\t],\n\t\t'database' => [\n\t\t\t'top' => 'Base de datos',\n\t\t\t'title' => 'Crear base de datos y usuario',\n\t\t\t'description' => 'froxlor requiere una base de datos y adicionalmente un usuario privilegiado para poder crear usuarios y bases de datos (opción GRANT). La base de datos y el usuario no privilegiado serán creados en este proceso. El usuario privilegiado debe existir.',\n\t\t\t'user' => 'Usuario no privilegiado de la base de datos',\n\t\t\t'dbname' => 'Nombre de la base de datos',\n\t\t\t'force_create' => '¿Hacer copia de seguridad y sobrescribir la base de datos si existe?'\n\t\t],\n\t\t'admin' => [\n\t\t\t'top' => 'Usuario administrador',\n\t\t\t'title' => 'Vamos a crear el usuario administrador principal.',\n\t\t\t'description' => 'Este usuario tendrá todos los privilegios para ajustar la configuración y añadir/actualizar/eliminar recursos como clientes, dominios, etc.'\n\t\t],\n\t\t'system' => [\n\t\t\t'top' => 'Configuración del sistema',\n\t\t\t'title' => 'Detalles sobre su servidor',\n\t\t\t'description' => 'Establezca su entorno así como los datos y opciones relevantes del servidor aquí para que froxlor conozca su sistema. Estos valores son cruciales para la configuración y el funcionamiento del sistema.',\n\t\t\t'ipv4' => 'Dirección IPv4 primaria (si procede)',\n\t\t\t'ipv6' => 'Dirección IPv6 primaria (si procede)',\n\t\t\t'servername' => 'Nombre del servidor (FQDN, sin dirección IP)',\n\t\t\t'phpbackend' => 'PHP backend',\n\t\t\t'activate_newsfeed' => 'Habilitar la fuente oficial de noticias<br/><small>(fuente externa: https://inside.froxlor.org/news/)</small>'\n\t\t],\n\t\t'install' => [\n\t\t\t'top' => 'Finalizar la configuración',\n\t\t\t'title' => 'Un último paso...',\n\t\t\t'description' => 'El siguiente comando descargará, instalará y configurará los servicios necesarios en tu sistema de acuerdo con los datos que has facilitado en este proceso de instalación.',\n\t\t\t'runcmd' => 'Ejecute el siguiente comando como usuario root en su shell en este servidor:',\n\t\t\t'manual_config' => 'Configuraré manualmente los servicios, llévame al inicio de sesión',\n\t\t\t'waitforconfig' => 'Esperando a que se configuren los servicios...'\n\t\t],\n\t\t'errors' => [\n\t\t\t'wrong_ownership' => 'Asegúrese de que los archivos froxlor son propiedad de %s:%s',\n\t\t\t'missing_extensions' => 'Las siguientes extensiones de php son necesarias y no están instaladas',\n\t\t\t'suggestedextensions' => 'No se han encontrado las siguientes extensiones de php pero se recomiendan',\n\t\t\t'databaseexists' => 'La base de datos ya existe, por favor establezca la opción override para reconstruir o elija otro nombre',\n\t\t\t'unabletocreatedb' => 'No se ha podido crear la base de datos de prueba',\n\t\t\t'unabletodropdb' => 'No se ha podido eliminar la base de datos de prueba',\n\t\t\t'mysqlusernameexists' => 'El usuario especificado para el usuario sin privilegios ya existe. Por favor, utilice otro nombre de usuario o elimínelo primero.',\n\t\t\t'unabletocreateuser' => 'No se ha podido crear el usuario de prueba',\n\t\t\t'unabletodropuser' => 'No se ha podido eliminar el usuario de prueba.',\n\t\t\t'unabletoflushprivs' => 'El usuario privilegiado no puede eliminar privilegios.',\n\t\t\t'nov4andnov6ip' => 'Debe indicarse una dirección IPv4 o IPv6.',\n\t\t\t'servernameneedstobevalid' => 'El nombre de servidor indicado no parece ser un FQDN o un nombre de host.',\n\t\t\t'websrvuserdoesnotexist' => 'El usuario del servidor web no parece existir en el sistema.',\n\t\t\t'websrvgrpdoesnotexist' => 'El webserver-group indicado no parece existir en el sistema.',\n\t\t\t'notyetconfigured' => 'Parece que los servicios aún no se han configurado (correctamente). Por favor, ejecute el comando que se muestra a continuación o marque la casilla para hacerlo más tarde.',\n\t\t\t'mandatory_field_not_set' => 'El campo obligatorio \"%s\" no está configurado.',\n\t\t\t'unexpected_database_error' => 'Se ha producido una excepción inesperada en la base de datos. %s',\n\t\t\t'sql_import_failed' => 'Error al importar datos SQL.',\n\t\t\t'unprivileged_sql_connection_failed' => 'Error al inicializar una conexión SQL sin privilegios.',\n\t\t\t'privileged_sql_connection_failed' => 'Error al inicializar la conexión SQL privilegiada.',\n\t\t\t'mysqldump_backup_failed' => 'No se puede crear una copia de seguridad de la base de datos, tenemos un error de mysqldump.',\n\t\t\t'sql_backup_file_missing' => 'No se puede crear una copia de seguridad de la base de datos, el archivo de copia de seguridad no existe.',\n\t\t\t'backup_binary_missing' => 'No se puede crear una copia de seguridad de la base de datos, asegúrese de que ha instalado mysqldump.',\n\t\t\t'creating_configfile_failed' => 'No se pueden crear archivos de configuración, no se puede escribir el archivo.',\n\t\t\t'database_already_exiting' => '¡Encontramos una base de datos y no se nos permitió sobreescribirla!'\n\t\t]\n\t],\n\t'welcome' => [\n\t\t'title' => '¡Bienvenido a froxlor!',\n\t\t'config_note' => 'Para que froxlor pueda comunicarse correctamente con el backend, tienes que configurarlo.',\n\t\t'config_now' => 'Configurar ahora'\n\t]\n];\n"
  },
  {
    "path": "lng/fr.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @author     Tim Zielosko <tim.zielosko@syscp.de>\n * @author     Aldo Reset <aldo.reset@placenet.org>\n * @author     Romain MARIADASSOU <roms2000@free.fr>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'admin' => [\n\t\t'overview' => 'Sommaire',\n\t\t'ressourcedetails' => 'Ressources utilisées',\n\t\t'systemdetails' => 'Informations du système',\n\t\t'froxlordetails' => 'Informations de froxlor',\n\t\t'installedversion' => 'Version installée',\n\t\t'latestversion' => 'Dernière version en date',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'Vérifier par internet',\n\t\t\t'error' => 'Erreur pour vérifier la dernière version',\n\t\t],\n\t\t'resources' => 'Ressources',\n\t\t'customer' => 'Compte',\n\t\t'customers' => 'Comptes',\n\t\t'customer_add' => 'Ajouter un compte',\n\t\t'customer_edit' => 'Modifier un compte',\n\t\t'domains' => 'Domaines',\n\t\t'domain_add' => 'Ajouter un domaine',\n\t\t'domain_edit' => 'Modifier le domaine',\n\t\t'subdomainforemail' => 'Sous-domaines comme domaine e-mail',\n\t\t'admin' => 'Administrateur',\n\t\t'admins' => 'Administrateurs',\n\t\t'admin_add' => 'Ajouter un administrateur',\n\t\t'admin_edit' => 'Modifier un administrateur',\n\t\t'customers_see_all' => 'Peut voir tous les comptes ?',\n\t\t'change_serversettings' => 'Peut modifier la configuration du serveur ?',\n\t\t'server' => 'Système',\n\t\t'serversettings' => 'Paramètres',\n\t\t'rebuildconf' => 'Régénérer la configuration',\n\t\t'stdsubdomain' => 'Sous-domaine type',\n\t\t'stdsubdomain_add' => 'Ajouter un sous-domaine type',\n\t\t'phpenabled' => 'PHP activé',\n\t\t'deactivated' => 'Désactiver',\n\t\t'deactivated_user' => 'Désactiver l\\'utilisateur',\n\t\t'sendpassword' => 'Envoyer le mot de passe',\n\t\t'ownvhostsettings' => 'Configuration spéciale du vHost',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Exemple de configuration',\n\t\t\t'overview' => 'Aperçu',\n\t\t\t'wizard' => 'Assistant',\n\t\t\t'distribution' => 'Distribution',\n\t\t\t'service' => 'Service',\n\t\t\t'daemon' => 'Démon',\n\t\t\t'http' => 'Serveur Web (HTTP)',\n\t\t\t'dns' => 'Serveur de Noms (DNS)',\n\t\t\t'mail' => 'Serveur de Mails (IMAP/POP3)',\n\t\t\t'smtp' => 'Serveur de Mails (SMTP)',\n\t\t\t'ftp' => 'Serveur FTP',\n\t\t\t'etc' => 'Autres (Système)',\n\t\t\t'choosedistribution' => '-- Choisissez une distribution --',\n\t\t\t'chooseservice' => '-- Choisissez un service --',\n\t\t\t'choosedaemon' => '-- Choisissez un démon --',\n\t\t\t'statistics' => 'Statistiques',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Modèles',\n\t\t\t'template_add' => 'Ajouter un modèle',\n\t\t\t'template_edit' => 'Modifier un modèle',\n\t\t\t'action' => 'Action',\n\t\t\t'email' => 'E-mail',\n\t\t\t'subject' => 'Référence',\n\t\t\t'mailbody' => 'Texte de l\\'e-mail',\n\t\t\t'createcustomer' => 'E-mail de bienvenue pour les nouveaux clients',\n\t\t\t'pop_success' => 'E-mail de bienvenue pour les nouveaux accès e-mail',\n\t\t\t'template_replace_vars' => 'Les variables qui seront remplacées dans le template :',\n\t\t\t'FIRSTNAME' => 'Sera remplacé par le prénom.',\n\t\t\t'NAME' => 'Sera remplacé par le nom.',\n\t\t\t'USERNAME' => 'Sera remplacé par le login.',\n\t\t\t'PASSWORD' => 'Sera remplacé par le mot de passe du client.',\n\t\t\t'EMAIL' => 'Sera remplacé par l\\'accès e-mail.',\n\t\t\t'TRAFFIC' => 'Sera remplacé par le taux de trafic qui a été attribué à l\\'utilisateur.',\n\t\t\t'TRAFFICUSED' => 'Sera remplacé par le taux de trafic qui a été consommé par l\\'utilisateur.',\n\t\t\t'pop_success_alternative' => 'Message de bienvenue envoyé à l\\'adresse e-mail alternative pour les nouveaux comptes e-mails',\n\t\t\t'EMAIL_PASSWORD' => 'Remplacer par le mot de passe du compte POP3 / IMAP.',\n\t\t],\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IPs et ports',\n\t\t\t'add' => 'Ajouter une IP / port',\n\t\t\t'edit' => 'Modifier une IP / port',\n\t\t\t'create_listen_statement' => 'Déclaration des ports d\\'écoute',\n\t\t\t'create_namevirtualhost_statement' => 'Déclaration des hôtes virtuels \"NameVirtualHost\"',\n\t\t\t'create_vhostcontainer' => 'Déclaration des conteneurs virtuels \"vHost\"',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Déclaration des noms d\\'hôtes \"ServerName\" dans les conteneurs virtuels \"vHost\"',\n\t\t\t'enable_ssl' => 'Est-ce un port SSL ?',\n\t\t\t'ssl_cert_file' => 'Emplacement du certificat SSL',\n\t\t],\n\t\t'memorylimitdisabled' => 'Désactivé',\n\t\t'valuemandatory' => 'Cette valeur est obligatoire',\n\t\t'valuemandatorycompany' => 'Vous devez indiquer au moins l\\'une des 3 valeurs suivantes : \"nom\" ou \"prénom\" ou \"entreprise\"',\n\t\t'serversoftware' => 'Logiciel Serveur',\n\t\t'phpversion' => 'Version de PHP',\n\t\t'phpmemorylimit' => 'Limite mémoire de PHP',\n\t\t'mysqlserverversion' => 'Version du serveur MySQL',\n\t\t'mysqlclientversion' => 'Version du client MySQL',\n\t\t'webserverinterface' => 'Interface Web',\n\t\t'accountsettings' => 'Paramètres du compte',\n\t\t'panelsettings' => 'Paramètres du panel',\n\t\t'systemsettings' => 'Paramètres du système',\n\t\t'webserversettings' => 'Paramètres du serveur Web',\n\t\t'mailserversettings' => 'Paramètres du serveur de Mail',\n\t\t'nameserversettings' => 'Paramètres du serveur de Noms',\n\t\t'updatecounters' => 'Recalculer les ressources utilisées',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Jamais',\n\t\t\t'choosableno' => 'A choisir, par défaut : non',\n\t\t\t'choosableyes' => 'A choisir, par défaut : oui',\n\t\t\t'always' => 'Toujours',\n\t\t],\n\t\t'webalizersettings' => 'Paramètres pour Webalizer',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normal',\n\t\t\t'quiet' => 'Silencieux',\n\t\t\t'veryquiet' => 'Aucune sortie',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Il n\\'est acutellement pas possible d\\'ajouter de domaines. Vous devez d\\'abord ajouter un client.',\n\t\t'loggersettings' => 'Paramètres des logs',\n\t\t'logger' => [\n\t\t\t'normal' => 'normal',\n\t\t\t'paranoid' => 'paranoÃ¯aque',\n\t\t],\n\t\t'emaildomain' => 'Domaine e-mail',\n\t\t'email_only' => 'Seulement des e-mails ?',\n\t\t'wwwserveralias' => 'Ajouter un \"www.\" à l\\'alias du serveur \"ServerAlias\"',\n\t\t'subject' => 'Sujet',\n\t\t'recipient' => 'Destinataire',\n\t\t'message' => 'Ecrire un message',\n\t\t'text' => 'Message',\n\t\t'sslsettings' => 'Paramètres SSL',\n\t\t'dkimsettings' => 'Paramètres DKIM',\n\t\t'caneditphpsettings' => 'Peut changer les paramétres PHP du domaine ?',\n\t\t'allips' => 'Toutes les adresses IP',\n\t\t'awstatssettings' => 'Paramètres Awstats',\n\t\t'domain_dns_settings' => 'Paramètres DNS',\n\t\t'activated' => 'Activé',\n\t\t'statisticsettings' => 'Paramètres des statistiques',\n\t\t'or' => 'ou',\n\t\t'sysload' => 'Charge du système',\n\t\t'noloadavailable' => 'Non disponible',\n\t\t'nouptimeavailable' => 'Non disponible',\n\t\t'nosubject' => '(Aucun sujet)',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Ancien mot de passe',\n\t\t'new_password' => 'Nouveau mot de passe',\n\t\t'new_password_confirm' => 'Nouveau mot de passe (confirmation)',\n\t\t'new_password_ifnotempty' => 'Nouveau mot de passe (Veuillez laisser vide pour ne pas changer)',\n\t\t'also_change_ftp' => 'Changer aussi le mot de passe du compte FTP principal ?',\n\t\t'also_change_webalizer' => 'Changer aussi le mot de passe des statistiques Webalizer ?',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Pas encore lancé',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Chemin',\n\t\t'name' => 'Nom',\n\t\t'firstname' => 'Prénom',\n\t\t'company' => 'Entreprise',\n\t\t'street' => 'Rue',\n\t\t'zipcode' => 'Code postal',\n\t\t'city' => 'Ville',\n\t\t'phone' => 'Téléphone',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'E-mail',\n\t\t'customernumber' => 'Numéro du client',\n\t\t'diskspace' => 'Espace Web (Mo)',\n\t\t'traffic' => 'Trafic (Go)',\n\t\t'mysqls' => 'Base(s) de données MySQL',\n\t\t'emails' => 'Adresse(s) e-mail',\n\t\t'accounts' => 'Accès e-mail',\n\t\t'forwarders' => 'Transfert(s) e-mail',\n\t\t'ftps' => 'Accès FTP',\n\t\t'subdomains' => 'Sous-domaine(s)',\n\t\t'domains' => 'Domaine(s)',\n\t\t'email_quota' => 'Quota e-mail',\n\t\t'email_imap' => 'E-mail IMAP',\n\t\t'email_pop3' => 'E-mail POP3',\n\t\t'mail_quota' => 'Quota e-mail',\n\t\t'title' => 'Titre',\n\t\t'country' => 'Pays',\n\t],\n\t'dkim' => [\n\t\t'dkim_prefix' => [\n\t\t\t'title' => 'Prefix DKIM',\n\t\t\t'description' => 'Veuillez entrer l\\'emplacement des fichiers RSA pour DKIM ainsi que l\\'emplacement du fichier de configuration pour le plugin Milter',\n\t\t],\n\t\t'dkim_domains' => [\n\t\t\t'title' => 'Nom du fichier DKIM',\n\t\t\t'description' => '<strong>Nom du fichier</strong> des paramètres DKIM pour les domaines tel que entré dans la configuration de DKIM-milter',\n\t\t],\n\t\t'dkim_dkimkeys' => [\n\t\t\t'title' => 'Nom du fichier des clefs DKIM',\n\t\t\t'description' => '<strong>Nom du fichier</strong> des paramètres des clefs DKIM tel que entré dans la configuration de DKIM-milter',\n\t\t],\n\t\t'dkimrestart_command' => [\n\t\t\t'title' => 'Commande de redémarrage de DKIM-milter',\n\t\t\t'description' => 'Veuillez entrer la commande de redémarrage du service DKIM-milter',\n\t\t],\n\t\t'use_dkim' => [\n\t\t\t'title' => 'Activer le support DKIM ?',\n\t\t\t'description' => 'Voulez-vous utiliser le système DKIM (DomainKeys Identified Mail) ?',\n\t\t],\n\t],\n\t'dns' => [\n\t\t'destinationip' => 'IP du domaine',\n\t\t'standardip' => 'IP standard du serveur',\n\t\t'a_record' => 'Enregistrement de type \"A\" (IPv6 optionnel)',\n\t\t'cname_record' => 'Enregistrement CNAME',\n\t\t'mxrecords' => 'Définition des enregistrements MX',\n\t\t'standardmx' => 'Enregistrements MX standard du serveur',\n\t\t'mxconfig' => 'Enregistrements MX personnalisé',\n\t\t'priority10' => 'Priorité 10',\n\t\t'priority20' => 'Priorité 20',\n\t\t'txtrecords' => 'Définir des enregistrement TXT',\n\t\t'txtexample' => 'Exemple (pour SPF) :<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'Dossier \"OpenBasedir\"',\n\t\t'docroot' => 'Identique au dossier ci-dessus',\n\t\t'homedir' => 'Dossier Principal',\n\t],\n\t'domains' => [\n\t\t'description' => 'Ici, vous pouvez ajouter des sites et domaines et changer leurs doosiers.<br />Il vous faudra patienter quelques minutes après chaque changement pour que la configuration soit activée.',\n\t\t'domainsettings' => 'Configuration des Domaines',\n\t\t'domainname' => 'Nom du Domaine',\n\t\t'subdomain_add' => 'Ajouter un sous-domaine',\n\t\t'subdomain_edit' => 'Changer un sous-domaine',\n\t\t'wildcarddomain' => 'Domaine générique (Wilcard) ?',\n\t\t'aliasdomain' => 'Alias pour le domaine',\n\t\t'noaliasdomain' => 'Domaine sans alias',\n\t\t'hasaliasdomains' => 'Le domaine possède un ou des alias.',\n\t\t'statstics' => 'Fréquentation',\n\t\t'isassigneddomain' => 'Le domaine est attribué',\n\t\t'add_date' => 'Ajouter à froxlor',\n\t\t'registration_date' => 'Ajouter à l\\'enregistrement',\n\t],\n\t'emails' => [\n\t\t'description' => 'Ici, vous pouvez ajouter vos boîtes e-mails.<br /><br />Les informations pour configurer votre logiciel e-mail sont les suivantes : <br /><br />Nom du serveur : <b><i>votre-domaine.com</i></b><br />Identifiant : <b><i>l\\'adresse e-mail</i></b><br />Mot de passe : <b><i>le mot de passe que vous avez choisi</i></b>',\n\t\t'emailaddress' => 'Adresse',\n\t\t'emails_add' => 'Ajouter une adresse e-mail',\n\t\t'emails_edit' => 'Changer une adresse e-mail',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => 'Définir comme adresse \"catchall\" ?',\n\t\t'account' => 'Accès',\n\t\t'account_add' => 'Ajouter un accès',\n\t\t'account_delete' => 'Supprimer l\\'accès',\n\t\t'from' => 'de',\n\t\t'to' => 'à',\n\t\t'forwarders' => 'Réexpédition',\n\t\t'forwarder_add' => 'Ajouter un renvoi',\n\t\t'alternative_emailaddress' => 'Adresse e-mail alternative',\n\t\t'quota' => 'Quota',\n\t\t'noquota' => 'Pas de quota',\n\t\t'updatequota' => 'Mise à jour',\n\t],\n\t'error' => [\n\t\t'error' => 'Erreur',\n\t\t'directorymustexist' => 'Le dossier que vous avez choisi n\\'existe pas. Veuillez ajouter le dossier avec votre client FTP.',\n\t\t'filemustexist' => 'Le fichier que vous avez sélectionné n\\'existe pas.',\n\t\t'allresourcesused' => 'Vous avez déjà utilisé toutes les ressources.',\n\t\t'domains_cantdeletemaindomain' => 'Vous ne pouvez pas supprimer un domaine qui est utilisé pour des adresses e-mails.',\n\t\t'domains_canteditdomain' => 'Vous n\\'avez pas le droit de configurer ce domaine.',\n\t\t'domains_cantdeletedomainwithemail' => 'Vous ne pouvez pas supprimer un domaine qui est utilisé pour des e-mails. Vous devez d\\'abord supprimer toutes les adresses e-mails qu\\'il contient.',\n\t\t'firstdeleteallsubdomains' => 'Il faut d\\'abord supprimer tous les sous-domaines avant d\\'ajouter un domaine \"wildcard\".',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Vous avez déjà défini une adresse \"catchall\" pour ce domaine.',\n\t\t'ftp_cantdeletemainaccount' => 'Vous ne pouvez pas supprimer votre accès principal.',\n\t\t'login' => 'Identifiant / mot de passe invalide.',\n\t\t'login_blocked' => 'Cet identifiant a été bloqué à cause de nombreuses tentatives de connexions invalides.<br />Veuillez réessayer dans %s secondes.',\n\t\t'notallreqfieldsorerrors' => 'Vous n\\'avez pas rempli toutes les cases obligatoires ou vous les avez remplis avec des informations invalides.',\n\t\t'oldpasswordnotcorrect' => 'L\\'ancien mot de passe n\\'est pas correct.',\n\t\t'youcantallocatemorethanyouhave' => 'Vous ne pouvez pas distribuer plus de ressources qu\\'il n\\'en reste.',\n\t\t'mustbeurl' => 'Vous n\\'avez pas entré une adresse URL valide.',\n\t\t'invalidpath' => 'Vous n\\'avez pas choisi une adresse URL valide (probablement à cause de problèmes avec le listing de dossiers ?)',\n\t\t'stringisempty' => 'Entrée manquante',\n\t\t'stringiswrong' => 'Entrée invalide',\n\t\t'newpasswordconfirmerror' => 'Le nouveau mot de passe et sa confirmation ne sont pas identiques pas.',\n\t\t'mydomain' => '\"domaine\"',\n\t\t'loginnameexists' => 'L\\'identifiant \"%s\" existe déjà.',\n\t\t'emailiswrong' => 'L\\'adresse \"%s\" contient des signes invalides ou est incomplète.',\n\t\t'loginnameiswrong' => 'L\\'identifiant \"%s\" contient des signes invalides.',\n\t\t'userpathcombinationdupe' => 'Cette combinaison d\\'identifiant et de dossier existe déjà.',\n\t\t'patherror' => 'Erreur générale ! Le dossier ne doit pas être vide.',\n\t\t'errordocpathdupe' => 'Il y a déjà une option concernant le dossier \"%s\".',\n\t\t'adduserfirst' => 'Vous devez d\\'abord ajouter un.',\n\t\t'domainalreadyexists' => 'Le domaine \"%s\" existe déjà.',\n\t\t'nolanguageselect' => 'Aucune langue choisis.',\n\t\t'nosubjectcreate' => 'Il faut entrer un sujet.',\n\t\t'nomailbodycreate' => 'Il faut entrer un corps de texte.',\n\t\t'templatenotfound' => 'Aucun modèle trouvé.',\n\t\t'alltemplatesdefined' => 'Vous avez déjà appliqué des modèles pour toutes les langues.',\n\t\t'wwwnotallowed' => 'Un sous-domaine ne peut pas s\\'appeler www.',\n\t\t'subdomainiswrong' => 'Le sous-domaine \"%s\" contient des signes invalides.',\n\t\t'domaincantbeempty' => 'Le nom de domaine ne doit pas être vide.',\n\t\t'domainexistalready' => 'Le domaine \"%s\" existe déjà.',\n\t\t'domainisaliasorothercustomer' => 'L\\'alias du domaine choisi est soit un alias existant d\\'un autre client ou soit fait référence à lui même.',\n\t\t'emailexistalready' => 'L\\'adresse \"%s\" existe déjà.',\n\t\t'maindomainnonexist' => 'Le domaine \"%s\" n\\'existe pas.',\n\t\t'destinationnonexist' => 'Veuillez écrire votre adresse de renvoi à l\\'emplacement \"à\".',\n\t\t'destinationalreadyexistasmail' => 'Le renvoi vers l\\'adresse \"%s\" existe déjà comme adresse active.',\n\t\t'destinationalreadyexist' => 'Il existe déjà une réexpédition vers l\\'adresse \"%s\".',\n\t\t'destinationiswrong' => 'L\\'adresse \"%s\" contient des signes invalides ou est incomplète.',\n\t\t'loginnameissystemaccount' => 'Vous ne pouvez pas créer de compte ressemblant aux comptes système (ex : \"%s\"). Veuillez entrer un autre nom de compte.',\n\t\t'ipstillhasdomains' => 'La combinaison IP / port est encore utilisée, veuillez réassigner le ou les domaines existant(s) avec cette adresse IP / port concerné(s) à une autre combinaison IP / port avant de supprimer celle-ci.',\n\t\t'cantdeletedefaultip' => 'Vous ne pouvez pas supprimer cette combinaison IP / Port, veuillez d\\'abord attribuer une autre combinaison IP / Port par défaut à ce revendeur avant de supprimer celle-ci.',\n\t\t'cantdeletesystemip' => 'Vous ne pouvez pas créer, modifier ou supprimer l\\'IP du système.',\n\t\t'myipdefault' => 'Choissez une combinaison IP / port par défaut.',\n\t\t'myipnotdouble' => 'Cette combinaison existe déjà',\n\t\t'cantchangesystemip' => 'Vous ne pouvez pas modifier l\\'adresse IP du système, ni en ajouter de nouvelle.',\n\t\t'sessiontimeoutiswrong' => 'Seule une valeur numérique pour le temps d\\'inactivité est autorisée.',\n\t\t'maxloginattemptsiswrong' => 'Seule une valeur numérique pour \"nombre maximum de tentative de connexion\" est autorisée.',\n\t\t'deactivatetimiswrong' => 'Seule une valeur numérique pour la durée de désactivation est autorisée.',\n\t\t'accountprefixiswrong' => 'Le \"Préfixe client\" n\\'est pas valide.',\n\t\t'mysqlprefixiswrong' => 'Le \"Préfixe SQL\" n\\'est pas valide.',\n\t\t'ftpprefixiswrong' => 'Le \"Préfixe FTP\" n\\'est pas valide.',\n\t\t'ipiswrong' => 'L\\'\"Adresse IP\" n\\'est pas valide.',\n\t\t'vmailuidiswrong' => 'L\\'\"UID e-mail\" est incorrect. Seul un UID numérique est autorisé.',\n\t\t'vmailgidiswrong' => 'Le \"GID e-mail\" est incorrect. Seul un GID numérique est autorisé.',\n\t\t'adminmailiswrong' => 'L\\'adresse e-mail de l\\'administrateur est incorrect. Seulement une adresse e-mail valide est autorisé.',\n\t\t'pagingiswrong' => 'La valeur \"Nombre de résultats page\" est incorrecte. Seul une valeur numérique est autorisée.',\n\t\t'phpmyadminiswrong' => 'Le lien pour phpMyAdmin n\\'est pas valide.',\n\t\t'webmailiswrong' => 'Le lien pour le WebMail n\\'est pas valide.',\n\t\t'webftpiswrong' => 'Le lien pour le WebFTP n\\'est pas valide.',\n\t\t'stringformaterror' => 'La valeur pour \"%s\" n\\'est pas dans un format reconnu.',\n\t\t'youcantdeleteyourself' => 'Vous ne pouvez pas supprimer votre propre compte pour des raisons évidente de sécurité ...',\n\t\t'youcanteditallfieldsofyourself' => 'Note : Vous ne pouvez pas éditer tous les champs de votre propre compte pour des raisons de sécurité.',\n\t\t'documentrootexists' => 'Le dossier \"%s\" existe déjà pour cet utilisateur. Veuillez le supprimer / déplacer avant de réessayer l\\'ajout de cet utilisateur.',\n\t\t'formtokencompromised' => 'La requète semble compromise. Pour des raisons de sécurité, vous avez été déconnecté.',\n\t\t'logerror' => 'Erreur log : %s',\n\t\t'nomessagetosend' => 'Vous n\\'avez pas entré de message.',\n\t\t'norecipientsgiven' => 'Vous n\\'avez pas spécifier de destinataire',\n\t\t'errorsendingmail' => 'Echec d\\'envoi du message à \"%s\"',\n\t\t'cannotreaddir' => 'Impossible de lire dossier \"%s\"',\n\t\t'vmailquotawrong' => 'La taille du quota doit être entre 1 et 999',\n\t\t'invalidip' => 'Adresse IP invalide : %s',\n\t\t'invalidmysqlhost' => 'Adresse hôte MySQL invalide : \"%s\"',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'Vous ne pouvez pas activer AWStats <u>et</u> Webalizer en même temps. Veuillez n\\'en choisir qu\\'un seul.',\n\t\t'cannotwritetologfile' => 'Ne peut ouvrir le fichier de log %s en écriture',\n\t],\n\t'extras' => [\n\t\t'description' => 'Ici, vous pouvez ajouter des extras, comme par exemple, la protection de dossiers du site.<br />Il vous faudra patienter quelques minutes après chaque changement pour que la configuration soit activée.',\n\t\t'directoryprotection_add' => 'Ajouter une protection de dossier',\n\t\t'view_directory' => 'Aperçu du dossier',\n\t\t'pathoptions_add' => 'Ajouter des options sur le dossier',\n\t\t'directory_browsing' => 'Afficher le contenu des dossiers',\n\t\t'pathoptions_edit' => 'Modifier les options de dossier',\n\t\t'errordocument404path' => 'Emplacement du document \"Erreur 404\"',\n\t\t'errordocument403path' => 'Emplacement du document \"Erreur 403\"',\n\t\t'errordocument500path' => 'Emplacement du document \"Erreur 500\"',\n\t\t'errordocument401path' => 'Emplacement du document \"Erreur 401\"',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Ici, vous pouvez ajouter des accès FTP supplémentaires.<br />Les changements, ainsi que l\\'accès, sont immédiatement opérationnels.',\n\t\t'account_add' => 'Ajouter un accès',\n\t],\n\t'index' => [\n\t\t'customerdetails' => 'Informations personnelles',\n\t\t'accountdetails' => 'Informations du compte',\n\t],\n\t'logger' => [\n\t\t'date' => 'Date',\n\t\t'type' => 'Type',\n\t\t'action' => 'Action',\n\t\t'user' => 'Utilisateur',\n\t\t'truncate' => 'Vider les logs',\n\t],\n\t'login' => [\n\t\t'username' => 'Identifiant',\n\t\t'password' => 'Mot de passe',\n\t\t'language' => 'Langue',\n\t\t'login' => 'Se connecter',\n\t\t'logout' => 'Se déconnecter',\n\t\t'profile_lng' => 'Langue du profil',\n\t\t'forgotpwd' => 'Mot de passe oublié ?',\n\t\t'presend' => 'Réinitialiser le mot de passe',\n\t\t'email' => 'Adresse e-mail',\n\t\t'remind' => 'Réinitialiser mon mot de passe',\n\t\t'usernotfound' => 'Erreur : utilisateur inconnu !',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Bonjour,\\\\n\\\\nvotre accès POP3 / IMAP {EMAIL}\\\\na été créé avec succès.\\\\n\\\\nCeci est un e-mail généré automatiquement, veuillez ne pas répondre à ce message.\\\\n\\\\nCordialement,\\\\nL\\'équipe froxlor\\\\nhttp://www.froxlor.org',\n\t\t\t'subject' => 'Accès POP3 / IMAP créé',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Bonjour {FIRSTNAME} {NAME},\\\\n\\\\nVous trouverez ci-dessous vos informations d\\'accès au panel d\\'administration :\\\\n\\\\nAdresse d\\'administration : http://demo.froxlor.org\\\\n\\\\nIdentifiant : {USERNAME}\\\\nMot de passe : {PASSWORD}\\\\n\\\\nCordialement,\\\\nL\\'équipe froxlor\\\\nhttp://www.froxlor.org\\\\n',\n\t\t\t'subject' => 'froxlor : Informations pour votre accès au panel d\\'administration',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Bonjour,\\\\n\\\\nVotre compte e-mail {EMAIL} a été correctement créé.\\\\n\\\\nVotre mot de passe est : {PASSWORD}.\\\\n\\\\nCeci est un message généré automatiquemenent, veuillez ne pas répondre à cet e-mail car il ne serait être consulter.\\\\n\\\\nCordialement,\\\\nL\\'équipe froxlor.',\n\t\t\t'subject' => 'Compte e-mail correctement créé',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'réinitialisation du mot de passe',\n\t\t\t'mailbody' => 'Bonjour {USERNAME},\\\\n\\\\nVotre mot de passe pour froxlor a été réinitialiser !\\\\nLe nouveau mot de passe est : {LINK}\\\\n\\\\nCordialement,\\\\nL\\'équipe froxlor.',\n\t\t],\n\t],\n\t'menu' => [\n\t\t'message' => 'Messages',\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Général',\n\t\t\t'changepassword' => 'Changer de mot de passe',\n\t\t\t'changelanguage' => 'Changer de langue',\n\t\t\t'username' => 'Utilisateur : ',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'E-mail',\n\t\t\t'emails' => 'Adresse(s) e-mail(s)',\n\t\t\t'webmail' => 'Webmail',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Bases de données',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domaines',\n\t\t\t'settings' => 'Paramétres des sites',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Comptes d\\'accès FTP',\n\t\t\t'webftp' => 'WebFTP',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extras',\n\t\t\t'directoryprotection' => 'Protection des dossiers',\n\t\t\t'pathoptions' => 'Options des dossiers',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Trafic',\n\t\t\t'current' => 'Mois actuel',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'Log système',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'Aucun e-mail n\\'a été envoyé car il n\\'existe aucun destinataire dans la base de données',\n\t\t'success' => 'Le message a été envoyé aux destinataires \"%s\"',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'Nom de la base de données',\n\t\t'databasedescription' => 'Description de la base de données',\n\t\t'database_create' => 'Ajouter une base de données',\n\t\t'description' => 'Ici, vous pouvez ajouter et effacer des bases de données MySQL.<br />Les changements, ainsi que les bases de données, sont immédiatement opérationnels.<br />Dans le menu, vous trouverez un lien vers phpMyAdmin, avec lequel vous pouvez gérer vos bases de données.<br /><br />L\\'accès aux bases de données depuis les scripts PHP fonctionne comme suit : (Il faut remplacer les valeurs en <i><b>italique</b></i> par vos informations !)<br /><br />$connexion = mysql_connect(\\'localhost\\', \\'<i><b>Votre identifiant</b></i>\\', \\'<i><b>Votre mot de passe</b></i>\\');<br />mysql_select_db(\\'<i><b>Le nom de la base de données</b></i>\\', $connexion);',\n\t],\n\t'panel' => [\n\t\t'edit' => 'Modifier',\n\t\t'delete' => 'Supprimer',\n\t\t'create' => 'Ajouter',\n\t\t'save' => 'Sauvegarder',\n\t\t'yes' => 'Oui',\n\t\t'no' => 'Non',\n\t\t'emptyfornochanges' => 'Laissez vide pour ne pas modifier',\n\t\t'emptyfordefault' => 'Laissez vide pour l\\'option standard',\n\t\t'path' => 'Dossier',\n\t\t'toggle' => 'Activer / Désactiver',\n\t\t'next' => 'continuer',\n\t\t'dirsmissing' => 'Dossiers non disponibles ou illisibles',\n\t\t'urloverridespath' => 'URL (supplante la valeur dossier)',\n\t\t'pathorurl' => 'Dossier ou URL',\n\t\t'ascending' => 'ascendant',\n\t\t'descending' => 'descendant',\n\t\t'search' => 'Rechercher',\n\t\t'used' => 'utilisé',\n\t\t'translator' => 'Traducteur(s)',\n\t\t'reset' => 'Ignorer les changements',\n\t\t'pathDescription' => 'Si le dossier n\\'existe pas, il sera créé automatiquement.',\n\t\t'back' => 'Retour',\n\t\t'reseller' => 'revendeur',\n\t\t'admin' => 'administrateur',\n\t\t'customer' => 'client(s)',\n\t\t'send' => 'envoyé',\n\t\t'nosslipsavailable' => 'Il n\\'y a actuellement aucune combinaison IP / Port configurer pour SSL',\n\t\t'backtooverview' => 'Retour à l\\'aperçu',\n\t\t'dateformat' => 'YYYY-MM-DD',\n\t\t'dateformat_function' => 'Y-m-d',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Par défaut',\n\t\t'never' => 'Jamais',\n\t\t'active' => 'Actif',\n\t\t'please_choose' => 'Veuillez choisir',\n\t],\n\t'pwdreminder' => [\n\t\t'success' => 'Mot de passe correctement réinitialiser.<br />Vous devriez recevoir un e-mail avec votre nouveau mot de passe d\\'ici quelques minutes.',\n\t\t'notallowed' => 'La réinitialisation des mots de passe est désactivée.',\n\t],\n\t'question' => [\n\t\t'question' => 'Question de sécurité',\n\t\t'admin_customer_reallydelete' => 'Etes-vous sûr de vouloir supprimer le compte \"%s\" ?<br />ATTENTION ! Toutes ses informations seront supprimées ! Une fois fait, il vous appartiendra de supprimer manuellement tous les dossiers du compte sur le système de fichiers.',\n\t\t'admin_domain_reallydelete' => 'Etes-vous sûr de vouloir supprimer le domaine \"%s\" ?',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Etes-vous sûr de vouloir désactiver les modes de sécurité suivants : OpenBasedir et / oû SafeMode ?',\n\t\t'admin_admin_reallydelete' => 'Etes-vous sûr de vouloir supprimer l\\'administrateur \"%s\" ?<br />Tous ses comptes seront affectés à l\\'administrateur principal.',\n\t\t'admin_template_reallydelete' => 'Etes-vous sûr de vouloir supprimer le modèle \"%s\" ?',\n\t\t'domains_reallydelete' => 'Etes-vous sûr de vouloir supprimer le domaine \"%s\" ?',\n\t\t'email_reallydelete' => 'Etes-vous sûr de vouloir supprimer l\\'adresse e-mail \"%s\" ? ',\n\t\t'email_reallydelete_account' => 'Etes-vous sûr de vouloir supprimer l\\'accès e-mail \"%s\" ?',\n\t\t'email_reallydelete_forwarder' => 'Etes-vous sûr de vouloir supprimer le renvoi vers \"%s\" ?',\n\t\t'extras_reallydelete' => 'Etes-vous sûr de vouloir supprimer la protection du dossier \"%s\" ?',\n\t\t'extras_reallydelete_pathoptions' => 'Etes-vous sûr de vouloir supprimer les options du dossier \"%s\" ?',\n\t\t'ftp_reallydelete' => 'Etes-vous sûr de vouloir supprimer l\\'accès ftp \"%s\" ?',\n\t\t'mysql_reallydelete' => 'Etes-vous sûr de vouloir supprimer la base de données \"%s\" ?<br />ATTENTION : Toutes les données seront perdues à jamais !',\n\t\t'admin_configs_reallyrebuild' => 'Etes-vous sûr de vouloir régénérer les fichiers de configuration Apache et Bind ?',\n\t\t'admin_ip_reallydelete' => 'Etes-vous sûr de vouloir supprimer l\\'adresse IP \"%s\" ?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Etes-vous sûr de vouloir différencier la racine principale de ce domaine de la racine principale du client ?',\n\t\t'admin_counters_reallyupdate' => 'Etes-vous sûr de vouloir recalculer les ressources utilisées ?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Etes-vous sûr de vouloir retirer tous les mots de passe en clairs des comptes e-mails de la table mail_users ? Cette action ne peut être annulée !',\n\t\t'logger_reallytruncate' => 'Etes-vous sûr de vouloir vider la table \"%s\" ?',\n\t\t'admin_quotas_reallywipe' => 'Etes-vous sûr de vouloir retirer tous les quotas de la table mail_users ? Cette action ne peut être annulée !',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Durée d\\'inactivité maximale',\n\t\t\t'description' => 'Combien de secondes d\\'inactivité avant qu\\'une session ne se ferme ?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Préfix des comptes',\n\t\t\t'description' => 'Quel préfix doivent avoir les comptes ?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'Préfix SQL',\n\t\t\t'description' => 'Quel préfix doivent avoir les bases de données ?',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'Préfix FTP',\n\t\t\t'description' => 'Quel préfix doivent avoir les accès FTP ?',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Dossier de stockage',\n\t\t\t'description' => 'Oû doivent être stockés tous les dossiers et fichiers des différents comptes ?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Dossier des fichiers de log',\n\t\t\t'description' => 'Oû doivent être stockés les archives des logs d\\'accès du serveur Web ?',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'Adresse IP',\n\t\t\t'description' => 'Quelle est l\\'adresse IP du serveur ?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Nom d\\'hôte',\n\t\t\t'description' => 'Quel est le nom d\\'hôte (hostname) du serveur ?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Commande de rechargement d\\'Apache',\n\t\t\t'description' => 'Quelle est la commande pour recharger / redémarrer Apache ?',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Emplacement du dossier de configuration de Bind / Named',\n\t\t\t'description' => 'Oû doit être stocké la configuration de Bind / Named ?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Commande de rechargement de Bind / Named',\n\t\t\t'description' => 'Quelle est la commande pour recharger / redémarrer Bind / Named ?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'UID des e-mails',\n\t\t\t'description' => 'Quel UID doivent avoir les e-mails ?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'GID des e-mails',\n\t\t\t'description' => 'Quel GID doivent avoir les e-mails ?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Emplacement des e-mails',\n\t\t\t'description' => 'Dans quel dossier doivent être stocker les e-mails ?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Adresse e-mail de l\\'administrateur',\n\t\t\t'description' => 'Quelle est l\\'adresse e-mail par défaut des e-mails envoyés par froxlor ?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'Adresse URL de phpMyAdmin',\n\t\t\t'description' => 'A quelle adresse se trouve phpMyAdmin ?',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'Adresse URL du WebMail',\n\t\t\t'description' => 'A quelle adresse se trouve le WebMail ?',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'Adresse URL du WebFTP',\n\t\t\t'description' => 'A quelle adresse se trouve le WebFTP ?',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Quelle langue est la langue par défaut ?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Nombre d\\'essais maximum avant désactivation',\n\t\t\t'description' => 'Nombre de tentatives maximum avant la désactivation de l\\'accès.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Durée de la désactivation',\n\t\t\t'description' => 'Durée (en secondes) pendant laquelle l\\'accès sera désactivé.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Mode de sélection des dossiers',\n\t\t\t'description' => 'Choisir un dossier par une liste déroulante ou l\\'entrer manuellement ?',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Serveurs de nom «Nameservers»',\n\t\t\t'description' => 'Une liste séparée par des virgules contenant les noms d\\'hôtes de tous les serveurs de noms. Le premier dans la liste sera le serveur primaire.',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'Serveurs de messagerie «MX»',\n\t\t\t'description' => 'Une liste séparée par des virgules contenant les serveurs de messagerie avec leur poid : un nombre et le nom d\\'hôte séparé par un espace; par exemple : \"10 mx.exemple.com\".',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Nombre de résultats par page',\n\t\t\t'description' => 'Nombre de résultats par page ? (0 = Désactive la pagination)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'IP / Port par défaut',\n\t\t\t'description' => 'Quel est l\\'IP / Port par défaut ?',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Dossier(s) de l\\'OpenBasedir',\n\t\t\t'description' => 'Liste de dossiers séparée par des virgules qui sera ajouté à la variable \"OpenBasedir\" des conteneurs vHosts.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Utiliser un tri naturel dans les différentes vues',\n\t\t\t'description' => 'Trier les listes comme web1 -> web2 -> etc ... -> web11 au lieu de web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Dossier \"DocumentRoot\" pour les utilisateurs désactivés',\n\t\t\t'description' => 'Quand un utilisateur est désactivé, ce dossier sera utilisé comme dossier racine pour le serveur Web. Laissez vide pour ne pas créer de vHost et ne rien afficher du tout lorsque l\\'utilisateur est désactivé.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Sauvegarder aussi les mots de passe des comptes e-mails de façon décrypter dans la base de données',\n\t\t\t'description' => 'Si cela est à Oui, tous les mots de passe seront aussi sauvegarder de façon décrypter dans la table mail_users (en texte clair pour toutes personnes qui auraient accès à la base de données). Activer cette option, uniquement si vous en avez vraiment besoin !',\n\t\t\t'removelink' => 'Cliquez ici pour retirer tous les mots de passe en texte clair de la base de données.',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'Comptes FTP @domaine',\n\t\t\t'description' => 'Les utilisateurs peuvent-ils créer des comptes FTP de la forme utilisateur@domaine.com ?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Utiliser PHP par mod_fcgid / suexec',\n\t\t\t'description' => 'Utiliser mod_fcgid / suexec / libnss_mysql pour lancer PHP avec le compte correspondant à l\\'utilisateur ?<br/><b>Cela à besoin d\\'une configuration spécifique d\\'Apache !</b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Dossier de configuration FCGI',\n\t\t\t\t'description' => 'Oû doivent être stockés les fichiers de configuration pour FCGI ?',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Dossier temporaire pour FCGI',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Utiliser une adresse e-mail alternative',\n\t\t\t'description' => 'Envoyer le mot de passe du compte e-mail à une adresse différents pour la création du compte e-mail ?',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Dossier / fichier de configuration des vHosts pour Apache',\n\t\t\t'description' => 'Oû doit être stocké le fichier de configuration des vHosts ? Vous pouvez soit entrer le nom d\\'un fichier (tous les vHosts dans un seul fichier), soit le nom d\\'un dossier (chacun des vHosts dans un fichier séparé du dossier).',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Fichier / dossier de configuration des options des dossiers pour Apache',\n\t\t\t'description' => 'Oû doit être stocké le fichier de configuration des options de dossiers ? Vous pouvez soit entrer le nom d\\'un fichier (toutes les options des dossiers dans un seul fichier), soit le nom d\\'un dossier (chacune des options de dossier dans un fichier séparé du dossier).',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Dossier du fichier htpasswd pour Apache',\n\t\t\t'description' => 'Oû doit être stocké le fichier de configuration de protection des dossiers \"htpasswd\" pour Apache ?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'Hôtes de connexion MySQL',\n\t\t\t'description' => 'Une liste séparée par des virgules contenant la liste des hôtes depuis lesquels les utilisateurs sont autorisés à se connecter au serveur MySQL.',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Sortie Webalizer',\n\t\t\t'description' => 'Verbosité du programme Webalizer',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Activer / Désactiver les logs',\n\t\t\t'severity' => 'Niveau de log',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Type(s) de log',\n\t\t\t\t'description' => 'Spécifiez les types de log séparés par des virgules.<br />Les types de log disponible sont : syslog, file, mysql',\n\t\t\t],\n\t\t\t'logfile' => 'Nom du fichier de log, dossier + nom du fichier',\n\t\t\t'logcron' => 'Loguer les travaux de cron',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Jamais',\n\t\t\t\t'once' => 'Une fois',\n\t\t\t\t'always' => 'Toujours',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'openssl_cnf' => 'Paramètres par défaut pour créer le certificat',\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Paramètres par défaut pour les vHosts',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Quota de la boîte aux lettres',\n\t\t\t'description' => 'Quota par défaut pour toutes nouvelles boîtes aux lettres créées.',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Utiliser les quotas de boîtes aux lettres pour les clients',\n\t\t\t'description' => 'Activez cette option pour utiliser les quotas sur les boîtes aux lettres. Par défaut, cette option est à <b>Non</b> car cela requiert une configuration spécifique.',\n\t\t\t'removelink' => 'Cliquez ici pour retirer tous les quotas de tous les comptes e-mails.',\n\t\t],\n\t\t'decimal_places' => 'Nombre de décimales à afficher pour le trafic / espace web',\n\t\t'webalizer_enabled' => 'Activer les statistiques Webalizer',\n\t\t'awstats_enabled' => 'Activer les statistiques AWStats',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Paramètres manuel des DNS du domaine',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Permettre aux clients de modifier les paramètes DNS du domaine',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Utiliser des noms d\\'utilisateurs compatible UNIX',\n\t\t\t'description' => 'Vous permet d\\'utiliser les <strong>-</strong> et <strong>_</strong> dans les noms d\\'utilisateurs si l\\'option est à <strong>Non</strong>',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Permettre aux clients de réinitialiser leurs mots de passe',\n\t\t\t'description' => 'Les clients peuvent réinitialiser leurs mots de passe et il sera envoyé à leurs propres adresses e-mails',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Permettre la réinitialisation des mots de passe par les administrateurs',\n\t\t\t'description' => 'Les administrateurs / revendeurs peuvent réinitialiser leurs mots de passe et il sera envoyé à leurs propres adresses e-mails',\n\t\t],\n\t],\n\t'traffic' => [\n\t\t'month' => 'Mois',\n\t\t'day' => 'Jour',\n\t\t'months' => [\n\t\t\t1 => 'Janvier',\n\t\t\t2 => 'Février',\n\t\t\t3 => 'Mars',\n\t\t\t4 => 'Avril',\n\t\t\t5 => 'Mai',\n\t\t\t6 => 'Juin',\n\t\t\t7 => 'Juillet',\n\t\t\t8 => 'Août',\n\t\t\t9 => 'Septembre',\n\t\t\t10 => 'Octobre',\n\t\t\t11 => 'Novembre',\n\t\t\t12 => 'Décembre',\n\t\t],\n\t\t'mb' => 'Trafic (Mo)',\n\t\t'distribution' => '<font color=\"#019522\">FTP</font> | <font color=\"#0000FF\">HTTP</font> | <font color=\"#800000\">E-mail</font>',\n\t\t'sumhttp' => 'Trafic HTTP total entrant',\n\t\t'sumftp' => 'Trafic FTP total entrant',\n\t\t'summail' => 'Trafic E-mail total entrant',\n\t],\n\t'translator' => 'Tim Zielosko, Aldo Reset, Romain MARIADASSOU',\n];\n"
  },
  {
    "path": "lng/hu.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'languages' => [\n\t\t'cz' => 'Czech',\n\t\t'de' => 'German',\n\t\t'en' => 'English',\n\t\t'fr' => 'French',\n\t\t'hu' => 'Hungarian',\n\t\t'it' => 'Italian',\n\t\t'nl' => 'Dutch',\n\t\t'pt' => 'Portuguese',\n\t\t'se' => 'Swedish',\n\t\t'sk' => 'Slovak',\n\t\t'es' => 'Spanish',\n\t\t'ca' => 'Catalan',\n\t\t'zh_CN' => 'Chinese (Simplified)',\n\t],\n\t'2fa' => [\n\t\t'2fa' => '2FA beállítások',\n\t\t'2fa_enabled' => 'Kétfaktoros hitelesítés (2FA) aktiválása',\n\t\t'2fa_removed' => '2FA sikeresen eltávolítva',\n\t\t'2fa_added' => '2FA sikeresen aktiválva<br><a class=\"alert-link\" href=\"%s?page=2fa\">2FA részletek megtekintése</a>',\n\t\t'2fa_add' => '2FA aktiválása',\n\t\t'2fa_delete' => '2FA deaktiválása',\n\t\t'2fa_verify' => 'Kód ellenőrzése',\n\t\t'2fa_overview_desc' => 'Itt aktiválhatja a kétfaktoros hitelesítést a fiókjához.<br><br>Használhat hitelesítő alkalmazást (időalapú egyszeri jelszó / TOTP), vagy hagyhatja, hogy a froxlor minden sikeres bejelentkezés után e-mailt küldjön a fiókjához tartozó címre egy egyszeri jelszóval.',\n\t\t'2fa_email_desc' => 'Fiókja be van állítva egyszeri jelszavak használatára e-mailen keresztül. A deaktiváláshoz kattintson a \"2FA deaktiválása\" gombra',\n\t\t'2fa_ga_desc' => 'Fiókja be van állítva időalapú egyszeri jelszavak használatára hitelesítő alkalmazáson keresztül. Kérjük, olvassa be az alábbi QR-kódot a kívánt hitelesítő alkalmazással a kódok generálásához. A deaktiváláshoz kattintson a \"2FA deaktiválása\" gombra',\n\t\t'2fa_not_activated' => 'A kétfaktoros hitelesítés nincs engedélyezve',\n\t\t'2fa_not_activated_for_user' => 'A kétfaktoros hitelesítés nincs engedélyezve a jelenlegi felhasználó számára',\n\t\t'type_2fa' => '2FA állapot',\n\t],\n\t'admin' => [\n\t\t'overview' => 'Áttekintés',\n\t\t'ressourcedetails' => 'Felhasznált erőforrások',\n\t\t'systemdetails' => 'Rendszer részletek',\n\t\t'froxlordetails' => 'froxlor részletek',\n\t\t'installedversion' => 'Telepített verzió',\n\t\t'latestversion' => 'Legújabb verzió',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'Keresés webszolgáltatáson keresztül',\n\t\t\t'error' => 'Hiba az olvasás közben',\n\t\t],\n\t\t'resources' => 'Erőforrások',\n\t\t'customer' => 'Ügyfél',\n\t\t'customers' => 'Ügyfelek',\n\t\t'customers_list_desc' => 'Ügyfelek kezelése',\n\t\t'customer_add' => 'Ügyfél létrehozása',\n\t\t'customer_edit' => 'Ügyfél szerkesztése',\n\t\t'username_default_msg' => 'Hagyja üresen az automatikusan generált értékhez',\n\t\t'password_default_msg' => 'Automatikusan generált, ha üres',\n\t\t'domains' => 'Domainek',\n\t\t'domain_add' => 'Domain létrehozása',\n\t\t'domain_edit' => 'Domain szerkesztése',\n\t\t'subdomainforemail' => 'Aldomainek e-mail domainként',\n\t\t'admin' => 'Admin',\n\t\t'admins' => 'Adminok',\n\t\t'admin_add' => 'Admin létrehozása',\n\t\t'admin_edit' => 'Admin szerkesztése',\n\t\t'customers_see_all' => 'Hozzáférhet más adminok/viszonteladók erőforrásaihoz?',\n\t\t'change_serversettings' => 'Módosíthatja a szerver beállításait?',\n\t\t'server' => 'Rendszer',\n\t\t'serversettings' => 'Beállítások',\n\t\t'serversettings_desc' => 'froxlor rendszer kezelése',\n\t\t'rebuildconf' => 'Konfigurációs fájlok újraépítése',\n\t\t'stdsubdomain' => 'Alapértelmezett aldomain',\n\t\t'stdsubdomain_add' => 'Alapértelmezett aldomain létrehozása',\n\t\t'phpenabled' => 'PHP engedélyezve',\n\t\t'deactivated' => 'Deaktivált',\n\t\t'deactivated_user' => 'Felhasználó deaktiválása',\n\t\t'sendpassword' => 'Jelszó küldése',\n\t\t'ownvhostsettings' => 'Saját vHost-beállítások',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Konfiguráció',\n\t\t\t'overview' => 'Áttekintés',\n\t\t\t'wizard' => 'Varázsló',\n\t\t\t'distribution' => 'Disztribúció',\n\t\t\t'service' => 'Szolgáltatás',\n\t\t\t'daemon' => 'Démon',\n\t\t\t'http' => 'Webszerver (HTTP)',\n\t\t\t'dns' => 'Névszerver (DNS)',\n\t\t\t'mail' => 'Levelezőszerver (IMAP/POP3)',\n\t\t\t'smtp' => 'Levelezőszerver (SMTP)',\n\t\t\t'ftp' => 'FTP-szerver',\n\t\t\t'etc' => 'Egyebek (Rendszer)',\n\t\t\t'choosedistribution' => '-- Válasszon disztribúciót --',\n\t\t\t'chooseservice' => '-- Válasszon szolgáltatást --',\n\t\t\t'choosedaemon' => '-- Válasszon démont --',\n\t\t\t'statistics' => 'Statisztikák',\n\t\t\t'compactoverview' => 'Kompakt áttekintés',\n\t\t\t'legend' => '<h3>Ön egy szolgáltatás/démon konfigurálására készül</h3>',\n\t\t\t'commands' => '<span class=\"text-danger\">Parancsok:</span> Ezeket a parancsokat soronként kell végrehajtani root felhasználóként a parancssorban. Biztonságosan másolhatja és beillesztheti az egész blokkot a parancssorba.',\n\t\t\t'files' => '<span class=\"text-danger\">Konfigurációs fájlok:</span> A szövegmezők előtti parancsoknak meg kell nyitniuk egy szerkesztőt a célfájllal. Csak másolja és illessze be a tartalmakat a szerkesztőbe, majd mentse a fájlt.<br><span class=\"text-danger\">Kérjük, vegye figyelembe:</span> A MySQL-jelszó biztonsági okokból nem lett lecserélve. Kérjük, cserélje le a \"FROXLOR_MYSQL_PASSWORD\"-öt saját maga, vagy használja az alábbi JavaScript űrlapot a helyszíni cseréhez. Ha elfelejtette a MySQL-jelszavát, megtalálja a \"lib/userdata.inc.php\" fájlban.',\n\t\t\t'importexport' => 'Importálás/Exportálás',\n\t\t\t'finishnote' => 'A paraméter fájl sikeresen létrehozva. Most futtassa a következő parancsot root-ként:',\n\t\t\t'description' => 'Rendszerszolgáltatások konfigurálása',\n\t\t\t'minihowto' => 'Ezen az oldalon megtekintheti a különböző konfigurációs sablonokat minden szolgáltatáshoz, szükség esetén (újra)konfigurálhat specifikus szolgáltatásokat, vagy exportálhatja az aktuális kiválasztást egy JSON fájlba, hogy a CLI szkriptekben vagy egy másik szerveren használhassa.<br><br><b>Megjegyzés:</b> a kiemelt szolgáltatások nem tükrözik az aktuális beállításait, hanem az aktuális beállítási értékek alapján mutatják a követelményeket/ajánlásokat.',\n\t\t\t'skipconfig' => 'Ne konfigurálja (újra)',\n\t\t\t'recommendednote' => 'Ajánlott/szükséges szolgáltatások az aktuális rendszerbeállítások alapján',\n\t\t\t'selectrecommended' => 'Ajánlottak kiválasztása',\n\t\t\t'downloadselected' => 'Kiválasztottak exportálása',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'E-mail sablonok',\n\t\t\t'template_add' => 'Sablon hozzáadása',\n\t\t\t'template_fileadd' => 'Fájl sablon hozzáadása',\n\t\t\t'template_edit' => 'Sablon szerkesztése',\n\t\t\t'action' => 'Művelet',\n\t\t\t'email' => 'E-mail és fájl sablonok',\n\t\t\t'subject' => 'Tárgy',\n\t\t\t'mailbody' => 'E-mail törzs',\n\t\t\t'createcustomer' => 'Üdvözlő e-mail új ügyfeleknek',\n\t\t\t'pop_success' => 'Üdvözlő e-mail új e-mail fiókokhoz',\n\t\t\t'template_replace_vars' => 'A sablonban helyettesítendő változók:',\n\t\t\t'SALUTATION' => 'Helyettesítve a megfelelő megszólítással (név vagy cég)',\n\t\t\t'FIRSTNAME' => 'Helyettesítve az ügyfél keresztnevével.',\n\t\t\t'NAME' => 'Helyettesítve az ügyfél nevével.',\n\t\t\t'COMPANY' => 'Helyettesítve az ügyfél cégnevével',\n\t\t\t'USERNAME' => 'Helyettesítve az ügyfél fiókjának felhasználónevével.',\n\t\t\t'PASSWORD' => 'Helyettesítve az ügyfél fiókjának jelszavával.',\n\t\t\t'EMAIL' => 'Helyettesítve a POP3/IMAP fiók címével.',\n\t\t\t'CUSTOMER_NO' => 'Helyettesítve az ügyfélszámmal',\n\t\t\t'TRAFFIC' => 'Helyettesítve a forgalommal, amely az ügyfélhez lett rendelve.',\n\t\t\t'TRAFFICUSED' => 'Helyettesítve a forgalommal, amelyet az ügyfél felhasznált.',\n\t\t\t'pop_success_alternative' => 'Üdvözlő e-mail új e-mail fiókokhoz, alternatív címre küldve',\n\t\t\t'EMAIL_PASSWORD' => 'Helyettesítve a POP3/IMAP fiók jelszavával.',\n\t\t\t'index_html' => 'Index fájl az újonnan létrehozott ügyfél könyvtárakhoz',\n\t\t\t'unconfigured_html' => 'Index fájl a nem konfigurált/ismeretlen domainekhez',\n\t\t\t'unconfigured_content_fallback' => 'Ez a domain konfigurálást igényel a froxlor szerver kezelőpanelen keresztül, mivel jelenleg nincs hozzárendelve egyetlen ügyfélhez sem.',\n\t\t\t'file_extension' => [\n\t\t\t\t'description' => 'Az index fájl kiterjesztésének 1-6 karakter hosszúnak kell lennie. A kiterjesztés csak a-z, A-Z és 0-9 karaktereket tartalmazhat<br><br>Alapértelmezett: html',\n\t\t\t\t'title' => 'Fájl kiterjesztés a fájl sablonhoz',\n\t\t\t],\n\t\t\t'SERVERNAME' => 'Helyettesítve a szerver nevével.',\n\t\t\t'CUSTOMER' => 'Helyettesítve az ügyfél bejelentkezési nevével. Csak az \"index fájl az újonnan létrehozott ügyfél könyvtárakhoz\" esetén',\n\t\t\t'ADMIN' => 'Helyettesítve az admin bejelentkezési nevével. Csak az \"index fájl az újonnan létrehozott ügyfél könyvtárakhoz\" esetén',\n\t\t\t'CUSTOMER_EMAIL' => 'Helyettesítve az ügyfél e-mail címével. Csak az \"index fájl az újonnan létrehozott ügyfél könyvtárakhoz\" esetén',\n\t\t\t'ADMIN_EMAIL' => 'Helyettesítve az admin e-mail címével. Csak az \"index fájl az újonnan létrehozott ügyfél könyvtárakhoz\" esetén',\n\t\t\t'filetemplates' => 'Fájl sablonok',\n\t\t\t'filecontent' => 'Fájl tartalom',\n\t\t\t'new_database_by_customer' => 'Ügyfél-értesítés adatbázis létrehozásakor',\n\t\t\t'new_ftpaccount_by_customer' => 'Ügyfél-értesítés FTP-felhasználó létrehozásakor',\n\t\t\t'newdatabase' => 'Értesítő e-mailek új adatbázisokhoz',\n\t\t\t'newftpuser' => 'Értesítő e-mailek új FTP-felhasználókhoz',\n\t\t\t'CUST_NAME' => 'Ügyfél neve',\n\t\t\t'DB_NAME' => 'Adatbázis neve',\n\t\t\t'DB_PASS' => 'Adatbázis jelszava',\n\t\t\t'DB_DESC' => 'Adatbázis leírása',\n\t\t\t'DB_SRV' => 'Adatbázis szerver',\n\t\t\t'PMA_URI' => 'URL a phpMyAdminhoz (ha meg van adva)',\n\t\t\t'USR_NAME' => 'FTP felhasználónév',\n\t\t\t'USR_PASS' => 'FTP jelszó',\n\t\t\t'USR_PATH' => 'FTP home könyvtár (relatív az ügyfél dokumentumgyökeréhez)',\n\t\t\t'forgotpwd' => 'Értesítő e-mailek jelszó-visszaállításhoz',\n\t\t\t'password_reset' => 'Ügyfél-értesítés jelszó-visszaállításhoz',\n\t\t\t'trafficmaxpercent' => 'Értesítő e-mail ügyfeleknek, amikor a forgalom adott maximális százalékát kimerítették',\n\t\t\t'MAX_PERCENT' => 'Helyettesítve a lemezhasználat/forgalom jelentésküldési határértékével százalékban.',\n\t\t\t'USAGE_PERCENT' => 'Helyettesítve a lemezhasználattal/forgalommal, amelyet az ügyfél kimerített, százalékban.',\n\t\t\t'diskmaxpercent' => 'Értesítő e-mail ügyfeleknek, amikor a lemezterület adott maximális százalékát kimerítették',\n\t\t\t'DISKAVAILABLE' => 'Helyettesítve a lemezhasználattal, amely az ügyfélhez lett rendelve.',\n\t\t\t'DISKUSED' => 'Helyettesítve a lemezhasználattal, amelyet az ügyfél kimerített.',\n\t\t\t'LINK' => 'Helyettesítve az ügyfél jelszó-visszaállítási linkjével.',\n\t\t\t'SERVER_HOSTNAME' => 'Helyettesíti a rendszer-hosztnevet (URL a froxlorhoz)',\n\t\t\t'SERVER_IP' => 'Helyettesíti az alapértelmezett szerver IP-címét',\n\t\t\t'SERVER_PORT' => 'Helyettesíti az alapértelmezett szerver portját',\n\t\t\t'DOMAINNAME' => 'Helyettesíti az ügyfél standard aldomainjét (üres lehet, ha nincs generálva)',\n\t\t],\n\t\t'webserver' => 'Webszerver',\n\t\t'createzonefile' => 'DNS zóna létrehozása a domainhez',\n\t\t'custombindzone' => 'Egyéni / nem kezelt zónafájl',\n\t\t'bindzonewarning' => 'üres az alapértelmezettekhez<br /><strong class=\"text-danger\">FIGYELEM:</strong> Ha zónafájlt használ, manuálisan kell kezelnie az összes szükséges rekordot az összes alzónához is.',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IP-k és Portok',\n\t\t\t'add' => 'IP/Port hozzáadása',\n\t\t\t'edit' => 'IP/Port szerkesztése',\n\t\t\t'ipandport' => 'IP/Port',\n\t\t\t'ip' => 'IP',\n\t\t\t'ipnote' => '<div id=\"ipnote\" class=\"invalid-feedback\">Megjegyzés: Bár privát IP-címek megengedettek, néhány funkció, mint például a DNS, nem biztos, hogy helyesen működik.<br>Csak akkor használjon privát IP-címeket, ha biztos benne.</div>',\n\t\t\t'port' => 'Port',\n\t\t\t'create_listen_statement' => 'Listen utasítás létrehozása',\n\t\t\t'create_namevirtualhost_statement' => 'NameVirtualHost utasítás létrehozása',\n\t\t\t'create_vhostcontainer' => 'vHost-Container létrehozása',\n\t\t\t'create_vhostcontainer_servername_statement' => 'ServerName utasítás létrehozása a vHost-Containerben',\n\t\t\t'enable_ssl' => 'Ez egy SSL Port?',\n\t\t\t'ssl_cert_file' => 'Az SSL tanúsítvány elérési útja',\n\t\t\t'webserverdefaultconfig' => 'Webszerver alapértelmezett konfiguráció',\n\t\t\t'webserverdomainconfig' => 'Webszerver domain konfiguráció',\n\t\t\t'webserverssldomainconfig' => 'Webszerver SSL konfiguráció',\n\t\t\t'ssl_key_file' => 'Az SSL kulcsfájl elérési útja',\n\t\t\t'ssl_ca_file' => 'Az SSL CA tanúsítvány elérési útja',\n\t\t\t'default_vhostconf_domain' => 'Alapértelmezett vHost-beállítások minden domain containerhez',\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'Az SSL CertificateChainFile elérési útja',\n\t\t\t\t'description' => 'Többnyire CA_Bundle, vagy hasonló, valószínűleg be kell állítania, ha SSL tanúsítványt vásárolt.',\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Egyéni docroot (üres = froxlorra mutat)',\n\t\t\t\t'description' => 'Meghatározhat egy egyéni dokumentum-gyökeret (a kérés célállomása) ehhez az IP/port kombinációhoz.<br /><strong>FIGYELEM:</strong> Kérjük, legyen óvatos azzal, amit ide beír!',\n\t\t\t],\n\t\t\t'ssl_paste_description' => 'Illessze be a teljes tanúsítvány tartalmát a szövegdobozba',\n\t\t\t'ssl_cert_file_content' => 'Az SSL tanúsítvány tartalma',\n\t\t\t'ssl_key_file_content' => 'Az SSL (privát) kulcsfájl tartalma',\n\t\t\t'ssl_ca_file_content' => 'Az SSL CA fájl tartalma (opcionális)',\n\t\t\t'ssl_ca_file_content_desc' => '<br /><br />Ügyfél-hitelesítés, csak akkor állítsa be, ha tudja, mi az.',\n\t\t\t'ssl_cert_chainfile_content' => 'A tanúsítvány láncfájl tartalma (opcionális)',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br /><br />Többnyire CA_Bundle, vagy hasonló, valószínűleg be kell állítania, ha SSL tanúsítványt vásárolt.',\n\t\t\t'ssl_default_vhostconf_domain' => 'Alapértelmezett SSL vHost-beállítások minden domain containerhez',\n\t\t],\n\t\t'memorylimitdisabled' => 'Letiltva',\n\t\t'valuemandatory' => 'Ez az érték kötelező',\n\t\t'valuemandatorycompany' => 'Vagy a \"név\" és \"keresztnév\", vagy a \"cég\" mezőt ki kell tölteni',\n\t\t'serversoftware' => 'Szerver szoftver',\n\t\t'phpversion' => 'PHP-verzió',\n\t\t'mysqlserverversion' => 'MySQL szerver verzió',\n\t\t'webserverinterface' => 'Webszerver interfész',\n\t\t'accountsettings' => 'Fiók beállítások',\n\t\t'panelsettings' => 'Panel beállítások',\n\t\t'systemsettings' => 'Rendszer beállítások',\n\t\t'webserversettings' => 'Webszerver beállítások',\n\t\t'mailserversettings' => 'Mail szerver beállítások',\n\t\t'nameserversettings' => 'Névszerver beállítások',\n\t\t'updatecounters' => 'Erőforrás-használat újraszámítása',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Soha',\n\t\t\t'choosableno' => 'Választható, alapértelmezett nem',\n\t\t\t'choosableyes' => 'Választható, alapértelmezett igen',\n\t\t\t'always' => 'Mindig',\n\t\t],\n\t\t'wipecleartextmailpwd' => 'Tiszta szöveges jelszavak törlése',\n\t\t'webalizersettings' => 'Webalizer beállítások',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normál',\n\t\t\t'quiet' => 'Csendes',\n\t\t\t'veryquiet' => 'Nincs kimenet',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Jelenleg nem lehet domaint hozzáadni. Először legalább egy ügyfelet kell hozzáadnia.',\n\t\t'loggersettings' => 'Naplózási beállítások',\n\t\t'logger' => [\n\t\t\t'normal' => 'normál',\n\t\t\t'paranoid' => 'paranoid',\n\t\t],\n\t\t'emaildomain' => 'Email domain',\n\t\t'email_only' => 'Csak email?',\n\t\t'wwwserveralias' => '\"www.\" ServerAlias hozzáadása',\n\t\t'subject' => 'Tárgy',\n\t\t'recipient' => 'Címzett',\n\t\t'message' => 'Üzenet írása',\n\t\t'text' => 'Üzenet',\n\t\t'sslsettings' => 'SSL beállítások',\n\t\t'specialsettings_replacements' => 'A következő változókat használhatja:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (ha alkalmazható)<br/>',\n\t\t'antispam_settings' => 'Spam elleni beállítások',\n\t\t'caneditphpsettings' => 'Módosíthatja a PHP-hoz kapcsolódó domain beállításokat?',\n\t\t'allips' => 'Összes IP',\n\t\t'awstatssettings' => 'AWstats beállítások',\n\t\t'domain_dns_settings' => 'Domain DNS beállítások',\n\t\t'activated' => 'Aktiválva',\n\t\t'statisticsettings' => 'Statisztikai beállítások',\n\t\t'or' => 'vagy',\n\t\t'sysload' => 'Rendszer terhelés',\n\t\t'noloadavailable' => 'nem elérhető',\n\t\t'nouptimeavailable' => 'nem elérhető',\n\t\t'nosubject' => '(Nincs tárgy)',\n\t\t'security_settings' => 'Biztonsági beállítások',\n\t\t'know_what_youre_doing' => 'Csak akkor változtasson, ha tudja, mit csinál!',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'froxlor verzió megjelenítése bejelentkezéskor',\n\t\t\t'description' => 'A froxlor verzió megjelenítése a láblécben a bejelentkezési oldalon',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'froxlor verzió megjelenítése a láblécben',\n\t\t\t'description' => 'A froxlor verzió megjelenítése a láblécben az oldalak többi részén',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'froxlor fejléc grafika',\n\t\t\t'description' => 'Milyen grafikát kell megjeleníteni a fejlécben',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'PHP konfiguráció',\n\t\t\t'description' => 'Rövid leírás',\n\t\t\t'actions' => 'Műveletek',\n\t\t\t'activedomains' => 'Használatban lévő domain(ek)',\n\t\t\t'notused' => 'Konfiguráció nincs használatban',\n\t\t\t'editsettings' => 'PHP beállítások módosítása',\n\t\t\t'addsettings' => 'Új PHP beállítások létrehozása',\n\t\t\t'viewsettings' => 'PHP beállítások megtekintése',\n\t\t\t'phpinisettings' => 'php.ini beállítások',\n\t\t\t'addnew' => 'Új PHP konfiguráció létrehozása',\n\t\t\t'binary' => 'PHP bináris',\n\t\t\t'fpmdesc' => 'PHP-FPM konfiguráció',\n\t\t\t'file_extensions' => 'Fájl kiterjesztések',\n\t\t\t'file_extensions_note' => '(pont nélkül, szóközzel elválasztva)',\n\t\t\t'enable_slowlog' => 'Slowlog engedélyezése (domainenként)',\n\t\t\t'request_terminate_timeout' => 'Kérés megszakítási időkorlát',\n\t\t\t'request_slowlog_timeout' => 'Slowlog kérés időkorlát',\n\t\t\t'activephpconfigs' => 'Használatban lévő PHP-konfiguráció(k)',\n\t\t\t'pass_authorizationheader' => 'HTTP AUTH BASIC/DIGEST fejléc továbbítása Apache-ból PHP-ba',\n\t\t],\n\t\t'misc' => 'Egyéb',\n\t\t'fpmsettings' => [\n\t\t\t'addnew' => 'Új PHP verzió létrehozása',\n\t\t\t'edit' => 'PHP verzió módosítása'\n\t\t],\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'A konfigurációkban lecserélendő változók',\n\t\t\t'pear_dir' => 'A globális beállításra lesz lecserélve a pear könyvtárhoz.',\n\t\t\t'open_basedir_c' => 'Beszúr egy ; (pontosvesszőt) az open_basedir kikommenteléséhez/letiltásához, ha be van állítva',\n\t\t\t'open_basedir' => 'A domain open_basedir beállítására lesz lecserélve.',\n\t\t\t'tmp_dir' => 'A domain ideiglenes könyvtárára lesz lecserélve.',\n\t\t\t'open_basedir_global' => 'A globális értékre lesz lecserélve, amely az open_basedir-hez lesz csatolva (lásd webszerver beállítások).',\n\t\t\t'customer_email' => 'A domain tulajdonosának e-mail címére lesz lecserélve.',\n\t\t\t'admin_email' => 'A domain adminisztrátorának e-mail címére lesz lecserélve.',\n\t\t\t'domain' => 'A domainre lesz lecserélve.',\n\t\t\t'customer' => 'A domain tulajdonosának bejelentkezési nevére lesz lecserélve.',\n\t\t\t'admin' => 'A domain adminisztrátorának bejelentkezési nevére lesz lecserélve.',\n\t\t\t'docroot' => 'A domain dokumentum-gyökerére lesz lecserélve.',\n\t\t\t'homedir' => 'Az ügyfél otthoni könyvtárára lesz lecserélve.',\n\t\t],\n\t\t'expert_settings' => 'Haladó beállítások!',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'PHP folyamatok ehhez a domainhez (üres az alapértelmezett értékhez)',\n\t\t],\n\t\t'phpserversettings' => 'PHP beállítások',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Maximális PHP kérések ehhez a domainhez (üres az alapértelmezett értékhez)',\n\t\t],\n\t\t'spfsettings' => 'Domain SPF beállítások',\n\t\t'specialsettingsforsubdomains' => 'Speciális beállítások alkalmazása minden aldomainre (*.example.com)',\n\t\t'accountdata' => 'Fiók adatok',\n\t\t'contactdata' => 'Kapcsolati adatok',\n\t\t'servicedata' => 'Szolgáltatási adatok',\n\t\t'newerversionavailable' => 'Újabb froxlor verzió érhető el.',\n\t\t'newerversiondetails' => 'Frissítés a <b>%s</b> verzióra most?<br/>(A jelenlegi verzió: %s)',\n\t\t'extractdownloadedzip' => 'Letöltött archívum kibontása \"%s\"?',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Cronjob beállítások',\n\t\t\t'add' => 'Cronjob hozzáadása',\n\t\t],\n\t\t'cronjob_edit' => 'Cronjob szerkesztése',\n\t\t'warning' => 'FIGYELEM - Kérjük, vegye figyelembe!',\n\t\t'lastlogin_succ' => 'Utolsó bejelentkezés',\n\t\t'ftpserver' => 'FTP Szerver',\n\t\t'ftpserversettings' => 'FTP Szerver beállítások',\n\t\t'webserver_user' => 'Webszerver felhasználónév',\n\t\t'webserver_group' => 'Webszerver csoportnév',\n\t\t'perlenabled' => 'Perl engedélyezve',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Helyi felhasználó az FCGID-hez (froxlor vHost)',\n\t\t'mod_fcgid_group' => 'Helyi csoport az FCGID-hez (froxlor vHost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[nincs megadva]',\n\t\t'store_defaultindex' => 'Alapértelmezett index-fájl tárolása az ügyfél dokumentumgyökerébe',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => 'Forgalom',\n\t\t'traffic_sub' => 'Részletek a forgalom használatáról',\n\t\t'domaintraffic' => 'Domainek',\n\t\t'customertraffic' => 'Ügyfelek',\n\t\t'assignedmax' => 'Hozzárendelt / Max',\n\t\t'usedmax' => 'Használt / Max',\n\t\t'used' => 'Használt',\n\t\t'speciallogwarning' => '<div id=\"speciallogfilenote\" class=\"invalid-feedback\">FIGYELEM: E beállítás megváltoztatásával elveszíti az összes régi statisztikáját ehhez a domainhez.</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => 'Külön naplófájl',\n\t\t\t'description' => 'Engedélyezze ezt, hogy külön hozzáférési naplófájlt kapjon ehhez a domainhez',\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => 'Domain szerkesztésének engedélyezése',\n\t\t\t'desc' => 'Ha igenre van állítva, az ügyfél módosíthatja a domain beállításait.<br />Ha nemre van állítva, az ügyfél semmit sem módosíthat.',\n\t\t],\n\t\t'writeaccesslog' => [\n\t\t\t'title' => 'Hozzáférési napló írása',\n\t\t\t'description' => 'Engedélyezze ezt, hogy hozzáférési naplófájlt kapjon ehhez a domainhez',\n\t\t],\n\t\t'writeerrorlog' => [\n\t\t\t'title' => 'Hibanapló írása',\n\t\t\t'description' => 'Engedélyezze ezt, hogy hibanapló-fájlt kapjon ehhez a domainhez',\n\t\t],\n\t\t'phpfpm.ininote' => 'Nem minden érték, amelyet meg szeretne határozni, használható a php-fpm pool konfigurációban',\n\t\t'phpinfo' => 'PHPinfo()',\n\t\t'selectserveralias' => 'ServerAlias érték a domainhez',\n\t\t'selectserveralias_desc' => 'Válassza ki, hogy a froxlor hozzon létre egy wildcard-bejegyzést (*.domain.tld), egy WWW-alias-t (www.domain.tld) vagy egyáltalán ne hozzon létre alias-t',\n\t\t'show_news_feed' => [\n\t\t\t'title' => 'Hírcsatorna megjelenítése az admin irányítópulton',\n\t\t\t'description' => 'Engedélyezze ezt, hogy megjelenítse a hivatalos froxlor hírcsatornát (https://inside.froxlor.org/news/) az irányítópultján, és soha ne maradjon le fontos információkról vagy kiadási bejelentésekről.',\n\t\t],\n\t\t'cronsettings' => 'Cronjob beállítások',\n\t\t'integritycheck' => 'Adatbázis érvényesítés',\n\t\t'integrityname' => 'Név',\n\t\t'integrityresult' => 'Eredmény',\n\t\t'integrityfix' => 'Problémák automatikus javítása',\n\t\t'customer_show_news_feed' => 'Hírcsatorna megjelenítése az ügyfél irányítópultján',\n\t\t'customer_news_feed_url' => [\n\t\t\t'title' => 'Egyéni RSS-hírcsatorna használata',\n\t\t\t'description' => 'Adjon meg egy egyéni RSS-hírcsatornát, amelyet az ügyfelei irányítópultján jelenít meg.<br /><small>Hagyja üresen, hogy a hivatalos froxlor hírcsatornát használja (https://inside.froxlor.org/news/).</small>',\n\t\t],\n\t\t'movetoadmin' => 'Ügyfél áthelyezése',\n\t\t'movecustomertoadmin' => [\n\t\t\t'title' => 'Ügyfél áthelyezése a kiválasztott adminisztrátorhoz/viszonteladóhoz',\n\t\t\t'description' => 'Hagyja üresen, ha nem kíván változtatni.<br />Ha a kívánt adminisztrátor nem jelenik meg a listában, akkor az ügyfélkorlátja elérte a maximumot.',\n\t\t],\n\t\t'note' => 'Megjegyzés',\n\t\t'mod_fcgid_umask' => [\n\t\t\t'title' => 'Umask (alapértelmezett: 022)',\n\t\t],\n\t\t'apcuinfo' => 'APCu információ',\n\t\t'opcacheinfo' => 'OPcache információ',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Let\\'s Encrypt használata',\n\t\t\t'description' => 'Szerezzen ingyenes tanúsítványt a <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a> szolgáltatástól. A tanúsítvány automatikusan létrejön és megújul.<br><strong class=\"text-danger\">FIGYELEM:</strong> Ha a wildcardok engedélyezve vannak, ez az opció automatikusan letiltásra kerül.',\n\t\t],\n\t\t'autoupdate' => 'Automatikus frissítés',\n\t\t'server_php' => 'PHP',\n\t\t'dnsenabled' => 'DNS szerkesztő engedélyezése',\n\t\t'froxlorvhost' => 'froxlor VirtualHost beállítások',\n\t\t'hostname' => 'Hosztnév',\n\t\t'memory' => 'Memória használat',\n\t\t'webserversettings_ssl' => 'Webszerver SSL beállítások',\n\t\t'domain_hsts_maxage' => [\n\t\t\t'title' => 'HTTP Strict Transport Security (HSTS)',\n\t\t\t'description' => 'Adja meg a Strict-Transport-Security fejléc max-age értékét<br>A <i>0</i> érték letiltja a HSTS-t a domainhez. A legtöbb felhasználó <i>31536000</i> (egy év) értéket állít be.',\n\t\t],\n\t\t'domain_hsts_incsub' => [\n\t\t\t'title' => 'HSTS alkalmazása minden aldomainre',\n\t\t\t'description' => 'Az opcionális \"includeSubDomains\" direktíva, ha jelen van, jelzi a felhasználói ügynöknek, hogy a HSTS politika érvényes erre a HSTS hosztra, valamint a hoszt domain nevének bármely aldomainjére.',\n\t\t],\n\t\t'domain_hsts_preload' => [\n\t\t\t'title' => 'Domain felvétele a HSTS előtöltési listába',\n\t\t\t'description' => 'Ha szeretné, hogy ez a domain szerepeljen a Chrome által karbantartott <a href=\"https://hstspreload.org/\" target=\"_blank\">HSTS előtöltési listában</a> (amit a Firefox és a Safari is használ), akkor aktiválja ezt.<br>A preload direktíva küldése az oldaláról ÁLLANDÓ KÖVETKEZMÉNYEKKEL járhat, és megakadályozhatja a felhasználókat abban, hogy hozzáférjenek az oldalához és annak bármely aldomainjéhez.<br>Kérjük, olvassa el a részleteket a <a href=\"https://hstspreload.org/#removal\" target=\"_blank\">https://hstspreload.org/#removal</a> oldalon, mielőtt a fejlécet \"preload\" értékkel küldené.',\n\t\t],\n\t\t'domain_ocsp_stapling' => [\n\t\t\t'title' => 'OCSP stapling',\n\t\t\t'description' => 'Lásd <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/OCSP_stapling\">Wikipedia</a> az OCSP stapling részletes magyarázatáért',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">FIGYELEM:</strong> Az OCSP staplinghez legalább 1.3.7-es Nginx verzió szükséges. Ha a verziója régebbi, a webszerver NEM fog megfelelően elindulni, amíg az OCSP stapling engedélyezve van!',\n\t\t],\n\t\t'domain_http2' => [\n\t\t\t'title' => 'HTTP2 támogatás',\n\t\t\t'description' => 'Lásd <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/2\">Wikipedia</a> a HTTP2 részletes magyarázatáért',\n\t\t],\n\t\t'domain_http3' => [\n\t\t\t'title' => 'HTTP3 támogatás',\n\t\t\t'description' => 'Lásd <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/3\">Wikipedia</a> a HTTP3 részletes magyarázatáért',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">FIGYELEM:</strong> Nginx 1.25.0 vagy újabb verzió és ssl-protocol TLSv1.3 szükséges a HTTP/3-hoz. Ha a verziója régebbi, a webszerver NEM fog megfelelően elindulni, amíg a HTTP/3 engedélyezve van!',\n\t\t],\n\t\t'testmail' => 'SMTP teszt',\n\t\t'phpsettingsforsubdomains' => 'PHP-konfiguráció alkalmazása minden aldomainre:',\n\t\t'plans' => [\n\t\t\t'name' => 'Csomag neve',\n\t\t\t'description' => 'Leírás',\n\t\t\t'last_update' => 'Utolsó frissítés',\n\t\t\t'plans' => 'Tárhely csomagok',\n\t\t\t'plan_details' => 'Csomag részletei',\n\t\t\t'add' => 'Új csomag hozzáadása',\n\t\t\t'edit' => 'Csomag szerkesztése',\n\t\t\t'use_plan' => 'Csomag alkalmazása',\n\t\t],\n\t\t'notryfiles' => [\n\t\t\t'title' => 'Nincs automatikusan generált try_files',\n\t\t\t'description' => 'Mondjon igent itt, ha egyedi try_files direktívát szeretne megadni a speciális beállításokban (szükséges néhány WordPress bővítményhez például).',\n\t\t],\n\t\t'logviewenabled' => 'Hozzáférés engedélyezése a hozzáférési/hiba-naplókhoz',\n\t\t'novhostcontainer' => '<br><br><small class=\"text-danger\">Egyik IP és port sem rendelkezik a \"vHost-Container létrehozása\" opcióval, sok beállítás itt nem lesz elérhető</small>',\n\t\t'ownsslvhostsettings' => 'Saját SSL vHost-beállítások',\n\t\t'domain_override_tls' => 'Rendszer TLS beállítások felülírása',\n\t\t'domain_override_tls_addinfo' => '<br /><span class=\"text-danger\">Csak akkor használatos, ha a \"Rendszer TLS beállítások felülírása\" \"Igen\"-re van állítva</span>',\n\t\t'domain_sslenabled' => 'SSL használatának engedélyezése',\n\t\t'domain_honorcipherorder' => 'A (szerver) titkosítási sorrend tiszteletben tartása, alapértelmezett <strong>nem</strong>',\n\t\t'domain_sessiontickets' => 'TLS sessiontickets engedélyezése (RFC 5077), alapértelmezett <strong>igen</strong>',\n\t\t'domain_sessionticketsenabled' => [\n\t\t\t'title' => 'TLS sessiontickets használatának globális engedélyezése',\n\t\t\t'description' => 'Alapértelmezett <strong>igen</strong><br>Apache-2.4.11+ vagy nginx-1.5.9+ szükséges',\n\t\t],\n\t\t'domaindefaultalias' => 'Alapértelmezett ServerAlias érték új domainekhez',\n\t\t'smtpsettings' => 'SMTP beállítások',\n\t\t'smtptestaddr' => 'Teszt e-mail küldése ide',\n\t\t'smtptestnote' => 'Vegye figyelembe, hogy az alábbi értékek a jelenlegi beállításait tükrözik, és csak ott módosíthatók (lásd a jobb felső sarokban található linket)',\n\t\t'smtptestsend' => 'Teszt e-mail küldése',\n\t\t'mysqlserver' => [\n\t\t\t'mysqlserver' => 'MySQL Szerver',\n\t\t\t'dbserver' => 'Szerver #',\n\t\t\t'caption' => 'Leírás',\n\t\t\t'host' => 'Hosztnév / IP',\n\t\t\t'port' => 'Port',\n\t\t\t'user' => 'Kiváltságos felhasználó',\n\t\t\t'add' => 'Új MySQL szerver hozzáadása',\n\t\t\t'edit' => 'MySQL szerver szerkesztése',\n\t\t\t'password' => 'Kiváltságos felhasználó jelszava',\n\t\t\t'password_emptynochange' => 'Új jelszó, hagyja üresen, ha nem kíván változtatni',\n\t\t\t'allowall' => [\n\t\t\t\t'title' => 'Engedélyezze ennek a szervernek a használatát minden jelenleg létező ügyfél számára',\n\t\t\t\t'description' => 'Állítsa \"igaz\"-ra, ha azt szeretné, hogy minden jelenleg létező ügyfél használhassa ezt az adatbázis-szervert, így adatbázisokat adhatnak hozzá. Ez a beállítás nem állandó, de többször is futtatható.',\n\t\t\t],\n\t\t\t'testconn' => 'Kapcsolat tesztelése mentéskor',\n\t\t\t'ssl' => 'SSL használata az adatbázis-szerverhez való kapcsolódáshoz',\n\t\t\t'ssl_cert_file' => 'Az SSL tanúsítvány hatóság fájl elérési útja',\n\t\t\t'verify_ca' => 'A szerver SSL tanúsítványának ellenőrzésének engedélyezése',\n\t\t],\n\t\t'settings_importfile' => 'Import fájl kiválasztása',\n\t\t'documentation' => 'Dokumentáció',\n\t\t'adminguide' => 'Adminisztrátori útmutató',\n\t\t'userguide' => 'Felhasználói útmutató',\n\t\t'apiguide' => 'API útmutató',\n\t\t'domain_duplicate' => 'Domain másolása',\n\t\t'domain_duplicate_named' => 'Másolat %s',\n\t\t'backups' => [\n\t\t\t'backups' => 'Biztonsági mentések',\n\t\t],\n\t\t'emaildomainwarning' => '<div id=\"emaildomainnote\" class=\"invalid-feedback\">FIGYELEM: E beállítás megváltoztatásával véglegesen törli az összes meglévő e-mail címet és fiókot.</div>',\n\t],\n\t'apcuinfo' => [\n\t\t'clearcache' => 'APCu gyorsítótár törlése',\n\t\t'generaltitle' => 'Általános gyorsítótár információ',\n\t\t'version' => 'APCu verzió',\n\t\t'phpversion' => 'PHP verzió',\n\t\t'host' => 'APCu hoszt',\n\t\t'sharedmem' => 'Megosztott memória',\n\t\t'sharedmemval' => '%d szegmens %s (%s memória)',\n\t\t'start' => 'Indítási idő',\n\t\t'uptime' => 'Üzemidő',\n\t\t'upload' => 'Fájl feltöltés támogatás',\n\t\t'cachetitle' => 'Gyorsítótár információ',\n\t\t'cvar' => 'Gyorsítótárazott változók',\n\t\t'hit' => 'Találatok',\n\t\t'miss' => 'Hiányzások',\n\t\t'reqrate' => 'Kérés arány (találatok, hiányzások)',\n\t\t'creqsec' => 'gyorsítótár kérések/másodperc',\n\t\t'hitrate' => 'Találati arány',\n\t\t'missrate' => 'Hiányzási arány',\n\t\t'insrate' => 'Beillesztési arány',\n\t\t'cachefull' => 'Gyorsítótár telítettség számláló',\n\t\t'runtime' => 'Futási beállítások',\n\t\t'memnote' => 'Memória használat',\n\t\t'total' => 'Összesen',\n\t\t'free' => 'Szabad',\n\t\t'used' => 'Használt',\n\t\t'hitmiss' => 'Találatok és hiányzások',\n\t\t'detailmem' => 'Részletes memória használat és töredezettség',\n\t\t'fragment' => 'Töredezettség',\n\t\t'nofragment' => 'Nincs töredezettség',\n\t\t'fragments' => 'Töredékek',\n\t],\n\t'apikeys' => [\n\t\t'no_api_keys' => 'Nincs API kulcs található',\n\t\t'key_add' => 'Új kulcs hozzáadása',\n\t\t'apikey_removed' => 'Az API kulcs #%s azonosítóval sikeresen eltávolítva',\n\t\t'apikey_added' => 'Egy új API kulcs sikeresen létrehozva',\n\t\t'clicktoview' => 'Kattintson a megtekintéshez',\n\t\t'allowed_from' => 'Engedélyezett innen',\n\t\t'allowed_from_help' => 'Vesszővel elválasztott IP címek / hálózatok listája.<br>Alapértelmezett üres (mindenhonnan engedélyezett).',\n\t\t'valid_until' => 'Érvényes eddig',\n\t\t'valid_until_help' => 'Dátum, ameddig érvényes, formátum: ÉÉÉÉ-HH-NNÓÓ:PP',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Régi jelszó',\n\t\t'new_password' => 'Új jelszó',\n\t\t'new_password_confirm' => 'Jelszó megerősítése',\n\t\t'new_password_ifnotempty' => 'Új jelszó (üres = nincs változás)',\n\t\t'also_change_ftp' => ' az FTP főfiók jelszavának megváltoztatása is',\n\t\t'also_change_stats' => ' a statisztikai oldal jelszavának megváltoztatása is',\n\t\t'also_change_global_mysql' => 'a globális MySQL fiók jelszavának megváltoztatása is',\n\t],\n\t'cron' => [\n\t\t'cronname' => 'cronjob-név',\n\t\t'lastrun' => 'utolsó futás',\n\t\t'interval' => 'időköz',\n\t\t'isactive' => 'engedélyezett',\n\t\t'description' => 'leírás',\n\t\t'changewarning' => 'Ezeknek az értékeknek a megváltoztatása negatív hatással lehet a froxlor és az automatizált feladatok működésére.<br />Kérjük, csak akkor változtasson itt értékeket, ha biztos benne, hogy tudja, mit csinál.',\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'nincs megadott leírás',\n\t\t'cron_tasks' => 'Konfigurációs fájlok generálása',\n\t\t'cron_legacy' => 'régi (legacy) cronjob',\n\t\t'cron_traffic' => 'Forgalom számítás',\n\t\t'cron_usage_report' => 'Web- és forgalmi jelentések',\n\t\t'cron_mailboxsize' => 'Postafiók méret számítás',\n\t\t'cron_letsencrypt' => 'Let\\'s Encrypt tanúsítvány frissítések',\n\t\t'cron_export' => 'Adat-export feladatok feldolgozása',\n\t\t'cron_backup' => 'Rendszer- és ügyfél biztonsági mentési feladatok feldolgozása',\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'Cronjob beállítások',\n\t\t'cronjobintervalv' => 'Futási időköz érték',\n\t\t'cronjobinterval' => 'Futási időköz',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Még nem futott',\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => 'percek',\n\t\t'hours' => 'órák',\n\t\t'days' => 'napok',\n\t\t'weeks' => 'hetek',\n\t\t'months' => 'hónapok',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Kezdőkönyvtár',\n\t\t'name' => 'Név',\n\t\t'firstname' => 'Keresztnév',\n\t\t'lastname' => 'Vezetéknév',\n\t\t'company' => 'Cég',\n\t\t'nameorcompany_desc' => 'Vagy a keresztnév/vezetéknév vagy a cég megadása kötelező',\n\t\t'street' => 'Utca',\n\t\t'zipcode' => 'Irányítószám',\n\t\t'city' => 'Város',\n\t\t'phone' => 'Telefon',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'Email',\n\t\t'customernumber' => 'Ügyfélazonosító',\n\t\t'diskspace' => 'Webtárhely',\n\t\t'traffic' => 'Forgalom',\n\t\t'mysqls' => 'MySQL-adatbázisok',\n\t\t'emails' => 'Email-címek',\n\t\t'accounts' => 'Email-fiókok',\n\t\t'forwarders' => 'Email-továbbítók',\n\t\t'ftps' => 'FTP-fiókok',\n\t\t'subdomains' => 'Aldomainek',\n\t\t'domains' => 'Domainek',\n\t\t'mib' => 'MiB',\n\t\t'gib' => 'GiB',\n\t\t'title' => 'Cím',\n\t\t'country' => 'Ország',\n\t\t'email_quota' => 'Email kvóta',\n\t\t'email_imap' => 'Email IMAP',\n\t\t'email_pop3' => 'Email POP3',\n\t\t'sendinfomail' => 'Adatok küldése emailben nekem',\n\t\t'generated_pwd' => 'Jelszó javaslat',\n\t\t'usedmax' => 'Használt / Max',\n\t\t'services' => 'Szolgáltatások',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Let\\'s Encrypt használata',\n\t\t\t'description' => 'Ingyenes tanúsítvány beszerzése a <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>-től. A tanúsítvány automatikusan létrejön és megújul.',\n\t\t],\n\t\t'selectserveralias_addinfo' => 'Ez az opció a domain szerkesztésekor állítható be. Kezdeti értéke az anyadomaintől öröklődik.',\n\t\t'total_diskspace' => 'Teljes lemezterület',\n\t\t'mysqlserver' => 'Használható mysql-szerver',\n\t],\n\t'diskquota' => 'Kvóta',\n\t'antispam' => [\n\t\t'config_file' => [\n\t\t\t'title' => 'Antispam beállítások fájlja',\n\t\t\t'description' => 'Kérjük, adja meg az email-antispam szabályok fájlnevét',\n\t\t],\n\t\t'reload_command' => [\n\t\t\t'title' => 'Milter újraindítási parancs',\n\t\t\t'description' => 'Kérjük, adja meg az rspamd szolgáltatás újraindítási parancsát',\n\t\t],\n\t\t'activated' => [\n\t\t\t'title' => 'Antispam aktiválása?',\n\t\t\t'description' => 'Szeretné használni az rspamd-et antispam szolgáltatásként?',\n\t\t],\n\t\t'dkim_keylength' => [\n\t\t\t'title' => 'DKIM Kulcshossz',\n\t\t\t'description' => 'Figyelem: A változtatások csak az új kulcsokra lesznek érvényesek<br/><br/>Speciális dns bejegyzést igényel a domainhez. Ha nem használja a névszerver funkciót, manuálisan kell kezelnie ezeket a bejegyzéseket.',\n\t\t],\n\t\t'spam_tag_level' => [\n\t\t\t'title' => 'Spam címkézési szint',\n\t\t\t'description' => 'Az a pontszám, ami szükséges egy email spamként való megjelöléséhez<br/>Alapértelmezett: 7.0'\n\t\t],\n\t\t'rewrite_subject' => [\n\t\t\t'title' => 'Tárgy átírása',\n\t\t\t'description' => 'Hozzáadja-e a <strong>***SPAM***</strong> jelzést az email tárgyához, ha alkalmazható',\n\t\t],\n\t\t'spam_kill_level' => [\n\t\t\t'title' => 'Spam eldobási szint',\n\t\t\t'description' => 'Az a pontszám, ami szükséges egy email teljes eldobásához<br/>Alapértelmezett: 14.0'\n\t\t],\n\t\t'bypass_spam' => [\n\t\t\t'title' => 'Spamszűrő megkerülése',\n\t\t\t'description' => 'Aktiválja a spamszűrés megkerülését/kikapcsolását ennél a címnél.<br/>Alapértelmezett: nem'\n\t\t],\n\t\t'policy_greylist' => [\n\t\t\t'title' => 'Szürkelista használata',\n\t\t\t'description' => 'A bejövő emaileket védi a <a href=\"https://en.wikipedia.org/wiki/Greylisting_(email)\" target=\"_blank\">szürkelista</a>.<br/>Alapértelmezett: igen'\n\t\t],\n\t\t'required_spf_dns' => 'Szükséges SPF DNS bejegyzés',\n\t\t'required_dmarc_dns' => 'Szükséges DMARC DNS bejegyzés',\n\t\t'required_dkim_dns' => 'Szükséges DKIM DNS bejegyzés',\n\t\t'default_select' => [\n\t\t\t'on_changeable' => 'Aktiválva, módosítható',\n\t\t\t'off_changeable' => 'Deaktiválva, módosítható',\n\t\t\t'on_unchangeable' => 'Aktiválva, nem módosítható',\n\t\t\t'off_unchangeable' => 'Deaktiválva, nem módosítható',\n\t\t],\n\t\t'default_bypass_spam' => [\n\t\t\t'title' => 'Spamszűrő megkerülésének alapértelmezett értéke',\n\t\t\t'description' => 'Az új email fiókoknál alapértelmezetten aktiválva van-e a \"Spamszűrő megkerülése\", és módosítható-e ez a beállítás az ügyfél által.<br/>Alapértelmezett: Deaktiválva, módosítható'\n\t\t],\n\t\t'default_spam_rewrite_subject' => [\n\t\t\t'title' => 'Tárgy átírásának alapértelmezett értéke',\n\t\t\t'description' => 'Az új email fiókoknál alapértelmezetten aktiválva van-e a \"Tárgy átírása\", és módosítható-e ez a beállítás az ügyfél által.<br/>Alapértelmezett: Aktiválva, módosítható'\n\t\t],\n\t\t'default_policy_greylist' => [\n\t\t\t'title' => 'Szürkelista használatának alapértelmezett értéke',\n\t\t\t'description' => 'Az új email fiókoknál alapértelmezetten aktiválva van-e a \"Szürkelista használata\", és módosítható-e ez a beállítás az ügyfél által.<br/>Alapértelmezett: Aktiválva, módosítható'\n\t\t],\n\t],\n\t'dns' => [\n\t\t'destinationip' => 'Domain IP-cím(ek)',\n\t\t'standardip' => 'Szerver alapértelmezett IP-címe',\n\t\t'a_record' => 'A-rekord (IPv6 opcionális)',\n\t\t'cname_record' => 'CNAME-rekord',\n\t\t'mxrecords' => 'MX rekordok meghatározása',\n\t\t'standardmx' => 'Szerver alapértelmezett MX rekordja',\n\t\t'mxconfig' => 'Egyéni MX rekordok',\n\t\t'priority10' => '10-es prioritás',\n\t\t'priority20' => '20-as prioritás',\n\t\t'txtrecords' => 'TXT rekordok meghatározása',\n\t\t'txtexample' => 'Példa (SPF-bejegyzés):<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'howitworks' => 'Itt kezelheti a domain DNS bejegyzéseit. Vegye figyelembe, hogy a froxlor automatikusan generálja az NS/MX/A/AAAA rekordokat. Az egyéni bejegyzések elsőbbséget élveznek, csak a hiányzó bejegyzések lesznek automatikusan generálva.',\n\t\t'nis2note' => [\n\t\t\t'title' => 'NIS2 információ',\n\t\t\t'content' => 'A DNS hosting/autoritatív DNS szolgáltatások digitális szolgáltatásnak minősülhetnek, fokozott biztonsági és jelentési kötelezettségekkel az <strong>EU-NIS2</strong> alatt. Kérjük, ellenőrizze, hogy a beállítása érintett-e a NIS2 által, és milyen intézkedések szükségesek.'\n\t\t],\n\t],\n\t'dnseditor' => [\n\t\t'edit' => 'DNS szerkesztése',\n\t\t'records' => 'rekordok',\n\t\t'notes' => [\n\t\t\t'A' => '32 bites IPv4 cím, a hosztnevek IP-címhez rendeléséhez használatos.',\n\t\t\t'AAAA' => '128 bites IPv6 cím, a hosztnevek IP-címhez rendeléséhez használatos.',\n\t\t\t'CAA' => 'A CAA erőforrásrekord lehetővé teszi a DNS domain név tulajdonosának, hogy meghatározzon egy vagy több hitelesítésszolgáltatót (CA), amely jogosult tanúsítványokat kiállítani az adott domainhez.<br>Szerkezet: <code>flag tag[issue|issuewild|iodef|contactmail|contactphone] érték</code><br>Példa: <code>0 issue \"ca.example.net\"<br>0 iodef \"mailto:security@example.com\"</code>',\n\t\t\t'CNAME' => 'A domain név aliasa, a DNS keresés folytatódik az új név újbóli keresésével. Csak aldomaineknél lehetséges!',\n\t\t\t'DNAME' => 'Aliast hoz létre a domain név fa teljes részfájához',\n\t\t\t'LOC' => 'Földrajzi helymeghatározási információ egy domain névhez.<br>Szerkezet: <code>( d1 [m1 [s1]] {\"N\"|\"S\"} d2 [m2 [s2]] {\"E\"|\"W\"} alt[\"m\"] [siz[\"m\"] [hp[\"m\"] [vp[\"m\"]]]] )</code><br>Leírás: <code>d1:     [0 .. 90]            (szélességi fok)\n            d2:     [0 .. 180]           (hosszúsági fok)\n            m1, m2: [0 .. 59]            (szélességi/hosszúsági perc)\n            s1, s2: [0 .. 59.999]        (szélességi/hosszúsági másodperc)\n            alt:    [-100000.00 .. 42849672.95] BY .01 (magasság méterben)\n            siz, hp, vp: [0 .. 90000000.00] (méret/pontosság méterben)</code><br>Példa: <code>52 22 23.000 N 4 53 32.000 E -2.00m 0.00m 10000m 10m</code>',\n\t\t\t'MX' => 'Mail exchange rekord, egy domain nevet rendel egy levelezőszerverhez az adott domainhez.<br>Példa: <code>10 mail.example.com</code><br>Megjegyzés: A prioritáshoz használja a fenti mezőt',\n\t\t\t'NS' => 'Egy DNS zónát delegál a megadott hivatalos névszerverek használatára.',\n\t\t\t'RP' => 'Felelős személy rekord<br>Szerkezet: <code>postafiók[@ helyett pontot használjon] txt-rekord-név</code><br>Példa: <code>team.froxlor.org. froxlor.org.</code>',\n\t\t\t'SRV' => 'Szolgáltatás helymeghatározó rekord, újabb protokolloknál használatos protokoll-specifikus rekordok létrehozása helyett, mint például az MX.<br>Szerkezet: <code>prioritás súly port cél</code><br>Példa: <code>0 5 5060 sipserver.example.com.</code><br>Megjegyzés: A prioritáshoz használja a fenti mezőt',\n\t\t\t'SSHFP' => 'Az SSHFP erőforrásrekord a secure shell (SSH) kulcs ujjlenyomatainak DNS-ben való közzétételére szolgál.<br>Szerkezet: <code>algoritmus típus ujjlenyomat</code><br>Algoritmusok: <code>0: fenntartott, 1: RSA, 2: DSA, 3: ECDSA, 4: Ed25519, 6: Ed448</code><br>Típusok: <code>0: fenntartott, 1: SHA-1, 2: SHA-256</code><br>Példa: <code>2 1 123456789abcdef67890123456789abcdef67890</code>',\n\t\t\t'TLSA' => 'A TLSA (TLS Hitelesítés) rekord egy TLS/SSL tanúsítvány ujjlenyomatának közzétételére szolgál. Általában DANE-hez használják.<br>A TLSA rekordok csak akkor megbízhatóak, ha a DNSSEC engedélyezve van a domainjén.<br>Szerkezet: <code>használat kiválasztó típus ujjlenyomat</code><br>Tanúsítvány használat: <code>0: PKIX-T, 1: PKIX-EE, 2: DANE-TA, 3: DANE-EE</code><br>Kiválasztó: <code>0: Teljes tanúsítvány használata, 1: Alany nyilvános kulcsának használata</code><br>Egyezési típus: <code>0: Teljes: Nincs Hash, 1: SHA-256 Hash, 2:SHA-512 Hash</code><br>Példa: <code>3 1 1 123456789abcdef67890123456789abcdef123456789abcdef123456789abcde</code>',\n\t\t\t'TXT' => 'Szabadon meghatározható, leíró szöveg.'\n\t\t]\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'OpenBasedir-útvonal',\n\t\t'inherited' => 'Ugyanaz, mint a szülő-domainnél',\n\t\t'docroot' => 'A fenti mező útvonala',\n\t\t'homedir' => 'Kezdőkönyvtár',\n\t\t'docparent' => 'A fenti mező útvonalának szülőkönyvtára',\n\t\t'ssl_certificate_placeholder' => '---- BEGIN CERTIFICATE---' . PHP_EOL . '[...]' . PHP_EOL . '----END CERTIFICATE----',\n\t\t'ssl_key_placeholder' => '---- BEGIN RSA PRIVATE KEY-----' . PHP_EOL . '[...]' . PHP_EOL . '-----END RSA PRIVATE KEY-----',\n\t],\n\t'domains' => [\n\t\t'description' => 'Itt hozhat létre (al)domaineket és módosíthatja azok útvonalait.<br />A rendszernek minden változtatás után időre van szüksége az új beállítások alkalmazásához.',\n\t\t'domainsettings' => 'Domain beállítások',\n\t\t'domainname' => 'Domain név',\n\t\t'subdomain_add' => 'Aldomain létrehozása',\n\t\t'subdomain_edit' => '(Al)domain szerkesztése',\n\t\t'wildcarddomain' => 'Wildcard domainként hozza létre?',\n\t\t'aliasdomain' => 'Alias a következő domainhez',\n\t\t'noaliasdomain' => 'Nincs alias domain',\n\t\t'hasaliasdomains' => 'Van alias domain(ek)',\n\t\t'statstics' => 'Használati statisztikák',\n\t\t'isassigneddomain' => 'Hozzárendelt domain',\n\t\t'add_date' => 'Hozzáadva a froxlorhoz',\n\t\t'registration_date' => 'Hozzáadva a nyilvántartáshoz',\n\t\t'topleveldomain' => 'Felső szintű domain',\n\t\t'associated_with_domain' => 'Társítva',\n\t\t'aliasdomains' => 'Alias domainek',\n\t\t'redirectifpathisurl' => 'Átirányítási kód (alapértelmezett: üres)',\n\t\t'redirectifpathisurlinfo' => 'Csak akkor kell kiválasztania egyet ezek közül, ha URL-t adott meg útvonalként<br/><strong class=\"text-danger\">MEGJEGYZÉS:</strong> A változtatások csak akkor lépnek életbe, ha a megadott útvonal egy URL.',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'IP-cím(ek)',\n\t\t\t'description' => 'Adjon meg egy vagy több IP-címet a domainhez.<br /><br /><div class=\"text-danger\">MEGJEGYZÉS: Az IP-címek nem változtathatók meg, ha a domain egy másik domain <strong>alias-domainje</strong>ként van beállítva.</div>',\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'SSL IP-cím(ek)',\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'SSL átirányítás',\n\t\t\t'description' => 'Ez az opció átirányításokat hoz létre a nem SSL virtuális hosztokhoz, így minden kérés az SSL virtuális hoszthoz lesz átirányítva.<br /><br />pl. egy kérés a <strong>http</strong>://domain.tld/ címre átirányít a <strong>https</strong>://domain.tld/ címre',\n\t\t],\n\t\t'serveraliasoption_wildcard' => 'Wildcard (*.domain.tld)',\n\t\t'serveraliasoption_www' => 'WWW (www.domain.tld)',\n\t\t'serveraliasoption_none' => 'Nincs alias',\n\t\t'domain_import' => 'Domainek importálása',\n\t\t'import_separator' => 'Elválasztó',\n\t\t'import_offset' => 'Eltolás',\n\t\t'import_file' => 'CSV-fájl',\n\t\t'import_description' => 'A részletes információkért az importfájl szerkezetéről és a sikeres importálás módjáról kérjük, látogasson el a <a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\" class=\"alert-link\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a> oldalra',\n\t\t'ssl_redirect_temporarilydisabled' => '<br>Az SSL átirányítás ideiglenesen ki van kapcsolva, amíg egy új Let\\'s Encrypt tanúsítvány generálódik. A tanúsítvány létrehozása után újra aktiválódik.',\n\t\t'termination_date' => 'Megszűnés dátuma',\n\t\t'termination_date_overview' => 'megszűnik: ',\n\t\t'ssl_certificates' => 'SSL tanúsítványok',\n\t\t'ssl_certificate_removed' => 'A(z) #%s azonosítójú tanúsítvány sikeresen eltávolítva',\n\t\t'ssl_certificate_error' => 'Hiba a tanúsítvány olvasásakor a következő domainhez: %s',\n\t\t'no_ssl_certificates' => 'Nincsenek SSL tanúsítvánnyal rendelkező domainek',\n\t\t'isaliasdomainof' => '%s alias domainje',\n\t\t'isbinddomain' => 'DNS zóna létrehozása',\n\t\t'dkimenabled' => 'DKIM engedélyezve',\n\t\t'openbasedirenabled' => 'Openbasedir korlátozás',\n\t\t'hsts' => 'HSTS engedélyezve',\n\t\t'aliasdomainid' => 'Alias domain azonosítója',\n\t\t'nodomainsassignedbyadmin' => 'Az Ön fiókjához jelenleg nincs (aktív) domain hozzárendelve. Kérjük, lépjen kapcsolatba az adminisztrátorral, ha úgy gondolja, hogy ez hibás.',\n\t\t'email_only' => 'Csak e-mail',\n\t],\n\t'emails' => [\n\t\t'description' => 'Itt hozhatja létre és módosíthatja e-mail címeit.<br />Egy fiók olyan, mint a postaláda a háza előtt. Ha valaki e-mailt küld Önnek, az a fiókba kerül.<br /><br />Az e-mailek letöltéséhez használja a következő beállításokat a levelezőprogramjában: (A <i>dőlt betűs</i> adatokat a megfelelő értékekre kell cserélnie!)<br />Kiszolgáló neve: <b><i>domainnév</i></b><br />Felhasználónév: <b><i>fióknév / e-mail cím</i></b><br />jelszó: <b><i>az Ön által választott jelszó</i></b>',\n\t\t'emailaddress' => 'E-mail cím',\n\t\t'emails_add' => 'E-mail cím létrehozása',\n\t\t'emails_edit' => 'E-mail cím szerkesztése',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => 'Catchall címként definiálva?',\n\t\t'account' => 'Fiók',\n\t\t'account_add' => 'Fiók létrehozása',\n\t\t'account_delete' => 'Fiók törlése',\n\t\t'from' => 'Forrás',\n\t\t'to' => 'Cél',\n\t\t'forwarders' => 'Továbbítók',\n\t\t'forwarder_add' => 'Továbbító létrehozása',\n\t\t'alternative_emailaddress' => 'Alternatív e-mail cím',\n\t\t'quota' => 'Kvóta',\n\t\t'noquota' => 'Nincs kvóta',\n\t\t'updatequota' => 'Kvóta frissítése',\n\t\t'quota_edit' => 'E-mail kvóta módosítása',\n\t\t'noemaildomainaddedyet' => 'Még nincs (e-mail) domain a fiókjában.',\n\t\t'back_to_overview' => 'Vissza a domain áttekintéshez',\n\t\t'accounts' => 'Fiókok',\n\t\t'emails' => 'Címek',\n\t\t'senders' => 'Engedélyezett feladók',\n\t\t'sender_add' => 'Engedélyezett feladó hozzáadása',\n\t\t'foreign_sender' => 'Engedélyezett (külső) feladó',\n\t\t'allowed_sender_info' => 'Egy <strong>engedélyezett feladóval</strong> lehetővé teszi egy létező e-mail fiók számára, hogy más feladó címmel küldjön e-maileket.<br><strong>Fontos:</strong> Az itt megadott cím/wildcard-domain nem lesz automatikusan postafiók – csak további, engedélyezett feladó azonosítóként szolgál.',\n\t],\n\t'error' => [\n\t\t'error' => 'Hiba',\n\t\t'directorymustexist' => 'A(z) %s könyvtárnak léteznie kell. Kérjük, hozza létre az FTP kliensével.',\n\t\t'filemustexist' => 'A(z) %s fájlnak léteznie kell.',\n\t\t'allresourcesused' => 'Már minden erőforrását felhasználta.',\n\t\t'domains_cantdeletemaindomain' => 'Nem törölhet hozzárendelt domaint.',\n\t\t'domains_canteditdomain' => 'Nem szerkesztheti ezt a domaint. Az admin letiltotta.',\n\t\t'domains_cantdeletedomainwithemail' => 'Nem törölhet olyan domaint, amely e-mail domainként van használatban. Először törölje az összes e-mail címet.',\n\t\t'firstdeleteallsubdomains' => 'Először törölnie kell az összes aldomaint, mielőtt létrehozhat egy wildcard domaint.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Már definiált egy catchall-t ehhez a domainhez.',\n\t\t'ftp_cantdeletemainaccount' => 'Nem törölheti a fő FTP fiókját',\n\t\t'login' => 'A megadott felhasználónév vagy jelszó helytelen. Kérjük, próbálja újra!',\n\t\t'login_blocked' => 'Ezt a fiókot felfüggesztettük a túl sok bejelentkezési hiba miatt. <br />Kérjük, próbálja újra %s másodperc múlva.',\n\t\t'notallreqfieldsorerrors' => 'Nem töltött ki minden mezőt, vagy néhány mezőt helytelenül töltött ki.',\n\t\t'oldpasswordnotcorrect' => 'A régi jelszó nem helyes.',\n\t\t'youcantallocatemorethanyouhave' => 'Nem oszthat ki több erőforrást, mint amennyivel rendelkezik.',\n\t\t'mustbeurl' => 'Nem adott meg érvényes vagy teljes URL-t (pl. http://valamilyendomain.com/error404.htm)',\n\t\t'invalidpath' => 'Nem választott érvényes URL-t (esetleg problémák a könyvtárlistázással?)',\n\t\t'stringisempty' => 'Hiányzó adat a mezőben',\n\t\t'stringiswrong' => 'Helytelen adat a mezőben',\n\t\t'newpasswordconfirmerror' => 'Az új jelszó és a megerősítés nem egyezik',\n\t\t'mydomain' => '\\'Domain\\'',\n\t\t'mydocumentroot' => '\\'Dokumentumgyökér\\'',\n\t\t'loginnameexists' => 'A(z) %s bejelentkezési név már létezik',\n\t\t'emailiswrong' => 'A(z) %s e-mail cím érvénytelen karaktereket tartalmaz vagy hiányos',\n\t\t'emailexists' => 'A(z) %s e-mail címet már használja egy másik admin',\n\t\t'emailexistsanon' => 'A(z) %s e-mail cím már használatban van.',\n\t\t'alternativeemailiswrong' => 'A megadott %s alternatív e-mail cím, ahova a hitelesítő adatokat küldenénk, érvénytelennek tűnik',\n\t\t'loginnameiswrong' => 'A(z) \"%s\" bejelentkezési név érvénytelen karaktereket tartalmaz.',\n\t\t'loginnameiswrong2' => 'A bejelentkezési név túl sok karaktert tartalmaz. Csak %s karakter engedélyezett.',\n\t\t'userpathcombinationdupe' => 'A felhasználónév és útvonal kombinációja már létezik',\n\t\t'patherror' => 'Általános hiba! Az útvonal nem lehet üres',\n\t\t'errordocpathdupe' => 'A(z) %s útvonalra vonatkozó opció már létezik',\n\t\t'adduserfirst' => 'Kérjük, először hozzon létre egy ügyfelet',\n\t\t'domainalreadyexists' => 'A(z) %s domain már hozzá van rendelve egy ügyfélhez',\n\t\t'nolanguageselect' => 'Nincs kiválasztott nyelv.',\n\t\t'nosubjectcreate' => 'Meg kell adnia egy témát ehhez az e-mail sablonhoz.',\n\t\t'nomailbodycreate' => 'Meg kell adnia egy e-mail szöveget ehhez az e-mail sablonhoz.',\n\t\t'templatenotfound' => 'A sablon nem található.',\n\t\t'alltemplatesdefined' => 'Nem definiálhat több sablont, már minden nyelv támogatott.',\n\t\t'wwwnotallowed' => 'A www nem engedélyezett aldomaineknél.',\n\t\t'subdomainiswrong' => 'A(z) %s aldomain érvénytelen karaktereket tartalmaz.',\n\t\t'domaincantbeempty' => 'A domain név nem lehet üres.',\n\t\t'domainexistalready' => 'A(z) %s domain már létezik.',\n\t\t'domainisaliasorothercustomer' => 'A kiválasztott alias domain vagy maga is egy alias domain, vagy más IP/port kombinációval rendelkezik, vagy egy másik ügyfélhez tartozik.',\n\t\t'emailexistalready' => 'A(z) %s e-mail cím már létezik.',\n\t\t'maindomainnonexist' => 'A(z) %s fő domain nem létezik.',\n\t\t'maindomaindeactivated' => 'A(z) %s fő domain deaktiválva van.',\n\t\t'destinationnonexist' => 'Kérjük, hozza létre a továbbítót a \\'Cél\\' mezőben.',\n\t\t'destinationalreadyexistasmail' => 'A(z) %s címre történő továbbítás már létezik aktív e-mail címként.',\n\t\t'destinationalreadyexist' => 'Már definiált egy továbbítót a(z) \"%s\" címre',\n\t\t'destinationiswrong' => 'A(z) %s továbbító érvénytelen karakter(eke)t tartalmaz vagy hiányos.',\n\t\t'dumpfoldercannotbedocroot' => 'Az adatmentések mappája nem lehet a saját könyvtára, kérjük, válasszon egy mappát a saját könyvtárán belül, pl. /dumps',\n\t\t'templatelanguagecombodefined' => 'A kiválasztott nyelv/sablon kombináció már definiálva van.',\n\t\t'templatelanguageinvalid' => 'A kiválasztott nyelv nem létezik',\n\t\t'ipstillhasdomains' => 'A törölni kívánt IP/Port kombinációhoz még tartoznak domainek, kérjük, rendelje át ezeket más IP/Port kombinációkhoz, mielőtt törölné ezt az IP/Port kombinációt.',\n\t\t'cantdeletedefaultip' => 'Nem törölheti az alapértelmezett IP/Port kombinációt, kérjük, állítson be egy másik IP/Port kombinációt alapértelmezettként, mielőtt törölné ezt az IP/Port kombinációt.',\n\t\t'cantdeletesystemip' => 'Nem törölheti az utolsó rendszer IP-t, vagy hozzon létre egy új IP/Port kombinációt a rendszer IP-hez, vagy változtassa meg a rendszer IP-t.',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'Port\\'',\n\t\t'myipdefault' => 'Ki kell választania egy IP/Port kombinációt, amely alapértelmezett lesz.',\n\t\t'myipnotdouble' => 'Ez az IP/Port kombináció már létezik.',\n\t\t'admin_domain_emailsystemhostname' => 'A szerver-hosztnév nem használható ügyfél-domainként.',\n\t\t'cantchangesystemip' => 'Nem változtathatja meg az utolsó rendszer IP-t, vagy hozzon létre egy másik új IP/Port kombinációt a rendszer IP-hez, vagy változtassa meg a rendszer IP-t.',\n\t\t'sessiontimeoutiswrong' => 'Csak numerikus \"munkamenet időtúllépés\" engedélyezett.',\n\t\t'maxloginattemptsiswrong' => 'Csak numerikus \"maximális bejelentkezési kísérletek\" engedélyezettek.',\n\t\t'deactivatetimiswrong' => 'Csak numerikus \"deaktiválási idő\" engedélyezett.',\n\t\t'accountprefixiswrong' => 'Az \"ügyfél-előtag\" helytelen.',\n\t\t'mysqlprefixiswrong' => 'Az \"SQL előtag\" helytelen.',\n\t\t'ftpprefixiswrong' => 'Az \"FTP előtag\" helytelen.',\n\t\t'ipiswrong' => 'Az \"IP-cím\" helytelen. Csak érvényes IP-cím engedélyezett.',\n\t\t'vmailuidiswrong' => 'A \"levelek-uid\" helytelen. Csak numerikus UID engedélyezett.',\n\t\t'vmailgidiswrong' => 'A \"levelek-gid\" helytelen. Csak numerikus GID engedélyezett.',\n\t\t'adminmailiswrong' => 'A \"feladó-cím\" helytelen. Csak érvényes e-mail cím engedélyezett.',\n\t\t'pagingiswrong' => 'Az \"bejegyzések oldalanként\" érték helytelen. Csak numerikus karakterek engedélyezettek.',\n\t\t'phpmyadminiswrong' => 'A phpMyAdmin-link nem érvényes link.',\n\t\t'webmailiswrong' => 'A webmail-link nem érvényes link.',\n\t\t'webftpiswrong' => 'A WebFTP-link nem érvényes link.',\n\t\t'stringformaterror' => 'A(z) \"%s\" mező értéke nem a várt formátumban van.',\n\t\t'loginnameisusingprefix' => 'Nem hozhat létre olyan fiókokat, amelyek \"%s\"-vel kezdődnek, mivel ez az előtag az automatikus fióknévadáshoz van beállítva. Kérjük, adjon meg másik fióknevet.',\n\t\t'loginnameissystemaccount' => 'A(z) \"%s\" fiók már létezik a rendszeren, és nem használható. Kérjük, adjon meg másik fióknevet.',\n\t\t'loginnameisreservedname' => 'A(z) \"%s\" fióknév a rendszer belső használatára van fenntartva, és nem használható.',\n\t\t'youcantdeleteyourself' => 'Biztonsági okokból nem törölheti saját magát.',\n\t\t'youcanteditallfieldsofyourself' => 'Megjegyzés: Biztonsági okokból nem szerkesztheti saját fiókjának minden mezőjét.',\n\t\t'documentrootexists' => 'A(z) \"%s\" könyvtár már létezik ennél az ügyfélnél. Kérjük, távolítsa el ezt, mielőtt újra hozzáadná az ügyfelet.',\n\t\t'norepymailiswrong' => 'A \"Noreply-cím\" helytelen. Csak érvényes e-mail cím engedélyezett.',\n\t\t'logerror' => 'Naplózási hiba: %s',\n\t\t'nomessagetosend' => 'Nem adott meg üzenetet.',\n\t\t'norecipientsgiven' => 'Nem adott meg címzettet',\n\t\t'errorsendingmail' => 'Az üzenet küldése a következő címre sikertelen: \"%s\"',\n\t\t'errorsendingmailpub' => 'Az üzenet küldése a megadott e-mail címre sikertelen',\n\t\t'cannotreaddir' => 'Nem lehet olvasni a(z) \"%s\" könyvtárat',\n\t\t'invalidip' => 'Érvénytelen IP-cím: %s',\n\t\t'invalidmysqlhost' => 'Érvénytelen MySQL host cím: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'Nem engedélyezheti egyszerre a Webalizer és az AWstats használatát, kérjük, válasszon egyet közülük',\n\t\t'cannotwritetologfile' => 'Nem lehet megnyitni a(z) %s naplófájlt írásra',\n\t\t'vmailquotawrong' => 'A kvótaméretnek pozitív számnak kell lennie.',\n\t\t'allocatetoomuchquota' => 'Megpróbált %s MB kvótát kiosztani, de nincs elég szabad helye.',\n\t\t'missingfields' => 'Nem minden kötelező mező lett kitöltve.',\n\t\t'requiredfield' => 'Ez a mező kötelező.',\n\t\t'accountnotexisting' => 'A megadott e-mail fiók nem létezik.',\n\t\t'nopermissionsorinvalidid' => 'Nincs elegendő jogosultsága ezeknek a beállításoknak a módosításához, vagy érvénytelen azonosítót adott meg.',\n\t\t'phpsettingidwrong' => 'Nem létezik PHP konfiguráció ezzel az azonosítóval',\n\t\t'descriptioninvalid' => 'A leírás túl rövid, túl hosszú vagy érvénytelen karaktereket tartalmaz.',\n\t\t'info' => 'Információ',\n\t\t'filecontentnotset' => 'A fájl nem lehet üres!',\n\t\t'customerdoesntexist' => 'A kiválasztott ügyfél nem létezik.',\n\t\t'admindoesntexist' => 'A kiválasztott admin nem létezik.',\n\t\t'ipportdoesntexist' => 'A kiválasztott ip/port kombináció nem létezik.',\n\t\t'usernamealreadyexists' => 'A(z) %s felhasználónév már létezik.',\n\t\t'plausibilitychecknotunderstood' => 'A hihetőségi ellenőrzés válasza nem érthető.',\n\t\t'errorwhensaving' => 'Hiba történt a(z) %s mező mentésekor',\n\t\t'hiddenfieldvaluechanged' => 'A(z) \"%s\" rejtett mező értéke megváltozott a beállítások szerkesztése közben.<br /><br />Ez általában nem nagy probléma, de a beállításokat nem lehetett elmenteni emiatt.',\n\t\t'notrequiredpasswordlength' => 'A megadott jelszó túl rövid. Kérjük, adjon meg legalább %s karaktert.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Hoppá, egy mező, amelynek opcióként kellene megjelennie a beállítások áttekintésében, nem elfogadott típusú. Ezért a fejlesztőket hibáztathatja. Ennek nem szabadna megtörténnie!',\n\t\t'pathmaynotcontaincolon' => 'Az Ön által megadott útvonal nem tartalmazhat kettőspontot (\":\"). Kérjük, adjon meg helyes útvonal értéket.',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => 'A megadott jelszó-komplexitás nem teljesült.<br />Kérjük, lépjen kapcsolatba az adminisztrátorával, ha kérdése van a komplexitási előírásokkal kapcsolatban',\n\t\t'invaliderrordocumentvalue' => 'Az ErrorDocument-ként megadott érték nem tűnik érvényes fájlnak, URL-nek vagy karakterláncnak.',\n\t\t'intvaluetoolow' => 'A megadott szám túl alacsony (%s mező)',\n\t\t'intvaluetoohigh' => 'A megadott szám túl magas (%s mező)',\n\t\t'phpfpmstillenabled' => 'A PHP-FPM jelenleg aktív. Kérjük, deaktiválja, mielőtt aktiválná az FCGID-t',\n\t\t'fcgidstillenabled' => 'Az FCGID jelenleg aktív. Kérjük, deaktiválja, mielőtt aktiválná a PHP-FPM-et',\n\t\t'domains_cantdeletedomainwithaliases' => 'Nem törölhet olyan domaint, amelyet alias-domainekhez használnak. Először törölnie kell az aliasokat.',\n\t\t'user_banned' => 'Az Ön fiókja zárolva lett. Kérjük, további információért lépjen kapcsolatba az adminisztrátorával.',\n\t\t'session_timeout' => 'Túl alacsony érték',\n\t\t'session_timeout_desc' => 'Nem ajánlott a munkamenet időtúllépését 1 percnél alacsonyabbra állítani.',\n\t\t'invalidhostname' => 'A hosztnévnek érvényes domainnek kell lennie. Nem lehet üres, és nem állhat csak szóközökből',\n\t\t'operationnotpermitted' => 'A művelet nem engedélyezett!',\n\t\t'featureisdisabled' => 'A(z) %s funkció le van tiltva. Kérjük, lépjen kapcsolatba a szolgáltatójával.',\n\t\t'usercurrentlydeactivated' => 'A(z) %s felhasználó jelenleg deaktiválva van',\n\t\t'setlessthanalreadyused' => 'Nem állíthat be kevesebb erőforrást a(z) \\'%s\\' számára, mint amennyit ez a felhasználó már használ<br />',\n\t\t'stringmustntbeempty' => 'A(z) %s mező értéke nem lehet üres',\n\t\t'sslcertificateismissingprivatekey' => 'Meg kell adnia egy privát kulcsot a tanúsítványához',\n\t\t'sslcertificatewrongdomain' => 'A megadott tanúsítvány nem ehhez a domainhez tartozik',\n\t\t'sslcertificateinvalidcert' => 'A megadott tanúsítvány tartalma nem tűnik érvényes tanúsítványnak',\n\t\t'sslcertificateinvalidcertkeypair' => 'A megadott privát kulcs nem tartozik a megadott tanúsítványhoz',\n\t\t'sslcertificateinvalidca' => 'A megadott CA tanúsítvány adatok nem tűnnek érvényes tanúsítványnak',\n\t\t'sslcertificateinvalidchain' => 'A megadott tanúsítványlánc adatok nem tűnnek érvényes tanúsítványnak',\n\t\t'givendirnotallowed' => 'A(z) %s mezőben megadott könyvtár nem engedélyezett.',\n\t\t'sslredirectonlypossiblewithsslipport' => 'A Let\\'s Encrypt használata csak akkor lehetséges, ha a domainhez legalább egy SSL-engedélyezett IP/port kombináció van hozzárendelve.',\n\t\t'fcgidstillenableddeadlock' => 'Az FCGID jelenleg aktív.<br />Kérjük, deaktiválja, mielőtt az Apache2 helyett másik webszerverre váltana',\n\t\t'send_report_title' => 'Hibajelentés küldése',\n\t\t'send_report_desc' => 'Köszönjük, hogy jelenti ezt a hibát és segít a froxlor fejlesztésében.<br />Ez az e-mail, amely a froxlor fejlesztői csapatának lesz elküldve:',\n\t\t'send_report' => 'Jelentés küldése',\n\t\t'send_report_error' => 'Hiba történt a jelentés küldésekor: <br />%s',\n\t\t'notallowedtouseaccounts' => 'Az Ön fiókja nem teszi lehetővé az IMAP/POP3 használatát. Nem adhat hozzá e-mail fiókokat.',\n\t\t'cannotdeletehostnamephpconfig' => 'Ez a PHP-konfiguráció a froxlor-vhost által használt, és nem törölhető.',\n\t\t'cannotdeletedefaultphpconfig' => 'Ez a PHP-konfiguráció alapértelmezettként van beállítva, és nem törölhető.',\n\t\t'passwordshouldnotbeusername' => 'A jelszó nem lehet ugyanaz, mint a felhasználónév.',\n\t\t'no_phpinfo' => 'Sajnáljuk, nem sikerült beolvasni a phpinfo() adatait',\n\t\t'moveofcustomerfailed' => 'Az ügyfél áthelyezése a kiválasztott adminisztrátorhoz/viszonteladóhoz sikertelen. Vegye figyelembe, hogy az ügyfélre vonatkozó minden egyéb módosítás sikeresen megtörtént ezen a ponton.<br><br>Hibaüzenet: %s',\n\t\t'domain_import_error' => 'A következő hiba történt a domainek importálása során: %s',\n\t\t'fcgidandphpfpmnogoodtogether' => 'Az FCGID és a PHP-FPM nem aktiválható egyidejűleg',\n\t\t'no_apcuinfo' => 'Nincs elérhető gyorsítótár információ. Úgy tűnik, az APCu nem fut.',\n\t\t'no_opcacheinfo' => 'Nincs elérhető OPCache információ. Úgy tűnik, az OPCache nincs betöltve.',\n\t\t'inactive_opcacheinfo' => 'Úgy tűnik, az OPCache telepítve van, de nincs aktiválva.',\n\t\t'nowildcardwithletsencrypt' => 'A Let\\'s Encrypt nem tud kezelni helyettesítő karakteres domaineket az ACME használatával a froxlorban (dns-kihívást igényel), sajnáljuk. Kérjük, állítsa a ServerAlias-t WWW-re, vagy teljesen tiltsa le',\n\t\t'customized_version' => 'Úgy tűnik, hogy a froxlor telepítése módosítva lett, sajnos nem tudunk támogatást nyújtani.',\n\t\t'autoupdate_0' => 'Ismeretlen hiba',\n\t\t'autoupdate_1' => 'A PHP allow_url_fopen beállítás le van tiltva. Az automatikus frissítéshez engedélyezni kell ezt a beállítást a php.ini fájlban',\n\t\t'autoupdate_2' => 'A PHP zip kiterjesztés nem található, kérjük, győződjön meg arról, hogy telepítve van és aktiválva van',\n\t\t'autoupdate_4' => 'A froxlor archívumot nem lehetett a lemezre menteni :(',\n\t\t'autoupdate_5' => 'A version.froxlor.org elfogadhatatlan értékeket adott vissza :(',\n\t\t'autoupdate_6' => 'Hoppá, nem volt megadva (érvényes) verzió a letöltéshez :(',\n\t\t'autoupdate_7' => 'A letöltött archívum nem található :(',\n\t\t'autoupdate_8' => 'Az archívumot nem lehetett kicsomagolni :(',\n\t\t'autoupdate_9' => 'A letöltött fájl nem felelt meg az integritás ellenőrzésnek. Kérjük, próbálja meg újra a frissítést.',\n\t\t'autoupdate_10' => 'A PHP minimálisan támogatott verziója 7.4.0',\n\t\t'autoupdate_11' => 'A webfrissítés le van tiltva',\n\t\t'mailaccistobedeleted' => 'Egy másik fiók ugyanezzel a névvel (%s) jelenleg törlés alatt áll, ezért most nem adható hozzá.',\n\t\t'customerhasongoingexportjob' => 'Már van egy adatexport feladat, amely feldolgozásra vár, kérjük, legyen türelemmel.',\n\t\t'exportfunctionnotenabled' => 'Az exportálási funkció nincs engedélyezve',\n\t\t'dns_domain_nodns' => 'A DNS nincs engedélyezve ehhez a domainhez',\n\t\t'dns_content_empty' => 'Nincs megadva tartalom',\n\t\t'dns_content_invalid' => 'Érvénytelen DNS tartalom',\n\t\t'dns_arec_noipv4' => 'Nem adtak meg érvényes IP-címet az A-rekordhoz',\n\t\t'dns_aaaarec_noipv6' => 'Nem adtak meg érvényes IP-címet az AAAA-rekordhoz',\n\t\t'dns_mx_prioempty' => 'Érvénytelen MX prioritás megadva',\n\t\t'dns_mx_needdom' => 'Az MX tartalom értékének érvényes domain névnek kell lennie',\n\t\t'dns_mx_noalias' => 'Az MX-tartalom értéke nem lehet CNAME bejegyzés.',\n\t\t'dns_cname_invaliddom' => 'Érvénytelen domain név a CNAME rekordhoz',\n\t\t'dns_cname_nomorerr' => 'Már létezik egy erőforrás-rekord ugyanazzal a rekordnévvel. Nem használható CNAME-ként.',\n\t\t'dns_other_nomorerr' => 'Már létezik egy CNAME rekord ugyanazzal a rekordnévvel. Nem használható más típushoz.',\n\t\t'dns_ns_invaliddom' => 'Érvénytelen domain név az NS rekordhoz',\n\t\t'dns_srv_prioempty' => 'Érvénytelen SRV prioritás megadva',\n\t\t'dns_srv_invalidcontent' => 'Érvénytelen SRV tartalom, a súly, port és cél mezőkből kell állnia, pl.: 5 5060 sipserver.example.com.',\n\t\t'dns_srv_needdom' => 'Az SRV cél értékének érvényes domain névnek kell lennie',\n\t\t'dns_srv_noalias' => 'Az SRV-cél értéke nem lehet CNAME bejegyzés.',\n\t\t'dns_duplicate_entry' => 'A rekord már létezik',\n\t\t'dns_notfoundorallowed' => 'A domain nem található vagy nincs jogosultsága',\n\t\t'domain_nopunycode' => 'Nem adhat meg punycode-ot (IDNA). A domain automatikusan konvertálva lesz',\n\t\t'domain_noipaddress' => 'Nem adható hozzá IP-cím domainként',\n\t\t'dns_record_toolong' => 'A rekordok/címkék legfeljebb 63 karakterből állhatnak',\n\t\t'noipportgiven' => 'Nincs megadva IP/port',\n\t\t'nosslippportgiven' => 'SSL engedélyezésekor ki kell választania egy SSL IP/port kombinációt',\n\t\t'jsonextensionnotfound' => 'Ez a funkció a php json-kiterjesztést igényli.',\n\t\t'cannotdeletesuperadmin' => 'Az első adminisztrátor nem törölhető.',\n\t\t'no_wwwcnamae_ifwwwalias' => 'Nem állítható be CNAME rekord a \"www\" számára, mivel a domain úgy van beállítva, hogy www-aliast generáljon. Kérjük, módosítsa a beállításokat \"Nincs alias\" vagy \"Helyettesítő karakter alias\" értékre',\n\t\t'local_group_exists' => 'A megadott csoport már létezik a rendszeren.',\n\t\t'local_group_invalid' => 'A megadott csoportnév érvénytelen',\n\t\t'invaliddnsforletsencrypt' => 'A domain DNS-e nem tartalmazza a kiválasztott IP-címek egyikét sem. A Let\\'s Encrypt tanúsítvány generálása nem lehetséges.',\n\t\t'notallowedphpconfigused' => 'Olyan php-konfigurációt próbál használni, amely nincs hozzárendelve az ügyfélhez',\n\t\t'pathmustberelative' => 'A felhasználónak nincs jogosultsága a vásárló saját könyvtárán kívüli könyvtárak megadására. Kérjük, adjon meg relatív útvonalat (kezdő / nélkül).',\n\t\t'mysqlserverstillhasdbs' => 'Nem lehet eltávolítani az adatbázis-szervert a vásárlók engedélyezési listájáról, mert még vannak rajta adatbázisok.',\n\t\t'domaincannotbeedited' => 'Nincs jogosultsága a(z) %s domain szerkesztésére',\n\t\t'invalidcronjobintervalvalue' => 'A cron feladat intervallumának a következők egyikének kell lennie: %s',\n\t\t'phpgdextensionnotavailable' => 'A PHP GD kiterjesztés nem érhető el. Nem lehet ellenőrizni a képadatokat',\n\t\t'2fa_wrongcode' => 'A megadott kód érvénytelen',\n\t\t'gnupgextensionnotavailable' => 'A PHP GnuPG kiterjesztés nem érhető el. Nem lehet ellenőrizni a PGP nyilvános kulcsot',\n\t\t'invalidpgppublickey' => 'A PGP nyilvános kulcs érvénytelen',\n\t\t'invalid_validtime' => 'Az érvényességi idő másodpercben csak 10 és 120 között lehet',\n\t\t'customerphpenabledbutnoconfig' => 'A vásárlónál a PHP aktiválva van, de nincs kiválasztva PHP-konfiguráció.',\n\t\t'emaildomainstillhasaddresses' => 'Nem lehet deaktiválni a levelezési domain jelzőt, mert még vannak e-mail címek ehhez a domainhez.',\n\t\t'invaliddocumentrooturl' => 'A megadott documentroot URL érvénytelen. Kérjük, adjon meg helyes URL-t vagy elérési utat.',\n\t\t'local_user_invalid' => 'A megadott felhasználónév érvénytelen vagy nem létezik',\n\t\t'local_user_isfroxloruser' => 'A megadott felhasználónév egy froxlor által kezelt felhasználónév, és ebben az összefüggésben nem használható',\n\t\t'tls13requiredforhttp3' => 'A domain http3 jelzője be van kapcsolva, de az ssl-protocols nem tartalmazza a TLSv1.3-at',\n\t\t'senderdomainnotowned' => 'A megadott \"%s\" domain nem használható.',\n\t\t'emailhasnoaccount' => 'A megadott \"%s\" e-mail címnek nincs fiókja, nem lehet hozzáadni a feladó címet.',\n\t],\n\t'extras' => [\n\t\t'description' => 'Itt hozzáadhat néhány extrát, például könyvtárvédelmet.<br />A rendszernek időre van szüksége az új beállítások alkalmazásához minden változtatás után.',\n\t\t'directoryprotection_add' => 'Könyvtárvédelem hozzáadása',\n\t\t'view_directory' => 'Könyvtár tartalmának megjelenítése',\n\t\t'pathoptions_add' => 'Útvonal opciók hozzáadása',\n\t\t'directory_browsing' => 'Könyvtár tartalmának böngészése',\n\t\t'pathoptions_edit' => 'Útvonal opciók szerkesztése',\n\t\t'error404path' => '404',\n\t\t'error403path' => '403',\n\t\t'error500path' => '500',\n\t\t'error401path' => '401',\n\t\t'errordocument404path' => 'ErrorDocument 404',\n\t\t'errordocument403path' => 'ErrorDocument 403',\n\t\t'errordocument500path' => 'ErrorDocument 500',\n\t\t'errordocument401path' => 'ErrorDocument 401',\n\t\t'execute_perl' => 'Perl/CGI végrehajtása',\n\t\t'htpasswdauthname' => 'Hitelesítés oka (AuthName)',\n\t\t'directoryprotection_edit' => 'Könyvtárvédelem szerkesztése',\n\t\t'export' => 'Adatmentés létrehozása',\n\t\t'dump_web' => 'Webadatok belefoglalása',\n\t\t'dump_mail' => 'Levelezési adatok belefoglalása',\n\t\t'dump_dbs' => 'Adatbázisok belefoglalása',\n\t\t'path_protection_label' => '<strong class=\"text-danger\">Fontos</strong>',\n\t\t'path_protection_info' => 'Erősen javasoljuk a megadott útvonal védelmét, lásd \"Extrák\" -> \"Könyvtárvédelem\"',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Itt hozhatja létre és módosíthatja FTP-fiókjait.<br />A változtatások azonnal életbe lépnek, és a fiókok azonnal használhatók.',\n\t\t'account_add' => 'Fiók létrehozása',\n\t\t'account_edit' => 'FTP-fiók szerkesztése',\n\t\t'editpassdescription' => 'Állítson be új jelszót, vagy hagyja üresen, ha nem kívánja megváltoztatni.',\n\t\t'sshkey_add' => 'SSH kulcs hozzáadása',\n\t\t'sshkey_edit' => 'SSH kulcs szerkesztése',\n\t],\n\t'gender' => [\n\t\t'title' => 'Megszólítás',\n\t\t'male' => 'Úr',\n\t\t'female' => 'Hölgy',\n\t\t'undef' => '',\n\t],\n\t'imprint' => 'Jogi megjegyzések',\n\t'index' => [\n\t\t'customerdetails' => 'Vásárló adatai',\n\t\t'accountdetails' => 'Fiók adatai',\n\t],\n\t'integrity_check' => [\n\t\t'databaseCharset' => 'Adatbázis karakterkészlete (UTF-8 ajánlott)',\n\t\t'domainIpTable' => 'IP &lt;&dash;&gt; domain hivatkozások',\n\t\t'subdomainSslRedirect' => 'Hibás SSL-átirányítási jelző nem SSL-es domainekhez',\n\t\t'froxlorLocalGroupMemberForFcgidPhpFpm' => 'froxlor-felhasználó az ügyfélcsoportokban (FCGID/php-fpm esetén)',\n\t\t'webserverGroupMemberForFcgidPhpFpm' => 'Webszerver-felhasználó az ügyfélcsoportokban (FCGID/php-fpm esetén)',\n\t\t'subdomainLetsencrypt' => 'SSL-port nélküli fő domaineknek nincsenek aktív SSL átirányítású aldomainjeik',\n\t],\n\t'logger' => [\n\t\t'date' => 'Dátum',\n\t\t'type' => 'Típus',\n\t\t'action' => 'Művelet',\n\t\t'user' => 'Felhasználó',\n\t\t'truncate' => 'Napló ürítése',\n\t\t'reseller' => 'Viszonteladó',\n\t\t'admin' => 'Adminisztrátor',\n\t\t'cron' => 'Ütemezett feladat',\n\t\t'login' => 'Bejelentkezés',\n\t\t'intern' => 'Belső',\n\t\t'unknown' => 'Ismeretlen',\n\t],\n\t'login' => [\n\t\t'username' => 'Felhasználónév',\n\t\t'password' => 'Jelszó',\n\t\t'language' => 'Nyelv',\n\t\t'login' => 'Bejelentkezés',\n\t\t'logout' => 'Kijelentkezés',\n\t\t'profile_lng' => 'Profil nyelve',\n\t\t'welcomemsg' => 'Kérjük, jelentkezzen be a fiókjához.',\n\t\t'forgotpwd' => 'Elfelejtette jelszavát?',\n\t\t'presend' => 'Jelszó visszaállítása',\n\t\t'email' => 'E-mail cím',\n\t\t'remind' => 'Jelszavam visszaállítása',\n\t\t'usernotfound' => 'Felhasználó nem található!',\n\t\t'backtologin' => 'Vissza a bejelentkezéshez',\n\t\t'combination_not_found' => 'A felhasználó és e-mail cím kombinációja nem található.',\n\t\t'2fa' => 'Kétfaktoros hitelesítés (2FA)',\n\t\t'2facode' => 'Kérjük, adja meg a 2FA kódot',\n\t\t'2faremember' => 'Böngésző megjegyzése',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Üdvözöljük,\\\\n\\\\ne-mail fiókja {EMAIL}\\\\nsikeresen beállításra került.\\\\n\\\\nEz egy automatikusan létrehozott\\\\ne-mail, kérjük, ne válaszoljon rá!\\\\n\\\\nÜdvözlettel, az adminisztrátor',\n\t\t\t'subject' => 'E-mail fiók sikeresen beállítva',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Tisztelt {SALUTATION},\\\\n\\\\níme a fiókja adatai:\\\\n\\\\nFelhasználónév: {USERNAME}\\\\nJelszó: {PASSWORD}\\\\n\\\\nKöszönjük,\\\\naz adminisztrátor',\n\t\t\t'subject' => 'Fiók információk',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Tisztelt {SALUTATION},\\\\n\\\\ne-mail fiókja {EMAIL}\\\\nsikeresen beállításra került.\\\\nA jelszava: {PASSWORD}.\\\\n\\\\nEz egy automatikusan létrehozott\\\\ne-mail, kérjük, ne válaszoljon rá!\\\\n\\\\nÜdvözlettel, az adminisztrátor',\n\t\t\t'subject' => 'E-mail fiók sikeresen beállítva',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Jelszó visszaállítása',\n\t\t\t'mailbody' => 'Tisztelt {SALUTATION},\\\\n\\\\níme a link az új jelszó beállításához. Ez a link a következő 24 órában érvényes.\\\\n\\\\n{LINK}\\\\n\\\\nKöszönjük,\\\\naz adminisztrátor',\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] Új adatbázis létrehozva',\n\t\t\t'mailbody' => 'Tisztelt {CUST_NAME},\n\nÖn éppen most hozott létre egy új adatbázist. Íme a megadott információk:\n\nAdatbázis neve: {DB_NAME}\nJelszó: {DB_PASS}\nLeírás: {DB_DESC}\nDB-Hoszt: {DB_SRV}\nphpMyAdmin: {PMA_URI}\nÜdvözlettel, az adminisztrátor',\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'Új ftp-felhasználó létrehozva',\n\t\t\t'mailbody' => 'Tisztelt {CUST_NAME},\n\nÖn éppen most hozott létre egy új ftp-felhasználót. Íme a megadott információk:\n\nFelhasználónév: {USR_NAME}\nJelszó: {USR_PASS}\nÚtvonal: {USR_PATH}\n\nÜdvözlettel, az adminisztrátor',\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => 'Tisztelt {SALUTATION},\\\\n\\\\nÖn felhasználta a rendelkezésre álló {TRAFFIC} forgalomból {TRAFFICUSED} mennyiséget.\\\\nEz több mint {MAX_PERCENT}%%.\\\\n\\\\nÜdvözlettel, az adminisztrátor',\n\t\t\t'subject' => 'Forgalmi korlát elérése',\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => 'Tisztelt {SALUTATION},\\\\n\\\\nÖn felhasználta a rendelkezésre álló {DISKAVAILABLE} tárhelyből {DISKUSED} mennyiséget.\\\\nEz több mint {MAX_PERCENT}%%.\\\\n\\\\nÜdvözlettel, az adminisztrátor',\n\t\t\t'subject' => 'Tárhely korlát elérése',\n\t\t],\n\t\t'2fa' => [\n\t\t\t'mailbody' => 'Üdvözöljük,\\\\n\\\\naz Ön 2FA bejelentkezési kódja: {CODE}.\\\\n\\\\nEz egy automatikusan létrehozott\\\\ne-mail, kérjük, ne válaszoljon rá!\\\\n\\\\nÜdvözlettel, az adminisztrátor',\n\t\t\t'subject' => 'froxlor - 2FA Kód',\n\t\t],\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Főoldal',\n\t\t\t'changepassword' => 'Jelszó módosítása',\n\t\t\t'changelanguage' => 'Nyelv módosítása',\n\t\t\t'username' => 'Bejelentkezve mint: ',\n\t\t\t'changetheme' => 'Téma módosítása',\n\t\t\t'apihelp' => 'API segítség',\n\t\t\t'apikeys' => 'API kulcsok',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'E-mail',\n\t\t\t'emails' => 'Címek',\n\t\t\t'webmail' => 'Webmail',\n\t\t\t'emailsoverview' => 'E-mail domainek áttekintése',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Adatbázisok',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domainek',\n\t\t\t'settings' => 'Domainek áttekintése',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Fiókok',\n\t\t\t'webftp' => 'WebFTP',\n\t\t\t'sshkeys' => 'SSH kulcsok',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extrák',\n\t\t\t'directoryprotection' => 'Könyvtárvédelem',\n\t\t\t'pathoptions' => 'Útvonal beállítások',\n\t\t\t'export' => 'Adatexport',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Forgalom',\n\t\t\t'current' => 'Aktuális hónap',\n\t\t\t'overview' => 'Teljes forgalom',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'PHP konfigurációk',\n\t\t\t'fpmdaemons' => 'PHP-FPM verziók',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'Rendszernapló',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'Nem került e-mail kiküldésre, mert nincsenek címzettek az adatbázisban',\n\t\t'success' => 'Sikeresen elküldve %s címzettnek',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'Felhasználó/Adatbázis név',\n\t\t'databasedescription' => 'Adatbázis leírása',\n\t\t'database_create' => 'Adatbázis létrehozása',\n\t\t'description' => 'Itt hozhatja létre és módosíthatja MySQL adatbázisait.<br />A változtatások azonnal életbe lépnek, és az adatbázis azonnal használható.<br />A bal oldali menüben találja a phpMyAdmin eszközt, amellyel könnyen kezelheti adatbázisát.<br /><br />Saját PHP szkriptjeiben az adatbázisok használatához a következő beállításokat használja: (A <i>dőlt betűs</i> adatokat cserélje ki a megfelelő értékekre!)<br />Kiszolgáló: <b><SQL_HOST></b><br />Felhasználónév: <b><i>adatbázisnév</i></b><br />Jelszó: <b><i>az Ön által választott jelszó</i></b><br />Adatbázis: <b><i>adatbázisnév</i></b>',\n\t\t'mysql_server' => 'MySQL-Szerver',\n\t\t'database_edit' => 'Adatbázis szerkesztése',\n\t\t'size' => 'Méret',\n\t\t'privileged_user' => 'Privilegizált adatbázis felhasználó',\n\t\t'privileged_passwd' => 'Jelszó a privilegizált felhasználóhoz',\n\t\t'unprivileged_passwd' => 'Jelszó a nem privilegizált felhasználóhoz',\n\t\t'mysql_ssl_ca_file' => 'SSL szerver tanúsítvány',\n\t\t'mysql_ssl_verify_server_certificate' => 'SSL szerver tanúsítvány ellenőrzése',\n\t\t'globaluserinfo' => 'Az adatbázisok eléréséhez használhatja a froxlor bejelentkezési adatait is (felhasználó: %s), amely automatikusan hozzáfér az összes adatbázisához.<br />Ajánlott <b>nem</b> használni ezt alkalmazásokhoz, csak adminisztrációhoz (pl. phpMyAdmin-on keresztül).',\n\t\t'edit_global_user' => 'Admin felhasználó szerkesztése',\n\t],\n\t'opcacheinfo' => [\n\t\t'generaltitle' => 'Általános információk',\n\t\t'resetcache' => 'OPcache visszaállítása',\n\t\t'version' => 'OPCache verzió',\n\t\t'phpversion' => 'PHP verzió',\n\t\t'runtimeconf' => 'Futásidejű konfiguráció',\n\t\t'start' => 'Indítás ideje',\n\t\t'lastreset' => 'Utolsó újraindítás',\n\t\t'oomrestarts' => 'OOM újraindítások száma',\n\t\t'hashrestarts' => 'Hash újraindítások száma',\n\t\t'manualrestarts' => 'Manuális újraindítások száma',\n\t\t'hitsc' => 'Találatok száma',\n\t\t'missc' => 'Hiányzások száma',\n\t\t'blmissc' => 'Feketelista hiányzások száma',\n\t\t'status' => 'Állapot',\n\t\t'never' => 'soha',\n\t\t'enabled' => 'OPcache engedélyezve',\n\t\t'cachefull' => 'Gyorsítótár tele',\n\t\t'restartpending' => 'Újraindítás függőben',\n\t\t'restartinprogress' => 'Újraindítás folyamatban',\n\t\t'cachedscripts' => 'Gyorsítótárazott szkriptek száma',\n\t\t'memusage' => 'Memóriahasználat',\n\t\t'totalmem' => 'Teljes memória',\n\t\t'usedmem' => 'Használt memória',\n\t\t'freemem' => 'Szabad memória',\n\t\t'wastedmem' => 'Elpazarolt memória',\n\t\t'maxkey' => 'Maximális kulcsok',\n\t\t'usedkey' => 'Használt kulcsok',\n\t\t'wastedkey' => 'Elpazarolt kulcsok',\n\t\t'strinterning' => 'String internálás',\n\t\t'strcount' => 'String szám',\n\t\t'keystat' => 'Gyorsítótárazott kulcsok statisztikája',\n\t\t'used' => 'Használt',\n\t\t'free' => 'Szabad',\n\t\t'blacklist' => 'Feketelista',\n\t\t'novalue' => '<i>nincs érték</i>',\n\t\t'true' => '<i>igaz</i>',\n\t\t'false' => '<i>hamis</i>',\n\t\t'funcsavail' => 'Elérhető funkciók',\n\t],\n\t'panel' => [\n\t\t'edit' => 'Szerkesztés',\n\t\t'delete' => 'Törlés',\n\t\t'create' => 'Létrehozás',\n\t\t'save' => 'Mentés',\n\t\t'yes' => 'Igen',\n\t\t'no' => 'Nem',\n\t\t'emptyfornochanges' => 'üresen hagyva nem változik',\n\t\t'emptyfordefault' => 'üresen hagyva alapértelmezett',\n\t\t'path' => 'Útvonal',\n\t\t'toggle' => 'Váltás',\n\t\t'next' => 'Következő',\n\t\t'dirsmissing' => 'Nem található vagy nem olvasható a könyvtár!',\n\t\t'unlimited' => '∞',\n\t\t'urloverridespath' => 'URL (felülírja az útvonalat)',\n\t\t'pathorurl' => 'Útvonal vagy URL',\n\t\t'ascending' => 'növekvő',\n\t\t'descending' => 'csökkenő',\n\t\t'search' => 'Keresés',\n\t\t'used' => 'használt',\n\t\t'translator' => 'Fordító',\n\t\t'reset' => 'Változtatások elvetése',\n\t\t'pathDescription' => 'Ha a könyvtár nem létezik, automatikusan létrehozásra kerül.',\n\t\t'pathDescriptionEx' => '<br /><br /><span class=\"text-danger\">Kérjük, vegye figyelembe:</span> A <code>/</code> útvonal nem engedélyezett az adminisztratív beállítások miatt, automatikusan <code>/választott.aldomain.tld/</code> értékre lesz állítva, ha nem állítja be másik könyvtárra.',\n\t\t'pathDescriptionSubdomain' => 'Ha a könyvtár nem létezik, automatikusan létrehozásra kerül.<br /><br />Ha másik domainre szeretne átirányítani, akkor ennek a bejegyzésnek http:// vagy https:// előtaggal kell kezdődnie.<br /><br />Ha az URL /-rel végződik, akkor mappának tekintjük, ha nem, akkor fájlként kezeljük.',\n\t\t'back' => 'Vissza',\n\t\t'reseller' => 'viszonteladó',\n\t\t'admin' => 'admin',\n\t\t'customer' => 'ügyfél/ügyfelek',\n\t\t'send' => 'küldés',\n\t\t'nosslipsavailable' => 'Jelenleg nincsenek ssl ip/port kombinációk ehhez a szerverhez',\n\t\t'backtooverview' => 'Vissza az áttekintéshez',\n\t\t'dateformat' => 'ÉÉÉÉ-HH-NN',\n\t\t'dateformat_function' => 'Y-m-d',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Alapértelmezett',\n\t\t'never' => 'Soha',\n\t\t'active' => 'Aktív',\n\t\t'please_choose' => 'Kérjük, válasszon',\n\t\t'allow_modifications' => 'Módosítások engedélyezése',\n\t\t'megabyte' => 'MegaByte',\n\t\t'not_supported' => 'Nem támogatott: ',\n\t\t'view' => 'megtekintés',\n\t\t'toomanydirs' => 'Túl sok alkönyvtár. Visszatérés a manuális útvonal-kiválasztáshoz.',\n\t\t'abort' => 'Megszakítás',\n\t\t'not_activated' => 'nincs aktiválva',\n\t\t'off' => 'ki',\n\t\t'options' => 'Opciók',\n\t\t'neverloggedin' => 'Még nem jelentkezett be',\n\t\t'descriptionerrordocument' => 'Lehet URL, fájl útvonala vagy egyszerűen egy \" \" közé zárt szöveg.<br />Hagyja üresen a szerver alapértelmezett értékének használatához.',\n\t\t'unlock' => 'Feloldás',\n\t\t'theme' => 'Téma',\n\t\t'variable' => 'Változó',\n\t\t'description' => 'Leírás',\n\t\t'cancel' => 'Mégse',\n\t\t'ssleditor' => 'SSL beállítások ehhez a domainhez',\n\t\t'ssleditor_infoshared' => 'Jelenleg a szülődomain tanúsítványát használja',\n\t\t'ssleditor_infoglobal' => 'Jelenleg globális tanúsítványt használ',\n\t\t'dashboard' => 'Irányítópult',\n\t\t'assigned' => 'Hozzárendelt',\n\t\t'available' => 'Elérhető',\n\t\t'news' => 'Hírek',\n\t\t'newsfeed_disabled' => 'A hírcsatorna le van tiltva. Kattintson a szerkesztés ikonra a beállításokhoz való ugráshoz.',\n\t\t'ftpdesc' => 'FTP leírás',\n\t\t'letsencrypt' => 'Let\\'s encrypt használata',\n\t\t'set' => 'Alkalmaz',\n\t\t'shell' => 'Shell',\n\t\t'exportpath' => [\n\t\t\t'title' => 'Az exportált adatok célútvonala',\n\t\t\t'description' => 'Ez az az útvonal, ahol az export-archívum tárolva lesz. Ha web-adatok is szerepelnek, az összes fájl a kezdőkönyvtárból tárolva lesz, kivéve az itt megadott mappát.',\n\t\t],\n\t\t'export_pgp_public_key' => [\n\t\t\t'title' => 'Nyilvános PGP kulcs a titkosításhoz',\n\t\t\t'description' => 'Ez a nyilvános PGP kulcs, amelyet az export titkosításához használnak. Ha ezt a mezőt üresen hagyja, az export nem lesz titkosítva.',\n\t\t],\n\t\t'pgp_public_key' => 'Nyilvános PGP kulcs',\n\t\t'none_value' => 'Nincs',\n\t\t'viewlogs' => 'Naplófájlok megtekintése',\n\t\t'not_configured' => 'A rendszer még nincs konfigurálva. Kattintson ide a konfigurációkhoz való ugráshoz.',\n\t\t'ihave_configured' => 'Konfiguráltam a szolgáltatásokat',\n\t\t'system_is_configured' => '<i class=\"fa-solid fa-circle-exclamation me-1\"></i>A rendszer már konfigurálva van',\n\t\t'settings_before_configuration' => 'Kérjük, győződjön meg róla, hogy a beállításokat a szolgáltatások konfigurálása előtt módosította',\n\t\t'image_field_delete' => 'A meglévő kép törlése',\n\t\t'usage_statistics' => 'Erőforrás-használat',\n\t\t'security_question' => 'Biztonsági kérdés',\n\t\t'listing_empty' => 'Nincsenek bejegyzések',\n\t\t'unspecified' => 'nincs megadva',\n\t\t'settingsmode' => 'Mód',\n\t\t'settingsmodebasic' => 'Alap',\n\t\t'settingsmodeadvanced' => 'Haladó',\n\t\t'settingsmodetoggle' => 'Kattintson a mód váltásához',\n\t\t'modalclose' => 'Bezárás',\n\t\t'managetablecolumnsmodal' => [\n\t\t\t'title' => 'Táblázat oszlopainak kezelése',\n\t\t\t'description' => 'Itt testreszabhatja a látható oszlopokat',\n\t\t],\n\t\t'mandatoryfield' => 'A mező kötelező',\n\t\t'select_all' => 'Összes kiválasztása',\n\t\t'unselect_all' => 'Összes kiválasztásának megszüntetése',\n\t\t'searchtablecolumnsmodal' => [\n\t\t\t'title' => 'Keresés a mezőkben',\n\t\t\t'description' => 'Válassza ki a mezőt, amelyben keresni szeretne'\n\t\t],\n\t\t'upload_import' => 'Feltöltés és importálás',\n\t\t'profile' => 'Profilom',\n\t\t'use_checkbox_for_unlimited' => 'A \"0\" érték deaktiválja ezt az erőforrást. A jobb oldali jelölőnégyzet lehetővé teszi a \"korlátlan\" használatot.',\n\t\t'use_checkbox_to_disable' => 'A letiltáshoz aktiválja a jelölőnégyzetet a beviteli mező jobb oldalán',\n\t\t'sshkeydesc' => 'SSH kulcs leírás',\n\t\t'ftpuser' => 'FTP felhasználó',\n\t\t'sshpubkey' => 'SSH nyilvános kulcs',\n\t\t'sshpubkeyph' => 'Illessze be az SSH nyilvános kulcsát ide',\n\t\t'sshfingerprint' => 'Ujjlenyomat',\n\t\t'start_setup' => 'Beállítások indítása',\n\t\t'distro_mismatch' => 'Úgy tűnik, hogy új disztribúcióra frissített. Kérjük, ne felejtse el újrakonfigurálni a szolgáltatásokat.',\n\t\t'set_new_distro' => 'Disztribúció beállítása',\n\t\t'dismiss' => 'Elvetés',\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => 'Helyi felhasználó a PHP-FPM-hez (froxlor vHost)',\n\t\t'vhost_httpgroup' => 'Helyi csoport a PHP-FPM-hez (froxlor vHost)',\n\t\t'ownvhost' => [\n\t\t\t'title' => 'PHP-FPM engedélyezése a froxlor vHost számára',\n\t\t\t'description' => 'Ha engedélyezve van, a froxlor is egy helyi felhasználó alatt fog futni',\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => 'mod_proxy / mod_proxy_fcgi használata',\n\t\t\t'description' => '<strong class=\"text-danger\">Engedélyezni kell, ha Debian 9.x (Stretch) vagy újabb verziót használ</strong>. Aktiválja a php-fpm mod_proxy_fcgi-n keresztüli használatához. Legalább apache-2.4.9 szükséges',\n\t\t],\n\t\t'ini_flags' => 'Adja meg a lehetséges <strong>php_flag</strong> értékeket a php.ini-hez. Egy bejegyzés soronként',\n\t\t'ini_values' => 'Adja meg a lehetséges <strong>php_value</strong> értékeket a php.ini-hez. Egy bejegyzés soronként',\n\t\t'ini_admin_flags' => 'Adja meg a lehetséges <strong>php_admin_flag</strong> értékeket a php.ini-hez. Egy bejegyzés soronként',\n\t\t'ini_admin_values' => 'Adja meg a lehetséges <strong>php_admin_value</strong> értékeket a php.ini-hez. Egy bejegyzés soronként',\n\t],\n\t'privacy' => 'Adatvédelmi irányelvek',\n\t'pwdreminder' => [\n\t\t'success' => 'A jelszó visszaállítása sikeresen kérve. Kérjük, kövesse az e-mailben kapott utasításokat.',\n\t\t'notallowed' => 'Ismeretlen felhasználó vagy a jelszó visszaállítása le van tiltva',\n\t\t'changed' => 'A jelszava sikeresen frissítve lett. Most már bejelentkezhet az új jelszavával.',\n\t\t'wrongcode' => 'Sajnáljuk, az aktiváló kód nem létezik vagy már lejárt.',\n\t\t'choosenew' => 'Új jelszó beállítása',\n\t],\n\t'question' => [\n\t\t'question' => 'Biztonsági kérdés',\n\t\t'admin_customer_reallydelete' => 'Valóban törölni szeretné a(z) %s ügyfelet? Ez nem vonható vissza!',\n\t\t'admin_domain_reallydelete' => 'Valóban törölni szeretné a(z) %s domaint?<br><span class=\"text-danger\"><strong>MEGJEGYZÉS:</strong> Az összes aldomain, ftp-fiók és e-mail cím/fiók, amely ehhez a domainhez kapcsolódik, eltávolításra kerül!</span>',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Valóban le szeretné tiltani ezt a biztonsági beállítást, az OpenBasedir-t?',\n\t\t'admin_admin_reallydelete' => 'Valóban törölni szeretné a(z) %s adminisztrátort? Minden ügyfél és domain az Ön fiókjához lesz áthelyezve.',\n\t\t'admin_template_reallydelete' => 'Valóban törölni szeretné a(z) \\'%s\\' sablont?',\n\t\t'domains_reallydelete' => 'Valóban törölni szeretné a(z) %s domaint?',\n\t\t'email_reallydelete' => 'Valóban törölni szeretné a(z) %s e-mail címet?',\n\t\t'email_reallydelete_account' => 'Valóban törölni szeretné a(z) %s e-mail fiókot?',\n\t\t'email_reallydelete_forwarder' => 'Valóban törölni szeretné a(z) %s továbbítót?',\n\t\t'extras_reallydelete' => 'Valóban törölni szeretné a(z) %s könyvtárvédelmet?',\n\t\t'extras_reallydelete_pathoptions' => 'Valóban törölni szeretné a(z) %s útvonal opciókat?',\n\t\t'extras_reallydelete_export' => 'Valóban meg szeretné szakítani a tervezett exportálási feladatot?',\n\t\t'ftp_reallydelete' => 'Valóban törölni szeretné a(z) %s FTP-fiókot?',\n\t\t'mysql_reallydelete' => 'Valóban törölni szeretné a(z) %s adatbázist? Ez nem vonható vissza!',\n\t\t'admin_configs_reallyrebuild' => 'Valóban újra szeretné építeni az összes konfigurációs fájlt?',\n\t\t'admin_customer_alsoremovefiles' => 'Felhasználói fájlok is eltávolításra kerüljenek?',\n\t\t'admin_customer_alsoremovemail' => 'Teljesen eltávolítja az e-mail adatokat a fájlrendszerből?',\n\t\t'admin_customer_alsoremoveftphomedir' => 'Az FTP-felhasználó kezdőkönyvtára is eltávolításra kerüljön?',\n\t\t'admin_ip_reallydelete' => 'Valóban törölni szeretné a(z) %s IP címet?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Biztos benne, hogy a domain dokumentumgyökere nem az ügyfél gyökérkönyvtárában lesz?',\n\t\t'admin_counters_reallyupdate' => 'Valóban újra szeretné számolni az erőforrás-használatot?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Valóban törölni szeretné az összes titkosítatlan e-mail fiók jelszavát a mail_users táblából? Ez nem vonható vissza! Az e-mail jelszavak titkosítatlan tárolásának beállítása is KI lesz kapcsolva',\n\t\t'logger_reallytruncate' => 'Valóban törölni szeretné a(z) \"%s\" táblát?',\n\t\t'admin_quotas_reallywipe' => 'Valóban törölni szeretné az összes kvótát a mail_users táblából? Ez nem vonható vissza!',\n\t\t'admin_quotas_reallyenforce' => 'Valóban érvényesíteni szeretné az alapértelmezett kvótát minden felhasználóra? Ez nem vonható vissza!',\n\t\t'phpsetting_reallydelete' => 'Valóban törölni szeretné ezeket a beállításokat? Az összes domain, amely jelenleg ezeket a beállításokat használja, az alapértelmezett konfigurációra lesz változtatva.',\n\t\t'fpmsetting_reallydelete' => 'Valóban törölni szeretné ezeket a php-fpm beállításokat? Az összes php konfiguráció, amely jelenleg ezeket a beállításokat használja, az alapértelmezett konfigurációra lesz változtatva.',\n\t\t'customer_reallyunlock' => 'Valóban fel szeretné oldani a(z) %s ügyfelet?',\n\t\t'admin_integritycheck_reallyfix' => 'Valóban meg szeretné próbálni automatikusan javítani az összes adatbázis integritási problémát?',\n\t\t'plan_reallydelete' => 'Valóban törölni szeretné a(z) %s tárhelytervet?',\n\t\t'apikey_reallydelete' => 'Valóban törölni szeretné ezt az api-kulcsot?',\n\t\t'apikey_reallyadd' => 'Valóban létre szeretne hozni egy új api-kulcsot?',\n\t\t'dnsentry_reallydelete' => 'Valóban törölni szeretné ezt a zóna bejegyzést?',\n\t\t'certificate_reallydelete' => 'Valóban törölni szeretné ezt a tanúsítványt?',\n\t\t'cache_reallydelete' => 'Valóban törölni szeretné a gyorsítótárat?',\n\t\t'please_enter_otp' => 'Kérjük, adja meg a 2FA kódot',\n\t\t'admin_mysqlserver_reallydelete' => 'Valóban törölni szeretné ezt a MySQL-szervert?',\n\t\t'email_reallydelete_sender' => 'Valóban törölni szeretné a(z) %s engedélyezett feladót?',\n\t\t'sshkey_reallydelete' => 'Valóban törölni szeretné a(z) %s ssh kulcsot?',\n\t],\n\t'redirect_desc' => [\n\t\t'rc_default' => 'alapértelmezett',\n\t\t'rc_movedperm' => 'véglegesen áthelyezve',\n\t\t'rc_found' => 'megtalálva',\n\t\t'rc_seeother' => 'lásd másik',\n\t\t'rc_tempred' => 'ideiglenes átirányítás',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Munkamenet időtúllépés',\n\t\t\t'description' => 'Mennyi ideig kell egy felhasználónak inaktívnak lennie, mielőtt a munkamenet érvénytelenné válik (másodperc)?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Ügyfél előtag',\n\t\t\t'description' => 'Milyen előtagot kell az ügyfélfiókoknak kapniuk?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'SQL előtag',\n\t\t\t'description' => 'Milyen előtagot kell a MySQL fiókoknak kapniuk?</br>Használja a \"RANDOM\" értéket, hogy 3 számjegyű véletlenszerű előtagot kapjon</br>Használja a \"DBNAME\" értéket, hogy az adatbázis név mezőt használja az ügyfél névvel együtt előtagként.',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP előtag',\n\t\t\t'description' => 'Milyen előtagot kell az ftp fiókoknak kapniuk?<br/><b>Ha ezt megváltoztatja, akkor meg kell változtatnia a kvóta SQL lekérdezést az FTP szerver konfigurációs fájljában, ha használja!</b> ',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Kezdőkönyvtár',\n\t\t\t'description' => 'Hol kell tárolni az összes kezdőkönyvtárat?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Naplófájlok könyvtára',\n\t\t\t'description' => 'Hol kell tárolni az összes naplófájlt?',\n\t\t],\n\t\t'logfiles_script' => [\n\t\t\t'title' => 'Egyéni szkript a naplófájlok csövezéséhez',\n\t\t\t'description' => 'Itt megadhat egy szkriptet, és szükség esetén használhatja a <strong>{LOGFILE}, {DOMAIN} és {CUSTOMER}</strong> helyőrzőket. Ha használni szeretné, akkor aktiválnia kell a <strong>Webszerver naplófájlok csövezése</strong> opciót is. Nincs szükség előtagolt cső karakterre.',\n\t\t],\n\t\t'logfiles_format' => [\n\t\t\t'title' => 'Hozzáférési napló formátum',\n\t\t\t'description' => 'Adjon meg egy egyéni naplóformátumot a webszerver specifikációinak megfelelően, hagyja üresen az alapértelmezettért. A formátumtól függően az idézőjelek közé kell tenni a karakterláncot.<br/>Ha nginx-szel használja, így fog kinézni: <i>log_format frx_custom {CONFIGURED_VALUE}</i>.<br/>Ha Apache-szal használja, így fog kinézni: <i>LogFormat {CONFIGURED_VALUE} frx_custom</i>.<br/><strong>Figyelem</strong>: A kód nem lesz ellenőrizve hibákra. Ha hibákat tartalmaz, a webszerver lehet, hogy nem indul újra!',\n\t\t],\n\t\t'logfiles_type' => [\n\t\t\t'title' => 'Hozzáférési napló típusa',\n\t\t\t'description' => 'Válasszon a <strong>kombinált</strong> vagy <strong>vhost_combined</strong> közül.',\n\t\t],\n\t\t'logfiles_piped' => [\n\t\t\t'title' => 'Webszerver naplófájlok csövezése a megadott szkripthez (lásd fent)',\n\t\t\t'description' => 'Ha egyéni szkriptet használ a naplófájlokhoz, akkor aktiválnia kell ezt, hogy végrehajtásra kerüljön',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'IP-cím',\n\t\t\t'description' => 'Mi a szerver fő IP-címe?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Hosztnév',\n\t\t\t'description' => 'Mi a szerver hosztneve?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Webszerver újratöltési parancs',\n\t\t\t'description' => 'Mi a webszerver parancsa a konfigurációs fájlok újratöltéséhez?',\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => 'Névszerver engedélyezése',\n\t\t\t'description' => 'Itt a névszerver globálisan engedélyezhető és letiltható.',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'DNS szerver konfigurációs könyvtár',\n\t\t\t'description' => 'Hol kell menteni a dns-szerver konfigurációs fájlokat?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'DNS szerver újratöltési parancs',\n\t\t\t'description' => 'Mi a parancs a dns szerver démon újratöltéséhez?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'Levelek-UID',\n\t\t\t'description' => 'Milyen UserID-t kell a leveleknek kapniuk?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Levelek-GID',\n\t\t\t'description' => 'Milyen GroupID-t kell a leveleknek kapniuk?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Levelek-kezdőkönyvtár',\n\t\t\t'description' => 'Hol kell tárolni az összes levelet?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Feladó',\n\t\t\t'description' => 'Mi a feladó címe a Panelből küldött e-maileknek?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin URL',\n\t\t\t'description' => 'Mi a phpMyAdmin URL-je? (http(s)://-vel kell kezdődnie)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'Webmail URL',\n\t\t\t'description' => 'Mi a webmail URL-je? (http(s)://-vel kell kezdődnie)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP URL',\n\t\t\t'description' => 'Mi a WebFTP URL-je? (http(s)://-vel kell kezdődnie)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Mi a szerver alapértelmezett nyelve?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Maximális bejelentkezési kísérletek',\n\t\t\t'description' => 'Maximális bejelentkezési kísérletek száma, amely után a fiók letiltásra kerül.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Letiltási idő',\n\t\t\t'description' => 'Idő (mp.), amíg egy fiók letiltásra kerül túl sok bejelentkezési próbálkozás után.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Útvonal bevitel típusa',\n\t\t\t'description' => 'Útvonalat egy legördülő menüvel vagy egy beviteli mezővel kell kiválasztani?',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Névszerverek',\n\t\t\t'description' => 'Egy vesszővel elválasztott lista, amely az összes névszerver hosztnevét tartalmazza. Az első lesz az elsődleges.',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'MX szerverek',\n\t\t\t'description' => 'Egy vesszővel elválasztott lista, amely egy számot és egy hosztnevet tartalmaz, szóközzel elválasztva (pl. \\'10 mx.example.com\\') az mx szervereket tartalmazza.',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Bejegyzések oldalanként',\n\t\t\t'description' => 'Hány bejegyzést kell megjeleníteni egy oldalon? (0 = lapozás letiltása)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'Alapértelmezett IP/Port',\n\t\t\t'description' => 'Válassza ki az összes IP-címet, amelyet alapértelmezettként szeretne használni új domain-ekhez',\n\t\t],\n\t\t'defaultsslip' => [\n\t\t\t'title' => 'Alapértelmezett SSL IP/Port',\n\t\t\t'description' => 'Válassza ki az összes SSL-kompatibilis IP-címet, amelyet alapértelmezettként szeretne használni új domain-ekhez',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Útvonalak hozzáadása az OpenBasedir-hez',\n\t\t\t'description' => 'Ezek az útvonalak (kettősponttal elválasztva) hozzáadódnak az OpenBasedir-nyilatkozathoz minden vHost-konténerben.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Természetes emberi rendezés használata a listanézetben',\n\t\t\t'description' => 'A listákat web1 -> web2 -> web11 sorrendben rendezi, nem pedig web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Dokumentumgyökér letiltott felhasználók számára',\n\t\t\t'description' => 'Amikor egy felhasználó letiltásra kerül, ez az útvonal lesz használva dokumentumgyökérként. Hagyja üresen, ha nem szeretne vHost-ot létrehozni.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'E-mail fiókok jelszavainak titkosítatlan mentése az adatbázisban',\n\t\t\t'description' => 'Ha ez be van állítva igenre, akkor az összes jelszó titkosítatlanul (olvashatóan mindenki számára, aki hozzáfér az adatbázishoz) lesz mentve a mail_users táblában. Csak akkor aktiválja ezt, ha SASL-t szeretne használni!',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'FTP fiókok @domain',\n\t\t\t'description' => 'Az ügyfelek létrehozhatnak FTP fiókokat user@customerdomain?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'FCGID engedélyezése',\n\t\t\t'description' => 'Használja ezt a PHP futtatásához a megfelelő felhasználói fiókkal.<br /><br /><b>Ehhez speciális webszerver konfiguráció szükséges Apache-hoz, lásd <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - kézikönyv</a></b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Konfigurációs könyvtár',\n\t\t\t\t'description' => 'Hol kell tárolni az összes fcgid-konfigurációs fájlt? Ha nem használ saját fordítású suexec binárist, ami a normál helyzet, akkor ennek az útnak a /var/www alatt kell lennie.<br /><br /><div class=\"text-danger\">MEGJEGYZÉS: Ennek a mappának a tartalma rendszeresen törlődik, ezért kerülje az adatok kézi tárolását benne.</div>',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Ideiglenes könyvtár',\n\t\t\t\t'description' => 'Hol kell tárolni az ideiglenes könyvtárakat',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Folyamatok domainenként',\n\t\t\t\t'description' => 'Hány folyamatot kell indítani/engedélyezni domainenként? A 0 érték ajánlott, mert a PHP akkor nagyon hatékonyan kezeli a folyamatok számát.',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper a Vhostokban',\n\t\t\t\t'description' => 'Hogyan legyen a wrapper beillesztve a Vhostokba',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Globális PEAR könyvtárak',\n\t\t\t\t'description' => 'Mely globális PEAR könyvtárakat kell kicserélni minden php.ini konfigurációban? Különböző könyvtárakat kettősponttal kell elválasztani.',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Maximális kérések domainenként',\n\t\t\t\t'description' => 'Hány kérést engedélyezzen domainenként?',\n\t\t\t],\n\t\t\t'defaultini' => 'Alapértelmezett PHP konfiguráció új domainekhez',\n\t\t\t'defaultini_ownvhost' => 'Alapértelmezett PHP konfiguráció froxlor-vHosthoz',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Tétlen időkorlát',\n\t\t\t\t'description' => 'Időkorlát beállítása Mod FastCGI-hez.',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Alternatív e-mail cím használata',\n\t\t\t'description' => 'A jelszó e-mailt egy másik címre küldje el az e-mail fiók létrehozása során',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Webszerver vHost konfigurációs fájl/könyvtárnév',\n\t\t\t'description' => 'Hol legyen tárolva a vHost konfiguráció? Megadhat egy fájlt (minden vHost egy fájlban) vagy könyvtárat (minden vHost saját fájlban) itt.',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Webszerver diroptions konfigurációs fájl/könyvtárnév',\n\t\t\t'description' => 'Hol legyen tárolva a diroptions konfiguráció? Megadhat egy fájlt (minden diroptions egy fájlban) vagy könyvtárat (minden diroption saját fájlban) itt.',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Webszerver htpasswd könyvtárnév',\n\t\t\t'description' => 'Hol legyenek tárolva a htpasswd fájlok a könyvtárvédelemhez?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'MySQL-Hozzáférési-Gazdák',\n\t\t\t'description' => 'Egy vesszővel elválasztott lista a gazdákról, ahonnan a felhasználók csatlakozhatnak a MySQL-szerverhez. Alhálózat engedélyezéséhez a hálózati maszk vagy cidr szintaxis érvényes.',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Webalizer kimenet',\n\t\t\t'description' => 'A webalizer-program részletessége',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Naplózás engedélyezve/letiltva',\n\t\t\t'severity' => 'Naplózási szint',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Napló típus(ok)',\n\t\t\t\t'description' => 'Adja meg a naplótípusokat. Több típus kiválasztásához tartsa lenyomva a CTRL-t a kiválasztás közben.<br />Elérhető naplótípusok: syslog, fájl, mysql',\n\t\t\t],\n\t\t\t'logfile' => [\n\t\t\t\t'title' => 'Napló fájlnév',\n\t\t\t\t'description' => 'Csak akkor használatos, ha a napló típus tartalmazza a \"fájl\"-t. Ez a fájl a froxlor/logs/ könyvtárban lesz létrehozva. Ez a mappa védett a nyilvános hozzáférés ellen.',\n\t\t\t],\n\t\t\t'logcron' => 'Cron feladatok naplózása',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Soha',\n\t\t\t\t'once' => 'Egyszer',\n\t\t\t\t'always' => 'Mindig',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => 'SSL használat engedélyezése',\n\t\t\t\t'description' => 'Jelölje be, ha SSL-t szeretne használni a webszerveréhez',\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'Az SSL tanúsítvány elérési útja',\n\t\t\t\t'description' => 'Adja meg az elérési utat, beleértve a .crt vagy .pem fájl (fő tanúsítvány) nevét',\n\t\t\t],\n\t\t\t'openssl_cnf' => 'Alapértelmezések a tanúsítvány fájl létrehozásához',\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'Az SSL kulcsfájl elérési útja',\n\t\t\t\t'description' => 'Adja meg az elérési utat, beleértve a privát kulcs fájl (.key többnyire) nevét',\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'Az SSL CA tanúsítvány elérési útja (opcionális)',\n\t\t\t\t'description' => 'Ügyfél hitelesítés, csak akkor állítsa be, ha tudja, mi az.',\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => 'Az engedélyezett SSL titkosítások konfigurálása',\n\t\t\t\t'description' => 'Ez egy lista a titkosításokról, amelyeket használni szeretne (vagy nem szeretne) SSL használatakor. A titkosítások listájához és azok beillesztéséhez/kizárásához lásd a \"CIPHER LIST FORMAT\" és \"CIPHER STRINGS\" szakaszokat a <a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">titkosítások kézikönyv oldalán</a>.<br /><br /><b>Alapértelmezett érték:</b><pre>ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305</pre>',\n\t\t\t],\n\t\t\t'apache24_ocsp_cache_path' => [\n\t\t\t\t'title' => 'Apache 2.4: az OCSP tároló gyorsítótárának elérési útja',\n\t\t\t\t'description' => 'Az OCSP válaszok tárolására használt gyorsítótár konfigurálása, amelyeket a TLS kézfogásokba foglalnak.',\n\t\t\t],\n\t\t\t'ssl_protocols' => [\n\t\t\t\t'title' => 'A TLS protokoll verziójának konfigurálása',\n\t\t\t\t'description' => 'Ez egy lista az SSL protokollokról, amelyeket használni szeretne (vagy nem szeretne) SSL használatakor. <b>Megjegyzés:</b> Néhány régebbi böngésző nem támogatja a legújabb protokoll verziókat.<br /><br /><b>Alapértelmezett érték:</b><pre>TLSv1.2</pre>',\n\t\t\t],\n\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t'title' => 'Explicit TLSv1.3 titkosítások konfigurálása, ha használatban van',\n\t\t\t\t'description' => 'Ez egy lista a titkosításokról, amelyeket használni szeretne (vagy nem szeretne) TLSv1.3 használatakor. A titkosítások listájához és azok beillesztéséhez/kizárásához lásd a <a href=\"https://wiki.openssl.org/index.php/TLS1.3\">TLSv1.3 dokumentációját</a>.<br /><br /><b>Alapértelmezett érték üres</b>',\n\t\t\t],\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Alapértelmezett vHost-beállítások',\n\t\t\t'description' => 'Ennek a mezőnek a tartalma közvetlenül beillesztésre kerül ebbe az ip/port vHost konténerbe. Az alábbi változókat használhatja:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (ha alkalmazható)<br/> Figyelem: A kód nem lesz ellenőrizve hibákra. Ha hibákat tartalmaz, a webszerver nem indulhat újra!',\n\t\t],\n\t\t'apache_globaldiropt' => [\n\t\t\t'title' => 'Könyvtár opciók az ügyfél előtaghoz',\n\t\t\t'description' => 'Ennek a mezőnek a tartalma beillesztésre kerül az 05_froxlor_dirfix_nofcgid.conf apache konfigurációba. Ha üres, az alapértelmezett érték kerül használatra:<br><br>apache >=2.4<br><code>Require all granted<br>AllowOverride All</code><br><br>apache <=2.2<br><code>Order allow,deny<br>allow from all</code>',\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => 'Ennek a mezőnek a tartalma közvetlenül beillesztésre kerül a domain vHost konténerbe. Az alábbi változókat használhatja:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (ha alkalmazható)<br/> Figyelem: A kód nem lesz ellenőrizve hibákra. Ha hibákat tartalmaz, a webszerver nem indulhat újra!',\n\t\t],\n\t\t'decimal_places' => 'Tizedesjegyek száma a forgalom/webtér kimenetben',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Ügyfél domain dns beállítások',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Engedélyezze az ügyfeleknek a domain dns beállítások szerkesztését',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'UNIX kompatibilis felhasználónevek használata',\n\t\t\t'description' => 'Lehetővé teszi a <strong>-</strong> és <strong>_</strong> használatát a felhasználónevekben, ha <strong>Nem</strong>',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Jelszó visszaállítás engedélyezése az ügyfelek számára',\n\t\t\t'description' => 'Az ügyfelek visszaállíthatják jelszavukat, és egy aktiváló linket küldünk az e-mail címükre',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Jelszó visszaállítás engedélyezése az adminok számára',\n\t\t\t'description' => 'Az adminok/viszonteladók visszaállíthatják jelszavukat, és egy aktiváló linket küldünk az e-mail címükre',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Postafiók-kvóta',\n\t\t\t'description' => 'Az újonnan létrehozott postafiókok alapértelmezett kvótája (MegaByte).',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Postafiók-kvóta használata az ügyfelek számára',\n\t\t\t'description' => 'Aktiválja a kvóták használatát a postafiókokon. Az alapértelmezett <b>Nem</b>, mivel ez speciális beállítást igényel.',\n\t\t\t'removelink' => 'Kattintson ide az összes kvóta törléséhez a levelezési fiókoknál.',\n\t\t\t'enforcelink' => 'Kattintson ide az alapértelmezett kvóta érvényesítéséhez az összes felhasználói levelezési fióknál.',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Többszörös bejelentkezés engedélyezése',\n\t\t\t'description' => 'Ha aktiválva van, egy felhasználó többször is bejelentkezhet.',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Domainek áthelyezésének engedélyezése adminok között',\n\t\t\t'description' => 'Ha aktiválva van, megváltoztathatja egy domain adminisztrátorát a domain beállításoknál.<br /><b>Figyelem:</b> Ha egy ügyfél nincs hozzárendelve ugyanahhoz az adminisztrátorhoz, mint a domain, az adminisztrátor láthatja az ügyfél összes többi domainjét!',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Engedélyezze a domainek áthelyezését az ügyfelek között',\n\t\t\t'description' => 'Ha aktiválva van, megváltoztathatja egy domain ügyfelét a domain beállításoknál.<br /><b>Figyelem:</b> A froxlor megváltoztatja a documentroot-ot az új ügyfél alapértelmezett homedir-jére (+ domain-mappa, ha aktiválva van)',\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'Ha igen, ezek az egyedi vHost-beállítások hozzáadódnak az összes aldomainhez; ha nem, az aldomain-speciális beállítások eltávolításra kerülnek.',\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Minimális jelszóhossz',\n\t\t\t'description' => 'Itt beállíthatja a jelszavak minimális hosszát. \\'0\\' azt jelenti: nincs minimális hosszúsági követelmény.',\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Az alapértelmezett index fájl tárolása új almappákban is',\n\t\t\t'description' => 'Ha engedélyezve van, az alapértelmezett index-fájl minden újonnan létrehozott aldomain-útvonalra elmentésre kerül (nem, ha a mappa már létezik!)',\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Válasz-cím',\n\t\t\t'description' => 'Adjon meg egy e-mail címet, mint válasz-cím a panel által küldött levelekhez.',\n\t\t],\n\t\t'adminmail_defname' => 'Panel e-mail küldő neve',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Ügyfél alapértelmezett aldomain',\n\t\t\t'description' => 'Milyen hosztnevet kell használni az ügyfél alapértelmezett aldomainjeinek létrehozásához. Ha üres, a rendszer-hosztnevet használja.',\n\t\t],\n\t\t'awstats_path' => 'AWStats \\'awstats_buildstaticpages.pl\\' elérési út',\n\t\t'awstats_conf' => 'AWStats konfigurációs útvonal',\n\t\t'defaultttl' => 'Domain TTL bind esetén másodpercben (alapértelmezett \\'604800\\' = 1 hét)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Alapértelmezett hibadokumentumok engedélyezése minden ügyfél számára',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'Fájl/URL a 401-es hibához',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'Fájl/URL a 403-as hibához',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'Fájl/URL a 404-es hibához',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'Fájl/URL az 500-as hibához',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'Ha a pureftpd van kiválasztva, a .ftpquota fájlok a felhasználói kvótákhoz létrejönnek és naponta frissülnek',\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Ügyfél átirányítások engedélyezése',\n\t\t\t'description' => 'Engedélyezze az ügyfelek számára, hogy kiválasszák az átirányításokhoz használt http-állapotkódot',\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Alapértelmezett átirányítás',\n\t\t\t'description' => 'Állítsa be az alapértelmezett átirányítási kódot, amelyet akkor kell használni, ha az ügyfél nem állítja be magának',\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Hozzon létre mail-, imap-, pop3- és smtp-\"A rekordot\" az MX-szerverekkel együtt',\n\t\t'froxlordirectlyviahostname' => 'froxlor közvetlen elérése a hosztnéven keresztül',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Jelszavak reguláris kifejezése',\n\t\t\t'description' => 'Itt beállíthat egy reguláris kifejezést a jelszavak összetettségére.<br />Üres = nincs külön követelmény',\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'FCGID engedélyezése a froxlor vHosthoz',\n\t\t\t'description' => 'Ha engedélyezve van, a froxlor is egy helyi felhasználó alatt fog futni',\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'SuExec megoldás engedélyezése',\n\t\t\t\t'description' => 'Csak akkor engedélyezze, ha az ügyfél docrootjai nincsenek az apache suexec útvonalán belül.<br />Ha engedélyezve van, a froxlor szimbolikus linket hoz létre az ügyfél perl-engedélyezett könyvtárából + /cgi-bin/ a megadott útvonalra.<br />Vegye figyelembe, hogy a perl csak a /cgi-bin/ alkönyvtárban fog működni, és nem magában a könyvtárban (ahogy ez a javítás nélkül történik!)',\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Útvonal az ügyfél perl-engedélyezett könyvtár szimbolikus linkjeihez',\n\t\t\t\t'description' => 'Csak akkor kell beállítania, ha a SuExec-megoldás engedélyezve van.<br />FIGYELEM: Győződjön meg róla, hogy ez az útvonal a suexec útvonalán belül van, különben ez a megoldás haszontalan',\n\t\t\t],\n\t\t],\n\t\t'awstats_awstatspath' => 'AWStats \\'awstats.pl\\' elérési út',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'AWstats ikonok mappájának elérési útja',\n\t\t\t'description' => 'pl. /usr/share/awstats/htdocs/icon/',\n\t\t],\n\t\t'login_domain_login' => 'Bejelentkezés engedélyezése domainekkel',\n\t\t'perl_server' => [\n\t\t\t'title' => 'Perl szerver socket helye',\n\t\t\t'description' => 'Egy egyszerű útmutató található itt: <a target=\"blank\" href=\"https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/\">nginx.com</a>',\n\t\t],\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP háttér',\n\t\t\t'description' => 'itt hallgat a PHP folyamat a nginx kéréseire, lehet egy unix socket vagy ip:port kombináció<br />*NEM használatos php-fpm-mel',\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'PHP újratöltési parancs',\n\t\t\t'description' => 'ezt használják a PHP háttér újratöltésére, ha bármelyik használatban van<br />Alapértelmezett: üres<br />*NEM használatos php-fpm-mel',\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => 'php-fpm engedélyezése',\n\t\t\t'description' => '<b>Ez speciális webszerver konfigurációt igényel, lásd <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">PHP-FPM kézikönyv</a></b>',\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'php-fpm konfigurációs könyvtár',\n\t\t\t'aliasconfigdir' => 'php-fpm konfigurációs alias-könyvtár',\n\t\t\t'reload' => 'php-fpm újraindítási parancs',\n\t\t\t'pm' => 'Folyamatkezelő vezérlés (pm)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'Gyermekfolyamatok száma',\n\t\t\t\t'description' => 'A gyermekfolyamatok száma, amelyeket létre kell hozni, amikor a pm \\'static\\'-ra van állítva, és a maximális gyermekfolyamatok száma, amelyeket létre kell hozni, amikor a pm \\'dynamic/ondemand\\'-ra van állítva<br />Egyenértékű a PHP_FCGI_CHILDREN-nel',\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'A létrehozott gyermekfolyamatok száma indításkor',\n\t\t\t\t'description' => 'Megjegyzés: Csak akkor használatos, ha a pm \\'dynamic\\'-ra van állítva',\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'A kívánt minimális számú tétlen szerverfolyamat',\n\t\t\t\t'description' => 'Megjegyzés: Csak akkor használatos, ha a pm \\'dynamic\\'-ra van állítva<br />Megjegyzés: Kötelező, ha a pm \\'dynamic\\'-ra van állítva',\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'A kívánt maximális számú tétlen szerverfolyamat',\n\t\t\t\t'description' => 'Megjegyzés: Csak akkor használatos, ha a pm \\'dynamic\\'-ra van állítva<br />Megjegyzés: Kötelező, ha a pm \\'dynamic\\'-ra van állítva',\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Kérések száma gyermekenként újraindítás előtt',\n\t\t\t\t'description' => 'Végtelen kérésfeldolgozáshoz adja meg a \\'0\\' értéket. Egyenértékű a PHP_FCGI_MAX_REQUESTS-szel.',\n\t\t\t],\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Tétlen időkorlát',\n\t\t\t\t'description' => 'Időkorlát beállítása PHP FPM FastCGI-hez.',\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'FastCGI IPC könyvtár',\n\t\t\t\t'description' => 'A könyvtár, ahol a php-fpm socketek tárolásra kerülnek a webszerver által.<br />Ennek a könyvtárnak olvashatónak kell lennie a webszerver számára',\n\t\t\t],\n\t\t\t'limit_extensions' => [\n\t\t\t\t'title' => 'Engedélyezett kiterjesztések',\n\t\t\t\t'description' => 'Korlátozza a fő szkript kiterjesztéseit, amelyeket az FPM engedélyez a feldolgozásra. Ez megakadályozhatja a konfigurációs hibákat a webszerver oldalán. Csak a .php kiterjesztésekre korlátozza az FPM-et, hogy megakadályozza a rosszindulatú felhasználókat más kiterjesztések használatában php kód végrehajtására. Alapértelmezett érték: .php',\n\t\t\t],\n\t\t\t'envpath' => 'Útvonalak hozzáadása a PATH környezeti változóhoz. Hagyja üresen, ha nincs PATH környezeti változó',\n\t\t\t'override_fpmconfig' => 'FPM-démon beállítások felülírása (pm, max_children, stb.)',\n\t\t\t'override_fpmconfig_addinfo' => '<br /><span class=\"text-danger\">Csak akkor használatos, ha az \"FPM-démon beállítások felülírása\" \"Igen\"-re van állítva</span>',\n\t\t\t'restart_note' => 'Figyelem: A konfiguráció nem lesz ellenőrizve hibák szempontjából. Ha hibákat tartalmaz, előfordulhat, hogy a PHP-FPM nem indul újra!',\n\t\t\t'custom_config' => [\n\t\t\t\t'title' => 'Egyéni konfiguráció',\n\t\t\t\t'description' => 'Egyéni konfiguráció hozzáadása minden PHP-FPM verzió példányhoz, például <i>pm.status_path = /status</i> a monitorozáshoz. Az alábbi változók itt használhatók. <strong>Figyelem: A konfiguráció nem lesz ellenőrizve hibák szempontjából. Ha hibákat tartalmaz, előfordulhat, hogy a PHP-FPM nem indul újra!</strong>',\n\t\t\t],\n\t\t\t'allow_all_customers' => [\n\t\t\t\t'title' => 'Ezen konfiguráció hozzárendelése az összes jelenleg létező ügyfélhez',\n\t\t\t\t'description' => 'Állítsa \"igaz\"-ra, ha szeretné hozzárendelni ezt a konfigurációt az összes jelenleg létező ügyfélhez, hogy használhassák. Ez a beállítás nem állandó, de többször is futtatható.',\n\t\t\t],\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => 'Web- és forgalomhasználatról szóló jelentések küldésének engedélyezése',\n\t\t\t'webmax' => [\n\t\t\t\t'title' => 'Figyelmeztetési szint százalékban a webtárhelyhez',\n\t\t\t\t'description' => 'Érvényes értékek 0-tól 150-ig. Ha 0-ra állítja, ez a jelentés kikapcsolódik.',\n\t\t\t],\n\t\t\t'trafficmax' => [\n\t\t\t\t'title' => 'Figyelmeztetési szint százalékban a forgalomhoz',\n\t\t\t\t'description' => 'Érvényes értékek 0-tól 150-ig. Ha 0-ra állítja, ez a jelentés kikapcsolódik.',\n\t\t\t],\n\t\t\t'report_web_bccadmin' => [\n\t\t\t\t'title' => 'Jelentés másolat az adminisztrátornak (BCC)',\n\t\t\t\t'description' => 'Ha aktiválva van, a használati jelentések másolata BCC-ben kerül elküldésre az adminisztrátornak',\n\t\t\t],\n\t\t],\n\t\t'dropdown' => 'Legördülő lista',\n\t\t'manual' => 'Kézi',\n\t\t'default_theme' => 'Alapértelmezett téma',\n\t\t'validate_domain' => 'Doménnevek ellenőrzése',\n\t\t'diskquota_enabled' => 'Kvóta aktiválva?',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'Útvonal a repquota-hoz',\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'Útvonal a quotatool-hoz',\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => 'Partíció, amelyen az ügyfelek fájljai tárolódnak',\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'Maildir név',\n\t\t\t'description' => 'Maildir könyvtár a felhasználó fiókjában. Általában \\'Maildir\\', néhány implementációban \\'.maildir\\', és közvetlenül a felhasználó könyvtárába, ha üresen hagyják.',\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => 'Catchall használata',\n\t\t\t'description' => 'Szeretné biztosítani ügyfelei számára a catchall funkciót?',\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => 'Apache 2.4 módosítások használata',\n\t\t\t'description' => '<strong class=\"text-danger\">FIGYELEM:</strong> csak akkor használja, ha ténylegesen apache 2.4 vagy magasabb verziót telepített<br />ellenkező esetben a webszerver nem fog tudni elindulni',\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'Útvonal a fastcgi_params fájlhoz',\n\t\t\t'description' => 'Adja meg az nginx fastcgi_params fájljának útvonalát, beleértve a fájlnevet',\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => 'Doménnév használata alapértelmezett értékként a DocumentRoot útvonalhoz',\n\t\t\t'description' => 'Ha engedélyezve van és a DocumentRoot útvonal üres, az alapértelmezett érték a (al)doménnév lesz.<br /><br />Példák: <br />/var/customers/webs/customer_name/example.com/<br />/var/customers/webs/customer_name/subdomain.example.com/',\n\t\t],\n\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t'title' => 'Aldomének elrejtése a PHP-konfiguráció áttekintésben',\n\t\t\t'description' => 'Ha aktiválva van, az ügyfelek aldoménjei nem lesznek felsorolva a php-konfigurációk áttekintésében, csak az aldomének száma látható.<br /><br />Megjegyzés: Ez csak akkor látható, ha engedélyezte az FCGID-t vagy a PHP-FPM-et',\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => 'Standard aldomének elrejtése a PHP-konfiguráció áttekintésben',\n\t\t\t'description' => 'Ha aktiválva van, az ügyfelek standard aldoménjei nem jelennek meg a php-konfigurációk áttekintésében<br /><br />Megjegyzés: Ez csak akkor látható, ha engedélyezte az FCGID-t vagy a PHP-FPM-et',\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => 'Válassza ki, melyik jelszó-titkosítási módszert kell használni',\n\t\t\t'description' => 'Válassza ki, melyik jelszó-titkosítási módszert kell használni. Ha megváltoztatja ezt a beállítást, csak az új jelszavak lesznek titkosítva az új módszerrel. A meglévő jelszavak nem változnak.',\n\t\t],\n\t\t'systemdefault' => 'Rendszer alapértelmezett',\n\t\t'panel_allow_theme_change_admin' => 'Adminisztrátorok számára a téma változtatásának engedélyezése',\n\t\t'panel_allow_theme_change_customer' => 'Ügyfelek számára a téma változtatásának engedélyezése',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'AXFR szerverek',\n\t\t\t'description' => 'IP-címek vesszővel elválasztott listája, amelyek számára engedélyezett a dns zónák átvitele (AXFR).',\n\t\t],\n\t\t'powerdns_mode' => [\n\t\t\t'title' => 'PowerDNS működési mód',\n\t\t\t'description' => 'Válassza ki a PowerDNS módot: Native a replikáció nélküli működéshez (Alapértelmezett) / Master, ha DNS replikációra van szükség.',\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Webszerver ügyfél-ssl tanúsítványok könyvtára',\n\t\t\t'description' => 'Hol kell létrehozni az ügyfél által megadott ssl-tanúsítványokat?<br /><br /><div class=\"text-danger\">MEGJEGYZÉS: Ennek a mappának a tartalma rendszeresen törlődik, ezért kerülje az adatok manuális tárolását benne.</div>',\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => 'Adminisztrátorok/viszonteladók számára az adatbázis-hibák jelentésének engedélyezése a froxlor felé',\n\t\t\t'description' => 'Kérjük, vegye figyelembe: Soha ne küldjön személyes (ügyfél)adatokat nekünk!',\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => 'Ügyfelek számára az adatbázis-hibák jelentésének engedélyezése a froxlor felé',\n\t\t\t'description' => 'Kérjük, vegye figyelembe: Soha ne küldjön személyes (ügyfél)adatokat nekünk!',\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => 'E-mail forgalom elemzése',\n\t\t\t'description' => 'Levelezőszerver naplóinak elemzésének engedélyezése a forgalom kiszámításához',\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'MDA típusa',\n\t\t\t'description' => 'A Mail Delivery Server típusa',\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'MDA napló',\n\t\t\t'description' => 'A Mail Delivery Server naplófájlja',\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'MTA típusa',\n\t\t\t'description' => 'A Mail Transfer Agent típusa',\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'MTA napló',\n\t\t\t'description' => 'A Mail Transfer Agent naplófájlja',\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'Cron konfigurációs fájl',\n\t\t\t'description' => 'Útvonal a cron-szolgáltatás konfigurációs fájljához. Ezt a fájlt a froxlor rendszeresen és automatikusan frissíti.<br />Megjegyzés: Kérjük, <b>győződjön meg róla</b>, hogy ugyanazt a fájlnevet használja, mint a fő froxlor cronjob esetében (alapértelmezett: /etc/cron.d/froxlor)!<br><br>Ha <b>FreeBSD</b>-t használ, kérjük, adja meg itt az <i>/etc/crontab</i> útvonalat!',\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Cron-démon újratöltési parancs',\n\t\t\t'description' => 'Adja meg a parancsot, amelyet végre kell hajtani a rendszer cron-démonjának újratöltéséhez',\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Cron végrehajtási parancs (php-bináris)',\n\t\t\t'description' => 'Parancs a cronjobjaink végrehajtásához. Csak akkor változtassa meg, ha tudja, mit csinál (alapértelmezett: \"/usr/bin/nice -n 5 /usr/bin/php -q\")!',\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => 'Automatikus adatbázis-frissítések engedélyezése',\n\t\t\t'description' => '<div class=\"text-danger\"><b>FIGYELEM:</b></div> Ez a beállítás lehetővé teszi a cronjob számára, hogy megkerülje a froxlor fájlok és az adatbázis verzióellenőrzését, és verzióeltérés esetén lefuttassa az adatbázis-frissítéseket.<br><br><div class=\"text-danger\">Az automatikus frissítés mindig az alapértelmezett értékeket állítja be az új beállításokhoz vagy változtatásokhoz. Ez nem mindig felel meg az Ön rendszerének. Kérjük, gondolja át kétszer, mielőtt aktiválja ezt a beállítást.</div>',\n\t\t],\n\t\t'dns_createhostnameentry' => 'Bind-zone/config létrehozása a rendszer hosztnévhez',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => 'Kisbetű',\n\t\t\t'description' => 'A jelszónak tartalmaznia kell legalább egy kisbetűt (a-z).',\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => 'Nagybetű',\n\t\t\t'description' => 'A jelszónak tartalmaznia kell legalább egy nagybetűt (A-Z).',\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => 'Számok',\n\t\t\t'description' => 'A jelszónak tartalmaznia kell legalább egy számot (0-9).',\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => 'Speciális karakter',\n\t\t\t'description' => 'A jelszónak tartalmaznia kell legalább egyet az alább meghatározott karakterek közül.',\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => 'Speciális karakterek listája',\n\t\t\t'description' => 'Ezen karakterek egyike szükséges, ha a fenti opció be van állítva.',\n\t\t],\n\t\t'apache_itksupport' => [\n\t\t\t'title' => 'Apache ITK-MPM módosítások használata',\n\t\t\t'description' => '<strong class=\"text-danger\">FIGYELEM:</strong> csak akkor használja, ha ténylegesen engedélyezve van az apache itk-mpm<br />ellenkező esetben a webszerver nem fog tudni elindulni',\n\t\t],\n\t\t'letsencryptca' => [\n\t\t\t'title' => 'ACME környezet',\n\t\t\t'description' => 'A Let\\'s Encrypt / ZeroSSL tanúsítványokhoz használandó környezet.',\n\t\t],\n\t\t'letsencryptchallengepath' => [\n\t\t\t'title' => 'Let\\'s Encrypt kihívások útvonala',\n\t\t\t'description' => 'Az a könyvtár, ahonnan a Let\\'s Encrypt kihívásokat globális aliason keresztül kell kínálni.',\n\t\t],\n\t\t'letsencryptkeysize' => [\n\t\t\t'title' => 'Új Let\\'s Encrypt tanúsítványok kulcsmérete',\n\t\t\t'description' => 'Az új Let\\'s Encrypt tanúsítványok kulcsmérete bitben.',\n\t\t],\n\t\t'letsencryptreuseold' => [\n\t\t\t'title' => 'Let\\'s Encrypt kulcs újrafelhasználása',\n\t\t\t'description' => 'Ha aktiválva van, minden megújításkor ugyanaz a kulcs lesz használva, ellenkező esetben minden alkalommal új kulcs generálódik.',\n\t\t],\n\t\t'leenabled' => [\n\t\t\t'title' => 'Let\\'s Encrypt engedélyezése',\n\t\t\t'description' => 'Ha aktiválva van, az ügyfelek lehetővé teszik a froxlor számára, hogy automatikusan generáljon és megújítson Let\\'s Encrypt ssl-tanúsítványokat ssl IP/port-tal rendelkező domainekhez.<br /><br />Kérjük, ne feledje, hogy engedélyezés esetén át kell néznie a webszerver konfigurációját, mert ez a funkció speciális beállítást igényel.',\n\t\t],\n\t\t'caa_entry' => [\n\t\t\t'title' => 'CAA DNS rekordok generálása',\n\t\t\t'description' => 'Automatikusan generál CAA rekordokat az SSL-engedélyezett domainekhez, amelyek Let\\'s Encrypt-et használnak',\n\t\t],\n\t\t'caa_entry_custom' => [\n\t\t\t'title' => 'További CAA DNS rekordok',\n\t\t\t'description' => 'A DNS Tanúsítvány Kibocsátó Engedélyezés (CAA) egy internetes biztonsági mechanizmus, amely lehetővé teszi a domainnév tulajdonosok számára, hogy jelezzék a tanúsítvány-kibocsátóknak,<br>hogy jogosultak-e digitális tanúsítványokat kibocsátani egy adott domainnévhez. Ezt egy új \"CAA\" Domain Name System (DNS) erőforrásrekord segítségével teszi.<br><br>Ennek a mezőnek a tartalma közvetlenül bekerül a DNS zónába (minden sor egy CAA rekordot eredményez).<br>Ha a Let\\'s Encrypt engedélyezve van ehhez a domainhez, ez a bejegyzés mindig automatikusan hozzáadódik, és nem kell manuálisan hozzáadni:<br><code>0 issue \"letsencrypt.org\"</code> (Ha a domain wildcard domain, akkor ehelyett issuewild lesz használva).<br>Az Incidens Jelentés engedélyezéséhez hozzáadhat egy <code>iodef</code> rekordot. Egy példa egy ilyen jelentés küldésére a <code>me@example.com</code> címre:<br><code>0 iodef \"mailto:me@example.com\"</code><br><br><strong>Figyelem:</strong> A kód nem lesz ellenőrizve hibák szempontjából. Ha hibákat tartalmaz',\n\t\t],\n\t\t'exportenabled' => [\n\t\t\t'title' => 'Adatexport engedélyezése ügyfelek számára',\n\t\t\t'description' => 'Ha aktiválva van, az ügyfél képes lesz adatexport feladatokat ütemezni (cron-export), amely archívumot generál a saját docroot-jában (az ügyfél által választható alkönyvtárban)',\n\t\t],\n\t\t'dnseditorenable' => [\n\t\t\t'title' => 'DNS szerkesztő engedélyezése',\n\t\t\t'description' => 'Lehetővé teszi az adminisztrátorok és ügyfelek számára a domain dns bejegyzések kezelését',\n\t\t],\n\t\t'dns_server' => [\n\t\t\t'title' => 'DNS szerver daemon',\n\t\t\t'description' => 'Ne feledje, hogy a daemonokat a froxlor konfigurációs sablonjai segítségével kell beállítani',\n\t\t],\n\t\t'panel_customer_hide_options' => [\n\t\t\t'title' => 'Menüelemek és forgalmi diagramok elrejtése az ügyfélpanelen',\n\t\t\t'description' => 'Válassza ki az ügyfélpanelen elrejtendő elemeket. Több opció kiválasztásához tartsa lenyomva a CTRL gombot kiválasztás közben.',\n\t\t],\n\t\t'allow_allow_customer_shell' => [\n\t\t\t'title' => 'Ügyfelek számára engedélyezze a shell hozzáférés engedélyezését ftp-felhasználók számára',\n\t\t\t'description' => '<strong class=\"text-danger\">Kérjük, vegye figyelembe: A shell hozzáférés lehetővé teszi a felhasználó számára különböző binárisok végrehajtását a rendszeren. Rendkívül óvatosan használja. Kérjük, csak akkor aktiválja ezt, ha TÉNYLEG tudja, mit csinál!!!</strong>',\n\t\t],\n\t\t'available_shells' => [\n\t\t\t'title' => 'Elérhető shell-ek listája',\n\t\t\t'description' => 'Vesszővel elválasztott lista azokról a shell-ekről, amelyek elérhetők az ügyfél számára az ftp-felhasználóik számára való kiválasztásra.<br><br>Vegye figyelembe, hogy az alapértelmezett shell <strong>/bin/false</strong> mindig választható lesz (ha engedélyezve van), még akkor is, ha ez a beállítás üres. Ez az alapértelmezett érték az ftp-felhasználók számára minden esetben',\n\t\t],\n\t\t'le_froxlor_enabled' => [\n\t\t\t'title' => 'Let\\'s Encrypt engedélyezése a froxlor vhost számára',\n\t\t\t'description' => 'Ha aktiválva van, a froxlor vhost automatikusan biztonságossá válik egy Let\\'s Encrypt tanúsítvány használatával.',\n\t\t],\n\t\t'le_froxlor_redirect' => [\n\t\t\t'title' => 'SSL-átirányítás engedélyezése a froxlor vhost számára',\n\t\t\t'description' => 'Ha aktiválva van, minden http kérés a froxlor-hoz átirányításra kerül a megfelelő SSL oldalra.',\n\t\t],\n\t\t'option_unavailable_websrv' => '<br><em class=\"text-danger\">Csak a következőkhöz érhető el: %s</em>',\n\t\t'option_unavailable' => '<br><em class=\"text-danger\">Az opció nem érhető el más beállítások miatt.</em>',\n\t\t'letsencryptacmeconf' => [\n\t\t\t'title' => 'Az acme.conf snippet útvonala',\n\t\t\t'description' => 'A konfigurációs snippet fájlneve, amely lehetővé teszi a webszerver számára az acme kihívás kiszolgálását.',\n\t\t],\n\t\t'mail_use_smtp' => 'Levelező beállítása SMTP használatára',\n\t\t'mail_smtp_host' => 'SMTP szerver megadása',\n\t\t'mail_smtp_usetls' => 'TLS titkosítás engedélyezése',\n\t\t'mail_smtp_auth' => 'SMTP hitelesítés engedélyezése',\n\t\t'mail_smtp_port' => 'Csatlakozáshoz használt TCP port',\n\t\t'mail_smtp_user' => 'SMTP felhasználónév',\n\t\t'mail_smtp_passwd' => 'SMTP jelszó',\n\t\t'mail_enable_allow_sender' => [\n\t\t\t'title' => 'Engedélyezett feladók funkció engedélyezése',\n\t\t\t'description' => 'Ha aktiválva van, lehetővé teszi a postafiók tulajdonosok számára, hogy más feladó címeket adjanak hozzá ehhez a postafiókhoz. Ezzel lehetővé válik más feladó címekkel történő levélküldés.',\n\t\t],\n\t\t'mail_allow_external_domains' => [\n\t\t\t'title' => 'Külső domainek engedélyezése mint engedélyezett feladók',\n\t\t\t'description' => 'Ha aktiválva van, a postafiók tulajdonosok külső domain-címeket is használhatnak engedélyezett feladóként (tehát olyan domaineket, amelyek nem tartoznak a Froxlor-hoz)',\n\t\t],\n\t\t'http2_support' => [\n\t\t\t'title' => 'HTTP2 támogatás',\n\t\t\t'description' => 'HTTP2 támogatás engedélyezése ssl-hez.<br><em class=\"text-danger\">CSAK AKKOR ENGEDÉLYEZZE, HA A WEBSZERVERE TÁMOGATJA EZT A FUNKCIÓT (nginx verzió 1.9.5+, apache2 verzió 2.4.17+)</em>',\n\t\t],\n\t\t'http3_support' => [\n\t\t\t'title' => 'HTTP3 támogatás',\n\t\t\t'description' => 'HTTP3 támogatás engedélyezése ssl-hez.<br><em class=\"text-danger\">CSAK AKKOR ENGEDÉLYEZZE, HA A WEBSZERVERE TÁMOGATJA EZT A FUNKCIÓT (nginx verzió 1.25.0+)</em>',\n\t\t],\n\t\t'nssextrausers' => [\n\t\t\t'title' => 'libnss-extrausers használata libnss-mysql helyett',\n\t\t\t'description' => 'Ne olvassa be a felhasználókat az adatbázisból, hanem fájlokból. Kérjük, csak akkor aktiválja, ha már elvégezte a szükséges konfigurációs lépéseket (rendszer -> libnss-extrausers).<br><strong class=\"text-danger\">Csak Debian/Ubuntu esetén (vagy ha saját maga fordította a libnss-extrausers-t!)</strong>',\n\t\t],\n\t\t'le_domain_dnscheck' => [\n\t\t\t'title' => 'Domainek DNS-ének ellenőrzése Let\\'s Encrypt használatakor',\n\t\t\t'description' => 'Ha aktiválva van, a froxlor ellenőrzi, hogy a domain, amely Let\\'s Encrypt tanúsítványt kér, legalább egy rendszer IP címre feloldódik-e.',\n\t\t],\n\t\t'le_domain_dnscheck_resolver' => [\n\t\t\t'title' => 'Külső névszerver használata DNS ellenőrzéshez',\n\t\t\t'description' => 'Ha be van állítva, a froxlor ezt a DNS-t fogja használni a domainok DNS-ének ellenőrzéséhez a Let\\'s Encrypt használatakor. Ha üres, a rendszer alapértelmezett DNS-feloldóját fogja használni.',\n\t\t],\n\t\t'phpsettingsforsubdomains' => [\n\t\t\t'description' => 'Ha igen, a kiválasztott php-konfiguráció frissítve lesz minden aldomainre',\n\t\t],\n\t\t'leapiversion' => [\n\t\t\t'title' => 'Válassza ki a Let\\'s Encrypt ACME implementációt',\n\t\t\t'description' => 'Jelenleg csak az ACME v2 implementáció támogatott a Let\\'s Encrypt-hez.',\n\t\t],\n\t\t'enable_api' => [\n\t\t\t'title' => 'Külső API használat engedélyezése',\n\t\t\t'description' => 'A froxlor API használatához aktiválnia kell ezt az opciót. Részletesebb információkért lásd <a href=\"https://docs.froxlor.org/latest/api-guide/\" target=\"_new\">https://docs.froxlor.org/</a>',\n\t\t],\n\t\t'api_customer_default' => '\"API hozzáférés engedélyezése\" alapértelmezett érték új ügyfeleknek',\n\t\t'dhparams_file' => [\n\t\t\t'title' => 'DHParams fájl (Diffie–Hellman kulcscsere)',\n\t\t\t'description' => 'Ha itt meg van adva egy dhparams.pem fájl, az bele lesz foglalva a webszerver konfigurációjába. Hagyja üresen a kikapcsoláshoz.<br>Példa: /etc/ssl/webserver/dhparams.pem<br><br>Ha a fájl nem létezik, automatikusan létrehozásra kerül a következő paranccsal: <code>openssl dhparam -out /etc/ssl/webserver/dhparams.pem 4096</code>. Ajánlott a fájlt előre létrehozni, mielőtt itt megadná, mivel a létrehozás elég sokáig tart és blokkolja a cronjob-ot.',\n\t\t],\n\t\t'errorlog_level' => [\n\t\t\t'title' => 'Hibanapló szintje',\n\t\t\t'description' => 'Adja meg a hibanapló szintjét. Az alapértelmezett \"warn\" apache-felhasználóknak és \"error\" nginx-felhasználóknak.',\n\t\t],\n\t\t'letsencryptecc' => [\n\t\t\t'title' => 'ECC / ECDSA tanúsítvány kiállítása',\n\t\t\t'description' => 'Ha érvényes kulcsméretnek van beállítva, a kiállított tanúsítvány ECC / ECDSA-t fog használni',\n\t\t],\n\t\t'froxloraliases' => [\n\t\t\t'title' => 'Domain aliasok a froxlor vhost-hoz',\n\t\t\t'description' => 'Vesszővel elválasztott lista a domainek hozzáadásához szerveraliasként a froxlor vhost-hoz',\n\t\t],\n\t\t'default_sslvhostconf' => [\n\t\t\t'title' => 'Alapértelmezett SSL vHost-beállítások',\n\t\t\t'description' => 'Ennek a mezőnek a tartalma közvetlenül bele lesz foglalva ebbe az ip/port vHost konténerbe. A következő változókat használhatja:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (ha alkalmazható)<br/> Figyelem: A kód nem lesz ellenőrizve hibákra. Ha hibákat tartalmaz, előfordulhat, hogy a webszerver nem indul újra!',\n\t\t],\n\t\t'includedefault_sslvhostconf' => 'Nem-SSL vHost-beállítások beillesztése az SSL-vHost-ba',\n\t\t'apply_specialsettings_default' => 'Alapértelmezett érték a \"Speciális beállítások alkalmazása minden aldomainre (*.example.com)\" beállításhoz domain szerkesztésekor',\n\t\t'apply_phpconfigs_default' => 'Alapértelmezett érték a \"PHP-konfiguráció alkalmazása minden aldomainre\" beállításhoz domain szerkesztésekor',\n\t\t'awstats' => [\n\t\t\t'logformat' => [\n\t\t\t\t'title' => 'LogFormat beállítás',\n\t\t\t\t'description' => 'Ha egyéni logformátumot használ a webszerverhez, módosítania kell az awstats LogFormat-ot is.<br/>Az alapértelmezett 1. További információkért nézze meg a dokumentációt <a target=\"_blank\" href=\"https://awstats.sourceforge.io/docs/awstats_config.html#LogFormat\">itt</a>.',\n\t\t\t],\n\t\t],\n\t\t'hide_incompatible_settings' => 'Inkompatibilis beállítások elrejtése',\n\t\t'soaemail' => 'SOA rekordokban használandó e-mail cím (ha üres, alapértelmezetten a panel beállításaiban megadott küldő címet használja)',\n\t\t'imprint_url' => [\n\t\t\t'title' => 'URL a jogi megjegyzésekhez / impresszumhoz',\n\t\t\t'description' => 'Adjon meg egy URL-t a jogi megjegyzések / impresszum oldalához. A link látható lesz a bejelentkezési képernyőn és a láblécben bejelentkezés után.',\n\t\t],\n\t\t'terms_url' => [\n\t\t\t'title' => 'URL a használati feltételekhez',\n\t\t\t'description' => 'Adjon meg egy URL-t a használati feltételek oldalához. A link látható lesz a bejelentkezési képernyőn és a láblécben bejelentkezés után.',\n\t\t],\n\t\t'privacy_url' => [\n\t\t\t'title' => 'URL az adatvédelmi irányelvekhez',\n\t\t\t'description' => 'Adjon meg egy URL-t az adatvédelmi irányelvek / impresszum oldalához. A link látható lesz a bejelentkezési képernyőn és a láblécben bejelentkezés után.',\n\t\t],\n\t\t'logo_image_header' => [\n\t\t\t'title' => 'Logó kép (Fejléc)',\n\t\t\t'description' => 'Töltse fel saját logó képét, amely a fejlécben jelenik meg bejelentkezés után (ajánlott magasság 30px)',\n\t\t],\n\t\t'logo_image_login' => [\n\t\t\t'title' => 'Logó kép (Bejelentkezés)',\n\t\t\t'description' => 'Töltse fel saját logó képét, amely bejelentkezéskor jelenik meg',\n\t\t],\n\t\t'logo_overridetheme' => [\n\t\t\t'title' => 'A témában meghatározott logó felülírása a \"Logó kép\" által (Fejléc és Bejelentkezés, lásd alább)',\n\t\t\t'description' => 'Ezt \"true\"-ra kell állítani, ha használni szeretné a feltöltött logót; alternatívaként még mindig használhatja a téma-alapú \"logo_custom.png\" és \"logo_custom_login.png\" lehetőséget.',\n\t\t],\n\t\t'logo_overridecustom' => [\n\t\t\t'title' => 'A témában meghatározott egyedi logó (logo_custom.png és logo_custom_login.png) felülírása a \"Logó kép\" által (Fejléc és Bejelentkezés, lásd alább)',\n\t\t\t'description' => 'Állítsa \"true\"-ra, ha figyelmen kívül szeretné hagyni a téma-specifikus egyedi logókat a fejléchez és bejelentkezéshez, és helyette a \"Logó kép\"-et szeretné használni',\n\t\t],\n\t\t'createstdsubdom_default' => [\n\t\t\t'title' => 'Előre kiválasztott érték az \"Alapértelmezett aldomain létrehozása\" opcióhoz ügyfél létrehozásakor',\n\t\t\t'description' => '',\n\t\t],\n\t\t'froxlorusergroup' => [\n\t\t\t'title' => 'Egyedi rendszercsoport minden ügyfél felhasználóhoz',\n\t\t\t'description' => 'A libnss-extrausers (rendszerbeállítások) használata szükséges ennek érvénybe lépéséhez. Az üres érték kihagyja a létrehozást vagy eltávolítja a meglévő csoportot.',\n\t\t],\n\t\t'acmeshpath' => [\n\t\t\t'title' => 'Az acme.sh útvonala',\n\t\t\t'description' => 'Állítsa be, hogy hova van telepítve az acme.sh, beleértve az acme.sh szkriptet<br>Az alapértelmezett <b>/root/.acme.sh/acme.sh</b>',\n\t\t],\n\t\t'update_channel' => [\n\t\t\t'title' => 'froxlor frissítési csatorna',\n\t\t\t'description' => 'Válassza ki a froxlor frissítési csatornáját. Az alapértelmezett \"stable\"',\n\t\t],\n\t\t'uc_stable' => 'stabil',\n\t\t'uc_testing' => 'tesztelés',\n\t\t'uc_nightly' => 'éjszakai',\n\t\t'traffictool' => [\n\t\t\t'toolselect' => 'Forgalomelemző',\n\t\t\t'webalizer' => 'Webalizer',\n\t\t\t'awstats' => 'AWStats',\n\t\t\t'goaccess' => 'goaccess'\n\t\t],\n\t\t'requires_reconfiguration' => 'Ezen beállítások módosítása szükségessé teheti a következő szolgáltatások újrakonfigurálását:<br><strong>%s</strong>',\n\t\t'req_limit_per_interval' => [\n\t\t\t'title' => 'HTTP kérések száma intervallumonként',\n\t\t\t'description' => 'Korlátozza a HTTP kérések számát intervallumonként (lásd alább) a froxlor-hoz, az alapértelmezett \"60\"',\n\t\t],\n\t\t'req_limit_interval' => [\n\t\t\t'title' => 'Sebességkorlátozási intervallum',\n\t\t\t'description' => 'Adja meg az időt másodpercben a HTTP kérések számához, az alapértelmezett \"60\"',\n\t\t],\n\t\t'option_requires_otp' => 'Ez a beállítás OTP validációt igényel',\n\t\t'panel_menu_collapsed' => [\n\t\t\t'title' => 'Menüszekciók összecsukása',\n\t\t\t'description' => 'Ha ki van kapcsolva, a bal oldali menüszekciók mindig ki lesznek nyitva.',\n\t\t],\n\t\t'le_renew_services' => [\n\t\t\t'title' => 'A froxlor Let\\'s Encrypt tanúsítvány használata ezekhez a szolgáltatásokhoz',\n\t\t\t'description' => 'Ha \"nincs\"-re van állítva (vagy az alábbi megújítási hook parancs üres), nem történik SSL-lel kapcsolatos konfigurációs módosítás a kiválasztott szolgáltatásoknál.<br><br>A kiválasztott szolgáltatások újratöltési parancsát hozzá kell adni a megújítási hook parancshoz, különben a konfigurációs változtatások vagy a megújított tanúsítványok nem lesznek megfelelően alkalmazva.',\n\t\t],\n\t\t'le_renew_hook' => [\n\t\t\t'title' => 'Let\\'s Encrypt megújítási hook parancs',\n\t\t\t'description' => 'Állítsa be erre a parancsra, amely újraindítja a fent kiválasztott szolgáltatásokat, hogy a megújított tanúsítványokat a szolgáltatás megfelelően használja.',\n\t\t],\n\t],\n\t'spf' => [\n\t\t'use_spf' => [\n\t\t\t'title' => 'SPF aktiválása a domainekhez?',\n\t\t\t'description' => 'Speciális DNS bejegyzést igényel a domainhez. Ha nem használja a névszerver funkciót, manuálisan kell kezelnie ezeket a bejegyzéseket.',\n\t\t],\n\t\t'spf_entry' => 'SPF bejegyzés minden domainhez',\n\t],\n\t'dmarc' => [\n\t\t'use_dmarc' => [\n\t\t\t'title' => 'DMARC aktiválása a domainekhez?',\n\t\t\t'description' => 'Speciális DNS bejegyzést igényel a domainhez. Ha nem használja a névszerver funkciót, manuálisan kell kezelnie ezeket a bejegyzéseket.',\n\t\t],\n\t\t'dmarc_entry' => 'DMARC bejegyzés minden domainhez',\n\t],\n\t'ssl_certificates' => [\n\t\t'certificate_for' => 'Tanúsítvány ehhez:',\n\t\t'valid_from' => 'Érvényes ettől:',\n\t\t'valid_until' => 'Érvényes eddig:',\n\t\t'issuer' => 'Kibocsátó',\n\t],\n\t'success' => [\n\t\t'success' => 'Információ',\n\t\t'clickheretocontinue' => 'Kattintson ide a folytatáshoz',\n\t\t'settingssaved' => 'A beállítások sikeresen mentve.',\n\t\t'rebuildingconfigs' => 'Sikeresen hozzáadva a konfigurációs fájlok újraépítési feladatai',\n\t\t'domain_import_successfully' => 'Sikeresen importálva %s domain.',\n\t\t'exportscheduled' => 'Az exportálási feladat ütemezve lett. Kérjük, várjon a feldolgozásra',\n\t\t'exportaborted' => 'Az ütemezett exportálás megszakítva',\n\t\t'dns_record_added' => 'Rekord sikeresen hozzáadva',\n\t\t'dns_record_deleted' => 'Rekord sikeresen törölve',\n\t\t'testmailsent' => 'Teszt e-mail sikeresen elküldve',\n\t\t'settingsimported' => 'Beállítások sikeresen importálva',\n\t\t'sent_error_report' => 'Hibajelentés sikeresen elküldve. Köszönjük a közreműködését.',\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Függőben lévő cron-feladatok',\n\t\t'REBUILD_VHOST' => 'Webszerver-konfiguráció újraépítése',\n\t\t'CREATE_HOME' => 'Új ügyfél hozzáadása: %s',\n\t\t'REBUILD_DNS' => 'Bind-konfiguráció újraépítése',\n\t\t'CREATE_FTP' => 'Könyvtár létrehozása új ftp-felhasználónak',\n\t\t'DELETE_CUSTOMER_FILES' => 'Ügyfél fájljainak törlése: %s',\n\t\t'noneoutstanding' => 'Jelenleg nincsenek függőben lévő feladatok a froxlor számára',\n\t\t'DELETE_EMAIL_DATA' => 'Ügyfél e-mail adatainak törlése.',\n\t\t'DELETE_FTP_DATA' => 'Ügyfél ftp-fiók adatainak törlése.',\n\t\t'REBUILD_RSPAMD' => 'Antispam-konfiguráció újraépítése.',\n\t\t'CREATE_QUOTA' => 'Kvóta beállítása a fájlrendszeren',\n\t\t'REBUILD_CRON' => 'A cron.d fájl újraépítése',\n\t\t'CREATE_CUSTOMER_DATADUMP' => 'Adatexportálási feladat az ügyfél számára: %s',\n\t\t'DELETE_DOMAIN_PDNS' => '%s domain törlése a PowerDNS adatbázisból',\n\t\t'DELETE_DOMAIN_SSL' => '%s domain SSL fájljainak törlése',\n\t\t'UPDATE_LE_SERVICES' => 'Rendszerszolgáltatások frissítése a Let\\'s Encrypt számára',\n\t],\n\t'terms' => 'Használati feltételek',\n\t'traffic' => [\n\t\t'month' => 'Hónap',\n\t\t'day' => 'Nap',\n\t\t'months' => [\n\t\t\t1 => 'Január',\n\t\t\t2 => 'Február',\n\t\t\t3 => 'Március',\n\t\t\t4 => 'Április',\n\t\t\t5 => 'Május',\n\t\t\t6 => 'Június',\n\t\t\t7 => 'Július',\n\t\t\t8 => 'Augusztus',\n\t\t\t9 => 'Szeptember',\n\t\t\t10 => 'Október',\n\t\t\t11 => 'November',\n\t\t\t12 => 'December',\n\t\t\t'jan' => 'Jan',\n\t\t\t'feb' => 'Feb',\n\t\t\t'mar' => 'Már',\n\t\t\t'apr' => 'Ápr',\n\t\t\t'may' => 'Máj',\n\t\t\t'jun' => 'Jún',\n\t\t\t'jul' => 'Júl',\n\t\t\t'aug' => 'Aug',\n\t\t\t'sep' => 'Szep',\n\t\t\t'oct' => 'Okt',\n\t\t\t'nov' => 'Nov',\n\t\t\t'dec' => 'Dec',\n\t\t\t'total' => 'Összesen',\n\t\t],\n\t\t'mb' => 'Forgalom',\n\t\t'sumtotal' => 'Teljes forgalom',\n\t\t'sumhttp' => 'HTTP forgalom',\n\t\t'sumftp' => 'FTP forgalom',\n\t\t'summail' => 'E-mail forgalom',\n\t\t'customer' => 'Ügyfél',\n\t\t'domain' => 'Domain',\n\t\t'trafficoverview' => 'Forgalom összesítés',\n\t\t'bycustomers' => 'Forgalom ügyfelenként',\n\t\t'details' => 'Részletek',\n\t\t'http' => 'HTTP',\n\t\t'ftp' => 'FTP',\n\t\t'mail' => 'E-mail',\n\t\t'nocustomers' => 'Legalább egy ügyfélre van szükség a forgalmi jelentések megtekintéséhez.',\n\t\t'top5customers' => 'Top 5 ügyfél',\n\t\t'nodata' => 'Nincs adat a megadott időszakra.',\n\t\t'ranges' => [\n\t\t\t'last24h' => 'utolsó 24 óra',\n\t\t\t'last7d' => 'utolsó 7 nap',\n\t\t\t'last30d' => 'utolsó 30 nap',\n\t\t\t'cm' => 'Aktuális hónap',\n\t\t\t'last3m' => 'utolsó 3 hónap',\n\t\t\t'last6m' => 'utolsó 6 hónap',\n\t\t\t'last12m' => 'utolsó 12 hónap',\n\t\t\t'cy' => 'Aktuális év',\n\t\t],\n\t\t'byrange' => 'Időszak szerint meghatározva',\n\t],\n\t'translator' => '',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'A froxlor egy újabb verziója telepítve lett, de még nincs beállítva.<br />Csak az adminisztrátor jelentkezhet be és fejezheti be a frissítést.',\n\t\t'update' => 'froxlor frissítés',\n\t\t'proceed' => 'Folytatás',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'A froxlor fájlok frissítve lettek a <strong>%s</strong> verzióra. A telepített verzió <strong>%s</strong>.',\n\t\t\t'part_b' => '<br /><br />Az ügyfelek nem tudnak bejelentkezni, amíg a frissítés be nem fejeződik.<br /><strong>Folytatja?</strong>',\n\t\t],\n\t\t'noupdatesavail' => 'Már a legújabb %s verziójú froxlor van telepítve.',\n\t\t'description' => 'Adatbázis frissítések futtatása a froxlor telepítéséhez',\n\t\t'uc_newinfo' => 'Egy újabb %s verzió érhető el: \"%s\" (Az Ön jelenlegi verziója: %s)',\n\t\t'notify_subject' => 'Új frissítés elérhető',\n\t\t'dbupdate_required' => 'A froxlor fájlok frissítve lettek, adatbázis frissítés szükséges',\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => 'Egyéni jegyzetek',\n\t\t\t'description' => 'Szabadon írhat ide bármilyen jegyzetet, amire szüksége van. Ezek megjelennek az admin/ügyfél áttekintőben a megfelelő felhasználónál.<br>A Markdown támogatott, a HTML eltávolításra kerül.',\n\t\t\t'show' => 'Mutassa a jegyzeteket a felhasználó irányítópultján',\n\t\t],\n\t\t'api_allowed' => [\n\t\t\t'title' => 'API hozzáférés engedélyezése',\n\t\t\t'description' => 'Ha engedélyezve van a beállításokban, ez a felhasználó létrehozhat API kulcsokat és hozzáférhet a froxlor API-hoz',\n\t\t\t'notice' => 'Az API hozzáférés nem engedélyezett az Ön fiókjához.',\n\t\t],\n\t\t'gui_access' => [\n\t\t\t'title' => 'WebUI bejelentkezés engedélyezése',\n\t\t\t'description' => 'Ha le van tiltva, a felhasználó nem tud bejelentkezni a froxlor webes felületére, de minden szolgáltatás (web, ftp, e-mail, adatbázisok, api-hozzáférés, stb.) normálisan működik.',\n\t\t],\n\t\t'shell_allowed' => [\n\t\t\t'title' => 'Shell hozzáférés engedélyezése',\n\t\t\t'description' => 'Ha aktiválva van, a felhasználó shell hozzáférést kap a rendszerhez SSH-n keresztül.',\n\t\t],\n\t],\n\t'install' => [\n\t\t'slogan' => 'froxlor Szerver Kezelőpanel',\n\t\t'preflight' => 'Rendszer ellenőrzés',\n\t\t'critical_error' => 'Kritikus hiba',\n\t\t'suggestions' => 'Nem kötelező, de ajánlott',\n\t\t'phpinfosuccess' => 'A rendszer PHP %s verzióval fut',\n\t\t'suggestionsnote' => 'Nincsenek kritikus hibák, amelyek megakadályoznák a telepítést, de kérjük, kövesse az alábbi ajánlásokat az optimális élmény érdekében.',\n\t\t'phpinfowarn' => 'A rendszer a PHP %s verziónál alacsonyabb verzióval fut',\n\t\t'phpinfoupdate' => 'Frissítse a jelenlegi PHP verzióját %s-ról %s-ra vagy magasabbra',\n\t\t'start_installation' => 'Telepítés indítása',\n\t\t'check_again' => 'Újratöltés az újbóli ellenőrzéshez',\n\t\t'switchmode_advanced' => 'Haladó opciók megjelenítése',\n\t\t'switchmode_basic' => 'Haladó opciók elrejtése',\n\t\t'dependency_check' => [\n\t\t\t'title' => 'Üdvözöljük a froxlorban',\n\t\t\t'description' => 'Ellenőrizzük a rendszert a függőségek szempontjából, hogy biztosítsuk, hogy minden szükséges PHP kiterjesztés és modul engedélyezve legyen, így a froxlor megfelelően működik.',\n\t\t],\n\t\t'database' => [\n\t\t\t'top' => 'Adatbázis',\n\t\t\t'title' => 'Adatbázis és felhasználó létrehozása',\n\t\t\t'description' => 'A froxlor adatbázist igényel, és ezenkívül <a href=\"https://docs.froxlor.org/latest/general/installation/tarball.html#_3-create-privileged-database-user\" target=\"_blank\">egy kiváltságos felhasználót</a> is, hogy képes legyen felhasználókat és adatbázisokat létrehozni (GRANT opció). A megadott adatbázis és a nem kiváltságos adatbázis-felhasználó létrejön ebben a folyamatban. A kiváltságos felhasználónak léteznie kell.',\n\t\t\t'user' => 'Nem kiváltságos adatbázis-felhasználó',\n\t\t\t'dbname' => 'Adatbázis neve',\n\t\t\t'force_create' => 'Adatbázis biztonsági mentése és felülírása, ha létezik?',\n\t\t],\n\t\t'admin' => [\n\t\t\t'top' => 'Adminisztrátor felhasználó',\n\t\t\t'title' => 'Hozzuk létre a fő adminisztrátor felhasználót.',\n\t\t\t'description' => 'Ez a felhasználó minden jogosultságot megkap a beállítások módosításához és az erőforrások, például ügyfelek, domainek stb. hozzáadásához/frissítéséhez/törléséhez.',\n\t\t\t'use_admin_email_as_sender' => 'Használja a fenti e-mail címet küldő címként. Ha nincs bejelölve, kérjük, adjon meg egy küldő címet alább.',\n\t\t\t'use_autogenerated_email_as_sender' => 'Hagyja üresen az alapértelmezettért: admin@servername',\n\t\t],\n\t\t'system' => [\n\t\t\t'top' => 'Rendszer beállítása',\n\t\t\t'title' => 'Részletek a szerveréről',\n\t\t\t'description' => 'Állítsa be a környezetét, valamint a szerverrel kapcsolatos adatokat és opciókat itt, hogy a froxlor tudjon a rendszeréről. Ezek az értékek kulcsfontosságúak a rendszer konfigurációjához és működéséhez.',\n\t\t\t'ipv4' => 'Elsődleges IPv4 cím (ha alkalmazható)',\n\t\t\t'ipv6' => 'Elsődleges IPv6 cím (ha alkalmazható)',\n\t\t\t'servername' => 'Szerver neve (FQDN, nem IP-cím)',\n\t\t\t'phpbackend' => 'PHP háttér',\n\t\t\t'activate_newsfeed' => 'Az hivatalos hírcsatorna engedélyezése<br><small>(külső forrás: https://inside.froxlor.org/news/)</small>',\n\t\t],\n\t\t'install' => [\n\t\t\t'top' => 'Beállítás befejezése',\n\t\t\t'title' => 'Egy utolsó lépés...',\n\t\t\t'description' => 'Az alábbi parancs letölti, telepíti és konfigurálja a szükséges szolgáltatásokat a rendszerén a telepítési folyamat során megadott adatok alapján.<br><br><span class=\"text-danger\">Győződjön meg róla, hogy az alábbi parancsot <b>root</b> felhasználóként futtatja a szerver shelljében/termináljában, és <b>legyen tisztában</b> azzal, hogy ez a parancs <b>felülírja</b> a használt szolgáltatások bármely meglévő konfigurációját (biztonsági mentések készülnek)!.<br>Ha nem szeretné felülírni a konfigurációkat, válassza az <i>Én manuálisan konfigurálom a szolgáltatásokat</i> opciót az oldal alján!</span>',\n\t\t\t'runcmd' => 'Futtassa az alábbi parancsot a telepítés befejezéséhez:',\n\t\t\t'manual_config' => 'Én manuálisan konfigurálom a szolgáltatásokat, csak vigyen a bejelentkezéshez',\n\t\t\t'waitforconfig' => 'Várakozás a szolgáltatások konfigurálására...',\n\t\t],\n\t\t'errors' => [\n\t\t\t'wrong_ownership' => 'Győződjön meg róla, hogy a froxlor fájlok tulajdonosa %s:%s',\n\t\t\t'missing_extensions' => 'A következő PHP kiterjesztések szükségesek és nincsenek telepítve',\n\t\t\t'suggestedextensions' => 'A következő PHP kiterjesztések nem találhatók, de ajánlottak',\n\t\t\t'databaseexists' => 'Az adatbázis már létezik, kérjük, állítsa be a felülírási opciót az újjáépítéshez, vagy válasszon másik nevet',\n\t\t\t'unabletocreatedb' => 'A tesztadatbázis nem hozható létre',\n\t\t\t'unabletodropdb' => 'A tesztadatbázis nem törölhető',\n\t\t\t'mysqlusernameexists' => 'A megadott nem kiváltságos felhasználó már létezik. Kérjük, használjon másik felhasználónevet, vagy törölje először.',\n\t\t\t'unabletocreateuser' => 'A tesztfelhasználó nem hozható létre',\n\t\t\t'unabletodropuser' => 'A tesztfelhasználó nem törölhető',\n\t\t\t'unabletoflushprivs' => 'A megadott kiváltságos felhasználó nem tudja frissíteni a jogosultságokat',\n\t\t\t'nov4andnov6ip' => 'Meg kell adni egy IPv4- vagy IPv6-címet',\n\t\t\t'servernameneedstobevalid' => 'A megadott szervernév nem tűnik FQDN-nek vagy hosztnévnek',\n\t\t\t'websrvuserdoesnotexist' => 'A megadott webszerver-felhasználó nem létezik a rendszeren',\n\t\t\t'websrvgrpdoesnotexist' => 'A megadott webszerver-csoport nem létezik a rendszeren',\n\t\t\t'notyetconfigured' => 'Úgy tűnik, hogy a szolgáltatások még nem lettek konfigurálva (sikeresen). Kérjük, futtassa az alábbi parancsot, vagy jelölje be a négyzetet, hogy később megtegye.',\n\t\t\t'mandatory_field_not_set' => 'A kötelező mező \"%s\" nincs beállítva!',\n\t\t\t'unexpected_database_error' => 'Váratlan adatbázis-kivétel történt. %s',\n\t\t\t'sql_import_failed' => 'Az SQL adatok importálása nem sikerült!',\n\t\t\t'unprivileged_sql_connection_failed' => 'A nem kiváltságos SQL kapcsolat inicializálása nem sikerült!',\n\t\t\t'privileged_sql_connection_failed' => 'A kiváltságos SQL kapcsolat inicializálása nem sikerült!',\n\t\t\t'mysqldump_backup_failed' => 'Nem lehet adatbázis biztonsági mentést készíteni, a mysqldump hibát adott.',\n\t\t\t'sql_backup_file_missing' => 'Nem lehet adatbázis biztonsági mentést készíteni, a biztonsági mentés fájl nem létezik.',\n\t\t\t'backup_binary_missing' => 'Nem lehet adatbázis biztonsági mentést készíteni, győződjön meg róla, hogy telepítette a mysqldump-ot.',\n\t\t\t'creating_configfile_failed' => 'Nem lehet konfigurációs fájlokat létrehozni, nem lehet írni a fájlt.',\n\t\t\t'database_already_exiting' => 'Találtunk egy adatbázist, és nem engedtük felülírni!'\n\t\t]\n\t],\n\t'welcome' => [\n\t\t'title' => 'Üdvözöljük a froxlorban!',\n\t\t'config_note' => 'Annak érdekében, hogy a froxlor megfelelően tudjon kommunikálni a háttérrel, konfigurálnia kell.',\n\t\t'config_now' => 'Konfigurálás most'\n\t],\n];\n"
  },
  {
    "path": "lng/index.html",
    "content": ""
  },
  {
    "path": "lng/it.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @author     Luca Piona <info@havanastudio.ch>\n * @author     Luca Longinotti <chtekk@gentoo.org>\n * @author     Emilien\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'admin' => [\n\t\t'overview' => 'Generale',\n\t\t'ressourcedetails' => 'Risorse utilizzate',\n\t\t'systemdetails' => 'Dettagli sistema',\n\t\t'froxlordetails' => 'Dettagli froxlor',\n\t\t'installedversion' => 'Versione installata',\n\t\t'latestversion' => 'Ultima versione disponibile',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'Cerca sul web',\n\t\t\t'error' => 'Errore durante la lettura',\n\t\t],\n\t\t'resources' => 'Risorse',\n\t\t'customer' => 'Cliente',\n\t\t'customers' => 'Clienti',\n\t\t'customer_add' => 'Crea cliente',\n\t\t'customer_edit' => 'Modifica cliente',\n\t\t'domains' => 'Domini',\n\t\t'domain_add' => 'Crea dominio',\n\t\t'domain_edit' => 'Modifica dominio',\n\t\t'subdomainforemail' => 'Sottodominio utilizzabile come dominio Email',\n\t\t'admin' => 'Admin',\n\t\t'admins' => 'Admin',\n\t\t'admin_add' => 'Crea admin',\n\t\t'admin_edit' => 'Modifica admin',\n\t\t'customers_see_all' => 'Può vedere tutti i clienti?',\n\t\t'change_serversettings' => 'Può cambiare le impostazioni del server?',\n\t\t'server' => 'Sistema',\n\t\t'serversettings' => 'Opzioni',\n\t\t'rebuildconf' => 'Rigenera configurazione',\n\t\t'stdsubdomain' => 'Sottodominio standard',\n\t\t'stdsubdomain_add' => 'Crea sottodominio standard',\n\t\t'phpenabled' => 'PHP abilitato',\n\t\t'deactivated' => 'Disattiva',\n\t\t'deactivated_user' => 'Disattiva utente',\n\t\t'sendpassword' => 'Invia password',\n\t\t'ownvhostsettings' => 'Impostazioni vHost speciali',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Configurazione servizi',\n\t\t\t'overview' => 'Panoramica',\n\t\t\t'distribution' => 'Distribuzione',\n\t\t\t'service' => 'Servizio',\n\t\t\t'daemon' => 'Demone',\n\t\t\t'etc' => 'Altro (Sistema)',\n\t\t\t'choosedistribution' => '-- Scegli una distribuzione --',\n\t\t\t'chooseservice' => '-- Scegli un servizio --',\n\t\t\t'choosedaemon' => '-- Scegli un demone --',\n\t\t\t'statistics' => 'Statistiche',\n\t\t\t'compactoverview' => 'Visualizzazione-Compatta',\n\t\t\t'wizard' => 'Wizard (assistente)',\n\t\t\t'http' => 'Server WEB (HTTP)',\n\t\t\t'dns' => 'Nameserver (DNS)',\n\t\t\t'mail' => 'Server di posta elettronica (IMAP/POP3)',\n\t\t\t'smtp' => 'Server di posta elettronica (SMTP)',\n\t\t\t'ftp' => 'Server FTP',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Template',\n\t\t\t'template_add' => 'Aggiungi template',\n\t\t\t'template_edit' => 'Modifica template',\n\t\t\t'action' => 'Azione',\n\t\t\t'email' => 'Email',\n\t\t\t'subject' => 'Soggetto:',\n\t\t\t'mailbody' => 'Testo dell\\'Email',\n\t\t\t'createcustomer' => 'Email di benvenuto per i nuovi clienti',\n\t\t\t'pop_success' => 'Benvenuto per ogni nuovo account Email',\n\t\t\t'template_replace_vars' => 'Variabili da cambiare nel template:',\n\t\t\t'FIRSTNAME' => 'Rimpiazzato con il nome del cliente.',\n\t\t\t'NAME' => 'Rimpiazzato con il cognome del cliente.',\n\t\t\t'USERNAME' => 'Rimpiazzato con il nome utente dell\\'account.',\n\t\t\t'PASSWORD' => 'Rimpiazzato con la password dell\\'account.',\n\t\t\t'EMAIL' => 'Rimapiazzato con l\\'indirizzo dell\\'account.',\n\t\t\t'TRAFFIC' => 'Sostituito con il traffico che è stato assegnato al cliente.',\n\t\t\t'TRAFFICUSED' => 'Sostituito con il traffico che è stato usato dal cliente.',\n\t\t\t'pop_success_alternative' => 'Email di Benvenuto spedita all\\'indirizzo alternativo per i nuovi account email',\n\t\t\t'EMAIL_PASSWORD' => 'Sostituito con la password dell\\'account POP3/IMAP.',\n\t\t\t'index_html' => 'file index per le nuove cartelle create dai clienti',\n\t\t\t'SERVERNAME' => 'Sostituito con il nomeserver.',\n\t\t\t'CUSTOMER' => 'Sostituito con il nome utente del cliente.',\n\t\t\t'ADMIN' => 'Sostituito con il nome utente dell\\'amministratore.',\n\t\t\t'CUSTOMER_EMAIL' => 'Sostituito con l\\'indirizzo e-mail del cliente.',\n\t\t\t'ADMIN_EMAIL' => 'Sostituito con l\\'indirizzo e-mail dell\\'amministratore.',\n\t\t\t'filetemplates' => 'File Modelli',\n\t\t\t'filecontent' => 'Contenuto File',\n\t\t\t'new_database_by_customer' => 'Notifica al cliente quando un database è stato creato',\n\t\t\t'new_ftpaccount_by_customer' => 'Notifica al cliente quando un utente FTP è stato creato',\n\t\t\t'newdatabase' => 'Mail di notifica per i nuovi database',\n\t\t\t'newftpuser' => 'Mail di notifica per i nuovi utenti ftp',\n\t\t\t'CUST_NAME' => 'Nome del Cliente',\n\t\t\t'DB_NAME' => 'Nome del Database',\n\t\t\t'DB_PASS' => 'Password del Database',\n\t\t\t'DB_DESC' => 'Descrizione del Database',\n\t\t\t'DB_SRV' => 'Server del Database',\n\t\t\t'PMA_URI' => 'URL a phpMyAdmin (se presente)',\n\t\t\t'USR_NAME' => 'Nome utente FTP',\n\t\t\t'USR_PASS' => 'Password FTP',\n\t\t\t'USR_PATH' => 'Cartella utente FTP (rispetto alla cartella docroot del cliente)',\n\t\t\t'forgotpwd' => 'Mail di notifica per il reset della password',\n\t\t\t'password_reset' => 'Notifica al cliente per il reset della password',\n\t\t\t'trafficmaxpercent' => 'Mail di notifica per i clienti che hanno raggiunto il limite di traffico',\n\t\t\t'MAX_PERCENT' => 'Sostituito con spazio/limite di traffico per l\\'invio dei report in precentuale.',\n\t\t\t'USAGE_PERCENT' => 'Sostituito con l\\'utilizzo del disco/limite di traffico, che è stato esaurito da parte del cliente in percentuale.',\n\t\t\t'diskmaxpercent' => 'Mail di notifica per i clienti che hanno raggiunto il limite di spazio su disco',\n\t\t\t'DISKAVAILABLE' => 'Sostituito con il spazio utilizzato in MB, che è stato assegnato al cliente.',\n\t\t\t'DISKUSED' => 'Sostituito con il spazio utilizzato in MB, che è stato esaurito da parte del cliente.',\n\t\t\t'SALUTATION' => 'Sostituito con un saluto corretto (nome o azienda)',\n\t\t\t'COMPANY' => 'Sostituisce con il nome dell \\'azienda del cliente',\n\t\t\t'LINK' => 'Sostituito con il link di azzeramento password cliente.',\n\t\t\t'SERVER_HOSTNAME' => 'Sostituisce il nome host del sistema (URL a froxlor)',\n\t\t\t'SERVER_IP' => 'Sostituisce l\\'indrizzo IP predefinito del server',\n\t\t\t'SERVER_PORT' => 'Sostituisce la porta predefinita del server',\n\t\t\t'DOMAINNAME' => 'Sostituisce il sottodominio predefinito dei clienti (può essere vuoto se non viene generato)',\n\t\t],\n\t\t'webserver' => 'Webserver',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IP e Porte',\n\t\t\t'add' => 'Aggiungi IP/Porta',\n\t\t\t'edit' => 'Modifica IP/Porta',\n\t\t\t'ipandport' => 'IP/Porta',\n\t\t\t'ip' => 'IP',\n\t\t\t'port' => 'Porta',\n\t\t\t'create_listen_statement' => 'Crea la direttiva Listen',\n\t\t\t'create_namevirtualhost_statement' => 'Crea la direttiva NameVirtualHost',\n\t\t\t'create_vhostcontainer' => 'Crea vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Crea la direttiva ServerName in vHost-Container',\n\t\t\t'enable_ssl' => 'Questa è una porta SSL?',\n\t\t\t'ssl_cert_file' => 'Percorso del certificato SSL (SSL certificate)',\n\t\t\t'webserverdefaultconfig' => 'Configurazione predefinita Webserver',\n\t\t\t'webserverdomainconfig' => 'Configurazione Dominio Webserver',\n\t\t\t'webserverssldomainconfig' => 'COnfigura SSL Webserver',\n\t\t\t'ssl_key_file' => 'Percorso al Keyfile SSL',\n\t\t\t'ssl_ca_file' => 'Percorso al CA certificate SSL',\n\t\t\t'default_vhostconf_domain' => 'Impostazioni predefinite vhost per ogni contenitore di dominio',\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Imposta Cartella principale (vuoto = va in froxlor)',\n\t\t\t\t'description' => 'Qui puoi definire una tua cartella principale (la destinazione di una richiesta) per questa combinazione IP/Porta.<br /><strong>ATTENZIONE:</strong> Fai molta attenzione a quello che scrivi qui!',\n\t\t\t],\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'Percorso al file catena dei certificati SSL',\n\t\t\t\t'description' => 'Principalmente Bundle CA, o similare, presubilmente vuoi impostare questo se hai acquistato un certificato SSL.',\n\t\t\t],\n\t\t\t'ssl_paste_description' => 'Incolla il contenuto completo del tuo certificato nella casella di testo',\n\t\t\t'ssl_cert_file_content' => 'Contenuto del certificato ssl',\n\t\t\t'ssl_key_file_content' => 'Contenuto del file di chiave (privata) ssl',\n\t\t\t'ssl_ca_file_content' => 'Contenuto del file ssl CA di autorità di certificazione (opzionale)',\n\t\t\t'ssl_ca_file_content_desc' => '<br /><br />Autenticazione client, imposta questo settaggio soltanto se sai di cosa si tratta.',\n\t\t\t'ssl_cert_chainfile_content' => 'Contenuto del file di catena di certificato (opzionale)',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br /><br />Abitualmente Bundle CA o similare, probabilmente vuoi impostare questo settaggio se hai acquistato un certificato SSL.',\n\t\t],\n\t\t'memorylimitdisabled' => 'Disabilitato',\n\t\t'valuemandatory' => 'Questo valore è obbligatorio',\n\t\t'valuemandatorycompany' => 'O i campi \"nome\" e \"cognome\" O il capo \"compagnia\" devono essere riempiti',\n\t\t'phpversion' => 'Versione PHP',\n\t\t'mysqlserverversion' => 'Versione MySQL Server',\n\t\t'webserverinterface' => 'Interfaccia Webserver',\n\t\t'accountsettings' => 'Impostazioni Account',\n\t\t'panelsettings' => 'Impostazioni Pannello',\n\t\t'systemsettings' => 'Impostazioni di Sistema',\n\t\t'webserversettings' => 'Impostazioni Server Web',\n\t\t'mailserversettings' => 'Impostazioni Server di Posta',\n\t\t'nameserversettings' => 'Impostazioni Nameserver',\n\t\t'updatecounters' => 'Ricalcolo risorse',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Mai',\n\t\t\t'choosableno' => 'Selezionabile, predefinito no',\n\t\t\t'choosableyes' => 'Selezionabile, predefinito si',\n\t\t\t'always' => 'Sempre',\n\t\t],\n\t\t'webalizersettings' => 'Impostazioni Webalizer',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normale',\n\t\t\t'quiet' => 'Modesto',\n\t\t\t'veryquiet' => 'Niente',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Adesso non è possibile aggiungere un dominio. Prima è necessario aggiungere almeno un cliente.',\n\t\t'loggersettings' => 'Impostazioni Log',\n\t\t'logger' => [\n\t\t\t'normal' => 'normale',\n\t\t\t'paranoid' => 'paranoico',\n\t\t],\n\t\t'emaildomain' => 'Email dominio',\n\t\t'email_only' => 'Solo email?',\n\t\t'wwwserveralias' => 'Aggiungi a \"www.\" ServerAlias',\n\t\t'subject' => 'Oggetto',\n\t\t'recipient' => 'Destinatario',\n\t\t'message' => 'Scrivi un messaggio',\n\t\t'text' => 'Messaggio',\n\t\t'sslsettings' => 'Impostazioni SSL',\n\t\t'dkimsettings' => 'Impostazioni DomainKey',\n\t\t'caneditphpsettings' => 'È possibile modificare le impostazioni di dominio relative a php?',\n\t\t'allips' => 'Tutti gli IP',\n\t\t'awstatssettings' => 'Impostazioni Awstats',\n\t\t'domain_dns_settings' => 'Impostazioni dominio dns',\n\t\t'activated' => 'Attivato',\n\t\t'statisticsettings' => 'Impostazioni Statistiche',\n\t\t'or' => 'o',\n\t\t'sysload' => 'Carico del sistema',\n\t\t'noloadavailable' => 'non disponibile',\n\t\t'nouptimeavailable' => 'non disponibile',\n\t\t'nosubject' => '(Nessun Oggetto)',\n\t\t'security_settings' => 'Opzioni di Sicurezza',\n\t\t'know_what_youre_doing' => 'Modifica solo, se sai quello che stai facendo!',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Mostra la versione di froxlor quando si effettua l\\'accesso',\n\t\t\t'description' => 'Mostra la versione di froxlor in fondo-pagina di accesso',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Mostra la versione di froxlor in fondo-pagina',\n\t\t\t'description' => 'Mostra la versione di froxlor in fondo ad ogni pagina',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Intestazione grafica per froxlor',\n\t\t\t'description' => 'Quale grafica vuoi mostrare nell\\'intestazione?',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'Configurazione PHP',\n\t\t\t'description' => 'Descrizione breve',\n\t\t\t'actions' => 'Azioni',\n\t\t\t'activedomains' => 'In uso per dominio/i',\n\t\t\t'notused' => 'Configurazione non utilizzata',\n\t\t\t'editsettings' => 'Modific impostazioni PHP',\n\t\t\t'addsettings' => 'Crea una nuova impostazione PHP',\n\t\t\t'viewsettings' => 'Mostra impostazioni PHP',\n\t\t\t'phpinisettings' => 'Impostazioni php.ini',\n\t\t\t'addnew' => 'Crea nuove impostazioni',\n\t\t\t'binary' => 'PHP Binary',\n\t\t\t'file_extensions' => 'Estensioni File',\n\t\t\t'file_extensions_note' => '(senza punto, separate da spazi)',\n\t\t\t'enable_slowlog' => 'Abilita slowlog (per dominio)',\n\t\t\t'request_terminate_timeout' => 'Richiedi terminate-timeout',\n\t\t\t'request_slowlog_timeout' => 'Richiedi slowlog-timeout',\n\t\t],\n\t\t'misc' => 'Varie',\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'Variabili che saranno sostituite nei file di configurazione',\n\t\t\t'pear_dir' => 'Verrà sostituito con le impostazioni globali per la cartella pear.',\n\t\t\t'open_basedir_c' => 'Inserirà un ; (punto e virgola) per commentare/disabilitare open_basedir se impostato',\n\t\t\t'open_basedir' => 'Verrà sostituito con l\\'impostazione open_basedir del dominio.',\n\t\t\t'tmp_dir' => 'Verrà sostituito con la cartella temporanea del dominio.',\n\t\t\t'open_basedir_global' => 'Verrà sostituito con il valore globale del percorso che sarà allegato al open_basedir',\n\t\t\t'customer_email' => 'Verrà sostituito con l\\'indirizzo email del cliente che possiede questo dominio.',\n\t\t\t'admin_email' => 'Verrà sostituito con l\\'indirizzo email dell\\'amministratore di questo dominio.',\n\t\t\t'domain' => 'Verrà sostituito con il dominio.',\n\t\t\t'customer' => 'Verrà sostituito con il nome utente del cliente che possiede questo dominio.',\n\t\t\t'admin' => 'Verrà sostituito con il nome utente dell\\'amministratore che possiede questo dominio.',\n\t\t],\n\t\t'expert_settings' => 'Impostazioni Avanzate!',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'Processi PHP per questo dominio (vuoto per lasciare il valore predefinito)',\n\t\t],\n\t\t'phpserversettings' => 'Impostazioni PHP',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Richieste PHP massime per questo dominio (vuoto per lasciare il valore predefinito)',\n\t\t],\n\t\t'spfsettings' => 'Impostazioni Dominio SPF',\n\t\t'specialsettingsforsubdomains' => 'Applica le impostazioni speciali a tutti i sottodomini (*.esempio.com)',\n\t\t'accountdata' => 'Dati conto',\n\t\t'contactdata' => 'Dati contatto',\n\t\t'servicedata' => 'Dati di servizio',\n\t\t'newerversionavailable' => 'È disponibile una nuova versione di Floxlor',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Impostazioni Cronjob',\n\t\t\t'add' => 'Aggiungi cronjob',\n\t\t],\n\t\t'cronjob_edit' => 'Modifica cronjob',\n\t\t'warning' => 'ATTENZIONE - Leggi attentamente!',\n\t\t'lastlogin_succ' => 'Ultimo accesso',\n\t\t'ftpserver' => 'Server FTP',\n\t\t'ftpserversettings' => 'Impostazioni del Server FTP',\n\t\t'webserver_user' => 'Utente Webserver',\n\t\t'webserver_group' => 'Gruppo Webserver',\n\t\t'perlenabled' => 'Perl abilitato',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Utente locale per FCGID (froxlor vhost)',\n\t\t'mod_fcgid_group' => 'Gruppo locale per FCGID (froxlor vhost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[se presente]',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => 'Traffico',\n\t\t'domaintraffic' => 'Domimi',\n\t\t'customertraffic' => 'Clienti',\n\t\t'serversoftware' => 'Software per Server',\n\t\t'store_defaultindex' => 'Archivio del file indice predefinito al percorso radice clienti',\n\t\t'assignedmax' => 'Assegnato / Max',\n\t\t'usedmax' => 'Usato / Max',\n\t\t'used' => 'Usato',\n\t\t'speciallogwarning' => '<div id=\"speciallogfilenote\" class=\"invalid-feedback\">AVVISO: Cambiando questa impostazione perderai tutte le vecchie statistiche per questo dominio.</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => 'File file log seperato',\n\t\t\t'description' => 'Spunta qui per un log di accesso separato per questo dominio',\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => 'Permetti la modifica del dominio',\n\t\t\t'desc' => 'Se settato a si, il cliente è abilitato a modificare varie impostazioni del dominio.<br />Se settato su no, il cliente non può modificare nulla.',\n\t\t],\n\t\t'phpfpm.ininote' => 'Non tutti i valori che potresti volere settare possono essere usati nella configurazione del pool php-fpm.',\n\t\t'phpinfo' => 'PHPinfo()',\n\t\t'selectserveralias' => 'valore ServerAlias per il dominio',\n\t\t'selectserveralias_desc' => 'Scegli se froxlor deve creare un settaggio wildcard (*.dominio.tld), o un alias WWW (www.dominio.tld) o nessun alias',\n\t\t'show_news_feed' => 'Mostra il feed notizie sul cruscotto dell \\'amministratore',\n\t\t'cronsettings' => 'Impostazioni Cronjob',\n\t\t'integritycheck' => 'Validazione Database',\n\t\t'integrityid' => '#',\n\t\t'integrityname' => 'Nome',\n\t\t'integrityresult' => 'Risultato',\n\t\t'integrityfix' => 'Risolvi problemi automaticamente',\n\t\t'customer_show_news_feed' => 'Mostra feed di notizie personalizzati sul cruscotto dei clienti',\n\t\t'customer_news_feed_url' => 'Feed RSS- per il feed di notizie personalizzato',\n\t\t'movetoadmin' => 'Trasferisci cliente',\n\t\t'movecustomertoadmin' => 'Trasferisci cliente all\\'amministratore/rivenditore selezionato<br /><small>Lascia questo vuoto per nessuna modifica.<br />Se l\\'amministratore desiderato non appare nella lista, il suo massimale di clienti e stato ragggiunto.</small>',\n\t\t'note' => 'Nota',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Vecchia password',\n\t\t'new_password' => 'Nuova password',\n\t\t'new_password_confirm' => 'Nuova password (verifica)',\n\t\t'new_password_ifnotempty' => 'Nuova password (vuota = non cambia)',\n\t\t'also_change_ftp' => ' cambia la password dell\\'account FTP principale?',\n\t\t'also_change_stats' => ' modificare anche la password di statistic',\n\t],\n\t'country' => [\n\t\t'AF' => 'Afganistan',\n\t\t'AX' => 'Isole Aland',\n\t\t'AL' => 'Albania',\n\t\t'DZ' => 'Algeria',\n\t\t'AS' => 'American Samoa',\n\t\t'AD' => 'Andorra',\n\t\t'AO' => 'Angola',\n\t\t'AI' => 'Anguilla',\n\t\t'AQ' => 'Antarctica',\n\t\t'AG' => 'Antigua and Barbuda',\n\t\t'AR' => 'Argentina',\n\t\t'AM' => 'Armenia',\n\t\t'AW' => 'Aruba',\n\t\t'AU' => 'Australia',\n\t\t'AT' => 'Austria',\n\t\t'AZ' => 'Azerbaijan',\n\t\t'BS' => 'Bahamas',\n\t\t'BH' => 'Bahrain',\n\t\t'BD' => 'Bangladesh',\n\t\t'BB' => 'Barbados',\n\t\t'BY' => 'Belarus',\n\t\t'BE' => 'Belgium',\n\t\t'BZ' => 'Belize',\n\t\t'BJ' => 'Benin',\n\t\t'BM' => 'Bermuda',\n\t\t'BT' => 'Bhutan',\n\t\t'BO' => 'Bolivia, Stato Plurinazionale della',\n\t\t'BQ' => 'Bonaire, Saint Eustatius e Saba',\n\t\t'BA' => 'Bosnia e Herzegovina',\n\t\t'BW' => 'Botswana',\n\t\t'BV' => 'Bouvet Island',\n\t\t'BR' => 'Brasile',\n\t\t'IO' => 'Territorio Britannico del oceano indiano',\n\t\t'BN' => 'Brunei Darussalam',\n\t\t'BG' => 'Bulgaria',\n\t\t'BF' => 'Burkina Faso',\n\t\t'BI' => 'Burundi',\n\t\t'KH' => 'Cambogia',\n\t\t'CM' => 'Camerun',\n\t\t'CA' => 'Canada',\n\t\t'CV' => 'Capo Verde',\n\t\t'KY' => 'Isole Cayman',\n\t\t'CF' => 'Repubblica dell\\'Africa Centrale',\n\t\t'TD' => 'Chad',\n\t\t'CL' => 'Chile',\n\t\t'CN' => 'Cina',\n\t\t'CX' => 'Isola di Natale',\n\t\t'CC' => 'Isole Cocos (Keeling)',\n\t\t'CO' => 'Colombia',\n\t\t'KM' => 'Comoros',\n\t\t'CG' => 'Congo',\n\t\t'CD' => 'Congo, Repubblica democratica del',\n\t\t'CK' => 'Isole Cook',\n\t\t'CR' => 'Costa Rica',\n\t\t'CI' => 'Costa D\\'avorio',\n\t\t'HR' => 'Croazia',\n\t\t'CU' => 'Cuba',\n\t\t'CW' => 'Curacao',\n\t\t'CY' => 'Cipro',\n\t\t'CZ' => 'Repubblica Ceca',\n\t\t'DK' => 'Danimarca',\n\t\t'DJ' => 'Djibouti',\n\t\t'DM' => 'Dominica',\n\t\t'DO' => 'Repubblica Dominicana',\n\t\t'EC' => 'Ecuador',\n\t\t'EG' => 'Egitto',\n\t\t'SV' => 'El Salvador',\n\t\t'GQ' => 'Guinea Equatoriale',\n\t\t'ER' => 'Eritrea',\n\t\t'EE' => 'Estonia',\n\t\t'ET' => 'Etiopia',\n\t\t'FK' => 'Isole Falkland (Malvinas)',\n\t\t'FO' => 'Isole Faroe',\n\t\t'FJ' => 'Fiji',\n\t\t'FI' => 'Finlandia',\n\t\t'FR' => 'Francia',\n\t\t'GF' => 'Guiana Francese',\n\t\t'PF' => 'Polinesia Francese',\n\t\t'TF' => 'Territori Francesi del Sud',\n\t\t'GA' => 'Gabon',\n\t\t'GM' => 'Gambia',\n\t\t'GE' => 'Georgia',\n\t\t'DE' => 'Germania',\n\t\t'GH' => 'Ghana',\n\t\t'GI' => 'Gibilterra',\n\t\t'GR' => 'Grecia',\n\t\t'GL' => 'Groenlandia',\n\t\t'GD' => 'Grenada',\n\t\t'GP' => 'Guadeloupe',\n\t\t'GU' => 'Guam',\n\t\t'GT' => 'Guatemala',\n\t\t'GG' => 'Guernsey',\n\t\t'GN' => 'Guinea',\n\t\t'GW' => 'Guinea-Bissau',\n\t\t'GY' => 'Guyana',\n\t\t'HT' => 'Haiti',\n\t\t'HM' => 'Isola Heard e Isola McDonald',\n\t\t'VA' => 'Stato del Vaticano',\n\t\t'HN' => 'Honduras',\n\t\t'HK' => 'Hong Kong',\n\t\t'HU' => 'Ungheria',\n\t\t'IS' => 'Islanda',\n\t\t'IN' => 'India',\n\t\t'ID' => 'Indonesia',\n\t\t'IR' => 'Iran, Repubblica Islamica del',\n\t\t'IQ' => 'Iraq',\n\t\t'IE' => 'Irlanda',\n\t\t'IM' => 'Isola Man',\n\t\t'IL' => 'Israele',\n\t\t'IT' => 'ITALIA',\n\t\t'JM' => 'Giamaica',\n\t\t'JP' => 'Giappone',\n\t\t'JE' => 'Jersey',\n\t\t'JO' => 'Giordania',\n\t\t'KZ' => 'Kazakistan',\n\t\t'KE' => 'Kenya',\n\t\t'KI' => 'Kiribati',\n\t\t'KP' => 'Corea, Repubblica popolare della',\n\t\t'KR' => 'Corea, Repubblica della',\n\t\t'KW' => 'Kuwait',\n\t\t'KG' => 'Kyrgyzstan',\n\t\t'LA' => 'Lao, Repubblica popolare del',\n\t\t'LV' => 'Lettonia',\n\t\t'LB' => 'Libano',\n\t\t'LS' => 'Lesotho',\n\t\t'LR' => 'Liberia',\n\t\t'LY' => 'Libia',\n\t\t'LI' => 'Liechtenstein',\n\t\t'LT' => 'Lituania',\n\t\t'LU' => 'Lussemburgo',\n\t\t'MO' => 'Macao',\n\t\t'MK' => 'Macedonia',\n\t\t'MG' => 'Madagascar',\n\t\t'MW' => 'Malawi',\n\t\t'MY' => 'Malesia',\n\t\t'MV' => 'Maldive',\n\t\t'ML' => 'Mali',\n\t\t'MT' => 'Malta',\n\t\t'MH' => 'Isole Marshall',\n\t\t'MQ' => 'Martinique',\n\t\t'MR' => 'Mauritania',\n\t\t'MU' => 'Mauritius',\n\t\t'YT' => 'Mayotte',\n\t\t'MX' => 'Messico',\n\t\t'FM' => 'Micronesia, Stati Federali del',\n\t\t'MD' => 'Moldavia',\n\t\t'MC' => 'Monaco',\n\t\t'MN' => 'Mongolia',\n\t\t'ME' => 'Montenegro',\n\t\t'MS' => 'Montserrat',\n\t\t'MA' => 'Marocco',\n\t\t'MZ' => 'Mozambico',\n\t\t'MM' => 'Myanmar',\n\t\t'NA' => 'Namibia',\n\t\t'NR' => 'Nauru',\n\t\t'NP' => 'Nepal',\n\t\t'NL' => 'Olanda',\n\t\t'NC' => 'Nuova Caledonia',\n\t\t'NZ' => 'Nuova Zelanda',\n\t\t'NI' => 'Nicaragua',\n\t\t'NE' => 'Niger',\n\t\t'NG' => 'Nigeria',\n\t\t'NU' => 'Niue',\n\t\t'NF' => 'Isole Norfolk',\n\t\t'MP' => 'Isole Mariana Settentrionali',\n\t\t'NO' => 'Norvegia',\n\t\t'OM' => 'Oman',\n\t\t'PK' => 'Pakistan',\n\t\t'PW' => 'Palau',\n\t\t'PS' => 'Territorio Occupato della Palestina',\n\t\t'PA' => 'Panama',\n\t\t'PG' => 'Papua Nuova Guinea',\n\t\t'PY' => 'Paraguay',\n\t\t'PE' => 'Peru',\n\t\t'PH' => 'Filippine',\n\t\t'PN' => 'Pitcairn',\n\t\t'PL' => 'Polonia',\n\t\t'PT' => 'Portogallo',\n\t\t'PR' => 'Porto Rico',\n\t\t'QA' => 'Qatar',\n\t\t'RE' => 'Reunion',\n\t\t'RO' => 'Romania',\n\t\t'RU' => 'Russia',\n\t\t'RW' => 'Ruanda',\n\t\t'BL' => 'Saint Barthelemy',\n\t\t'SH' => 'Saint Helena, Ascension and Tristan Da Cunha',\n\t\t'KN' => 'Saint Kitts and Nevis',\n\t\t'LC' => 'Saint Lucia',\n\t\t'MF' => 'Saint Martin (French Part)',\n\t\t'PM' => 'Saint Pierre and Miquelon',\n\t\t'VC' => 'Saint Vincent and the Grenadines',\n\t\t'WS' => 'Samoa',\n\t\t'SM' => 'San Marino',\n\t\t'ST' => 'Sao Tome and Principe',\n\t\t'SA' => 'Arabia Saudita',\n\t\t'SN' => 'Senegal',\n\t\t'RS' => 'Serbia',\n\t\t'SC' => 'Seychelles',\n\t\t'SL' => 'Sierra Leone',\n\t\t'SG' => 'Singapore',\n\t\t'SX' => 'Sint Maarten (Dutch Part)',\n\t\t'SK' => 'Slovacchia',\n\t\t'SI' => 'Slovenia',\n\t\t'SB' => 'Isole Solomon',\n\t\t'SO' => 'Somalia',\n\t\t'ZA' => 'Africa del Sud',\n\t\t'GS' => 'South Georgia and the South Sandwich Islands',\n\t\t'ES' => 'Spagna',\n\t\t'LK' => 'Sri Lanka',\n\t\t'SD' => 'Sudan',\n\t\t'SR' => 'Suriname',\n\t\t'SJ' => 'Svalbard and Jan Mayen',\n\t\t'SZ' => 'Swaziland',\n\t\t'SE' => 'Svezia',\n\t\t'CH' => 'Svizzera',\n\t\t'SY' => 'Siria',\n\t\t'TW' => 'Taiwan, Provincia della Cina',\n\t\t'TJ' => 'Tajikistan',\n\t\t'TZ' => 'Tanzania',\n\t\t'TH' => 'Tailandia',\n\t\t'TL' => 'Timor-Leste',\n\t\t'TG' => 'Togo',\n\t\t'TK' => 'Tokelau',\n\t\t'TO' => 'Tonga',\n\t\t'TT' => 'Trinidad and Tobago',\n\t\t'TN' => 'Tunisia',\n\t\t'TR' => 'Turchia',\n\t\t'TM' => 'Turkmenistan',\n\t\t'TC' => 'Turks and Caicos Islands',\n\t\t'TV' => 'Tuvalu',\n\t\t'UG' => 'Uganda',\n\t\t'UA' => 'Ucraina',\n\t\t'AE' => 'Emirati Arabi Uniti',\n\t\t'GB' => 'Gran Bretagna',\n\t\t'US' => 'Stati Uniti d\\'America',\n\t\t'UM' => 'Stati Uniti, Isole Minori',\n\t\t'UY' => 'Uruguay',\n\t\t'UZ' => 'Uzbekistan',\n\t\t'VU' => 'Vanuatu',\n\t\t'VE' => 'Venezuela',\n\t\t'VN' => 'Vietnam',\n\t\t'VG' => 'Isole Vergini Brittaniche',\n\t\t'VI' => 'Isole Vergini, U.S.',\n\t\t'WF' => 'Wallis and Futuna',\n\t\t'EH' => 'Sahara Occidentale',\n\t\t'YE' => 'Yemen',\n\t\t'ZM' => 'Zambia',\n\t\t'ZW' => 'Zimbabue',\n\t],\n\t'cron' => [\n\t\t'cronname' => 'Nome cronjob',\n\t\t'lastrun' => 'ultima esecuzione',\n\t\t'interval' => 'intervallo',\n\t\t'isactive' => 'abilitato',\n\t\t'description' => 'descrizione',\n\t\t'changewarning' => 'La modifica di questi valori può avere conseguenze negative nel comportamento di froxlor e dei suoi processi automatizzati<br />Per favore modifica questi valori solo se sei sicuro di quello che stai facendo!',\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'nessuna descrizione fornita',\n\t\t'cron_tasks' => 'generazione del file di configurazione',\n\t\t'cron_legacy' => 'legacy (vecchi) cronjob',\n\t\t'cron_traffic' => 'calcolo del traffico',\n\t\t'cron_usage_report' => 'Invia i report di utilizzo web e del traffico',\n\t\t'cron_mailboxsize' => 'Calcolo dimensioni caselle di posta',\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'impostazioni Cronjob',\n\t\t'cronjobinterval' => 'Durata intervallo',\n\t\t'cronjobintervalv' => 'valore di intervallo Runtime',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Non ancora avviato',\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => 'minuti',\n\t\t'hours' => 'ore',\n\t\t'days' => 'giorni',\n\t\t'weeks' => 'settimane',\n\t\t'months' => 'mesi',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Cartella Principale',\n\t\t'name' => 'Cognome',\n\t\t'firstname' => 'Nome',\n\t\t'company' => 'Ditta',\n\t\t'street' => 'Via',\n\t\t'zipcode' => 'CAP',\n\t\t'city' => 'Città',\n\t\t'phone' => 'Telefono',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'Email',\n\t\t'customernumber' => 'ID Cliente',\n\t\t'diskspace' => 'Spazio Web (MB)',\n\t\t'traffic' => 'Traffico (GB)',\n\t\t'mysqls' => 'Database MySQL',\n\t\t'emails' => 'Indirizzi Email',\n\t\t'accounts' => 'Account Email',\n\t\t'forwarders' => 'Reindirizzamenti Email',\n\t\t'ftps' => 'Account FTP',\n\t\t'subdomains' => 'Sottodomini',\n\t\t'domains' => 'Domini',\n\t\t'title' => 'Titolo',\n\t\t'country' => 'Paese',\n\t\t'email_quota' => 'Limite E-mail',\n\t\t'email_imap' => 'E-mail IMAP',\n\t\t'email_pop3' => 'E-mail POP3',\n\t\t'mail_quota' => 'Limite Mail',\n\t\t'sendinfomail' => 'Inviami i dati via email',\n\t\t'generated_pwd' => 'Password suggerita',\n\t\t'usedmax' => 'Usato / Massimo',\n\t\t'services' => 'Servizi',\n\t],\n\t'diskquota' => 'Quota',\n\t'dns' => [\n\t\t'destinationip' => 'Dominio IP',\n\t\t'a_record' => 'A-Record (IPv6 optionale)',\n\t\t'mxrecords' => 'Definisci MX records',\n\t\t'txtrecords' => 'Definisci TXT records',\n\t\t'txtexample' => 'Esempio (SPF-entry):<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'standardip' => 'IP predefinito del server',\n\t\t'cname_record' => 'Record CNAME',\n\t\t'standardmx' => 'Record MX predefinito del server',\n\t\t'mxconfig' => 'Record MX personalizzati',\n\t\t'priority10' => 'Priorità 10',\n\t\t'priority20' => 'Priorità 20',\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'Percorso OpenBasedir',\n\t\t'docroot' => 'Percorso del campo sopra',\n\t\t'homedir' => 'Cartella Home',\n\t],\n\t'domains' => [\n\t\t'description' => 'Qui puoi creare (sotto)domini e cambiare il loro percorso.<br />Il sistema, dopo ogni cambiamento, necessita di un po\\' di tempo per applicare le nuove impostazioni.',\n\t\t'domainsettings' => 'Opzioni del dominio',\n\t\t'domainname' => 'Nome del dominio',\n\t\t'subdomain_add' => 'Crea sottodominio',\n\t\t'subdomain_edit' => 'Modifica il (sotto)dominio',\n\t\t'wildcarddomain' => 'Crea una wildcarddomain?',\n\t\t'aliasdomain' => 'Alias per questo dominio',\n\t\t'noaliasdomain' => 'Nessun alias per il dominio',\n\t\t'hasaliasdomains' => 'Ha domini alias',\n\t\t'statstics' => 'Statistiche d\\'utilizzo',\n\t\t'isassigneddomain' => 'È dominio assegnato',\n\t\t'add_date' => 'Aggiunto a froxlor',\n\t\t'registration_date' => 'Aggiunto al registro',\n\t\t'topleveldomain' => 'Dominio di primo livello (TLD)',\n\t\t'associated_with_domain' => 'Associato',\n\t\t'aliasdomains' => 'Alias domini',\n\t\t'redirectifpathisurl' => 'Codice di redirezione (Predefinito: vuoto)',\n\t\t'redirectifpathisurlinfo' => 'È necessario selezionare uno di questi se hai inserito un URL come percorso',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'Indirizzi IP',\n\t\t\t'description' => 'Specifica uno o più indirizzi IP per il dominio.<br /><br /><div class=\"text-danger\">NOTA: L\\'indirizzo IP non può essere modificato quando il dominio è configurato come <strong>alias-domain</strong> di un altro dominio.</div>',\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'Indirizzi IP SSL',\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'Reindirizzamento SSL',\n\t\t\t'description' => 'Questa opzione crea un reindirizzamento per vhosts non-sll in modo che tutte le richieste vengono reindirizzate ai SSL-vhost.<br /><br />praticamente una richiesta a <strong>http</strong>://dominio.tld/ ti reindirizzera a <strong>https</strong>://dominio.tld/',\n\t\t],\n\t\t'serveraliasoption_wildcard' => 'Wildcard (*.dominio.tld)',\n\t\t'serveraliasoption_www' => 'WWW (www.dominio.tld)',\n\t\t'serveraliasoption_none' => 'Nessun alias',\n\t\t'domain_import' => 'Importa Dominii',\n\t\t'import_separator' => 'Separatore',\n\t\t'import_offset' => 'Offset',\n\t\t'import_file' => 'File CSV',\n\t\t'import_description' => 'Per ottenere informazioni dettagliate sulla struttura del file di importazione e  su come importare con successo, visita <a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a>',\n\t],\n\t'emails' => [\n\t\t'description' => 'Qui puoi creare e cambiare i tuoi indirizzi Email.<br />Un account è come la bucalettere davanti a casa tua. Se qualcuno ti manda un\\'Email, essa sarà recapitata all\\'interno del tuo account.<br /><br />Per scaricare le tue Email usa le seguenti impostazioni nel tuo programma di posta elettronica: (I dati scritti in <i>corsivo</i> vanno cambiati con i tuoi!)<br />Hostname: <b><i>Nome del dominio</i></b><br />Username: <b><i>Nome dell\\'account / Indirizzo Email</i></b><br />Password: <b><i>La password scelta</i></b>',\n\t\t'emailaddress' => 'Indirizzo Email',\n\t\t'emails_add' => 'Crea indirizzo Email',\n\t\t'emails_edit' => 'Modifica indirizzo Email',\n\t\t'catchall' => 'Catch-all',\n\t\t'iscatchall' => 'Definisci come indirizzo catch-all?',\n\t\t'account' => 'Account',\n\t\t'account_add' => 'Crea account',\n\t\t'account_delete' => 'Cancella account',\n\t\t'from' => 'Da',\n\t\t'to' => 'Per',\n\t\t'forwarders' => 'Reindirizzamenti',\n\t\t'forwarder_add' => 'Crea reindirizzamento',\n\t\t'alternative_emailaddress' => 'Indirizzo email alternativo',\n\t\t'quota' => 'Limite',\n\t\t'noquota' => 'Nessun limite',\n\t\t'updatequota' => 'Aggiorna Limite',\n\t\t'quota_edit' => 'Cambia limite E-Mail',\n\t\t'noemaildomainaddedyet' => 'Non hai ancora un (email-)dominio nel tuo account.',\n\t\t'back_to_overview' => 'Torna indietro nel riepilogo',\n\t],\n\t'error' => [\n\t\t'error' => 'Errore',\n\t\t'directorymustexist' => 'La cartella %s deve esistere. Per favore creala tramite il tuo client FTP.',\n\t\t'filemustexist' => 'Il file %s deve esistere.',\n\t\t'allresourcesused' => 'Hai già usato tutte le tue risorse.',\n\t\t'domains_cantdeletemaindomain' => 'Non puoi cancellare un dominio usato come dominio Email.',\n\t\t'domains_canteditdomain' => 'Non puoi modificare questo dominio. La funzione è stata disabilitata dall\\'admin.',\n\t\t'domains_cantdeletedomainwithemail' => 'Non puoi cancellare un dominio usato come dominio Email. Cancella prima tutti gli indirizzi Email che lo utilizzano.',\n\t\t'firstdeleteallsubdomains' => 'Prima di creare un dominio wildcard, cancella tutti i sottodomini presenti per quel dominio.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Hai già definito un catchall per questo dominio.',\n\t\t'ftp_cantdeletemainaccount' => 'Non puoi cancellare il tuo account FTP principale.',\n\t\t'login' => 'Il nome utente o la password da te immessi sono incorretti. Per favore riprova!',\n\t\t'login_blocked' => 'Questo account è stato sospeso per i troppi tentativi di login falliti. <br />Riprovi tra %s secondi.',\n\t\t'notallreqfieldsorerrors' => 'Alcuni campi sono stati lasciati vuoti o sono stati riempiti incorrettamente.',\n\t\t'oldpasswordnotcorrect' => 'La vecchia password non è corretta.',\n\t\t'youcantallocatemorethanyouhave' => 'Non puoi assegnare più risorse di quante ne possieda tu stesso.',\n\t\t'mustbeurl' => 'Non hai inserito un\\'indirizzo valido o completo (per es. http://qualchedominio.com/errore404.htm).',\n\t\t'invalidpath' => 'Non hai scelto un\\'indirizzo valido.',\n\t\t'stringisempty' => 'Manca il dato nel campo.',\n\t\t'stringiswrong' => 'Dato incorretto.',\n\t\t'newpasswordconfirmerror' => 'La nuova password non corrisponde a quella vecchia.',\n\t\t'mydomain' => '\\'Dominio\\'',\n\t\t'mydocumentroot' => '\\'Documentroot\\'',\n\t\t'loginnameexists' => 'Il login %s esiste già.',\n\t\t'emailiswrong' => 'L\\'indirizzo Email %s contiene caratteri invalidi o è incompleto.',\n\t\t'loginnameiswrong' => 'Il login %s contiene caratteri invalidi.',\n\t\t'userpathcombinationdupe' => 'La combinazione tra nome utente e percorso esiste già.',\n\t\t'patherror' => 'Errore! Il percorso non può essere vuoto.',\n\t\t'errordocpathdupe' => 'Le opzioni per la cartella %s esistono già.',\n\t\t'adduserfirst' => 'Per favore crea prima un utente ...',\n\t\t'domainalreadyexists' => 'Il dominio %s è già assegnato ad un cliente.',\n\t\t'nolanguageselect' => 'Nessuna lingua selezionata.',\n\t\t'nosubjectcreate' => 'Devi definire un titolo per questo template Email.',\n\t\t'nomailbodycreate' => 'Devi definiro un testo per questo template Email.',\n\t\t'templatenotfound' => 'Il template non è stato trovato.',\n\t\t'alltemplatesdefined' => 'Non puoi definire altri template, tutte le lingue sono già definite.',\n\t\t'wwwnotallowed' => 'www non è ammesso come sottodominio.',\n\t\t'subdomainiswrong' => 'Il sottodominio %s contiene caratteri invalidi.',\n\t\t'domaincantbeempty' => 'Il nome dominio non può essere vuoto.',\n\t\t'domainexistalready' => 'Il dominio %s esiste già.',\n\t\t'domainisaliasorothercustomer' => 'Il dominio alias selezionato è a sua volta un dominio alias o appartiene ad un altro cliente.',\n\t\t'emailexistalready' => 'L\\'indirizzo Email %s esiste già.',\n\t\t'maindomainnonexist' => 'Il dominio principale %s non esiste.',\n\t\t'destinationnonexist' => 'Per favore crea il tuo reindirizzamento nel campo \\'Destinazione\\'.',\n\t\t'destinationalreadyexistasmail' => 'Il reindirizzamento a %s esiste già come indirizzo Email attivo.',\n\t\t'destinationalreadyexist' => 'Hai già definito un reindirizzamento per %s .',\n\t\t'destinationiswrong' => 'Il reindirizzamento %s contiene caratteri invalidi o è incompleto.',\n\t\t'ipstillhasdomains' => 'La combinazione IP/Porta che vuoi eliminare ha ancora dei domini assegnati, per favore riassegna questi domini ad altre combinazioni IP/Porta prima di eliminare questa.',\n\t\t'cantdeletedefaultip' => 'Non puoi eliminare la combinazione IP/Porta default dei rivenditori, per favore imposta un\\'altra combinazione IP/Porta come default dei rivenditori prima di eliminare questa.',\n\t\t'cantdeletesystemip' => 'Non puoi eliminare l\\'ultima IP di sistema, crea un\\'altra combinazione IP/Porta per l\\'IP di sistema o cambia l\\'IP di sistema.',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'Porta\\'',\n\t\t'myipdefault' => 'Devi selezionare una combinazione IP/Porta che diventerà default.',\n\t\t'myipnotdouble' => 'Questa combinazione IP/Porta esiste già.',\n\t\t'admin_domain_emailsystemhostname' => 'Spiacente, l\\'hostname di sistema non può essere usato come dominio di un cliente',\n\t\t'cantchangesystemip' => 'Non puoi cambiare l\\'ultima IP di sistema, crea un\\'altra combinazione IP/Porta per l\\'IP di sistema o cambia l\\'IP di sistema.',\n\t\t'loginnameissystemaccount' => 'Non puoi creare account che siano analoghi a quelli di sistema (per esempio quelli che iniziano con \"%s\"). Digita un\\'altro nome account.',\n\t\t'sessiontimeoutiswrong' => '\"Timeout Sessione\" deve essere un numero.',\n\t\t'maxloginattemptsiswrong' => '\"Numero Massimo Tentativi Login\" deve essere un numero.',\n\t\t'deactivatetimiswrong' => '\"Durata Disattivamento\" deve essere un numero.',\n\t\t'accountprefixiswrong' => '&quopt;Prefisso Utente\" incorretto.',\n\t\t'mysqlprefixiswrong' => '&quopt;Prefisso SQL\" incorretto.',\n\t\t'ftpprefixiswrong' => '&quopt;Prefisso FTP\" incorretto.',\n\t\t'ipiswrong' => '\"Indirizzo IP\" incorretto. È permesso solo un indirizzo IP valido.',\n\t\t'vmailuidiswrong' => '\"UID Email\" incorretto. È permessa solo una UID numerica.',\n\t\t'vmailgidiswrong' => '\"GID Email\" incorretto. È permessa solo una GID numerica.',\n\t\t'adminmailiswrong' => '\"Mittente\" incorretto. È permesso solo un indirizzo Email valido.',\n\t\t'pagingiswrong' => 'Valore degli \"Elementi da visualizzare per pagina\" incorretto. Sono permessi solo numeri.',\n\t\t'phpmyadminiswrong' => 'Il link a phpMyAdmin è invalido.',\n\t\t'webmailiswrong' => 'Il link alla WebMail è invalido.',\n\t\t'webftpiswrong' => 'Il link al WebFTP è invalido.',\n\t\t'stringformaterror' => 'Il valore per il campo \"%s\" non è nel formato atteso.',\n\t\t'youcantdeleteyourself' => 'Non puoi cancellare te stesso per motivi di sicurezza.',\n\t\t'youcanteditallfieldsofyourself' => 'Nota: non puoi modificare tutti i campi del tuo account per motivi di sicurezza.',\n\t\t'documentrootexists' => 'La cartella \"%s\" è già presente per questo cliente. Cancella la cartella prima di aggiungere nuovamente il cliente.',\n\t\t'formtokencompromised' => 'La richiesta sembra essere compromessa. Per motivi di sicurezza sei stato disconnesso.',\n\t\t'logerror' => 'Errore Log: %s',\n\t\t'nomessagetosend' => 'Non hai inserito un messaggio.',\n\t\t'norecipientsgiven' => 'Non hai specificato alcun destinatario',\n\t\t'errorsendingmail' => 'Il messaggio a \"%s\" fallito',\n\t\t'cannotreaddir' => 'Impossibile leggere la cartella \"%s\"',\n\t\t'invalidip' => 'Indirizzo IP non valido: %s',\n\t\t'invalidmysqlhost' => 'Indirizzo MySQL non valido: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'Non è possibile abilitare Webalizer e Awstats allo stesso tempo, si prega di sceglierne uno solo',\n\t\t'cannotwritetologfile' => 'Impossibile aprire il file di log %s in scrittura',\n\t\t'vmailquotawrong' => 'Il limite deve essere un numero positivo.',\n\t\t'allocatetoomuchquota' => 'Si è tentato di allocare %s MB Limite, ma non c\\'è abbastanza spazio disponibile.',\n\t\t'missingfields' => 'Non tutti i campi obbligatori sono stati compilati.',\n\t\t'accountnotexisting' => 'L\\'account di posta elettronica dato non esiste.',\n\t\t'nopermissionsorinvalidid' => 'Non hai i permessi necessari per modificare le impostazioni o è stato fornito un ID non valido.',\n\t\t'phpsettingidwrong' => 'Una configurazione PHP con questo ID non esiste',\n\t\t'descriptioninvalid' => 'La descrizione è troppo corta, La descrizione è troppo corta o troppo lunga o contiene caratteri non validi.',\n\t\t'info' => 'Info',\n\t\t'filecontentnotset' => 'Il file non può essere vuoto!',\n\t\t'index_file_extension' => 'L\\'estensione file del file index deve essere compresa tra 1 e 6 caratteri. L\\'estensione può contenere solo i caratteri a-z, A-Z and 0-9',\n\t\t'customerdoesntexist' => 'Il cliente che si è selezionato non esiste.',\n\t\t'admindoesntexist' => 'L\\'amministratore che si è selezionato non esiste.',\n\t\t'ipportdoesntexist' => 'La combinazione IP/Porta selezionata non esiste.',\n\t\t'usernamealreadyexists' => 'Esiste già il nome utente: %s',\n\t\t'plausibilitychecknotunderstood' => 'Il controllo non ha capito la risposta di plausibilità',\n\t\t'errorwhensaving' => 'Verificato un errore durante il salvataggio del campo %s',\n\t\t'hiddenfieldvaluechanged' => 'Il valore per il campo nascosto \"%s\" è cambiato durante la modifica delle impostazioni.<br /><br />Questo non è solitamente un grosso problema, ma le impostazioni potrebbe non essere salvate a causa di questo.',\n\t\t'notrequiredpasswordlength' => 'La password scritta è troppo corta. Si prega di scrivere una password lunga almeno %s caratteri.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Woops, un campo che dovrebbe essere mostrato come opzione in Impostazioni-Principale non è un tipo esclusivo. Si possono rimproverare gli sviluppatori per questo. Questo non deve accadere!',\n\t\t'pathmaynotcontaincolon' => 'Il percorso che hai inserito non dovrebbe contenere i due punti (:). Inserisci un percorso valido.',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => 'La complessita della password specificata non è soddisfacente.<br />Si prega di contattare l\\'amministratore se avete domande sulla complessità della password',\n\t\t'intvaluetoolow' => 'Il numero dato è troppo basso (campo %s)',\n\t\t'intvaluetoohigh' => 'Il numero dato è troppo alto (campo %s)',\n\t\t'phpfpmstillenabled' => 'PHP-FPM è attivo. Si prega di disattivarlo prima di attivare FCGID',\n\t\t'fcgidstillenabled' => 'FCGID è attivo. Si prega di disattivarlo prima di attivare PHP-FPM',\n\t\t'domains_cantdeletedomainwithaliases' => 'Non è possibile cancellare un dominio che viene utilizzato per alias-domains. È necessario eliminare prima gli alias.',\n\t\t'user_banned' => 'Il tuo account è stato bannato. Contatta l\\'amministratore per maggiori informazioni.',\n\t\t'loginnameiswrong2' => 'Il nome utente contiente troppi caratteri. Sono permessi soltanto %s caratteri.',\n\t\t'session_timeout' => 'Valore troppo basso',\n\t\t'session_timeout_desc' => 'Non dovresti settare il timeout della sessione ad un valore minore di 1 minuto.',\n\t\t'invalidhostname' => 'Il nome del Host non può essere vuoto o contenere spazi',\n\t\t'operationnotpermitted' => 'Operazione non permessa!',\n\t\t'featureisdisabled' => 'Funzionalità %s è disabilitata. Perfavore contatta il tuo fornitore di servizi.',\n\t\t'usercurrentlydeactivated' => 'L\\'utente %s è attualmente disabilitato',\n\t\t'setlessthanalreadyused' => 'Non puoi impostare dei limiti minori per \\'%s\\', di quanto questo utente abbia già utilizzato<br />',\n\t\t'stringmustntbeempty' => 'Il valore per il campo %s non può essere vuoto',\n\t\t'sslcertificateismissingprivatekey' => 'Devi specificare una chiave privata per il tuo certificato',\n\t\t'sslcertificatewrongdomain' => 'Il certificato fornito non appartiene a questo dominio',\n\t\t'sslcertificateinvalidcert' => 'Il contenuto del certificato fornito non sembra essere un certificato valido',\n\t\t'sslcertificateinvalidcertkeypair' => 'La chiave privata fornita non sembra appartenere al certificato fornito',\n\t\t'sslcertificateinvalidca' => 'Il certificato CA fornito non sembra essere un certificato valido',\n\t\t'sslcertificateinvalidchain' => 'I dati della catena di certificato non sembrano essere un certificato valido',\n\t\t'givendirnotallowed' => 'La cartella fornita nel campo %s non è permessa.',\n\t\t'sslredirectonlypossiblewithsslipport' => 'L\\'utilizzo del reindirizzamento SSL è possibile soltanto quando il dominio ha almeno una combinazione IP/porta assegnata ed abilitata SSL.',\n\t\t'fcgidstillenableddeadlock' => 'FCGID è attualmente attivo.<br />Perfavore disattivalo prima di cambiare ad un server web diverso da Apache2',\n\t\t'send_report_title' => 'Invia rapporto errori',\n\t\t'send_report_desc' => 'Grazie per aver communicato questo errore, aiutandoci a migliorare froxlor.<br />Questa è la mail che verrà inviata agli svillupatori di froxlor:',\n\t\t'send_report' => 'Invia rapporto',\n\t\t'send_report_error' => 'Errore nell invio del rapporto: <br />%s',\n\t\t'notallowedtouseaccounts' => 'Il tuo account non permette l\\'utilizzo di IMAP/POP3. Non puoi aggiungere account di posta elettronica.',\n\t\t'cannotdeletehostnamephpconfig' => 'Questa configurazione PHP è utilizzata dal vhost froxlor e non può essere eliminata.',\n\t\t'cannotdeletedefaultphpconfig' => 'Questa configurazione PHP è impostata come predefinita e non può essere eliminata.',\n\t\t'passwordshouldnotbeusername' => 'La password deve essere diversa dal nome utente.',\n\t\t'no_phpinfo' => 'Ci dispiace, impossibile leggere phpinfo()',\n\t\t'moveofcustomerfailed' => 'Trasferimento del cliente all\\'amministratore/rivenditore selezionato fallito. Considera che tutte le altre modfiche al cliente sono state applicate con successo a questa fase.<br><br>Messaggio d\\'errore: %s',\n\t\t'domain_import_error' => 'Il seguente errore è occorsonell \\'importazione di dominii: %s',\n\t],\n\t'extras' => [\n\t\t'description' => 'Qui puoi aggiungere alcune opzioni extra, per esempio impostare delle cartelle protette.<br />Il sistema, dopo ogni cambiamento, necessita di un po\\' di tempo per applicare le nuove impostazioni.',\n\t\t'directoryprotection_add' => 'Aggiungi protezione cartella',\n\t\t'view_directory' => 'Mostra protezione cartella',\n\t\t'pathoptions_add' => 'Aggiungi opzioni cartella',\n\t\t'directory_browsing' => 'Visualizza file e cartelle',\n\t\t'pathoptions_edit' => 'Modifica opzioni cartella',\n\t\t'errordocument404path' => 'URL to ErrorDocument 404',\n\t\t'errordocument403path' => 'URL to ErrorDocument 403',\n\t\t'errordocument500path' => 'URL to ErrorDocument 500',\n\t\t'errordocument401path' => 'URL to ErrorDocument 401',\n\t\t'execute_perl' => 'Esegui perl/CGI',\n\t\t'htpasswdauthname' => 'Argomento di Autenticazione (AuthName)',\n\t\t'directoryprotection_edit' => 'modifica la protezione della directory',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Qui puoi creare e modificare i tuoi account FTP.<br />I cambiamenti sono effettuati in tempo reale e gli account si possono usare immediatamente.',\n\t\t'account_add' => 'Crea account',\n\t\t'account_edit' => 'Modifica acocunt FTP',\n\t\t'editpassdescription' => 'Imposta una nuova password o lascia vuoto per non cambiarla.',\n\t],\n\t'gender' => [\n\t\t'title' => 'Titolo',\n\t\t'male' => 'Sig.',\n\t\t'female' => 'Sig.ra',\n\t\t'undef' => '',\n\t],\n\t'index' => [\n\t\t'customerdetails' => 'Dettagli Cliente',\n\t\t'accountdetails' => 'Dettagli Account',\n\t],\n\t'logger' => [\n\t\t'date' => 'Data',\n\t\t'type' => 'Tipo',\n\t\t'action' => 'Azione',\n\t\t'user' => 'Utente',\n\t\t'truncate' => 'Log vuoto',\n\t\t'reseller' => 'Rivenditore',\n\t\t'admin' => 'Amministratore',\n\t\t'cron' => 'Cronjob',\n\t\t'login' => 'Login',\n\t\t'intern' => 'Interno',\n\t\t'unknown' => 'Sconosciuto',\n\t],\n\t'login' => [\n\t\t'username' => 'Nome Utente',\n\t\t'password' => 'Password',\n\t\t'language' => 'Lingua',\n\t\t'login' => 'Login',\n\t\t'logout' => 'Logout',\n\t\t'profile_lng' => 'Scegli la lingua',\n\t\t'forgotpwd' => 'Dimenticato la password?',\n\t\t'presend' => 'Reimposta la password',\n\t\t'email' => 'Indirizzo E-mail',\n\t\t'remind' => 'Reimposta la mia password',\n\t\t'usernotfound' => 'Utente non trovata!',\n\t\t'backtologin' => 'Torna al login',\n\t\t'combination_not_found' => 'Combinazione utente-indirizzo email non trovata.',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Salve,\\\\n\\\\nil tuo indirizzo Email {EMAIL}\\\\nè stato configurato con successo.\\\\n\\\\nQuesta è un\\'Email creata automaticamente,\\\\n per favore non rispondere!\\\\n\\\\nCordiali saluti, Amministratore.',\n\t\t\t'subject' => 'Indirizzo Email configurato con successo',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Salve {FIRSTNAME} {NAME},\\\\n\\\\nqueste sono le informazioni per il tuo account:\\\\n\\\\nNome Utente: {USERNAME}\\\\nPassword: {PASSWORD}\\\\n\\\\nGrazie,\\\\nAmministratore.',\n\t\t\t'subject' => 'Informazioni account',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Salve,\\\\n\\\\nil tuo account email {EMAIL}\\\\nè stato creato correttamente.\\\\nLa tua password è {PASSWORD}.\\\\n\\\\nQuesta è un\\'email creata automaticamente,\\\\n si prega di non rispondere a questa email!\\\\n\\\\nCordiali Saluti, Amministratore.',\n\t\t\t'subject' => 'Account email creato correttamente',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Reimposta password',\n\t\t\t'mailbody' => 'Salve {USERNAME},\\\\n\\\\nla tua password froxlor è stata reimpostata!\\\\nLa nuova password è: {LINK}\\\\n\\\\nGrazie,\\\\n Team froxlor',\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] Nuovo database creato',\n\t\t\t'mailbody' => 'Hello {CUST_NAME},\n\nhai appena aggiunto un nuovo database. Ecco le informazioni inserite:\n\nNome database: {DB_NAME}\nPassword: {DB_PASS}\nDescrizione: {DB_DESC}\nDB-Hostname: {DB_SRV}\n{PMA_URI}\nCordiali Saluti, Team froxlor',\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'Nuovo utente ftp creato',\n\t\t\t'mailbody' => 'Salve {CUST_NAME},\n\nhai appena aggiunto un nuovo utente ftp. Ecco le informazioni inserite:\n\nNome utente: {USR_NAME}\nPassword: {USR_PASS}\nPercorso: {USR_PATH}\n\nCordiali Saluti, Team froxlor',\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => 'Salve {NAME},\\\\n\\\\nhai utilizzato {TRAFFICUSED} MB di {TRAFFIC} MB traffico disponibile.\\\\nQuesto è più del {MAX_PERCENT}%%.\\\\n\\\\nCordiali Saluti, il Team froxlor',\n\t\t\t'subject' => 'Raggiunto il limite di traffico',\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => 'Salve {NAME},\\\\n\\\\nhai utilizzato {DISKUSED} MB di {DISKAVAILABLE} MB di spazio disponibile.\\\\nQuesto è più del {MAX_PERCENT}%%.\\\\n\\\\nCordiali Saluti, il Team froxlor',\n\t\t\t'subject' => 'Raggiungere il limite di spazio su disco',\n\t\t],\n\t],\n\t'menu' => [\n\t\t'message' => 'Messaggi',\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Principale',\n\t\t\t'changepassword' => 'Cambia la password',\n\t\t\t'changelanguage' => 'Cambia la lingua',\n\t\t\t'username' => 'Utente: ',\n\t\t\t'changetheme' => 'Cambia tema',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'Email',\n\t\t\t'emails' => 'Indirizzi',\n\t\t\t'webmail' => 'WebMail',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Database',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domini',\n\t\t\t'settings' => 'Opzioni',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Account',\n\t\t\t'webftp' => 'WebFTP',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extra',\n\t\t\t'directoryprotection' => 'Cartelle Protette',\n\t\t\t'pathoptions' => 'Opzioni Cartelle',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Traffico',\n\t\t\t'current' => 'Mese corrente',\n\t\t\t'table' => 'Traffico',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'Configurazioni PHP',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'Log di Sistema',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'Nessuna e-mail è stata inviata perch¸ non ci sono i destinatari nel database',\n\t\t'success' => 'Inviato correttamente il messaggio a %s recipients',\n\t],\n\t'mysql' => [\n\t\t'description' => 'Qui puoi creare e modificare il tuo database MySQL<br />Le modifiche sono istantanee e puoi usare subito il database.<br />Nel menù a sinistra trovi phpMyAdmin con cui puoi amministrare il tuo database.<br /><br />Per usare i database nei tuoi script php usa le seguenti impostazioni: (Le parole in <i>corsivo</i> devono essere modificate con quello che hai scritto!)<br />Hostname: <b><SQL_HOST></b><br />Utente: <b><i>Nome database</i></b><br />Password: <b><i>La password che hai scelto</i></b><br />Database: <b><i>Nome database</i></b>',\n\t\t'databasename' => 'Nome database',\n\t\t'databasedescription' => 'Descrizione database',\n\t\t'database_create' => 'Crea database',\n\t\t'mysql_server' => 'Server MySQL',\n\t\t'database_edit' => 'Modifica database',\n\t\t'size' => 'Dimensione',\n\t],\n\t'panel' => [\n\t\t'edit' => 'Modifica',\n\t\t'delete' => 'Cancella',\n\t\t'create' => 'Crea',\n\t\t'save' => 'Salva',\n\t\t'yes' => 'Si',\n\t\t'no' => 'No',\n\t\t'emptyfornochanges' => 'lasciare vuoto se non si vuole cambiare',\n\t\t'emptyfordefault' => 'lasciare vuoto per l\\'impostazione di default',\n\t\t'path' => 'Percorso',\n\t\t'toggle' => 'Cambia',\n\t\t'next' => 'Prossimo',\n\t\t'dirsmissing' => 'La cartella fornita non è stata trovata.',\n\t\t'urloverridespath' => 'URL (sovrascrive il percorso)',\n\t\t'pathorurl' => 'Percorso o URL',\n\t\t'ascending' => 'ascendente',\n\t\t'descending' => 'discendente',\n\t\t'search' => 'Cerca',\n\t\t'used' => 'utilizzato',\n\t\t'translator' => 'Traduttore',\n\t\t'reset' => 'Annulla le modifiche',\n\t\t'pathDescription' => 'Se la cartella non esiste, viene creata automaticamente.',\n\t\t'back' => 'Indietro',\n\t\t'reseller' => 'rivenditore',\n\t\t'admin' => 'amministratore',\n\t\t'customer' => 'cliente/i',\n\t\t'send' => 'invia',\n\t\t'nosslipsavailable' => 'Attualmente non ci sono combinazioni ssl ip/porta per questo server',\n\t\t'backtooverview' => 'Ritorna alla pagina precedente',\n\t\t'dateformat' => 'DD-MM-YYYY',\n\t\t'dateformat_function' => 'd-m-Y',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Predefinito',\n\t\t'never' => 'Mai',\n\t\t'active' => 'Attivo',\n\t\t'please_choose' => 'Scegli',\n\t\t'allow_modifications' => 'Permetti modifiche',\n\t\t'megabyte' => 'MegaByte',\n\t\t'not_supported' => 'Non supportato in: ',\n\t\t'view' => 'view',\n\t\t'toomanydirs' => 'Troppe sottocartelle. Rifare tornando indietro nella selezione manuale della cartella.',\n\t\t'abort' => 'Termina',\n\t\t'not_activated' => 'non attivato',\n\t\t'off' => 'off',\n\t\t'options' => 'opzioni',\n\t\t'neverloggedin' => 'Nessun login effettuato',\n\t\t'descriptionerrordocument' => 'Può essere un URL, un percorso ad un file o solo una stringa con un \" \"<br />Lasciare vuoto per usare il valore di default del server.',\n\t\t'unlock' => 'unlock',\n\t\t'theme' => 'Tema',\n\t\t'variable' => 'Variabile',\n\t\t'description' => 'Descrizione',\n\t\t'pathDescriptionEx' => '<br /><br />Se vuoi redirezionare ad un altro dominio, questo valore deve iniziare con http:// or https://.',\n\t\t'pathDescriptionSubdomain' => 'Se la cartella non esiste, viene creata automaticamente.<br /><br />Se vuoi redirezionare ad un altro dominio, questo valore deve iniziare con http:// or https://.<br /><br />Se la URL termina con / è considerata una cartella, altrimenti verrà trattata come un file.',\n\t\t'cancel' => 'Annulla',\n\t\t'ssleditor' => 'Impostazioni SSL per questo dominio',\n\t\t'dashboard' => 'Cruscotto',\n\t\t'assigned' => 'Assegnato',\n\t\t'available' => 'Disponibile',\n\t\t'news' => 'Notizie',\n\t\t'ftpdesc' => 'Descrizione FTP',\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => 'Utente locale per PHP-FPM (froxlor vHost)',\n\t\t'vhost_httpgroup' => 'Gruppo locale per PHP-FPM (froxlor vHost)',\n\t\t'ownvhost' => [\n\t\t\t'title' => 'Abilita PHP-FPM per i vHost froxlor',\n\t\t\t'description' => 'Se abilitato, froxlor potrà essere avviato attraverso un utente locale',\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => 'Usa mod_proxy / mod_proxy_fcgi',\n\t\t\t'description' => 'Attiva l\\'utilizzo di php-fpm attraverso mod_proxy_fcgi. Richiede almeno apache-2.4.9',\n\t\t],\n\t],\n\t'pwdreminder' => [\n\t\t'success' => 'La password è stata reimpostata con successo.<br />A questo punto riceverai una email con la nuova password.',\n\t\t'notallowed' => 'Il reimposta password è disabilitato',\n\t\t'changed' => 'La tua password è stata aggiornata con successo. Puoi accedere con le tue nuove credenziali.',\n\t\t'wrongcode' => 'Ci dispiace, il tuo codice di attivazione non esiste o è già scaduto.',\n\t\t'choosenew' => 'Setta la nuova password',\n\t],\n\t'question' => [\n\t\t'question' => 'Domanda di sicurezza',\n\t\t'admin_customer_reallydelete' => 'Sei sicuro di voler cancellare il cliente %s? Quest\\'azione non potrà essere annullata!',\n\t\t'admin_domain_reallydelete' => 'Sei sicuro di voler cancellare il dominio %s?',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Sei sicuro di voler disattivare queste opzioni di sicurezza (OpenBasedir)?',\n\t\t'admin_admin_reallydelete' => 'Sei sicuro di voler cancellare l\\'admin %s? Tutti i clienti e i domini saranno affidati all\\'amministratore principale.',\n\t\t'admin_template_reallydelete' => 'Sei sicuro di voler cancellare il template \\'%s\\'?',\n\t\t'domains_reallydelete' => 'Sei sicuro di voler cancellare il dominio %s?',\n\t\t'email_reallydelete' => 'Sei sicuro di voler cancellare l\\'indirizzo Email %s?',\n\t\t'email_reallydelete_account' => 'Sei sicuro di voler cancellare l\\'account Email di %s?',\n\t\t'email_reallydelete_forwarder' => 'Sei sicuro di voler cancellare il reindirizzamento a %s?',\n\t\t'extras_reallydelete' => 'Sei sicuro di voler cancellare la protezione per la cartella %s?',\n\t\t'extras_reallydelete_pathoptions' => 'Sei sicuro di voler cancellare le opzioni cartella per %s?',\n\t\t'ftp_reallydelete' => 'Sei sicuro di voler cancellare l\\'account FTP %s?',\n\t\t'mysql_reallydelete' => 'Sei sicuro di voler cancellare il database %s? Quest\\'azione non potrà essere annullata!',\n\t\t'admin_configs_reallyrebuild' => 'Sei sicuro di voler rigenerare i file di configurazione per Apache e Bind?',\n\t\t'admin_customer_alsoremovefiles' => 'Cancellare anche i file dell\\'utente?',\n\t\t'admin_ip_reallydelete' => 'Vuoi veramente eliminare l\\'indirizzo IP %s?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Sei sicuro di volere la cartella base dei dati web di questo dominio al di fuori  della cartella base del cliente?',\n\t\t'admin_counters_reallyupdate' => 'Sei sicuro di voler ricacolare il consumo delle risorse?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Sei sicuro di voler cancellare tutte le password in chiaro degli account email dalla tabella mail_users? Attenzione non si può tornare indietro!',\n\t\t'logger_reallytruncate' => 'Sei sicuro di voler troncare la tabella \"%s\"?',\n\t\t'admin_quotas_reallywipe' => 'Sei sicuro di voler cancellare tutti i limiti dalla tabella mail_users? Questa operazione non può essere annullata!',\n\t\t'admin_quotas_reallyenforce' => 'Sei sicuro di voler impostare il limite predefinito a tutti gli utenti? Questa operazione non può essere annullata!',\n\t\t'phpsetting_reallydelete' => 'Do you really want to delete these settings? All domains which use these settings currently will be changed to the default config.',\n\t\t'customer_reallyunlock' => 'Sei sicuro di voler sbloccare il cliente %s?',\n\t\t'admin_customer_alsoremovemail' => 'Eliminare completamente i dati della posta elettronica dal filesystem??',\n\t\t'admin_customer_alsoremoveftphomedir' => 'Rimuovere anche la cartella homedir dell\\'utente FTP?',\n\t\t'admin_integritycheck_reallyfix' => 'Vuoi veramente provare a risolvere i problemi di integrità del database automaticamente?',\n\t],\n\t'redirect_desc' => [\n\t\t'rc_default' => 'Predefinito',\n\t\t'rc_movedperm' => 'spostato in modo permanente',\n\t\t'rc_found' => 'trovato',\n\t\t'rc_seeother' => 'vedi gli altri',\n\t\t'rc_tempred' => 'reindirizzamento temporaneo',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Timeout della sessione',\n\t\t\t'description' => 'Quanto tempo un utente deve rimanere inattivo prima che la sessione diventi invalida (secondi)?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Prefisso Cliente',\n\t\t\t'description' => 'Che prefisso dovrebbero avere gli account dei clienti?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'Prefisso SQL',\n\t\t\t'description' => 'Che prefisso dovrebbero avere i database SQL?',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'Prefisso FTP',\n\t\t\t'description' => 'Che prefisso vuoi che per gli account ftp?<br/><b>Se si modifica questo devi anche modificare il limite (Quota) della query SQL nel file di configurazione del server FTP nel caso in cui venga usata!</b> ',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Cartella dati web',\n\t\t\t'description' => 'Dove devono essere immagazzinati tutti i dati web?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Cartella logfiles',\n\t\t\t'description' => 'Dove devono essere immagazzinati tutti i log?',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'Indirizzo IP',\n\t\t\t'description' => 'Qual\\'è l\\'indirizzo IP di questo server?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Hostname',\n\t\t\t'description' => 'QUal\\'è l\\'hostname di questo server?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Comando riavvio Apache',\n\t\t\t'description' => 'Qual\\'è il comando per riavviare Apache?',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Cartella configurazione Bind',\n\t\t\t'description' => 'Dove sono i file di configurazione per Bind?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Comando riavvio Bind',\n\t\t\t'description' => 'Qual\\'è il comando per riavviare Bind?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'UID Email',\n\t\t\t'description' => 'Che UserID dovrebbe avere l\\'utente che gestisce le Email?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'GID Email',\n\t\t\t'description' => 'Che GroupID dovrebbe avere l\\'utente che gestisce le Email?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Cartella Email',\n\t\t\t'description' => 'Dove devono essere immagazzinate tutte le Email?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Mittente',\n\t\t\t'description' => 'Qual\\'è l\\'indirizzo del mittente delle Email provenienti dal pannello?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'URL phpMyAdmin',\n\t\t\t'description' => 'Qual\\'è l\\'URL di phpMyAdmin? (deve cominciare per http://)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'URL WebMail',\n\t\t\t'description' => 'Qual\\'è l\\'URL della WebMail? (deve cominciare per http://)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'URL WebFTP',\n\t\t\t'description' => 'Qual\\'è l\\'URL del WebFTP? (deve cominciare per http://)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Qual\\'è la lingua standard del tuo server?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Numero massimo tentativi login',\n\t\t\t'description' => 'Numero massimo di tentativi di login prima che l\\'account sia disattivato.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Durata disattivamento',\n\t\t\t'description' => 'Tempo (sec.) di disattivazione dell\\'account dopo troppi tentativi di login.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Modalità di scelta percorsi/cartelle',\n\t\t\t'description' => 'Un percorso/cartella andrà scelto attraverso un menu a tendina o inserendolo a mano?',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Nameservers',\n\t\t\t'description' => 'Lista degli hostname (separati dalla virgola) di tutti i nameserver. Il primo della lista sarà il impostato come primario.',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'MX servers',\n\t\t\t'description' => 'Lista dei server mx (separati dalla virgola) numero spazio hostname (es. \\'10 mx.example.com\\').',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Elementi da visualizzare per pagina',\n\t\t\t'description' => 'Quanti elementi dovrebbero essere visualizzati su una pagina? (0 = disattiva impaginazione)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'IP/Porta default',\n\t\t\t'description' => 'Qual\\'è la combinazione IP/Porta default?',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Percoso da aggiungere a OpenBasedir',\n\t\t\t'description' => 'Questi percorsi (separati da colonne) verranno aggiunti allo statement OpenBasedir in ognuno vhost-container.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Usa l\\'ordinamento naturale in vista elenco',\n\t\t\t'description' => 'Disponi la lista come web1 -> web2 -> web11 al posto di web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Docroot per gli utenti disattivati',\n\t\t\t'description' => 'Quando un utente viene disattivato questo percorso viene usato come suo docroot. Lascia vuoto per non creare un vhost a tutti.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Salva le password in chiaro degli account email nel database',\n\t\t\t'description' => 'Se impostato a Si, tutte le password saranno salvate in chiaro (saranno leggibili a chiunque abbia accesso al database) nella tabella mail_users. Attiva questa opzione solo se necessaria!',\n\t\t\t'removelink' => 'Clicca qui per cancellare tutte le password in chiaro dalla tabella.',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'Account FTP @domain',\n\t\t\t'description' => 'I Clienti possono creare account ftp utente@dominiocliente?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Includi PHP via mod_fcgid/suexec',\n\t\t\t'description' => 'Usa mod_fcgid/suexec/libnss_mysql per avviare PHP con il corrispondente account-utente.<br/><b>Questo richiede una speciale configurazione del Webserver. Tutte le opzioni seguenti sono validi solo se il modulo è abilitato.</b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Cartella della configurazione',\n\t\t\t\t'description' => 'Dove vuoi che venga salvata la configurazione di fcgid? Se non ti sei compilato suexec da solo, di solito questo percorso è /var/www',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Cartella Temp',\n\t\t\t\t'description' => 'Dove va salvata la cartella temp',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Processi per Dominio',\n\t\t\t\t'description' => 'Quanti processi dovrebbero essere avviati/permessi per ogni dominio? Il valore 0 è raccomandato poichè PHP si autogestisce i processi in modo molto efficiente.',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper in Vhosts',\n\t\t\t\t'description' => 'Come dovrebbe essere il wrapper incluso nel Vhosts',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Cartella globale di PEAR',\n\t\t\t\t'description' => 'Quali sono le cartelle globali di PEAR che dovrebbero essere sostituite in ogni configurazione php.ini? Più cartelle devono essere separate da : (due punti).',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Richieste massime per dominio',\n\t\t\t\t'description' => 'Quante richieste dovrebbero essere permesse per dominio?',\n\t\t\t],\n\t\t\t'defaultini' => 'Configurazione di default di PHP per i nuovi domini',\n\t\t\t'defaultini_ownvhost' => 'Configurazione di Default di PHP per froxlor-vhost',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Timeout Inattività',\n\t\t\t\t'description' => 'Impostazione Timeout per il Mod FastCGI.',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Usa un\\'indirizzo email alternativo',\n\t\t\t'description' => 'Invia la password dell\\'email a un\\'indirizzo diverso da quello creato',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'File/cartella della configurazione vhost del Webserver',\n\t\t\t'description' => 'Dove vuoi che venga salvata la configurazione vhost? Qui puoi scegliere un file (tutti i vhosts in un file) o una cartella (ogni vhost avrà il suo file).',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Webserver diroptions configuration file/dirname',\n\t\t\t'description' => 'Dove vuoi che venga salvata la configurazione dir-options? Qui puoi scegliere un file (tutti i vhosts in un file) o una cartella (ogni vhost avrà il suo file).',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Cartella htpasswd del Webserver',\n\t\t\t'description' => 'Dove vuoi che vengano salvati i file htpasswd per la protezione delle cartelle?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'description' => 'Lista degli host (separati da una virgola) a cui gli utenti possono collegarsi al server MySQL.',\n\t\t\t'title' => 'Hosts Accesso MySQL',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'description' => 'Verbosità del programma webalizer',\n\t\t\t'title' => 'Webalizer output',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Abilita/Disabilita Log',\n\t\t\t'severity' => 'Livello Log',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Tipo di Log',\n\t\t\t\t'description' => 'Specificare tipo di Log. Per selezionare più tipi, tenere premuto CTRL durante la selezione.<br />Tipi di log disponibili: syslog, file, mysql',\n\t\t\t],\n\t\t\t'logfile' => 'Percorso completo e nome del file del Log',\n\t\t\t'logcron' => 'Log cronjobs (one run)',\n\t\t],\n\t\t'ssl' => [\n\t\t\t'openssl_cnf' => 'Defaults per la creazione del file Cert',\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => 'Abilita utilizzo SSL',\n\t\t\t\t'description' => 'Spunta questo se vuoi usare SSL per il tuo server web',\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'Percorso al certificato SSL',\n\t\t\t\t'description' => 'Specifica il percorso includendo il nome del file .crt o .pem (certificato principale)',\n\t\t\t],\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'Percorso al file di chiave SSL',\n\t\t\t\t'description' => 'Specifica il percorso includendo il nome del file per la chiave privata (abitualmente.key)',\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'Percorso al certificato della CA (autoritá certificatrice) SSL (opzionale)',\n\t\t\t\t'description' => 'Autenticazione client, da settare soltanto se desiderato.',\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => 'Configura le cifrature SSL permesse',\n\t\t\t\t'description' => 'Questa è una lista di cifrature che vuoi (o non vuoi) usare nelle communicazioni SSL. Per una lista delle cifrature e come includerle od escluderle, vedi le sezioni \"CIPHER LIST FORMAT\" e \"CIPHER STRINGS\" sulla <a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">man-page per le cifrature</a>.<br /><br /><b>Il valore predefinito è:</b><pre>ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH</pre>',\n\t\t\t],\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Impostazioni default vhost',\n\t\t\t'description' => 'Il contenuto di questo campo sarà incluso direttamente nel contenitore di dominio vhost. Attenzione: Il codice non sarà controllato per eventuali errori. Se contiene errori, il webserver non riavviarsi correttamente!',\n\t\t],\n\t\t'decimal_places' => 'Numero di cifre decimali del traffico/spazio web in uscita',\n\t\t'webalizer_enabled' => 'Abilita le statistiche webalizer',\n\t\t'awstats_enabled' => 'Abilita le statistiche awstats',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Impostazioni dominio dns del cliente',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Consenti ai clienti di modificare le impostazioni DNS del dominio',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Usa nomi utente UNIX compatibile',\n\t\t\t'description' => 'Consente di utilizzare <strong>-</strong> e <strong>_</strong> nei nomi utente se <strong>No</strong>',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Consenti ai clienti di reimpostare la password',\n\t\t\t'description' => 'I clienti possono reimpostare la propria password e una nuova password verrà inviata al loro indirizzo e-mail',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Consenti di reimpostare la password agli ammministratori',\n\t\t\t'description' => 'Amministratori/rivenditori possono reimpostare la propria password e una nuova password verrà inviata al loro indirizzo e-mail',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Limite casella email',\n\t\t\t'description' => 'Limite predefinito per una nuova casella email creata (MegaByte).',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Usa limiti per le caselle email dei clienti',\n\t\t\t'description' => 'Attiva per utilizzare i limiti nelle caselle email. Predefinito <b>No</b> poich¸ questo richiede una configurazione speciale.',\n\t\t\t'removelink' => 'Clicca qui per togliere tutti i limiti dalle caselle email.',\n\t\t\t'enforcelink' => 'Clicca qui per applicare il limite predefinito a tutte le caselle email degli utenti.',\n\t\t],\n\t\t'index_file_extension' => [\n\t\t\t'description' => 'Quale estensione di file deve essere usata per il file index nelle cartelle del cliente appena creato? Questa estensione di file sarà utilizzata se Lei o uno dei vostri amministratori ha creato il proprio file modello di index.',\n\t\t\t'title' => 'Estensione file per il file index delle cartelle dei clienti appena creati',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Permettere login multipli',\n\t\t\t'description' => 'Se attivato un utente può accedere più volte.',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Permettere di spostare domini tra gli amministratori',\n\t\t\t'description' => 'Se attivato si può cambiare l\\'amministratore di un dominio dalle impostazioni del dominio<br /><b>Attenzione:</b> Se un cliente non viene assegnato allo stesso amministratore e dominio, l\\'amministratore può vedere tutti gli altri domini di questo cliente!',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Permetti di spostare domini tra i clienti',\n\t\t\t'description' => 'Se attivato si può cambiare il cliente di un dominio dalle impostazioni del dominio.<br /><b>Attenzione:</b> froxlor non cambierà alcun percorso. Ciò potrebbe rendere inutilizzabile un dominio!',\n\t\t],\n\t\t'cron' => [\n\t\t\t'debug' => [\n\t\t\t\t'title' => 'Debug Cronscript',\n\t\t\t\t'description' => 'Attiva per mantenere il file lock dopo l\\'avvio di cron, solo per debug<br /><b>Attenzione:</b>Mantenendo il file di lock al successivo avvio cron potrebbe non funzionare correttamente',\n\t\t\t],\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'Se si queste impostazioni personalizzate dei vhost saranno aggiunte a tutti sottodomini; se no le impostazioni speciali dei sottodomini verranno rimosse.',\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Lunghezza minima della password',\n\t\t\t'description' => 'Qui è possibile impostare una lunghezza minima per le password.\\'0\\' significa: nessuna lunghezza minima richiesta.',\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Salva il file index predefinito anche nelle nuove sottocartelle',\n\t\t\t'description' => 'Se abilitato, il file index predefinito viene memorizzato per ogni cartella sottodominio appena creata (non se la cartella esiste già!)',\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Indirizzo di Risposta',\n\t\t\t'description' => 'Definire un indirizzo email come \\'Indirizzo di Risposta\\' per le email inviate dal Pannello',\n\t\t],\n\t\t'adminmail_defname' => 'Nome del mittente del Pannello nell\\'email',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Sottodominio standard cliente',\n\t\t\t'description' => 'Quale hostname dovrebbe essere usato per creare sottodomini standard per i clienti. Se vuoto, viene utilizzato l\\'hostname del sistema.',\n\t\t],\n\t\t'awstats_path' => 'Percorso a AWStats \\'awstats_buildstaticpages.pl\\'',\n\t\t'awstats_conf' => 'Cartella della configurazione di AWStats',\n\t\t'defaultttl' => 'Dominio TTL per impegnare in secondi (predefinito \\'604800\\' = 1 settimana)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Abilita errordocuments in automatico per tutti i clienti',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'File/URL per l\\'errore 401',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'File/URL per l\\'errore 403',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'File/URL per l\\'errore 404',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'File/URL per l\\'errore 500',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'Se pureftpd è selezionato i file .ftpquota per i limiti dell\\'utente sono creati e aggiornati giornalmente',\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Permetti ai clienti le redirezioni',\n\t\t\t'description' => 'Consente ai clienti di scegliere il codice di stato http per le redirezioni che verranno utilizzate',\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Redirezione Predefinita',\n\t\t\t'description' => 'Imposta il codice della redirezione predefinito che dovrebbe essere usato se il cliente non lo imposta',\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Crea mail-, imap-, pop3- and smtp-\"A record\" anche con MX-Servers impostato',\n\t\t'froxlordirectlyviahostname' => 'Accedi direttamente a froxlor via hostname',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Espressione per le password',\n\t\t\t'description' => 'Qui è possibile impostare una espressione regolare per la complessità delle password.<br />Vuoto = nessun requisito specificato',\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'Abilita FCGID per i vhost froxlor',\n\t\t\t'description' => 'Se attivato, froxlor verrà eseguito con un utente locale<br /><strong>ATTENZIONE:</strong>Questo richiede una configurazione manuale, vedi <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - handbook</a>',\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'Abilita SuExec workaround (solo con Apache)',\n\t\t\t\t'description' => 'Abilita solo se la docroots del cliente non sono all\\'interno del percorso suexec di Apache<br />Se attivato, froxlor genererà un link simbolico dalla cartella perl abilitata dei clienti + /cgi-bin/ al percorso specificato.<br />Nota: Perl funziona solo nelle sottocartelle /cgi-bin/ e non nella cartella stessa (come farebbe normalmente senza questa correzione!)',\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Percorso dei link simbolici della cartella abilitata perl del cliente',\n\t\t\t\t'description' => 'Imposta questo solo se la soluzione SuExec è abilitata.<br />ATTENZIONE: Assicurati che questo percorso sia all\\'interno del percorso suexec oppure questa soluzione è inutile',\n\t\t\t],\n\t\t],\n\t\t'awstats_awstatspath' => 'Percorso AWStats \\'awstats.pl\\'',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'Percorso della cartella delle icone di AWstats',\n\t\t\t'description' => 'es. /usr/share/awstats/htdocs/icon/',\n\t\t],\n\t\t'login_domain_login' => 'Permetti il login con i domini',\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP backend',\n\t\t\t'description' => 'questo è dove in ascolto il processo PHP per le richieste da nginx, può essere un socket unix combinazione IP:Porta',\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'Comando riavvio PHP',\n\t\t\t'description' => 'questo viene utilizzato per ricaricare il backend PHP se è in uso<br />Predefinito: vuoto',\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'Cartella di configurazione php-fpm',\n\t\t\t'reload' => 'comando di riavvio php-fpm',\n\t\t\t'pm' => 'Gestore processi (pm)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'Il numero di processi figli',\n\t\t\t\t'description' => 'Il numero di processi figli che vengono creati quando il pm è impostato a  \\'static\\' e il numero massimo di processi figli che vengono creati quando il pm è impostato a \\'dynamic\\'<br />Equivalente a PHP_FCGI_CHILDREN',\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'Il numero di processi figli creati all\\'avvio',\n\t\t\t\t'description' => 'Nota: Usato solo quando il pm è impostato a \\'dynamic\\'',\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'Il numero minimo di processi inattivi nel server',\n\t\t\t\t'description' => 'Nota: Usato solo quando il pm è impostato a \\'dynamic\\'<br />Nota: Obbligatorio quando il pm è impostato a \\'dynamic\\'',\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'Il numero massimo di processi inattivi nel server',\n\t\t\t\t'description' => 'Nota: Usato solo quando il pm è impostato a \\'dynamic\\'<br />Nota: Obbligatorio quando il pm è impostato a \\'dynamic\\'',\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Richieste per figli prima di rigenerare',\n\t\t\t\t'description' => 'Per le richieste senza fine specificare \\'0\\'. Equivalente a PHP_FCGI_MAX_REQUESTS.',\n\t\t\t],\n\t\t\t'aliasconfigdir' => 'Configurazione cartella Alias per php-fpm',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Timeout Inattività',\n\t\t\t\t'description' => 'Impostazione Timeout per PHP5 FPM FastCGI.',\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'Cartella FastCGI IPC',\n\t\t\t\t'description' => 'La cartella nella quale verrano salvati i socket php-fpm dal server web.<br />Questa cartella deve essere leggibile dal server Web',\n\t\t\t],\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => 'Abilita l\\'invio dei report di utilizzo web e del traffico',\n\t\t\t'webmax' => 'Percentuale di avviso per lo spazio web',\n\t\t\t'trafficmax' => 'Percentuale di avviso per il traffico',\n\t\t],\n\t\t'dropdown' => 'Dropdown',\n\t\t'manual' => 'Manuale',\n\t\t'default_theme' => 'Tema predefinito',\n\t\t'validate_domain' => 'Convalida dei domini',\n\t\t'bindenable' => [\n\t\t\t'title' => 'Abilita Nameserver',\n\t\t\t'description' => 'Qui il Nameserver può essere abilitato e disabilitato globalmente.',\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => 'Il contenuto di questo campo verrà incluso direttamente nella configurazione del contenitore del dominio vHost. ATTENZIONE: Non verrano verificati eventuali errori del codice contenuto. Se conterrà degli errori, vi è il rischio che il server WEB non si avvii più!',\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => 'Abilita php-fpm',\n\t\t\t'description' => '<b>Questa impostazione richiede una configurazione speciale del server web. Vedi il <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">manuale PHP-FPM</a></b>',\n\t\t],\n\t\t'diskquota_enabled' => 'Quota attivita?',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'Percorso a repquota',\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'Percorso al quotatool',\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => 'Partizione, sulla quale sono salvati i dati dei clienti',\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'nome Maildir',\n\t\t\t'description' => 'cartella Maildir nell account utente. Normalmente \\'Maildir\\', in alcune implementazioni \\'.maildir\\', e direttamente nella cartella utente se lasciato vuoto.',\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => 'Usa Catchall',\n\t\t\t'description' => 'Vuoi offrire ai tuoi clienti la funzionalità di catchall?',\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => 'Usa impostazioni per Apache 2.4',\n\t\t\t'description' => '<strong class=\"text-danger\">ATTENZIONE:</strong> spunta soltanto se hai installato la versione 2.4 o superiore di Apache<br />altrimenti il tuo server Web non si avvierà',\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'Percorso al file fastcgi_params',\n\t\t\t'description' => 'Specifica il percorso al file fastcgi_params di nginx includendo il nome del file',\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => 'Usa il nome del dominio come valore predefinito per il percorso DocumentRoot (radice dei documenti)',\n\t\t\t'description' => 'Se abilitato ed il percorso radice DocumentRoot è vuoto, il valore predefinito sarà il nome del (sotto)dominio.<br /><br />Esempio: <br />/var/customers/nome_cliente/example.com/<br />/var/customers/nome_cliente/sottodominio.example.com/',\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => 'Nascondi i sottodominii predefiniti nel riepilogo di configurazione PHP',\n\t\t\t'description' => 'Se attivato i sottodomini predefiniti dei clienti non saranno visualizzati nel riepilogo della configurazione php<br /><br />Nota: Questo è solo visibile se avete abilitato FCGID o PHP-FPM',\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => 'Scegli quale metodo crittografico deve essere usato per le password',\n\t\t],\n\t\t'systemdefault' => 'Predefinito di sistema',\n\t\t'panel_allow_theme_change_admin' => 'Permetti agli amministratori di cambiare il tema',\n\t\t'panel_allow_theme_change_customer' => 'Permetti ai clienti di cambiare il tema',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'Server AXFR',\n\t\t\t'description' => 'Un elenco separato da virgole di indirizzi IP autorizzati a trasferire zone dns (AXFR).',\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Cartella dei certificati ssl clienti del Webserver',\n\t\t\t'description' => 'Dove devono esssere creati i certificati ssl cliente?<br /><br /><div class=\"text-danger\">NOTA: Il contenuto di questa cartella viene cancellato regolarmente, onde evitare il salvataggio manuale di dati in essa.</div>',\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => 'Permetti agli amministratori/rivenditori di inviare errori di database a froxlor',\n\t\t\t'description' => 'Attenzione: Non inviarci MAI dati personali (dei clienti)!',\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => 'Permetti ai clienti di inviare errori di database a froxlor',\n\t\t\t'description' => 'Attenzione: Non inviarci MAI dati personali (dei clienti)!',\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => 'Analizza traffico posta',\n\t\t\t'description' => 'Abilita l\\\\\\'analisi dei log del server di posta per calcolare il traffico',\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'tipo MDA',\n\t\t\t'description' => 'Tipo del Server di consegna di posta',\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'log MDA',\n\t\t\t'description' => 'File log del Server di consegna di posta',\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'tipo MTA',\n\t\t\t'description' => 'Tipo agente di trasferimento di posta',\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'log MTA',\n\t\t\t'description' => 'File log dell\\\\\\'agente di trasferimento di posta',\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'File di configurazione Cron',\n\t\t\t'description' => 'Percorso al file di configurazione del servizio cron. Questo file verrà aggiornato regolarmente ed automaticamente da froxlor.<br />\nNota: Perfavore <b>sii sicuro</b> di usare lo stesso nome di file come per il cronjob principale di froxlor (predefinito: /etc/cron.d/froxlor)!<br><br>Se usi <b>FreeBSD</b>, qui specifica: <i>/etc/crontab</i>!',\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Commando per riavviare il servizio Cron',\n\t\t\t'description' => 'Specifica il commando da eseguire per riavviare il servizio cron del tuo sistema',\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Commando di esecuzione Cron (binario php)',\n\t\t\t'description' => 'Commando per eseguire i nostri cronjob. Modificalo soltanto se sai cosa stai facendo (predefinito: \"/usr/bin/nice -n 5 /usr/bin/php -q\")!',\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => 'Permetti aggiornamenti automatici del database',\n\t\t\t'description' => '<div class=\"text-danger\"><b>ATTENZIONE:</b></div> Questa impostazione permette al cronjob di bypassare la verifica di versione dei file e database di froxlors ed esegue gli aggiornamenti di database in caso si verificasse un disallineamento di versione.<br><br><div class=\"text-danger\">l\\'aggiornamento automatico imposterà sempre i valori predefiniti per nuove impostazioni o modifiche. Questo, non sempre potrebbe essere congruo ed adeguato per il vostro sistema. Pensaci due volte prima di attivare questa opzione</div>',\n\t\t],\n\t\t'dns_createhostnameentry' => 'Crea la zone/config di bind per il nome host del sistema',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => 'Caratteri minuscoli',\n\t\t\t'description' => 'La Password deve contenere almeno una lettera minuscola (a-z).',\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => 'Caratteri maiuscoli',\n\t\t\t'description' => 'La Password deve contenere almeno una lettere maiuscola (A-Z).',\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => 'Numeri',\n\t\t\t'description' => 'La Password deve contenere almeno un numero (0-9).',\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => 'Caratteri speciali',\n\t\t\t'description' => 'La Password deve contenere almeno uno dei caratteri speciali definiti nel campo sottostante.',\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => 'Lista dei caratteri speciali',\n\t\t\t'description' => 'Uno di questi caratteri è richiesto se è attivata l\\'opzione soprastante.',\n\t\t],\n\t],\n\t'spf' => [\n\t\t'use_spf' => 'Attiva SPF per i domini?',\n\t\t'spf_entry' => 'Impostazioni SPF per tutti i domini',\n\t],\n\t'success' => [\n\t\t'success' => 'Informazioni',\n\t\t'clickheretocontinue' => 'Clicca qui per continuare',\n\t\t'settingssaved' => 'Le impostazioni sono state salvate con successo.',\n\t\t'rebuildingconfigs' => 'Inseriti con successo i lavori per la ricostruzione del file di configurazione',\n\t\t'domain_import_successfully' => 'Importato %s dominii con successo.',\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Processi Cron in sospeso',\n\t\t'REBUILD_VHOST' => 'Ricostruzione della configurazione del webserver',\n\t\t'CREATE_HOME' => 'Aggiunto il nuovo cliente %s',\n\t\t'REBUILD_DNS' => 'Ricostruzione della configurazione di bind',\n\t\t'CREATE_FTP' => 'Creazione delle cartelle per i nuovi utenti ftp',\n\t\t'DELETE_CUSTOMER_FILES' => 'Eliminazione dei file del cliente %s',\n\t\t'noneoutstanding' => 'Attualmente non ci sono processi in sospeso per froxlor',\n\t\t'CREATE_QUOTA' => 'Setta quota al filesystem',\n\t\t'DELETE_EMAIL_DATA' => 'Elimina i dati di posta elettronica del cliente.',\n\t\t'DELETE_FTP_DATA' => 'Elimina i dati account-ftp del cliente.',\n\t\t'REBUILD_CRON' => 'Ricostruisci il file cron.d',\n\t],\n\t'traffic' => [\n\t\t'month' => 'Mese',\n\t\t'day' => 'Giorno',\n\t\t'months' => [\n\t\t\t1 => 'Gennaio',\n\t\t\t2 => 'Febbraio',\n\t\t\t3 => 'Marzo',\n\t\t\t4 => 'Aprile',\n\t\t\t5 => 'Maggio',\n\t\t\t6 => 'Giugno',\n\t\t\t7 => 'Luglio',\n\t\t\t8 => 'Agosto',\n\t\t\t9 => 'Settembre',\n\t\t\t10 => 'Ottobre',\n\t\t\t11 => 'Novembre',\n\t\t\t12 => 'Dicembre',\n\t\t\t'jan' => 'Gen',\n\t\t\t'feb' => 'Feb',\n\t\t\t'mar' => 'Mar',\n\t\t\t'apr' => 'Apr',\n\t\t\t'may' => 'Mag',\n\t\t\t'jun' => 'Giu',\n\t\t\t'jul' => 'Lug',\n\t\t\t'aug' => 'Ago',\n\t\t\t'sep' => 'Set',\n\t\t\t'oct' => 'Ott',\n\t\t\t'nov' => 'Nov',\n\t\t\t'dec' => 'Dic',\n\t\t\t'total' => 'Totale',\n\t\t],\n\t\t'mb' => 'Traffico (MB)',\n\t\t'distribution' => '<font color=\"#019522\">FTP</font> | <font color=\"#0000FF\">HTTP</font> | <font color=\"#800000\">Mail</font>',\n\t\t'sumhttp' => 'Sommatoria Traffico in ingresso HTTP',\n\t\t'sumftp' => 'Sommatoria Traffico in ingresso FTP',\n\t\t'summail' => 'Sommatoria Traffico in ingresso Mail',\n\t\t'customer' => 'Cliente',\n\t\t'domain' => 'Domini',\n\t\t'trafficoverview' => 'Riepilogo del traffico di',\n\t\t'details' => 'Dettagli',\n\t\t'http' => 'HTTP (MiB)',\n\t\t'ftp' => 'FTP (MiB)',\n\t\t'mail' => 'Mail (MiB)',\n\t],\n\t'translator' => 'Luca Longinotti, Luca Piona, Emilien, Christian Munari',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'Una nuova versione di froxlor è stata installata ma non ancora impostata.<br />Solo l\\'amministratore può accedere e completare l\\'aggiornamento.',\n\t\t'update' => 'Aggiorna froxlor',\n\t\t'proceed' => 'Procedi',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'I file di froxlor sono stati aggiornati alla versione <strong>%s</strong>. La versione installata è <strong>%s</strong>.',\n\t\t\t'part_b' => '<br /><br />I clienti non potranno accedere fino a quando l\\'aggiornamento non sarà completato.<br /><strong>Procedere?</strong>',\n\t\t],\n\t\t'noupdatesavail' => '<strong>È già presente l\\'ultima versione di froxlor.</strong>',\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => 'Note personali',\n\t\t\t'description' => 'Sentiti libero di inserire qualsi nota vuoi o necessiti qui. Apparirano nel riepilogo dell\\'amministratore/cliente perl \\'utente corrispondente.',\n\t\t\t'show' => 'Mostra le tue note nel cruscotto dell\\'utente',\n\t\t],\n\t],\n];\n"
  },
  {
    "path": "lng/nl.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @author     Sander Klein <roedie@roedie.nl>\n * @author     Frits Letteboer <f.letteboer@radiotwenterand.nl>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'admin' => [\n\t\t'overview' => 'Overzicht',\n\t\t'ressourcedetails' => 'Gebruikte resources',\n\t\t'systemdetails' => 'Systeem Details',\n\t\t'installedversion' => 'Geïnstalleerde Versie',\n\t\t'latestversion' => 'Laatste Versie',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'zoeken via webservice',\n\t\t\t'error' => 'Fout tijdens lezen',\n\t\t],\n\t\t'customer' => 'Klant',\n\t\t'customers' => 'Klanten',\n\t\t'customer_add' => 'Maak klant',\n\t\t'customer_edit' => 'Bewerk klant',\n\t\t'domains' => 'Domeinen',\n\t\t'domain_add' => 'Maak domein',\n\t\t'domain_edit' => 'Bewerk domein',\n\t\t'subdomainforemail' => 'Subdomein als emaildomein',\n\t\t'admin' => 'Beheerder',\n\t\t'admins' => 'Beheerders',\n\t\t'admin_add' => 'Maak beheerder',\n\t\t'admin_edit' => 'Bewerk beheerder',\n\t\t'customers_see_all' => 'Kan alle klanten zien?',\n\t\t'change_serversettings' => 'Kan server instellingen aanpassen?',\n\t\t'serversettings' => 'Instellingen',\n\t\t'rebuildconf' => 'Configuratie bestanden opnieuw aanmaken',\n\t\t'stdsubdomain' => 'Standaard subdomein',\n\t\t'stdsubdomain_add' => 'Maak standard subdomein',\n\t\t'deactivated' => 'Gedeactiveerd',\n\t\t'deactivated_user' => 'Gebruiker deactiveren',\n\t\t'sendpassword' => 'Verstuur wachtwoord',\n\t\t'ownvhostsettings' => 'Eigen vHost-Instellingen',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Configuratie',\n\t\t\t'overview' => 'Overzicht',\n\t\t\t'wizard' => 'Wizard',\n\t\t\t'distribution' => 'Distributie',\n\t\t\t'service' => 'Dienst',\n\t\t\t'etc' => 'Overigen (Systeem)',\n\t\t\t'choosedistribution' => '-- Kies een distributie --',\n\t\t\t'chooseservice' => '-- Kies een dienst --',\n\t\t\t'choosedaemon' => '-- Kies een daemon --',\n\t\t\t'statistics' => 'Statistieken',\n\t\t\t'compactoverview' => 'Compacte weergave',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Sjablonen',\n\t\t\t'template_add' => 'Maak sjabloon',\n\t\t\t'template_edit' => 'Bewerk sjabloon',\n\t\t\t'action' => 'Actie',\n\t\t\t'email' => 'E-Mail',\n\t\t\t'subject' => 'Onderwerp',\n\t\t\t'mailbody' => 'Mail inhoud',\n\t\t\t'createcustomer' => 'Welkomst bericht voor nieuwe klanten',\n\t\t\t'pop_success' => 'Welkomst bericht voor e-mail nieuw account',\n\t\t\t'template_replace_vars' => 'Variabelen die aangepast worden in het sjabloon:',\n\t\t\t'FIRSTNAME' => 'Vervangen door de voornaam van de klant.',\n\t\t\t'NAME' => 'Vervangen door de naam van de klant.',\n\t\t\t'USERNAME' => 'Vervangen door de gebruikersnaam van de klant.',\n\t\t\t'PASSWORD' => 'Vervangen door het wachtwoord van de klant.',\n\t\t\t'EMAIL' => 'Vervangen door het adres van het POP3/IMAP account.',\n\t\t\t'TRAFFIC' => 'Wordt vervangen door aan klant toegewezen dataverkeer.',\n\t\t\t'TRAFFICUSED' => 'Wordt vervangen door het verbruikte dataverkeer.',\n\t\t\t'pop_success_alternative' => 'Welkomstmail voor nieuwe emailaccounts, gestuurd naar een alternatief emailadres',\n\t\t\t'EMAIL_PASSWORD' => 'Vervangen door het POP3/IMAP-wachtwoord.',\n\t\t\t'index_html' => 'Standaardpagina voor nieuwe mappen/domeinen',\n\t\t\t'SERVERNAME' => 'Wordt vervangen door de naam van de server.',\n\t\t\t'CUSTOMER' => 'Wordt vervangen door de inlognaam van de klant.',\n\t\t\t'ADMIN' => 'Wordt vervangen door de inlognaam van de beheerder.',\n\t\t\t'CUSTOMER_EMAIL' => 'Wordt vervangen door het e-mailadres van de klant.',\n\t\t\t'ADMIN_EMAIL' => 'Wordt vervangen door het e-mailadres van de beheerder.',\n\t\t\t'filetemplates' => 'Bestandssjablonen',\n\t\t\t'filecontent' => 'Bestandsinhoud',\n\t\t\t'new_database_by_customer' => 'Klantnotificatie wanneer een database is aangemaakt',\n\t\t\t'new_ftpaccount_by_customer' => 'Klantnotificatie wanneer een FTP-gebruiker is aangemaakt',\n\t\t\t'newdatabase' => 'Notificatie mails voor nieuwe databases',\n\t\t\t'newftpuser' => 'Notificatie mails voor nieuwe FTP-gebruikers',\n\t\t\t'CUST_NAME' => 'Naam klant',\n\t\t\t'DB_NAME' => 'Naam database',\n\t\t\t'DB_PASS' => 'Wachtwoord database',\n\t\t\t'DB_DESC' => 'Beschrijving database',\n\t\t\t'DB_SRV' => 'Database server',\n\t\t\t'PMA_URI' => 'URL naar phpMyAdmin (indien opgegeven)',\n\t\t\t'USR_NAME' => 'Gebruikersnaam FTP',\n\t\t\t'USR_PASS' => 'Wachtwoord FTP',\n\t\t\t'USR_PATH' => 'Map FTP (relatief aan docroot van klant)',\n\t\t\t'forgotpwd' => 'Notificatie mails voor opnieuw instellen wachtwoord',\n\t\t\t'password_reset' => 'Klantnotificatie voor opnieuw instellen wachtwoord',\n\t\t],\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IP-adressen en Poorten',\n\t\t\t'add' => 'Maak IP/Poort',\n\t\t\t'edit' => 'Bewerk IP/Poort',\n\t\t\t'ipandport' => 'IP/Poort',\n\t\t\t'ip' => 'IP',\n\t\t\t'port' => 'Poort',\n\t\t\t'create_listen_statement' => '\\'Listen\\'-regel genereren',\n\t\t\t'create_namevirtualhost_statement' => '\\'NameVirtualHost\\'-regel genereren',\n\t\t\t'create_vhostcontainer' => 'vHost-Container genereren',\n\t\t\t'create_vhostcontainer_servername_statement' => '\\'ServerName\\'-regel vHost-Container genereren',\n\t\t\t'enable_ssl' => 'Is dit een SSL-poort?',\n\t\t\t'ssl_cert_file' => 'Pad naar SSL-certificaat',\n\t\t\t'webserverdefaultconfig' => 'Standaarconfiguratie webserver',\n\t\t\t'webserverdomainconfig' => 'Domeinconfiguratie webserver',\n\t\t\t'webserverssldomainconfig' => 'SSL-configuratie webserver',\n\t\t\t'ssl_key_file' => 'Pad naar SSL keyfile',\n\t\t\t'ssl_ca_file' => 'Pad naar SSL CA certificaat',\n\t\t\t'default_vhostconf_domain' => 'Standaard VHost-instellingen voor iedere domeincontainer',\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Aangepaste docroot (leeg = verwijzing naar froxlor)',\n\t\t\t\t'description' => 'U kunt voor deze IP/poortcombinatie een aangepaste document-root opgeven.<br /><strong>LET OP:</strong> Pas op wat u hier neerzet!',\n\t\t\t],\n\t\t],\n\t\t'memorylimitdisabled' => 'Gedeactiveerd',\n\t\t'valuemandatory' => 'Deze waarde is verplicht',\n\t\t'valuemandatorycompany' => 'De waarde \"naam\" en \"voornaam\" of \"bedrijf\" moet ingevoerd worden',\n\t\t'phpversion' => 'PHP-Versie',\n\t\t'phpmemorylimit' => 'PHP-Geheugen-Limiet',\n\t\t'mysqlserverversion' => 'MySQL Server Versie',\n\t\t'mysqlclientversion' => 'MySQL Client Versie',\n\t\t'accountsettings' => 'Account-instellingen',\n\t\t'panelsettings' => 'Paneel-instellingen',\n\t\t'systemsettings' => 'Systeem-instellingen',\n\t\t'webserversettings' => 'Webserver-instellingen',\n\t\t'mailserversettings' => 'Mailserver-instellingen',\n\t\t'nameserversettings' => 'Nameserver-instellingen',\n\t\t'updatecounters' => 'Gebruikte bronnen herberekenen',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Nooit',\n\t\t\t'choosableno' => 'Kiesbaar, standaard nee',\n\t\t\t'choosableyes' => 'Kiesbaar, standaard ja',\n\t\t\t'always' => 'Altijd',\n\t\t],\n\t\t'webalizersettings' => 'Instellingen voor Webalizer',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normaal',\n\t\t\t'quiet' => 'Stil',\n\t\t\t'veryquiet' => 'Geen uitvoer',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Het is niet mogelijk een domein toe te voegen. U dient tenminste een klant aan te maken.',\n\t\t'loggersettings' => 'Instellingen voor logs',\n\t\t'logger' => [\n\t\t\t'normal' => 'normaal',\n\t\t\t'paranoid' => 'paranoide',\n\t\t],\n\t\t'emaildomain' => 'Emaildomein',\n\t\t'email_only' => 'Alleen email?',\n\t\t'wwwserveralias' => 'Voeg een \"www.\" ServerAlias toe',\n\t\t'subject' => 'Onderwerp',\n\t\t'recipient' => 'Ontvanger',\n\t\t'message' => 'Bericht schrijven',\n\t\t'text' => 'Bericht',\n\t\t'sslsettings' => 'Instellingen voor SSL',\n\t\t'dkimsettings' => 'Instellingen voor DomainKeys',\n\t\t'allips' => 'Alle IP\\'s',\n\t\t'awstatssettings' => 'Instellingen voor AWstats',\n\t\t'domain_dns_settings' => 'DNS-instellingen voor domein',\n\t\t'activated' => 'Geactiveerd',\n\t\t'statisticsettings' => 'Instellingen voor statistieken',\n\t\t'or' => 'of',\n\t\t'sysload' => 'Systeembelasting',\n\t\t'noloadavailable' => 'niet beschikbaar',\n\t\t'nouptimeavailable' => 'niet beschikbaar',\n\t\t'nosubject' => '(Geen onderwerp)',\n\t\t'security_settings' => 'Beveiliging',\n\t\t'know_what_youre_doing' => 'Verander dit alleen wanneer u zeker weet wat u doet!',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Toon versie van froxlor bij het inloggen',\n\t\t\t'description' => 'Toont de versie van froxlor in de voettekst van de inlogpagina',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Toon versie van froxlor in de voettekst',\n\t\t\t'description' => 'Toont de versie van froxlor in de voettekst op de rest van de pagina\\'s',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Kopgrafiek voor froxlor',\n\t\t\t'description' => 'Afbeelding die getoond wordt in de kop',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'PHP Configuratie',\n\t\t\t'description' => 'Korte omschrijven',\n\t\t\t'actions' => 'Actie\\'s',\n\t\t\t'activedomains' => 'Wordt gebruikt door domein(en)',\n\t\t\t'notused' => 'Configuratie niet in gebruik',\n\t\t\t'editsettings' => 'Instellingen voor PHP aanpassen',\n\t\t\t'addsettings' => 'Nieuwe instellingen voor PHP aanmaken',\n\t\t\t'viewsettings' => 'Instellingen voor PHP weergeven',\n\t\t\t'phpinisettings' => 'Instellingen in php.ini',\n\t\t\t'addnew' => 'Nieuwe instellingen aanmaken',\n\t\t\t'binary' => 'PHP Uitvoerbaar bestand',\n\t\t\t'file_extensions' => 'Bestandsextensies',\n\t\t\t'file_extensions_note' => '(zonder punt, gescheiden door spaties)',\n\t\t],\n\t\t'misc' => 'Diversen',\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'Variabelen die worden vervangen in de instellingen',\n\t\t\t'pear_dir' => 'Wordt vervangen door de globale pear-map.',\n\t\t\t'open_basedir_c' => 'Voegt een ; (puntkomma) toe om open_basedir in- of uit te schakelen',\n\t\t\t'open_basedir' => 'Wordt vervangen door de open_basedir-instellingen voor het domein.',\n\t\t\t'tmp_dir' => 'Wordt vervangen door de tijdelijke map voor dit domein',\n\t\t\t'open_basedir_global' => 'Wordt vervangen door de globale waarde van het pad dat wordt toegevoegd aan de open_basedir.',\n\t\t\t'customer_email' => 'Wordt vervangen door het e-mailadres van de klant van het domein.',\n\t\t\t'admin_email' => 'Wordt vervangen door het e-mailadres van de beheerder van het domein.',\n\t\t\t'domain' => 'Wordt vervangen door het domein.',\n\t\t\t'customer' => 'Wordt vervangen door de loginnaam van de eigenaar van het domein.',\n\t\t\t'admin' => 'Wordt vervangen door de loginnaam van de beheerder van het domein.',\n\t\t],\n\t\t'expert_settings' => 'Instellingen voor experts!',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'Aantal PHP-processen voor dit domein. (Leeg betekent standaardinstellingen.)',\n\t\t],\n\t\t'phpserversettings' => 'PHP Instellingen',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Maximaal aantal PHP verzoeken voor dit domein (leeg geldt als standaardwaarde)',\n\t\t],\n\t\t'spfsettings' => 'SPF-instellingen domein',\n\t\t'specialsettingsforsubdomains' => 'Speciale instellingen toepassen op alle subdomeinen (*.example.com)',\n\t\t'accountdata' => 'Accountgegevens',\n\t\t'contactdata' => 'Contactgegevens',\n\t\t'servicedata' => 'Ondersteuningsgegevens',\n\t\t'newerversionavailable' => 'Er is een nieuwe versie van froxlor beschikbaar',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Instellingen cron-taken',\n\t\t\t'add' => 'Cron-taak toevoegen',\n\t\t],\n\t\t'cronjob_edit' => 'Cron-taak aanpassen',\n\t\t'warning' => 'WAARSCHUWING - Let op!',\n\t\t'lastlogin_succ' => 'Laatste login',\n\t\t'ftpserver' => 'FTP Server',\n\t\t'ftpserversettings' => 'Instellingen FTP-server',\n\t\t'webserver_user' => 'Gebruikersnaam webserver',\n\t\t'webserver_group' => 'Groepnaam webserver',\n\t\t'perlenabled' => 'Perl ingeschakeld',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Lokale gebruiker voor FCGID (froxlor vhost)',\n\t\t'mod_fcgid_group' => 'Lokale groep voor FCGID (froxlor vhost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[niet opgegeven]',\n\t\t'store_defaultindex' => 'Standaard indexbestand opslaan in map klant',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Oud wachtwoord',\n\t\t'new_password' => 'Nieuw wachtwoord',\n\t\t'new_password_confirm' => 'Nieuw wachtwoord (bevestigen)',\n\t\t'new_password_ifnotempty' => 'Nieuw wachtwoord (leeg = niet veranderen)',\n\t\t'also_change_ftp' => ' wijzig ook het wachtwoord van het hoofd FTP account',\n\t\t'also_change_stats' => ' wijzig ook het wachtwoord van de statistieken',\n\t],\n\t'cron' => [\n\t\t'cronname' => 'naam cron-taak',\n\t\t'lastrun' => 'laatst uitgevoerd',\n\t\t'interval' => 'interval',\n\t\t'isactive' => 'actief',\n\t\t'description' => 'beschrijving',\n\t\t'changewarning' => 'Het aanpassen van de ze waarden kunnen van negatieve invloed zijn op het gedrag van froxlor en haar geautomatiseerde taken.<br />Pas deze waarden alleen aan wanneer u *zeer zeker* bent van wat u doet.',\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'geen beschrijving opgegeven',\n\t\t'cron_tasks' => 'aanmaken configuratiebestanden',\n\t\t'cron_legacy' => 'oude cron-taak',\n\t\t'cron_traffic' => 'berekenen verkeersgegevens',\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'Instellingen cron-taak',\n\t\t'cronjobinterval' => 'Interval uitvoeren',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Nog niet uitgevoerd',\n\t],\n\t'cronmgmt' => [\n\t\t'seconds' => 'seconden',\n\t\t'minutes' => 'minuten',\n\t\t'hours' => 'uren',\n\t\t'days' => 'days',\n\t\t'weeks' => 'weken',\n\t\t'months' => 'maanden',\n\t],\n\t'customer' => [\n\t\t'name' => 'Naam',\n\t\t'firstname' => 'Voornaam',\n\t\t'company' => 'Bedrijfsnaam',\n\t\t'street' => 'Straat',\n\t\t'zipcode' => 'Postcode',\n\t\t'city' => 'Plaats',\n\t\t'phone' => 'Telefoonnummer',\n\t\t'fax' => 'Faxnummer',\n\t\t'email' => 'Email',\n\t\t'customernumber' => 'Klant ID',\n\t\t'diskspace' => 'Webruimte (MB)',\n\t\t'traffic' => 'Verkeer (GB)',\n\t\t'emails' => 'E-mail-Adressen',\n\t\t'subdomains' => 'Sub-Domein(en)',\n\t\t'domains' => 'Domein(en)',\n\t\t'title' => 'Titel',\n\t\t'country' => 'Land',\n\t\t'email_quota' => 'quotem voor e-mail',\n\t\t'email_imap' => 'E-mail IMAP',\n\t\t'email_pop3' => 'E-mail POP3',\n\t\t'mail_quota' => 'Mailquotum',\n\t\t'sendinfomail' => 'Stuur gegevens naar mij via e-mail',\n\t],\n\t'dns' => [\n\t\t'destinationip' => 'IP domein',\n\t\t'standardip' => 'Standaard server IP',\n\t\t'a_record' => 'A-record (IPv6 optioneel)',\n\t\t'cname_record' => 'CNAME-record',\n\t\t'mxrecords' => 'MX-records',\n\t\t'standardmx' => 'Standaard server MX-record',\n\t\t'mxconfig' => 'Aangepaste MX-records',\n\t\t'priority10' => 'Prioriteit 10',\n\t\t'priority20' => 'Prioriteit 20',\n\t\t'txtrecords' => 'TXT-records',\n\t\t'txtexample' => 'Voorbeeld (SPF-regel):<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'OpenBasedir-pad',\n\t\t'docroot' => 'Pad van bovenstaand veld',\n\t\t'homedir' => 'Home directory',\n\t],\n\t'domains' => [\n\t\t'description' => 'Hier kunt u nieuwe (sub-) domeinen maken en de paden aanpassen.<br />Het systeem heeft een paar minuten nodig om de wijzigingen door te voeren na iedere varandering.',\n\t\t'domainsettings' => 'Domein instellingen',\n\t\t'domainname' => 'Domeinnaam',\n\t\t'subdomain_add' => 'Maak subdomein',\n\t\t'subdomain_edit' => 'Bewerk (sub)domein',\n\t\t'wildcarddomain' => 'Maak als wildcarddomein?',\n\t\t'aliasdomain' => 'Alias voor domein',\n\t\t'noaliasdomain' => 'Geen alias domein',\n\t\t'hasaliasdomains' => 'Heeft alias domein(en)',\n\t\t'statstics' => 'Gebruiksstatistieken',\n\t\t'isassigneddomain' => 'Is toegewezen domein',\n\t\t'add_date' => 'Toegevoegd aan froxlor',\n\t\t'registration_date' => 'Toegevoegd aan register',\n\t\t'topleveldomain' => 'Top-Level-Domein',\n\t\t'associated_with_domain' => 'Toegekend',\n\t\t'aliasdomains' => 'Alternatieve domeinnamen',\n\t\t'redirectifpathisurl' => 'Doorverwijzingscode (standaard: leegt)',\n\t\t'redirectifpathisurlinfo' => 'U dient deze alleen op te geven indien u een URL als pad hebt opgegeven',\n\t],\n\t'emails' => [\n\t\t'description' => 'Hier kunt u e-mail adressen maken en wijzigen.<br />Een account is net als een brievenbus voor uw huis. Als iemand u mail stuurt wordt dit op uw account bezorgd.<br /><br />Om uw emails te downloaden moet u het volgende instellen in uw mailprogramma: (De <i>schuingedrukte</i> gegevens moeten gewijzigd worden in wat u ingegeven heeft!)<br />Servernaam: <b><i>Domeinnaam</i></b><br />Gebruikersnaam: <b><i>Account naam / E-mailadres</i></b><br />Wachtwoord: <b><i>het door u ingegeven wachtwoord</i></b>',\n\t\t'emailaddress' => 'E-mailadres',\n\t\t'emails_add' => 'Maak nieuw e-mailadres',\n\t\t'emails_edit' => 'Bewerk e-mailadres',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => 'Definieer als catchall-adres?',\n\t\t'account_add' => 'Maak nieuw account',\n\t\t'account_delete' => 'Verwijder account',\n\t\t'from' => 'Van',\n\t\t'to' => 'Aan',\n\t\t'forwarder_add' => 'Maak forwarder',\n\t\t'alternative_emailaddress' => 'Alternatief emailadres',\n\t\t'quota' => 'Quotum',\n\t\t'noquota' => 'Geen quotum',\n\t\t'updatequota' => 'Quotum aanpassen',\n\t\t'quota_edit' => 'E-mailquotum aanpassen',\n\t\t'noemaildomainaddedyet' => 'U hebt nog geen (email-)domein gekoppeld aan uw account.',\n\t],\n\t'error' => [\n\t\t'error' => 'Fout',\n\t\t'directorymustexist' => 'De map %s bestaat niet. Maak hem eerst aan met uw FTP client.',\n\t\t'filemustexist' => 'Het bestand %s bestaat niet.',\n\t\t'allresourcesused' => 'U heeft al uw resources al gebruikt.',\n\t\t'domains_cantdeletemaindomain' => 'U kunt een domein dat gebruikt wordt als email-domein niet verwijderen.',\n\t\t'domains_canteditdomain' => 'U kunt dit domein niet aanpassen. Dit is door de admin onbruikbaar gemaakt.',\n\t\t'domains_cantdeletedomainwithemail' => 'U kunt een domein dat gebruikt wordt als email-domein niet verwijderen. Verwijder eerst alle e-mail adressen.',\n\t\t'firstdeleteallsubdomains' => 'U moet eerst alle subdomeinen verwijderen voor u een wildcard domein kunt maken.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'U heeft al een catchall voor dit domein aangemaakt.',\n\t\t'ftp_cantdeletemainaccount' => 'U kunt uw hoofd FTP account niet verwijderen',\n\t\t'login' => 'De door u ingegeven gebruikersnaam en wachtwoord zijn verkeerd. Probeer opnieuw!',\n\t\t'login_blocked' => 'Dit account is inactief vanwege teveel login fouten. <br />Probeer het nog eens over %s seconden.',\n\t\t'notallreqfieldsorerrors' => 'U heeft niet alle velden goed, of helemaal niet ingevuld.',\n\t\t'oldpasswordnotcorrect' => 'Het oude wachtwoord is niet correct.',\n\t\t'youcantallocatemorethanyouhave' => 'U kunt niet meer resources gebruiken dan die u bezit.',\n\t\t'mustbeurl' => 'U heeft geen goed of compleet URL ingegeven (bijv. http://eenserver.com/error404.htm)',\n\t\t'invalidpath' => 'U heeft geen goed URL ingegeven (misschien een probleem met dirlisting?)',\n\t\t'stringisempty' => 'Geen waarde in invoerveld',\n\t\t'stringiswrong' => 'Verkeerde waarde in invoerveld',\n\t\t'newpasswordconfirmerror' => 'Het nieuwe wachtwoord en de bevestiging zijn niet gelijk',\n\t\t'loginnameexists' => 'Loginnaam %s bestaat al',\n\t\t'emailiswrong' => 'E-mailadres %s bevat illegale karakters of is niet compleet',\n\t\t'loginnameiswrong' => 'Loginnaam %s bevat illegale karakters',\n\t\t'userpathcombinationdupe' => 'Combinatie van Gebruikersnaam en Pad bestaat reeds',\n\t\t'patherror' => 'Generale Fou! Pad kan niet leeg zijn',\n\t\t'errordocpathdupe' => 'Optie voor pad %s bestaat reeds',\n\t\t'adduserfirst' => 'Maak klant eerst aan, aub',\n\t\t'domainalreadyexists' => 'Het domein %s is al aan een klant toegewezen',\n\t\t'nolanguageselect' => 'Geen taal geselecteerd.',\n\t\t'nosubjectcreate' => 'U moet een onderwerp ingeven voor dit e-mail sjabloon.',\n\t\t'nomailbodycreate' => 'U moet een tekst ingeven voor dit e-mail sjabloon.',\n\t\t'templatenotfound' => 'Sjabloon niet gevonden.',\n\t\t'alltemplatesdefined' => 'U kunt niet meer sjablonen definiëren, alle talen worden al ondersteund.',\n\t\t'wwwnotallowed' => 'www is niet toegestaan voor subdomeinen.',\n\t\t'subdomainiswrong' => 'Het subdomein %s bevat illegale karakters.',\n\t\t'domaincantbeempty' => 'De domeinnaam kan niet leeg zijn.',\n\t\t'domainexistalready' => 'Het domein %s bestaat reeds.',\n\t\t'domainisaliasorothercustomer' => 'Het geselecteerde alias domein verwijst naar zichzelf of is van een andere gebruiker.',\n\t\t'emailexistalready' => 'Het e-mail adres %s bestaat reeds.',\n\t\t'maindomainnonexist' => 'Het hoofd-domein %s bestaat niet.',\n\t\t'destinationnonexist' => 'Maak uw forwarder in het veld \\'Destination\\' alstublieft.',\n\t\t'destinationalreadyexistasmail' => 'De forwarder naar %s bestaat reeds als actief e-mail adres.',\n\t\t'destinationalreadyexist' => 'U heeft al een forwarder die verwijst naar %s .',\n\t\t'destinationiswrong' => 'De forwarder naar %s bevat illegale karakter(s) of is niet compleet.',\n\t\t'ipstillhasdomains' => 'De IP/Port combinatie die u verwijderen wilt heeft nog domeinen toegewezen, wijs deze opnieuw to aan andere IP/Poort combinaties voordat u deze IP/Poort combinatie verwijderd.',\n\t\t'cantdeletedefaultip' => 'U kunt de standaard reseller IP/Poort combinatie niet verwijderen, maak eerst een andere IP/Port combinatie standaard voor reseller voor dat u deze IP/Port combinatie verwijderd.',\n\t\t'cantdeletesystemip' => 'U kunt het laatste IP/Poort combinatie van het systeem niet verwijderen, maak eerste een andere IP/Port combinatie aan voor het systeem of wijzig het IP-adres van het systeem.',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'Poort\\'',\n\t\t'myipdefault' => 'U moet een IP/Poort combinatie selecteren die standaard moet worden.',\n\t\t'myipnotdouble' => 'Deze IP/Poort combinatie bestaat reeds.',\n\t\t'cantchangesystemip' => 'U kunt het laatste system IP niet wijzigen, maak eerst een nieuwe IP/Poort combinatie aan of wijzig het ip-adres van het systeem.',\n\t\t'sessiontimeoutiswrong' => 'Alleen numerieke \"Session Timeout\" zijn toegestaan.',\n\t\t'maxloginattemptsiswrong' => 'Alleen numerieke \"Maximaal aantal inlogpogingen\" zijn toegestaan.',\n\t\t'deactivatetimiswrong' => 'Alleen numerieke \"Deactiveringstijd\" zijn toegestaan.',\n\t\t'accountprefixiswrong' => 'Het \"Klant voorvoegsel\" is verkeerd.',\n\t\t'mysqlprefixiswrong' => 'Het \"SQL voorvoegsel\" is verkeerd.',\n\t\t'ftpprefixiswrong' => 'Het \"FTP voorvoegsel\" is verkeerd.',\n\t\t'ipiswrong' => 'Het \"IP-Adres\" is verkeerd. Alleen een geldig ip-adres is toegestaan.',\n\t\t'vmailuidiswrong' => 'Het \"Mails-uid\" is verkeerd. Alleen een numeriek UID is toegestaan.',\n\t\t'vmailgidiswrong' => 'Het \"Mails-gid\" is verkeerd. Alleen een numeriek GID is toegestaan.',\n\t\t'adminmailiswrong' => 'Het \"Afzender-adres\" is verkeerd. Alleen geldige e-mail adressen zijn toegestaan.',\n\t\t'pagingiswrong' => 'Het aantal \"Vermeldingen per pagina\" is verkeerd. Alleen numerieke karakters zijn toegestaan.',\n\t\t'phpmyadminiswrong' => 'De phpMyAdmin-link is niet een geldige link.',\n\t\t'webmailiswrong' => 'De WebMail-link is niet een geldige link.',\n\t\t'webftpiswrong' => 'De WebFTP-link is niet een geldige link.',\n\t\t'stringformaterror' => 'De waarde voor het veld \"%s\" is niet in het verwachte formaat.',\n\t\t'loginnameissystemaccount' => 'U kunt geen accounts aanmaken die gelijk zijn aan systeem accounts (bijvoorbeeld beginnend met \"%s\"). Kies een andere accountnaam aub.',\n\t\t'youcantdeleteyourself' => 'U kunt uw eigen account, omwille van veiligheidsredenen, niet verwijderen.',\n\t\t'youcanteditallfieldsofyourself' => 'Opmerking: U kunt, om veiligheidsredenen, niet alle velden van uw account aanpassen.',\n\t\t'documentrootexists' => 'De map \"%s\" voor deze klant bestaat reeds. Verwijder deze map alvorens het account aan te maken.',\n\t\t'formtokencompromised' => 'Het verzoek lijkt te zijn gecompromitteerd. U bent veiligheidshalve uitgelogd.',\n\t\t'logerror' => 'Log-Fout: %s',\n\t\t'nomessagetosend' => 'U hebt geen bericht opgegeven.',\n\t\t'norecipientsgiven' => 'U hebt geen ontvanger opgegeven',\n\t\t'errorsendingmail' => 'Het versturen van het bericht naar \"%s\" is mislukt',\n\t\t'cannotreaddir' => 'De map \"%s\" kan niet gelezen worden',\n\t\t'invalidip' => '%s is een ongeldig IP-adres',\n\t\t'invalidmysqlhost' => 'Ongeldig adres voor MySQL-host: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'U kunt Webalizer en AWstats niet tegelijkertijd gebruiken. Kies een van de twee.',\n\t\t'cannotwritetologfile' => 'Kan logbestand %s niet openen om naartoe te schrijven',\n\t\t'vmailquotawrong' => 'Het quotum dient een positief getal te zijn.',\n\t\t'allocatetoomuchquota' => 'U probeerde %s MB Quotum toe te kennen, maar u heeft niet voldoende over.',\n\t\t'missingfields' => 'Niet alle vereiste velden zijn ingevuld.',\n\t\t'accountnotexisting' => 'Het opgegeven e-mailaccount bestaat niet.',\n\t\t'nopermissionsorinvalidid' => 'U hebt geen toestemming om deze instellingen te wijzigen, of u hebt een ongeldig ID opgegeven.',\n\t\t'phpsettingidwrong' => 'Een configuratie voor PHP met dit ID bestaat niet',\n\t\t'descriptioninvalid' => 'De omschrijving is te kort, te lang of bevat ongeldige karakters.',\n\t\t'info' => 'Informatie',\n\t\t'filecontentnotset' => 'Het bestand mag niet leeg zijn!',\n\t\t'index_file_extension' => 'Het achtervoegsel dient tussen de 1 en 6 tekens lang te zijn. Het mag alleen tekens zal a-z, A-Z en 0-9 bevatten.',\n\t\t'customerdoesntexist' => 'De gekozen klant bestaat niet.',\n\t\t'admindoesntexist' => 'De gekozen beheerder bestaat niet.',\n\t\t'ipportdoesntexist' => 'De kozen combinatie poort/IP-adres bestaat niet.',\n\t\t'admin_domain_emailsystemhostname' => 'De naam van de server kan niet gebruikt worden als domein voor e-mail.',\n\t\t'usernamealreadyexists' => 'De gebruikersnaam %s is reeds in gebruik.',\n\t\t'errorwhensaving' => 'Fout tijdens opslaan veld %s',\n\t\t'hiddenfieldvaluechanged' => 'De waarde van het verborgen veld \"%s\" is gewijzigd tijdens het aanpassen van de instellingen.<br /><br />Dit is normaliter geen groot probleem maar heeft wel verhinderd dat de instellingen niet zijn opgeslagen.',\n\t\t'notrequiredpasswordlength' => 'Het opgegeven wachtwoord is te kort. Geef tenminste %s tekens op.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Woops, a field that should be displayed as an option in the settings-overview is not an excepted type. You can blame the developers for this. This should not happen!',\n\t\t'pathmaynotcontaincolon' => 'Het opgegeven pad mag geen dubbele punt (\":\") bevatten. Geef een correct pad op.',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => 'Er is niet voldaan aan de complexiteit voor het wachtwoord (regex: %s)',\n\t\t'intvaluetoolow' => 'Het opgegeven nummer is te laag (veld %s)',\n\t\t'intvaluetoohigh' => 'Het opgegeven nummer is te hoog (veld %s)',\n\t\t'phpfpmstillenabled' => 'PHP-FPM is op dit moment actief. Schakel dit eerst uit voordat u FCGID inschakelt',\n\t\t'fcgidstillenabled' => 'FCGID is op dit moment actief. Schakel dit eerst uit voordat u PHP-FPM inschakelt',\n\t],\n\t'extras' => [\n\t\t'description' => 'Hier kunt u wat extra instellingen doen zoals map beveiliging.<br />Het systeem heeft enkele minuten nodig om elke wijziging door te voeren.',\n\t\t'directoryprotection_add' => 'Map beveiliging toevoegen',\n\t\t'view_directory' => 'map inhoud laten zien',\n\t\t'pathoptions_add' => 'Pad opties toevoegen',\n\t\t'directory_browsing' => 'map inhoud browsen',\n\t\t'pathoptions_edit' => 'Pad opties bewerken',\n\t\t'errordocument404path' => 'URL naar Foutdocument 404',\n\t\t'errordocument403path' => 'URL naar Foutdocument 403',\n\t\t'errordocument500path' => 'URL naar Foutdocument 500',\n\t\t'errordocument401path' => 'URL naar Foutducument 401',\n\t\t'execute_perl' => 'perl/CGI uitvoeren',\n\t\t'htpasswdauthname' => 'Reden voor authenticatie (AuthName)',\n\t\t'directoryprotection_edit' => 'mapbeveiliging aanpassen',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Hier kunt u nieuwe FTP accounts maken of bestaande accounts wijzigen.<br />De wijzigingen worden direct doorgevoerd en het account kan direct gebruikt worden.',\n\t\t'account_add' => 'Maak nieuw account',\n\t\t'account_edit' => 'FTP account aanpassen',\n\t\t'editpassdescription' => 'Nieuw wachtwoord of leeg voor het oude wachtwoord.',\n\t],\n\t'index' => [\n\t\t'customerdetails' => 'Klant Details',\n\t\t'accountdetails' => 'Account Details',\n\t],\n\t'logger' => [\n\t\t'date' => 'Datum',\n\t\t'type' => 'Type',\n\t\t'action' => 'Actie',\n\t\t'user' => 'Gebruiker',\n\t\t'truncate' => 'Log legen',\n\t],\n\t'login' => [\n\t\t'username' => 'Gebruikersnaam',\n\t\t'password' => 'Wachtwoord',\n\t\t'language' => 'Taal',\n\t\t'login' => 'Inloggen',\n\t\t'logout' => 'Uitloggen',\n\t\t'profile_lng' => 'Profiel taal',\n\t\t'forgotpwd' => 'Wachtwoord vergeten?',\n\t\t'presend' => 'Wachtwoord opnieuw instellen',\n\t\t'email' => 'E-mailadres',\n\t\t'remind' => 'Mijn wachtwoord opnieuw instellen',\n\t\t'usernotfound' => 'Gebruiker niet gevonden!',\n\t\t'backtologin' => 'Terug naar inlogpagina',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Hallo,\\\\n\\\\nUw mail account {EMAIL}\\\\nis succesvol aangemaakt.\\\\n\\\\nDit is een automatisch verstuurde\\\\ne-mail, beantwoord deze niet AUB!\\\\n\\\\nMet vriendelijke groet, uw beheerder',\n\t\t\t'subject' => 'Mail account succesvol aangemaakt',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Hallo {FIRSTNAME} {NAME},\\\\n\\\\nhierbij uw account informatie:\\\\n\\\\nGebruikersnaam: {USERNAME}\\\\nWachtwoord: {PASSWORD}\\\\n\\\\nMet vriendelijke groet,\\\\nuw beheerder',\n\t\t\t'subject' => 'Account informatie',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Hallo,\\\\n\\\\nuw mailaccount {EMAIL}\\\\nis met succes opgezet.\\\\nUw wachtwoord is {PASSWORD}.\\\\n\\\\nDit is een automatisch gegenereerde\\\\ne-mail, u kunt hierop niet antwoorden!\\\\n\\\\nMet vriendelijk groet, uw beheerder',\n\t\t\t'subject' => 'Mailaccount actief gemaakt',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Wachtwoord opnieuw instellen',\n\t\t\t'mailbody' => 'Hallo {USERNAME},\\\\n\\\\nuw wachtwoord voor froxlor is opnieuw ingesteld!\\\\nHet nieuwe wachtwoord is: {LINK}\\\\n\\\\nMet vriendelijke groet,\\\\nuw beheerder',\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] Nieuwe database aangemaakt',\n\t\t\t'mailbody' => 'Geachte {CUST_NAME},\n\nu hebt zojuist een nieuwe database aangemaakt. Hier zijn nogmaals de ingevoerde gegevens:\n\nNaam database: {DB_NAME}\nWachtwoord: {DB_PASS}\nBeschrijving: {DB_DESC}\nHostnaam database: {DB_SRV}\nphpMyAdmin: {PMA_URI}\nMet vriendelijke groet, uw beheerder',\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'Nieuwe FTP-gebruiker aangemaakt',\n\t\t\t'mailbody' => 'Geachte {CUST_NAME},\n\nu hebt zojuist een nieuwe FTP-gebruiker aangemaakt. Hier is de opgegeven informatie:\n\nGebruikersnaam: {USR_NAME}\nWachtwoord: {USR_PASS}\nPad: {USR_PATH}\n\nMet vriendelijke groet, uw beheerder',\n\t\t],\n\t],\n\t'menu' => [\n\t\t'message' => 'Berichten',\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Main',\n\t\t\t'changepassword' => 'Wijzig wachtwoord',\n\t\t\t'changelanguage' => 'Wijzig taal',\n\t\t\t'username' => 'Ingelogd als: ',\n\t\t],\n\t\t'email' => [\n\t\t\t'emails' => 'Adressen',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domeinen',\n\t\t\t'settings' => 'Instellingen',\n\t\t],\n\t\t'extras' => [\n\t\t\t'directoryprotection' => 'Map beveiliging',\n\t\t\t'pathoptions' => 'pad opties',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Dataverkeer',\n\t\t\t'current' => 'Deze maand',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'PHP Configuratie\\'s',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'Systeemlog',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'Er is geen email verstuurd omdat er geen ontvangers in de database zijn',\n\t\t'success' => 'Bericht verzonden naar ontvangers %s',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'gebruiker/database naam',\n\t\t'databasedescription' => 'database omschrijving',\n\t\t'database_create' => 'Maak database',\n\t\t'description' => 'Hier kunt u MySQL-Databases maken en wijzigen.<br />De wijzigingen worden direct gemaakt en de database kan direkt gebruikt worden.<br />In het menu dat links staat vind u de tool phpMyAdmin welke u kunt gebruiken om uw database makkelijk te beheren.<br /><br />Om gebruikt te maken van uw database in uw eigen php programmas kunt u de volgende instellingen gebruiken: (De gegeven in <i>italics</i> moeten aangepast worden in wat u ingevoerd heeft!)<br />Hostnaam: <b><SQL_HOST></b><br />Gebruikersnaam: <b><i>Databasenaam</i></b><br />Wachtwoord: <b><i>het wachtwoord dat u gekozen heeft</i></b><br />Database: <b><i>Databasenaam</i></b>',\n\t],\n\t'panel' => [\n\t\t'edit' => 'bewerken',\n\t\t'delete' => 'verwijderen',\n\t\t'create' => 'nieuw',\n\t\t'save' => 'opslaan',\n\t\t'yes' => 'ja',\n\t\t'no' => 'nee',\n\t\t'emptyfornochanges' => 'leeg laten voor huidige instelling',\n\t\t'emptyfordefault' => 'leeg laten voor de standaard instellingen',\n\t\t'path' => 'Pad',\n\t\t'toggle' => 'In- of uitschalen',\n\t\t'next' => 'volgende',\n\t\t'dirsmissing' => 'De opgegeven map bestaat niet.',\n\t\t'urloverridespath' => 'URL (Vervangt path)',\n\t\t'pathorurl' => 'Pad of URL',\n\t\t'ascending' => 'oplopend',\n\t\t'descending' => 'aflopend',\n\t\t'search' => 'Zoeken',\n\t\t'used' => 'gebruikt',\n\t\t'translator' => 'Vertaler',\n\t\t'reset' => 'wijzigingen verwerpen',\n\t\t'pathDescription' => 'Indien de map niet bestaat wordt deze automatisch aangemaakt.<br /><br />Indien u wilt doorverwijzen naar een ander domein dient deze te beginnen met http:// of https://',\n\t\t'back' => 'Back',\n\t\t'reseller' => 'wederverkoper',\n\t\t'admin' => 'beheerder',\n\t\t'customer' => 'klant(en)',\n\t\t'send' => 'verzenden',\n\t\t'nosslipsavailable' => 'Er zijn op dit moment geen SSL IP/poorten beschikbaar',\n\t\t'backtooverview' => 'Terug naar overzicht',\n\t\t'dateformat' => 'YYYY-MM-DD',\n\t\t'dateformat_function' => 'Y-m-d',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Standaard',\n\t\t'never' => 'Nooit',\n\t\t'active' => 'Actief',\n\t\t'please_choose' => 'Maak een keuze',\n\t\t'allow_modifications' => 'Aanpassingen toestaan',\n\t\t'megabyte' => 'MegaByte',\n\t\t'not_supported' => 'Wordt niet ondersteund in: ',\n\t\t'view' => 'weergeven',\n\t\t'toomanydirs' => 'Teveel submappen. Er wordt teruggevallen op handmatige invoer.',\n\t\t'abort' => 'Afbreken',\n\t\t'not_activated' => 'niet actief',\n\t\t'off' => 'uit',\n\t\t'options' => 'opties',\n\t\t'neverloggedin' => 'Nog niet ingelogd',\n\t\t'descriptionerrordocument' => 'Kan een URL, pad naar een bestand of een tekenreeks zijn die is omsloten door \" \"<br />Laat leeg voor de standaardwaarde.',\n\t\t'unlock' => 'ontgrendelen',\n\t],\n\t'pwdreminder' => [\n\t\t'success' => 'Wachtwoord opnieuw ingesteld.<br />U ontvangt spoedig een e-mail met uw nieuwe wachtwoord.',\n\t\t'notallowed' => 'Het opnieuw instellen van wachtwoorden is uitgeschakeld',\n\t],\n\t'question' => [\n\t\t'question' => 'Beveiligingsvraag',\n\t\t'admin_customer_reallydelete' => 'Weet u zeker dat u de klant %s wilt verwijderen? Dit kan niet ongedaan worden gemaakt!',\n\t\t'admin_domain_reallydelete' => 'Weet u zeker dat u het domein %s wilt verwijderen?',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Weet u echt heel zeker dat deze beveiligingsinstellingen wilt deactiveren (OpenBasedir)?',\n\t\t'admin_admin_reallydelete' => 'Weet u zeker dat u de admin %s verwijderen wilt? Iedere klant en domein zal worden toegewezen aan de hoofd administrator.',\n\t\t'admin_template_reallydelete' => 'Weet u zeker dat u het sjabloon \\'%s\\' verwijderen wilt?',\n\t\t'domains_reallydelete' => 'Weet u zeker dat u het domein %s verwijderen wilt?',\n\t\t'email_reallydelete' => 'Weet u zeker dat u het e-mail adres %s verwijderen wilt?',\n\t\t'email_reallydelete_account' => 'Weet u zeker dat het e-mail account van %s verwijderen wilt?',\n\t\t'email_reallydelete_forwarder' => 'Weet u zeker dat u de forwarder %s verwijderen wilt?',\n\t\t'extras_reallydelete' => 'Weet u zeker dat u de map beveiliging voor de map %s verwijderen wilt?',\n\t\t'extras_reallydelete_pathoptions' => 'Weet u zeker dat u de pad-opties voor %s verwijderen wilt?',\n\t\t'ftp_reallydelete' => 'Weet u zeker dat u het FTP account %s verwijderen wilt?',\n\t\t'mysql_reallydelete' => 'Weet u zeker dat u de database %s verwijderen wilt? Dit kan niet ongedaan gemaakt worden!',\n\t\t'admin_configs_reallyrebuild' => 'Weet u zeker dat u de configuratie bestanden voor Apache en Bind opnieuw wilt opbouwen?',\n\t\t'admin_ip_reallydelete' => 'Weet u zeker dat u het IP adres %s verwijderen wilt?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Weet u zeker dat u de document root voor dit domein niet in de klant-root van de klant wil hebben?',\n\t\t'admin_counters_reallyupdate' => 'Weet u zeker dat u gebruikte bronnen wilt herberekenen?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Weet u zeker dat u alle onversleutelde wachtwoorden wilt verwijderen? Deze opdracht is niet terug te draaien!',\n\t\t'logger_reallytruncate' => 'Weet u zeker dat u de tabel \"%s\" wilt legen?',\n\t\t'admin_quotas_reallywipe' => 'Weet u zeker dat u alle quota wilt verwijderen? Dit is niet terug te draaien!',\n\t\t'admin_quotas_reallyenforce' => 'Weet u zeker dat u quota wilt afdwingen? Dit is niet terug te draaien!',\n\t\t'phpsetting_reallydelete' => 'Weet u zeker dat u deze instellingen wilt verwijderen? Alle domeinen die deze configuratie gebruiken zullen terugvallen op de standaardinstellingen.',\n\t\t'customer_reallyunlock' => 'Weet u zeker dat u klant %s? wilt ontgrendelen',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Sessie Timeout',\n\t\t\t'description' => 'Hoe lang moet een gebruiker inactief zijn voor dat de sessie ongeldig wordt (seconden)?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Klant Voorvoegsel',\n\t\t\t'description' => 'Welk voorvoegsel moet een klant account hebben?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'SQL Voorvoegsel',\n\t\t\t'description' => 'Welk voorvoegsel moet een mysql account hebben?',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP Voorvoegsel',\n\t\t\t'description' => 'Welk voorvoegsel dienen nieuwe FTP-accounts te krijgen?<br/><b>Indien u dit wijzigt, dient ook de query voor Quota in het configuratiebestand van de FTP-server aan te passen!</b> ',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Document map',\n\t\t\t'description' => 'Waar zullen alle gegeven opgeslagen worden?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Logfiles map',\n\t\t\t'description' => 'Waar zullen alle log-file opgeslagen worden?',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'IP-Adres',\n\t\t\t'description' => 'Wat is het IP-adres van deze server?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Hostnaam',\n\t\t\t'description' => 'Wat is de hostnaam van deze server?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Apache reload commando',\n\t\t\t'description' => 'Wat is het commando op apache te herladen?',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Bind configuratie map',\n\t\t\t'description' => 'Waar staan de bind configuratie bestanden?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Bind reload commando',\n\t\t\t'description' => 'Wat is het command om bind te herladen?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'Mails-Uid',\n\t\t\t'description' => 'Welk UserID moeten de e-mails hebben?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mails-Gid',\n\t\t\t'description' => 'Welke GroupID moeten e-mails hebben?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Mails-Homedir',\n\t\t\t'description' => 'Waar moeten alle e-mail opgeslagen worden?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Afzender',\n\t\t\t'description' => 'Wat is de afzender voor e-mail verstuurd vanuit het Panel?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin URL',\n\t\t\t'description' => 'Wat is de URL die verwijst naar phpMyAdmin? (moet beginnen met http://)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'WebMail URL',\n\t\t\t'description' => 'Wat is de URL die verwijst naar WebMail? (moet beginnen met http://)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP URL',\n\t\t\t'description' => 'Wat is de URL die verwijst naar WebFTP? (moet beginnen met http://)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Wat is uw standaard server taal?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Maximaal aantal inlog pogingen',\n\t\t\t'description' => 'Maximaal aantal inlog pogingen voor het account gedeactiveerd wordt.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Deactiveringstijd',\n\t\t\t'description' => 'Tijd (in seconden) dat een account gedeactiveerd wordt na te veel inlogpogingen.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Manier van Pad ingeven',\n\t\t\t'description' => 'Moet het pad geselecteerd worden met een \\'dropdown\\' menu of met een invoerveld?',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Vermeldingen per pagina',\n\t\t\t'description' => 'Hoeveel vermeldingen er getoond moeten worden per pagina? (0 = alles laten zien)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'Standaard IP/Poort',\n\t\t\t'description' => 'Wat is de standaard IP/Poort combinatie?',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Pad dat toegevoegd wordt aan OpenBasedir',\n\t\t\t'description' => 'Deze paden (gescheiden door dubbele punten) zullen worden toegevoegd aan het OpenBasedir-statement in iedere vhost-container.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Gebruik een natuurlijke manier van sorteren',\n\t\t\t'description' => 'Lijsten worden gesorteerd zoals web1 -> web2 -> web11 in plaats van web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Pad naar webinhoud voor gedeactiveerde gebruikers',\n\t\t\t'description' => 'Wanneer een gebruiker geactiveerd is, wordt dit pad gebruikt voor zijn/haar webinhoud.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Sla het wachtwoord ook onversleuteld op in de database',\n\t\t\t'description' => 'Indien ingesteld op JA worden wachtwoorden in klare tekst opgeslagen in de database (zichtbaar voor iedereen die toegang heeft tot de tabel mail_users). Activeer dit alleen wanneer u gebruik gaat maken van SASL!',\n\t\t\t'removelink' => 'Klik hier om alle onversleutelde wachtwoorden uit de database te verwijderen',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'FTP accounts @domein',\n\t\t\t'description' => 'Kunnen klanten FTP-accounts in de vorm gebruiker@domein aanmaken?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'PHP insluiten via mod_fcgid/suexec',\n\t\t\t'description' => 'Gebruik mod_fcgid/suexec/libnss_mysql om PHP uit te voeren onder het gebruikersaccount.<br/><b>Dit vereist een aangepaste configuratie van de webserver. Alle volgende optie\\'s zijn alleen geldig wanneer deze module actief is.</b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Configuratiemap',\n\t\t\t\t'description' => 'Waar dienen alle configuratiebestanden voor FCGID te worden opgeslagen? Indien u geen aangepaste versie van SuExec gebruikt, zoals gebruikelijk is, dient dit pad onder /var/www/ te liggen',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Map voor tijdelijke bestanden',\n\t\t\t\t'description' => 'Waar dienen de tijdelijke mappen te worden opgeslagen?',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Processen per domein',\n\t\t\t\t'description' => 'Hoeveel processen moeten gestart/toegestaan worden per domein? De waarde 0 wordt aangeraden, aangezien PHP zelf het aantal processen goed kan inschatten.',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper in vhosts',\n\t\t\t\t'description' => 'Hoe moet de wrapper ingesloten worden in vhosts?',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Globale PEAR-mappen',\n\t\t\t\t'description' => 'Welke PEAR-mappen dienen te worden ingesloten in elke php.ini? Bij meerdere mappen dienen deze te worden gescheiden door dubbele punten.',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Maximaal aantal verzoeken per domein',\n\t\t\t\t'description' => 'Toegestane aantal verzoeken per domein',\n\t\t\t],\n\t\t\t'defaultini' => 'Standaard PHP-configuratie voor nieuwe domeinen',\n\t\t\t'defaultini_ownvhost' => 'Standaard configuratie voor froxlor-vHost',\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Gebruik alternatief emailadres',\n\t\t\t'description' => 'Stuur het wachtwoord naar een ander adres dan het adres dat opgegeven werd tijdens het aanmaken van het emailadres.',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Bestands-/mapnaam voor vhost-configuratie webserver',\n\t\t\t'description' => 'Waar dient het vhost-configuratiebestand opgeslagen te worden? U kunt hier zowel een bestand (alle configuratie\\'s in 1 bestand) of een map (apart bestand voor iedere configuratie) opgeven.',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Bestands-/mapnaam voor diroptions-configuratie webserver',\n\t\t\t'description' => 'Waar dient het diroptions-configuratiebestand opgeslagen te worden? U kunt hier zowel een bestand (alle configuratie\\'s in 1 bestand) of een map (apart bestand voor iedere configuratie) opgeven.',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Mapnaam htpasswd-bestanden webserver',\n\t\t\t'description' => 'Waar dienen de htpasswd-bestanden, voor beveiligde toegang, opgeslagen te worden?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'Toegangshosts voor MySQL',\n\t\t\t'description' => 'Een door komma\\'s gescheiden lijst met hosts waarvandaan gebruikers verbinding mogen maken met de MySQL-server.',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Uitvoer Webalizer',\n\t\t\t'description' => 'Informatieniveau van Webalizer',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Logs in-/uitgeschakeld',\n\t\t\t'severity' => 'Logniveau',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Log-type(s)',\n\t\t\t\t'description' => 'Om meerdere types te selecteren, houd u CTRL ingedrukt terwijl u selecteert.<br />Beschikbare types zijn: syslog, bestand, mysql',\n\t\t\t],\n\t\t\t'logfile' => 'Pad naar logfile, inclusief bestandsnaam',\n\t\t\t'logcron' => 'Cronjobs loggen',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Nooit',\n\t\t\t\t'once' => 'Eenmalig',\n\t\t\t\t'always' => 'Altijd',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'openssl_cnf' => 'Standaardinstellingen certificaat',\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Standaard vhost-instellingen',\n\t\t\t'description' => 'De inhoud van dit veld wordt rechtstreeks in de vhost-container geplaatst. N.B.: Deze code wordt niet op fouten gecontroleerd. In geval van fouten kan het zijn dat de webserver niet meer start!',\n\t\t],\n\t\t'decimal_places' => 'Aantal getallen achter de komma in uitvoer dataverkeer',\n\t\t'webalizer_enabled' => 'Webalizer activeren',\n\t\t'awstats_enabled' => 'AWstats activeren',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Instellingen voor klantdomein',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Klanten toestaan de DNS-instellingen van het domein te wijzigen',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Gebruik gebruikersnamen die compatible zijn met UNIX',\n\t\t\t'description' => 'Staat het gebruik van <strong>-</strong> en <strong>_</strong> in gebruikersnaam toe, indien ingesteld op <strong>Nee</strong>',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Klanten toestaan hun wachtwoord opnieuw in te stellen',\n\t\t\t'description' => 'Klanten kunnen hun wachtwoorden opnieuw instellen. Het nieuwe wachtwoord wordt hen per e-mail toegestuurd.',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Beheerders/wederverkopers toestaan hun wachtwoorden opnieuw in te stellen.',\n\t\t\t'description' => 'Beheerders/wederverkopers kunnen hun wachtwoorden opnieuw instellen. Het nieuwe wachtwoord wordt hen per e-mail toegestuurd.',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Quotum voor mailbox',\n\t\t\t'description' => 'Het standaard quotum voor nieuwe mailboxen (MegaByte).',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Gebruik mailbox-quota voor mailboxen',\n\t\t\t'description' => 'Activeert het gebruik van quota voor mailboxen. Standaard is <b>Nee</b>, aangezien dit verdere configuratie vereist.',\n\t\t\t'removelink' => 'Klik hier om alle quota van mailbox te verwijderen.',\n\t\t\t'enforcelink' => 'Klik hier om het standaard quotum af te dwingen voor alle accounts.',\n\t\t],\n\t\t'index_file_extension' => [\n\t\t\t'description' => 'Welk achtervoegsel moet gebruikt worden voor het indexbestand? Dit achtervoegsel wordt gebruikt wanneer een van de beheerders een eigen indexsjabloon heeft gemaakt.',\n\t\t\t'title' => 'Achtervoegsel van het indexbestand in nieuwe mappen voor klanten.',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Meerdere logins toestaan',\n\t\t\t'description' => 'Indien dit is ingeschakeld kan een klant meerdere malen tegelijkertijd inloggen.',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Het verplaatsen van domeinen tussen beheerders toestaan',\n\t\t\t'description' => 'Indien actief, kunt u een domein toewijzen aan een andere beheerder.<br /><b>Let op:</b> Indien een klant niet is toegewezen aan de beheerder van het domein, kan de betreffende beheerde alle domeinen van deze klant zien!',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Het verplaatsen van domeinen tussen klanten toestaan',\n\t\t\t'description' => 'Indien actief, kunt u de klant van een domein veranderen.<br /><b>Let op:</b> froxlor zal niet het pad aanpassen. Dit kan ervoor zorgen dat het domein onbruikbaar wordt!',\n\t\t],\n\t\t'cron' => [\n\t\t\t'debug' => [\n\t\t\t\t'title' => 'Foutopsporing cronscript',\n\t\t\t\t'description' => 'Activeer dit om het lockbestand te bewaren nadat de cron-taak is afgehandeld, zodat het gerbuikt kan worden voor het opsporen van fouten.<br /><b>Let op:</b>Het vastzetten van het lockbestand kan ervoor zorgen dat de volgende cron-taak niet naar behoren functioneert.',\n\t\t\t],\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'Indien \"Ja\" zullen deze aangepaste VHost-instellingen worden toegepast op alle subdomeinen.',\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Minimumlengte wachtwoord',\n\t\t\t'description' => 'Hier kunt u een minimumlengte voor wachtwoorden opgeven. \\'0\\' betekent geen minimumlengte.',\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Standaard indexbestand ook plaatsen in nieuwe submappen',\n\t\t\t'description' => 'Indien actief wordt dit bestand automatisch geplaatst in nieuw aangemaakte submappen (indien deze nog niet bestaat).',\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Reply-To adres',\n\t\t\t'description' => 'Geef een e-mailadres dat gebruikt wordt als antwoord-aan adres voor mails die verzonden worden door het paneel.',\n\t\t],\n\t\t'adminmail_defname' => 'Panel e-mail sender name',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Standaarddomein voor klanten',\n\t\t\t'description' => 'Welke hostnaam dient gebruikt te worden voor standaard subdomeinen voor klanten. Indien leeg zal de naam van het systeem gebruikt worden.',\n\t\t],\n\t\t'awstats_path' => 'Pad naar \\'awstats_buildstaticpages.pl\\' van AWStats',\n\t\t'awstats_conf' => 'AWStats configuratiepad',\n\t\t'defaultttl' => 'Standaard TTL voor domeinen in seconden (standaard \\'604800\\' = 1 week)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Standaard foutdocumenten voor alle klanten activeren',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'Bestand/URL voor foutcode 401',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'Bestand/URL voor foutcode 403',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'Bestand/URL voor foutcode 404',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'Bestand/URL voor foutcode 500',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'Indien PureFTPD geselecteerd is, worden .ftpquota bestanden dagelijks aangemaakt en/of bijgewerkt',\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Klanten toestaan doorverwijzingen te maken',\n\t\t\t'description' => 'Klanten toestaan de HTTP-statuscode aan te passen die voor doorverwijzingen gebruikt worden',\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Standaard doorverwijzing',\n\t\t\t'description' => 'Kies de standaard doorverwijzingscode indien de klant dit zelf niet gedaan heeft',\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Maak mail-, imap-, pop3- en smtp-\"A record\" ook wanneer MX-Servers zijn ingesteld',\n\t\t'froxlordirectlyviahostname' => 'froxlor is direct toegankelijk via hostnaam',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Reguliere expressie voor wachtwoorden',\n\t\t\t'description' => 'Hier kunt u een reguliere expressie opgeven voor de complexiteit van wachtwoorden.<br />Leeg betekent geen speciale complexiteit',\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'FCGID inschakelen voor de VHost voor froxlor',\n\t\t\t'description' => 'Indien ingeschakeld wordt froxlor ook uitgevoerd onder een lokale gebruiker<br /><strong>Let op:</strong>Dit vereist handmatige configuratie, zie <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - handbook</a>',\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'Om SuExec heenwerken (Geldt alleen voor Apache)',\n\t\t\t\t'description' => 'Schakel dit alleen in indien de documentmappen van klanten niet in het pad van Apache SuExec vallen.<br />Indien ingeschakeld zal froxlor een symbolische link maken voor het pad waarvoor Perl actief is + /cgi-bin/.<br />Merk op dat Perl dan alleen werkt in de submap /cgi-bin/ en niet in de map zelf (zoals het werkt zonder deze oplossing!)',\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Pad naar symbolische links naar Perl-mappen van klanten',\n\t\t\t\t'description' => 'U dient dit alleen op te geven indien \"Om SuExec heenwerken\" actief is.<br />LET OP: Zorg ervoor dat deze map onder het pad van SuExec valt, anders is deze oplossing waardeloos',\n\t\t\t],\n\t\t],\n\t\t'awstats_awstatspath' => 'Pad naar AWStats \\'awstats.pl\\'',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'Pad naar iconen AWstats icons',\n\t\t\t'description' => 'bijvoorbeeld /usr/share/awstats/htdocs/icon/',\n\t\t],\n\t\t'login_domain_login' => 'Login met domeinen toestaan',\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP backend',\n\t\t\t'description' => 'dit is waar het PHP-proces luistert naar verzoeken van nginx, kan een unix socket van ip:poort combinatie zijn',\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'Commando voor het herladen van PHP',\n\t\t\t'description' => 'wordt gebruikt om de PHP backend opnieuw te laden, indien actief<br />Standaard: leeg',\n\t\t],\n\t\t'phpfpm' => 'php-fpm inschakelen',\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'Configuratiemap van php-fpm',\n\t\t\t'reload' => 'Commando voor het herstarten van php-fpm',\n\t\t\t'pm' => 'Process manager control (pm)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'Het aantal subprocessen',\n\t\t\t\t'description' => 'Het aantal subprocessen dat gestart wordt indien PM is ingesteld op \\'statisch\\' en het maximum aantal subprocessen wanneer PM is ingesteld op \\'dynamisch\\'<br />Gelijk aan PHP_FCGI_CHILDREN',\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'Het aantal subprocessen bij het starten',\n\t\t\t\t'description' => 'Noot: Wordt alleen gebruikt indien PM is ingesteld op \\'dynamisch\\'',\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'Het gewenste minimum aantal vrije subprocessen',\n\t\t\t\t'description' => 'Noot: Wordt alleen gebruikt indien PM is ingesteld op \\'dynamisch\\'<br />Noot: Verplicht wanneer PM ingesteld is op \\'dynamisch\\'',\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'THet gewenste minimum aantal vrije subprocessen',\n\t\t\t\t'description' => 'Noot: Wordt alleen gebruikt indien PM is ingesteld op \\'dynamisch\\'<br />Noot: Verplicht wanneer PM ingesteld is op \\'dynamisch\\'',\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Verzoeken voor subproces voordat het opnieuw gestart wordt',\n\t\t\t\t'description' => 'Voor het eindeloos verwerken kunt u deze waarde instellen op \\'0\\'. Gelijk aan PHP_FCGI_MAX_REQUESTS.',\n\t\t\t],\n\t\t],\n\t],\n\t'spf' => [\n\t\t'use_spf' => 'SPF voor domeinen activeren?',\n\t\t'spf_entry' => 'SPF regel voor alle domeinen',\n\t],\n\t'success' => [\n\t\t'success' => 'Informatie',\n\t\t'clickheretocontinue' => 'Klik hier om verder te gaan',\n\t\t'settingssaved' => 'De instellingen zijn opgeslagen.',\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Uitstaande cron-taken',\n\t\t'REBUILD_VHOST' => 'Bezig met opnieuw opbouwen van de configuratie van de webserver',\n\t\t'CREATE_HOME' => 'Klant met naam %s wordt toegevoegd',\n\t\t'REBUILD_DNS' => 'Opnieuw opbouwen bind-configuratie',\n\t\t'CREATE_FTP' => 'Map aanmaken voor nieuwe FTP-gebruiker',\n\t\t'DELETE_CUSTOMER_FILES' => 'Verwijderen klantbestanden van %s',\n\t\t'noneoutstanding' => 'Er zijn op dit moment geen uitstaande taken voor froxlor',\n\t],\n\t'traffic' => [\n\t\t'month' => 'Maand',\n\t\t'day' => 'Dag',\n\t\t'months' => [\n\t\t\t1 => 'Januari',\n\t\t\t2 => 'Februari',\n\t\t\t3 => 'Maart',\n\t\t\t4 => 'April',\n\t\t\t5 => 'Mei',\n\t\t\t6 => 'Juni',\n\t\t\t7 => 'Juli',\n\t\t\t8 => 'Augustus',\n\t\t\t9 => 'September',\n\t\t\t10 => 'Oktober',\n\t\t\t11 => 'November',\n\t\t\t12 => 'December',\n\t\t],\n\t\t'mb' => 'Datavekeer (MB)',\n\t\t'distribution' => '<font color=\"#019522\">FTP</font> | <font color=\"#0000FF\">HTTP</font> | <font color=\"#800000\">Mail</font>',\n\t\t'sumhttp' => 'Samenvatting HTTP-verkeer in',\n\t\t'sumftp' => 'Samenvatting FTP-verkeer in',\n\t\t'summail' => 'Samenvatting Mail-verkeer in',\n\t],\n\t'translator' => 'Sander Klein/Frits Letteboer',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'Een nieuwere versie van froxlor is geinstalleerd maar is nog niet geconfigureerd.<br />Alleen de beheerder kan inloggen en de update voltooien.',\n\t\t'update' => 'froxlor Update',\n\t\t'proceed' => 'Verdergaan',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'De bestanden van froxlor zijn bijgewerkt naar versie <strong>%s</strong>. De geinstalleerde versie is <strong>%s</strong>.',\n\t\t\t'part_b' => '<br /><br />Klanten kunnen niet inloggen totdat de update voltooid is.<br /><strong>Verdergaan?</strong>',\n\t\t],\n\t\t'noupdatesavail' => '<strong>U gebruikt reeds de meest recente versie van froxlor.</strong>',\n\t],\n];\n"
  },
  {
    "path": "lng/pt.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @author     Ricardo Luiz Costa <ricardo@winger.com.br>\n * @author     Thiago Goncalves de Castro <thiago@davoi.com.br>\n * @author     Rafael Andrade <slyppp@gmail.com>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'admin' => [\n\t\t'overview' => 'Visão geral',\n\t\t'ressourcedetails' => 'Recursos usados',\n\t\t'systemdetails' => 'Detalhes do sistema',\n\t\t'froxlordetails' => 'Detalhes do froxlor',\n\t\t'installedversion' => 'Versão instalada',\n\t\t'latestversion' => 'Ultima Versão',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'procurar pela internet',\n\t\t\t'error' => 'Erro de leitura',\n\t\t],\n\t\t'resources' => 'Recursos',\n\t\t'customer' => 'Cliente',\n\t\t'customers' => 'Clientes',\n\t\t'customer_add' => 'Criar cliente',\n\t\t'customer_edit' => 'Editar cliente',\n\t\t'domains' => 'Domínios',\n\t\t'domain_add' => 'Criar domínio',\n\t\t'domain_edit' => 'Editar domínio',\n\t\t'subdomainforemail' => 'Subdomínio como \"emaildomains\"',\n\t\t'admin' => 'Administrador',\n\t\t'admins' => 'Administradores',\n\t\t'admin_add' => 'Criar administrador',\n\t\t'admin_edit' => 'Editar administrador',\n\t\t'customers_see_all' => 'Mostrar todos os clientes',\n\t\t'change_serversettings' => 'Alterar configuraççes do servidor?',\n\t\t'server' => 'Sistema',\n\t\t'serversettings' => 'Configurações',\n\t\t'rebuildconf' => 'Escrever de novo os configs',\n\t\t'stdsubdomain' => 'Subdomínio padrão',\n\t\t'stdsubdomain_add' => 'Criar Subdomínio padrão',\n\t\t'phpenabled' => 'PHP Habilitado',\n\t\t'deactivated' => 'Desativado',\n\t\t'deactivated_user' => 'Desativar usuário',\n\t\t'sendpassword' => 'Enviar senha',\n\t\t'ownvhostsettings' => 'Own vHost-Settings',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Configurações',\n\t\t\t'overview' => 'Visão Geral',\n\t\t\t'wizard' => 'Assistente',\n\t\t\t'distribution' => 'Distribuição',\n\t\t\t'service' => 'Serviço',\n\t\t\t'daemon' => 'Daemon',\n\t\t\t'http' => 'Servidor Web (HTTP)',\n\t\t\t'dns' => 'Servidor de Nomes (DNS)',\n\t\t\t'mail' => 'Servidor de Emails (POP3/IMAP)',\n\t\t\t'smtp' => 'Servidor de Emails (SMTP)',\n\t\t\t'ftp' => 'Servidor FTP',\n\t\t\t'etc' => 'Outros (Sistema)',\n\t\t\t'choosedistribution' => 'Escolha uma distribuição',\n\t\t\t'chooseservice' => 'Escolha um serviço',\n\t\t\t'choosedaemon' => 'Escolha um daemon',\n\t\t\t'statistics' => 'Estatísticas',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Templates',\n\t\t\t'template_add' => 'Adicionar template',\n\t\t\t'template_edit' => 'Editar template',\n\t\t\t'action' => 'Ação',\n\t\t\t'email' => 'E-Mail',\n\t\t\t'subject' => 'Assunto',\n\t\t\t'mailbody' => 'Mensagem',\n\t\t\t'createcustomer' => 'E-mail de boas-vindas para novos clientes',\n\t\t\t'pop_success' => 'E-mail de boas-vindas para nova conta de e-mail',\n\t\t\t'template_replace_vars' => 'Variaveis para serem substituidas no template:',\n\t\t\t'FIRSTNAME' => 'Altere para o primeiro nome do cliente.',\n\t\t\t'NAME' => 'Altere para o nome do cliente.',\n\t\t\t'USERNAME' => 'Altere para nome da conta do cliente.',\n\t\t\t'PASSWORD' => 'Altere com a senha da conta do cliente.',\n\t\t\t'EMAIL' => 'Altere com os dados do servidor POP3/IMAP.',\n\t\t\t'TRAFFIC' => 'Substituído com o tráfego, o que foi atribuído ao cliente.',\n\t\t\t'TRAFFICUSED' => 'Substituído com o tráfego, que foi esgotado pela cliente.',\n\t\t\t'pop_success_alternative' => 'Bem-vindo para novas contas e-mail enviado ao endereço alternativo',\n\t\t\t'EMAIL_PASSWORD' => 'Substituído a senha da conta POP3/IMAP.',\n\t\t\t'index_html' => 'Indice de arquivo recém-criado no diretório de cliente',\n\t\t\t'SERVERNAME' => 'Substitua pelo nome do servidor.',\n\t\t\t'CUSTOMER' => 'Substitua pelo login do cliente.',\n\t\t\t'ADMIN' => 'Substitua pelo login do admin.',\n\t\t\t'CUSTOMER_EMAIL' => 'Substitua pelo endereço de email do cliente.',\n\t\t\t'ADMIN_EMAIL' => 'Substitua pelo endereço de email do administrador.',\n\t\t\t'filetemplates' => 'Modelo de Arquivo',\n\t\t\t'filecontent' => 'Conteúdo do Arquivo',\n\t\t],\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IPs e Portas',\n\t\t\t'add' => 'Adicionar IP/Porta',\n\t\t\t'edit' => 'Editar IP/Porta',\n\t\t\t'ipandport' => 'IP/Porta',\n\t\t\t'ip' => 'IP',\n\t\t\t'port' => 'Porta',\n\t\t\t'create_listen_statement' => 'Criar instrução de escuta',\n\t\t\t'create_namevirtualhost_statement' => 'Criar instrução de NameVirtualHost',\n\t\t\t'create_vhostcontainer' => 'Criar vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Criar instrução de ServerName  no vHost-Container',\n\t\t\t'enable_ssl' => 'Esta é uma porta SSL?',\n\t\t\t'ssl_cert_file' => 'Caminho para o certificado SSL',\n\t\t],\n\t\t'valuemandatory' => 'Este valor é imperativo.',\n\t\t'valuemandatorycompany' => 'Qualquer um \"nome\" e \"nome\" o \"companhia\" deve ser enchido.',\n\t\t'webserver' => 'Servidor Web',\n\t\t'memorylimitdisabled' => 'Desabilitado',\n\t\t'serversoftware' => 'Servidor de Software',\n\t\t'phpversion' => 'Versão do PHP',\n\t\t'phpmemorylimit' => 'Memória Limite do PHP',\n\t\t'mysqlserverversion' => 'Versão do MySQL Server',\n\t\t'mysqlclientversion' => 'Versão do MySQL Client',\n\t\t'webserverinterface' => 'Interface do Servidor Web',\n\t\t'accountsettings' => 'Configurações de Conta',\n\t\t'panelsettings' => 'Painel de Controle',\n\t\t'systemsettings' => 'Configurações do Sistema',\n\t\t'webserversettings' => 'Configurações do WebServer',\n\t\t'mailserversettings' => 'Configurações do Servidor de Email',\n\t\t'nameserversettings' => 'Configurações dos Servidores de Nomes',\n\t\t'updatecounters' => 'Recalcular utilização de recursos',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Nunca',\n\t\t\t'choosableno' => 'Escolhe, default não',\n\t\t\t'choosableyes' => 'Escolher, default sim',\n\t\t\t'always' => 'Sempre',\n\t\t],\n\t\t'webalizersettings' => 'Configurações do Webalizer',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normal',\n\t\t\t'quiet' => 'Quieto',\n\t\t\t'veryquiet' => 'Sem Saída',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Não adicionar um domínio corretamente. Você primeiro precisa adicionar um cliente.',\n\t\t'loggersettings' => 'Configurações de Logs',\n\t\t'logger' => [\n\t\t\t'normal' => 'normal',\n\t\t\t'paranoid' => 'paranóico',\n\t\t],\n\t\t'emaildomain' => 'Domínio de Email',\n\t\t'email_only' => 'Somente Email?',\n\t\t'wwwserveralias' => 'Adicionar um \"www.\" ServerAlias',\n\t\t'subject' => 'Assunto',\n\t\t'recipient' => 'Destinatário',\n\t\t'message' => 'Escrever uma mensagem',\n\t\t'text' => 'Mensagem',\n\t\t'sslsettings' => 'Configuração de SSL',\n\t\t'dkimsettings' => 'Configurações de Chave de Domínios',\n\t\t'caneditphpsettings' => 'Pode alterar as configurações PHP relacionadas com o domínio?',\n\t\t'allips' => 'Todos os IPs',\n\t\t'awstatssettings' => 'Configurações Awtats',\n\t\t'domain_dns_settings' => 'Configurações de DNS',\n\t\t'activated' => 'Ativado',\n\t\t'statisticsettings' => 'Configurações de Estatísticas',\n\t\t'or' => 'ou',\n\t\t'sysload' => 'Carga do Sistema',\n\t\t'noloadavailable' => 'Não disponível',\n\t\t'nouptimeavailable' => 'Não disponível',\n\t\t'nosubject' => '(Sem Assunto)',\n\t\t'accountdata' => 'Data da Conta',\n\t\t'contactdata' => 'Data de Contato',\n\t\t'servicedata' => 'Data de Serviço',\n\t\t'security_settings' => 'Opções de Segurança',\n\t\t'know_what_youre_doing' => 'Somente altere, se você sabe o que está fazendo',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Mostrar versão do froxlor no login',\n\t\t\t'description' => 'Mostar a versão do froxlor no rodapé da página de login',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Mostar versão do froxlor no rodapé',\n\t\t\t'description' => 'Mostar a versão do froxlor no rodapé do resto das páginas',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Cabeçalho gráfico do froxlor',\n\t\t\t'description' => 'Quais gráficos devem aparece no topor',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'Configurações do PHP',\n\t\t\t'description' => 'Descrição',\n\t\t\t'actions' => 'Ações',\n\t\t\t'activedomains' => 'Em uso pelo(s) domínio(s)',\n\t\t\t'notused' => 'Configuração não está em uso',\n\t\t\t'editsettings' => 'Alterar Configuração do PHP',\n\t\t\t'addsettings' => 'Criar novas configurações do PHP',\n\t\t\t'viewsettings' => 'Visualizar Configuração do PHP',\n\t\t\t'phpinisettings' => 'Configurações do php.ini',\n\t\t\t'addnew' => 'Criar novas configurações',\n\t\t\t'binary' => 'Binário do PHP',\n\t\t\t'file_extensions' => 'Extensões de arquivos',\n\t\t\t'file_extensions_note' => '(Sem pontos, separados por espaços)',\n\t\t],\n\t\t'misc' => 'Variados',\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'As variáveis que serão substituídas nas Configurações',\n\t\t\t'pear_dir' => 'Serão substituídos com a definição global para o diretório pear.',\n\t\t\t'open_basedir' => 'Serão substituídos com a definição do domínio open_basedir.',\n\t\t\t'tmp_dir' => 'Substituído com o diretório temporário do domínio.',\n\t\t\t'open_basedir_global' => 'Serão substituídos com o valor global do caminho que será anexado ao open_basedir.',\n\t\t\t'customer_email' => 'Serão substituídos com o endereço de e-mail do cliente que é dono desse domínio.',\n\t\t\t'admin_email' => 'Serão substituídos por e-mail do administrador quem possui esse domínio.',\n\t\t\t'domain' => 'Serão substituídos com o domínio.',\n\t\t\t'customer' => 'Será substituída pelo nome do login do cliente que é dono desse domínio.',\n\t\t\t'admin' => 'Será substituída pelo nome de login do administrador que possui esse domínio.',\n\t\t],\n\t\t'expert_settings' => 'Configurações Avançadas',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'Processos PHP para este domínio (vazio para usar valor padrão)',\n\t\t],\n\t\t'phpserversettings' => 'Configuração do PHP',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Máximo de requisições php para este domínio (vazio para valor default)',\n\t\t],\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Senha atual',\n\t\t'new_password' => 'Nova senha',\n\t\t'new_password_confirm' => 'Repita a nova senha',\n\t\t'new_password_ifnotempty' => 'Nova senha (em branco = não alterar)',\n\t\t'also_change_ftp' => ' trocar tambem a senha da conta principal de FTP',\n\t\t'also_change_stats' => 'Troca a senha das estatísticas',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Ainda não está rodando',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Diretório home',\n\t\t'name' => 'Sobrenome',\n\t\t'firstname' => 'Primeiro nome',\n\t\t'company' => 'Empresa',\n\t\t'street' => 'Endereço',\n\t\t'zipcode' => 'CEP',\n\t\t'city' => 'Cidade',\n\t\t'phone' => 'Telefone',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'E-mail',\n\t\t'customernumber' => 'Cliente ID',\n\t\t'diskspace' => 'Espaço de disco (MB)',\n\t\t'traffic' => 'Tráfego (GB)',\n\t\t'mysqls' => 'Bancos de dados-MySQL',\n\t\t'emails' => 'Endereços de e-mail',\n\t\t'accounts' => 'Contas de e-mail',\n\t\t'forwarders' => 'Redirecionamentos de e-mail',\n\t\t'ftps' => 'Contas de FTP',\n\t\t'subdomains' => 'Sub-Domínio(s)',\n\t\t'domains' => 'Domínio(s)',\n\t\t'email_quota' => 'E-mail Quota',\n\t\t'email_imap' => 'E-mail IMAP',\n\t\t'email_pop3' => 'E-mail POP3',\n\t\t'mail_quota' => 'Quota de Email',\n\t\t'title' => 'Título',\n\t\t'country' => 'País',\n\t],\n\t'dkim' => [\n\t\t'dkim_prefix' => [\n\t\t\t'title' => 'Prefixo',\n\t\t\t'description' => 'Por favor, especifique o caminho para o os arquivos DKIM RSA, bem como para os arquivos de configuração para o plugin Milter',\n\t\t],\n\t\t'dkim_domains' => [\n\t\t\t'title' => 'Nome de arquivo de domínios',\n\t\t\t'description' => '<em>Nome do Arquivo</em> dos Domínios do DKIM, parâmetro especificado na configuração do dkim-Milter',\n\t\t],\n\t\t'dkim_dkimkeys' => [\n\t\t\t'title' => 'Nome de arquivo de chaves',\n\t\t\t'description' => '<em>Nome do Arquivo</em>DKIM KeyList do parâmetro especificado na configuração dkim-Milter',\n\t\t],\n\t\t'dkimrestart_command' => [\n\t\t\t'title' => 'Comando para reiniciar o Milter',\n\t\t\t'description' => 'Por favor especifique um comando para reiniciar o DKIM Milter',\n\t\t],\n\t\t'use_dkim' => [\n\t\t\t'title' => 'Ativar suporte para DKIM?',\n\t\t\t'description' => 'Você deseja usar o sistema de chaves de domínio (DKIM) ?',\n\t\t],\n\t],\n\t'dns' => [\n\t\t'destinationip' => 'Domínio IP',\n\t\t'standardip' => 'IP padrão do servidor',\n\t\t'a_record' => 'Gravar-A(Opcional IPV6)',\n\t\t'cname_record' => 'Gravar-CNAME',\n\t\t'mxrecords' => 'Definir entradas MX',\n\t\t'standardmx' => 'Servidor MX padrão',\n\t\t'mxconfig' => 'Registros MX personalizados',\n\t\t'priority10' => 'Prioridade 10',\n\t\t'priority20' => 'Prioridade 20',\n\t\t'txtrecords' => 'Difinir entradas TXT',\n\t\t'txtexample' => 'Exemplo (Entrada-SPF):<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t],\n\t'domain' => [\n\t\t'docroot' => 'trajeto da linha acima de',\n\t\t'homedir' => 'diretório da casa',\n\t\t'openbasedirpath' => 'Caminho do OpenBaseDir',\n\t],\n\t'domains' => [\n\t\t'description' => 'Aqui você pode criar (sub-)domínios e alterar seu destino.<br />O sistema irá levar algum tempo para aplicar as novas configurações depois de salvas.',\n\t\t'domainsettings' => 'Configurar Domínio',\n\t\t'domainname' => 'Nome do domínio',\n\t\t'subdomain_add' => 'Criar Sub-domínio',\n\t\t'subdomain_edit' => 'Editar (sub)domínio',\n\t\t'wildcarddomain' => 'Criar um wildcarddomain?',\n\t\t'aliasdomain' => 'Aliás para o domínio',\n\t\t'noaliasdomain' => 'Não domínio do aliás',\n\t\t'hasaliasdomains' => 'Possui alinhas de domínio(s)',\n\t\t'statstics' => 'Estatísticas de Uso',\n\t\t'isassigneddomain' => 'É um domínio assinado',\n\t\t'add_date' => 'Adicionado no froxlor',\n\t\t'registration_date' => 'Adicionado no Registro',\n\t\t'topleveldomain' => 'Top-Level-Domain',\n\t\t'associated_with_domain' => 'Associado',\n\t\t'aliasdomains' => 'Encaminhamento de domínios',\n\t],\n\t'emails' => [\n\t\t'description' => 'Aqui você pode criar e alterer seus e-mails.<br />Uma conta é como uma caixa de correio na frente de sua casa. Quando alguem envia para você um e-mail, ele é colocado nesta conta.<br /><br />Para baixar seus e-mails use as seguintes configurações no seu propraga de e-mails favorito: (Os dados em <i>italico</i> devem ser substituidos pelo equivalente da conta que você criou!)<br />Hostname: <b><i>Nome de seu domínio</i></b><br />Usuário: <b><i>Nome da conta / Endereço de e-mail</i></b><br />Senha: <b><i>a senha que você escolheu</i></b>',\n\t\t'emailaddress' => 'Endereços de e-mail',\n\t\t'emails_add' => 'Criar e-mail',\n\t\t'emails_edit' => 'Editar e-mail',\n\t\t'catchall' => 'Pega tudo',\n\t\t'iscatchall' => 'Definir como endereço pega tudo?',\n\t\t'account' => 'Conta',\n\t\t'account_add' => 'Criar conta',\n\t\t'account_delete' => 'Excluir conta',\n\t\t'from' => 'Origem',\n\t\t'to' => 'Destino',\n\t\t'forwarders' => 'Redirecionamentos',\n\t\t'forwarder_add' => 'Criar redirecionamento',\n\t\t'alternative_emailaddress' => 'Endereço de E-mail alternativo',\n\t\t'quota' => 'Quota',\n\t\t'noquota' => 'Sem quota',\n\t\t'updatequota' => 'Atualizar',\n\t],\n\t'error' => [\n\t\t'error' => 'Erro',\n\t\t'directorymustexist' => 'O diretório %s deve existir. Por favor crie ele primeiro com seu programa de FTP.',\n\t\t'filemustexist' => 'O arquivo %s deve existir.',\n\t\t'allresourcesused' => 'Você já usou todos os seus recursos.',\n\t\t'domains_cantdeletemaindomain' => 'Você não pode deletar um domínio que esta sendo usado como email-domain.',\n\t\t'domains_canteditdomain' => 'Você não pode editar este domínio. Ele foi desabilitado pelo administrador.',\n\t\t'domains_cantdeletedomainwithemail' => 'Você não pode deletar um domínio que é usado como email-domain. Delete todos as contas de e-mail primeiro.',\n\t\t'firstdeleteallsubdomains' => 'Você deve deletar todos subdomínios antes de poder criar um wildcard domain.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Você já definiu uma conta pega tudo para este domínio.',\n\t\t'ftp_cantdeletemainaccount' => 'Você não pode deletar a conta principal de FTP',\n\t\t'login' => 'O usuário ou senha digitados, não estão corretos. Por favor tente novamente!',\n\t\t'login_blocked' => 'Esta conta está suspensa por exceder as tentativas de login permitidas. <br />Por favor tente novamente em %s segundos.',\n\t\t'notallreqfieldsorerrors' => 'Você não preencheu todos os campos ou preencheu algum campo incorretamente.',\n\t\t'oldpasswordnotcorrect' => 'A senha antiga não confere.',\n\t\t'youcantallocatemorethanyouhave' => 'Você não pode alocar mais recursos do que você mesmo possui.',\n\t\t'mustbeurl' => 'Você não digitou uma URL válida (ex. http://seudominio.com/erro404.htm)',\n\t\t'invalidpath' => 'Optou por um URL não válido (eventuais problemas na lista do directório)',\n\t\t'stringisempty' => 'Faltando informação no campo',\n\t\t'stringiswrong' => 'Erro na informação do campo',\n\t\t'newpasswordconfirmerror' => 'A nova senha e a confirmação não conferem',\n\t\t'mydomain' => '\\'Domínio\\'',\n\t\t'mydocumentroot' => '\\'Documento principal\\'',\n\t\t'loginnameexists' => 'Login %s já existe',\n\t\t'emailiswrong' => 'E-mail %s contém caracteres inválidos ou está incompleto',\n\t\t'loginnameiswrong' => 'Login %s contém caracteres inválidos',\n\t\t'loginnameiswrong2' => 'Login contém muitos caracteres. Somente %s caracteres são aceitos.',\n\t\t'userpathcombinationdupe' => 'Usuario e caminho já existem',\n\t\t'patherror' => 'Erro geral! o caminho não pode ficar em branco',\n\t\t'errordocpathdupe' => 'Opção de caminho %s já existe',\n\t\t'adduserfirst' => 'Por favor crie um cliente primeiro',\n\t\t'domainalreadyexists' => 'O domínio %s já está apontado para outro cliente',\n\t\t'nolanguageselect' => 'Nenhum idioma selecionado.',\n\t\t'nosubjectcreate' => 'Você deve definir um nome para este e-mail template.',\n\t\t'nomailbodycreate' => 'Você deve definir o texto para este e-mail template.',\n\t\t'templatenotfound' => 'Template não encontrado.',\n\t\t'alltemplatesdefined' => 'Você não pode definir mais templates, todos idiomas já suportados.',\n\t\t'wwwnotallowed' => 'www não é permitido como nome de subdomínio.',\n\t\t'subdomainiswrong' => 'O subdomínio %s contém caracteres inválidos.',\n\t\t'domaincantbeempty' => 'O nome do domínio não pode estar vazio.',\n\t\t'domainexistalready' => 'O domínio %s já existe.',\n\t\t'domainisaliasorothercustomer' => 'O domínio-alias escolhido é ele próprio um domínio-alias ou este pertence a um outro cliente.',\n\t\t'emailexistalready' => 'O E-mail %s já existe.',\n\t\t'maindomainnonexist' => 'O domínio principal %s não existe.',\n\t\t'destinationnonexist' => 'Por favor crie seu redirecionamento no campo \\'Destino\\'.',\n\t\t'destinationalreadyexistasmail' => 'O redirecionamento %s já existe como uma conta de e-mail.',\n\t\t'destinationalreadyexist' => 'Você já definiu um redirecionamento para %s .',\n\t\t'destinationiswrong' => 'O redirecionamento %s contém caracteres inválidos ou incompletos.',\n\t\t'ipstillhasdomains' => 'O IP/Porta que você quer deletar ainda possui domínios associados e eles, por favor altere o IP/Porta destes domínios antes de deletá-los.',\n\t\t'cantdeletedefaultip' => 'Você não pode deletar o IP/Porta padrão do revendedor, por favor defina outro IP/Porta como padrão antes deletar o IP/Porta desejado',\n\t\t'cantdeletesystemip' => 'Você não pode deletar o IP do sistema, nem criar uma nova combinação IP/Porta para o sistema ou trocar o IP do sistema.',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'Porta\\'',\n\t\t'myipdefault' => 'Você precisa selecionar o IP/Porta que será padrão.',\n\t\t'myipnotdouble' => 'Esta combinação  IP/Porta já existe.',\n\t\t'cantchangesystemip' => 'Você não pode mudar o último sistema IP, para criar uma outra combinação nova de IP/Port para o sistema IP ou para mudar o sistema IP',\n\t\t'loginnameissystemaccount' => 'Você não pode criar os clientes que são similares aos systemaccounts. Incorpore por favor um outro accountname.',\n\t\t'admin_domain_emailsystemhostname' => 'Desculpe. Você não pode usar o hostname do servidor como domínio de email',\n\t\t'sessiontimeoutiswrong' => 'Apenas numeros \"Timeout da sessão\" permitido.',\n\t\t'maxloginattemptsiswrong' => 'Apenas numero \"Tentativa maxima de Login\" permitido.',\n\t\t'deactivatetimiswrong' => 'Apenas numero \"Desativar Tempo\" permitido.',\n\t\t'accountprefixiswrong' => 'O \"Prefixo\" está errado.',\n\t\t'mysqlprefixiswrong' => 'O \"Prefixo SQL\" está errado.',\n\t\t'ftpprefixiswrong' => 'O \"Prefixo FTP\" está errado.',\n\t\t'ipiswrong' => 'O \"Endereço-IP\" está errado. Apenas um Endereço-IP válido é permitido.',\n\t\t'vmailuidiswrong' => 'O \"UID do E-mail\" Está errado. Só é permitido um número de ID.',\n\t\t'vmailgidiswrong' => 'O \"GID do E-mail\" Está errado. Só é permitido um número de ID.',\n\t\t'adminmailiswrong' => 'O \"Endereço de Envio\" está errado. Apenas um endereço de e-mail válido é permitido.',\n\t\t'pagingiswrong' => 'O \"Entradas por páginas\"-value está errado. Somente caracteres númericos são permitidos.',\n\t\t'phpmyadminiswrong' => 'O caminho para o phpMyAmin não é válido',\n\t\t'webmailiswrong' => 'O caminho para o Webmail não é válido',\n\t\t'webftpiswrong' => 'O caminho para o WebFTP não é válido',\n\t\t'stringformaterror' => 'O valor par ao campo \"%s\" não esta no formato correto.',\n\t\t'youcantdeleteyourself' => 'Você não pode apagar você mesmo por motivos de segurança',\n\t\t'youcanteditallfieldsofyourself' => 'Nota: Você não pode editar todos os campos de sua própria conta por motivos de segurança',\n\t\t'documentrootexists' => 'O Diretório \"%s\" já existe para este usuario. Por favor remova-o e depois tente novamente.',\n\t\t'formtokencompromised' => 'O Pedido parece estar correto. Por motivos de segurança você está desconectado.',\n\t\t'logerror' => 'Log-Erro: %s',\n\t\t'nomessagetosend' => 'Você não entrou com uma mensagem',\n\t\t'norecipientsgiven' => 'Você não especificou um destinatário',\n\t\t'errorsendingmail' => 'A mensagem para \"%s\" falhou',\n\t\t'cannotreaddir' => 'Não é possível ler o diretório \"%s\"',\n\t\t'vmailquotawrong' => 'A tamanho da quota deve ser entre 1 e 999',\n\t\t'invalidip' => 'Endereço de IP Inválido: %s',\n\t\t'invalidmysqlhost' => 'Endereço de servidor MySQL inválido: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'Você não pode ativar Webalizer e Awstats  ao mesmo tempo, por favor, escolha uma delas',\n\t\t'cannotwritetologfile' => 'Não pode abrir arquivo de log %s para escrita',\n\t\t'missingfields' => 'Nem todos os campos necessários estavam no campo.',\n\t\t'accountnotexisting' => 'Esta conta não existe.',\n\t\t'nopermissionsorinvalidid' => 'Você não tem permissões suficientes para alterar essa configuração ou um ID inválido foi dado.',\n\t\t'phpsettingidwrong' => 'Não existe uma configuração de PHP para este ID',\n\t\t'descriptioninvalid' => 'A descrição é muito curta, muito longa ou contém caracteres ilegais',\n\t\t'info' => 'Informações',\n\t\t'filecontentnotset' => 'O arquivo não pode ser vazio',\n\t\t'index_file_extension' => 'A extensão do índice do arquivo deve ficar entre 1 e 6 caracteres. A prorrogação só pode conter caracteres como az, AZ e 0-9',\n\t\t'customerdoesntexist' => 'O cliente que você escolheu não existe',\n\t\t'admindoesntexist' => 'O administrador que você escolheu não existe',\n\t\t'ipportdoesntexist' => 'A combinação de IP/Porta que você escolheu não existe',\n\t],\n\t'extras' => [\n\t\t'description' => 'Aqui você pode adicoionar alguns recursos extras, como por exemplo um diretório protegido.<br />O sistema ira precisar de algum tempo para aplicar suas alterações depois de salvas.',\n\t\t'directoryprotection_add' => 'Adicionar diretório pretogido',\n\t\t'view_directory' => 'Mostrar conteúdo do diretório',\n\t\t'pathoptions_add' => 'Adicionar opções de caminho',\n\t\t'directory_browsing' => 'Pesquizar conteúdo de diretório',\n\t\t'pathoptions_edit' => 'Esitar opções de caminhos',\n\t\t'errordocument404path' => 'URL para página de erro 404',\n\t\t'errordocument403path' => 'URL para página de erro 403',\n\t\t'errordocument500path' => 'URL para página de erro 500',\n\t\t'errordocument401path' => 'URL para página de erro 401',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Aqui você pode criar e alterar suas contas de FTP.<br />As alterações são instantâneas e podem ser utilizadas imediatamente depois de salvas.',\n\t\t'account_add' => 'Criar conta',\n\t],\n\t'index' => [\n\t\t'customerdetails' => 'Detalhes dos Clientes',\n\t\t'accountdetails' => 'Detalhes das Contas',\n\t],\n\t'logger' => [\n\t\t'date' => 'Data',\n\t\t'type' => 'Tipo',\n\t\t'action' => 'Ação',\n\t\t'user' => 'Usuário',\n\t\t'truncate' => 'Log Vazio',\n\t],\n\t'login' => [\n\t\t'username' => 'Usuário',\n\t\t'password' => 'Senha',\n\t\t'language' => 'Idioma',\n\t\t'login' => 'Login',\n\t\t'logout' => 'Sair',\n\t\t'profile_lng' => 'Idioma padrão',\n\t\t'forgotpwd' => 'Perdeu sua senha?',\n\t\t'presend' => 'Resetar senha',\n\t\t'email' => 'Endereço de E-mail',\n\t\t'remind' => 'Resetar minha senha',\n\t\t'usernotfound' => 'Úsuario não encontrado',\n\t\t'backtologin' => 'Voltar ao Login',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Olá,\\\\n\\\\n sua conta de e-mail {EMAIL}\\\\n foi criada com sucesso.\\\\n\\\\nEsta é uma mensagem automática\\\\neMail, por favor não responda!\\\\n\\\\nAtenciosamente, Equipe de desenvolvimento do froxlor',\n\t\t\t'subject' => 'Conta de e-mail criada com sucesso!',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Olá {FIRSTNAME} {NAME},\\\\n\\\\nseguem os detalhes de sua nova conta de e-mail:\\\\n\\\\nUsuario: {USERNAME}\\\\nSenha: {PASSWORD}\\\\n\\\\nObrigado,\\\\nEquipe de desenvolvimento do froxlor',\n\t\t\t'subject' => 'Informações da conta',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Oi,\\\\n\\\\nSua conta de email {EMAIL}\\\\nfoi configurada corretamente.\\\\nSua senha é{PASSWORD}.\\\\n\\\\nEmail criado automaticamente\\\\n, Por favor não responda!\\\\n\\\\nCumprimentos, Equipe froxlor.',\n\t\t\t'subject' => 'Conta de email criada com sucesso',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Reset de Senha',\n\t\t\t'mailbody' => 'Oi {USERNAME},\\\\n\\\\nsua senha do froxlor foi resetada!\\\\nA nova senha é: {LINK}\\\\n\\\\nObrigado,\\\\nequipe froxlor',\n\t\t],\n\t],\n\t'menu' => [\n\t\t'message' => 'Mensagens',\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Principal',\n\t\t\t'changepassword' => 'Trocar senha',\n\t\t\t'changelanguage' => 'Trocar idioma',\n\t\t\t'username' => 'Logado como',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'E-mail',\n\t\t\t'emails' => 'Endereços',\n\t\t\t'webmail' => 'WebMail',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Banco de dados',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domínios',\n\t\t\t'settings' => 'Configurações',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Contas',\n\t\t\t'webftp' => 'WebFTP',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extras',\n\t\t\t'directoryprotection' => 'Diretório protegido',\n\t\t\t'pathoptions' => 'Opções de caminhos',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Tráfego',\n\t\t\t'current' => 'Mês corrente',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'Configurações do PHP',\n\t\t],\n\t\t'logger' > [\n\t\t\t'logger' => 'Sistema-Log',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'Email não enviado porque não tem destinatário no banco de dados',\n\t\t'success' => 'Mensagens enviadas para %s destinatários com sucesso',\n\t],\n\t'mysql' => [\n\t\t'description' => 'Aqui você pode criar e alterar seus bancos de dados MySQL.<br />As alterações são instantâneas e podem ser utilizadas imediatamente depois de salvas.<br />No menu do lado esquerdo você pode encontrar a ferramenta phpMyAdmin e com ela facilmente administrar seus bancos de dados.<br /><br />Para usar seu banco de dados com scripts em PHP use as seguintes configurações: (Os dados em <i>italico</i> devem ser substituidos pelo equivalente do banco de dados que você criou!)<br />Hostname: <b><SQL_HOST></b><br />Usuario: <b><i>Nome do banco de dadose</i></b><br />Senha: <b><i>a senha que você escolheu</i></b><br />Banco de dados: <b><i>Nome do banco de dados',\n\t\t'databasename' => 'Usuario / Nome do banco de dados',\n\t\t'databasedescription' => 'Descrição do banco de dados',\n\t\t'database_create' => 'Criar banco de dados',\n\t],\n\t'panel' => [\n\t\t'edit' => 'Editar',\n\t\t'delete' => 'Deletar',\n\t\t'create' => 'Criar',\n\t\t'save' => 'Salvar',\n\t\t'yes' => 'Sim',\n\t\t'no' => 'Não',\n\t\t'emptyfornochanges' => 'Sair sem salvar',\n\t\t'emptyfordefault' => 'Restaurar padrão',\n\t\t'path' => 'Caminho',\n\t\t'toggle' => 'Toggle',\n\t\t'next' => 'Próximo',\n\t\t'dirsmissing' => 'Directório não disponível ou ilegível',\n\t\t'urloverridespath' => 'URL (Caminho Completo)',\n\t\t'pathorurl' => 'Caminho ou URL',\n\t\t'ascending' => 'Crescente',\n\t\t'descending' => 'Decrescente',\n\t\t'search' => 'Procurar',\n\t\t'used' => 'Usado',\n\t\t'translator' => 'Tradutor',\n\t\t'reset' => 'Descartar Mudanças',\n\t\t'pathDescription' => 'Se o diretório não existir, será criado automaticamente',\n\t\t'back' => 'Volta',\n\t\t'reseller' => 'Revenda',\n\t\t'admin' => 'Administrador',\n\t\t'customer' => 'Cliente(s)',\n\t\t'send' => 'Enviar',\n\t\t'nosslipsavailable' => 'Não existem atualmente IP SSL / Porta para este servidor.',\n\t\t'backtooverview' => 'Voltar para Visão Geral',\n\t\t'dateformat' => 'AAAA-MM-DD',\n\t\t'dateformat_function' => 'A-m-d',\n\t\t'timeformat_function' => 'H:i:S',\n\t\t'default' => 'Padrão',\n\t\t'never' => 'Nunca',\n\t\t'active' => 'Ativo',\n\t\t'please_choose' => 'Por favor escolha',\n\t\t'allow_modifications' => 'Aceita alteraçoes',\n\t\t'not_supported' => 'Não suportado em:',\n\t\t'view' => 'Visualizar',\n\t],\n\t'pwdreminder' => [\n\t\t'success' => 'Redefinição de senha com sucesso. <br /> Você agora deve receber um e-mail com sua nova senha.',\n\t\t'notallowed' => 'Reset de senhas está desativado',\n\t],\n\t'question' => [\n\t\t'question' => 'Pergunta de segurança',\n\t\t'admin_customer_reallydelete' => 'Você realmente deseja deletar o cliente %s? Este comando não poderá ser cancelado!',\n\t\t'admin_domain_reallydelete' => 'Você realmente deseja deletar o domínio %s?',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Você realmente deseja desativar estas configurações de segurança (OpenBasedir)?',\n\t\t'admin_admin_reallydelete' => 'Você realmente deseja deletar o administrador %s? Todos clientes e domínios serão realocados para o administrador principal.',\n\t\t'admin_template_reallydelete' => 'Você realmente deseja deletar o template \\'%s\\'?',\n\t\t'domains_reallydelete' => 'Você realmente deseja deletar o domínio %s?',\n\t\t'email_reallydelete' => 'Você realmente deseja deletar o e-mail %s?',\n\t\t'email_reallydelete_account' => 'Você realmente deseja deletar a conta de e-mail %s?',\n\t\t'email_reallydelete_forwarder' => 'Você realmente deseja deletar o redirecionamento %s?',\n\t\t'extras_reallydelete' => 'Você realmente deseja deletar a proteção do diretório %s?',\n\t\t'extras_reallydelete_pathoptions' => 'Você realmente deseja deletar o caminho %s?',\n\t\t'ftp_reallydelete' => 'Você realmente deseja deletar a conta de FTP %s?',\n\t\t'mysql_reallydelete' => 'Você realmente deseja deletar o banco de dados %s? Este comando não poderá ser cancelado!',\n\t\t'admin_configs_reallyrebuild' => 'Está certo que quer deixar reconfigurar os ficheiros de configuração de Apache e Bind?',\n\t\t'admin_customer_alsoremovefiles' => 'Remover arquivos do usuário também?',\n\t\t'admin_customer_alsoremovemail' => 'Remover todos os dados de e-mail do sistema de arquivos?',\n\t\t'admin_customer_alsoremoveftphomedir' => 'Remover o diretório home do usuário FTP?',\n\t\t'admin_ip_reallydelete' => 'Você realmente deseja deletar este endereço IP?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'É você certo, você quer a raiz do original para este domínio, não estando dentro do customerroot do cliente?',\n\t\t'admin_counters_reallyupdate' => 'Você deseja recalcular os recursos utilizados?',\n\t\t'logger_reallytruncate' => 'Você realmente deseja dividir a tabela \"%s\"?',\n\t\t'admin_quotas_reallywipe' => 'Você realmente deseja limpar todas as quotas na tabela  mail_users? Isto não pode ser revertido',\n\t\t'phpsetting_reallydelete' => 'Você realmente deseja apagar esta configuração? Todos os domínios que atualmente utilizam esta configuração serão alterada para a configuração padrão.',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Tempo esgotado',\n\t\t\t'description' => 'Quanto tempo o usuario deve estar inativo para ser desconectado (segundos)?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Prefixo do cliente',\n\t\t\t'description' => 'Qual o prefixo \"customeraccounts\" deve ter?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'SQL Prefixo',\n\t\t\t'description' => 'Qual prefixo as contas mysql devem ter?',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP Prefixo',\n\t\t\t'description' => 'Qual prefixo as contas de FTP devem ter?',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Diretório de documentação',\n\t\t\t'description' => 'Aonde os documentos dever ser gravados?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Diretório de LOG',\n\t\t\t'description' => 'Aonde os arquivos de log dever ser gravados?',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'Endereços de IP',\n\t\t\t'description' => 'Quais os Endereços IP deste servidor?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Hostname',\n\t\t\t'description' => 'Qual o Hostname deste servidor?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Comando de reiniciar o Apache',\n\t\t\t'description' => 'Qual o comando para reiniciar o apache?',\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => 'Habilitar Servidor de Nomes',\n\t\t\t'description' => 'Aqui o servidor de nomes pode ser habilitado ou desabilitado globalmente.',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Diretório de configuração do Bind',\n\t\t\t'description' => 'Aonde estão os arquivos de configuração do bind?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Comando de reiniciar o Bind',\n\t\t\t'description' => 'Qual o comando para reiniciar o bind?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'Mails-Uid',\n\t\t\t'description' => 'Qual UserID os e-mails devem ter?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mails-Gid',\n\t\t\t'description' => 'Qual GroupID os e-mails devem ter?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Mails-Homedir',\n\t\t\t'description' => 'Aonde os e-mails devem ser gravados?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Remetente',\n\t\t\t'description' => 'Qual o remetente dos e-mails enviados pelo painel?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin URL',\n\t\t\t'description' => 'Qual a URL do phpMyAdmin? (deve iniciar com http://)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'WebMail URL',\n\t\t\t'description' => 'Qual a URL do WebMail? (deve iniciar com http://)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP URL',\n\t\t\t'description' => 'Qual a URL do WebFTP? (deve iniciar com http://)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Qual o idioma padrão do servidor?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Tentativas maximas de Login',\n\t\t\t'description' => 'Tentativas maximas de Login para a conta ser desativada.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Tempo que a conta deve permanecer desativada',\n\t\t\t'description' => 'Tempo (sec.) qua a conta permanece desativada depois de muitas tentativas de login.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'File-Método de entrada',\n\t\t\t'description' => 'A escolha do file tem que ser feita através do Dropdown-Menu ou pode ser feita manualmente?',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Entradas por pagina',\n\t\t\t'description' => 'Quantas entradas devem ser mostradas por pagina? (0 = desabilitar paginas)',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Servidores DNS',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'Servidores de Email',\n\t\t\t'description' => 'Uma lista separada por vírgulas que contém o numero de prioridade e o hostname separados por um espaço (por exemplo: \\'mx.example.com 10 \\'), contendo os servidores mx.',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'IP/Porta Padrão',\n\t\t\t'description' => 'Qual é a IP/Porta Padrão?',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Caminho para adicionar OpenBasedir',\n\t\t\t'description' => 'Estes caminhos (separados por dois pontos) serão acrescentados ao OpenBasedir em cada vhost.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Usar classificação natural na visualização',\n\t\t\t'description' => 'Ordenar listas como: web1 -> web2 -> web11 ao invéz de web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Docroots desativado para usuários',\n\t\t\t'description' => 'Quando um usuário estiver desativado, esse caminho é usado como seu docroot. Deixe em branco para não criar um vhost a todos.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Salva as senhas de usuários sempre criptografia no banco de dados',\n\t\t\t'description' => 'Se você selecionar sim, todas as senhas serão guardadas descriptografadas (Poderá ser lido por todos com acesso ao banco de dados ) na tabela mail_users-table. Somente ative essa opção se você realmente precise!',\n\t\t\t'removelink' => 'Clique aqui para limpar todas as senhas não criptografadas da tabela<br />Você realmente deseja limpar todas as senhas não encriptadas a partir da tabela mail_users? Isto não pode ser revertido!',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'Contas FTP @domínio',\n\t\t\t'description' => 'Clientes podem criar contas de FTP user@domíniodocliente?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Incluir PHP via mod_fcgid/suexec',\n\t\t\t'description' => 'Use mod_fcgid/suexec/libnss_mysql to run PHP with the corresponding useraccount.<br/><b>This needs a special Apache configuration. All following options are only valid if the module is enabled.</b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Diretório de configuração',\n\t\t\t\t'description' => 'Aonde todos os arquivos de configuração do fcgid vão ser guardados? Se você não utiliza um binário compilado, está é uma situação normal, deve estar dentro de /var/www/',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Diretório Temporário',\n\t\t\t\t'description' => 'Aonde os arquivos temporários devem ser guardados',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Processos por domínio',\n\t\t\t\t'description' => 'Quantos processos devem ser iniciadas / permitidas por domínio? O valor 0 é recomendado. O PHP irá então gerir a quantidade de processos.',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper in Vhosts',\n\t\t\t\t'description' => 'Como os  wrapper vão ser incluídos nos vhosts',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Diretórios globais do PEAR',\n\t\t\t\t'description' => 'Diretórios globais do PEAR que deverão ser substituídos em cada configuração php.ini? Diferentes diretórios devem ser separados por dois pontos.',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Máximo de solicitações por Domínio',\n\t\t\t\t'description' => 'Quantas solicitações serão aceitas por domínio?',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Utilize endereço de e-mail alternativo',\n\t\t\t'description' => 'Enviar e-mail a senha para um endereço diferente durante uma criação de conta de e-mail',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Arquivo/Diretório de configurações do Apache Vhost',\n\t\t\t'description' => 'Onde as configuração de Vhost devem ser guardadas? Você pode especificar um arquivo (todos os vhosts em um arquivo) ou diretório (cada vhost com seu próprio arquivo) aqui.',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Configuração de diretório do Apache Arquivo/Nome do Diretório.',\n\t\t\t'description' => 'Quando as opções de configuração de diretório deve ser armazenada? Você poderia especificar um arquivo (todas as opções em um arquivo) ou diretório ( cada opção no seu próprio arquivo).',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Apache htpasswd dirname',\n\t\t\t'description' => 'Onde deve ser o diretório de arquivos htpasswd?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'Hosts de Acesso MySQL',\n\t\t\t'description' => 'Uma lista separada por vírgulas de hosts a partir do qual os utilizadores devem ter a possibilidade de conectar-se ao MySQL-Server.',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Saida do Webalizer',\n\t\t\t'description' => 'Modo verbose do webalizer',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Habilitar/Desabilitar Logs',\n\t\t\t'severity' => 'Nível de Logs',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Tipos de Log(s)',\n\t\t\t\t'description' => 'Especificar tipos de logs separados por vírgula.<br />Tipos de lógs disponíveis: syslog, file, mysql',\n\t\t\t],\n\t\t\t'logfile' => 'Caminho do Arquivo de Log incluindo nome de arquivo',\n\t\t\t'logcron' => 'Logar tarefas do cron',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Nunca',\n\t\t\t\t'once' => 'Uma vez',\n\t\t\t\t'always' => 'Sempre',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'openssl_cnf' => 'Padrão para criar o arquivo de certificado',\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Configuração de Vhost padrão',\n\t\t\t'description' => 'O conteúdo deste campo será incluído a cada novo vhost criado. Atenção: O código será checado para algum erro. Se contiver erros, o apache pode não iniciar mais',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Quota de Email',\n\t\t\t'description' => 'Quota default para novas caixas criadas',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Usar quota para clientes',\n\t\t\t'description' => 'Ative para usar cotas em caixas de email. Padrão é <b>Não</b> visto que requer uma configuração especial.',\n\t\t\t'removelink' => 'Clique aqui para limpar todas as quotas para as contas de email.',\n\t\t],\n\t\t'decimal_places' => 'Número de casas decimais no tráfego / espaço de paginas web',\n\t\t'webalizer_enabled' => 'Ativar estatísticas webalizer',\n\t\t'awstats_enabled' => 'Ativar estatísticas awstats',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Configurações DNS-Domiio personalizadas',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Aceita clientes para editar configurações de DNS',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Usar nomes compatíveis com UNIX',\n\t\t\t'description' => 'Aceita você usar <strong>-</strong> and <strong>_</strong> em nomes de usuários se <strong>No</strong>estiver marcado',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Aceita reset de senha por clientes',\n\t\t\t'description' => 'Os clientes podem redefinir sua senha e  serão enviadas para seu endereço de e-mail',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Ativa reset de senhas pelos administradores',\n\t\t\t'description' => 'Admins / Revendedor pode redefinir sua senha e a nova senha será enviada para seu endereço de e-mail',\n\t\t],\n\t\t'index_file_extension' => [\n\t\t\t'description' => 'Qual extensão deve ser utilizada para o índice no arquivo recém-criado no diretório do cliente? Esta extensão será utilizado, se você ou um de seus administradores criou o seu próprio índice no arquivo modelo.',\n\t\t\t'title' => 'Extensão do arquivo recém-criado no Ãndice do diretório do cliente.',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Ativa login múltiplo',\n\t\t\t'description' => 'Se ativado um usuário pode ter múltiplos logins',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Ativa mover domínios entre admins',\n\t\t\t'description' => 'If activated you can change the admin of a domain at domainsettings.<br /><b>Attention:</b> If a customer isn\\'t assigned to the same admin as the domain, the admin can see every other domain of that customer!',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Ativa mover domínios entre clientes',\n\t\t\t'description' => 'Se ativado você pode trocar o cliente de um domínio para administração de outro.<br /><b>Attention:</b> froxlor não troca nenhum caminho. Isto pode fazer com que domínios parem de funcionar',\n\t\t],\n\t],\n\t'traffic' => [\n\t\t'month' => 'Mês',\n\t\t'day' => 'Diariamente',\n\t\t'months' => [\n\t\t\t1 => 'Janeiro',\n\t\t\t2 => 'Fevereiro',\n\t\t\t3 => 'Março',\n\t\t\t4 => 'Abril',\n\t\t\t5 => 'Maio',\n\t\t\t6 => 'Junho',\n\t\t\t7 => 'Julho',\n\t\t\t8 => 'Agosto',\n\t\t\t9 => 'Setembro',\n\t\t\t10 => 'Outubro',\n\t\t\t11 => 'Novembro',\n\t\t\t12 => 'Dezembro',\n\t\t],\n\t\t'mb' => 'Tráfego (MB)',\n\t\t'distribution' => '<font color=\"#019522\">FTP</font> | <font color=\"#0000FF\">HTTP</font> | <font color=\"#800000\">E-Mail</font>',\n\t\t'sumhttp' => 'Resumo Tráfego de HTTP em',\n\t\t'sumftp' => 'Resumo Tráfego de FTP em',\n\t\t'summail' => 'Resumo Tráfego de HTTP em',\n\t],\n\t'translator' => 'Ricardo Luiz Costa, Rafael Andrade, Thiago Goncalves de Castro',\n];\n"
  },
  {
    "path": "lng/se.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @author     Staffan Starberg <staff@starberg.com>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'admin' => [\n\t\t'overview' => 'Översikt',\n\t\t'ressourcedetails' => 'Använda resurser',\n\t\t'systemdetails' => 'System Detaljer',\n\t\t'froxlordetails' => 'froxlor Detaljer',\n\t\t'installedversion' => 'Installerad version av froxlor',\n\t\t'latestversion' => 'Senaste version av froxlor',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => '[Sök senaste verison av froxlor via Internet]',\n\t\t\t'error' => 'Fel vid läsning, kontrollera uppkopplingen mot froxlor',\n\t\t],\n\t\t'resources' => 'Resurser',\n\t\t'customer' => 'Kunder',\n\t\t'customers' => 'Kunder',\n\t\t'customer_add' => '[Skapa en ny kund]',\n\t\t'customer_edit' => 'Ändra ny kund',\n\t\t'domains' => 'Domäner',\n\t\t'domain_add' => '[Skapa en ny domän]',\n\t\t'domain_edit' => 'Tillåt ändring av domänen',\n\t\t'subdomainforemail' => 'Sub-domän som E-postdomän (Subdomains as emaildomains)',\n\t\t'admin' => 'Admin',\n\t\t'admins' => 'Admins',\n\t\t'admin_add' => '[Skapa en ny admin]',\n\t\t'admin_edit' => 'Ändra admin',\n\t\t'customers_see_all' => 'Kan se alla kunder?',\n\t\t'change_serversettings' => 'Kan ändra serverinställningar?',\n\t\t'server' => 'Systemet',\n\t\t'serversettings' => 'Inställningar',\n\t\t'rebuildconf' => 'Uppdatera konfig filer',\n\t\t'stdsubdomain' => 'Standard subdomän',\n\t\t'stdsubdomain_add' => '[Skapa en ny standard subdomän]',\n\t\t'phpenabled' => 'PHP påslagen',\n\t\t'deactivated' => 'Inaktiv',\n\t\t'deactivated_user' => 'Avaktivera användare',\n\t\t'sendpassword' => 'Skicka lösenord',\n\t\t'ownvhostsettings' => 'Egna vHost-Inställningar',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Konfiguration',\n\t\t\t'overview' => 'Översikt',\n\t\t\t'wizard' => 'Guide',\n\t\t\t'mail' => 'E-postserver (POP3/IMAP)',\n\t\t\t'smtp' => 'E-postserver (SMTP)',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Mallar',\n\t\t\t'template_add' => '[Lägg till en ny mall]',\n\t\t\t'template_edit' => 'Ändra en befintlig mall',\n\t\t\t'action' => 'Action',\n\t\t\t'email' => 'E-Post',\n\t\t\t'subject' => 'Rubrik (subjekt)',\n\t\t\t'mailbody' => 'E-Postinnehåll (Mail body)',\n\t\t\t'createcustomer' => 'E-Post till nya kunder (Välkommen)',\n\t\t\t'pop_success' => 'E-Post för nya konton (Välkommen)',\n\t\t\t'template_replace_vars' => 'Variabler som kan ändras i mallen:',\n\t\t\t'FIRSTNAME' => 'Ändra till kundens förnamn.',\n\t\t\t'NAME' => 'Ändra till kundens efternamn.',\n\t\t\t'USERNAME' => 'Ändra till kundens kontonamns användarnamn.',\n\t\t\t'PASSWORD' => 'Ändra till kundens kontonamns lösenord.',\n\t\t\t'EMAIL' => 'Ändra till adressen för POP3/IMAP kontot.',\n\t\t\t'TRAFFIC' => 'Ersatt med trafikbegrnsningen som var tilldelad till kunden.',\n\t\t\t'TRAFFICUSED' => 'Ersatt med trafikbegrnsningen som var överskriden av kunden.',\n\t\t\t'pop_success_alternative' => 'Välkommstmeddelande för nya E-post konton som skickas till den alternativa adressen',\n\t\t\t'EMAIL_PASSWORD' => 'Ersatt med POP3/IMAP kontots lösenord.',\n\t\t],\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IPs and Ports',\n\t\t\t'add' => '[Lägg till IP/Port]',\n\t\t\t'edit' => 'Ändra IP/Port',\n\t\t\t'create_listen_statement' => 'Skapa \"Listen statement\"',\n\t\t\t'create_namevirtualhost_statement' => 'Skapa NameVirtualHost statement',\n\t\t\t'create_vhostcontainer' => 'Skapa vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Skapa ServerName statement i vHost-Container',\n\t\t],\n\t\t'memorylimitdisabled' => 'Avstängd',\n\t\t'valuemandatory' => 'Denna ruta måste fyllas i',\n\t\t'valuemandatorycompany' => 'Fyll i &quot;förnamn&quot; och &quot;efternamn&quot; eller &quot;företagsnamn&quot;',\n\t\t'serversoftware' => 'Webserver version',\n\t\t'phpversion' => 'PHP-Version',\n\t\t'phpmemorylimit' => 'PHP-Minnesgräns',\n\t\t'mysqlserverversion' => 'MySQL Server Version',\n\t\t'mysqlclientversion' => 'MySQL Klient Version',\n\t\t'webserverinterface' => 'Webserver Interface',\n\t\t'accountsettings' => 'Kontoinställningar',\n\t\t'panelsettings' => 'Panelinställningar',\n\t\t'systemsettings' => 'Systeminställningar',\n\t\t'webserversettings' => 'Webserverinställningar',\n\t\t'mailserversettings' => 'E-postserverinställningar',\n\t\t'nameserversettings' => 'Namnserverinställningar',\n\t\t'updatecounters' => 'Uppdatera status',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Aldrig',\n\t\t\t'choosableno' => 'Valbar, standardvärdet är Nej',\n\t\t\t'choosableyes' => 'Valbar, standardvärdet är Ja',\n\t\t\t'always' => 'Alltid',\n\t\t],\n\t\t'webalizersettings' => 'Webalizer inställningar',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normal',\n\t\t\t'quiet' => 'Tyst',\n\t\t\t'veryquiet' => 'Väldigt tyst',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Det går inte att skapa en ny domän innan det finns mins en upplagd kund.',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Gammalt lösenord',\n\t\t'new_password' => 'Nytt lösenord',\n\t\t'new_password_confirm' => 'Nytt lösenord (verifiera)',\n\t\t'new_password_ifnotempty' => 'Nytt lösenord (Tomt fältet = inga ändringar)',\n\t\t'also_change_ftp' => ' Ändra även lösenord för huvud FTP kontot',\n\t\t'also_change_stats' => ' Ändra även lösenord för statistik',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Hemkatalog',\n\t\t'name' => 'Efternamn',\n\t\t'firstname' => 'Förnamn',\n\t\t'company' => 'Företag',\n\t\t'street' => 'Postadress',\n\t\t'zipcode' => 'Postnummer',\n\t\t'city' => 'Postort',\n\t\t'phone' => 'Telefon',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'E-post',\n\t\t'customernumber' => 'Kundnummer',\n\t\t'diskspace' => 'Webb (MB)',\n\t\t'traffic' => 'Trafik (GB)',\n\t\t'mysqls' => 'SQL_DBas',\n\t\t'emails' => 'E-post_adresser',\n\t\t'accounts' => 'E-post_konton',\n\t\t'forwarders' => 'E-post_skicka_vidare',\n\t\t'ftps' => 'FTP_Kto',\n\t\t'subdomains' => 'Sub-Domäner',\n\t\t'domains' => 'Domäner',\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'OpenBasedir-path',\n\t\t'docroot' => 'Sökvägen från ovanstående fält',\n\t\t'homedir' => 'Hemkatalog',\n\t],\n\t'domains' => [\n\t\t'description' => 'Här kan du skapa (sub-)domäner och ändra i dem.<br />Systemet behöver dock lite tid på sig att genomföra ändringarna.',\n\t\t'domainsettings' => 'Domäninställningar',\n\t\t'domainname' => 'Domännamn',\n\t\t'subdomain_add' => '[Skapa en ny subdomän]',\n\t\t'subdomain_edit' => 'Ändra (sub)domän',\n\t\t'wildcarddomain' => 'Skapa som ospecificerad domän (Create as wildcarddomain?)',\n\t\t'aliasdomain' => 'Alias for domän',\n\t\t'noaliasdomain' => '(inget alias)',\n\t\t'hasaliasdomains' => 'Domänen har redan alias',\n\t\t'statstics' => 'Användarstatistik',\n\t\t'isassigneddomain' => 'Tilldelad domän ',\n\t],\n\t'emails' => [\n\t\t'description' => 'Här kan du skapa eller ändra dina E-postadresser.<br />Ett konto är som en brevlåda utanför huset. Om någon skickar dig E-post kommer det att hamna i din brevlåda (ditt konto).<br /><br />För att hämta din E-post så skall du använda följande inställningar i ditt E-postprogram: (Text i kursiv stil <i>italics</i> måste ändras till det som motsvaras av det du knappade in tidigare!)<br />Servernamn (Hostname): <b><i>Domännamn (Domainname)</i></b><br />Användarnamn (Username): <b><i>Konto namn (Account name) / E-postadress</i></b><br />Lösenord (Password): <b><i>lösenordet som du valde</i></b>',\n\t\t'emailaddress' => 'E-postadress',\n\t\t'emails_add' => '[Skapa en E-postadress]',\n\t\t'emails_edit' => 'Ändra E-postadressen',\n\t\t'catchall' => 'Maildump',\n\t\t'iscatchall' => 'Skapa en maildump?',\n\t\t'account' => 'Konto',\n\t\t'account_add' => 'Skapa konto',\n\t\t'account_delete' => 'Radera konto',\n\t\t'from' => 'Från',\n\t\t'to' => 'Till',\n\t\t'forwarders' => 'Skicka vidare:',\n\t\t'forwarder_add' => '[Skapa ny \"skicka vidare\"]',\n\t\t'alternative_emailaddress' => 'Alternative e-mail-address',\n\t],\n\t'error' => [\n\t\t'error' => 'Följande fel har uppstått',\n\t\t'directorymustexist' => 'Katalogen %s måste finnas. Skapa den med ditt FTP program.',\n\t\t'filemustexist' => 'Filen %s måste existera.',\n\t\t'allresourcesused' => 'Du har redan skapt så många konton som du har tillstånd till.',\n\t\t'domains_cantdeletemaindomain' => 'Du kan inte radera en domän som användes för E-post.',\n\t\t'domains_canteditdomain' => 'Endast administratörer kan ändra denna domän.',\n\t\t'domains_cantdeletedomainwithemail' => 'Du kan inte radera en domän som användes för E-post. Radera alla E-postadresser först',\n\t\t'firstdeleteallsubdomains' => 'Du måste radera alla sub-domäner innan du kan skapa en maildump (wildcard domain).',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Du har redan skapat en maildump för denna domän.',\n\t\t'ftp_cantdeletemainaccount' => 'Det går inte att radera huvud FTP kontot för domänen',\n\t\t'login' => 'Användarnamnet eller lösenordet var felaktigt, försök igen!',\n\t\t'login_blocked' => 'Kontot har blivit avstängt på grund av för många felaktiga inloggningsförsök. <br />Försök igen om %s sekunder.',\n\t\t'notallreqfieldsorerrors' => 'Du har inte fyllt i alla fält eller så har du skrivit in något som inte accepteras.',\n\t\t'oldpasswordnotcorrect' => 'Det gamla lösenordet är fel.',\n\t\t'youcantallocatemorethanyouhave' => 'Du kan inte skapa fler resurser än du äger själv (You cannot allocate more resources than you own for yourself).',\n\t\t'mustbeurl' => 'Du har inte skrivit in en korrekt url (e.g. http://somedomain.com/error404.htm)',\n\t\t'invalidpath' => 'Du har inte valt en korrekt url (Kanske har du lagt till en katalogsäkerhet så att katalogerna inte kan visas?)',\n\t\t'stringisempty' => 'Du måste skriva in något i fältet',\n\t\t'stringiswrong' => 'Fel inatningsfält',\n\t\t'newpasswordconfirmerror' => 'New password and confirmation does not match',\n\t\t'mydomain' => '\\'Domain\\'',\n\t\t'mydocumentroot' => '\\'Documentroot\\'',\n\t\t'loginnameexists' => 'Login-Name %s är upptaget',\n\t\t'emailiswrong' => 'E-post-Adressen \"%s\" innehåller ogiltiga tecken eller så är den inte komplett',\n\t\t'loginnameiswrong' => 'Login-Namnet %s innehåller ogiltiga tecken',\n\t\t'userpathcombinationdupe' => 'Användarnamnet och sökvägen tillsammans finns redan',\n\t\t'patherror' => 'Generellt Fel! sökvägen till katalogen kan inte vara tom',\n\t\t'errordocpathdupe' => 'Option för sökvägen %s finns redan',\n\t\t'adduserfirst' => 'Skapa användaren först',\n\t\t'domainalreadyexists' => 'Domänen %s ägs redan av en kund',\n\t\t'nolanguageselect' => 'Inget språk är valt.',\n\t\t'nosubjectcreate' => 'Du måste ha ett rubrik för denna E-postmall.',\n\t\t'nomailbodycreate' => 'Du måste ha skrivit in en E-post text för denna mall.',\n\t\t'templatenotfound' => 'E-postmallen hittades inte.',\n\t\t'alltemplatesdefined' => 'Du kan inte skapa flera mallar, alla språk finns redan.',\n\t\t'wwwnotallowed' => 'www är inte tillåtet att använda för subdomäner.',\n\t\t'subdomainiswrong' => 'Subdomänen %s innehåller ogiltiga tecken.',\n\t\t'domaincantbeempty' => 'Fältet för domännamn får inte vara tommt.',\n\t\t'domainexistalready' => 'Domänen %s finns redan.',\n\t\t'domainisaliasorothercustomer' => 'Den valda domänen är antingen en aliasdomän eller så ägs den redan av en annan kund.',\n\t\t'emailexistalready' => 'E-postadressen %s finns redan.',\n\t\t'maindomainnonexist' => 'Huvuddomänen %s finns inte.',\n\t\t'destinationnonexist' => 'Skapa en forwarder i fältet \\'Destination\\'.',\n\t\t'destinationalreadyexistasmail' => 'Denna forwarder %s, finns redan som aktiv E-postadress.',\n\t\t'destinationalreadyexist' => 'Du har redan skapat en forwarder till %s .',\n\t\t'destinationiswrong' => 'Denna forwarder: %s innehåller ogiltiga tecken eller så är den inte komplett adress.',\n\t\t'ipstillhasdomains' => 'IP/Port kombinationen som du vill radera har fortfarande domäner anslutna till sig, Flytta dessa till någon annan IP/Port kombination innan du raderar denna IP/Port kombination.',\n\t\t'cantdeletedefaultip' => 'Det går inte att ta bort den förvalda återförsäljarens IP/Port kombination, Välj en annan IP/Port kombination som förval för återförsäljare innan du raderar denna IP/Port kombination.',\n\t\t'cantdeletesystemip' => 'Det går inte att radera den sista system IP:n, Antingen skapar man en ny IP/Port kombination för system IP eller så ändrar man system IP:n.',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'Port\\'',\n\t\t'myipdefault' => 'Man måste välja en IP/Port kombination som skall bli standardvärdet.',\n\t\t'myipnotdouble' => 'Denna IP/Port kombination finns redan.',\n\t\t'cantchangesystemip' => 'Man kan inte ändra den senaste system IP-adressen. Skapa en helt ny IP/Port kombination för system IP:n eller ändra system IP-adressen.',\n\t\t'sessiontimeoutiswrong' => 'Bara siffror &quot;Session Timeout&quot; är tillåtna.',\n\t\t'maxloginattemptsiswrong' => 'Bara siffror &quot;Max Login Attempts&quot; är tillåtna.',\n\t\t'deactivatetimiswrong' => 'Bara siffror &quot;Deactivate Time&quot; är tillåtna.',\n\t\t'accountprefixiswrong' => 'Det här &quot;Customerprefix&quot; är fel.',\n\t\t'mysqlprefixiswrong' => 'Det här &quot;SQL Prefix&quot; är fel.',\n\t\t'ftpprefixiswrong' => 'Det här &quot;FTP Prefix&quot; är fel.',\n\t\t'ipiswrong' => 'Den här &quot;IP-Address&quot; är fel. Endast en giltig IP-adress är tillåten.',\n\t\t'vmailuidiswrong' => 'Den här &quot;Mails-uid&quot; är fel. Endast numerisk UID är tillåtenis allowed.',\n\t\t'vmailgidiswrong' => 'Den här &quot;Mails-gid&quot; är fel. Endast numerisk GID är tillåtenis allowed.',\n\t\t'adminmailiswrong' => 'Den här &quot;Sender-address&quot; är fel. Endast en giltig E-postadress är tillåten.',\n\t\t'pagingiswrong' => 'Den här &quot;Entries per Page&quot;-värdet är fel. Endast siffror är tillåtna.',\n\t\t'phpmyadminiswrong' => 'Den här phpMyAdmin-link är inte en giltig länk.',\n\t\t'webmailiswrong' => 'Den här WebMail-link är inte en giltig länk.',\n\t\t'webftpiswrong' => 'Den här WebFTP-link är inte en giltig länk.',\n\t\t'stringformaterror' => 'Värdet för fältet &quot;%s&quot; har inte rätt format.',\n\t\t'loginnameissystemaccount' => 'Det går inte att skapa ett konto som liknar ett systemkonto (Om det till exempel börjar med &quot;%s&quot;). Vlj ett annat kontonamn.',\n\t\t'youcantdeleteyourself' => 'Av säkerhetsskäl går inte att redera ditt eget konto.',\n\t\t'youcanteditallfieldsofyourself' => 'Notera: Av säkerhetsskäl går det inte att ändra ditt eget konto.',\n\t\t'documentrootexists' => 'Katalogen &quot;%s&quot; finns redan hos den här kunden. Radera detta först innan kunden skapas igen.',\n\t\t'formtokencompromised' => 'Den säkra anslutningen till froxlor har avslutats och du har av säkerhetsskäl automatiskt loggats ur.',\n\t],\n\t'extras' => [\n\t\t'description' => 'Här kan du ändra övriga saker såsom katalogskydd mm.<br />Systemet behöver dock lite tid på sig att genomföra ändringarna.',\n\t\t'directoryprotection_add' => '[Skapa ett nytt katalogskydd]',\n\t\t'view_directory' => 'Visa kataloginnehåll',\n\t\t'pathoptions_add' => '[Skapa ny regel för sökvägar]',\n\t\t'directory_browsing' => 'Visning av katalogstruktur',\n\t\t'pathoptions_edit' => 'Ändra sökvägsinställningar',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Här kan du skapa eller änra i dina FTP konton.<br />Ändringen genomförs omedelbart så man kan använda det nya/ändrade kontot direkt.',\n\t\t'account_add' => '[Skapa ett nytt FTP konto]',\n\t],\n\t'index' => [\n\t\t'customerdetails' => 'Kunddetaljer',\n\t\t'accountdetails' => 'Kontodetaljer',\n\t],\n\t'login' => [\n\t\t'username' => 'Användarnamn',\n\t\t'password' => 'Lösenord',\n\t\t'language' => 'Språk',\n\t\t'login' => 'Logga in',\n\t\t'logout' => 'Logga ut',\n\t\t'profile_lng' => 'Profilspråk',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Hej,\\\\n\\\\nDitt E-postkonto {EMAIL}\\\\nhar nu skapats.\\\\n\\\\nDetta är ett automatgenererat E-post meddelande\\\\n, Det går därför inte att svara på detta meddelande!\\\\n',\n\t\t\t'subject' => 'E-postkontot är nu skapat',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Hej {FIRSTNAME} {NAME},\\\\n\\\\nHär kommer kontoinformationen för ditt konto:\\\\n\\\\nAnvändarnamn (Username): {USERNAME}\\\\nLösenord (Password): {PASSWORD}\\\\n\\\\n',\n\t\t\t'subject' => 'Kontoinformation',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Hej,\\\\n\\\\nditt E-postkonto {EMAIL}\\\\nhar ny skapats.\\\\nDitt lösenord är {PASSWORD}.\\\\n\\\\nDetta är ett automatgenererat E-postmeddelande som det INTE går att svara på!\\\\n\\\\nLycka till önskar, froxlor',\n\t\t\t'subject' => 'E-postkontot är nu skapat',\n\t\t],\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Huvudsidan',\n\t\t\t'changepassword' => 'Ändra lösenord',\n\t\t\t'changelanguage' => 'Ändra språk',\n\t\t\t'username' => 'Inloggad som: ',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'E-post',\n\t\t\t'emails' => 'E-post',\n\t\t\t'webmail' => 'WebMail',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Databaser',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domäner',\n\t\t\t'settings' => 'Inställningar',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Konton',\n\t\t\t'webftp' => 'WebFTP',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extras',\n\t\t\t'directoryprotection' => 'Katalog säkerhet',\n\t\t\t'pathoptions' => 'Inställningar sökväg',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Trafik',\n\t\t\t'current' => 'Nuvarande månad',\n\t\t],\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'Användare/databasnamn',\n\t\t'databasedescription' => 'Beskrivning av databasen',\n\t\t'database_create' => '[Skapa en ny databas]',\n\t\t'description' => 'Här ändras eller skapas MySQL-Databaser.<br />Ändringen sker omedelbart och databasen kan användas direkt.<br />I menyn på vänster sida finns verktyget phpMyAdmin med vilket man enkelt kan ändra i sin databas.<br /><br />För att använda databasen i dina egna php-scripts använd följande inställningar: (Data med kursiv stil <i>italics</i> måste ändras till det du matat in!)<br />Servernamn (Hostname): <b><SQL_HOST></b><br />Användarnamn (Username): <b><i>Databsnamn (Databasename)</i></b><br />Lösenord (Password): <b><i>Lösenordet som du har valt</i></b><br />Databas (Database): <b><i>Databasnamn (Databasename)</i></b>',\n\t],\n\t'panel' => [\n\t\t'edit' => 'Ändra',\n\t\t'delete' => 'Radera',\n\t\t'create' => 'Skapa',\n\t\t'save' => 'Spara',\n\t\t'yes' => 'Ja',\n\t\t'no' => 'Nej',\n\t\t'emptyfornochanges' => 'Tomt fält = ingen ändring',\n\t\t'emptyfordefault' => 'Förvalt värde används om fältet lämnas tommt',\n\t\t'path' => 'Sökväg (Path)',\n\t\t'toggle' => 'Växla (Toggle)',\n\t\t'next' => 'nästa',\n\t\t'dirsmissing' => 'Kan inte hitta eller läsa katalogen!',\n\t\t'urloverridespath' => 'URL (skriver över sökvägen)',\n\t\t'pathorurl' => 'Sökväg eller URL',\n\t\t'ascending' => 'Stigande',\n\t\t'descending' => 'Fallande',\n\t\t'search' => 'Sök',\n\t\t'used' => 'använd',\n\t\t'translator' => 'Översättare',\n\t\t'reset' => 'Avbryt ändringarna',\n\t\t'pathDescription' => 'Katalogen kommer att skapas om den inte redan finns.',\n\t\t'back' => 'Tillbaka',\n\t],\n\t'question' => [\n\t\t'question' => 'Säkerhetsfråga',\n\t\t'admin_customer_reallydelete' => 'Är du säker på att du vill radera kunden %s? Om du väljer att radera går det inte att ångra sig efteråt!',\n\t\t'admin_domain_reallydelete' => 'Är du riktigt säker på att du vill radera domänen %s?',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Är du riktigt säker på att du vill avaktivera säkerhetsinställningarna (OpenBasedir and/or SafeMode)?',\n\t\t'admin_admin_reallydelete' => 'Är du riktigt säker på att du vill radera adminkontot %s? Alla kunder och domäner kommer att flyttas till ditt konto istället.',\n\t\t'admin_template_reallydelete' => 'Är du riktigt säker på att du vill radera mallen \\'%s\\'?',\n\t\t'domains_reallydelete' => 'Är du riktigt säker på att du vill radera domänen %s?',\n\t\t'email_reallydelete' => 'Är du riktigt säker på att du vill radera E-postadressen %s?',\n\t\t'email_reallydelete_account' => 'Är du riktigt säker på att du vill radera E-postkontot %s?',\n\t\t'email_reallydelete_forwarder' => 'Är du riktigt säker på att du vill radera forwardern till %s?',\n\t\t'extras_reallydelete' => 'Är du riktigt säker på att du vill radera katalogsäkerheten (directory protection) för %s?',\n\t\t'extras_reallydelete_pathoptions' => 'Är du riktigt säker på att du vill radera katalogalternativen (path options) för %s?',\n\t\t'ftp_reallydelete' => 'Är du riktigt säker på att du vill radera FTP kontot %s?',\n\t\t'mysql_reallydelete' => 'Är du riktigt säker på att du vill radera databasen %s? Om du väljer att radera går det inte att ångra sig efteråt!',\n\t\t'admin_configs_reallyrebuild' => 'Är du riktigt säker på att du vill skapa nya konfigurationsfiler för apache och bind?',\n\t\t'admin_ip_reallydelete' => 'Är du säker på att du vill radera IP addressen %s?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Dokumentkatalogen för denna domän inte kommer att ligga under kundkatalogen, är du säker på att du vill ändra detta?',\n\t\t'admin_counters_reallyupdate' => 'Vill du uppdatera alla statusberäkningar för kunder och admins?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Är du säker på att du vill radera alla okrupterade lösenord från tabellen mail_users? Du kan INTE ändra dig efteråt!',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Sessionen har avslutats för att den tog för lång tid att utföra (session Timeout)',\n\t\t\t'description' => 'Tiden (i sekunder) som användaren får vara inaktiv innan han måste logga in igen är (seconds)?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Kund ID (Customer prefix)',\n\t\t\t'description' => 'Vilket prefix skall användas till ett kundkonto?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'SQL ID (SQL Prefix)',\n\t\t\t'description' => 'Vilket prefix skall användas till mysql?',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP ID (FTP Prefix)',\n\t\t\t'description' => 'Vilket prefix skall användas till ftp?',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Hemkatalog',\n\t\t\t'description' => 'Vilken sökväg skall det vara till hemkatalogen?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Loggfilernas hemkatalog (Logfiles directory)',\n\t\t\t'description' => 'Vilken sökväg skall det vara till loggfilernas hemkatalog?',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'IP-Adress',\n\t\t\t'description' => 'Vilken IP-adress har denna server?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Datornamn (Hostname)',\n\t\t\t'description' => 'Villket Datornamn (Hostname) har denna server?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Ladda om Apache kommandot (Apache reload)',\n\t\t\t'description' => 'Ange sökvägen till programmet som laddar om Apache (reload apache) konfigurationsfiler?',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Bind konfigurationskatalog (Bind config directory)',\n\t\t\t'description' => 'Vilken sökväg skall det vara till bind:s konfigurationsfiler?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Ange sökvägen till programmet som laddar om Bind (reload bind) konfigurationsfiler?',\n\t\t\t'description' => 'Ange sökvägen till programmet som laddar om Bind (reload bind) konfigurationsfiler?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'Mails-UID',\n\t\t\t'description' => 'Vilket användarID (UserID) ska E-posten ha?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mails-GID',\n\t\t\t'description' => 'Vilket gruppID (GroupID) ska E-posten ha?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'E-post hemkatalog',\n\t\t\t'description' => 'I vilken katalog skall E-posten sparas?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Avsändare',\n\t\t\t'description' => 'Vilken avsändaradress skall E-post från admin panelen ha?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin URL',\n\t\t\t'description' => 'Vilken URL är det till phpMyAdmin? (Måste börja med http(s)://)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'WebMail URL',\n\t\t\t'description' => 'Vilken URL är det till WebMail? (Måste börja med http(s)://)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP URL',\n\t\t\t'description' => 'Vilken URL är det till  WebFTP? (Måste börja med http(s)://)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Vilket språk skall användas som standardspråk?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Max antal Login försök',\n\t\t\t'description' => 'Maximalt antal inloggningsförsök innan kontot stängs av.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Avstängningstid',\n\t\t\t'description' => 'Tid (sec.) som kontot stängs av efter för många felaktiga försök.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Typ av (path input)',\n\t\t\t'description' => 'Skall en sökväg väljas i en rullist eller matas in för hand?',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Nameservers',\n\t\t\t'description' => 'En kommaseparerad lista med namnet (hostname) på alla DNS:er. Den första blir den första som söks (primary).',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'MX servers',\n\t\t\t'description' => 'En kommaseparerad lista med nummer och namn separerade men mellanslag (ex. \\'10 mx.example.com\\') innehåller mx servrarna.',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Antal rader per sida',\n\t\t\t'description' => 'Hur många rader skall det vara på en sida? (0 = Stäng av sidbrytning)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'Förvald IP/Port',\n\t\t\t'description' => 'Vilken är den förvalda IP/Port kombinationen?',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Sökväg att lägga till OpenBasedir',\n\t\t\t'description' => 'Dessa sökvägar (separerade med kolon) kommer att läggas till OpenBasedir-statement i alla  vhost-container.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Använd mänsklig sortertering i listvisning',\n\t\t\t'description' => 'Sorterar listan så här web1 -> web2 -> web11 istället för web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Dokumentroot för avstängda användare',\n\t\t\t'description' => 'När en användare är avstängd kommer denna sökväg att användas som dokumentroot. Lämna fältet tommt om du inte vill skapa någon vhost.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Spara även lösenord till E-postkonton okrypterade i databassen',\n\t\t\t'description' => 'Om du valt Ja så kommer alla lösenord att sparas okrypterade (klartext, fullt läsbara för alla som har rättigheter till databasen) i tabellen mail_users-table. Aktivera detta endast om du är säker på vad du gör!',\n\t\t\t'removelink' => 'Klicka här för att radera alla okrypterade lösenord från tabellen.',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'FTP konton @domain',\n\t\t\t'description' => 'Kunder kan skapa Ftp accounts user@customerdomain?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Inkludera PHP via mod_fcgid/suexec',\n\t\t\t'description' => 'Använd mod_fcgid/suexec/libnss_mysql för att köra PHP med tillhörande användarkonto.<br/><b>Denna inställning behöver en speciell apache-konfiguration!</b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'FCGI konfigurationskatalog',\n\t\t\t\t'description' => 'I vilken katalog skall alla fcgi-konfigurationfiler lagras?',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'FCGI temporärkatalog',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Använd en alternativ E-postadress',\n\t\t\t'description' => 'Skicka lösenord med E-post till adressen under email-account-creation',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Apache vhost konfiguration fil/katalognamn',\n\t\t\t'description' => 'Var skall vhost konfigurationen sparas? Det går att specificera alla vhost i en fil eller en katalog där alla filerna ligger (varje vhost i sin egen fil).',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Apache diroptions konfiguration fil/katalognamn',\n\t\t\t'description' => 'Var skall diroptions konfigurationen sparas? Det går att specificera alla diroptions i en fil eller en katalog där alla filerna ligger (varje diroptions i sin egen fil).',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Apache htpasswd katalognamn',\n\t\t\t'description' => 'Var skall htpasswd konfigurationen för katalogsäkerheten?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'description' => 'En kommaseparerad lista med datornamn som tillåts att kontakta MySQL servern.',\n\t\t],\n\t],\n\t'traffic' => [\n\t\t'month' => 'Månad',\n\t\t'day' => 'Dag',\n\t\t'months' => [\n\t\t\t1 => 'Januari',\n\t\t\t2 => 'Februari',\n\t\t\t3 => 'Mars',\n\t\t\t4 => 'April',\n\t\t\t5 => 'Maj',\n\t\t\t6 => 'Juni',\n\t\t\t7 => 'Juli',\n\t\t\t8 => 'Augusti',\n\t\t\t9 => 'September',\n\t\t\t10 => 'Oktober',\n\t\t\t11 => 'November',\n\t\t\t12 => 'December',\n\t\t],\n\t\t'mb' => 'Trafik (MB)',\n\t\t'distribution' => '<font color=\"#019522\">FTP</font> | <font color=\"#0000FF\">HTTP</font> | <font color=\"#800000\">Mail</font>',\n\t\t'sumhttp' => 'Summa HTTP-Trafik i',\n\t\t'sumftp' => 'Summa FTP-Trafik i',\n\t\t'summail' => 'Summa E-posttrafik i',\n\t],\n\t'translator' => 'Staffan Starberg',\n];\n"
  },
  {
    "path": "lng/sk.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'languages' => [\n\t\t'cz' => 'Čeština',\n\t\t'de' => 'Nemčina',\n\t\t'en' => 'Angličtina',\n\t\t'fr' => 'Francúzština',\n\t\t'hu' => 'Maďarčina',\n\t\t'it' => 'Taliančina',\n\t\t'nl' => 'Holandčina',\n\t\t'pt' => 'Portugalčina',\n\t\t'se' => 'Švédčina',\n\t\t'sk' => 'Slovenčina',\n\t\t'es' => 'Španielčina',\n\t\t'ca' => 'Katalánčina',\n\t\t'zh_CN' => 'Čínčina (Zjednodušená)',\n\t],\n\t'2fa' => [\n\t\t'2fa' => 'Možnosti 2FA',\n\t\t'2fa_enabled' => 'Aktivovať dvojfázové overenie (2FA)',\n\t\t'2fa_removed' => '2FA úspešne odobraté',\n\t\t'2fa_added' => '2FA úspešne aktivované<br><a class=\"alert-link\" href=\"%s?page=2fa\">Zobraziť podrobnosti 2FA</a>',\n\t\t'2fa_add' => 'Aktivovať 2FA',\n\t\t'2fa_delete' => 'Deaktivovať 2FA',\n\t\t'2fa_verify' => 'Overiť kód',\n\t\t'2fa_overview_desc' => 'Tu môžete aktivovať dvojfaktorové overovanie vášho účtu.<br><br>Môžete buď použiť autentifikátor-aplikáciu (jednorazové heslo / TOTP) alebo nechať froxlor po každom úspešnom prihlásení pomocou jednorazového hesla poslať e-mail na vašu e-mailovú adresu.',\n\t\t'2fa_email_desc' => 'Váš účet je nastavený na používanie jednorazových hesiel cez e-mail. Ak chcete deaktivovať, kliknite na \"Deaktivovať 2FA\"',\n\t\t'2fa_ga_desc' => 'Váš účet je nastavený tak, aby používal jednorazové heslá založené na čase prostredníctvom autentifikačnej aplikácie. Naskenujte nižšie uvedený QR kód pomocou požadovanej autentifikačnej aplikácie na generovanie kódov. Ak chcete deaktivovať, kliknite na \"Deaktivovať 2FA\"',\n\t\t'2fa_not_activated' => 'Dvojfázové overovanie nie je povolené',\n\t\t'2fa_not_activated_for_user' => 'Dvojfaktorové overenie nie je pre aktuálneho používateľa povolené',\n\t\t'type_2fa' => 'Stav 2FA',\n\t],\n\t'admin' => [\n\t\t'overview' => 'Prehľad',\n\t\t'ressourcedetails' => 'Využitie zdrojov',\n\t\t'systemdetails' => 'Podrobnosti o systéme',\n\t\t'froxlordetails' => 'Podrobnosti o froxlore',\n\t\t'installedversion' => 'Nainštalovaná verzia',\n\t\t'latestversion' => 'Najnovšia verzia',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => 'Vyhľadávať prostredníctvom webovej služby',\n\t\t\t'error' => 'Pri čítaní došlo k chybe',\n\t\t],\n\t\t'resources' => 'Zdroje',\n\t\t'customer' => 'Zákazník',\n\t\t'customers' => 'Zákazníci',\n\t\t'customers_list_desc' => 'Spravovať svojich zákazníkov',\n\t\t'customer_add' => 'Vytvoriť zákazníka',\n\t\t'customer_edit' => 'Upraviť zákazníka',\n\t\t'username_default_msg' => 'Nechajte prázdne pre automaticky generovanú hodnotu',\n\t\t'password_default_msg' => 'Automaticky vygenerované, ak je prázdne',\n\t\t'domains' => 'Domény',\n\t\t'domain_add' => 'Vytvoriť doménu',\n\t\t'domain_edit' => 'Upraviť doménu',\n\t\t'subdomainforemail' => 'Subdomény ako e-mailové domény',\n\t\t'admin' => 'Admin',\n\t\t'admins' => 'Administrátori',\n\t\t'admin_add' => 'Vytvoriť administrátora',\n\t\t'admin_edit' => 'Upraviť administrátora',\n\t\t'customers_see_all' => 'Môžete pristupovať k ďalším správcom/predajcom?',\n\t\t'change_serversettings' => 'Môže zmeniť nastavenia servera?',\n\t\t'server' => 'Systém',\n\t\t'serversettings' => 'Nastavenia',\n\t\t'serversettings_desc' => 'Spravujte váš systém froxlor',\n\t\t'rebuildconf' => 'Znovu vytvoriť konfiguračné súbory',\n\t\t'stdsubdomain' => 'Štandardná subdoména',\n\t\t'stdsubdomain_add' => 'Vytvoriť štandardnú subdoménu',\n\t\t'phpenabled' => 'PHP povolené',\n\t\t'deactivated' => 'Deaktivované',\n\t\t'deactivated_user' => 'Deaktivovať používateľa',\n\t\t'sendpassword' => 'Poslať heslo',\n\t\t'ownvhostsettings' => 'Vlastné nastavenia vHost-servera',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => 'Konfigurácia',\n\t\t\t'overview' => 'Prehľad',\n\t\t\t'wizard' => 'Sprievodca',\n\t\t\t'distribution' => 'Distribúcia',\n\t\t\t'service' => 'Služba',\n\t\t\t'daemon' => 'Daemon',\n\t\t\t'http' => 'Webový server (HTTP)',\n\t\t\t'dns' => 'Nameserver (DNS)',\n\t\t\t'mail' => 'Poštový server (IMAP/POP3)',\n\t\t\t'smtp' => 'Mailový server (SMTP)',\n\t\t\t'ftp' => 'FTP server',\n\t\t\t'etc' => 'Ostatné (Systém)',\n\t\t\t'choosedistribution' => '-- Vyberte distribúciu --',\n\t\t\t'chooseservice' => '-- Vyberte službu --',\n\t\t\t'choosedaemon' => '-- Zvoľte daemon --',\n\t\t\t'statistics' => 'Štatistiky',\n\t\t\t'compactoverview' => 'Kompaktný prehľad',\n\t\t\t'legend' => '<h3>Chystáte sa nakonfigurovať službu/daemon</h3>',\n\t\t\t'commands' => '<span class=\"text-danger\">Príkazy:</span> Tieto príkazy majú byť spustené riadok po riadku ako používateľ root v shelli. Je bezpečné skopírovať celý blok a vložiť ho do konzoly.',\n\t\t\t'files' => '<span class=\"text-danger\">Konfiguračné súbory:</span> Príkazy pred textovými poľami by mali otvoriť editor s cieľovým súborom. Stačí skopírovať a vložiť obsah do editora a uložiť súbor.<br><span class=\"text-danger\">Upozornenie:</span> MySQL heslo nebolo z bezpečnostných dôvodov nahradené. Prosím nahraďte \"FROXLOR_MYSQL_PASSWORD\" samostatne alebo použite formulár javascript nižšie na nahradenie na webe. Ak ste zabudli heslo k MySQL, nájdete ho v \"lib/userdata.inc.php\"',\n\t\t\t'importexport' => 'Importovať/Exportovať',\n\t\t\t'finishnote' => 'Súbor parametrov bol úspešne vygenerovaný. Teraz spustite nasledujúci príkaz ako root:',\n\t\t\t'description' => 'Konfigurácia systémových služieb',\n\t\t\t'minihowto' => 'Na tejto stránke môžete zobraziť rôzne konfiguračné šablóny pre každú službu, (opätovne)konfigurovať špecifické služby v prípade potreby alebo exportovať aktuálny výber do JSON súboru na použitie v skriptoch CLI alebo na inom serveri.<br><br><b>Poznámka</b>: zvýraznené služby neodrážajú aktuálne nastavenia, ale zobrazujú požiadavky/odporúčania z vašich aktuálnych hodnôt nastavení.',\n\t\t\t'skipconfig' => 'Ne(re)konfigurovať',\n\t\t\t'recommendednote' => 'Odporúčané/požadované služby na základe aktuálnych nastavení systému',\n\t\t\t'selectrecommended' => 'Vybrať odporúčané',\n\t\t\t'downloadselected' => 'Exportovať vybrané',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => 'Šablóny e-mailov',\n\t\t\t'template_add' => 'Pridať šablónu',\n\t\t\t'template_fileadd' => 'Pridať šablónu súboru',\n\t\t\t'template_edit' => 'Upraviť šablónu',\n\t\t\t'action' => 'Akcia',\n\t\t\t'email' => 'E-mailové a súborové šablóny',\n\t\t\t'subject' => 'Predmet',\n\t\t\t'mailbody' => 'Text e-mailu',\n\t\t\t'createcustomer' => 'Uvítací e-mail pre nových zákazníkov',\n\t\t\t'pop_success' => 'Uvítací e-mail pre nové e-mailové účty',\n\t\t\t'template_replace_vars' => 'Premenné, ktoré majú byť nahradené v šablóne:',\n\t\t\t'SALUTATION' => 'Nahradené správnym oslovením (meno alebo názov spoločnosti)',\n\t\t\t'FIRSTNAME' => 'Nahradené krstným menom zákazníka.',\n\t\t\t'NAME' => 'Nahradené menom zákazníka.',\n\t\t\t'COMPANY' => 'Nahrádza názov firmy zákazníka',\n\t\t\t'USERNAME' => 'Nahradí používateľským menom zákazníka.',\n\t\t\t'PASSWORD' => 'Nahradí heslom zákazníka.',\n\t\t\t'EMAIL' => 'Nahradené adresou účtu POP3/IMAP.',\n\t\t\t'CUSTOMER_NO' => 'Nahradí číslo zákazníka',\n\t\t\t'TRAFFIC' => 'Nahradené prenosom, ktorý bol priradený zákazníkovi.',\n\t\t\t'TRAFFICUSED' => 'Nahradené prenosom, ktorý bol zákazníkom vyčerpaný.',\n\t\t\t'pop_success_alternative' => 'Uvítací e-mail pre nové e-mailové účty odoslaný na alternatívnu adresu',\n\t\t\t'EMAIL_PASSWORD' => 'Nahradí heslom účtu POP3/IMAP.',\n\t\t\t'index_html' => 'indexový súbor pre novo vytvorené adresáre zákazníkov',\n\t\t\t'unconfigured_html' => 'súbor indexu pre nenastavené/neznáme domény',\n\t\t\t'unconfigured_content_fallback' => 'Táto doména vyžaduje konfiguráciu prostredníctvom panela pre správu servera froxlor, pretože momentálne nie je priradená žiadnemu zákazníkovi.',\n\t\t\t'file_extension' => [\n\t\t\t\t'description' => 'Prípona súboru pre index musí byť v rozsahu 1 až 6 znakov. Prípona môže obsahovať iba znaky ako a-z, A-Z a 0-9<br><br>Predvolená: html',\n\t\t\t\t'title' => 'Prípona súboru pre šablónu súboru',\n\t\t\t],\n\t\t\t'SERVERNAME' => 'Nahradené názvom servera.',\n\t\t\t'CUSTOMER' => 'Nahradené prihlasovacím menom zákazníka. Iba pre \"indexový súbor pre novo vytvorené adresáre zákazníkov\"',\n\t\t\t'ADMIN' => 'Nahradené prihlasovacím menom správcu. Iba pre \"indexový súbor pre novo vytvorené adresáre zákazníkov\"',\n\t\t\t'CUSTOMER_EMAIL' => 'Nahradené e-mailovou adresou zákazníka. Iba pre \"indexový súbor pre novo vytvorené adresáre zákazníkov\"',\n\t\t\t'ADMIN_EMAIL' => 'Nahradené e-mailovou adresou správcu. Iba pre \"indexový súbor pre novo vytvorené adresáre zákazníkov\"',\n\t\t\t'filetemplates' => 'Šablóny súborov',\n\t\t\t'filecontent' => 'Obsah súboru',\n\t\t\t'new_database_by_customer' => 'Upozornenie zákazníka na vytvorenie databázy',\n\t\t\t'new_ftpaccount_by_customer' => 'Upozornenie zákazníka na vytvorenie ftp používateľa',\n\t\t\t'newdatabase' => 'Notifikačné e-maily o nových databázach',\n\t\t\t'newftpuser' => 'Notifikačné e-maily o nových ftp používateľoch',\n\t\t\t'CUST_NAME' => 'Meno zákazníka',\n\t\t\t'DB_NAME' => 'Názov databázy',\n\t\t\t'DB_PASS' => 'Heslo databázy',\n\t\t\t'DB_DESC' => 'Popis databázy',\n\t\t\t'DB_SRV' => 'Databázový server',\n\t\t\t'PMA_URI' => 'URL na phpMyAdmin (ak bolo poskytnuté)',\n\t\t\t'USR_NAME' => 'FTP používateľské meno',\n\t\t\t'USR_PASS' => 'FTP heslo',\n\t\t\t'USR_PATH' => 'Domovský adresár FTP (relatívny k adresáru customer-docroot)',\n\t\t\t'forgotpwd' => 'Notifikačné e-maily o obnovení hesla',\n\t\t\t'password_reset' => 'Upozornenie pre zákazníka na obnovenie hesla',\n\t\t\t'trafficmaxpercent' => 'Upozornenie pre zákazníkov pri vyčerpaní daného maximálneho percenta prenosu',\n\t\t\t'MAX_PERCENT' => 'Nahradené limitom využitia disku/limitu prenosu pre odosielanie správ v percentách.',\n\t\t\t'USAGE_PERCENT' => 'Nahradené využitím disku / prenosom, ktorý bol zákazníkom vyčerpaný v percentách.',\n\t\t\t'diskmaxpercent' => 'Oznamovací e-mail pre zákazníkov, ak je vyčerpané maximum percent z disku',\n\t\t\t'DISKAVAILABLE' => 'Nahradené využitím disku, ktorý bol priradený zákazníkovi.',\n\t\t\t'DISKUSED' => 'Nahradené využitím disku, ktorý bol zákazníkom vyčerpaný.',\n\t\t\t'LINK' => 'Nahradené odkazom na obnovenie hesla zákazníka.',\n\t\t\t'SERVER_HOSTNAME' => 'Nahradí názov systémového hostiteľa (URL do froxlor)',\n\t\t\t'SERVER_IP' => 'Nahradí predvolenú IP adresu servera',\n\t\t\t'SERVER_PORT' => 'Nahradí predvolený port servera',\n\t\t\t'DOMAINNAME' => 'Nahrádza štandardnú subdoménu zákazníka (môže byť prázdna, ak nie je vygenerovaná žiadna)',\n\t\t],\n\t\t'webserver' => 'Webový server',\n\t\t'createzonefile' => 'Vytvoriť DNS zónu pre doménu',\n\t\t'custombindzone' => 'Vlastný / nespravovaný súbor zóny',\n\t\t'bindzonewarning' => 'pre predvolené nastavenie<br /><strong class=\"text-danger\">POZOR:</strong> Ak používate zonefile, budete musieť ručne spravovať všetky požadované záznamy aj pre všetky podzóny.',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IP adresy a porty',\n\t\t\t'add' => 'Pridať IP/port',\n\t\t\t'edit' => 'Upraviť IP/port',\n\t\t\t'ipandport' => 'IP/Port',\n\t\t\t'ip' => 'IP',\n\t\t\t'ipnote' => '<div id=\"ipnote\" class=\"invalid-feedback\">Poznámka: Hoci sú súkromné IP adresy povolené, niektoré funkcie ako DNS sa nemusia správať správne.<br>Používajte iba súkromné IP adresy, ak si tým istí.</div>',\n\t\t\t'port' => 'Port',\n\t\t\t'create_listen_statement' => 'Vytvoriť Listen statement',\n\t\t\t'create_namevirtualhost_statement' => 'Vytvoriť zápis VirtualHost',\n\t\t\t'create_vhostcontainer' => 'Vytvoriť vHost-Container',\n\t\t\t'create_vhostcontainer_servername_statement' => 'Vytvoriť zápis ServerName v kontajneri vHost-Container',\n\t\t\t'enable_ssl' => 'Je toto SSL port?',\n\t\t\t'ssl_cert_file' => 'Cesta k certifikátu SSL',\n\t\t\t'webserverdefaultconfig' => 'Predvolené nastavenia webservera',\n\t\t\t'webserverdomainconfig' => 'Konfigurácia domény webservera',\n\t\t\t'webserverssldomainconfig' => 'Konfigurácia webservera SSL',\n\t\t\t'ssl_key_file' => 'Cesta k súboru kľúča SSL',\n\t\t\t'ssl_ca_file' => 'Cesta k SSL CA certifikátu',\n\t\t\t'default_vhostconf_domain' => 'Predvolené nastavenia vHost pre každý kontajner domény',\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'Cesta k súboru SSL CertificateChainFile',\n\t\t\t\t'description' => 'Väčšinou CA_Bundle alebo podobný, pravdepodobne ho budete chcieť nastaviť, ak ste si zakúpili SSL certifikát.',\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'title' => 'Vlastný docroot (prázdny = ukazuje na froxlor)',\n\t\t\t\t'description' => 'Tu môžete definovať vlastný koreňový adresár dokumentu (cieľ požiadavky) pre túto kombináciu IP/port.<br /><strong>POZOR:</strong> Dávajte pozor, čo tu zadávate!',\n\t\t\t],\n\t\t\t'ssl_paste_description' => 'Vložte celý obsah certifikátu do textového poľa',\n\t\t\t'ssl_cert_file_content' => 'Obsah certifikátu SSL',\n\t\t\t'ssl_key_file_content' => 'Obsah súboru SSL (súkromného) kľúča',\n\t\t\t'ssl_ca_file_content' => 'Obsah súboru SSL CA (nepovinné)',\n\t\t\t'ssl_ca_file_content_desc' => '<br /><br />Overovanie klienta, nastavte to len ak viete, čo robíte.',\n\t\t\t'ssl_cert_chainfile_content' => 'Obsah súboru certifikačného reťazca (nepovinné)',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br /><br />Najčastejšie CA_Bundle alebo podobný, pravdepodobne ho budete chcieť nastaviť, ak ste si zakúpili SSL certifikát.',\n\t\t\t'ssl_default_vhostconf_domain' => 'Predvolené nastavenia SSL vHost pre každý kontajner domény',\n\t\t],\n\t\t'memorylimitdisabled' => 'Zakázané',\n\t\t'valuemandatory' => 'Táto hodnota je povinná',\n\t\t'valuemandatorycompany' => 'Musí byť vyplnené buď \"meno\" a \"krstné meno\" alebo \"názov spoločnosti\"',\n\t\t'serversoftware' => 'Softvér servera',\n\t\t'phpversion' => 'Verzia PHP',\n\t\t'mysqlserverversion' => 'Verzia MySQL servera',\n\t\t'webserverinterface' => 'Rozhranie webservera',\n\t\t'accountsettings' => 'Nastavenia účtu',\n\t\t'panelsettings' => 'Nastavenia panela',\n\t\t'systemsettings' => 'Systémové nastavenia',\n\t\t'webserversettings' => 'Nastavenia webservera',\n\t\t'mailserversettings' => 'Nastavenia mailového servera',\n\t\t'nameserversettings' => 'Nastavenia nameservera',\n\t\t'updatecounters' => 'Prepočítať využitie zdrojov',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => 'Nikdy',\n\t\t\t'choosableno' => 'Voliteľné, predvolene nie',\n\t\t\t'choosableyes' => 'Voliteľné, predvolene áno',\n\t\t\t'always' => 'Vždy',\n\t\t],\n\t\t'wipecleartextmailpwd' => 'Vymazať heslo v čistom texte',\n\t\t'webalizersettings' => 'Nastavenia Webalizéra',\n\t\t'webalizer' => [\n\t\t\t'normal' => 'Normálny',\n\t\t\t'quiet' => 'Tiché',\n\t\t\t'veryquiet' => 'Žiadny výstup',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => 'Momentálne nie je možné pridať doménu. Najprv musíte pridať aspoň jedného zákazníka.',\n\t\t'loggersettings' => 'Nastavenia logovania',\n\t\t'logger' => [\n\t\t\t'normal' => 'normálny',\n\t\t\t'paranoid' => 'paranoidný',\n\t\t],\n\t\t'emaildomain' => 'E-mailová doména',\n\t\t'email_only' => 'Iba e-mail?',\n\t\t'wwwserveralias' => 'Pridať \"www.\" ServerAlias',\n\t\t'subject' => 'Predmet',\n\t\t'recipient' => 'Príjemca',\n\t\t'message' => 'Napísať správu',\n\t\t'text' => 'Správa',\n\t\t'sslsettings' => 'Nastavenia SSL',\n\t\t'specialsettings_replacements' => 'Môžete použiť nasledujúce premenné:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code> <code>{IP}</code>, <code>{PORT}</code> <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (ak je relevantné)<br/>',\n\t\t'antispam_settings' => 'Nastavenia Antispamu',\n\t\t'caneditphpsettings' => 'Môže zmeniť nastavenia PHP domény?',\n\t\t'allips' => 'Všetky IP adresy',\n\t\t'awstatssettings' => 'Nastavenia AWstats',\n\t\t'domain_dns_settings' => 'Nastavenia doménového DNS',\n\t\t'activated' => 'Aktivované',\n\t\t'statisticsettings' => 'Nastavenia štatistík',\n\t\t'or' => 'alebo',\n\t\t'sysload' => 'Systémové zaťaženie',\n\t\t'noloadavailable' => 'nie je k dispozícii',\n\t\t'nouptimeavailable' => 'nie je k dispozícii',\n\t\t'nosubject' => '(Bez predmetu)',\n\t\t'security_settings' => 'Možnosti zabezpečenia',\n\t\t'know_what_youre_doing' => 'Meňte iba ak viete, čo robíte!',\n\t\t'show_version_login' => [\n\t\t\t'title' => 'Zobraziť verziu froxlor pri prihlásení',\n\t\t\t'description' => 'Zobraziť verziu froxlor v päte na prihlasovacej stránke',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => 'Zobraziť verziu froxlor v päte',\n\t\t\t'description' => 'Zobraziť verziu froxlor v päte na ostatných stránkach',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'Grafické záhlavie pre froxlor',\n\t\t\t'description' => 'Aká grafika by mala byť zobrazená v záhlaví',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'PHP konfigurácia',\n\t\t\t'description' => 'Stručný popis',\n\t\t\t'actions' => 'Akcie',\n\t\t\t'activedomains' => 'Používa sa pre doménu/y',\n\t\t\t'notused' => 'Konfigurácia sa nepoužíva',\n\t\t\t'editsettings' => 'Zmeniť nastavenia PHP',\n\t\t\t'addsettings' => 'Vytvoriť nové nastavenia PHP',\n\t\t\t'viewsettings' => 'Zobraziť nastavenia PHP',\n\t\t\t'phpinisettings' => 'php.ini nastavenia',\n\t\t\t'addnew' => 'Vytvoriť novú konfiguráciu PHP',\n\t\t\t'binary' => 'PHP Binary',\n\t\t\t'fpmdesc' => 'PHP-FPM konfigurácia',\n\t\t\t'file_extensions' => 'Prípony súborov',\n\t\t\t'file_extensions_note' => '(bez bodky, oddelené medzerami)',\n\t\t\t'enable_slowlog' => 'Povoliť slowlog (pre každú doménu)',\n\t\t\t'request_terminate_timeout' => 'Časový limit ukončenia požiadavky',\n\t\t\t'request_slowlog_timeout' => 'Časový limit slowlogu',\n\t\t\t'activephpconfigs' => 'Používa sa pre php-config(y)',\n\t\t\t'pass_authorizationheader' => 'Predávanie hlavičiek HTTP AUTH BASIC/DIGEST z Apache do PHP',\n\t\t],\n\t\t'misc' => 'Ostatné',\n\t\t'fpmsettings' => [\n\t\t\t'addnew' => 'Vytvoriť novú verziu PHP',\n\t\t\t'edit' => 'Zmeniť verziu PHP'\n\t\t],\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => 'Premenné, ktoré budú nahradené v konfigurácii',\n\t\t\t'pear_dir' => 'Bude nahradené globálnym nastavením pre adresár pearl.',\n\t\t\t'open_basedir_c' => 'Vloží ; (bodkočiarka) do komentára/vypne open_basedir pri nastavení',\n\t\t\t'open_basedir' => 'Bude nahradené nastavením domény open_basedir.',\n\t\t\t'tmp_dir' => 'Bude nahradené dočasným adresárom domény.',\n\t\t\t'open_basedir_global' => 'Bude nahradená globálnou hodnotou cesty, ktorá bude pripojená k open_basedir (pozri nastavenia webservera).',\n\t\t\t'customer_email' => 'Bude nahradené e-mailovou adresou zákazníka, ktorý vlastní túto doménu.',\n\t\t\t'admin_email' => 'Bude nahradené e-mailovou adresou správcu, ktorý vlastní túto doménu.',\n\t\t\t'domain' => 'Bude nahradené doménou.',\n\t\t\t'customer' => 'Bude nahradené prihlasovacím menom zákazníka, ktorý vlastní túto doménu.',\n\t\t\t'admin' => 'Bude nahradené prihlasovacím menom správcu, ktorý vlastní túto doménu.',\n\t\t\t'docroot' => 'Bude nahradené koreňovým adresárom domény.',\n\t\t\t'homedir' => 'Bude nahradené domovským adresárom zákazníka.',\n\t\t],\n\t\t'expert_settings' => 'Pokročilé nastavenia!',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => 'PHP procesy pre túto doménu (prázdne pre predvolenú hodnotu)',\n\t\t],\n\t\t'phpserversettings' => 'Nastavenia PHP',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => 'Maximálny počet php požiadaviek pre túto doménu (ponechajte prázdne pre predvolenú hodnotu)',\n\t\t],\n\t\t'spfsettings' => 'Nastavenia SPF domén',\n\t\t'specialsettingsforsubdomains' => 'Použiť špeciálne nastavenia pre všetky subdomény (*.example.com)',\n\t\t'accountdata' => 'Údaje o účte',\n\t\t'contactdata' => 'Kontaktné údaje',\n\t\t'servicedata' => 'Údaje o službe',\n\t\t'newerversionavailable' => 'Je k dispozícii novšia verzia froxloru.',\n\t\t'newerversiondetails' => 'Aktualizovať na verziu <b>%s</b> teraz?<br/>(Vaša aktuálna verzia je: %s)',\n\t\t'extractdownloadedzip' => 'Extrahovať stiahnutý archív \"%s\"?',\n\t\t'cron' => [\n\t\t\t'cronsettings' => 'Nastavenia Cronjobu',\n\t\t\t'add' => 'Pridať cronjob',\n\t\t],\n\t\t'cronjob_edit' => 'Upraviť cronjob',\n\t\t'warning' => 'VAROVANIE - Berte prosím na vedomie!',\n\t\t'lastlogin_succ' => 'Posledné prihlásenie',\n\t\t'ftpserver' => 'FTP Server',\n\t\t'ftpserversettings' => 'Nastavenia FTP servera',\n\t\t'webserver_user' => 'Používateľské meno webservera',\n\t\t'webserver_group' => 'Názov skupiny webservera',\n\t\t'perlenabled' => 'Perl povolený',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => 'Lokálny používateľ pre FCGID (froxlor vHost)',\n\t\t'mod_fcgid_group' => 'Lokálna skupina pre FCGID (froxlor vHost)',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[neposkytnuté]',\n\t\t'store_defaultindex' => 'Uložiť predvolený indexový súbor zákazníkovi docroot',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => 'Prenos',\n\t\t'traffic_sub' => 'Podrobnosti o využití prenosu',\n\t\t'domaintraffic' => 'Domény',\n\t\t'customertraffic' => 'Zákazníci',\n\t\t'assignedmax' => 'Priradené / Max',\n\t\t'usedmax' => 'Použité / Max',\n\t\t'used' => 'Využité',\n\t\t'speciallogwarning' => '<div id=„speciallogfilenote\" class=„invalid-feedback\">Pozor: Zmenou tohto nastavenia stratíte všetky staré štatistiky pre túto doménu.</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => 'Oddeliť súbor logu',\n\t\t\t'description' => 'Povolením tejto možnosti získate pre túto doménu samostatný súbor protokolu prístupu',\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => 'Povoliť úpravy domény',\n\t\t\t'desc' => 'Ak je nastavené áno, môže zákazník zmeniť niekoľko nastavení domény.<br />Ak je nastavené nie, zákazník nemôže nič meniť.',\n\t\t],\n\t\t'writeaccesslog' => [\n\t\t\t'title' => 'Zapisovať protokol prístupu',\n\t\t\t'description' => 'Povolením tejto možnosti získate súbor protokolu prístupu pre túto doménu',\n\t\t],\n\t\t'writeerrorlog' => [\n\t\t\t'title' => 'Zapísať protokol chýb',\n\t\t\t'description' => 'Povolením tejto funkcie získate súbor s protokolom chýb pre túto doménu',\n\t\t],\n\t\t'phpfpm.ininote' => 'Nie všetky hodnoty, ktoré chcete definovať, môžu byť použité v konfigurácii php-fpm poolu',\n\t\t'phpinfo' => 'PHPinfo()',\n\t\t'selectserveralias' => 'Hodnota ServerAlias pre doménu',\n\t\t'selectserveralias_desc' => 'Vyberte, či by froxlor mal vytvoriť wildcard-entry (*.domain.tld), WWW-alias (www.domain.tld) alebo žiadny alias',\n\t\t'show_news_feed' => [\n\t\t\t'title' => 'Zobraziť novinky na admin nástenke',\n\t\t\t'description' => 'Povolením zobrazíte oficiálne novinky froxlor (https://inside.froxlor.org/news/) na vašej nástenke a nikdy nezmeškáte dôležité informácie alebo oznámenia o vydaní.',\n\t\t],\n\t\t'cronsettings' => 'Nastavenia Cronjobu',\n\t\t'integritycheck' => 'Overenie databázy',\n\t\t'integrityname' => 'Názov',\n\t\t'integrityresult' => 'Výsledok',\n\t\t'integrityfix' => 'Automaticky opraviť problémy',\n\t\t'customer_show_news_feed' => 'Zobraziť novinky na ovládacom paneli zákazníka',\n\t\t'customer_news_feed_url' => [\n\t\t\t'title' => 'Použiť vlastný RSS-feed',\n\t\t\t'description' => 'Zadajte vlastný RSS kanál, ktorý bude zobrazený vašim zákazníkom na ich nástenke.<br /><small>Ponechajte prázdne pre použitie oficiálneho froxlor newsfeedu (https://inside.froxlor.org/news/).</small>',\n\t\t],\n\t\t'movetoadmin' => 'Presunúť zákazníka',\n\t\t'movecustomertoadmin' => [\n\t\t\t'title' => 'Presunúť zákazníka na vybraného administrátora/predajcu',\n\t\t\t'description' => 'Ponechajte prázdne pre žiadnu zmenu.<br />Ak sa požadovaný administrátor nezobrazuje v zozname, jeho zákaznícky limit bol dosiahnutý.',\n\t\t],\n\t\t'note' => 'Poznámka',\n\t\t'mod_fcgid_umask' => [\n\t\t\t'title' => 'Umask (predvolené: 022)',\n\t\t],\n\t\t'apcuinfo' => 'APCu info',\n\t\t'opcacheinfo' => 'OPcache Info',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Použiť Let\\'s Encrypt',\n\t\t\t'description' => 'Získajte zdarma certifikát od <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. Certifikát bude vytvorený a obnovený automaticky.<br><strong class=\"text-danger\">POZNÁMKA:</strong> Ak sú povolené wildcards, táto možnosť bude automaticky zakázaná.',\n\t\t],\n\t\t'autoupdate' => 'Automatická aktualizácia',\n\t\t'server_php' => 'PHP',\n\t\t'dnsenabled' => 'Povoliť DNS editor',\n\t\t'froxlorvhost' => 'froxlor VirtualHost nastavenia',\n\t\t'hostname' => 'Názov servera',\n\t\t'memory' => 'Využitie pamäte',\n\t\t'webserversettings_ssl' => 'Nastavenia SSL Webservera',\n\t\t'domain_hsts_maxage' => [\n\t\t\t'title' => 'HTTP Strict Transport Security (HSTS)',\n\t\t\t'description' => 'Zadajte hodnotu maximálneho veku pre hlavičku Strict-Transport-Security<br>Hodnota <i>0</i> zakáže HSTS pre doménu. Väčšina používateľov nastavuje hodnotu <i>31536000</i> (jeden rok).',\n\t\t],\n\t\t'domain_hsts_incsub' => [\n\t\t\t'title' => 'Zahrnúť HSTS pre akúkoľvek subdoménu',\n\t\t\t'description' => 'Voliteľná direktíva „includeSubDomains\", ak je prítomná, signalizuje UA, že zásady HSTS sa vzťahujú na tohto hostiteľa HSTS aj na všetky subdomény názvu domény hostiteľa.',\n\t\t],\n\t\t'domain_hsts_preload' => [\n\t\t\t'title' => 'Zahrnúť doménu do zoznamu prednahrania HSTS',\n\t\t\t'description' => 'Ak chcete, aby bola táto doména zahrnutá do <a href=\"https://hstspreload.org/\" target=\"_blank\">HSTS preload listu</a> spravovaného Chrome (a používaného Firefoxom a Safari), použite túto aktiváciu.<br>Odoslanie preload direktívy z vašich stránok môže mať TRVALÉ NÁSLEDKY a zabrániť používateľom v prístupe na vaše stránky a všetky ich subdomény.<br>Pred odoslaním hlavičky s \"preload\" si prosím prečítajte podrobnosti na <a href=\"https://hstspreload.org/#removal\" target=\"_blank\">https://hstspreload.org/#removal</a>.',\n\t\t],\n\t\t'domain_ocsp_stapling' => [\n\t\t\t'title' => 'Stapling OCSP',\n\t\t\t'description' => 'Podrobné vysvetlenie stapling OCSP pozri <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/OCSP_stapling\">Wikipedia</a>',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">VAROVANIE:</strong> Nginx verzia 1.3.7 alebo vyššia je vyžadovaná pre OCSP stapling. Ak je vaša verzia staršia, webový server NEBUDE správne spustený, kým je povolený OCSP stapling!',\n\t\t],\n\t\t'domain_http2' => [\n\t\t\t'title' => 'HTTP2 podpora',\n\t\t\t'description' => 'Navštívte <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/2\">Wikipedia</a> pre podrobné vysvetlenie HTTP2 protokolu',\n\t\t],\n\t\t'domain_http3' => [\n\t\t\t'title' => 'HTTP3 podpora',\n\t\t\t'description' => 'Navštívte <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/3\">Wikipedia</a> pre podrobné vysvetlenie HTTP3 protokolu',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">VAROVANIE:</strong> Je vyžadovaná Nginx verzia 1.25.0 alebo vyššia a pre HTTP/3 je vyžadovaný ssl-protokol TLSv1.3. Ak je vaša verzia staršia, webový server sa pri zapnutom HTTP/3 správne nespustí!',\n\t\t],\n\t\t'testmail' => 'Test SMTP',\n\t\t'phpsettingsforsubdomains' => 'Použiť php konfiguráciu na všetky subdomény:',\n\t\t'plans' => [\n\t\t\t'name' => 'Názov plánu',\n\t\t\t'description' => 'Popis',\n\t\t\t'last_update' => 'Naposledy aktualizované',\n\t\t\t'plans' => 'Hostingové plány',\n\t\t\t'plan_details' => 'Detaily plánu',\n\t\t\t'add' => 'Pridať nový plán',\n\t\t\t'edit' => 'Upraviť plán',\n\t\t\t'use_plan' => 'Použiť plán',\n\t\t],\n\t\t'notryfiles' => [\n\t\t\t'title' => 'Žiadne automatické generovanie try_files',\n\t\t\t'description' => 'Tu zvoľte áno, ak chcete v špeciálnom nastavení zadať vlastnú direktívu try_files (potrebnú napríklad pre niektoré pluginy WordPressu).',\n\t\t],\n\t\t'logviewenabled' => 'Povoliť prístup k protokolom prístupu/chýb',\n\t\t'novhostcontainer' => '<br><br><small class=\"text-danger\">Žiadna z IP adries a portov nemá povolenú možnosť \"Vytvoriť vHost-Container\", mnoho nastavení tu nebude k dispozícii</small>',\n\t\t'ownsslvhostsettings' => 'Vlastné SSL vHost-nastavenia',\n\t\t'domain_override_tls' => 'Prepísať nastavenia systémového TLS',\n\t\t'domain_override_tls_addinfo' => '<br /><span class=\"text-danger\">Používa sa iba v prípade, že \"Prepísať nastavenia TLS systému\" je nastavené na \"Áno\"</span>',\n\t\t'domain_sslenabled' => 'Povoliť použitie SSL',\n\t\t'domain_honorcipherorder' => 'Dodržiavať poradie šifry (servera), predvolené <strong>nie</strong>',\n\t\t'domain_sessiontickets' => 'Povoliť TLS sessiontickety (RFC 5077), predvolené <strong>áno</strong>',\n\t\t'domain_sessionticketsenabled' => [\n\t\t\t'title' => 'Povoliť použitie ticketov TLS globálne',\n\t\t\t'description' => 'Predvolené <strong>áno</strong><br>Vyžaduje apache-2.4.11+ alebo nginx-1.5.9+',\n\t\t],\n\t\t'domaindefaultalias' => 'Predvolená hodnota serverAlias pre nové domény',\n\t\t'smtpsettings' => 'Nastavenia SMTP',\n\t\t'smtptestaddr' => 'Odoslať testovací e-mail na',\n\t\t'smtptestnote' => 'Nižšie uvedené hodnoty odrážajú vaše aktuálne nastavenia a možno ich upraviť iba tu (pozri odkaz v pravom hornom rohu)',\n\t\t'smtptestsend' => 'Odoslať testovací e-mail',\n\t\t'mysqlserver' => [\n\t\t\t'mysqlserver' => 'MySQL Server',\n\t\t\t'dbserver' => 'Server #',\n\t\t\t'caption' => 'Popis',\n\t\t\t'host' => 'Názov servera / IP',\n\t\t\t'port' => 'Port',\n\t\t\t'user' => 'Oprávnený používateľ',\n\t\t\t'add' => 'Pridať nový MySQL server',\n\t\t\t'edit' => 'Upraviť MySQL server',\n\t\t\t'password' => 'Heslo oprávneného používateľa',\n\t\t\t'password_emptynochange' => 'Nové heslo, ponechajte prázdne pre zachovanie aktuálneho',\n\t\t\t'allowall' => [\n\t\t\t\t'title' => 'Povoliť použitie tohto servera všetkým aktuálne existujúcim zákazníkom',\n\t\t\t\t'description' => 'Nastavte túto hodnotu na „true\", ak chcete povoliť používanie tohto databázového servera všetkým existujúcim zákazníkom, aby naň mohli pridávať databázy. Toto nastavenie nie je trvalé, ale možno ho spustiť viackrát.',\n\t\t\t],\n\t\t\t'testconn' => 'Otestovať pripojenie pri ukladaní',\n\t\t\t'ssl' => 'Použiť SSL pre pripojenie k databázovému serveru',\n\t\t\t'ssl_cert_file' => 'Cesta k súboru SSL Certifikačnej autority',\n\t\t\t'verify_ca' => 'Povoliť overenie serverového SSL certifikátu',\n\t\t],\n\t\t'settings_importfile' => 'Zvoliť importovaný súbor',\n\t\t'documentation' => 'Dokumentácia',\n\t\t'adminguide' => 'Admin sprievodca',\n\t\t'userguide' => 'Používateľský manuál',\n\t\t'apiguide' => 'Sprievodca API',\n\t\t'domain_duplicate' => 'Duplikovať doménu',\n\t\t'domain_duplicate_named' => 'Duplikovať %s',\n\t\t'backups' => [\n\t\t\t'backups' => 'Zálohy',\n\t\t],\n\t\t'emaildomainwarning' => '<div id=\"emaildomainnote\" class=\"invalid-feedback\">VAROVANIE: Zmenou tohto nastavenia trvale vymažete všetky existujúce e-mailové adresy a účty.</div>',\n\t],\n\t'apcuinfo' => [\n\t\t'clearcache' => 'Vymazať APCu vyrovnávaciu pamäť',\n\t\t'generaltitle' => 'Všeobecné informácie o vyrovnávacej pamäti',\n\t\t'version' => 'Verzia APCu',\n\t\t'phpversion' => 'Verzia PHP',\n\t\t'host' => 'APCu hostiteľ',\n\t\t'sharedmem' => 'Zdieľaná pamäť',\n\t\t'sharedmemval' => '%d Segment(y) s %s (%s pamäti)',\n\t\t'start' => 'Čas spustenia',\n\t\t'uptime' => 'Doba prevádzky',\n\t\t'upload' => 'Podpora nahrávania súborov',\n\t\t'cachetitle' => 'Informácie o vyrovnávacej pamäti',\n\t\t'cvar' => 'Premenné vo vyrovnávacej pamäti',\n\t\t'hit' => 'Zásahy',\n\t\t'miss' => 'Nevyužité',\n\t\t'reqrate' => 'Miera požiadaviek (zásahy, nevyužité)',\n\t\t'creqsec' => 'požiadavky vyrovnávacej pamäte za sekundu',\n\t\t'hitrate' => 'Rýchlosť zásahu',\n\t\t'missrate' => 'Nevyužitá miera',\n\t\t'insrate' => 'Insert Rate',\n\t\t'cachefull' => 'Plný počet vyrovnávacej pamäti',\n\t\t'runtime' => 'Nastavenia spustenia',\n\t\t'memnote' => 'Využitie pamäti',\n\t\t'total' => 'Celkom',\n\t\t'free' => 'Voľné',\n\t\t'used' => 'Využité',\n\t\t'hitmiss' => 'Zásahy & Nevyužité',\n\t\t'detailmem' => 'Podrobné využitie pamäti a fragmentácia',\n\t\t'fragment' => 'Fragmentácia',\n\t\t'nofragment' => 'Bez fragmentácie',\n\t\t'fragments' => 'Fragmentácia',\n\t],\n\t'apikeys' => [\n\t\t'no_api_keys' => 'Neboli nájdené žiadne API kľúče',\n\t\t'key_add' => 'Pridať nový kľúč',\n\t\t'apikey_removed' => 'Api kľúč s Id #%s bol úspešne odstránený',\n\t\t'apikey_added' => 'Bol úspešne vygenerovaný nový api kľúč',\n\t\t'clicktoview' => 'Kliknite pre zobrazenie',\n\t\t'allowed_from' => 'Povolené od',\n\t\t'allowed_from_help' => 'Čiarkami oddelený zoznam Ip adries / sietí.<br>Predvolené nastavenie je prázdne (povoliť od všetkých).',\n\t\t'valid_until' => 'Platné do',\n\t\t'valid_until_help' => 'Dátum platnosti, formát RRRR-MM-DDTh:mm',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => 'Staré heslo',\n\t\t'new_password' => 'Nové heslo',\n\t\t'new_password_confirm' => 'Potvrdiť heslo',\n\t\t'new_password_ifnotempty' => 'Nové heslo (prázdne = žiadna zmena)',\n\t\t'also_change_ftp' => ' tiež zmeniť heslo hlavného FTP účtu',\n\t\t'also_change_stats' => ' tiež zmeniť heslo pre stránku so štatistikou',\n\t\t'also_change_global_mysql' => 'tiež zmeniť heslo pre globálny MySQL účet',\n\t],\n\t'cron' => [\n\t\t'cronname' => 'názov cronjobu',\n\t\t'lastrun' => 'posledné spustenie',\n\t\t'interval' => 'interval',\n\t\t'isactive' => 'povolené',\n\t\t'description' => 'popis',\n\t\t'changewarning' => 'Zmena týchto hodnôt môže mať negatívny vplyv na správanie froxlor a jeho automatických úloh.<br />Zmeňte hodnoty tu iba ak ste si istí, že viete, čo robíte.',\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => 'nebol zadaný žiadny popis',\n\t\t'cron_tasks' => 'Generovanie konfiguračných súborov',\n\t\t'cron_legacy' => 'starší (starý) cronjob',\n\t\t'cron_traffic' => 'Výpočet dátovej prevádzky',\n\t\t'cron_usage_report' => 'Hlásenie o prevádzke a webe',\n\t\t'cron_mailboxsize' => 'Výpočet veľkosti schránky',\n\t\t'cron_letsencrypt' => 'Aktualizácia certifikátu Let\\'s Encrypt',\n\t\t'cron_export' => 'Spracovanie úloh exportu dát',\n\t\t'cron_backup' => 'Spracovanie úloh zálohovania systému a zákazníkov',\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => 'Nastavenia Cronjobu',\n\t\t'cronjobintervalv' => 'Hodnota intervalu behu',\n\t\t'cronjobinterval' => 'Interval behu',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => 'Ešte nespustené',\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => 'minút',\n\t\t'hours' => 'hodín',\n\t\t'days' => 'dní',\n\t\t'weeks' => 'týždňov',\n\t\t'months' => 'mesiacov',\n\t],\n\t'customer' => [\n\t\t'documentroot' => 'Domovský adresár',\n\t\t'name' => 'Názov',\n\t\t'firstname' => 'Krstné meno',\n\t\t'lastname' => 'Priezvisko',\n\t\t'company' => 'Firma',\n\t\t'nameorcompany_desc' => 'Krstné meno/priezvisko alebo firma je povinná',\n\t\t'street' => 'Ulica',\n\t\t'zipcode' => 'PSČ',\n\t\t'city' => 'Mesto',\n\t\t'phone' => 'Mobil',\n\t\t'fax' => 'Fax',\n\t\t'email' => 'Email',\n\t\t'customernumber' => 'ID zákazníka',\n\t\t'diskspace' => 'Webový priestor',\n\t\t'traffic' => 'Prevádzka',\n\t\t'mysqls' => 'Databázy MySQL',\n\t\t'emails' => 'E-mailové adresy',\n\t\t'accounts' => 'E-mailové účty',\n\t\t'forwarders' => 'E-mailoví preposielatelia',\n\t\t'ftps' => 'FTP účty',\n\t\t'subdomains' => 'Subdomény',\n\t\t'domains' => 'Domény',\n\t\t'mib' => 'MiB',\n\t\t'gib' => 'GiB',\n\t\t'title' => 'Názov',\n\t\t'country' => 'Krajina',\n\t\t'email_quota' => 'Kvóta e-mailu',\n\t\t'email_imap' => 'E-mail IMAP',\n\t\t'email_pop3' => 'E-mail POP3',\n\t\t'sendinfomail' => 'Poslať mi dáta e-mailom',\n\t\t'generated_pwd' => 'Návrh hesla',\n\t\t'usedmax' => 'Použité / Max',\n\t\t'services' => 'Služby',\n\t\t'letsencrypt' => [\n\t\t\t'title' => 'Použiť Let\\'s Encrypt',\n\t\t\t'description' => 'Získajte zadarmo certifikát od <a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>. Certifikát bude vytvorený a obnovený automaticky.',\n\t\t],\n\t\t'selectserveralias_addinfo' => 'Túto možnosť možno nastaviť pri úprave domény. Jej počiatočná hodnota je zdedená z nadradenej domény.',\n\t\t'total_diskspace' => 'Celkový priestor na disku',\n\t\t'mysqlserver' => 'Použiteľný mysql-server',\n\t],\n\t'diskquota' => 'Kvóta',\n\t'antispam' => [\n\t\t'config_file' => [\n\t\t\t'title' => 'Súbor s nastavením Antispamu',\n\t\t\t'description' => 'Zadajte názov súboru pre pravidlá e-mail-antispamu',\n\t\t],\n\t\t'reload_command' => [\n\t\t\t'title' => 'Príkaz na reštart systému Milter',\n\t\t\t'description' => 'Zadajte príkaz reštartu pre službu rspamd',\n\t\t],\n\t\t'activated' => [\n\t\t\t'title' => 'Aktivovať antispam?',\n\t\t\t'description' => 'Chcete použiť rspamd ako antispam službu?',\n\t\t],\n\t\t'dkim_keylength' => [\n\t\t\t'title' => 'DKIM dĺžka kľúča',\n\t\t\t'description' => 'Upozornenie: Zmeny sa budú vzťahovať iba na nové kľúče<br/><br/>Vyžaduje určitú položku dns pre doménu. Ak nepoužívate funkciu nameserver, budete musieť tieto položky ručne spravovať.',\n\t\t],\n\t\t'spam_tag_level' => [\n\t\t\t'title' => 'Úroveň Spam tagu',\n\t\t\t'description' => 'Počet bodov, ktorý je potrebný na označenie e-mailu ako spam<br/>Predvolené: 7.0'\n\t\t],\n\t\t'rewrite_subject' => [\n\t\t\t'title' => 'Prepisovať predmet',\n\t\t\t'description' => 'Či pridať <strong>***SPAM***</strong> do predmetu e-mailu, ak je to vhodné',\n\t\t],\n\t\t'spam_kill_level' => [\n\t\t\t'title' => 'Úroveň likvidácie spamu',\n\t\t\t'description' => 'Počet bodov, ktorý je potrebný na úplné vyradenie e-mailu<br/>Predvolené: 14.0'\n\t\t],\n\t\t'bypass_spam' => [\n\t\t\t'title' => 'Obísť spamfilter',\n\t\t\t'description' => 'Aktiváciou obídete/zakážete filtrovanie spamu pre túto adresu.<br/>Predvolené: nie'\n\t\t],\n\t\t'policy_greylist' => [\n\t\t\t'title' => 'Použiť greylisting',\n\t\t\t'description' => 'Prichádzajúce e-maily budú chránené <a href=\"https://en.wikipedia.org/wiki/Greylisting_(email)\" target=\"_blank\">greylisting</a>.<br/>Predvolené: áno'\n\t\t],\n\t\t'required_spf_dns' => 'Požadovaný SPF DNS záznam',\n\t\t'required_dmarc_dns' => 'Požadovaný DMARC DNS záznam',\n\t\t'required_dkim_dns' => 'Požadovaný DKIM DNS záznam',\n\t\t'default_select' => [\n\t\t\t'on_changeable' => 'Aktivované, nastaviteľné',\n\t\t\t'off_changeable' => 'Deaktivované, nastaviteľné',\n\t\t\t'on_unchangeable' => 'Aktivované, nedá sa nastaviť',\n\t\t\t'off_unchangeable' => 'Deaktivované, nedá sa nastaviť',\n\t\t],\n\t\t'default_bypass_spam' => [\n\t\t\t'title' => 'Obchádzať predvolenú hodnotu emailového spam filtra',\n\t\t\t'description' => 'Či majú nové e-mailové účty v predvolenom nastavení aktivovanú funkciu „Obísť spamfilter\" a či je toto nastavenie zákazníkom nastaviteľné. <br/>Predvolené nastavenie: Deaktivované, nastaviteľné'\n\t\t],\n\t\t'default_spam_rewrite_subject' => [\n\t\t\t'title' => 'Prepisovať predvolenú hodnotu predmetu',\n\t\t\t'description' => 'Či majú nové e-mailové účty v predvolenom nastavení aktivovanú funkciu „Prepisovať predmet\" a či je toto nastavenie zákazníkom nastaviteľné. <br/>Predvolené nastavenie: Aktivované, nastaviteľné'\n\t\t],\n\t\t'default_policy_greylist' => [\n\t\t\t'title' => 'Použiť predvolenú hodnotu greylisting',\n\t\t\t'description' => 'Či majú nové e-mailové účty v predvolenom nastavení aktivovanú funkciu „Použiť greylisting\" a či je toto nastavenie zákazníkom nastaviteľné. <br/>Predvolené nastavenie: Aktivované, nastaviteľné'\n\t\t],\n\t],\n\t'dns' => [\n\t\t'destinationip' => 'IP domény(y)',\n\t\t'standardip' => 'Štandardná IP adresa servera',\n\t\t'a_record' => 'A-záznam (voliteľne IPv6)',\n\t\t'cname_record' => 'Záznam CNAME',\n\t\t'mxrecords' => 'Definovať MX záznamy',\n\t\t'standardmx' => 'Štandardný MX záznam servera',\n\t\t'mxconfig' => 'Vlastné MX záznamy',\n\t\t'priority10' => 'Priorita 10',\n\t\t'priority20' => 'Priorita 20',\n\t\t'txtrecords' => 'Definovať TXT záznamy',\n\t\t'txtexample' => 'Príklad (SPF-entry):<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'howitworks' => 'Tu môžete spravovať DNS položky pre vašu doménu. Pamätajte, že froxlor automaticky vygeneruje NS/MX/A/AAAA záznamy za vás. Vlastné položky majú prednosť, automaticky sa vygenerujú iba chýbajúce položky.',\n\t\t'nis2note' => [\n\t\t\t'title' => 'NIS2 info',\n\t\t\t'content' => 'DNS hosting/autoritatívne DNS služby môžu byť považované za digitálne služby s vyššími bezpečnostnými a oznamovacími povinnosťami podľa <strong>EU-NIS2</strong>. Skontrolujte, či sa na vaše nastavenie vzťahuje NIS2 a aké opatrenia sú vyžadované.'\n\t\t],\n\t],\n\t'dnseditor' => [\n\t\t'edit' => 'upraviť DNS',\n\t\t'records' => 'záznamy',\n\t\t'notes' => [\n\t\t\t'A' => '32-bitová adresa IPv4 používaná na mapovanie názvov hostiteľov na IP adresu hostiteľa.',\n\t\t\t'AAAA' => '128-bitová IPv6 adresa, používaná na mapovanie mien na IP adresu hostiteľa.',\n\t\t\t'CAA' => 'Záznam zdroja CAA umožňuje držiteľovi mena domény DNS určiť jednu alebo viac certifikačných autorít (CA), ktoré sú oprávnené vydávať certifikáty pre túto doménu.<br>Štruktúra: <code>značka tag[issue|issuewild|iodef|contactmail|contactphone] hodnota</code><br>Príklad: <code>0 issue \"ca.example.net\"<br>0 iodef \"mailto:security@example.com\"</code>',\n\t\t\t'CNAME' => 'Alias názvu domény, vyhľadávanie DNS bude pokračovať opakovaním vyhľadávania s novým názvom. Možné iba pre subdomény!',\n\t\t\t'DNAME' => 'Vytvorí alias pre celý podstrom stromu domény',\n\t\t\t'LOC' => 'Informácie o geografickej polohe pre názov domény.<br>Štruktúra: <code>( d1 [m1 [s1]] {\"N\"|\"S\"} d2 [m2 [s2]] {\"E\"|\"W\"} alt[\"m\"] [siz[\"m\"] [hp[\"] [vp[\"m\"]]]] )</code><br> <code>d1: [0 . 90] (stupne zemepisnej šírky)\n\t\t\td2: [0 .. 180] (stupnica zemepisnej dĺžky)\n\t\t\tm1, m2: [0 .. 59] (minúty zemepisnej šírky/dĺžky)\n\t\t\ts1, s2: [0 .. 59. 99] (sekundy zemepisnej šírky/dĺžky)\n\t\t\tt [-100000.00 .. 42849672.95] BY . 1 (výška v metroch)\n\t\t\tveľkosť, hp, vp: [0 .. 90000000. 0] (veľkosť/presnosť v metroch)</code><br>Príklad: <code>52 22 23.000 N 4 53 32.000 E -2,00m 000m 10000m</code>',\n\t\t\t'MX' => 'Záznam o výmene e-mailov, namapuje doménu na mailserver pre túto doménu.<br>Príklad: <code>10 mail.example.com</code><br>Poznámka: Pre prioritu použite pole vyššie',\n\t\t\t'NS' => 'Deleguje zónu DNS, aby použila dané autoritatívne menné servery.',\n\t\t\t'RP' => 'Záznam zodpovednej osoby<br>Štruktúra: <code>mailbox[nahraďte @ bodkou] txt-record-name</code><br>Príklad: <code>team.froxlor.org. froxlor.org.</code>',\n\t\t\t'SRV' => 'Záznam polohy služby, používaný pre novšie protokoly namiesto vytvárania záznamov špecifických pre protokol, ako je MX.<br>Štruktúra: <code>prioritná váhový port cieľ</code><br>Príklad: <code>0 5 5060 sipserver.example.com.</code><br>Poznámka: Pre prioritu použite pole vyššie',\n\t\t\t'SSHFP' => 'Záznam zdroja SSHFP sa používa na zverejnenie odtlačkov prsta kľúča bezpečného shellu (SSH) v DNS.<br>Štruktúra: <code>typ algoritmu</code><br>Algoritmy: <code>0: vyhradené, 1: RSA, 2: DSA, 3: ECDSA, 4: Ed25519, 6: Typy Ed448</code><br> <code>0: vyhradené, 1: SHA-1, 2: SHA-256</code><br>Príklad: <code>2 1 123456789abcdef67890123456789abcdef67890</code>',\n\t\t\t'TLSA' => 'TLSA (TLS Authentication) záznam slúži na zverejnenie odtlačku prsta certifikátu TLS/SSL. Bežne sa používa pre DANE.<br>TLSA záznamy môžu byť dôveryhodné iba vtedy, ak je DNSSEC povolená na vašej doméne.<br>Štruktúra: <code>typ odtlačku prsta</code><br>využitie certifikátu: <code>0: PKIX-T, 1: PKIX-EE, 2: DANE-TA, 3: DANE-EE</code><br>selektor: <code>0: Použite celý certifikát 1: Použite podradený verejný kľúč</code><br>Zodpovedajúci typ: <code>0: Bez Hash, 1: SHA-256 Hash, 2:SHA-512 Hash</code><br>Príklad: <code>3 1 123456789abcdef67890123456789abcdef123456789abcdef123456789abcde</code>',\n\t\t\t'TXT' => 'Voľne definovaný, popisný text.'\n\t\t]\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'OpenBasedir-cesta',\n\t\t'inherited' => 'Rovnaké ako nadradená doména',\n\t\t'docroot' => 'Cesta z poľa vyššie',\n\t\t'homedir' => 'Domovský adresár',\n\t\t'docparent' => 'Nadradený adresár cesty z poľa uvedeného vyššie',\n\t\t'ssl_certificate_placeholder' => '---- BEGIN CERTIFICATE---' . PHP_EOL . '[...]' . PHP_EOL . '----END CERTIFICATE----',\n\t\t'ssl_key_placeholder' => '---- BEGIN RSA PRIVATE KEY-----' . PHP_EOL . '[...]' . PHP_EOL . '-----END RSA PRIVATE KEY-----',\n\t],\n\t'domains' => [\n\t\t'description' => 'Tu môžete vytvoriť (sub)domény a zmeniť ich cesty.<br />Systém bude potrebovať nejaký čas, aby mohol po každej zmene použiť nové nastavenie.',\n\t\t'domainsettings' => 'Nastavenia domény',\n\t\t'domainname' => 'Názov domény',\n\t\t'subdomain_add' => 'Vytvoriť subdoménu',\n\t\t'subdomain_edit' => 'Upraviť (sub)doménu',\n\t\t'wildcarddomain' => 'Vytvoriť ako wildcarddomain?',\n\t\t'aliasdomain' => 'Alias pre doménu',\n\t\t'noaliasdomain' => 'Žiadny alias domény',\n\t\t'hasaliasdomains' => 'Má alias doménu(y)',\n\t\t'statstics' => 'Štatistiky využitia',\n\t\t'isassigneddomain' => 'Je priradená doména',\n\t\t'add_date' => 'Pridané do froxloru',\n\t\t'registration_date' => 'Pridané do registra',\n\t\t'topleveldomain' => 'Top-Level-Doména',\n\t\t'associated_with_domain' => 'Pridružené',\n\t\t'aliasdomains' => 'Alias domén',\n\t\t'redirectifpathisurl' => 'Presmerovací kód (predvolené: prázdny)',\n\t\t'redirectifpathisurlinfo' => 'Musíte vybrať iba jednu z týchto položiek, ak ste zadali adresu URL ako cestu<br/><strong class=\"text-danger\">POZNÁMKA:</strong> Zmeny sú aplikované iba v prípade, že daná cesta je URL adresa.',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'IP adresa (adresy)',\n\t\t\t'description' => 'Zadajte jednu alebo viac IP adries domény.<br /><br /><div class=\"text-danger\">POZNÁMKA: IP adresy nemožno zmeniť, ak je doména nakonfigurovaná ako <strong>alias-doména</strong> inej domény.</div>',\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'SSL IP adresa/y',\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'Presmerovanie SSL',\n\t\t\t'description' => 'Táto voľba vytvorí presmerovanie pre nie ssl vhosty, takže všetky požiadavky sú presmerované na SSL-vhost.<br /><br />napr. požiadavka na <strong>http</strong>://domain.tld/ vás presmeruje na <strong>https</strong>://domain.tld/',\n\t\t],\n\t\t'serveraliasoption_wildcard' => 'Wildcard (*.domain.tld)',\n\t\t'serveraliasoption_www' => 'WWW (www.domain.tld)',\n\t\t'serveraliasoption_none' => 'Bez aliasu',\n\t\t'domain_import' => 'Importovať domény',\n\t\t'import_separator' => 'Oddeľovač',\n\t\t'import_offset' => 'Odsadenie',\n\t\t'import_file' => 'Súbor CSV',\n\t\t'import_description' => 'Podrobné informácie o štruktúre importného súboru a o úspešnom importe, navštívte prosím <a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\" class=\"alert-link\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a>',\n\t\t'ssl_redirect_temporarilydisabled' => '<br>Presmerovanie protokolu SSL je dočasne deaktivované, kým sa generuje nový certifikát Let\\'s Encrypt. Bude opäť aktivované po vygenerovaní certifikátu.',\n\t\t'termination_date' => 'Dátum ukončenia',\n\t\t'termination_date_overview' => 'ukončené dňa ',\n\t\t'ssl_certificates' => 'SSL certifikáty',\n\t\t'ssl_certificate_removed' => 'Certifikát s Id #%s bol úspešne odstránený',\n\t\t'ssl_certificate_error' => 'Chyba pri čítaní certifikátu pre doménu: %s',\n\t\t'no_ssl_certificates' => 'Neexistujú žiadne domény s SSL certifikátom',\n\t\t'isaliasdomainof' => 'Je aliasovou doménou pre %s',\n\t\t'isbinddomain' => 'Vytvoriť zónu DNS',\n\t\t'dkimenabled' => 'DKIM povolené',\n\t\t'openbasedirenabled' => 'Openbasedir obmedzenia',\n\t\t'hsts' => 'HSTS povolené',\n\t\t'aliasdomainid' => 'ID aliasovej domény',\n\t\t'nodomainsassignedbyadmin' => 'Váš účet momentálne nemá priradené žiadne (aktívne) domény. Ak si myslíte, že je to chyba, kontaktujte svojho správcu.',\n\t\t'email_only' => 'Len e-maily',\n\t],\n\t'emails' => [\n\t\t'description' => 'Tu môžete vytvárať a meniť svoje e-mailové adresy.<br />Účet je ako poštová schránka pred vaším domom. Ak vám niekto pošle e-mail, bude doručený na účet.<br /><br />Ak chcete stiahnuť svoje e-maily, použite nasledujúce nastavenia vo vašom poštovom programe: (Údaje v <i>kurzíve</i> musia byť zmenené na ekvivalenty, ktoré ste zadali!<br />Názov hostiteľa: <b><i>názov domény</i></b><br />Používateľské meno: <b><i>názov účtu / e-mailová adresa</i></b><br />heslo: <b><i>heslo, ktoré ste zvolili</i></b>',\n\t\t'emailaddress' => 'E-mailová adresa',\n\t\t'emails_add' => 'Vytvoriť e-mailovú adresu',\n\t\t'emails_edit' => 'Upraviť e-mailovú adresu',\n\t\t'catchall' => 'Catchall',\n\t\t'iscatchall' => 'Definovať ako catchall-adresu?',\n\t\t'account' => 'Účet',\n\t\t'account_add' => 'Vytvoriť účet',\n\t\t'account_delete' => 'Odstrániť účet',\n\t\t'from' => 'Zdroj',\n\t\t'to' => 'Destinácia',\n\t\t'forwarders' => 'Presmerovanie',\n\t\t'forwarder_add' => 'Vytvoriť preposielateľa',\n\t\t'alternative_emailaddress' => 'Alternatívna e-mailová adresa',\n\t\t'quota' => 'Kvóta',\n\t\t'noquota' => 'Bez kvóty',\n\t\t'updatequota' => 'Aktualizovať kvótu',\n\t\t'quota_edit' => 'Zmeniť e-mailovú kvótu',\n\t\t'noemaildomainaddedyet' => 'Vo svojom účte zatiaľ nemáte (e-mailovú) doménu.',\n\t\t'back_to_overview' => 'Späť na prehľad domény',\n\t\t'accounts' => 'Účty',\n\t\t'emails' => 'Adresy',\n\t\t'senders' => 'Povolený odosielateľ',\n\t\t'sender_add' => 'Pridať povoleného odosielateľa',\n\t\t'foreign_sender' => 'Povolený (externý) odosielateľ',\n\t\t'allowed_sender_info' => 'Pomocou nastavenia <strong>Povolený odosielateľ</strong> povolíte existujúcemu e-mailovému účtu odosielanie e-mailov s inou adresou odosielateľa.<br><strong>Dôležité:</strong> Adresa/wildcard-doména zadaná tu sa automaticky nestane poštovou schránkou – slúži iba ako ďalší povolený identifikátor odosielateľa.',\n\t],\n\t'error' => [\n\t\t'error' => 'Chyba',\n\t\t'directorymustexist' => 'Adresár %s musí existovať. Vytvorte ho prosím pomocou vášho FTP klienta.',\n\t\t'filemustexist' => 'Súbor %s musí existovať.',\n\t\t'allresourcesused' => 'Všetky svoje zdroje ste už využili.',\n\t\t'domains_cantdeletemaindomain' => 'Nemôžete odstrániť priradenú doménu.',\n\t\t'domains_canteditdomain' => 'Túto doménu nie je možné upravovať. Bola zakázaná administrátorom.',\n\t\t'domains_cantdeletedomainwithemail' => 'Nemôžete odstrániť doménu, ktorá sa používa ako e-mailová doména. Najprv odstráňte všetky e-mailové adresy.',\n\t\t'firstdeleteallsubdomains' => 'Najprv musíte odstrániť všetky subdomény, než budete môcť vytvoriť wildcard doménu.',\n\t\t'youhavealreadyacatchallforthisdomain' => 'Pre túto doménu ste už definovali catchall.',\n\t\t'ftp_cantdeletemainaccount' => 'Nemôžete odstrániť svoj hlavný FTP účet',\n\t\t'login' => 'Zadané používateľské meno alebo heslo je nesprávne. Skúste to prosím znova!',\n\t\t'login_blocked' => 'Tento účet bol pozastavený kvôli príliš mnohým chybám prihlásenia. <br />Skúste to prosím znova za %s sekúnd.',\n\t\t'notallreqfieldsorerrors' => 'Nevyplnili ste všetky polia alebo ste niektoré vyplnili nesprávne.',\n\t\t'oldpasswordnotcorrect' => 'Staré heslo nie je správne.',\n\t\t'youcantallocatemorethanyouhave' => 'Nemôžete prideliť viac zdrojov, než máte k dispozícii.',\n\t\t'mustbeurl' => 'Nezadali ste platnú alebo úplnú URL adresu (napr. http://somedomain.com/error404.htm)',\n\t\t'invalidpath' => 'Nezvolili ste platnú adresu URL (možno problémy so zoznamom adries?)',\n\t\t'stringisempty' => 'Chýbajúci vstup v poli',\n\t\t'stringiswrong' => 'Nesprávny vstup v poli',\n\t\t'newpasswordconfirmerror' => 'Nové heslo a potvrdenie hesla sa nezhodujú',\n\t\t'mydomain' => '\\'Doména\\'',\n\t\t'mydocumentroot' => '\\'Documentroot\\'',\n\t\t'loginnameexists' => 'Prihlasovacie meno %s už existuje',\n\t\t'emailiswrong' => 'E-mailová adresa %s obsahuje neplatné znaky alebo je nekompletná',\n\t\t'emailexists' => 'E-mailová adresa %s je už používaná iným správcom.',\n\t\t'emailexistsanon' => 'E-mailová adresa %s je už používaná.',\n\t\t'alternativeemailiswrong' => 'Zadaná alternatívna e-mailová adresa %s na odoslanie prihlasovacích údajov sa zdá byť neplatná',\n\t\t'loginnameiswrong' => 'Prihlasovacie meno \"%s\" obsahuje nepovolené znaky.',\n\t\t'loginnameiswrong2' => 'Prihlasovacie meno obsahuje príliš veľa znakov. Povolených je iba %s znakov.',\n\t\t'userpathcombinationdupe' => 'Kombinácia používateľského mena a cesty už existuje',\n\t\t'patherror' => 'Všeobecná chyba! Cesta nemôže byť prázdna',\n\t\t'errordocpathdupe' => 'Možnosť pre cestu %s už existuje',\n\t\t'adduserfirst' => 'Najprv prosím vytvorte zákazníka',\n\t\t'domainalreadyexists' => 'Doména %s je už priradená zákazníkovi',\n\t\t'nolanguageselect' => 'Nebol vybratý žiadny jazyk.',\n\t\t'nosubjectcreate' => 'Musíte definovať tému pre túto šablónu e-mailu.',\n\t\t'nomailbodycreate' => 'Musíte definovať text e-mailu pre túto šablónu e-mailu.',\n\t\t'templatenotfound' => 'Šablóna nebola nájdená.',\n\t\t'alltemplatesdefined' => 'Nemôžete definovať viac šablón, všetky jazyky sú už podporované.',\n\t\t'wwwnotallowed' => 'www nie je povolené pre subdomény.',\n\t\t'subdomainiswrong' => 'Subdoména %s obsahuje neplatné znaky.',\n\t\t'domaincantbeempty' => 'Názov domény nemôže byť prázdny.',\n\t\t'domainexistalready' => 'Doména %s už existuje.',\n\t\t'domainisaliasorothercustomer' => 'Vybratá aliasová doména je sama osebe doménou, má inú kombináciu ip/port alebo patrí inému zákazníkovi.',\n\t\t'emailexistalready' => 'E-mailová adresa %s už existuje.',\n\t\t'maindomainnonexist' => 'Hlavná doména %s neexistuje.',\n\t\t'maindomaindeactivated' => 'Hlavná doména %s je deaktivovaná.',\n\t\t'destinationnonexist' => 'Prosím vytvorte preposielateľa v poli \"Destination\".',\n\t\t'destinationalreadyexistasmail' => 'Preposielateľ do %s už existuje ako aktívna e-mailová adresa.',\n\t\t'destinationalreadyexist' => 'Už ste definovali presmerovanie na \"%s\"',\n\t\t'destinationiswrong' => 'Preposielateľ(ia) %s obsahuje neplatné znaky alebo je neúplný.',\n\t\t'dumpfoldercannotbedocroot' => 'Priečinok pre dátové výpisy nemôže byť váš domovský adresár, zvoľte prosím priečinok vo svojom domovskom adresári, napr. /dumps',\n\t\t'templatelanguagecombodefined' => 'Kombinácia vybratého jazyka/šablóny už bola definovaná.',\n\t\t'templatelanguageinvalid' => 'Vybratý jazyk neexistuje',\n\t\t'ipstillhasdomains' => 'Kombinácia IP/portu, ktorú chcete odstrániť, stále obsahuje priradené domény, pred zmazaním tejto kombinácie IP/portu ich prosím priraďte k iným kombináciám IP/portu.',\n\t\t'cantdeletedefaultip' => 'Nie je možné odstrániť predvolenú kombináciu IP/portu, pred zmazaním tejto kombinácie IP/portu prosím nastavte predvolené nastavenie inej kombinácie IP/portu.',\n\t\t'cantdeletesystemip' => 'Nie je možné odstrániť poslednú systémovú IP adresu ani vytvoriť novú kombináciu IP/Port pre systémovú IP adresu alebo zmeniť systémovú IP adresu.',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'Port\\'',\n\t\t'myipdefault' => 'Musíte vybrať kombináciu IP/port, ktorá by mala byť predvolená.',\n\t\t'myipnotdouble' => 'Táto kombinácia IP/portu už existuje.',\n\t\t'admin_domain_emailsystemhostname' => 'Názov servera nie je možné použiť ako doménu zákazníka.',\n\t\t'cantchangesystemip' => 'Nie je možné zmeniť poslednú systémovú IP adresu ani vytvoriť novú kombináciu IP/Port pre systémovú IP adresu alebo zmeniť systémovú IP adresu.',\n\t\t'sessiontimeoutiswrong' => 'Je povolená iba číselná hodnota pre \"časový limit relácie\".',\n\t\t'maxloginattemptsiswrong' => 'Sú povolené iba číselné \"maximálne pokusy o prihlásenie\".',\n\t\t'deactivatetimiswrong' => 'Sú povolené iba číselné \"doby deaktivácie\".',\n\t\t'accountprefixiswrong' => '\"Zákaznícky prefix\" je nesprávny.',\n\t\t'mysqlprefixiswrong' => '\"SQL prefix\" je nesprávny.',\n\t\t'ftpprefixiswrong' => 'Prefix FTP je nesprávny.',\n\t\t'ipiswrong' => '\"IP adresa\" je nesprávna. Je povolená iba platná IP adresa.',\n\t\t'vmailuidiswrong' => '\"UID e-mailov\" je nesprávne. Je povolené iba číselné UID.',\n\t\t'vmailgidiswrong' => 'Položka „mails-gid\" je chybná. Povolené je iba číselné GID.',\n\t\t'adminmailiswrong' => '\"Adresa odosielateľa\" je chybná. Povolená je iba platná e-mailová adresa.',\n\t\t'pagingiswrong' => 'Hodnota \"Položky na stránku\" je nesprávna. Povolené sú iba číselné znaky.',\n\t\t'phpmyadminiswrong' => 'PhpMyAdmin odkaz nie je platný.',\n\t\t'webmailiswrong' => 'Odkaz na webmail nie je platný.',\n\t\t'webftpiswrong' => 'WebFTP odkaz nie je platný.',\n\t\t'stringformaterror' => 'Hodnota pre pole \"%s\" nie je v očakávanom formáte.',\n\t\t'loginnameisusingprefix' => 'Nemôžete vytvoriť účty, ktoré začínajú znakom „%s\", pretože tento prefix je nastavený na automatické pomenovanie účtu. Zadajte prosím iný názov účtu.',\n\t\t'loginnameissystemaccount' => 'Účet \"%s\" už v systéme existuje a nie je ho možné použiť. Zadajte iný názov účtu.',\n\t\t'loginnameisreservedname' => 'Názov účtu „%s\" je vyhradený pre vnútorný systém a nie je ho možné použiť.',\n\t\t'youcantdeleteyourself' => 'Z bezpečnostných dôvodov nie je možné odstrániť samého seba.',\n\t\t'youcanteditallfieldsofyourself' => 'Poznámka: Z bezpečnostných dôvodov nie je možné upravovať všetky polia vlastného účtu.',\n\t\t'documentrootexists' => 'Adresár \"%s\" pre tohto zákazníka už existuje. Pred pridaním zákazníka ho odstráňte.',\n\t\t'norepymailiswrong' => 'Položka „Noreply-address\" je chybná. Povolená je iba platná e-mailová adresa.',\n\t\t'logerror' => 'Chyba logu: %s',\n\t\t'nomessagetosend' => 'Nezadali ste správu.',\n\t\t'norecipientsgiven' => 'Nezadali ste žiadneho príjemcu',\n\t\t'errorsendingmail' => 'Správa pre \"%s\" sa nepodarila',\n\t\t'errorsendingmailpub' => 'Správa na danú e-mailovú adresu sa nepodarila',\n\t\t'cannotreaddir' => 'Nie je možné prečítať adresár \"%s\"',\n\t\t'invalidip' => 'Neplatná IP adresa: %s',\n\t\t'invalidmysqlhost' => 'Neplatná adresa MySQL hostiteľa: %s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => 'Nemôžete povoliť Webalizer a AWstats súčasne, zvoľte si jeden z nich',\n\t\t'cannotwritetologfile' => 'Nie je možné otvoriť súbor protokolu %s na zápis',\n\t\t'vmailquotawrong' => 'Kvóta musí byť kladné číslo.',\n\t\t'allocatetoomuchquota' => 'Pokúsili ste sa prideliť kvótu %s MB, ale už vám nezostáva dostatok.',\n\t\t'missingfields' => 'Nie všetky povinné polia boli vyplnené.',\n\t\t'requiredfield' => 'Toto pole je povinné.',\n\t\t'accountnotexisting' => 'Zadaný e-mailový účet neexistuje.',\n\t\t'nopermissionsorinvalidid' => 'Nemáte dostatočné oprávnenia na zmenu tohto nastavenia alebo bolo udelené neplatné Id.',\n\t\t'phpsettingidwrong' => 'PHP konfigurácia s týmto ID neexistuje',\n\t\t'descriptioninvalid' => 'Popis je príliš krátky/príliš dlhý alebo obsahuje nepovolené znaky.',\n\t\t'info' => 'Info',\n\t\t'filecontentnotset' => 'Súbor nemôže byť prázdny!',\n\t\t'customerdoesntexist' => 'Zvolený zákazník neexistuje.',\n\t\t'admindoesntexist' => 'Zvolený administrátor neexistuje.',\n\t\t'ipportdoesntexist' => 'Kombinácia ip/portu, ktorú ste zvolili, neexistuje.',\n\t\t'usernamealreadyexists' => 'Používateľské meno %s už existuje.',\n\t\t'plausibilitychecknotunderstood' => 'Odpoveď kontroly vierohodnosti nie je zrozumiteľná.',\n\t\t'errorwhensaving' => 'Došlo k chybe pri ukladaní poľa %s',\n\t\t'hiddenfieldvaluechanged' => 'Hodnota skrytého poľa \"%s\" sa zmenila pri úpravách nastavení.<br /><br />Toto zvyčajne nie je veľký problém, ale z toho dôvodu nie je možné uložiť nastavenia.',\n\t\t'notrequiredpasswordlength' => 'Zadané heslo je príliš krátke. Zadajte prosím aspoň %s znakov.',\n\t\t'overviewsettingoptionisnotavalidfield' => 'Ups, pole, ktoré by malo byť zobrazené ako voľba v prehľade nastavení, nie je výnimočne povolený typ. Za to môžete viniť vývojárov. Nemalo by sa to stať!',\n\t\t'pathmaynotcontaincolon' => 'Cesta, ktorú ste zadali, by nemala obsahovať dvojbodku (\":\"). Zadajte správnu hodnotu cesty.',\n\t\t'invaliddocumentrooturl' => 'URL adresa, ktorú ste zadali pre koreňový adresár, nie je platná. Zadajte prosím správnu URL adresu alebo unixovú cestu.',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => 'Zložitosť zadaného hesla nebola dostatočná.<br />Ak máte nejaké otázky ohľadom zložitosti',\n\t\t'invaliderrordocumentvalue' => 'Hodnota uvedená ako chybový dokument sa nezdá byť platným súborom, URL alebo reťazcom.',\n\t\t'intvaluetoolow' => 'Zadané číslo je príliš nízke (pole %s)',\n\t\t'intvaluetoohigh' => 'Zadané číslo je príliš vysoké (pole %s)',\n\t\t'phpfpmstillenabled' => 'PHP-FPM je momentálne aktívny. Pred aktiváciou FCGID ho deaktivujte',\n\t\t'fcgidstillenabled' => 'FCGID je momentálne aktívny. Prosím deaktivujte ho pred aktiváciou PHP-FPM',\n\t\t'domains_cantdeletedomainwithaliases' => 'Nemôžete odstrániť doménu, ktorá sa používa pre aliasové domény. Najprv musíte aliasy odstrániť.',\n\t\t'user_banned' => 'Váš účet bol uzamknutý. Pre ďalšie informácie kontaktujte svojho správcu.',\n\t\t'session_timeout' => 'Hodnota je príliš nízka',\n\t\t'session_timeout_desc' => 'Časový limit relácie by nemal byť nižší ako 1 minúta.',\n\t\t'invalidhostname' => 'Názov hostiteľa musí byť platná doména. Nemôže byť prázdny ani nesmie obsahovať iba medzery',\n\t\t'operationnotpermitted' => 'Operácia nie je povolená!',\n\t\t'featureisdisabled' => 'Funkcia %s je zakázaná. Obraťte sa na svojho poskytovateľa služieb.',\n\t\t'usercurrentlydeactivated' => 'Používateľ %s je momentálne deaktivovaný',\n\t\t'setlessthanalreadyused' => 'Nie je možné nastaviť menej zdrojov \\'%s\\', ako tento používateľ už použil<br />',\n\t\t'stringmustntbeempty' => 'Hodnota poľa %s nesmie byť prázdna',\n\t\t'sslcertificateismissingprivatekey' => 'Musíte zadať súkromný kľúč k vášmu certifikátu',\n\t\t'sslcertificatewrongdomain' => 'Daný certifikát nepatrí k tejto doméne',\n\t\t'sslcertificateinvalidcert' => 'Daný obsah certifikátu sa nezdá byť platným certifikátom',\n\t\t'sslcertificateinvalidcertkeypair' => 'Zadaný súkromný kľúč nepatrí k danému certifikátu',\n\t\t'sslcertificateinvalidca' => 'Dané údaje certifikátov CA sa nezdajú byť platným certifikátom',\n\t\t'sslcertificateinvalidchain' => 'Údaje daného reťazca certifikátov sa nezdajú byť platným certifikátom',\n\t\t'givendirnotallowed' => 'Zadaný adresár v poli %s nie je povolený.',\n\t\t'sslredirectonlypossiblewithsslipport' => 'Použitie Let\\'s Encrypt je možné iba v prípade, že doména má priradenú aspoň jednu ssl-povolenú kombináciu IP/port.',\n\t\t'fcgidstillenableddeadlock' => 'FCGID je momentálne aktívny.<br />Prosím deaktivujte ho pred prepnutím na iný webový server ako Apache2',\n\t\t'send_report_title' => 'Odoslať správu o chybe',\n\t\t'send_report_desc' => 'Ďakujeme, že ste nahlásili túto chybu a pomohli nám zlepšiť froxlor.<br />Toto je e-mail, ktorý bude odoslaný vývojárom froxloru:',\n\t\t'send_report' => 'Odoslať hlásenie',\n\t\t'send_report_error' => 'Chyba pri odosielaní hlásenia: <br />%s',\n\t\t'notallowedtouseaccounts' => 'Váš účet neumožňuje používať IMAP/POP3. E-mailové účty nie je možné pridať.',\n\t\t'cannotdeletehostnamephpconfig' => 'Táto konfigurácia PHP sa používa vo froxlor-vhost a nie je možné ju odstrániť.',\n\t\t'cannotdeletedefaultphpconfig' => 'Táto konfigurácia PHP je nastavená ako predvolená a nie je možné ju odstrániť.',\n\t\t'passwordshouldnotbeusername' => 'Heslo by nemalo byť rovnaké ako používateľské meno.',\n\t\t'no_phpinfo' => 'Je nám ľúto, phpinfo() nie je možné načítať',\n\t\t'moveofcustomerfailed' => 'Presun zákazníka k vybranému administrátorovi/predajcovi zlyhal. Majte na pamäti, že všetky ostatné zmeny zákazníka boli úspešne aplikované v tejto fáze.<br><br>Správa o chybe: %s',\n\t\t'domain_import_error' => 'Pri importe domén došlo k chybe: %s',\n\t\t'fcgidandphpfpmnogoodtogether' => 'FCGID a PHP-FPM nie je možné súčasne aktivovať',\n\t\t'no_apcuinfo' => 'Žiadne informácie o cache nie sú k dispozícii. APCu sa nezdá byť spustená.',\n\t\t'no_opcacheinfo' => 'Žiadne informácie o OPCache nie sú k dispozícii. OPCache sa nezdá byť načítaná.',\n\t\t'inactive_opcacheinfo' => 'OPCache sa zdá byť nainštalovaná, ale nie je aktivovaná.',\n\t\t'nowildcardwithletsencrypt' => 'Let\\'s Encrypt nevie spracovávať wildcard domény pomocou ACME vo froxlore (vyžaduje dns-challenge), ospravedlňujeme sa. Nastavte prosím ServerAlias na WWW alebo ho úplne zakážte',\n\t\t'customized_version' => 'Zdá sa, že vaša inštalácia froxloru bola upravená, na úpravy neposkytujeme podporu, je nám ľúto.',\n\t\t'autoupdate_0' => 'Neznáma chyba',\n\t\t'autoupdate_1' => 'Nastavenie PHP allow_url_fopen je zakázané. Autoupdate musí byť povolený v php.ini',\n\t\t'autoupdate_2' => 'PHP zip rozšírenie nebolo nájdené, uistite sa, že je nainštalované a aktivované',\n\t\t'autoupdate_4' => 'Archív froxloru nemohol byť uložený na disk :(',\n\t\t'autoupdate_5' => 'version.froxlor.org vrátil neprijateľné hodnoty :(',\n\t\t'autoupdate_6' => 'Ojoj, na stiahnutie nebola zadaná žiadna (platná) verzia :(',\n\t\t'autoupdate_7' => 'Stiahnutý archív nebol nájdený :(',\n\t\t'autoupdate_8' => 'Archív nie je možné extrahovať :(',\n\t\t'autoupdate_9' => 'Stiahnutý súbor neprešiel kontrolou integrity. Skúste prosím aktualizovať znova.',\n\t\t'autoupdate_10' => 'Minimálna podporovaná verzia PHP je 7.4.0',\n\t\t'autoupdate_11' => 'Webupdate je zakázaný',\n\t\t'mailaccistobedeleted' => 'Iný účet s rovnakým názvom (%s) je momentálne mazaný, a preto ho nie je možné teraz pridať.',\n\t\t'customerhasongoingexportjob' => 'Na spracovanie už čaká úloha exportu dát, buďte prosím trpezliví.',\n\t\t'exportfunctionnotenabled' => 'Funkcia exportu nie je povolená',\n\t\t'dns_domain_nodns' => 'DNS nie je pre túto doménu povolené',\n\t\t'dns_content_empty' => 'Nebol zadaný žiadny obsah',\n\t\t'dns_content_invalid' => 'Neplatný obsah DNS',\n\t\t'dns_arec_noipv4' => 'Neplatná IP adresa pre A-záznam',\n\t\t'dns_aaaarec_noipv6' => 'Neplatná IP adresa pre zadaný záznam AAAA',\n\t\t'dns_mx_prioempty' => 'Bola zadaná neplatná MX priorita',\n\t\t'dns_mx_needdom' => 'Hodnota MX obsahu musí byť platný názov domény',\n\t\t'dns_mx_noalias' => 'Hodnota MX nemôže byť položka CNAME.',\n\t\t'dns_cname_invaliddom' => 'Neplatný názov domény pre záznam CNAME',\n\t\t'dns_cname_nomorerr' => 'Záznam zdroja s rovnakým názvom záznamu už existuje. Nie je možné ho použiť ako CNAME.',\n\t\t'dns_other_nomorerr' => 'Záznam CNAME s rovnakým názvom záznamu už existuje. Nie je možné ho použiť pre iný typ.',\n\t\t'dns_ns_invaliddom' => 'Neplatný názov domény pre NS záznam',\n\t\t'dns_srv_prioempty' => 'Bola zadaná neplatná SRV priorita',\n\t\t'dns_srv_invalidcontent' => 'Neplatný obsah SRV musí obsahovať váhu, port a cieľ, napr.: 5 5060 sipserver.example.com.',\n\t\t'dns_srv_needdom' => 'Hodnota SRV cieľa musí byť platný názov domény',\n\t\t'dns_srv_noalias' => 'Hodnota SRV cieľa nemôže byť položka CNAME.',\n\t\t'dns_duplicate_entry' => 'Záznam už existuje',\n\t\t'dns_notfoundorallowed' => 'Doména nebola nájdená alebo nemáte oprávnenie',\n\t\t'domain_nopunycode' => 'Nesmiete špecifikovať punycode (IDNA). Doména bude automaticky prevedená',\n\t\t'domain_noipaddress' => 'Nie je možné pridať IP adresu ako doménu',\n\t\t'dns_record_toolong' => 'Záznamy/popisky môžu mať iba 63 znakov',\n\t\t'noipportgiven' => 'Nie je zadaný žiadny IP/port',\n\t\t'nosslippportgiven' => 'Pri povolení SSL je potrebné zvoliť SSL IP/port',\n\t\t'jsonextensionnotfound' => 'Táto funkcia vyžaduje rozšírenie php-json.',\n\t\t'cannotdeletesuperadmin' => 'Prvého správcu nie je možné odstrániť.',\n\t\t'no_wwwcnamae_ifwwwalias' => 'Nie je možné nastaviť CNAME záznam pre \"www\" ako doménu pre generovanie www-aliasu. Zmeňte prosím nastavenie buď na \"No alias\" alebo \"Wildcard alias\"',\n\t\t'local_group_exists' => 'Táto skupina už v systéme existuje.',\n\t\t'local_group_invalid' => 'Zadaný názov skupiny je neplatný',\n\t\t'local_user_invalid' => 'Zadané používateľské meno je neplatné alebo neexistuje',\n\t\t'local_user_isfroxloruser' => 'Zadané používateľské meno je spravované froxlorom a nie je možné ho v tomto kontexte použiť',\n\t\t'invaliddnsforletsencrypt' => 'DNS domény neobsahuje žiadnu z vybraných IP adries. Vytvorenie Let\\'s Encrypt certifikátu nie je možné.',\n\t\t'notallowedphpconfigused' => 'Pokus o použitie php konfigurácie, ktorá nie je priradená zákazníkovi',\n\t\t'pathmustberelative' => 'Používateľ nemá oprávnenie špecifikovať adresáre mimo domovský adresár zákazníka. Zadajte relatívnu cestu (bez úvodného /).',\n\t\t'mysqlserverstillhasdbs' => 'Databázový server nie je možné odstrániť zo zoznamu povolených zákazníkov, pretože na ňom stále existujú databázy.',\n\t\t'domaincannotbeedited' => 'Nemáte oprávnenie upravovať doménu %s',\n\t\t'invalidcronjobintervalvalue' => 'Interval Cronjobu musí byť jeden z: %s',\n\t\t'phpgdextensionnotavailable' => 'Rozšírenie PHP GD nie je dostupné. Nie je možné overiť dáta obrázkov',\n\t\t'2fa_wrongcode' => 'Zadaný kód nie je platný',\n\t\t'gnupgextensionnotavailable' => 'Rozšírenie PHP GnuPG nie je dostupné. Nie je možné overiť PGP verejný kľúč',\n\t\t'invalidpgppublickey' => 'PGP verejný kľúč nie je platný',\n\t\t'invalid_validtime' => 'Platný čas v sekundách môže byť iba medzi 10 a 120',\n\t\t'customerphpenabledbutnoconfig' => 'Zákazník má PHP aktivované, ale nebola vybraná žiadna konfigurácia PHP.',\n\t\t'emaildomainstillhasaddresses' => 'Nie je možné deaktivovať flag poštovej domény, pretože pre túto doménu stále existujú e-mailové adresy.',\n\t\t'tls13requiredforhttp3' => 'Flag domény http3 je povolený, ale protokoly SSL nezahŕňajú TLSv1.3.',\n\t\t'senderdomainnotowned' => 'Zadaná doména „%s\" nemôže byť použitá.',\n\t\t'emailhasnoaccount' => 'Zadaná e-mailová adresa „%s\" nemá žiadny účet, nie je možné pridať adresu odosielateľa.',\n\t],\n\t'extras' => [\n\t\t'description' => 'Tu môžete pridať niektoré doplnky, napríklad ochranu adresárov.<br />Po každej zmene bude systém potrebovať určitý čas, aby aplikoval nové nastavenia.',\n\t\t'directoryprotection_add' => 'Pridať ochranu adresára',\n\t\t'view_directory' => 'Zobraziť obsah adresára',\n\t\t'pathoptions_add' => 'Pridať možnosti cesty',\n\t\t'directory_browsing' => 'Prehliadanie obsahu adresára',\n\t\t'pathoptions_edit' => 'Upraviť možnosti cesty',\n\t\t'error404path' => '404',\n\t\t'error403path' => '403',\n\t\t'error500path' => '500',\n\t\t'error401path' => '401',\n\t\t'errordocument404path' => 'ErrorDocument 404',\n\t\t'errordocument403path' => 'ErrorDocument 403',\n\t\t'errordocument500path' => 'ErrorDocument 500',\n\t\t'errordocument401path' => 'ErrorDocument 401',\n\t\t'execute_perl' => 'Spustiť perl/CGI',\n\t\t'htpasswdauthname' => 'Dôvod overenia (AuthName)',\n\t\t'directoryprotection_edit' => 'Upraviť ochranu adresára',\n\t\t'export' => 'Vytvoriť výpis dát',\n\t\t'dump_web' => 'Zahrnúť webové dáta',\n\t\t'dump_mail' => 'Zahrnúť e-mailové dáta',\n\t\t'dump_dbs' => 'Zahrnúť databázy',\n\t\t'path_protection_label' => '<strong class=\"text-danger\">Dôležité</strong>',\n\t\t'path_protection_info' => 'Dôrazne odporúčame chrániť danú cestu, pozri \"Extra\" -> \"Ochrana adresárov\"',\n\t],\n\t'ftp' => [\n\t\t'description' => 'Tu môžete vytvoriť a zmeniť svoje FTP účty.<br />Zmeny sú vykonané okamžite a účty môžu byť použité okamžite.',\n\t\t'account_add' => 'Vytvoriť účet',\n\t\t'account_edit' => 'Upraviť ftp účet',\n\t\t'editpassdescription' => 'Nastavte nové heslo alebo ponechajte prázdne pre zachovanie existujúceho.',\n\t\t'sshkey_add' => 'Pridať ssh kľúč',\n\t\t'sshkey_edit' => 'Upraviť ssh kľúč',\n\t],\n\t'gender' => [\n\t\t'title' => 'Titul',\n\t\t'male' => 'Pán',\n\t\t'female' => 'Pani',\n\t\t'undef' => '',\n\t],\n\t'imprint' => 'Právne ustanovenia',\n\t'index' => [\n\t\t'customerdetails' => 'Podrobnosti o zákazníkovi',\n\t\t'accountdetails' => 'Nastavenie účtu',\n\t],\n\t'integrity_check' => [\n\t\t'databaseCharset' => 'Sada znakov databázy (mala by byť UTF-8)',\n\t\t'domainIpTable' => 'IP &lt;&dash;&gt; referencie domén',\n\t\t'subdomainSslRedirect' => 'Falošný príznak presmerovania SSL pre non-ssl domény',\n\t\t'froxlorLocalGroupMemberForFcgidPhpFpm' => 'froxlor-používatelia v skupinách zákazníkov (pre FCGID/php-fpm)',\n\t\t'webserverGroupMemberForFcgidPhpFpm' => 'Webový používateľ v skupinách zákazníkov (pre FCGID/php-fpm)',\n\t\t'subdomainLetsencrypt' => 'Hlavné domény bez SSL portu nemajú žiadne subdomény s aktívnym presmerovaním SSL',\n\t],\n\t'logger' => [\n\t\t'date' => 'Dátum',\n\t\t'type' => 'Typ',\n\t\t'action' => 'Akcia',\n\t\t'user' => 'Používateľ',\n\t\t'truncate' => 'Vyprázdniť protokol',\n\t\t'reseller' => 'Predajca',\n\t\t'admin' => 'Administrátor',\n\t\t'cron' => 'Cronjob',\n\t\t'login' => 'Prihlásenie',\n\t\t'intern' => 'Interné',\n\t\t'unknown' => 'Neznámy',\n\t],\n\t'login' => [\n\t\t'username' => 'Používateľské meno',\n\t\t'password' => 'Heslo',\n\t\t'language' => 'Jazyk',\n\t\t'login' => 'Prihlásiť sa',\n\t\t'logout' => 'Odhlásiť sa',\n\t\t'profile_lng' => 'Jazyk profilu',\n\t\t'welcomemsg' => 'Pre prístup k vášmu účtu sa prihláste.',\n\t\t'forgotpwd' => 'Zabudli ste heslo?',\n\t\t'presend' => 'Obnoviť heslo',\n\t\t'email' => 'E-mailová adresa',\n\t\t'remind' => 'Obnoviť heslo',\n\t\t'usernotfound' => 'Používateľ nebol nájdený!',\n\t\t'backtologin' => 'Späť na prihlásenie',\n\t\t'combination_not_found' => 'Kombinácia používateľa a e-mailovej adresy nenájdená.',\n\t\t'2fa' => 'Dvojfázové overenie (2FA)',\n\t\t'2facode' => 'Zadajte prosím 2FA kód',\n\t\t'2faremember' => 'Dôverovať prehliadaču',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => 'Dobrý deň,\\\\n\\\\nváš e-mailový účet {EMAIL}\\\\nbyl úspešne vytvorený.\\\\n\\\\nToto je automaticky vytvorený\\\\ne-mail, prosím neodpovedajte naň!\\\\n\\\\nVáš správca',\n\t\t\t'subject' => 'E-mailový účet bol úspešne nastavený',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => 'Dobrý deň {SALUTATION},\\\\n\\\\ntu je váš účet:\\\\n\\\\nPoužívateľské meno: {USERNAME}\\\\nHeslo: {PASSWORD}\\\\n\\\\nĎakujeme,\\\\nváš správca',\n\t\t\t'subject' => 'Informácie o účte',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => 'Dobrý deň {SALUTATION},\\\\n\\\\nVáš e-mailový účet {EMAIL}\\\\nbyl úspešne vytvorený.\\\\nVaše heslo je {PASSWORD}.\\\\n\\\\nToto je automaticky vytvorený e-mail\\\\n, prosím neodpovedajte naň!\\\\n\\\\nS úctou, váš správca',\n\t\t\t'subject' => 'E-mailový účet bol úspešne nastavený',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => 'Obnovenie hesla',\n\t\t\t'mailbody' => 'Dobrý deň, {SALUTATION},\\\\n\\\\ntu je váš odkaz pre nastavenie nového hesla. Tento odkaz je platný po dobu nasledujúcich 24 hodín.\\\\n\\\\n{LINK}\\\\n\\\\nĎakujeme,\\\\nváš správca',\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] Bola vytvorená nová databáza',\n\t\t\t'mailbody' => 'Dobrý deň {CUST_NAME},\n\npráve ste pridali novú databázu. Tu sú zadané informácie:\n\nNázov databázy: {DB_NAME}\nHeslo: {DB_PASS}\nPopis: {DB_DESC}\nHostiteľ databázy: {DB_SRV}\nphpMyAdmin: {PMA_URI}\nS pozdravom váš správca',\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => 'Nový ftp používateľ vytvorený',\n\t\t\t'mailbody' => 'Dobrý deň {CUST_NAME},\n\npráve ste pridali nového ftp používateľa. Tu sú zadané informácie:\n\nPoužívateľské meno: {USR_NAME}\nHeslo: {USR_PASS}\nCesta: {USR_PATH}\n\nS úctou, váš správca',\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => 'Vážený/á {SALUTATION},\\\\n\\\\npoužili ste {TRAFFICUSED} z dostupnej prevádzky {TRAFFIC}.\\\\nToto je viac ako {MAX_PERCENT}%%.\\\\n\\\\nS úctou váš správca',\n\t\t\t'subject' => 'Dosiahnutie limitu prevádzky',\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => 'Vážený/á {SALUTATION},\\\\n\\\\npoužili ste {DISKUSED} z dostupného {DISKAVAILABLE} miesta na disku.\\\\nToto je viac ako {MAX_PERCENT}%%.\\\\n\\\\nS úctou váš správca',\n\t\t\t'subject' => 'Dosiahnutie limitu na disku',\n\t\t],\n\t\t'2fa' => [\n\t\t\t'mailbody' => 'Dobrý deň,\\\\n\\\\nváš 2FA prihlasovací kód je: {CODE}.\\\\n\\\\nToto je automaticky vytvorený\\\\ne-mail, prosím neodpovedajte naň!\\\\n\\\\nVáš správca',\n\t\t\t'subject' => 'froxlor - 2FA kód',\n\t\t],\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => 'Hlavný',\n\t\t\t'changepassword' => 'Zmeniť heslo',\n\t\t\t'changelanguage' => 'Zmeniť jazyk',\n\t\t\t'username' => 'Prihlásený ako: ',\n\t\t\t'changetheme' => 'Zmeniť motív',\n\t\t\t'apihelp' => 'Nápoveda API',\n\t\t\t'apikeys' => 'API kľúče',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => 'Email',\n\t\t\t'emails' => 'Adresy',\n\t\t\t'webmail' => 'Webmail',\n\t\t\t'emailsoverview' => 'Prehľad e-mailových domén',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => 'Databázy',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => 'Domény',\n\t\t\t'settings' => 'Prehľad domén',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => 'Účty',\n\t\t\t'webftp' => 'WebFTP',\n\t\t\t'sshkeys' => 'SSH kľúče',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => 'Extra',\n\t\t\t'directoryprotection' => 'Ochrana adresára',\n\t\t\t'pathoptions' => 'Možnosti cesty',\n\t\t\t'export' => 'Export dát',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => 'Prevádzka',\n\t\t\t'current' => 'Aktuálny mesiac',\n\t\t\t'overview' => 'Celková prevádzka',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'PHP konfigurácia',\n\t\t\t'fpmdaemons' => 'Verzie PHP-FPM',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => 'Systémový protokol',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => 'Nebol odoslaný žiadny e-mail, pretože v databáze nie sú žiadni príjemcovia',\n\t\t'success' => 'Správa bola úspešne odoslaná príjemcom %s',\n\t],\n\t'mysql' => [\n\t\t'databasename' => 'Používateľ/Meno databázy',\n\t\t'databasedescription' => 'Popis databázy',\n\t\t'database_create' => 'Vytvoriť databázu',\n\t\t'description' => 'Tu môžete vytvoriť a zmeniť databázy MySQL.<br />Zmeny sú vykonané okamžite a databázymôžu byť použité okamžite.<br />V menu na ľavej strane nájdete nástroj phpMyAdmin, pomocou ktorého môžete ľahko spravovať svoju databázu.<br /><br />Pre použitie Vašich databáz vo vlastných php-skriptoch použite nasledujúce nastavenia: (Dáta <i>kurzívou</i> je potrebné zmeniť na ekvivalenty, ktoré ste zadali!<br />Názov hostiteľa: <b><SQL_HOST></b><br />Používateľské meno: <b><i>názov databázy</i></b><br />Heslo: <b><i>heslo, ktoré ste si vybrali</i></b><br />Databáza: <b><i>názov databázy</i></b>',\n\t\t'mysql_server' => 'Server MySQL',\n\t\t'database_edit' => 'Upraviť databázu',\n\t\t'size' => 'Veľkosť',\n\t\t'privileged_user' => 'Oprávnený databázový používateľ',\n\t\t'privileged_passwd' => 'Heslo pre oprávneného používateľa',\n\t\t'unprivileged_passwd' => 'Heslo pre neoprávneného používateľa',\n\t\t'mysql_ssl_ca_file' => 'Certifikát SSL servera',\n\t\t'mysql_ssl_verify_server_certificate' => 'Overiť certifikát SSL servera',\n\t\t'globaluserinfo' => 'Pre prístup k databázam môžete navyše použiť vaše froxlor prihlásenie (používateľ: %s), ktoré má automaticky prístup ku všetkým databázam.<br />Odporúčame <b>ne</b>použiť pre aplikácie, iba pre správu (napr. cez phpMyAdmin).',\n\t\t'edit_global_user' => 'Upraviť administrátora',\n\t],\n\t'opcacheinfo' => [\n\t\t'generaltitle' => 'Všeobecné informácie',\n\t\t'resetcache' => 'Resetovať OPcache',\n\t\t'version' => 'Verzia OPCache',\n\t\t'phpversion' => 'Verzia PHP',\n\t\t'runtimeconf' => 'Spustiteľná konfigurácia',\n\t\t'start' => 'Čas spustenia',\n\t\t'lastreset' => 'Posledný reštart',\n\t\t'oomrestarts' => 'Počet reštartov OOM',\n\t\t'hashrestarts' => 'Počet reštartov hash',\n\t\t'manualrestarts' => 'Manuálny počet reštartov',\n\t\t'hitsc' => 'Počet zásahov',\n\t\t'missc' => 'Nevyužitý počet',\n\t\t'blmissc' => 'Počet zmeškaných záznamov na čiernej listine',\n\t\t'status' => 'Stav',\n\t\t'never' => 'nikdy',\n\t\t'enabled' => 'OPcache povolené',\n\t\t'cachefull' => 'Medzipamäť je plná',\n\t\t'restartpending' => 'Čaká na reštart',\n\t\t'restartinprogress' => 'Prebieha reštart',\n\t\t'cachedscripts' => 'Počet skriptov v medzipamäti',\n\t\t'memusage' => 'Využitie pamäte',\n\t\t'totalmem' => 'Celková pamäť',\n\t\t'usedmem' => 'Využitá pamäť',\n\t\t'freemem' => 'Voľná pamäť',\n\t\t'wastedmem' => 'Premárená pamäť',\n\t\t'maxkey' => 'Maximálny počet kľúčov',\n\t\t'usedkey' => 'Použité kľúče',\n\t\t'wastedkey' => 'Premárnené kľúče',\n\t\t'strinterning' => 'String interning',\n\t\t'strcount' => 'Počet reťazcov',\n\t\t'keystat' => 'Štatistika kľúčov v medzipamäti',\n\t\t'used' => 'Využité',\n\t\t'free' => 'Voľné',\n\t\t'blacklist' => 'Čierna listina',\n\t\t'novalue' => '<i>žiadna hodnota</i>',\n\t\t'true' => '<i>true</i>',\n\t\t'false' => '<i>false</i>',\n\t\t'funcsavail' => 'Dostupné funkcie',\n\t],\n\t'panel' => [\n\t\t'edit' => 'Upraviť',\n\t\t'delete' => 'Zmazať',\n\t\t'create' => 'Vytvoriť',\n\t\t'save' => 'Uložiť',\n\t\t'yes' => 'Áno',\n\t\t'no' => 'Nie',\n\t\t'emptyfornochanges' => 'prázdne pre žiadne zmeny',\n\t\t'emptyfordefault' => 'prázdne pre predvolené nastavenie',\n\t\t'path' => 'Cesta',\n\t\t'toggle' => 'Prepínač',\n\t\t'next' => 'Ďalej',\n\t\t'dirsmissing' => 'Adresár sa nedá nájsť alebo prečítať!',\n\t\t'unlimited' => '∞',\n\t\t'urloverridespath' => 'URL (prepísanie cesty)',\n\t\t'pathorurl' => 'Cesta alebo URL',\n\t\t'ascending' => 'vzostupne',\n\t\t'descending' => 'zostupne',\n\t\t'search' => 'Vyhľadať',\n\t\t'used' => 'využité',\n\t\t'translator' => 'Prekladač',\n\t\t'reset' => 'Zahodiť zmeny',\n\t\t'pathDescription' => 'Ak priečinok neexistuje, bude vytvorený automaticky.',\n\t\t'pathDescriptionEx' => '<br /><br /><span class=\"text-danger\">Upozornenie:</span> Cesta <code>/</code> nie je povolená z dôvodu administratívneho nastavenia, bude automaticky nastavená na <code>/chosen.subdomain.tld/</code> ak nie je nastavená na iný adresár.',\n\t\t'pathDescriptionSubdomain' => 'Ak priečinok neexistuje, bude vytvorený automaticky.<br /><br />Ak chcete presmerovať na inú doménu, musí tento záznam začínať http:// alebo https://.<br /><br />Ak URL končí / je považovaná za priečinok, ak nie, je považovaná za súbor.',\n\t\t'back' => 'Späť',\n\t\t'reseller' => 'predajca',\n\t\t'admin' => 'admin',\n\t\t'customer' => 'zákazník/zákazníci',\n\t\t'send' => 'odoslať',\n\t\t'nosslipsavailable' => 'V súčasnosti neexistujú žiadne kombinácie ssl ip/port pre tento server',\n\t\t'backtooverview' => 'Späť na prehľad',\n\t\t'dateformat' => 'RRRR-MM-DD',\n\t\t'dateformat_function' => 'R-m-d',\n\t\t'timeformat_function' => 'H:i:s',\n\t\t'default' => 'Predvolené',\n\t\t'never' => 'Nikdy',\n\t\t'active' => 'Aktívny',\n\t\t'please_choose' => 'Vyberte prosím',\n\t\t'allow_modifications' => 'Povoliť zmeny',\n\t\t'megabyte' => 'MegaByte',\n\t\t'not_supported' => 'Nepodporované v: ',\n\t\t'view' => 'zobrazenie',\n\t\t'toomanydirs' => 'Príliš veľa podadresárov. Vráťte sa na manuálny výber cesty.',\n\t\t'abort' => 'Prerušiť',\n\t\t'not_activated' => 'nie je aktivované',\n\t\t'off' => 'vyp.',\n\t\t'options' => 'Možnosti',\n\t\t'neverloggedin' => 'Zatiaľ žiadne prihlásenie',\n\t\t'descriptionerrordocument' => 'Môže byť adresa URL, cesta k súboru alebo len reťazec zabalený okolo \" \"<br />Nechajte prázdne pre použitie predvolenej hodnoty servera.',\n\t\t'unlock' => 'Odomknúť',\n\t\t'theme' => 'Motív',\n\t\t'variable' => 'Premenná',\n\t\t'description' => 'Popis',\n\t\t'cancel' => 'Zrušiť',\n\t\t'ssleditor' => 'Nastavenia SSL pre túto doménu',\n\t\t'ssleditor_infoshared' => 'V súčasnosti používa certifikát nadradenej domény',\n\t\t'ssleditor_infoglobal' => 'V súčasnosti používa globálny certifikát',\n\t\t'dashboard' => 'Nástenka',\n\t\t'assigned' => 'Priradené',\n\t\t'available' => 'K dispozícii',\n\t\t'news' => 'Novinky',\n\t\t'newsfeed_disabled' => 'Novinky sú zakázané. Kliknutím na ikonu úpravy prejdete do nastavení.',\n\t\t'ftpdesc' => 'Popis FTP',\n\t\t'letsencrypt' => 'Používa Let\\'s Encrypt',\n\t\t'set' => 'Aplikovať',\n\t\t'shell' => 'Konzola',\n\t\t'sshkeydesc' => 'Popis kľúča SSH',\n\t\t'ftpuser' => 'FTP používateľ',\n\t\t'sshpubkey' => 'Verejný SSH kľúč',\n\t\t'sshpubkeyph' => \"Začína na „ssh-ed25519“, „ssh-rsa“, „ecdsa-sha2-nistp256“, „ecdsa-sha2-nistp384“, „ecdsa-sha2-nistp521“, „sk-ecdsa-sha2-nistp256@openssh.com“ alebo „sk-ssh-ed25519@openssh.com'\",\n\n\t\t'sshfingerprint' => 'Odtlačok',\n\t\t'exportpath' => [\n\t\t\t'title' => 'Cieľová cesta pre exportované dáta',\n\t\t\t'description' => 'Toto je cesta, kde bude exportovaný archív uložený. Ak sú webové dáta zahrnuté, všetky súbory z domovského adresára sú uložené mimo priečinka zadaného tu.',\n\t\t],\n\t\t'export_pgp_public_key' => [\n\t\t\t'title' => 'Verejný PGP kľúč pre šifrovanie',\n\t\t\t'description' => 'Toto je verejný PGP kľúč, ktorý bude použitý na zašifrovanie exportu. Ak necháte toto pole prázdne, export nebude zašifrovaný.',\n\t\t],\n\t\t'pgp_public_key' => 'Verejný PGP kľúč',\n\t\t'none_value' => 'Žiadna',\n\t\t'viewlogs' => 'Zobraziť protokoly',\n\t\t'not_configured' => 'Systém ešte nie je nakonfigurovaný. Kliknite sem pre prechod do konfigurácie.',\n\t\t'start_setup' => 'Spustiť nastavenie',\n\t\t'ihave_configured' => 'Nakonfiguroval som služby',\n\t\t'system_is_configured' => '<i class=\"fa-solid fa-circle-exclamation me-1\"></i>Systém je už nastavený ako nakonfigurovaný',\n\t\t'settings_before_configuration' => 'Uistite sa, že ste upravili nastavenia pred konfiguráciou služieb tu',\n\t\t'image_field_delete' => 'Odstrániť existujúci obrázok',\n\t\t'usage_statistics' => 'Využitie zdrojov',\n\t\t'security_question' => 'Bezpečnostná otázka',\n\t\t'listing_empty' => 'Neboli nájdené žiadne záznamy',\n\t\t'unspecified' => 'nešpecifikované',\n\t\t'settingsmode' => 'Režim',\n\t\t'settingsmodebasic' => 'Základný',\n\t\t'settingsmodeadvanced' => 'Rozšírený',\n\t\t'settingsmodetoggle' => 'Prepínanie režimu',\n\t\t'modalclose' => 'Zavrieť',\n\t\t'managetablecolumnsmodal' => [\n\t\t\t'title' => 'Správa stĺpcov tabuľky',\n\t\t\t'description' => 'Tu si môžete prispôsobiť viditeľné stĺpce',\n\t\t],\n\t\t'mandatoryfield' => 'Pole je povinné',\n\t\t'select_all' => 'Vybrať všetko',\n\t\t'unselect_all' => 'Odznačiť všetko',\n\t\t'searchtablecolumnsmodal' => [\n\t\t\t'title' => 'Hľadať v poliach',\n\t\t\t'description' => 'Vyberte pole, v ktorom chcete vyhľadať'\n\t\t],\n\t\t'upload_import' => 'Nahrať a importovať',\n\t\t'profile' => 'Môj profil',\n\t\t'use_checkbox_for_unlimited' => 'Hodnota „0\" deaktivuje tento zdroj. Zaškrtávacie políčko vpravo umožňuje „neobmedzené\" použitie.',\n\t\t'use_checkbox_to_disable' => 'Ak chcete túto funkciu deaktivovať, zaškrtnite políčko napravo od textového poľa.',\n\t\t'distro_mismatch' => 'Zdá sa, že ste vykonali upgrade na novú distribúciu. Nezabudnite prosím zodpovedajúcim spôsobom prekonfigovať služby.',\n\t\t'set_new_distro' => 'Nastaviť distribúciu',\n\t\t'dismiss' => 'Zavrieť',\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => 'Miestny používateľ pre PHP-FPM (froxlor vHost)',\n\t\t'vhost_httpgroup' => 'Miestna skupina pre PHP-FPM (froxlor vHost)',\n\t\t'ownvhost' => [\n\t\t\t'title' => 'Povoliť PHP-FPM pre froxlor vHost',\n\t\t\t'description' => 'Ak je povolené, froxlor bude spustený tiež pod miestnym používateľom',\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => 'Použiť mod_proxy / mod_proxy_fcgi',\n\t\t\t'description' => '<strong class=\"text-danger\">Pri používaní Debianu 9.x (Stretch) alebo novšieho</strong>musí byť povolené použitie php-fpm cez mod_proxy_fcgi. Vyžaduje aspoň apache-2.4.9',\n\t\t],\n\t\t'ini_flags' => 'Zadajte možný <strong>php_flag</strong> pre php.ini. Jeden záznam na riadok',\n\t\t'ini_values' => 'Zadajte možnú <strong>php_value</strong> pre php.ini. Jeden záznam na riadok',\n\t\t'ini_admin_flags' => 'Zadajte možný <strong>php_admin_flag</strong> pre php.ini. Jeden záznam na riadok',\n\t\t'ini_admin_values' => 'Zadajte možnú <strong>php_value</strong> pre php.ini. Jeden záznam na riadok',\n\t],\n\t'privacy' => 'Zásady ochrany súkromia',\n\t'pwdreminder' => [\n\t\t'success' => 'Obnovenie hesla bolo úspešne požiadané. Postupujte podľa pokynov v e-maile, ktorý ste dostali.',\n\t\t'notallowed' => 'Neznámy používateľ alebo obnovenie hesla je zakázané',\n\t\t'changed' => 'Vaše heslo bolo úspešne aktualizované. Teraz sa môžete prihlásiť pomocou nového hesla.',\n\t\t'wrongcode' => 'Je nám ľúto, váš aktivačný kód neexistuje alebo už vypršal.',\n\t\t'choosenew' => 'Nastaviť nové heslo',\n\t],\n\t'question' => [\n\t\t'question' => 'Bezpečnostná otázka',\n\t\t'admin_customer_reallydelete' => 'Naozaj chcete odstrániť zákazníka %s? Túto akciu nie je možné vrátiť späť!',\n\t\t'admin_domain_reallydelete' => 'Naozaj chcete odstrániť doménu %s?<br><span class=\"text-danger\"><strong>POZNÁMKA:</strong> Všetky subdomény, ftp-účty a e-mailové adresy/účty spojené s touto doménou budú odstránené!</span>',\n\t\t'admin_domain_reallydisablesecuritysetting' => 'Naozaj chcete zakázať toto nastavenie zabezpečenia OpenBasedir?',\n\t\t'admin_admin_reallydelete' => 'Naozaj chcete odstrániť správcu %s? Každý zákazník a doména budú znovu priradené k vášmu účtu.',\n\t\t'admin_template_reallydelete' => 'Naozaj chcete odstrániť šablónu \\'%s\\'?',\n\t\t'domains_reallydelete' => 'Naozaj chcete odstrániť doménu %s?',\n\t\t'email_reallydelete' => 'Naozaj chcete odstrániť e-mailovú adresu %s?',\n\t\t'email_reallydelete_account' => 'Naozaj chcete zmazať e-mailový účet %s?',\n\t\t'email_reallydelete_forwarder' => 'Naozaj chcete odstrániť preposielateľa %s?',\n\t\t'email_reallydelete_sender' => 'Naozaj chcete odstrániť povoleného odosielateľa %s?',\n\t\t'extras_reallydelete' => 'Naozaj chcete odstrániť ochranu adresára pre %s?',\n\t\t'extras_reallydelete_pathoptions' => 'Naozaj chcete odstrániť možnosti cesty pre %s?',\n\t\t'extras_reallydelete_export' => 'Naozaj chcete prerušiť plánovanú prácu na exporte?',\n\t\t'ftp_reallydelete' => 'Naozaj chcete odstrániť FTP účet %s?',\n\t\t'sshkey_reallydelete' => 'Naozaj chcete zmazať ssh-kľúč %s?',\n\t\t'mysql_reallydelete' => 'Naozaj chcete odstrániť databázu %s? Túto akciu nie je možné vrátiť späť!',\n\t\t'admin_configs_reallyrebuild' => 'Naozaj chcete znovu zostaviť všetky konfiguračné súbory?',\n\t\t'admin_customer_alsoremovefiles' => 'Odstrániť aj používateľské súbory?',\n\t\t'admin_customer_alsoremovemail' => 'Úplne odstrániť e-mailové dáta zo súborového systému?',\n\t\t'admin_customer_alsoremoveftphomedir' => 'Odobrať aj domovský adresár FTP používateľa?',\n\t\t'admin_ip_reallydelete' => 'Naozaj chcete odstrániť IP adresu %s?',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => 'Ste si istí, že chcete, aby koreň dokumentu pre túto doménu nebol v rámci koreňa zákazníka?',\n\t\t'admin_counters_reallyupdate' => 'Naozaj chcete prepočítať využitie zdrojov?',\n\t\t'admin_cleartextmailpws_reallywipe' => 'Naozaj chcete vymazať všetky nešifrované heslá e-mailového účtu z tabuľky mail_users? Túto akciu nie je možné vrátiť späť! Nastavenie nešifrovaných e-mailových hesiel bude tiež nastavené na vypnutie',\n\t\t'logger_reallytruncate' => 'Naozaj chcete zredukovať tabuľku \"%s\"?',\n\t\t'admin_quotas_reallywipe' => 'Naozaj chcete vymazať všetky kvóty v tabuľke mail_users? Toto nie je možné vrátiť späť!',\n\t\t'admin_quotas_reallyenforce' => 'Naozaj chcete vynútiť predvolenú kvótu pre všetkých používateľov? Toto nie je možné vrátiť späť!',\n\t\t'phpsetting_reallydelete' => 'Naozaj chcete odstrániť tieto nastavenia? Všetky domény, ktoré v súčasnosti používajú tieto nastavenia, budú zmenené na predvolené nastavenia.',\n\t\t'fpmsetting_reallydelete' => 'Naozaj chcete zmazať tieto nastavenia php-fpm? Všetky php konfigurácie, ktoré v súčasnosti používajú tieto nastavenia, budú zmenené na predvolené nastavenia.',\n\t\t'customer_reallyunlock' => 'Naozaj chcete odomknúť zákazníka %s?',\n\t\t'admin_integritycheck_reallyfix' => 'Naozaj chcete skúsiť automaticky opraviť všetky problémy s integritou databázy?',\n\t\t'plan_reallydelete' => 'Naozaj chcete odstrániť plán hostovania %s?',\n\t\t'apikey_reallydelete' => 'Naozaj chcete odstrániť tento api kľúč?',\n\t\t'apikey_reallyadd' => 'Naozaj chcete vytvoriť nový api kľúč?',\n\t\t'dnsentry_reallydelete' => 'Naozaj chcete odstrániť tento záznam zóny?',\n\t\t'certificate_reallydelete' => 'Naozaj chcete odstrániť tento certifikát?',\n\t\t'cache_reallydelete' => 'Naozaj chcete vymazať medzipamäť?',\n\t\t'please_enter_otp' => 'Zadajte prosím 2FA kód',\n\t\t'admin_mysqlserver_reallydelete' => 'Naozaj chcete zmazať tento MySQL-server?',\n\t],\n\t'redirect_desc' => [\n\t\t'rc_default' => 'predvolené',\n\t\t'rc_movedperm' => 'trvalo presunuté',\n\t\t'rc_found' => 'nájdené',\n\t\t'rc_seeother' => 'viď ostatné',\n\t\t'rc_tempred' => 'dočasné presmerovanie',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => 'Vypršanie platnosti relácie',\n\t\t\t'description' => 'Ako dlho musí byť používateľ neaktívny, kým bude relácia neplatná (sekundy)?',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => 'Prefix zákazníka',\n\t\t\t'description' => 'Aký prefix by mali mať zákaznícke účty?',\n\t\t],\n\t\t'mysqlprefix' => [\n\t\t\t'title' => 'SQL Prefix',\n\t\t\t'description' => 'Aký prefix by mali mať MySQL účty?</br>Použite \"RANDOM\" ako hodnotu na získanie trojmiestneho náhodného prefixu</br>Použite \"DBNAME\" ako hodnotu, pole názvu databázy je použité spolu s názvom zákazníka ako prefix.',\n\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP Prefix',\n\t\t\t'description' => 'Aký prefix by mali mať ftp účty?<br/><b>Ak toto zmeníte, musíte tiež zmeniť Quota SQL Query v konfiguračnom súbore FTP servera, ak ho používate!</b> ',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => 'Domovský adresár',\n\t\t\t'description' => 'Kde by mali byť uložené všetky domovské adresáre?',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => 'Adresár súborov protokolu',\n\t\t\t'description' => 'Kde majú byť uložené všetky súbory protokolov?',\n\t\t],\n\t\t'logfiles_script' => [\n\t\t\t'title' => 'Vlastný skript pre odosielanie súborov protokolu',\n\t\t\t'description' => 'Tu môžete zadať skript a použiť zástupné symboly <strong>{LOGFILE}, {DOMAIN} a {CUSTOMER}</strong> v prípade potreby. Ak ho chcete použiť, budete musieť aktivovať aj <strong>nastavenie logov webservera</strong>. Nie je potrebný žiadny vopred stanovený pipe-znak.',\n\t\t],\n\t\t'logfiles_format' => [\n\t\t\t'title' => 'Formát prístupového protokolu',\n\t\t\t'description' => 'Tu zadajte vlastný formát logu podľa špecifikácií webových serverov, ponechajte prázdny pre predvolený. V závislosti od vášho formátu musí byť reťazec uvedený.<br/>Ak je použité s nginx, bude vyzerať ako <i>log_format frx_custom {CONFIGURED_VALUE}</i>.<br/>Ak je použité s Apache, bude vyzerať ako <i>LogFormat {CONFIGURED_VALUE} frx_custom</i>.<br/><strong>Pozornosť</strong>: Kód nebude skontrolovaný na žiadne chyby. Ak obsahuje chyby, webový server nemusí znovu spustiť!',\n\t\t],\n\t\t'logfiles_type' => [\n\t\t\t'title' => 'Typ prístupového protokolu',\n\t\t\t'description' => 'Tu si vyberte medzi <strong>combined</strong> alebo <strong>vhost_combined</strong>.',\n\t\t],\n\t\t'logfiles_piped' => [\n\t\t\t'title' => 'Presmerovanie súborov protokolu webového servera do zadaného skriptu (viď vyššie)',\n\t\t\t'description' => 'Ak používate vlastný skript pre logy, musíte ho aktivovať, aby mohol byť spustený',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'IP adresa',\n\t\t\t'description' => 'Aká je hlavná IP adresa tohto servera?',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => 'Názov servera',\n\t\t\t'description' => 'Aký je názov hostiteľa tohto servera?',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Príkaz pre opätovné načítanie webového servera',\n\t\t\t'description' => 'Aký je príkaz webového servera pre opätovné načítanie konfiguračných súborov?',\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => 'Povoliť nameserver',\n\t\t\t'description' => 'Tu je možné globálne povoliť a zakázať nameserver.',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'Konfiguračný adresár servera DNS',\n\t\t\t'description' => 'Kde by mali byť uložené konfiguračné súbory dns-servera?',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'Príkaz pre opätovné načítanie servera DNS',\n\t\t\t'description' => 'Aký je príkaz pre znovunačítanie dns servera daemon?',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => 'UID-Mailov',\n\t\t\t'description' => 'Aké užívateľské Id by mali mať e-maily?',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => 'Mails-GID',\n\t\t\t'description' => 'Aké GroupID by mali mať e-maily?',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Domovský adresár pre e-maily',\n\t\t\t'description' => 'Kde by mali byť uložené všetky e-maily?',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => 'Odosielateľ',\n\t\t\t'description' => 'Aká je adresa odosielateľa pre e-maily odoslané z panela?',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin URL',\n\t\t\t'description' => 'Aká je adresa URL na phpMyAdmin? (musí začínať http(s)://)',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'Webmail URL',\n\t\t\t'description' => 'Aká je URL adresa webovej pošty? (musí začínať http(s)://)',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP URL',\n\t\t\t'description' => 'Aká je URL adresa na WebFTP? (musí začínať http(s)://)',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => 'Aký je váš štandardný jazyk servera?',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => 'Maximálny počet pokusov o prihlásenie',\n\t\t\t'description' => 'Maximálny počet pokusov o prihlásenie, po ktorých je účet zakázaný.',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => 'Čas deaktivácie',\n\t\t\t'description' => 'Čas (sek.) po príliš mnohých pokusoch o prihlásenie, po ktorom je deaktivovaný účet.',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => 'Typ vstupu cesty',\n\t\t\t'description' => 'Mala by byť cesta vybraná v rozbaľovacom menu alebo vo vstupnom poli?',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => 'Nameservery',\n\t\t\t'description' => 'Čiarkou oddelený zoznam obsahujúci mená všetkých nameserverov. Prvý bude primárny.',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'MX servery',\n\t\t\t'description' => 'Čiarkou oddelený zoznam obsahujúci dvojicu čísel a názvu hostiteľa oddelených medzerou (napr. \\'10 mx.example.com\\') obsahujúci mx servery.',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => 'Počet položiek na stránku',\n\t\t\t'description' => 'Koľko záznamov sa zobrazí na jednej stránke? (0 = zakázať stránkovanie)',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => 'Predvolená IP/port',\n\t\t\t'description' => 'Vyberte všetky IP adresy, ktoré chcete použiť ako predvolené pre nové domény',\n\t\t],\n\t\t'defaultsslip' => [\n\t\t\t'title' => 'Predvolená SSL IP/port',\n\t\t\t'description' => 'Vyberte všetky ssl IP adresy, ktoré chcete použiť ako predvolené pre nové domény',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => 'Cesty, ktoré sa pripoja do súboru OpenBasedir',\n\t\t\t'description' => 'Tieto cesty (oddelené dvojbodkami) budú pridané do príkazu OpenBasedir v každom kontajneri vHost.',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => 'Použiť prirodzené ľudské triedenie v zobrazení zoznamu',\n\t\t\t'description' => 'Zoradí zoznamy ako web1 -> web2 -> web11 namiesto web1 -> web11 -> web2.',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => 'Docroot pre deaktivovaných používateľov',\n\t\t\t'description' => 'Ak je používateľ deaktivovaný, použije sa táto cesta ako docroot. Ponechajte prázdne, ak vôbec nechcete vytvárať vHost hostiteľa.',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => 'Tiež ukladať nešifrované heslá e-mailových účtov do databázy',\n\t\t\t'description' => 'Ak je toto nastavené na Áno, všetky heslá budú tiež uložené v tabuľke mail_users-table (nešifrovaný text, jednoducho čitateľný pre všetkých s prístupom k databáze). Aktivujte len ak chcete použiť SASL!',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'FTP účty @doména',\n\t\t\t'description' => 'Zákazníci môžu vytvoriť FTP účty používateľ@doménazákazníka?',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => 'Povoliť FCGID',\n\t\t\t'description' => 'Použite pre spustenie PHP s príslušným používateľským účtom.<br /><br /><b>Toto vyžaduje špeciálnu konfiguráciu webového servera pre Apache, viď <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - príručka</a></b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => 'Adresár konfigurácie',\n\t\t\t\t'description' => 'Kde by mali byť uložené všetky fcgid konfiguračné súbory? Ak nepoužívate vlastný skompilovaný suexec binárny súbor, čo je bežná situácia, táto cesta musí byť pod /var/www/<br /><br /><div class=\"text-danger\">POZNÁMKA: Obsah tejto priečinky je pravidelne mazaný, aby sa zabránilo ukladaniu dát v nej manuálne.</div>',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => 'Dočasný adresár',\n\t\t\t\t'description' => 'Kde by mali byť uložené dočasné adresáre',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => 'Procesy na doménu',\n\t\t\t\t'description' => 'Koľko procesov by malo byť spustených/povolených v danej doméne? Hodnota 0 je odporúčaná, pretože PHP potom bude veľmi efektívne spravovať samotný počet procesov.',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'Wrapper v hostiteľoch Vhosts',\n\t\t\t\t'description' => 'Ako by mal byť wrapper zahrnutý do Vhosts',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => 'Globálne adresáre PEAR',\n\t\t\t\t'description' => 'Ktoré globálne adresáre PEAR by mali byť nahradené v každom konfiguračnom súbore php.ini? Rôzne adresáre musia byť oddelené dvojbodkou.',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => 'Maximálny počet požiadaviek na doménu',\n\t\t\t\t'description' => 'Koľko požiadaviek by malo byť povolených na doménu?',\n\t\t\t],\n\t\t\t'defaultini' => 'Predvolená konfigurácia PHP pre nové domény',\n\t\t\t'defaultini_ownvhost' => 'Predvolená konfigurácia PHP pre froxlor-vHost',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Časový limit nečinnosti',\n\t\t\t\t'description' => 'Časový limit nastavenia Mod FastCGI.',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => 'Použiť alternatívnu e-mailovú adresu',\n\t\t\t'description' => 'Poslať heslo na inú adresu pri vytváraní e-mailu',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Konfiguračný súbor/adresár webservera',\n\t\t\t'description' => 'Kde má byť uložená konfigurácia vHost? Tu môžete zadať buď súbor (všetky vHosty v jednom súbore), alebo adresár (každý vHost vo vlastnom súbore).',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Konfiguračný súbor/názov adresára webového servera diroptions',\n\t\t\t'description' => 'Kde má byť uložená konfigurácia diroptions? Tu môžete zadať buď súbor (všetky dioptrie v jednom súbore), alebo adresár (každá dioptria vo vlastnom súbore).',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Webserver htpasswd dirname',\n\t\t\t'description' => 'Kde majú byť súbory htpasswd pre ochranu adresára uložené?',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'MySQL-Access-Hosts',\n\t\t\t'description' => 'Čiarkami oddelený zoznam hostiteľov, od ktorých by mali mať používatelia možnosť pripojiť sa k serveru MySQL-Server. Pre povolenie podsiete je platná sieťová maska alebo cidr.',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Výstup Webalizátora',\n\t\t\t'description' => 'Verbosita webalizéra',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => 'Logovanie povolené/zakázané',\n\t\t\t'severity' => 'Úroveň protokolovania',\n\t\t\t'types' => [\n\t\t\t\t'title' => 'Typ (typy) logov',\n\t\t\t\t'description' => 'Zadajte typy logov. Ak chcete vybrať viac typov, podržte CTRL pri výbere.<br />Dostupné typy logov sú: syslog, súbor, mysql',\n\t\t\t],\n\t\t\t'logfile' => [\n\t\t\t\t'title' => 'Názov súboru pre log',\n\t\t\t\t'description' => 'Používa sa len v prípade, že typ logu obsahuje \"súbor\". Tento súbor bude vytvorený v froxlor/logs/. Táto priečinka je chránená pred verejným prístupom.',\n\t\t\t],\n\t\t\t'logcron' => 'Zaznamenávať cronjoby',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => 'Nikdy',\n\t\t\t\t'once' => 'Raz',\n\t\t\t\t'always' => 'Vždy',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => 'Povoliť využitie SSL',\n\t\t\t\t'description' => 'Zaškrtnite, ak chcete použiť SSL pre váš webový server',\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'Cesta k SSL certifikátu',\n\t\t\t\t'description' => 'Zadajte cestu vrátane názvu súboru .crt alebo .pem (hlavný certifikát)',\n\t\t\t],\n\t\t\t'openssl_cnf' => 'Predvolené nastavenia pre vytvorenie Cert súboru',\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'Cesta k súboru kľúča SSL',\n\t\t\t\t'description' => 'Zadajte cestu vrátane názvu súboru pre súkromný kľúč (väčšinou .key)',\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'Cesta k SSL CA certifikátu (voliteľné)',\n\t\t\t\t'description' => 'Overovanie klienta, nastavte to len ak viete, čo to je.',\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => 'Konfigurovať povolené SSL šifry',\n\t\t\t\t'description' => 'Toto je zoznam šifier, ktoré chcete (alebo nechcete) použiť pri komunikácii SSL. Pre zoznam šifier a spôsob, ako ich zahrnúť/vylúčiť viď sekcie \"CIPHER LIST FORMAT\" a \"CIPHER STRINGS\" na <a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">man-stránke pre šifry</a>.<br /><br /><b>Predvolená hodnota je:</b><pre>ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305</pre>',\n\t\t\t],\n\t\t\t'apache24_ocsp_cache_path' => [\n\t\t\t\t'title' => 'Apache 2.4: cesta k OCSP stapling cache',\n\t\t\t\t'description' => 'Konfiguruje medzipamäť použitú na ukladanie odpovedí OCSP, ktoré sú zahrnuté do TLS handshakes.',\n\t\t\t],\n\t\t\t'ssl_protocols' => [\n\t\t\t\t'title' => 'Konfigurácia verzie protokolu TLS',\n\t\t\t\t'description' => 'Toto je zoznam ssl protokolov, ktoré chcete (alebo nechcete) použiť pri použití SSL. <b>Upozornenie:</b> Niektoré staršie prehliadače nemusia podporovať najnovšie verzie protokolu.<br /><br /><b>Predvolená hodnota je:</b><pre>TLSv1.2</pre>',\n\t\t\t],\n\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t'title' => 'Nakonfigurujte explicitné TLSv1.3 šifry, ak sú použité',\n\t\t\t\t'description' => 'Toto je zoznam šifier, ktoré chcete (alebo nechcete) použiť pri komunikácii TLSv1.3. Zoznam šifier a ako ich zahrnúť/vylúčiť, viď <a href=\"https://wiki.openssl.org/index.php/TLS1.3\">dokumentácia pre TLSv1.</a>.<br /><br /><b>Predvolená hodnota je prázdna</b>',\n\t\t\t],\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => 'Predvolené nastavenia vHost-servera',\n\t\t\t'description' => 'Obsah tohto poľa bude priamo zahrnutý do tohto kontajnera s ip/portom. Môžete použiť nasledujúce premenné:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code> <code>{FPMSOCKET}</code> (ak existuje)<br/> Upozornenie: Kód nebude skontrolovaný na výskyt chýb. Ak obsahuje chyby, webový server nemusí znovu spustiť!',\n\t\t],\n\t\t'apache_globaldiropt' => [\n\t\t\t'title' => 'Možnosti adresára pre prefix zákazníka',\n\t\t\t'description' => 'Obsah tohto poľa bude zahrnutý do 05_froxlor_dirfix_nofcgid.conf apache config. Ak je prázdne, použije sa predvolená hodnota:<br><br>apache >=2.<br><code>Require all granted<br>AllowOverride All</code><br><br>apache <=2.<br><code>Order allow,deny<br>allow from all</code>',\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => 'Obsah tohto poľa bude priamo zahrnutý do vHost kontajnera. Môžete použiť nasledujúce premenné:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code> <code>{FPMSOCKET}</code> (ak existuje)<br/> Upozornenie: Kód nebude skontrolovaný na obsah chýb. Ak obsahuje chyby, webový server nemusí znovu spustiť!',\n\t\t],\n\t\t'decimal_places' => 'Počet desatinných miest vo výstupe prevádzky/webového priestoru',\n\t\t'selfdns' => [\n\t\t\t'title' => 'Nastavenia dns domény zákazníka',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => 'Umožniť zákazníkom upraviť nastavenia dns domény',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => 'Použiť používateľské mená kompatibilné s UNIX',\n\t\t\t'description' => 'Umožňuje používať <strong>-</strong> a <strong>_</strong> v používateľských menách, ak <strong>Nie</strong>',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => 'Povoliť obnovenie hesla zákazníkom',\n\t\t\t'description' => 'Zákazníci môžu obnoviť svoje heslo a aktivačný odkaz bude odoslaný na ich e-mailovú adresu',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => 'Povoliť obnovenie hesla administrátorom',\n\t\t\t'description' => 'Správcovia/predajcovia môžu obnoviť svoje heslo a na ich e-mailovú adresu bude zaslaný aktivačný odkaz',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => 'Kvóta pre poštovú schránku',\n\t\t\t'description' => 'Predvolená kvóta pre nové vytvorené poštové schránky (MegaByte).',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => 'Použiť kvóty pre poštovú schránku pre zákazníkov',\n\t\t\t'description' => 'Aktivujte pre použitie kvót na poštových schránkach. Predvolená hodnota je <b>Nie</b>, pretože to vyžaduje špeciálne nastavenie.',\n\t\t\t'removelink' => 'Kliknutím sem vymažete všetky kvóty pre e-mailové účty.',\n\t\t\t'enforcelink' => 'Kliknutím sem vynútite predvolenú kvótu na všetky zákaznícke e-mailové účty.',\n\t\t],\n\t\t'mail_enable_allow_sender' => [\n\t\t\t'title' => 'Povoliť zákazníkom používanie „povoleného odosielateľa\"',\n\t\t\t'description' => 'Ak je táto funkcia povolená, zákazníci môžu určiť „povoleného odosielateľa\" pre e-mailové účty, z ktorých budú odosielať správy. <br>Predvolené nastavenie: vypnuté',\n\t\t],\n\t\t'mail_allow_external_domains' => [\n\t\t\t'title' => 'Povoliť externé domény pre „povolených odosielateľov\"',\n\t\t\t'description' => 'Ak je táto možnosť povolená, zákazník môže zadať ako „povoleného odosielateľa\" pre e-mailové účty ľubovoľnú doménu (okrem domén, ktoré tento systém nevlastní).<br>Predvolené: vypnuté',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => 'Povoliť viacnásobné prihlásenie',\n\t\t\t'description' => 'Ak je aktivované, používateľ sa môže prihlásiť viackrát.',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => 'Povoliť presúvanie domén medzi správcami',\n\t\t\t'description' => 'Ak je aktivované, môžete zmeniť administrátora domény v nastaveniach domény.<br /><b>Upozornenie:</b> Ak zákazník nie je priradený rovnakému správcovi ako doména, administrátor môže vidieť všetky ostatné domény tohto zákazníka!',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => 'Povoliť presúvanie domén medzi zákazníkmi',\n\t\t\t'description' => 'Ak je aktivované, môžete zmeniť zákazníka domény v nastaveniach domény.<br /><b>Upozornenie:</b> froxlor zmení koreňový adresár dokumentu na predvolený domovský adresár nového zákazníka (+ doménový priečinok, ak je aktivovaný)',\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => 'Ak je zvolené áno, tieto vlastné nastavenia vHost sa pridajú ku všetkým subdoménam; ak nie, špeciálne nastavenia subdomény budú odstránené.',\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => 'Minimálna dĺžka hesla',\n\t\t\t'description' => 'Tu môžete nastaviť minimálnu dĺžku hesla. „0\" znamená, že nie je požadovaná žiadna minimálna dĺžka.',\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => 'Uložiť predvolený súbor indexu aj do nových podpriečinkov',\n\t\t\t'description' => 'Ak je povolené, predvolený indexový súbor sa ukladá do každej novo vytvorenej subdoménovej cesty (nie ak priečinok už existuje!)',\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => 'Adresa pre odpoveď',\n\t\t\t'description' => 'Zadajte e-mailovú adresu ako adresu pre odpoveď pre e-maily odoslané panelom.',\n\t\t],\n\t\t'adminmail_defname' => 'Meno odosielateľa e-mailu v paneli',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => 'Štandardná subdoména zákazníka',\n\t\t\t'description' => 'Aký názov hostiteľa sa má použiť na vytvorenie štandardných subdomén pre zákazníka. Ak je prázdne, použije sa systémový názov hostiteľa.',\n\t\t],\n\t\t'awstats_path' => 'Cesta k AWStats \\'awstats_buildstaticpages.pl\\'',\n\t\t'awstats_conf' => 'Cesta ku konfigurácii AWStats',\n\t\t'defaultttl' => 'TTL domény pre záväznosť v sekundách (predvolené \\'604800\\' = 1 týždeň)',\n\t\t'defaultwebsrverrhandler_enabled' => 'Povoliť predvolené chybové dokumenty pre všetkých zákazníkov',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => 'Súbor/URL pre chybu 401',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => 'Súbor/URL pre chybu 403',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => 'Súbor/URL pre chybu 404',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => 'Súbor/URL pre chybu 500',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => 'Ak je zvolený pureftpd, súbory .ftpquota pre zákaznícke kvóty sa vytvárajú a denne aktualizujú',\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => 'Povoliť presmerovania zákazníkov',\n\t\t\t'description' => 'Umožniť zákazníkom vybrať kód pre http-status presmerovaní, ktoré sa použijú',\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => 'Predvolené presmerovanie',\n\t\t\t'description' => 'Nastavte predvolený kód presmerovaní, ktorý sa má použiť, ak ho zákazník nenastaví sám',\n\t\t],\n\t\t'mail_also_with_mxservers' => 'Vytvoriť mail-, imap-, pop3- a smtp-„A record\" aj pri nastavení MX-serverov',\n\t\t'froxlordirectlyviahostname' => 'Prístup k froxloru priamo prostredníctvom názvu hostiteľa',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => 'Regulárny výraz pre heslá',\n\t\t\t'description' => 'Tu môžete nastaviť regulárny výraz pre zložitosť hesiel.<br />Prázdne = žiadne špecifické požiadavky',\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => 'Povoliť FCGID pre froxlor vHost',\n\t\t\t'description' => 'Ak je povolené, froxlor bude spustený aj pod lokálnym používateľom',\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => 'Povoliť SuExec workaround',\n\t\t\t\t'description' => 'Povoľte len v prípade, že zákaznícke docrooty nie sú v apache suexec ceste.<br />Ak je povolené, froxlor vygeneruje symbolický odkaz zo zákazníkových perl-enabled adresárov + /cgi-bin/ na danú cestu.<br />Upozornenie, že perl bude fungovať iba v podadresári priečinkov /cgi-bin/ a nie v samotnom priečinku (ako to robí bez tejto opravy!)',\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => 'Cesta pre symlinky adresárov zákazníka s povoleným perlom',\n\t\t\t\t'description' => 'Toto je potrebné nastaviť len ak je povolený SuExec-workaround.<br />POZOR: Uistite sa, že táto cesta je v rámci hlavnej cesty, inak je toto riešenie zbytočné',\n\t\t\t],\n\t\t],\n\t\t'awstats_awstatspath' => 'Cesta k AWStats \\'awstats.pl\\'',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'Cesta ku priečinku ikon AWstats',\n\t\t\t'description' => 'napr. /usr/share/awstats/htdocs/icon/',\n\t\t],\n\t\t'login_domain_login' => 'Povoliť prihlásenie pomocou domén',\n\t\t'perl_server' => [\n\t\t\t'title' => 'Umiestnenie soketu perl servera',\n\t\t\t'description' => 'Jednoduchý návod nájdete na: <a target=\"blank\" href=\"https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/\">nginx.com</a>',\n\t\t],\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP backend',\n\t\t\t'description' => 'tu proces PHP počúva požiadavky z nginxu, môže to byť unixový soket s kombináciou ip:port<br />*Nepoužíva sa s php-fpm',\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'Príkaz na opätovné načítanie PHP',\n\t\t\t'description' => 'toto sa používa na obnovenie PHP backendu, ak je použitý<br />Predvolené: prázdne<br />*NEPOUŽÍVA sa s php-fpm',\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => 'Povoliť php-fpm',\n\t\t\t'description' => '<b>To vyžaduje špeciálnu konfiguráciu webservera, pozri <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">PHP-FPM príručka</a></b>',\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'Adresár konfigurácie php-fpm',\n\t\t\t'aliasconfigdir' => 'Konfigurácia adresára aliasu php-fpm',\n\t\t\t'reload' => 'príkaz na reštart php-fpm',\n\t\t\t'pm' => 'Riadenie správcu procesu (pm)',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => 'Počet podradených procesov',\n\t\t\t\t'description' => 'Počet podradených procesov, ktoré sa majú vytvoriť, keď je hodnota pm nastavená na \\'static\\' a maximálny počet podradených procesov, ktoré sa majú vytvoriť, keď je hodnota pm nastavená na \\'dynamic/ondemand\\'<br />ekvivalent hodnoty PHP_FCGI_CHILDREN',\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => 'Počet podradených procesov vytvorených pri spustení',\n\t\t\t\t'description' => 'Poznámka: Používa sa len v prípade, že pm je nastavené na \\'dynamic\\'',\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => 'Požadovaný minimálny počet nečinných procesov servera',\n\t\t\t\t'description' => 'Poznámka: Používa sa len pri nastavení pm na \\'dynamic\\'<br />Poznámka: Povinné pri nastavení pm na \\'dynamic\\'',\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => 'Požadovaný maximálny počet nečinných procesov servera',\n\t\t\t\t'description' => 'Poznámka: Používa sa len v prípade, že pm je nastavené na \\'dynamic\\'<br />Poznámka: Povinné, keď je pm nastavené na \\'dynamic\\'',\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => 'Požiadavky na proces pred opätovným vytvorením',\n\t\t\t\t'description' => 'Pre nekonečné spracovanie požiadaviek zadajte \\'0\\'. Ekvivalentné s PHP_FCGI_MAX_REQUESTS.',\n\t\t\t],\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => 'Časový limit nečinnosti',\n\t\t\t\t'description' => 'Nastavenie časového limitu pre PHP FastCGI.',\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'FastCGI adresár IPC',\n\t\t\t\t'description' => 'Adresár, kam bude webový server ukladať sokety php-fpm.<br />Tento adresár musí byť pre webový server čitateľný',\n\t\t\t],\n\t\t\t'limit_extensions' => [\n\t\t\t\t'title' => 'Povolené prípony',\n\t\t\t\t'description' => 'Obmedzuje prípony hlavného skriptu, ktoré FPM dovolí analyzovať. To môže zabrániť chybám v konfigurácii na strane webového servera. Mali by ste obmedziť FPM len na prípony .php, aby ste zabránili škodlivým používateľom používať iné prípony na spustenie kódu php. Predvolená hodnota: .php',\n\t\t\t],\n\t\t\t'envpath' => 'Cesty, ktoré sa pridajú do prostredia PATH. Nechajte prázdne, ak premenná prostredia PATH neexistuje',\n\t\t\t'override_fpmconfig' => 'Prepísať nastavenia FPM-daemon (pm, max_children atď.)',\n\t\t\t'override_fpmconfig_addinfo' => '<br /><span class=\"text-danger\">Používa sa len v prípade, že „Prepísať nastavenia FPM-daemon\" je nastavené na „Áno\"</span>',\n\t\t\t'restart_note' => 'Upozornenie: Konfigurácia nebude skontrolovaná na výskyt chýb. Ak obsahuje chyby, PHP-FPM nemusí znovu spustiť!',\n\t\t\t'custom_config' => [\n\t\t\t\t'title' => 'Vlastná konfigurácia',\n\t\t\t\t'description' => 'Pridajte vlastnú konfiguráciu pre každú inštanciu verzie PHP-FPM, napríklad <i>pm.status_path = /status</i> pre monitorovanie. Nižšie uvedené premenné možno použiť tu.  <strong>Upozornenie: Konfigurácia nebude skontrolovaná na žiadne chyby. Ak obsahuje chyby, PHP-FPM nemusí znovu spustiť!</strong>',\n\t\t\t],\n\t\t\t'allow_all_customers' => [\n\t\t\t\t'title' => 'Priradiť túto konfiguráciu všetkým aktuálne existujúcim zákazníkom',\n\t\t\t\t'description' => 'Nastavte túto hodnotu na „true\", ak chcete túto konfiguráciu priradiť všetkým aktuálne existujúcim zákazníkom, aby ju mohli používať. Toto nastavenie nie je trvalé, ale možno ho spustiť viackrát.',\n\t\t\t],\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => 'Povoliť odosielanie hlásení o využití webu a prevádzky',\n\t\t\t'webmax' => [\n\t\t\t\t'title' => 'Úroveň varovania v percentách pre webový priestor',\n\t\t\t\t'description' => 'Platné hodnoty sú 0 až 150. Nastavením tejto hodnoty na 0 zakážete toto hlásenie.',\n\t\t\t],\n\t\t\t'trafficmax' => [\n\t\t\t\t'title' => 'Úroveň varovania v percentách pre prevádzku',\n\t\t\t\t'description' => 'Platné hodnoty sú 0 až 150. Nastavením tejto hodnoty na 0 zakážete toto hlásenie.',\n\t\t\t],\n\t\t\t'report_web_bccadmin' => [\n\t\t\t\t'title' => 'BCC e-mail pre oznámenia o využití webu správcovi',\n\t\t\t\t'description' => 'Ak je táto funkcia povolená, varovanie o využití miesta na disku zasielané zákazníkovi sa zasiela aj príslušnému správcovi/predajcovi (BCC).'\n\t\t\t],\n\t\t],\n\t\t'dropdown' => 'Rozbaľovacia ponuka',\n\t\t'manual' => 'Manuálne',\n\t\t'default_theme' => 'Predvolená šablóna',\n\t\t'validate_domain' => 'Overovať názvy domén',\n\t\t'diskquota_enabled' => 'Kvóta aktivovaná?',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'Cesta k repquota',\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'Cesta k quotatool',\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => 'Oddiel, na ktorom sú uložené zákaznícke súbory',\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'Názov maildir',\n\t\t\t'description' => 'Maildir adresár do používateľského účtu. Zvyčajne \\'Maildir\\', v niektorých implementáciách \\'.maildir\\' a priamo do adresára používateľa, ak zostane prázdne.',\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => 'Použiť Catchall',\n\t\t\t'description' => 'Chcete svojim zákazníkom poskytnúť funkciu catchall?',\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => 'Použiť úpravy pre Apache 2.4',\n\t\t\t'description' => '<strong class=\"text-danger\">POZOR:</strong> použite, len ak máte nainštalovanú apache verziu 2.4 alebo vyššiu<br />inak váš webserver nebude môcť spustiť',\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'Cesta k súboru fastcgi_params',\n\t\t\t'description' => 'Zadajte cestu k súboru fastcgi_params nginx vrátane názvu súboru',\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => 'Použiť názov domény ako predvolenú hodnotu pre cestu koreňového adresára dokumentu',\n\t\t\t'description' => 'Ak je povolené a cesta DocumentRoot je prázdna, predvolenou hodnotou bude názov (sub)domény.<br /><br />Príklady: <br />/var/customers/webs/customer_name/example.com/<br />/var/customers/webs/customer_name/subdomain.example.com/',\n\t\t],\n\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t'title' => 'Skryť subdomény v prehľade PHP-konfigurácie',\n\t\t\t'description' => 'Ak je aktivované, subdomény zákazníkov nebudú zobrazené v prehľade php-konfigurácií, zobrazí sa len počet subdomén.<br /><br />Poznámka: Toto je viditeľné len v prípade, že ste povolili FCGID alebo PHP-FPM',\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => 'Skryť štandardné subdomény v prehľade konfigurácie PHP',\n\t\t\t'description' => 'Ak je aktivované, štandardné subdomény pre zákazníkov sa nebudú zobrazovať v prehľade php-konfigurácií<br /><br />Poznámka: Toto je viditeľné len v prípade, že ste povolili FCGID alebo PHP-FPM',\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => 'Vyberte, ktorá metóda šifrovania hesla sa má použiť',\n\t\t\t'description' => 'Vyberte, ktorá metóda šifrovania hesiel sa má použiť. Ak toto nastavenie zmeníte, budú šifrované len nové heslá novou metódou. Existujúce heslá sa nezmenia.',\n\t\t],\n\t\t'systemdefault' => 'Predvolené systémové nastavenie',\n\t\t'panel_allow_theme_change_admin' => 'Povoliť administrátorom zmeniť motív',\n\t\t'panel_allow_theme_change_customer' => 'Umožniť zákazníkom zmenu motívu',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'Servery AXFR',\n\t\t\t'description' => 'Čiarkami oddelený zoznam IP adries, ktoré môžu prenášať (AXFR) zóny.',\n\t\t],\n\t\t'powerdns_mode' => [\n\t\t\t'title' => 'Prevádzkový režim PowerDNS',\n\t\t\t'description' => 'Vyberte režim PowerDNS: Natívny pre žiadnu replikáciu (predvolené) / Master, ak je potrebná DNS replikácia.',\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Adresár zákazníckych certifikátov webových serverov',\n\t\t\t'description' => 'Kde majú byť vytvorené ssl-certifikáty zadané zákazníkom?<br /><br /><div class=\"text-danger\">POZNÁMKA: Obsah tohto priečinka je pravidelne mazaný, aby sa zabránilo ručnému ukladaniu dát do tohto priečinka.</div>',\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => 'Povoliť správcom/predajcom nahlásenie chýb databázy froxlor',\n\t\t\t'description' => 'Upozornenie: Nikdy nám neposielajte žiadne osobné (zákaznícke) údaje!',\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => 'Umožniť zákazníkom nahlásiť chyby databázy froxlor',\n\t\t\t'description' => 'Upozornenie: Nikdy nám neposielajte žiadne osobné (zákaznícke) údaje!',\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => 'Analyzovať prenos pošty',\n\t\t\t'description' => 'Povoliť analýzu protokolov mailservera na výpočet prevádzky',\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'MDA typ',\n\t\t\t'description' => 'Typ poštového servera',\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'Protokol MDA',\n\t\t\t'description' => 'Súbor protokolu servera na doručovanie pošty',\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'MTA typ',\n\t\t\t'description' => 'Typ sprostredkovateľa na prenos pošty',\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'Protokol MTA',\n\t\t\t'description' => 'Súbor protokolu agenta na prenos pošty',\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'Konfiguračný súbor cronu',\n\t\t\t'description' => 'Cesta ku konfiguračnému súboru cron-service. Tento súbor bude pravidelne a automaticky aktualizovaný froxlorom.<br />Poznámka: <b>Uistite sa</b>, že používate rovnaký názov súboru ako pre hlavný froxlor cronjob (predvolené: /etc/cron.d/froxlor)!<br><br>Ak používate <b>FreeBSD</b>, zadajte tu <i>/etc/crontab</i>!',\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Príkaz na opätovné načítanie Cron-daemona',\n\t\t\t'description' => 'Určite príkaz, ktorý sa má vykonať na opätovné načítanie systémov cron-daemon',\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Príkaz na vykonanie cronu (php-binary)',\n\t\t\t'description' => 'Príkaz na vykonanie našich cronjobov. Meňte to len ak viete, čo robíte (predvolené: \"/usr/bin/nice -n 5 /usr/bin/php -q\")!',\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => 'Povoliť automatické aktualizácie databázy',\n\t\t\t'description' => '<div class=\"text-danger\"><b>POZOR:</b></div> Toto nastavenie umožňuje cronjob obísť kontrolu verzií froxlor súborov a databázy a spustiť aktualizácie databázy v prípade, že dôjde k nezhodám verzií.<br><br><div class=\"text-danger\">Automatická aktualizácia vždy nastaví predvolené hodnoty pre nové nastavenia alebo zmeny. Toto nemusí vždy vyhovovať vášmu systému. Pred aktiváciou tejto možnosti</div> sa prosím dvakrát zamyslite',\n\t\t],\n\t\t'dns_createhostnameentry' => 'Vytvoriť bind-zone/config pre názov hostiteľa systému',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => 'Malé písmeno',\n\t\t\t'description' => 'Heslo musí obsahovať aspoň jedno malé písmeno (a-z).',\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => 'Veľké písmeno',\n\t\t\t'description' => 'Heslo musí obsahovať aspoň jedno veľké písmeno (A-Z).',\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => 'Čísla',\n\t\t\t'description' => 'Heslo musí obsahovať aspoň jedno číslo (0-9).',\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => 'Špeciálny znak',\n\t\t\t'description' => 'Heslo musí obsahovať aspoň jeden z nižšie uvedených znakov.',\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => 'Zoznam špeciálnych znakov',\n\t\t\t'description' => 'Jeden z týchto znakov je vyžadovaný, ak je nastavená vyššie uvedená možnosť.',\n\t\t],\n\t\t'apache_itksupport' => [\n\t\t\t'title' => 'Použiť úpravy ITK-MPM Apache',\n\t\t\t'description' => '<strong class=\"text-danger\">POZOR:</strong> použite, len ak máte skutočne povolené apache itk-mpm<br />inak váš webserver nebude môcť spustiť',\n\t\t],\n\t\t'letsencryptca' => [\n\t\t\t'title' => 'ACME prostredie',\n\t\t\t'description' => 'Prostredie, ktoré sa má použiť pre certifikáty Let\\'s Encrypt / ZeroSSL.',\n\t\t],\n\t\t'letsencryptchallengepath' => [\n\t\t\t'title' => 'Cesta k výzvam Let\\'s Encrypt',\n\t\t\t'description' => 'Adresár, z ktorého by mali byť ponúkané výzvy Let\\'s Encrypt prostredníctvom globálneho aliasu.',\n\t\t],\n\t\t'letsencryptkeysize' => [\n\t\t\t'title' => 'Veľkosť kľúča pre nové Let\\'s Encrypt certifikáty',\n\t\t\t'description' => 'Veľkosť kľúča v bitoch pre nové Let\\'s Encrypt certifikáty.',\n\t\t],\n\t\t'letsencryptreuseold' => [\n\t\t\t'title' => 'Znovu použiť Let\\'s Encrypt kľúč',\n\t\t\t'description' => 'Ak je aktivované, pri každom obnovení sa použije rovnaký kľúč, inak sa zakaždým vygeneruje nový kľúč.',\n\t\t],\n\t\t'leenabled' => [\n\t\t\t'title' => 'Použiť Let\\'s Encrypt',\n\t\t\t'description' => 'Ak je aktivované, zákazníci môžu nechať froxlor automaticky generovať a obnovovať šifrovacie certifikáty domén s ssl IP/portom.<br /><br />Nezabudnite, že musíte prejsť konfiguráciou webservera, ak je povolené, pretože táto funkcia vyžaduje špeciálnu konfiguráciu.',\n\t\t],\n\t\t'caa_entry' => [\n\t\t\t'title' => 'Generovať CAA DNS záznamy',\n\t\t\t'description' => 'Automaticky generuje CAA záznamy pre domény s podporou SSL, ktoré používajú Let\\'s Encrypt',\n\t\t],\n\t\t'caa_entry_custom' => [\n\t\t\t'title' => 'Ďalšie záznamy CAA DNS',\n\t\t\t'description' => 'DNS Certification Authority autorizácia (CAA) je mechanizmus politiky bezpečnosti internetu, ktorý držiteľom doménových mien umožňuje certifikovať autorite<br>, či sú oprávnení vydávať digitálne certifikáty pre určitý názov domény. Robí to pomocou nového zdrojového záznamu doménového systému „CAA\" (DNS).<br><br>Obsah tohto poľa bude zahrnutý do DNS zóny priamo (každý riadok vyústí v CAA záznam).<br>Ak je pre túto doménu povolené šifrovanie, táto položka bude vždy pridaná automaticky a nemusí byť pridaná ručne:<br><code>0 issue \"letsencrypt.org\"</code> (Ak je doména doménou so zástupnými znakmi, použije sa namiesto toho issuewild).<br>Ak chcete povoliť hlásenie incidentov, môžete pridať záznam <code>iodef</code>. Príklad na odoslanie správy na <code>me@example.com</code> bude:<br><code>0 iodef \"mailto:me@example.com\"</code><br><br><strong>Upozornenie:</strong> Kód nebude skontrolovaný na výskyt chýb. Ak obsahuje chyby, vaše CAA záznamy nemusia fungovať!',\n\t\t],\n\t\t'exportenabled' => [\n\t\t\t'title' => 'Povoliť export dát pre zákazníkov',\n\t\t\t'description' => 'Ak je aktivované, zákazník bude môcť naplánovať úlohy na export dát (cron-export), ktoré generujú archív vo svojom docroote (podadresár vybraný zákazníkom)',\n\t\t],\n\t\t'dnseditorenable' => [\n\t\t\t'title' => 'Povoliť DNS editor',\n\t\t\t'description' => 'Umožňuje administrátorom a zákazníkovi spravovať dns záznamy domén',\n\t\t],\n\t\t'dns_server' => [\n\t\t\t'title' => 'Daemon DNS servera',\n\t\t\t'description' => 'Nezabudnite, že „daemony\" je potrebné konfigurovať pomocou konfiguračných šablón froxloru',\n\t\t],\n\t\t'panel_customer_hide_options' => [\n\t\t\t'title' => 'Skrytie položiek ponúk a grafov návštevnosti v zákazníckom paneli',\n\t\t\t'description' => 'Vyberte položky, ktoré chcete skryť v paneli zákazníka. Ak chcete vybrať viac možností, podržte CTRL pri výbere.',\n\t\t],\n\t\t'allow_allow_customer_shell' => [\n\t\t\t'title' => 'Umožniť zákazníkom povoliť prístup cez shell pre používateľov ftp',\n\t\t\t'description' => '<strong class=\"text-danger\">Upozornenie: Prístup k Shell umožňuje používateľovi spustiť rôzne binárne súbory vo vašom systéme. Používajte s mimoriadnou opatrnosťou. Aktivujte prosím len ak viete, čo robíte!!!</strong>',\n\t\t],\n\t\t'available_shells' => [\n\t\t\t'title' => 'Zoznam dostupných príkazov',\n\t\t\t'description' => 'Čiarkami oddelený zoznam shellov, z ktorých si zákazník môže vybrať pre svojich ftp používateľov.<br><br>Všimnite si, že predvolený shell <strong>/bin/false</strong> bude vždy na výber (ak je povolený), aj keď je toto nastavenie prázdne. V každom prípade je to predvolená hodnota proftp používateľa',\n\t\t],\n\t\t'le_froxlor_enabled' => [\n\t\t\t'title' => 'Povoliť Let\\'s Encrypt pre hostiteľa froxlor',\n\t\t\t'description' => 'Ak je aktivované, vhost froxloru bude automaticky zabezpečený pomocou Let\\'s Encrypt certifikátu.',\n\t\t],\n\t\t'le_froxlor_redirect' => [\n\t\t\t'title' => 'Povoliť presmerovanie SSL pre vhost froxloru',\n\t\t\t'description' => 'Ak je aktivované, všetky http požiadavky na váš froxlor budú presmerované na príslušný server SSL.',\n\t\t],\n\t\t'option_unavailable_websrv' => '<br><em class=\"text-danger\">K dispozícii iba pre: %s</em>',\n\t\t'option_unavailable' => '<br><em class=\"text-danger\">Možnosť nie je dostupná kvôli iným nastaveniam.</em>',\n\t\t'letsencryptacmeconf' => [\n\t\t\t'title' => 'Cesta k snippetu acme.conf',\n\t\t\t'description' => 'Názov súboru v konfiguračnom textovom bloku, ktorý umožňuje webovému serveru obsluhovať výzvu acme.',\n\t\t],\n\t\t'mail_use_smtp' => 'Nastaviť e-mail na použitie SMTP',\n\t\t'mail_smtp_host' => 'Zadajte SMTP server',\n\t\t'mail_smtp_usetls' => 'Povoliť šifrovanie TLS',\n\t\t'mail_smtp_auth' => 'Povoliť overovanie SMTP',\n\t\t'mail_smtp_port' => 'TCP port pre pripojenie k',\n\t\t'mail_smtp_user' => 'SMTP používateľské meno',\n\t\t'mail_smtp_passwd' => 'SMTP heslo',\n\t\t'http2_support' => [\n\t\t\t'title' => 'Podpora HTTP2',\n\t\t\t'description' => 'povoliť podporu HTTP2 pre ssl.<br><em class=\"text-danger\">POVOĽTE IBA AK VÁŠ SERVER TÚTO FUNKCIU PODPORUJE (nginx verzia 1.9.5+, apache2 verzia 2.4.17+)</em>',\n\t\t],\n\t\t'http3_support' => [\n\t\t\t'title' => 'Podpora HTTP3',\n\t\t\t'description' => 'povoliť podporu HTTP3 pre ssl.<br><em class=\"text-danger\">POVOĽTE IBA V PRÍPADE, ŽE VÁŠ WEBOVÝ SERVER TÚTO FUNKCIU PODPORUJE (nginx verzia 1.25.0+)</em>',\n\t\t],\n\t\t'nssextrausers' => [\n\t\t\t'title' => 'Použiť libnss-extrauser namiesto libnss-mysql',\n\t\t\t'description' => 'Nečítať používateľov z databázy, ale zo súborov. Aktivujte, prosím, len ak ste už prešli požadovanými krokmi konfigurácie (system -> libnss-extrausers).<br><strong class=\"text-danger\">Iba pre Debian/Ubuntu (alebo ak ste sami skompilovali libnss-extrauser!)</strong>',\n\t\t],\n\t\t'le_domain_dnscheck' => [\n\t\t\t'title' => 'Overiť DNS domén pri použití Let\\'s Encrypt',\n\t\t\t'description' => 'Ak je aktivované, froxlor overí, či sa doména, ktorá žiada o certifikát, smeruje aspoň na jednu z IP adries systému.',\n\t\t],\n\t\t'le_domain_dnscheck_resolver' => [\n\t\t\t'title' => 'Použiť externý nameserver na overenie DNS',\n\t\t\t'description' => 'Ak je nastavené, froxlor použije tento DNS na overenie DNS domén pri použití Let\\'s Encrypt. Ak je prázdne, použije sa predvolený prekladač DNS systému.',\n\t\t],\n\t\t'phpsettingsforsubdomains' => [\n\t\t\t'description' => 'Ak áno, zvolený php-config bude aktualizovaný na všetky subdomény',\n\t\t],\n\t\t'leapiversion' => [\n\t\t\t'title' => 'Zvoľte si implementáciu ACME pre Let\\'s Encrypt',\n\t\t\t'description' => 'V súčasnosti je podporovaná iba implementácia ACME v2 pre Let\\'s Encrypt.',\n\t\t],\n\t\t'enable_api' => [\n\t\t\t'title' => 'Povoliť externé použitie API',\n\t\t\t'description' => 'Ak chcete používať froxlor API, musíte aktivovať túto možnosť. Podrobnejšie informácie nájdete na <a href=\"https://docs.froxlor.org/latest/api-guide/\" target=\"_new\">https://docs.froxlor.org/</a>',\n\t\t],\n\t\t'api_customer_default' => '„Povoliť prístup k API\" pre nových zákazníkov',\n\t\t'dhparams_file' => [\n\t\t\t'title' => 'Súbor DHParams (výmena kľúčov Diffie-Hellman)',\n\t\t\t'description' => 'Ak je tu zadaný súbor dhparams.pem, bude zahrnutý do konfigurácie webservera. Nechajte prázdne na vypnutie.<br>Príklad: /etc/ssl/webserver/dhparams.pem<br><br>Ak súbor neexistuje, bude vytvorený automaticky nasledujúcim príkazom: <code>openssl dhparam -out /etc/ssl/webserver/dhparams.pem 4096</code>. Pred zadaním súboru sa odporúča súbor vytvoriť, pretože vytváranie trvá dosť dlho a blokuje cronjob.',\n\t\t],\n\t\t'errorlog_level' => [\n\t\t\t'title' => 'Úroveň protokolov chýb',\n\t\t\t'description' => 'Zadajte úroveň protokolu chýb. Predvolená hodnota je „warn\" pre apache-používateľov a „error\" pre nginx-používateľov.',\n\t\t],\n\t\t'letsencryptecc' => [\n\t\t\t'title' => 'Vystaviť certifikát ECC / certifikát ECDSA',\n\t\t\t'description' => 'Ak je nastavené na platnú veľkosť kľúča, vystavený certifikát bude používať ECC / ECDSA',\n\t\t],\n\t\t'froxloraliases' => [\n\t\t\t'title' => 'Aliasy domén pre froxlor vhost',\n\t\t\t'description' => 'Čiarkami oddelený zoznam domén, ktoré majú byť pridané ako serverový alias do froxlor vhost',\n\t\t],\n\t\t'default_sslvhostconf' => [\n\t\t\t'title' => 'Predvolené nastavenia SSL vHost-servera',\n\t\t\t'description' => 'Obsah tohto poľa bude priamo zahrnutý do tohto kontajnera s ip/portom. Môžete použiť nasledujúce premenné:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code> <code>{FPMSOCKET}</code> (ak existuje)<br/> Upozornenie: Kód nebude skontrolovaný na výskyt chýb. Ak obsahuje chyby, webový server nemusí znovu spustiť!',\n\t\t],\n\t\t'includedefault_sslvhostconf' => 'Zahrnúť non-SSL vHost-nastavenia v SSL-vHost',\n\t\t'apply_specialsettings_default' => 'Predvolená hodnota pre „Použiť špeciálne nastavenia pre všetky subdomény (*.example.com)\" pri úprave domény',\n\t\t'apply_phpconfigs_default' => 'Predvolená hodnota pre nastavenie „Použiť php konfiguráciu na všetky subdomény\" pri úprave domény',\n\t\t'awstats' => [\n\t\t\t'logformat' => [\n\t\t\t\t'title' => 'Nastavenie LogFormat',\n\t\t\t\t'description' => 'Ak používate vlastný logformat pre svoj webový server, musíte zmeniť aj awstats LogFormat.<br/>Predvolené je 1. Pre viac informácií skontrolujte dokumentáciu <a target=\"_blank\" href=\"https://awstats.sourceforge.io/docs/awstats_config.html#LogFormat\">tu</a>.',\n\t\t\t],\n\t\t],\n\t\t'hide_incompatible_settings' => 'Skryť nekompatibilné nastavenia',\n\t\t'soaemail' => 'E-mailová adresa na použitie v SOA záznamoch (predvolená adresa odosielateľa z nastavení panela, ak je prázdna)',\n\t\t'imprint_url' => [\n\t\t\t'title' => 'URL adresa k právnym poznámkam / odtlačku',\n\t\t\t'description' => 'Zadajte URL adresu svojich právnych poznámok / stránky s odtlačkami. Odkaz bude viditeľný na prihlasovacej obrazovke a v päte po prihlásení.',\n\t\t],\n\t\t'terms_url' => [\n\t\t\t'title' => 'URL k podmienkam použitia',\n\t\t\t'description' => 'Zadajte URL adresu k podmienkam používania. Odkaz bude viditeľný na prihlasovacej obrazovke a v päte pri prihlásení.',\n\t\t],\n\t\t'privacy_url' => [\n\t\t\t'title' => 'URL k zásadám ochrany osobných údajov',\n\t\t\t'description' => 'Zadajte URL adresu svojej stránky so zásadami ochrany osobných údajov / stránky s odtlačkami. Odkaz bude viditeľný na prihlasovacej obrazovke a v päte po prihlásení.',\n\t\t],\n\t\t'logo_image_header' => [\n\t\t\t'title' => 'Obrázok loga (záhlavie)',\n\t\t\t'description' => 'Nahrajte vlastný obrázok loga, ktorý sa zobrazí v záhlaví po prihlásení (odporúčaná výška 30px)',\n\t\t],\n\t\t'logo_image_login' => [\n\t\t\t'title' => 'Obrázok loga (Prihlasovacia obrazovka)',\n\t\t\t'description' => 'Nahrajte vlastný obrázok loga, ktorý sa zobrazí počas prihlásenia',\n\t\t],\n\t\t'logo_overridetheme' => [\n\t\t\t'title' => 'Prepísať logo definované v motíve pomocou „Logo Image\" (hlavička a prihlasovacia stránka, pozri nižšie)',\n\t\t\t'description' => 'Toto musí byť nastavené na „true\", ak máte v úmysle použiť nahrané logo_custom. Alternatívne môžete stále používať „logo_custom.png\" a „logo_custom_login.png\".',\n\t\t],\n\t\t'logo_overridecustom' => [\n\t\t\t'title' => 'Prepísať vlastné logo (logo_custom.png a logo_custom_login.png) definované v šablóne „Obrázok loga\" (hlavička a prihlásenie, pozri nižšie)',\n\t\t\t'description' => 'Nastavte na „true\" ak chcete ignorovať vlastné logá pre záhlavie a prihlásenie a použiť „Logo Image\"',\n\t\t],\n\t\t'createstdsubdom_default' => [\n\t\t\t'title' => 'Predvolená hodnota pre „Vytvoriť štandardnú subdoménu\" pri vytváraní zákazníka',\n\t\t\t'description' => '',\n\t\t],\n\t\t'froxlorusergroup' => [\n\t\t\t'title' => 'Vlastná systémová skupina pre všetkých zákazníkov',\n\t\t\t'description' => 'Vyžaduje sa použitie libnss-extrauser (systémového nastavenia). Vytvorenie prázdnych hodnôt preskočí alebo odstráni existujúcu skupinu.',\n\t\t],\n\t\t'acmeshpath' => [\n\t\t\t'title' => 'Cesta k acme.sh',\n\t\t\t'description' => 'Nastavte na miesto, kde je acme.sh nainštalovaný, vrátane skriptu acme.sh<br>Predvolené je <b>/root/.acme.sh/acme.sh</b>',\n\t\t],\n\t\t'update_channel' => [\n\t\t\t'title' => 'froxlor aktualizačný-kanál',\n\t\t\t'description' => 'Vyberte aktualizačný kanál froxlor. Predvolená hodnota je „stabilný\"',\n\t\t],\n\t\t'uc_stable' => 'stabilný',\n\t\t'uc_testing' => 'testovací',\n\t\t'uc_nightly' => 'nightly',\n\t\t'traffictool' => [\n\t\t\t'toolselect' => 'Analyzátor prevádzky',\n\t\t\t'webalizer' => 'Webalizer',\n\t\t\t'awstats' => 'AWStats',\n\t\t\t'goaccess' => 'goaccess'\n\t\t],\n\t\t'requires_reconfiguration' => 'Zmena tohto nastavenia môže vyžadovať zmenu konfigurácie nasledujúcich služieb:<br><strong>%s</strong>',\n\t\t'req_limit_per_interval' => [\n\t\t\t'title' => 'Počet HTTP požiadaviek na interval',\n\t\t\t'description' => 'Obmedziť počet HTTP požiadaviek na interval (pozri nižšie) pre froxlor, predvolená hodnota je „60\"',\n\t\t],\n\t\t'req_limit_interval' => [\n\t\t\t'title' => 'Interval obmedzenia rýchlosti',\n\t\t\t'description' => 'Zadajte čas v sekundách pre počet HTTP požiadaviek. Predvolená hodnota je „60\"',\n\t\t],\n\t\t'option_requires_otp' => 'Toto nastavenie vyžaduje overenie cez OTP',\n\t\t'panel_menu_collapsed' => [\n\t\t\t'title' => 'Zbaliť sekcie menu',\n\t\t\t'description' => 'Ak dôjde k deaktivácii, ľavé časti menu budú vždy rozšírené.',\n\t\t],\n\t\t'le_renew_services' => [\n\t\t\t'title' => 'Pre tieto služby použite certifikát froxloru Let\\'s Encrypt',\n\t\t\t'description' => 'Ak je nastavená hodnota none (alebo je nižšie uvedený príkaz renew-hook prázdny), pri vybraných službách nebudú vykonané žiadne úpravy konfigurácie týkajúce sa ssl.<br><br>Príkaz reload-command pre vybrané služby by mal byť pridaný do príkazu renew-hook, inak sa zmeny konfigurácie alebo obnovené certifikáty nemusia správne použiť.',\n\t\t],\n\t\t'le_renew_hook' => [\n\t\t\t'title' => 'Príkaz renew-hook programu Let\\'s Encrypt',\n\t\t\t'description' => 'Nastavte príkaz, ktorý reštartuje vyššie vybrané služby, aby služba mohla obnovené certifikáty správne používať.',\n\t\t],\n\t],\n\t'spf' => [\n\t\t'use_spf' => [\n\t\t\t'title' => 'Aktivovať SPF pre domény?',\n\t\t\t'description' => 'Vyžaduje pre doménu určitú položku dns. Ak nepoužívate funkciu nameserver, budete musieť tieto položky ručne spravovať.',\n\t\t],\n\t\t'spf_entry' => 'SPF položka pre všetky domény',\n\t],\n\t'dmarc' => [\n\t\t'use_dmarc' => [\n\t\t\t'title' => 'Aktivovať DMARC pre domény?',\n\t\t\t'description' => 'Vyžaduje pre doménu určitú položku dns. Ak nepoužívate funkciu nameserver, budete musieť tieto položky ručne spravovať.',\n\t\t],\n\t\t'dmarc_entry' => 'DMARC záznam pre všetky domény',\n\t],\n\t'ssl_certificates' => [\n\t\t'certificate_for' => 'Certifikát pre',\n\t\t'valid_from' => 'Platné od',\n\t\t'valid_until' => 'Platné do',\n\t\t'issuer' => 'Vydavateľ',\n\t],\n\t'success' => [\n\t\t'success' => 'Informácie',\n\t\t'clickheretocontinue' => 'Kliknite tu pre pokračovanie',\n\t\t'settingssaved' => 'Nastavenie bolo úspešne uložené.',\n\t\t'rebuildingconfigs' => 'Úspešne vložené úlohy na obnovenie konfiguračných súborov',\n\t\t'domain_import_successfully' => 'Úspešne importovaných %s domén.',\n\t\t'exportscheduled' => 'Vaša exportná úloha bola naplánovaná. Počkajte prosím na jej spracovanie',\n\t\t'exportaborted' => 'Váš plánovaný export bol zrušený',\n\t\t'dns_record_added' => 'Záznam bol úspešne pridaný',\n\t\t'dns_record_deleted' => 'Záznam bol úspešne odstránený',\n\t\t'testmailsent' => 'Testovací e-mail bol úspešne odoslaný',\n\t\t'settingsimported' => 'Nastavenie bolo úspešne importované',\n\t\t'sent_error_report' => 'Hlásenie o chybách bolo úspešne odoslané. Ďakujeme za váš príspevok.',\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => 'Nevybavené úlohy',\n\t\t'REBUILD_VHOST' => 'Obnovenie konfigurácie webservera',\n\t\t'CREATE_HOME' => 'Pridávanie nového zákazníka %s',\n\t\t'REBUILD_DNS' => 'Obnovenie bind konfigurácie',\n\t\t'CREATE_FTP' => 'Vytváranie adresára pre nového ftp-používateľa',\n\t\t'DELETE_CUSTOMER_FILES' => 'Mazanie zákazníckych súborov %s',\n\t\t'noneoutstanding' => 'V súčasnosti nie sú žiadne nevybavené úlohy pre froxlor',\n\t\t'DELETE_EMAIL_DATA' => 'Odstrániť e-mailové dáta zákazníka.',\n\t\t'DELETE_FTP_DATA' => 'Odstrániť dáta ftp účtu.',\n\t\t'REBUILD_RSPAMD' => 'Obnovenie konfigurácie antispamu.',\n\t\t'CREATE_QUOTA' => 'Nastaviť kvótu na súborovom systéme',\n\t\t'REBUILD_CRON' => 'Obnovenie cron.d-súboru',\n\t\t'CREATE_CUSTOMER_DATADUMP' => 'Úloha na export dát pre zákazníka %s',\n\t\t'DELETE_DOMAIN_PDNS' => 'Odstrániť doménu %s z databázy PowerDNS',\n\t\t'DELETE_DOMAIN_SSL' => 'Odstrániť ssl súbory domény %s',\n\t\t'UPDATE_LE_SERVICES' => 'Aktualizácia systémových služieb pre Let\\'s Encrypt',\n\t],\n\t'terms' => 'Podmienky použitia',\n\t'traffic' => [\n\t\t'month' => 'Mesiac',\n\t\t'day' => 'Deň',\n\t\t'months' => [\n\t\t\t1 => 'Január',\n\t\t\t2 => 'Február',\n\t\t\t3 => 'Marec',\n\t\t\t4 => 'Apríl',\n\t\t\t5 => 'Máj',\n\t\t\t6 => 'Jún',\n\t\t\t7 => 'Júl',\n\t\t\t8 => 'August',\n\t\t\t9 => 'September',\n\t\t\t10 => 'Október',\n\t\t\t11 => 'November',\n\t\t\t12 => 'December',\n\t\t\t'jan' => 'Jan',\n\t\t\t'feb' => 'Feb',\n\t\t\t'mar' => 'Mar',\n\t\t\t'apr' => 'Apr',\n\t\t\t'may' => 'Máj',\n\t\t\t'jun' => 'Jún',\n\t\t\t'jul' => 'Júl',\n\t\t\t'aug' => 'Aug',\n\t\t\t'sep' => 'Sep',\n\t\t\t'oct' => 'Okt',\n\t\t\t'nov' => 'Nov',\n\t\t\t'dec' => 'Dec',\n\t\t\t'total' => 'Celkom',\n\t\t],\n\t\t'mb' => 'Prevádzka',\n\t\t'sumtotal' => 'Celková prevádzka',\n\t\t'sumhttp' => 'HTTP prevádzka',\n\t\t'sumftp' => 'FTP prevádzka',\n\t\t'summail' => 'Prenos e-mailov',\n\t\t'customer' => 'Zákazník',\n\t\t'domain' => 'Doména',\n\t\t'trafficoverview' => 'Prehľad prevádzky',\n\t\t'bycustomers' => 'Prevádzka podľa zákazníkov',\n\t\t'details' => 'Podrobnosti',\n\t\t'http' => 'HTTP',\n\t\t'ftp' => 'FTP',\n\t\t'mail' => 'Mail',\n\t\t'nocustomers' => 'Potrebujete aspoň jedného zákazníka na zobrazenie štatistík prevádzky.',\n\t\t'top5customers' => 'Top 5 zákazníkov',\n\t\t'nodata' => 'Pre daný rozsah neboli nájdené žiadne dáta.',\n\t\t'ranges' => [\n\t\t\t'last24h' => 'posledných 24 hodín',\n\t\t\t'last7d' => 'posledných 7 dní',\n\t\t\t'last30d' => 'posledných 30 dní',\n\t\t\t'cm' => 'Aktuálny mesiac',\n\t\t\t'last3m' => 'posledné 3 mesiace',\n\t\t\t'last6m' => 'posledných 6 mesiacov',\n\t\t\t'last12m' => 'posledných 12 mesiacov',\n\t\t\t'cy' => 'Aktuálny rok',\n\t\t],\n\t\t'byrange' => 'Určené podľa rozsahu',\n\t],\n\t'translator' => '',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => 'Bola nainštalovaná novšia verzia froxloru, ale ešte nebola nastavená.<br />Iba správca sa môže prihlásiť a dokončiť aktualizáciu.',\n\t\t'update' => 'Aktualizácia froxloru',\n\t\t'proceed' => 'Pokračovať',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'froxlor súbory boli aktualizované na verziu <strong>%s</strong>. Nainštalovaná verzia je <strong>%s</strong>.',\n\t\t\t'part_b' => '<br /><br />Zákazníci sa nebudú môcť prihlásiť, kým nebude aktualizácia dokončená.<br /><strong>Pokračovať?</strong>',\n\t\t],\n\t\t'noupdatesavail' => 'Už máte najnovšiu verziu %sfroxlor nainštalovanú.',\n\t\t'description' => 'Prebieha aktualizácia databázy pre vašu inštaláciu froxlor',\n\t\t'uc_newinfo' => 'K dispozícii je novšia verzia %s: „%s\" (Vaša aktuálna verzia je: %s)',\n\t\t'notify_subject' => 'K dispozícii je nová aktualizácia',\n\t\t'dbupdate_required' => 'froxlor súbory boli aktualizované, je vyžadovaná aktualizácia databázy',\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => 'Vlastné poznámky',\n\t\t\t'description' => 'Nebojte sa tu vložiť akékoľvek poznámky, ktoré chcete/potrebujete. Zobrazia sa v prehľade správcu/zákazníka pre príslušného používateľa.<br>Markdown je podporovaný, HTML bude odstránené.',\n\t\t\t'show' => 'Zobraziť svoje poznámky na nástenke používateľa',\n\t\t],\n\t\t'api_allowed' => [\n\t\t\t'title' => 'Povoliť prístup k API',\n\t\t\t'description' => 'Ak je povolené v nastaveniach, tento používateľ môže vytvoriť API kľúče a pristupovať k froxlor API',\n\t\t\t'notice' => 'Prístup k API nie je povolený pre váš účet.',\n\t\t],\n\t\t'shell_allowed' => [\n\t\t\t'title' => 'Povoliť prístup k shellu',\n\t\t\t'description' => 'Ak je táto možnosť povolená v nastaveniach, tento používateľ môže priradiť prístup k shellu ftp používateľom.',\n\t\t],\n\t\t'gui_access' => [\n\t\t\t'title' => 'Povoliť prihlásenie do WebUI',\n\t\t\t'description' => 'Ak je zakázané, používateľ sa nemôže prihlásiť do froxlor web-ui, ale všetky služby (web, ftp, mail, databáza, api-prístup atď.) budú fungovať normálne.',\n\t\t],\n\t],\n\t'install' => [\n\t\t'slogan' => 'panel na správu servera froxlor',\n\t\t'preflight' => 'Kontrola systému',\n\t\t'critical_error' => 'Kritická chyba',\n\t\t'suggestions' => 'Nie je vyžadované, ale odporúča sa',\n\t\t'phpinfosuccess' => 'Váš systém beží s PHP %s',\n\t\t'suggestionsnote' => 'Neexistujú žiadne kritické chyby, ktoré by bránili inštalácii, ale pre optimálne fungovanie prosím postupujte podľa nižšie uvedených odporúčaní.',\n\t\t'phpinfowarn' => 'Váš systém beží na nižšej verzii ako PHP %s',\n\t\t'phpinfoupdate' => 'Aktualizujte vašu aktuálnu verziu PHP z %s na %s alebo vyššiu',\n\t\t'start_installation' => 'Spustiť inštaláciu',\n\t\t'check_again' => 'Znovu načítajte a skontrolujte',\n\t\t'switchmode_advanced' => 'Zobraziť rozšírené možnosti',\n\t\t'switchmode_basic' => 'Skryť rozšírené možnosti',\n\t\t'dependency_check' => [\n\t\t\t'title' => 'Vitajte vo froxlore',\n\t\t\t'description' => 'Skontrolujeme závislosť systému, aby sme zaistili, že budú povolené všetky požadované php rozšírenia a moduly, aby froxlor bežal správne.',\n\t\t],\n\t\t'database' => [\n\t\t\t'top' => 'Databáza',\n\t\t\t'title' => 'Vytvoriť databázu a používateľa',\n\t\t\t'description' => 'froxlor vyžaduje databázu a navyše <a href=\"https://docs.froxlor.org/latest/general/installation/tarball.html#_3-create-privileged-database-user\" target=\"_blank\">privilegovaného používateľa</a>, aby mohol vytvárať používateľov a databázy (možnosť GRANT). Daná databáza a neprivilegovaný databázový používateľ bude vytvorený v tomto procese. Oprávnený používateľ musí existovať.',\n\t\t\t'user' => 'Neoprávnený databázový používateľ',\n\t\t\t'dbname' => 'Názov databázy',\n\t\t\t'force_create' => 'Zálohovať a prepísať databázu, ak existuje?',\n\t\t],\n\t\t'admin' => [\n\t\t\t'top' => 'Admin používateľ',\n\t\t\t'title' => 'Poďme vytvoriť hlavného administrátora.',\n\t\t\t'description' => 'Tomuto používateľovi budú udelené všetky oprávnenia na úpravu nastavení a pridávanie/aktualizáciu/mazanie zdrojov, ako sú zákazníci, domény atď.',\n\t\t\t'use_admin_email_as_sender' => 'Použite vyššie uvedenú e-mailovú adresu ako adresu odosielateľa. Ak nie je zaškrtnuté, zadajte prosím nižšie uvedenú adresu odosielateľa.',\n\t\t\t'use_autogenerated_email_as_sender' => 'Nechajte prázdne pre predvolené: admin@nazovservera',\n\t\t],\n\t\t'system' => [\n\t\t\t'top' => 'Systémové nastavenie',\n\t\t\t'title' => 'Podrobnosti o vašom serveri',\n\t\t\t'description' => 'Nastavte si svoje prostredie rovnako ako dáta a možnosti súvisiace so serverom, aby sa froxlor mohol dozvedieť o vašom systéme. Tieto hodnoty sú kľúčové pre konfiguráciu a fungovanie systému.',\n\t\t\t'ipv4' => 'Primárna IPv4 adresa (ak sa použije)',\n\t\t\t'ipv6' => 'Primárna IPv6 adresa (ak sa použije)',\n\t\t\t'servername' => 'Názov servera (FQDN, žiadna ip-adresa)',\n\t\t\t'phpbackend' => 'PHP backend',\n\t\t\t'activate_newsfeed' => 'Povoliť oficiálne novinky<br><small>(externý zdroj: https://inside.froxlor.org/news/)</small>',\n\t\t],\n\t\t'install' => [\n\t\t\t'top' => 'Dokončiť nastavenie',\n\t\t\t'title' => 'Posledný krok...',\n\t\t\t'description' => 'Nižšie uvedený príkaz stiahne, nainštaluje a nakonfiguruje požadované služby vo vašom systéme podľa údajov, ktoré ste zadali v tomto inštalačnom procese.<br><br><span class=\"text-danger\">Nezabudnite spustiť nasledujúci príkaz ako <b>root</b> na shell/terminál servera a <b>uvedomte si</b>, že tento príkaz <b>prepíše</b> akúkoľvek existujúcu konfiguráciu pre použité služby (budú vytvorené zálohy)!.<br>Ak nechcete prepísať žiadne konfigurácie, vyberte možnosť <i>Nakonfigurujem služby ručne</i> v dolnej časti tejto stránky!</span>',\n\t\t\t'runcmd' => 'Spustite nasledujúci príkaz na dokončenie inštalácie:',\n\t\t\t'manual_config' => 'Služby nakonfigurujem ručne, stačí ma presmerovať na prihlásenie',\n\t\t\t'waitforconfig' => 'Čakanie na konfiguráciu služieb...',\n\t\t],\n\t\t'errors' => [\n\t\t\t'wrong_ownership' => 'Uistite sa, že súbory froxlor sú vlastnené %s:%s',\n\t\t\t'missing_extensions' => 'Nasledujúce php rozšírenia sú vyžadované a nie sú nainštalované',\n\t\t\t'suggestedextensions' => 'Nasledujúce php rozšírenia sa nepodarilo nájsť, ale sú odporúčané',\n\t\t\t'databaseexists' => 'Databáza už existuje, prosím nastavte možnosť prepísania pre obnovu alebo zvoľte iný názov',\n\t\t\t'unabletocreatedb' => 'Nie je možné vytvoriť testovaciu databázu',\n\t\t\t'unabletodropdb' => 'Nie je možné zrušiť testovaciu databázu',\n\t\t\t'mysqlusernameexists' => 'Používateľ určený pre používateľa bez oprávnení už existuje. Použite prosím iné používateľské meno, alebo ho odstráňte.',\n\t\t\t'unabletocreateuser' => 'Nie je možné vytvoriť testovacieho používateľa',\n\t\t\t'unabletodropuser' => 'Nie je možné zrušiť testovacieho používateľa',\n\t\t\t'unabletoflushprivs' => 'Zadaný privilegovaný používateľ nemôže vymazať oprávnenia',\n\t\t\t'nov4andnov6ip' => 'Musí byť zadaná adresa IPv4 alebo IPv6',\n\t\t\t'servernameneedstobevalid' => 'Zadaný názov servera sa nezdá byť FQDN alebo hostname',\n\t\t\t'websrvuserdoesnotexist' => 'Zdá sa, že používateľ webového servera v systéme neexistuje',\n\t\t\t'websrvgrpdoesnotexist' => 'Zdá sa, že daná skupina webservera v systéme neexistuje',\n\t\t\t'notyetconfigured' => 'Zdá sa, že služby ešte neboli nakonfigurované (úspešne). Prosím vykonajte príkaz nižšie alebo zaškrtnite políčko pre neskoršie spracovanie.',\n\t\t\t'mandatory_field_not_set' => 'Povinné pole „%s\" nie je nastavené!',\n\t\t\t'unexpected_database_error' => 'Došlo k neočakávanej výnimke databázy. %s',\n\t\t\t'sql_import_failed' => 'Nepodarilo sa importovať SQL dáta!',\n\t\t\t'unprivileged_sql_connection_failed' => 'Nepodarilo sa inicializovať neprivilegované SQL pripojenie!',\n\t\t\t'privileged_sql_connection_failed' => 'Nepodarilo sa inicializovať privilegované SQL pripojenie!',\n\t\t\t'mysqldump_backup_failed' => 'Nie je možné vytvoriť zálohu databázy, došlo k chybe mysqldump.',\n\t\t\t'sql_backup_file_missing' => 'Nie je možné vytvoriť zálohu databázy, záložný súbor neexistuje.',\n\t\t\t'backup_binary_missing' => 'Nie je možné vytvoriť zálohu databázy, uistite sa, že máte nainštalovaný mysqldump.',\n\t\t\t'creating_configfile_failed' => 'Nie je možné vytvoriť konfiguračné súbory, nie je možné zapisovať do súboru.',\n\t\t\t'database_already_exiting' => 'Našli sme databázu a nebolo možné ju prepísať!'\n\t\t]\n\t],\n\t'welcome' => [\n\t\t'title' => 'Vitajte vo froxlore!',\n\t\t'config_note' => 'Aby mohol froxlor správne komunikovať so zálohou, musíte ju nakonfigurovať.',\n\t\t'config_now' => 'Nastaviť teraz'\n\t],\n];\n"
  },
  {
    "path": "lng/zh_CN.lng.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nreturn [\n\t'languages' => [\n\t\t'cz' => '捷克语',\n\t\t'de' => '德语',\n\t\t'en' => '英语',\n\t\t'fr' => '法语',\n\t\t'hu' => '匈牙利语',\n\t\t'it' => '意大利语',\n\t\t'nl' => '荷兰语',\n\t\t'pt' => '葡萄牙语',\n\t\t'se' => '瑞典语',\n\t\t'sk' => '斯洛伐克语',\n\t\t'es' => '西班牙语',\n\t\t'ca' => '加泰罗尼亚语',\n\t\t'zh_CN' => '简体中文',\n\t],\n\t'2fa' => [\n\t\t'2fa' => '2FA选项',\n\t\t'2fa_enabled' => '启用双因素身份验证（2FA）',\n\t\t'2fa_removed' => '成功停用2FA',\n\t\t'2fa_added' => '2FA启用成功<br><a class=\"alert-link\" href=\"%s?page=2fa\">查看2FA详情</a>',\n\t\t'2fa_add' => '启用2FA',\n\t\t'2fa_delete' => '停用2FA',\n\t\t'2fa_verify' => '验证代码',\n\t\t'2fa_overview_desc' => '在这里，您可以为您的帐户激活双因素身份验证。<br><br>您可以使用验证器应用程序（基于时间的一次性密码/ TOTP），或者让froxlor在每次使用一次性密码成功登录后向您的帐户地址发送电子邮件。',\n\t\t'2fa_email_desc' => '您的账户已设置为通过电子邮件使用一次性密码。要停用，请点击\"停用2FA\"',\n\t\t'2fa_ga_desc' => '您的账户已设置为通过验证器应用程序使用基于时间的一次性密码。请使用您选择的验证器应用程序扫描下方的二维码以生成代码。要停用，请点击\"停用2FA\"',\n\t\t'2fa_not_activated' => '未启用双因素身份验证',\n\t\t'2fa_not_activated_for_user' => '未为当前用户启用双因素身份验证',\n\t\t'type_2fa' => '2FA状态',\n\t],\n\t'admin' => [\n\t\t'overview' => '概述',\n\t\t'ressourcedetails' => '资源详情',\n\t\t'systemdetails' => '系统详情',\n\t\t'froxlordetails' => 'froxlor详细信息',\n\t\t'installedversion' => '安装版本',\n\t\t'latestversion' => '最新版本',\n\t\t'lookfornewversion' => [\n\t\t\t'clickhere' => '通过网络服务搜索',\n\t\t\t'error' => '读取时出错',\n\t\t],\n\t\t'resources' => '资源',\n\t\t'customer' => '客户',\n\t\t'customers' => '客户',\n\t\t'customers_list_desc' => '管理您的客户',\n\t\t'customer_add' => '创建客户',\n\t\t'customer_edit' => '编辑客户',\n\t\t'username_default_msg' => '留空以使用自动生成的值',\n\t\t'password_default_msg' => '为空时自动生成',\n\t\t'domains' => '域名',\n\t\t'domain_add' => '创建域名',\n\t\t'domain_edit' => '编辑域名',\n\t\t'subdomainforemail' => '子域名作为邮件域名',\n\t\t'admin' => '管理员',\n\t\t'admins' => '管理员',\n\t\t'admin_add' => '创建管理员',\n\t\t'admin_edit' => '编辑管理员',\n\t\t'customers_see_all' => '是否可以访问其他管理员/经销商的资源？',\n\t\t'change_serversettings' => '是否可以更改服务器设置？',\n\t\t'server' => '服务器',\n\t\t'serversettings' => '服务器设置',\n\t\t'serversettings_desc' => '管理您的froxlor系统',\n\t\t'rebuildconf' => '重新生成配置文件',\n\t\t'stdsubdomain' => '标准子域',\n\t\t'stdsubdomain_add' => '创建标准子域',\n\t\t'phpenabled' => 'PHP已启用',\n\t\t'deactivated' => '已停用',\n\t\t'deactivated_user' => '停用用户',\n\t\t'sendpassword' => '发送密码',\n\t\t'ownvhostsettings' => '自定义vHost设置',\n\t\t'configfiles' => [\n\t\t\t'serverconfiguration' => '配置',\n\t\t\t'overview' => '概述',\n\t\t\t'wizard' => '向导',\n\t\t\t'distribution' => '系统发行版',\n\t\t\t'service' => '服务',\n\t\t\t'daemon' => '守护进程',\n\t\t\t'http' => 'Web服务器 (HTTP)',\n\t\t\t'dns' => 'DNS服务器',\n\t\t\t'mail' => '邮件服务器 (IMAP/POP3)',\n\t\t\t'smtp' => '邮件服务器 (SMTP)',\n\t\t\t'ftp' => 'FTP服务器',\n\t\t\t'etc' => '其他 (系统)',\n\t\t\t'choosedistribution' => '-- 选择系统发行版 --',\n\t\t\t'chooseservice' => '-- 选择服务 --',\n\t\t\t'choosedaemon' => '-- 选择守护进程 --',\n\t\t\t'statistics' => '统计',\n\t\t\t'compactoverview' => '简洁概述',\n\t\t\t'legend' => '<h3>您将要配置服务/守护进程</h3>',\n\t\t\t'commands' => '<span class=\"text-danger\">命令：</span>这些命令应以root用户身份在Shell中逐行执行。可以安全地复制整个块并粘贴到Shell中。',\n\t\t\t'files' => '<span class=\"text-danger\">配置文件：</span>文本框前的命令应该打开一个带有目标文件的编辑器。只需将内容复制并粘贴到编辑器中，然后保存文件。<br><span class=\"text-danger\">请注意：</span>出于安全原因，MySQL密码尚未被替换。请自行替换\"FROXLOR_MYSQL_PASSWORD\"或使用下面的JavaScript表单在现场替换。如果您忘记了MySQL密码，可以在\"lib/userdata.inc.php\"中找到它',\n\t\t\t'importexport' => '导入/导出',\n\t\t\t'finishnote' => '参数文件生成成功。现在以root身份运行以下命令：',\n\t\t\t'description' => '配置系统服务',\n\t\t\t'minihowto' => '在此页面上，您可以查看每个服务的不同配置模板，（重新）配置特定服务（如果需要）或将当前选择导出到JSON文件以在CLI脚本或其他服务器上使用。<br><br><b>请注意</b>，突出显示的服务并不反映您当前的设置，而是显示基于当前设置值的要求/建议。',\n\t\t\t'skipconfig' => '不(重新)配置',\n\t\t\t'recommendednote' => '基于当前系统设置的建议/所需服务',\n\t\t\t'selectrecommended' => '选择推荐',\n\t\t\t'downloadselected' => '导出选定',\n\t\t],\n\t\t'templates' => [\n\t\t\t'templates' => '邮件模板',\n\t\t\t'template_add' => '添加模板',\n\t\t\t'template_fileadd' => '添加文件模板',\n\t\t\t'template_edit' => '编辑模板',\n\t\t\t'action' => '操作',\n\t\t\t'email' => '邮件和文件模板',\n\t\t\t'subject' => '主题',\n\t\t\t'mailbody' => '邮件正文',\n\t\t\t'createcustomer' => '新客户欢迎邮件',\n\t\t\t'pop_success' => '新邮件账户欢迎邮件',\n\t\t\t'template_replace_vars' => '模板中将被替换的变量：',\n\t\t\t'SALUTATION' => '替换为正确的称呼（姓名或公司）',\n\t\t\t'FIRSTNAME' => '替换为客户的名字。',\n\t\t\t'NAME' => '替换为客户的全名。',\n\t\t\t'COMPANY' => '替换为客户的公司名称。',\n\t\t\t'USERNAME' => '替换为客户的账户用户名。',\n\t\t\t'PASSWORD' => '替换为客户的账户密码。',\n\t\t\t'EMAIL' => '替换为POP3/IMAP账户的地址。',\n\t\t\t'CUSTOMER_NO' => '替换为客户编号。',\n\t\t\t'TRAFFIC' => '替换为分配给客户的流量。',\n\t\t\t'TRAFFICUSED' => '替换为客户已使用的流量。',\n\t\t\t'pop_success_alternative' => '发送至备用地址的新邮件账户欢迎邮件',\n\t\t\t'EMAIL_PASSWORD' => '替换为POP3/IMAP账户密码。',\n\t\t\t'index_html' => '新创建客户目录的索引文件',\n\t\t\t'unconfigured_html' => '未配置/未知域的索引文件',\n\t\t\t'unconfigured_content_fallback' => '此域名需要通过froxlor服务器管理面板进行配置，因为它当前未分配给任何客户。',\n\t\t\t'file_extension' => [\n\t\t\t\t'description' => '索引文件的文件扩展名长度必须在1到6个字符之间。扩展名只能包含a-z、A-Z和0-9等字符<br><br>默认值：html',\n\t\t\t\t'title' => '文件模板的文件扩展名',\n\t\t\t],\n\t\t\t'SERVERNAME' => '替换为服务器名称。',\n\t\t\t'CUSTOMER' => '替换为客户的登录名。仅适用于\"新创建客户目录的索引文件\"',\n\t\t\t'ADMIN' => '替换为管理员的登录名。仅适用于\"新创建客户目录的索引文件\"',\n\t\t\t'CUSTOMER_EMAIL' => '替换为客户的电子邮件地址。仅适用于\"新创建客户目录的索引文件\"',\n\t\t\t'ADMIN_EMAIL' => '替换为管理员的电子邮件地址。仅适用于\"新创建客户目录的索引文件\"',\n\t\t\t'filetemplates' => '文件模板',\n\t\t\t'filecontent' => '文件内容',\n\t\t\t'new_database_by_customer' => '创建数据库时的客户通知',\n\t\t\t'new_ftpaccount_by_customer' => '创建FTP用户时的客户通知',\n\t\t\t'newdatabase' => '新数据库的通知邮件',\n\t\t\t'newftpuser' => '新FTP用户的通知邮件',\n\t\t\t'CUST_NAME' => '客户名称',\n\t\t\t'DB_NAME' => '数据库名称',\n\t\t\t'DB_PASS' => '数据库密码',\n\t\t\t'DB_DESC' => '数据库描述',\n\t\t\t'DB_SRV' => '数据库服务器',\n\t\t\t'PMA_URI' => 'phpMyAdmin的URL（如果有）',\n\t\t\t'USR_NAME' => 'FTP用户名',\n\t\t\t'USR_PASS' => 'FTP密码',\n\t\t\t'USR_PATH' => 'FTP主目录（相对于客户文档根目录）',\n\t\t\t'forgotpwd' => '密码重置的通知邮件',\n\t\t\t'password_reset' => '密码重置的客户通知',\n\t\t\t'trafficmaxpercent' => '当流量配额百分比耗尽时向客户发送通知邮件',\n\t\t\t'MAX_PERCENT' => '替换为发送报告的磁盘使用/流量限制百分比。',\n\t\t\t'USAGE_PERCENT' => '替换为客户已使用的磁盘使用率/流量百分比。',\n\t\t\t'diskmaxpercent' => '当磁盘空间配额百分比耗尽时向客户发送通知邮件',\n\t\t\t'DISKAVAILABLE' => '替换为分配给客户的磁盘使用量。',\n\t\t\t'DISKUSED' => '替换为客户已使用的磁盘空间。',\n\t\t\t'LINK' => '替换为客户密码重置链接。',\n\t\t\t'SERVER_HOSTNAME' => '替换系统主机名（指向froxlor的URL）。',\n\t\t\t'SERVER_IP' => '替换默认服务器IP地址。',\n\t\t\t'SERVER_PORT' => '替换默认服务器端口。',\n\t\t\t'DOMAINNAME' => '替换客户的标准子域（如果没有生成则为空）。',\n\t\t],\n\t\t'webserver' => 'Web服务器',\n\t\t'createzonefile' => '创建域的DNS区域',\n\t\t'custombindzone' => '自定义/非托管区域文件',\n\t\t'bindzonewarning' => '留空使用默认值<br /><strong class=\"text-danger\">注意：</strong>如果您使用区域文件，则必须手动管理所有子区域所需的所有记录。',\n\t\t'ipsandports' => [\n\t\t\t'ipsandports' => 'IP和端口',\n\t\t\t'add' => '添加IP/端口',\n\t\t\t'edit' => '编辑IP/端口',\n\t\t\t'ipandport' => 'IP/端口',\n\t\t\t'ip' => 'IP地址',\n\t\t\t'ipnote' => '<div id=\"ipnote\" class=\"invalid-feedback\">注意：虽然允许使用私有IP地址，但某些功能（如DNS）可能无法正常工作。<br>只有在确定的情况下才使用私有IP地址。</div>',\n\t\t\t'port' => '端口',\n\t\t\t'create_listen_statement' => '创建Listen语句',\n\t\t\t'create_namevirtualhost_statement' => '创建NameVirtualHost语句',\n\t\t\t'create_vhostcontainer' => '创建vHost容器',\n\t\t\t'create_vhostcontainer_servername_statement' => '在vHost容器中创建ServerName语句',\n\t\t\t'enable_ssl' => '这是SSL端口吗？',\n\t\t\t'ssl_cert_file' => 'SSL证书的路径',\n\t\t\t'webserverdefaultconfig' => 'Web服务器默认配置',\n\t\t\t'webserverdomainconfig' => 'Web服务器域名配置',\n\t\t\t'webserverssldomainconfig' => 'Web服务器SSL配置',\n\t\t\t'ssl_key_file' => 'SSL密钥文件的路径',\n\t\t\t'ssl_ca_file' => 'SSL CA证书的路径',\n\t\t\t'default_vhostconf_domain' => '每个域名容器的默认vHost设置',\n\t\t\t'ssl_cert_chainfile' => [\n\t\t\t\t'title' => 'SSL证书链文件的路径',\n\t\t\t\t'description' => '大多数情况下是CA_Bundle或类似的，如果您购买了SSL证书，您可能需要设置此选项。',\n\t\t\t],\n\t\t\t'docroot' => [\n\t\t\t\t'title' => '自定义文档根目录（留空指向froxlor）',\n\t\t\t\t'description' => '您可以在这里为这个IP/端口组合定义一个自定义的文档根目录（请求的目标位置）。<br /><strong>注意：</strong>请小心您输入的内容！',\n\t\t\t],\n\t\t\t'ssl_paste_description' => '将完整的证书内容粘贴到文本框中',\n\t\t\t'ssl_cert_file_content' => 'SSL证书的内容',\n\t\t\t'ssl_key_file_content' => 'SSL私钥文件的内容',\n\t\t\t'ssl_ca_file_content' => 'SSL CA文件的内容（可选）',\n\t\t\t'ssl_ca_file_content_desc' => '<br /><br />客户端身份验证，只有当您知道它是什么时才设置它。',\n\t\t\t'ssl_cert_chainfile_content' => '证书链文件的内容（可选）',\n\t\t\t'ssl_cert_chainfile_content_desc' => '<br /><br />大多数情况下是CA_Bundle或类似的，如果您购买了SSL证书，您可能需要设置此选项。',\n\t\t\t'ssl_default_vhostconf_domain' => '每个域名容器的默认SSL vHost设置',\n\t\t],\n\t\t'memorylimitdisabled' => '已禁用',\n\t\t'valuemandatory' => '此值是必需的',\n\t\t'valuemandatorycompany' => '必须填写\"姓名\"和\"名字\"或\"公司\"',\n\t\t'serversoftware' => '服务器软件',\n\t\t'phpversion' => 'PHP版本',\n\t\t'mysqlserverversion' => 'MySQL服务器版本',\n\t\t'webserverinterface' => 'Web服务器接口',\n\t\t'accountsettings' => '帐号设置',\n\t\t'panelsettings' => '面板设置',\n\t\t'systemsettings' => '系统设置',\n\t\t'webserversettings' => 'Web服务器设置',\n\t\t'mailserversettings' => '邮件服务器设置',\n\t\t'nameserversettings' => 'DNS服务器设置',\n\t\t'updatecounters' => '重新计算资源使用情况',\n\t\t'subcanemaildomain' => [\n\t\t\t'never' => '从不',\n\t\t\t'choosableno' => '可选，默认为否',\n\t\t\t'choosableyes' => '可选，默认为是',\n\t\t\t'always' => '始终',\n\t\t],\n\t\t'wipecleartextmailpwd' => '清除明文密码',\n\t\t'webalizersettings' => 'Webalizer设置',\n\t\t'webalizer' => [\n\t\t\t'normal' => '正常',\n\t\t\t'quiet' => '安静',\n\t\t\t'veryquiet' => '无输出',\n\t\t],\n\t\t'domain_nocustomeraddingavailable' => '当前无法添加域名。您首先需要至少添加一个客户。',\n\t\t'loggersettings' => '日志设置',\n\t\t'logger' => [\n\t\t\t'normal' => '正常',\n\t\t\t'paranoid' => '详细',\n\t\t],\n\t\t'emaildomain' => '邮件域名',\n\t\t'email_only' => '仅限邮件',\n\t\t'wwwserveralias' => '添加\"www.\" ServerAlias',\n\t\t'subject' => '主题',\n\t\t'recipient' => '收件人',\n\t\t'message' => '编写消息',\n\t\t'text' => '消息',\n\t\t'sslsettings' => 'SSL设置',\n\t\t'specialsettings_replacements' => '您可以使用以下变量：<br/><code>{DOMAIN}</code>、<code>{DOCROOT}</code>、<code>{CUSTOMER}</code>、<code>{IP}</code>、<code>{PORT}</code>、<code>{SCHEME}</code>、<code>{FPMSOCKET}</code>（如果适用）<br/>',\n\t\t'antispam_settings' => '反垃圾邮件设置',\n\t\t'caneditphpsettings' => '可以更改PHP相关的域名设置吗？',\n\t\t'allips' => '所有IP',\n\t\t'awstatssettings' => 'AWstats设置',\n\t\t'domain_dns_settings' => '域名DNS设置',\n\t\t'activated' => '已激活',\n\t\t'statisticsettings' => '统计设置',\n\t\t'or' => '或',\n\t\t'sysload' => '系统负载',\n\t\t'noloadavailable' => '不可用',\n\t\t'nouptimeavailable' => '不可用',\n\t\t'nosubject' => '（无主题）',\n\t\t'security_settings' => '安全选项',\n\t\t'know_what_youre_doing' => '只有当您知道您在做什么的时候，才能更改！',\n\t\t'show_version_login' => [\n\t\t\t'title' => '登录时显示froxlor版本',\n\t\t\t'description' => '在登录页面的页脚中显示froxlor版本',\n\t\t],\n\t\t'show_version_footer' => [\n\t\t\t'title' => '在页脚中显示froxlor版本',\n\t\t\t'description' => '在其余页面的页脚中显示froxlor版本',\n\t\t],\n\t\t'froxlor_graphic' => [\n\t\t\t'title' => 'froxlor的标题图片',\n\t\t\t'description' => '标题中应显示什么图片',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'title' => 'PHP配置',\n\t\t\t'description' => '简短描述',\n\t\t\t'actions' => '操作',\n\t\t\t'activedomains' => '正在用于域名',\n\t\t\t'notused' => '配置未使用',\n\t\t\t'editsettings' => '更改PHP设置',\n\t\t\t'addsettings' => '创建新的PHP设置',\n\t\t\t'viewsettings' => '查看PHP设置',\n\t\t\t'phpinisettings' => 'php.ini设置',\n\t\t\t'addnew' => '创建新的PHP配置',\n\t\t\t'binary' => 'PHP二进制文件',\n\t\t\t'fpmdesc' => 'PHP-FPM配置',\n\t\t\t'file_extensions' => '文件扩展名',\n\t\t\t'file_extensions_note' => '（不带点，以空格分隔）',\n\t\t\t'enable_slowlog' => '启用慢日志（每个域名）',\n\t\t\t'request_terminate_timeout' => '请求终止超时',\n\t\t\t'request_slowlog_timeout' => '请求慢日志超时',\n\t\t\t'activephpconfigs' => '正在使用的PHP配置',\n\t\t\t'pass_authorizationheader' => '将HTTP AUTH BASIC/DIGEST头从Apache传递到PHP',\n\t\t],\n\t\t'misc' => '杂项',\n\t\t'fpmsettings' => [\n\t\t\t'addnew' => '创建新的PHP版本',\n\t\t\t'edit' => '更改PHP版本'\n\t\t],\n\t\t'phpconfig' => [\n\t\t\t'template_replace_vars' => '配置中将被替换的变量',\n\t\t\t'pear_dir' => '将被替换为pear目录的全局设置。',\n\t\t\t'open_basedir_c' => '将插入一个;(分号)以在设置时注释/禁用open_basedir',\n\t\t\t'open_basedir' => '将被替换为域名的open_basedir设置。',\n\t\t\t'tmp_dir' => '将被替换为域名的临时目录。',\n\t\t\t'open_basedir_global' => '将被替换为路径的全局值，该路径将附加到open_basedir（请参阅Web服务器设置）。',\n\t\t\t'customer_email' => '将被替换为拥有此域名的客户的电子邮件地址。',\n\t\t\t'admin_email' => '将被替换为拥有此域名的管理员的电子邮件地址。',\n\t\t\t'domain' => '将被替换为域名。',\n\t\t\t'customer' => '将被替换为拥有此域名的客户的登录名。',\n\t\t\t'admin' => '将被替换为拥有此域名的管理员的登录名。',\n\t\t\t'docroot' => '将被替换为域名的文档根目录。',\n\t\t\t'homedir' => '将被替换为客户的主目录。',\n\t\t],\n\t\t'expert_settings' => '专家设置！',\n\t\t'mod_fcgid_starter' => [\n\t\t\t'title' => '此域名的PHP进程（留空使用默认值）',\n\t\t],\n\t\t'phpserversettings' => 'PHP设置',\n\t\t'mod_fcgid_maxrequests' => [\n\t\t\t'title' => '此域名的最大PHP请求数（留空使用默认值）',\n\t\t],\n\t\t'spfsettings' => '域名SPF设置',\n\t\t'specialsettingsforsubdomains' => '将特殊设置应用于所有子域名（*.example.com）',\n\t\t'accountdata' => '账户数据',\n\t\t'contactdata' => '联系数据',\n\t\t'servicedata' => '服务数据',\n\t\t'newerversionavailable' => 'froxlor有新版本可用。',\n\t\t'newerversiondetails' => '是否立即更新到版本<b>%s</b>？<br/>（您的当前版本为：%s）',\n\t\t'extractdownloadedzip' => '解压下载的归档文件 \"%s\"？',\n\t\t'cron' => [\n\t\t\t'cronsettings' => '定时任务设置',\n\t\t\t'add' => '添加定时任务',\n\t\t],\n\t\t'cronjob_edit' => '编辑定时任务',\n\t\t'warning' => '请注意！',\n\t\t'lastlogin_succ' => '上次登录',\n\t\t'ftpserver' => 'FTP服务器',\n\t\t'ftpserversettings' => 'FTP服务器设置',\n\t\t'webserver_user' => 'Web服务器用户名',\n\t\t'webserver_group' => 'Web服务器组名',\n\t\t'perlenabled' => 'Perl已启用',\n\t\t'fcgid_settings' => 'FCGID',\n\t\t'mod_fcgid_user' => '用于FCGID的本地用户（froxlor vHost）',\n\t\t'mod_fcgid_group' => '用于FCGID的本地组（froxlor vHost）',\n\t\t'perl_settings' => 'Perl/CGI',\n\t\t'notgiven' => '[未指定]',\n\t\t'store_defaultindex' => '将默认索引文件存储到客户文档根目录',\n\t\t'phpfpm_settings' => 'PHP-FPM',\n\t\t'traffic' => '流量',\n\t\t'traffic_sub' => '流量使用详情',\n\t\t'domaintraffic' => '域名',\n\t\t'customertraffic' => '客户',\n\t\t'assignedmax' => '已分配/最大',\n\t\t'usedmax' => '已用/最大',\n\t\t'used' => '已用',\n\t\t'speciallogwarning' => '<div id=\"speciallogfilenote\" class=\"invalid-feedback\">警告：更改此设置将丢失此域名的所有旧统计数据。</div>',\n\t\t'speciallogfile' => [\n\t\t\t'title' => '单独的日志文件',\n\t\t\t'description' => '启用此选项可获取此域名的单独访问日志文件',\n\t\t],\n\t\t'domain_editable' => [\n\t\t\t'title' => '允许编辑域名',\n\t\t\t'desc' => '如果设置为是，则允许客户更改多个域名设置。<br />如果设置为否，则客户不能更改任何内容。',\n\t\t],\n\t\t'writeaccesslog' => [\n\t\t\t'title' => '写入访问日志',\n\t\t\t'description' => '启用此选项可获取此域名的访问日志文件',\n\t\t],\n\t\t'writeerrorlog' => [\n\t\t\t'title' => '写入错误日志',\n\t\t\t'description' => '启用此选项可获取此域名的错误日志文件',\n\t\t],\n\t\t'phpfpm.ininote' => '并不是所有您想要定义的值都可以在PHP-FPM池配置中使用',\n\t\t'phpinfo' => 'PHP 信息',\n\t\t'selectserveralias' => '域名的 ServerAlias 值',\n\t\t'selectserveralias_desc' => '选择froxlor是否应创建通配符条目（*.domain.tld）、WWW别名（www.domain.tld）或根本不创建别名',\n\t\t'show_news_feed' => [\n\t\t\t'title' => '在管理员仪表板上显示新闻源',\n\t\t\t'description' => '启用此功能可以在您的仪表板上显示官方froxlor新闻源（https://inside.froxlor.org/news/），并且永远不会错过重要信息或发布公告。',\n\t\t],\n\t\t'cronsettings' => '定时任务设置',\n\t\t'integritycheck' => '数据库完整性检查',\n\t\t'integrityname' => '名称',\n\t\t'integrityresult' => '结果',\n\t\t'integrityfix' => '自动修复问题',\n\t\t'customer_show_news_feed' => '在客户控制面板上显示新闻源',\n\t\t'customer_news_feed_url' => [\n\t\t\t'title' => '使用自定义RSS源',\n\t\t\t'description' => '指定一个自定义的RSS源，将显示给您的客户在他们的仪表板上。<br /><small>将此留空以使用官方froxlor新闻源（https://inside.froxlor.org/news/）。</small>',\n\t\t],\n\t\t'movetoadmin' => '移动客户',\n\t\t'movecustomertoadmin' => [\n\t\t\t'title' => '将客户移动到选定的管理员/经销商',\n\t\t\t'description' => '留空则不更改。<br />如果所需的管理员没有出现在列表中，则已达到其客户数量限制。',\n\t\t],\n\t\t'note' => '注意',\n\t\t'mod_fcgid_umask' => [\n\t\t\t'title' => 'Umask（默认值：022）',\n\t\t],\n\t\t'apcuinfo' => 'APCu信息',\n\t\t'opcacheinfo' => 'OPcache信息',\n\t\t'letsencrypt' => [\n\t\t\t'title' => '使用Let\\'s Encrypt',\n\t\t\t'description' => '从<a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>获取免费证书。证书将自动创建和续订。<br><strong class=\"text-danger\">注意：</strong>如果启用了通配符，此选项将自动禁用。',\n\t\t],\n\t\t'autoupdate' => '自动更新',\n\t\t'server_php' => 'PHP',\n\t\t'dnsenabled' => '启用DNS编辑器',\n\t\t'froxlorvhost' => 'froxlor VirtualHost设置',\n\t\t'hostname' => '主机名',\n\t\t'memory' => '内存使用',\n\t\t'webserversettings_ssl' => 'Web服务器SSL设置',\n\t\t'domain_hsts_maxage' => [\n\t\t\t'title' => 'HTTP严格传输安全（HSTS）',\n\t\t\t'description' => '指定安全传输标头的最大有效期<br>值<i>0</i>将禁用域名的HSTS。大多数用户将值设置为<i>31536000</i>（一年）。',\n\t\t],\n\t\t'domain_hsts_incsub' => [\n\t\t\t'title' => '包含所有子域的HSTS',\n\t\t\t'description' => '可选的\"includeSubDomains\"指令（如果存在）向用户代理发出信号，表明HSTS策略适用于此HSTS主机以及主机域名的任何子域。',\n\t\t],\n\t\t'domain_hsts_preload' => [\n\t\t\t'title' => '在HSTS预加载列表中包含域名',\n\t\t\t'description' => '如果您希望将此域名包含在Chrome维护的<a href=\"https://hstspreload.org/\" target=\"_blank\">HSTS预加载列表</a>中（并被Firefox和Safari使用），请启用此选项。<br>从您的站点发送preload指令可能会产生永久性后果，并阻止用户访问您的站点及其任何子域。<br>在发送带有\"preload\"的标头之前，请阅读<a href=\"https://hstspreload.org/#removal\" target=\"_blank\">https://hstspreload.org/#removal</a>上的详细信息。',\n\t\t],\n\t\t'domain_ocsp_stapling' => [\n\t\t\t'title' => 'OCSP装订',\n\t\t\t'description' => '有关OCSP装订的详细说明，请参阅<a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/OCSP_stapling\">维基百科</a>',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">警告：</strong>OCSP装订需要Nginx 1.3.7或更高版本。如果您的版本较旧，启用OCSP装订时Web服务器将无法正确启动！',\n\t\t],\n\t\t'domain_http2' => [\n\t\t\t'title' => 'HTTP/2支持',\n\t\t\t'description' => '有关HTTP/2的详细说明，请参阅<a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/2\">维基百科</a>',\n\t\t],\n\t\t'domain_http3' => [\n\t\t\t'title' => 'HTTP/3支持',\n\t\t\t'description' => '有关HTTP/3的详细说明，请参阅<a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/HTTP/3\">维基百科</a>',\n\t\t\t'nginx_version_warning' => '<br /><strong class=\"text-danger\">警告：</strong>HTTP/3需要Nginx 1.25.0或更高版本以及SSL协议TLSv1.3。如果您的版本较旧，启用HTTP/3时Web服务器将无法正确启动！',\n\t\t],\n\t\t'testmail' => 'SMTP测试',\n\t\t'phpsettingsforsubdomains' => '将PHP配置应用于所有子域名：',\n\t\t'plans' => [\n\t\t\t'name' => '计划名称',\n\t\t\t'description' => '描述',\n\t\t\t'last_update' => '最后更新',\n\t\t\t'plans' => '托管计划',\n\t\t\t'plan_details' => '计划详情',\n\t\t\t'add' => '添加新计划',\n\t\t\t'edit' => '编辑计划',\n\t\t\t'use_plan' => '应用计划',\n\t\t],\n\t\t'notryfiles' => [\n\t\t\t'title' => '不自动生成try_files',\n\t\t\t'description' => '如果您想在特殊设置中指定一个自定义的try_files指令（例如一些WordPress插件需要），请在此处选择是。',\n\t\t],\n\t\t'logviewenabled' => '启用访问/错误日志查看',\n\t\t'novhostcontainer' => '<br><br><small class=\"text-danger\">没有任何IP和端口启用了\"创建vHost容器\"选项，许多设置将不可用</small>',\n\t\t'ownsslvhostsettings' => '自定义SSL vHost设置',\n\t\t'domain_override_tls' => '覆盖系统TLS设置',\n\t\t'domain_override_tls_addinfo' => '<br /><span class=\"text-danger\">仅在\"覆盖系统TLS设置\"设置为\"是\"时使用</span>',\n\t\t'domain_sslenabled' => '启用SSL',\n\t\t'domain_honorcipherorder' => '遵循（服务器）密码顺序，默认<strong>否</strong>',\n\t\t'domain_sessiontickets' => '启用TLS会话票据（RFC 5077），默认<strong>是</strong>',\n\t\t'domain_sessionticketsenabled' => [\n\t\t\t'title' => '启用全局TLS会话票据的使用',\n\t\t\t'description' => '默认<strong>是</strong><br>需要Apache 2.4.11+或Nginx 1.5.9+',\n\t\t],\n\t\t'domaindefaultalias' => '新域名的默认ServerAlias值',\n\t\t'smtpsettings' => 'SMTP设置',\n\t\t'smtptestaddr' => '发送测试邮件至',\n\t\t'smtptestnote' => '请注意，下面的值反映了您当前的设置，只能在那里进行调整（请参阅右上角的链接）',\n\t\t'smtptestsend' => '发送测试邮件',\n\t\t'mysqlserver' => [\n\t\t\t'mysqlserver' => 'MySQL服务器',\n\t\t\t'dbserver' => '数据库服务器',\n\t\t\t'caption' => '说明',\n\t\t\t'host' => '主机名/IP',\n\t\t\t'port' => '端口',\n\t\t\t'user' => '特权用户',\n\t\t\t'add' => '添加新的MySQL服务器',\n\t\t\t'edit' => '编辑MySQL服务器',\n\t\t\t'password' => '已验证的用户密码',\n\t\t\t'password_emptynochange' => '新密码，留空不更改',\n\t\t\t'allowall' => [\n\t\t\t\t'title' => '允许所有现有客户使用此服务器',\n\t\t\t\t'description' => '如果要将此数据库服务器的使用权限授予所有现有客户，使他们能够在其上添加数据库，请设置为\"true\"。此设置不是永久性的，但可以多次运行。',\n\t\t\t],\n\t\t\t'testconn' => '保存时测试连接',\n\t\t\t'ssl' => '使用SSL连接到数据库服务器',\n\t\t\t'ssl_cert_file' => 'SSL证书颁发机构的文件路径',\n\t\t\t'verify_ca' => '启用服务器SSL证书验证',\n\t\t],\n\t\t'settings_importfile' => '选择导入文件',\n\t\t'documentation' => '文档',\n\t\t'adminguide' => '管理员指南',\n\t\t'userguide' => '用户指南',\n\t\t'apiguide' => 'API指南',\n\t\t'domain_duplicate' => '重复域名',\n\t\t'domain_duplicate_named' => '重复%s',\n\t\t'backups' => [\n\t\t\t'backups' => '备份',\n\t\t],\n\t\t'emaildomainwarning' => '<div id=\"emaildomainnote\" class=\"invalid-feedback\">警告：更改此设置将永久删除所有现有的电子邮件地址和账户。</div>',\n\t],\n\t'apcuinfo' => [\n\t\t'clearcache' => '清除APCu缓存',\n\t\t'generaltitle' => '常规缓存信息',\n\t\t'version' => 'APCu版本',\n\t\t'phpversion' => 'PHP版本',\n\t\t'host' => 'APCu主机',\n\t\t'sharedmem' => '共享内存',\n\t\t'sharedmemval' => '%d个段，具有%s（%s内存）',\n\t\t'start' => '开始时间',\n\t\t'uptime' => '运行时间',\n\t\t'upload' => '文件上传支持',\n\t\t'cachetitle' => '缓存信息',\n\t\t'cvar' => '缓存的变量',\n\t\t'hit' => '命中',\n\t\t'miss' => '未命中',\n\t\t'reqrate' => '请求率（命中，未命中）',\n\t\t'creqsec' => '缓存请求/秒',\n\t\t'hitrate' => '命中率',\n\t\t'missrate' => '未命中率',\n\t\t'insrate' => '插入率',\n\t\t'cachefull' => '缓存满计数',\n\t\t'runtime' => '运行时设置',\n\t\t'memnote' => '内存使用',\n\t\t'total' => '总计',\n\t\t'free' => '空闲',\n\t\t'used' => '已用',\n\t\t'hitmiss' => '命中和未命中',\n\t\t'detailmem' => '详细的内存使用和碎片',\n\t\t'fragment' => '碎片化',\n\t\t'nofragment' => '无碎片',\n\t\t'fragments' => '碎片',\n\t],\n\t'apikeys' => [\n\t\t'no_api_keys' => '未找到API密钥',\n\t\t'key_add' => '添加新密钥',\n\t\t'apikey_removed' => 'ID为#%s的API密钥已成功删除',\n\t\t'apikey_added' => '已成功生成新的API密钥',\n\t\t'clicktoview' => '点击查看',\n\t\t'allowed_from' => '允许来源',\n\t\t'allowed_from_help' => '以逗号分隔的IP地址/网络列表。<br>默认为空（允许所有人）。',\n\t\t'valid_until' => '有效期至',\n\t\t'valid_until_help' => '有效日期，格式为YYYY-MM-DDThh:mm',\n\t],\n\t'changepassword' => [\n\t\t'old_password' => '旧密码',\n\t\t'new_password' => '新密码',\n\t\t'new_password_confirm' => '确认密码',\n\t\t'new_password_ifnotempty' => '新密码（留空则不更改）',\n\t\t'also_change_ftp' => '同时更改主FTP账户的密码',\n\t\t'also_change_stats' => '同时更改统计页面的密码',\n\t\t'also_change_global_mysql' => '同时更改全局MySQL账户的密码',\n\t],\n\t'cron' => [\n\t\t'cronname' => '定时任务名称',\n\t\t'lastrun' => '上次运行',\n\t\t'interval' => '间隔',\n\t\t'isactive' => '已启用',\n\t\t'description' => '描述',\n\t\t'changewarning' => '更改这些值可能会对froxlor及其自动化任务的行为产生负面影响。<br />请仅在您确定知道自己在做什么的情况下更改此处的值。',\n\t],\n\t'crondesc' => [\n\t\t'cron_unknown_desc' => '未给出描述',\n\t\t'cron_tasks' => '生成配置文件',\n\t\t'cron_legacy' => '旧版定时任务',\n\t\t'cron_traffic' => '流量计算',\n\t\t'cron_usage_report' => 'Web和流量报告',\n\t\t'cron_mailboxsize' => '邮箱容量计算',\n\t\t'cron_letsencrypt' => 'Let\\'s Encrypt证书更新',\n\t\t'cron_export' => '处理数据导出作业',\n\t\t'cron_backup' => '处理系统和客户备份作业',\n\t],\n\t'cronjob' => [\n\t\t'cronjobsettings' => '定时任务设置',\n\t\t'cronjobintervalv' => '运行间隔值',\n\t\t'cronjobinterval' => '运行间隔',\n\t],\n\t'cronjobs' => [\n\t\t'notyetrun' => '尚未运行',\n\t],\n\t'cronmgmt' => [\n\t\t'minutes' => '分钟',\n\t\t'hours' => '小时',\n\t\t'days' => '天',\n\t\t'weeks' => '周',\n\t\t'months' => '个月',\n\t],\n\t'customer' => [\n\t\t'documentroot' => '主目录',\n\t\t'name' => '名称',\n\t\t'firstname' => '名字',\n\t\t'lastname' => '姓氏',\n\t\t'company' => '公司',\n\t\t'nameorcompany_desc' => '名字/姓氏或公司是必需的',\n\t\t'street' => '街道',\n\t\t'zipcode' => '邮政编码',\n\t\t'city' => '城市',\n\t\t'phone' => '电话',\n\t\t'fax' => '传真',\n\t\t'email' => '邮箱',\n\t\t'customernumber' => '客户ID',\n\t\t'diskspace' => '网站空间',\n\t\t'traffic' => '流量',\n\t\t'mysqls' => 'MySQL数据库',\n\t\t'emails' => '邮箱地址',\n\t\t'accounts' => '邮箱账户',\n\t\t'forwarders' => '邮件转发',\n\t\t'ftps' => 'FTP账户',\n\t\t'subdomains' => '子域名',\n\t\t'domains' => '域名',\n\t\t'mib' => 'MiB',\n\t\t'gib' => 'GiB',\n\t\t'title' => '标题',\n\t\t'country' => '国家',\n\t\t'email_quota' => '邮箱配额',\n\t\t'email_imap' => '邮箱IMAP',\n\t\t'email_pop3' => '邮箱POP3',\n\t\t'sendinfomail' => '通过邮箱发送数据给我',\n\t\t'generated_pwd' => '密码建议',\n\t\t'usedmax' => '已用/最大',\n\t\t'services' => '服务',\n\t\t'letsencrypt' => [\n\t\t\t'title' => '使用Let\\'s Encrypt',\n\t\t\t'description' => '从<a href=\"https://letsencrypt.org\">Let\\'s Encrypt</a>获取免费证书。证书将自动创建和续订。',\n\t\t],\n\t\t'selectserveralias_addinfo' => '此选项可在编辑域名时设置。其初始值继承自父域名。',\n\t\t'total_diskspace' => '总磁盘空间',\n\t\t'mysqlserver' => '可用的MySQL服务器',\n\t],\n\t'diskquota' => '配额',\n\t'antispam' => [\n\t\t'config_file' => [\n\t\t\t'title' => '反垃圾邮件设置文件',\n\t\t\t'description' => '请指定邮件反垃圾邮件规则的文件名',\n\t\t],\n\t\t'reload_command' => [\n\t\t\t'title' => 'Milter重启命令',\n\t\t\t'description' => '请指定rspamd服务的重启命令',\n\t\t],\n\t\t'activated' => [\n\t\t\t'title' => '启用反垃圾邮件？',\n\t\t\t'description' => '您想使用rspamd作为反垃圾邮件服务吗？',\n\t\t],\n\t\t'dkim_keylength' => [\n\t\t\t'title' => 'DKIM密钥长度',\n\t\t\t'description' => '注意：更改仅适用于新密钥<br/><br/>需要域名的特定DNS条目。如果您不使用名称服务器功能，则必须手动管理这些条目。',\n\t\t],\n\t\t'spam_tag_level' => [\n\t\t\t'title' => '垃圾邮件标记级别',\n\t\t\t'description' => '将邮件标记为垃圾邮件所需的分数<br/>默认值：7.0'\n\t\t],\n\t\t'rewrite_subject' => [\n\t\t\t'title' => '重写主题',\n\t\t\t'description' => '是否在邮件主题中添加<strong>***SPAM***</strong>（如适用）',\n\t\t],\n\t\t'spam_kill_level' => [\n\t\t\t'title' => '垃圾邮件拦截级别',\n\t\t\t'description' => '完全丢弃邮件所需的分数<br/>默认值：14.0'\n\t\t],\n\t\t'bypass_spam' => [\n\t\t\t'title' => '绕过垃圾邮件过滤器',\n\t\t\t'description' => '启用以绕过/禁用此地址的垃圾邮件过滤。<br/>默认值：否'\n\t\t],\n\t\t'policy_greylist' => [\n\t\t\t'title' => '使用灰名单',\n\t\t\t'description' => '传入的邮件将受到<a href=\"https://en.wikipedia.org/wiki/Greylisting_(email)\" target=\"_blank\">灰名单</a>保护。<br/>默认值：是'\n\t\t],\n\t\t'required_spf_dns' => '需要SPF DNS条目',\n\t\t'required_dmarc_dns' => '需要DMARC DNS条目',\n\t\t'required_dkim_dns' => '需要DKIM DNS条目',\n\t\t'default_select' => [\n\t\t\t'on_changeable' => '启用，可调整',\n\t\t\t'off_changeable' => '停用，可调整',\n\t\t\t'on_unchangeable' => '启用，不可调整',\n\t\t\t'off_unchangeable' => '停用，不可调整',\n\t\t],\n\t\t'default_bypass_spam' => [\n\t\t\t'title' => '绕过垃圾邮件过滤器默认值',\n\t\t\t'description' => '新邮件账户是否默认启用\"绕过垃圾邮件过滤器\"，以及此设置是否可由客户调整。<br/>默认值：停用，可调整'\n\t\t],\n\t\t'default_spam_rewrite_subject' => [\n\t\t\t'title' => '重写主题默认值',\n\t\t\t'description' => '新邮件账户是否默认启用\"重写主题\"，以及此设置是否可由客户调整。<br/>默认值：启用，可调整'\n\t\t],\n\t\t'default_policy_greylist' => [\n\t\t\t'title' => '使用灰名单默认值',\n\t\t\t'description' => '新邮件账户是否默认启用\"使用灰名单\"，以及此设置是否可由客户调整。<br/>默认值：启用，可调整'\n\t\t],\n\t],\n\t'dns' => [\n\t\t'destinationip' => '域名IP地址',\n\t\t'standardip' => '服务器标准IP',\n\t\t'a_record' => 'A记录（IPv6可选）',\n\t\t'cname_record' => 'CNAME记录',\n\t\t'mxrecords' => '定义MX记录',\n\t\t'standardmx' => '服务器标准MX记录',\n\t\t'mxconfig' => '自定义MX记录',\n\t\t'priority10' => '优先级10',\n\t\t'priority20' => '优先级20',\n\t\t'txtrecords' => '定义TXT记录',\n\t\t'txtexample' => '示例（SPF条目）：<br />v=spf1 ip4:xxx.xxx.xx.0/23 -all',\n\t\t'howitworks' => '在这里您可以管理域名的DNS记录。请注意，froxlor将自动为您生成NS/MX/A/AAAA记录。自定义条目优先，只有缺失的条目才会自动生成。',\n\t\t'nis2note' => [\n\t\t\t'title' => 'NIS2信息',\n\t\t\t'content' => 'DNS托管/权威DNS服务可能被视为一种数字服务，根据<strong>EU-NIS2</strong>，其安全性和报告义务有所增加。请检查您的设置是否受NIS2影响以及需要采取哪些措施。'\n\t\t],\n\t],\n\t'dnseditor' => [\n\t\t'edit' => '编辑DNS',\n\t\t'records' => '记录',\n\t\t'notes' => [\n\t\t\t'A' => '32位IPv4地址，用于将主机名映射到主机的IP地址。',\n\t\t\t'AAAA' => '128位IPv6地址，用于将主机名映射到主机的IP地址。',\n\t\t\t'CAA' => 'CAA资源记录允许DNS域名持有者指定一个或多个授权为该域名颁发证书的证书颁发机构（CA）。<br>结构：<code>flag tag[issue|issuewild|iodef|contactmail|contactphone] value</code><br>示例：<code>0 issue \"ca.example.net\"<br>0 iodef \"mailto:security@example.com\"</code>',\n\t\t\t'CNAME' => '域名的别名，DNS查找将通过使用新名称重试查找来继续。仅适用于子域名！',\n\t\t\t'DNAME' => '为域名树的整个子树创建别名',\n\t\t\t'LOC' => '域名的地理位置信息。<br>结构：<code>( d1 [m1 [s1]] {\"N\"|\"S\"} d2 [m2 [s2]] {\"E\"|\"W\"} alt[\"m\"] [siz[\"m\"] [hp[\"m\"] [vp[\"m\"]]]] )</code><br>描述：<code>d1: [0..90] (纬度度数), d2: [0..180] (经度度数), m1,m2: [0..59] (分), s1,s2: [0..59.999] (秒), alt: 海拔高度(米)</code><br>示例：<code>52 22 23.000 N 4 53 32.000 E -2.00m 0.00m 10000m 10m</code>',\n\t\t\t'MX' => '邮件交换记录，将域名映射到该域的邮件服务器。<br>示例：<code>10 mail.example.com</code><br>注：对于优先级，请使用上面的字段',\n\t\t\t'NS' => '委派DNS区域以使用给定的权威名称服务器。',\n\t\t\t'RP' => '负责人记录<br>结构：<code>邮箱[将@替换为点] txt-record-name</code><br>示例：<code>team.froxlor.org. froxlor.org.</code>',\n\t\t\t'SRV' => '服务位置记录，用于较新的协议，而不是创建特定于协议的记录，如MX。<br>结构：<code>优先级 权重 端口 目标</code><br>示例：<code>0 5 5060 sipserver.example.com.</code><br>注：对于优先级，请使用上面的字段',\n\t\t\t'SSHFP' => 'SSHFP资源记录用于在DNS中发布安全外壳（SSH）密钥指纹。<br>结构：<code>算法 类型 指纹</code><br>算法：<code>0：保留，1：RSA，2：DSA，3：ECDSA，4：Ed25519，6：Ed448</code><br>类型：<code>0：保留，1：SHA-1，2：SHA-256</code><br>示例：<code>2 1 123456789abcdef67890123456789abcdef67890</code>',\n\t\t\t'TLSA' => 'TLSA（TLS认证）记录用于发布TLS/SSL证书的指纹。它通常用于DANE。<br>只有在您的域名上启用了DNSSEC，TLSA记录才能被信任。<br>结构：<code>使用 选择器 类型 指纹</code><br>证书用法：<code>0：PKIX-T，1：PKIX-EE，2：DANE-TA，3：DANE-EE</code><br>选择器：<code>0：使用完整证书，1：使用主题公钥</code><br>匹配类型：<code>0：完整：无哈希，1：SHA-256哈希，2：SHA-512哈希</code><br>示例：<code>3 1 1 123456789abcdef67890123456789abcdef123456789abcdef123456789abcde</code>',\n\t\t\t'TXT' => '自由定义的描述性文本。'\n\t\t]\n\t],\n\t'domain' => [\n\t\t'openbasedirpath' => 'OpenBasedir路径',\n\t\t'inherited' => '与父域名相同',\n\t\t'docroot' => '从上方字段开始的路径',\n\t\t'homedir' => '主目录',\n\t\t'docparent' => '上方字段中路径的父目录',\n\t\t'ssl_certificate_placeholder' => '---- 开始证书----' . PHP_EOL . '[...]' . PHP_EOL . '----结束证书----',\n\t\t'ssl_key_placeholder' => '---- 开始RSA私钥----' . PHP_EOL . '[...]' . PHP_EOL . '-----结束RSA私钥-----',\n\t],\n\t'domains' => [\n\t\t'description' => '在这里，您可以创建（子）域并更改其路径。<br />每次更改后，系统都需要一些时间来应用新设置。',\n\t\t'domainsettings' => '域设置',\n\t\t'domainname' => '域名',\n\t\t'subdomain_add' => '创建子域名',\n\t\t'subdomain_edit' => '编辑（子）域',\n\t\t'wildcarddomain' => '创建为通配符域？',\n\t\t'aliasdomain' => '别名域名',\n\t\t'noaliasdomain' => '没有别名域',\n\t\t'hasaliasdomains' => '具有别名域',\n\t\t'statstics' => '使用统计',\n\t\t'isassigneddomain' => '已分配域',\n\t\t'add_date' => '添加至froxlor',\n\t\t'registration_date' => '添加到注册表',\n\t\t'topleveldomain' => '顶级域名',\n\t\t'associated_with_domain' => '关联',\n\t\t'aliasdomains' => '别名域名',\n\t\t'redirectifpathisurl' => '重定向代码（默认：空）',\n\t\t'redirectifpathisurlinfo' => '仅当您输入URL作为路径时才需要选择其中之一<br/><strong class=\"text-danger\">注意：</strong>仅当给定路径是URL时才应用更改。',\n\t\t'ipandport_multi' => [\n\t\t\t'title' => 'IP地址',\n\t\t\t'description' => '为域名指定一个或多个IP地址。<br /><br /><div class=\"text-danger\">注意：当域名配置为另一个域名的<strong>别名域名</strong>时，无法更改IP地址。</div>',\n\t\t],\n\t\t'ipandport_ssl_multi' => [\n\t\t\t'title' => 'SSL IP地址',\n\t\t],\n\t\t'ssl_redirect' => [\n\t\t\t'title' => 'SSL重定向',\n\t\t\t'description' => '此选项为非SSL虚拟主机创建重定向，以便将所有请求重定向到SSL虚拟主机。<br /><br />例如，对<strong>http</strong>://domain.tld/的请求将重定向到<strong>https</strong>://domain.tld/',\n\t\t],\n\t\t'serveraliasoption_wildcard' => '通配符（*.domain.tld）',\n\t\t'serveraliasoption_www' => '万维网（www.domain.tld）',\n\t\t'serveraliasoption_none' => '没有别名',\n\t\t'domain_import' => '导入域名',\n\t\t'import_separator' => '分隔符',\n\t\t'import_offset' => '偏移',\n\t\t'import_file' => 'CSV文件',\n\t\t'import_description' => '有关导入文件结构和如何成功导入的详细信息，请访问<a href=\"https://docs.froxlor.org/latest/admin-guide/domain-import/\" target=\"_blank\" class=\"alert-link\">https://docs.froxlor.org/latest/admin-guide/domain-import/</a>',\n\t\t'ssl_redirect_temporarilydisabled' => '<br>SSL重定向在生成新的Let\\'s Encrypt证书时暂时停用。它将在生成证书后再次激活。',\n\t\t'termination_date' => '终止日期',\n\t\t'termination_date_overview' => '终止于',\n\t\t'ssl_certificates' => 'SSL证书',\n\t\t'ssl_certificate_removed' => '已成功删除ID为#%s的证书',\n\t\t'ssl_certificate_error' => '读取域名%s的证书时出错',\n\t\t'no_ssl_certificates' => '没有配置SSL证书的域名',\n\t\t'isaliasdomainof' => '是%s的别名域',\n\t\t'isbinddomain' => '创建DNS区域',\n\t\t'dkimenabled' => '已启用DKIM',\n\t\t'openbasedirenabled' => '启用Openbasedir限制',\n\t\t'hsts' => '启用HSTS',\n\t\t'aliasdomainid' => '别名域ID',\n\t\t'nodomainsassignedbyadmin' => '您的帐户当前没有分配（活动）域。如果您认为这是错误的，请与管理员联系。',\n\t\t'email_only' => '仅限邮件',\n\t],\n\t'emails' => [\n\t\t'description' => '在这里，您可以创建和更改您的电子邮件地址。<br />账户就像你家门口的信箱，如果有人给你发了邮件，它就会被放进账户里。<br /><br />要下载您的电子邮件，请在邮件程序中使用以下设置：（斜体的数据<i></i>必须更改为您键入的等效值！）<br />主机名：<b><i>域名</i></b><br />用户名：<b><i>帐户名称/电子邮件地址</i></b><br />密码：<b><i>您选择的密码</i></b>',\n\t\t'emailaddress' => '电子邮件地址',\n\t\t'emails_add' => '创建电子邮件地址',\n\t\t'emails_edit' => '编辑电子邮件地址',\n\t\t'catchall' => '全局收件箱',\n\t\t'iscatchall' => '定义为全局收件箱？',\n\t\t'account' => '账户',\n\t\t'account_add' => '创建帐户',\n\t\t'account_delete' => '删除帐户',\n\t\t'from' => '发件人',\n\t\t'to' => '收件人',\n\t\t'forwarders' => '邮件转发',\n\t\t'forwarder_add' => '添加邮件转发',\n\t\t'alternative_emailaddress' => '备用电子邮件地址',\n\t\t'quota' => '配额',\n\t\t'noquota' => '无配额',\n\t\t'updatequota' => '更新配额',\n\t\t'quota_edit' => '更改电子邮件地址',\n\t\t'noemaildomainaddedyet' => '您的帐户中还没有（电子邮件-）域。',\n\t\t'back_to_overview' => '返回到域名概览',\n\t\t'accounts' => '邮箱账户',\n\t\t'emails' => '邮箱',\n\t\t'senders' => '允许的发件人',\n\t\t'sender_add' => '添加允许的发件人',\n\t\t'foreign_sender' => '允许的（外部）发送方',\n\t\t'allowed_sender_info' => '使用允许的<strong>发件人</strong>，您允许现有的电子邮件帐户发送具有不同发件人地址的电子邮件。<br><strong>重要：</strong>此处输入的地址/通配符域不会自动成为邮箱-它仅用作附加的允许发件人标识符。',\n\t],\n\t'error' => [\n\t\t'error' => '错误',\n\t\t'directorymustexist' => '目录%s必须存在。请使用FTP客户端创建它。',\n\t\t'filemustexist' => '文件%s必须存在。',\n\t\t'allresourcesused' => '你已经用尽了所有的资源。',\n\t\t'domains_cantdeletemaindomain' => '您不能删除已分配的域。',\n\t\t'domains_canteditdomain' => '您无法编辑此域。它已被管理员禁用。',\n\t\t'domains_cantdeletedomainwithemail' => '您不能删除用作电子邮件域的域。请先删除所有电子邮件地址。',\n\t\t'firstdeleteallsubdomains' => '您必须先删除所有子域，然后才能创建一个DNS域。',\n\t\t'youhavealreadyacatchallforthisdomain' => '您已经为此域定义了一个总括。',\n\t\t'ftp_cantdeletemainaccount' => '您无法删除您的主FTP帐户',\n\t\t'login' => '您输入的用户名或密码不正确，请重新输入！',\n\t\t'login_blocked' => '此帐户已暂停，因为太多的登录错误。<br />请在%s秒后重试。',\n\t\t'notallreqfieldsorerrors' => '您没有填写全部或部分字段填写错误。',\n\t\t'oldpasswordnotcorrect' => '旧密码不正确。',\n\t\t'youcantallocatemorethanyouhave' => '你不能分配比你自己拥有的更多的资源。',\n\t\t'mustbeurl' => '您没有键入有效或完整的URL（例如http：//somedomain.com/error404.htm）',\n\t\t'invalidpath' => '你没有选择一个有效的URL（可能是目录列表有问题？）',\n\t\t'stringisempty' => '字段中缺少输入',\n\t\t'stringiswrong' => '字段输入错误',\n\t\t'newpasswordconfirmerror' => '新密码和确认不匹配',\n\t\t'mydomain' => '域名',\n\t\t'mydocumentroot' => '\\'Documentroot\\'',\n\t\t'loginnameexists' => '登录名%s已存在',\n\t\t'emailiswrong' => '电子邮件地址%s包含无效字符或不完整',\n\t\t'emailexists' => '电子邮件地址%s已被其他管理员使用',\n\t\t'emailexistsanon' => '电子邮件地址%s已在使用中。',\n\t\t'alternativeemailiswrong' => '要将凭据发送到的给定备用电子邮件地址%s似乎无效',\n\t\t'loginnameiswrong' => '登录名\"%s\"包含非法字符。',\n\t\t'loginnameiswrong2' => '登录名包含太多字符。只允许%s个字符。',\n\t\t'userpathcombinationdupe' => '用户名和路径的组合已存在',\n\t\t'patherror' => '页面不能为空！',\n\t\t'errordocpathdupe' => '路径%s的选项已存在',\n\t\t'adduserfirst' => '请先创建客户',\n\t\t'domainalreadyexists' => '域%s已分配给客户',\n\t\t'nolanguageselect' => '未选择语言。',\n\t\t'nosubjectcreate' => '您必须为此邮件模板定义一个主题。',\n\t\t'nomailbodycreate' => '必须为此邮件模板定义邮件文本。',\n\t\t'templatenotfound' => '未找到模板。',\n\t\t'alltemplatesdefined' => '您不能定义更多的模板，所有语言都已支持。',\n\t\t'wwwnotallowed' => 'www不允许用于子域名。',\n\t\t'subdomainiswrong' => '子域%s包含无效字符。',\n\t\t'domaincantbeempty' => '域名不能为空。',\n\t\t'domainexistalready' => '域%s已存在。',\n\t\t'domainisaliasorothercustomer' => '所选别名域本身就是别名域、具有不同的IP/端口组合或属于另一个客户。',\n\t\t'emailexistalready' => '电子邮件地址%s已存在。',\n\t\t'maindomainnonexist' => '主域%s不存在。',\n\t\t'maindomaindeactivated' => '主域%s已停用。',\n\t\t'destinationnonexist' => '请在“目的地”字段中创建您的货运代理。',\n\t\t'destinationalreadyexistasmail' => '到%s的转发器已作为活动电子邮件地址存在。',\n\t\t'destinationalreadyexist' => '您已经定义了到\"%s\"的转发器。',\n\t\t'destinationiswrong' => '转发程序%s包含无效字符或不完整。',\n\t\t'dumpfoldercannotbedocroot' => '数据转储文件夹不能是您的主目录，请在主目录中选择一个文件夹，例如/dumps',\n\t\t'templatelanguagecombodefined' => '已定义选定的语言/模板组合。',\n\t\t'templatelanguageinvalid' => '所选语言不存在',\n\t\t'ipstillhasdomains' => '您要删除的IP/端口组合仍有分配给它的域，请在删除此IP/端口组合之前将其重新分配给其他IP/端口组合。',\n\t\t'cantdeletedefaultip' => '您无法删除默认IP/端口组合，请在删除此IP/端口组合之前将另一个IP/端口组合设为默认。',\n\t\t'cantdeletesystemip' => '您无法删除最后一个系统IP，请为系统IP创建新的IP/端口组合或更改系统IP。',\n\t\t'myipaddress' => '\\'IP\\'',\n\t\t'myport' => '\\'端口\\'',\n\t\t'myipdefault' => '您需要选择应成为默认的IP/端口组合。',\n\t\t'myipnotdouble' => '此IP/端口组合已存在。',\n\t\t'admin_domain_emailsystemhostname' => 'server-hostname不能用作customer-domain。',\n\t\t'cantchangesystemip' => '您无法更改最后一个系统IP，请为系统IP创建另一个新的IP/端口组合或更改系统IP。',\n\t\t'sessiontimeoutiswrong' => '只允许数字\"会话超时\"。',\n\t\t'maxloginattemptsiswrong' => '只允许数字\"最大登录尝试次数\"。',\n\t\t'deactivatetimiswrong' => '只允许数字\"停用时间\"。',\n\t\t'accountprefixiswrong' => '\"客户前缀\"错误。',\n\t\t'mysqlprefixiswrong' => '\"SQL前缀\"错误。',\n\t\t'ftpprefixiswrong' => '\"FTP前缀\"错误。',\n\t\t'ipiswrong' => '\"IP地址\"错误。只允许有效的IP地址。',\n\t\t'vmailuidiswrong' => '\"邮件UID\"错误。只允许数字UID。',\n\t\t'vmailgidiswrong' => '\"邮件GID\"错误。只允许数字GID。',\n\t\t'adminmailiswrong' => '\"发件人地址\"错误。只允许有效的电子邮件地址。',\n\t\t'pagingiswrong' => '\"每页条目数\"值错误。只允许数字字符。',\n\t\t'phpmyadminiswrong' => 'phpMyAdmin-link不是有效的链接。“',\n\t\t'webmailiswrong' => 'webmail-link不是有效的链接。',\n\t\t'webftpiswrong' => 'WebFTP链接不是有效链接。',\n\t\t'stringformaterror' => '字段\"%s\"的值格式不正确。',\n\t\t'loginnameisusingprefix' => '您不能创建以\"%s\"开头的帐户，因为此前缀已设置为用于自动帐户命名。请输入其他帐户名。',\n\t\t'loginnameissystemaccount' => '帐户\"%s\"在系统上已存在，无法使用。请输入其他帐户名。',\n\t\t'loginnameisreservedname' => '帐户名\"%s\"是为系统内部保留的，无法使用。',\n\t\t'youcantdeleteyourself' => '出于安全原因，您无法删除自己。',\n\t\t'youcanteditallfieldsofyourself' => '注意：出于安全原因，您无法编辑自己帐户的所有字段。',\n\t\t'documentrootexists' => '目录\"%s\"已为此客户存在。请在再次添加客户之前将其删除。',\n\t\t'norepymailiswrong' => '\"无回复地址\"错误。只允许有效的电子邮件地址。',\n\t\t'logerror' => '日志错误：%s',\n\t\t'nomessagetosend' => '您没有输入消息。',\n\t\t'norecipientsgiven' => '您未指定任何收件人',\n\t\t'errorsendingmail' => '发送到\"%s\"的消息失败',\n\t\t'errorsendingmailpub' => '发送到给定电子邮件地址的消息失败',\n\t\t'cannotreaddir' => '无法读取目录\"%s\"',\n\t\t'invalidip' => '无效IP地址：%s',\n\t\t'invalidmysqlhost' => 'MySQL主机地址无效：%s',\n\t\t'cannotuseawstatsandwebalizeratonetime' => '您不能同时启用Webalizer和AWstats，请选择其中一个',\n\t\t'cannotwritetologfile' => '无法打开日志文件%s进行写入',\n\t\t'vmailquotawrong' => '定额必须为正数。',\n\t\t'allocatetoomuchquota' => '您尝试分配%s MB内存，但剩余内存不足。',\n\t\t'missingfields' => '未填写所有必填字段。',\n\t\t'requiredfield' => '此字段为必填字段。',\n\t\t'accountnotexisting' => '指定的电子邮件帐户不存在。',\n\t\t'nopermissionsorinvalidid' => '您没有足够的权限更改这些设置，或者提供的ID无效。',\n\t\t'phpsettingidwrong' => '此ID的PHP配置不存在',\n\t\t'descriptioninvalid' => '描述太短、太长或包含非法字符。',\n\t\t'info' => '信息',\n\t\t'filecontentnotset' => '档案不能为空！',\n\t\t'customerdoesntexist' => '您选择的客户不存在。',\n\t\t'admindoesntexist' => '您所选择的管理员不存在。',\n\t\t'ipportdoesntexist' => '您选择的IP/端口组合不存在。',\n\t\t'usernamealreadyexists' => '用户名%s已存在。',\n\t\t'plausibilitychecknotunderstood' => '无法理解可解释性检查的答案。',\n\t\t'errorwhensaving' => '保存字段%s时出错',\n\t\t'hiddenfieldvaluechanged' => '编辑设置时，隐藏字段\"%s\"的值发生了变化。<br /><br />这通常不是大问题，但因此无法保存设置。',\n\t\t'notrequiredpasswordlength' => '给定的密码太短。请至少输入%s个字符。',\n\t\t'overviewsettingoptionisnotavalidfield' => '哎呀，一个应该在设置概述中显示为选项的字段不是一个例外类型。你可以为此责怪开发人员。这不应该发生！',\n\t\t'pathmaynotcontaincolon' => '您输入的路径不应包含冒号(\":\"请输入正确的路径值。)',\n\t\t'invaliddocumentrooturl' => '请输入正确的URL或unix-path。请输入正确的URL或unix-path。',\n\t\t'exception' => '%s',\n\t\t'notrequiredpasswordcomplexity' => '不满足指定的密码复杂性。<br />如果您对复杂性规范有任何疑问，请与管理员联系',\n\t\t'invaliderrordocumentvalue' => '作为ErrorDocument给出的值似乎不是有效的文件、URL或字符串。',\n\t\t'intvaluetoolow' => '给定的数字太小（字段%s）',\n\t\t'intvaluetoohigh' => '给定的数字太高（字段%s）',\n\t\t'phpfpmstillenabled' => 'PHP-FPM当前处于活动状态。请在激活FCGID之前将其停用',\n\t\t'fcgidstillenabled' => 'FCGID当前处于活动状态。请在激活PHP-FPM之前将其停用',\n\t\t'domains_cantdeletedomainwithaliases' => '您不能删除用于别名域的域。您必须首先删除别名。',\n\t\t'user_banned' => '您的帐户已被锁定。有关详细信息，请与管理员联系。',\n\t\t'session_timeout' => '值过低',\n\t\t'session_timeout_desc' => '您不应将会话超时设置为低于1分钟。',\n\t\t'invalidhostname' => '主机名必须是有效的域。它不能为空，也不能仅由空格组成',\n\t\t'operationnotpermitted' => '不允许操作！',\n\t\t'featureisdisabled' => '功能%s已禁用。请与您的服务提供商联系。',\n\t\t'usercurrentlydeactivated' => '用户%s当前已停用',\n\t\t'setlessthanalreadyused' => '您不能设置比此用户已使用的资源少的\\'%s\\'资源<br />',\n\t\t'stringmustntbeempty' => '字段%s的值不能为空',\n\t\t'sslcertificateismissingprivatekey' => '您需要为证书指定私钥',\n\t\t'sslcertificatewrongdomain' => '给定的证书不属于此域',\n\t\t'sslcertificateinvalidcert' => '给定的证书内容似乎不是有效的证书',\n\t\t'sslcertificateinvalidcertkeypair' => '给定私钥不属于给定证书',\n\t\t'sslcertificateinvalidca' => '给定的CA证书数据似乎不是有效的证书',\n\t\t'sslcertificateinvalidchain' => '给定的证书链数据似乎不是有效的证书',\n\t\t'givendirnotallowed' => '不允许字段%s中的给定目录。',\n\t\t'sslredirectonlypossiblewithsslipport' => '只有当域至少分配了一个启用ssl的IP/端口组合时，才可以使用Let\\'s Encrypt。',\n\t\t'fcgidstillenableddeadlock' => 'FCGID目前处于活动状态。<br />请在切换到Apache2以外的其他Web服务器之前将其停用',\n\t\t'send_report_title' => '发送错误报告',\n\t\t'send_report_desc' => '感谢您报告此错误并帮助我们改进froxlor。<br />这是将发送给froxlor开发团队的电子邮件：',\n\t\t'send_report' => '发送报告',\n\t\t'send_report_error' => '发送报告时出错：<br />%s的文件',\n\t\t'notallowedtouseaccounts' => '您的帐户不允许使用IMAP/POP 3。您无法添加电子邮件帐户。',\n\t\t'cannotdeletehostnamephpconfig' => '此PHP配置由froxlor-vhost使用，无法删除。',\n\t\t'cannotdeletedefaultphpconfig' => '此PHP配置设置为默认值，无法删除。',\n\t\t'passwordshouldnotbeusername' => '密码不应与用户名相同。',\n\t\t'no_phpinfo' => '抱歉，无法读取phpinfo（）',\n\t\t'moveofcustomerfailed' => '将客户移动到选定的管理员/代理失败。请记住，在此阶段已成功应用对客户的所有其他更改。<br><br>错误消息：%s',\n\t\t'domain_import_error' => '导入域时出现以下错误：%s',\n\t\t'fcgidandphpfpmnogoodtogether' => 'FCGID和PHP-FPM不能同时激活',\n\t\t'no_apcuinfo' => '没有可用的缓存信息。APCu似乎没有运行。',\n\t\t'no_opcacheinfo' => '没有可用的OPCache信息。OPCache似乎未加载。',\n\t\t'inactive_opcacheinfo' => 'OPCache似乎已安装但未激活。',\n\t\t'nowildcardwithletsencrypt' => 'Let\\'s Encrypt无法在froxlor中使用ACME处理通配符域（需要dns-challenge），对不起。请将服务器设置为WWW或完全禁用它',\n\t\t'customized_version' => '您的froxlor安装似乎已被修改，不支持抱歉。',\n\t\t'autoupdate_0' => '未知错误',\n\t\t'autoupdate_1' => 'PHP设置allow_url_fopen被禁用。Autoupdate需要在php.ini中启用此设置',\n\t\t'autoupdate_2' => 'PHP zip扩展未找到，请确保已安装并激活',\n\t\t'autoupdate_4' => 'froxlor存档无法存储到磁盘：（',\n\t\t'autoupdate_5' => 'version.froxlor.org返回了不可接受的值：（',\n\t\t'autoupdate_6' => '哎呀，没有（有效的）版本给下载：（',\n\t\t'autoupdate_7' => '无法找到下载的存档：（',\n\t\t'autoupdate_8' => '无法解压缩存档：（',\n\t\t'autoupdate_9' => '下载的文件未通过完整性检查。请尝试再次更新。',\n\t\t'autoupdate_10' => '最低支持的PHP版本为7.4.0',\n\t\t'autoupdate_11' => 'Webupdate已禁用',\n\t\t'mailaccistobedeleted' => '当前正在删除另一个同名帐户（%s），因此此时无法添加。',\n\t\t'customerhasongoingexportjob' => '已经有一个数据导出作业等待处理，请耐心等待。',\n\t\t'exportfunctionnotenabled' => '未启用导出功能',\n\t\t'dns_domain_nodns' => '未为此域启用DNS',\n\t\t'dns_content_empty' => '没有给出内容',\n\t\t'dns_content_invalid' => 'DNS内容无效',\n\t\t'dns_arec_noipv4' => '没有给出A记录的有效IP地址',\n\t\t'dns_aaaarec_noipv6' => '没有有效的IP地址为AAA-记录给出',\n\t\t'dns_mx_prioempty' => '给定的MX优先级无效',\n\t\t'dns_mx_needdom' => 'MX内容值必须是有效的域名',\n\t\t'dns_mx_noalias' => 'MX-content值不能是CNAME条目。',\n\t\t'dns_cname_invaliddom' => 'CNAME记录的域名无效',\n\t\t'dns_cname_nomorerr' => '已存在具有相同记录名的资源记录。它不能用作CNAME。',\n\t\t'dns_other_nomorerr' => '已经存在具有相同记录名的CNAME记录。它不能用于其他类型。',\n\t\t'dns_ns_invaliddom' => 'NS记录的域名无效',\n\t\t'dns_srv_prioempty' => '给定的SRV优先级无效',\n\t\t'dns_srv_invalidcontent' => 'SRV内容无效，必须包含字段weight、port和target，例如：5 5060 sipserver.example.com。',\n\t\t'dns_srv_needdom' => 'SRV目标值必须是有效的域名',\n\t\t'dns_srv_noalias' => 'SRV-target值不能是CNAME条目。',\n\t\t'dns_duplicate_entry' => '记录已存在',\n\t\t'dns_notfoundorallowed' => '未找到或没有权限',\n\t\t'domain_nopunycode' => '您不能指定punycode（IDNA）。域名将自动转换',\n\t\t'domain_noipaddress' => '无法将IP地址添加为域',\n\t\t'dns_record_toolong' => '记录/标签最多只能包含63个字符',\n\t\t'noipportgiven' => '未提供IP/端口',\n\t\t'nosslippportgiven' => '启用SSL时，您需要选择SSL IP/端口',\n\t\t'jsonextensionnotfound' => '此功能需要php json-extension。',\n\t\t'cannotdeletesuperadmin' => '无法删除第一个管理员。',\n\t\t'no_wwwcnamae_ifwwwalias' => '无法为\"www\"设置CNAME记录，因为该域名已设置为生成www别名。请将设置更改为\"无别名\"或\"通配符别名\"',\n\t\t'local_group_exists' => '系统上已存在给定的组。',\n\t\t'local_group_invalid' => '给定的组名无效',\n\t\t'local_user_invalid' => '指定的用户名无效或不存在',\n\t\t'local_user_isfroxloruser' => '给定的用户名是froxlor管理的用户名，不能在此上下文中使用',\n\t\t'invaliddnsforletsencrypt' => '域DNS不包括任何选定的IP地址。无法生成Let\\'s Encrypt证书。',\n\t\t'notallowedphpconfigused' => '尝试使用未分配给客户的php-config',\n\t\t'pathmustberelative' => '用户没有权限指定客户主目录以外的目录。请指定相对路径（不以/开头）。',\n\t\t'mysqlserverstillhasdbs' => '无法从客户允许列表中删除数据库服务器，因为该服务器上仍有数据库。',\n\t\t'domaincannotbeedited' => '不允许您编辑域%s',\n\t\t'invalidcronjobintervalvalue' => 'Cronjob间隔必须是以下之一：%s',\n\t\t'phpgdextensionnotavailable' => 'PHP GD扩展不可用。无法验证图像数据',\n\t\t'2fa_wrongcode' => '输入的代码无效',\n\t\t'gnupgextensionnotavailable' => 'PHP GnuPG扩展不可用。无法验证PGP公钥',\n\t\t'invalidpgppublickey' => 'PGP公钥无效',\n\t\t'invalid_validtime' => '有效时间（秒）只能介于10和120之间',\n\t\t'customerphpenabledbutnoconfig' => '客户已激活PHP，但未选择PHP配置。',\n\t\t'emaildomainstillhasaddresses' => '无法停用邮件域标志，因为此域仍有电子邮件地址。',\n\t\t'tls13requiredforhttp3' => '域http3标志已启用，但ssl协议不包括TLSv1.3',\n\t\t'senderdomainnotowned' => '给定的域名\"%s\"无法使用。',\n\t\t'emailhasnoaccount' => '给定的电子邮件地址\"%s\"没有账户，无法添加发件人地址。',\n\t],\n\t'extras' => [\n\t\t'description' => '您可以在这里添加一些额外的功能，例如目录保护。<br />每次更改后，系统都需要一些时间来应用新设置。',\n\t\t'directoryprotection_add' => '添加目录保护',\n\t\t'view_directory' => '显示目录内容',\n\t\t'pathoptions_add' => '添加路径选项',\n\t\t'directory_browsing' => '目录内容浏览',\n\t\t'pathoptions_edit' => '编辑路径选项',\n\t\t'error404path' => '404',\n\t\t'error403path' => '403',\n\t\t'error500path' => '500',\n\t\t'error401path' => '401',\n\t\t'errordocument404path' => '错误文档404',\n\t\t'errordocument403path' => '错误文档403',\n\t\t'errordocument500path' => '错误文档500',\n\t\t'errordocument401path' => '错误文档401',\n\t\t'execute_perl' => '执行Perl/CGI',\n\t\t'htpasswdauthname' => '身份验证原因（AuthName）',\n\t\t'directoryprotection_edit' => '编辑目录保护',\n\t\t'export' => '创建数据转储',\n\t\t'dump_web' => '包括Web数据',\n\t\t'dump_mail' => '包括邮件数据',\n\t\t'dump_dbs' => '包括数据库',\n\t\t'path_protection_label' => '<strong class=\"text-danger\">重要</strong>',\n\t\t'path_protection_info' => '我们强烈建议保护给定路径，参见\" extras\" -> \"目录保护\"',\n\t],\n\t'ftp' => [\n\t\t'description' => '在这里，您可以创建和更改您的FTP帐户。<br />这些变化是即时进行的，帐户可以立即使用。',\n\t\t'account_add' => '创建帐户',\n\t\t'account_edit' => '编辑ftp帐户',\n\t\t'editpassdescription' => '设置新密码或留空不作更改。',\n\t\t'sshkey_add' => '添加ssh-key',\n\t\t'sshkey_edit' => '编辑ssh-key',\n\t],\n\t'gender' => [\n\t\t'title' => '标题',\n\t\t'male' => '先生',\n\t\t'female' => '夫人',\n\t\t'undef' => '',\n\t],\n\t'imprint' => '法律声明',\n\t'index' => [\n\t\t'customerdetails' => '客户详细信息',\n\t\t'accountdetails' => '账户的详细信息',\n\t],\n\t'integrity_check' => [\n\t\t'databaseCharset' => '数据库字符集（应为UTF-8）',\n\t\t'domainIpTable' => 'IP与域名引用',\n\t\t'subdomainSslRedirect' => '非SSL域的SSL重定向标志为假',\n\t\t'froxlorLocalGroupMemberForFcgidPhpFpm' => '客户组中的froxlor-user（用于FCGID/php-fpm）',\n\t\t'webserverGroupMemberForFcgidPhpFpm' => '客户组中的Webserver-user（对于FCGID/php-fpm）',\n\t\t'subdomainLetsencrypt' => '没有分配SSL端口的主域没有任何具有活动SSL重定向的子域',\n\t],\n\t'logger' => [\n\t\t'date' => '日期',\n\t\t'type' => '类型',\n\t\t'action' => '操作',\n\t\t'user' => '用户',\n\t\t'truncate' => '清空日志',\n\t\t'reseller' => '经销商',\n\t\t'admin' => '管理员',\n\t\t'cron' => 'Cronjob',\n\t\t'login' => '登录',\n\t\t'intern' => '内部',\n\t\t'unknown' => '未知',\n\t],\n\t'login' => [\n\t\t'username' => '用户名',\n\t\t'password' => '密码',\n\t\t'language' => '语言',\n\t\t'login' => '登录',\n\t\t'logout' => '注销',\n\t\t'profile_lng' => '配置文件语言',\n\t\t'welcomemsg' => '请登录以访问您的帐户。',\n\t\t'forgotpwd' => '忘记密码？',\n\t\t'presend' => '重置密码',\n\t\t'email' => '电子邮件地址',\n\t\t'remind' => '重置密码',\n\t\t'usernotfound' => '页面没有找到！',\n\t\t'backtologin' => '返回登录',\n\t\t'combination_not_found' => '未找到用户和电子邮件地址的组合。',\n\t\t'2fa' => '双重认证（2FA）',\n\t\t'2facode' => '请输入2FA代码',\n\t\t'2faremember' => '信任浏览器',\n\t],\n\t'mails' => [\n\t\t'pop_success' => [\n\t\t\t'mailbody' => '你好，我是\\n\\n您的邮件帐户{EMAIL}\\n已成功建立。\\n\\n这是一个自动创建的\\n电子邮件，请不要回答！\\n\\n你诚挚的，你的管理员',\n\t\t\t'subject' => '邮件帐户设置成功',\n\t\t],\n\t\t'createcustomer' => [\n\t\t\t'mailbody' => '你好{敬礼}，\\n\\n这是您帐户信息：\\n\\n用户名：{用户名}\\n密码：{PASSWORD}\\n\\n谢谢你，\\n您的管理员',\n\t\t\t'subject' => '账户信息',\n\t\t],\n\t\t'pop_success_alternative' => [\n\t\t\t'mailbody' => '你好{敬礼}，\\n\\n您的邮件帐户{EMAIL}\\n已成功建立。\\n您的密码是{PASSWORD}。\\n\\n这是一个自动创建的\\n电子邮件，请不要回答！\\n\\n你诚挚的，你的管理员',\n\t\t\t'subject' => '邮件帐户设置成功',\n\t\t],\n\t\t'password_reset' => [\n\t\t\t'subject' => '密码重置',\n\t\t\t'mailbody' => '你好{敬礼}，\\n\\n这是你设置新密码的链接。2这个链接在接下来的24小时内有效。\\n\\n联系我们\\n\\n谢谢你，\\n您的管理员',\n\t\t],\n\t\t'new_database_by_customer' => [\n\t\t\t'subject' => '[froxlor] 新建数据库',\n\t\t\t'mailbody' => '您好{CUST_NAME}，\\n\\n您刚刚添加了一个新的数据库。下面是输入的信息：\\n\\n数据库名称：{DB_NAME}\\n密码：{DB_PASS}\\n描述：{DB_DESC}\\n DB主机名：{DB_SRV}\\n phpMyAdmin：{PMA_URI}\\n你诚挚的，你的管理员',\n\t\t],\n\t\t'new_ftpaccount_by_customer' => [\n\t\t\t'subject' => '新建FTP用户',\n\t\t\t'mailbody' => '您好{CUST_NAME}，\\n\\n你刚刚添加了一个新的ftp用户。下面是输入的信息：\\n\\n用户名：{USR_NAME}\\n密码：{USR_PASS}\\n路径：{USR_PATH}\\n\\n你诚挚的，你的管理员',\n\t\t],\n\t\t'trafficmaxpercent' => [\n\t\t\t'mailbody' => '亲爱的{SALUTATION}，\\n\\n您使用了可用{TRAFFIC}流量的{TRAFFICUSED}。\\n这大于{MAX_PERCENT}%%。\\n\\n你诚挚的，你的管理员',\n\t\t\t'subject' => '达到您的流量限制',\n\t\t],\n\t\t'diskmaxpercent' => [\n\t\t\t'mailbody' => '亲爱的{SALUTATION}，\\n\\n您已使用可用{DISKAVAILABLE}磁盘空间中的{DISKUSED}。\\n这大于{MAX_PERCENT}%%。\\n\\n你诚挚的，你的管理员',\n\t\t\t'subject' => '达到磁盘空间限制',\n\t\t],\n\t\t'2fa' => [\n\t\t\t'mailbody' => '你好，我是\\n\\n您的2FA登录代码是：{CODE}。\\n\\n这是一个自动创建的\\n电子邮件，请不要回答！\\n\\n你诚挚的，你的管理员',\n\t\t\t'subject' => 'froxlor - 2FA代码',\n\t\t],\n\t],\n\t'menue' => [\n\t\t'main' => [\n\t\t\t'main' => '主页',\n\t\t\t'changepassword' => '更改密码',\n\t\t\t'changelanguage' => '更改语言',\n\t\t\t'username' => '登录名：',\n\t\t\t'changetheme' => '更改主题',\n\t\t\t'apihelp' => 'API帮助',\n\t\t\t'apikeys' => 'API密钥',\n\t\t],\n\t\t'email' => [\n\t\t\t'email' => '邮件',\n\t\t\t'emails' => '邮箱地址',\n\t\t\t'webmail' => 'Webmail',\n\t\t\t'emailsoverview' => '邮件域名概览',\n\t\t],\n\t\t'mysql' => [\n\t\t\t'mysql' => 'MySQL',\n\t\t\t'databases' => '数据库',\n\t\t\t'phpmyadmin' => 'phpMyAdmin',\n\t\t],\n\t\t'domains' => [\n\t\t\t'domains' => '域',\n\t\t\t'settings' => '域名概述',\n\t\t],\n\t\t'ftp' => [\n\t\t\t'ftp' => 'FTP',\n\t\t\t'accounts' => '账户',\n\t\t\t'webftp' => 'WebFTP',\n\t\t\t'sshkeys' => 'SSH密钥',\n\t\t],\n\t\t'extras' => [\n\t\t\t'extras' => '额外',\n\t\t\t'directoryprotection' => '目录保护',\n\t\t\t'pathoptions' => '路径选项',\n\t\t\t'export' => '数据导出',\n\t\t],\n\t\t'traffic' => [\n\t\t\t'traffic' => '流量',\n\t\t\t'current' => '当月',\n\t\t\t'overview' => '总流量',\n\t\t],\n\t\t'phpsettings' => [\n\t\t\t'maintitle' => 'PHP配置',\n\t\t\t'fpmdaemons' => 'PHP-FPM版本',\n\t\t],\n\t\t'logger' => [\n\t\t\t'logger' => '系统日志',\n\t\t],\n\t],\n\t'message' => [\n\t\t'norecipients' => '由于数据库中没有收件人，因此未发送电子邮件',\n\t\t'success' => '已成功将邮件发送给%s个收件人',\n\t],\n\t'mysql' => [\n\t\t'databasename' => '用户/数据库名称',\n\t\t'databasedescription' => '数据库描述',\n\t\t'database_create' => '创建数据库',\n\t\t'description' => '在这里，您可以创建和更改您的MySQL数据库。<br />更改立即进行，数据库可以立即使用。<br />在左侧的菜单中，您可以找到工具phpMyAdmin，使用它可以轻松管理数据库。<br /><br />要在您自己的php脚本中使用您的数据库，请使用以下设置：（斜体字的数据<i></i>必须更改为您键入的等效数据！）<br />主机名：<b><SQL_HOST></b><br />数据<b><i>库</i></b><br />密码：<b><i>您选择的密码</i></b><br />数据库：<b><i>数据库</i></b>',\n\t\t'mysql_server' => 'MySQL-Server',\n\t\t'database_edit' => '编辑数据库',\n\t\t'size' => '大小',\n\t\t'privileged_user' => '有权限的数据库用户',\n\t\t'privileged_passwd' => '特权用户的密码',\n\t\t'unprivileged_passwd' => '非特权用户的密码',\n\t\t'mysql_ssl_ca_file' => 'SSL服务器证书',\n\t\t'mysql_ssl_verify_server_certificate' => '验证SSL服务器证书',\n\t\t'globaluserinfo' => '要访问您的数据库，您还可以使用froxlor登录名（用户：%s），它可以自动访问您的所有数据库。<br />建议<b>不要</b>将其用于应用程序，仅用于管理（例如通过phpMyAdmin）。',\n\t\t'edit_global_user' => '编辑管理员用户',\n\t],\n\t'opcacheinfo' => [\n\t\t'generaltitle' => '一般信息',\n\t\t'resetcache' => '重置OPcache',\n\t\t'version' => 'OPCache版本',\n\t\t'phpversion' => 'PHP版本',\n\t\t'runtimeconf' => '运行时配置',\n\t\t'start' => '开始时间',\n\t\t'lastreset' => '上次重新启动',\n\t\t'oomrestarts' => 'OOM重启计数',\n\t\t'hashrestarts' => '哈希重启计数',\n\t\t'manualrestarts' => '手动重启计数',\n\t\t'hitsc' => '点击数',\n\t\t'missc' => '未中计数',\n\t\t'blmissc' => '黑名单小姐计数',\n\t\t'status' => '状态',\n\t\t'never' => '从未',\n\t\t'enabled' => 'OPcache已启用',\n\t\t'cachefull' => '缓存已满',\n\t\t'restartpending' => '等待重新启动',\n\t\t'restartinprogress' => '正在重新启动',\n\t\t'cachedscripts' => '缓存脚本计数',\n\t\t'memusage' => '内存使用',\n\t\t'totalmem' => '总内存',\n\t\t'usedmem' => '已用内存',\n\t\t'freemem' => '空闲内存',\n\t\t'wastedmem' => '已浪费内存',\n\t\t'maxkey' => '最大键数',\n\t\t'usedkey' => '使用的键',\n\t\t'wastedkey' => '已浪费键',\n\t\t'strinterning' => '字符串驻留',\n\t\t'strcount' => '字符串计数',\n\t\t'keystat' => '缓存密钥统计信息',\n\t\t'used' => '已用',\n\t\t'free' => '空闲',\n\t\t'blacklist' => '黑名单',\n\t\t'novalue' => '<i>无值</i>',\n\t\t'true' => '<i>真</i>',\n\t\t'false' => '<i>假</i>',\n\t\t'funcsavail' => '可用功能',\n\t],\n\t'panel' => [\n\t\t'edit' => '编辑',\n\t\t'delete' => '删除',\n\t\t'create' => '创建',\n\t\t'save' => '保存',\n\t\t'yes' => '是',\n\t\t'no' => '否',\n\t\t'emptyfornochanges' => '为空，表示没有更改',\n\t\t'emptyfordefault' => '默认值为空',\n\t\t'path' => '路径',\n\t\t'toggle' => '切换',\n\t\t'next' => '下一步',\n\t\t'dirsmissing' => '无法找到或读取目录！',\n\t\t'unlimited' => '∞',\n\t\t'urloverridespath' => 'URL（覆盖路径）',\n\t\t'pathorurl' => '路径或URL',\n\t\t'ascending' => '升序',\n\t\t'descending' => '降序',\n\t\t'search' => '搜索',\n\t\t'used' => '使用',\n\t\t'translator' => '译者',\n\t\t'reset' => '放弃更改',\n\t\t'pathDescription' => '如果该目录不存在，它将自动创建。',\n\t\t'pathDescriptionEx' => '<br /><br /><span class=\"text-danger\">请注意：</span>由于管理设置，路径<code>/</code>不被允许，如果未设置为其他目录，它将自动设置为<code>/chosen.subdomain.tld/</code>。',\n\t\t'pathDescriptionSubdomain' => '如果该目录不存在，它将自动创建。<br /><br />如果你想重定向到另一个域，那么这个条目必须以http：//或https：//开头。<br /><br />如果URL以/结尾，则将其视为文件夹，否则将其视为文件。',\n\t\t'back' => '返回',\n\t\t'reseller' => '经销商',\n\t\t'admin' => '管理员',\n\t\t'customer' => '客户',\n\t\t'send' => '发送',\n\t\t'nosslipsavailable' => '当前没有此服务器的SSL IP/端口组合',\n\t\t'backtooverview' => '返回概览',\n\t\t'dateformat' => 'YYYY-MM-DD',\n\t\t'dateformat_function' => 'Y-m-D',\n\t\t'timeformat_function' => 'H：i：s',\n\t\t'default' => '默认',\n\t\t'never' => '从未',\n\t\t'active' => '激活',\n\t\t'please_choose' => '请选择',\n\t\t'allow_modifications' => '允许修改',\n\t\t'megabyte' => '兆字节',\n\t\t'not_supported' => '不支持：',\n\t\t'view' => '视图',\n\t\t'toomanydirs' => '子目录太多。退回到手动路径选择。',\n\t\t'abort' => '中止',\n\t\t'not_activated' => '未激活',\n\t\t'off' => '关闭',\n\t\t'options' => '选项',\n\t\t'neverloggedin' => '尚未登录',\n\t\t'descriptionerrordocument' => '可以是URL、文件路径或用\"\"包裹的字符串<br />留空以使用服务器默认值。',\n\t\t'unlock' => '解锁',\n\t\t'theme' => '主题',\n\t\t'variable' => '可变',\n\t\t'description' => '描述',\n\t\t'cancel' => '取消',\n\t\t'ssleditor' => '此域的SSL设置',\n\t\t'ssleditor_infoshared' => '当前使用父域证书',\n\t\t'ssleditor_infoglobal' => '当前正在使用全局证书',\n\t\t'dashboard' => '仪表板',\n\t\t'assigned' => '分配',\n\t\t'available' => '可用',\n\t\t'news' => '新闻',\n\t\t'newsfeed_disabled' => '新闻源已禁用。单击编辑图标可转到设置。',\n\t\t'ftpdesc' => 'FTP描述',\n\t\t'letsencrypt' => '使用Let\\'s encrypt',\n\t\t'set' => '适用',\n\t\t'shell' => 'Shell',\n\t\t'sshkeydesc' => 'SSH密钥描述',\n\t\t'ftpuser' => 'FTP用户',\n\t\t'sshpubkey' => 'SSH公钥',\n\t\t'sshpubkeyph' => \"以'ssh-ed25519'、'ssh-rsa'、'ecdsa-sha2-nistp256'、'ecdsa-sha2-nistp384'、'ecdsa-sha2-nistp521'、'sk-ecdsa-sha2-nistp256@openssh.com'或'sk-ssh-ed25519@openssh.com'开头\",\n\t\t'sshfingerprint' => '指纹',\n\t\t'exportpath' => [\n\t\t\t'title' => '导出数据的目标路径',\n\t\t\t'description' => '这是导出存档的存储路径。如果包含web数据，则会存储主目录中的所有文件，但不包括此处指定的文件夹。',\n\t\t],\n\t\t'export_pgp_public_key' => [\n\t\t\t'title' => '用于加密的公共PGP密钥',\n\t\t\t'description' => '这是用于加密导出的公共PGP密钥。如果您将此字段留空，则导出将不会加密。',\n\t\t],\n\t\t'pgp_public_key' => '公共PGP密钥',\n\t\t'none_value' => '无',\n\t\t'viewlogs' => '查看日志文件',\n\t\t'not_configured' => '系统尚未配置。单击此处可转到配置。',\n\t\t'start_setup' => '启动安装程序',\n\t\t'ihave_configured' => '我已经配置了服务',\n\t\t'system_is_configured' => '<i class=\"fa-solid fa-circle-exclamation me-1\"></i>系统已设置为已配置',\n\t\t'settings_before_configuration' => '请确保在此处配置服务之前调整了设置',\n\t\t'image_field_delete' => '删除现有的当前图像',\n\t\t'usage_statistics' => '资源使用',\n\t\t'security_question' => '安全问题',\n\t\t'listing_empty' => '未找到任何条目',\n\t\t'unspecified' => '未指明',\n\t\t'settingsmode' => '模式',\n\t\t'settingsmodebasic' => '基本',\n\t\t'settingsmodeadvanced' => '高级',\n\t\t'settingsmodetoggle' => '单击以切换模式',\n\t\t'modalclose' => '关闭',\n\t\t'managetablecolumnsmodal' => [\n\t\t\t'title' => '管理表列',\n\t\t\t'description' => '在这里，您可以自定义可见列',\n\t\t],\n\t\t'mandatoryfield' => '字段为必填',\n\t\t'select_all' => '选择所有',\n\t\t'unselect_all' => '取消选择所有',\n\t\t'searchtablecolumnsmodal' => [\n\t\t\t'title' => '在字段中搜索',\n\t\t\t'description' => '选择要搜索的字段。'\n\t\t],\n\t\t'upload_import' => '上传和导入',\n\t\t'profile' => '我的个人资料',\n\t\t'use_checkbox_for_unlimited' => '值\"0\"将停用此资源。右侧的复选框允许\"无限制\"使用。',\n\t\t'use_checkbox_to_disable' => '要禁用，请激活输入字段右侧的复选框',\n\t\t'distro_mismatch' => '您似乎已升级到新的发行版。请记住相应地重新配置服务。',\n\t\t'set_new_distro' => '设置发行版',\n\t\t'dismiss' => '驳回',\n\t],\n\t'phpfpm' => [\n\t\t'vhost_httpuser' => '用于PHP-FPM的本地用户（froxlor vHost）',\n\t\t'vhost_httpgroup' => '用于PHP-FPM的本地组（froxlor vHost）',\n\t\t'ownvhost' => [\n\t\t\t'title' => '为froxlor vHost启用PHP-FPM',\n\t\t\t'description' => '如果启用，froxlor也将在本地用户下运行',\n\t\t],\n\t\t'use_mod_proxy' => [\n\t\t\t'title' => '使用mod_proxy / mod_proxy_fcgi',\n\t\t\t'description' => '<strong class=\"text-danger\">使用Debian 9.x (Stretch)或更高版本时必须启用</strong>。激活以通过mod_proxy_fcgi使用php-fpm。需要apache-2.4.9或更高版本',\n\t\t],\n\t\t'ini_flags' => '为php.ini输入可能的<strong>php_flag</strong>。每行一个条目',\n\t\t'ini_values' => '为php.ini输入可能的<strong>php_value</strong>。每行一个条目',\n\t\t'ini_admin_flags' => '为php.ini输入可能的<strong>php_admin_flag</strong>。每行一个条目',\n\t\t'ini_admin_values' => '为php.ini输入可能的<strong>php_admin_value</strong>。每行一个条目',\n\t],\n\t'privacy' => '隐私政策',\n\t'pwdreminder' => [\n\t\t'success' => '已成功请求密码重置。请按照您收到的电子邮件中的说明进行操作。',\n\t\t'notallowed' => '未知用户或密码重置已被禁用',\n\t\t'changed' => '您的密码已成功更新。您现在可以使用新密码登录。',\n\t\t'wrongcode' => '抱歉，您的激活码不存在或已过期。',\n\t\t'choosenew' => '设置新密码',\n\t],\n\t'question' => [\n\t\t'question' => '安全问题',\n\t\t'admin_customer_reallydelete' => '是否确实要删除客户%s？此操作无法撤消！',\n\t\t'admin_domain_reallydelete' => '您确定要删除域名%s吗？<br><span class=\"text-danger\"><strong>注意：</strong>与此域名关联的所有子域名、FTP账户和电子邮件地址/账户都将被删除！</span>',\n\t\t'admin_domain_reallydisablesecuritysetting' => '您真的要禁用此安全设置OpenBasedir吗？',\n\t\t'admin_admin_reallydelete' => '是否确实要删除管理员%s？每个客户和域都将重新分配给您的帐户。',\n\t\t'admin_template_reallydelete' => '是否确实要删除模板\\'%s\\'？',\n\t\t'domains_reallydelete' => '是否确实要删除域%s？',\n\t\t'email_reallydelete' => '确实要删除电子邮件地址%s吗？',\n\t\t'email_reallydelete_account' => '确实要删除%s的电子邮件帐户吗？',\n\t\t'email_reallydelete_forwarder' => '确实要删除转发器%s吗？',\n\t\t'email_reallydelete_sender' => '确实要删除允许的发件人%s吗？',\n\t\t'extras_reallydelete' => '确实要删除%s的目录保护吗？',\n\t\t'extras_reallydelete_pathoptions' => '确实要删除%s的路径选项吗？',\n\t\t'extras_reallydelete_export' => '是否确实要中止计划的导出作业？',\n\t\t'ftp_reallydelete' => '是否确实要删除FTP帐户%s？',\n\t\t'sshkey_reallydelete' => '你真的要删除ssh-key %s吗？',\n\t\t'mysql_reallydelete' => '是否确实要删除数据库%s？此操作无法撤消！',\n\t\t'admin_configs_reallyrebuild' => '您确定要重建所有的配置文件吗？',\n\t\t'admin_customer_alsoremovefiles' => '也删除用户文件？',\n\t\t'admin_customer_alsoremovemail' => '从文件系统中完全删除电子邮件数据？',\n\t\t'admin_customer_alsoremoveftphomedir' => '也删除FTP用户homedir？',\n\t\t'admin_ip_reallydelete' => '是否确实要删除IP地址%s？',\n\t\t'admin_domain_reallydocrootoutofcustomerroot' => '您确定要此域的文档根目录，而不是客户的客户根目录吗？',\n\t\t'admin_counters_reallyupdate' => '您确定要重新计算资源使用量吗？',\n\t\t'admin_cleartextmailpws_reallywipe' => '是否确实要从表mail_users中擦除所有未加密的邮件帐户密码？此操作无法恢复！存储未加密的电子邮件密码的设置也将设置为OFF',\n\t\t'logger_reallytruncate' => '您确定要清空表\"%s\"吗？',\n\t\t'admin_quotas_reallywipe' => '是否确实要擦除表mail_users上的所有配额？此操作无法恢复！',\n\t\t'admin_quotas_reallyenforce' => '您真的要对所有用户强制使用默认配额吗？这无法恢复！',\n\t\t'phpsetting_reallydelete' => '是否确实要删除这些设置？当前使用这些设置的所有域都将更改为默认配置。',\n\t\t'fpmsetting_reallydelete' => '你真的要删除这些php-fpm设置吗？所有当前使用这些设置的php配置将被更改为默认配置。',\n\t\t'customer_reallyunlock' => '您确定要解除锁定客户%s吗？',\n\t\t'admin_integritycheck_reallyfix' => '您真的要尝试自动修复所有数据库完整性问题吗？',\n\t\t'plan_reallydelete' => '是否确实要删除主机计划%s？',\n\t\t'apikey_reallydelete' => '您确定要删除此api-key吗？',\n\t\t'apikey_reallyadd' => '您确定要建立新的api-key吗？',\n\t\t'dnsentry_reallydelete' => '您确定要删除这个区域项目吗？',\n\t\t'certificate_reallydelete' => '您确定要删除此证书吗？',\n\t\t'cache_reallydelete' => '您确定要清除该高速缓存吗？',\n\t\t'please_enter_otp' => '请输入2FA代码',\n\t\t'admin_mysqlserver_reallydelete' => '您确定要删除此MySQL服务器吗？',\n\t],\n\t'redirect_desc' => [\n\t\t'rc_default' => '默认',\n\t\t'rc_movedperm' => '永久重定向 (301)',\n\t\t'rc_found' => '临时重定向 (302)',\n\t\t'rc_seeother' => '资源已移动 (303)',\n\t\t'rc_tempred' => '临时重定向 (307)',\n\t],\n\t'serversettings' => [\n\t\t'session_timeout' => [\n\t\t\t'title' => '会话超时',\n\t\t\t'description' => '在会话失效之前，用户必须处于非活动状态多长时间（秒）？',\n\t\t],\n\t\t'accountprefix' => [\n\t\t\t'title' => '客户前缀',\n\t\t\t'description' => '客户帐户应使用哪个前缀？',\n\t\t],\n\t\t\t'mysqlprefix' => [\n\t\t\t\t'title' => 'SQL前缀',\n\t\t\t\t'description' => 'MySQL账户应使用哪个前缀？</br>使用 \"RANDOM\" 作为值可获得3位随机前缀</br>使用 \"DBNAME\" 作为值时，数据库名字段将与客户名称一起用作前缀。',\n\t\t\t],\n\t\t'ftpprefix' => [\n\t\t\t'title' => 'FTP前缀',\n\t\t\t'description' => 'ftp帐户应该有哪个前缀？<br/><b>如果你改变了这一点，你也必须改变FTP服务器配置文件中的SQL查询，以防你使用它！</b>',\n\t\t],\n\t\t'documentroot_prefix' => [\n\t\t\t'title' => '主目录',\n\t\t\t'description' => '所有的主目录应该存储在哪里？',\n\t\t],\n\t\t'logfiles_directory' => [\n\t\t\t'title' => '日志文件目录',\n\t\t\t'description' => '所有日志文件都应该存储在哪里？',\n\t\t],\n\t\t'logfiles_script' => [\n\t\t\t'title' => '自定义脚本将日志文件传输到',\n\t\t\t'description' => '您可以在这里指定一个脚本，并在需要时使用占位符<strong>{LOGFILE}，{DOMAIN}和{CUSTOMER}</strong>。如果您想使用它，您还需要激活<strong>Pipe Web服务器日志文件</strong>选项。不需要前缀管道字符。',\n\t\t],\n\t\t'logfiles_format' => [\n\t\t\t'title' => '日志格式',\n\t\t\t'description' => '根据您的网络服务器规范，在此输入自定义日志格式，留空为默认值。根据您的格式，字符串必须加引号。<br/>如果与nginx一起使用，它将看起来像<i>log_format frx_custom {CONFIGURED_VALUE}</i>。<br/>如果与Apache一起使用，它将看起来像<i>LogFormat {CONFIGURED_VALUE} frx_custom</i>。<br/><strong>注意</strong>：代码不会被检查是否有任何错误。如果它包含错误，Web服务器可能无法再次启动！',\n\t\t],\n\t\t'logfiles_type' => [\n\t\t\t'title' => '访问日志类型',\n\t\t\t'description' => '在这里选择 <strong>combined</strong> 或 <strong>vhost_combined</strong>。',\n\t\t],\n\t\t'logfiles_piped' => [\n\t\t\t'title' => '将Web服务器日志文件传输到指定的脚本（参见上文）',\n\t\t\t'description' => '当使用日志文件的自定义脚本时，您需要激活它才能执行它',\n\t\t],\n\t\t'ipaddress' => [\n\t\t\t'title' => 'ip地址',\n\t\t\t'description' => '这个服务器的主IP地址是什么？',\n\t\t],\n\t\t'hostname' => [\n\t\t\t'title' => '主机名',\n\t\t\t'description' => '这台服务器的主机名是什么？',\n\t\t],\n\t\t'apachereload_command' => [\n\t\t\t'title' => 'Web服务器重载命令',\n\t\t\t'description' => '重新加载Web服务器配置文件的命令是什么？',\n\t\t],\n\t\t'bindenable' => [\n\t\t\t'title' => '启用域名服务器',\n\t\t\t'description' => '在这里可以全局启用和禁用域名服务器。',\n\t\t],\n\t\t'bindconf_directory' => [\n\t\t\t'title' => 'DNS服务器配置目录',\n\t\t\t'description' => 'DNS服务器配置文件应该保存在哪里？',\n\t\t],\n\t\t'bindreload_command' => [\n\t\t\t'title' => 'DNS服务器重新加载命令',\n\t\t\t'description' => '重新加载dns服务器守护进程的命令是什么？',\n\t\t],\n\t\t'vmail_uid' => [\n\t\t\t'title' => '邮件-UID',\n\t\t\t'description' => '邮件应该有哪个用户ID？',\n\t\t],\n\t\t'vmail_gid' => [\n\t\t\t'title' => '邮件-GID',\n\t\t\t'description' => '邮件应该使用哪个GroupID？',\n\t\t],\n\t\t'vmail_homedir' => [\n\t\t\t'title' => 'Mails-homedir',\n\t\t\t'description' => '所有邮件都应该存放在哪里？',\n\t\t],\n\t\t'adminmail' => [\n\t\t\t'title' => '发送者',\n\t\t\t'description' => '从Panel发送的电子邮件的发件人地址是什么？',\n\t\t],\n\t\t'phpmyadmin_url' => [\n\t\t\t'title' => 'phpMyAdmin URL',\n\t\t\t'description' => 'phpMyAdmin的URL是什么？（必须以http（s）：//开头）',\n\t\t],\n\t\t'webmail_url' => [\n\t\t\t'title' => 'Webmail URL',\n\t\t\t'description' => 'Webmail的URL是什么？（必须以http（s）：//开头）',\n\t\t],\n\t\t'webftp_url' => [\n\t\t\t'title' => 'WebFTP URL',\n\t\t\t'description' => 'WebFTP的URL是什么？（必须以http（s）：//开头）',\n\t\t],\n\t\t'language' => [\n\t\t\t'description' => '你的标准服务器语言是什么？',\n\t\t],\n\t\t'maxloginattempts' => [\n\t\t\t'title' => '最大登录尝试次数',\n\t\t\t'description' => '最大登录尝试次数，在此之后帐户将被禁用。',\n\t\t],\n\t\t'deactivatetime' => [\n\t\t\t'title' => '去激活时间',\n\t\t\t'description' => '帐户在多次登录尝试后被禁用的时间（秒）。',\n\t\t],\n\t\t'pathedit' => [\n\t\t\t'title' => '路径输入类型',\n\t\t\t'description' => '路径应该通过菜单还是通过输入字段来选择？',\n\t\t],\n\t\t'nameservers' => [\n\t\t\t'title' => '域名服务器',\n\t\t\t'description' => '一个逗号分隔的列表，包含所有域名服务器的主机名。第一个将是主服务器。',\n\t\t],\n\t\t'mxservers' => [\n\t\t\t'title' => 'MX服务器',\n\t\t\t'description' => '一个逗号分隔的列表，包含一个数字和一个主机名（用空格分隔）（例如“10 mx.example.com”），其中包含mx服务器。',\n\t\t],\n\t\t'paging' => [\n\t\t\t'title' => '每页条目数',\n\t\t\t'description' => '一页上应显示多少个条目？（0 =禁用分页）',\n\t\t],\n\t\t'defaultip' => [\n\t\t\t'title' => '默认IP/端口',\n\t\t\t'description' => '选择所有要用作新域默认值的IP地址',\n\t\t],\n\t\t'defaultsslip' => [\n\t\t\t'title' => '默认SSL IP/端口',\n\t\t\t'description' => '选择所有要用作新域默认值的启用ssl的IP地址',\n\t\t],\n\t\t'phpappendopenbasedir' => [\n\t\t\t'title' => '要附加到OpenBasedir的路径',\n\t\t\t'description' => '这些路径（用冒号分隔）将被添加到每个vHost-container中的OpenBasedir-语句中。',\n\t\t],\n\t\t'natsorting' => [\n\t\t\t'title' => '在列表视图中使用自然的人工排序',\n\t\t\t'description' => '将列表排序为web 1-> web2 -> web 11，而不是web 1-> web 11-> web2。',\n\t\t],\n\t\t'deactivateddocroot' => [\n\t\t\t'title' => '已停用用户的Docroot',\n\t\t\t'description' => '当用户被停用时，此路径将用作他的docroot。留空表示根本不创建vHost。',\n\t\t],\n\t\t'mailpwcleartext' => [\n\t\t\t'title' => '同时以明文形式保存邮件账户密码',\n\t\t\t'description' => '如果设置为是，所有密码也将以明文形式保存在 mail_users 表中（具有数据库访问权限的每个人都可读取）。只有当您打算使用 SASL 时才激活此设置！',\n\t\t],\n\t\t'ftpdomain' => [\n\t\t\t'title' => 'FTP帐户@domain',\n\t\t\t'description' => '客户可以创建FTP帐户user@customerdomain？',\n\t\t],\n\t\t'mod_fcgid' => [\n\t\t\t'title' => '启用FCGID',\n\t\t\t'description' => '使用此选项可使用相应的用户账户运行PHP。<br /><br /><b>这需要对Apache进行特殊的Web服务器配置，请参阅 <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/fcgid/\">FCGID - 手册</a></b>',\n\t\t\t'configdir' => [\n\t\t\t\t'title' => '配置目录',\n\t\t\t\t'description' => '所有FCGID配置文件应该存储在哪里？如果您不使用自编译的suexec二进制文件（这是正常情况），此路径必须在 /var/www/ 下<br /><br /><div class=\"text-danger\">注意：此文件夹的内容会定期删除，因此请避免手动在其中存储数据。</div>',\n\t\t\t],\n\t\t\t'tmpdir' => [\n\t\t\t\t'title' => '临时目录',\n\t\t\t\t'description' => '临时目录应该存储在哪里？',\n\t\t\t],\n\t\t\t'starter' => [\n\t\t\t\t'title' => '每个域的进程数',\n\t\t\t\t'description' => '每个域应该启动/允许多少个进程？建议使用0，因为PHP将非常有效地管理进程数量。',\n\t\t\t],\n\t\t\t'wrapper' => [\n\t\t\t\t'title' => 'VHost中的包装器',\n\t\t\t\t'description' => '包装器应如何包含在VHost中？',\n\t\t\t],\n\t\t\t'peardir' => [\n\t\t\t\t'title' => '全局PEAR目录',\n\t\t\t\t'description' => '在每个php.ini配置中应该替换哪些全局PEAR目录？不同的目录必须用冒号分隔。',\n\t\t\t],\n\t\t\t'maxrequests' => [\n\t\t\t\t'title' => '每个域的最大请求数',\n\t\t\t\t'description' => '每个域应该允许多少个请求？',\n\t\t\t],\n\t\t\t'defaultini' => '新域名的默认PHP配置',\n\t\t\t'defaultini_ownvhost' => 'froxlor-vHost的默认PHP配置',\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => '空闲超时',\n\t\t\t\t'description' => 'Mod FastCGI的空闲超时设置。',\n\t\t\t],\n\t\t],\n\t\t'sendalternativemail' => [\n\t\t\t'title' => '使用备用电子邮件地址',\n\t\t\t'description' => '在创建电子邮件帐户时，将密码电子邮件发送到不同的地址',\n\t\t],\n\t\t'apacheconf_vhost' => [\n\t\t\t'title' => 'Web服务器vHost配置文件/目录名',\n\t\t\t'description' => 'vHost配置应该存储在哪里？您可以在此处指定一个文件（所有vHost都在一个文件中）或目录（每个vHost都在自己的文件中）。',\n\t\t],\n\t\t'apacheconf_diroptions' => [\n\t\t\t'title' => 'Web服务器目录配置文件/目录名',\n\t\t\t'description' => '您可以在这里指定一个文件（所有的目录都在一个文件中）或目录（每个目录都在自己的文件中）。',\n\t\t],\n\t\t'apacheconf_htpasswddir' => [\n\t\t\t'title' => 'Webserver htpasswd目录名',\n\t\t\t'description' => '用于目录保护的htpasswd文件应该存储在哪里？',\n\t\t],\n\t\t'mysql_access_host' => [\n\t\t\t'title' => 'MySQL访问主机',\n\t\t\t'description' => '一个逗号分隔的主机列表，允许用户从这些主机连接到MySQL服务器。要允许子网，网络掩码或CIDR语法有效。',\n\t\t],\n\t\t'webalizer_quiet' => [\n\t\t\t'title' => 'Webalizer输出',\n\t\t\t'description' => 'Webalizer程序的详细程度',\n\t\t],\n\t\t'logger' => [\n\t\t\t'enable' => '启用/禁用日志记录',\n\t\t\t'severity' => '日志记录级别',\n\t\t\t'types' => [\n\t\t\t\t'title' => '日志类型',\n\t\t\t\t'description' => '指定日志类型。若要选择多个类型，请在选择时按住Ctrl。<br />可用的日志类型有：系统日志、文件、MySQL',\n\t\t\t],\n\t\t\t'logfile' => [\n\t\t\t\t'title' => '日志文件',\n\t\t\t\t'description' => '仅在日志类型包含\"文件\"时使用。此文件将在froxlor/logs/中创建。此文件夹受保护，防止公共访问。',\n\t\t\t],\n\t\t\t'logcron' => 'Log cron工作',\n\t\t\t'logcronoption' => [\n\t\t\t\t'never' => '从未',\n\t\t\t\t'once' => '一旦',\n\t\t\t\t'always' => '总是',\n\t\t\t],\n\t\t],\n\t\t'ssl' => [\n\t\t\t'use_ssl' => [\n\t\t\t\t'title' => '启用SSL使用',\n\t\t\t\t'description' => '如果您想为您的Web服务器使用SSL，请选中此选项',\n\t\t\t],\n\t\t\t'ssl_cert_file' => [\n\t\t\t\t'title' => 'SSL证书的路径',\n\t\t\t\t'description' => '指定路径，包括.crt或.pem文件（主证书）的文件名',\n\t\t\t],\n\t\t\t'openssl_cnf' => '用于创建Cert文件的配置文件',\n\t\t\t'ssl_key_file' => [\n\t\t\t\t'title' => 'SSL密钥文件的路径',\n\t\t\t\t'description' => '指定路径，包括私钥文件的文件名（主要是.key）',\n\t\t\t],\n\t\t\t'ssl_ca_file' => [\n\t\t\t\t'title' => 'SSL CA证书的路径（可选）',\n\t\t\t\t'description' => '客户端身份验证，只有当你知道它是什么时才设置它。',\n\t\t\t],\n\t\t\t'ssl_cipher_list' => [\n\t\t\t\t'title' => '配置允许的SSL加密套件',\n\t\t\t\t'description' => '这是您在使用SSL时希望（或不希望）使用的加密套件列表。关于加密套件以及如何包含/排除它们，请参阅<a href=\"https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html\">加密套件手册</a>中的\"CIPHER LIST FORMAT\"和\"CIPHER STRINGS\"部分。<br /><br /><b>默认值为：</b><pre>ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128</pre>',\n\t\t\t],\n\t\t\t'apache24_ocsp_cache_path' => [\n\t\t\t\t'title' => 'Apache 2.4：OCSP装订缓存的路径',\n\t\t\t\t'description' => '配置用于存储包含在TLS握手中的OCSP响应的该高速缓存。',\n\t\t\t],\n\t\t\t'ssl_protocols' => [\n\t\t\t\t'title' => '配置TLS协议版本',\n\t\t\t\t'description' => '这是您在使用SSL时希望（或不希望）使用的SSL协议列表。<b>注意：</b>某些较旧的浏览器可能不支持最新的协议版本。<br /><br /><b>默认值为：</b><pre>TLSv1.2</pre>',\n\t\t\t],\n\t\t\t'tlsv13_cipher_list' => [\n\t\t\t\t'title' => '配置显式TLSv1.3加密套件（如果使用）',\n\t\t\t\t'description' => '这是您在使用TLSv1.3时希望（或不希望）使用的加密套件列表。关于加密套件以及如何包含/排除它们，请参阅<a href=\"https://wiki.openssl.org/index.php/TLS1.3\">TLSv1.3文档</a>。<br /><br /><b>默认值为空</b>',\n\t\t\t],\n\t\t],\n\t\t'default_vhostconf' => [\n\t\t\t'title' => '默认vHost设置',\n\t\t\t'description' => '此字段的内容将直接包含在此ip/port vHost容器中。您可以使用以下变量：<br/><code>{域}</code>、<code>{DOCROOT}</code>、<code>{客户}</code>、<code>{IP}</code>、<code>{端口}</code>、<code>{方案}</code>、<code>{FPMSOCKET}</code>（如果适用）<br/>注意：代码不会被检查是否有任何错误。如果它包含错误，Web服务器可能无法再次启动！',\n\t\t],\n\t\t'apache_globaldiropt' => [\n\t\t\t'title' => '客户前缀的目录选项',\n\t\t\t'description' => '此字段的内容将包含在 05_froxlor_dirfix_nofcgid.conf Apache配置中。如果为空，则使用默认值：<br><br>apache >= 2.4<br><code>Require all granted<br>AllowOverride All</code><br><br>apache <= 2.2<br><code>Order allow,deny<br>allow from all</code>',\n\t\t],\n\t\t'default_vhostconf_domain' => [\n\t\t\t'description' => '此字段的内容将直接包含到域vHost容器中。您可以使用以下变量：<br/><code>{域}</code>、<code>{DOCROOT}</code>、<code>{客户}</code>、<code>{IP}</code>、<code>{端口}</code>、<code>{方案}</code>、<code>{FPMSOCKET}</code>（如果适用）<br/>注意：代码不会被检查是否有任何错误。如果它包含错误，Web服务器可能无法再次启动！',\n\t\t],\n\t\t'decimal_places' => '流量/网站空间输出的小数位数',\n\t\t'selfdns' => [\n\t\t\t'title' => '客户域dns设置',\n\t\t],\n\t\t'selfdnscustomer' => [\n\t\t\t'title' => '允许客户编辑域dns设置',\n\t\t],\n\t\t'unix_names' => [\n\t\t\t'title' => '使用UNIX兼容的用户名',\n\t\t\t'description' => '如果选择\"否\"，允许您在用户名中使用 <strong>-</strong> 和 <strong>_</strong>',\n\t\t],\n\t\t'allow_password_reset' => [\n\t\t\t'title' => '允许客户重置密码',\n\t\t\t'description' => '客户可以重置密码，激活链接将发送到他们的电子邮件地址',\n\t\t],\n\t\t'allow_password_reset_admin' => [\n\t\t\t'title' => '允许管理员重置密码',\n\t\t\t'description' => '管理员/用户可以重置密码，激活链接将发送到他们的电子邮件地址',\n\t\t],\n\t\t'mail_quota' => [\n\t\t\t'title' => '邮箱配额',\n\t\t\t'description' => '新创建邮箱的默认配额（兆字节）。',\n\t\t],\n\t\t'mail_quota_enabled' => [\n\t\t\t'title' => '为客户使用邮箱配额',\n\t\t\t'description' => '激活以在邮箱上使用配额。默认值为<b>否</b>，因为这需要特殊设置。',\n\t\t\t'removelink' => '单击此处清除邮件帐户的所有配额。',\n\t\t\t'enforcelink' => '单击此处对所有用户邮件帐户强制使用默认配额。',\n\t\t],\n\t\t'mail_enable_allow_sender' => [\n\t\t\t'title' => '允许客户使用\"允许的发件人\"',\n\t\t\t'description' => '如果启用，客户可以为邮件账户指定\"允许的发件人\"来发送邮件。<br>默认：关闭',\n\t\t],\n\t\t'mail_allow_external_domains' => [\n\t\t\t'title' => '允许\"允许的发件人\"使用外部域名',\n\t\t\t'description' => '如果启用，客户可以为邮件账户输入任何域名（除了本系统上不拥有的域名）作为\"允许的发件人\"。<br>默认：关闭',\n\t\t],\n\t\t'session_allow_multiple_login' => [\n\t\t\t'title' => '允许多次登录',\n\t\t\t'description' => '如果激活，用户可以多次登录。',\n\t\t],\n\t\t'panel_allow_domain_change_admin' => [\n\t\t\t'title' => '允许在管理员之间移动域',\n\t\t\t'description' => '如果激活，您可以在域设置中更改域的管理员。<br /><b>注意：</b>如果客户没有被分配到与域相同的管理员，则管理员可以看到该客户的所有其他域！',\n\t\t],\n\t\t'panel_allow_domain_change_customer' => [\n\t\t\t'title' => '允许在客户之间移动域',\n\t\t\t'description' => '如果激活，您可以在域名设置中更改域名的客户。<br /><b>注意：</b>froxlor将documentroot更改为新客户的默认homedir（如果激活，则+ domain-folder）',\n\t\t],\n\t\t'specialsettingsforsubdomains' => [\n\t\t\t'description' => '如果是，这些自定义vHost设置将被添加到所有子域;如果没有子域，则将删除特殊设置。',\n\t\t],\n\t\t'panel_password_min_length' => [\n\t\t\t'title' => '最小密码长度',\n\t\t\t'description' => '在这里您可以设置密码的最小长度。\\'0\\'表示：不需要最小长度。',\n\t\t],\n\t\t'system_store_index_file_subs' => [\n\t\t\t'title' => '将默认索引文件也存储到新目录',\n\t\t\t'description' => '如果启用，默认索引文件将存储到新创建的每个子域路径中（如果文件夹已经存在，则不会！）',\n\t\t],\n\t\t'adminmail_return' => [\n\t\t\t'title' => '答复地址',\n\t\t\t'description' => '将电子邮件地址定义为面板发送的邮件的回复地址。',\n\t\t],\n\t\t'adminmail_defname' => 'Panel电子邮件发件人名称',\n\t\t'stdsubdomainhost' => [\n\t\t\t'title' => '客户标准子域',\n\t\t\t'description' => '为客户创建标准子域时应使用的主机名。如果为空，则使用系统主机名。',\n\t\t],\n\t\t'awstats_path' => 'AWStats“awstats_buildstaticpages.pl”的路径',\n\t\t'awstats_conf' => 'AWStats配置路径',\n\t\t'defaultttl' => '绑定的域TTL（以秒为单位）（默认值“604800”= 1周）',\n\t\t'defaultwebsrverrhandler_enabled' => '为所有客户启用默认错误文档',\n\t\t'defaultwebsrverrhandler_err401' => [\n\t\t\t'title' => '401错误页面',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err403' => [\n\t\t\t'title' => '403错误页面',\n\t\t\t'description' => '',\n\t\t],\n\t\t'defaultwebsrverrhandler_err404' => '404错误页面',\n\t\t'defaultwebsrverrhandler_err500' => [\n\t\t\t'title' => '文件/URL错误500',\n\t\t\t'description' => '',\n\t\t],\n\t\t'ftpserver' => [\n\t\t\t'desc' => '如果选择了pureftpd，则会每天创建和更新用户配额的.ftpquota文件',\n\t\t],\n\t\t'customredirect_enabled' => [\n\t\t\t'title' => '允许客户重定向',\n\t\t\t'description' => '允许客户选择将被使用的重定向的http状态代码',\n\t\t],\n\t\t'customredirect_default' => [\n\t\t\t'title' => '默认重定向',\n\t\t\t'description' => '设置默认的重定向代码，如果客户没有自己设置，则应使用该代码',\n\t\t],\n\t\t'mail_also_with_mxservers' => '同时为设置了MX服务器的邮件、imap、pop3和smtp创建\"A记录\"',\n\t\t'froxlordirectlyviahostname' => '通过主机名直接访问froxlor',\n\t\t'panel_password_regex' => [\n\t\t\t'title' => '密码的正则表达式',\n\t\t\t'description' => '在这里，您可以设置一个正则表达式的密码复杂性。<br />空=无具体要求',\n\t\t],\n\t\t'mod_fcgid_ownvhost' => [\n\t\t\t'title' => '为froxlor vHost启用FCGID',\n\t\t\t'description' => '如果启用，froxlor也将在本地用户下运行',\n\t\t],\n\t\t'perl' => [\n\t\t\t'suexecworkaround' => [\n\t\t\t\t'title' => '启用SuExec解决方法',\n\t\t\t\t'description' => '仅当客户文档根不在apache suexec路径内时才启用。<br />如果启用，froxlor将生成一个从客户的perl启用目录+ /cgi-bin/到给定路径的符号链接。<br />请注意，perl将只能在文件夹cgi/cgi-bin/中工作，而不能在文件夹本身中工作（如果没有此修复，perl将工作！）',\n\t\t\t],\n\t\t\t'suexeccgipath' => [\n\t\t\t\t'title' => '客户启用Perl的目录符号链接的路径',\n\t\t\t\t'description' => '只有在启用SuExec解决方法时才需要设置此选项。<br />注意：请确保此路径位于suexec路径内，否则此解决方法将无效',\n\t\t\t],\n\t\t],\n\t\t'awstats_awstatspath' => 'AWStats“awstats.pl”的路径',\n\t\t'awstats_icons' => [\n\t\t\t'title' => 'AWstats icons文件夹的路径',\n\t\t\t'description' => '例如/usr/share/awstats/htdocs/icon/',\n\t\t],\n\t\t'login_domain_login' => '允许使用域登录',\n\t\t'perl_server' => [\n\t\t\t'title' => 'Perl服务器套接字位置',\n\t\t\t'description' => 'A simple guide can be found at: <a target=\"blank\" href=\"https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/\">nginx.com</a>',\n\t\t],\n\t\t'nginx_php_backend' => [\n\t\t\t'title' => 'Nginx PHP后端',\n\t\t\t'description' => '这是PHP进程监听来自nginx的请求的地方，可以是ip：port组合的unix套接字<br /> * 不使用php-fpm',\n\t\t],\n\t\t'phpreload_command' => [\n\t\t\t'title' => 'PHP reload命令',\n\t\t\t'description' => '这是用来重新加载PHP后端，如果有的话是使用<br />默认为空<br /> * 不使用php-fpm',\n\t\t],\n\t\t'phpfpm' => [\n\t\t\t'title' => '启用php-fpm',\n\t\t\t'description' => '<b>This needs a special webserver configuration see <a target=\"_blank\" href=\"https://docs.froxlor.org/latest/admin-guide/configuration/php-fpm/\">PHP-FPM handbook</a></b>',\n\t\t],\n\t\t'phpfpm_settings' => [\n\t\t\t'configdir' => 'php-fpm的配置目录',\n\t\t\t'aliasconfigdir' => '配置别名-php-fpm目录',\n\t\t\t'reload' => 'php-fpm restart命令',\n\t\t\t'pm' => '流程经理控制（下午）',\n\t\t\t'max_children' => [\n\t\t\t\t'title' => '子进程的数量',\n\t\t\t\t'description' => '当pm设置为\\'static\\'时要创建的子进程数和当pm设置为\\'dynamic/ondemand\\'时要创建的子进程的最大数<br />与PHP_FCGI_CHILDREN等效',\n\t\t\t],\n\t\t\t'start_servers' => [\n\t\t\t\t'title' => '启动时创建的子进程数',\n\t\t\t\t'description' => '注意：仅在pm设置为“dynamic”时使用',\n\t\t\t],\n\t\t\t'min_spare_servers' => [\n\t\t\t\t'title' => '所需的最小空闲服务器进程数',\n\t\t\t\t'description' => '注意：仅在pm设置为“dynamic”时使用<br />注意：当pm设置为“dynamic”时，',\n\t\t\t],\n\t\t\t'max_spare_servers' => [\n\t\t\t\t'title' => '所需的空闲服务器进程的最大数量',\n\t\t\t\t'description' => '注意：仅在pm设置为“dynamic”时使用<br />注意：当pm设置为“dynamic”时，',\n\t\t\t],\n\t\t\t'max_requests' => [\n\t\t\t\t'title' => '重生前每个孩子的请求数',\n\t\t\t\t'description' => '对于无休止的请求处理，请指定“0”。等效于PHP_FCGI_MAX_REQUESTS。',\n\t\t\t],\n\t\t\t'idle_timeout' => [\n\t\t\t\t'title' => '空闲超时',\n\t\t\t\t'description' => 'PHP FPM FastCGI的配置文件。',\n\t\t\t],\n\t\t\t'ipcdir' => [\n\t\t\t\t'title' => 'FastCGI IPC目录',\n\t\t\t\t'description' => 'php-fpm套接字将由web服务器存储的目录。<br />此目录必须对Web服务器可读',\n\t\t\t],\n\t\t\t'limit_extensions' => [\n\t\t\t\t'title' => '允许的分机',\n\t\t\t\t'description' => '限制FPM允许解析的主脚本的扩展名。这可以防止Web服务器端的配置错误。您应该只将FPM限制为.php扩展名，以防止恶意用户使用其他扩展名来执行php代码。默认值：.php',\n\t\t\t],\n\t\t\t'envpath' => '要添加到PATH环境中的路径。如果没有PATH环境变量，请留空',\n\t\t\t'override_fpmconfig' => '设置FPM守护程序设置（pm、max_children等）',\n\t\t\t'override_fpmconfig_addinfo' => '<br /><span class=\"text-danger\">仅在\"覆盖FPM守护程序设置\"设置为\"是\"时使用</span>',\n\t\t\t'restart_note' => '注意：配置不会被检查是否有任何错误。如果它包含错误，PHP-FPM可能不会再次启动！',\n\t\t\t'custom_config' => [\n\t\t\t\t'title' => '自定义配置',\n\t\t\t\t'description' => '为每个PHP-FPM版本实例添加自定义配置，例如<i>pm.status_path = /status</i>进行监控。下面的变量可以在这里使用。<strong>注意：配置不会检查任何错误。如果包含错误，PHP-FPM可能无法重新启动！</strong>',\n\t\t\t],\n\t\t\t'allow_all_customers' => [\n\t\t\t\t'title' => '将此配置分配给所有当前现有客户',\n\t\t\t\t'description' => 'Set this to \"true\" if you want to assign this configuration to all currently existing customers so it can be used by them. This setting is not permanent but can be run multiple times.',\n\t\t\t],\n\t\t],\n\t\t'report' => [\n\t\t\t'report' => '启用发送有关Web和流量使用的报告',\n\t\t\t'webmax' => [\n\t\t\t\t'title' => 'Web空间的警告级别（百分比）',\n\t\t\t\t'description' => '有效值为0到150。将此值设置为0将禁用此报告。',\n\t\t\t],\n\t\t\t'trafficmax' => [\n\t\t\t\t'title' => '以百分比表示的流量警告级别',\n\t\t\t\t'description' => '有效值为0到150。将此值设置为0将禁用此报告。',\n\t\t\t],\n\t\t\t'report_web_bccadmin' => [\n\t\t\t\t'title' => 'BCC电子邮件，用于向管理员发送Web使用通知',\n\t\t\t\t'description' => '如果启用，则发送给客户的有关磁盘空间使用情况的警告也会发送给相应的管理员/管理员（BCC）'\n\t\t\t],\n\t\t],\n\t\t'dropdown' => '下拉',\n\t\t'manual' => '手动',\n\t\t'default_theme' => '默认主题',\n\t\t'validate_domain' => '验证域名',\n\t\t'diskquota_enabled' => '是否启用配额？',\n\t\t'diskquota_repquota_path' => [\n\t\t\t'description' => 'repquota 的路径',\n\t\t],\n\t\t'diskquota_quotatool_path' => [\n\t\t\t'description' => 'quotatool 的路径',\n\t\t],\n\t\t'diskquota_customer_partition' => [\n\t\t\t'description' => '存储客户文件的分区',\n\t\t],\n\t\t'vmail_maildirname' => [\n\t\t\t'title' => 'Maildir名称',\n\t\t\t'description' => 'Maildir目录到用户的帐户。通常是\\'Maildir\\'，在某些实现中是\\'. maildir\\'，如果留空则直接到用户的目录。',\n\t\t],\n\t\t'catchall_enabled' => [\n\t\t\t'title' => '使用通配符',\n\t\t\t'description' => '您想为您的客户提供通配符功能吗？',\n\t\t],\n\t\t'apache_24' => [\n\t\t\t'title' => '使用Apache 2.4的修改',\n\t\t\t'description' => '<strong class=\"text-danger\">注意：</strong>仅在您实际安装了Apache 2.4或更高版本时使用<br />否则您的Web服务器将无法启动',\n\t\t],\n\t\t'nginx_fastcgiparams' => [\n\t\t\t'title' => 'fastcgi_params文件的路径',\n\t\t\t'description' => '指定nginx的fastcgi_params文件的路径，包括文件名',\n\t\t],\n\t\t'documentroot_use_default_value' => [\n\t\t\t'title' => '使用域名作为DocumentRoot路径的默认值',\n\t\t\t'description' => '如果启用并且DocumentRoot路径为空，则默认值为（子）域名。<br /><br />示例如下：<br />/var/customers/webs/customer_name/example.com/<br />/var/customers/webs/customer_name/subdomain.example.com/',\n\t\t],\n\t\t'panel_phpconfigs_hidesubdomains' => [\n\t\t\t'title' => '在PHP配置概览中隐藏子域',\n\t\t\t'description' => '如果启用，客户的子域将不会在PHP配置概览中列出，只会显示子域的数量。<br /><br />注意：只有在启用了FCGID或PHP-FPM的情况下才能看到。',\n\t\t],\n\t\t'panel_phpconfigs_hidestdsubdomain' => [\n\t\t\t'title' => '在PHP配置概览中隐藏标准子域',\n\t\t\t'description' => '如果启用，客户的标准子域将不会显示在PHP配置概览中。<br /><br />注意：只有在启用了FCGID或PHP-FPM的情况下才能看到。',\n\t\t],\n\t\t'passwordcryptfunc' => [\n\t\t\t'title' => '选择要使用的密码加密方法',\n\t\t\t'description' => '选择要使用的密码加密方法。如果更改此设置，则仅使用新方法加密新密码。现有密码不会更改。',\n\t\t],\n\t\t'systemdefault' => '系统默认',\n\t\t'panel_allow_theme_change_admin' => '允许管理员更改主题',\n\t\t'panel_allow_theme_change_customer' => '允许客户更改主题',\n\t\t'axfrservers' => [\n\t\t\t'title' => 'AXFR服务器',\n\t\t\t'description' => '允许传输（AXFR）dns区域的IP地址的逗号分隔列表。',\n\t\t],\n\t\t'powerdns_mode' => [\n\t\t\t'title' => 'PowerDNS操作模式',\n\t\t\t'description' => '如果需要DNS复制，请选择PowerDNS模式：Native for no replication（Default）/ Master。',\n\t\t],\n\t\t'customerssl_directory' => [\n\t\t\t'title' => 'Webserver客户SSL证书目录',\n\t\t\t'description' => '客户指定的SSL证书应该创建在哪里？<br /><br /><div class=\"text-danger\">注意：此文件夹的内容会定期删除，因此请避免手动在其中存储数据。</div>',\n\t\t],\n\t\t'allow_error_report_admin' => [\n\t\t\t'title' => '允许管理员/经销商向froxlor报告数据库错误',\n\t\t\t'description' => '请注意：不要向我们发送任何个人（客户）数据！',\n\t\t],\n\t\t'allow_error_report_customer' => [\n\t\t\t'title' => '允许客户向froxlor报告数据库错误',\n\t\t\t'description' => '请注意：不要向我们发送任何个人（客户）数据！',\n\t\t],\n\t\t'mailtraffic_enabled' => [\n\t\t\t'title' => '分析邮件流量',\n\t\t\t'description' => '启用分析邮件服务器日志以计算流量',\n\t\t],\n\t\t'mdaserver' => [\n\t\t\t'title' => 'MDA型',\n\t\t\t'description' => '邮件传递服务器的类型',\n\t\t],\n\t\t'mdalog' => [\n\t\t\t'title' => 'MDA日志',\n\t\t\t'description' => '邮件传递服务器的日志文件',\n\t\t],\n\t\t'mtaserver' => [\n\t\t\t'title' => 'MTA类型',\n\t\t\t'description' => '邮件传输代理的类型',\n\t\t],\n\t\t'mtalog' => [\n\t\t\t'title' => 'MTA日志',\n\t\t\t'description' => '邮件传输代理的日志文件',\n\t\t],\n\t\t'system_cronconfig' => [\n\t\t\t'title' => 'Cron配置文件',\n\t\t\t'description' => 'cron-service配置文件的路径。froxlor将定期自动更新此文件。<br />注意：请<b>确保</b>使用与主froxlor cronjob相同的文件名（默认：/etc/cron.d/froxlor）！<br><br>如果您使用的是<b>FreeBSD</b>，请在这里指定<i>/etc/crontab</i>！',\n\t\t],\n\t\t'system_crondreload' => [\n\t\t\t'title' => 'Cron-daemon reload命令',\n\t\t\t'description' => '指定要执行的命令以重新加载系统cron-daemon',\n\t\t],\n\t\t'system_croncmdline' => [\n\t\t\t'title' => 'Cron执行命令（php二进制）',\n\t\t\t'description' => '执行cron任务的命令。仅在您知道自己在做什么时才更改此选项（默认：\"/usr/bin/nice -n 5 /usr/bin/php -q\"）！',\n\t\t],\n\t\t'system_cron_allowautoupdate' => [\n\t\t\t'title' => '允许自动数据库更新',\n\t\t\t'description' => '<div class=\"text-danger\"><b>注意：</b></div> 此设置允许cronjob绕过froxlor文件和数据库的版本检查，并在发生版本不匹配时运行数据库更新。<br><br><div class=\"text-danger\">自动更新始终会为新设置或更改设置默认值。这可能并不总是适合您的系统。请在激活此选项之前三思而行</div>',\n\t\t],\n\t\t'dns_createhostnameentry' => '为系统主机名创建绑定区域/配置',\n\t\t'panel_password_alpha_lower' => [\n\t\t\t'title' => '小写字符',\n\t\t\t'description' => '密码必须至少包含一个小写字母（a-z）。',\n\t\t],\n\t\t'panel_password_alpha_upper' => [\n\t\t\t'title' => '大写字符',\n\t\t\t'description' => '密码必须包含至少一个大写字母（A-Z）。',\n\t\t],\n\t\t'panel_password_numeric' => [\n\t\t\t'title' => '数字',\n\t\t\t'description' => '密码必须包含至少一个数字（0-9）。',\n\t\t],\n\t\t'panel_password_special_char_required' => [\n\t\t\t'title' => '特殊字符',\n\t\t\t'description' => '密码必须至少包含下面定义的一个字符。',\n\t\t],\n\t\t'panel_password_special_char' => [\n\t\t\t'title' => '特殊字符列表',\n\t\t\t'description' => '如果设置了上述选项，则需要其中一个字符。',\n\t\t],\n\t\t'apache_itksupport' => [\n\t\t\t'title' => '使用Apache ITK-MPM的修改',\n\t\t\t'description' => '<strong class=\"text-danger\">注意：</strong>仅在您启用了Apache ITK-MPM时使用<br />否则您的Web服务器将无法启动',\n\t\t],\n\t\t'letsencryptca' => [\n\t\t\t'title' => 'ACME环境',\n\t\t\t'description' => '用于Let\\'s Encrypt / ZeroSSL证书的环境。',\n\t\t],\n\t\t'letsencryptchallengepath' => [\n\t\t\t'title' => 'Let\\'s Encrypt挑战之路',\n\t\t\t'description' => '应通过全局别名提供Let\\'s Encrypt挑战的目录。',\n\t\t],\n\t\t'letsencryptkeysize' => [\n\t\t\t'title' => '新Let\\'s Encrypt证书的密钥大小',\n\t\t\t'description' => '新Let\\'s Encrypt证书的密钥大小（以位为单位）。',\n\t\t],\n\t\t'letsencryptreuseold' => [\n\t\t\t'title' => '重新使用Let\\'s Encrypt密钥',\n\t\t\t'description' => '如果激活，则每次续订都将使用相同的密钥，否则每次都会生成一个新密钥。',\n\t\t],\n\t\t'leenabled' => [\n\t\t\t'title' => '启用Let\\'s Encrypt',\n\t\t\t'description' => '如果激活，客户可以让froxlor自动为具有ssl IP/端口的域生成和续订Let\\'s Encrypt ssl证书。<br /><br />请记住，您需要通过网络服务器配置启用时，因为这个功能需要一个特殊的配置。',\n\t\t],\n\t\t'caa_entry' => [\n\t\t\t'title' => '生成CAA DNS记录',\n\t\t\t'description' => '自动为使用Let\\'s Encrypt的启用SSL的域生成CAA记录',\n\t\t],\n\t\t'caa_entry_custom' => [\n\t\t\t'title' => '其他CAA DNS记录',\n\t\t\t'description' => 'DNS Certification Authority Authorization (CAA) is an Internet security policy mechanism which allows domain name holders to indicate to certificate authorities<br>whether they are authorized to issue digital certificates for a particular domain name. It does this by means of a new \"CAA\" Domain Name System (DNS) resource record.<br><br>The content of this field will be included into the DNS zone directly (each line results in a CAA record).<br>If Let\\'s Encrypt is enabled for this domain, this entry will always be added automatically and does not need to be added manually:<br><code>0 issue \"letsencrypt.org\"</code> (If domain is a wildcard domain, issuewild will be used instead).<br>To enable Incident Reporting, you can add an <code>iodef</code> record. An example for sending such report to <code>me@example.com</code> would be:<br><code>0 iodef \"mailto:me@example.com\"</code><br><br><strong>Attention:</strong> The code won\\'t be checked for any errors. If it contains errors, your CAA records might not work!',\n\t\t],\n\t\t'exportenabled' => [\n\t\t\t'title' => '为客户启用数据导出',\n\t\t\t'description' => '如果激活，客户将能够安排数据导出作业（cron-export），在其docroot中生成存档（可由客户自行选择）',\n\t\t],\n\t\t'dnseditorenable' => [\n\t\t\t'title' => '启用DNS编辑器',\n\t\t\t'description' => '允许管理员和客户管理域dns条目',\n\t\t],\n\t\t'dns_server' => [\n\t\t\t'title' => 'DNS服务器守护进程',\n\t\t\t'description' => '请记住，必须使用froxlors配置模板来配置守护进程',\n\t\t],\n\t\t'panel_customer_hide_options' => [\n\t\t\t'title' => '在客户面板中隐藏菜单项和流量图表',\n\t\t\t'description' => '选择要在客户面板中隐藏的项目。要选择多个选项，请在选择时按住Ctrl。',\n\t\t],\n\t\t'allow_allow_customer_shell' => [\n\t\t\t'title' => '允许客户为FTP用户启用shell访问',\n\t\t\t'description' => '<strong class=\"text-danger\">请注意：Shell访问允许用户在您的系统上执行各种二进制文件。请极度谨慎地使用。如果您真的知道自己在做什么，请仅激活此选项！！！</strong>',\n\t\t],\n\t\t'available_shells' => [\n\t\t\t'title' => '可用外壳列表',\n\t\t\t'description' => '逗号分隔的shell列表，可供客户为其ftp用户选择。<br><br>请注意，默认的shell<strong>/bin/false</strong>将始终是一个选项（如果启用），即使此设置为空。它是任何情况下ftp用户的默认值。',\n\t\t],\n\t\t'le_froxlor_enabled' => [\n\t\t\t'title' => '为froxlor vhost启用Let\\'s Encrypt',\n\t\t\t'description' => '如果激活，froxlor vhost将自动使用Let\\'s Encrypt证书进行保护。',\n\t\t],\n\t\t'le_froxlor_redirect' => [\n\t\t\t'title' => '为froxlor vhost启用SSL重定向',\n\t\t\t'description' => '如果激活，所有对您的froxlor的http请求将被重定向到相应的SSL站点。',\n\t\t],\n\t\t'option_unavailable_websrv' => '<br><em class=\"text-danger\">仅适用于：%s</em>',\n\t\t'option_unavailable' => '<br><em class=\"text-danger\">由于其他设置，此选项不可用。</em>',\n\t\t'letsencryptacmeconf' => [\n\t\t\t'title' => 'acme.conf代码段的路径',\n\t\t\t'description' => '允许web服务器为acme挑战提供服务的配置片段的文件名。',\n\t\t],\n\t\t'mail_use_smtp' => '设置邮件程序以使用SMTP',\n\t\t'mail_smtp_host' => '指定SMTP服务器',\n\t\t'mail_smtp_usetls' => '启用TLS加密',\n\t\t'mail_smtp_auth' => '启用SMTP身份验证',\n\t\t'mail_smtp_port' => '要连接的TCP端口',\n\t\t'mail_smtp_user' => 'SMTP用户名',\n\t\t'mail_smtp_passwd' => 'SMTP密码',\n\t\t'http2_support' => [\n\t\t\t'title' => 'HTTP/2 支持',\n\t\t\t'description' => '为SSL启用HTTP/2支持。<br><em class=\"text-danger\">仅在您的Web服务器支持此功能时启用（nginx 1.9.5+，apache 2.4.17+）</em>',\n\t\t],\n\t\t'http3_support' => [\n\t\t\t'title' => 'HTTP/3 支持',\n\t\t\t'description' => '为SSL启用HTTP/3支持。<br><em class=\"text-danger\">仅在您的Web服务器支持此功能时启用（nginx 1.25.0+）</em>',\n\t\t],\n\t\t'nssextrausers' => [\n\t\t\t'title' => '使用 libnss-extrausers 代替 libnss-mysql',\n\t\t\t'description' => '不从数据库读取用户，而是从文件读取。请仅在您已经完成所需配置步骤（系统 -> libnss-extrausers）后才激活此项。<br><strong class=\"text-danger\">仅适用于 Debian/Ubuntu（或如果您自己编译了 libnss-extrausers！）</strong>',\n\t\t],\n\t\t'le_domain_dnscheck' => [\n\t\t\t'title' => '使用Let\\'s Encrypt时验证域名的DNS',\n\t\t\t'description' => '如果启用，froxlor将验证请求Let\\'s Encrypt证书的域名是否解析为至少一个系统IP地址。',\n\t\t],\n\t\t'le_domain_dnscheck_resolver' => [\n\t\t\t'title' => '使用外部名称服务器进行DNS验证',\n\t\t\t'description' => '如果设置，froxlor将在使用Let\\'s Encrypt时使用此DNS来验证域的DNS。如果为空，将使用系统的默认DNS解析器。',\n\t\t],\n\t\t'phpsettingsforsubdomains' => [\n\t\t\t'description' => '如果是，所选的php-config将被更新到所有子域',\n\t\t],\n\t\t'leapiversion' => [\n\t\t\t'title' => '选择Let\\'s Encrypt ACME实现',\n\t\t\t'description' => '目前仅支持Let\\'s Encrypt的ACME v2实现。',\n\t\t],\n\t\t'enable_api' => [\n\t\t\t'title' => '启用外部API使用',\n\t\t\t'description' => '为了使用froxlor API，您需要激活此选项。如需更多详细信息，请参阅 <a href=\"https://docs.froxlor.org/latest/api-guide/\" target=\"_new\">https://docs.froxlor.org/</a>',\n\t\t],\n\t\t'api_customer_default' => '新客户的\"允许API访问\"默认值',\n\t\t'dhparams_file' => [\n\t\t\t'title' => 'DHParams文件（Diffie-Hellman密钥交换）',\n\t\t\t'description' => '如果在此处指定了dhparams.pem文件，则它将包含在Web服务器配置中。保留为空将禁用。<br>例如：/etc/ssl/webserver/dhparams.pem<br><br>如果该文件不存在，则会使用以下命令自动创建该文件：<code>openssl dhparam -out /etc/ssl/webserver/dhparams.pem 4096</code>。建议在此处指定该文件之前创建该文件，因为创建过程需要相当长的时间并会阻塞cronjob。',\n\t\t],\n\t\t'errorlog_level' => [\n\t\t\t'title' => '错误日志级别',\n\t\t\t'description' => '指定错误日志级别。Apache用户的默认值是 \"warn\"，Nginx用户的默认值是 \"error\"。',\n\t\t],\n\t\t'letsencryptecc' => [\n\t\t\t'title' => '颁发ECC / ECDSA证书',\n\t\t\t'description' => '如果设置为有效密钥大小，则颁发的证书将使用ECC / ECDSA',\n\t\t],\n\t\t'froxloraliases' => [\n\t\t\t'title' => 'froxlor vhost的域名别名',\n\t\t\t'description' => '要作为服务器别名添加到froxlor vhost的域的逗号分隔列表',\n\t\t],\n\t\t'default_sslvhostconf' => [\n\t\t\t'title' => '默认SSL vHost设置',\n\t\t\t'description' => '此字段的内容将直接包含在此ip/port vHost容器中。您可以使用以下变量：<br/><code>{域}</code>、<code>{DOCROOT}</code>、<code>{客户}</code>、<code>{IP}</code>、<code>{端口}</code>、<code>{方案}</code>、<code>{FPMSOCKET}</code>（如果适用）<br/>注意：代码不会被检查是否有任何错误。如果它包含错误，Web服务器可能无法再次启动！',\n\t\t],\n\t\t'includedefault_sslvhostconf' => '在SSL-vHost中包括非SSL vHost设置',\n\t\t'apply_specialsettings_default' => '编辑域名时，\"将特殊设置应用到所有子域名 (*.example.com)\"设置的默认值',\n\t\t'apply_phpconfigs_default' => '编辑域名时，\"将php-config应用到所有子域名\"设置的默认值',\n\t\t'awstats' => [\n\t\t\t'logformat' => [\n\t\t\t\t'title' => 'LogFormat设置',\n\t\t\t\t'description' => '如果您为Web服务器使用了自定义日志格式，您也需要更改awstats的LogFormat。<br/>默认值为1。如需更多信息，请参阅<a target=\"_blank\" href=\"https://awstats.sourceforge.io/docs/awstats_config.html#LogFormat\">文档</a>。',\n\t\t\t],\n\t\t],\n\t\t'hide_incompatible_settings' => '隐藏不兼容的设置',\n\t\t'soaemail' => 'SOA记录中使用的邮件地址（如果为空，则默认为面板设置中的发件人地址）',\n\t\t'imprint_url' => [\n\t\t\t'title' => '法律的注释/印记的URL',\n\t\t\t'description' => '指定一个指向您的法律的注释/印记网站的URL。登录后，该链接将显示在登录屏幕和页脚上。',\n\t\t],\n\t\t'terms_url' => [\n\t\t\t'title' => '使用条款的URL',\n\t\t\t'description' => '指定一个指向您的使用条款网站的URL。登录后，该链接将在登录屏幕和页脚上显示。',\n\t\t],\n\t\t'privacy_url' => [\n\t\t\t'title' => '隐私政策',\n\t\t\t'description' => '指定您的隐私政策网站/印记网站的URL。登录时，该链接将在登录屏幕和页脚上可见。',\n\t\t],\n\t\t'logo_image_header' => [\n\t\t\t'title' => 'Logo图片（表头）',\n\t\t\t'description' => '上传您自己的徽标图像，以便在登录后显示在标题中（建议高度30px）',\n\t\t],\n\t\t'logo_image_login' => [\n\t\t\t'title' => 'Logo图片（登录）',\n\t\t\t'description' => '上传您自己的Logo图片以便在登录时显示',\n\t\t],\n\t\t'logo_overridetheme' => [\n\t\t\t'title' => '覆盖主题中定义的Logo（标题和登录，见下文）',\n\t\t\t'description' => '如果您打算使用上传的Logo，需要将其设置为\"true\"；或者您仍然可以使用基于主题的 \"logo_custom.png\" 和 \"logo_custom_login.png\" 方式。',\n\t\t],\n\t\t'logo_overridecustom' => [\n\t\t\t'title' => '覆盖主题中定义的自定义Logo（logo_custom.png 和 logo_custom_login.png）（标题和登录，见下文）',\n\t\t\t'description' => '如果您想忽略主题特定的自定义Logo（标题和登录）并使用\"Logo图片\"，请将此设置为\"true\"。',\n\t\t],\n\t\t'createstdsubdom_default' => [\n\t\t\t'title' => 'Preselected value for \"Create standard subdomain\" when creating a customer',\n\t\t\t'description' => '',\n\t\t],\n\t\t'froxlorusergroup' => [\n\t\t\t'title' => '所有客户用户的自定义系统组',\n\t\t\t'description' => '需要使用libnss-extrausers（系统设置）才能生效。空值将跳过创建或删除现有组。',\n\t\t],\n\t\t'acmeshpath' => [\n\t\t\t'title' => 'acme.sh 的路径',\n\t\t\t'description' => '将其设置为acme.sh的安装路径，包含acme.sh脚本<br>默认值为 <b>/root/.acme.sh/acme.sh</b>',\n\t\t],\n\t\t'update_channel' => [\n\t\t\t'title' => 'froxlor更新通道',\n\t\t\t'description' => '选择froxlor的更新通道。默认值为\"stable\"（稳定版）',\n\t\t],\n\t\t'uc_stable' => '稳定',\n\t\t'uc_testing' => '测试',\n\t\t'uc_nightly' => '每晚',\n\t\t'traffictool' => [\n\t\t\t'toolselect' => '流量分析器',\n\t\t\t'webalizer' => 'Webalizer',\n\t\t\t'awstats' => 'AWStats',\n\t\t\t'goaccess' => 'GoAccess'\n\t\t],\n\t\t'requires_reconfiguration' => '更改此设置可能需要重新配置以下服务：<br><strong>%s</strong>',\n\t\t'req_limit_per_interval' => [\n\t\t\t'title' => '每个时间间隔的HTTP请求数',\n\t\t\t'description' => '限制每个时间间隔（见下文）到froxlor的HTTP请求数，默认值为\"60\"',\n\t\t],\n\t\t'req_limit_interval' => [\n\t\t\t'title' => '速率限制间隔',\n\t\t\t'description' => '指定HTTP请求数的时间（秒），默认值为\"60\"',\n\t\t],\n\t\t'option_requires_otp' => '此设置需要OTP验证',\n\t\t'panel_menu_collapsed' => [\n\t\t\t'title' => '折叠菜单部分',\n\t\t\t'description' => '如果取消激活，左侧菜单部分将始终展开。',\n\t\t],\n\t\t'le_renew_services' => [\n\t\t\t'title' => '为这些服务使用froxlor Let\\'s Encrypt证书',\n\t\t\t'description' => '如果设置为none（或下面的renew-hook命令为空），则不会对所选服务进行有关ssl的配置调整。<br><br>所选服务的reload-command应添加到renew-hook命令中，否则可能无法正确应用配置更改或更新的证书。',\n\t\t],\n\t\t'le_renew_hook' => [\n\t\t\t'title' => 'Let\\'s Encrypt renew-hook命令',\n\t\t\t'description' => '将此设置为重新启动上面选择的服务的命令，以便服务正确使用续订的证书。',\n\t\t],\n\t],\n\t'spf' => [\n\t\t'use_spf' => [\n\t\t\t'title' => '为域名启用SPF？',\n\t\t\t'description' => '需要为域名配置特定的DNS条目。如果您不使用域名服务器功能，则必须手动管理这些条目。',\n\t\t],\n\t\t'spf_entry' => '所有域名的SPF条目',\n\t],\n\t'dmarc' => [\n\t\t'use_dmarc' => [\n\t\t\t'title' => '为域名启用DMARC？',\n\t\t\t'description' => '需要为域名配置特定的DNS条目。如果您不使用域名服务器功能，则必须手动管理这些条目。',\n\t\t],\n\t\t'dmarc_entry' => '所有域名的DMARC条目',\n\t],\n\t'ssl_certificates' => [\n\t\t'certificate_for' => '证书',\n\t\t'valid_from' => '有效期从',\n\t\t'valid_until' => '有效期至',\n\t\t'issuer' => '颁发者',\n\t],\n\t'success' => [\n\t\t'success' => '信息',\n\t\t'clickheretocontinue' => '点击此处继续',\n\t\t'settingssaved' => '设置已成功保存。',\n\t\t'rebuildingconfigs' => '已成功插入重新生成配置文件的任务',\n\t\t'domain_import_successfully' => '已成功导入 %s 个域名。',\n\t\t'exportscheduled' => '您的导出任务已安排。请等待处理',\n\t\t'exportaborted' => '您的计划导出已取消',\n\t\t'dns_record_added' => 'DNS记录添加成功',\n\t\t'dns_record_deleted' => 'DNS记录删除成功',\n\t\t'testmailsent' => '测试邮件发送成功',\n\t\t'settingsimported' => '设置导入成功',\n\t\t'sent_error_report' => '错误报告发送成功。感谢您的贡献。',\n\t],\n\t'tasks' => [\n\t\t'outstanding_tasks' => '待处理的cron任务',\n\t\t'REBUILD_VHOST' => '重新生成Web服务器配置',\n\t\t'CREATE_HOME' => '正在添加新客户 %s',\n\t\t'REBUILD_DNS' => '重新生成BIND配置',\n\t\t'CREATE_FTP' => '正在为新的FTP用户创建目录',\n\t\t'DELETE_CUSTOMER_FILES' => '正在删除客户文件 %s',\n\t\t'noneoutstanding' => 'froxlor目前没有待处理的任务',\n\t\t'DELETE_EMAIL_DATA' => '删除客户电子邮件数据。',\n\t\t'DELETE_FTP_DATA' => '删除客户FTP账户数据。',\n\t\t'REBUILD_RSPAMD' => '正在重新生成反垃圾邮件配置。',\n\t\t'CREATE_QUOTA' => '在文件系统上设置配额',\n\t\t'REBUILD_CRON' => '重新生成cron.d文件',\n\t\t'CREATE_CUSTOMER_DATADUMP' => '客户 %s 的数据导出任务',\n\t\t'DELETE_DOMAIN_PDNS' => '从PowerDNS数据库中删除域名 %s',\n\t\t'DELETE_DOMAIN_SSL' => '删除域名 %s 的SSL文件',\n\t\t'UPDATE_LE_SERVICES' => '更新Let\\'s Encrypt的系统服务',\n\t],\n\t'terms' => '使用条款',\n\t'traffic' => [\n\t\t'month' => '月',\n\t\t'day' => '天',\n\t\t'months' => [\n\t\t\t1 => '一月',\n\t\t\t2 => '二月',\n\t\t\t3 => '三月',\n\t\t\t4 => '四月',\n\t\t\t5 => '五月',\n\t\t\t6 => '六月',\n\t\t\t7 => '七月',\n\t\t\t8 => '八月',\n\t\t\t9 => '九月',\n\t\t\t10 => '十月',\n\t\t\t11 => '十一月',\n\t\t\t12 => '十二月',\n\t\t\t'jan' => '一月',\n\t\t\t'feb' => '二月',\n\t\t\t'mar' => '三月',\n\t\t\t'apr' => '四月',\n\t\t\t'may' => '五月',\n\t\t\t'jun' => '六月',\n\t\t\t'jul' => '七月',\n\t\t\t'aug' => '八月',\n\t\t\t'sep' => '九月',\n\t\t\t'oct' => '十月',\n\t\t\t'nov' => '十一月',\n\t\t\t'dec' => '十二月',\n\t\t\t'total' => '总计',\n\t\t],\n\t\t'mb' => '流量',\n\t\t'sumtotal' => '总流量',\n\t\t'sumhttp' => 'HTTP流量',\n\t\t'sumftp' => 'FTP流量',\n\t\t'summail' => '邮件流量',\n\t\t'customer' => '客户',\n\t\t'domain' => '域名',\n\t\t'trafficoverview' => '流量概览',\n\t\t'bycustomers' => '按客户统计流量',\n\t\t'details' => '详情',\n\t\t'http' => 'HTTP',\n\t\t'ftp' => 'FTP',\n\t\t'mail' => '邮件',\n\t\t'nocustomers' => '您至少需要一位客户才能查看流量报告。',\n\t\t'top5customers' => '前5位客户',\n\t\t'nodata' => '未找到指定范围内的数据。',\n\t\t'ranges' => [\n\t\t\t'last24h' => '过去24小时',\n\t\t\t'last7d' => '过去7天',\n\t\t\t'last30d' => '过去30天',\n\t\t\t'cm' => '本月',\n\t\t\t'last3m' => '过去3个月',\n\t\t\t'last6m' => '过去6个月',\n\t\t\t'last12m' => '过去12个月',\n\t\t\t'cy' => '本年',\n\t\t],\n\t\t'byrange' => '按范围指定',\n\t],\n\t'translator' => '',\n\t'update' => [\n\t\t'updateinprogress_onlyadmincanlogin' => '已安装较新版本的froxlor，但尚未完成设置。<br />只有管理员才能登录并完成更新。',\n\t\t'update' => 'froxlor更新',\n\t\t'proceed' => '继续',\n\t\t'update_information' => [\n\t\t\t'part_a' => 'froxlor文件已更新到版本 <strong>%s</strong>。当前安装的版本为 <strong>%s</strong>。',\n\t\t\t'part_b' => '<br /><br />在更新完成之前，客户将无法登录。<br /><strong>继续吗？</strong>',\n\t\t],\n\t\t'noupdatesavail' => '您已经安装了froxlor的最新%s版本。',\n\t\t'description' => '为froxlor安装运行数据库更新',\n\t\t'uc_newinfo' => '有更新的%s版本可用：\"%s\"（您当前的版本是：%s）',\n\t\t'notify_subject' => '有新的更新可用',\n\t\t'dbupdate_required' => 'froxlor文件已更新，需要更新数据库',\n\t],\n\t'usersettings' => [\n\t\t'custom_notes' => [\n\t\t\t'title' => '自定义备注',\n\t\t\t'description' => '您可以在这里自由添加任何您想要/需要的备注。它们将显示在相应用户的管理员/客户概览中。<br>支持Markdown，HTML将被移除。',\n\t\t\t'show' => '在用户的仪表板上显示您的备注',\n\t\t],\n\t\t'api_allowed' => [\n\t\t\t'title' => '允许API访问',\n\t\t\t'description' => '在设置中启用后，此用户可以创建API密钥并访问froxlor API',\n\t\t\t'notice' => '您的账户不允许访问API。',\n\t\t],\n\t\t'shell_allowed' => [\n\t\t\t'title' => '允许Shell访问',\n\t\t\t'description' => '在设置中启用后，此用户可以将Shell访问权限分配给FTP用户',\n\t\t],\n\t\t'gui_access' => [\n\t\t\t'title' => '允许WebUI登录',\n\t\t\t'description' => '禁用时，用户无法登录到froxlor Web界面，但所有服务（Web、FTP、邮件、数据库、API访问等）将正常工作。',\n\t\t],\n\t],\n\t'install' => [\n\t\t'slogan' => 'froxlor服务器管理面板',\n\t\t'preflight' => '系统检查',\n\t\t'critical_error' => '严重错误',\n\t\t'suggestions' => '非必需但建议',\n\t\t'phpinfosuccess' => '您的系统正在运行PHP %s',\n\t\t'suggestionsnote' => '没有阻止安装的严重错误，但请遵循以下建议以获得最佳体验。',\n\t\t'phpinfowarn' => '您的系统运行的PHP版本低于 %s',\n\t\t'phpinfoupdate' => '将您当前的PHP版本从%s更新到%s或更高版本',\n\t\t'start_installation' => '开始安装',\n\t\t'check_again' => '重新检查',\n\t\t'switchmode_advanced' => '显示高级选项',\n\t\t'switchmode_basic' => '隐藏高级选项',\n\t\t'dependency_check' => [\n\t\t\t'title' => '欢迎使用froxlor',\n\t\t\t'description' => '我们检查系统依赖项，以确保所有必需的PHP扩展和模块都已启用，从而确保froxlor正常运行。',\n\t\t],\n\t\t'database' => [\n\t\t\t'top' => '数据库',\n\t\t\t'title' => '创建数据库和用户',\n\t\t\t'description' => 'froxlor需要一个数据库，还需要一个<a href=\"https://docs.froxlor.org/latest/general/installation/tarball.html#_3-create-privileged-database-user\" target=\"_blank\">特权用户</a>才能创建用户和数据库（GRANT选项）。在此过程中将创建指定的数据库和非特权数据库用户。特权用户必须存在。',\n\t\t\t'user' => '非特权数据库用户',\n\t\t\t'dbname' => '数据库名称',\n\t\t\t'force_create' => '备份并覆盖数据库（如果存在）？',\n\t\t],\n\t\t'admin' => [\n\t\t\t'top' => '管理员用户',\n\t\t\t'title' => '让我们创建主管理员用户。',\n\t\t\t'description' => '该用户将被授予所有权限来调整设置以及添加/更新/删除客户、域名等资源。',\n\t\t\t'use_admin_email_as_sender' => '使用上面的电子邮件地址作为发件人地址。如果未选中，请在下方指定发件人地址。',\n\t\t\t'use_autogenerated_email_as_sender' => '留空默认为：admin@服务器名',\n\t\t],\n\t\t'system' => [\n\t\t\t'top' => '系统设置',\n\t\t\t'title' => '关于您服务器的详细信息',\n\t\t\t'description' => '在这里设置您的环境以及服务器相关数据和选项，让froxlor了解您的系统。这些值对于系统配置和运行至关重要。',\n\t\t\t'ipv4' => '主IPv4地址（如果适用）',\n\t\t\t'ipv6' => '主IPv6地址（如果适用）',\n\t\t\t'servername' => '服务器名称（FQDN，非IP地址）',\n\t\t\t'phpbackend' => 'PHP后端',\n\t\t\t'activate_newsfeed' => '启用官方新闻源<br><small>（外部来源：https://inside.froxlor.org/news/）</small>',\n\t\t],\n\t\t'install' => [\n\t\t\t'top' => '完成设置',\n\t\t\t'title' => '最后一步...',\n\t\t\t'description' => '根据您在安装过程中提供的数据，下面的命令将在您的系统上下载、安装和配置所需的服务。<br><br><span class=\"text-danger\">请确保在服务器的shell/终端上以<b>root</b>身份运行以下命令，并<b>注意</b>此命令将<b>覆盖</b>任何现有配置（会创建备份）！。<br>如果您不想覆盖任何配置，请在页面底部选择<i>我将手动配置服务</i>！</span>',\n\t\t\t'runcmd' => '运行以下命令完成安装：',\n\t\t\t'manual_config' => '我将手动配置服务，只需带我到登录页面',\n\t\t\t'waitforconfig' => '正在等待服务配置...',\n\t\t],\n\t\t'errors' => [\n\t\t\t'wrong_ownership' => '确保froxlor文件属于%s：%s',\n\t\t\t'missing_extensions' => '以下PHP扩展是必需的，但未安装',\n\t\t\t'suggestedextensions' => '以下PHP扩展无法找到，但建议使用',\n\t\t\t'databaseexists' => '数据库已存在，请设置覆盖选项以重新生成或选择其他名称',\n\t\t\t'unabletocreatedb' => '无法创建测试数据库',\n\t\t\t'unabletodropdb' => '无法删除测试数据库',\n\t\t\t'mysqlusernameexists' => '为非特权用户指定的用户已存在。请使用其他用户名或先将其删除。',\n\t\t\t'unabletocreateuser' => '无法创建测试用户',\n\t\t\t'unabletodropuser' => '无法删除测试用户',\n\t\t\t'unabletoflushprivs' => '给定的特权用户无法刷新权限',\n\t\t\t'nov4andnov6ip' => '必须提供IPv4或IPv6地址',\n\t\t\t'servernameneedstobevalid' => '给定的服务器名称似乎不是FQDN或主机名',\n\t\t\t'websrvuserdoesnotexist' => '给定的Web服务器用户似乎在系统上不存在',\n\t\t\t'websrvgrpdoesnotexist' => '给定的Web服务器用户组似乎在系统上不存在',\n\t\t\t'notyetconfigured' => '似乎服务尚未配置（成功）。请运行下面显示的命令或选中复选框以稍后执行。',\n\t\t\t'mandatory_field_not_set' => '必填字段\"%s\"未设置！',\n\t\t\t'unexpected_database_error' => '发生意外的数据库异常。%s',\n\t\t\t'sql_import_failed' => '导入SQL数据失败！',\n\t\t\t'unprivileged_sql_connection_failed' => '初始化非特权SQL连接失败！',\n\t\t\t'privileged_sql_connection_failed' => '初始化特权SQL连接失败！',\n\t\t\t'mysqldump_backup_failed' => '无法创建数据库备份，我们从mysqldump收到一个错误。',\n\t\t\t'sql_backup_file_missing' => '无法创建数据库备份，备份文件不存在。',\n\t\t\t'backup_binary_missing' => '无法创建数据库备份，请确保已安装mysqldump。',\n\t\t\t'creating_configfile_failed' => '无法创建配置文件，无法写入文件。',\n\t\t\t'database_already_exiting' => '我们发现了一个数据库，不允许覆盖它！'\n\t\t]\n\t],\n\t'welcome' => [\n\t\t'title' => '欢迎使用froxlor！',\n\t\t'config_note' => '为了让froxlor能够与后端正常通信，您必须对其进行配置。',\n\t\t'config_now' => '立即配置'\n\t],\n];\n"
  },
  {
    "path": "logfiles_viewer.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nif (!defined('AREA')) {\n\theader(\"Location: index.php\");\n\texit();\n}\n\nuse Froxlor\\Api\\Commands\\SubDomains;\nuse Froxlor\\Customer\\Customer;\nuse Froxlor\\FileDir;\nuse Froxlor\\Settings;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// This file is being included in admin_domains and customer_domains\n// and therefore does not need to require lib/init.php\n\n$domain_id = (int)Request::any('domain_id');\n$last_n = (int)Request::any('number_of_lines', 100);\n\n// user's with logviewenabled = false\nif (AREA != 'admin' && $userinfo['logviewenabled'] != '1') {\n\t// back to domain overview\n\tResponse::redirectTo($filename, [\n\t\t'page' => 'domains'\n\t]);\n}\n\nif (function_exists('exec')) {\n\t// get domain-info\n\ttry {\n\t\t$json_result = SubDomains::getLocal($userinfo, [\n\t\t\t'id' => $domain_id\n\t\t])->get();\n\t} catch (Exception $e) {\n\t\tResponse::dynamicError($e->getMessage());\n\t}\n\t$domain = json_decode($json_result, true)['data'];\n\n\tif ($domain['email_only']) {\n\t\tResponse::dynamicError(\"There are no webserver logfiles for email only domains.\");\n\t}\n\n\t$speciallogfile = '';\n\tif ($domain['speciallogfile'] == '1') {\n\t\tif ($domain['parentdomainid'] == '0') {\n\t\t\t$speciallogfile = '-' . $domain['domain'];\n\t\t} else {\n\t\t\t$speciallogfile = '-' . $domain['parentdomain'];\n\t\t}\n\t}\n\t// The normal access/error - logging is enabled\n\t$error_log = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . Customer::getCustomerDetail($domain['customerid'], 'loginname') . $speciallogfile . '-error.log');\n\t$access_log = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . Customer::getCustomerDetail($domain['customerid'], 'loginname') . $speciallogfile . '-access.log');\n\n\t// error log\n\tif (file_exists($error_log)) {\n\t\t$result = FileDir::safe_exec('tail -n ' . $last_n . ' ' . escapeshellarg($error_log));\n\t\t$error_log_content = implode(\"\\n\", $result);\n\t} else {\n\t\t$error_log_content = \"Error-Log\" . (AREA == 'admin' ? \" '\" . $error_log . \"'\" : \"\") . \" does not seem to exist\";\n\t}\n\n\t// access log\n\tif (file_exists($access_log)) {\n\t\t$result = FileDir::safe_exec('tail -n ' . $last_n . ' ' . escapeshellarg($access_log));\n\t\t$access_log_content = implode(\"\\n\", $result);\n\t} else {\n\t\t$access_log_content = \"Access-Log\" . (AREA == 'admin' ? \" '\" . $access_log . \"'\" : \"\") . \" does not seem to exist\";\n\t}\n\n\tUI::view('user/logfiles.html.twig', [\n\t\t'error_log_content' => $error_log_content,\n\t\t'access_log_content' => $access_log_content,\n\t\t'actions_links' => [\n\t\t\t[\n\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domains',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $domain_id\n\t\t\t\t]),\n\t\t\t\t'label' => lng('admin.domain_edit'),\n\t\t\t\t'icon' => 'fa-solid fa-pen'\n\t\t\t],\n\t\t\t[\n\t\t\t\t'href' => $linker->getLink(['section' => 'domains', 'page' => 'domains']),\n\t\t\t\t'label' => lng('panel.backtooverview'),\n\t\t\t\t'icon' => 'fa-solid fa-reply'\n\t\t\t]\n\t\t]\n\t]);\n} else {\n\tif (AREA == 'admin') {\n\t\tResponse::dynamicError('You need to allow the exec() function in the froxlor-vhost php-config');\n\t} else {\n\t\tResponse::dynamicError('Required function exec() is not allowed. Please contact the system administrator.');\n\t}\n}\n"
  },
  {
    "path": "logs/index.html",
    "content": ""
  },
  {
    "path": "package.json",
    "content": "{\n\t\"name\": \"froxlor\",\n\t\"private\": true,\n\t\"scripts\": {\n\t\t\"dev\": \"vite\",\n\t\t\"build\": \"vite build\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@fortawesome/fontawesome-free\": \"^6.7.2\",\n\t\t\"@popperjs/core\": \"^2.11.8\",\n\t\t\"@vitejs/plugin-vue\": \"^5.2.1\",\n\t\t\"axios\": \"^1.15.2\",\n\t\t\"bootstrap\": \"^5.3.3\",\n\t\t\"chart.js\": \"^4.4.8\",\n\t\t\"jquery\": \"^3.7.1\",\n\t\t\"jquery-validation\": \"^1.21.0\",\n\t\t\"laravel-vite-plugin\": \"^1.2.0\",\n\t\t\"lodash\": \"^4.18.1\",\n\t\t\"postcss\": \"^8.5.13\",\n\t\t\"resolve-url-loader\": \"^5.0.0\",\n\t\t\"sass\": \"^1.85.1\",\n\t\t\"vite\": \"^6.4.2\",\n\t\t\"vue\": \"^3.5.13\"\n\t},\n\t\"engines\": {\n\t\t\"node\": \">=22\"\n\t}\n}\n"
  },
  {
    "path": "phpcs.xml",
    "content": "<?xml version=\"1.0\"?>\n<ruleset name=\"PSR2-Froxlor\">\n\t<description>PSR2 with tabs instead of spaces.</description>\n\t<config name=\"show_progress\" value=\"1\" />\n\t<config name=\"ignore_warnings_on_exit\" value=\"1\" />\n\t<arg name=\"tab-width\" value=\"4\" />\n\t<!-- Show sniff codes in all reports -->\n\t<arg value=\"s\" />\n\t<rule ref=\"PSR2\">\n\t\t<exclude name=\"Generic.WhiteSpace.DisallowTabIndent\" />\n\t\t<exclude name=\"Generic.Files.LineLength\" />\n\t\t<exclude\n\t\t\tname=\"Generic.ControlStructures.InlineControlStructure\" />\n\t\t<exclude\n\t\t\tname=\"Squiz.WhiteSpace.ControlStructureSpacing.SpacingAfterOpen\" />\n\t\t<exclude name=\"Squiz.WhiteSpace.SuperfluousWhitespace.EndLine\" />\n\t</rule>\n\n\t<rule ref=\"PSR1.Classes.ClassDeclaration.MissingNamespace\">\n\t\t<exclude-pattern>tests/</exclude-pattern>\n\t</rule>\n\n\t<rule ref=\"PSR1.Methods.CamelCapsMethodName.NotCamelCaps\">\n\t\t<exclude-pattern>lib/Froxlor/Settings.php</exclude-pattern>\n\t\t<exclude-pattern>lib/Froxlor/FileDir.php</exclude-pattern>\n\t\t<exclude-pattern>lib/Froxlor/Validate/Validate.php</exclude-pattern>\n\t\t<exclude-pattern>lib/Froxlor/Database/Database.php</exclude-pattern>\n\t\t<exclude-pattern>lib/Froxlor/UI/Response.php</exclude-pattern>\n\t</rule>\n\n\t<rule ref=\"PSR1.Files.SideEffects.FoundWithSymbols\">\n\t\t<exclude-pattern>tests/bootstrap.php</exclude-pattern>\n\t</rule>\n</ruleset>\n"
  },
  {
    "path": "phpdox.xml",
    "content": "<phpdox xmlns=\"http://xml.phpdox.net/config\">\n\t<project name=\"froxlor\" source=\"${basedir}/lib/Froxlor\"\n\t\tworkdir=\"${basedir}/build/phpdox\">\n\t\t<collector publiconly=\"true\" backend=\"parser\">\n\t\t\t<include mask=\"*.php\" />\n\t\t</collector>\n\n\t\t<generator output=\"${basedir}/build\">\n\t\t\t<enrich base=\"${basedir}/build\">\n\t\t\t\t<!-- add phploc output -->\n\t\t\t\t<source type=\"phploc\">\n\t\t\t\t\t<file name=\"logs/phploc.xml\" />\n\t\t\t\t</source>\n\n\t\t\t\t<!-- PHP Code Sniffer findings -->\n\t\t\t\t<source type=\"phpcs\">\n\t\t\t\t\t<file name=\"logs/checkstyle-standard.xml\" />\n\t\t\t\t</source>\n\n                                <!-- PHP Code Sniffer PHPCompatibility -->\n                                <source type=\"phpcs\">\n                                        <file name=\"logs/checkstyle-compat.xml\" />\n                                </source>\n\n\t\t\t\t<!-- PHPMessDetector -->\n\t\t\t\t<source type=\"pmd\">\n\t\t\t\t\t<file name=\"logs/pmd.xml\" />\n\t\t\t\t</source>\n\n\t\t\t\t<!-- PHPUnit Coverage XML <source type=\"phpunit\"> </source> -->\n\t\t\t</enrich>\n\t\t\t<build engine=\"html\" enabled=\"true\" output=\"api\">\n\t\t\t\t<file extension=\"html\" />\n\t\t\t</build>\n\t\t</generator>\n\t</project>\n</phpdox>\n"
  },
  {
    "path": "phpmd.xml",
    "content": "<?xml version=\"1.0\"?>\n<ruleset name=\"froxlor ruleset\"\n\txmlns=\"http://pmd.sf.net/ruleset/1.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd\"\n\txsi:noNamespaceSchemaLocation=\"http://pmd.sf.net/ruleset_xml_schema.xsd\">\n\t<description>\n\t\tfroxlor ruleset.\n\t</description>\n\n\t<rule ref=\"rulesets/design.xml\">\n\t\t<exclude name=\"NumberOfChildren\" />\n\t</rule>\n\t<rule ref=\"rulesets/unusedcode.xml\" />\n\n\t<rule ref=\"rulesets/codesize.xml\">\n\t\t<exclude name=\"CyclomaticComplexity\" />\n\t\t<exclude name=\"ExcessiveClassComplexity\" />\n\t\t<exclude name=\"ExcessiveClassLength\" />\n\t\t<exclude name=\"ExcessiveMethodLength\" />\n\t\t<exclude name=\"NPathComplexity\" />\n\t</rule>\n\n\t<rule ref=\"rulesets/naming.xml\">\n\t\t<exclude name=\"ShortVariable\" />\n\t\t<exclude name=\"LongVariable\" />\n\t</rule>\n\n\t<rule ref=\"rulesets/naming.xml/ShortVariable\">\n\t\t<properties>\n\t\t\t<property name=\"exceptions\" value=\"id,ip\" />\n\t\t</properties>\n\t</rule>\n\n\t<rule ref=\"rulesets/codesize.xml/CyclomaticComplexity\">\n\t\t<priority>1</priority>\n\t\t<properties>\n\t\t\t<property name=\"reportLevel\" value=\"150\" />\n\t\t</properties>\n\t</rule>\n\n\t<rule ref=\"rulesets/codesize.xml/ExcessiveClassComplexity\">\n\t\t<properties>\n\t\t\t<property name=\"maximum\" value=\"300\" />\n\t\t</properties>\n\t</rule>\n\n</ruleset>\n"
  },
  {
    "path": "phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" backupGlobals=\"false\" backupStaticAttributes=\"false\" colors=\"false\" convertErrorsToExceptions=\"true\" convertNoticesToExceptions=\"true\" convertWarningsToExceptions=\"true\" processIsolation=\"false\" stopOnFailure=\"false\" bootstrap=\"tests/bootstrap.php\" xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/9.3/phpunit.xsd\">\n  <coverage processUncoveredFiles=\"true\">\n    <include>\n      <directory suffix=\".php\">./lib/Froxlor</directory>\n    </include>\n    <report>\n      <clover outputFile=\"build/logs/clover.xml\"/>\n      <crap4j outputFile=\"build/logs/crap4j.xml\"/>\n      <html outputDirectory=\"build/coverage\" lowUpperBound=\"35\" highLowerBound=\"70\"/>\n    </report>\n  </coverage>\n  <testsuites>\n    <testsuite name=\"froxlor\">\n      <!-- we need to specify the order of the tests for dependency-reasons -->\n      <directory>tests/Global</directory>\n      <directory>tests/Admins</directory>\n      <directory>tests/Customers</directory>\n      <directory>tests/IpsAndPorts</directory>\n      <directory>tests/Domains</directory>\n      <directory>tests/Cronjobs</directory>\n      <directory>tests/SubDomains</directory>\n      <directory>tests/Certificates</directory>\n      <directory>tests/Ftps</directory>\n      <directory>tests/Emails</directory>\n      <directory>tests/Extras</directory>\n      <directory>tests/Backup</directory>\n      <directory>tests/DomainZones</directory>\n      <directory>tests/Mysqls</directory>\n      <directory>tests/PhpAndFpm</directory>\n      <directory>tests/Traffic</directory>\n      <directory>tests/Froxlor</directory>\n      <directory>tests/Cron</directory>\n    </testsuite>\n  </testsuites>\n  <logging>\n    <junit outputFile=\"build/logs/junit.xml\"/>\n  </logging>\n</phpunit>\n"
  },
  {
    "path": "ssl_certificates.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nif (!defined('AREA')) {\n\theader(\"Location: index.php\");\n\texit();\n}\n\nuse Froxlor\\Api\\Commands\\Certificates;\nuse Froxlor\\Api\\Commands\\Domains;\nuse Froxlor\\Api\\Commands\\SubDomains;\nuse Froxlor\\FroxlorLogger;\nuse Froxlor\\UI\\Collection;\nuse Froxlor\\UI\\HTML;\nuse Froxlor\\UI\\Listing;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// This file is being included in admin_domains and customer_domains\n// and therefore does not need to require lib/init.php\n\n$success_message = \"\";\n$id = (int)Request::any('id');\n\n// do the delete and then just show a success-message and the certificates list again\nif ($action == 'delete') {\n\tHTML::askYesNo('certificate_reallydelete', $filename, [\n\t\t'id' => $id,\n\t\t'page' => $page,\n\t\t'action' => 'deletesure'\n\t], '', [\n\t\t'section' => 'domains',\n\t\t'page' => $page\n\t]);\n} elseif (Request::post('send') == 'send' && $action == 'deletesure' && $id > 0) {\n\ttry {\n\t\t$json_result = Certificates::getLocal($userinfo, [\n\t\t\t'id' => $id\n\t\t])->delete();\n\t\t$success_message = lng('domains.ssl_certificate_removed', [$id]);\n\t} catch (Exception $e) {\n\t\tResponse::dynamicError($e->getMessage());\n\t}\n}\n\n$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, \"viewed domains::ssl_certificates\");\n\ntry {\n\t$certificates_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/tablelisting.sslcertificates.php';\n\t$collection = (new Collection(Certificates::class, $userinfo))\n\t\t->withPagination($certificates_list_data['sslcertificates_list']['columns'],\n\t\t\t$certificates_list_data['sslcertificates_list']['default_sorting']);\n\tif ($userinfo['adminsession'] == 1) {\n\t\t$collection->has('domains', Domains::class, 'domainid', 'id');\n\t} else {\n\t\t$collection->has('domains', SubDomains::class, 'domainid', 'id');\n\t}\n} catch (Exception $e) {\n\tResponse::dynamicError($e->getMessage());\n}\n\nUI::view('user/table.html.twig', [\n\t'listing' => Listing::format($collection, $certificates_list_data, 'sslcertificates_list'),\n]);\n"
  },
  {
    "path": "ssl_editor.php",
    "content": "<?php\n\n/**\n * This file is part of the froxlor project.\n * Copyright (c) 2010 the froxlor Team (see authors).\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; either version 2\n * of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, you can also view it online at\n * https://files.froxlor.org/misc/COPYING.txt\n *\n * @copyright  the authors\n * @author     froxlor team <team@froxlor.org>\n * @license    https://files.froxlor.org/misc/COPYING.txt GPLv2\n */\n\nif (!defined('AREA')) {\n\theader(\"Location: index.php\");\n\texit();\n}\n\nuse Froxlor\\Api\\Commands\\Certificates;\nuse Froxlor\\Api\\Commands\\SubDomains;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\PhpHelper;\nuse Froxlor\\UI\\Panel\\UI;\nuse Froxlor\\UI\\Request;\nuse Froxlor\\UI\\Response;\n\n// This file is being included in admin_domains and customer_domains\n// and therefore does not need to require lib/init.php\n\nif ($action == '' || $action == 'view') {\n\t// get domain\n\ttry {\n\t\t$json_result = SubDomains::getLocal($userinfo, [\n\t\t\t'id' => $id\n\t\t])->get();\n\t} catch (Exception $e) {\n\t\tResponse::dynamicError($e->getMessage());\n\t}\n\t$result_domain = json_decode($json_result, true)['data'];\n\n\tif ($result_domain['email_only']) {\n\t\tResponse::dynamicError(\"There are no ssl-certificates for email only domains.\");\n\t}\n\n\tif (Request::post('send') == 'send') {\n\t\t$do_insert = Request::post('do_insert', 0) == 1;\n\t\ttry {\n\t\t\tif ($do_insert) {\n\t\t\t\tCertificates::getLocal($userinfo, Request::postAll())->add();\n\t\t\t} else {\n\t\t\t\tCertificates::getLocal($userinfo, Request::postAll())->update();\n\t\t\t}\n\t\t} catch (Exception $e) {\n\t\t\tResponse::dynamicError($e->getMessage());\n\t\t}\n\t\t// back to domain overview\n\t\tResponse::redirectTo($filename, [\n\t\t\t'page' => 'domains'\n\t\t]);\n\t}\n\n\t$stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`\n\t\tWHERE `domainid`= :domainid\");\n\t$result = Database::pexecute_first($stmt, [\n\t\t\"domainid\" => $id\n\t]);\n\n\t$do_insert = false;\n\t// if no entry can be found, behave like we have empty values\n\tif (!is_array($result) || !isset($result['ssl_cert_file'])) {\n\t\t$result = [\n\t\t\t'ssl_cert_file' => '',\n\t\t\t'ssl_key_file' => '',\n\t\t\t'ssl_ca_file' => '',\n\t\t\t'ssl_cert_chainfile' => ''\n\t\t];\n\t\t$do_insert = true;\n\t}\n\n\t$result = PhpHelper::htmlentitiesArray($result);\n\n\t$ssleditor_data = include_once dirname(__FILE__) . '/lib/formfields/formfield.domain_ssleditor.php';\n\n\t$title = ['title'];\n\t$image = $ssleditor_data['domain_ssleditor']['image'];\n\n\tUI::view('user/form.html.twig', [\n\t\t'formaction' => $linker->getLink(['section' => 'domains', 'page' => 'domainssleditor', 'id' => $id]),\n\t\t'formdata' => $ssleditor_data['domain_ssleditor'],\n\t\t'editid' => $id,\n\t\t'actions_links' => [\n\t\t\t[\n\t\t\t\t'class' => 'btn-outline-secondary',\n\t\t\t\t'href' => $linker->getLink([\n\t\t\t\t\t'section' => 'domains',\n\t\t\t\t\t'page' => 'domains',\n\t\t\t\t\t'action' => 'edit',\n\t\t\t\t\t'id' => $id\n\t\t\t\t]),\n\t\t\t\t'label' => lng('admin.domain_edit'),\n\t\t\t\t'icon' => 'fa-solid fa-pen'\n\t\t\t],\n\t\t\t[\n\t\t\t\t'class' => 'btn-outline-primary',\n\t\t\t\t'href' => $linker->getLink(['section' => 'domains', 'page' => 'overview']),\n\t\t\t\t'label' => lng('admin.domains'),\n\t\t\t\t'icon' => 'fa-solid fa-globe'\n\t\t\t]\n\t\t]\n\t]);\n}\n"
  },
  {
    "path": "templates/index.html",
    "content": ""
  },
  {
    "path": "tests/Admins/AdminsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Api\\Commands\\Admins;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Admins\n */\nclass AdminsTest extends TestCase\n{\n\n\tpublic function testAdminAdminsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'reseller',\n\t\t\t'email' => 'testreseller@froxlor.org',\n\t\t\t'name' => 'Testreseller',\n\t\t\t'admin_password' => 'h0lYmo1y',\n\t\t\t'diskspace' => - 1,\n\t\t\t'traffic' => - 1,\n\t\t\t'customers' => 15,\n\t\t\t'domains' => 15,\n\t\t\t'subdomains' => 15,\n\t\t\t'emails' => - 1,\n\t\t\t'email_accounts' => 15,\n\t\t\t'email_forwarders' => 15,\n\t\t\t'email_imap' => 1,\n\t\t\t'email_pop3' => 0,\n\t\t\t'ftps' => 15,\n\t\t\t'mysqls' => 15,\n\t\t\t'sendpassword' => 0,\n\t\t\t'phpenabled' => 1,\n\t\t\t'ip' => array()\n\t\t];\n\n\t\t$json_result = Admins::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$admin_id = $result['adminid'];\n\n\t\t// get admin and check results\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'id' => $admin_id\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$this->assertEquals('reseller', $result['loginname']);\n\t\t$this->assertEquals('testreseller@froxlor.org', $result['email']);\n\t\t$this->assertEquals(0, $result['customers_see_all']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminAdminsAdd\n\t */\n\tpublic function testAdminAdminsAddLoginnameExists()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'reseller',\n\t\t\t'email' => 'testreseller@froxlor.org',\n\t\t\t'name' => 'Testreseller'\n\t\t];\n\n\t\t$this->expectExceptionMessage('Loginname reseller already exists');\n\t\tAdmins::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminAdminsAddLoginnameExists\n\t */\n\tpublic function testAdminAdminsAddLoginnameIsSystemaccount()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'web2',\n\t\t\t'email' => 'testreseller@froxlor.org',\n\t\t\t'name' => 'Testreseller'\n\t\t];\n\n\t\t$this->expectExceptionMessage('You cannot create accounts that begin with \"web\", as this prefix is set to be used for the automatic account-naming. Please enter another account name.');\n\t\tAdmins::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminAdminsAddLoginnameIsSystemaccount\n\t */\n\tpublic function testAdminAdminsAddLoginnameInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'reslr-',\n\t\t\t'email' => 'testreseller@froxlor.org',\n\t\t\t'name' => 'Testreseller'\n\t\t];\n\n\t\t$this->expectExceptionMessage('Loginname \"reslr-\" contains illegal characters.');\n\t\tAdmins::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminAdminsAddLoginnameIsSystemaccount\n\t */\n\tpublic function testAdminAdminsAddLoginnameInvalidEmail()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'reslr',\n\t\t\t'email' => 'testreseller.froxlor.org',\n\t\t\t'name' => 'Testreseller'\n\t\t];\n\n\t\t$this->expectExceptionMessage('Email-address testreseller.froxlor.org contains invalid characters or is incomplete');\n\t\tAdmins::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminAdminsAddNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$testadmin_userdata = $admin_userdata;\n\t\t$testadmin_userdata['adminsession'] = 0;\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tAdmins::getLocal($testadmin_userdata, array())->add();\n\t}\n\n\tpublic function testAdminAdminsGetNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"Admin with loginname 'unknown' could not be found\");\n\t\t// get unknown admin\n\t\tAdmins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'unknown'\n\t\t))->get();\n\t}\n\n\tpublic function testAdminAdminsList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Admins::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\n\t\t$json_result = Admins::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testAdminAdminsListLimitOffsetOrderSearch()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Admins::getLocal($admin_userdata, [\n\t\t\t'sql_orderby' => [\n\t\t\t\t'loginname' => 'DESC'\n\t\t\t]\n\t\t])->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\t\t$this->assertEquals('reseller', $result['list'][0]['loginname']);\n\n\t\t$json_result = Admins::getLocal($admin_userdata, [\n\t\t\t'sql_limit' => 1\n\t\t])->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('admin', $result['list'][0]['loginname']);\n\n\t\t$json_result = Admins::getLocal($admin_userdata, [\n\t\t\t'sql_limit' => 1,\n\t\t\t'sql_offset' => 1\n\t\t])->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('reseller', $result['list'][0]['loginname']);\n\n\t\t$json_result = Admins::getLocal($admin_userdata, [\n\t\t\t'sql_search' => [\n\t\t\t\t'loginname' => [\n\t\t\t\t\t'value' => 'adm',\n\t\t\t\t\t'op' => null /* LIKE */\n\t\t\t\t]\n\t\t\t]\n\t\t])->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('admin', $result['list'][0]['loginname']);\n\t}\n\n\tpublic function testResellerAdminsGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\n\t\t// try to read superadmin with an access-less reseller account\n\t\t$this->expectException(Exception::class);\n\t\t$this->expectExceptionCode(403);\n\n\t\t$json_result = Admins::getLocal($reseller_userdata, array(\n\t\t\t'loginname' => 'admin'\n\t\t))->get();\n\t}\n\n\tpublic function testResellerAdminsListNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\n\t\tAdmins::getLocal($reseller_userdata)->listing();\n\t}\n\n\tpublic function testAdminAdminsUnlock()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// update reseller to have correct test-data\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `loginfail_count` = '5' WHERE `loginname` = 'reseller'\");\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->unlock();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['loginfail_count']);\n\t}\n\n\tpublic function testAdminAdminsUnlockNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$testadmin_userdata = $admin_userdata;\n\t\t$testadmin_userdata['adminsession'] = 0;\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tAdmins::getLocal($testadmin_userdata, array(\n\t\t\t'loginname' => 'admin'\n\t\t))->unlock();\n\t}\n\n\tpublic function testAdminAdminsDeleteMyOwn()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionMessage(\"You cannot delete yourself for security reasons.\");\n\t\tAdmins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'admin'\n\t\t))->delete();\n\t}\n\n\tpublic function testAdminAdminsDeleteReseller()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// add test reseller\n\t\t$data = [\n\t\t\t'new_loginname' => 'resellertest',\n\t\t\t'email' => 'testreseller2@froxlor.org',\n\t\t\t'name' => 'Testreseller'\n\t\t];\n\n\t\t$json_result = Admins::getLocal($admin_userdata, $data)->add();\n\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'resellertest'\n\t\t))->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('resellertest', $result['loginname']);\n\t}\n\n\tpublic function testResellerAdminsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tAdmins::getLocal($reseller_userdata, array(\n\t\t\t'loginname' => 'admin'\n\t\t))->delete();\n\t}\n\n\tpublic function testAdminAdminsEditMyOwn()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// update admin to have correct test-data\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `theme` = 'Froxlor', `def_language` = 'Deutsch' WHERE `loginname` = 'admin'\");\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'admin',\n\t\t\t'theme' => '',\n\t\t\t'def_language' => 'English'\n\t\t))->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('Froxlor', $result['theme']);\n\t\t$this->assertEquals('English', $result['def_language']);\n\t}\n\n\tpublic function testAdminAdminsEdit()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// update admin to have correct test-data\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_ADMINS . \"` SET `deactivated` = '0' WHERE `loginname` = 'reseller'\");\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller',\n\t\t\t'deactivated' => '1'\n\t\t))->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['deactivated']);\n\n\t\t// reactivate\n\t\tAdmins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller',\n\t\t\t'deactivated' => '0'\n\t\t))->update();\n\t}\n\n\tpublic function testAdminsAdminsEditNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$testadmin_userdata = $admin_userdata;\n\t\t$testadmin_userdata['adminsession'] = 0;\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tAdmins::getLocal($testadmin_userdata, array(\n\t\t\t'loginname' => 'admin'\n\t\t))->update();\n\t}\n\n\tpublic function testAdminsAdminsCannotDeleteFirstAdmin()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$testadmin_userdata = $admin_userdata;\n\t\t$testadmin_userdata['adminid'] = 10;\n\n\t\t$this->expectExceptionMessage(\"The first admin cannot be deleted.\");\n\t\tAdmins::getLocal($testadmin_userdata, array(\n\t\t\t'loginname' => 'admin'\n\t\t))->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/Backup/DataDumpTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Settings;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\DataDump;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\DataDump\n */\nclass DataDumpTest extends TestCase\n{\n\n\tpublic function testAdminDataDumpNotEnabled()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('system.exportenabled', 0, true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(405);\n\t\t$this->expectExceptionMessage(\"You cannot access this resource\");\n\t\tDataDump::getLocal($customer_userdata)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDataDumpNotEnabled\n\t */\n\tpublic function testAdminDataDumpExtrasHidden()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('system.exportenabled', 1, true);\n\t\tSettings::Set('panel.customer_hide_options', 'extras', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(405);\n\t\t$this->expectExceptionMessage(\"You cannot access this resource\");\n\t\tDataDump::getLocal($customer_userdata)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDataDumpExtrasHidden\n\t */\n\tpublic function testAdminDataDumpExtrasExportHidden()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.customer_hide_options', 'extras.export', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(405);\n\t\t$this->expectExceptionMessage(\"You cannot access this resource\");\n\t\tDataDump::getLocal($customer_userdata)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDataDumpExtrasExportHidden\n\t */\n\tpublic function testCustomerDataDumpAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.customer_hide_options', '', true);\n\t\tDatabase::query(\"TRUNCATE TABLE `panel_tasks`;\");\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'path' => '/my-dump',\n\t\t\t'dump_dbs' => 2,\n\t\t\t'dump_mail' => 3,\n\t\t\t'dump_web' => 4\n\t\t];\n\t\t$json_result = DataDump::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'my-dump/', $result['destdir']);\n\t\t$this->assertEquals('1', $result['dump_dbs']);\n\t\t$this->assertEquals('1', $result['dump_mail']);\n\t\t$this->assertEquals('1', $result['dump_web']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDataDumpAdd\n\t */\n\tpublic function testCustomerDataDumpAddPathNotDocroot()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'path' => '/'\n\t\t];\n\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage('The folder for data-dumps cannot be your homedir, please chose a folder within your homedir, e.g. /dumps');\n\t\t$json_result = DataDump::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDataDumpGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(303);\n\t\tDataDump::getLocal($admin_userdata)->get();\n\t}\n\n\tpublic function testAdminDataDumpUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(303);\n\t\tDataDump::getLocal($admin_userdata)->update();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDataDumpAdd\n\t */\n\tpublic function testAdminDataDumpListing()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = DataDump::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('1', $result['list'][0]['data']['dump_dbs']);\n\t\t$this->assertEquals('1', $result['list'][0]['data']['dump_mail']);\n\t\t$this->assertEquals('1', $result['list'][0]['data']['dump_web']);\n\n\t\t$json_result = DataDump::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDataDumpAdd\n\t */\n\tpublic function testCustomerDataDumpDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'job_entry' => 1\n\t\t];\n\t\t$json_result = DataDump::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue($result);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDataDumpListing\n\t */\n\tpublic function testCustomerDataDumpDeleteNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'job_entry' => 1337\n\t\t];\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage('Data export job with id #1337 could not be found');\n\t\tDataDump::getLocal($customer_userdata, $data)->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/Bulk/DomainBulkTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Domains;\nuse Froxlor\\Bulk\\DomainBulkAction;\n\n/**\n *\n * @covers \\Froxlor\\Bulk\\BulkAction\n * @covers \\Froxlor\\Bulk\\DomainBulkAction\n */\nclass DomainBulkTest extends TestCase\n{\n\tpublic function testNoImportFile()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionMessage(\"No file was given for import\");\n\t\t$bulk = new DomainBulkAction(null, $admin_userdata);\n\t\t$bulk->doImport(\";\", 0);\n\t}\n\n\tpublic function testImportFileDoesNotExist()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionMessage(\"The file '/tmp/nonexisting.csv' could not be found\");\n\t\t$bulk = new DomainBulkAction(\"/tmp/nonexisting.csv\", $admin_userdata);\n\t\t$bulk->doImport(\";\", 0);\n\t}\n\n\tpublic function testImportDomains()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = <<<EOC\ndomain;loginname;\nimported-a.com;test1;\nimported-b.com;test1;\nimported-c.com;test2;\nEOC;\n\t\tfile_put_contents('/tmp/import-test.csv', $content);\n\t\t$bulk = new DomainBulkAction(\"/tmp/import-test.csv\", $admin_userdata);\n\t\t$result = $bulk->doImport(\";\", 0);\n\n\t\t$this->assertEquals(3, $result['all']);\n\t\t$this->assertEquals(2, $result['imported']);\n\t\t$this->assertEquals(\"Customer with loginname 'test2' could not be found\", $bulk->getErrors()[0]);\n\n\t\t// now check whether the domain really exists for test1 user\n\t\t$data = [\n\t\t\t'domain' => 'imported-a.com'\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('imported-a.com', $result['domain']);\n\t\t$this->assertEquals(1, $result['customerid']);\n\t}\n\n\tpublic function testImportDomainsMaxAlloc()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// fake allocation restriction\n\t\t$admin_userdata['domains'] = 1;\n\n\t\t$content = <<<EOC\ndomain;loginname;\nimported-a.com;test1;\nimported-b.com;test1;\nimported-c.com;test2;\nEOC;\n\t\tfile_put_contents('/tmp/import-test.csv', $content);\n\t\t$bulk = new DomainBulkAction(\"/tmp/import-test.csv\", $admin_userdata);\n\t\t$result = $bulk->doImport(\";\", 0);\n\n\t\t$this->assertEquals(3, $result['all']);\n\t\t$this->assertEquals(0, $result['imported']);\n\t\t$this->assertEquals(\"You have reached your maximum allocation of domains (\" . $admin_userdata['domains'] . \")\", $result['notes']);\n\t}\n}\n"
  },
  {
    "path": "tests/Certificates/CertificatesTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Certificates;\nuse Froxlor\\Api\\Commands\\SubDomains;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Certificates\n */\nclass CertificatesTest extends TestCase\n{\n\n\tpublic function testAdminCertificatesAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = SubDomains::getLocal($admin_userdata, array(\n\t\t\t'domainname' => 'test2.local'\n\t\t))->get();\n\t\t$domain = json_decode($json_result, true)['data'];\n\t\t$domainid = $domain['id'];\n\n\t\t$certdata = $this->generateKey();\n\t\t$json_result = Certificates::getLocal($admin_userdata, array(\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'ssl_cert_file' => $certdata['cert'],\n\t\t\t'ssl_key_file' => $certdata['key']\n\t\t))->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($domainid, $result['domainid']);\n\t}\n\n\tpublic function testResellerCertificatesAddAgain()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\n\t\t$certdata = $this->generateKey();\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage(\"Domain 'test2.local' already has a certificate. Did you mean to call update?\");\n\t\t$json_result = Certificates::getLocal($reseller_userdata, array(\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'ssl_cert_file' => $certdata['cert'],\n\t\t\t'ssl_key_file' => $certdata['key']\n\t\t))->add();\n\t}\n\n\tpublic function testCustomerCertificatesAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$json_result = SubDomains::getLocal($admin_userdata, array(\n\t\t\t'domainname' => 'mysub2.test2.local'\n\t\t))->get();\n\t\t$domain = json_decode($json_result, true)['data'];\n\t\t$domainid = $domain['id'];\n\n\t\t$certdata = $this->generateKey();\n\t\t$json_result = Certificates::getLocal($customer_userdata, array(\n\t\t\t'domainname' => 'mysub2.test2.local',\n\t\t\t'ssl_cert_file' => $certdata['cert'],\n\t\t\t'ssl_key_file' => $certdata['key']\n\t\t))->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($domainid, $result['domainid']);\n\t}\n\n\tpublic function testAdminCertificatesList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Certificates::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\n\t\t$json_result = Certificates::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testResellerCertificatesList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\n\t\t$json_result = Certificates::getLocal($reseller_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\n\t\t$json_result = Certificates::getLocal($reseller_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testCustomerCertificatesList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$json_result = Certificates::getLocal($customer_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\n\t\t$json_result = Certificates::getLocal($customer_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testAdminCertificatesUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$certdata = $this->generateKey();\n\t\t$json_result = Certificates::getLocal($admin_userdata, array(\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'ssl_cert_file' => $certdata['cert'],\n\t\t\t'ssl_key_file' => $certdata['key']\n\t\t))->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(str_replace(\"\\n\", \"\", $certdata['cert']), str_replace(\"\\n\", \"\", $result['ssl_cert_file']));\n\t}\n\n\tpublic function testCustomerCertificatesUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$certdata = $this->generateKey();\n\t\t$json_result = Certificates::getLocal($customer_userdata, array(\n\t\t\t'domainname' => 'mysub2.test2.local',\n\t\t\t'ssl_cert_file' => $certdata['cert'],\n\t\t\t'ssl_key_file' => $certdata['key']\n\t\t))->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(str_replace(\"\\n\", \"\", $certdata['cert']), str_replace(\"\\n\", \"\", $result['ssl_cert_file']));\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCertificatesUpdate\n\t */\n\tpublic function testCustomerCertificatesDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$json_result = Certificates::getLocal($customer_userdata, array(\n\t\t\t'id' => 1\n\t\t))->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(isset($result['domainid']) && $result['domainid'] > 0);\n\t}\n\n\tprivate function generateKey()\n\t{\n\t\t$dn = array(\n\t\t\t\"countryName\" => \"DE\",\n\t\t\t\"stateOrProvinceName\" => \"Hessen\",\n\t\t\t\"localityName\" => \"Frankfurt\",\n\t\t\t\"organizationName\" => \"Froxlor\",\n\t\t\t\"organizationalUnitName\" => \"Testing\",\n\t\t\t\"commonName\" => \"test2.local\",\n\t\t\t\"emailAddress\" => \"team@froxlor.org\"\n\t\t);\n\n\t\t// generate key pair\n\t\t$privkey = openssl_pkey_new(array(\n\t\t\t\"private_key_bits\" => 2048,\n\t\t\t\"private_key_type\" => OPENSSL_KEYTYPE_RSA\n\t\t));\n\n\t\t// generate csr\n\t\t$csr = openssl_csr_new($dn, $privkey, array(\n\t\t\t'digest_alg' => 'sha256'\n\t\t));\n\n\t\t// generate self-signed certificate\n\t\t$sscert = openssl_csr_sign($csr, null, $privkey, 365, array(\n\t\t\t'digest_alg' => 'sha256'\n\t\t));\n\n\t\t// export\n\t\topenssl_x509_export($sscert, $certout);\n\t\topenssl_pkey_export($privkey, $pkeyout, null);\n\n\t\treturn array(\n\t\t\t'cert' => $certout,\n\t\t\t'key' => $pkeyout\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/Cron/TaskIdTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Cron\\TaskId;\n\n/**\n *\n * @covers \\Froxlor\\Cron\\TaskId\n */\nclass TaskIDTest extends TestCase\n{\n\tprivate $fixedids = array(\n\t\t'REBUILD_VHOST' => 1,\n\n\t\t'CREATE_HOME' => 2,\n\n\t\t'REBUILD_DNS' => 4,\n\n\t\t'CREATE_FTP' => 5,\n\n\t\t'DELETE_CUSTOMER_FILES' => 6,\n\n\t\t'DELETE_EMAIL_DATA' => 7,\n\n\t\t'DELETE_FTP_DATA' => 8,\n\n\t\t'CREATE_QUOTA' => 10,\n\n\t\t'DELETE_DOMAIN_PDNS' => 11,\n\n\t\t'DELETE_DOMAIN_SSL' => 12,\n\n\t\t'CREATE_CUSTOMER_DATADUMP' => 20,\n\n\t\t'REBUILD_CRON' => 99,\n\t);\n\n\tpublic function testValidTaskId()\n\t{\n\n\t\t$isId99Valid = TaskId::isValid(99);\n\t\t$this->assertTrue($isId99Valid, \"Task id 99 must be valid\");\n\n\t\t$isIdStringValid = TaskId::isValid('99');\n\t\t$this->assertTrue($isIdStringValid, \"String task ids should be valid\");\n\n\t\t$isNegativeValid = TaskId::isValid(-1);\n\t\t$this->assertFalse($isNegativeValid, \"Negative task should be invalid\");\n\t}\n\n\tpublic function testIdMappingCorrect() {\n\t\tforeach($this->fixedids as $name => $expected) {\n\t\t\t$result = constant(\"\\Froxlor\\Cron\\TaskId::$name\");\n\t\t\t$this->assertEquals( $expected, $result, \"Task $name has bad mapping\");\n\t\t}\n\t}\n\n\tpublic function testConvertToConstant() {\n\t\tforeach($this->fixedids as $name => $taskid) {\n\t\t\t$result = TaskId::convertToConstant($taskid);\n\t\t\t$this->assertEquals( $name, $result, \"Task $name has bad mapping from id to name\");\n\t\t}\n\n\t\t$unknownIDResult = TaskId::isValid(10101010);\n\t\t$this->assertFalse($unknownIDResult);\n\t}\n}\n"
  },
  {
    "path": "tests/Cronjobs/CronjobsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Cronjobs;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Cronjobs\n */\nclass CronjobsTest extends TestCase\n{\n\n\tpublic function testAdminCronjobsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$json_result = Cronjobs::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(isset($result['list'][0]['module']));\n\t\t$this->assertTrue(isset($result['list'][0]['cronfile']));\n\n\t\t$json_result = Cronjobs::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(6, $result);\n\t}\n\n\tpublic function testCustomerCronjobsListNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tCronjobs::getLocal($customer_userdata)->listing();\n\t}\n\n\tpublic function testAdminCronjobsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [];\n\t\t$this->expectExceptionCode(303);\n\t\t$this->expectExceptionMessage(\"You cannot add new cronjobs yet.\");\n\t\tCronjobs::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminCronjobsGetNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"cronjob with id #999 could not be found\");\n\t\tCronjobs::getLocal($admin_userdata, array(\n\t\t\t'id' => 999\n\t\t))->get();\n\t}\n\n\tpublic function testCustomerCronjobsGetNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tCronjobs::getLocal($customer_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t}\n\n\tpublic function testAdminCronjobsEdit()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'isactive' => 0,\n\t\t\t'interval_value' => 10\n\t\t];\n\t\t$json_result = Cronjobs::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['isactive']);\n\t\t$this->assertEquals('10 MINUTE', $result['interval']);\n\t}\n\n\tpublic function testResellerCronjobsEditNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'isactive' => 1\n\t\t];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tCronjobs::getLocal($reseller_userdata, $data)->update();\n\t}\n\n\tpublic function testAdminCronjobsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => 3\n\t\t];\n\t\t$this->expectExceptionCode(303);\n\t\t$this->expectExceptionMessage(\"You cannot delete system cronjobs.\");\n\t\tCronjobs::getLocal($admin_userdata, $data)->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/Customers/CustomersTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Settings;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\SubDomains;\nuse Froxlor\\Api\\Commands\\Ftps;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Customers\n * @covers \\Froxlor\\Api\\Commands\\Admins\n */\nclass CustomersTest extends TestCase\n{\n\n\tpublic function testAdminCustomersAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'test1',\n\t\t\t'email' => 'team@froxlor.org',\n\t\t\t'firstname' => 'Test',\n\t\t\t'name' => 'Testman',\n\t\t\t'customernumber' => 1337,\n\t\t\t'diskspace' => 0,\n\t\t\t'diskspace_ul' => 1,\n\t\t\t'traffic' => - 1,\n\t\t\t'subdomains' => 15,\n\t\t\t'emails' => - 1,\n\t\t\t'email_accounts' => 15,\n\t\t\t'email_forwarders' => 15,\n\t\t\t'email_imap' => 1,\n\t\t\t'email_pop3' => 0,\n\t\t\t'ftps' => 15,\n\t\t\t'mysqls' => 15,\n\t\t\t'createstdsubdomain' => 1,\n\t\t\t'new_customer_password' => 'h0lYmo1y',\n\t\t\t'sendpassword' => TRAVIS_CI == 1 ? 0 : 1,\n\t\t\t'phpenabled' => 1,\n\t\t\t'dnsenabled' => 1,\n\t\t\t'store_defaultindex' => 1,\n\t\t\t'custom_notes' => 'secret',\n\t\t\t'custom_notes_show' => 0,\n\t\t\t'gender' => 5,\n\t\t\t'allowed_phpconfigs' => array(\n\t\t\t\t1\n\t\t\t)\n\t\t];\n\n\t\t$json_result = Customers::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['customerid']);\n\t\t$this->assertEquals('team@froxlor.org', $result['email']);\n\t\t$this->assertEquals(1337, $result['customernumber']);\n\t\t$this->assertEquals(15, $result['subdomains']);\n\t\t$this->assertEquals('secret', $result['custom_notes']);\n\n\t\t$stdsubdomain = $result['standardsubdomain'] ?? false;\n\t\tif (! $stdsubdomain) {\n\t\t\t$this->fail('No standardsubdomain where there should be one');\n\t\t} else {\n\t\t\t// validate that the std-subdomain has been added\n\t\t\t$json_result = SubDomains::getLocal($admin_userdata, array(\n\t\t\t\t'id' => $result['standardsubdomain']\n\t\t\t))->get();\n\t\t\t$result = json_decode($json_result, true)['data'];\n\t\t\t$this->assertEquals('test1.dev.froxlor.org', $result['domain']);\n\t\t}\n\t}\n\n\tpublic function testAdminCustomersAddEmptyMail()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'test2',\n\t\t\t'email' => ' ',\n\t\t\t'firstname' => 'Test2',\n\t\t\t'name' => 'Testman2'\n\t\t];\n\n\t\t$this->expectExceptionMessage('Requested parameter \"email\" is empty where it should not be for \"Customers:add\"');\n\t\tCustomers::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminCustomersAddInvalidMail()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'test2',\n\t\t\t'email' => 'test.froxlor.org',\n\t\t\t'firstname' => 'Test2',\n\t\t\t'name' => 'Testman2'\n\t\t];\n\n\t\t$this->expectExceptionMessage(\"Email-address test.froxlor.org contains invalid characters or is incomplete\");\n\t\tCustomers::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAdd\n\t */\n\tpublic function testAdminCustomersList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Customers::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertFalse(isset($result['list'][0]['webspace_used']));\n\n\t\t$json_result = Customers::getLocal($admin_userdata, [\n\t\t\t'show_usages' => true\n\t\t])->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertTrue(isset($result['list'][0]['webspace_used']));\n\n\t\t$json_result = Customers::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAdd\n\t */\n\tpublic function testResellerCustomersList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = Customers::getLocal($reseller_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['count']);\n\n\t\t$json_result = Customers::getLocal($reseller_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAdd\n\t */\n\tpublic function testCustomerCustomersList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\t$json_result = Customers::getLocal($customer_userdata)->listing();\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\t$json_result = Customers::getLocal($customer_userdata)->listingCount();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAdd\n\t */\n\tpublic function testCustomerCustomersGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$json_result = Customers::getLocal($customer_userdata, array(\n\t\t\t'id' => $customer_userdata['customerid']\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$this->assertEquals(1, $result['customerid']);\n\t\t$this->assertEquals('team@froxlor.org', $result['email']);\n\t\t$this->assertEquals(1337, $result['customernumber']);\n\t\t$this->assertEquals(15, $result['subdomains']);\n\t\t$this->assertEquals('Froxlor', $result['theme']);\n\t\t$this->assertEquals('', $result['custom_notes']);\n\t}\n\n\tpublic function testAdminCustomersGetNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"Customer with id #999 could not be found\");\n\t\tCustomers::getLocal($admin_userdata, array(\n\t\t\t'id' => 999\n\t\t))->get();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAdd\n\t */\n\tpublic function testCustomerCustomersGetForeign()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectException(Exception::class);\n\t\t$this->expectExceptionCode(401);\n\n\t\tCustomers::getLocal($customer_userdata, array(\n\t\t\t'id' => 2\n\t\t))->get();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAdd\n\t */\n\tpublic function testAdminCustomerUpdateDeactivate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\tCustomers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1,\n\t\t\t'deactivated' => 1\n\t\t))->update();\n\n\t\t// get customer and check results\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$this->assertEquals(1, $result['customerid']);\n\t\t$this->assertEquals(1, $result['deactivated']);\n\t\t// standard-subdomains will be removed on deactivation\n\t\t$this->assertEquals(0, $result['standardsubdomain']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomerUpdateDeactivate\n\t */\n\tpublic function testCustomerCustomersGetWhenDeactivated()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectException(Exception::class);\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage(\"Account suspended\");\n\n\t\tCustomers::getLocal($customer_userdata, array(\n\t\t\t'id' => $customer_userdata['customerid']\n\t\t))->get();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerCustomersGetWhenDeactivated\n\t */\n\tpublic function testCustomerCustomersUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// reactivate customer\n\t\t// get customer\n\t\tCustomers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1,\n\t\t\t'deactivated' => 0\n\t\t))->update();\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\tCustomers::getLocal($customer_userdata, array(\n\t\t\t'id' => $customer_userdata['customerid'],\n\t\t\t'def_language' => 'English',\n\t\t\t'theme' => 'Froxlor',\n\t\t\t'new_customer_password' => 'h0lYmo1y2'\n\t\t))->update();\n\n\t\t$json_result = Customers::getLocal($customer_userdata, array(\n\t\t\t'id' => $customer_userdata['customerid']\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$this->assertEquals('Froxlor', $result['theme']);\n\t\t$this->assertEquals('English', $result['def_language']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAdd\n\t */\n\tpublic function testResellerCustomersAddAllocateMore()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\n\t\t$this->expectExceptionMessage(\"You cannot allocate more resources than you own for yourself.\");\n\t\t// add new customer\n\t\t$data = [\n\t\t\t'new_loginname' => 'test2',\n\t\t\t'email' => 'test2@froxlor.org',\n\t\t\t'firstname' => 'Test',\n\t\t\t'name' => 'Testman',\n\t\t\t'customernumber' => 1338,\n\t\t\t'subdomains' => - 1,\n\t\t\t'new_customer_password' => 'h0lYmo1y'\n\t\t];\n\t\tCustomers::getLocal($reseller_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerCustomersDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tCustomers::getLocal($customer_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->delete();\n\t}\n\n\tpublic function testResellerCustomersDeleteNotOwned()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$this->expectExceptionCode(404);\n\t\tCustomers::getLocal($reseller_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->delete();\n\t}\n\n\tpublic function testAdminCustomersDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// add new customer\n\t\t$data = [\n\t\t\t'new_loginname' => 'test2',\n\t\t\t'email' => 'test2@froxlor.org',\n\t\t\t'firstname' => 'Test',\n\t\t\t'name' => 'Testman',\n\t\t\t'customernumber' => 1338,\n\t\t\t'new_customer_password' => 'h0lYmo1y'\n\t\t];\n\t\tCustomers::getLocal($admin_userdata, $data)->add();\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test2'\n\t\t))->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test2', $result['loginname']);\n\t}\n\n\tpublic function testAdminCustomersUnlock()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// update customer to have correct test-data\n\t\tDatabase::query(\"UPDATE `\" . TABLE_PANEL_CUSTOMERS . \"` SET `loginfail_count` = '5' WHERE `loginname` = 'test1'\");\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->unlock();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['loginfail_count']);\n\t}\n\n\tpublic function testAdminCustomersUnlockNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$testadmin_userdata = $admin_userdata;\n\t\t$testadmin_userdata['adminsession'] = 0;\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tCustomers::getLocal($testadmin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->unlock();\n\t}\n\n\tpublic function testAdminCustomersMoveNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$testadmin_userdata = $admin_userdata;\n\t\t$testadmin_userdata['change_serversettings'] = 0;\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tCustomers::getLocal($testadmin_userdata, array(\n\t\t\t'loginname' => 'test1',\n\t\t\t'adminid' => 1\n\t\t))->move();\n\t}\n\n\tpublic function testAdminCustomersMoveTargetIsSource()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage(\"Cannot move customer to the same admin/reseller as he currently is assigned to\");\n\t\tCustomers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1',\n\t\t\t'adminid' => 1\n\t\t))->move();\n\t}\n\n\tpublic function testAdminCustomersMove()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1',\n\t\t\t'adminid' => 2\n\t\t))->move();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$this->assertEquals(2, $result['adminid']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersMove\n\t */\n\tpublic function testAdminCustomersAddLoginnameIsSystemaccount()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'web1',\n\t\t\t'email' => 'team@froxlor.org',\n\t\t\t'firstname' => 'Test',\n\t\t\t'name' => 'Testman',\n\t\t\t'customernumber' => 1338,\n\t\t\t'diskspace' => - 1,\n\t\t\t'traffic' => - 1,\n\t\t\t'subdomains' => 15,\n\t\t\t'emails' => - 1,\n\t\t\t'email_accounts' => 15,\n\t\t\t'email_forwarders' => 15,\n\t\t\t'email_imap' => 1,\n\t\t\t'email_pop3' => 0,\n\t\t\t'ftps' => 15,\n\t\t\t'mysqls' => 15,\n\t\t\t'createstdsubdomain' => 1,\n\t\t\t'new_customer_password' => 'h0lYmo1y',\n\t\t\t'sendpassword' => TRAVIS_CI == 1 ? 0 : 1,\n\t\t\t'phpenabled' => 1,\n\t\t\t'store_defaultindex' => 1,\n\t\t\t'custom_notes' => 'secret',\n\t\t\t'custom_notes_show' => 0,\n\t\t\t'gender' => 5,\n\t\t\t'allowed_phpconfigs' => array(\n\t\t\t\t1\n\t\t\t)\n\t\t];\n\n\t\t$this->expectExceptionMessage('You cannot create accounts that begin with \"web\", as this prefix is set to be used for the automatic account-naming. Please enter another account name.');\n\t\tCustomers::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAddLoginnameIsSystemaccount\n\t */\n\tpublic function testAdminCustomersAddAutoLoginname()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('system.lastaccountnumber', 0, true);\n\n\t\t$data = [\n\t\t\t'new_loginname' => '',\n\t\t\t'email' => 'team@froxlor.org',\n\t\t\t'firstname' => 'Test2',\n\t\t\t'name' => 'Testman2',\n\t\t\t'customernumber' => 1338,\n\t\t\t'sendpassword' => 0,\n\t\t\t'perlenabled' => 2,\n\t\t\t'dnsenabled' => 4,\n\t\t\t'createstdsubdomain' => 0\n\t\t];\n\n\t\t$json_result = Customers::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('web1', $result['loginname']);\n\t\t$this->assertEquals(1338, $result['customernumber']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAddAutoLoginname\n\t */\n\tpublic function testAdminCustomersAddLoginnameExists()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'test1',\n\t\t\t'email' => 'team@froxlor.org',\n\t\t\t'firstname' => 'Test2',\n\t\t\t'name' => 'Testman2',\n\t\t\t'customernumber' => 1339\n\t\t];\n\n\t\t$this->expectExceptionMessage('Loginname test1 already exists');\n\t\tCustomers::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAddLoginnameExists\n\t */\n\tpublic function testAdminCustomersAddLoginnameInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'user-',\n\t\t\t'email' => 'team@froxlor.org',\n\t\t\t'firstname' => 'Test2',\n\t\t\t'name' => 'Testman2',\n\t\t\t'customernumber' => 1339\n\t\t];\n\n\t\t$this->expectExceptionMessage('Loginname \"user-\" contains illegal characters.');\n\t\tCustomers::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAddLoginnameExists\n\t */\n\tpublic function testAdminCustomersAddLoginnameInvalid2()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$loginname = str_repeat(\"x\", \\Froxlor\\Database\\Database::getSqlUsernameLength() + 1);\n\t\t$data = [\n\t\t\t'new_loginname' => $loginname,\n\t\t\t'email' => 'team@froxlor.org',\n\t\t\t'firstname' => 'Test2',\n\t\t\t'name' => 'Testman2',\n\t\t\t'customernumber' => 1339\n\t\t];\n\n\t\t$this->expectExceptionMessage('Loginname contains too many characters. Only ' . (\\Froxlor\\Database\\Database::getSqlUsernameLength() - strlen(Settings::Get('customer.mysqlprefix'))) . ' characters are allowed.');\n\t\tCustomers::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminCustomersAddAutoLoginname\n\t */\n\tpublic function testResellerCustomersAddNoFtpValidateDefaultUserExists()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\n\t\t// set available ftp resources to 0 to validate that when the customer\n\t\t// is added the default ftp user for the customer is created too regardless of\n\t\t// available resource of the reseller/admin\n\t\t$reseller_userdata['ftps'] = 0;\n\n\t\t// add new customer\n\t\t$data = [\n\t\t\t'new_loginname' => 'testftpx',\n\t\t\t'email' => 'testftp@froxlor.org',\n\t\t\t'firstname' => 'Test',\n\t\t\t'name' => 'Ftpman',\n\t\t\t'customernumber' => 1339,\n\t\t\t'new_customer_password' => 'h0lYmo1y'\n\t\t];\n\t\tCustomers::getLocal($reseller_userdata, $data)->add();\n\n\t\t// get FTP user\n\t\t$json_result = Ftps::getLocal($reseller_userdata, [\n\t\t\t'username' => 'testftpx'\n\t\t])->get();\n\t\t$ftp_data = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(\"testftpx\", $ftp_data['username']);\n\n\t\t// now get rid of the customer again\n\t\t$json_result = Customers::getLocal($reseller_userdata, array(\n\t\t\t'loginname' => 'testftpx'\n\t\t))->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('testftpx', $result['loginname']);\n\t}\n}\n"
  },
  {
    "path": "tests/Customers/HostingPlansTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Settings;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\HostingPlans;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\HostingPlans\n * @covers \\Froxlor\\Api\\Commands\\Customers\n */\nclass HostingPlansTest extends TestCase\n{\n\n\tpublic function testAdminPlanAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'name' => 'test',\n\t\t\t'description' => 'first test plan',\n\t\t\t'diskspace' => 0,\n\t\t\t'diskspace_ul' => 1,\n\t\t\t'traffic' => - 1,\n\t\t\t'subdomains' => 15,\n\t\t\t'emails' => - 1,\n\t\t\t'email_accounts' => 15,\n\t\t\t'email_forwarders' => 15,\n\t\t\t'email_imap' => 1,\n\t\t\t'email_pop3' => 0,\n\t\t\t'ftps' => 15,\n\t\t\t'mysqls' => 15,\n\t\t\t'phpenabled' => 1,\n\t\t\t'dnsenabled' => 1,\n\t\t\t'allowed_phpconfigs' => array(\n\t\t\t\t1\n\t\t\t)\n\t\t];\n\n\t\t$json_result = HostingPlans::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$result['value'] = json_decode($result['value'], true);\n\t\tforeach ($result['value'] as $index => $value) {\n\t\t\t$result[$index] = $value;\n\t\t}\n\t\t$this->assertEquals('test', $result['name']);\n\t\t$this->assertEquals(- 1, $result['diskspace']);\n\t\t$this->assertEquals(15, $result['email_accounts']);\n\t\t$this->assertEquals([\n\t\t\t1\n\t\t], $result['allowed_phpconfigs']);\n\t}\n\n\tpublic function testAdminPlanAddEmptyName()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'description' => 'test plan'\n\t\t];\n\n\t\t$this->expectExceptionMessage('Requested parameter \"name\" could not be found for \"HostingPlans:add\"');\n\t\tHostingPlans::getLocal($admin_userdata, $data)->add();\n\n\t\t$data['name'] = null;\n\t\t$this->expectExceptionMessage('Requested parameter \"name\" is empty where it should not be for \"HostingPlans:add\"');\n\t\tHostingPlans::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminPlanAdd\n\t */\n\tpublic function testAdminPlanList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = HostingPlans::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\n\t\t$json_result = HostingPlans::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminPlanAdd\n\t */\n\tpublic function testResellerPlanList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = HostingPlans::getLocal($reseller_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['count']);\n\n\t\t$json_result = HostingPlans::getLocal($reseller_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminPlanAdd\n\t */\n\tpublic function testCustomerPlanList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\t$json_result = HostingPlans::getLocal($customer_userdata)->listing();\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\t$json_result = HostingPlans::getLocal($customer_userdata)->listingCount();\n\t}\n\n\tpublic function testCustomerPlanAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\n\t\t$json_result = HostingPlans::getLocal($customer_userdata)->add();\n\t}\n\n\tpublic function testCustomerPlanGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\n\t\t$json_result = HostingPlans::getLocal($customer_userdata)->get();\n\t}\n\n\tpublic function testCustomerPlanUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\n\t\t$json_result = HostingPlans::getLocal($customer_userdata)->update();\n\t}\n\n\tpublic function testCustomerPlanDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\n\t\t$json_result = HostingPlans::getLocal($customer_userdata)->delete();\n\t}\n\n\tpublic function testAdminPlanGetNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"Hosting-plan with id #999 could not be found\");\n\t\tHostingPlans::getLocal($admin_userdata, array(\n\t\t\t'id' => 999\n\t\t))->get();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminPlanAdd\n\t */\n\tpublic function testAdminPlanUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tHostingPlans::getLocal($admin_userdata, array(\n\t\t\t'planname' => 'test',\n\t\t\t'name' => '',\n\t\t\t'ftps' => '20'\n\t\t))->update();\n\n\t\t$json_result = HostingPlans::getLocal($admin_userdata, array(\n\t\t\t'planname' => 'test'\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$result['value'] = json_decode($result['value'], true);\n\t\tforeach ($result['value'] as $index => $value) {\n\t\t\t$result[$index] = $value;\n\t\t}\n\t\t$this->assertEquals(20, $result['ftps']);\n\t\t$this->assertEquals(- 1, $result['diskspace']);\n\t\t$this->assertEquals(15, $result['email_accounts']);\n\t\t$this->assertEquals([\n\t\t\t1\n\t\t], $result['allowed_phpconfigs']);\n\t}\n\n\tpublic function testResellerPlanDeleteNotOwned()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$this->expectExceptionCode(404);\n\t\tHostingPlans::getLocal($reseller_userdata, array(\n\t\t\t'planname' => 'test'\n\t\t))->delete();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminPlanAdd\n\t */\n\tpublic function testAdminPlanDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// add new customer\n\t\t$data = [\n\t\t\t'name' => 'test2',\n\t\t\t'description' => 'second test plan'\n\t\t];\n\t\tHostingPlans::getLocal($admin_userdata, $data)->add();\n\t\t$json_result = HostingPlans::getLocal($admin_userdata, array(\n\t\t\t'planname' => 'test2'\n\t\t))->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test2', $result['name']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminPlanAdd\n\t */\n\tpublic function testAdminCustomersAddWithHostingPlan()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = HostingPlans::getLocal($admin_userdata, array(\n\t\t\t'planname' => 'test'\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'new_loginname' => 'test1hp',\n\t\t\t'email' => 'team@froxlor.org',\n\t\t\t'firstname' => 'Test',\n\t\t\t'name' => 'Testman',\n\t\t\t'customernumber' => 1337,\n\t\t\t'createstdsubdomain' => 0,\n\t\t\t'new_customer_password' => 'h0lYmo1y',\n\t\t\t'sendpassword' => TRAVIS_CI == 1 ? 0 : 1,\n\t\t\t'store_defaultindex' => 1,\n\t\t\t'custom_notes' => 'secret',\n\t\t\t'custom_notes_show' => 0,\n\t\t\t'gender' => 5,\n\t\t\t'hosting_plan_id' => $result['id']\n\t\t];\n\n\t\t$json_result = Customers::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(- 1024, $result['diskspace']);\n\t\t$this->assertEquals(15, $result['subdomains']);\n\t\t$this->assertEquals(1, $result['phpenabled']);\n\t\t$this->assertJsonStringEqualsJsonString(json_encode([\n\t\t\t1\n\t\t]), $result['allowed_phpconfigs']);\n\n\t\t// remove customer\n\t\tCustomers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1hp'\n\t\t))->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/DomainZones/DomainZonesTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Settings;\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\DomainZones;\nuse Froxlor\\Api\\Commands\\Domains;\nuse Froxlor\\Api\\Commands\\SubDomains;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\SubDomains\n * @covers \\Froxlor\\Api\\Commands\\DomainZones\n */\nclass DomainZonesTest extends TestCase\n{\n\n\tpublic function testCustomerDomainZonesGet()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('system.dnsenabled', 1, true);\n\t\tSettings::Set('system.mxservers', 'mx.hostname.tld', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($customer_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$this->assertEquals('$ORIGIN test2.local.', $result[1]);\n\t\t$resstr = preg_replace('/\\s+/', '', $result[count($result)-2]);\n\t\t$against = preg_replace('/\\s+/', '', '@ 604800  IN      MX      10      mx.hostname.tld.');\n\t\t$this->assertEquals($against, $resstr);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesGet\n\t */\n\tpublic function testCustomerDomainZonesGetNoSubdomains()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'mysub2.test2.local'\n\t\t];\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage(\"DNS zones can only be generated for the main domain, not for subdomains\");\n\t\tDomainZones::getLocal($customer_userdata, $data)->get();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesGet\n\t */\n\tpublic function testCustomerDomainZonesGetWithDMARC()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('dmarc.use_dmarc', 1, true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($customer_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$foundCnt = 0;\n\t\t$foundStr = '';\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, 0, 7) == '_dmarc\t') {\n\t\t\t\t$foundCnt++;\n\t\t\t\t$foundStr = $entry;\n\t\t\t}\n\t\t}\n\t\t$this->assertEquals(1, $foundCnt);\n\t\t$resstr = preg_replace('/\\s+/', '', $foundStr);\n\t\t$against = preg_replace('/\\s+/', '', '_dmarc\t604800\tIN\tTXT\tv=DMARC1;p=none;');\n\t\t$this->assertEquals($against, $resstr);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesGetWithDMARC\n\t */\n\tpublic function testCustomerDomainZonesGetWithDMARCSubdomain()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// enable isemaildomain for subdomain\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$data = [\n\t\t\t'domainname' => 'mysub2.test2.local',\n\t\t\t'isemaildomain' => 1,\n\t\t\t'customerid' => 1\n\t\t];\n\t\t$json_result = SubDomains::getLocal($reseller_userdata, $data)->update();\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($customer_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$foundCnt = 0;\n\t\t$foundStr = '';\n\t\t$this->assertTrue(count($result) > 1);\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, 0, 14) == '_dmarc.mysub2\t') {\n\t\t\t\t$foundCnt++;\n\t\t\t\t$foundStr = $entry;\n\t\t\t}\n\t\t}\n\t\t$this->assertEquals(1, $foundCnt);\n\t\t$resstr = preg_replace('/\\s+/', '', $foundStr);\n\t\t$against = preg_replace('/\\s+/', '', '_dmarc.mysub2\t604800\tIN\tTXT\tv=DMARC1;p=none;');\n\t\t$this->assertEquals($against, $resstr);\n\t}\n\n\tpublic function testAdminDomainZonesUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(303);\n\t\tDomainZones::getLocal($admin_userdata)->update();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesGet\n\t */\n\tpublic function testCustomerDomainZonesAddA()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'www2',\n\t\t\t'type' => 'A',\n\t\t\t'content' => '127.0.0.1'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, 0, 4) == 'www2') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('www2\t18000\tIN\tA\t127.0.0.1', $entry);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesAddA\n\t */\n\tpublic function testAdminDomainZonesListing()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'www2',\n\t\t\t'type' => 'A',\n\t\t\t'content' => '127.0.0.1'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('www2', $result['list'][0]['record']);\n\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesAddA\n\t */\n\tpublic function testCustomerDomainZonesAddAInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'www3',\n\t\t\t'type' => 'A',\n\t\t\t'content' => 'a.b.c.d',\n\t\t\t'ttl' => - 1\n\t\t];\n\t\t$this->expectExceptionMessage(\"No valid IP address for A-record given\");\n\t\tDomainZones::getLocal($customer_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesAddA\n\t */\n\tpublic function testCustomerDomainZonesAddADuplicate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'www2',\n\t\t\t'type' => 'A',\n\t\t\t'content' => '127.0.0.1',\n\t\t\t'ttl' => - 1\n\t\t];\n\t\t$this->expectExceptionMessage(\"Record already exists\");\n\t\tDomainZones::getLocal($customer_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesGet\n\t */\n\tpublic function testCustomerDomainZonesAddAAAA()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'www3',\n\t\t\t'type' => 'AAAA',\n\t\t\t'content' => '::1'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, 0, 4) == 'www3') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('www3\t18000\tIN\tAAAA\t::1', $entry);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesAddA\n\t */\n\tpublic function testCustomerDomainZonesAddAAAAInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'www4',\n\t\t\t'type' => 'AAAA',\n\t\t\t'content' => 'z:z123.123',\n\t\t\t'ttl' => - 1\n\t\t];\n\t\t$this->expectExceptionMessage(\"No valid IP address for AAAA-record given\");\n\t\tDomainZones::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddMX()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '',\n\t\t\t'type' => 'MX',\n\t\t\t'prio' => 10,\n\t\t\t'content' => 'mail.example.com.'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen('mail.example.com.') * - 1) == 'mail.example.com.') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tMX\t10\tmail.example.com.', $entry);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainZonesAddMX\n\t */\n\tpublic function testAdminDomainZonesAddMXNoPrio()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '',\n\t\t\t'type' => 'MX',\n\t\t\t'content' => 'mail.example.com.'\n\t\t];\n\t\t$this->expectExceptionMessage(\"Invalid MX priority given\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainZonesAddMX\n\t */\n\tpublic function testAdminDomainZonesAddMXInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '',\n\t\t\t'type' => 'MX',\n\t\t\t'prio' => 20,\n\t\t\t'content' => 'localhost'\n\t\t];\n\t\t$this->expectExceptionMessage(\"The MX content value must be a valid domain-name\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIssue()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issue \"letsencrypt.org\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, - strlen($content)) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t0 issue \"letsencrypt.org\"', $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIssueWithParameters()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issue \"letsencrypt.org; account=230123\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen($content) * - 1) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t' . $content, $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIssueWithTwoParameters()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issue \"letsencrypt.org; account=230123 policy=ev\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen($content) * - 1) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t' . $content, $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAInvalidIssueValue()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issue \"\"letsencrypt.org\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAInvalidIssueDomain()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issue \"no-valid-domain\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAInvalidIssueTld()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issue \"no-valid-domai.n\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIssueWild()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issuewild \"letsencrypt.org\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen($content) * - 1) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t' . $content, $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIssueWildWithParameters()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issuewild \"letsencrypt.org; account=230123\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen($content) * - 1) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t' . $content, $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIssueWildWithTwoParameters()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issuewild \"letsencrypt.org; account=230123 policy=ev\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen($content) * - 1) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t' . $content, $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAInvalidIssueWildValue()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issuewild \"\"letsencrypt.org\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAInvalidIssueWildDomain()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issuewild \"no-valid-domain\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAInvalidIssueWildTld()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 issuewild \"no-valid-domai.n\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIodefMail()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 iodef \"mailto:security@example.com\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen($content) * - 1) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t' . $content, $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIodefMailInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 iodef \"mailtosecurity@example.com\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIodefHttp()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 iodef \"http://iodef.example.com/\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen($content) * - 1) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t' . $content, $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIodefHttpInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 iodef \"http:/iodef.example.com/\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIodefHttps()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 iodef \"https://iodef.example.com/\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen($content) * - 1) == $content) {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tCAA\t' . $content, $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCAAIodefHttpsInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$content = '0 iodef \"https:/iodef.example.com/\"';\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '@',\n\t\t\t'type' => 'CAA',\n\t\t\t'content' => $content\n\t\t];\n\t\t$this->expectExceptionMessage(\"DNS content invalid\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddCname()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'db',\n\t\t\t'type' => 'CNAME',\n\t\t\t'content' => 'db.example.com.'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen('db.example.com.') * - 1) == 'db.example.com.') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('db\t18000\tIN\tCNAME\tdb.example.com.', $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddCnameLocal()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'db2',\n\t\t\t'type' => 'CNAME',\n\t\t\t'content' => 'db3'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen('db3.test2.local.') * - 1) == 'db3.test2.local.') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('db2\t18000\tIN\tCNAME\tdb3.test2.local.', $entry);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainZonesAddCname\n\t */\n\tpublic function testAdminDomainZonesAddCnameInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '',\n\t\t\t'type' => 'CNAME',\n\t\t\t'content' => 'localhost.'\n\t\t];\n\t\t$this->expectExceptionMessage(\"Invalid domain-name for CNAME record\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainZonesAddCname\n\t */\n\tpublic function testAdminDomainZonesAddCnameInvalidWwwAlias()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// set domain to www-alias\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'selectserveralias' => '1'\n\t\t];\n\t\tDomains::getLocal($admin_userdata, $data)->update();\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'www',\n\t\t\t'type' => 'CNAME',\n\t\t\t'content' => 'testing.local'\n\t\t];\n\t\t$this->expectExceptionMessage('Cannot set CNAME record for \"www\" as domain is set to generate a www-alias. Please change settings to either \"No alias\" or \"Wildcard alias\"');\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainZonesAddCname\n\t */\n\tpublic function testAdminDomainZonesAddForExistingCname()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// set domain to www-alias\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'selectserveralias' => '1'\n\t\t];\n\t\tDomains::getLocal($admin_userdata, $data)->update();\n\n\t\tforeach ([\n\t\t\t'A' => '127.0.0.1',\n\t\t\t'AAAA' => '::1',\n\t\t\t'MX' => 'mail.example.com.',\n\t\t\t'NS' => 'ns.example.com.'\n\t\t] as $type => $val) {\n\t\t\t$data = [\n\t\t\t\t'domainname' => 'test2.local',\n\t\t\t\t'record' => 'db',\n\t\t\t\t'type' => $type,\n\t\t\t\t'content' => $val\n\t\t\t];\n\t\t\t$this->expectExceptionMessage('There already exists a CNAME record with the same record-name. It can not be used for another type.');\n\t\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainZonesAddCname\n\t */\n\tpublic function testAdminDomainZonesAddCnameUnderscore()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => 'dkimtest',\n\t\t\t'type' => 'CNAME',\n\t\t\t'content' => 'test._domainkey.myhost.tld.'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen('test._domainkey.myhost.tld.') * - 1) == 'test._domainkey.myhost.tld.') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('dkimtest\t18000\tIN\tCNAME\ttest._domainkey.myhost.tld.', $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddNS()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '',\n\t\t\t'type' => 'NS',\n\t\t\t'content' => 'ns.example.com.'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, strlen('ns.example.com.') * - 1) == 'ns.example.com.') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('@\t18000\tIN\tNS\tns.example.com.', $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddNsInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '',\n\t\t\t'type' => 'NS',\n\t\t\t'content' => 'localhost.'\n\t\t];\n\t\t$this->expectExceptionMessage(\"Invalid domain-name for NS record\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainZonesAddTXT()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '_test1',\n\t\t\t'type' => 'TXT',\n\t\t\t'content' => 'aw yeah'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, 0, 6) == '_test1') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('_test1\t18000\tIN\tTXT\taw yeah', $entry);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesGetWithDMARC\n\t */\n\tpublic function testAdminDomainZonesAddTXTCustomDMARC()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '_dmarc',\n\t\t\t'type' => 'TXT',\n\t\t\t'content' => 'v=DMARC1;p=none;overwrite=TRUE'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$foundCnt = 0;\n\t\t$foundStr = '';\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, 0, 7) == '_dmarc\t') {\n\t\t\t\t$foundCnt++;\n\t\t\t\t$foundStr = $entry;\n\t\t\t}\n\t\t}\n\t\t$this->assertEquals(1, $foundCnt);\n\t\t$resstr = preg_replace('/\\s+/', '', $foundStr);\n\t\t$against = preg_replace('/\\s+/', '', '_dmarc\t18000\tIN\tTXT\tv=DMARC1;p=none;overwrite=TRUE');\n\t\t$this->assertEquals($against, $resstr);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDomainZonesGetWithDMARCSubdomain\n\t */\n\tpublic function testAdminDomainZonesAddTXTCustomDMARCSubdomain()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '_dmarc.mysub2',\n\t\t\t'type' => 'TXT',\n\t\t\t'content' => 'v=DMARC1;p=none;overwrite=TRUE'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$foundCnt = 0;\n\t\t$foundStr = '';\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, 0, 14) == '_dmarc.mysub2\t') {\n\t\t\t\t$foundCnt++;\n\t\t\t\t$foundStr = $entry;\n\t\t\t}\n\t\t}\n\t\t$this->assertEquals(1, $foundCnt);\n\t\t$resstr = preg_replace('/\\s+/', '', $foundStr);\n\t\t$against = preg_replace('/\\s+/', '', '_dmarc.mysub2\t18000\tIN\tTXT\tv=DMARC1;p=none;overwrite=TRUE');\n\t\t$this->assertEquals($against, $resstr);\n\t}\n\n\tpublic function testAdminDomainZonesAddSRV()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '_test2',\n\t\t\t'type' => 'SRV',\n\t\t\t'prio' => 50,\n\t\t\t'content' => '2 1 srv.example.com.'\n\t\t];\n\t\t$json_result = DomainZones::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue(count($result) > 1);\n\t\t$found = false;\n\t\tforeach ($result as $entry) {\n\t\t\tif (substr($entry, 0, 6) == '_test2') {\n\t\t\t\t$found = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t$this->assertTrue($found);\n\t\t$this->assertEquals('_test2\t18000\tIN\tSRV\t50\t2 1 srv.example.com.', $entry);\n\t}\n\n\tpublic function testAdminDomainZonesAddSrvInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'record' => '_test2',\n\t\t\t'type' => 'SRV',\n\t\t\t'prio' => 50,\n\t\t\t'content' => 'srv.example.com.'\n\t\t];\n\t\t$this->expectExceptionMessage(\"Invalid SRV content, must contain of fields weight, port and target, e.g.: 5 5060 sipserver.example.com.\");\n\t\tDomainZones::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerDomainZonesDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'entry_id' => 1\n\t\t];\n\t\t$json_result = DomainZones::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true);\n\t\t$this->assertTrue($result['data']);\n//\t\t$this->assertEquals(200, http_response_code());\n\t}\n\n\tpublic function testCustomerDomainZonesDeleteUnmodified()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'entry_id' => 1337\n\t\t];\n\t\t$json_result = DomainZones::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true);\n\t\t$this->assertTrue($result['data']);\n\t\t//$this->assertEquals(304, http_response_code());\n\t}\n}\n"
  },
  {
    "path": "tests/Domains/DomainsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Settings;\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Domains;\nuse Froxlor\\Database\\Database;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Domains\n * @covers \\Froxlor\\Api\\Commands\\SubDomains\n */\nclass DomainsTest extends TestCase\n{\n\n\tpublic function testAdminDomainsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'domain' => 'TEST.local',\n\t\t\t'customerid' => $customer_userdata['customerid'],\n\t\t\t'override_tls' => 1,\n\t\t\t'ssl_protocols' => array(\n\t\t\t\t'TLSv1.2',\n\t\t\t\t'TLSv1.3'\n\t\t\t),\n\t\t\t'description' => 'awesome domain'\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test.local/', $result['documentroot']);\n\t\t$this->assertTrue(in_array('TLSv1.3', explode(\",\", $result['ssl_protocols'])));\n\t\t$this->assertEquals('0', $result['isemaildomain']);\n\t\t$this->assertEquals('awesome domain', $result['description']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsAdd\n\t */\n\tpublic function testAdminDomainsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$json_result = Domains::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('test.local', $result['list'][0]['domain']);\n\t\t$this->assertEquals(2, count($result['list'][0]['ipsandports']));\n\t\t$this->assertEquals(\"82.149.225.56\", $result['list'][0]['ipsandports'][1]['ip']);\n\n\t\t$json_result = Domains::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\n\t\t$json_result = Domains::getLocal($admin_userdata, [\n\t\t\t'with_ips' => false\n\t\t])->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEmpty($result['list'][0]['ipsandports']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsAdd\n\t */\n\tpublic function testResellerDomainsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = Domains::getLocal($reseller_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['count']);\n\n\t\t$json_result = Domains::getLocal($reseller_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result);\n\t}\n\n\tpublic function testResellerDomainsAddWithCanEditPhpSettingsDefaultIp()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$reseller_userdata['caneditphpsettings'] = 1;\n\t\t$data = [\n\t\t\t'domain' => 'test2.local',\n\t\t\t'customerid' => 1,\n\t\t\t'isbinddomain' => 1\n\t\t];\n\t\t// the reseller is not allowed to use the default ip/port\n\t\t$this->expectExceptionMessage(\"The ip/port combination you have chosen doesn't exist.\");\n\t\tDomains::getLocal($reseller_userdata, $data)->add();\n\t}\n\n\tpublic function testResellerDomainsAddWithCanEditPhpSettingsAllowedIp()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// first, allow reseller access to ip #4\n\t\tAdmins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller',\n\t\t\t'ipaddress' => 4\n\t\t))->update();\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$data = [\n\t\t\t'domain' => 'test2.local',\n\t\t\t'customerid' => 1,\n\t\t\t'ipandport' => 4,\n\t\t\t'isemaildomain' => 1,\n\t\t\t'subcanemaildomain' => 2\n\t\t];\n\t\t$json_result = Domains::getLocal($reseller_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test2.local', $result['domain']);\n\t\t$this->assertEquals(2, $result['subcanemaildomain']);\n\t}\n\n\tpublic function testResellerDomainsAddWithAbsolutePathNoChangeServerSettings()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$data = [\n\t\t\t'domain' => 'test3.local',\n\t\t\t'customerid' => 1,\n\t\t\t'documentroot' => '/some/absolute/directory/the_reseller/cannot/set/',\n\t\t\t'ipandport' => 4\n\t\t];\n\t\t$this->expectExceptionMessage(\"The user does not have the permission to specify directories outside the customers home-directory. Please specify a relative path (no leading /).\");\n\t\t$json_result = Domains::getLocal($reseller_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsAdd\n\t */\n\tpublic function testResellerDomainsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'ssl_protocols' => 'TLSv1',\n\t\t\t'documentroot' => '/var/customers/webs/test1/sub/'\n\t\t];\n\t\t$json_result = Domains::getLocal($reseller_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEmpty($result['ssl_protocols']);\n\t\t$this->assertEquals('test2.local', $result['domain']);\n\t\t$this->assertEquals('/var/customers/webs/test1/sub/', $result['documentroot']);\n\t}\n\n\t/**\n\t *\n\t * @depends testResellerDomainsUpdate\n\t */\n\tpublic function testResellerDomainsUpdateAboslutePathNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local',\n\t\t\t'documentroot' => '/some/other/dir'\n\t\t];\n\t\t$this->expectExceptionMessage(\"The user does not have the permission to specify directories outside the customers home-directory. Please specify a relative path (no leading /).\");\n\t\t$json_result = Domains::getLocal($reseller_userdata, $data)->update();\n\t}\n\n\tpublic function testAdminDomainsAddSysHostname()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'domain' => 'dev.froxlor.org',\n\t\t\t'customerid' => 1\n\t\t];\n\t\t$this->expectExceptionMessage('The server-hostname cannot be used as customer-domain.');\n\t\tDomains::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainsAddNoPunycode()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'domain' => 'xn--asdasd.tld',\n\t\t\t'customerid' => 1\n\t\t];\n\t\t$this->expectExceptionMessage('You must not specify punycode (IDNA). The domain will automatically be converted');\n\t\tDomains::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDomainsAddInvalidDomain()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'domain' => 'dom?*ain.tld',\n\t\t\t'customerid' => 1\n\t\t];\n\t\t$this->expectExceptionMessage(\"Wrong Input in Field 'Domain'\");\n\t\tDomains::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsAdd\n\t */\n\tpublic function testAdminDomainsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'email_only' => 1,\n\t\t\t'override_tls' => 0,\n\t\t\t'documentroot' => 'web',\n\t\t\t'description' => 'changed desc'\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['email_only']);\n\t\t$this->assertFalse(in_array('TLSv1.3', explode(\",\", $result['ssl_protocols'])));\n\t\t$this->assertEquals('test.local', $result['domain']);\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'web/', $result['documentroot']);\n\t\t$this->assertEquals('changed desc', $result['description']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsAdd\n\t */\n\tpublic function testAdminDomainsUpdateAbsolutePath()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'documentroot' => '/web'\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('/web/', $result['documentroot']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsAdd\n\t */\n\tpublic function testAdminDomainsUpdateIssue756()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'ssl_redirect' => 1\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t// get ssl ip/port for domain which should still exist\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT COUNT(*) as numips\n\t\t\tFROM `\" . TABLE_DOMAINTOIP . \"` di\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_IPSANDPORTS . \"` i ON i.id = di.id_ipandports\n\t\t\tLEFT JOIN `\" . TABLE_PANEL_DOMAINS . \"` d ON d.id = di.id_domain\n\t\t\tWHERE d.id = :did AND i.ssl = 1\n\t\t\");\n\t\t$result_ips = Database::pexecute_first($sel_stmt, [\n\t\t\t'did' => $result['id']\n\t\t], true, true);\n\t\t$this->assertEquals(1, $result_ips['numips']);\n\n\t\t// test clearing\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'ssl_ipandport' => array()\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t// get ssl ip/port for domain which should still exist\n\t\t$result_ips = Database::pexecute_first($sel_stmt, [\n\t\t\t'did' => $result['id']\n\t\t], true, true);\n\t\t$this->assertEquals(1, $result_ips['numips']);\n\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'remove_ssl_ipandport' => 1\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t// get ssl ip/port for domain which should still exist\n\t\t$result_ips = Database::pexecute_first($sel_stmt, [\n\t\t\t'did' => $result['id']\n\t\t], true, true);\n\t\t$this->assertEquals(0, $result_ips['numips']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsUpdate\n\t */\n\tpublic function testAdminDomainsMoveButUnknownCustomer()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'customerid' => $customer_userdata['customerid'] + 1\n\t\t];\n\t\tSettings::Set('panel.allow_domain_change_customer', 1);\n\t\t$this->expectExceptionMessage(\"Customer with id #2 could not be found\");\n\t\tDomains::getLocal($admin_userdata, $data)->update();\n\t}\n\n\tpublic function testAdminDomainsMove()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// add new customer\n\t\t$data = [\n\t\t\t'new_loginname' => 'test3',\n\t\t\t'email' => 'test3@froxlor.org',\n\t\t\t'firstname' => 'Test',\n\t\t\t'name' => 'Testman',\n\t\t\t'customernumber' => 1339,\n\t\t\t'new_customer_password' => 'h0lYmo1y'\n\t\t];\n\t\t$json_result = Customers::getLocal($admin_userdata, $data)->add();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'customerid' => $customer_userdata['customerid']\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['customerid'], $result['customerid']);\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test.local/', $result['documentroot']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsMove\n\t */\n\tpublic function testAdminDomainsDuplicate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'domain' => 'test.duplicate.local',\n\t\t\t'description' => 'duplicated domain'\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->duplicate();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('/var/customers/webs/test3/test.duplicate.local/', $result['documentroot']);\n\t\t$this->assertEquals(1, $result['email_only']);\n\t\t$this->assertEquals('test.duplicate.local', $result['domain']);\n\t\t$this->assertEquals('duplicated domain', $result['description']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDomainsDuplicate\n\t */\n\tpublic function testAdminDomainsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'domainname' => 'test.local',\n\t\t\t'delete_mainsubdomains' => 1\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test.local', $result['domain']);\n\n\t\t// remove customer again\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test3'\n\t\t))->delete();\n\t}\n\n\tpublic function testCustomerDomainsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\t$json_result = Domains::getLocal($customer_userdata)->listing();\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\t$json_result = Domains::getLocal($customer_userdata)->listingCount();\n\t}\n\n\tpublic function testAdminIdnDomainsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'domain' => 'täst.local',\n\t\t\t'customerid' => $customer_userdata['customerid']\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'xn--tst-qla.local/', $result['documentroot']);\n\t\t$this->assertEquals('xn--tst-qla.local', $result['domain']);\n\t\t$this->assertEquals('täst.local', $result['domain_ace']);\n\n\t\tDomains::getLocal($admin_userdata, [\n\t\t\t'domainname' => 'täst.local'\n\t\t])->delete();\n\t}\n\n\t/**\n\t *\n\t * @refs https://github.com/Froxlor/Froxlor/issues/899\n\t */\n\tpublic function testAdminIdn2DomainsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'domain' => 'उदाहरण.भारत',\n\t\t\t'customerid' => $customer_userdata['customerid']\n\t\t];\n\t\t$json_result = Domains::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'xn--p1b6ci4b4b3a.xn--h2brj9c/', $result['documentroot']);\n\t\t$this->assertEquals('xn--p1b6ci4b4b3a.xn--h2brj9c', $result['domain']);\n\t\t$this->assertEquals('उदाहरण.भारत', $result['domain_ace']);\n\n\t\tDomains::getLocal($admin_userdata, [\n\t\t\t'domainname' => 'उदाहरण.भारत'\n\t\t])->delete();\n\t}\n\n\tpublic function testAdminDomainsAddDnsLetsEncryptFail()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\tSettings::Set('system.le_domain_dnscheck', 1);\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'domain' => 'no-dns.local',\n\t\t\t'customerid' => $customer_userdata['customerid'],\n\t\t\t'letsencrypt' => 1,\n\t\t\t'description' => 'no dns domain'\n\t\t];\n\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage('The domains DNS does not include any of the chosen IP addresses. Let\\'s Encrypt certificate generation not possible.');\n\t\tDomains::getLocal($admin_userdata, $data)->add();\n\t}\n}\n"
  },
  {
    "path": "tests/Emails/EmailsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Settings;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Emails;\nuse Froxlor\\Api\\Commands\\EmailForwarders;\nuse Froxlor\\Api\\Commands\\EmailAccounts;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Emails\n * @covers \\Froxlor\\Api\\Commands\\EmailForwarders\n * @covers \\Froxlor\\Api\\Commands\\EmailAccounts\n * @covers \\Froxlor\\Api\\Commands\\Customers\n * @covers \\Froxlor\\Api\\Commands\\Admins\n */\nclass MailsTest extends TestCase\n{\n\n\tpublic function testCustomerEmailsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// set domains as hidden to test whether the internal flag works\n\t\tSettings::Set('panel.customer_hide_options', 'domains', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'email_part' => 'info',\n\t\t\t'domain' => 'test2.local',\n\t\t\t'description' => 'awesome email'\n\t\t];\n\t\t$json_result = Emails::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(\"info@test2.local\", $result['email_full']);\n\t\t$this->assertEquals(0, $result['iscatchall']);\n\t\t$this->assertEquals('awesome email', $result['description']);\n\n\t\t// reset setting\n\t\tSettings::Set('panel.customer_hide_options', '', true);\n\t}\n\n\tpublic function testAdminEmailsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'email_part' => 'catchall',\n\t\t\t'domain' => 'test2.local',\n\t\t\t'iscatchall' => 1,\n\t\t\t'customerid' => 1\n\t\t];\n\t\t$json_result = Emails::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(\"catchall@test2.local\", $result['email_full']);\n\t\t$this->assertEquals(1, $result['iscatchall']);\n\t}\n\n\tpublic function testAdminEmailsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'emailaddr' => 'catchall@test2.local',\n\t\t\t'iscatchall' => 0,\n\t\t\t'customerid' => 1\n\t\t];\n\t\t$json_result = Emails::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['iscatchall']);\n\t}\n\n\tpublic function testCustomerEmailsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'catchall@test2.local',\n\t\t\t'iscatchall' => 1,\n\t\t\t'description' => 'now with catchall'\n\t\t];\n\t\t$json_result = Emails::getLocal($customer_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['iscatchall']);\n\t\t$this->assertEquals('now with catchall', $result['description']);\n\t}\n\n\tpublic function testCustomerEmailForwardersAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'destination' => 'other@domain.tld'\n\t\t];\n\t\t$json_result = EmailForwarders::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('other@domain.tld', $result['destination']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersAdd\n\t */\n\tpublic function testCustomerEmailForwardersAddNoMoreResources()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$customer_userdata['email_forwarders_used'] = $customer_userdata['email_forwarders'];\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage(\"No more resources available\");\n\t\tEmailForwarders::getLocal($customer_userdata)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersAddNoMoreResources\n\t */\n\tpublic function testCustomerEmailForwardersAddEmailHidden()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.customer_hide_options', 'email', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(405);\n\t\t$this->expectExceptionMessage(\"You cannot access this resource\");\n\t\tEmailForwarders::getLocal($customer_userdata)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersAddEmailHidden\n\t */\n\tpublic function testCustomerEmailForwardersDeleteEmailHidden()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.customer_hide_options', 'email', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(405);\n\t\t$this->expectExceptionMessage(\"You cannot access this resource\");\n\t\tEmailForwarders::getLocal($customer_userdata)->delete();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersDeleteEmailHidden\n\t */\n\tpublic function testCustomerEmailForwardersAddAnother()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.customer_hide_options', '', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'destination' => 'other2@domain.tld'\n\t\t];\n\t\t$json_result = EmailForwarders::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('other@domain.tld other2@domain.tld', $result['destination']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersAddAnother\n\t */\n\tpublic function testCustomerEmailForwardersListing()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.customer_hide_options', '', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local'\n\t\t];\n\t\t$json_result = EmailForwarders::getLocal($customer_userdata, $data)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\t\t$this->assertEquals(0, $result['list'][0]['id']);\n\t\t$this->assertEquals('other@domain.tld', $result['list'][0]['address']);\n\t\t$this->assertEquals(1, $result['list'][1]['id']);\n\t\t$this->assertEquals('other2@domain.tld', $result['list'][1]['address']);\n\n\t\t$json_result = EmailForwarders::getLocal($customer_userdata, $data)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersDeleteEmailHidden\n\t */\n\tpublic function testCustomerEmailForwardersAddWithSpaces()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.customer_hide_options', '', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'destination' => 'other3@domain.tld  '\n\t\t];\n\t\t$json_result = EmailForwarders::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('other@domain.tld other2@domain.tld other3@domain.tld', $result['destination']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersAdd\n\t */\n\tpublic function testCustomerEmailForwardersAddExistingAsMail()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'destination' => 'info@test2.local'\n\t\t];\n\t\t$this->expectExceptionMessage(\"The forwarder to info@test2.local already exists as active email-address.\");\n\t\tEmailForwarders::getLocal($customer_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersAdd\n\t */\n\tpublic function testCustomerEmailForwardersAddExistingAsDestination()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'destination' => 'other@domain.tld'\n\t\t];\n\t\t$this->expectExceptionMessage('You have already defined a forwarder to \"other@domain.tld\"');\n\t\tEmailForwarders::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerEmailForwardersAddInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'destination' => '@domain.com'\n\t\t];\n\t\t$this->expectExceptionMessage(\"The forwarder domain.com contains invalid character(s) or is incomplete.\");\n\t\tEmailForwarders::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminEmailForwadersUndefinedGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(303);\n\t\tEmailForwarders::getLocal($admin_userdata)->get();\n\t}\n\n\tpublic function testAdminEmailForwadersUndefinedUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(303);\n\t\tEmailForwarders::getLocal($admin_userdata)->update();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersAddAnother\n\t */\n\tpublic function testCustomerEmailForwardersDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'forwarderid' => 1\n\t\t];\n\t\t$json_result = EmailForwarders::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('other@domain.tld other3@domain.tld', $result['destination']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerEmailForwardersAddAnother\n\t */\n\tpublic function testCustomerEmailForwardersDeleteUnknown()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'forwarderid' => 1337\n\t\t];\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"Unknown forwarder id\");\n\t\tEmailForwarders::getLocal($customer_userdata, $data)->delete();\n\t}\n\n\tpublic function testCustomerEmailsListing()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.customer_hide_options', '', true);\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$json_result = Emails::getLocal($customer_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\t\t$this->assertEquals(\"info@test2.local\", $result['list'][0]['email']);\n\t\t$this->assertEquals(\"@test2.local\", $result['list'][1]['email']);\n\n\t\t$json_result = Emails::getLocal($customer_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testCustomerEmailAccountsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\tSettings::Set('panel.sendalternativemail', 1, true);\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'email_password' => \\Froxlor\\System\\Crypt::generatePassword(),\n\t\t\t'alternative_email' => 'noone@example.com',\n\t\t\t'email_quota' => 1337,\n\t\t\t'sendinfomail' => TRAVIS_CI == 1 ? 0 : 1\n\t\t];\n\t\t$json_result = EmailAccounts::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['popaccountid']);\n\n\t\tswitch (Settings::Get('system.passwordcryptfunc')) {\n\t\t\tcase 'argon2i':\n\t\t\t\t$cpPrefix = '{ARGON2I}';\n\t\t\t\tbreak;\n\t\t\tcase 'argon2id':\n\t\t\t\t$cpPrefix = '{ARGON2ID}';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t$cpPrefix = '{BLF-CRYPT}';\n\t\t\t\tbreak;\n\t\t}\n\t\t// password is not being returned by API, so query directly\n\t\t$sel_stmt = Database::prepare(\"SELECT `password_enc` FROM `\" . TABLE_MAIL_USERS . \"` WHERE `email` = :email\");\n\t\t$result2 = Database::pexecute_first($sel_stmt, ['email' => $result['email']]);\n\t\t$this->assertEquals($cpPrefix, substr($result2['password_enc'], 0, strlen($cpPrefix)));\n\t}\n\n\tpublic function testAdminEmailAccountsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'email_password' => \\Froxlor\\System\\Crypt::generatePassword(),\n\t\t\t'alternative_email' => 'noone@example.com',\n\t\t\t'email_quota' => 1338\n\t\t];\n\t\t$json_result = EmailAccounts::getLocal($customer_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t// quota is disabled\n\t\t$this->assertEquals(0, $result['quota']);\n\t}\n\n\tpublic function testAdminEmailAccountsUpdateDeactivated()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// disable\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'loginname' => 'test1',\n\t\t\t'deactivated' => 1\n\t\t];\n\t\t$json_result = EmailAccounts::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t// quota is disabled\n\t\t$this->assertEquals(0, $result['imap']);\n\t\t$this->assertEquals(0, $result['pop3']);\n\t\t$this->assertEquals('N', $result['postfix']);\n\t\t// re-enable\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'loginname' => 'test1',\n\t\t\t'deactivated' => 0\n\t\t];\n\t\t$json_result = EmailAccounts::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t// quota is disabled\n\t\t$this->assertEquals(1, $result['imap']);\n\t\t$this->assertEquals(1, $result['pop3']);\n\t\t$this->assertEquals('Y', $result['postfix']);\n\t}\n\n\tpublic function testAdminEmailAccountsUndefinedGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(303);\n\t\tEmailAccounts::getLocal($admin_userdata)->get();\n\t}\n\n\tpublic function testAdminEmailAccountsUndefinedListing()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(303);\n\t\tEmailAccounts::getLocal($admin_userdata)->listing();\n\t}\n\n\tpublic function testCustomerEmailAccountsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'delete_userfiles' => 1\n\t\t];\n\t\t$json_result = EmailAccounts::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['popaccountid']);\n\t}\n\n\tpublic function testCustomerEmailsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// remove possible existing delete tasks\n\t\tDatabase::query(\"TRUNCATE `\" . TABLE_PANEL_TASKS . \"`\");\n\n\t\tSettings::Set('panel.sendalternativemail', 0, true);\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t// add account\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'email_password' => \\Froxlor\\System\\Crypt::generatePassword(),\n\t\t\t'alternative_email' => 'noone@example.com',\n\t\t\t'sendinfomail' => TRAVIS_CI == 1 ? 0 : 1\n\t\t];\n\t\t$json_result = EmailAccounts::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['popaccountid']);\n\n\t\t// now delete the whole address\n\t\t$data = [\n\t\t\t'emailaddr' => 'info@test2.local',\n\t\t\t'delete_userfiles' => 1\n\t\t];\n\t\t$json_result = Emails::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(\"info@test2.local\", $result['email_full']);\n\t}\n}\n"
  },
  {
    "path": "tests/Extras/DirOptionsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\DirOptions;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\DirOptions\n */\nclass DirOptionsTest extends TestCase\n{\n\n\tpublic function testCustomerDirOptionsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'path' => '/test',\n\t\t\t'options_indexes' => 1,\n\t\t\t'options_cgi' => 1,\n\t\t\t'error404path' => '/404.html',\n\t\t\t'error403path' => '/403.html',\n\t\t\t'error500path' => '/500.html'\n\t\t];\n\t\t$json_result = DirOptions::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']);\n\t\t$this->assertEquals('1', $result['options_cgi']);\n\t\t$this->assertEquals('/403.html', $result['error403path']);\n\t}\n\n\tpublic function testCustomerDirOptionsAddDuplicatePath()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'path' => '/test',\n\t\t\t'options_indexes' => 0,\n\t\t\t'options_cgi' => 0,\n\t\t\t'error404path' => '/404a.html',\n\t\t\t'error403path' => '/403a.html',\n\t\t\t'error500path' => '/500a.html'\n\t\t];\n\t\t$this->expectExceptionMessage(\"Option for path /test/ already exists\");\n\t\tDirOptions::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminDirOptionsGet()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'loginname' => 'test1'\n\t\t];\n\t\t$json_result = DirOptions::getLocal($admin_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']);\n\t}\n\n\tpublic function testResellerDirOptionsGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'loginname' => 'test1'\n\t\t];\n\t\t$json_result = DirOptions::getLocal($reseller_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']);\n\t}\n\n\tpublic function testCustomerDirOptionsGetNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'id' => 1337\n\t\t];\n\t\t$this->expectExceptionMessage(\"Directory option with id #1337 could not be found\");\n\t\tDirOptions::getLocal($admin_userdata, $data)->get();\n\t}\n\n\tpublic function testCustomerDirOptionsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'options_indexes' => 0,\n\t\t\t'options_cgi' => 0,\n\t\t\t'error403path' => '/403-test.html'\n\t\t];\n\t\t$json_result = DirOptions::getLocal($customer_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']);\n\t\t$this->assertEquals('0', $result['options_cgi']);\n\t\t$this->assertEquals('/403-test.html', $result['error403path']);\n\t}\n\n\tpublic function testAdminDirOptionsList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$json_result = DirOptions::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['list'][0]['path']);\n\n\t\t$json_result = DirOptions::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminDirOptionsList\n\t */\n\tpublic function testCustomerDirOptionsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'id' => 1\n\t\t];\n\t\t$json_result = DirOptions::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']);\n\n\t\t$data = [\n\t\t\t'id' => 1\n\t\t];\n\t\t$this->expectExceptionMessage(\"Directory option with id #1 could not be found\");\n\t\tDirOptions::getLocal($admin_userdata, $data)->get();\n\t}\n\n\tpublic function testCustomerDirOptionsAddMalformed()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'path' => '/testmalformed',\n\t\t\t'error404path' => '/\"'.PHP_EOL.'something/../../../../weird 404.html'.PHP_EOL.'#'\n\t\t];\n\t\t$json_result = DirOptions::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$expected = '/\"something/././././weird\\ 404.html#';\n\t\t$this->assertEquals($expected, $result['error404path']);\n\t}\n\n\tpublic function testCustomerDirOptionsAddMalformedInvalid()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'path' => '/testmalformed',\n\t\t\t'error404path' => '\"'.PHP_EOL.'IncludeOptional /something/else/'.PHP_EOL.'#'\n\t\t];\n\t\t$this->expectExceptionMessage(\"The value given as ErrorDocument does not seem to be a valid file, URL or string.\");\n\t\tDirOptions::getLocal($customer_userdata, $data)->add();\n\n\t\t$data = [\n\t\t\t'path' => '/testmalformed',\n\t\t\t'error404path' => '\"something\"oh no a quote within the string\"'\n\t\t];\n\t\t$this->expectExceptionMessage(\"The value given as ErrorDocument does not seem to be a valid file, URL or string.\");\n\t\tDirOptions::getLocal($customer_userdata, $data)->add();\n\t}\n}\n"
  },
  {
    "path": "tests/Extras/DirProtectionsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\DirProtections;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\DirProtections\n */\nclass DirProtectionsTest extends TestCase\n{\n\n\tpublic function testCustomerDirProtectionsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'path' => '/test',\n\t\t\t'username' => 'testing',\n\t\t\t'directory_password' => \\Froxlor\\System\\Crypt::generatePassword(),\n\t\t\t'directory_authname' => 'test1'\n\t\t];\n\t\t$json_result = DirProtections::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']);\n\t\t$this->assertEquals('test1', $result['authname']);\n\t}\n\n\tpublic function testCustomerDirProtectionsAddSameUserPath()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'path' => '/test',\n\t\t\t'username' => 'testing',\n\t\t\t'directory_password' => \\Froxlor\\System\\Crypt::generatePassword(),\n\t\t\t'directory_authname' => 'test2'\n\t\t];\n\t\t$this->expectExceptionMessage(\"Combination of username and path already exists\");\n\t\tDirProtections::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerDirProtectionsAddPasswordEqualsUsername()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$up = \\Froxlor\\System\\Crypt::generatePassword();\n\t\t$data = [\n\t\t\t'path' => '/test',\n\t\t\t'username' => $up,\n\t\t\t'directory_password' => $up,\n\t\t\t'directory_authname' => 'test3'\n\t\t];\n\t\t$this->expectExceptionMessage(\"The password should not be the same as the username.\");\n\t\tDirProtections::getLocal($customer_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDirProtectionsAdd\n\t */\n\tpublic function testAdminDirProtectionsGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'username' => 'testing',\n\t\t\t'customerid' => 1\n\t\t];\n\t\t$json_result = DirProtections::getLocal($admin_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']);\n\t\t$this->assertEquals('test1', $result['authname']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDirProtectionsAdd\n\t */\n\tpublic function testResellerDirProtectionsGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'username' => 'testing'\n\t\t];\n\t\t$json_result = DirProtections::getLocal($reseller_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']);\n\t\t$this->assertEquals('test1', $result['authname']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDirProtectionsAdd\n\t */\n\tpublic function testCustomerDirProtectionsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$json_result = DirProtections::getLocal($customer_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$data_old = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'directory_password' => \\Froxlor\\System\\Crypt::generatePassword(),\n\t\t\t'directory_authname' => 'test1337'\n\t\t];\n\t\t$json_result = DirProtections::getLocal($customer_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertTrue($data_old['password'] != $result['password']);\n\t\t$this->assertTrue($data_old['authname'] != $result['authname']);\n\t\t$this->assertEquals('test1337', $result['authname']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDirProtectionsAdd\n\t */\n\tpublic function testCustomerDirProtectionsList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$json_result = DirProtections::getLocal($customer_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\t\t$this->assertEquals('test1', $result['list'][0]['username']);\n\t\t$this->assertEquals('testing', $result['list'][1]['username']);\n\n\t\t$json_result = DirProtections::getLocal($customer_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerDirProtectionsList\n\t */\n\tpublic function testCustomerDirProtectionsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\tDirProtections::getLocal($customer_userdata, array(\n\t\t\t'username' => 'testing'\n\t\t))->delete();\n\n\t\t$json_result = DirProtections::getLocal($customer_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('test1', $result['list'][0]['username']);\n\t}\n}\n"
  },
  {
    "path": "tests/Froxlor/FroxlorTest.php",
    "content": "<?php\n\nuse Froxlor\\Api\\Commands\\Froxlor;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Froxlor\n */\nclass FroxlorTest extends TestCase\n{\n\n\tpublic function testFroxlorcheckUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Froxlor::getLocal($admin_userdata)->checkUpdate();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertContains($result['isnewerversion'] ?? -1, [0, 1]);\n\t\t$this->assertNotEmpty($result['version']);\n\t}\n}\n"
  },
  {
    "path": "tests/Froxlor/IPToolsTest.php",
    "content": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\System\\IPTools;\n\n/**\n *\n * @covers \\Froxlor\\System\\IPTools\n */\nclass IPToolsTest extends TestCase\n{\n\tpublic function testValidateIPv6()\n\t{\n\t\t$result = IPTools::is_ipv6('1.1.1.1/4');\n\t\t$this->assertFalse($result);\n\t\t$result = IPTools::is_ipv6('1.1.1.1');\n\t\t$this->assertFalse($result);\n\t\t$result = IPTools::is_ipv6('::ffff:10.20.30.40');\n\t\t$this->assertEquals('::ffff:10.20.30.40', $result);\n\t\t$result = IPTools::is_ipv6('2620:0:2d0:200::7/32');\n\t\t$this->assertFalse($result);\n\t\t$result = IPTools::is_ipv6('2620:0:2d0:200::7');\n\t\t$this->assertEquals('2620:0:2d0:200::7', $result);\n\t}\n\n\tpublic function testValidateIPinRange()\n\t{\n\t\t$result = IPTools::ip_in_range([0=>'82.149.225.46',1=>24], '123.213.132.1');\n\t\t$this->assertFalse($result);\n\t\t$result = IPTools::ip_in_range([0=>'82.149.225.46',1=>24], '2620:0:2d0:200::7');\n\t\t$this->assertFalse($result);\n\t\t$result = IPTools::ip_in_range([0=>'82.149.225.46',1=>24], '82.149.225.152');\n\t\t$this->assertTrue($result);\n\t\t$result = IPTools::ip_in_range([0=>'2620:0:2d0:200::1',1=>116], '2620:0:2d0:200::fff1');\n\t\t$this->assertFalse($result);\n\t\t$result = IPTools::ip_in_range([0=>'2620:0:2d0:200::1',1=>64], '2620:0:2d0:200::fff1');\n\t\t$this->assertTrue($result);\n\t}\n}\n"
  },
  {
    "path": "tests/Froxlor/SettingsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n *\n * @covers \\Froxlor\\Settings\n * @covers \\Froxlor\\Settings\\FroxlorVhostSettings\n */\nclass SettingsTest extends TestCase\n{\n\n\tprotected function setUp(): void\n\t{\n\t\t// start fresh\n\t\t\\Froxlor\\Settings::Stash();\n\t}\n\n\tpublic function testSettingGet()\n\t{\n\t\t$syshostname = \\Froxlor\\Settings::Get('system.hostname');\n\t\t$this->assertEquals(\"dev.froxlor.org\", $syshostname);\n\t}\n\n\tpublic function testSettingGetNoSeparator()\n\t{\n\t\t$nullval = \\Froxlor\\Settings::Get('system');\n\t\t$this->assertNull($nullval);\n\t}\n\n\tpublic function testSettingGetUnknown()\n\t{\n\t\t$nullval = \\Froxlor\\Settings::Get('thissetting.doesnotexist');\n\t\t$this->assertNull($nullval);\n\t}\n\n\tpublic function testSettingsAddNew()\n\t{\n\t\t\\Froxlor\\Settings::AddNew('temp.setting', 'empty');\n\t\t$actval = \\Froxlor\\Settings::Get('temp.setting');\n\t\t$this->assertEquals(\"empty\", $actval);\n\t}\n\n\tpublic function testSettingsAddNewSettingExists()\n\t{\n\t\t$result = \\Froxlor\\Settings::AddNew('system.ipaddress', '127.0.0.1');\n\t\t$this->assertFalse($result);\n\t}\n\n\t/**\n\t *\n\t * @depends testSettingsAddNew\n\t */\n\tpublic function testSettingSetNoSave()\n\t{\n\t\t$actval = \\Froxlor\\Settings::Get('temp.setting');\n\t\t$this->assertEquals(\"empty\", $actval);\n\t\t\\Froxlor\\Settings::Set('temp.setting', 'temp-value', false);\n\t\t$tmpval = \\Froxlor\\Settings::Get('temp.setting');\n\t\t$this->assertEquals(\"temp-value\", $tmpval);\n\t\t\\Froxlor\\Settings::Stash();\n\t\t$actval = \\Froxlor\\Settings::Get('temp.setting');\n\t\t$this->assertEquals(\"empty\", $actval);\n\t}\n\n\t/**\n\t *\n\t * @depends testSettingsAddNew\n\t */\n\tpublic function testSettingsSetInstantSave()\n\t{\n\t\t\\Froxlor\\Settings::Set('temp.setting', 'temp-value');\n\t\t\\Froxlor\\Settings::Stash();\n\t\t$tmpval = \\Froxlor\\Settings::Get('temp.setting');\n\t\t$this->assertEquals(\"temp-value\", $tmpval);\n\t}\n\n\t/**\n\t *\n\t * @depends testSettingsAddNew\n\t */\n\tpublic function testSettingsSetFlushSave()\n\t{\n\t\t\\Froxlor\\Settings::Set('temp.setting', 'another-temp-value', false);\n\t\t\\Froxlor\\Settings::Flush();\n\t\t$actval = \\Froxlor\\Settings::Get('temp.setting');\n\t\t$this->assertEquals(\"another-temp-value\", $actval);\n\t}\n\n\tpublic function testSettingsIsInList()\n\t{\n\t\t$result = \\Froxlor\\Settings::IsInList(\"system.mysql_access_host\", \"localhost\");\n\t\t$this->assertTrue($result);\n\t\t$result = \\Froxlor\\Settings::IsInList(\"system.mysql_access_host\", \"my-super-domain.de\");\n\t\t$this->assertFalse($result);\n\t}\n\t\n\tpublic function testFroxlorVhostSettings()\n\t{\n\t\t// bootstrap.php adds two IPs, one ssl one non-ssl both with vhostcontainer = 1\n\t\t$result = \\Froxlor\\Settings\\FroxlorVhostSettings::hasVhostContainerEnabled(false);\n\t\t$this->assertTrue($result);\n\t\t$result = \\Froxlor\\Settings\\FroxlorVhostSettings::hasVhostContainerEnabled(true);\n\t\t$this->assertTrue($result);\n\t\t// now disable both\n\t\t\\Froxlor\\Database\\Database::query(\"UPDATE `\". TABLE_PANEL_IPSANDPORTS . \"` SET `vhostcontainer` = '0'\");\n\t\t$result = \\Froxlor\\Settings\\FroxlorVhostSettings::hasVhostContainerEnabled(false);\n\t\t$this->assertFalse($result);\n\t\t$result = \\Froxlor\\Settings\\FroxlorVhostSettings::hasVhostContainerEnabled(true);\n\t\t$this->assertFalse($result);\n\t\t// and change back\n\t\t\\Froxlor\\Database\\Database::query(\"UPDATE `\". TABLE_PANEL_IPSANDPORTS . \"` SET `vhostcontainer` = '1'\");\n\t}\n}\n"
  },
  {
    "path": "tests/Froxlor/StoreTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\nuse Froxlor\\Settings;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Settings\\Store;\n\n/**\n *\n * @covers \\Froxlor\\Settings\\Store\n */\nclass StoreTest extends TestCase\n{\n\n\tpublic function testStoreSettingClearCertificates()\n\t{\n\t\t// when froxlor vhost setting \"use lets encrypt\" is set to false, the corresponding\n\t\t// certificate needs to be cleaned\n\t\t// for testing purposes, let's add some entry to the table\n\t\tDatabase::query(\"INSERT INTO `domain_ssl_settings` SET `domainid` = '0', `ssl_cert_file` = 'test-content'\");\n\n\t\t$fielddata = array(\n\t\t\t'label' => 'le_froxlor_enabled',\n\t\t\t'settinggroup' => 'system',\n\t\t\t'varname' => 'le_froxlor_enabled'\n\t\t);\n\t\tStore::storeSettingClearCertificates('system_le_froxlor_enabled', $fielddata, 0);\n\n\t\t// there should be no entry in domain_ssl_settings now\n\t\t$result = Database::query(\"SELECT COUNT(*) as entries FROM `domain_ssl_settings` WHERE `domainid` = '0'\");\n\t\t$result = $result->fetch(\\PDO::FETCH_ASSOC);\n\t\t$this->assertEquals(0, (int) $result['entries']);\n\n\t\t// truncate the table for other tests\n\t\tDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`;\");\n\t}\n\n\tpublic function testStoreSettingDefaultIp()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// the customer should have a std-subdomin\n\t\tCustomers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1,\n\t\t\t'createstdsubdomain' => 1\n\t\t))->update();\n\t\t\n\t\t// we need a second non-ssl IP\n\t\tDatabase::query(\"INSERT INTO `panel_ipsandports` SET `ip` = '82.149.225.47', `port` = '80'\");\n\t\t$ip_id = Database::lastInsertId();\n\t\t$default_ip = Settings::Get('system.defaultip');\n\n\t\t// get all std-subdomains\n\t\t$customerstddomains_result_stmt = Database::prepare(\"\n\t\t\tSELECT `standardsubdomain` FROM `\" . TABLE_PANEL_CUSTOMERS . \"` WHERE `standardsubdomain` <> '0'\n\t\t\");\n\t\tDatabase::pexecute($customerstddomains_result_stmt);\n\n\t\t$ids = array();\n\t\twhile ($customerstddomains_row = $customerstddomains_result_stmt->fetch(\\PDO::FETCH_ASSOC)) {\n\t\t\t$ids[] = (int) $customerstddomains_row['standardsubdomain'];\n\t\t}\n\t\t\n\t\tif (count($ids) <= 0) {\n\t\t\t$this->fail(\"There should be customer std-subdomains for this test to make sense\");\n\t\t}\n\t\t\n\t\t// check that they have the current default IP set\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_DOMAINTOIP . \"`\n\t\t\tWHERE `id_domain` IN (\" . implode(', ', $ids) . \") AND `id_ipandports` = :ipid\n\t\t\");\n\t\tDatabase::pexecute($sel_stmt, array('ipid' => $default_ip));\n\t\t$current_result = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\t// we assume there are entries\n\t\t$this->assertTrue(count($current_result) > 0);\n\n\t\t$fielddata = array(\n\t\t\t'label' => 'serversettingsipaddress',\n\t\t\t'settinggroup' => 'system',\n\t\t\t'varname' => 'defaultip'\n\t\t);\n\t\tStore::storeSettingDefaultIp('serversettings_ipaddress', $fielddata, $ip_id);\n\t\t\n\t\t// check that they do not have the current default IP set anymore\n\t\tDatabase::pexecute($sel_stmt, array('ipid' => $default_ip));\n\t\t$current_result = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\t// we assume there are entries\n\t\t$this->assertTrue(count($current_result) == 0);\n\t\t\n\t\t// check that they have the new default IP set\n\t\tDatabase::pexecute($sel_stmt, array('ipid' => $ip_id));\n\t\t$current_result = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\t// we assume there are entries\n\t\t$this->assertTrue(count($current_result) > 0);\n\t}\n\t\n\tpublic function testStoreSettingDefaultTheme()\n\t{\n\t\t$current_theme = Settings::Get('panel.default_theme');\n\t\t// allow theme changing for admins/customers so a new default won't overwrite\n\t\tSettings::Set('panel.allow_theme_change_customer', 1);\n\t\tSettings::Set('panel.allow_theme_change_admin', 1);\n\t\t$fielddata = array(\n\t\t\t'label' => 'panel_default_theme',\n\t\t\t'settinggroup' => 'panel',\n\t\t\t'varname' => 'default_theme'\n\t\t);\n\t\tStore::storeSettingDefaultTheme('panel_default_theme', $fielddata, \"newTheme\");\n\t\t$this->assertTrue($current_theme != Settings::Get('panel.default_theme'));\n\t\t$this->assertEquals(\"newTheme\", Settings::Get('panel.default_theme'));\n\t\t// validate admin/customer field did not change\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\tWHERE `theme` = :newtheme\n\t\t\");\n\t\tDatabase::pexecute($sel_stmt, array('newtheme' => \"newTheme\"));\n\t\t$current_result = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\t// we assume there are entries\n\t\t$this->assertTrue(count($current_result) == 0);\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\tWHERE `theme` = :newtheme\n\t\t\");\n\t\tDatabase::pexecute($sel_stmt, array('newtheme' => \"newTheme\"));\n\t\t$current_result = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\t// we assume there are entries\n\t\t$this->assertTrue(count($current_result) == 0);\n\t\t// now do not allow changing of themes so the theme should get updated for all admins/customers\n\t\t// allow theme changing for admins/customers so a new default won't overwrite\n\t\tSettings::Set('panel.allow_theme_change_customer', 0);\n\t\tSettings::Set('panel.allow_theme_change_admin', 0);\n\t\tStore::storeSettingDefaultTheme('panel_default_theme', $fielddata, \"newTheme\");\n\t\t// validate admin/customer field did change\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_ADMINS . \"`\n\t\t\tWHERE `theme` = :newtheme\n\t\t\");\n\t\tDatabase::pexecute($sel_stmt, array('newtheme' => \"newTheme\"));\n\t\t$current_result = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\t// we assume there are entries\n\t\t$this->assertTrue(count($current_result) > 0);\n\t\t$sel_stmt = Database::prepare(\"\n\t\t\tSELECT * FROM `\" . TABLE_PANEL_CUSTOMERS . \"`\n\t\t\tWHERE `theme` = :newtheme\n\t\t\");\n\t\tDatabase::pexecute($sel_stmt, array('newtheme' => \"newTheme\"));\n\t\t$current_result = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\t// we assume there are entries\n\t\t$this->assertTrue(count($current_result) > 0);\n\t\t// set back to default\n\t\tStore::storeSettingDefaultTheme('panel_default_theme', $fielddata, $current_theme);\n\t}\n}\n"
  },
  {
    "path": "tests/Froxlor/ValidateTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Validate\\Validate;\n\n/**\n *\n * @covers \\Froxlor\\Validate\\Validate\n * @covers \\Froxlor\\UI\\Response\n * @covers \\Froxlor\\FroxlorLogger\n * @covers \\Froxlor\\Idna\\IdnaWrapper\n */\nclass ValidateTest extends TestCase\n{\n\n\tpublic function testValidate()\n\t{\n\t\t$teststr = Validate::validate(\"user input\", \"test-field\", '', '', [], true);\n\t\t$this->assertEquals(\"user input\", $teststr);\n\t}\n\n\tpublic function testValidateStrInEmptyDefault()\n\t{\n\t\t$teststr = Validate::validate(\"user input\", \"test-field\", '', '', [\n\t\t\t\"user test\",\n\t\t\t\"user input\",\n\t\t\t\"user bla\"\n\t\t], true);\n\t\t$this->assertEquals(\"user input\", $teststr);\n\t}\n\n\tpublic function testValidateEmptyDefaultNoArray()\n\t{\n\t\t$teststr = Validate::validate(\"user input\", \"test-field\", '', '', \"user input\", true);\n\t\t$this->assertEquals(\"user input\", $teststr);\n\t}\n\n\tpublic function testValidateRemoveNotAllowedChar()\n\t{\n\t\t$teststr = Validate::validate(\"user \" . PHP_EOL . \"input\", \"test-field\", '', '', [], true);\n\t\t$this->assertEquals(\"user input\", $teststr);\n\t}\n\n\tpublic function testValidateStringFormatError()\n\t{\n\t\t$this->expectException(\"Exception\");\n\t\t$this->expectExceptionCode(400);\n\t\tValidate::validate(\"user input\", \"test-field\", '/^[A-Z]+$/i', '', [], true);\n\t}\n\n\tpublic function testValidateIp()\n\t{\n\t\t$result = Validate::validate_ip2(\"12.34.56.78\", false, 'invalidip', false, false, false, false, true);\n\t\t$this->assertEquals(\"12.34.56.78\", $result);\n\t}\n\n\tpublic function testValidateIpPrivNotAllowed()\n\t{\n\t\t$this->expectException(\"Exception\");\n\t\t$this->expectExceptionCode(400);\n\t\tValidate::validate_ip2(\"10.0.0.1\", false, 'invalidip', false, false, false, false, true);\n\t}\n\n\tpublic function testValidateIpPrivNotAllowedBool()\n\t{\n\t\t$result = Validate::validate_ip2(\"10.0.0.1\", true, 'invalidip', false, false, false, false, true);\n\t\t$this->assertFalse($result);\n\t}\n\n\tpublic function testValidateIpCidrNotAllowed()\n\t{\n\t\t$this->expectException(\"Exception\");\n\t\t$this->expectExceptionCode(400);\n\t\tValidate::validate_ip2(\"12.34.56.78/24\", false, 'invalidip', false, false, false, false, true);\n\t}\n\n\tpublic function testValidateIpCidrNotAllowedBool()\n\t{\n\t\t$result = Validate::validate_ip2(\"12.34.56.78/24\", true, 'invalidip', false, false, false, false, true);\n\t\t$this->assertFalse($result);\n\t}\n\n\tpublic function testValidateIpCidr()\n\t{\n\t\t$result = Validate::validate_ip2(\"12.34.56.78/24\", false, 'invalidip', false, false, true, false, true);\n\t\t$this->assertEquals(\"12.34.56.78/24\", $result);\n\t}\n\n    public function testValidateIpv6Disallowed()\n    {\n        $this->expectException(\"Exception\");\n        $this->expectExceptionCode(400);\n        Validate::validate_ip2(\"2620:0:2d0:200::7/32\", false, 'invalidip', false, false, true, true, true);\n    }\n\n\tpublic function testValidateIpLocalhostAllowed()\n\t{\n\t\t$result = Validate::validate_ip2(\"127.0.0.1/32\", false, 'invalidip', true, false, true, false, true);\n\t\t$this->assertEquals(\"127.0.0.1/32\", $result);\n\t}\n\n    public function testValidateCidrNoationToNetmaskNotationIPv4()\n    {\n        $result = Validate::validate_ip2(\"1.1.1.1/4\", false, 'invalidip', true, false, true, true, true);\n        $this->assertEquals(\"1.1.1.1/240.0.0.0\", $result);\n        $result = Validate::validate_ip2(\"8.8.8.8/18\", false, 'invalidip', true, false, true, true, true);\n        $this->assertEquals(\"8.8.8.8/255.255.192.0\", $result);\n        $result = Validate::validate_ip2(\"8.8.8.8/1\", false, 'invalidip', true, false, true, true, true);\n        $this->assertEquals(\"8.8.8.8/128.0.0.0\", $result);\n    }\n\n\tpublic function testValidateIpLocalhostAllowedWrongIp()\n\t{\n\t\t$this->expectException(\"Exception\");\n\t\t$this->expectExceptionCode(400);\n\t\tValidate::validate_ip2(\"127.0.0.2\", false, 'invalidip', true, false, false, false, true);\n\t}\n\n\tpublic function testValidateUrl()\n\t{\n\t\t$result = Validate::validateUrl(\"https://froxlor.org/\");\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"https://froxlor.org/\", true);\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"http://forum.froxlor.org/\");\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"https://api.froxlor.org/doc/0.10.0/index.php\");\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"https://api.froxlor.org/doc/0.10.0/index.php\", true);\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"#froxlor\");\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateUrl(\"https://82.149.225.211/\");\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"https://82.149.225.211/\", true);\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"https://82.149.225.300\");\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateUrl(\"82.149.225.211:443\");\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"172.16.0.1:8080\");\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateUrl(\"172.16.0.1:8080\", true);\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"https://xn--frxlr-kuac.de/\", true);\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUrl(\"https://2a10:ec2::193:107:51:5/test\");\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateUrl(\"https://[2a10:ec2::193:107:51:5]\");\n\t\t$this->assertTrue($result);\n\t}\n\n\tpublic function testValidateDomain()\n\t{\n\t\t$result = Validate::validateDomain('froxlor.org');\n\t\t$this->assertEquals('froxlor.org', $result);\n\t\t$result = Validate::validateDomain('_dmarc.froxlor.org');\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateDomain('_dmarc.froxlor.org', true);\n\t\t$this->assertEquals('_dmarc.froxlor.org', $result);\n\t\t$result = Validate::validateDomain('test._dmarc.froxlor.org', true);\n\t\t$this->assertEquals('test._dmarc.froxlor.org', $result);\n\t\t$result = Validate::validateDomain('0815');\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateDomain('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz');\n\t\t$this->assertFalse($result);\n\t}\n\n\tpublic function testValidateHostname()\n\t{\n\t\t$result = Validate::validateLocalHostname('localhost');\n\t\t$this->assertEquals('localhost', $result);\n\t\t$result = Validate::validateLocalHostname('froxlor-srv02');\n\t\t$this->assertEquals('froxlor-srv02', $result);\n\t\t$result = Validate::validateLocalHostname('froxlor_org');\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateLocalHostname('froxlor.org');\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateLocalHostname('a--------------------------------------------------------------');\n\t\t$this->assertEquals('a--------------------------------------------------------------', $result);\n\t\t$result = Validate::validateLocalHostname('-hostname');\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateLocalHostname('a-----------------------------------------------------------------');\n\t\t$this->assertFalse($result);\n\t}\n\n\tpublic function testValidateEmail()\n\t{\n\t\t$result = Validate::validateEmail('team@froxlor.org');\n\t\t$this->assertEquals('team@froxlor.org', $result);\n\t\t$result = Validate::validateEmail('team.froxlor.org');\n\t\t$this->assertFalse($result);\n\t}\n\n\tpublic function testValidateUsername()\n\t{\n\t\t$result = Validate::validateUsername('web123sql2');\n\t\t$this->assertTrue($result);\n\t\t$mysql_max = \\Froxlor\\Database\\Database::getSqlUsernameLength() - strlen(\\Froxlor\\Settings::Get('customer.mysqlprefix'));\n\t\t$result = Validate::validateUsername('web123sql2', true, $mysql_max);\n\t\t$this->assertTrue($result);\n\t\t// too long\n\t\t$result = Validate::validateUsername('myperfectsuperduperwebuserwhosnameisenormouslylongandprettyandshouldinnowaybeaccepted123sql2', true, $mysql_max);\n\t\t$this->assertFalse($result);\n\t\t// not unix-conform\n\t\t$result = Validate::validateUsername('web123-sql2', true, $mysql_max);\n\t\t$this->assertFalse($result);\n\t\t// non-unix-conform\n\t\t$result = Validate::validateUsername('web123-sql2', false, $mysql_max);\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateUsername('web123--sql2', false, $mysql_max);\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateUsername('-web123sql2', false, $mysql_max);\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateUsername('web123sql2-', false, $mysql_max);\n\t\t$this->assertFalse($result);\n\t}\n\n\tpublic function testValidateSqlInterval()\n\t{\n\t\t$result = Validate::validateSqlInterval('60 HOUR');\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateSqlInterval('2 MONTH');\n\t\t$this->assertTrue($result);\n\t\t$result = Validate::validateSqlInterval();\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateSqlInterval('2 QUARTER');\n\t\t$this->assertFalse($result);\n\t\t$result = Validate::validateSqlInterval('1DAY');\n\t\t$this->assertFalse($result);\n\t}\n}\n"
  },
  {
    "path": "tests/Ftps/FtpsTest.php",
    "content": "<?php\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Ftps;\nuse Froxlor\\Froxlor;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Admins\n * @covers \\Froxlor\\Api\\Commands\\Customers\n * @covers \\Froxlor\\Api\\Commands\\Ftps\n */\nclass FtpsTest extends TestCase\n{\n\n\tpublic function testAdminFtpsGetId()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Ftps::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t// should be the ftp user of the first added customr 'test1'\n\t\t$this->assertEquals('test1', $result['username']);\n\t}\n\n\tpublic function testResellerFtpsGetId()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = Ftps::getLocal($reseller_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test1', $result['username']);\n\t}\n\n\tpublic function testCustomerFtpsGetId()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$json_result = Ftps::getLocal($customer_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t// should be the ftp user of the first added customr 'test1'\n\t\t$this->assertEquals('test1', $result['username']);\n\t}\n\n\tpublic function testCustomerFtpsGetOtherId()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$this->expectExceptionCode(404);\n\n\t\tFtps::getLocal($customer_userdata, array(\n\t\t\t'id' => 10\n\t\t))->get();\n\t}\n\n\tpublic function testAdminFtpsList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Ftps::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\n\t\t$json_result = Ftps::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testAdminFtpsListSpecificCustomer()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Ftps::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('test1', $result['list'][0]['username']);\n\t}\n\n\tpublic function testResellerFtpsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = Ftps::getLocal($reseller_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('test1', $result['list'][0]['username']);\n\n\t\t$json_result = Ftps::getLocal($reseller_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\t}\n\n\tpublic function testCustomerFtpsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$json_result = Ftps::getLocal($customer_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('test1', $result['list'][0]['username']);\n\n\t\t$json_result = Ftps::getLocal($customer_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\t}\n\n\tpublic function testCustomerFtpsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'ftp_password' => 'h4xXx0r',\n\t\t\t'path' => '/',\n\t\t\t'ftp_description' => 'testing',\n\t\t\t'sendinfomail' => TRAVIS_CI == 1 ? 0 : 1\n\t\t];\n\t\t$json_result = Ftps::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'], $result['homedir']);\n\t}\n\n\tpublic function testCustomerFtpsAddSymlinkOutsideHomedir()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data']; //\n\n\t\t$customer_userdata['documentroot'] = sys_get_temp_dir() . '/';\n\t\t@unlink($customer_userdata['documentroot'] . '/frx');\n\t\tsymlink(Froxlor::getInstallDir(), $customer_userdata['documentroot'] . '/frx');\n\n\t\t$data = [\n\t\t\t'ftp_password' => 'h4xXx0r',\n\t\t\t'path' => '/frx/sub',\n\t\t\t'ftp_description' => 'testing',\n\t\t\t'sendinfomail' => TRAVIS_CI == 1 ? 0 : 1\n\t\t];\n\n\t\t$this->expectExceptionMessage('Found symlink pointing outside of customer home directory: /frx');\n\t\tFtps::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerFtpsAddNoMoreResources()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data']; //\n\n\t\t$customer_userdata['ftps_used'] = 100;\n\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage('No more resources available');\n\t\tFtps::getLocal($customer_userdata)->add();\n\t}\n\n\tpublic function testAdminFtpsAddCustomerRequired()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'ftp_password' => 'h4xXx0r',\n\t\t\t'path' => '/',\n\t\t\t'ftp_description' => 'testing',\n\t\t\t'sendinfomail' => 1\n\t\t];\n\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage('Requested parameter \"loginname\" is empty where it should not be for \"Customers:get\"');\n\t\tFtps::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerFtpsEdit()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'username' => 'test1ftp1',\n\t\t\t'ftp_password' => 'h4xXx0r2',\n\t\t\t'path' => '/subfolder',\n\t\t\t'ftp_description' => 'testing2'\n\t\t];\n\t\t$json_result = Ftps::getLocal($customer_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'subfolder/', $result['homedir']);\n\t\t$this->assertEquals('testing2', $result['description']);\n\t}\n\n\tpublic function testAdminFtpsEdit()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'username' => 'test1ftp1',\n\t\t\t'customerid' => 1,\n\t\t\t'ftp_password' => 'h4xXx0r2',\n\t\t\t'path' => '/anotherfolder',\n\t\t\t'ftp_description' => 'testing3'\n\t\t];\n\t\t$json_result = Ftps::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'anotherfolder/', $result['homedir']);\n\t\t$this->assertEquals('testing3', $result['description']);\n\t}\n\n\tpublic function testAdminFtpsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'customerid' => $customer_userdata['customerid'],\n\t\t\t'ftp_password' => 'h4xXx0r',\n\t\t\t'path' => '/',\n\t\t\t'ftp_description' => 'testing',\n\t\t\t'sendinfomail' => TRAVIS_CI == 1 ? 0 : 1\n\t\t];\n\t\t$json_result = Ftps::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'], $result['homedir']);\n\t}\n\n\tpublic function testCustomerFtpsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'username' => 'test1ftp1'\n\t\t];\n\t\t$json_result = Ftps::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test1ftp1', $result['username']);\n\t}\n\n\tpublic function testAdminFtpsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'username' => 'test1ftp2'\n\t\t];\n\t\t$json_result = Ftps::getLocal($admin_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test1ftp2', $result['username']);\n\t}\n\n\tpublic function testCustomerFtpsDeleteDefaultUser()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'username' => 'test1'\n\t\t];\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage('You cannot delete your main FTP account');\n\t\tFtps::getLocal($customer_userdata, $data)->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/Global/ApiParameterTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Froxlor;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Froxlor\n */\nclass ApiParameterTest extends TestCase\n{\n\n\tpublic function testMissingRequiredParameter()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage('Requested parameter \"key\" could not be found for \"Froxlor:getSetting\"');\n\t\tFroxlor::getLocal($admin_userdata)->getSetting();\n\t}\n}\n"
  },
  {
    "path": "tests/Global/FroxlorRpcTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Api\\FroxlorRPC;\n\n/**\n *\n * @covers \\Froxlor\\Api\\FroxlorRPC\n */\nclass FroxlorRpcTest extends TestCase\n{\n\n\tpublic function testNoCredentialsGiven()\n\t{\n\t\t$this->expectExceptionCode(401);\n\t\t$this->expectExceptionMessage(\"Unauthenticated. Please provide api user credentials.\");\n\t\tFroxlorRPC::validateRequest(\"\");\n\t}\n\n\tpublic function testValidateAuthInvalid()\n\t{\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Invalid authorization credentials\");\n\t\t$_SERVER['PHP_AUTH_USER'] = 'asd';\n\t\t$_SERVER['PHP_AUTH_PW'] = 'asd';\n\t\tFroxlorRPC::validateRequest(\"\");\n\t}\n\n\tpublic function testValidateAuthAllowFromInvalid()\n\t{\n\t\t$_SERVER['REMOTE_ADDR'] = '127.0.0.1';\n\t\tDatabase::query(\"UPDATE `api_keys` SET `allowed_from` = '123.123.123.123';\");\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Invalid authorization credentials\");\n\t\t$_SERVER['PHP_AUTH_USER'] = 'test';\n\t\t$_SERVER['PHP_AUTH_PW'] = 'test';\n\t\tFroxlorRPC::validateRequest(\"\");\n\t}\n\n\tpublic function testEmptyRequestBody()\n\t{\n\t\tDatabase::query(\"UPDATE `api_keys` SET `allowed_from` = '';\");\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage(\"Empty request body.\");\n\t\t$_SERVER['PHP_AUTH_USER'] = 'test';\n\t\t$_SERVER['PHP_AUTH_PW'] = 'test';\n\t\tFroxlorRPC::validateRequest(\"\");\n\t}\n\n\tpublic function testInvalidJSON()\n\t{\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage(\"Invalid JSON Format.\");\n\t\t$_SERVER['PHP_AUTH_USER'] = 'test';\n\t\t$_SERVER['PHP_AUTH_PW'] = 'test';\n\t\tFroxlorRPC::validateRequest('asd');\n\t}\n\n\tpublic function testNoCommandGiven()\n\t{\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage(\"Please provide a command.\");\n\t\t$_SERVER['PHP_AUTH_USER'] = 'test';\n\t\t$_SERVER['PHP_AUTH_PW'] = 'test';\n\t\tFroxlorRPC::validateRequest(json_encode(['cmd' => 'test']));\n\t}\n\n\tpublic function testInvalidCommandGiven()\n\t{\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage(\"The given command is invalid.\");\n\t\t$_SERVER['PHP_AUTH_USER'] = 'test';\n\t\t$_SERVER['PHP_AUTH_PW'] = 'test';\n\t\tFroxlorRPC::validateRequest(json_encode(['command' => 'Froxlor']));\n\t}\n\n\tpublic function testUnknownCommandGiven()\n\t{\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage(\"Unknown command\");\n\t\t$_SERVER['PHP_AUTH_USER'] = 'test';\n\t\t$_SERVER['PHP_AUTH_PW'] = 'test';\n\t\tFroxlorRPC::validateRequest(json_encode(['command' => 'SomeModule.cmd']));\n\t}\n\n\tpublic function testCommandOk()\n\t{\n\t\t$_SERVER['PHP_AUTH_USER'] = 'test';\n\t\t$_SERVER['PHP_AUTH_PW'] = 'test';\n\t\t$result = FroxlorRPC::validateRequest(json_encode(['command' => 'Froxlor.listFunctions']));\n\t\t$this->assertEquals('Froxlor', $result['command']['class']);\n\t\t$this->assertEquals('listFunctions', $result['command']['method']);\n\t\t$this->assertNull($result['params']);\n\t}\n\n\tpublic function testApiPhpEscaping()\n\t{\n\t\t$key = $this->generateKey();\n\t\t$request = array(\n\t\t\t'command' => 'Froxlor.listFunctions',\n\t\t\t'params' => $key\n\t\t);\n\t\t$json_request = json_encode($request);\n\t\t$decoded_request = json_decode($json_request, true);\n\t\t$decoded_request = $this->stripcslashes_deep($decoded_request);\n\t\t$this->assertEquals($key['key'], $decoded_request['params']['key']);\n\t\t$this->assertEquals($key['cert'], $decoded_request['params']['cert']);\n\t}\n\n\tprivate function stripcslashes_deep($value)\n\t{\n\t\treturn is_array($value) ? array_map([$this, 'stripcslashes_deep'], $value) : stripcslashes($value);\n\t}\n\n\tprivate function generateKey()\n\t{\n\t\t$dn = array(\n\t\t\t\"countryName\" => \"DE\",\n\t\t\t\"stateOrProvinceName\" => \"Hessen\",\n\t\t\t\"localityName\" => \"Frankfurt\",\n\t\t\t\"organizationName\" => \"Froxlor\",\n\t\t\t\"organizationalUnitName\" => \"Testing\",\n\t\t\t\"commonName\" => \"test2.local\",\n\t\t\t\"emailAddress\" => \"team@froxlor.org\"\n\t\t);\n\n\t\t// generate key pair\n\t\t$privkey = openssl_pkey_new(array(\n\t\t\t\"private_key_bits\" => 2048,\n\t\t\t\"private_key_type\" => OPENSSL_KEYTYPE_RSA\n\t\t));\n\n\t\t// generate csr\n\t\t$csr = openssl_csr_new($dn, $privkey, array(\n\t\t\t'digest_alg' => 'sha256'\n\t\t));\n\n\t\t// generate self-signed certificate\n\t\t$sscert = openssl_csr_sign($csr, null, $privkey, 365, array(\n\t\t\t'digest_alg' => 'sha256'\n\t\t));\n\n\t\t// export\n\t\topenssl_x509_export($sscert, $certout);\n\t\topenssl_pkey_export($privkey, $pkeyout, null);\n\n\t\treturn array(\n\t\t\t'cert' => $certout,\n\t\t\t'key' => $pkeyout\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/IpsAndPorts/IpsAndPortsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\IpsAndPorts;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\IpsAndPorts\n */\nclass IpsAndPortsTest extends TestCase\n{\n\n\tpublic function testAdminIpsAndPortsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$json_result = IpsAndPorts::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\t\t$this->assertEquals('82.149.225.46', $result['list'][0]['ip']);\n\n\t\t$json_result = IpsAndPorts::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testResellerIpsAndPortsListHasNone()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// update reseller to allow no ip access\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller',\n\t\t\t'ipaddress' => array()\n\t\t))->update();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\t$json_result = IpsAndPorts::getLocal($reseller_userdata)->listing();\n\t}\n\n\tpublic function testAdminIpsAndPortsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'ip' => '82.149.225.47'\n\t\t];\n\t\t$json_result = IpsAndPorts::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(3, $result['id']);\n\t\t$this->assertEquals(80, $result['port']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminIpsAndPortsAdd\n\t */\n\tpublic function testAdminIpsAndPortsAddExists()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionMessage(\"This IP/Port combination already exists.\");\n\t\t$data = [\n\t\t\t'ip' => '82.149.225.47'\n\t\t];\n\t\tIpsAndPorts::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminIpsAndPortsAddIpv6()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'ip' => '2a01:0440:0000:0012:0082:0149:0225:0046',\n\t\t\t'docroot' => '/var/www/html'\n\t\t];\n\t\t$json_result = IpsAndPorts::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('2a01:440:0:12:82:149:225:46', $result['ip']);\n\t\t$this->assertEquals('/var/www/html/', $result['docroot']);\n\t}\n\n\tpublic function testAdminIpsAndPortsGetNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"IP/port with id #999 could not be found\");\n\t\tIpsAndPorts::getLocal($admin_userdata, array(\n\t\t\t'id' => 999\n\t\t))->get();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminIpsAndPortsAdd\n\t */\n\tpublic function testResellerIpsAndPortsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// update reseller to allow ip access to ip id #3\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller',\n\t\t\t'ipaddress' => array(\n\t\t\t\t3\n\t\t\t)\n\t\t))->update();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = IpsAndPorts::getLocal($reseller_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals('82.149.225.47', $result['list'][0]['ip']);\n\n\t\t$json_result = IpsAndPorts::getLocal($reseller_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testResellerIpsAndPortsList\n\t */\n\tpublic function testResellerIpsAndPortsGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// update reseller to allow ip access to ip id #2\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = IpsAndPorts::getLocal($reseller_userdata, array(\n\t\t\t'id' => 3\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('82.149.225.47', $result['ip']);\n\t}\n\n\t/**\n\t *\n\t * @depends testResellerIpsAndPortsList\n\t */\n\tpublic function testResellerIpsAndPortsGetRestrictedNotOwned()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$this->expectExceptionCode(405);\n\t\t$this->expectExceptionMessage(\"You cannot access this resource\");\n\t\tIpsAndPorts::getLocal($reseller_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t}\n\n\tpublic function testResellerIpsAndPortsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\t$data = [\n\t\t\t'ip' => '82.149.225.48'\n\t\t];\n\t\tIpsAndPorts::getLocal($reseller_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerIpsAndPortsGetNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tIpsAndPorts::getLocal($customer_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t}\n\n\tpublic function testAdminIpsAndPortsEdit()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'listen_statement' => 1,\n\t\t\t'docroot' => '/var/www/html'\n\t\t];\n\t\t$json_result = IpsAndPorts::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['listen_statement']);\n\t\t$this->assertEquals('/var/www/html/', $result['docroot']);\n\t}\n\n\tpublic function testResellerIpsAndPortsEditNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'listen_statement' => 0\n\t\t];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tIpsAndPorts::getLocal($reseller_userdata, $data)->update();\n\t}\n\n\tpublic function testCustomerIpsAndPortsEditNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'listen_statement' => 0\n\t\t];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tIpsAndPorts::getLocal($customer_userdata, $data)->update();\n\t}\n\n\tpublic function testAdminIpsAndPortsEditCantChangeSystemIp()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => 1,\n\t\t\t'ip' => '123.123.123.123'\n\t\t];\n\t\t$this->expectExceptionMessage(\"You cannot change the last system IP, either create another new IP/Port combination for the system IP or change the system IP.\");\n\t\tIpsAndPorts::getLocal($admin_userdata, $data)->update();\n\t}\n\n\tpublic function testAdminIpsAndPortsDeleteCantDeleteDefaultIp()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => 1\n\t\t];\n\t\t$this->expectExceptionMessage(\"You cannot delete the default IP/Port combination, please make another IP/Port combination default for before deleting this IP/Port combination.\");\n\t\tIpsAndPorts::getLocal($admin_userdata, $data)->delete();\n\t}\n\n\tpublic function testAdminIpsAndPortsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => 3\n\t\t];\n\t\t$json_result = IpsAndPorts::getLocal($admin_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('82.149.225.47', $result['ip']);\n\t}\n\n\tpublic function testResellerIpsAndPortsDeleteNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$data = [\n\t\t\t'id' => 1\n\t\t];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tIpsAndPorts::getLocal($reseller_userdata, $data)->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/Mysqls/MysqlServerTest.php",
    "content": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\MysqlServer;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n\n * @covers \\Froxlor\\Api\\Commands\\MysqlServer\n */\nclass MysqlServerTest extends TestCase\n{\n\n\tpublic function testAdminMysqlServerAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'mysql_host' => '192.168.1.254',\n\t\t\t'privileged_user' => 'froxroot',\n\t\t\t'privileged_password' => 'p4ssw0rd',\n\t\t\t'description' => 'Second mysql-server',\n\t\t\t'test_connection' => false\n\t\t];\n\t\t$json_result = MysqlServer::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['dbserver']);\n\t}\n\n\tpublic function testAdminMysqlServerAddInvalidHostOrIP()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'mysql_host' => 'abc123+',\n\t\t\t'privileged_user' => 'someone',\n\t\t\t'privileged_password' => 'something'\n\t\t];\n\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage('Invalid mysql server ip/hostname');\n\t\tMysqlServer::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t * @depends testAdminMysqlServerAdd\n\t */\n\tpublic function testAdminMysqlServerDeleteDefault()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'dbserver' => 0\n\t\t];\n\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage('Cannot delete first/default mysql-server');\n\t\tMysqlServer::getLocal($admin_userdata, $data)->delete();\n\t}\n\n\t/**\n\t * @depends testAdminMysqlServerAdd\n\t */\n\tpublic function testAdminMysqlServerDeleteUnknown()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'dbserver' => 1337\n\t\t];\n\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage('Mysql server not found');\n\t\tMysqlServer::getLocal($admin_userdata, $data)->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/Mysqls/MysqlsTest.php",
    "content": "<?php\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Mysqls;\nuse Froxlor\\Api\\Commands\\MysqlServer;\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Settings;\nuse Froxlor\\Settings\\Store;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Mysqls\n * @covers \\Froxlor\\Api\\Commands\\MysqlServer\n * @covers \\Froxlor\\Api\\Commands\\Customers\n * @covers \\Froxlor\\Api\\Commands\\Admins\n * @covers \\Froxlor\\Database\\DbManager\n * @covers \\Froxlor\\Database\\Manager\\DbManagerMySQL\n * @covers \\Froxlor\\Settings\\Store\n */\nclass MysqlsTest extends TestCase\n{\n\n\tpublic function testCustomerMysqlsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$newPwd = \\Froxlor\\System\\Crypt::generatePassword();\n\t\t$data = [\n\t\t\t'mysql_password' => $newPwd,\n\t\t\t'description' => 'testdb',\n\t\t\t'sendinfomail' => TRAVIS_CI == 1 ? 0 : 1\n\t\t];\n\t\t$json_result = Mysqls::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('testdb', $result['description']);\n\t\t$this->assertEquals(0, $result['dbserver']);\n\n\t\t// test connection\n\t\ttry {\n\t\t\t$test_conn = new \\PDO(\"mysql:host=127.0.0.1\", 'test1sql1', $newPwd);\n\t\t\tunset($test_conn);\n\t\t} catch (PDOException $e) {\n\t\t\t$this->fail($e->getMessage());\n\t\t}\n\t}\n\n\tpublic function testCustomerMysqlsDBNameAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t// Set customer.mysqlprefix to DBNAME\n\t\tSettings::Set('customer.mysqlprefix', 'DBNAME');\n\n\t\t$newPwd = \\Froxlor\\System\\Crypt::generatePassword();\n\t\t$data = [\n\t\t\t'mysql_password' => $newPwd,\n\t\t\t'custom_suffix' => 'abc123',\n\t\t\t'description' => 'testdb',\n\t\t\t'sendinfomail' => TRAVIS_CI == 1 ? 0 : 1\n\t\t];\n\t\t$json_result = Mysqls::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test1_abc123', $result['databasename']);\n\t\t$this->assertEquals(0, $result['dbserver']);\n\n\t\t// test connection\n\t\ttry {\n\t\t\t$test_conn = new \\PDO(\"mysql:host=127.0.0.1\", 'test1_abc123', $newPwd);\n\t\t\tunset($test_conn);\n\t\t} catch (PDOException $e) {\n\t\t\t$this->fail($e->getMessage());\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerMysqlsAdd\n\t */\n\tpublic function testAdminMysqlsGet()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Mysqls::getLocal($admin_userdata, array(\n\t\t\t'dbname' => 'test1sql1'\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$this->assertEquals('test1sql1', $result['databasename']);\n\t\t$this->assertEquals('testdb', $result['description']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerMysqlsAdd\n\t */\n\tpublic function testResellerMysqlsGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = Mysqls::getLocal($reseller_userdata, array(\n\t\t\t'dbname' => 'test1sql1'\n\t\t))->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test1sql1', $result['databasename']);\n\t\t$this->assertEquals('testdb', $result['description']);\n\t}\n\n\tpublic function testCustomerMysqlsGetUnknown()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'dbname' => 'test1sql5'\n\t\t];\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"MySQL database with dbname 'test1sql5' could not be found\");\n\t\tMysqls::getLocal($customer_userdata, $data)->get();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerMysqlsAdd\n\t */\n\tpublic function testAdminMysqlsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$newPwd = \\Froxlor\\System\\Crypt::generatePassword();\n\t\t$data = [\n\t\t\t'dbname' => 'test1sql1',\n\t\t\t'mysql_password' => $newPwd,\n\t\t\t'description' => 'testdb-upd',\n\t\t\t'loginname' => 'test1'\n\t\t];\n\t\t$json_result = Mysqls::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('testdb-upd', $result['description']);\n\n\t\t// test connection\n\t\ttry {\n\t\t\t$test_conn = new \\PDO(\"mysql:host=127.0.0.1\", 'test1sql1', $newPwd);\n\t\t\tunset($test_conn);\n\t\t} catch (PDOException $e) {\n\t\t\t$this->fail($e->getMessage());\n\t\t}\n\t}\n\n\n\t/**\n\t *\n\t * @depends testCustomerMysqlsAdd\n\t */\n\tpublic function testAdminMysqlsUpdatePwdOnly()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$newPwd = \\Froxlor\\System\\Crypt::generatePassword();\n\t\t$data = [\n\t\t\t'dbname' => 'test1sql1',\n\t\t\t'mysql_password' => $newPwd,\n\t\t\t'loginname' => 'test1'\n\t\t];\n\t\t$json_result = Mysqls::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('testdb-upd', $result['description']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerMysqlsAdd\n\t */\n\tpublic function testCustomerMysqlsList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$json_result = Mysqls::getLocal($customer_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\t\t$this->assertEquals('test1sql1', $result['list'][0]['databasename']);\n\t\t$this->assertEquals('test1_abc123', $result['list'][1]['databasename']);\n\n\t\t$json_result = Mysqls::getLocal($customer_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\n\t\t$data = [\n\t\t\t'mysql_server' => '0'\n\t\t];\n\t\t$json_result = MysqlServer::getLocal($admin_userdata, $data)->databasesOnServer();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('2', $result['count']);\n\n\t\t$data = [\n\t\t\t'mysql_server' => '1'\n\t\t];\n\t\t$json_result = MysqlServer::getLocal($admin_userdata, $data)->databasesOnServer();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('0', $result['count']);\n\t}\n\n\t/**\n\t * @depends testCustomerMysqlsList\n\t */\n\tpublic function testUpdateCustomerAllowedMysqlWithExistingDbs()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$this->expectExceptionMessage(\"Cannot remove database server from customers allow-list as there are still databases on it.\");\n\t\t// reactivate customer\n\t\t// get customer\n\t\tCustomers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1',\n\t\t\t'allowed_mysqlserver' => [1]\n\t\t))->update();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerMysqlsList\n\t */\n\tpublic function testCustomerMysqlsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'dbname' => 'test1sql1'\n\t\t];\n\t\t$json_result = Mysqls::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test1sql1', $result['databasename']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerMysqlsList\n\t */\n\tpublic function testCustomerMysqlsDBNameDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'dbname' => 'test1_abc123'\n\t\t];\n\t\t$json_result = Mysqls::getLocal($customer_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test1_abc123', $result['databasename']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerMysqlsAdd\n\t */\n\tpublic function testStoreSettingIpAddress()\n\t{\n\t\t// this settings test is here because it directly changes mysql users / privileges\n\t\t$fielddata = array(\n\t\t\t'label' => 'serversettings.ipaddress',\n\t\t\t'settinggroup' => 'system',\n\t\t\t'varname' => 'ipaddress'\n\t\t);\n\t\tStore::storeSettingIpAddress('system_system_ipaddress', $fielddata, '82.149.225.47');\n\n\t\t$mysql_access_hosts = Settings::Get('system.mysql_access_host');\n\t\t$this->assertTrue(strpos($mysql_access_hosts, '82.149.225.47') !== false);\n\t}\n\n\t/**\n\t *\n\t * @depends testStoreSettingIpAddress\n\t */\n\tpublic function testGetAllSqlUsers()\n\t{\n\t\t\\Froxlor\\Database\\Database::needRoot(true);\n\t\t$dbm = new \\Froxlor\\Database\\DbManager(\\Froxlor\\FroxlorLogger::getInstanceOf());\n\t\t$users = $dbm->getManager()->getAllSqlUsers(false);\n\t\tforeach ($users as $user => $data) {\n\t\t\tif (strtolower($user) == 'root' || strtolower($user) == 'mariadb.sys' || strtolower($user) == 'public' || empty(trim($user))) {\n\t\t\t\t// some systems seem to have a user for mariadb on version 10.4\n\t\t\t\t// we do not want to test that one\n\t\t\t\t// as well as a PUBLIC user and an empty user\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tforeach ($data['hosts'] as $host => $hostdata) {\n\t\t\t\t$this->assertNotEmpty($hostdata['password'], \"No password for user '\" . $user . \"'@'\" . $host . \"'\");\n\t\t\t}\n\t\t}\n\n\t\tif (TRAVIS_CI == 0) {\n\t\t\t// just to be sure, not required for travis as the vm is fresh every time\n\t\t\tDatabase::needRoot(true);\n\t\t\tDatabase::query(\"DROP USER IF EXISTS froxlor010@10.0.0.10;\");\n\t\t}\n\n\t\t// grant privileges to another host\n\t\t$testdata = $users['froxlor010'];\n\t\t$password = [\n\t\t\t'password' => $testdata['hosts']['localhost']['password'],\n\t\t\t'plugin' => $testdata['hosts']['localhost']['plugin']\n\t\t];\n\t\t$dbm->getManager()->grantPrivilegesTo('froxlor010', $password, '10.0.0.10', true);\n\n\t\t// select all entries from mysql.user for froxlor010 to compare password-hashes\n\t\t$sel_stmt = Database::prepare(\"SELECT * FROM mysql.user WHERE `User` = :usr\");\n\t\tDatabase::pexecute($sel_stmt, [\n\t\t\t'usr' => 'froxlor010'\n\t\t]);\n\t\t$results = $sel_stmt->fetchAll(\\PDO::FETCH_ASSOC);\n\t\tforeach ($results as $user) {\n\t\t\tif ($user['Host'] == '10.0.0.10') {\n\t\t\t\t// user 'froxlor010'@'10.0.0.10' has been added after we ran\n\t\t\t\t// $dbm->getManager()->getAllSqlUsers(false) so it cannot be part of the array\n\t\t\t\t$this->assertArrayNotHasKey($user['Host'], $testdata['hosts']);\n\t\t\t} else {\n\t\t\t\t$passwd = $user['Password'] ?? $user['authentication_string'];\n\t\t\t\t$this->assertEquals($testdata['hosts'][$user['Host']]['password'], $passwd, \"Wrong authentication_string for user '\" . $user['User'] . \"'@'\" . $user['Host'] . \"'\");\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/PhpAndFpm/FpmDaemonsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\FpmDaemons;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\FpmDaemons\n */\nclass FpmDaemonsTest extends TestCase\n{\n\n\tprivate static $id = 0;\n\n\tpublic function testAdminFpmDaemonsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'description' => 'test2 fpm',\n\t\t\t'reload_cmd' => 'service php8.1-fpm reload',\n\t\t\t'config_dir' => '/etc/php/8.1/fpm/pool.d',\n\t\t\t'limit_extensions' => ''\n\t\t];\n\t\t$json_result = FpmDaemons::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('/etc/php/8.1/fpm/pool.d/', $result['config_dir']);\n\t\t$this->assertEquals('dynamic', $result['pm']);\n\t\t$this->assertEquals(5, $result['max_children']);\n\t\t$this->assertEquals('.php', $result['limit_extensions']);\n\t\tself::$id = $result['id'];\n\t}\n\n\tpublic function testAdminFpmDaemonsAddUnknownPM()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'description' => 'test2 fpm',\n\t\t\t'reload_cmd' => 'service php7.3-fpm reload',\n\t\t\t'config_dir' => '/etc/php/7.3/fpm/pool.d',\n\t\t\t'pm' => 'supermegapm'\n\t\t];\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage(\"Unknown process manager\");\n\t\tFpmDaemons::getLocal($admin_userdata, $data)->add();\n\t}\n\n\tpublic function testAdminFpmDaemonsAddInvalidDesc()\n\t{\n\t\t// max 50. characters\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'description' => str_repeat('test', 30),\n\t\t\t'reload_cmd' => 'service php7.3-fpm reload',\n\t\t\t'config_dir' => '/etc/php/7.3/fpm/pool.d'\n\t\t];\n\t\t$this->expectExceptionMessage(\"The description is too short, too long or contains illegal characters.\");\n\t\tFpmDaemons::getLocal($admin_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminFpmDaemonsAdd\n\t */\n\tpublic function testAdminFpmDaemonsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => self::$id,\n\t\t\t'description' => 'test2 fpm edit',\n\t\t\t'pm' => 'dynamic',\n\t\t\t'max_children' => '10',\n\t\t\t'start_servers' => '4',\n\t\t\t'limit_extensions' => '.php .php.xml'\n\t\t];\n\t\t$json_result = FpmDaemons::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('/etc/php/8.1/fpm/pool.d/', $result['config_dir']);\n\t\t$this->assertEquals(10, $result['max_children']);\n\t\t$this->assertEquals('.php .php.xml', $result['limit_extensions']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminFpmDaemonsUpdate\n\t */\n\tpublic function testAdminFpmDaemonsUpdate2()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => self::$id,\n\t\t\t'limit_extensions' => ''\n\t\t];\n\t\t$json_result = FpmDaemons::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('.php', $result['limit_extensions']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminFpmDaemonsAdd\n\t */\n\tpublic function testAdminFpmDaemonsUpdateUnknownPM()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => self::$id,\n\t\t\t'pm' => 'supermegapm'\n\t\t];\n\t\t$this->expectExceptionCode(406);\n\t\t$this->expectExceptionMessage(\"Unknown process manager\");\n\t\tFpmDaemons::getLocal($admin_userdata, $data)->update();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminFpmDaemonsAdd\n\t */\n\tpublic function testAdminFpmDaemonsUpdateInvalidDesc()\n\t{\n\t\t// max 50. characters\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => self::$id,\n\t\t\t'description' => str_repeat('test', 30)\n\t\t];\n\t\t$this->expectExceptionMessage(\"The description is too short, too long or contains illegal characters.\");\n\t\tFpmDaemons::getLocal($admin_userdata, $data)->update();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminFpmDaemonsUpdate\n\t */\n\tpublic function testAdminFpmDaemonsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$json_result = FpmDaemons::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\t\t$this->assertEquals('System default', $result['list'][0]['description']);\n\t\t$this->assertEquals('test2 fpm edit', $result['list'][1]['description']);\n\n\t\t$json_result = FpmDaemons::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testAdminFpmDaemonsGetNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"fpm-daemon with id #-1 could not be found\");\n\t\tFpmDaemons::getLocal($admin_userdata, array(\n\t\t\t'id' => - 1\n\t\t))->get();\n\t}\n\n\tpublic function testCustomerFpmDaemonsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tFpmDaemons::getLocal($customer_userdata)->add();\n\t}\n\n\tpublic function testCustomerFpmDaemonsGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tFpmDaemons::getLocal($customer_userdata)->get();\n\t}\n\n\tpublic function testCustomerFpmDaemonsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tFpmDaemons::getLocal($customer_userdata)->listing();\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tFpmDaemons::getLocal($customer_userdata)->listingCount();\n\t}\n\n\tpublic function testCustomerFpmDaemonsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tFpmDaemons::getLocal($customer_userdata)->update();\n\t}\n\n\tpublic function testCustomerFpmDaemonsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tFpmDaemons::getLocal($customer_userdata)->delete();\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminFpmDaemonsList\n\t */\n\tpublic function testAdminFpmDaemonsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => self::$id\n\t\t];\n\t\t$json_result = FpmDaemons::getLocal($admin_userdata, $data)->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('/etc/php/8.1/fpm/pool.d/', $result['config_dir']);\n\t\t$this->assertEquals(10, $result['max_children']);\n\t\t$this->assertEquals('.php', $result['limit_extensions']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminFpmDaemonsDelete\n\t */\n\tpublic function testAdminFpmDaemonsDeleteDefaultConfig()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => 1\n\t\t];\n\t\t$this->expectExceptionMessage(\"This PHP-configuration is set as default and cannot be deleted.\");\n\t\tFpmDaemons::getLocal($admin_userdata, $data)->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/PhpAndFpm/PhpSettingsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Settings;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\PhpSettings;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\PhpSettings\n */\nclass PhpSettingsText extends TestCase\n{\n\n\tprivate static $id = 0;\n\n\tpublic function testAdminPhpSettingsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$json_result = PhpSettings::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(\"Default Config\", $result['list'][0]['description']);\n\t\t$this->assertEquals(\"/usr/bin/php-cgi\", $result['list'][0]['binary']);\n\n\t\t$json_result = PhpSettings::getLocal($admin_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result);\n\t}\n\n\tpublic function testCustomerPhpSettingsListNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tPhpSettings::getLocal($customer_userdata)->listing();\n\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tPhpSettings::getLocal($customer_userdata)->listingCount();\n\t}\n\n\tpublic function testAdminPhpSettingsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'description' => 'test php',\n\t\t\t'phpsettings' => 'error_reporting=E_ALL',\n\t\t\t'fpmconfig' => Settings::Get('phpfpm.defaultini')\n\t\t];\n\t\t$json_result = PhpSettings::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('error_reporting=E_ALL', $result['phpsettings']);\n\t\t$this->assertEquals('60s', $result['fpm_reqterm']);\n\t\tself::$id = $result['id'];\n\t}\n\n\tpublic function testAdminPhpSettingsGet()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'id' => self::$id\n\t\t];\n\t\t$json_result = PhpSettings::getLocal($admin_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('error_reporting=E_ALL', $result['phpsettings']);\n\t\t$this->assertEquals('60s', $result['fpm_reqterm']);\n\t}\n\n\tpublic function testAdminPhpSettingsGetNotFound()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$this->expectExceptionCode(404);\n\t\t$this->expectExceptionMessage(\"php-config with id #999 could not be found\");\n\t\tPhpSettings::getLocal($admin_userdata, array(\n\t\t\t'id' => 999\n\t\t))->get();\n\t}\n\n\tpublic function testCustomerPhpSettingsGetNotAllowed()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$this->expectExceptionCode(403);\n\t\t$this->expectExceptionMessage(\"Not allowed to execute given command.\");\n\t\tPhpSettings::getLocal($customer_userdata, array(\n\t\t\t'id' => 1\n\t\t))->get();\n\t}\n\n\t/**\n\t * @depends testAdminPhpSettingsAdd\n\t */\n\tpublic function testAdminPhpSettingsAddForAll()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$data = [\n\t\t\t'description' => 'test php #2',\n\t\t\t'phpsettings' => 'error_reporting=E_ALL',\n\t\t\t'fpmconfig' => Settings::Get('phpfpm.defaultini'),\n\t\t\t'allow_all_customers' => true\n\t\t];\n\t\t$json_result = PhpSettings::getLocal($admin_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$required_id = $result['id'];\n\n\t\t$json_result = Customers::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\n\t\t$allowed_cnt = 0;\n\t\tforeach ($result['list'] as $customer) {\n\t\t\t$cust_phpconfigsallowed = json_decode($customer['allowed_phpconfigs'], true);\n\t\t\tif (!in_array($required_id, $cust_phpconfigsallowed)) {\n\t\t\t\t$this->fail(\"Customer does not have php-config assigned which was added for all customers\");\n\t\t\t}\n\t\t\t$allowed_cnt++;\n\t\t}\n\t\t$this->assertTrue($allowed_cnt == $result['count']);\n\t}\n}\n"
  },
  {
    "path": "tests/SubDomains/SubDomainsTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Api\\Commands\\Admins;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\SubDomains;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\SubDomains\n * @covers \\Froxlor\\Api\\Commands\\Domains\n * @covers \\Froxlor\\Api\\Commands\\Customers\n * @covers \\Froxlor\\Api\\Commands\\Admins\n */\nclass SubDomainsTest extends TestCase\n{\n\n\tpublic function testCustomerSubDomainsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'subdomain' => 'mySub',\n\t\t\t'domain' => 'test2.local'\n\t\t];\n\t\t$json_result = SubDomains::getLocal($customer_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('mysub.test2.local', $result['domain']);\n\t}\n\n\tpublic function testResellerSubDomainsAdd()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\n\t\t$data = [\n\t\t\t'subdomain' => 'mySub2',\n\t\t\t'domain' => 'test2.local',\n\t\t\t'customerid' => 1\n\t\t];\n\t\t$json_result = SubDomains::getLocal($reseller_userdata, $data)->add();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('mysub2.test2.local', $result['domain']);\n\t}\n\n\tpublic function testCustomerSubDomainsAddNoPunycode()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'subdomain' => 'xn--asd',\n\t\t\t'domain' => 'unknown.froxlor.org'\n\t\t];\n\t\t$this->expectExceptionMessage('You must not specify punycode (IDNA). The domain will automatically be converted');\n\t\tSubDomains::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerSubDomainsAddMainDomainUnknown()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'subdomain' => 'wohoo',\n\t\t\t'domain' => 'unknown.froxlor.org'\n\t\t];\n\t\t$this->expectExceptionMessage('The main-domain unknown.froxlor.org does not exist.');\n\t\tSubDomains::getLocal($customer_userdata, $data)->add();\n\t}\n\n\tpublic function testCustomerSubDomainsAddInvalidDomain()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\n\t\t$data = [\n\t\t\t'subdomain' => '#+?',\n\t\t\t'domain' => 'unknown.froxlor.org'\n\t\t];\n\t\t$this->expectExceptionMessage(\"Wrong Input in Field 'Domain'\");\n\t\tSubDomains::getLocal($customer_userdata, $data)->add();\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerSubDomainsAdd\n\t */\n\tpublic function testAdminSubDomainsGet()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'mysub.test2.local'\n\t\t];\n\t\t$json_result = SubDomains::getLocal($admin_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('mysub.test2.local', $result['domain']);\n\t\t$this->assertEquals(1, $result['customerid']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerSubDomainsAdd\n\t */\n\tpublic function testAdminSubDomainsGetMainDomain()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$data = [\n\t\t\t'domainname' => 'test2.local'\n\t\t];\n\t\t$json_result = SubDomains::getLocal($admin_userdata, $data)->get();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('test2.local', $result['domain']);\n\t\t$this->assertEquals(1, $result['customerid']);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerSubDomainsAdd\n\t */\n\tpublic function testAdminSubDomainsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'domainname' => 'mysub.test2.local',\n\t\t\t'path' => 'mysub.test2.local',\n\t\t\t'isemaildomain' => 1,\n\t\t\t'customerid' => $customer_userdata['customerid']\n\t\t];\n\t\t$json_result = SubDomains::getLocal($admin_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals($customer_userdata['documentroot'] . 'mysub.test2.local/', $result['documentroot']);\n\t}\n\n\t/**\n\t *\n\t * @depends testAdminSubDomainsUpdate\n\t */\n\tpublic function testCustomerSubDomainsUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'domainname' => 'mysub.test2.local',\n\t\t\t'url' => 'https://www.froxlor.org/',\n\t\t\t'isemaildomain' => 0\n\t\t];\n\t\t$json_result = SubDomains::getLocal($customer_userdata, $data)->update();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('https://www.froxlor.org/', $result['documentroot']);\n\t}\n\n\tpublic function testCustomerSubDomainsList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$json_result = SubDomains::getLocal($customer_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(3, $result['count']);\n\n\t\t$json_result = SubDomains::getLocal($customer_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(3, $result);\n\t}\n\n\tpublic function testResellerSubDomainsList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get reseller\n\t\t$json_result = Admins::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'reseller'\n\t\t))->get();\n\t\t$reseller_userdata = json_decode($json_result, true)['data'];\n\t\t$reseller_userdata['adminsession'] = 1;\n\t\t$json_result = SubDomains::getLocal($reseller_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(3, $result['count']);\n\n\t\t$json_result = SubDomains::getLocal($reseller_userdata)->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(3, $result);\n\t}\n\n\tpublic function testAdminSubDomainsListWithCustomer()\n\t{\n\t\tglobal $admin_userdata;\n\t\t$json_result = SubDomains::getLocal($admin_userdata, [\n\t\t\t'loginname' => 'test1'\n\t\t])->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(3, $result['count']);\n\n\t\t$json_result = SubDomains::getLocal($admin_userdata, [\n\t\t\t'loginname' => 'test1'\n\t\t])->listingCount();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(3, $result);\n\t}\n\n\t/**\n\t *\n\t * @depends testCustomerSubDomainsList\n\t */\n\tpublic function testCustomerSubDomainsDelete()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$json_result = SubDomains::getLocal($customer_userdata, [\n\t\t\t'domainname' => 'mysub.test2.local'\n\t\t])->delete();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals('mysub.test2.local', $result['domain']);\n\t\t$this->assertEquals($customer_userdata['customerid'], $result['customerid']);\n\t}\n\n\tpublic function testCustomerSubDomainsAddDnsLetsEncryptFail()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t\\Froxlor\\Settings::Set('system.le_domain_dnscheck', 1);\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$data = [\n\t\t\t'subdomain' => 'nodns',\n\t\t\t'domain' => 'test2.local',\n\t\t\t'letsencrypt' => 1\n\t\t];\n\n\t\t$this->expectExceptionCode(400);\n\t\t$this->expectExceptionMessage('The domains DNS does not include any of the chosen IP addresses. Let\\'s Encrypt certificate generation not possible.');\n\t\tSubDomains::getLocal($customer_userdata, $data)->add();\n\t}\n}\n"
  },
  {
    "path": "tests/Traffic/TrafficTest.php",
    "content": "<?php\nuse PHPUnit\\Framework\\TestCase;\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Api\\Commands\\Customers;\nuse Froxlor\\Api\\Commands\\Traffic;\n\n/**\n *\n * @covers \\Froxlor\\Api\\ApiCommand\n * @covers \\Froxlor\\Api\\ApiParameter\n * @covers \\Froxlor\\Api\\Commands\\Traffic\n * @covers \\Froxlor\\Api\\Commands\\Customers\n * @covers \\Froxlor\\Api\\Commands\\Admins\n */\nclass TrafficTest extends TestCase\n{\n\n\tpublic static function setUpBeforeClass(): void\n\t{\n\t\t$ins_stmt = Database::prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_TRAFFIC . \"` SET\n\t\t\t`customerid` = :cid,\n\t\t\t`year` = :y, `month` = :m, `day` = :d,\n\t\t\t`stamp` = :ts,\n\t\t\t`http` = :http, `ftp_up` = :fup, `ftp_down` = :fdown, `mail` = :mail\n\t\t\");\n\n\t\t$ins_adm_stmt = Database::prepare(\"\n\t\t\tINSERT INTO `\" . TABLE_PANEL_TRAFFIC_ADMINS . \"` SET\n\t\t\t`adminid` = :aid,\n\t\t\t`year` = :y, `month` = :m, `day` = :d,\n\t\t\t`stamp` = :ts,\n\t\t\t`http` = :http, `ftp_up` = :fup, `ftp_down` = :fdown, `mail` = :mail\n\t\t\");\n\n\t\t$http = 5 * 1024 * 1024 * 1024; // 5 GB\n\t\t$fup = 50 * 1024 * 1024; // 50 MB\n\t\t$fdown = 2 * 1024 * 1024 * 1024; // 2 GB\n\t\t$mail = 250 * 1024 * 1024; // 250 MB\n\n\t\tforeach (array(\n\t\t\t1,\n\t\t\t2,\n\t\t\t3\n\t\t) as $cid) {\n\t\t\tDatabase::pexecute($ins_stmt, array(\n\t\t\t\t'cid' => $cid,\n\t\t\t\t'y' => date('Y'),\n\t\t\t\t'm' => date('m'),\n\t\t\t\t'd' => date('d'),\n\t\t\t\t'ts' => time(),\n\t\t\t\t'http' => $http,\n\t\t\t\t'fup' => $fup,\n\t\t\t\t'fdown' => $fdown,\n\t\t\t\t'mail' => $mail\n\t\t\t));\n\t\t}\n\n\t\tDatabase::pexecute($ins_adm_stmt, array(\n\t\t\t'aid' => 1,\n\t\t\t'y' => date('Y'),\n\t\t\t'm' => date('m'),\n\t\t\t'd' => date('d'),\n\t\t\t'ts' => time(),\n\t\t\t'http' => $http * 2,\n\t\t\t'fup' => $fup * 2,\n\t\t\t'fdown' => $fdown * 2,\n\t\t\t'mail' => $mail * 2\n\t\t));\n\t}\n\n\tpublic function testAdminTrafficList()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Traffic::getLocal($admin_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$http = 2 * (5 * 1024 * 1024 * 1024 * 1024); // 2x 5 GB\n\t\t$this->assertEquals($http, $result['list'][0]['http']);\n\n\t\t$this->expectExceptionCode(303);\n\t\t$this->expectExceptionMessage(\"You cannot count the traffic data list\");\n\t\tTraffic::getLocal($admin_userdata)->listingCount();\n\t}\n\n\tpublic function testAdminTrafficListSpecificDate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Traffic::getLocal($admin_userdata, array(\n\t\t\t'year' => date('Y') + 1,\n\t\t\t'month' => date('m'),\n\t\t\t'day' => date('d')\n\t\t))->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(0, $result['count']);\n\t}\n\n\tpublic function testAdminTrafficListCustomers()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Traffic::getLocal($admin_userdata, array(\n\t\t\t'customer_traffic' => 1\n\t\t))->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(2, $result['count']);\n\t\t$this->assertEquals(1, $result['list'][0]['customerid']);\n\t\t$this->assertEquals(3, $result['list'][1]['customerid']);\n\t}\n\n\tpublic function testAdminTrafficListCustomersFilterCustomer()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$json_result = Traffic::getLocal($admin_userdata, array(\n\t\t\t'customer_traffic' => 1,\n\t\t\t'loginname' => 'test1'\n\t\t))->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$this->assertEquals(1, $result['list'][0]['customerid']);\n\t}\n\n\tpublic function testCustomerTrafficList()\n\t{\n\t\tglobal $admin_userdata;\n\t\t// get customer\n\t\t$json_result = Customers::getLocal($admin_userdata, array(\n\t\t\t'loginname' => 'test1'\n\t\t))->get();\n\t\t$customer_userdata = json_decode($json_result, true)['data'];\n\t\t$json_result = Traffic::getLocal($customer_userdata)->listing();\n\t\t$result = json_decode($json_result, true)['data'];\n\t\t$this->assertEquals(1, $result['count']);\n\t\t$mail = 250 * 1024 * 1024 * 1024; // 250 MB\n\t\t$this->assertEquals($mail, $result['list'][0]['mail']);\n\n\t\t$this->expectExceptionCode(303);\n\t\t$this->expectExceptionMessage(\"You cannot count the traffic data list\");\n\t\tTraffic::getLocal($admin_userdata)->listingCount();\n\t}\n\n\tpublic function testAdminTrafficAdd()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$this->expectExceptionCode(303);\n\t\t$this->expectExceptionMessage(\"You cannot add traffic data\");\n\t\tTraffic::getLocal($admin_userdata)->add();\n\t}\n\n\tpublic function testAdminTrafficGet()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$this->expectExceptionCode(303);\n\t\t$this->expectExceptionMessage(\"To get specific traffic details use year, month and/or day parameter for Traffic.listing()\");\n\t\tTraffic::getLocal($admin_userdata)->get();\n\t}\n\n\tpublic function testAdminTrafficUpdate()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$this->expectExceptionCode(303);\n\t\t$this->expectExceptionMessage(\"You cannot update traffic data\");\n\t\tTraffic::getLocal($admin_userdata)->update();\n\t}\n\n\tpublic function testAdminTrafficDelete()\n\t{\n\t\tglobal $admin_userdata;\n\n\t\t$this->expectExceptionCode(303);\n\t\t$this->expectExceptionMessage(\"You cannot delete traffic data\");\n\t\tTraffic::getLocal($admin_userdata)->delete();\n\t}\n}\n"
  },
  {
    "path": "tests/bootstrap.php",
    "content": "<?php\n\ndefine('DEV_FROXLOR', 1);\n\nif (file_exists('/etc/froxlor-test.pwd') && file_exists('/etc/froxlor-test.rpwd')) {\n\t// froxlor jenkins test-system\n\t$pwd = trim(file_get_contents('/etc/froxlor-test.pwd'));\n\t$rpwd = trim(file_get_contents('/etc/froxlor-test.rpwd'));\n\tdefine('TRAVIS_CI', 0);\n} else {\n\t// github actions\n\t$pwd = 'fr0xl0r.TravisCI';\n\t$rpwd = 'fr0xl0r.TravisCI';\n\tdefine('TRAVIS_CI', 1);\n}\n\nif (@php_sapi_name() !== 'cli') {\n\t// not to be called via browser\n\tdie;\n}\n\n$userdata_content = \"<?php\n\\$sql['user'] = 'froxlor010';\n\\$sql['password'] = '$pwd';\n\\$sql['host'] = '127.0.0.1';\n\\$sql['db'] = 'froxlor010';\n\\$sql_root[0]['user'] = 'root';\n\\$sql_root[0]['password'] = '$rpwd';\n\\$sql_root[0]['host'] = '127.0.0.1';\n\\$sql_root[0]['caption'] = 'Test default';\n\\$sql['debug'] = true;\" . PHP_EOL;\n\n$userdata = dirname(__DIR__) . '/lib/userdata.inc.php';\n\nif (file_exists($userdata)) {\n\trename($userdata, $userdata . \".bak\");\n}\nfile_put_contents($userdata, $userdata_content);\n\n// include autoloader / api / etc\nrequire dirname(__DIR__) . '/vendor/autoload.php';\n// include functions\nrequire dirname(__DIR__) . '/lib/functions.php';\n// include table definitions\nrequire dirname(__DIR__) . '/lib/tables.inc.php';\n\nuse Froxlor\\Database\\Database;\nuse Froxlor\\Settings;\n\nif (TRAVIS_CI == 0) {\n\tDatabase::needRoot(true);\n\tDatabase::query(\"DROP DATABASE IF EXISTS `froxlor010`;\");\n\tDatabase::query(\"CREATE DATABASE `froxlor010`;\");\n\t$sql = include(dirname(__DIR__) . \"/install/froxlor.sql.php\");\n\tfile_put_contents(\"/tmp/froxlor.sql\", $sql);\n\texec(\"mysql -u root -p\" . $rpwd . \" froxlor010 < /tmp/froxlor.sql\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1sql1'@'localhost';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1sql1'@'127.0.0.1';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1sql1'@'172.17.0.1';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1sql1'@'82.149.225.46';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1sql1'@'2a01:440:1:12:82:149:225:46';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1_abc123'@'localhost';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1_abc123'@'127.0.0.1';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1_abc123'@'172.17.0.1';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1_abc123'@'82.149.225.46';\");\n\tDatabase::query(\"DROP USER IF EXISTS 'test1_abc123'@'2a01:440:1:12:82:149:225:46';\");\n\tDatabase::query(\"DROP DATABASE IF EXISTS `test1sql1`;\");\n\tDatabase::query(\"DROP DATABASE IF EXISTS `test1_abc123`;\");\n\tDatabase::needRoot(false);\n}\n\n// clear all tables\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_CUSTOMERS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_DOMAINS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_DOMAINTOIP . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_DOMAIN_DNS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_FTP_USERS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_FTP_GROUPS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_FTP_QUOTATALLIES . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_MAIL_VIRTUAL . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_MAIL_USERS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_DISKSPACE . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_TRAFFIC . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_TRAFFIC_ADMINS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_TASKS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_LOG . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_HTPASSWDS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_HTACCESS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_DOMAINREDIRECTS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_ADMINS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_IPSANDPORTS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_API_KEYS . \"`;\");\nDatabase::query(\"TRUNCATE TABLE `\" . TABLE_PANEL_DATABASES . \"`;\");\nDatabase::query(\"ALTER TABLE `\" . TABLE_PANEL_FPMDAEMONS . \"` AUTO_INCREMENT=2;\");\n\n// add superadmin\nDatabase::query(\"INSERT INTO `\" . TABLE_PANEL_ADMINS . \"` SET\n\t`loginname` = 'admin',\n\t`password` = '\" . \\Froxlor\\System\\Crypt::makeCryptPassword('admin') . \"',\n\t`name` = 'Froxlor-Administrator',\n\t`email` = 'admin@dev.froxlor.org',\n\t`def_language` = 'English',\n\t`customers` = -1,\n\t`customers_see_all` = 1,\n\t`caneditphpsettings` = 1,\n\t`domains` = -1,\n\t`change_serversettings` = 1,\n\t`diskspace` = -1024,\n\t`mysqls` = -1,\n\t`emails` = -1,\n\t`email_accounts` = -1,\n\t`email_forwarders` = -1,\n\t`email_quota` = -1,\n\t`ftps` = -1,\n\t`subdomains` = -1,\n\t`traffic` = -1048576,\n\t`ip` = -1\n\");\n$adminid = Database::lastInsertId();\n\n// add api-key\nDatabase::query(\"INSERT INTO `\" . TABLE_API_KEYS . \"` SET\n\t`adminid` = '1',\n\t`customerid` = '0',\n\t`apikey` = 'test',\n\t`secret` = 'test',\n\t`valid_until` = -1,\n\t`allowed_from` = ''\n\");\n\n// add first ip (system default)\nDatabase::query(\"INSERT INTO `\" . TABLE_PANEL_IPSANDPORTS . \"` SET\n\t`ip` = '82.149.225.46',\n\t`port` = '80',\n\t`listen_statement` = '0',\n\t`namevirtualhost_statement` = '0',\n\t`vhostcontainer` = '1',\n\t`vhostcontainer_servername_statement` = '1',\n\t`specialsettings` = '',\n\t`ssl` = '0'\n\");\n$defaultip = Database::lastInsertId();\nSettings::Set('system.defaultip', $defaultip, true);\n\n// add ssl ip (system default)\nDatabase::query(\"INSERT INTO `\" . TABLE_PANEL_IPSANDPORTS . \"` SET\n\t`ip` = '82.149.225.56',\n\t`port` = '443',\n\t`listen_statement` = '0',\n\t`namevirtualhost_statement` = '0',\n\t`vhostcontainer` = '1',\n\t`vhostcontainer_servername_statement` = '1',\n\t`specialsettings` = '',\n\t`ssl` = '1'\n\");\n$defaultip = Database::lastInsertId();\nSettings::Set('system.defaultsslip', $defaultip, true);\n\n// get userdata of admin 'admin'\n$sel_stmt = Database::prepare(\"SELECT * FROM `\" . TABLE_PANEL_ADMINS . \"` WHERE `adminid` = '1'\");\n$admin_userdata = Database::pexecute_first($sel_stmt);\n$admin_userdata['adminsession'] = 1;\n\n$log = \\Froxlor\\FroxlorLogger::getInstanceOf($admin_userdata);\n\nSettings::Set('panel.standardlanguage', 'English', true);\nSettings::Set('panel.adminmail', 'admin@dev.froxlor.org', true);\nSettings::Set('panel.allow_domain_change_admin', '1', true);\nSettings::Set('panel.allow_domain_change_customer', '1', true);\nSettings::Set('system.lastguid', '10000', true);\nSettings::Set('system.ipaddress', '82.149.225.46', true);\nSettings::Set('system.documentroot_use_default_value', '1', true);\nSettings::Set('system.hostname', 'dev.froxlor.org', true);\nSettings::Set('system.nameservers', 'dev.froxlor.org', true);\nSettings::Set('system.mysql_access_host', 'localhost,127.0.0.1,::1,172.17.0.1,2a01:440:1:12:82:149:225:46,82.149.225.46', true);\nSettings::Set('system.use_ssl', '1', true);\nSettings::Set('system.froxlordirectlyviahostname', '1', true);\nSettings::Set('system.dns_createhostnameentry', '1', true);\nSettings::Set('system.bind_enable', '1', true);\nSettings::Set('system.dnsenabled', '1', true);\nSettings::Set('system.dns_server', 'PowerDNS', true);\nSettings::Set('phpfpm.enabled', '1', true);\nSettings::Set('phpfpm.enabled_ownvhost', '1', true);\nSettings::Set('system.update_channel', 'testing', true);\n"
  },
  {
    "path": "vite.config.js",
    "content": "import { defineConfig } from 'vite';\nimport laravel from 'laravel-vite-plugin';\nimport vue from '@vitejs/plugin-vue';\n\nexport default defineConfig({\n\tbuild: {\n\t\tchunkSizeWarningLimit: 1000,\n\t},\n\tbase: \"./\",\n\tplugins: [\n\t\tlaravel({\n\t\t\tinput: [\n\t\t\t\t'templates/Froxlor/assets/scss/app.scss',\n\t\t\t\t'templates/Froxlor/assets/js/app.js',\n\t\t\t],\n\t\t\thotFile: 'templates/Froxlor/hot',\n\t\t\tbuildDirectory: '../templates/Froxlor/build',\n\t\t\trefresh: true,\n\t\t}),\n\t\tvue({\n\t\t\ttemplate: {\n\t\t\t\ttransformAssetUrls: {\n\t\t\t\t\tbase: null,\n\t\t\t\t\tincludeAbsolute: false,\n\t\t\t\t},\n\t\t\t},\n\t\t}),\n\t],\n\tresolve: {\n\t\talias: {\n\t\t\tvue: 'vue/dist/vue.esm-bundler.js',\n\t\t},\n\t},\n});\n"
  }
]