= NOW()"; $reponse = Requete_preparee_bdd($requete, array(array('i', $id_shipmember)), __FILE__, __LINE__); while ($donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC)) $acces[] = $donnees; Liberation_jeu_de_resultat_bdd($reponse); return $acces; } //************************************************************************************************************ // Fonction : Indique si le compte d'un ShipMember est activé, c'est-à-dire si son titulaire a prouvé qu'il // contrôle l'adresse e-mail (connexion avec le mot de passe provisoire reçu, puis choix d'un mot de // passe : la ligne TB_Activation est alors supprimée). // Reçoit : La clé primaire du ShipMember // Retour : true si activé //************************************************************************************************************ function Compte_est_active($id_shipmember) { $reponse = Requete_preparee_bdd("SELECT COUNT(*) AS nb FROM TB_Activation WHERE ID_SHIPMEMBERS=?", array(array('i', $id_shipmember)), __FILE__, __LINE__); $donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC); Liberation_jeu_de_resultat_bdd($reponse); return ($donnees['nb'] == 0); } //************************************************************************************************************ // Fonction : Normalise un code d'invitation saisi ou reçu (majuscules, espaces et tirets retirés, puis tiret // remis au milieu) et vérifie son format. // Reçoit : La valeur brute (GET, POST, saisie manuelle...) // Retour : Le code normalisé (ex : "K7QX-M3PW"), ou false si le format est invalide //************************************************************************************************************ function Normaliser_code_invitation($valeur) { if (!is_string($valeur)) return false; // écarte ?code_acces[]=... (tableau) $brut = strtoupper(preg_replace('/[\s-]/', '', $valeur)); $moitie = intdiv(CODE_INVITATION_LONGUEUR, 2); if (preg_match('/^['.CODE_INVITATION_ALPHABET.']{'.CODE_INVITATION_LONGUEUR.'}\z/', $brut) !== 1) return false; return substr($brut, 0, $moitie).'-'.substr($brut, $moitie); } //************************************************************************************************************ // Fonction : Génère un code d'invitation unique (générateur aléatoire cryptographique random_int). // Reçoit : Rien // Retour : Le code (ex : "K7QX-M3PW") //************************************************************************************************************ function Generer_code_invitation() { $max = strlen(CODE_INVITATION_ALPHABET) - 1; do { $brut = ""; for ($i = 0; $i < CODE_INVITATION_LONGUEUR; $i++) $brut .= CODE_INVITATION_ALPHABET[random_int(0, $max)]; $code = Normaliser_code_invitation($brut); $reponse = Requete_preparee_bdd("SELECT COUNT(*) AS nb FROM TB_Lien_bateau_shipmember WHERE Code=?", array(array('s', $code)), __FILE__, __LINE__); $donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC); Liberation_jeu_de_resultat_bdd($reponse); } while ($donnees['nb'] != 0); return $code; } //************************************************************************************************************ // Fonction : Crée une invitation nominative (statut 'en_attente'). // Reçoit : Le bateau, l'adresse e-mail du destinataire (obligatoire), son prénom et son nom (facultatifs, pour // l'affichage), le type d'accès, la fin de l'accès accordé (format 'Y-m-d', vide = sans limite) et le // ShipMember qui invite. // Retour : ['ok'=>true, 'id'=>int, 'code'=>string] ou ['ok'=>false, 'raison'=>string] // NB : les droits de celui qui invite sont vérifiés par la page appelante. //************************************************************************************************************ function Creer_invitation($id_bateau, $email, $prenom, $nom, $id_type_acces, $fin_acces, $id_cree_par) { $email = strtolower(trim((string)$email)); if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) return ['ok' => false, 'raison' => t("Adresse e-mail invalide.")]; $prenom = trim((string)$prenom); $nom = trim((string)$nom); $prenom = ($prenom === '') ? null : mb_substr($prenom, 0, 50); $nom = ($nom === '') ? null : mb_substr($nom, 0, 50); // Type d'accès : doit exister et être valide (l'ancien type "Administrateur" est dévalidé) : $reponse = Requete_preparee_bdd("SELECT COUNT(*) AS nb FROM TB_Types_acces_bateau_shipmember WHERE Cle_primaire=? AND Validite=1", array(array('i', $id_type_acces)), __FILE__, __LINE__); $donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC); Liberation_jeu_de_resultat_bdd($reponse); if ($donnees['nb'] == 0) return ['ok' => false, 'raison' => t("Type d'accès invalide.")]; // Fin de l'accès accordé : date du jour en fin de journée, ou sans limite : if ($fin_acces === null || trim($fin_acces) === '') $fin_validite = '9999-12-31 23:59:59'; else { $date = date_create_from_format('Y-m-d', trim($fin_acces)); if ($date === false) return ['ok' => false, 'raison' => t("Date de fin d'accès invalide.")]; $fin_validite = $date->format('Y-m-d').' 23:59:59'; } $duree = isset($_SESSION['Config__Duree_validite_invitation']) ? (int)$_SESSION['Config__Duree_validite_invitation'] : DUREE_VALIDITE_INVITATION_DEFAUT; if ($duree <= 0) $duree = DUREE_VALIDITE_INVITATION_DEFAUT; $code = Generer_code_invitation(); $requete = "INSERT INTO TB_Lien_bateau_shipmember (Statut, ID_Shipmember, ID_Bateau, Email_invite, Prenom_invite, Nom_invite, Code, Expiration_invitation, Fin_validite, Cree_par, ID_Type_acces_bateau_shipmember) VALUES ('en_attente', 0, ?, ?, ?, ?, ?, DATE_ADD(NOW(), INTERVAL ? DAY), ?, ?, ?)"; Requete_preparee_bdd($requete, array( array('i', $id_bateau), array('s', $email), array('s', $prenom), array('s', $nom), array('s', $code), array('i', $duree), array('s', $fin_validite), array('i', $id_cree_par), array('i', $id_type_acces)), __FILE__, __LINE__); return ['ok' => true, 'id' => Recuperation_id_cree_bdd(), 'code' => $code]; } //************************************************************************************************************ // Fonction : Informations sur une invitation à partir de son code (bandeau d'accueil d'un lien d'invitation). // Reçoit : Le code (brut ou normalisé) // Retour : Un tableau (ID, Statut, Email_invite, Prenom_invite, Nom_invite, Nom_bateau, Type_acces, // Expiree [0/1]) ou null si le code est inconnu ou mal formé //************************************************************************************************************ function Invitation_par_code($code) { $code = Normaliser_code_invitation($code); if ($code === false) return null; $colonne_type = (isset($_SESSION['langue']) && $_SESSION['langue'] == 'EN') ? 'Nom_en' : 'Nom_fr'; $requete = "SELECT L.ID, L.Statut, L.Email_invite, L.Prenom_invite, L.Nom_invite, B.Nom AS Nom_bateau, T.".$colonne_type." AS Type_acces, (L.Expiration_invitation IS NOT NULL AND L.Expiration_invitation <= NOW()) AS Expiree FROM TB_Lien_bateau_shipmember L JOIN TB_Bateaux B ON B.Cle_primaire = L.ID_Bateau LEFT JOIN TB_Types_acces_bateau_shipmember T ON T.Cle_primaire = L.ID_Type_acces_bateau_shipmember WHERE L.Code=?"; $reponse = Requete_preparee_bdd($requete, array(array('s', $code)), __FILE__, __LINE__); $donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC); Liberation_jeu_de_resultat_bdd($reponse); return $donnees ?: null; } //************************************************************************************************************ // Fonction : Invitations en attente (non expirées) adressées à une adresse e-mail. // Reçoit : L'adresse e-mail du ShipMember connecté // Retour : Un tableau de lignes (ID, Nom_bateau, Type_acces, Expiration_invitation, Prenom_invitant, Nom_invitant) //************************************************************************************************************ function Invitations_en_attente($email) { $invitations = []; $colonne_type = (isset($_SESSION['langue']) && $_SESSION['langue'] == 'EN') ? 'Nom_en' : 'Nom_fr'; $requete = "SELECT L.ID, B.Nom AS Nom_bateau, T.".$colonne_type." AS Type_acces, L.Expiration_invitation, S.Prenom AS Prenom_invitant, S.Nom AS Nom_invitant FROM TB_Lien_bateau_shipmember L JOIN TB_Bateaux B ON B.Cle_primaire = L.ID_Bateau LEFT JOIN TB_Types_acces_bateau_shipmember T ON T.Cle_primaire = L.ID_Type_acces_bateau_shipmember LEFT JOIN TB_Shipmembers S ON S.ID = L.Cree_par WHERE L.Statut='en_attente' AND L.Email_invite=? AND (L.Expiration_invitation IS NULL OR L.Expiration_invitation > NOW()) ORDER BY L.Date_creation"; $reponse = Requete_preparee_bdd($requete, array(array('s', strtolower(trim((string)$email)))), __FILE__, __LINE__); while ($donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC)) $invitations[] = $donnees; Liberation_jeu_de_resultat_bdd($reponse); return $invitations; } //************************************************************************************************************ // Fonction : Accepte une invitation pour le ShipMember connecté. // Toutes les conditions (en attente, adresse identique, non expirée) sont vérifiées DANS la requête de // mise à jour elle-même : une invitation ne peut donc être acceptée qu'une fois, même par deux // requêtes simultanées. // Reçoit : L'ID de l'invitation, la clé primaire et l'adresse e-mail du ShipMember connecté // Retour : ['ok'=>true] ou ['ok'=>false, 'raison'=>string] //************************************************************************************************************ function Accepter_invitation($id_lien, $id_shipmember, $email) { if (!Compte_est_active($id_shipmember)) { return ['ok' => false, 'raison' => t("Votre compte doit être activé (mot de passe provisoire reçu par e-mail) avant de pouvoir accepter une invitation.")]; } $requete = "UPDATE TB_Lien_bateau_shipmember SET Statut='actif', ID_Shipmember=?, Date_activation=NOW() WHERE ID=? AND Statut='en_attente' AND Email_invite=? AND (Expiration_invitation IS NULL OR Expiration_invitation > NOW())"; Requete_preparee_bdd($requete, array( array('i', $id_shipmember), array('i', $id_lien), array('s', strtolower(trim((string)$email)))), __FILE__, __LINE__); if (Nb_lignes_modifiees() == 1) return ['ok' => true]; return ['ok' => false, 'raison' => t("Cette invitation n'est plus valable (déjà utilisée, expirée, révoquée, ou adressée à une autre adresse).")]; } //************************************************************************************************************ // Fonction : Refuse une invitation (elle passe à 'revoque'). // Reçoit : L'ID de l'invitation et l'adresse e-mail du ShipMember connecté // Retour : true si l'invitation a bien été refusée //************************************************************************************************************ function Refuser_invitation($id_lien, $email) { $requete = "UPDATE TB_Lien_bateau_shipmember SET Statut='revoque' WHERE ID=? AND Statut='en_attente' AND Email_invite=?"; Requete_preparee_bdd($requete, array(array('i', $id_lien), array('s', strtolower(trim((string)$email)))), __FILE__, __LINE__); return (Nb_lignes_modifiees() == 1); } //************************************************************************************************************ // Fonction : Masque partiellement une adresse e-mail pour l'afficher à quelqu'un qui n'est pas encore connecté // (ex : "pierre.durand@exemple.fr" -> "p•••@exemple.fr"). // Reçoit : L'adresse // Retour : L'adresse masquée //************************************************************************************************************ function Masquer_email($email) { $position = strpos((string)$email, '@'); if ($position === false || $position == 0) return "•••"; return mb_substr($email, 0, 1).'•••'.substr($email, $position); } //************************************************************************************************************ // Clé du type d'accès "Propriétaire" dans TB_Types_acces_bateau_shipmember (cf Devalidation_profils = 1 : tous // les types SAUF Propriétaire). //************************************************************************************************************ const TYPE_ACCES_PROPRIETAIRE = 2; //************************************************************************************************************ // Fonction : Charge un accès (ou une invitation) À CONDITION qu'il appartienne au bateau indiqué - toute action // sur un accès passe par là, pour qu'un identifiant envoyé par le navigateur ne puisse jamais viser // l'accès d'un autre bateau. // Reçoit : L'ID de l'accès et la clé primaire du bateau affiché // Retour : La ligne (ID, Statut, Code, ID_Type_acces_bateau_shipmember, Expiration_invitation) ou null //************************************************************************************************************ function Lien_du_bateau($id_lien, $id_bateau) { $requete = "SELECT ID, Statut, Code, ID_Type_acces_bateau_shipmember, Expiration_invitation FROM TB_Lien_bateau_shipmember WHERE ID=? AND ID_Bateau=?"; $reponse = Requete_preparee_bdd($requete, array(array('i', $id_lien), array('i', $id_bateau)), __FILE__, __LINE__); $donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC); Liberation_jeu_de_resultat_bdd($reponse); return $donnees ?: null; } //************************************************************************************************************ // Fonction : Indique si l'utilisateur peut suspendre, réactiver ou révoquer un accès d'un type donné. // Administrateur : toujours. Devalidation_profils = 2 : tous les types. = 1 : tous sauf Propriétaire. // Reçoit : Les droits de l'utilisateur sur le bateau ($acces_profil), s'il est administrateur, le type visé // Retour : true si autorisé //************************************************************************************************************ function Peut_devalider_acces($acces_profil, $est_administrateur, $id_type_cible) { if ($est_administrateur) return true; $niveau = isset($acces_profil['Devalidation_profils']) ? (int)$acces_profil['Devalidation_profils'] : 0; if ($niveau >= 2) return true; if ($niveau == 1) return ((int)$id_type_cible != TYPE_ACCES_PROPRIETAIRE); return false; } //************************************************************************************************************ // Fonction : Met à jour en session la liste des accès actifs du ShipMember connecté ($_SESSION['bateau_shipmember'] // et $_SESSION['acces']), à partir de la base - la liste est REMPLACÉE (l'ancien code y ajoutait des // lignes sans la vider : des accès révoqués pouvaient y rester jusqu'à la déconnexion). // Reçoit : La clé primaire du ShipMember // Retour : Rien //************************************************************************************************************ function Rafraichir_acces_session($id_shipmember) { $_SESSION['bateau_shipmember'] = Acces_actifs_shipmember($id_shipmember); $_SESSION['acces'] = count($_SESSION['bateau_shipmember']); }