matrix-register/lib/request.php

211 lines
7 KiB
PHP

<?php
/**
* file: lib/request.php
* date: 28.02.2021
* user: bernd@nr18.space
* desc:
*/
if (!defined('INCLUDES_ALLOWED'))
die('Access denied.');
require("base.php");
require("static/mail.php");
class Request extends BaseClass {
/**
* Klasse zur Bearbeitung einer Anfrage nach einem Matrix Accuont. Erbt
* aus der Klasse BaseClass ein Konfigurations- und ein Datenbankobjekt
* ($this->config, $this->db), die Funktion generateToken() und sowie
* die Variable $this-token.
*/
public function checkRequest(string &$message): bool {
/**
* Hauptfunktion der Klasse Request - steuert alle Prüfungen, das
* Speichern des Requests und das Versenden der Email. Gibt True
* oder False zurück und setzt die Variable "message", welche auf
* der Webseite ausgegeben wird.
*/
if (!isset($this->db)) {
$this->log->e("There is no database");
$message = "Something goes wrong";
return false;
}
$this->log->i("Request started for nick: {$_POST['login']}");
if (false === $this->checkCaptcha()) {
$message = "Captcha invalid";
return false;
} else if (false === $this->checkEmail()) {
$message = "Email invalid";
return false;
} else if (false === $this->checkMXID($this->config->getMxDomain())) {
$message = "User ID invalid";
return false;
} else if (false === $this->checkUser()) {
$message = "User Id is already taken";
return false;
} else {
if ($this->generateToken(16) === true) {
if ($this->saveRequest() === true) {
if ($this->sendVerificationMail() === true) {
$login = htmlspecialchars($_POST['login']);
$message = "Your request for '{$login}' is saved and a
verification mail is send";
return true;
}
}
}
}
return false;
}
private function saveRequest(): bool {
/**
* Veranlaßt die Speicherung der Anfrage in der Tabelle requests.
* Bekommt aus der Datenbank (auch im Falle einer PDO Exception)
* einen Boolean zurück.
*/
try {
$response = $this->db->saveRequest($this->token);
} catch (Exception $e) {
$this->log->e("Error: Database returns: {$e->getMessage()}");
}
if ($response === true) {
return true;
}
return false;
}
private function sendVerificationMail(): bool {
/**
* Verschickt die Mail mit dem Verifizierungslink.
* TODO: Reicht filter_input()? Was kann hier passieren?
*/
$mailTo = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$mxdomain = $this->config->getMxDomain();
$baseurl = $this->config->getBaseURL();
$validator = $this->config->getValidator();
$mailFrom = $this->config->getMailFrom();
$mailSubject = $this->config->getMailSubject();
$mailClosure = $this->config->getMailClosure();
$this->log->d("Try to send verification mail");
$link = $baseurl . $validator . $this->token . "\r\n\r\n";
$mailbody = MAILTEXT1 . " {$mxdomain} " . MAILTEXT2 . "\r\n\r\n" . $link . $mailClosure;
if (mail($mailTo, $mailSubject, $mailbody, $mailFrom))
{
$this->log->i("Validationmail successfull send");
return true;
}
$this->log->e("Sending validation mail failed");
return false;
}
private function checkCaptcha(): bool {
/**
* Prüfen, ob das Captcha die korrekte Hausnummer abbildet.
* Filter_input gibt im Erfolgsfall einen Integer zurück.
*/
$this->log->d("Checking captcha");
$captcha = filter_input(INPUT_POST, 'captcha', FILTER_VALIDATE_INT);
if ($captcha == 26) {
$this->log->d("Captcha valid");
return true;
}
$this->log->e("Invalid captcha");
return false;
}
private function checkEmail(): bool {
/**
* Prüfen, ob die Emailadresse schematisch gültig ist. filter_input
* gibt im Erfolgsfall den Wert, im Fehlerfall false oder null, wenn
* die Variable nicht gestzt ist, zurück. Letzteres wird beim
* vorangehenden Test checkForms geprüft und kann daher nicht
* auftreten.
*/
$this->log->d("Checking email schema");
if (filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL)) {
$this->log->d("Email schema is valid");
return true;
}
$this->log->e("Email schema invalid");
return false;
}
private function checkMXID(string $mxid): bool {
/**
* Prüft, ob der gewünschte localpart nur Zeichen enthält, die von
* der Matrix-Spezifikation erlaubt sind. Die Spezifikation erlaubt
* nur Kleinbuchstaben, Ziffern, Minus, Punkt, Gleichheitszeichen,
* Unterstrich und Schrägstrich. Geprüft wird auch, ob die Länge des
* gewünschten localpart zusammen mit dem domainpart sowie dem @ und
* : die Länge von 255 Zeichen nicht überschreiten.
*/
$this->log->d("Check MXID localpart");
$str_array = str_split($_POST['login']);
$max_length = 255 - (mb_strlen($mxid) + 2);
$specials = array('-', '.', '=', '_', '/');
$numbers = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
$possibles = array_merge(range('a', 'z'), $numbers, $specials);
foreach ($str_array as $item) {
if (in_array($item, $possibles, true) === false) {
$this->log->e("MXID localpart: invalid character: {$item}");
return false;
}
}
if (mb_strlen($_POST['login']) > $max_length) {
$this->log->e("MXID localpart: too long");
return false;
}
$this->log->d("MXID localpart is valid");
return true;
}
private function checkUser(): bool {
/**
* Prüft, ob der gewünschte Nutzernamen nicht bereits vergeben ist.
* Dazu wird in den Datenbanktabellen users (bereits registrierte
* benutzer) und requests (bereits beantragte benutzer) nach einer
* eventuellen Übereinstimmung geschaut. Eine spezielle Behandlung
* des Suchstrings ist nach meiner Meinung hier nicht nötig, da der
* String vorher auf Matrix-konforme Zeichen getestet wurde und die
* Datenbankfuntionen prepared Statements verwendet.
*/
$this->log->d("Checking if username is available");
$nick = $_POST['login'];
if ($this->db->UserExistsInRequests($nick) === true) {
return false;
}
if ($this->db->UserExistsInUsers($nick) === true) {
return false;
}
return true;
}
}
?>