forked from xziino/ff14-mitigator
190 lines
6.3 KiB
PHP
190 lines
6.3 KiB
PHP
<?php
|
|
|
|
function load_env_file(string $path): void {
|
|
if (!is_file($path)) return;
|
|
|
|
foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || str_starts_with($line, '#')) continue;
|
|
|
|
[$key, $value] = array_pad(explode('=', $line, 2), 2, '');
|
|
$key = trim($key);
|
|
if ($key === '') continue;
|
|
|
|
$value = trim($value);
|
|
if (
|
|
strlen($value) >= 2
|
|
&& (($value[0] === '"' && $value[-1] === '"') || ($value[0] === "'" && $value[-1] === "'"))
|
|
) {
|
|
$value = substr($value, 1, -1);
|
|
}
|
|
|
|
$_ENV[$key] = $value;
|
|
putenv($key . '=' . $value);
|
|
}
|
|
}
|
|
|
|
function env_value(string $key, ?string $default = null): string {
|
|
$value = $_ENV[$key] ?? getenv($key);
|
|
if ($value === false || $value === null || $value === '') {
|
|
if ($default !== null) return $default;
|
|
throw new RuntimeException('Missing required environment value: ' . $key);
|
|
}
|
|
return (string)$value;
|
|
}
|
|
|
|
function env_bool(string $key, bool $default = false): bool {
|
|
$value = strtolower(env_value($key, $default ? 'true' : 'false'));
|
|
return in_array($value, ['1', 'true', 'yes', 'on'], true);
|
|
}
|
|
|
|
function env_list(string $key): array {
|
|
$value = env_value($key, '');
|
|
return array_values(array_filter(array_map('trim', explode(',', $value)), fn($v) => $v !== ''));
|
|
}
|
|
|
|
load_env_file(__DIR__ . '/.env');
|
|
|
|
define('DEV_MODE', env_bool('DEV_MODE'));
|
|
define('CLIENT_ID', env_value('CLIENT_ID'));
|
|
define('REDIRECT_URI', env_value('REDIRECT_URI'));
|
|
define('AUTHORIZE_URI', env_value('AUTHORIZE_URI'));
|
|
define('TOKEN_URI', env_value('TOKEN_URI'));
|
|
define('GRAPHQL_URI', env_value('GRAPHQL_URI'));
|
|
define('ADMIN_USER_IDS', env_list('ADMIN_USER_IDS'));
|
|
|
|
function session_start_safe(): void {
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
session_set_cookie_params([
|
|
'lifetime' => 0,
|
|
'path' => '/',
|
|
'secure' => false, // set to true in production (HTTPS)
|
|
'httponly' => true,
|
|
'samesite' => 'Lax',
|
|
]);
|
|
session_start();
|
|
}
|
|
}
|
|
|
|
function default_return_path(): string {
|
|
$script = str_replace('\\', '/', $_SERVER['SCRIPT_NAME'] ?? '/index.php');
|
|
$base = rtrim(dirname(dirname($script)), '/');
|
|
return ($base === '' ? '' : $base) . '/index.php';
|
|
}
|
|
|
|
function safe_return_path(?string $value): string {
|
|
$value = trim((string)$value);
|
|
if ($value === '') return default_return_path();
|
|
|
|
$parts = parse_url($value);
|
|
if ($parts === false) return default_return_path();
|
|
if (isset($parts['host'])) {
|
|
$currentHost = strtolower(explode(':', $_SERVER['HTTP_HOST'] ?? '')[0]);
|
|
if (strtolower($parts['host']) !== $currentHost) return default_return_path();
|
|
} elseif (str_starts_with($value, '//')) {
|
|
return default_return_path();
|
|
}
|
|
|
|
$path = $parts['path'] ?? '';
|
|
if ($path === '') $path = default_return_path();
|
|
if ($path[0] !== '/') $path = '/' . ltrim($path, '/');
|
|
|
|
$query = isset($parts['query']) ? '?' . $parts['query'] : '';
|
|
return $path . $query;
|
|
}
|
|
|
|
function current_return_path(): string {
|
|
return safe_return_path($_SERVER['REQUEST_URI'] ?? null);
|
|
}
|
|
|
|
function auth_start_href(?string $returnPath = null): string {
|
|
return 'auth/start.php?return=' . rawurlencode($returnPath ?? current_return_path());
|
|
}
|
|
|
|
function fflogs_graphql_user_query(string $query, string $token): array {
|
|
$payload = json_encode(['query' => $query]);
|
|
if ($payload === false) {
|
|
return ['error' => 'Could not encode GraphQL payload'];
|
|
}
|
|
|
|
if (function_exists('curl_init')) {
|
|
$ch = curl_init(GRAPHQL_URI);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => $payload,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Content-Type: application/json',
|
|
'Authorization: Bearer ' . $token,
|
|
],
|
|
CURLOPT_SSL_VERIFYPEER => !DEV_MODE,
|
|
]);
|
|
$body = curl_exec($ch);
|
|
$err = curl_error($ch);
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
if ($err) return ['error' => $err, 'status' => $code];
|
|
return ['body' => $body, 'status' => $code];
|
|
}
|
|
|
|
$context = stream_context_create([
|
|
'http' => [
|
|
'method' => 'POST',
|
|
'content' => $payload,
|
|
'header' => implode("\r\n", [
|
|
'Content-Type: application/json',
|
|
'Authorization: Bearer ' . $token,
|
|
]),
|
|
'timeout' => 30,
|
|
],
|
|
'ssl' => [
|
|
'verify_peer' => !DEV_MODE,
|
|
'verify_peer_name' => !DEV_MODE,
|
|
],
|
|
]);
|
|
$body = file_get_contents(GRAPHQL_URI, false, $context);
|
|
if ($body === false) return ['error' => 'GraphQL request failed'];
|
|
return ['body' => $body, 'status' => 200];
|
|
}
|
|
|
|
function fetch_current_fflogs_user(string $token): ?array {
|
|
$result = fflogs_graphql_user_query('query { userData { currentUser { id name } } }', $token);
|
|
if (!empty($result['error'])) {
|
|
$_SESSION['fflogs_user_error'] = $result['error'];
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode((string)($result['body'] ?? ''), true);
|
|
$user = $data['data']['userData']['currentUser'] ?? null;
|
|
if (!is_array($user) || empty($user['id'])) {
|
|
$_SESSION['fflogs_user_error'] = $data['errors'][0]['message'] ?? 'Could not read current FFLogs user';
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'id' => (string)$user['id'],
|
|
'name' => (string)($user['name'] ?? ''),
|
|
];
|
|
}
|
|
|
|
function current_fflogs_user(bool $forceRefresh = false): ?array {
|
|
if (!$forceRefresh && !empty($_SESSION['fflogs_user']) && is_array($_SESSION['fflogs_user'])) {
|
|
return $_SESSION['fflogs_user'];
|
|
}
|
|
if (empty($_SESSION['access_token']) || (($_SESSION['token_expires'] ?? 0) <= time())) {
|
|
return null;
|
|
}
|
|
|
|
$user = fetch_current_fflogs_user($_SESSION['access_token']);
|
|
if ($user !== null) {
|
|
$_SESSION['fflogs_user'] = $user;
|
|
unset($_SESSION['fflogs_user_error']);
|
|
}
|
|
return $user;
|
|
}
|
|
|
|
function is_admin_user(): bool {
|
|
$user = current_fflogs_user();
|
|
return $user !== null && in_array((string)$user['id'], ADMIN_USER_IDS, true);
|
|
}
|