MMCT TEAM
Server IP : 217.21.85.138  /  Your IP : 216.73.216.103
Web Server : LiteSpeed
System : Linux in-mum-web906.main-hosting.eu 4.18.0-553.37.1.lve.el8.x86_64 #1 SMP Mon Feb 10 22:45:17 UTC 2025 x86_64
User : u915722082 ( 915722082)
PHP Version : 7.4.33
Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : OFF  |  Python : OFF
Directory (0755) :  /home/u915722082/.nvm/../public_html/lohri/user/

[  Home  ][  C0mmand  ][  Upload File  ]

Current File : /home/u915722082/.nvm/../public_html/lohri/user/ajax_forgot.php
<?php
// ajax_forgot.php
header('Content-Type: application/json; charset=utf-8');

// Enable error reporting for debugging (disable in production)
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('log_errors', 1);

session_start();

require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/../lib/Mailer.php';

try {
    $conn = getDatabaseConnection();
    
    // Use UTC inside MySQL to avoid timezone drift
    $conn->query("SET time_zone = '+00:00'");
    
    // Safe JSON helper
    function send_json($status, $message, $extra = []) {
        echo json_encode(array_merge(['status' => $status, 'message' => $message], $extra));
        exit;
    }
    
    // Check if request method is POST
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        send_json('error', 'Invalid request method');
    }
    
    // Inputs
    $email = trim($_POST['email'] ?? '');
    $otp   = trim($_POST['otp'] ?? '');
    
    // Validate email
    if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        send_json('error', 'Invalid email address');
    }
    
    // STEP 1: Generate & send OTP
    if ($otp === '') {
        $q = $conn->prepare("SELECT uid, fname FROM tbl_user WHERE email = ? LIMIT 1");
        if (!$q) {
            error_log("Database prepare error: " . $conn->error);
            send_json('error', 'Database error occurred');
        }
        
        $q->bind_param('s', $email);
        $q->execute();
        $result = $q->get_result();
        $user = $result->fetch_assoc();
        $q->close();
        
        // Do not reveal if account exists for security
        if (!$user) {
            // Still send success message to prevent email enumeration
            // But don't actually send email
            error_log("Password reset attempted for non-existent email: $email");
            send_json('ok', 'If this email exists, OTP has been sent', ['step' => 'otp_sent']);
        }
        
        // Generate 6-digit OTP with leading zeros
        $genOtp = str_pad(strval(random_int(100000, 999999)), 6, '0', STR_PAD_LEFT);
        
        // **FIX: Delete ALL old OTPs for this email (not just user_id)**
        // This ensures no conflicts from previous attempts
        $del = $conn->prepare("
            DELETE pr FROM password_resets pr
            JOIN tbl_user u ON pr.user_id = u.uid
            WHERE u.email = ?
        ");
        if (!$del) {
            error_log("Database prepare error: " . $conn->error);
            send_json('error', 'Database error occurred');
        }
        
        $del->bind_param('s', $email);
        $del->execute();
        $deletedRows = $del->affected_rows;
        $del->close();
        
        error_log("Deleted $deletedRows old OTP(s) for email: $email");
        
        // Insert new OTP with proper UTC timestamp
        $insSql = "
            INSERT INTO password_resets (user_id, otp, otp_expires_at, used, created_at)
            VALUES (?, ?, DATE_ADD(UTC_TIMESTAMP(), INTERVAL 10 MINUTE), 0, UTC_TIMESTAMP())
        ";
        
        $ins = $conn->prepare($insSql);
        if (!$ins) {
            error_log("Database prepare error: " . $conn->error);
            send_json('error', 'Database error occurred');
        }
        
        $ins->bind_param('is', $user['uid'], $genOtp);
        $ok = $ins->execute();
        
        if (!$ok) {
            error_log("Insert OTP error: " . $ins->error);
            $ins->close();
            send_json('error', 'Failed to create OTP');
        }
        
        $insertId = $ins->insert_id;
        $ins->close();
        
        error_log("New OTP inserted with ID: $insertId for user: " . $user['uid']);
        
        // Send OTP email
        $emailSent = Mailer::sendOtp($email, $user['fname'] ?? 'User', $genOtp);
        
        if (!$emailSent) {
            error_log("Failed to send OTP email to: " . $email);
            // **FIX: Delete the OTP if email fails to send**
            $conn->query("DELETE FROM password_resets WHERE id = $insertId");
            send_json('error', 'Failed to send OTP email. Please try again.');
        }
        
        // Log successful OTP generation (remove in production)
        error_log("OTP generated and sent for user: " . $user['uid'] . ", email: $email, OTP: " . $genOtp);
        
        send_json('ok', 'OTP sent to your email', ['step' => 'otp_sent']);
    }
    
    // STEP 2: Verify OTP
    if ($otp !== '') {
        // Validate OTP format - must be exactly 6 digits
        if (!preg_match('/^\d{6}$/', $otp)) {
            send_json('error', 'OTP must be exactly 6 digits');
        }
        
        // **FIX: More detailed query to debug issues**
        $sel = $conn->prepare("
            SELECT 
                pr.id, 
                pr.user_id, 
                pr.otp,
                pr.otp_expires_at,
                pr.used,
                pr.created_at,
                u.email,
                u.fname,
                CASE 
                    WHEN pr.used = 1 THEN 'already_used'
                    WHEN pr.otp_expires_at <= UTC_TIMESTAMP() THEN 'expired'
                    ELSE 'valid'
                END as otp_status,
                TIMESTAMPDIFF(SECOND, UTC_TIMESTAMP(), pr.otp_expires_at) as seconds_remaining
            FROM password_resets pr
            JOIN tbl_user u ON pr.user_id = u.uid
            WHERE u.email = ?
              AND pr.otp = ?
            ORDER BY pr.id DESC
            LIMIT 1
        ");
        
        if (!$sel) {
            error_log("Database prepare error: " . $conn->error);
            send_json('error', 'Database error occurred');
        }
        
        $sel->bind_param('ss', $email, $otp);
        $sel->execute();
        $result = $sel->get_result();
        $row = $result->fetch_assoc();
        $sel->close();
        
        if (!$row) {
            error_log("No OTP found for email: $email, OTP: $otp");
            send_json('error', 'Invalid OTP');
        }
        
        // **FIX: Check OTP status and provide specific error messages**
        if ($row['otp_status'] === 'already_used') {
            error_log("OTP already used for email: $email, OTP: $otp");
            send_json('error', 'This OTP has already been used. Please request a new one.');
        }
        
        if ($row['otp_status'] === 'expired') {
            error_log("Expired OTP for email: $email, OTP: $otp, expired " . abs($row['seconds_remaining']) . " seconds ago");
            send_json('error', 'OTP has expired. Please request a new one.');
        }
        
        if ($row['otp_status'] !== 'valid') {
            error_log("Invalid OTP status for email: $email, status: " . $row['otp_status']);
            send_json('error', 'Invalid or expired OTP');
        }
        
        // Mark OTP as used
        $upd = $conn->prepare("UPDATE password_resets SET used = 1 WHERE id = ?");
        if (!$upd) {
            error_log("Database prepare error: " . $conn->error);
            send_json('error', 'Database error occurred');
        }
        
        $upd->bind_param('i', $row['id']);
        $updateSuccess = $upd->execute();
        $upd->close();
        
        if (!$updateSuccess) {
            error_log("Failed to mark OTP as used for reset ID: " . $row['id']);
            send_json('error', 'Failed to verify OTP');
        }
        
        // Store email in session for password reset page
        $_SESSION['reset_email'] = $email;
        $_SESSION['reset_verified'] = true;
        $_SESSION['reset_time'] = time();
        
        error_log("OTP verified successfully for email: $email, user: " . $row['user_id']);
        
        send_json('ok', 'OTP verified successfully', ['step' => 'otp_verified']);
    }
    
} catch (Exception $e) {
    error_log("Exception in ajax_forgot.php: " . $e->getMessage() . " | " . $e->getTraceAsString());
    send_json('error', 'An unexpected error occurred. Please try again.');
} catch (Error $e) {
    error_log("Fatal error in ajax_forgot.php: " . $e->getMessage() . " | " . $e->getTraceAsString());
    send_json('error', 'A system error occurred. Please try again.');
}
?>

MMCT - 2023