diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..ffa292ae --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contributing + +Before you start working on a PR, contact us via IRC in #froxlor on Freenode or +the forum at https://forum.froxlor.org to get a clue whether someone else isn't +already working on it or if we don't want to invest the effort in favour of +working on Froxlor 2.0. +Of course, bug fixes are always welcome. +However, at this stage of the 0.9.x branch, we are not looking for new +features or refactoring, especially not the kind which requires changes to a +lot of files. +Currently, we are working on a complete re-write, which, at this point in +time, is not yet public to keep delays due to discussions about internal +details to a minimum. + + + + +## Checklist + +General rules for PRs are: +* Please save us all some trouble and unnecessary round-trips by _testing_ your +changes. + +* Re-write your commit history to provide a CLEAN history! + + * 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... + + +Thanks! + + + + +### Webserver changes +If you make changes to the functionality of webserver configuration, please +make sure your implementation covers both apache **and** nginx. + + + + +### l10n + +If you add new language strings, please make sure you add the english fallback +strings in + +* `lng/english.lng.php` +* `install/lng/english.lng.php` (if applicable) + + + + +### New settings +If you add new settings, please make sure you add the default values to + +* `install/froxlor.sql` +* handle the update (see `install/updates/froxlor/0.9/update_0.9.inc.php`) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..ff27e343 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,64 @@ +# Bug report vs. support request +If you're unsure of whether your problem is a bug or a configuration error +* contact us via IRC in #froxlor on freenode +* or post a thread in our forum at https://forum.froxlor.org + +As a rule of thumb: before reporting an issue +* 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 +* try with the git master + + + + + +# Summary + +Please provide a concise summary of the problem you're experiencing... + + + + +# System information +* Froxlor version: $version/$gitSHA1 +* Web server: apache2/nginx/lighttpd +* DNS server: Bind/PowerDNS (standalone)/PowerDNS (Bind-backend) +* POP/IMAP server: Courier/Dovecot +* SMTP server: postfix/exim +* FTP server: proftpd/pureftpd +* OS/Version: ... + + + + +# Steps to reproduce + +1. +2. +3. + + + + +# Expected behavior + +1. +2. +3. + + + + +# Actual behavior + +1. +2. +3. + + + + +# Log files/log entries +syslog: +
+example +diff --git a/README.md b/README.md index 67c8c554..33cb17cb 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,13 @@ Developed by experienced server administrators, this panel simplifies the effort 3. Point your browser to http://[ip-of-webserver]/froxlor 4. Follow the installer 5. Login as administrator -6. Adjust "Server > Settings" according to your needs -7. Choose your distribution under "Server > Configuration" +6. Adjust "System > Settings" according to your needs +7. Choose your distribution under "System > Configuration" 8. Follow the steps for your services 9. Have fun! ### Detailed installation -http://redmine.froxlor.org/projects/froxlor/wiki/Installationtarball +https://github.com/Froxlor/Froxlor/wiki/Install-froxlor-from-tarball ## Help @@ -30,12 +30,12 @@ irc://chat.freenode.net/froxlor ### Forum -The community is located on http://forum.froxlor.org +The community is located on https://forum.froxlor.org/ ### Wiki More documentation may be found in the froxlor - wiki: -http://redmine.froxlor.org/projects/froxlor/wiki +https://github.com/Froxlor/Froxlor/wiki ## License @@ -44,31 +44,21 @@ May be found in COPYING ## Downloads ### Tarball -http://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](http://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](http://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1) +https://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) ### Debian repository -[HowTo](http://redmine.froxlor.org/projects/froxlor/wiki/Installationdebian) +[HowTo](https://github.com/Froxlor/Froxlor/wiki/Install-froxlor-on-debian) /etc/apt/sources.list.d/froxlor.list > deb http://debian.froxlor.org {wheezy|jessie} main ### Gentoo repository -[HowTo](http://redmine.froxlor.org/projects/froxlor/wiki/Installationgentoo) +[HowTo](https://github.com/Froxlor/Froxlor/wiki/Install-froxlor-on-gentoo) -http://files.froxlor.org/gentoo/repositories.xml +https://files.froxlor.org/gentoo/repositories.xml -## Let's Encrypt support - -This version of Froxlor contains a test implementation of support for [Let's Encrypt](https://letsencrypt.org). This is (as Let's Encrypt is in itself) -still a beta version and may break your system. The way it currently works is by creating a (sub-)domain with the default system - certificate, -after which the Let's Encrypt cronjob orders the certificate for this (sub-)domain and inserts the certificates in the database. With the next run -of the default cronjob, the certificates will be updated on the disk and the webserver reloaded. - -This has 2 known side-effects at the moment: -* The basic ip/port combinations don't work with the Froxlor - integration of Let's Encrypt, since it needs a certificate for the very first creation -* After creating a domain, it will have the default certificate for a short time (by default 5 minutes until the cronjob runs the next time) - -It may be possible to fix these issues, but they are not a priority at the moment +## Contributing +[see here](.github/CONTRIBUTING.md) diff --git a/actions/admin/settings/120.system.php b/actions/admin/settings/120.system.php index b08ce88e..e1b4c5cd 100644 --- a/actions/admin/settings/120.system.php +++ b/actions/admin/settings/120.system.php @@ -94,6 +94,14 @@ return array( 'plausibility_check_method' => 'checkMysqlAccessHost', 'save_method' => 'storeSettingMysqlAccessHost', ), + 'system_nssextrausers' => array( + 'label' => $lng['serversettings']['nssextrausers'], + 'settinggroup' => 'system', + 'varname' => 'nssextrausers', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), 'system_index_file_extension' => array( 'label' => $lng['serversettings']['index_file_extension'], 'settinggroup' => 'system', diff --git a/actions/admin/settings/122.froxlorvhost.php b/actions/admin/settings/122.froxlorvhost.php index 6175bb73..5ddbfc30 100644 --- a/actions/admin/settings/122.froxlorvhost.php +++ b/actions/admin/settings/122.froxlorvhost.php @@ -80,6 +80,19 @@ return array( 'save_method' => 'storeSettingField', 'visible' => Settings::Get('system.use_ssl') ), + 'system_http2_support' => array( + 'label' => $lng['serversettings']['http2_support'], + 'settinggroup' => 'system', + 'varname' => 'http2_support', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2', + 'nginx' + ), + 'visible' => Settings::Get('system.use_ssl') + ), /** * FCGID */ diff --git a/actions/admin/settings/130.webserver.php b/actions/admin/settings/130.webserver.php index 8a117d5c..4374b148 100644 --- a/actions/admin/settings/130.webserver.php +++ b/actions/admin/settings/130.webserver.php @@ -179,17 +179,6 @@ return array( 'nginx' ) ), - 'system_nginx_http2_support' => array( - 'label' => $lng['serversettings']['nginx_http2_support'], - 'settinggroup' => 'system', - 'varname' => 'nginx_http2_support', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField', - 'websrv_avail' => array( - 'nginx' - ) - ), 'system_nginx_php_backend' => array( 'label' => $lng['serversettings']['nginx_php_backend'], 'settinggroup' => 'system', diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 56c27cd5..7bdeb3e4 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -30,6 +30,20 @@ return array( 'save_method' => 'storeSettingField', 'overview_option' => true ), + 'system_ssl_protocols' => array( + 'label' => $lng['serversettings']['ssl']['ssl_protocols'], + 'settinggroup' => 'system', + 'varname' => 'ssl_protocols', + 'type' => 'option', + 'default' => 'TLSv1,TLSv1.2', + 'option_mode' => 'multiple', + 'option_options' => array( + 'TLSv1' => 'TLSv1', + 'TLSv1.1' => 'TLSv1.1', + 'TLSv1.2' => 'TLSv1.2' + ), + 'save_method' => 'storeSettingField' + ), 'system_ssl_cipher_list' => array( 'label' => $lng['serversettings']['ssl']['ssl_cipher_list'], 'settinggroup' => 'system', @@ -79,6 +93,17 @@ return array( 'default' => '', 'save_method' => 'storeSettingField' ), + 'system_apache24_ocsp_cache_path' => array( + 'label' => $lng['serversettings']['ssl']['apache24_ocsp_cache_path'], + 'settinggroup' => 'system', + 'varname' => 'apache24_ocsp_cache_path', + 'type' => 'string', + 'string_type' => 'string', + 'string_emptyallowed' => false, + 'default' => 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)', + 'visible' => Settings::Get('system.webserver') == "apache2" && Settings::Get('system.apache24') == 1, + 'save_method' => 'storeSettingField' + ), 'system_leenabled' => array( 'label' => $lng['serversettings']['leenabled'], 'settinggroup' => 'system', @@ -95,7 +120,20 @@ return array( 'type' => 'string', 'string_type' => 'file', 'default' => '/etc/apache2/conf-enabled/acme.conf', - 'save_method' => 'storeSettingField', + 'save_method' => 'storeSettingField' + ), + 'system_leapiversion' => array( + 'label' => $lng['serversettings']['leapiversion'], + 'settinggroup' => 'system', + 'varname' => 'leapiversion', + 'type' => 'option', + 'default' => '1', + 'option_mode' => 'one', + 'option_options' => array( + '1' => 'ACME v1', + '2' => 'ACME v2' + ), + 'save_method' => 'storeSettingField' ), 'system_letsencryptca' => array( 'label' => $lng['serversettings']['letsencryptca'], @@ -105,8 +143,8 @@ return array( 'default' => 'testing', 'option_mode' => 'one', 'option_options' => array( - 'testing' => 'https://acme-staging.api.letsencrypt.org (Test)', - 'production' => 'https://acme-v01.api.letsencrypt.org (Live)' + 'testing' => 'https://acme-staging' . (Settings::Get('system.leapiversion') == '2' ? '-v02' : '') . '.api.letsencrypt.org (Test)', + 'production' => 'https://acme-v0' . Settings::Get('system.leapiversion') . '.api.letsencrypt.org (Live)' ), 'save_method' => 'storeSettingField' ), @@ -153,6 +191,14 @@ return array( 'type' => 'bool', 'default' => false, 'save_method' => 'storeSettingField' + ), + 'system_disable_le_selfcheck' => array( + 'label' => $lng['serversettings']['disable_le_selfcheck'], + 'settinggroup' => 'system', + 'varname' => 'disable_le_selfcheck', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' ) ) ) diff --git a/actions/admin/settings/135.fcgid.php b/actions/admin/settings/135.fcgid.php index 050ede3b..212c03f7 100644 --- a/actions/admin/settings/135.fcgid.php +++ b/actions/admin/settings/135.fcgid.php @@ -104,7 +104,7 @@ return array( 'type' => 'int', 'default' => 30, 'save_method' => 'storeSettingField' - ), + ) ) ) ) diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index 750a4660..c8b6e0ea 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -40,15 +40,6 @@ return array( 'option_options_method' => 'getPhpConfigs', 'save_method' => 'storeSettingField' ), - 'system_phpfpm_configdir' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['configdir'], - 'settinggroup' => 'phpfpm', - 'varname' => 'configdir', - 'type' => 'string', - 'string_type' => 'confdir', - 'default' => '/etc/php-fpm.d/', - 'save_method' => 'storeSettingField' - ), 'system_phpfpm_aliasconfigdir' => array( 'label' => $lng['serversettings']['phpfpm_settings']['aliasconfigdir'], 'settinggroup' => 'phpfpm', @@ -73,9 +64,22 @@ return array( 'varname' => 'peardir', 'type' => 'string', 'string_type' => 'dir', + 'string_delimiter' => ':', + 'string_emptyallowed' => true, 'default' => '/usr/share/php/:/usr/share/php5/', 'save_method' => 'storeSettingField' ), + 'system_phpfpm_envpath' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['envpath'], + 'settinggroup' => 'phpfpm', + 'varname' => 'envpath', + 'type' => 'string', + 'string_type' => 'dir', + 'string_delimiter' => ':', + 'string_emptyallowed' => true, + 'default' => '/usr/local/bin:/usr/bin:/bin', + 'save_method' => 'storeSettingField' + ), 'system_phpfpm_fastcgi_ipcdir' => array( 'label' => $lng['serversettings']['phpfpm_settings']['ipcdir'], 'settinggroup' => 'phpfpm', @@ -85,72 +89,6 @@ return array( 'default' => '/var/lib/apache2/fastcgi/', 'save_method' => 'storeSettingField' ), - 'system_phpfpm_reload' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['reload'], - 'settinggroup' => 'phpfpm', - 'varname' => 'reload', - 'type' => 'string', - 'default' => '/etc/init.d/php-fpm restart', - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_pm' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['pm'], - 'settinggroup' => 'phpfpm', - 'varname' => 'pm', - 'type' => 'option', - 'default' => 'static', - 'option_mode' => 'one', - 'option_options' => array('static' => 'static', 'dynamic' => 'dynamic', 'ondemand' => 'ondemand'), - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_max_children' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['max_children'], - 'settinggroup' => 'phpfpm', - 'varname' => 'max_children', - 'type' => 'int', - 'default' => 1, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_start_servers' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['start_servers'], - 'settinggroup' => 'phpfpm', - 'varname' => 'start_servers', - 'type' => 'int', - 'default' => 20, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_min_spare_servers' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['min_spare_servers'], - 'settinggroup' => 'phpfpm', - 'varname' => 'min_spare_servers', - 'type' => 'int', - 'default' => 5, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_max_spare_servers' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['max_spare_servers'], - 'settinggroup' => 'phpfpm', - 'varname' => 'max_spare_servers', - 'type' => 'int', - 'default' => 35, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_max_requests' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['max_requests'], - 'settinggroup' => 'phpfpm', - 'varname' => 'max_requests', - 'type' => 'int', - 'default' => 0, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_idle_timeout' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['idle_timeout'], - 'settinggroup' => 'phpfpm', - 'varname' => 'idle_timeout', - 'type' => 'int', - 'default' => 30, - 'save_method' => 'storeSettingField' - ), 'system_phpfpm_use_mod_proxy' => array( 'label' => $lng['phpfpm']['use_mod_proxy'], 'settinggroup' => 'phpfpm', @@ -160,6 +98,38 @@ return array( 'visible' => Settings::Get('system.apache24'), 'save_method' => 'storeSettingField' ), + 'system_phpfpm_ini_flags' => array( + 'label' => $lng['phpfpm']['ini_flags'], + 'settinggroup' => 'phpfpm', + 'varname' => 'ini_flags', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_phpfpm_ini_values' => array( + 'label' => $lng['phpfpm']['ini_values'], + 'settinggroup' => 'phpfpm', + 'varname' => 'ini_values', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_phpfpm_ini_admin_flags' => array( + 'label' => $lng['phpfpm']['ini_admin_flags'], + 'settinggroup' => 'phpfpm', + 'varname' => 'ini_admin_flags', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_phpfpm_ini_admin_values' => array( + 'label' => $lng['phpfpm']['ini_admin_values'], + 'settinggroup' => 'phpfpm', + 'varname' => 'ini_admin_values', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField' + ) ), ), ), diff --git a/admin_admins.php b/admin_admins.php index 32e5de12..4e5a54d5 100644 --- a/admin_admins.php +++ b/admin_admins.php @@ -170,6 +170,11 @@ if ($page == 'admins' "); Database::pexecute($del_stmt, array('adminid' => $id)); + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DISKSPACE_ADMINS . "` WHERE `adminid` = :adminid + "); + Database::pexecute($del_stmt, array('adminid' => $id)); + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `adminid` = :userid WHERE `adminid` = :adminid @@ -440,7 +445,7 @@ if ($page == 'admins' } else { $language_options = ''; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options.= makeoption($language_name, $language_file, $userinfo['language'], true); } @@ -840,13 +845,13 @@ if ($page == 'admins' } $language_options = ''; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options.= makeoption($language_name, $language_file, $result['def_language'], true); } $ipaddress = makeoption($lng['admin']['allips'], "-1", $result['ip']); $ipsandports_stmt = Database::query(" - SELECT `id`, `ip` FROM `" . TABLE_PANEL_IPSANDPORTS . "` GROUP BY `ip` ORDER BY `ip`, `port` ASC + SELECT `id`, `ip` FROM `" . TABLE_PANEL_IPSANDPORTS . "` GROUP BY `id`, `ip` ORDER BY `ip`, `port` ASC "); while ($row = $ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { diff --git a/admin_autoupdate.php b/admin_autoupdate.php index da4da2ae..701e8011 100644 --- a/admin_autoupdate.php +++ b/admin_autoupdate.php @@ -17,7 +17,6 @@ * @since 0.9.35 * */ - define('AREA', 'admin'); require './lib/init.php'; @@ -26,177 +25,188 @@ define('UPDATE_URI', "https://version.froxlor.org/Froxlor/legacy/" . $version); define('RELEASE_URI', "https://autoupdate.froxlor.org/froxlor-{version}.zip"); define('CHECKSUM_URI', "https://autoupdate.froxlor.org/froxlor-{version}.zip.sha256"); -// check for allow_url_fopen -if (ini_get('allow_url_fopen') === false) { - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 1)); -} - // check for archive-stuff if (! extension_loaded('zip')) { - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 2)); + redirectTo($filename, array( + 's' => $s, + 'page' => 'error', + 'errno' => 2 + )); } // display initial version check if ($page == 'overview') { - + // log our actions $log->logAction(ADM_ACTION, LOG_NOTICE, "checking auto-update"); - + // check for new version - $latestversion = @file(UPDATE_URI); - - if (isset($latestversion[0])) { - $latestversion = explode('|', $latestversion[0]); - - if (is_array($latestversion) - && count($latestversion) >= 1 - ) { - $_version = $latestversion[0]; - $_message = isset($latestversion[1]) ? $latestversion[1] : ''; - $_link = isset($latestversion[2]) ? $latestversion[2] : htmlspecialchars($filename . '?s=' . urlencode($s) . '&page=' . urlencode($page) . '&lookfornewversion=yes'); - - // add the branding so debian guys are not gettings confused - // about their version-number - $version_label = $_version.$branding; - $version_link = $_link; - $message_addinfo = $_message; - - // not numeric -> error-message - if (!preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) { - // check for customized version to not output - // "There is a newer version of froxlor" besides the error-message - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 3)); - } elseif (version_compare2($version, $_version) == -1) { - // there is a newer version - yay - $isnewerversion = 1; - } else { - // nothing new - $isnewerversion = 0; - } - - // anzeige über version-status mit ggfls. formular - // zum update schritt #1 -> download - if ($isnewerversion == 1) { - $text = 'There is a newer version available. Update to version '.$_version.' now?
- {$fieldlabel} + {$fieldlabel}
diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index c928f465..090fb936 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -14,9 +14,11 @@ * @package Install * */ -if (! defined('AREA') || (defined('AREA') && AREA != 'admin') || ! isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { - header('Location: ../../../../index.php'); - exit(); +if (!defined('_CRON_UPDATE')) { + if (! defined('AREA') || (defined('AREA') && AREA != 'admin') || ! isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { + header('Location: ../../../../index.php'); + exit(); + } } if (isFroxlorVersion('0.9-r0')) { @@ -3565,3 +3567,388 @@ if(isFroxlorVersion('0.9.37-greylist')) { Settings::AddNew("mail.greylist_disabled_default", "0"); updateToVersion('0.9.37-greylist2'); } + +if (isDatabaseVersion('201611180')) { + + showUpdateStep("Updating database table definition for panel_domains"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `phpenabled` tinyint(1) NOT NULL default '1' AFTER `parentdomainid`;"); + lastStepStatus(0); + + showUpdateStep("Adding field for let's-encrypt registration status"); + Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` add `leregistered` TINYINT(1) NOT NULL DEFAULT 0;"); + lastStepStatus(0); + + showUpdateStep("Adding system setting for let's-encrypt registration status"); + Settings::AddNew('system.leregistered', '0'); + lastStepStatus(0); + + showUpdateStep("Adding unique key to ipsandports table"); + Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` ADD UNIQUE KEY `ip_port` (`ip`,`port`)"); + lastStepStatus(0); + + updateToDbVersion('201612110'); +} + +if (isFroxlorVersion('0.9.38.4')) { + + showUpdateStep("Updating from 0.9.38.4 to 0.9.38.5", false); + updateToVersion('0.9.38.5'); +} + +if (isFroxlorVersion('0.9.38.5')) { + + showUpdateStep("Updating from 0.9.38.5 to 0.9.38.6", false); + updateToVersion('0.9.38.6'); +} + +if (isFroxlorVersion('0.9.38.6')) { + + showUpdateStep("Updating from 0.9.38.6 to 0.9.38.7", false); + updateToVersion('0.9.38.7'); +} + +if (isDatabaseVersion('201612110')) { + + showUpdateStep("Adding field for OCSP stapling"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . + "` ADD `ocsp_stapling` TINYINT(1) NOT NULL DEFAULT '0';"); + lastStepStatus(0); + + showUpdateStep("Adding default setting for Apache 2.4 OCSP cache path"); + Settings::AddNew('system.apache24_ocsp_cache_path', 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)'); + lastStepStatus(0); + + updateToDbVersion('201704100'); +} + +if (isDatabaseVersion('201704100')) { + + showUpdateStep("Adding new setting for libnss-extrausers"); + $system_nssextrausers= isset($_POST['system_nssextrausers']) ? (int) $_POST['system_nssextrausers'] : 0; + Settings::AddNew('system.nssextrausers', $system_nssextrausers); + lastStepStatus(0); + + updateToDbVersion('201705050'); +} + +if (isDatabaseVersion('201705050')) { + + showUpdateStep("Updating HTTP2 setting"); + if (Settings::Get('system.nginx_http2_support') != null) { + Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `varname` = 'http2_support' WHERE `varname` = 'nginx_http2_support';"); + } else { + Settings::AddNew('system.http2_support', 0); + } + lastStepStatus(0); + showUpdateStep("Adding domain field for HTTP2 stapling"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `http2` TINYINT(1) NOT NULL DEFAULT '0';"); + lastStepStatus(0); + + updateToDbVersion('201708240'); +} + +if (isDatabaseVersion('201708240')) { + + showUpdateStep("Adding new 'disable LE self-check' setting"); + $system_disable_le_selfcheck = isset($_POST['system_disable_le_selfcheck']) ? (int) $_POST['system_disable_le_selfcheck'] : 0; + Settings::AddNew('system.disable_le_selfcheck', $system_disable_le_selfcheck); + lastStepStatus(0); + + updateToDbVersion('201712310'); + + showUpdateStep("Updating from 0.9.38.7 to 0.9.38.8", false); + updateToVersion('0.9.38.8'); +} + +if (isDatabaseVersion('201712310')) { + + showUpdateStep("Adding field for fpm-daemon configs"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` ADD `fpmsettingid` int(11) NOT NULL DEFAULT '1';"); + lastStepStatus(0); + + showUpdateStep("Adding new fpm-daemons table"); + Database::query("DROP TABLE IF EXISTS `panel_fpmdaemons`;"); + $sql = "CREATE TABLE `panel_fpmdaemons` ( + `id` int(11) unsigned NOT NULL auto_increment, + `description` varchar(50) NOT NULL, + `reload_cmd` varchar(255) NOT NULL, + `config_dir` varchar(255) NOT NULL, + `pm` varchar(15) NOT NULL DEFAULT 'static', + `max_children` int(4) NOT NULL DEFAULT '1', + `start_servers` int(4) NOT NULL DEFAULT '20', + `min_spare_servers` int(4) NOT NULL DEFAULT '5', + `max_spare_servers` int(4) NOT NULL DEFAULT '35', + `max_requests` int(4) NOT NULL DEFAULT '0', + `idle_timeout` int(4) NOT NULL DEFAULT '30', + PRIMARY KEY (`id`), + UNIQUE KEY `reload` (`reload_cmd`), + UNIQUE KEY `config` (`config_dir`) + ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci;"; + Database::query($sql); + lastStepStatus(0); + + showUpdateStep("Converting php-fpm settings to new layout"); + $ins_stmt = Database::prepare(" + INSERT INTO `panel_fpmdaemons` SET + `id` = 1, + `description` = 'System default', + `reload_cmd` = :reloadcmd, + `config_dir` = :confdir, + `pm` = :pm, + `max_children` = :maxc, + `start_servers` = :starts, + `min_spare_servers` = :minss, + `max_spare_servers` = :maxss, + `max_requests` = :maxr, + `idle_timeout` = :it + "); + Database::pexecute($ins_stmt, array( + 'reloadcmd' => Settings::Get('phpfpm.reload'), + 'confdir' => Settings::Get('phpfpm.configdir'), + 'pm' => Settings::Get('phpfpm.pm'), + 'maxc' => Settings::Get('phpfpm.max_children'), + 'starts' => Settings::Get('phpfpm.start_servers'), + 'minss' => Settings::Get('phpfpm.min_spare_servers'), + 'maxss' => Settings::Get('phpfpm.max_spare_servers'), + 'maxr' => Settings::Get('phpfpm.max_requests'), + 'it' => Settings::Get('phpfpm.idle_timeout') + )); + lastStepStatus(0); + + showUpdateStep("Deleting unneeded settings"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'reload'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'configdir'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'pm'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'max_children'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'start_servers'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'min_spare_servers'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'max_spare_servers'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'max_requests'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'idle_timeout'"); + lastStepStatus(0); + + updateToDbVersion('201801070'); +} + +if (isDatabaseVersion('201801070')) { + + showUpdateStep("Adding field allowed_phpconfigs for customers"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `allowed_phpconfigs` varchar(500) NOT NULL default '';"); + lastStepStatus(0); + + updateToDbVersion('201801080'); +} + +if (isDatabaseVersion('201801080')) { + + showUpdateStep("Adding new setting for Let's Encrypt ACME version"); + Settings::AddNew('system.leapiversion', '1'); + lastStepStatus(0); + + updateToDbVersion('201801090'); +} + +if (isDatabaseVersion('201801090')) { + + showUpdateStep("Adding field pass_authorizationheader for php-configs"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` ADD `pass_authorizationheader` tinyint(1) NOT NULL default '0';"); + lastStepStatus(0); + + updateToDbVersion('201801091'); +} + +if (isDatabaseVersion('201801091')) { + + showUpdateStep("Adding new setting for SSL protocols"); + Settings::AddNew('system.ssl_protocols', 'TLSv1,TLSv1.2'); + lastStepStatus(0); + + updateToDbVersion('201801100'); +} + +if (isDatabaseVersion('201801100')) { + + showUpdateStep("Adding field for security.limit_extensions fpm-setting"); + Database::query("ALTER TABLE `" . TABLE_PANEL_FPMDAEMONS . "` ADD `limit_extensions` varchar(255) NOT NULL default '.php';"); + lastStepStatus(0); + + updateToDbVersion('201801101'); +} + +if (isDatabaseVersion('201801101')) { + + showUpdateStep("Adding dynamic php-fpm php.ini settings"); + Settings::AddNew('phpfpm.ini_flags', 'asp_tags +display_errors +display_startup_errors +html_errors +log_errors +magic_quotes_gpc +magic_quotes_runtime +magic_quotes_sybase +mail.add_x_header +session.cookie_secure +session.use_cookies +short_open_tag +track_errors +xmlrpc_errors +suhosin.simulation +suhosin.session.encrypt +suhosin.session.cryptua +suhosin.session.cryptdocroot +suhosin.cookie.encrypt +suhosin.cookie.cryptua +suhosin.cookie.cryptdocroot +suhosin.executor.disable_eval +mbstring.func_overload'); + Settings::AddNew('phpfpm.ini_values', 'auto_append_file +auto_prepend_file +date.timezone +default_charset +error_reporting +include_path +log_errors_max_len +mail.log +max_execution_time +session.cookie_domain +session.cookie_lifetime +session.cookie_path +session.name +session.serialize_handler +upload_max_filesize +xmlrpc_error_number +session.auto_start +always_populate_raw_post_data +suhosin.session.cryptkey +suhosin.session.cryptraddr +suhosin.session.checkraddr +suhosin.cookie.cryptkey +suhosin.cookie.plainlist +suhosin.cookie.cryptraddr +suhosin.cookie.checkraddr +suhosin.executor.func.blacklist +suhosin.executor.eval.whitelist'); + Settings::AddNew('phpfpm.ini_admin_flags', 'allow_call_time_pass_reference +allow_url_fopen +allow_url_include +auto_detect_line_endings +cgi.fix_pathinfo +cgi.force_redirect +enable_dl +expose_php +file_uploads +ignore_repeated_errors +ignore_repeated_source +log_errors +register_argc_argv +report_memleaks +opcache.enable +opcache.consistency_checks +opcache.dups_fix +opcache.load_comments +opcache.revalidate_path +opcache.save_comments +opcache.use_cwd +opcache.validate_timestamps +opcache.fast_shutdown'); + Settings::AddNew('phpfpm.ini_admin_values', 'cgi.redirect_status_env +date.timezone +disable_classes +disable_functions +error_log +gpc_order +max_input_time +max_input_vars +memory_limit +open_basedir +output_buffering +post_max_size +precision +sendmail_path +session.gc_divisor +session.gc_probability +variables_order +opcache.log_verbosity_level +opcache.restrict_api +opcache.revalidate_freq +opcache.max_accelerated_files +opcache.memory_consumption +opcache.interned_strings_buffer'); + lastStepStatus(0); + + updateToDbVersion('201801110'); +} + +if (isDatabaseVersion('201801110')) { + + showUpdateStep("Adding php-fpm php PATH setting for envrironment"); + Settings::AddNew("phpfpm.envpath", '/usr/local/bin:/usr/bin:/bin'); + lastStepStatus(0); + + updateToDbVersion('201801260'); +} + +if (isFroxlorVersion('0.9.38.8')) { + + showUpdateStep("Updating from 0.9.38.8 to 0.9.39 final", false); + updateToVersion('0.9.39'); +} + +if (isFroxlorVersion('0.9.39')) { + + showUpdateStep("Updating from 0.9.39 to 0.9.39.1", false); + updateToVersion('0.9.39.1'); +} + +if (isFroxlorVersion('0.9.39.1')) { + + showUpdateStep("Updating from 0.9.39.1 to 0.9.39.2", false); + updateToVersion('0.9.39.2'); +} + +if (isDatabaseVersion('201801260')) { + + showUpdateStep("Adding new plans table"); + Database::query("DROP TABLE IF EXISTS `panel_plans`;"); + $sql = "CREATE TABLE `panel_plans` ( + `id` int(11) NOT NULL auto_increment, + `adminid` int(11) NOT NULL default '0', + `name` varchar(255) NOT NULL default '', + `description` text NOT NULL, + `value` longtext NOT NULL, + `ts` int(15) NOT NULL default '0', + PRIMARY KEY (id), + KEY adminid (adminid) + ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci;"; + Database::query($sql); + lastStepStatus(0); + + updateToDbVersion('201802120'); +} + +if (isDatabaseVersion('201802120')) { + + showUpdateStep("Adding domain field for try_files flag"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `notryfiles` tinyint(1) DEFAULT '0';"); + lastStepStatus(0); + + updateToDbVersion('201802130'); +} + +if (isFroxlorVersion('0.9.39.2')) { + + showUpdateStep("Updating from 0.9.39.2 to 0.9.39.3", false); + updateToVersion('0.9.39.3'); +} + +if (isFroxlorVersion('0.9.39.3')) { + + showUpdateStep("Updating from 0.9.39.3 to 0.9.39.4", false); + updateToVersion('0.9.39.4'); +} + +if (isFroxlorVersion('0.9.39.4')) { + + showUpdateStep("Updating from 0.9.39.4 to 0.9.39.5", false); + updateToVersion('0.9.39.5'); +} diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index a25b78fc..26b47282 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -709,4 +709,22 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $question .= '- {$content} -
-+ {$content} +
+
- * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
- *
- * @var string|callable
- * @see SMTP::$Debugoutput
- */
- public $Debugoutput = 'echo';
-
- /**
- * Whether to keep SMTP connection open after each message.
- * If this is set to true then to close the connection
- * requires an explicit call to smtpClose().
- * @var boolean
- */
- public $SMTPKeepAlive = false;
-
- /**
- * Whether to split multiple to addresses into multiple messages
- * or send them all in one message.
- * Only supported in `mail` and `sendmail` transports, not in SMTP.
- * @var boolean
- */
- public $SingleTo = false;
-
- /**
- * Storage for addresses when SingleTo is enabled.
- * @var array
- * @TODO This should really not be public
- */
- public $SingleToArray = array();
-
- /**
- * Whether to generate VERP addresses on send.
- * Only applicable when sending via SMTP.
- * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
- * @link http://www.postfix.org/VERP_README.html Postfix VERP info
- * @var boolean
- */
- public $do_verp = false;
-
- /**
- * Whether to allow sending messages with an empty body.
- * @var boolean
- */
- public $AllowEmpty = false;
-
- /**
- * The default line ending.
- * @note The default remains "\n". We force CRLF where we know
- * it must be used via self::CRLF.
- * @var string
- */
- public $LE = "\n";
-
- /**
- * DKIM selector.
- * @var string
- */
- public $DKIM_selector = '';
-
- /**
- * DKIM Identity.
- * Usually the email address used as the source of the email.
- * @var string
- */
- public $DKIM_identity = '';
-
- /**
- * DKIM passphrase.
- * Used if your key is encrypted.
- * @var string
- */
- public $DKIM_passphrase = '';
-
- /**
- * DKIM signing domain name.
- * @example 'example.com'
- * @var string
- */
- public $DKIM_domain = '';
-
- /**
- * DKIM private key file path.
- * @var string
- */
- public $DKIM_private = '';
-
- /**
- * Callback Action function name.
- *
- * The function that handles the result of the send email action.
- * It is called out by send() for each email sent.
- *
- * Value can be any php callable: http://www.php.net/is_callable
- *
- * Parameters:
- * boolean $result result of the send action
- * string $to email address of the recipient
- * string $cc cc email addresses
- * string $bcc bcc email addresses
- * string $subject the subject
- * string $body the email body
- * string $from email address of sender
- * @var string
- */
- public $action_function = '';
-
- /**
- * What to put in the X-Mailer header.
- * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
- * @var string
- */
- public $XMailer = '';
-
- /**
- * Which validator to use by default when validating email addresses.
- * May be a callable to inject your own validator, but there are several built-in validators.
- * @see PHPMailer::validateAddress()
- * @var string|callable
- * @static
- */
- public static $validator = 'auto';
-
- /**
- * An instance of the SMTP sender class.
- * @var SMTP
- * @access protected
- */
- protected $smtp = null;
-
- /**
- * The array of 'to' names and addresses.
- * @var array
- * @access protected
- */
- protected $to = array();
-
- /**
- * The array of 'cc' names and addresses.
- * @var array
- * @access protected
- */
- protected $cc = array();
-
- /**
- * The array of 'bcc' names and addresses.
- * @var array
- * @access protected
- */
- protected $bcc = array();
-
- /**
- * The array of reply-to names and addresses.
- * @var array
- * @access protected
- */
- protected $ReplyTo = array();
-
- /**
- * An array of all kinds of addresses.
- * Includes all of $to, $cc, $bcc
- * @var array
- * @access protected
- * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
- */
- protected $all_recipients = array();
-
- /**
- * An array of names and addresses queued for validation.
- * In send(), valid and non duplicate entries are moved to $all_recipients
- * and one of $to, $cc, or $bcc.
- * This array is used only for addresses with IDN.
- * @var array
- * @access protected
- * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
- * @see PHPMailer::$all_recipients
- */
- protected $RecipientsQueue = array();
-
- /**
- * An array of reply-to names and addresses queued for validation.
- * In send(), valid and non duplicate entries are moved to $ReplyTo.
- * This array is used only for addresses with IDN.
- * @var array
- * @access protected
- * @see PHPMailer::$ReplyTo
- */
- protected $ReplyToQueue = array();
-
- /**
- * The array of attachments.
- * @var array
- * @access protected
- */
- protected $attachment = array();
-
- /**
- * The array of custom headers.
- * @var array
- * @access protected
- */
- protected $CustomHeader = array();
-
- /**
- * The most recent Message-ID (including angular brackets).
- * @var string
- * @access protected
- */
- protected $lastMessageID = '';
-
- /**
- * The message's MIME type.
- * @var string
- * @access protected
- */
- protected $message_type = '';
-
- /**
- * The array of MIME boundary strings.
- * @var array
- * @access protected
- */
- protected $boundary = array();
-
- /**
- * The array of available languages.
- * @var array
- * @access protected
- */
- protected $language = array();
-
- /**
- * The number of errors encountered.
- * @var integer
- * @access protected
- */
- protected $error_count = 0;
-
- /**
- * The S/MIME certificate file path.
- * @var string
- * @access protected
- */
- protected $sign_cert_file = '';
-
- /**
- * The S/MIME key file path.
- * @var string
- * @access protected
- */
- protected $sign_key_file = '';
-
- /**
- * The optional S/MIME extra certificates ("CA Chain") file path.
- * @var string
- * @access protected
- */
- protected $sign_extracerts_file = '';
-
- /**
- * The S/MIME password for the key.
- * Used only if the key is encrypted.
- * @var string
- * @access protected
- */
- protected $sign_key_pass = '';
-
- /**
- * Whether to throw exceptions for errors.
- * @var boolean
- * @access protected
- */
- protected $exceptions = false;
-
- /**
- * Unique ID used for message ID and boundaries.
- * @var string
- * @access protected
- */
- protected $uniqueid = '';
-
- /**
- * Error severity: message only, continue processing.
- */
- const STOP_MESSAGE = 0;
-
- /**
- * Error severity: message, likely ok to continue processing.
- */
- const STOP_CONTINUE = 1;
-
- /**
- * Error severity: message, plus full stop, critical error reached.
- */
- const STOP_CRITICAL = 2;
-
- /**
- * SMTP RFC standard line ending.
- */
- const CRLF = "\r\n";
-
- /**
- * The maximum line length allowed by RFC 2822 section 2.1.1
- * @var integer
- */
- const MAX_LINE_LENGTH = 998;
-
- /**
- * Constructor.
- * @param boolean $exceptions Should we throw external exceptions?
- */
- public function __construct($exceptions = null)
- {
- if ($exceptions !== null) {
- $this->exceptions = (boolean)$exceptions;
- }
- }
-
- /**
- * Destructor.
- */
- public function __destruct()
- {
- //Close any open SMTP connection nicely
- $this->smtpClose();
- }
-
- /**
- * Call mail() in a safe_mode-aware fashion.
- * Also, unless sendmail_path points to sendmail (or something that
- * claims to be sendmail), don't pass params (not a perfect fix,
- * but it will do)
- * @param string $to To
- * @param string $subject Subject
- * @param string $body Message Body
- * @param string $header Additional Header(s)
- * @param string $params Params
- * @access private
- * @return boolean
- */
- private function mailPassthru($to, $subject, $body, $header, $params)
- {
- //Check overloading of mail function to avoid double-encoding
- if (ini_get('mbstring.func_overload') & 1) {
- $subject = $this->secureHeader($subject);
- } else {
- $subject = $this->encodeHeader($this->secureHeader($subject));
- }
- //Can't use additional_parameters in safe_mode
- //@link http://php.net/manual/en/function.mail.php
- if (ini_get('safe_mode') or !$this->UseSendmailOptions) {
- $result = @mail($to, $subject, $body, $header);
- } else {
- $result = @mail($to, $subject, $body, $header, $params);
- }
- return $result;
- }
-
- /**
- * Output debugging info via user-defined method.
- * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
- * @see PHPMailer::$Debugoutput
- * @see PHPMailer::$SMTPDebug
- * @param string $str
- */
- protected function edebug($str)
- {
- if ($this->SMTPDebug <= 0) {
- return;
- }
- //Avoid clash with built-in function names
- if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
- call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
- return;
- }
- switch ($this->Debugoutput) {
- case 'error_log':
- //Don't output, just log
- error_log($str);
- break;
- case 'html':
- //Cleans up output a bit for a better looking, HTML-safe output
- echo htmlentities(
- preg_replace('/[\r\n]+/', '', $str),
- ENT_QUOTES,
- 'UTF-8'
- )
- . "
- * // Use default conversion
- * $plain = $mail->html2text($html);
- * // Use your own custom converter
- * $plain = $mail->html2text($html, function($html) {
- * $converter = new MyHtml2text($html);
- * return $converter->get_text();
- * });
- *
- * @param string $html The HTML text to convert
- * @param boolean|callable $advanced Any boolean value to use the internal converter,
- * or provide your own callable for custom conversion.
- * @return string
- */
- public function html2text($html, $advanced = false)
- {
- if (is_callable($advanced)) {
- return call_user_func($advanced, $html);
- }
- return html_entity_decode(
- trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
- ENT_QUOTES,
- $this->CharSet
- );
- }
-
- /**
- * Get the MIME type for a file extension.
- * @param string $ext File extension
- * @access public
- * @return string MIME type of file.
- * @static
- */
- public static function _mime_types($ext = '')
- {
- $mimes = array(
- 'xl' => 'application/excel',
- 'js' => 'application/javascript',
- 'hqx' => 'application/mac-binhex40',
- 'cpt' => 'application/mac-compactpro',
- 'bin' => 'application/macbinary',
- 'doc' => 'application/msword',
- 'word' => 'application/msword',
- 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
- 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
- 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
- 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
- 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
- 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
- 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
- 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
- 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
- 'class' => 'application/octet-stream',
- 'dll' => 'application/octet-stream',
- 'dms' => 'application/octet-stream',
- 'exe' => 'application/octet-stream',
- 'lha' => 'application/octet-stream',
- 'lzh' => 'application/octet-stream',
- 'psd' => 'application/octet-stream',
- 'sea' => 'application/octet-stream',
- 'so' => 'application/octet-stream',
- 'oda' => 'application/oda',
- 'pdf' => 'application/pdf',
- 'ai' => 'application/postscript',
- 'eps' => 'application/postscript',
- 'ps' => 'application/postscript',
- 'smi' => 'application/smil',
- 'smil' => 'application/smil',
- 'mif' => 'application/vnd.mif',
- 'xls' => 'application/vnd.ms-excel',
- 'ppt' => 'application/vnd.ms-powerpoint',
- 'wbxml' => 'application/vnd.wap.wbxml',
- 'wmlc' => 'application/vnd.wap.wmlc',
- 'dcr' => 'application/x-director',
- 'dir' => 'application/x-director',
- 'dxr' => 'application/x-director',
- 'dvi' => 'application/x-dvi',
- 'gtar' => 'application/x-gtar',
- 'php3' => 'application/x-httpd-php',
- 'php4' => 'application/x-httpd-php',
- 'php' => 'application/x-httpd-php',
- 'phtml' => 'application/x-httpd-php',
- 'phps' => 'application/x-httpd-php-source',
- 'swf' => 'application/x-shockwave-flash',
- 'sit' => 'application/x-stuffit',
- 'tar' => 'application/x-tar',
- 'tgz' => 'application/x-tar',
- 'xht' => 'application/xhtml+xml',
- 'xhtml' => 'application/xhtml+xml',
- 'zip' => 'application/zip',
- 'mid' => 'audio/midi',
- 'midi' => 'audio/midi',
- 'mp2' => 'audio/mpeg',
- 'mp3' => 'audio/mpeg',
- 'mpga' => 'audio/mpeg',
- 'aif' => 'audio/x-aiff',
- 'aifc' => 'audio/x-aiff',
- 'aiff' => 'audio/x-aiff',
- 'ram' => 'audio/x-pn-realaudio',
- 'rm' => 'audio/x-pn-realaudio',
- 'rpm' => 'audio/x-pn-realaudio-plugin',
- 'ra' => 'audio/x-realaudio',
- 'wav' => 'audio/x-wav',
- 'bmp' => 'image/bmp',
- 'gif' => 'image/gif',
- 'jpeg' => 'image/jpeg',
- 'jpe' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'png' => 'image/png',
- 'tiff' => 'image/tiff',
- 'tif' => 'image/tiff',
- 'eml' => 'message/rfc822',
- 'css' => 'text/css',
- 'html' => 'text/html',
- 'htm' => 'text/html',
- 'shtml' => 'text/html',
- 'log' => 'text/plain',
- 'text' => 'text/plain',
- 'txt' => 'text/plain',
- 'rtx' => 'text/richtext',
- 'rtf' => 'text/rtf',
- 'vcf' => 'text/vcard',
- 'vcard' => 'text/vcard',
- 'xml' => 'text/xml',
- 'xsl' => 'text/xml',
- 'mpeg' => 'video/mpeg',
- 'mpe' => 'video/mpeg',
- 'mpg' => 'video/mpeg',
- 'mov' => 'video/quicktime',
- 'qt' => 'video/quicktime',
- 'rv' => 'video/vnd.rn-realvideo',
- 'avi' => 'video/x-msvideo',
- 'movie' => 'video/x-sgi-movie'
- );
- if (array_key_exists(strtolower($ext), $mimes)) {
- return $mimes[strtolower($ext)];
- }
- return 'application/octet-stream';
- }
-
- /**
- * Map a file name to a MIME type.
- * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
- * @param string $filename A file name or full path, does not need to exist as a file
- * @return string
- * @static
- */
- public static function filenameToType($filename)
- {
- // In case the path is a URL, strip any query string before getting extension
- $qpos = strpos($filename, '?');
- if (false !== $qpos) {
- $filename = substr($filename, 0, $qpos);
- }
- $pathinfo = self::mb_pathinfo($filename);
- return self::_mime_types($pathinfo['extension']);
- }
-
- /**
- * Multi-byte-safe pathinfo replacement.
- * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
- * Works similarly to the one in PHP >= 5.2.0
- * @link http://www.php.net/manual/en/function.pathinfo.php#107461
- * @param string $path A filename or path, does not need to exist as a file
- * @param integer|string $options Either a PATHINFO_* constant,
- * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
- * @return string|array
- * @static
- */
- public static function mb_pathinfo($path, $options = null)
- {
- $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
- $pathinfo = array();
- if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
- if (array_key_exists(1, $pathinfo)) {
- $ret['dirname'] = $pathinfo[1];
- }
- if (array_key_exists(2, $pathinfo)) {
- $ret['basename'] = $pathinfo[2];
- }
- if (array_key_exists(5, $pathinfo)) {
- $ret['extension'] = $pathinfo[5];
- }
- if (array_key_exists(3, $pathinfo)) {
- $ret['filename'] = $pathinfo[3];
- }
- }
- switch ($options) {
- case PATHINFO_DIRNAME:
- case 'dirname':
- return $ret['dirname'];
- case PATHINFO_BASENAME:
- case 'basename':
- return $ret['basename'];
- case PATHINFO_EXTENSION:
- case 'extension':
- return $ret['extension'];
- case PATHINFO_FILENAME:
- case 'filename':
- return $ret['filename'];
- default:
- return $ret;
- }
- }
-
- /**
- * Set or reset instance properties.
- * You should avoid this function - it's more verbose, less efficient, more error-prone and
- * harder to debug than setting properties directly.
- * Usage Example:
- * `$mail->set('SMTPSecure', 'tls');`
- * is the same as:
- * `$mail->SMTPSecure = 'tls';`
- * @access public
- * @param string $name The property name to set
- * @param mixed $value The value to set the property to
- * @return boolean
- * @TODO Should this not be using the __set() magic function?
- */
- public function set($name, $value = '')
- {
- if (property_exists($this, $name)) {
- $this->$name = $value;
- return true;
- } else {
- $this->setError($this->lang('variable_set') . $name);
- return false;
- }
- }
-
- /**
- * Strip newlines to prevent header injection.
- * @access public
- * @param string $str
- * @return string
- */
- public function secureHeader($str)
- {
- return trim(str_replace(array("\r", "\n"), '', $str));
- }
-
- /**
- * Normalize line breaks in a string.
- * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
- * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
- * @param string $text
- * @param string $breaktype What kind of line break to use, defaults to CRLF
- * @return string
- * @access public
- * @static
- */
- public static function normalizeBreaks($text, $breaktype = "\r\n")
- {
- return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
- }
-
- /**
- * Set the public and private key files and password for S/MIME signing.
- * @access public
- * @param string $cert_filename
- * @param string $key_filename
- * @param string $key_pass Password for private key
- * @param string $extracerts_filename Optional path to chain certificate
- */
- public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
- {
- $this->sign_cert_file = $cert_filename;
- $this->sign_key_file = $key_filename;
- $this->sign_key_pass = $key_pass;
- $this->sign_extracerts_file = $extracerts_filename;
- }
-
- /**
- * Quoted-Printable-encode a DKIM header.
- * @access public
- * @param string $txt
- * @return string
- */
- public function DKIM_QP($txt)
- {
- $line = '';
- for ($i = 0; $i < strlen($txt); $i++) {
- $ord = ord($txt[$i]);
- if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
- $line .= $txt[$i];
- } else {
- $line .= '=' . sprintf('%02X', $ord);
- }
- }
- return $line;
- }
-
- /**
- * Generate a DKIM signature.
- * @access public
- * @param string $signHeader
- * @throws phpmailerException
- * @return string
- */
- public function DKIM_Sign($signHeader)
- {
- if (!defined('PKCS7_TEXT')) {
- if ($this->exceptions) {
- throw new phpmailerException($this->lang('extension_missing') . 'openssl');
- }
- return '';
- }
- $privKeyStr = file_get_contents($this->DKIM_private);
- if ($this->DKIM_passphrase != '') {
- $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
- } else {
- $privKey = openssl_pkey_get_private($privKeyStr);
- }
- if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { //sha1WithRSAEncryption
- openssl_pkey_free($privKey);
- return base64_encode($signature);
- }
- openssl_pkey_free($privKey);
- return '';
- }
-
- /**
- * Generate a DKIM canonicalization header.
- * @access public
- * @param string $signHeader Header
- * @return string
- */
- public function DKIM_HeaderC($signHeader)
- {
- $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
- $lines = explode("\r\n", $signHeader);
- foreach ($lines as $key => $line) {
- list($heading, $value) = explode(':', $line, 2);
- $heading = strtolower($heading);
- $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
- $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
- }
- $signHeader = implode("\r\n", $lines);
- return $signHeader;
- }
-
- /**
- * Generate a DKIM canonicalization body.
- * @access public
- * @param string $body Message Body
- * @return string
- */
- public function DKIM_BodyC($body)
- {
- if ($body == '') {
- return "\r\n";
- }
- // stabilize line endings
- $body = str_replace("\r\n", "\n", $body);
- $body = str_replace("\n", "\r\n", $body);
- // END stabilize line endings
- while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
- $body = substr($body, 0, strlen($body) - 2);
- }
- return $body;
- }
-
- /**
- * Create the DKIM header and body in a new message header.
- * @access public
- * @param string $headers_line Header lines
- * @param string $subject Subject
- * @param string $body Body
- * @return string
- */
- public function DKIM_Add($headers_line, $subject, $body)
- {
- $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
- $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
- $DKIMquery = 'dns/txt'; // Query method
- $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
- $subject_header = "Subject: $subject";
- $headers = explode($this->LE, $headers_line);
- $from_header = '';
- $to_header = '';
- $date_header = '';
- $current = '';
- foreach ($headers as $header) {
- if (strpos($header, 'From:') === 0) {
- $from_header = $header;
- $current = 'from_header';
- } elseif (strpos($header, 'To:') === 0) {
- $to_header = $header;
- $current = 'to_header';
- } elseif (strpos($header, 'Date:') === 0) {
- $date_header = $header;
- $current = 'date_header';
- } else {
- if (!empty($$current) && strpos($header, ' =?') === 0) {
- $$current .= $header;
- } else {
- $current = '';
- }
- }
- }
- $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
- $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
- $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
- $subject = str_replace(
- '|',
- '=7C',
- $this->DKIM_QP($subject_header)
- ); // Copied header fields (dkim-quoted-printable)
- $body = $this->DKIM_BodyC($body);
- $DKIMlen = strlen($body); // Length of body
- $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
- if ('' == $this->DKIM_identity) {
- $ident = '';
- } else {
- $ident = ' i=' . $this->DKIM_identity . ';';
- }
- $dkimhdrs = 'DKIM-Signature: v=1; a=' .
- $DKIMsignatureType . '; q=' .
- $DKIMquery . '; l=' .
- $DKIMlen . '; s=' .
- $this->DKIM_selector .
- ";\r\n" .
- "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
- "\th=From:To:Date:Subject;\r\n" .
- "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
- "\tz=$from\r\n" .
- "\t|$to\r\n" .
- "\t|$date\r\n" .
- "\t|$subject;\r\n" .
- "\tbh=" . $DKIMb64 . ";\r\n" .
- "\tb=";
- $toSign = $this->DKIM_HeaderC(
- $from_header . "\r\n" .
- $to_header . "\r\n" .
- $date_header . "\r\n" .
- $subject_header . "\r\n" .
- $dkimhdrs
- );
- $signed = $this->DKIM_Sign($toSign);
- return $dkimhdrs . $signed . "\r\n";
- }
-
- /**
- * Detect if a string contains a line longer than the maximum line length allowed.
- * @param string $str
- * @return boolean
- * @static
- */
- public static function hasLineLongerThanMax($str)
- {
- //+2 to include CRLF line break for a 1000 total
- return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
- }
-
- /**
- * Allows for public read access to 'to' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getToAddresses()
- {
- return $this->to;
- }
-
- /**
- * Allows for public read access to 'cc' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getCcAddresses()
- {
- return $this->cc;
- }
-
- /**
- * Allows for public read access to 'bcc' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getBccAddresses()
- {
- return $this->bcc;
- }
-
- /**
- * Allows for public read access to 'ReplyTo' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getReplyToAddresses()
- {
- return $this->ReplyTo;
- }
-
- /**
- * Allows for public read access to 'all_recipients' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getAllRecipientAddresses()
- {
- return $this->all_recipients;
- }
-
- /**
- * Perform a callback.
- * @param boolean $isSent
- * @param array $to
- * @param array $cc
- * @param array $bcc
- * @param string $subject
- * @param string $body
- * @param string $from
- */
- protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
- {
- if (!empty($this->action_function) && is_callable($this->action_function)) {
- $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
- call_user_func_array($this->action_function, $params);
- }
- }
+ /**
+ * The PHPMailer Version number.
+ * @var string
+ */
+ public $Version = '5.2.26';
+
+ /**
+ * Email priority.
+ * Options: null (default), 1 = High, 3 = Normal, 5 = low.
+ * When null, the header is not set at all.
+ * @var integer
+ */
+ public $Priority = null;
+
+ /**
+ * The character set of the message.
+ * @var string
+ */
+ public $CharSet = 'iso-8859-1';
+
+ /**
+ * The MIME Content-type of the message.
+ * @var string
+ */
+ public $ContentType = 'text/plain';
+
+ /**
+ * The message encoding.
+ * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
+ * @var string
+ */
+ public $Encoding = '8bit';
+
+ /**
+ * Holds the most recent mailer error message.
+ * @var string
+ */
+ public $ErrorInfo = '';
+
+ /**
+ * The From email address for the message.
+ * @var string
+ */
+ public $From = 'root@localhost';
+
+ /**
+ * The From name of the message.
+ * @var string
+ */
+ public $FromName = 'Root User';
+
+ /**
+ * The Sender email (Return-Path) of the message.
+ * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
+ * @var string
+ */
+ public $Sender = '';
+
+ /**
+ * The Return-Path of the message.
+ * If empty, it will be set to either From or Sender.
+ * @var string
+ * @deprecated Email senders should never set a return-path header;
+ * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
+ * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
+ */
+ public $ReturnPath = '';
+
+ /**
+ * The Subject of the message.
+ * @var string
+ */
+ public $Subject = '';
+
+ /**
+ * An HTML or plain text message body.
+ * If HTML then call isHTML(true).
+ * @var string
+ */
+ public $Body = '';
+
+ /**
+ * The plain-text message body.
+ * This body can be read by mail clients that do not have HTML email
+ * capability such as mutt & Eudora.
+ * Clients that can read HTML will view the normal Body.
+ * @var string
+ */
+ public $AltBody = '';
+
+ /**
+ * An iCal message part body.
+ * Only supported in simple alt or alt_inline message types
+ * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
+ * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
+ * @link http://kigkonsult.se/iCalcreator/
+ * @var string
+ */
+ public $Ical = '';
+
+ /**
+ * The complete compiled MIME message body.
+ * @access protected
+ * @var string
+ */
+ protected $MIMEBody = '';
+
+ /**
+ * The complete compiled MIME message headers.
+ * @var string
+ * @access protected
+ */
+ protected $MIMEHeader = '';
+
+ /**
+ * Extra headers that createHeader() doesn't fold in.
+ * @var string
+ * @access protected
+ */
+ protected $mailHeader = '';
+
+ /**
+ * Word-wrap the message body to this number of chars.
+ * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
+ * @var integer
+ */
+ public $WordWrap = 0;
+
+ /**
+ * Which method to use to send mail.
+ * Options: "mail", "sendmail", or "smtp".
+ * @var string
+ */
+ public $Mailer = 'mail';
+
+ /**
+ * The path to the sendmail program.
+ * @var string
+ */
+ public $Sendmail = '/usr/sbin/sendmail';
+
+ /**
+ * Whether mail() uses a fully sendmail-compatible MTA.
+ * One which supports sendmail's "-oi -f" options.
+ * @var boolean
+ */
+ public $UseSendmailOptions = true;
+
+ /**
+ * Path to PHPMailer plugins.
+ * Useful if the SMTP class is not in the PHP include path.
+ * @var string
+ * @deprecated Should not be needed now there is an autoloader.
+ */
+ public $PluginDir = '';
+
+ /**
+ * The email address that a reading confirmation should be sent to, also known as read receipt.
+ * @var string
+ */
+ public $ConfirmReadingTo = '';
+
+ /**
+ * The hostname to use in the Message-ID header and as default HELO string.
+ * If empty, PHPMailer attempts to find one with, in order,
+ * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
+ * 'localhost.localdomain'.
+ * @var string
+ */
+ public $Hostname = '';
+
+ /**
+ * An ID to be used in the Message-ID header.
+ * If empty, a unique id will be generated.
+ * You can set your own, but it must be in the format "
+ * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
+ *
+ * @var string|callable
+ * @see SMTP::$Debugoutput
+ */
+ public $Debugoutput = 'echo';
+
+ /**
+ * Whether to keep SMTP connection open after each message.
+ * If this is set to true then to close the connection
+ * requires an explicit call to smtpClose().
+ * @var boolean
+ */
+ public $SMTPKeepAlive = false;
+
+ /**
+ * Whether to split multiple to addresses into multiple messages
+ * or send them all in one message.
+ * Only supported in `mail` and `sendmail` transports, not in SMTP.
+ * @var boolean
+ */
+ public $SingleTo = false;
+
+ /**
+ * Storage for addresses when SingleTo is enabled.
+ * @var array
+ * @TODO This should really not be public
+ */
+ public $SingleToArray = array();
+
+ /**
+ * Whether to generate VERP addresses on send.
+ * Only applicable when sending via SMTP.
+ * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link http://www.postfix.org/VERP_README.html Postfix VERP info
+ * @var boolean
+ */
+ public $do_verp = false;
+
+ /**
+ * Whether to allow sending messages with an empty body.
+ * @var boolean
+ */
+ public $AllowEmpty = false;
+
+ /**
+ * The default line ending.
+ * @note The default remains "\n". We force CRLF where we know
+ * it must be used via self::CRLF.
+ * @var string
+ */
+ public $LE = "\n";
+
+ /**
+ * DKIM selector.
+ * @var string
+ */
+ public $DKIM_selector = '';
+
+ /**
+ * DKIM Identity.
+ * Usually the email address used as the source of the email.
+ * @var string
+ */
+ public $DKIM_identity = '';
+
+ /**
+ * DKIM passphrase.
+ * Used if your key is encrypted.
+ * @var string
+ */
+ public $DKIM_passphrase = '';
+
+ /**
+ * DKIM signing domain name.
+ * @example 'example.com'
+ * @var string
+ */
+ public $DKIM_domain = '';
+
+ /**
+ * DKIM private key file path.
+ * @var string
+ */
+ public $DKIM_private = '';
+
+ /**
+ * DKIM private key string.
+ * If set, takes precedence over `$DKIM_private`.
+ * @var string
+ */
+ public $DKIM_private_string = '';
+
+ /**
+ * Callback Action function name.
+ *
+ * The function that handles the result of the send email action.
+ * It is called out by send() for each email sent.
+ *
+ * Value can be any php callable: http://www.php.net/is_callable
+ *
+ * Parameters:
+ * boolean $result result of the send action
+ * array $to email addresses of the recipients
+ * array $cc cc email addresses
+ * array $bcc bcc email addresses
+ * string $subject the subject
+ * string $body the email body
+ * string $from email address of sender
+ * @var string
+ */
+ public $action_function = '';
+
+ /**
+ * What to put in the X-Mailer header.
+ * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
+ * @var string
+ */
+ public $XMailer = '';
+
+ /**
+ * Which validator to use by default when validating email addresses.
+ * May be a callable to inject your own validator, but there are several built-in validators.
+ * @see PHPMailer::validateAddress()
+ * @var string|callable
+ * @static
+ */
+ public static $validator = 'auto';
+
+ /**
+ * An instance of the SMTP sender class.
+ * @var SMTP
+ * @access protected
+ */
+ protected $smtp = null;
+
+ /**
+ * The array of 'to' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $to = array();
+
+ /**
+ * The array of 'cc' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $cc = array();
+
+ /**
+ * The array of 'bcc' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $bcc = array();
+
+ /**
+ * The array of reply-to names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $ReplyTo = array();
+
+ /**
+ * An array of all kinds of addresses.
+ * Includes all of $to, $cc, $bcc
+ * @var array
+ * @access protected
+ * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
+ */
+ protected $all_recipients = array();
+
+ /**
+ * An array of names and addresses queued for validation.
+ * In send(), valid and non duplicate entries are moved to $all_recipients
+ * and one of $to, $cc, or $bcc.
+ * This array is used only for addresses with IDN.
+ * @var array
+ * @access protected
+ * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
+ * @see PHPMailer::$all_recipients
+ */
+ protected $RecipientsQueue = array();
+
+ /**
+ * An array of reply-to names and addresses queued for validation.
+ * In send(), valid and non duplicate entries are moved to $ReplyTo.
+ * This array is used only for addresses with IDN.
+ * @var array
+ * @access protected
+ * @see PHPMailer::$ReplyTo
+ */
+ protected $ReplyToQueue = array();
+
+ /**
+ * The array of attachments.
+ * @var array
+ * @access protected
+ */
+ protected $attachment = array();
+
+ /**
+ * The array of custom headers.
+ * @var array
+ * @access protected
+ */
+ protected $CustomHeader = array();
+
+ /**
+ * The most recent Message-ID (including angular brackets).
+ * @var string
+ * @access protected
+ */
+ protected $lastMessageID = '';
+
+ /**
+ * The message's MIME type.
+ * @var string
+ * @access protected
+ */
+ protected $message_type = '';
+
+ /**
+ * The array of MIME boundary strings.
+ * @var array
+ * @access protected
+ */
+ protected $boundary = array();
+
+ /**
+ * The array of available languages.
+ * @var array
+ * @access protected
+ */
+ protected $language = array();
+
+ /**
+ * The number of errors encountered.
+ * @var integer
+ * @access protected
+ */
+ protected $error_count = 0;
+
+ /**
+ * The S/MIME certificate file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_cert_file = '';
+
+ /**
+ * The S/MIME key file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_key_file = '';
+
+ /**
+ * The optional S/MIME extra certificates ("CA Chain") file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_extracerts_file = '';
+
+ /**
+ * The S/MIME password for the key.
+ * Used only if the key is encrypted.
+ * @var string
+ * @access protected
+ */
+ protected $sign_key_pass = '';
+
+ /**
+ * Whether to throw exceptions for errors.
+ * @var boolean
+ * @access protected
+ */
+ protected $exceptions = false;
+
+ /**
+ * Unique ID used for message ID and boundaries.
+ * @var string
+ * @access protected
+ */
+ protected $uniqueid = '';
+
+ /**
+ * Error severity: message only, continue processing.
+ */
+ const STOP_MESSAGE = 0;
+
+ /**
+ * Error severity: message, likely ok to continue processing.
+ */
+ const STOP_CONTINUE = 1;
+
+ /**
+ * Error severity: message, plus full stop, critical error reached.
+ */
+ const STOP_CRITICAL = 2;
+
+ /**
+ * SMTP RFC standard line ending.
+ */
+ const CRLF = "\r\n";
+
+ /**
+ * The maximum line length allowed by RFC 2822 section 2.1.1
+ * @var integer
+ */
+ const MAX_LINE_LENGTH = 998;
+
+ /**
+ * Constructor.
+ * @param boolean $exceptions Should we throw external exceptions?
+ */
+ public function __construct($exceptions = null)
+ {
+ if ($exceptions !== null) {
+ $this->exceptions = (boolean)$exceptions;
+ }
+ //Pick an appropriate debug output format automatically
+ $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html');
+ }
+
+ /**
+ * Destructor.
+ */
+ public function __destruct()
+ {
+ //Close any open SMTP connection nicely
+ $this->smtpClose();
+ }
+
+ /**
+ * Call mail() in a safe_mode-aware fashion.
+ * Also, unless sendmail_path points to sendmail (or something that
+ * claims to be sendmail), don't pass params (not a perfect fix,
+ * but it will do)
+ * @param string $to To
+ * @param string $subject Subject
+ * @param string $body Message Body
+ * @param string $header Additional Header(s)
+ * @param string $params Params
+ * @access private
+ * @return boolean
+ */
+ private function mailPassthru($to, $subject, $body, $header, $params)
+ {
+ //Check overloading of mail function to avoid double-encoding
+ if (ini_get('mbstring.func_overload') & 1) {
+ $subject = $this->secureHeader($subject);
+ } else {
+ $subject = $this->encodeHeader($this->secureHeader($subject));
+ }
+
+ //Can't use additional_parameters in safe_mode, calling mail() with null params breaks
+ //@link http://php.net/manual/en/function.mail.php
+ if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
+ $result = @mail($to, $subject, $body, $header);
+ } else {
+ $result = @mail($to, $subject, $body, $header, $params);
+ }
+ return $result;
+ }
+ /**
+ * Output debugging info via user-defined method.
+ * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
+ * @see PHPMailer::$Debugoutput
+ * @see PHPMailer::$SMTPDebug
+ * @param string $str
+ */
+ protected function edebug($str)
+ {
+ if ($this->SMTPDebug <= 0) {
+ return;
+ }
+ //Avoid clash with built-in function names
+ if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
+ call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
+ return;
+ }
+ switch ($this->Debugoutput) {
+ case 'error_log':
+ //Don't output, just log
+ error_log($str);
+ break;
+ case 'html':
+ //Cleans up output a bit for a better looking, HTML-safe output
+ echo htmlentities(
+ preg_replace('/[\r\n]+/', '', $str),
+ ENT_QUOTES,
+ 'UTF-8'
+ )
+ . "
and must not be empty
+ * will look for an image file in $basedir/images/a.png and convert it to inline.
+ * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
+ * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
+ * @access public
+ * @param string $message HTML message string
+ * @param string $basedir Absolute path to a base directory to prepend to relative paths to images
+ * @param boolean|callable $advanced Whether to use the internal HTML to text converter
+ * or your own custom converter @see PHPMailer::html2text()
+ * @return string $message The transformed message Body
+ */
+ public function msgHTML($message, $basedir = '', $advanced = false)
+ {
+ preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
+ if (array_key_exists(2, $images)) {
+ if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
+ // Ensure $basedir has a trailing /
+ $basedir .= '/';
+ }
+ foreach ($images[2] as $imgindex => $url) {
+ // Convert data URIs into embedded images
+ if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
+ $data = substr($url, strpos($url, ','));
+ if ($match[2]) {
+ $data = base64_decode($data);
+ } else {
+ $data = rawurldecode($data);
+ }
+ $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
+ if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
+ $message = str_replace(
+ $images[0][$imgindex],
+ $images[1][$imgindex] . '="cid:' . $cid . '"',
+ $message
+ );
+ }
+ continue;
+ }
+ if (
+ // Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
+ !empty($basedir)
+ // Ignore URLs containing parent dir traversal (..)
+ && (strpos($url, '..') === false)
+ // Do not change urls that are already inline images
+ && substr($url, 0, 4) !== 'cid:'
+ // Do not change absolute URLs, including anonymous protocol
+ && !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
+ ) {
+ $filename = basename($url);
+ $directory = dirname($url);
+ if ($directory == '.') {
+ $directory = '';
+ }
+ $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
+ if (strlen($directory) > 1 && substr($directory, -1) != '/') {
+ $directory .= '/';
+ }
+ if ($this->addEmbeddedImage(
+ $basedir . $directory . $filename,
+ $cid,
+ $filename,
+ 'base64',
+ self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
+ )
+ ) {
+ $message = preg_replace(
+ '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
+ $images[1][$imgindex] . '="cid:' . $cid . '"',
+ $message
+ );
+ }
+ }
+ }
+ }
+ $this->isHTML(true);
+ // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
+ $this->Body = $this->normalizeBreaks($message);
+ $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
+ if (!$this->alternativeExists()) {
+ $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
+ self::CRLF . self::CRLF;
+ }
+ return $this->Body;
+ }
+
+ /**
+ * Convert an HTML string into plain text.
+ * This is used by msgHTML().
+ * Note - older versions of this function used a bundled advanced converter
+ * which was been removed for license reasons in #232.
+ * Example usage:
+ *
+ * // Use default conversion
+ * $plain = $mail->html2text($html);
+ * // Use your own custom converter
+ * $plain = $mail->html2text($html, function($html) {
+ * $converter = new MyHtml2text($html);
+ * return $converter->get_text();
+ * });
+ *
+ * @param string $html The HTML text to convert
+ * @param boolean|callable $advanced Any boolean value to use the internal converter,
+ * or provide your own callable for custom conversion.
+ * @return string
+ */
+ public function html2text($html, $advanced = false)
+ {
+ if (is_callable($advanced)) {
+ return call_user_func($advanced, $html);
+ }
+ return html_entity_decode(
+ trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
+ ENT_QUOTES,
+ $this->CharSet
+ );
+ }
+
+ /**
+ * Get the MIME type for a file extension.
+ * @param string $ext File extension
+ * @access public
+ * @return string MIME type of file.
+ * @static
+ */
+ public static function _mime_types($ext = '')
+ {
+ $mimes = array(
+ 'xl' => 'application/excel',
+ 'js' => 'application/javascript',
+ 'hqx' => 'application/mac-binhex40',
+ 'cpt' => 'application/mac-compactpro',
+ 'bin' => 'application/macbinary',
+ 'doc' => 'application/msword',
+ 'word' => 'application/msword',
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+ 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+ 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
+ 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+ 'class' => 'application/octet-stream',
+ 'dll' => 'application/octet-stream',
+ 'dms' => 'application/octet-stream',
+ 'exe' => 'application/octet-stream',
+ 'lha' => 'application/octet-stream',
+ 'lzh' => 'application/octet-stream',
+ 'psd' => 'application/octet-stream',
+ 'sea' => 'application/octet-stream',
+ 'so' => 'application/octet-stream',
+ 'oda' => 'application/oda',
+ 'pdf' => 'application/pdf',
+ 'ai' => 'application/postscript',
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'mif' => 'application/vnd.mif',
+ 'xls' => 'application/vnd.ms-excel',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'wbxml' => 'application/vnd.wap.wbxml',
+ 'wmlc' => 'application/vnd.wap.wmlc',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dxr' => 'application/x-director',
+ 'dvi' => 'application/x-dvi',
+ 'gtar' => 'application/x-gtar',
+ 'php3' => 'application/x-httpd-php',
+ 'php4' => 'application/x-httpd-php',
+ 'php' => 'application/x-httpd-php',
+ 'phtml' => 'application/x-httpd-php',
+ 'phps' => 'application/x-httpd-php-source',
+ 'swf' => 'application/x-shockwave-flash',
+ 'sit' => 'application/x-stuffit',
+ 'tar' => 'application/x-tar',
+ 'tgz' => 'application/x-tar',
+ 'xht' => 'application/xhtml+xml',
+ 'xhtml' => 'application/xhtml+xml',
+ 'zip' => 'application/zip',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'mpga' => 'audio/mpeg',
+ 'aif' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'ra' => 'audio/x-realaudio',
+ 'wav' => 'audio/x-wav',
+ 'bmp' => 'image/bmp',
+ 'gif' => 'image/gif',
+ 'jpeg' => 'image/jpeg',
+ 'jpe' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'eml' => 'message/rfc822',
+ 'css' => 'text/css',
+ 'html' => 'text/html',
+ 'htm' => 'text/html',
+ 'shtml' => 'text/html',
+ 'log' => 'text/plain',
+ 'text' => 'text/plain',
+ 'txt' => 'text/plain',
+ 'rtx' => 'text/richtext',
+ 'rtf' => 'text/rtf',
+ 'vcf' => 'text/vcard',
+ 'vcard' => 'text/vcard',
+ 'xml' => 'text/xml',
+ 'xsl' => 'text/xml',
+ 'mpeg' => 'video/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mov' => 'video/quicktime',
+ 'qt' => 'video/quicktime',
+ 'rv' => 'video/vnd.rn-realvideo',
+ 'avi' => 'video/x-msvideo',
+ 'movie' => 'video/x-sgi-movie'
+ );
+ if (array_key_exists(strtolower($ext), $mimes)) {
+ return $mimes[strtolower($ext)];
+ }
+ return 'application/octet-stream';
+ }
+
+ /**
+ * Map a file name to a MIME type.
+ * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
+ * @param string $filename A file name or full path, does not need to exist as a file
+ * @return string
+ * @static
+ */
+ public static function filenameToType($filename)
+ {
+ // In case the path is a URL, strip any query string before getting extension
+ $qpos = strpos($filename, '?');
+ if (false !== $qpos) {
+ $filename = substr($filename, 0, $qpos);
+ }
+ $pathinfo = self::mb_pathinfo($filename);
+ return self::_mime_types($pathinfo['extension']);
+ }
+
+ /**
+ * Multi-byte-safe pathinfo replacement.
+ * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
+ * Works similarly to the one in PHP >= 5.2.0
+ * @link http://www.php.net/manual/en/function.pathinfo.php#107461
+ * @param string $path A filename or path, does not need to exist as a file
+ * @param integer|string $options Either a PATHINFO_* constant,
+ * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
+ * @return string|array
+ * @static
+ */
+ public static function mb_pathinfo($path, $options = null)
+ {
+ $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
+ $pathinfo = array();
+ if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
+ if (array_key_exists(1, $pathinfo)) {
+ $ret['dirname'] = $pathinfo[1];
+ }
+ if (array_key_exists(2, $pathinfo)) {
+ $ret['basename'] = $pathinfo[2];
+ }
+ if (array_key_exists(5, $pathinfo)) {
+ $ret['extension'] = $pathinfo[5];
+ }
+ if (array_key_exists(3, $pathinfo)) {
+ $ret['filename'] = $pathinfo[3];
+ }
+ }
+ switch ($options) {
+ case PATHINFO_DIRNAME:
+ case 'dirname':
+ return $ret['dirname'];
+ case PATHINFO_BASENAME:
+ case 'basename':
+ return $ret['basename'];
+ case PATHINFO_EXTENSION:
+ case 'extension':
+ return $ret['extension'];
+ case PATHINFO_FILENAME:
+ case 'filename':
+ return $ret['filename'];
+ default:
+ return $ret;
+ }
+ }
+
+ /**
+ * Set or reset instance properties.
+ * You should avoid this function - it's more verbose, less efficient, more error-prone and
+ * harder to debug than setting properties directly.
+ * Usage Example:
+ * `$mail->set('SMTPSecure', 'tls');`
+ * is the same as:
+ * `$mail->SMTPSecure = 'tls';`
+ * @access public
+ * @param string $name The property name to set
+ * @param mixed $value The value to set the property to
+ * @return boolean
+ * @TODO Should this not be using the __set() magic function?
+ */
+ public function set($name, $value = '')
+ {
+ if (property_exists($this, $name)) {
+ $this->$name = $value;
+ return true;
+ } else {
+ $this->setError($this->lang('variable_set') . $name);
+ return false;
+ }
+ }
+
+ /**
+ * Strip newlines to prevent header injection.
+ * @access public
+ * @param string $str
+ * @return string
+ */
+ public function secureHeader($str)
+ {
+ return trim(str_replace(array("\r", "\n"), '', $str));
+ }
+
+ /**
+ * Normalize line breaks in a string.
+ * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
+ * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
+ * @param string $text
+ * @param string $breaktype What kind of line break to use, defaults to CRLF
+ * @return string
+ * @access public
+ * @static
+ */
+ public static function normalizeBreaks($text, $breaktype = "\r\n")
+ {
+ return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
+ }
+
+ /**
+ * Set the public and private key files and password for S/MIME signing.
+ * @access public
+ * @param string $cert_filename
+ * @param string $key_filename
+ * @param string $key_pass Password for private key
+ * @param string $extracerts_filename Optional path to chain certificate
+ */
+ public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
+ {
+ $this->sign_cert_file = $cert_filename;
+ $this->sign_key_file = $key_filename;
+ $this->sign_key_pass = $key_pass;
+ $this->sign_extracerts_file = $extracerts_filename;
+ }
+
+ /**
+ * Quoted-Printable-encode a DKIM header.
+ * @access public
+ * @param string $txt
+ * @return string
+ */
+ public function DKIM_QP($txt)
+ {
+ $line = '';
+ for ($i = 0; $i < strlen($txt); $i++) {
+ $ord = ord($txt[$i]);
+ if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
+ $line .= $txt[$i];
+ } else {
+ $line .= '=' . sprintf('%02X', $ord);
+ }
+ }
+ return $line;
+ }
+
+ /**
+ * Generate a DKIM signature.
+ * @access public
+ * @param string $signHeader
+ * @throws phpmailerException
+ * @return string The DKIM signature value
+ */
+ public function DKIM_Sign($signHeader)
+ {
+ if (!defined('PKCS7_TEXT')) {
+ if ($this->exceptions) {
+ throw new phpmailerException($this->lang('extension_missing') . 'openssl');
+ }
+ return '';
+ }
+ $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
+ if ('' != $this->DKIM_passphrase) {
+ $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
+ } else {
+ $privKey = openssl_pkey_get_private($privKeyStr);
+ }
+ //Workaround for missing digest algorithms in old PHP & OpenSSL versions
+ //@link http://stackoverflow.com/a/11117338/333340
+ if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
+ in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
+ if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
+ openssl_pkey_free($privKey);
+ return base64_encode($signature);
+ }
+ } else {
+ $pinfo = openssl_pkey_get_details($privKey);
+ $hash = hash('sha256', $signHeader);
+ //'Magic' constant for SHA256 from RFC3447
+ //@link https://tools.ietf.org/html/rfc3447#page-43
+ $t = '3031300d060960864801650304020105000420' . $hash;
+ $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
+ $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t);
+
+ if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
+ openssl_pkey_free($privKey);
+ return base64_encode($signature);
+ }
+ }
+ openssl_pkey_free($privKey);
+ return '';
+ }
+
+ /**
+ * Generate a DKIM canonicalization header.
+ * @access public
+ * @param string $signHeader Header
+ * @return string
+ */
+ public function DKIM_HeaderC($signHeader)
+ {
+ $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
+ $lines = explode("\r\n", $signHeader);
+ foreach ($lines as $key => $line) {
+ list($heading, $value) = explode(':', $line, 2);
+ $heading = strtolower($heading);
+ $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
+ $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
+ }
+ $signHeader = implode("\r\n", $lines);
+ return $signHeader;
+ }
+
+ /**
+ * Generate a DKIM canonicalization body.
+ * @access public
+ * @param string $body Message Body
+ * @return string
+ */
+ public function DKIM_BodyC($body)
+ {
+ if ($body == '') {
+ return "\r\n";
+ }
+ // stabilize line endings
+ $body = str_replace("\r\n", "\n", $body);
+ $body = str_replace("\n", "\r\n", $body);
+ // END stabilize line endings
+ while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
+ $body = substr($body, 0, strlen($body) - 2);
+ }
+ return $body;
+ }
+
+ /**
+ * Create the DKIM header and body in a new message header.
+ * @access public
+ * @param string $headers_line Header lines
+ * @param string $subject Subject
+ * @param string $body Body
+ * @return string
+ */
+ public function DKIM_Add($headers_line, $subject, $body)
+ {
+ $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
+ $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
+ $DKIMquery = 'dns/txt'; // Query method
+ $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
+ $subject_header = "Subject: $subject";
+ $headers = explode($this->LE, $headers_line);
+ $from_header = '';
+ $to_header = '';
+ $date_header = '';
+ $current = '';
+ foreach ($headers as $header) {
+ if (strpos($header, 'From:') === 0) {
+ $from_header = $header;
+ $current = 'from_header';
+ } elseif (strpos($header, 'To:') === 0) {
+ $to_header = $header;
+ $current = 'to_header';
+ } elseif (strpos($header, 'Date:') === 0) {
+ $date_header = $header;
+ $current = 'date_header';
+ } else {
+ if (!empty($$current) && strpos($header, ' =?') === 0) {
+ $$current .= $header;
+ } else {
+ $current = '';
+ }
+ }
+ }
+ $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
+ $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
+ $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
+ $subject = str_replace(
+ '|',
+ '=7C',
+ $this->DKIM_QP($subject_header)
+ ); // Copied header fields (dkim-quoted-printable)
+ $body = $this->DKIM_BodyC($body);
+ $DKIMlen = strlen($body); // Length of body
+ $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
+ if ('' == $this->DKIM_identity) {
+ $ident = '';
+ } else {
+ $ident = ' i=' . $this->DKIM_identity . ';';
+ }
+ $dkimhdrs = 'DKIM-Signature: v=1; a=' .
+ $DKIMsignatureType . '; q=' .
+ $DKIMquery . '; l=' .
+ $DKIMlen . '; s=' .
+ $this->DKIM_selector .
+ ";\r\n" .
+ "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
+ "\th=From:To:Date:Subject;\r\n" .
+ "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
+ "\tz=$from\r\n" .
+ "\t|$to\r\n" .
+ "\t|$date\r\n" .
+ "\t|$subject;\r\n" .
+ "\tbh=" . $DKIMb64 . ";\r\n" .
+ "\tb=";
+ $toSign = $this->DKIM_HeaderC(
+ $from_header . "\r\n" .
+ $to_header . "\r\n" .
+ $date_header . "\r\n" .
+ $subject_header . "\r\n" .
+ $dkimhdrs
+ );
+ $signed = $this->DKIM_Sign($toSign);
+ return $dkimhdrs . $signed . "\r\n";
+ }
+
+ /**
+ * Detect if a string contains a line longer than the maximum line length allowed.
+ * @param string $str
+ * @return boolean
+ * @static
+ */
+ public static function hasLineLongerThanMax($str)
+ {
+ //+2 to include CRLF line break for a 1000 total
+ return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
+ }
+
+ /**
+ * Allows for public read access to 'to' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getToAddresses()
+ {
+ return $this->to;
+ }
+
+ /**
+ * Allows for public read access to 'cc' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getCcAddresses()
+ {
+ return $this->cc;
+ }
+
+ /**
+ * Allows for public read access to 'bcc' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getBccAddresses()
+ {
+ return $this->bcc;
+ }
+
+ /**
+ * Allows for public read access to 'ReplyTo' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getReplyToAddresses()
+ {
+ return $this->ReplyTo;
+ }
+
+ /**
+ * Allows for public read access to 'all_recipients' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getAllRecipientAddresses()
+ {
+ return $this->all_recipients;
+ }
+
+ /**
+ * Perform a callback.
+ * @param boolean $isSent
+ * @param array $to
+ * @param array $cc
+ * @param array $bcc
+ * @param string $subject
+ * @param string $body
+ * @param string $from
+ */
+ protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
+ {
+ if (!empty($this->action_function) && is_callable($this->action_function)) {
+ $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
+ call_user_func_array($this->action_function, $params);
+ }
+ }
}
/**
@@ -3912,13 +4032,13 @@ class PHPMailer
*/
class phpmailerException extends Exception
{
- /**
- * Prettify error message output
- * @return string
- */
- public function errorMessage()
- {
- $errorMsg = '' . $this->getMessage() . "
- * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
- *
- * @var string|callable
- */
- public $Debugoutput = 'echo';
-
- /**
- * Whether to use VERP.
- * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
- * @link http://www.postfix.org/VERP_README.html Info on VERP
- * @var boolean
- */
- public $do_verp = false;
-
- /**
- * The timeout value for connection, in seconds.
- * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
- * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
- * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
- * @var integer
- */
- public $Timeout = 300;
-
- /**
- * How long to wait for commands to complete, in seconds.
- * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
- * @var integer
- */
- public $Timelimit = 300;
-
- /**
- * The socket for the server connection.
- * @var resource
- */
- protected $smtp_conn;
-
- /**
- * Error information, if any, for the last SMTP command.
- * @var array
- */
- protected $error = array(
- 'error' => '',
- 'detail' => '',
- 'smtp_code' => '',
- 'smtp_code_ex' => ''
- );
-
- /**
- * The reply the server sent to us for HELO.
- * If null, no HELO string has yet been received.
- * @var string|null
- */
- protected $helo_rply = null;
-
- /**
- * The set of SMTP extensions sent in reply to EHLO command.
- * Indexes of the array are extension names.
- * Value at index 'HELO' or 'EHLO' (according to command that was sent)
- * represents the server name. In case of HELO it is the only element of the array.
- * Other values can be boolean TRUE or an array containing extension options.
- * If null, no HELO/EHLO string has yet been received.
- * @var array|null
- */
- protected $server_caps = null;
-
- /**
- * The most recent reply received from the server.
- * @var string
- */
- protected $last_reply = '';
-
- /**
- * Output debugging info via a user-selected method.
- * @see SMTP::$Debugoutput
- * @see SMTP::$do_debug
- * @param string $str Debug string to output
- * @param integer $level The debug level of this message; see DEBUG_* constants
- * @return void
- */
- protected function edebug($str, $level = 0)
- {
- if ($level > $this->do_debug) {
- return;
- }
- //Avoid clash with built-in function names
- if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
- call_user_func($this->Debugoutput, $str, $this->do_debug);
- return;
- }
- switch ($this->Debugoutput) {
- case 'error_log':
- //Don't output, just log
- error_log($str);
- break;
- case 'html':
- //Cleans up output a bit for a better looking, HTML-safe output
- echo htmlentities(
- preg_replace('/[\r\n]+/', '', $str),
- ENT_QUOTES,
- 'UTF-8'
- )
- . "
+ * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
+ *
+ * @var string|callable
+ */
+ public $Debugoutput = 'echo';
+
+ /**
+ * Whether to use VERP.
+ * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link http://www.postfix.org/VERP_README.html Info on VERP
+ * @var boolean
+ */
+ public $do_verp = false;
+
+ /**
+ * The timeout value for connection, in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
+ * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
+ * @var integer
+ */
+ public $Timeout = 300;
+
+ /**
+ * How long to wait for commands to complete, in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * @var integer
+ */
+ public $Timelimit = 300;
+
+ /**
+ * @var array Patterns to extract an SMTP transaction id from reply to a DATA command.
+ * The first capture group in each regex will be used as the ID.
+ */
+ protected $smtp_transaction_id_patterns = array(
+ 'exim' => '/[0-9]{3} OK id=(.*)/',
+ 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/',
+ 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/'
+ );
+
+ /**
+ * @var string The last transaction ID issued in response to a DATA command,
+ * if one was detected
+ */
+ protected $last_smtp_transaction_id;
+
+ /**
+ * The socket for the server connection.
+ * @var resource
+ */
+ protected $smtp_conn;
+
+ /**
+ * Error information, if any, for the last SMTP command.
+ * @var array
+ */
+ protected $error = array(
+ 'error' => '',
+ 'detail' => '',
+ 'smtp_code' => '',
+ 'smtp_code_ex' => ''
+ );
+
+ /**
+ * The reply the server sent to us for HELO.
+ * If null, no HELO string has yet been received.
+ * @var string|null
+ */
+ protected $helo_rply = null;
+
+ /**
+ * The set of SMTP extensions sent in reply to EHLO command.
+ * Indexes of the array are extension names.
+ * Value at index 'HELO' or 'EHLO' (according to command that was sent)
+ * represents the server name. In case of HELO it is the only element of the array.
+ * Other values can be boolean TRUE or an array containing extension options.
+ * If null, no HELO/EHLO string has yet been received.
+ * @var array|null
+ */
+ protected $server_caps = null;
+
+ /**
+ * The most recent reply received from the server.
+ * @var string
+ */
+ protected $last_reply = '';
+
+ /**
+ * Output debugging info via a user-selected method.
+ * @see SMTP::$Debugoutput
+ * @see SMTP::$do_debug
+ * @param string $str Debug string to output
+ * @param integer $level The debug level of this message; see DEBUG_* constants
+ * @return void
+ */
+ protected function edebug($str, $level = 0)
+ {
+ if ($level > $this->do_debug) {
+ return;
+ }
+ //Avoid clash with built-in function names
+ if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
+ call_user_func($this->Debugoutput, $str, $level);
+ return;
+ }
+ switch ($this->Debugoutput) {
+ case 'error_log':
+ //Don't output, just log
+ error_log($str);
+ break;
+ case 'html':
+ //Cleans up output a bit for a better looking, HTML-safe output
+ echo gmdate('Y-m-d H:i:s') . ' ' . htmlentities(
+ preg_replace('/[\r\n]+/', '', $str),
+ ENT_QUOTES,
+ 'UTF-8'
+ ) . "Reference #179.d8be1402.1458059103.3613c4db
if (! is_array($response)) {
- throw new RuntimeException("Invalid response from LE for domain $domain. Whole response: " . $response);
+ throw new RuntimeException("Invalid response from LE for domain $domain. Whole response: " . json_encode($response));
}
if (! array_key_exists('challenges', $response)) {
@@ -188,17 +214,18 @@ class lescript
$this->log("Token for $domain saved at $tokenPath and should be available at $uri");
// simple self check
- $selfcheckContextOptions = array('http' => array('header' => "User Agent: Froxlor/".$this->version));
- $selfcheckContext = stream_context_create($selfcheckContextOptions);
- if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) {
- $errmsg = json_encode(error_get_last());
- if ($errmsg != "null") {
- $errmsg = "; PHP error: " . $errmsg;
- } else {
- $errmsg = "";
+ if (Settings::Get('system.disable_le_selfcheck') == '0')
+ {
+ $selfcheckpayload = HttpClient::urlGet($uri);
+ if ($payload !== trim($selfcheckpayload)) {
+ $errmsg = json_encode(error_get_last());
+ if ($errmsg != "null") {
+ $errmsg = "; PHP error: " . $errmsg;
+ } else {
+ $errmsg = "";
+ }
+ $this->logger->logAction(CRON_ACTION, LOG_WARNING, "[Lets Encrypt self-check] Please check $uri - token seems to be not available. This is just a simple self-check, it might be wrong but consider using this information when Let's Encrypt fails to issue a certificate" . $errmsg);
}
- @unlink($tokenPath);
- $this->logger->logAction(CRON_ACTION, LOG_ERR, "letsencrypt Please check $uri - token not available" . $errmsg);
}
$this->log("Sending request to challenge");
@@ -309,6 +336,21 @@ class lescript
);
}
+ private function setLeRegisteredState($state)
+ {
+ if ($this->isLeProduction) {
+ if ($this->isFroxlorVhost) {
+ Settings::Set('system.leregistered', $state);
+ } else {
+ $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `leregistered` = :registered " . "WHERE `customerid` = :customerid;");
+ Database::pexecute($upd_stmt, array(
+ 'registered' => $state,
+ 'customerid' => $this->customerId
+ ));
+ }
+ }
+ }
+
private function parsePemFromBody($body)
{
$pem = chunk_split(base64_encode($body), 64, "\n");
@@ -317,21 +359,16 @@ class lescript
private function postNewReg()
{
+ $this->log('Getting last terms of service URL');
+ $directory = $this->client->get('/directory');
+ if (!isset($directory['meta']) || !isset($directory['meta']['terms-of-service'])) {
+ throw new \RuntimeException("No terms of service link available!");
+ }
$this->log('Sending registration to letsencrypt server');
return $this->signedRequest('/acme/new-reg', array(
'resource' => 'new-reg',
- 'agreement' => $this->license
- ));
- }
-
- private function postRegAgreement($uri)
- {
- $this->log('Accepting agreement at URL: ' . $this->license);
-
- return $this->signedRequest($uri, array(
- 'resource' => 'reg',
- 'agreement' => $this->license
+ 'agreement' => $directory['meta']['terms-of-service']
));
}
@@ -536,13 +573,6 @@ class Client
preg_match_all('~Link: <(.+)>;rel="up"~', $this->lastHeader, $matches);
return $matches[1];
}
-
- public function getAgreementURL()
- {
- preg_match_all('~Link: <(.+)>;rel="terms-of-service"~', $this->lastHeader, $matches);
- return $matches[1][0];
- }
-
}
class Base64UrlSafeEncoder
diff --git a/lib/classes/ssl/class.lescript_v2.php b/lib/classes/ssl/class.lescript_v2.php
new file mode 100644
index 00000000..448222eb
--- /dev/null
+++ b/lib/classes/ssl/class.lescript_v2.php
@@ -0,0 +1,594 @@
+
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Reference #179.d8be1402.1458059103.3613c4db
+ if (! is_array($response)) {
+ throw new RuntimeException("Invalid response from LE for domain $domain. Whole response: " . json_encode($response));
+ }
+
+ if (! array_key_exists('authorizations', $response)) {
+ throw new RuntimeException("No authorizations received for $domain. Whole response: " . json_encode($response));
+ }
+
+ // get authorization
+ $auth_response = $this->client->get($response['authorizations'][0]);
+
+ if (! array_key_exists('challenges', $auth_response)) {
+ throw new RuntimeException("No challenges received for $domain. Whole response: " . json_encode($auth_response));
+ }
+
+ // choose http-01 challenge only
+ $challenge = array_reduce($auth_response['challenges'], function ($v, $w) {
+ return $v ? $v : ($w['type'] == 'http-01' ? $w : false);
+ });
+
+ if (! $challenge) {
+ throw new RuntimeException("HTTP Challenge for $domain is not available. Whole response: " . json_encode($response));
+ }
+
+ $this->log("Got challenge token for $domain");
+ $location = $challenge['url'];
+ $finalizeLink = $response['finalize'];
+
+ // 2. saving authentication token for web verification
+ // ---------------------------------------------------
+
+ $directory = Settings::Get('system.letsencryptchallengepath') . '/.well-known/acme-challenge';
+ $tokenPath = $directory . '/' . $challenge['token'];
+
+ if (! file_exists($directory) && ! @mkdir($directory, 0755, true)) {
+ throw new \RuntimeException("Couldn't create directory to expose challenge: ${tokenPath}");
+ }
+
+ $header = array(
+ // need to be in precise order!
+ "e" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["e"]),
+ "kty" => "RSA",
+ "n" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["n"])
+ );
+ $payload = $challenge['token'] . '.' . Base64UrlSafeEncoder::encode(hash('sha256', json_encode($header), true));
+
+ file_put_contents($tokenPath, $payload);
+ chmod($tokenPath, 0644);
+
+ // 3. verification process itself
+ // -------------------------------
+
+ $uri = "http://${domain}/.well-known/acme-challenge/${challenge['token']}";
+
+ $this->log("Token for $domain saved at $tokenPath and should be available at $uri");
+
+ // simple self check
+ if (Settings::Get('system.disable_le_selfcheck') == '0') {
+ $selfcheckpayload = HttpClient::urlGet($uri);
+ if ($payload !== trim($selfcheckpayload)) {
+ $errmsg = json_encode(error_get_last());
+ if ($errmsg != "null") {
+ $errmsg = "; PHP error: " . $errmsg;
+ } else {
+ $errmsg = "";
+ }
+ $this->logger->logAction(CRON_ACTION, LOG_WARNING, "[Lets Encrypt self-check] Please check $uri - token seems to be not available. This is just a simple self-check, it might be wrong but consider using this information when Let's Encrypt fails to issue a certificate" . $errmsg);
+ }
+ }
+
+ $this->log("Sending request to challenge");
+
+ // send request to challenge
+ $result = $this->signedRequest($challenge['url'], array(
+ "type" => "http-01",
+ "keyAuthorization" => $payload,
+ "token" => $challenge['token']
+ ), false);
+
+ // waiting loop
+ // we wait for a maximum of 30 seconds to avoid endless loops
+ $count = 0;
+ do {
+ if (empty($result['status']) || $result['status'] == "invalid") {
+ @unlink($tokenPath);
+ throw new \RuntimeException("Verification ended with error: " . json_encode($result));
+ }
+ $ended = ! ($result['status'] === "pending" || $result['status'] === "processing");
+
+ if (! $ended) {
+ $this->log("Verification " . $result['status'] . ", sleeping 1s");
+ sleep(1);
+ $count ++;
+ }
+
+ $result = $this->client->get($location);
+ } while (! $ended && $count < 30);
+
+ $this->log("Verification ended with status: ${result['status']}");
+ @unlink($tokenPath);
+ }
+
+ // requesting certificate
+ // ----------------------
+
+ // generate private key for domain if not exist
+ if (empty($domainkey) || Settings::Get('system.letsencryptreuseold') == 0) {
+ $keys = $this->generateKey();
+ $domainkey = $keys['private'];
+ }
+
+ // load domain key
+ $privateDomainKey = openssl_pkey_get_private($domainkey);
+
+ if (empty($csr)) {
+ $csr = $this->generateCSR($privateDomainKey, $domains);
+ }
+
+ // request certificates creation
+ $result = $this->signedRequest($finalizeLink, array(
+ 'csr' => $csr
+ ), false);
+ if ($this->client->getLastCode() !== 200) {
+ throw new \RuntimeException("Invalid response code: " . $this->client->getLastCode() . ", " . json_encode($result));
+ }
+ if (! isset($result['certificate'])) {
+ throw new \RuntimeException("No certificate URL specified in result");
+ }
+
+ $certificates = array();
+ $certdata = $this->client->get($result['certificate']);
+ $this->log("Got certificate! YAY!");
+ $certificates[] = $certdata;
+ foreach ($this->client->getLastLinks() as $link) {
+ $this->log("Requesting chained cert at $link");
+ $result = $this->client->get($link);
+ $certificates[] = $result;
+ }
+
+ if (empty($certificates))
+ throw new \RuntimeException('No certificates generated');
+
+ $fullchain = implode("\n", $certificates);
+ $crt = array_shift($certificates);
+ $chain = implode("\n", $certificates);
+
+ $this->log("Done, returning new certificates and key");
+ return array(
+ 'fullchain' => $fullchain,
+ 'crt' => $crt,
+ 'chain' => $chain,
+ 'key' => $domainkey,
+ 'csr' => $csr
+ );
+ }
+
+ private function setLeRegisteredState($state)
+ {
+ if ($this->isLeProduction) {
+ if ($this->isFroxlorVhost) {
+ Settings::Set('system.leregistered', $state);
+ } else {
+ $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `leregistered` = :registered " . "WHERE `customerid` = :customerid;");
+ Database::pexecute($upd_stmt, array(
+ 'registered' => $state,
+ 'customerid' => $this->customerId
+ ));
+ }
+ }
+ }
+
+ private function parsePemFromBody($body)
+ {
+ $pem = chunk_split(base64_encode($body), 64, "\n");
+ return "-----BEGIN CERTIFICATE-----\n" . $pem . "-----END CERTIFICATE-----\n";
+ }
+
+ private function postNewReg()
+ {
+ $this->log('Getting last terms of service URL');
+ $directory = $this->client->get('/directory');
+ if (! isset($directory['meta']) || ! isset($directory['meta']['termsOfService'])) {
+ throw new \RuntimeException("No terms of service link available!");
+ }
+ $this->log('Sending registration to letsencrypt server');
+
+ return $this->signedRequest($this->_req_uris['newAccount'], array(
+ 'termsOfServiceAgreed' => true
+ ));
+ }
+
+ private function generateCSR($privateKey, array $domains)
+ {
+ $domain = reset($domains);
+ $san = implode(",", array_map(function ($dns) {
+ return "DNS:" . $dns;
+ }, $domains));
+ $tmpConf = tmpfile();
+ $tmpConfMeta = stream_get_meta_data($tmpConf);
+ $tmpConfPath = $tmpConfMeta["uri"];
+
+ // workaround to get SAN working
+ fwrite($tmpConf, 'HOME = .
+RANDFILE = $ENV::HOME/.rnd
+[ req ]
+default_bits = ' . Settings::Get('system.letsencryptkeysize') . '
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+[ v3_req ]
+basicConstraints = CA:FALSE
+subjectAltName = ' . $san . '
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment');
+
+ $csr = openssl_csr_new(array(
+ "CN" => $domain,
+ "ST" => Settings::Get('system.letsencryptstate'),
+ "C" => Settings::Get('system.letsencryptcountrycode'),
+ "O" => "Unknown"
+ ), $privateKey, array(
+ "config" => $tmpConfPath,
+ "digest_alg" => "sha256"
+ ));
+
+ if (! $csr)
+ throw new \RuntimeException("CSR couldn't be generated! " . openssl_error_string());
+
+ openssl_csr_export($csr, $csr);
+ fclose($tmpConf);
+
+ preg_match('~REQUEST-----(.*)-----END~s', $csr, $matches);
+
+ return trim(Base64UrlSafeEncoder::encode(base64_decode($matches[1])));
+ }
+
+ private function generateKey()
+ {
+ $res = openssl_pkey_new(array(
+ "private_key_type" => OPENSSL_KEYTYPE_RSA,
+ "private_key_bits" => (int) Settings::Get('system.letsencryptkeysize')
+ ));
+
+ if (! openssl_pkey_export($res, $privateKey)) {
+ throw new \RuntimeException("Key export failed!");
+ }
+
+ $details = openssl_pkey_get_details($res);
+
+ return array(
+ 'private' => $privateKey,
+ 'public' => $details['key']
+ );
+ }
+
+ private function signedRequest($uri, array $payload, $needs_jwk = true)
+ {
+ $privateKey = openssl_pkey_get_private($this->accountKey);
+ $details = openssl_pkey_get_details($privateKey);
+
+ $header = array(
+ "alg" => "RS256"
+ );
+
+ if ($needs_jwk) {
+ $header["jwk"] = array(
+ "kty" => "RSA",
+ "n" => Base64UrlSafeEncoder::encode($details["rsa"]["n"]),
+ "e" => Base64UrlSafeEncoder::encode($details["rsa"]["e"])
+ );
+ } else {
+ // need account-url
+ $header["kid"] = $this->_acc_location;
+ }
+
+ $protected = $header;
+ $protected["nonce"] = $this->client->getLastNonce();
+ $protected["url"] = $uri;
+
+ $payload64 = Base64UrlSafeEncoder::encode(json_encode($payload, JSON_UNESCAPED_SLASHES));
+ $protected64 = Base64UrlSafeEncoder::encode(json_encode($protected));
+
+ openssl_sign($protected64 . '.' . $payload64, $signed, $privateKey, "SHA256");
+
+ $signed64 = Base64UrlSafeEncoder::encode($signed);
+
+ $data = array(
+ 'protected' => $protected64,
+ 'payload' => $payload64,
+ 'signature' => $signed64
+ );
+
+ $this->log("Sending signed request to $uri");
+ return $this->client->post($uri, json_encode($data));
+ }
+
+ protected function log($message)
+ {
+ $this->logger->logAction(CRON_ACTION, LOG_INFO, "letsencrypt-v2 " . $message);
+ }
+}
+
+class Client
+{
+
+ private $lastCode;
+
+ public $lastHeader;
+
+ private $base;
+
+ public function __construct($base)
+ {
+ $this->base = $base;
+ }
+
+ private function curl($method, $url, $data = null)
+ {
+ $headers = array(
+ 'Accept: application/json',
+ 'Content-Type: application/json'
+ );
+ $handle = curl_init();
+ curl_setopt($handle, CURLOPT_URL, preg_match('~^http~', $url) ? $url : $this->base . $url);
+ curl_setopt($handle, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($handle, CURLOPT_HEADER, true);
+
+ // DO NOT DO THAT!
+ // curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false);
+ // curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
+
+ switch ($method) {
+ case 'GET':
+ break;
+ case 'POST':
+ curl_setopt($handle, CURLOPT_POST, true);
+ curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
+ break;
+ }
+ $response = curl_exec($handle);
+
+ if (curl_errno($handle)) {
+ throw new \RuntimeException('Curl: ' . curl_error($handle));
+ }
+
+ $header_size = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
+
+ $header = substr($response, 0, $header_size);
+ $body = substr($response, $header_size);
+
+ $this->lastHeader = $header;
+ $this->lastCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
+
+ $data = json_decode($body, true);
+ return $data === null ? $body : $data;
+ }
+
+ public function post($url, $data)
+ {
+ return $this->curl('POST', $url, $data);
+ }
+
+ public function get($url)
+ {
+ return $this->curl('GET', $url);
+ }
+
+ public function getLastNonce()
+ {
+ if (preg_match('~Replay\-Nonce: (.+)~i', $this->lastHeader, $matches)) {
+ return trim($matches[1]);
+ }
+
+ $this->curl('GET', '/directory');
+ return $this->getLastNonce();
+ }
+
+ public function getLastLocation()
+ {
+ if (preg_match('~Location: (.+)~i', $this->lastHeader, $matches)) {
+ return trim($matches[1]);
+ }
+ return null;
+ }
+
+ public function getLastCode()
+ {
+ return $this->lastCode;
+ }
+
+ public function getLastLinks()
+ {
+ preg_match_all('~Link: <(.+)>;rel="up"~', $this->lastHeader, $matches);
+ return $matches[1];
+ }
+}
+
+class Base64UrlSafeEncoder
+{
+
+ public static function encode($input)
+ {
+ return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
+ }
+
+ public static function decode($input)
+ {
+ $remainder = strlen($input) % 4;
+ if ($remainder) {
+ $padlen = 4 - $remainder;
+ $input .= str_repeat('=', $padlen);
+ }
+ return base64_decode(strtr($input, '-_', '+/'));
+ }
+}
diff --git a/lib/classes/webserver/class.ConfigIO.php b/lib/classes/webserver/class.ConfigIO.php
index cbe79b10..14e95085 100644
--- a/lib/classes/webserver/class.ConfigIO.php
+++ b/lib/classes/webserver/class.ConfigIO.php
@@ -17,13 +17,14 @@
* @since 0.9.29
*
*/
-
-class ConfigIO {
+class ConfigIO
+{
/**
* constructor
*/
- public function __construct() {}
+ public function __construct()
+ {}
/**
* clean up former created configs, including (if enabled)
@@ -32,39 +33,40 @@ class ConfigIO {
*
* @return null
*/
- public function cleanUp() {
-
+ public function cleanUp()
+ {
+
// old error logs
$this->_cleanErrLogs();
-
+
// awstats files
$this->_cleanAwstatsFiles();
-
+
// fcgid files
$this->_cleanFcgidFiles();
-
+
// php-fpm files
$this->_cleanFpmFiles();
-
+
// clean webserver-configs
$this->_cleanWebserverConfigs();
-
+
// old htpasswd files
$this->_cleanHtpasswdFiles();
-
+
// customer-specified ssl-certificates
$this->_cleanCustomerSslCerts();
}
- private function _cleanErrLogs() {
-
- $err_dir = makeCorrectDir(FROXLOR_INSTALL_DIR."/logs/");
- if (@is_dir($err_dir)) {
- // now get rid of old stuff
- //(but append /*.log so we don't delete the directory)
- $err_dir.='/*.log';
- safe_exec('rm -rf '. makeCorrectFile($err_dir));
- }
+ private function _cleanErrLogs()
+ {
+ $err_dir = makeCorrectDir(FROXLOR_INSTALL_DIR . "/logs/");
+ if (@is_dir($err_dir)) {
+ // now get rid of old stuff
+ // (but append /*.log so we don't delete the directory)
+ $err_dir .= '/*.log';
+ safe_exec('rm -f ' . makeCorrectFile($err_dir));
+ }
}
/**
@@ -73,8 +75,9 @@ class ConfigIO {
*
* @return null
*/
- private function _cleanCustomerSslCerts() {
-
+ private function _cleanCustomerSslCerts()
+ {
+
/*
* only clean up if we're actually using SSL
*/
@@ -82,14 +85,14 @@ class ConfigIO {
// get correct directory
$configdir = $this->_getFile('system', 'customer_ssl_path');
if ($configdir !== false) {
-
+
$configdir = makeCorrectDir($configdir);
-
+
if (@is_dir($configdir)) {
// now get rid of old stuff
- //(but append /* so we don't delete the directory)
- $configdir.='/*';
- safe_exec('rm -rf '. makeCorrectFile($configdir));
+ // (but append /* so we don't delete the directory)
+ $configdir .= '/*';
+ safe_exec('rm -f ' . makeCorrectFile($configdir));
}
}
}
@@ -100,39 +103,38 @@ class ConfigIO {
*
* @return null
*/
- private function _cleanWebserverConfigs() {
-
+ private function _cleanWebserverConfigs()
+ {
+
// get directories
$configdirs = array();
$dir = $this->_getFile('system', 'apacheconf_vhost');
if ($dir !== false)
$configdirs[] = makeCorrectDir($dir);
-
+
$dir = $this->_getFile('system', 'apacheconf_diroptions');
if ($dir !== false)
$configdirs[] = makeCorrectDir($dir);
-
+
// file pattern
$pattern = "/^([0-9]){2}_(froxlor|syscp)_(.+)\.conf$/";
-
+
// check ALL the folders
foreach ($configdirs as $config_dir) {
-
+
// check directory
if (@is_dir($config_dir)) {
-
+
// create directory iterator
- $its = new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator($config_dir)
- );
-
+ $its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($config_dir));
+
// iterate through all subdirs,
// look for vhost/diroption files
// and delete them
- foreach ($its as $fullFileName => $it ) {
+ foreach ($its as $fullFileName => $it) {
if ($it->isFile() && preg_match($pattern, $it->getFilename())) {
// remove file
- safe_exec('rm -f '. escapeshellarg(makeCorrectFile($its->getPathname())));
+ safe_exec('rm -f ' . escapeshellarg(makeCorrectFile($its->getPathname())));
}
}
}
@@ -144,19 +146,20 @@ class ConfigIO {
*
* @return null
*/
- private function _cleanHtpasswdFiles() {
-
+ private function _cleanHtpasswdFiles()
+ {
+
// get correct directory
$configdir = $this->_getFile('system', 'apacheconf_htpasswddir');
-
+
if ($configdir !== false) {
$configdir = makeCorrectDir($configdir);
-
+
if (@is_dir($configdir)) {
// now get rid of old stuff
- //(but append /* so we don't delete the directory)
- $configdir.='/*';
- safe_exec('rm -rf '. makeCorrectFile($configdir));
+ // (but append /* so we don't delete the directory)
+ $configdir .= '/*';
+ safe_exec('rm -f ' . makeCorrectFile($configdir));
}
}
}
@@ -166,37 +169,36 @@ class ConfigIO {
*
* @return null
*/
- private function _cleanAwstatsFiles() {
-
+ private function _cleanAwstatsFiles()
+ {
if (Settings::Get('system.awstats_enabled') == '0') {
return;
}
-
- //dhr: cleanout froxlor-generated awstats configs prior to re-creation
+
+ // dhr: cleanout froxlor-generated awstats configs prior to re-creation
$awstatsclean['header'] = "## GENERATED BY FROXLOR\n";
$awstatsclean['headerold'] = "## GENERATED BY SYSCP\n";
$awstatsclean['path'] = $this->_getFile('system', 'awstats_conf');
-
+
/**
* don't do anything if the directory does not exist
- * (e.g. awstats not installed yet or whatever)
+ * (e.g.
+ * awstats not installed yet or whatever)
* fixes #45
- */
+ */
if ($awstatsclean['path'] !== false && is_dir($awstatsclean['path'])) {
$awstatsclean['dir'] = dir($awstatsclean['path']);
while ($awstatsclean['entry'] = $awstatsclean['dir']->read()) {
- $awstatsclean['fullentry'] = makeCorrectFile($awstatsclean['path'].'/'.$awstatsclean['entry']);
+ $awstatsclean['fullentry'] = makeCorrectFile($awstatsclean['path'] . '/' . $awstatsclean['entry']);
/**
* don't do anything if the file does not exist
- */
+ */
if (@file_exists($awstatsclean['fullentry'])) {
$awstatsclean['fh'] = fopen($awstatsclean['fullentry'], 'r');
- $awstatsclean['headerRead'] = fgets($awstatsclean['fh'], strlen($awstatsclean['header'])+1);
+ $awstatsclean['headerRead'] = fgets($awstatsclean['fh'], strlen($awstatsclean['header']) + 1);
fclose($awstatsclean['fh']);
-
- if ($awstatsclean['headerRead'] == $awstatsclean['header']
- || $awstatsclean['headerRead'] == $awstatsclean['headerold']
- ) {
+
+ if ($awstatsclean['headerRead'] == $awstatsclean['header'] || $awstatsclean['headerRead'] == $awstatsclean['headerold']) {
$awstats_conf_file = makeCorrectFile($awstatsclean['fullentry']);
@unlink($awstats_conf_file);
}
@@ -204,7 +206,7 @@ class ConfigIO {
}
}
unset($awstatsclean);
- //end dhr
+ // end dhr
}
/**
@@ -212,39 +214,37 @@ class ConfigIO {
*
* @return null
*/
- private function _cleanFcgidFiles() {
-
+ private function _cleanFcgidFiles()
+ {
if (Settings::Get('system.mod_fcgid') == '0') {
return;
}
-
+
// get correct directory
$configdir = $this->_getFile('system', 'mod_fcgid_configdir');
if ($configdir !== false) {
-
+
$configdir = makeCorrectDir($configdir);
-
+
if (@is_dir($configdir)) {
// create directory iterator
- $its = new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator($configdir)
- );
-
+ $its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($configdir));
+
// iterate through all subdirs,
// look for php-fcgi-starter files
// and take immutable-flag away from them
// so we can delete them :)
- foreach ($its as $fullFileName => $it ) {
+ foreach ($its as $fullFileName => $it) {
if ($it->isFile() && $it->getFilename() == 'php-fcgi-starter') {
// set chattr -i
removeImmutable($its->getPathname());
}
}
-
+
// now get rid of old stuff
- //(but append /* so we don't delete the directory)
- $configdir.='/*';
- safe_exec('rm -rf '. makeCorrectFile($configdir));
+ // (but append /* so we don't delete the directory)
+ $configdir .= '/*';
+ safe_exec('rm -rf ' . makeCorrectFile($configdir));
}
}
}
@@ -254,33 +254,36 @@ class ConfigIO {
*
* @return null
*/
- private function _cleanFpmFiles() {
-
+ private function _cleanFpmFiles()
+ {
if (Settings::Get('phpfpm.enabled') == '0') {
return;
}
-
- // get correct directory
- $configdir = $this->_getFile('phpfpm', 'configdir');
- if ($configdir !== false) {
-
- $configdir = makeCorrectDir($configdir);
-
+
+ // get all fpm config paths
+ $fpmconf_sel = Database::prepare("SELECT config_dir FROM `" . TABLE_PANEL_FPMDAEMONS . "`");
+ Database::pexecute($fpmconf_sel);
+ $fpmconf_paths = $fpmconf_sel->fetchAll(PDO::FETCH_ASSOC);
+ // clean all php-fpm config-dirs
+ foreach ($fpmconf_paths as $configdir) {
+ $configdir = makeCorrectDir($configdir['config_dir']);
if (@is_dir($configdir)) {
// now get rid of old stuff
- //(but append /* so we don't delete the directory)
- $configdir.='/*';
- safe_exec('rm -rf '. makeCorrectFile($configdir));
+ // (but append /*.conf so we don't delete the directory)
+ $configdir .= '/*.conf';
+ safe_exec('rm -f ' . makeCorrectFile($configdir));
+ } else {
+ safe_exec('mkdir -p ' . $configdir);
}
}
-
+
// also remove aliasconfigdir #1273
$aliasconfigdir = $this->_getFile('phpfpm', 'aliasconfigdir');
if ($aliasconfigdir !== false) {
$aliasconfigdir = makeCorrectDir($aliasconfigdir);
if (@is_dir($aliasconfigdir)) {
- $aliasconfigdir.='/*';
- safe_exec('rm -rf '. makeCorrectFile($aliasconfigdir));
+ $aliasconfigdir .= '/*';
+ safe_exec('rm -rf ' . makeCorrectFile($aliasconfigdir));
}
}
}
@@ -288,17 +291,21 @@ class ConfigIO {
/**
* returns a file/direcotry from the settings and checks whether it exists
*
- * @param string $group settings-group
- * @param string $varname var-name
- * @param boolean $check_exists check if the file exists
- *
+ * @param string $group
+ * settings-group
+ * @param string $varname
+ * var-name
+ * @param boolean $check_exists
+ * check if the file exists
+ *
* @return string|boolean complete path including filename if any or false on error
*/
- private function _getFile($group, $varname, $check_exists = true) {
-
+ private function _getFile($group, $varname, $check_exists = true)
+ {
+
// read from settings
- $file = Settings::Get($group.'.'.$varname);
-
+ $file = Settings::Get($group . '.' . $varname);
+
// check whether it exists
if ($check_exists && @file_exists($file) == false) {
return false;
diff --git a/lib/classes/webserver/class.WebserverBase.php b/lib/classes/webserver/class.WebserverBase.php
index d67b9868..f16073c8 100644
--- a/lib/classes/webserver/class.WebserverBase.php
+++ b/lib/classes/webserver/class.WebserverBase.php
@@ -17,8 +17,8 @@
* @since 0.9.31
*
*/
-
-class WebserverBase {
+class WebserverBase
+{
/**
* returns an array with all entries required for all
@@ -26,27 +26,45 @@ class WebserverBase {
*
* @return array
*/
- public static function getVhostsToCreate() {
-
+ public static function getVhostsToCreate()
+ {
$query = "SELECT `d`.*, `pd`.`domain` AS `parentdomain`, `c`.`loginname`,
`d`.`phpsettingid`, `c`.`adminid`, `c`.`guid`, `c`.`email`,
`c`.`documentroot` AS `customerroot`, `c`.`deactivated`,
- `c`.`phpenabled` AS `phpenabled`, `d`.`mod_fcgid_starter`,
- `d`.`mod_fcgid_maxrequests`
- FROM `".TABLE_PANEL_DOMAINS."` `d`
+ `c`.`phpenabled` AS `phpenabled_customer`,
+ `d`.`phpenabled` AS `phpenabled_vhost`,
+ `d`.`mod_fcgid_starter`,`d`.`mod_fcgid_maxrequests`,
+ `d`.`ocsp_stapling`
+ FROM `" . TABLE_PANEL_DOMAINS . "` `d`
- LEFT JOIN `".TABLE_PANEL_CUSTOMERS."` `c` USING(`customerid`)
- LEFT JOIN `".TABLE_PANEL_DOMAINS."` `pd` ON (`pd`.`id` = `d`.`parentdomainid`)
+ LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`)
+ LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `pd` ON (`pd`.`id` = `d`.`parentdomainid`)
WHERE `d`.`aliasdomain` IS NULL AND `d`.`email_only` <> '1'
ORDER BY `d`.`parentdomainid` DESC, `d`.`iswildcarddomain`, `d`.`domain` ASC;
";
-
+
$result_domains_stmt = Database::query($query);
-
+
+ // prepare IP statement
+ $ip_stmt = Database::prepare("
+ SELECT `di`.`id_domain` , `p`.`ssl`, `p`.`ssl_cert_file`, `p`.`ssl_key_file`, `p`.`ssl_ca_file`, `p`.`ssl_cert_chainfile`
+ FROM `" . TABLE_DOMAINTOIP . "` `di`, `" . TABLE_PANEL_IPSANDPORTS . "` `p`
+ WHERE `p`.`id` = `di`.`id_ipandports`
+ AND `di`.`id_domain` = :domainid
+ AND `p`.`ssl` = '1'
+ ");
+
+ // prepare fpm-config select query
+ $fpm_sel_stmt = Database::prepare("
+ SELECT f.id FROM `" . TABLE_PANEL_FPMDAEMONS . "` f
+ LEFT JOIN `" . TABLE_PANEL_PHPCONFIGS . "` p ON p.fpmsettingid = f.id
+ WHERE p.id = :phpconfigid
+ ");
+
$domains = array();
while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {
-
+
// set whole domain
$domains[$domain['domain']] = $domain;
// set empty-defaults for non-ssl
@@ -55,31 +73,38 @@ class WebserverBase {
$domains[$domain['domain']]['ssl_key_file'] = '';
$domains[$domain['domain']]['ssl_ca_file'] = '';
$domains[$domain['domain']]['ssl_cert_chainfile'] = '';
-
+
// now, if the domain has an ssl ip/port assigned, get
// the corresponding information from the db
if (domainHasSslIpPort($domain['id'])) {
- $ip_stmt = Database::prepare("
- SELECT `di`.`id_domain` , `p`.`ssl`, `p`.`ssl_cert_file`, `p`.`ssl_key_file`, `p`.`ssl_ca_file`, `p`.`ssl_cert_chainfile`
- FROM `".TABLE_DOMAINTOIP."` `di`, `".TABLE_PANEL_IPSANDPORTS."` `p`
- WHERE `p`.`id` = `di`.`id_ipandports`
- AND `di`.`id_domain` = :domainid
- AND `p`.`ssl` = '1'
- ");
- $ssl_ip = Database::pexecute_first($ip_stmt, array('domainid' => $domain['id']));
-
+ $ssl_ip = Database::pexecute_first($ip_stmt, array(
+ 'domainid' => $domain['id']
+ ));
+
// set ssl info for domain
$domains[$domain['domain']]['ssl'] = '1';
$domains[$domain['domain']]['ssl_cert_file'] = $ssl_ip['ssl_cert_file'];
$domains[$domain['domain']]['ssl_key_file'] = $ssl_ip['ssl_key_file'];
$domains[$domain['domain']]['ssl_ca_file'] = $ssl_ip['ssl_ca_file'];
$domains[$domain['domain']]['ssl_cert_chainfile'] = $ssl_ip['ssl_cert_chainfile'];
+ }
+
+ // read fpm-config-id if using fpm
+ if ((int) Settings::Get('phpfpm.enabled') == 1) {
+ $fpm_config = Database::pexecute_first($fpm_sel_stmt, array(
+ 'phpconfigid' => $domain['phpsettingid']
+ ));
+ if ($fpm_config) {
+ $domains[$domain['domain']]['fpm_config_id'] = $fpm_config['id'];
+ } else {
+ // fallback
+ $domains[$domain['domain']]['fpm_config_id'] = 1;
+ }
}
}
-
+
return $domains;
}
-
}
diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml
index 69b4dd8d..7b68d374 100644
--- a/lib/configfiles/gentoo.xml
+++ b/lib/configfiles/gentoo.xml
@@ -353,7 +353,7 @@ exit "$RETVAL"
{$lng['admin']['configfiles']['legend']} {$lng['admin']['configfiles']['legend']} {$lng['admin']['configfiles']['commands']} {$lng['admin']['configfiles']['files']}
- {$lng['admin']['configfiles']['commands']}
- {$lng['admin']['configfiles']['files']}
Let op:Dit vereist handmatige configuratie, zie FCGID - handbook';
+$lng['serversettings']['mod_fcgid_ownvhost']['description'] = 'Indien ingeschakeld wordt Froxlor ook uitgevoerd onder een lokale gebruiker
Let op:Dit vereist handmatige configuratie, zie FCGID - handbook';
$lng['admin']['mod_fcgid_user'] = 'Lokale gebruiker voor FCGID (Froxlor vhost)';
$lng['admin']['mod_fcgid_group'] = 'Lokale groep voor FCGID (Froxlor vhost)';
diff --git a/lng/english.lng.php b/lng/english.lng.php
index 256ebf04..c3ea6f0b 100644
--- a/lng/english.lng.php
+++ b/lng/english.lng.php
@@ -538,7 +538,7 @@ $lng['panel']['back'] = 'Back';
// ADDED IN 1.2.16-svn12
$lng['serversettings']['mod_fcgid']['title'] = 'Enable FCGID';
-$lng['serversettings']['mod_fcgid']['description'] = 'Use this to run PHP with the corresponding useraccount.
This needs a special webserver configuration for Apache, see FCGID - handbook';
+$lng['serversettings']['mod_fcgid']['description'] = 'Use this to run PHP with the corresponding useraccount.
This needs a special webserver configuration for Apache, see FCGID - handbook';
$lng['serversettings']['sendalternativemail']['title'] = 'Use alternative email-address';
$lng['serversettings']['sendalternativemail']['description'] = 'Send the password-email to a different address during email-account-creation';
$lng['emails']['alternative_emailaddress'] = 'Alternative e-mail-address';
@@ -944,6 +944,7 @@ $lng['admin']['phpsettings']['phpinisettings'] = 'php.ini settings';
$lng['error']['nopermissionsorinvalidid'] = 'You don\'t have enough permissions to change these settings or an invalid id was given.';
$lng['panel']['view'] = 'view';
$lng['question']['phpsetting_reallydelete'] = 'Do you really want to delete these settings? All domains which use these settings currently will be changed to the default config.';
+$lng['question']['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.';
$lng['admin']['phpsettings']['addnew'] = 'Create new settings';
$lng['error']['phpsettingidwrong'] = 'A PHP Configuration with this id doesn\'t exist';
$lng['error']['descriptioninvalid'] = 'The description is too short, too long or contains illegal characters.';
@@ -1005,6 +1006,7 @@ $lng['error']['ipportdoesntexist'] = 'The ip/port combination you have chosen do
$lng['admin']['phpserversettings'] = 'PHP Settings';
$lng['admin']['phpsettings']['binary'] = 'PHP Binary';
+$lng['admin']['phpsettings']['fpmdesc'] = 'PHP-FPM config';
$lng['admin']['phpsettings']['file_extensions'] = 'File extensions';
$lng['admin']['phpsettings']['file_extensions_note'] = '(without dot, separated by spaces)';
$lng['admin']['mod_fcgid_maxrequests']['title'] = 'Maximum php requests for this domain (empty for default value)';
@@ -1106,7 +1108,7 @@ $lng['dkim']['dkim_notes']['description'] = 'Notes that might be of interest to
$lng['dkim']['dkim_add_adsp']['title'] = 'Add DKIM ADSP entry';
$lng['dkim']['dkim_add_adsp']['description'] = 'If you don\'t know what this is, leave it "enabled"';
$lng['dkim']['dkim_add_adsppolicy']['title'] = 'ADSP policy';
-$lng['dkim']['dkim_add_adsppolicy']['description'] = 'For more information about this setting see DKIM ADSP policies';
+$lng['dkim']['dkim_add_adsppolicy']['description'] = 'For more information about this setting see DKIM ADSP policies';
$lng['admin']['cron']['cronsettings'] = 'Cronjob settings';
$lng['cron']['cronname'] = 'cronjob-name';
@@ -1297,7 +1299,7 @@ $lng['error']['intvaluetoolow'] = 'The given number is too low (field %s)';
$lng['error']['intvaluetoohigh'] = 'The given number is too high (field %s)';
$lng['admin']['phpfpm_settings'] = 'PHP-FPM';
$lng['serversettings']['phpfpm']['title'] = 'Enable php-fpm';
-$lng['serversettings']['phpfpm']['description'] = 'This needs a special webserver configuration see FPM-handbook for Apache2 or nginx';
+$lng['serversettings']['phpfpm']['description'] = 'This needs a special webserver configuration see FPM-handbook for Apache2 or nginx';
$lng['serversettings']['phpfpm_settings']['configdir'] = 'Configuration directory of php-fpm';
$lng['serversettings']['phpfpm_settings']['aliasconfigdir'] = 'Configuration Alias-directory of php-fpm';
$lng['serversettings']['phpfpm_settings']['reload'] = 'php-fpm restart command';
@@ -1660,7 +1662,7 @@ $lng['traffic']['mail'] = 'Mail (MiB)';
$lng['serversettings']['mod_fcgid']['idle_timeout']['title'] = 'Idle Timeout';
$lng['serversettings']['mod_fcgid']['idle_timeout']['description'] = 'Timeout setting for Mod FastCGI.';
$lng['serversettings']['phpfpm_settings']['idle_timeout']['title'] = 'Idle Timeout';
-$lng['serversettings']['phpfpm_settings']['idle_timeout']['description'] = 'Timeout setting for PHP5 FPM FastCGI.';
+$lng['serversettings']['phpfpm_settings']['idle_timeout']['description'] = 'Timeout setting for PHP FPM FastCGI.';
// ADDED IN 0.9.27-svn2
$lng['panel']['cancel'] = 'Cancel';
@@ -1830,7 +1832,7 @@ $lng['serversettings']['panel_password_special_char_required']['description'] =
$lng['serversettings']['panel_password_special_char']['title'] = 'Special characters list';
$lng['serversettings']['panel_password_special_char']['description'] = 'One of these characters is required if the above option is set.';
$lng['phpfpm']['use_mod_proxy']['title'] = 'Use mod_proxy / mod_proxy_fcgi';
-$lng['phpfpm']['use_mod_proxy']['description'] = 'Activate to use php-fpm via mod_proxy_fcgi. Requires at least apache-2.4.9';
+$lng['phpfpm']['use_mod_proxy']['description'] = 'Must be enabled when using Debian 9.x (Stretch). Activate to use php-fpm via mod_proxy_fcgi. Requires at least apache-2.4.9';
$lng['error']['no_phpinfo'] = 'Sorry, unable to read phpinfo()';
$lng['admin']['movetoadmin'] = 'Move customer';
@@ -1844,16 +1846,16 @@ $lng['domains']['import_file'] = 'CSV-File';
$lng['success']['domain_import_successfully'] = 'Successfully imported %s domains.';
$lng['error']['domain_import_error'] = 'Following error occurred while importing domains: %s';
$lng['admin']['note'] = 'Note';
-$lng['domains']['import_description'] = 'Detailed information about the structure of the import-file and how to import successfully, please visit http://redmine.froxlor.org/projects/froxlor/wiki/DomainBulkActionDoc';
+$lng['domains']['import_description'] = 'Detailed information about the structure of the import-file and how to import successfully, please visit https://github.com/Froxlor/Froxlor/wiki/Domain-import-documenation';
$lng['usersettings']['custom_notes']['title'] = 'Custom notes';
$lng['usersettings']['custom_notes']['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.';
$lng['usersettings']['custom_notes']['show'] = 'Show your notes on the dashboard of the user';
$lng['error']['fcgidandphpfpmnogoodtogether'] = 'FCGID and PHP-FPM cannot be activated at the same time';
// Added in Froxlor 0.9.34
-$lng['admin']['configfiles']['legend'] = 'You are about to configure a service/daemon. The following legend explains the nomenclature.';
+$lng['admin']['configfiles']['legend'] = 'You are about to configure a service/daemon
';
$lng['admin']['configfiles']['commands'] = 'Commands: 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.';
-$lng['admin']['configfiles']['files'] = 'Configfiles: This is an example of the contents of a configuration file. The commands before these textfields should open an editor with the target file. Just copy and paste the contents into the editor and save the file.
Please note: The MySQL-password has not been replaced for security reasons. Please replace "MYSQL_PASSWORD" on your own. If you forgot your MySQL-password you\'ll find it in "lib/userdata.inc.php"';
+$lng['admin']['configfiles']['files'] = 'Configfiles: 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.
Please note: 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"';
$lng['serversettings']['apache_itksupport']['title'] = 'Use modifications for Apache ITK-MPM';
$lng['serversettings']['apache_itksupport']['description'] = 'ATTENTION: use only if you acutally have apache itk-mpm enabled
otherwise your webserver will not be able to start';
$lng['integrity_check']['DatabaseCharset'] = 'Characterset of database (should be UTF-8)';
@@ -1942,7 +1944,7 @@ $lng['admin']['letsencrypt']['description'] = 'Get a free certificate from Let\'s Encrypt. The certificate will be created and renewed automatically.
ATTENTION: This feature is still in beta.';
$lng['error']['sslredirectonlypossiblewithsslipport'] = 'Using Let\'s Encrypt is only possible when the domain has at least one ssl-enabled IP/port combination assigned.';
-$lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt cannot (yet) handle wildcard-domains. Please set the ServerAlias to WWW or disable it completely';
+$lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt cannot handle wildcard-domains using ACME v1. Please set the ServerAlias to WWW or disable it completely';
$lng['panel']['letsencrypt'] = 'Using Let\'s encrypt';
$lng['crondesc']['cron_letsencrypt'] = 'updating Let\'s Encrypt certificates';
$lng['serversettings']['letsencryptca']['title'] = "Let's Encrypt environment";
@@ -1976,7 +1978,7 @@ $lng['error']['autoupdate_9'] = 'The downloaded file did not pass the integrity
$lng['admin']['server_php'] = 'PHP';
$lng['domains']['termination_date'] = 'Date of termination';
-$lng['domains']['termination_date_overview'] = 'canceled until ';
+$lng['domains']['termination_date_overview'] = 'terminated as of ';
$lng['panel']['set'] = 'Apply';
$lng['customer']['selectserveralias_addinfo'] = 'This option can be set when editing the domain. Its initial value is inherited from the parent-domain.';
$lng['error']['mailaccistobedeleted'] = "Another account with the same name (%s) is currently being deleted and can therefore not be added at this moment.";
@@ -2042,8 +2044,8 @@ $lng['serversettings']['le_froxlor_enabled']['description'] = "If activated, the
$lng['serversettings']['le_froxlor_redirect']['title'] = "Enable SSL-redirect for the froxlor vhost";
$lng['serversettings']['le_froxlor_redirect']['description'] = "If activated, all http requests to your froxlor will be redirected to the corresponding SSL site.";
$lng['admin']['froxlorvhost'] = 'Froxlor VirtualHost settings';
-$lng['serversettings']['option_unavailable_websrv'] = '
Availble only for: %s';
-$lng['serversettings']['option_unavailable'] = '
Option not availble due to other settings.';
+$lng['serversettings']['option_unavailable_websrv'] = '
Available only for: %s';
+$lng['serversettings']['option_unavailable'] = '
Option not available due to other settings.';
$lng['serversettings']['letsencryptacmeconf']['title'] = "Path to the acme.conf snippet";
$lng['serversettings']['letsencryptacmeconf']['description'] = "File name of the config snippet which allows the web server to serve the acme challenge.";
$lng['admin']['hostname'] = 'Hostname';
@@ -2065,7 +2067,57 @@ $lng['admin']['domain_hsts_maxage']['description'] = 'Specify the max-age value
$lng['admin']['domain_hsts_incsub']['title'] = 'Include HSTS for any subdomain';
$lng['admin']['domain_hsts_incsub']['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.';
$lng['admin']['domain_hsts_preload']['title'] = 'Include domain in HSTS preload list';
-$lng['admin']['domain_hsts_preload']['description'] = 'If you would like this domain to be included in the HSTS preload list maintained by Chrome (and used by Firefox and Safari), then use activate this.
Sending the preload directive from your site can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains.
Please read the details at hstspreload.appspot.com/#removal before sending the header with "preload".';
+$lng['admin']['domain_hsts_preload']['description'] = 'If you would like this domain to be included in the HSTS preload list maintained by Chrome (and used by Firefox and Safari), then use activate this.
Sending the preload directive from your site can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains.
Please read the details at hstspreload.appspot.com/#removal before sending the header with "preload".';
-$lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Support';
-$lng['serversettings']['nginx_http2_support']['description'] = 'enable http2 support for ssl. ENABLE ONLY IF YOUR Nginx SUPPORT THIS FEATURE. (version 1.9.5+)';
+$lng['serversettings']['http2_support']['title'] = 'HTTP2 Support';
+$lng['serversettings']['http2_support']['description'] = 'enable HTTP2 support for ssl.
ENABLE ONLY IF YOUR WEBSERVER SUPPORTS THIS FEATURE (nginx version 1.9.5+, apache2 version 2.4.17+)';
+
+$lng['error']['noipportgiven'] = 'No IP/port given';
+
+// Added in froxlor 0.9.38.8
+$lng['admin']['domain_ocsp_stapling']['title'] = 'OCSP stapling';
+$lng['admin']['domain_ocsp_stapling']['description'] = 'See Wikipedia for a detailed explanation of OCSP stapling';
+$lng['admin']['domain_ocsp_stapling']['nginx_version_warning'] = '
WARNING: 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!';
+$lng['serversettings']['ssl']['apache24_ocsp_cache_path']['title'] = 'Apache 2.4: path to the OCSP stapling cache';
+$lng['serversettings']['ssl']['apache24_ocsp_cache_path']['description'] = 'Configures the cache used to store OCSP responses which get included in TLS handshakes.';
+$lng['serversettings']['nssextrausers']['title'] = 'Use libnss-extrausers instead of libnss-mysql';
+$lng['serversettings']['nssextrausers']['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).
For Debian/Ubuntu only (or if you have compiled libnss-extrausers yourself!)';
+$lng['admin']['domain_http2']['title'] = 'HTTP2 support';
+$lng['admin']['domain_http2']['description'] = 'See Wikipedia for a detailed explanation of HTTP2';
+$lng['admin']['testmail'] = 'SMTP test';
+$lng['success']['testmailsent'] = 'Test mail sent successfully';
+$lng['serversettings']['disable_le_selfcheck']['title'] = "Disable Let's Encrypt local self-check";
+$lng['serversettings']['disable_le_selfcheck']['description'] = "If activated, froxlor will not perform its self-check for token accessability. Needed for NATed IP's or similar.";
+$lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM versions';
+$lng['admin']['phpsettings']['activephpconfigs'] = 'In use for php-config(s)';
+$lng['admin']['phpsettingsforsubdomains'] = 'Apply php-config to all subdomains:';
+$lng['serversettings']['phpsettingsforsubdomains']['description'] = 'If yes the chosen php-config will be updated to all subdomains';
+$lng['serversettings']['leapiversion']['title'] = "Chose Let's Encrypt ACME implementation";
+$lng['serversettings']['leapiversion']['description'] = "Chose between ACME v1 and ACME v2 implementation for Let's Encrypt.";
+$lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt can only validate wildcard-domains by DNS with ACME v2, sorry. Please set the ServerAlias to WWW or disable it completely';
+$lng['admin']['phpsettings']['pass_authorizationheader'] = 'Add "-pass-header Authorization" / "CGIPassAuth On" to vhosts';
+$lng['serversettings']['ssl']['ssl_protocols']['title'] = 'Configure the TLS protocol version';
+$lng['serversettings']['ssl']['ssl_protocols']['description'] = 'This is a list of ssl protocols that you want (or don\'t want) to use when using SSL. Notice: Some older browsers may not support the newest protcol versions.
Default value is:TLSv1, TLSv1.2
';
+$lng['serversettings']['phpfpm_settings']['limit_extensions']['title'] = 'Allowed extensions';
+$lng['serversettings']['phpfpm_settings']['limit_extensions']['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';
+$lng['phpfpm']['ini_flags'] = 'Enter possible php_flags for php.ini. One entry per line';
+$lng['phpfpm']['ini_values'] = 'Enter possible php_values for php.ini. One entry per line';
+$lng['phpfpm']['ini_admin_flags'] = 'Enter possible php_admin_flags for php.ini. One entry per line';
+$lng['phpfpm']['ini_admin_values'] = 'Enter possible php_admin_values for php.ini. One entry per line';
+$lng['serversettings']['phpfpm_settings']['envpath'] = 'Paths to add to the PATH environment. Leave empty for no PATH environment variable';
+$lng['admin']['configfiles']['importexport'] = 'Import/Export';
+$lng['success']['settingsimported'] = 'Settings imported successfully';
+$lng['error']['jsonextensionnotfound'] = 'This feature requires the php json-extension.';
+
+// added in froxlor 0.9.39
+$lng['admin']['plans']['name'] = 'Plan name';
+$lng['admin']['plans']['description'] = 'Description';
+$lng['admin']['plans']['last_update'] = 'Last updated';
+$lng['admin']['plans']['plans'] = 'Hosting plans';
+$lng['admin']['plans']['plan_details'] = 'Plan details';
+$lng['admin']['plans']['add'] = 'Add new plan';
+$lng['admin']['plans']['edit'] = 'Edit plan';
+$lng['admin']['plans']['use_plan'] = 'Apply plan';
+$lng['question']['plan_reallydelete'] = 'Do you really want to delete the hosting plan %s?';
+$lng['admin']['notryfiles']['title'] = 'No autogenerated try_files';
+$lng['admin']['notryfiles']['description'] = 'Say yes here if you want to specify a custom try_files directive in specialsettings (needed for some wordpress plugins for example).';
diff --git a/lng/french.lng.php b/lng/french.lng.php
index aea6d93c..9375aa18 100644
--- a/lng/french.lng.php
+++ b/lng/french.lng.php
@@ -569,18 +569,18 @@ $lng['serversettings']['webalizer_quiet']['description'] = 'Verbosité du progra
$lng['ticket']['admin_email'] = 'root@localhost';
$lng['ticket']['noreply_email'] = 'billets@froxlor';
-$lng['admin']['ticketsystem'] = 'Système de billets';
-$lng['menue']['ticket']['ticket'] = 'Billets de support';
+$lng['admin']['ticketsystem'] = 'Système de tickets';
+$lng['menue']['ticket']['ticket'] = 'Tickets support';
$lng['menue']['ticket']['categories'] = 'Catégories de support';
-$lng['menue']['ticket']['archive'] = 'Archives de billets';
+$lng['menue']['ticket']['archive'] = 'Archives de tickets';
$lng['ticket']['description'] = 'Entrez une description !';
-$lng['ticket']['ticket_new'] = 'Ouvrir un nouveau billet';
-$lng['ticket']['ticket_reply'] = 'Réponse au billet';
-$lng['ticket']['ticket_reopen'] = 'Réouvrir le billet';
+$lng['ticket']['ticket_new'] = 'Ouvrir un nouveau ticket';
+$lng['ticket']['ticket_reply'] = 'Réponse au ticket';
+$lng['ticket']['ticket_reopen'] = 'Réouvrir le ticket';
$lng['ticket']['ticket_newcateory'] = 'Créer une nouvelle catégorie';
$lng['ticket']['ticket_editcateory'] = 'Editer la catégorie';
-$lng['ticket']['ticket_view'] = 'Voir l\'historique du billet';
-$lng['ticket']['ticketcount'] = 'Billets';
+$lng['ticket']['ticket_view'] = 'Voir l\'historique du ticket';
+$lng['ticket']['ticketcount'] = 'Tickets';
$lng['ticket']['ticket_answers'] = 'Réponses';
// $lng['ticket']['lastchange'] = 'Dernière action';
$lng['ticket']['lastchange'] = 'Dernier changement';
@@ -601,8 +601,8 @@ $lng['ticket']['answer'] = 'Répondre';
$lng['ticket']['close'] = 'Fermer';
$lng['ticket']['reopen'] = 'Réouvrir';
$lng['ticket']['archive'] = 'Archive';
-$lng['ticket']['ticket_delete'] = 'Effacer le billet';
-$lng['ticket']['lastarchived'] = 'Billets récemment archivés';
+$lng['ticket']['ticket_delete'] = 'Effacer le ticket';
+$lng['ticket']['lastarchived'] = 'Tickets récemment archivés';
$lng['ticket']['archivedtime'] = 'Archivé';
$lng['ticket']['open'] = 'Ouvert';
$lng['ticket']['wait_reply'] = 'Attente d\'une réponse';
@@ -610,43 +610,43 @@ $lng['ticket']['replied'] = 'Répondu';
$lng['ticket']['closed'] = 'Fermé';
$lng['ticket']['staff'] = 'L\'équipe';
$lng['ticket']['customer'] = 'Client';
-$lng['ticket']['old_tickets'] = 'Messages du billet';
+$lng['ticket']['old_tickets'] = 'Messages du ticket';
$lng['ticket']['search'] = 'Rechercher dans les archives';
$lng['ticket']['nocustomer'] = 'Aucun choix';
$lng['ticket']['archivesearch'] = 'Résultat de la recherche dans les archives';
-$lng['ticket']['noresults'] = 'Aucun billet trouvé';
-$lng['ticket']['notmorethanxopentickets'] = 'Pour éviter les abus, vous ne pouvez avoir plus de %s billets ouverts';
+$lng['ticket']['noresults'] = 'Aucun ticket trouvé';
+$lng['ticket']['notmorethanxopentickets'] = 'Pour éviter les abus, vous ne pouvez avoir plus de %s tickets ouverts';
$lng['ticket']['supportstatus'] = 'Etat du support';
$lng['ticket']['supportavailable'] = 'Nos équipes de support sont disponibles et prètes à vous assister.';
$lng['ticket']['supportnotavailable'] = 'Nos équipes de support ne sont actuellement pas disponibles.';
-$lng['admin']['templates']['ticket'] = 'E-mail de notification pour les billets de support';
-$lng['admin']['templates']['SUBJECT'] = 'Sera remplacé par le sujet du billet de support.';
-$lng['admin']['templates']['new_ticket_for_customer'] = 'Informe le client que le billet a été envoyé';
-$lng['admin']['templates']['new_ticket_by_customer'] = 'Notifie l\'administrateur qu\'un nouveau billet a été ouvert par un client';
-$lng['admin']['templates']['new_reply_ticket_by_customer'] = 'Notifie l\'administrateur d\'une réponse du client au billet';
-$lng['admin']['templates']['new_ticket_by_staff'] = 'Informe le client qu\'un billet a été ouvert par l\'équipe de support';
-$lng['admin']['templates']['new_reply_ticket_by_staff'] = 'Informe le client d\'une réponse de l\'équipe de support au billet';
-$lng['mails']['new_ticket_for_customer']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nVotre demande de billet de support ayant comme sujet "{SUBJECT}" a été envoyé.\n\nVous receverez une notification lorsque votre billet aura une réponse.\n\nMerci,\nL\'équipe Froxlor.';
-$lng['mails']['new_ticket_for_customer']['subject'] = 'Votre billet de support a été envoyé';
-$lng['mails']['new_ticket_by_customer']['mailbody'] = 'Bonjour administrateur,\n\nUn nouveau billet de support ayant comme sujet "{SUBJECT}" a été ouvert.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nl\'équipe Froxlor.';
-$lng['mails']['new_ticket_by_customer']['subject'] = 'Nouveau billet de support soumis';
-$lng['mails']['new_reply_ticket_by_customer']['mailbody'] = 'Bonjour administrateur,\n\nLe billet de support "{SUBJECT}" a reçu une réponse de la part du client.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\'équipe Froxlor.';
-$lng['mails']['new_reply_ticket_by_customer']['subject'] = 'Nouvelle réponse au billet de support';
-$lng['mails']['new_ticket_by_staff']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nUn billet de support ayant comme sujet "{SUBJECT}" a été ouvert pour vous par notre équipe.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\'équipe Froxlor.';
+$lng['admin']['templates']['ticket'] = 'E-mail de notification pour les tickets de support';
+$lng['admin']['templates']['SUBJECT'] = 'Sera remplacé par le sujet du ticket de support.';
+$lng['admin']['templates']['new_ticket_for_customer'] = 'Informe le client que le ticket a été envoyé';
+$lng['admin']['templates']['new_ticket_by_customer'] = 'Notifie l\'administrateur qu\'un nouveau ticket a été ouvert par un client';
+$lng['admin']['templates']['new_reply_ticket_by_customer'] = 'Notifie l\'administrateur d\'une réponse du client au ticket';
+$lng['admin']['templates']['new_ticket_by_staff'] = 'Informe le client qu\'un ticket a été ouvert par l\'équipe de support';
+$lng['admin']['templates']['new_reply_ticket_by_staff'] = 'Informe le client d\'une réponse de l\'équipe de support au ticket';
+$lng['mails']['new_ticket_for_customer']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nVotre demande de ticket de support ayant comme sujet "{SUBJECT}" a été envoyé.\n\nVous receverez une notification lorsque votre billet aura une réponse.\n\nMerci,\nL\'équipe Froxlor.';
+$lng['mails']['new_ticket_for_customer']['subject'] = 'Votre ticket de support a été envoyé';
+$lng['mails']['new_ticket_by_customer']['mailbody'] = 'Bonjour administrateur,\n\nUn nouveau ticket de support ayant comme sujet "{SUBJECT}" a été ouvert.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nl\'équipe Froxlor.';
+$lng['mails']['new_ticket_by_customer']['subject'] = 'Nouveau ticket de support soumis';
+$lng['mails']['new_reply_ticket_by_customer']['mailbody'] = 'Bonjour administrateur,\n\nLe ticket de support "{SUBJECT}" a reçu une réponse de la part du client.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\'équipe Froxlor.';
+$lng['mails']['new_reply_ticket_by_customer']['subject'] = 'Nouvelle réponse au ticket de support';
+$lng['mails']['new_ticket_by_staff']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nUn ticket de support ayant comme sujet "{SUBJECT}" a été ouvert pour vous par notre équipe.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\'équipe Froxlor.';
$lng['mails']['new_ticket_by_staff']['subject'] = 'Nouvelle demande de support soumise';
-$lng['mails']['new_reply_ticket_by_staff']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nLe billet de support ayant comme sujet "{SUBJECT}" a reçu une réponse par notre équipe.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\équipe Froxlor.';
-$lng['mails']['new_reply_ticket_by_staff']['subject'] = 'Nouvelle réponse au billet de support';
-$lng['question']['ticket_reallyclose'] = 'Etes-vous sûr de vouloir clôturer le billet "%s" ?';
-$lng['question']['ticket_reallydelete'] = 'Etes-vous sûr de vouloir supprimer le billet "%s" ?';
+$lng['mails']['new_reply_ticket_by_staff']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nLe ticket de support ayant comme sujet "{SUBJECT}" a reçu une réponse par notre équipe.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\équipe Froxlor.';
+$lng['mails']['new_reply_ticket_by_staff']['subject'] = 'Nouvelle réponse au ticket de support';
+$lng['question']['ticket_reallyclose'] = 'Etes-vous sûr de vouloir clôturer le ticket "%s" ?';
+$lng['question']['ticket_reallydelete'] = 'Etes-vous sûr de vouloir supprimer le ticket "%s" ?';
$lng['question']['ticket_reallydeletecat'] = 'Etes-vous sûr de vouloir supprimer la catégorie "%s" ?';
-$lng['question']['ticket_reallyarchive'] = 'Etes-vous sûr de vouloir archiver le billet "%s" ?';
-$lng['error']['nomoreticketsavailable'] = 'Vous n\'avez plus de billets de disponibles. Veuillez contacter votre administrateur.';
-$lng['error']['nocustomerforticket'] = 'Ne peut créer de billet sans client';
-$lng['error']['categoryhastickets'] = 'La catégorie possède des billets.
Veuillez d\'abord supprimer tous les billets de cette catégorie.';
-$lng['admin']['ticketsettings'] = 'Paramètres des billets de support';
-$lng['admin']['archivelastrun'] = 'Derniers billets archivés';
+$lng['question']['ticket_reallyarchive'] = 'Etes-vous sûr de vouloir archiver le ticket "%s" ?';
+$lng['error']['nomoreticketsavailable'] = 'Vous n\'avez plus de tickets de disponibles. Veuillez contacter votre administrateur.';
+$lng['error']['nocustomerforticket'] = 'Impossible de créer un ticket sans clients dans la base';
+$lng['error']['categoryhastickets'] = 'La catégorie possède des tickets.
Veuillez d\'abord supprimer tous les tickets de cette catégorie.';
+$lng['admin']['ticketsettings'] = 'Paramètres des tickets de support';
+$lng['admin']['archivelastrun'] = 'Derniers tickets archivés';
$lng['serversettings']['ticket']['noreply_email']['title'] = 'Adresse e-mail de non réponse';
-$lng['serversettings']['ticket']['noreply_email']['description'] = 'L\'adresse e-mail de l\'expéditeur de notification pour les billets de support, quelque chose du type no-reply@domaine.com';
+$lng['serversettings']['ticket']['noreply_email']['description'] = 'L\'adresse e-mail de l\'expéditeur de notification pour les tickets de support, quelque chose du type no-reply@domaine.com';
$lng['serversettings']['ticket']['worktime_begin']['title'] = 'Début du support (hh:mm)';
$lng['serversettings']['ticket']['worktime_begin']['description'] = 'Horaire de début du support';
$lng['serversettings']['ticket']['worktime_end']['title'] = 'Fin du support (hh:mm)';
@@ -655,21 +655,21 @@ $lng['serversettings']['ticket']['worktime_sat'] = 'Support disponible le samedi
$lng['serversettings']['ticket']['worktime_sun'] = 'Support disponible le dimanche ?';
$lng['serversettings']['ticket']['worktime_all']['title'] = 'Aucune limite horaire pour le support';
$lng['serversettings']['ticket']['worktime_all']['description'] = 'Si "Oui", les options pour le début et la fin du support seront écrasés.';
-$lng['serversettings']['ticket']['archiving_days'] = 'Après combien de jours un billet fermé sera automatiquement archivé ?';
-$lng['customer']['tickets'] = 'Billet de support';
+$lng['serversettings']['ticket']['archiving_days'] = 'Après combien de jours un ticket fermé sera automatiquement archivé ?';
+$lng['customer']['tickets'] = 'Ticket de support';
// ADDED IN 1.2.18-svn4
$lng['admin']['domain_nocustomeraddingavailable'] = 'Il n\'est acutellement pas possible d\'ajouter de domaines. Vous devez d\'abord ajouter un client.';
-$lng['serversettings']['ticket']['enable'] = 'Activer le système de billets';
-$lng['serversettings']['ticket']['concurrentlyopen'] = 'Combien de billets peuvent être ouverts au même moment ?';
+$lng['serversettings']['ticket']['enable'] = 'Activer le système de tickets';
+$lng['serversettings']['ticket']['concurrentlyopen'] = 'Combien de tickets peuvent être ouverts au même moment ?';
$lng['error']['norepymailiswrong'] = 'L\'adresse de "non réponse" n\'est pas bonne. Une adresse e-mail valide doit être entrée.';
-$lng['error']['tadminmailiswrong'] = 'L\'adresse de "l\'administrateur de billets" n\'est pas bonne. Une adresse e-mail valide doit être entrée.';
-$lng['ticket']['awaitingticketreply'] = 'Vous avez %s billet(s) de support non répondu(s).';
+$lng['error']['tadminmailiswrong'] = 'L\'adresse de "l\'administrateur de tickets" n\'est pas bonne. Une adresse e-mail valide doit être entrée.';
+$lng['ticket']['awaitingticketreply'] = 'Vous avez %s ticket(s) de support non répondu(s).';
// ADDED IN 1.2.18-svn5
-$lng['serversettings']['ticket']['noreply_name'] = 'Nom de l\'expéditeur e-mail des billets';
+$lng['serversettings']['ticket']['noreply_name'] = 'Nom de l\'expéditeur e-mail des tickets';
// ADDED IN 1.2.19-svn1
@@ -679,8 +679,8 @@ $lng['serversettings']['mod_fcgid']['tmpdir']['title'] = 'Dossier temporaire pou
// ADDED IN 1.2.19-svn3
-$lng['serversettings']['ticket']['reset_cycle']['title'] = 'Intervalle de réinitialisation des billets utilisés';
-$lng['serversettings']['ticket']['reset_cycle']['description'] = 'Remettre le compteur de billets à 0 dans le temps imparti';
+$lng['serversettings']['ticket']['reset_cycle']['title'] = 'Intervalle de réinitialisation des tickets utilisés';
+$lng['serversettings']['ticket']['reset_cycle']['description'] = 'Remettre le compteur de tickets à 0 dans le temps imparti';
$lng['admin']['tickets']['daily'] = 'Journalière';
$lng['admin']['tickets']['weekly'] = 'Hebdomadaire';
$lng['admin']['tickets']['monthly'] = 'Mensuelle';
diff --git a/lng/german.lng.php b/lng/german.lng.php
index 9462bd4e..2515d1d6 100644
--- a/lng/german.lng.php
+++ b/lng/german.lng.php
@@ -534,7 +534,7 @@ $lng['panel']['back'] = 'Zurück';
// ADDED IN 1.2.16-svn12
$lng['serversettings']['mod_fcgid']['title'] = 'PHP über mod_fcgid/suexec einbinden';
-$lng['serversettings']['mod_fcgid']['description'] = 'PHP wird unter dem Benutzer des Kunden ausgeführt.
Dies benötigt eine spezielle Webserver-Konfiguration für Apache, siehe FCGID-Handbuch.';
+$lng['serversettings']['mod_fcgid']['description'] = 'PHP wird unter dem Benutzer des Kunden ausgeführt.
Dies benötigt eine spezielle Webserver-Konfiguration für Apache, siehe FCGID-Handbuch.';
$lng['serversettings']['sendalternativemail']['title'] = 'Alternative E-Mail-Adresse benutzen';
$lng['serversettings']['sendalternativemail']['description'] = 'Während des Erstellens eines Accounts das Passwort an eine andere E-Mail-Adresse senden';
$lng['emails']['alternative_emailaddress'] = 'Alternative E-Mail-Adresse';
@@ -939,6 +939,7 @@ $lng['admin']['phpsettings']['phpinisettings'] = 'php.ini-Einstellungen';
$lng['error']['nopermissionsorinvalidid'] = 'Entweder fehlen Ihnen die nötigen Rechte diese Einstellung zu ändern oder es wurde eine ungültige ID übergeben';
$lng['panel']['view'] = 'ansehen';
$lng['question']['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.';
+$lng['question']['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.';
$lng['admin']['phpsettings']['addnew'] = 'Neue Konfiguration erstellen';
$lng['error']['phpsettingidwrong'] = 'Eine PHP-Konfiguration mit dieser ID existiert nicht';
$lng['error']['descriptioninvalid'] = 'Der Beschreibungstext ist zu kurz, zu lang oder enthält ungültige Zeichen';
@@ -1002,6 +1003,7 @@ $lng['error']['ipportdoesntexist'] = 'Die gewählte IP/Port-Kombination existier
$lng['admin']['phpserversettings'] = 'PHP-Einstellungen';
$lng['admin']['phpsettings']['binary'] = 'PHP-Binary';
+$lng['admin']['phpsettings']['fpmdesc'] = 'PHP-FPM Config';
$lng['admin']['phpsettings']['file_extensions'] = 'Dateiendungen';
$lng['admin']['phpsettings']['file_extensions_note'] = '(ohne Punkt, durch Leerzeichen getrennt)';
$lng['admin']['mod_fcgid_maxrequests']['title'] = 'Maximale PHP-Requests für diese Domain (leer für Standardwert)';
@@ -1080,7 +1082,7 @@ $lng['dkim']['dkim_notes']['description'] = 'Eine Notiz, welche für Menschen in
$lng['dkim']['dkim_add_adsp']['title'] = 'DKIM-ADSP Eintrag hinzufügen';
$lng['dkim']['dkim_add_adsp']['description'] = 'Wenn unsicher oder unbekannt, belassen sie es auf "aktiviert"';
$lng['dkim']['dkim_add_adsppolicy']['title'] = 'ADSP-Richtlinie';
-$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Mehr Informationen zu dieser Einstellung (englisch) DKIM-ADSP-Policies';
+$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Mehr Informationen zu dieser Einstellung (englisch) DKIM-ADSP-Policies';
$lng['admin']['cron']['cronsettings'] = 'Cronjob-Einstellungen';
$lng['cron']['cronname'] = 'Cronjob-Name';
@@ -1276,7 +1278,7 @@ $lng['error']['intvaluetoolow'] = 'Die angegebene Zahl ist zu klein (Feld "%s")'
$lng['error']['intvaluetoohigh'] = 'Die angegebene Zahl ist zu groß (Feld "%s")';
$lng['admin']['phpfpm_settings'] = 'PHP-FPM';
$lng['serversettings']['phpfpm']['title'] = 'Aktiviere PHP-FPM';
-$lng['serversettings']['phpfpm']['description'] = 'Dies benötigt eine spezielle Webserver-Konfiguration, siehe FPM-Handbuch für Apache2 oder nginx';
+$lng['serversettings']['phpfpm']['description'] = 'Dies benötigt eine spezielle Webserver-Konfiguration, siehe FPM-Handbuch für Apache2 oder nginx';
$lng['serversettings']['phpfpm_settings']['configdir'] = 'Pfad zu php-fpm-Konfigurationen';
$lng['serversettings']['phpfpm_settings']['aliasconfigdir'] = 'Alias-Ordner der php-fpm Konfiguration';
$lng['serversettings']['phpfpm_settings']['reload'] = 'Kommando zum Neustarten von php-fpm';
@@ -1388,7 +1390,7 @@ $lng['traffic']['mail'] = 'Mail (MiB)';
$lng['serversettings']['mod_fcgid']['idle_timeout']['title'] = 'Idle-Timeout';
$lng['serversettings']['mod_fcgid']['idle_timeout']['description'] = 'Timeout-Einstellung für mod_FastCGI.';
$lng['serversettings']['phpfpm_settings']['idle_timeout']['title'] = 'Idle-Timeout';
-$lng['serversettings']['phpfpm_settings']['idle_timeout']['description'] = 'Timeout-Einstellung für PHP5-FPM FastCGI.';
+$lng['serversettings']['phpfpm_settings']['idle_timeout']['description'] = 'Timeout-Einstellung für PHP-FPM FastCGI.';
// ADDED IN 0.9.27-svn2
$lng['admin']['delete_statistics'] = 'Statistiken Löschen';
@@ -1556,7 +1558,7 @@ $lng['serversettings']['panel_password_special_char_required']['description'] =
$lng['serversettings']['panel_password_special_char']['title'] = 'Sonderzeichen-Liste';
$lng['serversettings']['panel_password_special_char']['description'] = 'Mindestens eines dieser Sonderzeichen muss in dem Passwort vorkommen, sofern die Sonderzeichen-Option aktiviert ist.';
$lng['phpfpm']['use_mod_proxy']['title'] = 'Verwende mod_proxy / mod_proxy_fcgi';
-$lng['phpfpm']['use_mod_proxy']['description'] = 'Diese Option kann aktiviert werden, um php-fpm via mod_proxy_fcgi einzubinden. Dies setzt mindestens apache-2.4.9 voraus';
+$lng['phpfpm']['use_mod_proxy']['description'] = 'Muss gesetzt sein bei Debian 9.x (Stretch). Diese Option kann aktiviert werden, um php-fpm via mod_proxy_fcgi einzubinden. Dies setzt mindestens apache-2.4.9 voraus';
$lng['error']['no_phpinfo'] = 'Entschuldigung, es ist nicht möglich die phpinfo() auszulesen.';
$lng['admin']['movetoadmin'] = 'Kunde verschieben';
@@ -1570,16 +1572,16 @@ $lng['domains']['import_file'] = 'CSV-Datei';
$lng['success']['domain_import_successfully'] = 'Erfolgreich %s Domains importiert.';
$lng['error']['domain_import_error'] = 'Der folgende Fehler trat beim Importieren der Domains auf: %s';
$lng['admin']['note'] = 'Hinweis';
-$lng['domains']['import_description'] = 'Detaillierte Informationen über den Aufbau der Importdatei und einen erfolgreichen Import gibt es hier: http://redmine.froxlor.org/projects/froxlor/wiki/DomainBulkActionDoc (englisch)';
+$lng['domains']['import_description'] = 'Detaillierte Informationen über den Aufbau der Importdatei und einen erfolgreichen Import gibt es hier: https://github.com/Froxlor/Froxlor/wiki/Domain-import-documenation (englisch)';
$lng['usersettings']['custom_notes']['title'] = 'Eigene Notizen';
$lng['usersettings']['custom_notes']['description'] = 'Hier können Notizen je nach Lust und Laune eingetragen werden. Diese werden in der Administrator/Kunden-Übersicht bei dem jeweiligen Benutzer angezeigt.';
$lng['usersettings']['custom_notes']['show'] = 'Zeige die Notizen auf dem Dashboard des Benutzers';
$lng['error']['fcgidandphpfpmnogoodtogether'] = 'FCGID und PHP-FPM können nicht gleichzeitig aktiviert werden.';
// Added in Froxlor 0.9.34
-$lng['admin']['configfiles']['legend'] = 'Du konfigurierst nun einen Service/Daemon. Die folgende Legende zeigt unsere Nomenklatur.';
+$lng['admin']['configfiles']['legend'] = 'Du konfigurierst nun einen Service/Daemon.
';
$lng['admin']['configfiles']['commands'] = 'Kommandos: 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.';
-$lng['admin']['configfiles']['files'] = 'Konfigurationsdateien: Dies ist der Inhalt einer Konfigurationsdatei. 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.
Beachten Sie: Das MySQL-Passwort wurde aus Sicherheitsgründen nicht ersetzt. Bitte ersetzen Sie "MYSQL_PASSWORD" manuell durch das entsprechende Passwort. Falls Sie es vergessen haben sollten, finden Sie es in der Datei "lib/userdata.inc.php".';
+$lng['admin']['configfiles']['files'] = 'Konfigurationsdateien: 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.
Bitte beachten: Das MySQL-Passwort wurde aus Sicherheitsgründen nicht ersetzt. Bitte ersetze "FROXLOR_MYSQL_PASSWORD" manuell oder nutze 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".';
$lng['serversettings']['apache_itksupport']['title'] = 'Anpassungen für Apache ITK-MPM verwenden';
$lng['serversettings']['apache_itksupport']['description'] = '
ACHTUNG: Dieses Feature befindet sich noch im Test.';
$lng['error']['sslredirectonlypossiblewithsslipport'] = 'Die Nutzung von Let\'s Encrypt ist nur möglich, wenn die Domain mindestens eine IP/Port - Kombination mit aktiviertem SSL zugewiesen hat.';
-$lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt kann (noch) nicht mit Wildcard-Domains umgehen. Bitte den ServerAlias auf WWW setzen oder deaktivieren';
+$lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt kann in ACME v1 nicht mit Wildcard-Domains umgehen. Bitte den ServerAlias auf WWW setzen oder deaktivieren';
$lng['panel']['letsencrypt'] = 'Benutzt Let\'s encrypt';
$lng['crondesc']['cron_letsencrypt'] = 'aktualisiert Let\'s Encrypt Zertifikate';
$lng['serversettings']['letsencryptca']['title'] = "Let's Encrypt Umgebung";
@@ -1717,4 +1719,56 @@ $lng['admin']['domain_hsts_maxage']['description'] = '"max-age" Wert für den St
$lng['admin']['domain_hsts_incsub']['title'] = 'Inkludiere HSTS für jede Subdomain';
$lng['admin']['domain_hsts_incsub']['description'] = 'Die optionale "includeSubDomains" Direktive, wenn vorhanden, signalisiert dem UA, dass die HSTS Regel für diese Domain und auch jede Subdomain dieser gilt.';
$lng['admin']['domain_hsts_preload']['title'] = 'Füge Domain in die HSTS preload Liste hinzu';
-$lng['admin']['domain_hsts_preload']['description'] = 'Wenn die Domain in die HSTS preload Liste, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktiviere diese Einstellung.
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.
Beachte Details unter hstspreload.appspot.com/#removal bevor ein Header mit "preload" gesendet wird.';
+$lng['admin']['domain_hsts_preload']['description'] = 'Wenn die Domain in die HSTS preload Liste, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktiviere diese Einstellung.
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.
Beachte Details unter hstspreload.appspot.com/#removal bevor ein Header mit "preload" gesendet wird.';
+
+$lng['serversettings']['http2_support']['title'] = 'HTTP2 Unterstützung';
+$lng['serversettings']['http2_support']['description'] = 'Aktiviere HTTP2 Unterstützung für SSL.
NUR AKTIVIEREN, WENN DER WEBSERVER DIESE FUNKTION UNTERSTÜTZT (nginx version 1.9.5+, apache2 version 2.4.17+)';
+
+$lng['error']['noipportgiven'] = 'Keine IP/Port angegeben';
+
+// Added in froxlor 0.9.38.8
+$lng['admin']['domain_ocsp_stapling']['title'] = 'OCSP stapling';
+$lng['admin']['domain_ocsp_stapling']['description'] = 'Siehe Wikipedia für eine ausführliche Beschreibung von OCSP-Stapling';
+$lng['admin']['domain_ocsp_stapling']['nginx_version_warning'] = '
WARNUNG: 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.';
+$lng['serversettings']['ssl']['apache24_ocsp_cache_path']['title'] = 'Apache 2.4: Pfad zum OCSP-Stapling-Cache';
+$lng['serversettings']['ssl']['apache24_ocsp_cache_path']['description'] = 'Konfiguriert den Cache-Pfad zum Zwischenspeichern der OCSP-Antworten,
die an TLS-Handshakes angehängt werden.';
+$lng['serversettings']['nssextrausers']['title'] = 'Verwende libnss-extrausers anstatt libnss-mysql';
+$lng['serversettings']['nssextrausers']['description'] = 'Lese Benutzer nicht direkt aus der Datenbank sondern über Dateien, bitte nur aktivieren, wenn die entsprechende Konfiguration vorgenommen wurde (System -> libnss-extrausers).
Nur für Debian/Ubuntu (oder wenn libnss-extrausers manuell kompiliert wurde!)';
+$lng['admin']['domain_http2']['title'] = 'HTTP2 Unterstützung';
+$lng['admin']['domain_http2']['description'] = 'Siehe Wikipedia für eine ausführliche Beschreibung von HTTP2';
+$lng['admin']['testmail'] = 'SMTP Test';
+$lng['success']['testmailsent'] = 'Test E-Mail erfolgreich gesendet';
+$lng['serversettings']['disable_le_selfcheck']['title'] = "Deaktiviere Let's Encrypt lokale Selbstprüfung";
+$lng['serversettings']['disable_le_selfcheck']['description'] = "Wenn aktiviert wird Froxlor keine Erreichbarkeitsprüfung des Tokens vornehmen. Nötig bei ge-NAT-eten IP's oder Ähnlichem";
+$lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM Versionen';
+$lng['admin']['phpsettings']['activephpconfigs'] = 'In Verwendung für PHP-Konfiguration(en)';
+$lng['admin']['phpsettingsforsubdomains'] = 'PHP-Config für alle Subdomains übernehmen:';
+$lng['serversettings']['phpsettingsforsubdomains']['description'] = 'Wenn ja, wird die gewählte PHP-Config für alle Subdomains übernommen';
+$lng['serversettings']['leapiversion']['title'] = "Wähle Let's Encrypt ACME Implementierung";
+$lng['serversettings']['leapiversion']['description'] = "Wähle zwischen ACME v1 und ACME v2 Implementierung von Let's Encrypt.";
+$lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt kann in ACME v2 Wildcard-Domains nur via DNS validieren, sorry. Bitte den ServerAlias auf WWW setzen oder deaktivieren';
+$lng['admin']['phpsettings']['pass_authorizationheader'] = 'Füge "-pass-header Authorization" / "CGIPassAuth On" in Vhosts ein';
+$lng['serversettings']['ssl']['ssl_protocols']['title'] = 'SSL Protokollversion festlegen';
+$lng['serversettings']['ssl']['ssl_protocols']['description'] = 'Dies ist eine Liste von SSL/TLS Protokollversionen die genutzt werden sollen (oder auch nicht genutzt werden sollen), wenn SSL verwendet wird. Hinweis: Ältere Browser sind möglicherweise nicht vollständig zum neusten Protokoll kompatibel.
Standard-Wert ist:TLSv1, TLSv1.2
';
+$lng['serversettings']['phpfpm_settings']['limit_extensions']['title'] = 'Erlaubte Dateiendungen';
+$lng['serversettings']['phpfpm_settings']['limit_extensions']['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';
+$lng['phpfpm']['ini_flags'] = 'Mögliche php_flags für die php.ini. Pro Zeile eine Direktive';
+$lng['phpfpm']['ini_values'] = 'Mögliche php_values für die php.ini. Pro Zeile eine Direktive';
+$lng['phpfpm']['ini_admin_flags'] = 'Mögliche php_admin_flags für die php.ini. Pro Zeile eine Direktive';
+$lng['phpfpm']['ini_admin_values'] = 'Mögliche php_admin_values für die php.ini. Pro Zeile eine Direktive';
+$lng['serversettings']['phpfpm_settings']['envpath'] = 'Pfade für die PATH Umgebungsvariable. Leerlassen, um keine PATH Umgebungsvariable zu setzen.';
+$lng['success']['settingsimported'] = 'Einstellungnen erfolgreich importiert';
+$lng['error']['jsonextensionnotfound'] = 'Diese Funktion benötigt die PHP json-Erweiterung.';
+
+// added in froxlor 0.9.39
+$lng['admin']['plans']['name'] = 'Plan Name';
+$lng['admin']['plans']['description'] = 'Beschreibung';
+$lng['admin']['plans']['last_update'] = 'Zuletzt aktualisiert';
+$lng['admin']['plans']['plans'] = 'Hosting Pläne';
+$lng['admin']['plans']['plan_details'] = 'Plan Details';
+$lng['admin']['plans']['add'] = 'Neuen Plan anlegen';
+$lng['admin']['plans']['edit'] = 'Plan editieren';
+$lng['admin']['plans']['use_plan'] = 'Plan übernehmen';
+$lng['question']['plan_reallydelete'] = 'Wollen Sie den Hosting-Plan "%s" wirklich löschen?';
+$lng['admin']['notryfiles']['title'] = 'Keine generierte try_files Anweisung';
+$lng['admin']['notryfiles']['description'] = 'Wähle "Ja", wenn eine eigene try_files Direktive in den "eigenen Vhost Einstellungen" angegeben werden soll (z.B. nötig für manche Wordpress Plugins).';
diff --git a/lng/italian.lng.php b/lng/italian.lng.php
index c636e70e..7962e250 100644
--- a/lng/italian.lng.php
+++ b/lng/italian.lng.php
@@ -435,7 +435,7 @@ $lng['panel']['translator'] = 'Traduttore';
$lng['error']['stringformaterror'] = 'Il valore per il campo "%s" non è nel formato atteso.';
// ADDED IN 1.2.15-rc1
-// Translated by marone42@googlemail.com on 03/15/2007 (see https://trac.froxlor.org/ticket/126#comment:21)
+// Translated by marone42@googlemail.com on 03/15/2007
$lng['admin']['phpversion'] = 'Versione PHP';
$lng['admin']['mysqlserverversion'] = 'Versione MySQL Server';
@@ -1050,7 +1050,7 @@ $lng['dkim']['dkim_notes']['description'] = 'Nota potrebbe essere di interesse,
$lng['dkim']['dkim_add_adsp']['title'] = 'Aggiungi un valore DKIM ADSP';
$lng['dkim']['dkim_add_adsp']['description'] = 'Se non si sa di cosa si tratta, lasciare "enabled"';
$lng['dkim']['dkim_add_adsppolicy']['title'] = 'Regola ADSP';
-$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Per ulteriori informazioni su questa impostazione leggere DKIM ADSP policies';
+$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Per ulteriori informazioni su questa impostazione leggere DKIM ADSP policies';
$lng['admin']['cron']['cronsettings'] = 'Impostazioni Cronjob';
$lng['cron']['cronname'] = 'Nome cronjob';
@@ -1171,7 +1171,7 @@ $lng['serversettings']['perl_path']['description'] = 'Rilevante solo se si utili
// ADDED IN FROXLOR 0.9.12-svn1
$lng['admin']['fcgid_settings'] = 'FCGID';
$lng['serversettings']['mod_fcgid_ownvhost']['title'] = 'Abilita FCGID per i vhost Froxlor';
-$lng['serversettings']['mod_fcgid_ownvhost']['description'] = 'Se attivato, Froxlor verrà eseguito con un utente locale
ATTENZIONE:Questo richiede una configurazione manuale, vedi FCGID - handbook';
+$lng['serversettings']['mod_fcgid_ownvhost']['description'] = 'Se attivato, Froxlor verrà eseguito con un utente locale
ATTENZIONE:Questo richiede una configurazione manuale, vedi FCGID - handbook';
$lng['admin']['mod_fcgid_user'] = 'Utente locale per FCGID (Froxlor vhost)';
$lng['admin']['mod_fcgid_group'] = 'Gruppo locale per FCGID (Froxlor vhost)';
@@ -1361,7 +1361,7 @@ $lng['admin']['store_defaultindex'] = 'Archivio del file indice predefinito al p
$lng['admin']['ipsandports']['ssl_cert_chainfile']['title'] = 'Percorso al file catena dei certificati SSL';
$lng['admin']['ipsandports']['ssl_cert_chainfile']['description'] = 'Principalmente Bundle CA, o similare, presubilmente vuoi impostare questo se hai acquistato un certificato SSL.';
$lng['serversettings']['phpfpm']['title'] = 'Abilita php-fpm';
-$lng['serversettings']['phpfpm']['description'] = 'Questa impostazione richiede una configurazione speciale del server web. Vedi il manuale FPM per Apache2 o nginx';
+$lng['serversettings']['phpfpm']['description'] = 'Questa impostazione richiede una configurazione speciale del server web. Vedi il manuale FPM per Apache2 o nginx';
$lng['serversettings']['phpfpm_settings']['aliasconfigdir'] = 'Configurazione cartella Alias per php-fpm';
$lng['gender']['title'] = 'Titolo';
$lng['gender']['male'] = 'Sig.';
@@ -1795,7 +1795,7 @@ $lng['domains']['import_file'] = 'File CSV';
$lng['success']['domain_import_successfully'] = 'Importato %s dominii con successo.';
$lng['error']['domain_import_error'] = 'Il seguente errore è occorsonell \'importazione di dominii: %s';
$lng['admin']['note'] = 'Nota';
-$lng['domains']['import_description'] = 'Per ottenere informazioni dettagliate sulla struttura del file di importazione e su come importare con successo, visita http://redmine.froxlor.org/projects/froxlor/wiki/DomainBulkActionDoc';
+$lng['domains']['import_description'] = 'Per ottenere informazioni dettagliate sulla struttura del file di importazione e su come importare con successo, visita https://github.com/Froxlor/Froxlor/wiki/Domain-import-documenation';
$lng['usersettings']['custom_notes']['title'] = 'Note personali';
$lng['usersettings']['custom_notes']['description'] = 'Sentiti libero di inserire qualsi nota vuoi o necessiti qui. Apparirano nel riepilogo dell\'amministratore/cliente perl \'utente corrispondente.';
$lng['usersettings']['custom_notes']['show'] = 'Mostra le tue note nel cruscotto dell\'utente';
diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php
index 46c83926..68a5ba3c 100644
--- a/scripts/classes/class.DnsBase.php
+++ b/scripts/classes/class.DnsBase.php
@@ -233,7 +233,7 @@ abstract class DnsBase
$pubkey_file_handler = fopen($pubkey_filename, "w");
fwrite($pubkey_file_handler, $domain['dkim_pubkey']);
fclose($pubkey_file_handler);
- safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename));
+ safe_exec("chmod 0644 " . escapeshellarg($pubkey_filename));
}
$dkimdomains .= $domain['domain'] . "\n";
diff --git a/scripts/classes/class.Extrausers.php b/scripts/classes/class.Extrausers.php
new file mode 100644
index 00000000..2ad38b73
--- /dev/null
+++ b/scripts/classes/class.Extrausers.php
@@ -0,0 +1,83 @@
+ (2017-)
+ * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
+ * @package Cron
+ *
+ */
+class Extrausers
+{
+
+ public static function generateFiles(&$cronlog)
+ {
+ // passwd
+ $passwd = '/var/lib/extrausers/passwd';
+ $sql = "SELECT username,'x' as password,uid,gid,'Froxlor User' as comment,homedir,shell, login_enabled FROM ftp_users ORDER BY uid ASC";
+ self::_generateFile($passwd, $sql, $cronlog);
+
+ // group
+ $group = '/var/lib/extrausers/group';
+ $sql = "SELECT groupname,'x' as password,gid,members FROM ftp_groups ORDER BY gid ASC";
+ self::_generateFile($group, $sql, $cronlog);
+
+ // shadow
+ $shadow = '/var/lib/extrausers/shadow';
+ $sql = "SELECT username,password FROM ftp_users ORDER BY gid ASC";
+ self::_generateFile($shadow, $sql, $cronlog);
+
+ // set correct permissions
+ @chmod('/var/lib/extrausers/', 0755);
+ @chmod('/var/lib/extrausers/passwd', 0644);
+ @chmod('/var/lib/extrausers/group', 0644);
+ @chmod('/var/lib/extrausers/shadow', 0640);
+ }
+
+ private static function _generateFile($file, $query, &$cronlog)
+ {
+ $type = basename($file);
+ $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Creating ' . $type . ' file');
+
+ if (! file_exists($file)) {
+ $cronlog->logAction(CRON_ACTION, LOG_NOTICE, $type . ' file does not yet exist');
+ @mkdir(dirname($file), 0750, true);
+ touch($file);
+ }
+
+ $data_sel_stmt = Database::query($query);
+ $data_content = "";
+ $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Writing ' . $data_sel_stmt->rowCount() . ' entries to ' . $type . ' file');
+ while ($u = $data_sel_stmt->fetch(PDO::FETCH_ASSOC)) {
+ switch ($type) {
+ case 'passwd':
+ if ($u['login_enabled'] != 'Y') {
+ $u['password'] = '*';
+ $u['shell'] = '/bin/false';
+ $u['comment'] = 'Locked Froxlor User';
+ }
+ $line = $u['username'] . ':' . $u['password'] . ':' . $u['uid'] . ':' . $u['gid'] . ':' . $u['comment'] . ':' . $u['homedir'] . ':' . $u['shell'] . PHP_EOL;
+ break;
+ case 'group':
+ $line = $u['groupname'] . ':' . $u['password'] . ':' . $u['gid'] . ':' . $u['members'] . PHP_EOL;
+ break;
+ case 'shadow':
+ $line = $u['username'] . ':' . $u['password'] . ':' . floor(time() / 86400 - 1) . ':0:99999:7:::' . PHP_EOL;
+ break;
+ }
+ $data_content .= $line;
+ }
+ if (file_put_contents($file, $data_content) !== false) {
+ $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Succesfully wrote ' . $type . ' file');
+ } else {
+ $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Error when writing ' . $type . ' file entries');
+ }
+ }
+}
diff --git a/scripts/froxlor_master_cronjob.php b/scripts/froxlor_master_cronjob.php
index 3e4923d4..a6d0d817 100644
--- a/scripts/froxlor_master_cronjob.php
+++ b/scripts/froxlor_master_cronjob.php
@@ -68,6 +68,9 @@ for ($x = 1; $x < count($argv); $x++) {
$cronlog->setCronDebugFlag(defined('CRON_DEBUG_FLAG'));
+$tasks_cnt_stmt = Database::query("SELECT COUNT(*) as jobcnt FROM `panel_tasks`");
+$tasks_cnt = $tasks_cnt_stmt->fetch(PDO::FETCH_ASSOC);
+
// do we have anything to include?
if (count($jobs_to_run) > 0) {
// include all jobs we want to execute
@@ -76,6 +79,22 @@ if (count($jobs_to_run) > 0) {
$cronfile = getCronFile($cron);
require_once $cronfile;
}
+
+ if ($tasks_cnt['jobcnt'] > 0)
+ {
+ if (Settings::Get('system.nssextrausers') == 1)
+ {
+ include_once makeCorrectFile(FROXLOR_INSTALL_DIR.'/scripts/classes/class.Extrausers.php');
+ Extrausers::generateFiles($cronlog);
+ }
+
+ // clear NSCD cache if using fcgid or fpm, #1570
+ if (Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
+ $false_val = false;
+ safe_exec('nscd -i passwd 1> /dev/null', $false_val, array('>'));
+ safe_exec('nscd -i group 1> /dev/null', $false_val, array('>'));
+ }
+ }
}
fwrite($debugHandler, 'Cronfiles have been included' . "\n");
diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php
index 2101fcca..b25b9a8d 100644
--- a/scripts/jobs/cron_letsencrypt.php
+++ b/scripts/jobs/cron_letsencrypt.php
@@ -20,6 +20,12 @@ if (! defined('MASTER_CRONJOB'))
*
*/
+if (Settings::Get('system.leapiversion') == '2') {
+ // use ACME v2 is specified
+ require_once __DIR__ . '/cron_letsencrypt_v2.php';
+ exit;
+}
+
$cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating Let's Encrypt certificates");
if (! extension_loaded('curl')) {
@@ -43,6 +49,7 @@ $certificates_stmt = Database::query("
dom.`ssl_redirect`,
cust.`leprivatekey`,
cust.`lepublickey`,
+ cust.`leregistered`,
cust.`customerid`,
cust.`loginname`
FROM
@@ -103,6 +110,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') {
'documentroot' => FROXLOR_INSTALL_DIR,
'leprivatekey' => Settings::Get('system.leprivatekey'),
'lepublickey' => Settings::Get('system.lepublickey'),
+ 'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => null,
'ssl_cert_file' => null,
diff --git a/scripts/jobs/cron_letsencrypt_v2.php b/scripts/jobs/cron_letsencrypt_v2.php
new file mode 100644
index 00000000..6b1148d3
--- /dev/null
+++ b/scripts/jobs/cron_letsencrypt_v2.php
@@ -0,0 +1,294 @@
+
+ * @author Froxlor team
";
+ $san_list .= $san . "
";
}
}
}
diff --git a/templates/Sparkle/admin/configfiles/configfiles.tpl b/templates/Sparkle/admin/configfiles/configfiles.tpl
index f35b4051..d469aed3 100644
--- a/templates/Sparkle/admin/configfiles/configfiles.tpl
+++ b/templates/Sparkle/admin/configfiles/configfiles.tpl
@@ -11,25 +11,15 @@ $header
+
-
-
-
-
-
+
+ class="disabled">
{$row['domain']}
+
+
+ {$title}
+
+
+
+ {$title}
+
+
+
+ {$lng['menue']['phpsettings']['maintitle']}
+
+
+
+
+
+
+
+ $tablecontent
+
+ {$lng['admin']['phpsettings']['description']}
+ {$lng['admin']['phpsettings']['activephpconfigs']}
+ {$lng['serversettings']['phpfpm_settings']['reload']}
+ {$lng['serversettings']['phpfpm_settings']['configdir']}
+ {$lng['serversettings']['phpfpm_settings']['pm']}
+ {$lng['panel']['options']}
+
+
diff --git a/templates/Sparkle/admin/phpconfig/overview.tpl b/templates/Sparkle/admin/phpconfig/overview.tpl
index 6fcfc428..aac895ac 100644
--- a/templates/Sparkle/admin/phpconfig/overview.tpl
+++ b/templates/Sparkle/admin/phpconfig/overview.tpl
@@ -19,7 +19,11 @@ $header
{$row['description']}
+ {$configs}
+ {$row['reload_cmd']}
+ {$row['config_dir']}
+ {$row['pm']}
+
+
+
+
+
+
+
+
{$lng['admin']['phpsettings']['description']}
{$lng['admin']['phpsettings']['activedomains']}
- {$lng['admin']['phpsettings']['binary']}
+ {$lng['admin']['phpsettings']['fpmdesc']}
+ {$lng['admin']['phpsettings']['binary']}
+ {$lng['admin']['phpsettings']['file_extensions']}
{$lng['panel']['options']}
diff --git a/templates/Sparkle/admin/phpconfig/overview_overview.tpl b/templates/Sparkle/admin/phpconfig/overview_overview.tpl
index 2df5fd6f..0ccc9f58 100644
--- a/templates/Sparkle/admin/phpconfig/overview_overview.tpl
+++ b/templates/Sparkle/admin/phpconfig/overview_overview.tpl
@@ -1,7 +1,11 @@
{$row['description']}
{$domains}
- {$row['binary']}
+ {$row['fpmdesc']}
+ {$row['binary']}
+ {$row['file_extensions']}
diff --git a/templates/Sparkle/admin/plans/plans.tpl b/templates/Sparkle/admin/plans/plans.tpl
new file mode 100644
index 00000000..ed9c4432
--- /dev/null
+++ b/templates/Sparkle/admin/plans/plans.tpl
@@ -0,0 +1,59 @@
+$header
+
+
+
+ {$lng['admin']['plans']['plans']}
+
+
+
+ {$title}
+
+
+
+ {$title}
+
+
diff --git a/templates/Sparkle/admin/settings/importexport/index.tpl b/templates/Sparkle/admin/settings/importexport/index.tpl
new file mode 100644
index 00000000..41da2dfb
--- /dev/null
+++ b/templates/Sparkle/admin/settings/importexport/index.tpl
@@ -0,0 +1,26 @@
+$header
+{$row['name']}
+ {$row['description']}
+ {$row['adminname']}
+ {$row['ts_format']}
+
+
+
+
+
+
+
+
+
+
+
+ {$lng['admin']['configfiles']['importexport']}
+
+
diff --git a/templates/Sparkle/admin/settings/settings_overview.tpl b/templates/Sparkle/admin/settings/settings_overview.tpl
index c1cd32ac..4a59c4c7 100644
--- a/templates/Sparkle/admin/settings/settings_overview.tpl
+++ b/templates/Sparkle/admin/settings/settings_overview.tpl
@@ -3,7 +3,8 @@
{$lng['admin']['serversettings']}
- [{$lng['admin']['configfiles']['compactoverview']}]
+ [{$lng['admin']['configfiles']['compactoverview']}]
+ [{$lng['admin']['configfiles']['importexport']}]
diff --git a/templates/Sparkle/admin/settings/testmail.tpl b/templates/Sparkle/admin/settings/testmail.tpl
new file mode 100644
index 00000000..6b882563
--- /dev/null
+++ b/templates/Sparkle/admin/settings/testmail.tpl
@@ -0,0 +1,48 @@
+$header
+
{$lng['admin']['serversettings']}
- [{$lng['admin']['configfiles']['overview']}]
+ [{$lng['admin']['configfiles']['overview']}]
+ [{$lng['admin']['configfiles']['importexport']}]
+
+
+ {$lng['admin']['testmail']}
+
+
+
+
+ {$lng['serversettings']['mail_smtp_user']}
+ Host
+ Port
+ SMTP Auth
+ TLS
+
+
+ {$mail_smtp_user}
+ {$mail_smtp_host}
+ {$mail_smtp_port}
+
+ ![]()
+ ![]()
+
+