SESSION_LIFETIME) { logout_user(); return false; } } $_SESSION['last_activity'] = time(); return true; } // ============================================ // FUNZIONI SICUREZZA // ============================================ /** * Hash sicuro di una password * * @param string $password Password in chiaro * @return string Password hashata */ function hash_password($password) { return password_hash($password, PASSWORD_DEFAULT); } /** * Verifica una password contro il suo hash * * @param string $password Password in chiaro * @param string $hash Hash salvato nel database * @return bool True se corrisponde, false altrimenti */ function verify_password($password, $hash) { return password_verify($password, $hash); } /** * Genera un token casuale sicuro * * @param int $length Lunghezza del token * @return string Token generato */ function generate_token($length = 32) { return bin2hex(random_bytes($length)); } /** * Sanitizza input utente per prevenire XSS * * @param string $data Dati da sanitizzare * @return string Dati sanitizzati */ function sanitize_input($data) { $data = trim($data); $data = stripslashes($data); $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); return $data; } /** * Valida un indirizzo email * * @param string $email Email da validare * @return bool True se valida, false altrimenti */ function validate_email($email) { return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; } // ============================================ // FUNZIONI DATABASE - UTENTI // ============================================ /** * Ottiene un utente dal database tramite email * * @param string $email Email dell'utente * @return array|false Dati utente o false se non trovato */ function get_user_by_email($email) { $pdo = get_db_connection(); $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ? AND deleted_at IS NULL"); $stmt->execute([$email]); return $stmt->fetch(); } /** * Ottiene un utente dal database tramite ID * * @param int $user_id ID dell'utente * @return array|false Dati utente o false se non trovato */ function get_user_by_id($user_id) { $pdo = get_db_connection(); $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ? AND deleted_at IS NULL"); $stmt->execute([$user_id]); return $stmt->fetch(); } /** * Crea un nuovo utente nel database * * @param string $email Email * @param string $password Password in chiaro (verrà hashata) * @param string $first_name Nome * @param string $last_name Cognome * @param bool $is_admin Se è amministratore * @param string|null $email_token Token verifica email * @param string|null $token_expires Scadenza token * @return int|false ID del nuovo utente o false in caso di errore */ function create_user($email, $password, $first_name, $last_name, $is_admin = false, $email_token = null, $token_expires = null) { $pdo = get_db_connection(); $hashed_password = hash_password($password); try { $stmt = $pdo->prepare(" INSERT INTO users ( email, password, first_name, last_name, is_admin, email_verified, email_token, email_token_expires, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW()) "); $stmt->execute([ $email, $hashed_password, $first_name, $last_name, $is_admin ? 1 : 0, $is_admin ? 1 : 0, // Admin sempre verificati $email_token, $token_expires ]); return $pdo->lastInsertId(); } catch (PDOException $e) { error_log("Create user error: " . $e->getMessage()); return false; } } // ============================================ // FUNZIONI DATABASE - LEZIONI // ============================================ /** * Ottiene tutte le lezioni demo (gratuite) * * @return array Array di lezioni demo */ function get_demo_lessons() { $pdo = get_db_connection(); $stmt = $pdo->query(" SELECT * FROM lessons WHERE is_demo = 1 AND is_active = 1 ORDER BY created_at DESC "); return $stmt->fetchAll(); } /** * Ottiene tutte le lezioni attive * * @param string $type Tipo di lezione: 'all', 'video', 'live' * @return array Array di lezioni */ function get_all_lessons($type = 'all') { $pdo = get_db_connection(); $sql = "SELECT * FROM lessons WHERE is_active = 1"; if ($type !== 'all') { $sql .= " AND type = :type"; } $sql .= " ORDER BY created_at DESC"; $stmt = $pdo->prepare($sql); if ($type !== 'all') { $stmt->execute(['type' => $type]); } else { $stmt->execute(); } return $stmt->fetchAll(); } /** * Ottiene una lezione specifica tramite ID * * @param int $lesson_id ID della lezione * @return array|false Dati lezione o false se non trovata */ function get_lesson_by_id($lesson_id) { $pdo = get_db_connection(); $stmt = $pdo->prepare("SELECT * FROM lessons WHERE id = ?"); $stmt->execute([$lesson_id]); return $stmt->fetch(); } /** * Verifica se un utente ha acquistato una lezione * * @param int $user_id ID utente * @param int $lesson_id ID lezione * @return bool True se acquistata, false altrimenti */ function user_owns_lesson($user_id, $lesson_id) { $pdo = get_db_connection(); $stmt = $pdo->prepare(" SELECT COUNT(*) as count FROM purchases WHERE user_id = ? AND lesson_id = ? AND status = 'completed' "); $stmt->execute([$user_id, $lesson_id]); $result = $stmt->fetch(); return $result['count'] > 0; } /** * Ottiene tutte le lezioni acquistate da un utente * * @param int $user_id ID utente * @return array Array di lezioni acquistate */ function get_user_purchased_lessons($user_id) { $pdo = get_db_connection(); $stmt = $pdo->prepare(" SELECT l.*, p.purchased_at, p.amount as paid_amount FROM lessons l INNER JOIN purchases p ON l.id = p.lesson_id WHERE p.user_id = ? AND p.status = 'completed' ORDER BY p.purchased_at DESC "); $stmt->execute([$user_id]); return $stmt->fetchAll(); } // ============================================ // FUNZIONI RECUPERO PASSWORD // ============================================ /** * Crea un token per il recupero password * * @param int $user_id ID utente * @return string Token generato */ function create_password_reset_token($user_id) { $pdo = get_db_connection(); $token = generate_token(32); $expires_at = date('Y-m-d H:i:s', time() + PASSWORD_RESET_TOKEN_LIFETIME); // Cancella eventuali token precedenti per questo utente $stmt = $pdo->prepare("DELETE FROM password_resets WHERE user_id = ?"); $stmt->execute([$user_id]); // Crea nuovo token $stmt = $pdo->prepare(" INSERT INTO password_resets (user_id, token, expires_at, created_at) VALUES (?, ?, ?, NOW()) "); $stmt->execute([$user_id, $token, $expires_at]); return $token; } /** * Verifica la validità di un token di reset password * * @param string $token Token da verificare * @return int|false User ID se valido, false altrimenti */ function verify_password_reset_token($token) { $pdo = get_db_connection(); $stmt = $pdo->prepare(" SELECT user_id FROM password_resets WHERE token = ? AND expires_at > NOW() AND used_at IS NULL "); $stmt->execute([$token]); $result = $stmt->fetch(); return $result ? $result['user_id'] : false; } /** * Marca un token come utilizzato * * @param string $token Token da marcare */ function mark_token_as_used($token) { $pdo = get_db_connection(); $stmt = $pdo->prepare("UPDATE password_resets SET used_at = NOW() WHERE token = ?"); $stmt->execute([$token]); } // ============================================ // FUNZIONI EMAIL // ============================================ /** * Invia un'email (funzione base con mail() PHP) * * @param string $to Destinatario * @param string $subject Oggetto * @param string $message Messaggio HTML * @return bool True se inviata, false altrimenti */ function send_email($to, $subject, $message) { if (USE_SMTP) { return send_smtp_email($to, $subject, $message); } else { // Fallback a mail() PHP nativo $headers = "MIME-Version: 1.0" . "\r\n"; $headers .= "Content-type:text/html;charset=UTF-8" . "\r\n"; $headers .= "From: " . MAIL_FROM_NAME . " <" . MAIL_FROM . ">" . "\r\n"; return mail($to, $subject, $message, $headers); } } /** * Invia email tramite script Python * * @param string $to Email destinatario * @param string $subject Oggetto email * @param string $message Corpo email (HTML) * @return bool True se inviata, false altrimenti */ function send_smtp_email($to, $subject, $message) { try { // Prepara dati JSON per lo script Python $data = json_encode([ 'to' => $to, 'subject' => $subject, 'html' => $message ], JSON_UNESCAPED_UNICODE); // Percorso dello script Python $script_path = __DIR__ . '/../send_email.py'; // Verifica che lo script esista if (!file_exists($script_path)) { error_log("Email Error: Script send_email.py non trovato"); return false; } // Esegui script Python $command = 'python3 ' . escapeshellarg($script_path) . ' ' . escapeshellarg($data) . ' 2>&1'; exec($command, $output, $return_code); // Parse risposta JSON $result = json_decode(implode("\n", $output), true); if ($return_code === 0 && isset($result['success']) && $result['success']) { return true; } else { $error_msg = isset($result['error']) ? $result['error'] : 'Errore sconosciuto'; error_log("Email Error: " . $error_msg); return false; } } catch (Exception $e) { error_log("Email Error: " . $e->getMessage()); return false; } } /** * Invia email tramite SMTP PHP nativo (deprecato - usa send_smtp_email) * * @param string $to Email destinatario * @param string $subject Oggetto email * @param string $message Corpo email (HTML) * @return bool True se inviata, false altrimenti */ function send_smtp_email_legacy($to, $subject, $message) { try { // Connessione al server SMTP con supporto SSL/TLS $context = stream_context_create([ 'ssl' => [ 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true ] ]); // Per SSL (porta 465) usa protocollo ssl://, per TLS (porta 587) connessione normale $protocol = (SMTP_ENCRYPTION === 'ssl') ? 'ssl://' : ''; $smtp = stream_socket_client( $protocol . SMTP_HOST . ':' . SMTP_PORT, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context ); if (!$smtp) { error_log("SMTP Error: Impossibile connettersi a " . SMTP_HOST . ":" . SMTP_PORT . " - $errstr ($errno)"); return false; } // Funzione helper per leggere risposta $read = function() use ($smtp) { return fgets($smtp, 515); }; // Funzione helper per inviare comando $send = function($cmd) use ($smtp) { fputs($smtp, $cmd . "\r\n"); }; // Leggi il banner di benvenuto $read(); // Inizia handshake SMTP $send("EHLO " . SMTP_HOST); $read(); // Inizia TLS se richiesto if (SMTP_ENCRYPTION === 'tls') { $send("STARTTLS"); $response = $read(); // Verifica risposta 220 if (strpos($response, '220') !== 0) { error_log("SMTP Error: STARTTLS fallito - " . $response); fclose($smtp); return false; } // Abilita TLS con metodo compatibile (fallback automatico tra TLSv1.2, TLSv1.1, TLSv1.0) $crypto_methods = [ STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, STREAM_CRYPTO_METHOD_TLS_CLIENT, STREAM_CRYPTO_METHOD_SSLv23_CLIENT ]; $tls_enabled = false; foreach ($crypto_methods as $method) { if (@stream_socket_enable_crypto($smtp, true, $method)) { $tls_enabled = true; break; } } if (!$tls_enabled) { error_log("SMTP Error: Impossibile avviare crittografia TLS con nessun metodo"); fclose($smtp); return false; } $send("EHLO " . SMTP_HOST); $read(); } // Autenticazione $send("AUTH LOGIN"); $read(); $send(base64_encode(SMTP_USERNAME)); $read(); $send(base64_encode(SMTP_PASSWORD)); $response = $read(); // Verifica autenticazione if (strpos($response, '235') === false) { error_log("SMTP Error: Autenticazione fallita - " . $response); fclose($smtp); return false; } // Imposta mittente $send("MAIL FROM: <" . MAIL_FROM . ">"); $read(); // Imposta destinatario $send("RCPT TO: <" . $to . ">"); $read(); // Inizia corpo messaggio $send("DATA"); $read(); // Costruisci headers $headers = "From: " . MAIL_FROM_NAME . " <" . MAIL_FROM . ">\r\n"; $headers .= "To: <" . $to . ">\r\n"; $headers .= "Subject: " . $subject . "\r\n"; $headers .= "MIME-Version: 1.0\r\n"; $headers .= "Content-Type: text/html; charset=UTF-8\r\n"; $headers .= "Date: " . date('r') . "\r\n"; // Invia email $send($headers . "\r\n" . $message . "\r\n."); $read(); // Chiudi connessione $send("QUIT"); $read(); fclose($smtp); return true; } catch (Exception $e) { error_log("SMTP Error: " . $e->getMessage()); return false; } } /** * Invia email di recupero password * * @param string $email Email destinatario * @param string $token Token di reset * @return bool True se inviata, false altrimenti */ function send_password_reset_email($email, $token) { $reset_link = SITE_URL . "/reset_password.php?token=" . $token; $subject = "Recupero Password - " . SITE_NAME; $message = "
Hai richiesto il recupero della password per il tuo account su " . SITE_NAME . ".
Clicca sul pulsante qui sotto per reimpostare la tua password:
Reimposta PasswordOppure copia e incolla questo link nel tuo browser:
{$reset_link}
Questo link è valido per " . (PASSWORD_RESET_TOKEN_LIFETIME / 60) . " minuti.
Se non hai richiesto tu questo recupero, ignora questa email.
Questa è un'email automatica, non rispondere a questo messaggio.