Go back'; exit; } $pid = (int)($_SESSION['linkedin_oauth']['project_id'] ?? 0); unset($_SESSION['linkedin_oauth']); if (isset($_GET['error'])) { $msg = htmlspecialchars($_GET['error_description'] ?? $_GET['error']); echo "LinkedIn denied access: $msg. Go back"; exit; } $code = $_GET['code'] ?? ''; if (!$code) { echo 'Missing code. Go back'; exit; } $client_id = defined('LINKEDIN_CLIENT_ID') ? LINKEDIN_CLIENT_ID : (getenv('LINKEDIN_CLIENT_ID') ?: ($_SERVER['LINKEDIN_CLIENT_ID'] ?? null)); $client_secret = defined('LINKEDIN_CLIENT_SECRET') ? LINKEDIN_CLIENT_SECRET : (getenv('LINKEDIN_CLIENT_SECRET') ?: ($_SERVER['LINKEDIN_CLIENT_SECRET'] ?? null)); $redirect_uri = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . '/linkedin/callback'; // Exchange code for tokens $ch = curl_init('https://www.linkedin.com/oauth/v2/accessToken'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query([ 'grant_type' => 'authorization_code', 'code' => $code, 'redirect_uri' => $redirect_uri, 'client_id' => $client_id, 'client_secret' => $client_secret, ]), CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'], ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $data = json_decode($body, true); if ($status !== 200 || empty($data['access_token'])) { $err = htmlspecialchars($data['error_description'] ?? $data['error'] ?? 'Unknown error'); echo "LinkedIn token exchange failed: $err. Go back"; exit; } // Get the project's config.json path $stmt = $db->prepare('SELECT path FROM projects WHERE id = ? AND is_active = 1'); $stmt->execute([$pid]); $project = $stmt->fetch(); if (!$project) { echo 'Project not found. Go back'; exit; } $config_path = realpath($project['path']) . '/data/config.json'; if (!file_exists($config_path)) { echo "config.json not found at $config_path. Go back"; exit; } $config = json_decode(file_get_contents($config_path), true); if (!is_array($config)) { echo 'Could not parse config.json. Go back'; exit; } if (!isset($config['linkedin'])) $config['linkedin'] = []; $config['linkedin']['access_token'] = $data['access_token']; $config['linkedin']['refresh_token'] = $data['refresh_token'] ?? null; $expires_in = $data['expires_in'] ?? 5184000; $config['linkedin']['token_expiry'] = date('c', time() + $expires_in); $tmp = $config_path . '.tmp'; file_put_contents($tmp, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); rename($tmp, $config_path); Audit::log($db, 'linkedin_oauth_connect', $pid); header('Location: /project/' . $pid . '?tab=botconfig'); exit;