Compare commits

..

10 Commits

Author SHA1 Message Date
Francesco Picone
95a7faf5eb fix 2025-12-09 17:54:22 +01:00
Francesco Picone
402826b7ef add user confirmation 2025-12-09 17:50:01 +01:00
Francesco Picone
10f709a970 change from mail 2025-12-09 17:38:21 +01:00
Francesco Picone
568a02251c fix send mail with python script 2025-12-09 17:35:40 +01:00
Francesco Picone
b79b31df69 fix 2025-12-09 17:27:26 +01:00
Francesco Picone
b612146dfe fix 2025-12-09 17:25:37 +01:00
Francesco Picone
80e733adfb fix 2025-12-09 17:24:34 +01:00
Francesco Picone
77377cbaf1 fix 2025-12-09 17:23:11 +01:00
Francesco Picone
1ea20d24a0 . 2025-12-09 17:20:42 +01:00
Francesco Picone
33b24d8395 fix mail 2025-12-09 17:18:58 +01:00
13 changed files with 987 additions and 78 deletions

1
.gitignore vendored
View File

@@ -19,6 +19,7 @@ test_email.php
# Log
*.log
logs/
# OS
.DS_Store

View File

@@ -49,13 +49,10 @@ Piattaforma completa per vendere videolezioni e lezioni live di Pilates. Svilupp
### Requisiti
- **PHP** 7.4 o superiore (8.0+ raccomandato)
- **MySQL** 5.7 o superiore (o MariaDB 10.3+)
- **Python** 3.6 o superiore (per invio email SMTP)
- **Web Server** (Apache, Nginx, o PHP built-in server per sviluppo)
- **Account PayPal** Sandbox (per test) o Business (per produzione)
- **FFmpeg** (opzionale, per conversione automatica video in MP4)
- **PHP** 7.4 o superiore
- **MySQL** 5.7 o superiore (o MariaDB 10.3+)
- **Web Server** (Apache, Nginx, o PHP built-in server per sviluppo)
- **Account PayPal** Sandbox (per test) o Business (per produzione)
### Passo 1: Copia i File
```bash
@@ -115,7 +112,7 @@ define('SECRET_KEY', 'CAMBIA-QUESTA-CHIAVE-CON-STRINGA-CASUALE-LUNGA-E-SICURA');
```
#### Email SMTP con Gmail ⭐ **IMPORTANTE**
Per inviare email (recupero password), configura Gmail SMTP:
Per inviare email (recupero password, registrazione), configura Gmail SMTP:
1. **Abilita verifica 2 fattori** sul tuo account Gmail
2. Vai su [Password per le app](https://myaccount.google.com/apppasswords)
@@ -127,14 +124,30 @@ In `includes/config.php`:
```php
define('USE_SMTP', true); // Attiva SMTP
define('SMTP_HOST', 'smtp.gmail.com');
define('SMTP_PORT', 587);
define('SMTP_PORT', 587); // 587 per TLS, 465 per SSL
define('SMTP_USERNAME', 'tua-email@gmail.com'); // La tua Gmail
define('SMTP_PASSWORD', 'abcd efgh ijkl mnop'); // Password app (16 caratteri)
define('SMTP_ENCRYPTION', 'tls');
define('SMTP_ENCRYPTION', 'tls'); // tls (porta 587) o ssl (porta 465)
```
⚠️ **Non usare la password normale di Gmail**, usa solo la password applicazione!
**Sistema di invio email:**
- Le email vengono inviate tramite script Python (`send_email.py`)
- Python 3.6+ deve essere installato sul server
- Lo script legge automaticamente le credenziali da `config.php`
- Più affidabile e stabile rispetto a SMTP nativo PHP
Verifica installazione Python:
```bash
python3 --version
```
Se Python non è installato:
- **Ubuntu/Debian**: `sudo apt install python3`
- **CentOS/RHEL**: `sudo yum install python3`
- **Windows**: Scarica da [python.org](https://www.python.org/downloads/)
#### PayPal Sandbox (per Test)
1. Vai su [PayPal Developer](https://developer.paypal.com/)
2. Accedi con il tuo account PayPal
@@ -520,11 +533,39 @@ Crea `.htaccess` nella cartella `includes/`:
- La piattaforma è già aggiornata per gestire valori null
- Se vedi ancora warning, verifica che tutti i file siano aggiornati
### Reset Password non funziona
- Verifica che le email vengano inviate correttamente
- Configura SMTP in `config.php` se `mail()` non funziona
- Il token scade dopo 1 ora per sicurezza
- Controlla che la tabella `users` abbia i campi `reset_token` e `reset_expires`
### Reset Password / Email non funzionano
- Verifica che Python 3 sia installato: `python3 --version`
- Controlla credenziali SMTP in `config.php`
- Verifica che `send_email.py` abbia permessi esecuzione
- Testa invio dal profilo admin: pulsante "🧪 Testa Invio Email"
- Il token reset scade dopo 1 ora per sicurezza
- Controlla log errori PHP per dettagli (`error_log()`)
**Test manuale script Python:**
```bash
cd /path/to/pilates-platform
python3 send_email.py '{"to":"test@esempio.com","subject":"Test","html":"<p>Test email</p>"}'
```
**Errori comuni:**
- `python3: command not found` → Installa Python 3
- `Authentication failed` → Verifica password applicazione Gmail
- `Connection refused` → Controlla porta (587 per TLS, 465 per SSL)
- `Script not found` → Verifica che `send_email.py` esista nella root del progetto
- `Permission denied` → Su Linux: `chmod +x send_email.py`
- Il token reset scade dopo 1 ora per sicurezza
- Controlla log errori PHP per dettagli
**Test manuale script Python:**
```bash
cd /path/to/pilates-platform
python3 send_email.py '{"to":"test@esempio.com","subject":"Test","html":"<p>Test email</p>"}'
```
**Errori comuni:**
- `python3: command not found` → Installa Python 3
- `Authentication failed` → Verifica password applicazione Gmail
- `Connection refused` → Controlla porta (587 per TLS, 465 per SSL)
---
@@ -550,6 +591,7 @@ pilates-platform/
├── process_payment.php # Elabora pagamento PayPal
├── stream_video.php # Streaming protetto video (verifica permessi)
├── convert_videos.php # Conversione batch video in MP4 H.264
├── send_email.py # Script Python per invio email SMTP
├── test_password.php # Utility test hash password (debug)
├── test_email.php # Utility test invio email SMTP (debug)
└── README.md # Questo file

View File

@@ -29,6 +29,45 @@ if (isset($_GET['toggle_active']) && is_numeric($_GET['toggle_active'])) {
exit;
}
// Gestione re-invio email verifica
if (isset($_GET['resend_verification']) && is_numeric($_GET['resend_verification'])) {
$user_id = (int)$_GET['resend_verification'];
try {
$pdo = get_db_connection();
$stmt = $pdo->prepare("SELECT id, email, first_name, email_verified FROM users WHERE id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();
if ($user && !$user['email_verified']) {
// Genera nuovo token
$email_token = bin2hex(random_bytes(32));
$token_expires = date('Y-m-d H:i:s', strtotime('+24 hours'));
$stmt = $pdo->prepare("UPDATE users SET email_token = ?, email_token_expires = ? WHERE id = ?");
$stmt->execute([$email_token, $token_expires, $user_id]);
// Invia email
$verify_url = SITE_URL . "/verify_email.php?token=" . $email_token;
$subject = "Conferma il tuo account su " . SITE_NAME;
$body = "<p>Ciao <strong>" . htmlspecialchars($user['first_name']) . "</strong>,</p>
<p>Un amministratore ha inviato nuovamente il link di verifica per il tuo account.</p>
<p><a href='" . $verify_url . "' style='display: inline-block; padding: 12px 30px; background: #667eea; color: white; text-decoration: none; border-radius: 5px;'>Conferma Email</a></p>
<p>Questo link è valido per 24 ore.</p>";
send_email($user['email'], $subject, $body);
set_flash_message('success', 'Email di verifica inviata a ' . htmlspecialchars($user['email']));
} else {
set_flash_message('error', 'Utente già verificato o non trovato');
}
} catch (Exception $e) {
set_flash_message('error', 'Errore durante l\'invio dell\'email');
}
header('Location: users.php');
exit;
}
// Ottieni tutti gli utenti non admin
$pdo = get_db_connection();
$stmt = $pdo->query("
@@ -102,6 +141,7 @@ $users = $stmt->fetchAll();
<tr>
<th>Nome</th>
<th>Email</th>
<th>Stato Email</th>
<th>Registrato il</th>
<th>Ultimo Accesso</th>
<th>Acquisti</th>
@@ -116,7 +156,16 @@ $users = $stmt->fetchAll();
<td>
<strong><?php echo htmlspecialchars($user['first_name'] . ' ' . $user['last_name']); ?></strong>
</td>
<td><?php echo htmlspecialchars($user['email']); ?></td>
<td>
<?php echo htmlspecialchars($user['email']); ?>
</td>
<td>
<?php if ($user['email_verified']): ?>
<span class="text-success">✓ Verificata</span>
<?php else: ?>
<span class="text-warning">⚠️ Non verificata</span>
<?php endif; ?>
</td>
<td><?php echo format_date($user['created_at']); ?></td>
<td>
<?php
@@ -137,6 +186,14 @@ $users = $stmt->fetchAll();
class="btn btn-small <?php echo $user['is_active'] ? 'btn-danger' : 'btn-success'; ?>">
<?php echo $user['is_active'] ? 'Blocca' : 'Sblocca'; ?>
</a>
<?php if (!$user['email_verified']): ?>
<a href="users.php?resend_verification=<?php echo $user['id']; ?>"
class="btn btn-small btn-secondary"
style="margin-left: 5px;"
onclick="return confirm('Inviare nuovamente l\'email di verifica?');">
📧 Re-Invia
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>

View File

@@ -0,0 +1,23 @@
-- Aggiunge campi per verifica email agli utenti esistenti
-- Esegui questo script per aggiornare il database senza perdere dati
USE pilatesplatform;
-- Aggiungi campi per verifica email
ALTER TABLE users
ADD COLUMN email_verified BOOLEAN DEFAULT FALSE COMMENT 'True se email verificata' AFTER is_active,
ADD COLUMN email_token VARCHAR(64) DEFAULT NULL COMMENT 'Token per verifica email' AFTER email_verified,
ADD COLUMN email_token_expires DATETIME DEFAULT NULL COMMENT 'Scadenza token verifica email' AFTER email_token;
-- Aggiungi indici per performance
ALTER TABLE users
ADD INDEX idx_email_verified (email_verified),
ADD INDEX idx_email_token (email_token);
-- Imposta tutti gli utenti esistenti come verificati (per retrocompatibilità)
UPDATE users SET email_verified = TRUE WHERE email_verified = FALSE;
-- Imposta admin come verificato
UPDATE users SET email_verified = TRUE WHERE is_admin = TRUE;
SELECT 'Migrazione completata! Campi email_verified, email_token, email_token_expires aggiunti.' AS status;

View File

@@ -22,6 +22,9 @@ CREATE TABLE IF NOT EXISTS users (
last_name VARCHAR(100) NOT NULL COMMENT 'Cognome',
is_admin BOOLEAN DEFAULT FALSE COMMENT 'True se amministratore',
is_active BOOLEAN DEFAULT TRUE COMMENT 'Permette di disabilitare account',
email_verified BOOLEAN DEFAULT FALSE COMMENT 'True se email verificata',
email_token VARCHAR(64) DEFAULT NULL COMMENT 'Token per verifica email',
email_token_expires DATETIME DEFAULT NULL COMMENT 'Scadenza token verifica email',
profile_image VARCHAR(255) DEFAULT NULL COMMENT 'URL immagine profilo',
phone VARCHAR(20) DEFAULT NULL COMMENT 'Numero telefono opzionale',
created_at DATETIME NOT NULL COMMENT 'Data registrazione',
@@ -31,6 +34,8 @@ CREATE TABLE IF NOT EXISTS users (
INDEX idx_email (email),
INDEX idx_is_admin (is_admin),
INDEX idx_email_verified (email_verified),
INDEX idx_email_token (email_token),
INDEX idx_deleted_at (deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Utenti della piattaforma';
@@ -230,6 +235,45 @@ INSERT INTO lessons (title, description, type, video_url, video_platform, thumbn
NOW()
);
-- ============================================
-- MIGRAZIONE: CONVERSIONE VIDEO (9 Dicembre 2025)
-- ============================================
-- Aggiunge campo per tracciare conversione video in MP4 H.264
ALTER TABLE lessons
ADD COLUMN IF NOT EXISTS video_converted BOOLEAN DEFAULT FALSE COMMENT 'True se il video è stato convertito in MP4 H.264'
AFTER video_platform;
-- Aggiorna i video esistenti come non convertiti
UPDATE lessons
SET video_converted = FALSE
WHERE type = 'video'
AND video_platform = 'local'
AND video_url IS NOT NULL
AND (video_converted IS NULL OR video_converted = FALSE);
-- ============================================
-- MIGRAZIONE: VERIFICA EMAIL (9 Dicembre 2025)
-- ============================================
-- Aggiunge campi per sistema di verifica email
-- Aggiungi campi per verifica email (se non esistono già)
ALTER TABLE users
ADD COLUMN IF NOT EXISTS email_verified BOOLEAN DEFAULT FALSE COMMENT 'True se email verificata' AFTER is_active,
ADD COLUMN IF NOT EXISTS email_token VARCHAR(64) DEFAULT NULL COMMENT 'Token per verifica email' AFTER email_verified,
ADD COLUMN IF NOT EXISTS email_token_expires DATETIME DEFAULT NULL COMMENT 'Scadenza token verifica email' AFTER email_token;
-- Aggiungi indici per performance (se non esistono già)
ALTER TABLE users
ADD INDEX IF NOT EXISTS idx_email_verified (email_verified),
ADD INDEX IF NOT EXISTS idx_email_token (email_token);
-- Imposta tutti gli utenti esistenti come verificati (per retrocompatibilità)
UPDATE users SET email_verified = TRUE WHERE email_verified = FALSE OR email_verified IS NULL;
-- Imposta admin come verificato
UPDATE users SET email_verified = TRUE WHERE is_admin = TRUE;
-- ============================================
-- FINE SCRIPT
-- ============================================

View File

@@ -67,19 +67,21 @@ define('PAYPAL_API_URL', PAYPAL_SANDBOX ?
// ============================================
// Configurazione per l'invio di email
define('MAIL_FROM', 'pilatesstudio@gmail.com');
define('MAIL_FROM', 'rosavirgadamo_pilates@gmail.com');
define('MAIL_FROM_NAME', 'Pilates Studio');
// Usa SMTP per invio email affidabile
define('USE_SMTP', true); // Imposta false per usare mail() PHP di base
// Impostazioni SMTP - Gmail
// NOTA: Le email vengono inviate tramite script Python (send_email.py) per maggiore affidabilità
// Per Gmail: usa password applicazione da https://myaccount.google.com/apppasswords
// Requisito: Python 3.6+ installato sul server (testa con: python3 --version)
define('SMTP_HOST', 'smtp.gmail.com');
define('SMTP_PORT', 587);
define('SMTP_USERNAME', 'pyco.networking@gmail.com'); // Inserisci la tua email Gmail
define('SMTP_PASSWORD', 'iaxuxsvggkfmbzpa'); // Password applicazione (16 caratteri)
define('SMTP_ENCRYPTION', 'tls'); // tls o ssl
define('SMTP_ENCRYPTION', 'tls'); // tls (porta 587) o ssl (porta 465)
// ============================================
// CONFIGURAZIONE UPLOAD FILE

View File

@@ -208,17 +208,22 @@ function get_user_by_id($user_id) {
* @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) {
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, created_at)
VALUES (?, ?, ?, ?, ?, NOW())
INSERT INTO users (
email, password, first_name, last_name, is_admin,
email_verified, email_token, email_token_expires, created_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())
");
$stmt->execute([
@@ -226,12 +231,16 @@ function create_user($email, $password, $first_name, $last_name, $is_admin = fal
$hashed_password,
$first_name,
$last_name,
$is_admin ? 1 : 0
$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;
}
}
@@ -433,7 +442,7 @@ function send_email($to, $subject, $message) {
}
/**
* Invia email tramite SMTP (Gmail)
* Invia email tramite script Python
*
* @param string $to Email destinatario
* @param string $subject Oggetto email
@@ -442,11 +451,75 @@ function send_email($to, $subject, $message) {
*/
function send_smtp_email($to, $subject, $message) {
try {
// Connessione al server SMTP
$smtp = fsockopen(SMTP_HOST, SMTP_PORT, $errno, $errstr, 30);
// 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);
error_log("SMTP Error: Impossibile connettersi a " . SMTP_HOST . ":" . SMTP_PORT . " - $errstr ($errno)");
return false;
}
@@ -470,12 +543,32 @@ function send_smtp_email($to, $subject, $message) {
// Inizia TLS se richiesto
if (SMTP_ENCRYPTION === 'tls') {
$send("STARTTLS");
$read();
$response = $read();
// Usa una versione più compatibile di TLS
$crypto_method = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT;
if (!stream_socket_enable_crypto($smtp, true, $crypto_method)) {
error_log("SMTP Error: Impossibile avviare crittografia TLS");
// 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;
}

View File

@@ -39,39 +39,45 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$user = get_user_by_email($email);
if ($user && verify_password($password, $user['password'])) {
// Password corretta - effettua il login
// Password corretta - verifica se email è confermata
// Aggiorna data ultimo accesso
$pdo = get_db_connection();
$stmt = $pdo->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
$stmt->execute([$user['id']]);
// Log attività
$stmt = $pdo->prepare("
INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at)
VALUES (?, 'login', 'Utente ha effettuato il login', ?, ?, NOW())
");
$stmt->execute([
$user['id'],
$_SERVER['REMOTE_ADDR'] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null
]);
// Imposta la sessione
login_user($user['id'], $user['is_admin']);
// Reindirizza alla pagina appropriata
if (isset($_SESSION['redirect_after_login'])) {
$redirect = $_SESSION['redirect_after_login'];
unset($_SESSION['redirect_after_login']);
header("Location: $redirect");
} elseif ($user['is_admin']) {
header('Location: admin/dashboard.php');
// Admin possono sempre accedere
if (!$user['is_admin'] && !$user['email_verified']) {
$error = 'Devi verificare la tua email prima di accedere. <a href="resend_verification.php?email=' . urlencode($email) . '" style="color: #667eea; font-weight: bold;">Invia nuovo link</a>';
} elseif (!$user['is_active']) {
$error = 'Il tuo account è stato disattivato. Contatta l\'amministratore.';
} else {
header('Location: user/dashboard.php');
}
exit;
// Aggiorna data ultimo accesso
$pdo = get_db_connection();
$stmt = $pdo->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
$stmt->execute([$user['id']]);
// Log attività
$stmt = $pdo->prepare("
INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at)
VALUES (?, 'login', 'Utente ha effettuato il login', ?, ?, NOW())
");
$stmt->execute([
$user['id'],
$_SERVER['REMOTE_ADDR'] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null
]);
// Imposta la sessione
login_user($user['id'], $user['is_admin']);
// Reindirizza alla pagina appropriata
if (isset($_SESSION['redirect_after_login'])) {
$redirect = $_SESSION['redirect_after_login'];
unset($_SESSION['redirect_after_login']);
header("Location: $redirect");
} elseif ($user['is_admin']) {
header('Location: admin/dashboard.php');
} else {
header('Location: user/dashboard.php');
}
exit;
}
} else {
$error = 'Email o password non corretti';
}
@@ -103,7 +109,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<?php if ($error): ?>
<div class="alert alert-error">
<?php echo htmlspecialchars($error); ?>
<?php echo $error; // Contiene HTML sicuro per link ?>
</div>
<?php endif; ?>

View File

@@ -45,14 +45,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (get_user_by_email($email)) {
$error = 'Questa email è già registrata';
} else {
// Crea il nuovo utente
$user_id = create_user($email, $password, $first_name, $last_name);
// Genera token di verifica email
$email_token = bin2hex(random_bytes(32));
$token_expires = date('Y-m-d H:i:s', strtotime('+24 hours'));
// Crea il nuovo utente (non verificato)
$user_id = create_user($email, $password, $first_name, $last_name, false, $email_token, $token_expires);
if ($user_id) {
$success = true;
// Invia email di benvenuto
$subject = "Benvenuto su " . SITE_NAME . "!";
// Invia email di verifica
$verify_url = SITE_URL . "/verify_email.php?token=" . $email_token;
$subject = "Conferma il tuo account su " . SITE_NAME;
$body = "
<html>
<head>
@@ -63,27 +68,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
.content { background: #f9f9f9; padding: 30px; border-radius: 0 0 8px 8px; }
.button { display: inline-block; padding: 12px 30px; background: #667eea; color: white; text-decoration: none; border-radius: 5px; margin: 20px 0; }
.footer { text-align: center; color: #999; font-size: 12px; margin-top: 20px; }
.warning { background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h1>Benvenuto su " . SITE_NAME . "!</h1>
<h1>Conferma il tuo account</h1>
</div>
<div class='content'>
<p>Ciao <strong>" . htmlspecialchars($first_name) . "</strong>,</p>
<p>Grazie per esserti registrato sulla nostra piattaforma! Il tuo account è stato creato con successo.</p>
<p>Grazie per esserti registrato su " . SITE_NAME . "!</p>
<p>Per completare la registrazione e attivare il tuo account, devi confermare il tuo indirizzo email cliccando sul pulsante qui sotto:</p>
<div style='text-align: center;'>
<a href='" . $verify_url . "' class='button'>✅ Conferma Email</a>
</div>
<p style='font-size: 14px; color: #666;'>Oppure copia e incolla questo link nel tuo browser:<br>
<a href='" . $verify_url . "'>" . $verify_url . "</a></p>
<div class='warning'>
<strong>⏰ Importante:</strong> Questo link è valido per 24 ore. Dopo questo periodo dovrai richiedere un nuovo link di verifica.
</div>
<p><strong>I tuoi dati di accesso:</strong></p>
<ul>
<li>Email: " . htmlspecialchars($email) . "</li>
<li>Password: quella che hai scelto durante la registrazione</li>
</ul>
<p>Ora puoi accedere al catalogo delle nostre lezioni e iniziare il tuo percorso di Pilates!</p>
<div style='text-align: center;'>
<a href='" . SITE_URL . "/user/dashboard.php' class='button'>Vai alla Dashboard</a>
</div>
<p>Se hai domande o hai bisogno di assistenza, non esitare a contattarci.</p>
<p>Buon allenamento!<br>Il team di " . SITE_NAME . "</p>
<p>Una volta verificata la tua email, potrai accedere al catalogo delle nostre lezioni e iniziare il tuo percorso di Pilates!</p>
<p>Se non hai richiesto questa registrazione, ignora questa email.</p>
<p>A presto!<br>Il team di " . SITE_NAME . "</p>
</div>
<div class='footer'>
<p>Questa è una email automatica, per favore non rispondere a questo messaggio.</p>
@@ -99,7 +111,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = get_db_connection();
$stmt = $pdo->prepare("
INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at)
VALUES (?, 'register', 'Nuovo utente registrato', ?, ?, NOW())
VALUES (?, 'register', 'Nuovo utente registrato - in attesa verifica email', ?, ?, NOW())
");
$stmt->execute([
$user_id,
@@ -107,11 +119,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SERVER['HTTP_USER_AGENT'] ?? null
]);
// Login automatico dopo registrazione
login_user($user_id, false);
// Reindirizza dopo 2 secondi
header("refresh:2;url=user/dashboard.php");
// NON fare login automatico - utente deve verificare email prima
// Reindirizza a pagina di conferma
header("refresh:3;url=login.php");
} else {
$error = 'Errore durante la registrazione. Riprova più tardi.';
}
@@ -154,7 +164,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div>
<?php endif; ?>
<?php if (!$success): ?>
<?php if ($success): ?>
<div class="alert alert-success">
<h3 style="margin-top: 0;">✅ Registrazione completata!</h3>
<p>Ti abbiamo inviato un'email di verifica a <strong><?php echo htmlspecialchars($email); ?></strong></p>
<p>Per attivare il tuo account, clicca sul link che troverai nell'email.</p>
<p class="text-muted" style="font-size: 14px; margin-top: 15px;">
⚠️ Il link è valido per 24 ore. Se non lo trovi, controlla la cartella spam.
</p>
<p style="margin-top: 20px;">Sarai reindirizzato alla pagina di login tra pochi secondi...</p>
</div>
<?php elseif (!$success): ?>
<form method="POST" action="">
<div class="form-group">
<label for="first_name" class="form-label">Nome</label>

207
resend_verification.php Normal file
View File

@@ -0,0 +1,207 @@
<?php
/**
* Re-Invio Email di Verifica
*
* Permette agli utenti di richiedere un nuovo link di verifica email
*/
require_once 'includes/config.php';
require_once 'includes/functions.php';
session_start();
$success = false;
$error = '';
$email = sanitize_input($_GET['email'] ?? '');
// Processa il form
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = sanitize_input($_POST['email'] ?? '');
if (empty($email)) {
$error = 'Inserisci la tua email';
} elseif (!validate_email($email)) {
$error = 'Email non valida';
} else {
try {
$pdo = get_db_connection();
// Trova utente
$stmt = $pdo->prepare("
SELECT id, first_name, email_verified
FROM users
WHERE email = ?
AND deleted_at IS NULL
");
$stmt->execute([$email]);
$user = $stmt->fetch();
if (!$user) {
// Per sicurezza, non rivelare se l'email esiste
$success = true;
} elseif ($user['email_verified']) {
$error = 'Questo account è già stato verificato. Puoi effettuare il login.';
} else {
// Genera nuovo token
$email_token = bin2hex(random_bytes(32));
$token_expires = date('Y-m-d H:i:s', strtotime('+24 hours'));
// Aggiorna token
$stmt = $pdo->prepare("
UPDATE users
SET email_token = ?,
email_token_expires = ?,
updated_at = NOW()
WHERE id = ?
");
$stmt->execute([$email_token, $token_expires, $user['id']]);
// Log attività
$stmt = $pdo->prepare("
INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at)
VALUES (?, 'resend_verification', 'Richiesto nuovo link verifica email', ?, ?, NOW())
");
$stmt->execute([
$user['id'],
$_SERVER['REMOTE_ADDR'] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null
]);
// Invia email
$verify_url = SITE_URL . "/verify_email.php?token=" . $email_token;
$subject = "Conferma il tuo account su " . SITE_NAME;
$body = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; text-align: center; border-radius: 8px 8px 0 0; }
.content { background: #f9f9f9; padding: 30px; border-radius: 0 0 8px 8px; }
.button { display: inline-block; padding: 12px 30px; background: #667eea; color: white; text-decoration: none; border-radius: 5px; margin: 20px 0; }
.footer { text-align: center; color: #999; font-size: 12px; margin-top: 20px; }
.warning { background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h1>Nuovo Link di Verifica</h1>
</div>
<div class='content'>
<p>Ciao <strong>" . htmlspecialchars($user['first_name']) . "</strong>,</p>
<p>Hai richiesto un nuovo link per verificare il tuo account su " . SITE_NAME . ".</p>
<p>Per completare la registrazione e attivare il tuo account, clicca sul pulsante qui sotto:</p>
<div style='text-align: center;'>
<a href='" . $verify_url . "' class='button'>✅ Conferma Email</a>
</div>
<p style='font-size: 14px; color: #666;'>Oppure copia e incolla questo link nel tuo browser:<br>
<a href='" . $verify_url . "'>" . $verify_url . "</a></p>
<div class='warning'>
<strong>⏰ Importante:</strong> Questo link è valido per 24 ore. Questo nuovo link sostituisce quello precedente.
</div>
<p>Se non hai richiesto questo link, ignora questa email e il tuo account rimarrà non verificato.</p>
<p>A presto!<br>Il team di " . SITE_NAME . "</p>
</div>
<div class='footer'>
<p>Questa è una email automatica, per favore non rispondere a questo messaggio.</p>
</div>
</div>
</body>
</html>
";
send_email($email, $subject, $body);
$success = true;
}
} catch (PDOException $e) {
$error = 'Errore durante l\'invio. Riprova più tardi.';
error_log("Resend verification error: " . $e->getMessage());
}
}
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Re-Invio Verifica Email - <?php echo SITE_NAME; ?></title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<header class="header">
<div class="container">
<div class="header-content">
<h1 class="logo">
<a href="index.php" style="color: inherit; text-decoration: none;"><?php echo SITE_NAME; ?></a>
</h1>
</div>
</div>
</header>
<main class="container" style="max-width: 500px; margin: 50px auto;">
<div class="card">
<h2 class="section-title" style="text-align: center;">📧 Re-Invio Email di Verifica</h2>
<?php if ($error): ?>
<div class="alert alert-error">
<?php echo htmlspecialchars($error); ?>
</div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success">
<h3 style="margin-top: 0;">✅ Email Inviata!</h3>
<p>Se l'email è registrata nel nostro sistema, riceverai un nuovo link di verifica.</p>
<p class="text-muted" style="font-size: 14px; margin-top: 15px;">
Controlla la tua casella email (e la cartella spam). Il link è valido per 24 ore.
</p>
<div style="text-align: center; margin-top: 25px;">
<a href="login.php" class="btn btn-primary">
← Torna al Login
</a>
</div>
</div>
<?php else: ?>
<p class="text-center text-muted" style="margin-bottom: 20px;">
Inserisci la tua email per ricevere un nuovo link di verifica
</p>
<form method="POST" action="">
<div class="form-group">
<label for="email" class="form-label">Email</label>
<input
type="email"
id="email"
name="email"
class="form-control"
value="<?php echo htmlspecialchars($email); ?>"
required
autofocus
placeholder="tua-email@esempio.com"
>
</div>
<button type="submit" class="btn btn-primary btn-large" style="width: 100%;">
📧 Invia Nuovo Link
</button>
</form>
<div class="text-center mt-2">
<p class="text-muted">
<a href="login.php">← Torna al Login</a>
</p>
</div>
<?php endif; ?>
</div>
</main>
<footer class="footer">
<div class="container">
<p>&copy; <?php echo date('Y'); ?> <?php echo SITE_NAME; ?>. Tutti i diritti riservati.</p>
</div>
</footer>
</body>
</html>

175
send_email.py Normal file
View File

@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""
Script Python per invio email SMTP
Legge le credenziali da config.php e invia email tramite Gmail
Log solo in caso di errore
"""
import sys
import json
import re
import smtplib
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from pathlib import Path
from datetime import datetime
# Configura logging solo per errori
log_file = Path(__file__).parent / 'logs' / 'email_errors.log'
log_file.parent.mkdir(exist_ok=True)
logging.basicConfig(
filename=str(log_file),
level=logging.ERROR,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
def parse_php_config(config_path):
"""Estrae le configurazioni SMTP dal file config.php"""
config = {}
try:
with open(config_path, 'r', encoding='utf-8') as f:
content = f.read()
# Estrai le define con regex
patterns = {
'SMTP_HOST': r"define\('SMTP_HOST',\s*'([^']+)'\)",
'SMTP_PORT': r"define\('SMTP_PORT',\s*(\d+)\)",
'SMTP_USERNAME': r"define\('SMTP_USERNAME',\s*'([^']+)'\)",
'SMTP_PASSWORD': r"define\('SMTP_PASSWORD',\s*'([^']+)'\)",
'SMTP_ENCRYPTION': r"define\('SMTP_ENCRYPTION',\s*'([^']+)'\)",
'MAIL_FROM': r"define\('MAIL_FROM',\s*'([^']+)'\)",
'MAIL_FROM_NAME': r"define\('MAIL_FROM_NAME',\s*'([^']+)'\)",
}
for key, pattern in patterns.items():
match = re.search(pattern, content)
if match:
value = match.group(1)
# Converti porta in intero
if key == 'SMTP_PORT':
config[key] = int(value)
else:
config[key] = value
return config
except Exception as e:
print(json.dumps({
'success': False,
'error': f'Errore lettura config.php: {str(e)}'
}))
sys.exit(1)
def send_email(to_email, subject, html_body, config):
"""Invia email tramite SMTP"""
try:
# Crea messaggio
msg = MIMEMultipart('alternative')
msg['From'] = f"{config['MAIL_FROM_NAME']} <{config['MAIL_FROM']}>"
msg['To'] = to_email
msg['Subject'] = subject
# Aggiungi corpo HTML
html_part = MIMEText(html_body, 'html', 'utf-8')
msg.attach(html_part)
# Connetti al server SMTP
if config['SMTP_ENCRYPTION'] == 'ssl':
# SSL diretto (porta 465)
server = smtplib.SMTP_SSL(config['SMTP_HOST'], config['SMTP_PORT'], timeout=30)
else:
# TLS con STARTTLS (porta 587)
server = smtplib.SMTP(config['SMTP_HOST'], config['SMTP_PORT'], timeout=30)
server.ehlo()
server.starttls()
server.ehlo()
# Login
server.login(config['SMTP_USERNAME'], config['SMTP_PASSWORD'])
# Invia email
server.send_message(msg)
server.quit()
return {
'success': True,
'message': f'Email inviata con successo a {to_email}'
}
except smtplib.SMTPAuthenticationError as e:
error_msg = f'Autenticazione SMTP fallita per {to_email}: {str(e)}'
logging.error(error_msg)
return {
'success': False,
'error': error_msg
}
except smtplib.SMTPException as e:
error_msg = f'Errore SMTP invio a {to_email}: {str(e)}'
logging.error(error_msg)
return {
'success': False,
'error': error_msg
}
except Exception as e:
error_msg = f'Errore generico invio a {to_email}: {str(e)}'
logging.error(error_msg)
return {
'success': False,
'error': error_msg
}
def main():
"""Funzione principale"""
if len(sys.argv) < 2:
print(json.dumps({
'success': False,
'error': 'Uso: python send_email.py <json_data>'
}))
sys.exit(1)
try:
# Parse input JSON
data = json.loads(sys.argv[1])
if 'to' not in data or 'subject' not in data or 'html' not in data:
error = 'Parametri mancanti: to, subject, html sono obbligatori'
logging.error(error)
print(json.dumps({
'success': False,
'error': error
}))
sys.exit(1)
# Leggi configurazione
script_dir = Path(__file__).parent
config_path = script_dir / 'includes' / 'config.php'
config = parse_php_config(config_path)
# Invia email
result = send_email(data['to'], data['subject'], data['html'], config)
print(json.dumps(result))
sys.exit(0 if result['success'] else 1)
except json.JSONDecodeError as e:
error = f'JSON non valido: {str(e)}'
logging.error(error)
print(json.dumps({
'success': False,
'error': error
}))
sys.exit(1)
except Exception as e:
error = f'Errore: {str(e)}'
logging.error(error)
print(json.dumps({
'success': False,
'error': error
}))
sys.exit(1)
if __name__ == '__main__':
main()

43
test_python.py Normal file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""
Test rapido per verificare che Python e le librerie necessarie siano installate
"""
import sys
def test_imports():
"""Verifica che tutte le librerie necessarie siano disponibili"""
required_modules = {
'smtplib': 'Libreria SMTP (built-in)',
'email': 'Libreria email (built-in)',
'json': 'Libreria JSON (built-in)',
're': 'Libreria regex (built-in)',
'pathlib': 'Libreria path (built-in)'
}
print("🐍 Test Python per Pilates Platform")
print(f" Python version: {sys.version}")
print()
all_ok = True
for module, description in required_modules.items():
try:
__import__(module)
print(f"{module:12} - {description}")
except ImportError:
print(f"{module:12} - MANCANTE!")
all_ok = False
print()
if all_ok:
print("✅ Tutte le librerie necessarie sono installate!")
print("✅ Python è pronto per inviare email.")
return 0
else:
print("❌ Alcune librerie sono mancanti. Installa Python 3.6+")
return 1
if __name__ == '__main__':
sys.exit(test_imports())

196
verify_email.php Normal file
View File

@@ -0,0 +1,196 @@
<?php
/**
* Verifica Email
*
* Conferma l'indirizzo email dell'utente tramite token
*/
require_once 'includes/config.php';
require_once 'includes/functions.php';
session_start();
$success = false;
$error = '';
$email = '';
// Verifica token
if (isset($_GET['token']) && !empty($_GET['token'])) {
$token = sanitize_input($_GET['token']);
try {
$pdo = get_db_connection();
// Trova utente con questo token
$stmt = $pdo->prepare("
SELECT id, email, first_name, email_token_expires, email_verified
FROM users
WHERE email_token = ?
AND deleted_at IS NULL
");
$stmt->execute([$token]);
$user = $stmt->fetch();
if (!$user) {
$error = 'Token non valido o già utilizzato.';
} elseif ($user['email_verified']) {
$error = 'Questo account è già stato verificato. Puoi effettuare il login.';
$email = $user['email'];
} elseif (strtotime($user['email_token_expires']) < time()) {
$error = 'Il token è scaduto. Richiedi un nuovo link di verifica dalla pagina di login.';
$email = $user['email'];
} else {
// Token valido - verifica email
$stmt = $pdo->prepare("
UPDATE users
SET email_verified = TRUE,
email_token = NULL,
email_token_expires = NULL,
updated_at = NOW()
WHERE id = ?
");
$stmt->execute([$user['id']]);
// Log attività
$stmt = $pdo->prepare("
INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at)
VALUES (?, 'email_verified', 'Email verificata con successo', ?, ?, NOW())
");
$stmt->execute([
$user['id'],
$_SERVER['REMOTE_ADDR'] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null
]);
// Invia email di benvenuto
$subject = "Benvenuto su " . SITE_NAME . "!";
$body = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; text-align: center; border-radius: 8px 8px 0 0; }
.content { background: #f9f9f9; padding: 30px; border-radius: 0 0 8px 8px; }
.button { display: inline-block; padding: 12px 30px; background: #667eea; color: white; text-decoration: none; border-radius: 5px; margin: 20px 0; }
.footer { text-align: center; color: #999; font-size: 12px; margin-top: 20px; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h1>✅ Account Attivato!</h1>
</div>
<div class='content'>
<p>Ciao <strong>" . htmlspecialchars($user['first_name']) . "</strong>,</p>
<p>🎉 Il tuo account è stato verificato con successo!</p>
<p>Ora puoi accedere alla piattaforma e iniziare a esplorare il nostro catalogo di lezioni di Pilates.</p>
<div style='text-align: center;'>
<a href='" . SITE_URL . "/login.php' class='button'>Accedi Ora</a>
</div>
<p><strong>Cosa puoi fare ora:</strong></p>
<ul>
<li>🎥 Guarda le lezioni demo gratuite</li>
<li>📚 Esplora il catalogo completo</li>
<li>🛒 Acquista le lezioni che ti interessano</li>
<li>👤 Personalizza il tuo profilo</li>
</ul>
<p>Se hai domande o hai bisogno di assistenza, non esitare a contattarci.</p>
<p>Buon allenamento!<br>Il team di " . SITE_NAME . "</p>
</div>
<div class='footer'>
<p>Questa è una email automatica, per favore non rispondere a questo messaggio.</p>
</div>
</div>
</body>
</html>
";
send_email($user['email'], $subject, $body);
$success = true;
$email = $user['email'];
}
} catch (PDOException $e) {
$error = 'Errore durante la verifica. Riprova più tardi.';
error_log("Email verification error: " . $e->getMessage());
}
} else {
$error = 'Token mancante. Controlla il link nell\'email di verifica.';
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verifica Email - <?php echo SITE_NAME; ?></title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<header class="header">
<div class="container">
<div class="header-content">
<h1 class="logo">
<a href="index.php" style="color: inherit; text-decoration: none;"><?php echo SITE_NAME; ?></a>
</h1>
</div>
</div>
</header>
<main class="container" style="max-width: 600px; margin: 50px auto;">
<div class="card">
<h2 class="section-title" style="text-align: center;">
<?php echo $success ? '✅ Email Verificata!' : '❌ Verifica Fallita'; ?>
</h2>
<?php if ($success): ?>
<div class="alert alert-success">
<p style="font-size: 16px; margin-bottom: 15px;">
Il tuo account è stato attivato con successo!
</p>
<p>
Email verificata: <strong><?php echo htmlspecialchars($email); ?></strong>
</p>
<p style="margin-top: 20px;">
Ora puoi accedere alla piattaforma e iniziare il tuo percorso di Pilates!
</p>
<div style="text-align: center; margin-top: 30px;">
<a href="login.php" class="btn btn-primary btn-large">
🔐 Vai al Login
</a>
</div>
</div>
<?php else: ?>
<div class="alert alert-error">
<p style="font-size: 16px; margin-bottom: 15px;">
<?php echo htmlspecialchars($error); ?>
</p>
<?php if ($email): ?>
<p style="margin-top: 20px;">
Puoi richiedere un nuovo link di verifica dalla pagina di login.
</p>
<?php endif; ?>
<div style="text-align: center; margin-top: 30px;">
<a href="login.php" class="btn btn-primary">
← Torna al Login
</a>
<?php if ($email): ?>
<a href="resend_verification.php?email=<?php echo urlencode($email); ?>" class="btn btn-secondary" style="margin-left: 10px;">
📧 Invia Nuovo Link
</a>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
</main>
<footer class="footer">
<div class="container">
<p>&copy; <?php echo date('Y'); ?> <?php echo SITE_NAME; ?>. Tutti i diritti riservati.</p>
</div>
</footer>
</body>
</html>