diff --git a/install/froxlor.sql b/install/froxlor.sql
index b7f30869..fd52390c 100644
--- a/install/froxlor.sql
+++ b/install/froxlor.sql
@@ -198,6 +198,7 @@ CREATE TABLE `panel_customers` (
`lepublickey` mediumtext default NULL,
`leprivatekey` mediumtext default NULL,
`leregistered` tinyint(1) NOT NULL default '0',
+ `leaccount` varchar(255) default '',
`allowed_phpconfigs` varchar(500) NOT NULL default '',
PRIMARY KEY (`customerid`),
UNIQUE KEY `loginname` (`loginname`)
@@ -654,6 +655,7 @@ opcache.interned_strings_buffer'),
('system', 'hsts_incsub', '0'),
('system', 'hsts_preload', '0'),
('system', 'leregistered', '0'),
+ ('system', 'leaccount', ''),
('system', 'nssextrausers', '0'),
('system', 'disable_le_selfcheck', '0'),
('system', 'ssl_protocols', 'TLSv1,TLSv1.2'),
@@ -694,7 +696,7 @@ opcache.interned_strings_buffer'),
('panel', 'password_special_char', '!?<>§$%+#=@'),
('panel', 'customer_hide_options', ''),
('panel', 'version', '0.10.0'),
- ('panel', 'db_version', '201805290');
+ ('panel', 'db_version', '201809180');
DROP TABLE IF EXISTS `panel_tasks`;
diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php
index da6bc8f1..6895df63 100644
--- a/install/lib/class.FroxlorInstall.php
+++ b/install/lib/class.FroxlorInstall.php
@@ -964,84 +964,39 @@ class FroxlorInstall
} else {
$content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
}
-
+
+ // check for session-extension
+ $this->_requirementCheckFor($content, $_die, 'session', false, 'phpsession');
+
+ // check for ctype-extension
+ $this->_requirementCheckFor($content, $_die, 'ctype', false, 'phpctype');
+
+ // check for SimpleXML-extension
+ $this->_requirementCheckFor($content, $_die, 'simplexml', false, 'phpsimplexml');
+
// check for xml-extension
- $content .= $this->_status_message('begin', $this->_lng['requirements']['phpxml']);
-
- if (! extension_loaded('xml')) {
- $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']);
- $_die = true;
- } else {
- $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
- }
+ $this->_requirementCheckFor($content, $_die, 'xml', false, 'phpxml');
// check for filter-extension
- $content .= $this->_status_message('begin', $this->_lng['requirements']['phpfilter']);
-
- if (! extension_loaded('filter')) {
- $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']);
- $_die = true;
- } else {
- $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
- }
+ $this->_requirementCheckFor($content, $_die, 'filter', false, 'phpfilter');
// check for posix-extension
- $content .= $this->_status_message('begin', $this->_lng['requirements']['phpposix']);
-
- if (! extension_loaded('posix')) {
- $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']);
- $_die = true;
- } else {
- $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
- }
-
+ $this->_requirementCheckFor($content, $_die, 'posix', false, 'phpposix');
+
// check for mbstring-extension
- $content .= $this->_status_message('begin', $this->_lng['requirements']['phpmbstring']);
-
- if (! extension_loaded('mbstring')) {
- $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']);
- $_die = true;
- } else {
- $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
- }
+ $this->_requirementCheckFor($content, $_die, 'mbstring', false, 'phpmbstring');
// check for curl extension
- $content .= $this->_status_message('begin', $this->_lng['requirements']['phpcurl']);
-
- if (! extension_loaded('curl')) {
- $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']);
- $_die = true;
- } else {
- $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
- }
+ $this->_requirementCheckFor($content, $_die, 'curl', false, 'phpcurl');
// check for json extension
- $content .= $this->_status_message('begin', $this->_lng['requirements']['phpjson']);
-
- if (! extension_loaded('json')) {
- $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']);
- $_die = true;
- } else {
- $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
- }
+ $this->_requirementCheckFor($content, $_die, 'json', false, 'phpjson');
// check for bcmath extension
- $content .= $this->_status_message('begin', $this->_lng['requirements']['phpbcmath']);
-
- if (! extension_loaded('bcmath')) {
- $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['bcmathdescription']);
- } else {
- $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
- }
+ $this->_requirementCheckFor($content, $_die, 'bcmath', true, 'phpbcmath', 'bcmathdescription');
// check for zip extension
- $content .= $this->_status_message('begin', $this->_lng['requirements']['phpzip']);
-
- if (! extension_loaded('zip')) {
- $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['zipdescription']);
- } else {
- $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
- }
+ $this->_requirementCheckFor($content, $_die, 'zip', true, 'phpzip', 'zipdescription');
// check for open_basedir
$content .= $this->_status_message('begin', $this->_lng['requirements']['openbasedir']);
@@ -1051,6 +1006,16 @@ class FroxlorInstall
} else {
$content .= $this->_status_message('green', 'off');
}
+
+ // check for mysqldump binary in order to backup existing database
+ $content .= $this->_status_message('begin', $this->_lng['requirements']['mysqldump']);
+
+ if (file_exists("/usr/bin/mysqldump") || file_exists("/usr/local/bin/mysqldump")) {
+ $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
+ } else {
+ $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['mysqldumpmissing']);
+ }
+
$content .= "";
// check if we have unrecoverable errors
@@ -1073,6 +1038,22 @@ class FroxlorInstall
'pagenavigation' => $navigation
);
}
+
+ private function _requirementCheckFor(&$content, &$_die, $ext = '', $optional = false, $lng_txt = "", $lng_desc = "")
+ {
+ $content .= $this->_status_message('begin', $this->_lng['requirements'][$lng_txt]);
+
+ if (! extension_loaded($ext)) {
+ if (!$optional) {
+ $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']);
+ $_die = true;
+ } else {
+ $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements'][$lng_desc]);
+ }
+ } else {
+ $content .= $this->_status_message('green', $this->_lng['requirements']['installed']);
+ }
+ }
/**
* send no-caching headers and set the default timezone
@@ -1141,12 +1122,24 @@ class FroxlorInstall
}
}
- $lngfile = $this->_basepath . '/install/lng/' . $this->_activelng . '.lng.php';
+ // require english base language as fallback
+ $lngfile = $this->_basepath . '/install/lng/' . $standardlanguage . '.lng.php';
if (file_exists($lngfile)) {
// includes file /lng/$language.lng.php if it exists
require $lngfile;
$this->_lng = $lng;
}
+
+ // require chosen language if not english
+ if ($this->_activelng != $standardlanguage)
+ {
+ $lngfile = $this->_basepath . '/install/lng/' . $this->_activelng . '.lng.php';
+ if (file_exists($lngfile)) {
+ // includes file /lng/$language.lng.php if it exists
+ require $lngfile;
+ $this->_lng = $lng;
+ }
+ }
}
/**
diff --git a/install/lng/english.lng.php b/install/lng/english.lng.php
index a4cea2dc..c1020569 100644
--- a/install/lng/english.lng.php
+++ b/install/lng/english.lng.php
@@ -28,6 +28,9 @@ $lng['requirements']['newerphpprefered'] = 'Good, but php-5.6 is prefered.';
$lng['requirements']['phpmagic_quotes_runtime'] = 'magic_quotes_runtime...';
$lng['requirements']['phpmagic_quotes_runtime_description'] = 'PHP setting "magic_quotes_runtime" must be set to "Off". We have disabled it temporary for now please fix the coresponding php.ini.';
$lng['requirements']['phppdo'] = 'PHP PDO extension and PDO-MySQL driver...';
+$lng['requirements']['phpsession'] = 'PHP session-extension...';
+$lng['requirements']['phpctype'] = 'PHP ctype-extension...';
+$lng['requirements']['phpsimplexml'] = 'PHP SimpleXML-extension...';
$lng['requirements']['phpxml'] = 'PHP XML-extension...';
$lng['requirements']['phpfilter'] = 'PHP filter-extension...';
$lng['requirements']['phpposix'] = 'PHP posix-extension...';
@@ -40,6 +43,8 @@ $lng['requirements']['bcmathdescription'] = 'Traffic-calculation related functio
$lng['requirements']['zipdescription'] = 'The auto-update feature requires the zip extension.';
$lng['requirements']['openbasedir'] = 'open_basedir...';
$lng['requirements']['openbasedirenabled'] = 'Froxlor will not work properly with open_basedir enabled. Please disable open_basedir for Froxlor in the coresponding php.ini';
+$lng['requirements']['mysqldump'] = 'MySQL dump tool';
+$lng['requirements']['mysqldumpmissing'] = 'Automatic backup of possible existing database is not possible. Please install mysql-client tools';
$lng['requirements']['diedbecauseofrequirements'] = 'Cannot install Froxlor without these requirements! Try to fix them and retry.';
$lng['requirements']['froxlor_succ_checks'] = 'All requirements are satisfied';
diff --git a/install/lng/german.lng.php b/install/lng/german.lng.php
index 4e4638db..49998936 100644
--- a/install/lng/german.lng.php
+++ b/install/lng/german.lng.php
@@ -28,6 +28,9 @@ $lng['requirements']['newerphpprefered'] = 'Passt, aber php-5.6 wird bevorzugt.'
$lng['requirements']['phpmagic_quotes_runtime'] = 'magic_quotes_runtime';
$lng['requirements']['phpmagic_quotes_runtime_description'] = 'Die PHP Einstellung "magic_quotes_runtime" muss deaktiviert sein ("Off"). Die Einstellung wurde temporär deaktiviert, bitte ändern Sie diese in der entsprechenden php.ini.';
$lng['requirements']['phppdo'] = 'PHP PDO Erweiterung und PDO-MySQL Treiber...';
+$lng['requirements']['phpsession'] = 'PHP session-Erweiterung...';
+$lng['requirements']['phpctype'] = 'PHP ctype-Erweiterung...';
+$lng['requirements']['phpsimplexml'] = 'PHP SimpleXML-Erweiterung...';
$lng['requirements']['phpxml'] = 'PHP XML-Erweiterung...';
$lng['requirements']['phpfilter'] = 'PHP filter-Erweiterung...';
$lng['requirements']['phpposix'] = 'PHP posix-Erweiterung...';
@@ -40,6 +43,8 @@ $lng['requirements']['bcmathdescription'] = 'Traffic-Berechnungs bezogene Funkti
$lng['requirements']['zipdescription'] = 'Die Auto-Update Funktion benötigt die zip Erweiterung.';
$lng['requirements']['openbasedir'] = 'open_basedir genutzt wird...';
$lng['requirements']['openbasedirenabled'] = 'Froxlor wird mit aktiviertem open_basedir nicht vollständig funktionieren. Bitte deaktivieren Sie open_basedir für Froxlor in der entsprechenden php.ini';
+$lng['requirements']['mysqldump'] = 'MySQL dump Tool';
+$lng['requirements']['mysqldumpmissing'] = 'Ein automatisches Backup einer möglicherweise schon existierenden Datenbank nicht möglich. Bitte mysql-client installieren';
$lng['requirements']['diedbecauseofrequirements'] = 'Kann Froxlor ohne diese Voraussetzungen nicht installieren! Beheben Sie die angezeigten Probleme und versuchen Sie es erneut.';
$lng['requirements']['froxlor_succ_checks'] = 'Alle Vorraussetzungen sind erfüllt';
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 17432b94..6b07bc11 100644
--- a/install/updates/froxlor/0.9/update_0.9.inc.php
+++ b/install/updates/froxlor/0.9/update_0.9.inc.php
@@ -3998,3 +3998,16 @@ if (isDatabaseVersion('201805241')) {
updateToDbVersion('201805290');
}
}
+
+if (isDatabaseVersion('201805290')) {
+
+ showUpdateStep("Adding leaccount field to panel customers");
+ Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD COLUMN `leaccount` varchar(255) default '' AFTER `leregistered`;");
+ lastStepStatus(0);
+
+ showUpdateStep("Adding system setting for let's-encrypt account");
+ Settings::AddNew('system.leaccount', "");
+ lastStepStatus(0);
+
+ updateToDbVersion('201809180');
+}
diff --git a/lib/classes/cURL/class.HttpClient.php b/lib/classes/cURL/class.HttpClient.php
index 53a52114..6953624a 100644
--- a/lib/classes/cURL/class.HttpClient.php
+++ b/lib/classes/cURL/class.HttpClient.php
@@ -10,13 +10,15 @@ class HttpClient
*
* @return array
*/
- public static function urlGet($url)
+ public static function urlGet($url, $follow_location = true)
{
include FROXLOR_INSTALL_DIR . '/lib/version.inc.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERAGENT, 'Froxlor/' . $version);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ if ($follow_location) {
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ }
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
if ($output === false) {
diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php
index cdc848f6..acf80e86 100644
--- a/lib/classes/ssl/class.lescript.php
+++ b/lib/classes/ssl/class.lescript.php
@@ -82,7 +82,7 @@ class lescript
Settings::Set('system.leprivatekey', $keys['private']);
Settings::Set('system.leregistered', 0); // key is not registered
} else {
- $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private, `leregistered` = :registered " . "WHERE `customerid` = :customerid;");
+ $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private, `leregistered` = :registered WHERE `customerid` = :customerid;");
Database::pexecute($upd_stmt, array(
'public' => $keys['public'],
'private' => $keys['private'],
@@ -216,7 +216,7 @@ class lescript
// simple self check
if (Settings::Get('system.disable_le_selfcheck') == '0')
{
- $selfcheckpayload = HttpClient::urlGet($uri);
+ $selfcheckpayload = HttpClient::urlGet($uri, false);
if ($payload !== trim($selfcheckpayload)) {
$errmsg = json_encode(error_get_last());
if ($errmsg != "null") {
@@ -342,7 +342,7 @@ class lescript
if ($this->isFroxlorVhost) {
Settings::Set('system.leregistered', $state);
} else {
- $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `leregistered` = :registered " . "WHERE `customerid` = :customerid;");
+ $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `leregistered` = :registered WHERE `customerid` = :customerid;");
Database::pexecute($upd_stmt, array(
'registered' => $state,
'customerid' => $this->customerId
diff --git a/lib/classes/ssl/class.lescript_v2.php b/lib/classes/ssl/class.lescript_v2.php
index 448222eb..9bb135c0 100644
--- a/lib/classes/ssl/class.lescript_v2.php
+++ b/lib/classes/ssl/class.lescript_v2.php
@@ -76,6 +76,7 @@ class lescript_v2
$this->customerId = (! $isFroxlorVhost ? $certrow['customerid'] : null);
$this->isFroxlorVhost = $isFroxlorVhost;
$this->isLeProduction = (Settings::Get('system.letsencryptca') == 'production');
+ $this->_acc_location = $certrow['leaccount'];
$leregistered = $certrow['leregistered'];
@@ -93,7 +94,7 @@ class lescript_v2
Settings::Set('system.leprivatekey', $keys['private']);
Settings::Set('system.leregistered', 0); // key is not registered
} else {
- $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private, `leregistered` = :registered " . "WHERE `customerid` = :customerid;");
+ $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private, `leregistered` = :registered WHERE `customerid` = :customerid;");
Database::pexecute($upd_stmt, array(
'public' => $keys['public'],
'private' => $keys['private'],
@@ -149,42 +150,55 @@ class lescript_v2
// start domains authentication
// ----------------------------
-
+
+ // Prepare order
+ $domains_in_order = array();
foreach ($domains as $domain) {
+ $domains_in_order []= array(
+ "type" => "dns",
+ "value" => $domain
+ );
+ }
+
+ // Send new-order request
+ $response = $this->signedRequest($this->_req_uris['newOrder'], array(
+ "identifiers" => $domains_in_order
+ ), false);
+
+ if ($this->client->getLastCode() == 403) {
+ $this->log("Got status 403 - setting LE status to unregistered.");
+ $this->_acc_location = '';
+ $this->setLeRegisteredState(0);
+ throw new RuntimeException("Got 'unauthorized' response - we need to re-register at next run. Whole response: " . json_encode($response));
+ }
+
+ // if response is not an array but a string, it's most likely a server-error, e.g.
+ //
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)); + } + + $authorizations = $response['authorizations']; + $finalizeLink = $response['finalize']; + + $i = 0; + + foreach ($authorizations as $authorization) { // 1. getting available authentication options // ------------------------------------------- + + $domain = $response['identifiers'][$i++]['value']; $this->log("Requesting challenge for $domain"); - $response = $this->signedRequest($this->_req_uris['newOrder'], array( - "identifiers" => array( - array( - "type" => "dns", - "value" => $domain - ) - ) - ), false); - - if ($this->client->getLastCode() == 403) { - $this->log("Got status 403 - setting LE status to unregistered."); - $this->setLeRegisteredState(0); - throw new RuntimeException("Got 'unauthorized' response - we need to re-register at next run. Whole response: " . json_encode($response)); - } - - // if response is not an array but a string, it's most likely a server-error, e.g. - //
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]);
+ $auth_response = $this->client->get($authorization);
if (! array_key_exists('challenges', $auth_response)) {
throw new RuntimeException("No challenges received for $domain. Whole response: " . json_encode($auth_response));
@@ -201,7 +215,6 @@ class lescript_v2
$this->log("Got challenge token for $domain");
$location = $challenge['url'];
- $finalizeLink = $response['finalize'];
// 2. saving authentication token for web verification
// ---------------------------------------------------
@@ -233,7 +246,7 @@ class lescript_v2
// simple self check
if (Settings::Get('system.disable_le_selfcheck') == '0') {
- $selfcheckpayload = HttpClient::urlGet($uri);
+ $selfcheckpayload = HttpClient::urlGet($uri, false);
if ($payload !== trim($selfcheckpayload)) {
$errmsg = json_encode(error_get_last());
if ($errmsg != "null") {
@@ -336,10 +349,12 @@ class lescript_v2
if ($this->isLeProduction) {
if ($this->isFroxlorVhost) {
Settings::Set('system.leregistered', $state);
+ Settings::Set('system.leaccount', $this->_acc_location);
} else {
- $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `leregistered` = :registered " . "WHERE `customerid` = :customerid;");
+ $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `leregistered` = :registered, `leaccount` = :kid WHERE `customerid` = :customerid;");
Database::pexecute($upd_stmt, array(
'registered' => $state,
+ 'kid' => $this->_acc_location,
'customerid' => $this->customerId
));
}
@@ -495,8 +510,8 @@ class Client
private function curl($method, $url, $data = null)
{
$headers = array(
- 'Accept: application/json',
- 'Content-Type: application/json'
+ 'Accept: application/jose+json',
+ 'Content-Type: application/jose+json'
);
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, preg_match('~^http~', $url) ? $url : $this->base . $url);
@@ -550,7 +565,7 @@ class Client
return trim($matches[1]);
}
- $this->curl('GET', '/directory');
+ $this->curl('GET', '/acme/new-nonce');
return $this->getLastNonce();
}
diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml
index 7b68d374..5ca0d4ad 100644
--- a/lib/configfiles/gentoo.xml
+++ b/lib/configfiles/gentoo.xml
@@ -2138,7 +2138,7 @@ protocol lda {
driver = mysql
connect = host=