From 868edf853bc0c62d0a5bec3f5d9e99e9ce7bf479 Mon Sep 17 00:00:00 2001 From: JMP-MS Date: Fri, 28 Aug 2026 20:43:52 +0200 Subject: [PATCH] Delete php directory --- php/aspic.php | 93 --------- php/contacts.php | 301 --------------------------- php/db.php | 16 -- php/index.php | 17 -- php/preferences.php | 103 --------- php/professions.php | 79 ------- php/prospect_doc.php | 48 ----- php/prospect_web-form-001_ajoute.php | 8 - php/prospects_core.php | 277 ------------------------ php/prospects_verifier.php | 86 -------- php/realisations.php | 100 --------- php/statuts.php | 48 ----- 12 files changed, 1176 deletions(-) delete mode 100644 php/aspic.php delete mode 100644 php/contacts.php delete mode 100644 php/db.php delete mode 100644 php/index.php delete mode 100644 php/preferences.php delete mode 100644 php/professions.php delete mode 100644 php/prospect_doc.php delete mode 100644 php/prospect_web-form-001_ajoute.php delete mode 100644 php/prospects_core.php delete mode 100644 php/prospects_verifier.php delete mode 100644 php/realisations.php delete mode 100644 php/statuts.php diff --git a/php/aspic.php b/php/aspic.php deleted file mode 100644 index 8b5e354..0000000 --- a/php/aspic.php +++ /dev/null @@ -1,93 +0,0 @@ - false, 'error' => 'Méthode non autorisée']); - exit; -} - -$data = json_decode(file_get_contents('php://input'), true); -$identifiant = isset($data['identifiant']) ? trim((string) $data['identifiant']) : ''; -$motdepasse = isset($data['motdepasse']) ? (string) $data['motdepasse'] : ''; - -if ($identifiant === '' || $motdepasse === '') { - http_response_code(400); - echo json_encode(['success' => false, 'error' => 'Identifiant et mot de passe sont obligatoires.']); - exit; -} - -try { - $pdo = getDbConnection(); -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['success' => false, 'error' => 'Connexion base de données impossible']); - exit; -} - -// USR_Code est un code court (3 caractères) conventionnellement saisi en -// majuscules ; comparaison insensible à la casse sur l'identifiant, comme -// dans le prototype front (voir ASPIC_ecran_identification.html, -// trouverUtilisateurDemo). Le mot de passe, lui, reste comparé strictement -// (sensible à la casse). -$stmt = $pdo->prepare('SELECT USR_Code, USR_Nom, USR_MDP FROM ASP_Users WHERE UPPER(USR_Code) = UPPER(?)'); -$stmt->execute([$identifiant]); -$user = $stmt->fetch(); - -// hash_equals() plutôt que === : comparaison en temps constant, pour ne pas -// laisser une attaque par mesure de timing déduire des informations sur le -// mot de passe stocké. Mot de passe en clair (USR_MDP) assumé consciemment -// pour cette application interne (voir journal section 2) — cette précaution -// est un geste peu coûteux, pas une garantie de sécurité forte sur ce point. -if (!$user || !hash_equals($user['USR_MDP'], $motdepasse)) { - // Message volontairement générique : ne pas révéler si c'est l'identifiant - // ou le mot de passe qui est en cause. Le compteur d'essais / la sortie au - // 3e échec sont gérés côté front, pas ici (voir en-tête de ce fichier). - echo json_encode(['success' => false]); - exit; -} - -// Session ouverte : les endpoints suivants (contacts.php, professions.php, -// statuts.php, realisations.php) exigent désormais $_SESSION['USR_Code'] -// (voir journal, section 15). -$_SESSION['USR_Code'] = $user['USR_Code']; - -echo json_encode([ - 'success' => true, - 'user' => [ - 'USR_Code' => $user['USR_Code'], - 'USR_Nom' => $user['USR_Nom'], - ], -]); diff --git a/php/contacts.php b/php/contacts.php deleted file mode 100644 index 1b50508..0000000 --- a/php/contacts.php +++ /dev/null @@ -1,301 +0,0 @@ - 'Non identifié — veuillez vous connecter.']); - exit; -} - -$method = $_SERVER['REQUEST_METHOD']; - -try { - $pdo = getDbConnection(); -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Connexion base de données impossible']); - exit; -} - -// Colonnes éditables depuis le panneau de détail du contact (hors CNT_Code, -// toujours généré/lecture seule, et hors CNT_Incr/CNT_Actif). -const CHAMPS_EDITABLES = [ - 'CNT_Nom', 'CNT_Prenom', 'CNT_Statut', 'CNT_Société', 'CNT_Service', - 'CNT_Fonction', 'CNT_AdrLig1', 'CNT_AdrLig2', 'CNT_AdrLig3', 'CNT_AdrCP', - 'CNT_AdrVille', 'CNT_SIRET', 'CNT_Tel', 'CNT_Portable', 'CNT_Mail', - 'CNT_Notes', 'CNT_CodeProfession', -]; - -// Liste blanche des colonnes de tri autorisées (celles réellement triables -// depuis l'écran liste des contacts). -const COLONNES_TRI_AUTORISEES = ['CNT_Nom', 'CNT_AdrVille']; - -/** - * Construit un motif LIKE à partir d'une valeur de filtre utilisateur, - * en échappant d'abord les caractères spéciaux LIKE déjà présents - * (antislash, %, _) avant de traduire le joker "*" de l'utilisateur en "%". - * Le caractère d'échappement utilisé (un seul antislash) est renvoyé à part, - * pour être passé en paramètre lié à la clause ESCAPE — jamais concaténé - * dans le texte SQL (voir note d'en-tête). - */ -function construireMotifLike(string $valeur): array -{ - $echappe = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $valeur); - $motif = str_replace('*', '%', $echappe); - return [$motif, '\\']; -} - -try { - switch ($method) { - case 'GET': - if (isset($_GET['id'])) { - $stmt = $pdo->prepare('SELECT * FROM ASP_Contacts WHERE CNT_Incr = ? AND CNT_Actif = 1'); - $stmt->execute([$_GET['id']]); - $contact = $stmt->fetch(); - if (!$contact) { - http_response_code(404); - echo json_encode(['error' => 'Contact introuvable']); - exit; - } - echo json_encode($contact); - exit; - } - - $tri = $_GET['tri'] ?? 'CNT_Nom'; - if (!in_array($tri, COLONNES_TRI_AUTORISEES, true)) { - $tri = 'CNT_Nom'; - } - $sens = (isset($_GET['sens']) && strtolower($_GET['sens']) === 'desc') ? 'DESC' : 'ASC'; - - $conditions = ['CNT_Actif = 1']; - $params = []; - - $filtresColonnes = [ - 'f_nom' => 'CNT_Nom', - 'f_prenom' => 'CNT_Prenom', - 'f_ville' => 'CNT_AdrVille', - 'f_tel' => 'CNT_Tel', - 'f_portable' => 'CNT_Portable', - 'f_mail' => 'CNT_Mail', - ]; - foreach ($filtresColonnes as $param => $colonne) { - if (!empty($_GET[$param])) { - [$motif, $echap] = construireMotifLike($_GET[$param]); - $conditions[] = "$colonne LIKE ? ESCAPE ?"; - $params[] = $motif; - $params[] = $echap; - } - } - // Département = 2 premiers caractères de CNT_AdrCP. - if (!empty($_GET['f_dept'])) { - [$motif, $echap] = construireMotifLike($_GET['f_dept']); - $conditions[] = 'LEFT(CNT_AdrCP, 2) LIKE ? ESCAPE ?'; - $params[] = $motif; - $params[] = $echap; - } - - $sql = 'SELECT *, LEFT(CNT_AdrCP, 2) AS CNT_Dept FROM ASP_Contacts WHERE ' - . implode(' AND ', $conditions) - . " ORDER BY $tri $sens, CNT_Prenom ASC"; - - $stmtTotal = $pdo->prepare('SELECT COUNT(*) FROM ASP_Contacts WHERE ' . implode(' AND ', $conditions)); - $stmtTotal->execute($params); - $total = (int)$stmtTotal->fetchColumn(); - - $stmt = $pdo->prepare($sql); - $stmt->execute($params); - echo json_encode(['contacts' => $stmt->fetchAll(), 'total' => $total]); - break; - - case 'POST': - $data = json_decode(file_get_contents('php://input'), true) ?? []; - $nom = trim($data['CNT_Nom'] ?? ''); - if ($nom === '') { - http_response_code(400); - echo json_encode(['error' => 'Le nom est obligatoire']); - exit; - } - - // Étape 1 (voir en-tête du fichier) : ANN_Code de l'année courante, - // recherché AVANT l'INSERT pour ne jamais créer un contact sans - // code faute d'année configurée dans ASP_Annees. - $anneeCourante = (int)date('Y'); - $stmtAnnee = $pdo->prepare('SELECT ANN_Code FROM ASP_Annees WHERE ANN_Année = ?'); - $stmtAnnee->execute([$anneeCourante]); - $annCode = $stmtAnnee->fetchColumn(); - if ($annCode === false) { - http_response_code(500); - echo json_encode(['error' => "Aucun code configuré pour l'année $anneeCourante dans ASP_Annees — impossible de générer le Code du contact."]); - exit; - } - - $colonnes = []; - $placeholders = []; - $params = []; - foreach (CHAMPS_EDITABLES as $champ) { - $colonnes[] = $champ; - $placeholders[] = '?'; - $valeur = $data[$champ] ?? null; - $params[] = ($valeur === '' ? null : $valeur); - } - - // Étape 2 : création du contact (CNT_Code encore NULL à ce stade, - // il dépend de CNT_Incr qui n'existe qu'après l'INSERT). - $sql = 'INSERT INTO ASP_Contacts (' . implode(', ', $colonnes) . ') VALUES (' . implode(', ', $placeholders) . ')'; - $stmt = $pdo->prepare($sql); - $stmt->execute($params); - $nouvelIncr = (int)$pdo->lastInsertId(); - - // Étapes 3-4 : nombre aléatoire 0-999 zéro-padé sur 3 caractères, - // concaténation ANN_Code-CNT_Incr+aléatoire (voir journal section 2 - // pour la logique détaillée, et section 16 pour cette implémentation). - $aleatoire = str_pad((string)random_int(0, 999), 3, '0', STR_PAD_LEFT); - $code = $annCode . '-' . $nouvelIncr . $aleatoire; - - $stmtCode = $pdo->prepare('UPDATE ASP_Contacts SET CNT_Code = ? WHERE CNT_Incr = ?'); - $stmtCode->execute([$code, $nouvelIncr]); - - echo json_encode(['id' => $nouvelIncr, 'code' => $code]); - break; - - case 'PUT': - if (!isset($_GET['id'])) { - http_response_code(400); - echo json_encode(['error' => 'id manquant']); - exit; - } - $data = json_decode(file_get_contents('php://input'), true) ?? []; - $nom = trim($data['CNT_Nom'] ?? ''); - if ($nom === '') { - http_response_code(400); - echo json_encode(['error' => 'Le nom est obligatoire']); - exit; - } - - // Vérification d'EXISTENCE séparée de l'UPDATE lui-même : par - // défaut, rowCount() après un UPDATE reflète le nombre de lignes - // RÉELLEMENT MODIFIÉES par MySQL/MariaDB, pas le nombre de lignes - // trouvées par le WHERE. Une re-soumission du formulaire sans - // changement réel (ou un enregistrement où, par coïncidence, - // aucune des valeurs soumises ne diffère de ce qui est déjà en - // base) donnerait alors rowCount() = 0, et l'ancien code - // l'interprétait à tort comme "contact introuvable" (404) — bug - // corrigé le 24 août 2026, repéré par Jean-Marc ("erreur si je - // remplis Notes ou Informations", en réalité lié à des essais - // répétés sans changement effectif des champs du contact). - $verif = $pdo->prepare('SELECT CNT_Incr FROM ASP_Contacts WHERE CNT_Incr = ? AND CNT_Actif = 1'); - $verif->execute([$_GET['id']]); - if (!$verif->fetch()) { - http_response_code(404); - echo json_encode(['error' => 'Contact introuvable ou déjà désactivé']); - exit; - } - - $assignations = []; - $params = []; - foreach (CHAMPS_EDITABLES as $champ) { - $assignations[] = "$champ = ?"; - $valeur = $data[$champ] ?? null; - $params[] = ($valeur === '' ? null : $valeur); - } - $params[] = $_GET['id']; - - $sql = 'UPDATE ASP_Contacts SET ' . implode(', ', $assignations) . ' WHERE CNT_Incr = ? AND CNT_Actif = 1'; - $stmt = $pdo->prepare($sql); - $stmt->execute($params); - - echo json_encode(['success' => true]); - break; - - case 'DELETE': - if (!isset($_GET['id'])) { - http_response_code(400); - echo json_encode(['error' => 'id manquant']); - exit; - } - // Suppression logique uniquement (voir journal, section 2) — - // jamais de DELETE SQL réel. Ici rowCount() reste un test valide - // (contrairement au PUT ci-dessus) : CNT_Actif passe toujours - // réellement de 1 à 0, donc rowCount() = 0 signifie bien "déjà - // désactivé ou inexistant". - $stmt = $pdo->prepare('UPDATE ASP_Contacts SET CNT_Actif = 0 WHERE CNT_Incr = ? AND CNT_Actif = 1'); - $stmt->execute([$_GET['id']]); - - if ($stmt->rowCount() === 0) { - http_response_code(404); - echo json_encode(['error' => 'Contact introuvable ou déjà désactivé']); - exit; - } - echo json_encode(['success' => true]); - break; - - default: - http_response_code(405); - echo json_encode(['error' => 'Méthode non autorisée']); - } -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]); -} diff --git a/php/db.php b/php/db.php deleted file mode 100644 index 31aa4c2..0000000 --- a/php/db.php +++ /dev/null @@ -1,16 +0,0 @@ - PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]; - - return new PDO($dsn, $user, $pass, $options); -} diff --git a/php/index.php b/php/index.php deleted file mode 100644 index f76b3b6..0000000 --- a/php/index.php +++ /dev/null @@ -1,17 +0,0 @@ - 'Non identifié — veuillez vous connecter.']); - exit; -} - -$method = $_SERVER['REQUEST_METHOD']; - -try { - $pdo = getDbConnection(); -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Connexion base de données impossible']); - exit; -} - -// Whitelist des colonnes de préférence modifiables — n'accepter que celles-ci -// dans le corps d'un PUT, jamais un nom de colonne arbitraire fourni par le -// client (même logique que CHAMPS_EDITABLES dans contacts.php). -const CHAMPS_RATIO = ['USR_RatioPanneauContact', 'USR_RatioPanneauProspect']; - -try { - switch ($method) { - case 'GET': - $stmt = $pdo->prepare( - 'SELECT USR_RatioPanneauContact, USR_RatioPanneauProspect FROM ASP_Users WHERE USR_Code = ?' - ); - $stmt->execute([$_SESSION['USR_Code']]); - $ligne = $stmt->fetch(); - if (!$ligne) { - http_response_code(404); - echo json_encode(['error' => 'Utilisateur introuvable']); - exit; - } - echo json_encode([ - 'USR_RatioPanneauContact' => (int)$ligne['USR_RatioPanneauContact'], - 'USR_RatioPanneauProspect' => (int)$ligne['USR_RatioPanneauProspect'], - ]); - break; - - case 'PUT': - $data = json_decode(file_get_contents('php://input'), true) ?? []; - - $assignations = []; - $params = []; - foreach (CHAMPS_RATIO as $champ) { - if (!array_key_exists($champ, $data)) { - continue; - } - $valeur = $data[$champ]; - if (!is_numeric($valeur) || (int)$valeur < 0 || (int)$valeur > 100) { - http_response_code(400); - echo json_encode(['error' => "$champ doit être un entier compris entre 0 et 100"]); - exit; - } - $assignations[] = "$champ = ?"; - $params[] = (int)$valeur; - } - - if (!$assignations) { - http_response_code(400); - echo json_encode(['error' => 'Aucun champ de préférence reconnu à mettre à jour']); - exit; - } - - $params[] = $_SESSION['USR_Code']; - $sql = 'UPDATE ASP_Users SET ' . implode(', ', $assignations) . ' WHERE USR_Code = ?'; - $stmt = $pdo->prepare($sql); - $stmt->execute($params); - echo json_encode(['success' => true]); - break; - - default: - http_response_code(405); - echo json_encode(['error' => 'Méthode non autorisée']); - } -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]); -} diff --git a/php/professions.php b/php/professions.php deleted file mode 100644 index 2374884..0000000 --- a/php/professions.php +++ /dev/null @@ -1,79 +0,0 @@ - 'Non identifié — veuillez vous connecter.']); - exit; -} - -$method = $_SERVER['REQUEST_METHOD']; - -try { - $pdo = getDbConnection(); -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Connexion base de données impossible']); - exit; -} - -try { - switch ($method) { - case 'GET': - $stmt = $pdo->query('SELECT PRF_Incr, PRF_Libelle FROM ASP_Professions ORDER BY PRF_Libelle ASC'); - echo json_encode(['professions' => $stmt->fetchAll()]); - break; - - case 'POST': - $data = json_decode(file_get_contents('php://input'), true); - $libelle = trim($data['PRF_Libelle'] ?? ''); - if ($libelle === '') { - http_response_code(400); - echo json_encode(['error' => 'Le libellé de la profession est obligatoire']); - exit; - } - - // Pas de contrainte UNIQUE en base sur PRF_Libelle à ce stade (voir - // journal) : on évite ici un doublon évident et strictement identique, - // sans garantie absolue en cas de création concurrente. - $verif = $pdo->prepare('SELECT PRF_Incr FROM ASP_Professions WHERE PRF_Libelle = ?'); - $verif->execute([$libelle]); - $existant = $verif->fetch(); - if ($existant) { - echo json_encode(['id' => (int)$existant['PRF_Incr'], 'existant' => true]); - exit; - } - - $stmt = $pdo->prepare('INSERT INTO ASP_Professions (PRF_Libelle) VALUES (?)'); - $stmt->execute([$libelle]); - echo json_encode(['id' => (int)$pdo->lastInsertId(), 'existant' => false]); - break; - - default: - http_response_code(405); - echo json_encode(['error' => 'Méthode non autorisée']); - } -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]); -} diff --git a/php/prospect_doc.php b/php/prospect_doc.php deleted file mode 100644 index bb498af..0000000 --- a/php/prospect_doc.php +++ /dev/null @@ -1,48 +0,0 @@ -&b=&c=&d= - -require __DIR__ . '/prospects_core.php'; - -// Destination neutre si les paramètres ont été trafiqués : ta page d'accueil -// plutôt qu'une réponse d'erreur qui confirmerait la tentative au visiteur. -const URL_REDIRECTION_SI_INVALIDE = 'https://mevelia-solutions.bzh'; - -$idProspect = filter_input(INPUT_GET, 'a', FILTER_VALIDATE_INT); -$idDoc = filter_input(INPUT_GET, 'b', FILTER_VALIDATE_INT); -$idCampagne = filter_input(INPUT_GET, 'c', FILTER_VALIDATE_INT); -$signature = $_GET['d'] ?? ''; - -if (!$idProspect || !$idDoc || !$idCampagne || $signature === '') { - header('Location: ' . URL_REDIRECTION_SI_INVALIDE, true, 302); - exit; -} - -$signatureAttendue = signerParametresDoc($idProspect, $idDoc, $idCampagne); -if (!hash_equals($signatureAttendue, $signature)) { - header('Location: ' . URL_REDIRECTION_SI_INVALIDE, true, 302); - exit; -} - -$pdo = getDbConnection(); - -$stmt = $pdo->prepare('SELECT PSP_CodeCampagne FROM ASP_Prospects WHERE PSP_Incr = ?'); -$stmt->execute([$idProspect]); -$prospect = $stmt->fetch(); - -$stmt = $pdo->prepare('SELECT DOC_URL, DOC_Libelle FROM ASP_Docs WHERE DOC_Incr = ?'); -$stmt->execute([$idDoc]); -$doc = $stmt->fetch(); - -if (!$prospect || !$doc) { - header('Location: ' . URL_REDIRECTION_SI_INVALIDE, true, 302); - exit; -} - -journaliserOperation($idProspect, $prospect['PSP_CodeCampagne'] . '-LEC', $doc['DOC_Libelle']); - -header('Location: ' . $doc['DOC_URL'], true, 302); -exit; diff --git a/php/prospect_web-form-001_ajoute.php b/php/prospect_web-form-001_ajoute.php deleted file mode 100644 index 0986683..0000000 --- a/php/prospect_web-form-001_ajoute.php +++ /dev/null @@ -1,8 +0,0 @@ -prepare('SELECT MAI_Objet, MAI_Corps, MAI_CorpsHtml FROM ASP_Mails WHERE MAI_Code = ?'); - $stmt->execute([$codeMail]); - $modele = $stmt->fetch(); - - if (!$modele) { - error_log("ASPIC prospects: modèle de mail introuvable ($codeMail)"); - return false; - } - - $variables = $variables + ['LogoUrl' => LOGO_URL]; - - $objet = $modele['MAI_Objet']; - $corpsTexte = $modele['MAI_Corps']; - $corpsHtml = $modele['MAI_CorpsHtml']; - foreach ($variables as $cle => $valeur) { - $motif = '{(' . $cle . ')}'; - $objet = str_replace($motif, (string) $valeur, $objet); - $corpsTexte = str_replace($motif, (string) $valeur, $corpsTexte); - if ($corpsHtml !== null) { - $corpsHtml = str_replace($motif, (string) $valeur, $corpsHtml); - } - } - - $apiKey = getenv('BREVO_API_KEY'); - if (!$apiKey) { - error_log('ASPIC prospects: BREVO_API_KEY non configurée'); - return false; - } - - $donneesEnvoi = [ - 'sender' => ['name' => BREVO_FROM_NAME, 'email' => BREVO_FROM_EMAIL], - 'to' => [['email' => $destinataire]], - 'replyTo' => ['email' => BREVO_REPLY_TO], - 'subject' => $objet, - 'textContent' => $corpsTexte, - ]; - if ($corpsHtml !== null && trim($corpsHtml) !== '') { - $donneesEnvoi['htmlContent'] = $corpsHtml; - } - $payload = json_encode($donneesEnvoi); - - $ch = curl_init('https://api.brevo.com/v3/smtp/email'); - curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => $payload, - CURLOPT_HTTPHEADER => [ - 'accept: application/json', - 'api-key: ' . $apiKey, - 'content-type: application/json', - ], - CURLOPT_TIMEOUT => 10, - ]); - $reponse = curl_exec($ch); - $codeHttp = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $erreurCurl = curl_error($ch); - curl_close($ch); - - if ($codeHttp < 200 || $codeHttp >= 300) { - error_log("ASPIC prospects: échec envoi Brevo (HTTP $codeHttp) : $reponse $erreurCurl"); - return false; - } - return true; -} - -/** - * Journalise une opération sur un prospect dans ASP_Operations. - * OPE_CodeUser est toujours 'Vik' (Viktor) : ces événements sont automatiques, - * jamais déclenchés par un utilisateur ASPIC connecté. - */ -function journaliserOperation(int $idProspect, string $codeAction, ?string $commentaire = null): void -{ - $pdo = getDbConnection(); - $stmt = $pdo->prepare( - 'INSERT INTO ASP_Operations (OPE_IncrProspect, OPE_CodeUser, OPE_Date, OPE_Action, OPE_Comment) - VALUES (?, ?, NOW(), ?, ?)' - ); - $stmt->execute([$idProspect, 'Vik', $codeAction, $commentaire]); -} - -/** - * Calcule la signature HMAC des paramètres d'un lien de documentation (a/b/c), - * pour empêcher qu'un visiteur ne les modifie sans que ça se voie. - * La clé ne doit JAMAIS être exposée côté client. - */ -function signerParametresDoc(int $idProspect, int $idDoc, int $idCampagne): string -{ - $cle = getenv('LIEN_DOC_CLE_SECRETE'); - if (!$cle) { - error_log('ASPIC prospects: LIEN_DOC_CLE_SECRETE non configurée'); - $cle = ''; - } - return substr(hash_hmac('sha256', "$idProspect-$idDoc-$idCampagne", $cle), 0, 16); -} - -/** - * Construit un lien de documentation signé, à insérer dans un mail. - */ -function genererLienDoc(int $idProspect, int $idDoc, int $idCampagne): string -{ - $signature = signerParametresDoc($idProspect, $idDoc, $idCampagne); - return ASPIC_BASE_URL . '/prospect_doc.php?a=' . $idProspect - . '&b=' . $idDoc . '&c=' . $idCampagne . '&d=' . $signature; -} - -/** - * Détermine si un numéro de téléphone saisi va dans PSP_Tel ou PSP_Portable : - * préfixe 06/07 (après suppression des espaces) => portable, sinon => fixe. - */ -function routerTelephone(string $telephoneBrut): array -{ - $nettoye = preg_replace('/\s+/', '', $telephoneBrut); - if ($nettoye === '') { - return ['tel' => null, 'portable' => null]; - } - $prefixe = substr($nettoye, 0, 2); - if ($prefixe === '06' || $prefixe === '07') { - return ['tel' => null, 'portable' => $nettoye]; - } - return ['tel' => $nettoye, 'portable' => null]; -} - -/** - * Traite la soumission du formulaire public pour une campagne donnée. - * Dérive automatiquement les codes ASP_Actions et le code du mail de vérification - * à partir du code de campagne, suivant la convention "-CRE/-VER/-VAL/-DOC/-LEC" - * (codes ASP_Actions) et "-DEM" (mail de vérification). - */ -function traiterAjoutProspect(string $codeCampagne): void -{ - autoriserCorsSiNecessaire(); - header('Content-Type: application/json; charset=utf-8'); - - $data = json_decode(file_get_contents('php://input'), true) ?? []; - - // Honeypot anti-spam : champ invisible pour un humain, rempli seulement par un robot. - if (!empty($data['site_web'] ?? '')) { - http_response_code(200); - echo json_encode(['success' => true]); - exit; - } - - $nom = trim((string) ($data['nom'] ?? '')); - $prenom = trim((string) ($data['prenom'] ?? '')); - $telephone = trim((string) ($data['telephone'] ?? '')); - $mail = trim((string) ($data['mail'] ?? '')); - - if ($mail === '' && $telephone === '') { - http_response_code(400); - echo json_encode(['error' => 'Merci de renseigner au moins une adresse mail ou un numéro de téléphone.']); - exit; - } - if (strlen($nom) > 40 || strlen($prenom) > 40 || strlen($mail) > 40) { - http_response_code(400); - echo json_encode(['error' => 'Un des champs saisis est trop long.']); - exit; - } - $telNettoye = preg_replace('/\s+/', '', $telephone); - if (strlen($telNettoye) > 14) { - http_response_code(400); - echo json_encode(['error' => 'Le numéro de téléphone saisi est trop long.']); - exit; - } - if ($mail !== '' && !filter_var($mail, FILTER_VALIDATE_EMAIL)) { - http_response_code(400); - echo json_encode(['error' => "L'adresse mail saisie n'est pas valide."]); - exit; - } - - $tel = routerTelephone($telephone); - - try { - $pdo = getDbConnection(); - - $token = genererTokenVerification(); - $expiration = date('Y-m-d H:i:s', strtotime('+48 hours')); - - $stmt = $pdo->prepare( - 'INSERT INTO ASP_Prospects - (PSP_Nom, PSP_Prenom, PSP_Tel, PSP_Portable, PSP_Mail, PSP_CodeCampagne, - PSP_MailVerifie, PSP_TokenVerification, PSP_TokenExpiration, PSP_Actif) - VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, 1)' - ); - $stmt->execute([ - $nom !== '' ? $nom : null, - $prenom !== '' ? $prenom : null, - $tel['tel'], - $tel['portable'], - $mail !== '' ? $mail : null, - $codeCampagne, - $token, - $expiration, - ]); - $idProspect = (int) $pdo->lastInsertId(); - - journaliserOperation($idProspect, $codeCampagne . '-CRE'); - - if ($mail !== '') { - $lien = ASPIC_BASE_URL . '/prospects_verifier.php?token=' . urlencode($token); - $envoiOk = envoyerMail($codeCampagne . '-DEM', $mail, [ - 'Prenom' => $prenom, - 'Nom' => $nom, - 'Lien' => $lien, - ]); - if ($envoiOk) { - journaliserOperation($idProspect, $codeCampagne . '-VER'); - } else { - error_log("ASPIC prospects: échec envoi mail de vérification pour le prospect $idProspect"); - } - } - - echo json_encode(['success' => true]); - } catch (PDOException $e) { - error_log('ASPIC prospects: erreur PDO - ' . $e->getMessage()); - http_response_code(500); - echo json_encode(['error' => 'Erreur serveur, merci de réessayer plus tard.']); - } -} diff --git a/php/prospects_verifier.php b/php/prospects_verifier.php deleted file mode 100644 index b23e2b5..0000000 --- a/php/prospects_verifier.php +++ /dev/null @@ -1,86 +0,0 @@ -' - . 'ASPIC - Vérification' - . '' - . '

' . htmlspecialchars($texte) . '

' - . ''; -} - -$token = $_GET['token'] ?? ''; -if ($token === '') { - http_response_code(400); - pageMessage('Lien de vérification invalide.'); - exit; -} - -$pdo = getDbConnection(); -$stmt = $pdo->prepare( - 'SELECT PSP_Incr, PSP_Prenom, PSP_Nom, PSP_Mail, PSP_CodeCampagne, PSP_TokenExpiration - FROM ASP_Prospects - WHERE PSP_TokenVerification = ? AND PSP_MailVerifie = 0 AND PSP_Actif = 1' -); -$stmt->execute([$token]); -$prospect = $stmt->fetch(); - -if (!$prospect) { - pageMessage('Ce lien de vérification est invalide ou a déjà été utilisé.'); - exit; -} - -if (strtotime($prospect['PSP_TokenExpiration']) < time()) { - pageMessage('Ce lien de vérification a expiré.'); - exit; -} - -$idProspect = (int) $prospect['PSP_Incr']; -$codeCampagne = $prospect['PSP_CodeCampagne']; - -$pdo->prepare( - 'UPDATE ASP_Prospects - SET PSP_MailVerifie = 1, PSP_TokenVerification = NULL, PSP_TokenExpiration = NULL - WHERE PSP_Incr = ?' -)->execute([$idProspect]); - -journaliserOperation($idProspect, $codeCampagne . '-VAL'); - -// Envoi automatique de la doc par défaut de la campagne, si elle en a une de définie. -$stmt = $pdo->prepare('SELECT CMP_Incr, CMP_DocDefaut FROM ASP_Campagnes WHERE CMP_CodeCampagne = ?'); -$stmt->execute([$codeCampagne]); -$campagne = $stmt->fetch(); - -if ($campagne && $campagne['CMP_DocDefaut']) { - $idDoc = (int) $campagne['CMP_DocDefaut']; - $idCampagneIncr = (int) $campagne['CMP_Incr']; - - $stmt = $pdo->prepare('SELECT DOC_Libelle FROM ASP_Docs WHERE DOC_Incr = ?'); - $stmt->execute([$idDoc]); - $doc = $stmt->fetch(); - - if ($doc) { - $lien = genererLienDoc($idProspect, $idDoc, $idCampagneIncr); - $envoiOk = envoyerMail($codeCampagne . '-DOC', $prospect['PSP_Mail'], [ - 'Prenom' => $prospect['PSP_Prenom'], - 'Nom' => $prospect['PSP_Nom'], - 'Lien' => $lien, - 'Titre' => $doc['DOC_Libelle'], - ]); - if ($envoiOk) { - journaliserOperation($idProspect, $codeCampagne . '-DOC'); - } else { - error_log("ASPIC prospects: échec envoi mail de doc pour le prospect $idProspect"); - } - } -} - -pageMessage('Merci, votre adresse mail est vérifiée ! Vous allez recevoir notre documentation très prochainement.'); diff --git a/php/realisations.php b/php/realisations.php deleted file mode 100644 index 2e09f60..0000000 --- a/php/realisations.php +++ /dev/null @@ -1,100 +0,0 @@ - : historique des réalisations d'un contact, avec le - * libellé de l'action (jointure sur ASP_Actions), trié du plus ancien - * au plus récent. - * POST : journalise une nouvelle réalisation (REA_IncrContact, REA_Action - * obligatoires ; REA_Comment optionnel). REA_Date est déterminée côté - * serveur (NOW()). - * - * ⚠️ Session utilisateur intégrée le 24 août 2026 (voir journal, sections 14 - * et 15) : accès réservé aux utilisateurs identifiés, et REA_CodeUser est - * désormais lu directement dans `$_SESSION['USR_Code']` (ouverte par - * `aspic.php`) — le champ REA_CodeUser du corps de la requête POST n'est - * plus utilisé et ne peut plus être falsifié par le client. Le "JMP" en dur - * précédent a été supprimé (n'avait de sens que faute de session). - */ - -session_start(); -header('Content-Type: application/json; charset=utf-8'); -require __DIR__ . '/db.php'; - -if (empty($_SESSION['USR_Code'])) { - http_response_code(401); - echo json_encode(['error' => 'Non identifié — veuillez vous connecter.']); - exit; -} - -$method = $_SERVER['REQUEST_METHOD']; - -try { - $pdo = getDbConnection(); -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Connexion base de données impossible']); - exit; -} - -try { - switch ($method) { - case 'GET': - if (!isset($_GET['id'])) { - http_response_code(400); - echo json_encode(['error' => 'id (REA_IncrContact) manquant']); - exit; - } - $stmt = $pdo->prepare( - 'SELECT r.REA_Incr, r.REA_Date, r.REA_CodeUser, r.REA_Action, r.REA_Comment, - a.ACT_Libelle - FROM ASP_Realisations r - LEFT JOIN ASP_Actions a ON a.ACT_Code = r.REA_Action - WHERE r.REA_IncrContact = ? - ORDER BY r.REA_Date ASC' - ); - $stmt->execute([$_GET['id']]); - echo json_encode(['realisations' => $stmt->fetchAll()]); - break; - - case 'POST': - $data = json_decode(file_get_contents('php://input'), true); - $idContact = $data['REA_IncrContact'] ?? null; - $action = $data['REA_Action'] ?? null; - if (!$idContact || !$action) { - http_response_code(400); - echo json_encode(['error' => 'REA_IncrContact et REA_Action sont obligatoires']); - exit; - } - - // REA_CodeUser vient de la session (voir en-tête de ce fichier), - // plus du corps de la requête : impossible pour le client de se - // faire passer pour un autre utilisateur. - $codeUser = $_SESSION['USR_Code']; - $commentaire = $data['REA_Comment'] ?? ''; - - $stmt = $pdo->prepare( - 'INSERT INTO ASP_Realisations (REA_IncrContact, REA_CodeUser, REA_Date, REA_Action, REA_Comment) - VALUES (?, ?, NOW(), ?, ?)' - ); - $stmt->execute([$idContact, $codeUser, $action, $commentaire]); - echo json_encode(['id' => (int)$pdo->lastInsertId()]); - break; - - default: - http_response_code(405); - echo json_encode(['error' => 'Méthode non autorisée']); - } -} catch (PDOException $e) { - // Toute exception PDO (ex. REA_Action trop long pour la colonne, valeur - // rejetée par une contrainte) renvoie du JSON exploitable côté front - // plutôt qu'une erreur PHP brute — voir contacts.php pour le même - // correctif du 24 août 2026. - http_response_code(500); - echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]); -} diff --git a/php/statuts.php b/php/statuts.php deleted file mode 100644 index 14398e2..0000000 --- a/php/statuts.php +++ /dev/null @@ -1,48 +0,0 @@ - 'Non identifié — veuillez vous connecter.']); - exit; -} - -if ($_SERVER['REQUEST_METHOD'] !== 'GET') { - http_response_code(405); - echo json_encode(['error' => 'Méthode non autorisée']); - exit; -} - -try { - $pdo = getDbConnection(); -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Connexion base de données impossible']); - exit; -} - -try { - // Tri par STA_Incr (ordre de création/incrément), pas par libellé — - // demande explicite de Jean-Marc du 24 août 2026. - $stmt = $pdo->query('SELECT STA_Code, STA_Libellé FROM ASP_Statuts ORDER BY STA_Incr ASC'); - echo json_encode(['statuts' => $stmt->fetchAll()]); -} catch (PDOException $e) { - http_response_code(500); - echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]); -}