Fix icon hover colors, text alignment, and UI improvements

This commit is contained in:
Krikorios
2026-02-25 23:25:59 +02:00
parent 00fda3e045
commit fcc4352afa
46 changed files with 4011 additions and 600 deletions
+88 -17
View File
@@ -148,7 +148,10 @@ class MySQLDB {
/** Ensure the table exists (auto-create a generic key-value/json table) */
private function ensureTable($table) {
$safe = preg_replace('/[^a-zA-Z0-9_]/', '', $table);
$this->pdo->exec("CREATE TABLE IF NOT EXISTS `$safe` (
if ($safe === '' || strlen($safe) > 64) {
throw new Exception('Invalid table name');
}
$this->pdo->exec("CREATE TABLE IF NOT EXISTS `{$safe}` (
`id` VARCHAR(64) NOT NULL PRIMARY KEY,
`data` JSON NOT NULL,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
@@ -390,8 +393,9 @@ function requireAuth() {
*/
function jsonResponse($data, $code = 200) {
http_response_code($code);
echo json_encode($data);
exit();
ob_clean();
echo json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
die();
}
/**
@@ -449,6 +453,8 @@ function requireSameOriginRequest() {
$allowedOrigins[] = 'http://localhost:8080';
$allowedOrigins[] = 'http://localhost:8000';
$allowedOrigins[] = 'http://127.0.0.1:8080';
$allowedOrigins[] = 'http://localhost';
$allowedOrigins[] = 'http://127.0.0.1';
}
$origin = trim((string)($_SERVER['HTTP_ORIGIN'] ?? ''));
@@ -469,6 +475,11 @@ function requireSameOriginRequest() {
jsonResponse(['success' => false, 'message' => 'Forbidden referer'], 403);
}
// In development mode, allow requests without Origin/Referer headers (e.g., for curl testing)
if ($appEnv !== 'production') {
return;
}
// Browser request with neither header is suspicious for public form submissions.
jsonResponse(['success' => false, 'message' => 'CSRF validation failed'], 403);
}
@@ -503,12 +514,29 @@ function getSetting($key, $default = null) {
* Send email (mail() or SMTP based on admin settings)
*/
function sendEmail($to, $subject, $htmlBody, $plainBody = '', $replyTo = null) {
// Validate required parameters
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
return ['success' => false, 'message' => 'Invalid recipient email address'];
}
if (trim((string)$subject) === '') {
return ['success' => false, 'message' => 'Subject cannot be empty'];
}
if (trim((string)$htmlBody) === '') {
return ['success' => false, 'message' => 'Email body cannot be empty'];
}
if (!empty($replyTo) && !filter_var($replyTo, FILTER_VALIDATE_EMAIL)) {
return ['success' => false, 'message' => 'Invalid reply-to email address'];
}
// Determine transport: admin setting → auto-detect from .env
$defaultTransport = (ENV_SMTP_HOST !== '') ? 'smtp' : 'mail';
$transport = strtolower((string)getSetting('email_transport', $defaultTransport));
$fromEmail = trim((string)getSetting('smtp_from_email', ENV_SMTP_FROM_EMAIL ?: ADMIN_EMAIL));
if ($fromEmail === '') {
if ($fromEmail === '' || !filter_var($fromEmail, FILTER_VALIDATE_EMAIL)) {
$fromEmail = ADMIN_EMAIL;
}
@@ -578,34 +606,60 @@ function sendEmailViaSmtp($payload) {
$port = (int)$payload['port'];
$encryption = $payload['encryption'];
$remote = $encryption === 'ssl' ? 'ssl://' . $host : $host;
$socket = @stream_socket_client($remote . ':' . $port, $errno, $errstr, 20, STREAM_CLIENT_CONNECT);
if (!$socket) {
return ['success' => false, 'message' => 'SMTP connect failed: ' . $errstr];
// Validate port range
if ($port < 1 || $port > 65535) {
return ['success' => false, 'message' => 'Invalid SMTP port'];
}
stream_set_timeout($socket, 20);
$remote = $encryption === 'ssl' ? 'ssl://' . $host : $host;
$errno = 0;
$errstr = '';
$socket = @stream_socket_client($remote . ':' . $port, $errno, $errstr, 30, STREAM_CLIENT_CONNECT);
if (!$socket) {
return ['success' => false, 'message' => 'SMTP connect failed: ' . ($errstr ?: 'Unknown error')];
}
stream_set_timeout($socket, 30);
$expect = function($codes) use ($socket) {
$response = '';
$timeout = false;
while (($line = fgets($socket, 515)) !== false) {
$response .= $line;
if (preg_match('/^\d{3}\s/', $line)) {
break;
}
}
// Check for timeout
if ($line === false) {
$metadata = stream_get_meta_data($socket);
if (isset($metadata['timed_out']) && $metadata['timed_out']) {
throw new Exception('SMTP timeout - no response from server');
}
}
if ($response === '') {
throw new Exception('No response from SMTP server');
}
$code = (int)substr($response, 0, 3);
if (!in_array($code, (array)$codes, true)) {
throw new Exception(trim($response));
throw new Exception(trim($response ?: 'Unknown SMTP error'));
}
return $response;
};
$send = function($command) use ($socket) {
fwrite($socket, $command . "\r\n");
if (!is_resource($socket)) {
throw new Exception('Socket connection lost');
}
$bytes = fwrite($socket, $command . "\r\n");
if ($bytes === false) {
throw new Exception('Failed to write to socket');
}
};
try {
@@ -662,16 +716,27 @@ function sendEmailViaSmtp($payload) {
$message .= $payload['html'] . "\r\n\r\n";
$message .= "--{$boundary}--\r\n.";
fwrite($socket, $message . "\r\n");
if (!is_resource($socket)) {
throw new Exception('Socket connection lost before sending message');
}
$bytes = fwrite($socket, $message . "\r\n");
if ($bytes === false) {
throw new Exception('Failed to send message to SMTP server');
}
$expect([250]);
$send('QUIT');
fclose($socket);
if (is_resource($socket)) {
fclose($socket);
}
return ['success' => true, 'message' => 'Sent via SMTP'];
} catch (Exception $e) {
fclose($socket);
return ['success' => false, 'message' => 'SMTP send failed: ' . $e->getMessage()];
if (is_resource($socket)) {
fclose($socket);
}
error_log('MSPE SMTP error: ' . $e->getMessage());
return ['success' => false, 'message' => 'Email delivery failed'];
}
}
@@ -726,7 +791,13 @@ function handleFileUpload($file, $subdir = '') {
// Create upload directory
$uploadDir = UPLOAD_DIR . ($subdir ? $subdir . '/' : '');
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
if (!mkdir($uploadDir, 0755, true)) {
return ['success' => false, 'message' => 'Failed to create upload directory'];
}
}
if (!is_writable($uploadDir)) {
return ['success' => false, 'message' => 'Upload directory is not writable'];
}
// Generate unique filename (cryptographically random)