111 lines
4.0 KiB
PHP
111 lines
4.0 KiB
PHP
<?php
|
|
/*
|
|
* Reflector Dashboard — web front-end for SvxReflector
|
|
* Copyright (C) 2026 SA7AUX (Björn)
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
* Software Foundation, either version 3 of the License, or (at your option) any
|
|
* later version. See the LICENSE file for details.
|
|
*/
|
|
declare(strict_types=1);
|
|
require __DIR__ . '/lib.php';
|
|
|
|
$j = json_in();
|
|
$callsign = base_call(trim((string)($j['callsign'] ?? '')));
|
|
$firstname = trim((string)($j['firstname'] ?? ''));
|
|
$lastname = trim((string)($j['lastname'] ?? ''));
|
|
$email = norm_email((string)($j['email'] ?? ''));
|
|
$city = trim((string)($j['city'] ?? ''));
|
|
$state = trim((string)($j['state'] ?? ''));
|
|
$country = trim((string)($j['country'] ?? ''));
|
|
$pass1 = (string)($j['password'] ?? '');
|
|
$pass2 = (string)($j['password2'] ?? '');
|
|
|
|
// Rate limit: 3 registrations per hour per IP
|
|
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
|
rate_limit("register:{$ip}", 3, 3600);
|
|
|
|
// Validation
|
|
if ($callsign === '' || $firstname === '' || $email === '' || $pass1 === '') {
|
|
json_out(['ok' => false, 'error' => 'Callsign, first name, email and password are required.'], 400);
|
|
}
|
|
if (strlen($callsign) < 3 || strlen($callsign) > 10) {
|
|
json_out(['ok' => false, 'error' => 'Callsign must be 3-10 characters.'], 400);
|
|
}
|
|
if (!preg_match('/^[A-Z0-9]+$/', $callsign)) {
|
|
json_out(['ok' => false, 'error' => 'Callsign must contain only letters and numbers.'], 400);
|
|
}
|
|
if ($pass1 !== $pass2) {
|
|
json_out(['ok' => false, 'error' => 'Passwords do not match.'], 400);
|
|
}
|
|
if (strlen($pass1) < 6) {
|
|
json_out(['ok' => false, 'error' => 'Password must be at least 6 characters.'], 400);
|
|
}
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
json_out(['ok' => false, 'error' => 'Invalid email address.'], 400);
|
|
}
|
|
|
|
// Global callsign ban blocks registration
|
|
if (is_call_banned($callsign)) {
|
|
json_out(['ok' => false, 'error' => 'This callsign is not permitted to register.'], 403);
|
|
}
|
|
|
|
// Check duplicates
|
|
$r = redis();
|
|
$existing_cs = $r->get(call_index_key($callsign));
|
|
if (is_string($existing_cs) && $existing_cs !== '') {
|
|
json_out(['ok' => false, 'error' => 'Callsign or email already registered.'], 409);
|
|
}
|
|
$existing_em = $r->get(email_index_key($email));
|
|
if (is_string($existing_em) && $existing_em !== '') {
|
|
json_out(['ok' => false, 'error' => 'Callsign or email already registered.'], 409);
|
|
}
|
|
|
|
// Create account
|
|
$uuid = uuidv4();
|
|
$now = (string)time();
|
|
|
|
$r->hMSet(acct_key($uuid), [
|
|
'callsign' => $callsign,
|
|
'firstname' => $firstname,
|
|
'lastname' => $lastname,
|
|
'shortname' => $firstname,
|
|
'email' => $email,
|
|
'city' => $city,
|
|
'state' => $state,
|
|
'country' => $country,
|
|
'password_hash' => password_hash($pass1, PASSWORD_DEFAULT),
|
|
'email_verified' => '0',
|
|
'acct_flags' => '1',
|
|
'created_at' => $now,
|
|
]);
|
|
|
|
// Create indexes
|
|
$r->set(call_index_key($callsign), $uuid);
|
|
$r->set(email_index_key($email), $uuid);
|
|
|
|
// Generate email verification token
|
|
$tid = token_id();
|
|
$secret = token_secret();
|
|
$hash = token_hash($secret);
|
|
|
|
$k = rkey("token:verify:$tid");
|
|
$r->hMSet($k, ['uid' => $uuid, 'hash' => $hash]);
|
|
$r->expire($k, 60 * 60 * 24);
|
|
|
|
// Send verification email
|
|
$base = rtrim(cfg()['base_url'], '/');
|
|
$link = $base . '/auth/verify.php?tid=' . urlencode($tid) . '&token=' . urlencode($secret);
|
|
$body = "Hello {$firstname},\n\n";
|
|
$body .= "Thank you for registering on Reflector Dashboard with callsign {$callsign}.\n\n";
|
|
$body .= "Please verify your email address by clicking the link below:\n\n";
|
|
$body .= "{$link}\n\n";
|
|
$body .= "This link is valid for 24 hours.\n\n";
|
|
$body .= "After email verification, an administrator will review and activate your account.\n\n";
|
|
$body .= "73,\nReflector Dashboard";
|
|
$mail_sent = mail_send($email, "Verify your email - Reflector Dashboard", $body);
|
|
|
|
json_out(['ok' => true, 'callsign' => $callsign, 'email_sent' => $mail_sent]);
|