From ffce2fc28b342d749026d9d9a0995e590a272adb Mon Sep 17 00:00:00 2001 From: Krombel Date: Wed, 4 Apr 2018 20:43:24 +0200 Subject: [PATCH 1/4] run some autoformat --- MatrixConnection.php | 256 +++++------ config.sample.php | 42 +- cron.php | 154 ++++--- database.php | 565 +++++++++++++------------ helpers.php | 41 +- internal/directory_search.php | 6 +- internal/identity_bulk.php | 17 +- internal/identity_single.php | 3 +- internal/intercept_change_password.php | 76 ++-- internal/login.php | 13 +- lang/lang.de-de.php | 1 + language.php | 13 +- mail_templates.php | 73 ++-- 13 files changed, 641 insertions(+), 619 deletions(-) diff --git a/MatrixConnection.php b/MatrixConnection.php index dd04201..1ae1f81 100644 --- a/MatrixConnection.php +++ b/MatrixConnection.php @@ -1,4 +1,5 @@ hs = $homeserver; - $this->at = $access_token; - } + private $hs; + private $at; - function send($room_id, $message) { - if (!$this->at) { - error_log("No access token defined"); - return false; - } + function __construct($homeserver, $access_token) { + $this->hs = $homeserver; + $this->at = $access_token; + } - $send_message = NULL; - if (!$message) { - error_log("no message to send"); - return false; - } elseif(is_array($message)) { - $send_message = $message; - } elseif ($message instanceof MatrixMessage) { - $send_message = $message->get_object(); - } else { - error_log("message is of not valid type\n"); - return false; - } + function send($room_id, $message) { + if (!$this->at) { + error_log("No access token defined"); + return false; + } - $url="https://".$this->hs."/_matrix/client/r0/rooms/" - . urlencode($room_id) ."/send/m.room.message?access_token=".$this->at; - $handle = curl_init($url); - curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); - curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($handle, CURLOPT_TIMEOUT, 60); - curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($send_message)); - curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json")); + $send_message = NULL; + if (!$message) { + error_log("no message to send"); + return false; + } elseif (is_array($message)) { + $send_message = $message; + } elseif ($message instanceof MatrixMessage) { + $send_message = $message->get_object(); + } else { + error_log("message is of not valid type\n"); + return false; + } - $response = $this->exec_curl_request($handle); - return isset($response["event_id"]); - } + $url = "https://" . $this->hs . "/_matrix/client/r0/rooms/" + . urlencode($room_id) . "/send/m.room.message?access_token=" . $this->at; + $handle = curl_init($url); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($handle, CURLOPT_TIMEOUT, 60); + curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($send_message)); + curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json")); - function send_msg($room_id, $message) { - return $this->send($room_id, array( - "msgtype" => "m.notice", - "body" => $message - ) - ); - } + $response = $this->exec_curl_request($handle); + return isset($response["event_id"]); + } - function hasUser($username) { - if (!$username) { - throw new Exception ("no user given to lookup"); - } + function send_msg($room_id, $message) { + return $this->send($room_id, array( + "msgtype" => "m.notice", + "body" => $message + ) + ); + } - $url = "https://".$this->hs."/_matrix/client/r0/profile/@" . $username . ":" . $this->hs; - $handle = curl_init($url); - curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); - curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($handle, CURLOPT_TIMEOUT, 60); - curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json")); + function hasUser($username) { + if (!$username) { + throw new Exception("no user given to lookup"); + } - $res = $this->exec_curl_request($handle); - return !(isset($res["errcode"]) && $res["errcode"] == "M_UNKNOWN"); - } + $url = "https://" . $this->hs . "/_matrix/client/r0/profile/@" . $username . ":" . $this->hs; + $handle = curl_init($url); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($handle, CURLOPT_TIMEOUT, 60); + curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json")); - function register($username, $password, $shared_secret) { - if (!$username) { - error_log("no username provided"); - } - if (!$password) { - error_log("no message to send"); - } + $res = $this->exec_curl_request($handle); + return !(isset($res["errcode"]) && $res["errcode"] == "M_UNKNOWN"); + } - $mac = hash_hmac('sha1', $username, $shared_secret); + function register($username, $password, $shared_secret) { + if (!$username) { + error_log("no username provided"); + } + if (!$password) { + error_log("no message to send"); + } - $data = array( - "username" => $username, - "password" => $password, - "mac" => $mac, - ); - $url = "https://".$this->hs."/_matrix/client/v2_alpha/register"; - $handle = curl_init($url); - curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); - curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($handle, CURLOPT_TIMEOUT, 60); - curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json")); - curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($data)); + $mac = hash_hmac('sha1', $username, $shared_secret); - return $this->exec_curl_request($handle); - } + $data = array( + "username" => $username, + "password" => $password, + "mac" => $mac, + ); + $url = "https://" . $this->hs . "/_matrix/client/v2_alpha/register"; + $handle = curl_init($url); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($handle, CURLOPT_TIMEOUT, 60); + curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json")); + curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($data)); - function exec_curl_request($handle) { - $response = curl_exec($handle); - if ($response === false) { - $errno = curl_errno($handle); - $error = curl_error($handle); - error_log("Curl returned error $errno: $error\n"); - curl_close($handle); - return false; - } - $http_code = intval(curl_getinfo($handle, CURLINFO_HTTP_CODE)); - curl_close($handle); + return $this->exec_curl_request($handle); + } + + function exec_curl_request($handle) { + $response = curl_exec($handle); + if ($response === false) { + $errno = curl_errno($handle); + $error = curl_error($handle); + error_log("Curl returned error $errno: $error\n"); + curl_close($handle); + return false; + } + $http_code = intval(curl_getinfo($handle, CURLINFO_HTTP_CODE)); + curl_close($handle); + + if ($http_code >= 500) { + // do not want to DDOS server if something goes wrong + sleep(10); + return false; + } else if ($http_code != 200) { + $response = json_decode($response, true); + error_log("Request has failed with error {$response['error']}\n"); + if ($http_code == 401) { + throw new Exception('Invalid access token provided'); + } + } else { + $response = json_decode($response, true); + } + return $response; + } - if ($http_code >= 500) { - // do not want to DDOS server if something goes wrong - sleep(10); - return false; - } else if ($http_code != 200) { - $response = json_decode($response, true); - error_log("Request has failed with error {$response['error']}\n"); - if ($http_code == 401) { - throw new Exception('Invalid access token provided'); - } - } else { - $response = json_decode($response, true); - } - return $response; - } } -class MatrixMessage -{ - private $message; +class MatrixMessage { - function __construct() { - $this->message = ["msgtype" => "m.notice"]; - } + private $message; - function set_type($msgtype) { - $this->message["msgtype"] = $msgtype; - } + function __construct() { + $this->message = ["msgtype" => "m.notice"]; + } - function set_format($format) { - $this->message["format"] = $format; - } + function set_type($msgtype) { + $this->message["msgtype"] = $msgtype; + } - function set_body($body) { - $this->message["body"] = $body; - } + function set_format($format) { + $this->message["format"] = $format; + } - function set_formatted_body($fbody, $format="org.matrix.custom.html") { - $this->message["formatted_body"] = $fbody; - $this->message["format"] = $format; - } + function set_body($body) { + $this->message["body"] = $body; + } + + function set_formatted_body($fbody, $format = "org.matrix.custom.html") { + $this->message["formatted_body"] = $fbody; + $this->message["format"] = $format; + } + + function get_object() { + return $this->message; + } - function get_object() { - return $this->message; - } } + ?> diff --git a/config.sample.php b/config.sample.php index 6028e67..12152c8 100644 --- a/config.sample.php +++ b/config.sample.php @@ -1,26 +1,22 @@ "example.com", - "access_token" => "To be used for sending the registration notification", - - // Which e-mail-adresse shall the bot use to send e-mails? - "register_email" => 'register_bot@example.com', - // Where should the bot post registration requests to? - "register_room" => '$registerRoomID:example.com', - - // Where is the public part of the bot located? make sure you have a / at the end - "webroot" => "https://myregisterdomain.net/", - - // optional: Do you have a place where howTo's are located? If not leave this value out - "howToURL" => "https://my-url-for-storing-howTos.net", - - // When you want to collect the password on registration set this to true - "getPasswordOnRegistration" => false, - - // to define where the data should be stored: - "databaseURI" => "sqlite:" . dirname(__FILE__) . "/db_file.sqlite", - // credentials for sqlite not used - "databaseUser" => "dbUser123", - "databasePass" => "secretPassword", -] + "homeserver" => "example.com", + "access_token" => "To be used for sending the registration notification", + // Which e-mail-adresse shall the bot use to send e-mails? + "register_email" => 'register_bot@example.com', + // Where should the bot post registration requests to? + "register_room" => '$registerRoomID:example.com', + // Where is the public part of the bot located? make sure you have a / at the end + "webroot" => "https://myregisterdomain.net/", + // optional: Do you have a place where howTo's are located? If not leave this value out + "howToURL" => "https://my-url-for-storing-howTos.net", + // When you want to collect the password on registration set this to true + "getPasswordOnRegistration" => false, + // to define where the data should be stored: + "databaseURI" => "sqlite:" . dirname(__FILE__) . "/db_file.sqlite", + // credentials for sqlite not used + "databaseUser" => "dbUser123", + "databasePass" => "secretPassword", + ] ?> diff --git a/cron.php b/cron.php index 1ef0ec0..ad6dd77 100644 --- a/cron.php +++ b/cron.php @@ -1,4 +1,5 @@ query($sql) as $row) { - $first_name = $row["first_name"]; - $last_name = $row["last_name"]; - $username = $row["username"]; - $email = $row["email"]; - $state = $row["state"]; + $first_name = $row["first_name"]; + $last_name = $row["last_name"]; + $username = $row["username"]; + $email = $row["email"]; + $state = $row["state"]; - try { - switch ($state) { - case RegisterState::PendingEmailSend: - $verify_url = $config["webroot"] . "/verify.php?t=" . $row["verify_token"]; - $success = send_mail_pending_verification( - $config["homeserver"], - $row["first_name"] . " " . $row["last_name"], - $row["email"], - $verify_url); + try { + switch ($state) { + case RegisterState::PendingEmailSend: + $verify_url = $config["webroot"] . "/verify.php?t=" . $row["verify_token"]; + $success = send_mail_pending_verification( + $config["homeserver"], $row["first_name"] . " " . $row["last_name"], $row["email"], $verify_url); - if ($success) { - $mx_db->setRegistrationStateById(RegisterState::PendingEmailVerify, $row["id"]); - } else { - throw new Exception("Could not send mail to ".$row["first_name"]." ".$row["last_name"]."(".$row["id"].")"); - } - break; - case RegisterState::PendingAdminSend: - require_once("MatrixConnection.php"); - $adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $row["admin_token"]; - $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); - $mxMsg = new MatrixMessage(); - $mxMsg->set_body($first_name . ' ' . $last_name . " möchte sich registrieren und hat folgende Notiz hinterlassen:\r\n" - . $row["note"] . "\r\n" - . "Zum Bearbeiten hier klicken:\r\n" . $adminUrl); - $mxMsg->set_formatted_body($first_name . ' ' . $last_name . " möchte sich registrieren und hat folgende Notiz hinterlassen:
" - . $row["note"] . "
" - . "Zum Bearbeiten hier klicken"); - $mxMsg->set_type("m.text"); - $response = $mxConn->send($config["register_room"], $mxMsg); + if ($success) { + $mx_db->setRegistrationStateById(RegisterState::PendingEmailVerify, $row["id"]); + } else { + throw new Exception("Could not send mail to " . $row["first_name"] . " " . $row["last_name"] . "(" . $row["id"] . ")"); + } + break; + case RegisterState::PendingAdminSend: + require_once("MatrixConnection.php"); + $adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $row["admin_token"]; + $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); + $mxMsg = new MatrixMessage(); + $mxMsg->set_body($first_name . ' ' . $last_name . " möchte sich registrieren und hat folgende Notiz hinterlassen:\r\n" + . $row["note"] . "\r\n" + . "Zum Bearbeiten hier klicken:\r\n" . $adminUrl); + $mxMsg->set_formatted_body($first_name . ' ' . $last_name . " möchte sich registrieren und hat folgende Notiz hinterlassen:
" + . $row["note"] . "
" + . "Zum Bearbeiten hier klicken"); + $mxMsg->set_type("m.text"); + $response = $mxConn->send($config["register_room"], $mxMsg); - if ($response) { - $mx_db->setRegistrationStateById(RegisterState::PendingAdminVerify, $row["id"]); + if ($response) { + $mx_db->setRegistrationStateById(RegisterState::PendingAdminVerify, $row["id"]); - send_mail_pending_approval($config["homeserver"], $first_name . " " . $last_name, $email); - } else { - throw new Exception("Could not send notification for ".$row["first_name"]." ".$row["last_name"]."(".$row["id"].") to admins."); - } - break; - case RegisterState::PendingRegistration: - // Registration got accepted but registration failed + send_mail_pending_approval($config["homeserver"], $first_name . " " . $last_name, $email); + } else { + throw new Exception("Could not send notification for " . $row["first_name"] . " " . $row["last_name"] . "(" . $row["id"] . ") to admins."); + } + break; + case RegisterState::PendingRegistration: + // Registration got accepted but registration failed - $password = $mx_db->addUser($row["first_name"], $row["last_name"], $row["username"], $row["email"]); - if ($password != NULL) { - // send registration_success - $res = send_mail_registration_success($config["homeserver"], $first_name . " " . $last_name, $email, $username, $password, $config["howToURL"]); - if ($res) { - $mx_db->setRegistrationStateById(RegisterState::AllDone, $row["id"]); - } else { - $mx_db->setRegistrationStateById(RegisterState::PendingSendRegistrationMail, $row["id"]); - } - } else { - send_mail_registration_allowed_but_failed($config["homeserver"], $first_name . " " . $last_name, $email); - $mxMsg = new MatrixMessage(); - $mxMsg->set_type("m.text"); - $mxMsg->set_body("Fehler beim Registrieren von " . $first_name . " " . $last_name . "."); - $mxConn->send($config["register_room"], $mxMsg); - throw new Exception($language["REGISTRATION_FAILED"]); - } - break; - case RegisterState::PendingSendRegistrationMail: - print ("Error: Unhandled state: PendingSendRegistrationMail for " . $first_name . " " . $last_name . " (" . $username . ")\n"); - break; - case RegisterState::RegistrationDeclined: - case RegisterState::AllDone: - // do reqular cleanup - break; - } - } catch (Exception $e) { - print("Error while handling cron for " . $first_name . " " . $last_name . " (" . $username . ")\n"); - print($e->getMessage()); - } + $password = $mx_db->addUser($row["first_name"], $row["last_name"], $row["username"], $row["email"]); + if ($password != NULL) { + // send registration_success + $res = send_mail_registration_success($config["homeserver"], $first_name . " " . $last_name, $email, $username, $password, $config["howToURL"]); + if ($res) { + $mx_db->setRegistrationStateById(RegisterState::AllDone, $row["id"]); + } else { + $mx_db->setRegistrationStateById(RegisterState::PendingSendRegistrationMail, $row["id"]); + } + } else { + send_mail_registration_allowed_but_failed($config["homeserver"], $first_name . " " . $last_name, $email); + $mxMsg = new MatrixMessage(); + $mxMsg->set_type("m.text"); + $mxMsg->set_body("Fehler beim Registrieren von " . $first_name . " " . $last_name . "."); + $mxConn->send($config["register_room"], $mxMsg); + throw new Exception($language["REGISTRATION_FAILED"]); + } + break; + case RegisterState::PendingSendRegistrationMail: + print ("Error: Unhandled state: PendingSendRegistrationMail for " . $first_name . " " . $last_name . " (" . $username . ")\n"); + break; + case RegisterState::RegistrationDeclined: + case RegisterState::AllDone: + // do reqular cleanup + break; + } + } catch (Exception $e) { + print("Error while handling cron for " . $first_name . " " . $last_name . " (" . $username . ")\n"); + print($e->getMessage()); + } } ?> diff --git a/database.php b/database.php index 537a07d..d654c66 100644 --- a/database.php +++ b/database.php @@ -1,4 +1,5 @@ db = new PDO($db_input, $user, $password); - $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $this->db->exec("CREATE TABLE IF NOT EXISTS registrations( + private $db = NULL; + + /** + * Creates mxDatabase object + * @param config object which has following members: + * databaseURI: path to the sqlite file where the credentials should be stored + * or a param which can be used to connect to a database with PDO + * databaseUser and databasePass when authentication is required + * register_email which email does the register bot have (here used for providing lookup) + */ + function __construct($config) { + if (empty($config)) { + throw new Exception("config is empty"); + } + if (!isset($config["databaseURI"])) { + throw new Exception("'databaseURI' not defined"); + } + $db_input = $config["databaseURI"]; + $user = ''; + $password = ''; + if (isset($config["databaseUser"]) && isset($config["databasePass"])) { + // only use it when both are defined + $user = $config["databaseUser"]; + $password = $config["databasePass"]; + } + // create database file when not existent yet + $this->db = new PDO($db_input, $user, $password); + $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->db->exec("CREATE TABLE IF NOT EXISTS registrations( id SERIAL PRIMARY KEY, state INT DEFAULT 0, first_name TEXT, @@ -86,7 +85,7 @@ class mxDatabase admin_token TEXT, request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP )"); - $this->db->exec("CREATE TABLE IF NOT EXISTS logins ( + $this->db->exec("CREATE TABLE IF NOT EXISTS logins ( id SERIAL PRIMARY KEY, active INT DEFAULT 1, first_name TEXT, @@ -97,271 +96,273 @@ class mxDatabase create_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP )"); - // make sure the bot is allowed to login - if (!$this->userRegistered("register_bot")) { - $password = $this->addUser("Register", "Bot", "register_bot", $config["register_email"]); - $config["register_password"] = $password; - $myfile = fopen(dirname(__FILE__) . "/config.json", "w"); - fwrite($myfile, json_encode($config, JSON_PRETTY_PRINT)); - fclose($myfile); - } + // make sure the bot is allowed to login + if (!$this->userRegistered("register_bot")) { + $password = $this->addUser("Register", "Bot", "register_bot", $config["register_email"]); + $config["register_password"] = $password; + $myfile = fopen(dirname(__FILE__) . "/config.json", "w"); + fwrite($myfile, json_encode($config, JSON_PRETTY_PRINT)); + fclose($myfile); + } - // set writeable when not set already - if (strpos($db_input, "sqlite") === 0) { - $sqlite_file = substr($db_input, strlen("sqlite:")); - if (!is_writable($sqlite_file)) { - chmod($sqlite_file, 0660); - } - unset($sqlite_file); - } - } + // set writeable when not set already + if (strpos($db_input, "sqlite") === 0) { + $sqlite_file = substr($db_input, strlen("sqlite:")); + if (!is_writable($sqlite_file)) { + chmod($sqlite_file, 0660); + } + unset($sqlite_file); + } + } - /** - * WARNING: This allows accessing the database directly. - * This was only be added for convenience. You are advised to not use this function extensively - * - * @param sql String wich will be passed directly to the database - * @return Response of PDO::query() - */ - function query($sql) { - return $this->db->query($sql); - } + /** + * WARNING: This allows accessing the database directly. + * This was only be added for convenience. You are advised to not use this function extensively + * + * @param sql String wich will be passed directly to the database + * @return Response of PDO::query() + */ + function query($sql) { + return $this->db->query($sql); + } - function setRegistrationStateVerify($state, $token) { - $sql = "UPDATE registrations SET state = " . $state - . " WHERE verify_token = '" . $token . "';"; + function setRegistrationStateVerify($state, $token) { + $sql = "UPDATE registrations SET state = " . $state + . " WHERE verify_token = '" . $token . "';"; - return $this->db->exec($sql); - } + return $this->db->exec($sql); + } - function setRegistrationStateById($state, $id) { - $sql = "UPDATE registrations SET state = " . $state - . " WHERE id = '" . $id . "';"; + function setRegistrationStateById($state, $id) { + $sql = "UPDATE registrations SET state = " . $state + . " WHERE id = '" . $id . "';"; - return $this->db->exec($sql); - } + return $this->db->exec($sql); + } - function setRegistrationStateAdmin($state, $token) { - $sql = "UPDATE registrations SET state = " . $state - . " WHERE admin_token = '" . $token . "';"; + function setRegistrationStateAdmin($state, $token) { + $sql = "UPDATE registrations SET state = " . $state + . " WHERE admin_token = '" . $token . "';"; - return $this->db->exec($sql); - } + return $this->db->exec($sql); + } - function setRegistrationState($state, $token) { - $sql = "UPDATE registrations SET state = " . $state - . " WHERE verify_token = '" . $token . "' OR admin_token = '" . $token . "';"; + function setRegistrationState($state, $token) { + $sql = "UPDATE registrations SET state = " . $state + . " WHERE verify_token = '" . $token . "' OR admin_token = '" . $token . "';"; - return $this->db->exec($sql); - } + return $this->db->exec($sql); + } - function userPendingRegistrations($username) { - $sql = "SELECT COUNT(*) FROM registrations WHERE username = '" . $username . "' AND NOT state = " - . RegisterState::RegistrationDeclined . " LIMIT 1;"; - $res = $this->db->query($sql); - if ($res->fetchColumn() > 0) { - return true; - } - return false; - } - function userRegistered($username) { - $sql = "SELECT COUNT(*) FROM logins WHERE localpart = '" . $username . "' LIMIT 1;"; - $res = $this->db->query($sql); - if ($res->fetchColumn() > 0) { - return true; - } - return false; - } + function userPendingRegistrations($username) { + $sql = "SELECT COUNT(*) FROM registrations WHERE username = '" . $username . "' AND NOT state = " + . RegisterState::RegistrationDeclined . " LIMIT 1;"; + $res = $this->db->query($sql); + if ($res->fetchColumn() > 0) { + return true; + } + return false; + } - /** - * Adds user to the database. Next steps should be sending a verify-mail to the user - * @param first_name First name of the user - * @param last_name Sirname of the user - * @param username the future localpart of that user - * @param note Note the user typed in to give a hint - * @param email E-Mail-Adress which will be stored into the database. - * This will be send to the server on first login - * - * @return ["verify_token"] - */ - function addRegistration($first_name, $last_name, $username, $note, $email) { - if ($this->userPendingRegistrations($username)) { - throw new Exception("USERNAME_PENDING_REGISTRATION"); - } - if ($this->userRegistered($username)) { - throw new Exception("USERNAME_REGISTERED"); - } + function userRegistered($username) { + $sql = "SELECT COUNT(*) FROM logins WHERE localpart = '" . $username . "' LIMIT 1;"; + $res = $this->db->query($sql); + if ($res->fetchColumn() > 0) { + return true; + } + return false; + } - $verify_token = bin2hex(random_bytes(16)); - $admin_token = bin2hex(random_bytes(16)); + /** + * Adds user to the database. Next steps should be sending a verify-mail to the user + * @param first_name First name of the user + * @param last_name Sirname of the user + * @param username the future localpart of that user + * @param note Note the user typed in to give a hint + * @param email E-Mail-Adress which will be stored into the database. + * This will be send to the server on first login + * + * @return ["verify_token"] + */ + function addRegistration($first_name, $last_name, $username, $note, $email) { + if ($this->userPendingRegistrations($username)) { + throw new Exception("USERNAME_PENDING_REGISTRATION"); + } + if ($this->userRegistered($username)) { + throw new Exception("USERNAME_REGISTERED"); + } - $this->db->exec("INSERT INTO registrations + $verify_token = bin2hex(random_bytes(16)); + $admin_token = bin2hex(random_bytes(16)); + + $this->db->exec("INSERT INTO registrations (first_name, last_name, username, note, email, verify_token, admin_token) - VALUES ('" . $first_name."','" . $last_name . "','" . $username . "','" . $note . "','" - . $email."','" .$verify_token."','" .$admin_token."')"); + VALUES ('" . $first_name . "','" . $last_name . "','" . $username . "','" . $note . "','" + . $email . "','" . $verify_token . "','" . $admin_token . "')"); - return [ - "verify_token"=> $verify_token, - ]; - } + return [ + "verify_token" => $verify_token, + ]; + } - /** - * Gets the user for the verify_admin page. - * - * @return ArrayOfUser|NULL Array with "first_name, last_name, username, note and email" - * as members - */ - function getUserForApproval($admin_token) { - $sql = "SELECT COUNT(*) FROM registrations WHERE admin_token = '" . $admin_token . "'" - . " AND state = " . RegisterState::PendingAdminVerify . " LIMIT 1;"; - $res = $this->db->query($sql); + /** + * Gets the user for the verify_admin page. + * + * @return ArrayOfUser|NULL Array with "first_name, last_name, username, note and email" + * as members + */ + function getUserForApproval($admin_token) { + $sql = "SELECT COUNT(*) FROM registrations WHERE admin_token = '" . $admin_token . "'" + . " AND state = " . RegisterState::PendingAdminVerify . " LIMIT 1;"; + $res = $this->db->query($sql); - if ($res->fetchColumn() > 0) { - $sql = "SELECT first_name, last_name, username, note, email FROM registrations" - . " WHERE admin_token = '" . $admin_token . "'" - . " AND state = " . RegisterState::PendingAdminVerify - . " LIMIT 1;"; - foreach ($this->db->query($sql) as $row) { - // will only be executed once - return $row; - } - } - return NULL; - } + if ($res->fetchColumn() > 0) { + $sql = "SELECT first_name, last_name, username, note, email FROM registrations" + . " WHERE admin_token = '" . $admin_token . "'" + . " AND state = " . RegisterState::PendingAdminVerify + . " LIMIT 1;"; + foreach ($this->db->query($sql) as $row) { + // will only be executed once + return $row; + } + } + return NULL; + } - /** - * Gets the user when it opens the page to verify its mail - * - * @return ArrayOfUser|NULL Array with "first_name, last_name, note, email and admin_token" - * as members - */ - function getUserForVerify($verify_token) { - $sql = "SELECT COUNT(*) FROM registrations WHERE verify_token = '" . $verify_token . "'" - . " AND state = " . RegisterState::PendingEmailVerify . " LIMIT 1;"; - $res = $this->db->query($sql); + /** + * Gets the user when it opens the page to verify its mail + * + * @return ArrayOfUser|NULL Array with "first_name, last_name, note, email and admin_token" + * as members + */ + function getUserForVerify($verify_token) { + $sql = "SELECT COUNT(*) FROM registrations WHERE verify_token = '" . $verify_token . "'" + . " AND state = " . RegisterState::PendingEmailVerify . " LIMIT 1;"; + $res = $this->db->query($sql); - if ($res->fetchColumn() > 0) { - $sql = "SELECT first_name, last_name, note, email, admin_token FROM registrations " - . " WHERE verify_token = '" . $verify_token . "'" - . " AND state = " . RegisterState::PendingEmailVerify . " LIMIT 1;"; - foreach ($this->db->query($sql) as $row) { - // will only be executed once - return $row; - } - } - return NULL; - } + if ($res->fetchColumn() > 0) { + $sql = "SELECT first_name, last_name, note, email, admin_token FROM registrations " + . " WHERE verify_token = '" . $verify_token . "'" + . " AND state = " . RegisterState::PendingEmailVerify . " LIMIT 1;"; + foreach ($this->db->query($sql) as $row) { + // will only be executed once + return $row; + } + } + return NULL; + } - function getUserForLogin($localpart, $password) { - $sql = "SELECT COUNT(*) FROM logins WHERE localpart = '" . $localpart - . "' AND active = 1 LIMIT 1;"; - $res = $this->db->query($sql); + function getUserForLogin($localpart, $password) { + $sql = "SELECT COUNT(*) FROM logins WHERE localpart = '" . $localpart + . "' AND active = 1 LIMIT 1;"; + $res = $this->db->query($sql); - if ($res->fetchColumn() > 0) { - $sql = "SELECT first_name, last_name, email, password_hash FROM logins " - . " WHERE localpart = '" . $localpart . "' AND active = 1 LIMIT 1;"; - foreach ($this->db->query($sql) as $row) { - if (password_verify($password, $row["password_hash"])) { - return $row; - } - } - } - return NULL; - } + if ($res->fetchColumn() > 0) { + $sql = "SELECT first_name, last_name, email, password_hash FROM logins " + . " WHERE localpart = '" . $localpart . "' AND active = 1 LIMIT 1;"; + foreach ($this->db->query($sql) as $row) { + if (password_verify($password, $row["password_hash"])) { + return $row; + } + } + } + return NULL; + } - /** - * adds User to be able to login afterwards. - * @param first_name First name of the user - * @param last_name Sirname of the user - * @param username the future localpart of that user - * @param email E-Mail-Adress which will be stored into the database. - * This will be send to the server on first login - * - * @return password|NULL with member password as this method generates a - * password and saves that into the database - * NULL when failed - * - */ - function addUser($first_name, $last_name, $username, $email) { - // check if user already exists and abort in that case - if ($this->userRegistered($username)) { - return NULL; - } + /** + * adds User to be able to login afterwards. + * @param first_name First name of the user + * @param last_name Sirname of the user + * @param username the future localpart of that user + * @param email E-Mail-Adress which will be stored into the database. + * This will be send to the server on first login + * + * @return password|NULL with member password as this method generates a + * password and saves that into the database + * NULL when failed + * + */ + function addUser($first_name, $last_name, $username, $email) { + // check if user already exists and abort in that case + if ($this->userRegistered($username)) { + return NULL; + } - // generate a password with 10 characters - $password = bin2hex(openssl_random_pseudo_bytes(5)); - $password_hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>12]); + // generate a password with 10 characters + $password = bin2hex(openssl_random_pseudo_bytes(5)); + $password_hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 12]); - $sql = "INSERT INTO logins (first_name, last_name, localpart, password_hash, email) VALUES " - . "('" . $first_name."','" . $last_name . "','" . $username . "','" - . $password_hash . "','" . $email . "');"; + $sql = "INSERT INTO logins (first_name, last_name, localpart, password_hash, email) VALUES " + . "('" . $first_name . "','" . $last_name . "','" . $username . "','" + . $password_hash . "','" . $email . "');"; - if ($this->db->exec($sql)) { - return $password; - } - return NULL; - } + if ($this->db->exec($sql)) { + return $password; + } + return NULL; + } - function updatePassword($localpart, $old_password, $new_password) { - $user = $this->getUserForLogin($localpart, $old_password); - if ($user == NULL) { - throw new Exception ("user with that credentials not found"); - } + function updatePassword($localpart, $old_password, $new_password) { + $user = $this->getUserForLogin($localpart, $old_password); + if ($user == NULL) { + throw new Exception("user with that credentials not found"); + } - // The credentials were fine. So now set the new password - $password_hash = password_hash($new_password, PASSWORD_BCRYPT, ["cost"=>12]); + // The credentials were fine. So now set the new password + $password_hash = password_hash($new_password, PASSWORD_BCRYPT, ["cost" => 12]); - $sql = "UPDATE logins SET password_hash = '" . $password_hash . "'" - . "WHERE localpart = '" . $localpart . "'"; + $sql = "UPDATE logins SET password_hash = '" . $password_hash . "'" + . "WHERE localpart = '" . $localpart . "'"; - if ($this->db->exec($sql)) { - return true; - } - return false; - } + if ($this->db->exec($sql)) { + return true; + } + return false; + } - function searchUserByName($search_term) { - $term = filter_var($search_term, FILTER_SANITIZE_STRING); - $result = array(); - $sql = "SELECT COUNT(*) FROM logins WHERE" - . " localpart LIKE '" . $term . "%' AND active = 1;"; - $res = $this->db->query($sql); + function searchUserByName($search_term) { + $term = filter_var($search_term, FILTER_SANITIZE_STRING); + $result = array(); + $sql = "SELECT COUNT(*) FROM logins WHERE" + . " localpart LIKE '" . $term . "%' AND active = 1;"; + $res = $this->db->query($sql); - if ($res->fetchColumn() > 0) { - $sql = "SELECT first_name, last_name, localpart FROM logins WHERE" - . " localpart LIKE '" . $term . "%' AND active = 1;"; - foreach ($this->db->query($sql) as $row) { - array_push($result, [ - "display_name" => $row["first_name"] . " " . $row["last_name"], - "user_id" => $row["localpart"], - ]); - } - } - return $result; - } + if ($res->fetchColumn() > 0) { + $sql = "SELECT first_name, last_name, localpart FROM logins WHERE" + . " localpart LIKE '" . $term . "%' AND active = 1;"; + foreach ($this->db->query($sql) as $row) { + array_push($result, [ + "display_name" => $row["first_name"] . " " . $row["last_name"], + "user_id" => $row["localpart"], + ]); + } + } + return $result; + } - function searchUserByEmail($search_term) { - $term = filter_var($search_term, FILTER_SANITIZE_STRING); - $result = array(); - $sql = "SELECT COUNT(*) FROM logins WHERE" - . " email = '" . $term . "' AND active = 1;"; - $res = $this->db->query($sql); + function searchUserByEmail($search_term) { + $term = filter_var($search_term, FILTER_SANITIZE_STRING); + $result = array(); + $sql = "SELECT COUNT(*) FROM logins WHERE" + . " email = '" . $term . "' AND active = 1;"; + $res = $this->db->query($sql); + + if ($res->fetchColumn() > 0) { + $sql = "SELECT first_name, last_name, localpart FROM logins WHERE" + . " email = '" . $term . "' AND active = 1;"; + foreach ($this->db->query($sql) as $row) { + array_push($result, [ + "display_name" => $row["first_name"] . " " . $row["last_name"], + "user_id" => $row["localpart"], + ]); + } + } + return $result; + } - if ($res->fetchColumn() > 0) { - $sql = "SELECT first_name, last_name, localpart FROM logins WHERE" - . " email = '" . $term . "' AND active = 1;"; - foreach ($this->db->query($sql) as $row) { - array_push($result, [ - "display_name" => $row["first_name"] . " " . $row["last_name"], - "user_id" => $row["localpart"], - ]); - } - } - return $result; - } } if (!isset($mx_db)) { - $mx_db = new mxDatabase($config); + $mx_db = new mxDatabase($config); } ?> diff --git a/helpers.php b/helpers.php index 8eb32a4..c48846c 100644 --- a/helpers.php +++ b/helpers.php @@ -1,18 +1,33 @@ \ No newline at end of file diff --git a/internal/directory_search.php b/internal/directory_search.php index 1201c50..d6419cd 100644 --- a/internal/directory_search.php +++ b/internal/directory_search.php @@ -1,4 +1,5 @@ false, "result" => [], ]; @@ -23,7 +24,7 @@ try { $inputJSON = file_get_contents('php://input'); $input = json_decode($inputJSON, TRUE); if (empty($input)) { - throw new Exception('no valid json as input present'); + throw new Exception('no valid json as input present'); } if (!isset($input["by"])) { throw new Exception('"by" is not defined'); @@ -41,7 +42,6 @@ try { default: throw new Exception('unknown type for "by" param'); } - } catch (Exception $e) { error_log("failed with error: " . $e->getMessage()); $response["error"] = $e->getMessage(); diff --git a/internal/identity_bulk.php b/internal/identity_bulk.php index 47341cf..120871e 100644 --- a/internal/identity_bulk.php +++ b/internal/identity_bulk.php @@ -1,4 +1,5 @@ searchUserByEmail($lookup["address"]); if (!empty($res2)) { array_push($response["lookup"], [ - "medium" => $lookup["medium"], - "address" => $lookup["address"], - "id" => [ - "type" => "localpart", - "value" => $res2[0]["user_id"], - ] + "medium" => $lookup["medium"], + "address" => $lookup["address"], + "id" => [ + "type" => "localpart", + "value" => $res2[0]["user_id"], ] + ] ); } - break; + break; case "msisdn": // This is reserved for number lookups throw new Exception("unimplemented lookup medium"); diff --git a/internal/identity_single.php b/internal/identity_single.php index f6ae628..8a317d9 100644 --- a/internal/identity_single.php +++ b/internal/identity_single.php @@ -1,4 +1,5 @@ updatePassword( - $localpart, - $input["auth"]["password"], - $input["new_password"] - )) { - throw new Exception("invalid credentials or another error while updating"); - } + require_once("../database.php"); + if (!$mx_db->updatePassword( + $localpart, $input["auth"]["password"], $input["new_password"] + )) { + throw new Exception("invalid credentials or another error while updating"); + } } catch (Exception $e) { - header("HTTP/1.0 500 Internal Error"); - error_log("failed with error: " . $e->getMessage()); - $response = [ - "errorcode" => "M_UNKNOWN", - "error" => $e->getMessage(), - ]; + header("HTTP/1.0 500 Internal Error"); + error_log("failed with error: " . $e->getMessage()); + $response = [ + "errorcode" => "M_UNKNOWN", + "error" => $e->getMessage(), + ]; } print (json_encode($response, JSON_PRETTY_PRINT)); ?> diff --git a/internal/login.php b/internal/login.php index a178b39..57d7685 100644 --- a/internal/login.php +++ b/internal/login.php @@ -1,4 +1,5 @@ getUserForLogin($localpart, $password); diff --git a/lang/lang.de-de.php b/lang/lang.de-de.php index cd2c310..3dd390b 100644 --- a/lang/lang.de-de.php +++ b/lang/lang.de-de.php @@ -1,4 +1,5 @@ +?> \ No newline at end of file diff --git a/mail_templates.php b/mail_templates.php index 951d94c..72e1400 100644 --- a/mail_templates.php +++ b/mail_templates.php @@ -1,4 +1,5 @@ -- 2.39.5 From f808615f22e2ca4c9a42981b8f5e220950fb2688 Mon Sep 17 00:00:00 2001 From: Krombel Date: Sun, 15 Apr 2018 21:20:01 +0200 Subject: [PATCH 2/4] start implementing multiple modes for operation - synapse: Only trigger register calls and do not store anything for longterm - local: Provide an identity store and register to the own backend --- config.sample.php | 6 +++ database.php | 2 +- internal/directory_search.php | 2 +- internal/identity_bulk.php | 2 +- internal/identity_single.php | 2 +- internal/login.php | 2 +- public/index.php | 80 ++++++++++++++++++++++------------- public/verify.php | 7 ++- public/verify_admin.php | 11 ++++- 9 files changed, 75 insertions(+), 39 deletions(-) diff --git a/config.sample.php b/config.sample.php index 12152c8..717c35e 100644 --- a/config.sample.php +++ b/config.sample.php @@ -11,7 +11,13 @@ $config = [ "webroot" => "https://myregisterdomain.net/", // optional: Do you have a place where howTo's are located? If not leave this value out "howToURL" => "https://my-url-for-storing-howTos.net", + // set the mode of operation. Basically this defines where the data is stored: + // - synapse (using the register endpoint - so no further auth config necessary + // - local (recommended; using a table in the database to store credentials; + // synapse has to be configured to use that) + "operationMode" => "local", // When you want to collect the password on registration set this to true + // only evaluated when operationMode = local "getPasswordOnRegistration" => false, // to define where the data should be stored: "databaseURI" => "sqlite:" . dirname(__FILE__) . "/db_file.sqlite", diff --git a/database.php b/database.php index d654c66..fef634c 100644 --- a/database.php +++ b/database.php @@ -241,7 +241,7 @@ class mxDatabase { $res = $this->db->query($sql); if ($res->fetchColumn() > 0) { - $sql = "SELECT first_name, last_name, note, email, admin_token FROM registrations " + $sql = "SELECT first_name, last_name, note, email, username, admin_token FROM registrations " . " WHERE verify_token = '" . $verify_token . "'" . " AND state = " . RegisterState::PendingEmailVerify . " LIMIT 1;"; foreach ($this->db->query($sql) as $row) { diff --git a/internal/directory_search.php b/internal/directory_search.php index d6419cd..2149a82 100644 --- a/internal/directory_search.php +++ b/internal/directory_search.php @@ -46,5 +46,5 @@ try { error_log("failed with error: " . $e->getMessage()); $response["error"] = $e->getMessage(); } -print (json_encode($response, JSON_PRETTY_PRINT) . "\n"); +print (json_encode($response, JSON_PRETTY_PRINT)); ?> diff --git a/internal/identity_bulk.php b/internal/identity_bulk.php index 120871e..8c87e12 100644 --- a/internal/identity_bulk.php +++ b/internal/identity_bulk.php @@ -66,5 +66,5 @@ try { error_log("ídentity_bulk failed with error: " . $e->getMessage()); $response["error"] = $e->getMessage(); } -print (json_encode($response, JSON_PRETTY_PRINT) . "\n"); +print (json_encode($response, JSON_PRETTY_PRINT)); ?> diff --git a/internal/identity_single.php b/internal/identity_single.php index 8a317d9..fde3dd8 100644 --- a/internal/identity_single.php +++ b/internal/identity_single.php @@ -61,5 +61,5 @@ try { "error" => $e->getMessage() ]; } -print (json_encode($response, JSON_PRETTY_PRINT) . "\n"); +print (json_encode($response, JSON_PRETTY_PRINT)); ?> diff --git a/internal/login.php b/internal/login.php index 57d7685..9f44dbe 100644 --- a/internal/login.php +++ b/internal/login.php @@ -108,5 +108,5 @@ try { error_log("Auth failed with error: " . $e->getMessage()); $response["auth"]["error"] = $e->getMessage(); } -print (json_encode($response, JSON_PRETTY_PRINT) . "\n"); +print (json_encode($response, JSON_PRETTY_PRINT)); ?> diff --git a/public/index.php b/public/index.php index 735a24e..ede9a46 100644 --- a/public/index.php +++ b/public/index.php @@ -13,12 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -require_once "../language.php"; -if (!file_exists("../config.php")) { - print($language["NO_CONFIGURATION"]); - exit(); -} -require_once "../config.php"; // enforce admin via https if (!isset($_SERVER['HTTPS'])) { @@ -26,6 +20,25 @@ if (!isset($_SERVER['HTTPS'])) { exit(); } +require_once "../language.php"; +if (!file_exists("../config.php")) { + print($language["NO_CONFIGURATION"]); + exit(); +} +require_once "../config.php"; + +// this values will not be used when using the register operation type +$storeFirstLastName = false; +if (isset($config["operationMode"]) && $config["operationMode"] === "local") { + $storeFirstLastName = true; +} + +// currently the case to store the password on our own is the only supported one +$storePassword = false; +if (isset($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"] && + isset($config["operationMode"]) && $config["operationMode"] === "synapse") { + $storePassword = true; +} session_start(); if ($_SERVER["REQUEST_METHOD"] == "POST") { @@ -53,17 +66,22 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { if (!isset($_POST["email"]) || !filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) { throw new Exception("EMAIL_INVALID_FORMAT"); } - if (isset($_POST["first_name"]) && ! preg_match("/[A-Z][a-z]+/", $_POST["first_name"])) { - throw new Exception("FIRSTNAME_INVALID_FORMAT"); - } - if (isset($_POST["last_name"]) && ! preg_match("/[A-Z][a-z]+/", $_POST["last_name"])) { - throw new Exception("SIRNAME_INVALID_FORMAT"); - } + if ($storeFirstLastName) { + // only require first_name and last_name when we will evaluate them + if (!isset($_POST["first_name"]) || ! preg_match("/[A-Z][a-z]+/", $_POST["first_name"])) { + throw new Exception("FIRSTNAME_INVALID_FORMAT"); + } + if (!isset($_POST["last_name"]) || ! preg_match("/[A-Z][a-z]+/", $_POST["last_name"])) { + throw new Exception("SIRNAME_INVALID_FORMAT"); + } + $first_name = filter_var($_POST["first_name"], FILTER_SANITIZE_STRING); + $last_name = filter_var($_POST["last_name"], FILTER_SANITIZE_STRING); + } else { + $first_name = $last_name = ""; + } - $first_name = filter_var($_POST["first_name"], FILTER_SANITIZE_STRING); - $last_name = filter_var($_POST["last_name"], FILTER_SANITIZE_STRING); $username = filter_var($_POST["username"], FILTER_SANITIZE_STRING); - if (isset($_POST["password"])) { + if ($storePassword && isset($_POST["password"])) { $password = filter_var($_POST["password"], FILTER_SANITIZE_STRING); } $note = filter_var($_POST["note"], FILTER_SANITIZE_STRING); @@ -82,7 +100,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { require_once "../mail_templates.php"; $success = send_mail_pending_verification( $config["homeserver"], - $first_name . " " . $last_name, + $storeFirstLastName ? $first_name . " " . $last_name : $username, $email, $verify_url); @@ -137,6 +155,7 @@ body{
+
@@ -151,6 +170,7 @@ body{
+
@@ -164,7 +184,7 @@ body{
- +
@@ -177,7 +197,7 @@ body{
- + "> @@ -194,6 +214,14 @@ body{
- - - + + diff --git a/public/verify.php b/public/verify.php index b426dc8..b46b900 100644 --- a/public/verify.php +++ b/public/verify.php @@ -46,6 +46,7 @@ try { } $first_name = $user["first_name"]; $last_name = $user["last_name"]; + $username = $user["username"]; $note = $user["note"]; $email = $user["email"]; $admin_token = $user["admin_token"]; @@ -54,10 +55,12 @@ try { $adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $admin_token; $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); $mxMsg = new MatrixMessage(); - $mxMsg->set_body($first_name . ' ' . $last_name . "möchte sich registrieren und hat folgende Notiz hinterlassen:\r\n" + $mxMsg->set_body((strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username) + . " möchte sich registrieren und hat folgende Notiz hinterlassen:\r\n" . $note . "\r\n" . "Zum Bearbeiten hier klicken:\r\n" . $adminUrl); - $mxMsg->set_formatted_body($first_name . ' ' . $last_name . " möchte sich registrieren und hat folgende Notiz hinterlassen:
" + $mxMsg->set_formatted_body((strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username) + . " möchte sich registrieren und hat folgende Notiz hinterlassen:
" . $note . "
" . "Zum Bearbeiten hier klicken"); $mxMsg->set_type("m.text"); diff --git a/public/verify_admin.php b/public/verify_admin.php index 00ecbd5..6235ed7 100644 --- a/public/verify_admin.php +++ b/public/verify_admin.php @@ -95,7 +95,12 @@ try { print("

" . $language["ADMIN_REGISTER_ACCEPTED_BODY"] . "

"); } elseif ($action == RegisterState::RegistrationDeclined) { $mx_db->setRegistrationStateAdmin(RegisterState::RegistrationDeclined, $token); - send_mail_registration_decline($config["homeserver"], $first_name . " " . $last_name, $email, $decline_reason); + send_mail_registration_decline( + $config["homeserver"], + strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username, + $email, + $decline_reason + ); print("" . $language["ADMIN_VERIFY_SITE_TITLE"] . ""); print(""); print("

" . $language["ADMIN_VERIFY_SITE_TITLE"] . "

"); @@ -131,6 +136,8 @@ background: rgba(255, 255, 255, 0.8);
+
@@ -145,7 +152,7 @@ background: rgba(255, 255, 255, 0.8);
- +
-- 2.39.5 From 6143a23dd8fedab16328ff9e43e58e515624c78a Mon Sep 17 00:00:00 2001 From: Krombel Date: Mon, 16 Apr 2018 14:29:40 +0200 Subject: [PATCH 3/4] autoformat to reduce merge conflicts --- public/index.php | 403 ++++++++++++++++++++-------------------- public/verify.php | 115 ++++++------ public/verify_admin.php | 298 +++++++++++++++-------------- 3 files changed, 404 insertions(+), 412 deletions(-) diff --git a/public/index.php b/public/index.php index ede9a46..af5ebe6 100644 --- a/public/index.php +++ b/public/index.php @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - // enforce admin via https if (!isset($_SERVER['HTTPS'])) { - header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301); - exit(); + header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301); + exit(); } require_once "../language.php"; if (!file_exists("../config.php")) { - print($language["NO_CONFIGURATION"]); - exit(); + print($language["NO_CONFIGURATION"]); + exit(); } require_once "../config.php"; @@ -36,219 +35,215 @@ if (isset($config["operationMode"]) && $config["operationMode"] === "local") { // currently the case to store the password on our own is the only supported one $storePassword = false; if (isset($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"] && - isset($config["operationMode"]) && $config["operationMode"] === "synapse") { + isset($config["operationMode"]) && $config["operationMode"] === "synapse") { $storePassword = true; } session_start(); if ($_SERVER["REQUEST_METHOD"] == "POST") { - try { - if (!isset($_SESSION["token"]) || !isset($_POST["token"]) || $_SESSION["token"] != $_POST["token"]) { - // token not present or invalid - throw new Exception("UNKNOWN_SESSION"); - } - if (!isset($_POST["username"])) { - throw new Exception("UNKNOWN_USERNAME"); - } - if (strlen($_POST["username"] > 20 || strlen($_POST["username"]) < 3)) { - throw new Exception("USERNAME_LENGTH_INVALID"); - } - if (ctype_alnum($_POST['username']) != true) { - throw new Exception("USERNAME_NOT_ALNUM"); - } - if (isset($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"] && - $_POST["password"] != $_POST["password_confirm"]) { - throw new Exception("PASSWORD_NOT_MATCH"); - } - if (isset($_POST["note"]) && strlen($_POST["note"]) > 50) { - throw new Exception("NOTE_LENGTH_EXEEDED"); - } - if (!isset($_POST["email"]) || !filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) { - throw new Exception("EMAIL_INVALID_FORMAT"); - } - if ($storeFirstLastName) { - // only require first_name and last_name when we will evaluate them - if (!isset($_POST["first_name"]) || ! preg_match("/[A-Z][a-z]+/", $_POST["first_name"])) { - throw new Exception("FIRSTNAME_INVALID_FORMAT"); - } - if (!isset($_POST["last_name"]) || ! preg_match("/[A-Z][a-z]+/", $_POST["last_name"])) { - throw new Exception("SIRNAME_INVALID_FORMAT"); - } - $first_name = filter_var($_POST["first_name"], FILTER_SANITIZE_STRING); - $last_name = filter_var($_POST["last_name"], FILTER_SANITIZE_STRING); - } else { - $first_name = $last_name = ""; - } + try { + if (!isset($_SESSION["token"]) || !isset($_POST["token"]) || $_SESSION["token"] != $_POST["token"]) { + // token not present or invalid + throw new Exception("UNKNOWN_SESSION"); + } + if (!isset($_POST["username"])) { + throw new Exception("UNKNOWN_USERNAME"); + } + if (strlen($_POST["username"] > 20 || strlen($_POST["username"]) < 3)) { + throw new Exception("USERNAME_LENGTH_INVALID"); + } + if (ctype_alnum($_POST['username']) != true) { + throw new Exception("USERNAME_NOT_ALNUM"); + } + if (isset($config["getPasswordOnRegistration"]) && $config["getPasswordOnRegistration"] && + $_POST["password"] != $_POST["password_confirm"]) { + throw new Exception("PASSWORD_NOT_MATCH"); + } + if (isset($_POST["note"]) && strlen($_POST["note"]) > 50) { + throw new Exception("NOTE_LENGTH_EXEEDED"); + } + if (!isset($_POST["email"]) || !filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) { + throw new Exception("EMAIL_INVALID_FORMAT"); + } + if ($storeFirstLastName) { + // only require first_name and last_name when we will evaluate them + if (!isset($_POST["first_name"]) || !preg_match("/[A-Z][a-z]+/", $_POST["first_name"])) { + throw new Exception("FIRSTNAME_INVALID_FORMAT"); + } + if (!isset($_POST["last_name"]) || !preg_match("/[A-Z][a-z]+/", $_POST["last_name"])) { + throw new Exception("SIRNAME_INVALID_FORMAT"); + } + $first_name = filter_var($_POST["first_name"], FILTER_SANITIZE_STRING); + $last_name = filter_var($_POST["last_name"], FILTER_SANITIZE_STRING); + } else { + $first_name = $last_name = ""; + } - $username = filter_var($_POST["username"], FILTER_SANITIZE_STRING); - if ($storePassword && isset($_POST["password"])) { - $password = filter_var($_POST["password"], FILTER_SANITIZE_STRING); - } - $note = filter_var($_POST["note"], FILTER_SANITIZE_STRING); - $email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL); + $username = filter_var($_POST["username"], FILTER_SANITIZE_STRING); + if ($storePassword && isset($_POST["password"])) { + $password = filter_var($_POST["password"], FILTER_SANITIZE_STRING); + } + $note = filter_var($_POST["note"], FILTER_SANITIZE_STRING); + $email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL); - require_once("../database.php"); - $res = $mx_db->addRegistration($first_name, $last_name, $username, $note, $email); + require_once("../database.php"); + $res = $mx_db->addRegistration($first_name, $last_name, $username, $note, $email); - if (!isset($res["verify_token"])) { - error_log("sth. went wrong. registration did not throw but admin_token not set"); - throw Exception ("Unknown Error"); - } - $verify_token = $res["verify_token"]; + if (!isset($res["verify_token"])) { + error_log("sth. went wrong. registration did not throw but admin_token not set"); + throw Exception("Unknown Error"); + } + $verify_token = $res["verify_token"]; - $verify_url = $config["webroot"] . "/verify.php?t=" . $verify_token; - require_once "../mail_templates.php"; - $success = send_mail_pending_verification( - $config["homeserver"], - $storeFirstLastName ? $first_name . " " . $last_name : $username, - $email, - $verify_url); + $verify_url = $config["webroot"] . "/verify.php?t=" . $verify_token; + require_once "../mail_templates.php"; + $success = send_mail_pending_verification( + $config["homeserver"], $storeFirstLastName ? $first_name . " " . $last_name : $username, $email, $verify_url); - $mx_db->setRegistrationStateVerify( - ($success ? RegisterState::PendingEmailVerify : RegisterState::PendingEmailSend), - $verify_token); + $mx_db->setRegistrationStateVerify( + ($success ? RegisterState::PendingEmailVerify : RegisterState::PendingEmailSend), $verify_token); - print("Erfolgreich"); - print(""); - print("

Erfolgreich

"); - print("

Bitte überprüfe deine E-Mails um deine E-Mail-Adresse zu bestätigen.

"); - print("Zur Registrierungsseite"); - } catch (Exception $e) { - print("" . $language["REGISTRATION_REQUEST_FAILED"] . ""); - print(""); - print("

" . $language["REGISTRATION_REQUEST_FAILED"] . "

"); - if (isset($language[$e->getMessage()])) { - print("

" . $language[$e->getMessage()] . "

"); - } else { - print("

" . $e->getMessage() . "

"); - } - print("Zur Registrierungsseite"); - } + print("Erfolgreich"); + print(""); + print("

Erfolgreich

"); + print("

Bitte überprüfe deine E-Mails um deine E-Mail-Adresse zu bestätigen.

"); + print("Zur Registrierungsseite"); + } catch (Exception $e) { + print("" . $language["REGISTRATION_REQUEST_FAILED"] . ""); + print(""); + print("

" . $language["REGISTRATION_REQUEST_FAILED"] . "

"); + if (isset($language[$e->getMessage()])) { + print("

" . $language[$e->getMessage()] . "

"); + } else { + print("

" . $e->getMessage() . "

"); + } + print("Zur Registrierungsseite"); + } } else { - $_SESSION["token"] = bin2hex(random_bytes(16)); -?> - Registriere dich für <?php echo $config["homeserver"]; ?> - - - - - - -
-
-
-
-
-

Bitte für registrieren2-Schritt-Registrierung

-
-
- - -
-
-
- -
-
-
-
- -
-
-
- - -
- -
- -
- -
- -
- -
- -
-
-
- -
-
-
-
- -
-
-
- - "> - - - -

Hinweis:
- ist ein geschlossenes Chat-Netzwerk in dem jeder Nutzer bestätigt werden muss.
- Du bekommst eine E-Mail wenn jemand deine Mitgliedschaft bestätigt hat. An diese wird auch dein initiales Passwort gesendet. - Hinterlasse also bitte einen Hinweis zu dir (der nur den entsprechenden Personen gezeigt wird).
- Liebe Grüße vom Team von -

-
-
-
-
-
- + + + +
+
+
+
+
+

Bitte für registrieren2-Schritt-Registrierung

+
+
+
- var first_name = document.getElementById("first_name"); - first_name.oninvalid = function(event) { - event.target.setCustomValidity("Vorname muss das Format haben"); - } - first_name.onkeyup = function(event) { - event.target.setCustomValidity(""); - } - var last_name = document.getElementById("last_name"); - last_name.oninvalid = function(event) { - event.target.setCustomValidity("Nachname muss das Format haben"); - } - last_name.onkeyup = function(event) { - event.target.setCustomValidity(""); - } +
+
+
+ +
+
+
+
+ +
+
+
+ + +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+
+
+
+ +
+
+
+ + "> + + + +

Hinweis:
+ ist ein geschlossenes Chat-Netzwerk in dem jeder Nutzer bestätigt werden muss.
+ Du bekommst eine E-Mail wenn jemand deine Mitgliedschaft bestätigt hat. An diese wird auch dein initiales Passwort gesendet. + Hinterlasse also bitte einen Hinweis zu dir (der nur den entsprechenden Personen gezeigt wird).
+ Liebe Grüße vom Team von +

+
+
+
+
+
+ + - + diff --git a/public/verify.php b/public/verify.php index b46b900..605e528 100644 --- a/public/verify.php +++ b/public/verify.php @@ -15,82 +15,81 @@ */ require_once "../language.php"; if (!file_exists("../config.php")) { - print($language["NO_CONFIGURATION"]); - exit(); + print($language["NO_CONFIGURATION"]); + exit(); } require_once "../config.php"; require_once "../mail_templates.php"; // enforce admin via https if (!isset($_SERVER['HTTPS'])) { - header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301); - exit(); + header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301); + exit(); } session_start(); try { - if ($_SERVER["REQUEST_METHOD"] != "GET") { - throw new Exception("Method not allowed"); - } - if (!isset($_GET["t"])) { - throw new Exception("UNKNOWN_TOKEN"); - } - $token = filter_var($_GET["t"], FILTER_SANITIZE_STRING); + if ($_SERVER["REQUEST_METHOD"] != "GET") { + throw new Exception("Method not allowed"); + } + if (!isset($_GET["t"])) { + throw new Exception("UNKNOWN_TOKEN"); + } + $token = filter_var($_GET["t"], FILTER_SANITIZE_STRING); - require_once("../database.php"); + require_once("../database.php"); - $user = $mx_db->getUserForVerify($token); - if ($user == NULL) { - throw new Exception("UNKNOWN_TOKEN"); - } - $first_name = $user["first_name"]; - $last_name = $user["last_name"]; - $username = $user["username"]; - $note = $user["note"]; - $email = $user["email"]; - $admin_token = $user["admin_token"]; + $user = $mx_db->getUserForVerify($token); + if ($user == NULL) { + throw new Exception("UNKNOWN_TOKEN"); + } + $first_name = $user["first_name"]; + $last_name = $user["last_name"]; + $username = $user["username"]; + $note = $user["note"]; + $email = $user["email"]; + $admin_token = $user["admin_token"]; - require_once("../MatrixConnection.php"); - $adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $admin_token; - $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); - $mxMsg = new MatrixMessage(); - $mxMsg->set_body((strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username) - . " möchte sich registrieren und hat folgende Notiz hinterlassen:\r\n" - . $note . "\r\n" - . "Zum Bearbeiten hier klicken:\r\n" . $adminUrl); - $mxMsg->set_formatted_body((strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username) - . " möchte sich registrieren und hat folgende Notiz hinterlassen:
" - . $note . "
" - . "Zum Bearbeiten hier klicken"); - $mxMsg->set_type("m.text"); - $response = $mxConn->send($config["register_room"], $mxMsg); + require_once("../MatrixConnection.php"); + $adminUrl = $config["webroot"] . "/verify_admin.php?t=" . $admin_token; + $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); + $mxMsg = new MatrixMessage(); + $mxMsg->set_body((strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username) + . " möchte sich registrieren und hat folgende Notiz hinterlassen:\r\n" + . $note . "\r\n" + . "Zum Bearbeiten hier klicken:\r\n" . $adminUrl); + $mxMsg->set_formatted_body((strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username) + . " möchte sich registrieren und hat folgende Notiz hinterlassen:
" + . $note . "
" + . "Zum Bearbeiten hier klicken"); + $mxMsg->set_type("m.text"); + $response = $mxConn->send($config["register_room"], $mxMsg); - if ($response) { - $message = $language["SEND_MATRIX_FAIL"]; - } - $mx_db->setRegistrationStateVerify( - ($response ? RegisterState::PendingAdminVerify : RegisterState::PendingAdminSend), - $token); + if ($response) { + $message = $language["SEND_MATRIX_FAIL"]; + } + $mx_db->setRegistrationStateVerify( + ($response ? RegisterState::PendingAdminVerify : RegisterState::PendingAdminSend), $token); - send_mail_pending_approval($config["homeserver"], $first_name . " " . $last_name, $email); + send_mail_pending_approval($config["homeserver"], $first_name . " " . $last_name, $email); - print("" . $language["VERIFICATION_SUCEEDED"] . ""); - print(""); - print("

" . $language["VERIFICATION_SUCEEDED"] . "

"); - print("

" . $language["VERIFICATION_SUCCESS_BODY"] . "

"); - print("Zur Registrierungsseite"); + print("" . $language["VERIFICATION_SUCEEDED"] . ""); + print(""); + print("

" . $language["VERIFICATION_SUCEEDED"] . "

"); + print("

" . $language["VERIFICATION_SUCCESS_BODY"] . "

"); + print("Zur Registrierungsseite"); } catch (Exception $e) { - print("" . $language["VERIFICATION_FAILED"] . ""); - print(""); - print("

" . $language["VERIFICATION_FAILED"] . "

"); - if (isset($language[$e->getMessage()])) { - print("

" . $language[$e->getMessage()] . "

"); - } else { - print("

" . $e->getMessage() . "

"); - } - print("Zur Registrierungsseite"); + print("" . $language["VERIFICATION_FAILED"] . ""); + print(""); + print("

" . $language["VERIFICATION_FAILED"] . "

"); + if (isset($language[$e->getMessage()])) { + print("

" . $language[$e->getMessage()] . "

"); + } else { + print("

" . $e->getMessage() . "

"); + } + print("Zur Registrierungsseite"); } ?> - + diff --git a/public/verify_admin.php b/public/verify_admin.php index 6235ed7..8cdc928 100644 --- a/public/verify_admin.php +++ b/public/verify_admin.php @@ -15,177 +15,175 @@ */ require_once "../language.php"; if (!file_exists("../config.php")) { - print($language["NO_CONFIGURATION"]); - exit(); + print($language["NO_CONFIGURATION"]); + exit(); } require_once "../config.php"; require_once "../mail_templates.php"; // enforce admin via https if (!isset($_SERVER['HTTPS'])) { - header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301); - exit(); + header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301); + exit(); } session_start(); try { - if ($_SERVER["REQUEST_METHOD"] != "GET") { - throw new Exception("Method not allowed"); - } - if (!isset($_GET["t"])) { - throw new Exception("UNKNOWN_TOKEN"); - } - $token = filter_var($_GET["t"], FILTER_SANITIZE_STRING); + if ($_SERVER["REQUEST_METHOD"] != "GET") { + throw new Exception("Method not allowed"); + } + if (!isset($_GET["t"])) { + throw new Exception("UNKNOWN_TOKEN"); + } + $token = filter_var($_GET["t"], FILTER_SANITIZE_STRING); - require_once("../database.php"); + require_once("../database.php"); - $action = NULL; - if (isset($_GET["allow"])) { - $action = RegisterState::RegistrationAccepted; - } - $decline_reason = NULL; - if (isset($_GET["deny"])) { - $action = RegisterState::RegistrationDeclined; - if (isset($_GET["reason"])) { - $decline_reason = filter_var($_GET["reason"], FILTER_SANITIZE_STRING); - } - } + $action = NULL; + if (isset($_GET["allow"])) { + $action = RegisterState::RegistrationAccepted; + } + $decline_reason = NULL; + if (isset($_GET["deny"])) { + $action = RegisterState::RegistrationDeclined; + if (isset($_GET["reason"])) { + $decline_reason = filter_var($_GET["reason"], FILTER_SANITIZE_STRING); + } + } - $user = $mx_db->getUserForApproval($token); - if ($user == NULL) { - throw new Exception("UNKNOWN_TOKEN"); - } + $user = $mx_db->getUserForApproval($token); + if ($user == NULL) { + throw new Exception("UNKNOWN_TOKEN"); + } - $first_name = $user["first_name"]; - $last_name = $user["last_name"]; - $username = $user["username"]; - $note = $user["note"]; - $email = $user["email"]; + $first_name = $user["first_name"]; + $last_name = $user["last_name"]; + $username = $user["username"]; + $note = $user["note"]; + $email = $user["email"]; - if ($action == RegisterState::RegistrationAccepted) { - $mx_db->setRegistrationStateAdmin(RegisterState::PendingRegistration, $token); + if ($action == RegisterState::RegistrationAccepted) { + $mx_db->setRegistrationStateAdmin(RegisterState::PendingRegistration, $token); - // register user - require_once("../MatrixConnection.php"); - $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); + // register user + require_once("../MatrixConnection.php"); + $mxConn = new MatrixConnection($config["homeserver"], $config["access_token"]); - // generate a password with 8 characters - $password = $mx_db->addUser($first_name, $last_name, $username, $email); - if ($password != NULL) { - // send registration_success - $res = send_mail_registration_success($config["homeserver"], $first_name . " " . $last_name, $email, $username, $password, $config["howToURL"]); - if ($res) { - $mx_db->setRegistrationStateAdmin(RegisterState::AllDone, $token); - } else { - $mx_db->setRegistrationStateAdmin(RegisterState::PendingSendRegistrationMail, $token); - } - } else { - send_mail_registration_allowed_but_failed($config["homeserver"], $first_name . " " . $last_name, $email); - $mxMsg = new MatrixMessage(); - $mxMsg->set_type("m.text"); - $mxMsg->set_body("Fehler beim Registrieren von " . $first_name . " " . $last_name . "."); - $mxConn->send($config["register_room"], $mxMsg); - throw new Exception("REGISTRATION_FAILED"); - } + // generate a password with 8 characters + $password = $mx_db->addUser($first_name, $last_name, $username, $email); + if ($password != NULL) { + // send registration_success + $res = send_mail_registration_success($config["homeserver"], $first_name . " " . $last_name, $email, $username, $password, $config["howToURL"]); + if ($res) { + $mx_db->setRegistrationStateAdmin(RegisterState::AllDone, $token); + } else { + $mx_db->setRegistrationStateAdmin(RegisterState::PendingSendRegistrationMail, $token); + } + } else { + send_mail_registration_allowed_but_failed($config["homeserver"], $first_name . " " . $last_name, $email); + $mxMsg = new MatrixMessage(); + $mxMsg->set_type("m.text"); + $mxMsg->set_body("Fehler beim Registrieren von " . $first_name . " " . $last_name . "."); + $mxConn->send($config["register_room"], $mxMsg); + throw new Exception("REGISTRATION_FAILED"); + } - print("" . $language["ADMIN_VERIFY_SITE_TITLE"] . ""); - print(""); - print("

" . $language["ADMIN_VERIFY_SITE_TITLE"] . "

"); - print("

" . $language["ADMIN_REGISTER_ACCEPTED_BODY"] . "

"); - } elseif ($action == RegisterState::RegistrationDeclined) { - $mx_db->setRegistrationStateAdmin(RegisterState::RegistrationDeclined, $token); - send_mail_registration_decline( - $config["homeserver"], - strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username, - $email, - $decline_reason - ); - print("" . $language["ADMIN_VERIFY_SITE_TITLE"] . ""); - print(""); - print("

" . $language["ADMIN_VERIFY_SITE_TITLE"] . "

"); - print("

" . $language["ADMIN_REGISTER_DECLINED_BODY"] . "

"); - } else { + print("" . $language["ADMIN_VERIFY_SITE_TITLE"] . ""); + print(""); + print("

" . $language["ADMIN_VERIFY_SITE_TITLE"] . "

"); + print("

" . $language["ADMIN_REGISTER_ACCEPTED_BODY"] . "

"); + } elseif ($action == RegisterState::RegistrationDeclined) { + $mx_db->setRegistrationStateAdmin(RegisterState::RegistrationDeclined, $token); + send_mail_registration_decline( + $config["homeserver"], strlen($first_name . $last_name) > 0 ? $first_name . " " . $last_name : $username, $email, $decline_reason + ); + print("" . $language["ADMIN_VERIFY_SITE_TITLE"] . ""); + print(""); + print("

" . $language["ADMIN_VERIFY_SITE_TITLE"] . "

"); + print("

" . $language["ADMIN_REGISTER_DECLINED_BODY"] . "

"); + } else { - print("" . $language["ADMIN_VERIFY_SITE_TITLE"] . ""); - ?> - - - - - - -
-
-
-
-
-

-
-
-
- -
-
-
- -
-
-
-
- -
-
-
- -
- -
+ .centered-form .panel{ + background: rgba(255, 255, 255, 0.8); + box-shadow: rgba(0, 0, 0, 0.3) 20px 20px 20px; + } + + + + + +
+
+
+
+
+

+
+
+ + +
+
+
+ +
+
+
+
+ +
+
+
+ +
+ +
-
- -
- - - +
+ +
+ + + - -
-
-
-
-
-