Enable conference registration

Change-Id: I5e973513d78c63a48e62ff1aa3a5d9a1621f4a4d
diff --git a/User.php b/User.php
index c07dd5e..5e8487e 100644
--- a/User.php
+++ b/User.php
@@ -19,11 +19,11 @@
     public bool $privacy_policy_signed;
     public bool $author;
     public $accepted_paper_id;
-    public $participation_confirmed;
+    public bool $participation_confirmed;
     public $participation_confirmed_at;
     public bool $student;
     public bool $conference_dinner;
-    public $total_costs;
+    public float $total_due;
 
     function __construct()
     {
@@ -47,7 +47,7 @@
         $this->participation_confirmed_at = "";
         $this->student = false;
         $this->conference_dinner = false;
-        $this->total_costs = 0;
+        $this->total_due = 0.0;
     }
 
     function __construct1(object $data)
@@ -117,6 +117,8 @@
                 if (is_bool($value)) {
                     if ($value) $value = "yes";
                     else $value = "no";
+                } elseif(is_float($value)) {
+                    $key .= " €";
                 }
                 $value = str_replace("\r\n", "<br>", $value);
                 $key = str_replace("_", " ", ucfirst($key));
diff --git a/db_backend.php b/db_backend.php
index 5a92dc0..e1be86e 100644
--- a/db_backend.php
+++ b/db_backend.php
@@ -5,74 +5,74 @@
 
 use \User as User;
 
-function open_db() {
-    global $DB, $DB_USER, $DB_PASS;
-    try {
-        # MS SQL Server and Sybase with PDO_DBLIB 
-        $DBH = new PDO($DB, $DB_USER, $DB_PASS);
-    }
-    catch(PDOException $e) {
-        echo $e->getMessage();
-    }
-    return $DBH;
-}
-
-function db_add_user(User $user)
+class DB
 {
-    $DBH = open_db();
-    $columns = getColumnNames($DBH, "person");
-    foreach ((array) $user as $key => $value) {
-        if (array_key_exists($key, (array) $columns)) {
-            $a[$key] = $value;
+
+    public $pdo;
+    public $log;
+
+    function __construct($log)
+    {
+        global $DB, $DB_USER, $DB_PASS;
+        $this->log = $log;
+        try {
+            $this->pdo = new PDO($DB, $DB_USER, $DB_PASS);
+            if($log)
+                $log->info("Connected to database $DB");
+        } catch (PDOException $e) {
+            echo $e->getMessage();
+            if($log)
+                $log->error($e->getMessage());
         }
     }
-    $keys = array_keys($a);
-    $sql = "INSERT INTO person (".implode(", ",$keys).") \n";
-    $sql .= "VALUES ( :".implode(", :",$keys).")";
-    $q = $DBH->prepare($sql);
-    $i=1;
-    foreach($a as $value)
-        $q->bindParam($i++, $value);
-    print $sql. "\n";
-    return $q->execute($a);
-}
 
-function getColumnNames($dbh, $table)
-{
-    $sql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table";
-    try {
-        $stmt = $dbh->prepare($sql);
-        $stmt->bindValue(':table', $table, PDO::PARAM_STR);
-        $stmt->execute();
-        $output = array();
-        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
-            $output[$row['COLUMN_NAME']] = $row['COLUMN_NAME'];
+    function add_user(User $user)
+    {
+        $columns = $this->getColumnNames("person");
+        foreach ((array) $user as $key => $value) {
+            if (array_key_exists($key, (array) $columns)) {
+                $a[$key] = $value;
+            }
         }
-        return $output;
-    } catch (PDOException $pe) {
-        trigger_error('Could not connect to MySQL database. ' . $pe->getMessage(), E_USER_ERROR);
+        $keys = array_keys($a);
+        $sql = "INSERT INTO person (" . implode(", ", $keys) . ") \n";
+        $sql .= "VALUES ( :" . implode(", :", $keys) . ")";
+        $q = $this->pdo->prepare($sql);
+        $i = 1;
+        foreach ($a as $value)
+            $q->bindParam($i++, $value);
+        $this->log->debug($sql);
+        return $q->execute($a);
+    }
+
+    function getColumnNames($table)
+    {
+        $sql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table";
+        try {
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindValue(':table', $table, PDO::PARAM_STR);
+            $stmt->execute();
+            $output = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $output[$row['COLUMN_NAME']] = $row['COLUMN_NAME'];
+            }
+            return $output;
+        } catch (PDOException $pe) {
+            trigger_error('Could not connect to MySQL database. ' . $pe->getMessage(), E_USER_ERROR);
+        }
+    }
+
+    function mail_count($email)
+    {
+        $sql = "SELECT COUNT(*) FROM person WHERE email = :email";
+        try {
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindValue(':email', $email, PDO::PARAM_STR);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            return $row['COUNT(*)'];
+        } catch (PDOException $pe) {
+            trigger_error('Could not connect to MySQL database. ' . $pe->getMessage(), E_USER_ERROR);
+        }
     }
 }
-
-try {
-    # MS SQL Server and Sybase with PDO_DBLIB 
-    $DBH = new PDO($DB, $DB_USER, $DB_PASS);
-    $table = "person";
-    $sql = "SHOW COLUMNS FROM " . $table;
-    $stmt = $DBH->prepare($sql);
-    # $stmt->bindValue(':table', $table, PDO::PARAM_STR);
-    $stmt->execute();
-    $output = array();
-    while($row = $stmt->fetch(PDO::FETCH_NUM)){
-        $output[] = $row[0];
-        print $row[0] . "\n";
-        }
-    }
-  catch(PDOException $e) {
-      echo $e->getMessage();
-  }
-
-  $user=new User();
-  $user->username="test";
-  print $user->to_string();
-  db_add_user($user);
diff --git a/index.php b/index.php
index d4310c8..5ce87e6 100755
--- a/index.php
+++ b/index.php
@@ -5,6 +5,7 @@
 include_once 'utils.php';
 include_once 'mail.php';
 include_once 'ldap.php';
+include_once 'db_backend.php';
 include_once 'validators.php';
 include_once 'User.php';
 require __DIR__ . '/vendor/autoload.php';
@@ -13,6 +14,7 @@
 use Monolog\Handler\StreamHandler;
 use Monolog\Handler\RotatingFileHandler;
 use \User as User;
+use \DB as DB;
 include $TEMPLATE . "strings.php";
 $log = new Logger('signup');
 $log->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/signup.log', 0, Logger::DEBUG));
@@ -124,17 +126,23 @@
 
 function verify_request(User $user)
 {
+    global $log, $CONFERENCE_REGISTRATION, $CAPTCHA_LENGTH;
+
     $TEMPLATE = template_path();
     unset($_SESSION['captcha_token']);
     include $TEMPLATE . 'strings.php';
     $password = $_POST["password"];
     $error = "";
 
-    $error .= validate_username($user->username);
+    if (!$CONFERENCE_REGISTRATION) {
+        $error .= validate_username($user->username);
+        $error .= validate_password($password);
+    }
+    $log->debug("validating request");
     $error .= validate_name($user->first_name, $FIRST_NAME_VALIDATION_ERROR);
     $error .= validate_name($user->last_name, $LAST_NAME_VALIDATION_ERROR);
+    $log->debug("request validated: $error");
     $error .= validate_email($user->email);
-    $error .= validate_password($password);
 
 
     if ($CAPTCHA_LENGTH > 0  &&  !(isset($_SESSION['captcha']) && PhraseBuilder::comparePhrases($_SESSION['captcha'], $_POST['captcha']))) {
@@ -156,7 +164,8 @@
     $pending = redis_get("pending");
     if ($pending) {
         $maillist = $pending->mails;
-        array_push($maillist, $user->email);
+        if(is_array($maillist))
+            array_push($maillist, $user->email);
     } else
         $maillist = [$user->email];
     redis_set("pending", (object)["mails" => $maillist], $MAIL_CONFIRMATION_AWAIT_DELAY);
@@ -217,12 +226,14 @@
                 }
                 break;
             case "recover":
+                if ($CONFERENCE_REGISTRATION)
+                    break;
                 $TEMPLATE = template_path();
                 unset($_SESSION['captcha_token']);
                 include $TEMPLATE . 'strings.php';
 
                 $email = $_POST["email"];
-                if (!ldap_mail_count($email)) {
+                if (($CONFERENCE_REGISTRATION & ! (new DB($log))->mail_count($email)) || !ldap_mail_count($email)) {
                     unset($_POST['email']);
                     $error = $error . $STRINGS->recover_email_not_registered;
                 }
@@ -260,6 +271,8 @@
                 break;
 
             case "password_change":
+                if ($CONFERENCE_REGISTRATION)
+                    break;
                 $password = $_POST['password'];
                 $error = validate_password($password);
                 if ($error) {
@@ -295,7 +308,7 @@
                 $token = $_GET["token"];
                 $user = redis_get_user($token);
                 if ($user && gettype($user) == "object") {
-                    if (ldap_add_user($user)) {
+                    if (($CONFERENCE_REGISTRATION && (new DB($log))->add_user($user)) || (!$CONFERENCE_REGISTRATION && ldap_add_user($user))) {
                         if ($REDIRECT_TO)
                             header("refresh:5;url=" . $REDIRECT_TO);
 
@@ -308,7 +321,7 @@
                             }
                         }
                         redis_inc_ipdata(getClientIP(), "register");
-                        $log->info("User registered: " . $user->to_string());
+                        $log->info("User registration completed: " . $user->to_string());
                         echo $STRINGS->email_confirmation;
                         if (isset($POST_REGISTER_HOOK)) $POST_REGISTER_HOOK($user);
                         include $TEMPLATE . "mail_confirmed.htm";
diff --git a/static/main.js b/static/main.js
index a07eb52..277b4ef 100644
--- a/static/main.js
+++ b/static/main.js
@@ -2,7 +2,7 @@
 (function () {
     'use strict';
     window.addEventListener('load', function () {
-        update_total_costs();
+        update_total_due();
         // Fetch all the forms we want to apply custom Bootstrap validation styles to
         var forms = document.getElementsByClassName('needs-validation');
         // Loop over them and prevent submission
@@ -18,7 +18,7 @@
     }, false);
 })();
 
-function update_total_costs() {
+function update_total_due() {
     var costs = 0;
 
 
@@ -31,7 +31,7 @@
             costs += parseInt($("#conference_dinner_price").text(), 10);
         }
     }
-    $("#total_costs").val(costs.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2}));
+    $("#total_due").val(costs.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2}));
 }
 
 function update_paper_id_field() {
diff --git a/templates/email.php b/templates/email.php
index 58eb17f..c55c38e 100644
--- a/templates/email.php
+++ b/templates/email.php
@@ -8,10 +8,10 @@
 
 $WELCOME_TEMPLATE = (object)[
         "subject" => "[" . $SERVICE_ACRONYM . "] Thank you for your " . $SERVICE_ACRONYM . " registration",
-        "text"    => "Thank for registering at " . $SERVICE_ACRONYM . " .",
+        "text"    => "Thank for registering for " . $SERVICE_ACRONYM . " .",
         "html" => "<html><body>
                 <p>Dear {{full_name}},</p>
-                <p>Thank for registering at " . $SERVICE_ACRONYM . " .</p>"
+                <p>Thank for registering for " . $SERVICE_ACRONYM . " .</p>"
                 . $REGISTRATION_INFORMATION . "
                 <p>
                 <p>Best regards,</p>
diff --git a/templates/register.htm b/templates/register.htm
index 5e4df50..3ce7a33 100644
--- a/templates/register.htm
+++ b/templates/register.htm
@@ -507,7 +507,7 @@
         <div class="form-group form-check">
             <input type="checkbox" class="form-check-input" name="participation_confirmed" id="participate" required
                 <?php echo isset($_POST['participation_confirmed']) ? 'checked' : '' ?>
-                oninput="update_total_costs()"
+                oninput="update_total_due()"
                 value="participation_confirmed">
             <label class="form-check-label fs-4" for="participation_confirmed">I confirm that I will be attending the <?php echo $SERVICE_ACRONYM; ?> conference. 
                 <?php if(new DateTimeImmutable("now") <= new DateTimeImmutable($EARLYBIRD_DEADLINE." 23:59:59 HST")) {
@@ -529,7 +529,7 @@
         <div class="form-group form-check">
             <input type="checkbox" class="form-check-input" name="student" id="student"
                 <?php echo isset($_POST['student']) ? 'checked' : '' ?>
-                oninput="update_total_costs()"
+                oninput="update_total_due()"
                 value="student">
             <label class="form-check-label fs-4" for="student">I am student and will therefore benefit from student discount of <strong>€ <span id="student_discount"><?php echo $STUDENT_DISCOUNT; ?></span></strong>.
             </label>
@@ -541,7 +541,7 @@
         <div class="form-group form-check">
             <input type="checkbox" class="form-check-input" name="conference_dinner" id="conference_dinner"
                 <?php echo isset($_POST['conference_dinner']) ? 'checked' : '' ?>
-                oninput="update_total_costs()"
+                oninput="update_total_due()"
                 value="conference_dinner">
             <label class="form-check-label fs-4" for="conference_dinner">I will be attending the conference dinner on 20 July at 8pm at the Rheinterrassen for the price of <strong>€ <span id="conference_dinner_price"><?php echo $CONFERENCE_DINNER; ?></span></strong>.
             </label>
@@ -551,7 +551,7 @@
             <label class="form-label fw-bold" for="exampleInputAmount">Total Amount in Euro to be paid</label>
             <div class="input-group">
               <div class="input-group-text fs-3">€</div>
-              <input lang="en" name="total_costs" id="total_costs" class="form-control text-end fw-bold fs-3" readonly>
+              <input lang="en" name="total_due" id="total_due" class="form-control text-end fw-bold fs-3" readonly>
             </div>
             <div id="totalHelp" class="form-text fs-4">Only bank transfer is available as a payment method. After submitting the form, you will be asked to confirm your e-mail address. After you have confirmed it, you will receive an invoice with all bank details.</div>
     </div>
diff --git a/templates/strings.php b/templates/strings.php
index 557a60f..dc2f1a3 100644
--- a/templates/strings.php
+++ b/templates/strings.php
@@ -1,4 +1,6 @@
 <?php
+include_once 'config.php';
+
 $RUNTIME_ERROR = (object)[
         "not_found" => "<center><h2>This page does not exist!</h2></center>",
         "template_not_found" => "Either you did not create the folder '{{template}}' or strings.php is missing on it. Maybe you have set LANG_CC: '{{langcc}}' wrong on config.php?",
@@ -30,7 +32,7 @@
 ];
 
 $EMAIL_VALIDATION_ERROR = (object)[
-        "registered" => "This email is already belongs to an account. Did you <a href='{{link}}'>forget your password?</a><br>",
+        "registered" => "This email is already belongs to an account. It seems like you are already registered. Please check your mailbox." . ($CONFERENCE_REGISTRATION? "": "Or did you <a href='{{link}}'>forget your password?</a><br>"),
         "invalid" => "Invalid email format<br>",
         "blacklisted" => "This email service is not allowed<br>",
         "pending" => "This email is already pending approval, check your mailbox or try to register with a different email<br>",
diff --git a/validators.php b/validators.php
index e40222f..966f1d1 100644
--- a/validators.php
+++ b/validators.php
@@ -3,6 +3,9 @@
 include_once 'redis.php';
 include_once 'config.php';
 include_once 'utils.php';
+include_once 'db_backend.php';
+
+use \DB as DB;
 
 $TEMPLATE = template_path();
 
@@ -63,26 +66,35 @@
 
 function validate_email(string $email)
 {
-        global $TEMPLATE;
-        include "config.php";
+        global $CONFERENCE_REGISTRATION, $TEMPLATE, $log, $MAIL_HOST_BLACKLIST, $EMAIL_VALIDATION_ERROR, $BASE_URL;
         include $TEMPLATE . 'strings.php';
         $error = "";
 
-        if (ldap_mail_count($email)) {
+        if (($CONFERENCE_REGISTRATION && (new DB(null))->mail_count($email)) || (!$CONFERENCE_REGISTRATION && ldap_mail_count($email))) {
+                $log->info("Email already registered");
                 $error = $error . format($EMAIL_VALIDATION_ERROR->registered, ["link" => $BASE_URL . "?type=recover"]);
         }
-        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
-                $error = $error . $EMAIL_VALIDATION_ERROR->invalid;
-        } elseif (in_array(explode("@", $email)[1], $MAIL_HOST_BLACKLIST)) {
+        if (in_array(explode("@", $email)[1], $MAIL_HOST_BLACKLIST)) {
                 $error = $error . $EMAIL_VALIDATION_ERROR->blacklisted;
         }
+        $log->debug("Checking if email is pending");
         $pending = redis_get("pending");
         if ($pending) {
+                $log->debug("Email might be pending");
                 $maillist = $pending->mails;
-                if (in_array($email, $maillist)) {
-                        $error = $error . $EMAIL_VALIDATION_ERROR->pending;
+                if (is_array($maillist) && in_array($email, $maillist)) {
+                        if ($CONFERENCE_REGISTRATION) {
+                                unset($maillist[array_search($user->email, $maillist)]);
+                                redis_set("pending", (object)["mails" => $maillist], $MAIL_CONFIRMATION_AWAIT_DELAY);
+                                $log->debug("Email was pending, but let participant change details.");
+                                echo '<div class="alert alert-warning" role="alert">A confirmation request has already been sent to this email, but no problem.</div>';
+                        } else {
+                                $error = $error . $EMAIL_VALIDATION_ERROR->pending;
+                                $log->debug("Email is pending");
+                        }
                 }
         }
+        $log->debug("email validated, result: $error");
         return $error;
 }