false, 'message' => 'Le portail utilisateur UCP est actuellement désactivé par l’administrateur.']); exit; } // Lecture du payload JSON si applicable $payload = $_POST; $contentType = (string)($_SERVER['CONTENT_TYPE'] ?? ''); if (str_contains($contentType, 'application/json')) { $raw = file_get_contents('php://input'); $decoded = json_decode((string)$raw, true); if (is_array($decoded)) { $payload = $decoded; } } // ------------------------------------------------------------------------- // 1. Authentification (routes publiques de session) // ------------------------------------------------------------------------- if ($action === 'login') { $username = (string)($payload['username'] ?? ''); $password = (string)($payload['password'] ?? ''); if (UcpAuth::login($db, $username, $password)) { $user = UcpAuth::getCurrentUser($db); echo json_encode([ 'success' => true, 'username' => $user['username'] ?? $username, 'csrf_token' => UcpAuth::getCsrfToken(), 'message' => 'Connexion réussie.', ]); } else { http_response_code(401); echo json_encode([ 'success' => false, 'message' => 'Identifiant ou mot de passe incorrect.', ]); } exit; } if ($action === 'logout') { UcpAuth::logout(); echo json_encode(['success' => true, 'message' => 'Déconnexion effectuée.']); exit; } // ------------------------------------------------------------------------- // 2. Barrière d'authentification UCP obligatoire // ------------------------------------------------------------------------- $currentUser = UcpAuth::getCurrentUser($db); if (!$currentUser) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'Session expirée ou non authentifiée.']); exit; } // Protection CSRF sur toutes les actions modificatrices POST if ($_SERVER['REQUEST_METHOD'] === 'POST') { $csrfHeader = (string)($_SERVER['HTTP_X_CSRF_TOKEN'] ?? $payload['csrf_token'] ?? ''); if (!UcpAuth::verifyCsrfToken($csrfHeader)) { http_response_code(403); echo json_encode(['success' => false, 'message' => 'Jeton de sécurité CSRF invalide. Rechargez la page.']); exit; } } $ucpUserId = (int)$currentUser['id']; $allowedExts = UcpAuth::getAllowedExtensions($db, $ucpUserId); switch ($action) { case 'get_state': $branding = $db->query('SELECT company_name, company_logo, ucp_logo FROM settings WHERE id = 1 LIMIT 1')->fetch(PDO::FETCH_ASSOC) ?: []; $compLogo = trim((string)($branding['company_logo'] ?? '')); $ucpLogo = trim((string)($branding['ucp_logo'] ?? '')); echo json_encode([ 'success' => true, 'data' => [ 'user' => [ 'id' => $ucpUserId, 'username' => (string)$currentUser['username'], 'display_name' => (string)$currentUser['display_name'], 'email' => (string)$currentUser['email'], 'last_login_at' => $currentUser['last_login_at'], ], 'branding' => [ 'company_name' => (string)($branding['company_name'] ?? ''), 'company_logo' => $compLogo, 'ucp_logo' => $ucpLogo, 'effective_logo' => $ucpLogo !== '' ? $ucpLogo : $compLogo, ], 'csrf_token' => UcpAuth::getCsrfToken(), 'extensions' => UcpManager::getExtensionsState($db, $allowedExts, $ucpUserId), 'forward_targets' => UcpManager::getForwardTargets($db), 'feature_codes' => UcpManager::getFeatureCodesForUser($db), ], ]); break; case 'save_call_features': $ext = (string)($payload['extension'] ?? ''); UcpManager::setCallFeatures($db, $allowedExts, $ext, [ 'dnd' => !empty($payload['dnd']), 'cf' => (string)($payload['cf'] ?? ''), 'cfu' => (string)($payload['cfu'] ?? ''), ]); echo json_encode([ 'success' => true, 'extensions' => UcpManager::getExtensionsState($db, $allowedExts, $ucpUserId), 'message' => "Paramètres d'appel du poste {$ext} appliqués en direct.", ]); break; case 'get_voicemails': $ext = (string)($_GET['extension'] ?? $payload['extension'] ?? ''); if ($ext === '' && !empty($allowedExts)) { $ext = (string)$allowedExts[0]['extension']; } $messages = $ext !== '' ? UcpManager::getVoicemails($allowedExts, $ext) : []; echo json_encode([ 'success' => true, 'messages' => $messages, ]); break; case 'stream_voicemail': $ext = (string)($_GET['extension'] ?? ''); $folder = (string)($_GET['folder'] ?? ''); $msgId = (string)($_GET['id'] ?? ''); $download = !empty($_GET['download']); $filePath = UcpManager::getVoicemailAudioPath($allowedExts, $ext, $folder, $msgId); $size = filesize($filePath); $fileExt = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); $mime = ($fileExt === 'gsm') ? 'audio/gsm' : 'audio/wav'; while (ob_get_level()) { ob_end_clean(); } header("Content-Type: {$mime}"); header('Accept-Ranges: bytes'); header('Cache-Control: no-store, no-cache, must-revalidate'); if ($download) { $dlName = "voicemail_{$ext}_{$msgId}.{$fileExt}"; header("Content-Disposition: attachment; filename=\"{$dlName}\""); } if (isset($_SERVER['HTTP_RANGE'])) { [$param, $range] = explode('=', $_SERVER['HTTP_RANGE'], 2); if (strtolower(trim($param)) === 'bytes') { [$from, $to] = explode('-', $range, 2); $from = intval($from); $to = ($to === '') ? ($size - 1) : intval($to); if ($from > $to || $from >= $size) { http_response_code(416); exit; } $length = $to - $from + 1; http_response_code(206); header("Content-Range: bytes {$from}-{$to}/{$size}"); header("Content-Length: {$length}"); $fp = fopen($filePath, 'rb'); fseek($fp, $from); $remaining = $length; while ($remaining > 0 && !feof($fp)) { $chunk = min(8192, $remaining); echo fread($fp, $chunk); $remaining -= $chunk; flush(); } fclose($fp); exit; } } header("Content-Length: {$size}"); readfile($filePath); exit; case 'toggle_voicemail_read': $ext = (string)($payload['extension'] ?? ''); $folder = (string)($payload['folder'] ?? ''); $msgId = (string)($payload['id'] ?? ''); UcpManager::moveVoicemailFolder($db, $allowedExts, $ext, $folder, $msgId); echo json_encode([ 'success' => true, 'messages' => UcpManager::getVoicemails($allowedExts, $ext), 'extensions' => UcpManager::getExtensionsState($db, $allowedExts), 'message' => ($folder === 'INBOX') ? 'Message marqué comme lu (archivé).' : 'Message marqué comme non lu.', ]); break; case 'delete_voicemail': $ext = (string)($payload['extension'] ?? ''); $folder = (string)($payload['folder'] ?? ''); $msgId = (string)($payload['id'] ?? ''); UcpManager::deleteVoicemail($db, $allowedExts, $ext, $folder, $msgId); echo json_encode([ 'success' => true, 'messages' => UcpManager::getVoicemails($allowedExts, $ext), 'extensions' => UcpManager::getExtensionsState($db, $allowedExts), 'message' => 'Message vocal supprimé définitivement.', ]); break; case 'save_voicemail_prefs': $ext = (string)($payload['extension'] ?? ''); $pin = (string)($payload['pin'] ?? ''); $email = (string)($payload['email'] ?? ''); UcpManager::updateVoicemailPrefs($db, $allowedExts, $ext, $pin, $email); $freshAllowed = UcpAuth::getAllowedExtensions($db, $ucpUserId); echo json_encode([ 'success' => true, 'extensions' => UcpManager::getExtensionsState($db, $freshAllowed), 'message' => 'Préférences messagerie vocale enregistrées et appliquées.', ]); break; case 'get_personal_cdr': $data = UcpManager::getPersonalCdr($db, $allowedExts, [ 'direction' => $_GET['direction'] ?? 'all', 'disposition' => $_GET['disposition'] ?? 'all', 'period' => $_GET['period'] ?? 'all', 'search' => $_GET['search'] ?? '', ]); echo json_encode([ 'success' => true, 'data' => $data, ]); break; case 'get_phonebook': echo json_encode([ 'success' => true, 'contacts' => UcpManager::getPhonebookForUser($db, $allowedExts), ]); break; case 'save_my_email': $ext = (string)($payload['extension'] ?? ''); $email = (string)($payload['email'] ?? ''); UcpManager::updateMyContactEmail($db, $allowedExts, $ext, $email); echo json_encode([ 'success' => true, 'contacts' => UcpManager::getPhonebookForUser($db, $allowedExts), 'extensions' => UcpManager::getExtensionsState($db, $allowedExts), 'message' => "E-mail annuaire du poste {$ext} mis à jour.", ]); break; case 'upload_my_avatar': $ext = (string)($_POST['extension'] ?? ''); if (empty($_FILES['avatar'])) { throw new InvalidArgumentException("Aucun fichier image reçu."); } $avatarUrl = UcpManager::uploadMyAvatar($db, $allowedExts, $ext, $_FILES['avatar']); echo json_encode([ 'success' => true, 'photo_url' => $avatarUrl, 'contacts' => UcpManager::getPhonebookForUser($db, $allowedExts), 'extensions' => UcpManager::getExtensionsState($db, $allowedExts), 'message' => "Photo de profil du poste {$ext} mise à jour.", ]); break; case 'avatar': $file = (string)($_GET['file'] ?? ''); $path = UcpManager::resolveAvatarPath($file); if (!$path) { http_response_code(404); exit; } $extName = strtolower(pathinfo($path, PATHINFO_EXTENSION)); $mime = match ($extName) { 'png' => 'image/png', 'webp' => 'image/webp', default => 'image/jpeg', }; while (ob_get_level()) { ob_end_clean(); } header("Content-Type: {$mime}"); header('Cache-Control: public, max-age=3600'); readfile($path); exit; case 'change_password': $cur = (string)($payload['current_password'] ?? ''); $new = (string)($payload['new_password'] ?? ''); UcpAuth::updateOwnPassword($db, $ucpUserId, $cur, $new); echo json_encode([ 'success' => true, 'message' => 'Mot de passe UCP modifié avec succès.', ]); break; default: http_response_code(400); echo json_encode(['success' => false, 'message' => 'Action UCP inconnue.']); break; } } catch (InvalidArgumentException $e) { http_response_code(400); if ($isBinaryEndpoint) { header('Content-Type: text/plain; charset=utf-8'); echo $e->getMessage(); } else { echo json_encode(['success' => false, 'message' => $e->getMessage()]); } } catch (\Throwable $e) { http_response_code(500); if ($isBinaryEndpoint) { header('Content-Type: text/plain; charset=utf-8'); echo 'Erreur serveur.'; } else { echo json_encode(['success' => false, 'message' => $e->getMessage()]); } }