Fix icon hover colors, text alignment, and UI improvements
This commit is contained in:
+88
-17
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user