T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/soundstudiopro.com/public_html/api/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/public_html/api/messages.php
<?php
/**
 * Messages API Endpoint
 * Handles all messaging operations: send, get conversations, get messages, mark as read
 */

header('Content-Type: application/json');
session_start();
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../includes/security.php';

// Check authentication
if (!isset($_SESSION['user_id'])) {
    echo json_encode(['success' => false, 'message' => 'Authentication required']);
    exit;
}

$user_id = $_SESSION['user_id'];
$pdo = getDBConnection();

if (!$pdo) {
    echo json_encode(['success' => false, 'message' => 'Database connection failed']);
    exit;
}

// Update last_activity timestamp for online tracking
// This ensures users appear online when actively using the messaging system
try {
    $pdo->exec("ALTER TABLE users ADD COLUMN IF NOT EXISTS last_activity TIMESTAMP NULL DEFAULT NULL");
    $updateStmt = $pdo->prepare("UPDATE users SET last_activity = NOW() WHERE id = ?");
    $updateStmt->execute([$user_id]);
} catch (Exception $e) {
    // Ignore errors - column might not exist yet or update might fail
    error_log("Error updating last_activity in messages API: " . $e->getMessage());
}

// Ensure user_messages table exists
try {
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS user_messages (
            id INT AUTO_INCREMENT PRIMARY KEY,
            sender_id INT NOT NULL,
            receiver_id INT NOT NULL,
            message TEXT NOT NULL,
            is_read BOOLEAN DEFAULT FALSE,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE,
            FOREIGN KEY (receiver_id) REFERENCES users(id) ON DELETE CASCADE,
            INDEX idx_sender (sender_id),
            INDEX idx_receiver (receiver_id),
            INDEX idx_created (created_at),
            INDEX idx_read (is_read)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    ");
} catch (PDOException $e) {
    error_log("Error creating user_messages table: " . $e->getMessage());
}

$method = $_SERVER['REQUEST_METHOD'];
$action = $_GET['action'] ?? $_POST['action'] ?? '';

try {
    switch ($action) {
        case 'get_conversations':
            // Get all conversations for the current user
            $stmt = $pdo->prepare("
                SELECT 
                    CASE 
                        WHEN sender_id = ? THEN receiver_id 
                        ELSE sender_id 
                    END as other_user_id,
                    u.name as other_user_name,
                    u.email as other_user_email,
                    up.profile_image,
                    MAX(um.created_at) as last_message_time,
                    MAX(um.message) as last_message,
                    SUM(CASE WHEN um.receiver_id = ? AND um.is_read = FALSE THEN 1 ELSE 0 END) as unread_count,
                    MAX(CASE WHEN um.sender_id = ? THEN 1 ELSE 0 END) as is_sender
                FROM user_messages um
                JOIN users u ON (
                    CASE 
                        WHEN um.sender_id = ? THEN u.id = um.receiver_id 
                        ELSE u.id = um.sender_id 
                    END
                )
                LEFT JOIN user_profiles up ON u.id = up.user_id
                WHERE um.sender_id = ? OR um.receiver_id = ?
                GROUP BY other_user_id, u.name, u.email, up.profile_image
                ORDER BY last_message_time DESC
            ");
            $stmt->execute([$user_id, $user_id, $user_id, $user_id, $user_id, $user_id]);
            $conversations = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            // Format conversations
            foreach ($conversations as &$conv) {
                $conv['unread_count'] = (int)$conv['unread_count'];
                $conv['is_sender'] = (bool)$conv['is_sender'];
                $conv['last_message'] = html_entity_decode($conv['last_message'] ?? '', ENT_QUOTES | ENT_HTML5, 'UTF-8');
                $conv['last_message'] = mb_substr($conv['last_message'], 0, 100);
                $conv['avatar'] = formatProfileImage($conv['profile_image'], $conv['other_user_name']);
            }
            
            echo json_encode(['success' => true, 'conversations' => $conversations]);
            break;
            
        case 'get_messages':
            // Get messages between current user and another user
            $other_user_id = $_GET['user_id'] ?? null;
            if (!$other_user_id || !is_numeric($other_user_id)) {
                echo json_encode(['success' => false, 'message' => 'Invalid user ID']);
                exit;
            }
            
            $other_user_id = (int)$other_user_id;
            
            // Get messages
            $stmt = $pdo->prepare("
                SELECT 
                    um.*,
                    u.name as sender_name,
                    up.profile_image as sender_image
                FROM user_messages um
                JOIN users u ON um.sender_id = u.id
                LEFT JOIN user_profiles up ON u.id = up.user_id
                WHERE (um.sender_id = ? AND um.receiver_id = ?) 
                   OR (um.sender_id = ? AND um.receiver_id = ?)
                ORDER BY um.created_at ASC
            ");
            $stmt->execute([$user_id, $other_user_id, $other_user_id, $user_id]);
            $messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            // Mark messages as read
            $stmt = $pdo->prepare("
                UPDATE user_messages 
                SET is_read = TRUE 
                WHERE sender_id = ? AND receiver_id = ? AND is_read = FALSE
            ");
            $stmt->execute([$other_user_id, $user_id]);
            
            // Get other user info with online status
            // Calculate online status directly in SQL for better timezone handling
            $stmt = $pdo->prepare("
                SELECT 
                    u.id, 
                    u.name, 
                    u.email, 
                    up.profile_image, 
                    up.bio, 
                    u.last_activity,
                    CASE 
                        WHEN u.last_activity IS NOT NULL 
                             AND u.last_activity >= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
                        THEN 1 
                        ELSE 0 
                    END as is_online
                FROM users u
                LEFT JOIN user_profiles up ON u.id = up.user_id
                WHERE u.id = ?
            ");
            $stmt->execute([$other_user_id]);
            $other_user = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$other_user) {
                echo json_encode(['success' => false, 'message' => 'User not found']);
                exit;
            }
            
            // Use the is_online value calculated by MySQL
            $is_online = (bool)($other_user['is_online'] ?? false);
            
            // Format messages
            foreach ($messages as &$msg) {
                $msg['message'] = html_entity_decode($msg['message'], ENT_QUOTES | ENT_HTML5, 'UTF-8');
                $msg['is_sender'] = (int)$msg['sender_id'] === $user_id;
                $msg['is_read'] = (bool)$msg['is_read'];
                $msg['read_at'] = $msg['is_read'] ? $msg['updated_at'] : null;
                $msg['sender_avatar'] = formatProfileImage($msg['sender_image'], $msg['sender_name']);
            }
            
            echo json_encode([
                'success' => true, 
                'messages' => $messages,
                'other_user' => [
                    'id' => $other_user['id'],
                    'name' => $other_user['name'],
                    'avatar' => formatProfileImage($other_user['profile_image'], $other_user['name']),
                    'is_online' => $is_online
                ]
            ]);
            break;
            
        case 'send_message':
            // Send a new message
            $receiver_id = $_POST['receiver_id'] ?? null;
            $message = trim($_POST['message'] ?? '');
            
            if (!$receiver_id || !is_numeric($receiver_id)) {
                echo json_encode(['success' => false, 'message' => 'Invalid receiver ID']);
                exit;
            }
            
            if (empty($message)) {
                echo json_encode(['success' => false, 'message' => 'Message cannot be empty']);
                exit;
            }
            
            $receiver_id = (int)$receiver_id;
            
            if ($receiver_id === $user_id) {
                echo json_encode(['success' => false, 'message' => 'You cannot send messages to yourself']);
                exit;
            }
            
            // Verify receiver exists
            $stmt = $pdo->prepare("SELECT id FROM users WHERE id = ?");
            $stmt->execute([$receiver_id]);
            if (!$stmt->fetch()) {
                echo json_encode(['success' => false, 'message' => 'User not found']);
                exit;
            }
            
            // Normalize message length while keeping raw text (escaping happens on output)
            $message = mb_substr($message, 0, 5000);
            
            // Insert message
            $stmt = $pdo->prepare("
                INSERT INTO user_messages (sender_id, receiver_id, message) 
                VALUES (?, ?, ?)
            ");
            $stmt->execute([$user_id, $receiver_id, $message]);
            
            $message_id = $pdo->lastInsertId();
            
            // Get the created message
            $stmt = $pdo->prepare("
                SELECT 
                    um.*,
                    u.name as sender_name,
                    up.profile_image as sender_image
                FROM user_messages um
                JOIN users u ON um.sender_id = u.id
                LEFT JOIN user_profiles up ON u.id = up.user_id
                WHERE um.id = ?
            ");
            $stmt->execute([$message_id]);
            $new_message = $stmt->fetch(PDO::FETCH_ASSOC);
            
            $new_message['is_sender'] = true;
            $new_message['is_read'] = false;
            $new_message['message'] = html_entity_decode($new_message['message'], ENT_QUOTES | ENT_HTML5, 'UTF-8');
            $new_message['sender_avatar'] = $new_message['sender_image'] ?: getInitialsAvatar($new_message['sender_name']);
            
            echo json_encode(['success' => true, 'message' => $new_message]);
            break;
            
        case 'mark_read':
            // Mark messages as read
            $other_user_id = $_POST['user_id'] ?? null;
            if (!$other_user_id || !is_numeric($other_user_id)) {
                echo json_encode(['success' => false, 'message' => 'Invalid user ID']);
                exit;
            }
            
            $other_user_id = (int)$other_user_id;
            
            $stmt = $pdo->prepare("
                UPDATE user_messages 
                SET is_read = TRUE 
                WHERE sender_id = ? AND receiver_id = ? AND is_read = FALSE
            ");
            $stmt->execute([$other_user_id, $user_id]);
            
            echo json_encode(['success' => true, 'marked' => $stmt->rowCount()]);
            break;
            
        case 'get_unread_count':
            // Get total unread message count
            $stmt = $pdo->prepare("
                SELECT COUNT(*) as unread_count
                FROM user_messages
                WHERE receiver_id = ? AND is_read = FALSE
            ");
            $stmt->execute([$user_id]);
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            
            echo json_encode(['success' => true, 'unread_count' => (int)$result['unread_count']]);
            break;
            
        case 'search_users':
            // Search for users to message
            $query = trim($_GET['q'] ?? '');
            if (empty($query) || strlen($query) < 2) {
                echo json_encode(['success' => true, 'users' => []]);
                exit;
            }
            
            $search_term = '%' . $query . '%';
            $stmt = $pdo->prepare("
                SELECT 
                    u.id,
                    u.name,
                    u.email,
                    up.profile_image,
                    up.bio
                FROM users u
                LEFT JOIN user_profiles up ON u.id = up.user_id
                WHERE u.id != ? 
                  AND (u.name LIKE ? OR u.email LIKE ?)
                ORDER BY u.name ASC
                LIMIT 20
            ");
            $stmt->execute([$user_id, $search_term, $search_term]);
            $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            foreach ($users as &$user) {
                $user['avatar'] = formatProfileImage($user['profile_image'], $user['name']);
            }
            
            echo json_encode(['success' => true, 'users' => $users]);
            break;
            
        case 'mark_unseen':
            // Mark all messages in a conversation as unread
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Authentication required']);
                exit;
            }
            
            $other_user_id = $_POST['user_id'] ?? null;
            if (!$other_user_id || !is_numeric($other_user_id)) {
                echo json_encode(['success' => false, 'message' => 'Invalid user ID']);
                exit;
            }
            
            $other_user_id = (int)$other_user_id;
            
            if ($other_user_id === $user_id) {
                echo json_encode(['success' => false, 'message' => 'Invalid operation']);
                exit;
            }
            
            // Mark all messages from the other user as unread
            $stmt = $pdo->prepare("
                UPDATE user_messages 
                SET is_read = FALSE 
                WHERE sender_id = ? AND receiver_id = ? AND is_read = TRUE
            ");
            $stmt->execute([$other_user_id, $user_id]);
            
            $marked_count = $stmt->rowCount();
            
            echo json_encode([
                'success' => true, 
                'message' => 'Conversation marked as unread',
                'marked_count' => $marked_count
            ]);
            break;
            
        case 'delete_conversation':
            // Delete all messages in a conversation between current user and another user
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Authentication required']);
                exit;
            }
            
            $other_user_id = $_POST['user_id'] ?? null;
            if (!$other_user_id || !is_numeric($other_user_id)) {
                echo json_encode(['success' => false, 'message' => 'Invalid user ID']);
                exit;
            }
            
            $other_user_id = (int)$other_user_id;
            
            if ($other_user_id === $user_id) {
                echo json_encode(['success' => false, 'message' => 'Invalid operation']);
                exit;
            }
            
            // Delete all messages between the two users
            $stmt = $pdo->prepare("
                DELETE FROM user_messages 
                WHERE (sender_id = ? AND receiver_id = ?) 
                   OR (sender_id = ? AND receiver_id = ?)
            ");
            $stmt->execute([$user_id, $other_user_id, $other_user_id, $user_id]);
            
            $deleted_count = $stmt->rowCount();
            
            echo json_encode([
                'success' => true, 
                'message' => 'Conversation deleted successfully',
                'deleted_count' => $deleted_count
            ]);
            break;
            
        case 'delete_message':
            // Delete a single message (only if user is the sender)
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Authentication required']);
                exit;
            }
            
            $message_id = $_POST['message_id'] ?? null;
            if (!$message_id || !is_numeric($message_id)) {
                echo json_encode(['success' => false, 'message' => 'Invalid message ID']);
                exit;
            }
            
            $message_id = (int)$message_id;
            
            // Verify the message exists and belongs to the current user
            $stmt = $pdo->prepare("
                SELECT id, sender_id 
                FROM user_messages 
                WHERE id = ?
            ");
            $stmt->execute([$message_id]);
            $message = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$message) {
                echo json_encode(['success' => false, 'message' => 'Message not found']);
                exit;
            }
            
            // Only allow deletion if user is the sender
            if ((int)$message['sender_id'] !== $user_id) {
                echo json_encode(['success' => false, 'message' => 'You can only delete your own messages']);
                exit;
            }
            
            // Delete the message
            $stmt = $pdo->prepare("DELETE FROM user_messages WHERE id = ?");
            $stmt->execute([$message_id]);
            
            echo json_encode([
                'success' => true, 
                'message' => 'Message deleted successfully',
                'message_id' => $message_id
            ]);
            break;
            
        case 'forward_message':
            // Forward a message to another user
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Authentication required']);
                exit;
            }
            
            $message_id = $_POST['message_id'] ?? null;
            $receiver_id = $_POST['receiver_id'] ?? null;
            
            if (!$message_id || !is_numeric($message_id)) {
                echo json_encode(['success' => false, 'message' => 'Invalid message ID']);
                exit;
            }
            
            if (!$receiver_id || !is_numeric($receiver_id)) {
                echo json_encode(['success' => false, 'message' => 'Invalid receiver ID']);
                exit;
            }
            
            $message_id = (int)$message_id;
            $receiver_id = (int)$receiver_id;
            
            if ($receiver_id === $user_id) {
                echo json_encode(['success' => false, 'message' => 'You cannot forward messages to yourself']);
                exit;
            }
            
            // Get the original message
            $stmt = $pdo->prepare("
                SELECT message, sender_id 
                FROM user_messages 
                WHERE id = ?
            ");
            $stmt->execute([$message_id]);
            $originalMessage = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$originalMessage) {
                echo json_encode(['success' => false, 'message' => 'Message not found']);
                exit;
            }
            
            // Verify receiver exists
            $stmt = $pdo->prepare("SELECT id, name FROM users WHERE id = ?");
            $stmt->execute([$receiver_id]);
            $receiver = $stmt->fetch(PDO::FETCH_ASSOC);
            if (!$receiver) {
                echo json_encode(['success' => false, 'message' => 'Receiver not found']);
                exit;
            }
            
            // Create forwarded message with prefix
            $forwardedMessage = 'Forwarded: ' . $originalMessage['message'];
            $forwardedMessage = mb_substr($forwardedMessage, 0, 5000);
            
            // Insert forwarded message
            $stmt = $pdo->prepare("
                INSERT INTO user_messages (sender_id, receiver_id, message) 
                VALUES (?, ?, ?)
            ");
            $stmt->execute([$user_id, $receiver_id, $forwardedMessage]);
            
            $new_message_id = $pdo->lastInsertId();
            
            echo json_encode([
                'success' => true, 
                'message' => 'Message forwarded successfully',
                'message_id' => $new_message_id,
                'receiver_name' => $receiver['name']
            ]);
            break;
            
        default:
            echo json_encode(['success' => false, 'message' => 'Invalid action']);
            break;
    }
} catch (PDOException $e) {
    error_log("Messages API error: " . $e->getMessage());
    echo json_encode(['success' => false, 'message' => 'Database error occurred']);
}

// Helper function to format profile image URL
function formatProfileImage($profile_image, $name) {
    // If no profile image, return initials
    if (empty($profile_image)) {
        return getInitialsAvatar($name);
    }
    
    // Trim whitespace
    $profile_image = trim($profile_image);
    
    // If it's already a full URL (http/https), return as is
    if (preg_match('/^https?:\/\//i', $profile_image)) {
        return $profile_image;
    }
    
    // If it starts with /, it's a valid relative path
    if (strpos($profile_image, '/') === 0) {
        return $profile_image;
    }
    
    // If it looks like a file path but doesn't start with /, try to fix it
    // Check if it contains common image extensions
    if (preg_match('/\.(jpg|jpeg|png|gif|webp)$/i', $profile_image)) {
        // If it's in uploads directory, add leading slash
        if (strpos($profile_image, 'uploads/') !== false || strpos($profile_image, 'profile') !== false) {
            // Check if it already has a directory structure
            if (strpos($profile_image, '/') === false) {
                // It's just a filename, assume it's in uploads/profile_images/
                return '/uploads/profile_images/' . $profile_image;
            } else {
                // It has a path but no leading slash
                return '/' . ltrim($profile_image, '/');
            }
        }
        
        // If it starts with a directory name (like 'ds/'), try to construct proper path
        if (preg_match('/^[a-z0-9_]+\//i', $profile_image)) {
            // This might be a malformed path, try to find it in uploads
            $filename = basename($profile_image);
            return '/uploads/profile_images/' . $filename;
        }
    }
    
    // If we can't determine a valid image path, return initials
    return getInitialsAvatar($name);
}

// Helper function to generate avatar from initials
function getInitialsAvatar($name) {
    $initials = '';
    $words = explode(' ', trim($name));
    foreach ($words as $word) {
        if (!empty($word)) {
            $initials .= mb_substr($word, 0, 1);
            if (mb_strlen($initials) >= 2) break;
        }
    }
    return mb_strtoupper($initials ?: '?');
}


CasperSecurity Mini