commit a16dda44dbf166ba65a7131a26419ce2cbde15aa Author: root Date: Sun Nov 10 07:13:49 2024 +0000 first commit diff --git a/dataBase/database-dependencies/changeuserrole.py b/dataBase/database-dependencies/changeuserrole.py new file mode 100644 index 0000000..dc48d59 --- /dev/null +++ b/dataBase/database-dependencies/changeuserrole.py @@ -0,0 +1,42 @@ +import mysql.connector + +def change_user_role(): + # Database connection + conn = mysql.connector.connect( + host="localhost", + user="root", + password="", + database="messaging_app" + ) + cursor = conn.cursor() + + # Show all users + cursor.execute("SELECT username FROM users") + users = cursor.fetchall() + print("Current users:") + for user in users: + print(user[0]) + + print("Roles available: mod, admin") + # Get input from user + username = input("Enter the username: ") + role = input("Enter the role: ") + + # Check if the user exists in the users table + cursor.execute("SELECT * FROM users WHERE username = %s", (username,)) + user = cursor.fetchone() + + if user: + # Insert into admins table + cursor.execute("INSERT INTO admins (username, role) VALUES (%s, %s)", (username, role)) + conn.commit() + print("User role updated successfully.") + else: + print("User does not exist in the users table.") + + # Close the connection + cursor.close() + conn.close() + +if __name__ == "__main__": + change_user_role() \ No newline at end of file diff --git a/dataBase/database-dependencies/clearchat.py b/dataBase/database-dependencies/clearchat.py new file mode 100644 index 0000000..1b7ba64 --- /dev/null +++ b/dataBase/database-dependencies/clearchat.py @@ -0,0 +1,16 @@ +import mysql.connector + + +def connect_to_db(): + conn = mysql.connector.connect( + host="localhost", user="root", password="", database="messaging_app" + ) + return conn + +conn = connect_to_db() +cursor = conn.cursor() + +cursor.execute("DELETE FROM group_messages") +conn.commit() + +print("Deleted Messages") \ No newline at end of file diff --git a/dataBase/database-dependencies/clearprivate.py b/dataBase/database-dependencies/clearprivate.py new file mode 100644 index 0000000..b8a37c9 --- /dev/null +++ b/dataBase/database-dependencies/clearprivate.py @@ -0,0 +1,16 @@ +import mysql.connector + + +def connect_to_db(): + conn = mysql.connector.connect( + host="localhost", user="root", password="", database="messaging_app" + ) + return conn + +conn = connect_to_db() +cursor = conn.cursor() + +cursor.execute("DELETE FROM private_messages") +conn.commit() + +print("Deleted Messages") \ No newline at end of file diff --git a/dataBase/database-dependencies/flushdb.py b/dataBase/database-dependencies/flushdb.py new file mode 100644 index 0000000..81cd20c --- /dev/null +++ b/dataBase/database-dependencies/flushdb.py @@ -0,0 +1,34 @@ +import mysql.connector + + +def connect_to_db(): + conn = mysql.connector.connect( + host="localhost", user="root", password="", database="messaging_app" + ) + return conn + + +def flush_database(): + conn = connect_to_db() + cursor = conn.cursor() + + tables = [ + "group_messages", + "private_messages", + ] + + for table in tables: + try: + cursor.execute(f"TRUNCATE TABLE {table};") + print(f"Flushed table: {table}") + except mysql.connector.Error as err: + print(f"Error flushing table {table}: {err}") + + conn.commit() + cursor.close() + conn.close() + print("All tables flushed successfully!") + + +if __name__ == "__main__": + flush_database() diff --git a/dataBase/database-dependencies/fulldbwipe.py b/dataBase/database-dependencies/fulldbwipe.py new file mode 100644 index 0000000..cfe8a68 --- /dev/null +++ b/dataBase/database-dependencies/fulldbwipe.py @@ -0,0 +1,42 @@ +import mysql.connector + + +def connect_to_db(): + conn = mysql.connector.connect( + host="localhost", user="root", password="", database="messaging_app" + ) + return conn + + +def flush_database(): + conn = connect_to_db() + cursor = conn.cursor() + + try: + cursor.execute("SET FOREIGN_KEY_CHECKS = 0;") + + tables = [ + "group_messages", + "private_messages", + "users", + "admins", + ] + + for table in tables: + cursor.execute(f"TRUNCATE TABLE {table};") + print(f"Flushed table: {table}") + + except mysql.connector.Error as err: + print(f"Error flushing tables: {err}") + + finally: + cursor.execute("SET FOREIGN_KEY_CHECKS = 1;") + + conn.commit() + cursor.close() + conn.close() + print("All tables flushed successfully!") + + +if __name__ == "__main__": + flush_database() diff --git a/dataBase/database-dependencies/makeadmintable.py b/dataBase/database-dependencies/makeadmintable.py new file mode 100644 index 0000000..4b5b6a1 --- /dev/null +++ b/dataBase/database-dependencies/makeadmintable.py @@ -0,0 +1,63 @@ +import mysql.connector +from mysql.connector import errorcode +import json +import os + +# Correct the path to the JSON file +json_path = os.path.join(os.path.dirname(__file__), '..', 'dbinfo.json') + +try: + with open(json_path, "r") as file: + db_info = json.load(file) +except FileNotFoundError: + print(f"File not found: {json_path}") + exit(1) +except json.JSONDecodeError: + print(f"Error decoding JSON from file: {json_path}") + exit(1) + +# Ensure the required keys are in the JSON +required_keys = ["host", "user", "password"] +for key in required_keys: + if key not in db_info: + print(f"Missing required key in JSON: {key}") + exit(1) + +config = { + "host": db_info["host"], + "user": db_info["user"], + "password": db_info["password"], + "database": "messaging_app" +} + +conn = None + +try: + # Connect to the database + conn = mysql.connector.connect(**config) + cursor = conn.cursor() + + # Create the admins table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS admins ( + username VARCHAR(255) PRIMARY KEY, + role ENUM('mod', 'admin') NOT NULL + ) + ''') + + # Commit the changes + conn.commit() + print("Admins table created successfully.") + +except mysql.connector.Error as err: + if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: + print("Something is wrong with your user name or password") + elif err.errno == errorcode.ER_BAD_DB_ERROR: + print("Database does not exist") + else: + print(err) +finally: + # Close the connection + if conn is not None and conn.is_connected(): + cursor.close() + conn.close() \ No newline at end of file diff --git a/dataBase/database-dependencies/populatedb.py b/dataBase/database-dependencies/populatedb.py new file mode 100644 index 0000000..b52c193 --- /dev/null +++ b/dataBase/database-dependencies/populatedb.py @@ -0,0 +1,127 @@ +import mysql.connector +import random +import string + + +def connect_to_db(): + conn = mysql.connector.connect( + host="localhost", user="root", password="", database="messaging_app" + ) + return conn + + +def random_string(length=20): + letters = string.ascii_letters + string.digits + return "".join(random.choice(letters) for i in range(length)) + + +def create_tables(cursor): + cursor.execute(""" + CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS groups ( + id INT AUTO_INCREMENT PRIMARY KEY, + group_name VARCHAR(255) NOT NULL, + created_by INT NOT NULL, + FOREIGN KEY (created_by) REFERENCES users(id) + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS group_messages ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + group_id INT NOT NULL, + message TEXT NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (group_id) REFERENCES groups(id) + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS private_messages ( + id INT AUTO_INCREMENT PRIMARY KEY, + sender_id INT NOT NULL, + receiver_id INT NOT NULL, + message TEXT NOT NULL, + FOREIGN KEY (sender_id) REFERENCES users(id), + FOREIGN KEY (receiver_id) REFERENCES users(id) + ) + """) + + +def create_sample_users(cursor, num_users=10): + for i in range(num_users): + username = f"user{i + 1}" + password = random_string(10) + cursor.execute( + "INSERT INTO users (username, password) VALUES (%s, %s)", + (username, password), + ) + print(f"Created {num_users} sample users.") + + +def populate_database(): + conn = connect_to_db() + cursor = conn.cursor() + + create_tables(cursor) + create_sample_users(cursor) + + cursor.execute("SELECT COUNT(*) FROM users") + user_count = cursor.fetchone()[0] + print(f"Total users in database: {user_count}") + + group_ids = [] + for i in range(1, 11): + group_name = f"Group {i}" + cursor.execute( + "INSERT INTO groups (group_name, created_by) VALUES (%s, %s)", + (group_name, 1), + ) + group_ids.append(cursor.lastrowid) + + print(f"Created groups with IDs: {group_ids}") + + for _ in range(30): + user_id = random.randint(1, user_count) + group_id = random.choice(group_ids) + message = f"Message from {user_id} in group {group_id}: {random_string()}" + try: + cursor.execute( + "INSERT INTO group_messages (user_id, group_id, message) VALUES (%s, %s, %s)", + (user_id, group_id, message), + ) + except mysql.connector.Error as err: + print(f"Error inserting group message: {err}") + + for _ in range(30): + sender_id = random.randint(1, user_count) + receiver_id = random.randint(1, user_count) + while sender_id == receiver_id: + receiver_id = random.randint(1, user_count) + message = ( + f"{random_string()}" + ) + try: + cursor.execute( + "INSERT INTO private_messages (sender_id, receiver_id, message) VALUES (%s, %s, %s)", + (sender_id, receiver_id, message), + ) + except mysql.connector.Error as err: + print(f"Error inserting private message: {err}") + + conn.commit() + cursor.close() + conn.close() + print("Database populated with sample data!") + + +if __name__ == "__main__": + populate_database() diff --git a/dataBase/database-dependencies/updatedb.py b/dataBase/database-dependencies/updatedb.py new file mode 100644 index 0000000..1867b95 --- /dev/null +++ b/dataBase/database-dependencies/updatedb.py @@ -0,0 +1,99 @@ +import json +import mysql.connector +import os +def get_db_info(): + # Correct the path to the JSON file + json_path = os.path.join(os.path.dirname(__file__), '..', 'dbinfo.json') + + try: + with open(json_path, "r") as file: + db_info = json.load(file) + return db_info + except FileNotFoundError: + print(f"File not found: {json_path}") + exit(1) + except json.JSONDecodeError: + print(f"Error decoding JSON from file: {json_path}") + exit(1) + +def connect_to_db(): + db_info = get_db_info() + conn = mysql.connector.connect( + host=db_info["host"], user=db_info["user"], password=db_info["password"], database="messaging_app" + ) + return conn + +def create_database(): + db_info = get_db_info() + conn = mysql.connector.connect( + host=db_info["host"], user=db_info["user"], password=db_info["password"] + ) + cursor = conn.cursor() + cursor.execute("CREATE DATABASE IF NOT EXISTS messaging_app") + conn.commit() + cursor.close() + conn.close() + +def create_tables(): + conn = connect_to_db() + cursor = conn.cursor() + + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """ + ) + + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS group_messages ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + message TEXT NOT NULL, + sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + file_path VARCHAR(255), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ); + """ + ) + + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS private_messages ( + id INT AUTO_INCREMENT PRIMARY KEY, + sender_id INT NOT NULL, + receiver_id INT NOT NULL, + message TEXT NOT NULL, + sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + file_path VARCHAR(255), + FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (receiver_id) REFERENCES users(id) ON DELETE CASCADE + ); + """ + ) + + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS admins ( + username VARCHAR(255) PRIMARY KEY, + role ENUM('mod', 'admin') NOT NULL + ); + """ + ) + + conn.commit() + cursor.close() + conn.close() + +if __name__ == "__main__": + try: + create_database() + create_tables() + print("Database and tables created successfully!") + except FileNotFoundError as e: + print(e) diff --git a/dataBase/dbMasterFile.py b/dataBase/dbMasterFile.py new file mode 100644 index 0000000..dcaea30 --- /dev/null +++ b/dataBase/dbMasterFile.py @@ -0,0 +1,25 @@ +import subprocess +import os + +def run_script(script_path): + if os.path.exists(script_path): + try: + subprocess.run(['python3', script_path], check=True) + print(f"Successfully ran {script_path}") + except subprocess.CalledProcessError as e: + print(f"Error running {script_path}: {e}") + else: + print(f"Script {script_path} does not exist.") + +def main(): + base_dir = os.path.dirname(os.path.abspath(__file__)) + scripts = [ + os.path.join(base_dir, 'database-dependencies', 'updatedb.py'), + os.path.join(base_dir, 'database-dependencies', 'makeadmintable.py') + ] + + for script in scripts: + run_script(script) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dataBase/dbinfo.json b/dataBase/dbinfo.json new file mode 100644 index 0000000..5143b1c --- /dev/null +++ b/dataBase/dbinfo.json @@ -0,0 +1,5 @@ +{ + "host": "localhost", + "user": "root", + "password": "" +} \ No newline at end of file diff --git a/global.php b/global.php new file mode 100644 index 0000000..961d701 --- /dev/null +++ b/global.php @@ -0,0 +1,308 @@ +prepare($sql_check_roles); +$stmt_check_roles->bind_param("s", $username); +$stmt_check_roles->execute(); +$result_check_roles = $stmt_check_roles->get_result(); + +while ($row = $result_check_roles->fetch_assoc()) { + if ($row['role'] == 'admin') { + $is_admin = true; + } elseif ($row['role'] == 'mod') { + $is_mod = true; + } +} + +$stmt_check_roles->close(); + +if ($_SERVER['REQUEST_METHOD'] == 'POST') { + if (isset($_POST['group_message'])) { + $group_message = $_POST['group_message']; + + $stmt = $conn->prepare("INSERT INTO group_messages (user_id, message) VALUES (?, ?)"); + $stmt->bind_param("is", $user_id, $group_message); + + if ($stmt->execute()) { + echo json_encode(['status' => 'success']); + } else { + echo json_encode(['status' => 'error', 'message' => $conn->error]); + } + + $stmt->close(); + exit; + } +} + +$sql_group_messages = "SELECT gm.message, u.username, gm.sent_at + FROM group_messages gm + JOIN users u ON gm.user_id = u.id + ORDER BY gm.sent_at DESC"; +$result_group_messages = $conn->query($sql_group_messages); +?> + + + + + + Group Messages + + + + +
+

Welcome, !

+ +

Public Chat

+ + + +
+ + + +
+ +
+ + + + + + + + + + + + + + + + + + + + diff --git a/index.php b/index.php new file mode 100644 index 0000000..e6e00a6 --- /dev/null +++ b/index.php @@ -0,0 +1,3 @@ + { + const newMessage = document.createElement("li"); + newMessage.innerHTML = msg; + messageList.insertBefore(newMessage, messageList.firstChild); + }); + } + }; + xhr.send(); +} + +setInterval(fetchMessages, 1000); \ No newline at end of file diff --git a/js/login.js b/js/login.js new file mode 100644 index 0000000..e37acc6 --- /dev/null +++ b/js/login.js @@ -0,0 +1,30 @@ +document.addEventListener('DOMContentLoaded', function() { + const submitButton = document.querySelector('#submit-login'); + + submitButton.addEventListener('click', (event) => { + event.preventDefault(); // Prevent form submission + + let username = document.querySelector('#username').value; + let password = document.querySelector('#password').value; + + if (!username.includes(' ')) { + fetch('./php/login.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}` + }) + .then(response => response.text()) + .then(data => { + if (data.includes('Invalid credentials!') || data.includes('User not found!')) { + alert(data); + } else { + window.location.href = './global.php'; + } + }) + .catch(error => console.error('Error:', error)); + }; + }); + console.log('DOM fully loaded and parsed'); +}); \ No newline at end of file diff --git a/js/private.js b/js/private.js new file mode 100644 index 0000000..55b4393 --- /dev/null +++ b/js/private.js @@ -0,0 +1,83 @@ +document.addEventListener('DOMContentLoaded', function() { + const messageForm = document.getElementById('privateMessageForm'); + const messageInput = messageForm.querySelector('textarea[name="private_message"]'); + const receiverIdInput = messageForm.querySelector('input[name="receiver_id"]'); + const fileInput = messageForm.querySelector('input[name="file"]'); + const userButtons = document.querySelectorAll('button[name="select_user"]'); + let lastTimestamp = '1970-01-01 00:00:00'; + + userButtons.forEach(button => { + button.addEventListener('click', function(event) { + event.preventDefault(); + const selectedUserId = this.value; + receiverIdInput.value = selectedUserId; + lastTimestamp = '1970-01-01 00:00:00'; // Reset timestamp when a new user is selected + fetchPrivateMessages(selectedUserId); + startMessagePolling(selectedUserId); + }); + }); + + messageForm.addEventListener('submit', function(event) { + event.preventDefault(); + + const formData = new FormData(this); + console.log('Sending data:', Object.fromEntries(formData)); + + fetch('./php/send_private_message.php', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + console.log('Response:', data); + if (data.status === 'success') { + fetchPrivateMessages(receiverIdInput.value); + messageInput.value = ''; + fileInput.value = ''; + } else { + alert(data.message); + } + }) + .catch(error => { + console.error('Error:', error); + }); + }); + + messageInput.addEventListener('keypress', function(event) { + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); + messageForm.dispatchEvent(new Event('submit')); + } + }); + + function fetchPrivateMessages(selectedUserId) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", `./php/fetch_private_messages.php?user_id=${selectedUserId}&last_timestamp=${encodeURIComponent(lastTimestamp)}`, true); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + const response = JSON.parse(xhr.responseText); + const messages = response.messages; + lastTimestamp = response.latest_timestamp; + + const messageList = document.getElementById("messageList"); + + messages.forEach(msg => { + const newMessage = document.createElement("li"); + newMessage.innerHTML = msg; + messageList.appendChild(newMessage); + }); + messageList.scrollTop = messageList.scrollHeight; + } + }; + xhr.send(); + } + + function startMessagePolling(selectedUserId) { + if (window.messagePollingInterval) { + clearInterval(window.messagePollingInterval); + } + window.messagePollingInterval = setInterval(() => { + fetchPrivateMessages(selectedUserId); + }, 1000); // Poll every 1 second + } +}); \ No newline at end of file diff --git a/js/pushChatScroll.js b/js/pushChatScroll.js new file mode 100644 index 0000000..2b2447d --- /dev/null +++ b/js/pushChatScroll.js @@ -0,0 +1,13 @@ +document.addEventListener('DOMContentLoaded', function() { + const chat = document.querySelector('#messageList'); + if (chat) { + const observer = new MutationObserver(function() { + chat.scrollTop = chat.scrollHeight; + }); + + observer.observe(chat, { childList: true }); + + // Initial scroll to bottom in case the list is already populated + chat.scrollTop = chat.scrollHeight; + } +}); \ No newline at end of file diff --git a/js/register.js b/js/register.js new file mode 100644 index 0000000..d0ab02e --- /dev/null +++ b/js/register.js @@ -0,0 +1,34 @@ +document.addEventListener('DOMContentLoaded', function() { + const submitButton = document.querySelector('#submit-register'); + + submitButton.addEventListener('click', (event) => { + event.preventDefault(); // Prevent form submission + + let username = document.querySelector('#username').value; + let email = document.querySelector('#email').value; // Capture email + let password = document.querySelector('#password').value; + let confirm_password = document.querySelector('#confirm-password').value; + + if (password === confirm_password) { + fetch('./php/register.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: `username=${encodeURIComponent(username)}&email=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}` + }) + .then(response => response.text()) + .then(data => { + if (data.includes('Invalid credentials!') || data.includes('User not found!')) { + alert(data); + } else { + window.location.href = './global.php'; + } + }) + .catch(error => console.error('Error:', error)); + } else { + alert('Incorrect Credentials', 'Please enter the right details.'); + } + }); + console.log('DOM fully loaded and parsed'); +}); \ No newline at end of file diff --git a/js/sendGlobal.js b/js/sendGlobal.js new file mode 100644 index 0000000..1c184d9 --- /dev/null +++ b/js/sendGlobal.js @@ -0,0 +1,59 @@ +document.getElementById("sendButton").addEventListener("click", sendMessage); +document + .getElementById("messageInput") + .addEventListener("keydown", function (event) { + if (event.key === "Enter") { + sendMessage(); + } + }); + +function sendMessage() { + const chat = document.querySelector('#messageList'); + if (chat) { + const observer = new MutationObserver(function() { + chat.scrollTop = chat.scrollHeight; + }); + + observer.observe(chat, { childList: true }); + + // Initial scroll to bottom in case the list is already populated + chat.scrollTop = chat.scrollHeight; + } + + var message = document.getElementById("messageInput").value.trim(); + var fileInput = document.getElementById("fileInput").files[0]; + + // Basic validation + if (!message && !fileInput) { + alert("Please enter a message or select a file."); + return; + } + + var formData = new FormData(); + formData.append("group_message", message); + + if (fileInput) { + formData.append("file", fileInput); + } + + var xhr = new XMLHttpRequest(); + xhr.open("POST", "./php/sendGlobal.php", true); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + var response = JSON.parse(xhr.responseText); + if (response.status === "success") { + document.getElementById("messageInput").value = ""; + document.getElementById("fileInput").value = ""; + } else { + alert("Error: " + response.message); + } + } else { + alert("Error: Unable to send message."); + } + } + }; + + xhr.send(formData); +} diff --git a/js/themeModal.js b/js/themeModal.js new file mode 100644 index 0000000..1e0abc8 --- /dev/null +++ b/js/themeModal.js @@ -0,0 +1,43 @@ +const button = document.querySelector('.settings'); + +button.addEventListener('click', () => { + const modal = document.createElement('div'); + modal.classList.add('settingsModal'); + + modal.innerHTML = ` +
+ + + + + + + + + + + + + + + + +
+ `; + + document.body.appendChild(modal); + + document.getElementById('applyTheme').addEventListener('click', () => { + const background = document.getElementById('background').value; + const sidebar = document.getElementById('sidebar').value; + const buttons = document.getElementById('buttons').value; + const mention = document.getElementById('mention').value; + const mentionborder = document.getElementById('mentionborder').value; + + document.body.style.backgroundColor = background; + document.querySelector('.sidebar').style.backgroundColor = sidebar; + document.querySelectorAll('input[type=button]').forEach(button => button.style.backgroundColor = buttons); + document.querySelectorAll('.mention').forEach(mention => mention.style.backgroundColor = mention); + document.querySelectorAll('.highlight').forEach(mention => mention.style.borderColor = mentionborder); + }); +}); diff --git a/login.php b/login.php new file mode 100644 index 0000000..d3095a9 --- /dev/null +++ b/login.php @@ -0,0 +1,30 @@ + + + + + + Strife | Login + + + +
+

Welcome to Strife!

+

Please enter your details to log in.

+
+
+ + + + + + + +
+
+

Don't have an account?

+

Create one here!

+ Register +
+ + + diff --git a/php/db.php b/php/db.php new file mode 100644 index 0000000..13c8175 --- /dev/null +++ b/php/db.php @@ -0,0 +1,12 @@ +connect_error) { + die("Connection failed: " . $conn->connect_error); +} + diff --git a/php/fetchMessages.php b/php/fetchMessages.php new file mode 100644 index 0000000..d76717e --- /dev/null +++ b/php/fetchMessages.php @@ -0,0 +1,98 @@ + ? + ORDER BY gm.sent_at DESC + LIMIT 25"; + + $stmt = $conn->prepare($sql_group_messages); + $stmt->bind_param("s", $last_timestamp); + $stmt->execute(); + $result_group_messages = $stmt->get_result(); + + if ($result_group_messages->num_rows > 0) { + $messages = []; + $latest_timestamp = ''; + + $sql_users = "SELECT username FROM users"; + $user_result = $conn->query($sql_users); + $users = []; + while ($user = $user_result->fetch_assoc()) { + $users[$user['username']] = true; + } + + while ($message = $result_group_messages->fetch_assoc()) { + $message_text = htmlspecialchars($message['message']); + $highlight_class = ''; + + preg_match_all('/@(\w+)/', $message['message'], $mentions); + $unique_mentions = array_unique($mentions[1]); + foreach ($unique_mentions as $mention) { + if (isset($users[$mention])) { + $message_text = preg_replace('/@' . preg_quote($mention, '/') . '/', "@$mention", $message_text, 1); + if ($mention === $current_user) { + $highlight_class = 'highlight'; + } + } + } + + $message_text = preg_replace('/(https?:\/\/[^\s]+)/', '$1', $message_text); + $message_text = preg_replace('/\b(www\.[^\s]+)/', '$1', $message_text); + + $output = "" . htmlspecialchars($message['username']) . ": " . $message_text; + + if (!empty($message['file_path'])) { + $path = '/projects/strife/uploads/'; + $full_file_path = $_SERVER['DOCUMENT_ROOT'] . $path . $message['file_path']; + + if (file_exists($full_file_path)) { + $file_type = mime_content_type($full_file_path); + + if (strpos($file_type, 'image') !== false) { + $output .= "
image"; + } elseif (strpos($file_type, 'audio') !== false) { + $output .= "
"; + } elseif (strpos($file_type, 'video') !== false) { + $output .= "
"; + } else { + $output .= "
Unsupported file type: " . htmlspecialchars($file_type); + } + } else { + $output .= "
File does not exist."; + } + } + + $output .= " (" . htmlspecialchars($message['sent_at']) . ")
"; + $messages[] = $output; + + if ($latest_timestamp === '' || $message['sent_at'] > $latest_timestamp) { + $latest_timestamp = $message['sent_at']; + } + } + + header('Content-Type: application/json'); + echo json_encode(['messages' => array_reverse($messages), 'latest_timestamp' => $latest_timestamp]); + exit; + } + + if (time() - $start_time >= $timeout) { + break; + } + + usleep(5000); +} + +header('Content-Type: application/json'); +echo json_encode(['messages' => [], 'latest_timestamp' => $last_timestamp]); diff --git a/php/fetch_private_messages.php b/php/fetch_private_messages.php new file mode 100644 index 0000000..e6aa00c --- /dev/null +++ b/php/fetch_private_messages.php @@ -0,0 +1,97 @@ + "error", "message" => "You must be logged in."]); + exit; +} + +$user_id = $_SESSION['user_id']; +$selected_user_id = isset($_GET['user_id']) ? intval($_GET['user_id']) : 0; +$last_timestamp = isset($_GET['last_timestamp']) ? $_GET['last_timestamp'] : '1970-01-01 00:00:00'; + +$timeout = 10; // Timeout in seconds +$start_time = time(); + +while (true) { + $sql_private_messages = " + SELECT pm.message, u.username AS sender, UNIX_TIMESTAMP(pm.sent_at) AS sent_at, pm.file_path + FROM private_messages pm + JOIN users u ON pm.sender_id = u.id + WHERE ((pm.sender_id = ? AND pm.receiver_id = ?) + OR (pm.sender_id = ? AND pm.receiver_id = ?)) + AND pm.sent_at > ? + ORDER BY pm.sent_at ASC"; + + $stmt_private_messages = $conn->prepare($sql_private_messages); + $stmt_private_messages->bind_param("iiiis", $user_id, $selected_user_id, $selected_user_id, $user_id, $last_timestamp); + $stmt_private_messages->execute(); + $result_private_messages = $stmt_private_messages->get_result(); + + if ($result_private_messages->num_rows > 0) { + $private_messages = []; + $latest_timestamp = $last_timestamp; + + while ($row = $result_private_messages->fetch_assoc()) { + $row['message'] = decryptMessage($row['message'], DECRYPTION_KEY); + + $message_text = htmlspecialchars($row['message']); + $output = "
  • " . htmlspecialchars($row['sender']) . ": " . $message_text; + + if (!empty($row['file_path'])) { + $path = '/projects/strife/uploads/'; + $full_file_path = $_SERVER['DOCUMENT_ROOT'] . $path . $row['file_path']; + + if (file_exists($full_file_path)) { + $file_type = mime_content_type($full_file_path); + + if (strpos($file_type, 'image') !== false) { + $output .= "
    image"; + } elseif (strpos($file_type, 'audio') !== false) { + $output .= "
    "; + } elseif (strpos($file_type, 'video') !== false) { + $output .= "
    "; + } else { + $output .= "
    Unsupported file type: " . htmlspecialchars($file_type); + } + } else { + $output .= "
    File does not exist."; + } + } + + $output .= " (" . date('Y-m-d H:i:s', $row['sent_at']) . ")
  • "; + $private_messages[] = $output; + + if ($row['sent_at'] > $latest_timestamp) { + $latest_timestamp = date('Y-m-d H:i:s', $row['sent_at']); + } + } + + header('Content-Type: application/json'); + echo json_encode(['messages' => $private_messages, 'latest_timestamp' => $latest_timestamp]); + $stmt_private_messages->close(); + $conn->close(); + exit; + } + + if (time() - $start_time >= $timeout) { + break; + } + + usleep(500000); // Sleep for 0.5 seconds +} + +header('Content-Type: application/json'); +echo json_encode(['messages' => [], 'latest_timestamp' => $last_timestamp]); +$stmt_private_messages->close(); +$conn->close(); \ No newline at end of file diff --git a/php/login.php b/php/login.php new file mode 100644 index 0000000..764540e --- /dev/null +++ b/php/login.php @@ -0,0 +1,33 @@ +prepare($sql); + $stmt->bind_param("s", $username); + $stmt->execute(); + $result = $stmt->get_result(); + + if ($result->num_rows > 0) { + $user = $result->fetch_assoc(); + + if (password_verify($password, $user['password'])) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + header("Location: home.php"); + exit; + } else { + echo "Invalid credentials!"; + } + } else { + echo "User not found!"; + } + } else { + echo "Database connection error!"; + } +} diff --git a/php/logout.php b/php/logout.php new file mode 100644 index 0000000..1d76c6b --- /dev/null +++ b/php/logout.php @@ -0,0 +1,5 @@ +prepare($check_sql); + $stmt->bind_param("ss", $username, $email); + $stmt->execute(); + $result = $stmt->get_result(); + + if ($result->num_rows > 0) { + echo "Username or email already exists. Please choose a different one."; + } else { + $sql = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)"; + $stmt = $conn->prepare($sql); + $stmt->bind_param("sss", $username, $email, $password); + + if ($stmt->execute() === TRUE) { + echo "Registration successful! Login here"; + } else { + echo "Error: " . $conn->error; + } + } + + $stmt->close(); +} +$conn->close(); +?> \ No newline at end of file diff --git a/php/sendGlobal.php b/php/sendGlobal.php new file mode 100644 index 0000000..6fb47eb --- /dev/null +++ b/php/sendGlobal.php @@ -0,0 +1,59 @@ + "error", "message" => "You must be logged in to send a message."]); + exit; + } + + $user_id = $_SESSION['user_id']; + $message = isset($_POST['group_message']) ? trim($_POST['group_message']) : ''; + + $file_path = null; + + if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) { + $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'audio/mpeg', 'video/mp4', 'audio/mp4']; + $file_type = $_FILES['file']['type']; + + if (!in_array($file_type, $allowed_types)) { + echo json_encode(["status" => "error", "message" => "File type not allowed."]); + exit; + } + + $upload_dir = '../uploads/'; + if (!is_dir($upload_dir)) { + if (!mkdir($upload_dir, 0777, true) && !is_dir($upload_dir)) { + echo json_encode(["status" => "error", "message" => "Failed to create upload directory."]); + exit; + } + } + + $file_name = basename($_FILES['file']['name']); + $file_path = $upload_dir . uniqid() . "_" . $file_name; + + if (!move_uploaded_file($_FILES['file']['tmp_name'], $file_path)) { + echo json_encode(["status" => "error", "message" => "File upload failed."]); + exit; + } + } + + $stmt = $conn->prepare("INSERT INTO group_messages (user_id, message, file_path) VALUES (?, ?, ?)"); + if ($stmt === false) { + echo json_encode(["status" => "error", "message" => "Error preparing the statement: {$conn->error}"]); + exit; + } + + $stmt->bind_param("iss", $user_id, $message, $file_path); + + if ($stmt->execute()) { + echo json_encode(["status" => "success", "message" => "Message sent successfully!"]); + } else { + echo json_encode(["status" => "error", "message" => "Error executing query: {$stmt->error}"]); + error_log("Database Insert Error: {$stmt->error}"); + } + + $stmt->close(); + $conn->close(); +} diff --git a/php/send_private_message.php b/php/send_private_message.php new file mode 100644 index 0000000..01dd8d7 --- /dev/null +++ b/php/send_private_message.php @@ -0,0 +1,86 @@ + "error", "message" => "You must be logged in to send a message."]); + exit; + } + + $user_id = $_SESSION['user_id']; + $receiver_id = isset($_POST['receiver_id']) ? intval($_POST['receiver_id']) : 0; + $message = isset($_POST['private_message']) ? trim($_POST['private_message']) : ''; + + if (empty($message) || empty($receiver_id)) { + echo json_encode(["status" => "error", "message" => "Message or receiver ID is missing."]); + exit; + } + + $file_path = null; + + if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) { + $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'audio/mpeg', 'video/mp4', 'audio/mp4']; + $file_type = $_FILES['file']['type']; + + if (!in_array($file_type, $allowed_types)) { + echo json_encode(["status" => "error", "message" => "File type not allowed."]); + exit; + } + + $upload_dir = '../uploads/'; + if (!is_dir($upload_dir)) { + if (!mkdir($upload_dir, 0777, true) && !is_dir($upload_dir)) { + echo json_encode(["status" => "error", "message" => "Failed to create upload directory."]); + exit; + } + } + + $file_name = basename($_FILES['file']['name']); + $file_path = $upload_dir . uniqid() . "_" . $file_name; + + if (!move_uploaded_file($_FILES['file']['tmp_name'], $file_path)) { + echo json_encode(["status" => "error", "message" => "File upload failed."]); + exit; + } + } + + // Encrypt the message + $encrypted_message = encryptMessage($message, ENCRYPTION_KEY); + + $stmt = $conn->prepare("INSERT INTO private_messages (sender_id, receiver_id, message, file_path) VALUES (?, ?, ?, ?)"); + + if ($stmt === false) { + echo json_encode(["status" => "error", "message" => "Error preparing the statement: {$conn->error}"]); + exit; + } + + $stmt->bind_param("iiss", $user_id, $receiver_id, $encrypted_message, $file_path); + + if ($stmt->execute()) { + echo json_encode(["status" => "success", "message" => "Message sent successfully!"]); + } else { + echo json_encode(["status" => "error", "message" => "Error executing query: {$stmt->error}"]); + error_log("Database Insert Error: {$stmt->error}"); + } + + $stmt->close(); + $conn->close(); +} else { + echo json_encode(["status" => "error", "message" => "Invalid request method."]); +} diff --git a/private.php b/private.php new file mode 100644 index 0000000..8db1388 --- /dev/null +++ b/private.php @@ -0,0 +1,65 @@ +prepare($sql_users); +$stmt_users->bind_param("i", $user_id); +$stmt_users->execute(); +$result_users = $stmt_users->get_result(); + +$stmt_users->close(); +?> + + + + + + Private Messages + + + + + +
    +

    Welcome, !

    + +
    +

    Your Private Messages

    +
      + +
    +
    + + + + +
    +
    + + +
    + + \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..dc33525 --- /dev/null +++ b/readme.md @@ -0,0 +1,20 @@ +# Strife - PHP Messaging App + +Welcome to Strife, a PHP-based messaging application. + +## Installation + +To get started with Strife, follow these steps: + +1. Install the required Python packages: + ```bash + pip3 install -r requirements.txt + ``` + +2. Set up the database: + ```bash + python3 ./database/dbMasterFile.py + ``` + +You're all set! Enjoy using Strife. + diff --git a/register.php b/register.php new file mode 100644 index 0000000..a61d63d --- /dev/null +++ b/register.php @@ -0,0 +1,36 @@ + + + + + + Strife | Register + + + +
    +

    Welcome to Strife!

    +

    Please enter your details to register.

    +
    +
    + + + + + + + + + + + + + +
    +
    +

    Already have an account?

    +

    Log in below!

    + Login +
    + + + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5bb5574 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +mysql-connector-python \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..670b933 --- /dev/null +++ b/style.css @@ -0,0 +1,408 @@ +/* General Reset */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Comic Sans MS', cursive; +} + +::-webkit-scrollbar { + width: 0; +} + +html { + color-scheme: dark; +} + +/* Body Styling */ +body { + display: flex; + flex-direction: row; + height: 100vh; + background-color: #2f3136; /* Discord dark background */ + color: #ffffff; /* White text */ + padding: 0; + font-family: 'Comic Sans MS', cursive; +} + +/* Shared Section Styling for Login and Register */ +#login, +#register { + background-color: #202225; /* Slightly darker background */ + padding: 40px 40px; + border-radius: 10px; + max-width: 400px; + width: 100%; + text-align: center; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.8); + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +h2 { + color: #ffffff; + margin-bottom: 24px; + font-size: 24px; +} + +p { + color: #b9bbbe; /* Light gray for instructions */ + margin-bottom: 10px; + font-size: 16px; +} + +hr { + border: 0; + border-top: 1px solid #4f545c; + margin: 20px 0; +} + +h3 { + color: #b9bbbe; + margin: 16px 0 10px; + font-size: 16px; +} + +a { + color: #5865f2; /* Discord blue */ + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +/* Form Styling */ +form { + display: flex; + flex-direction: column; + gap: 15px; +} + +input[type="text"], +input[type="password"] { + padding: 12px 15px; + background-color: #40444b; + color: #dcddde; + border: 1px solid #4f545c; + border-radius: 5px; + outline: none; + font-size: 14px; + transition: border-color 0.2s ease; + width: 100%; +} + +input[type="text"]:focus, +input[type="password"]:focus { + border-color: #5865f2; +} + +button { + padding: 12px 20px; + background-color: #5865f2; /* Discord blue */ + color: #ffffff; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s ease; + width: 100%; +} + +button:hover { + background-color: #4752c4; +} + +button:active { + background-color: #3b44a1; +} + +/* Add Responsiveness for smaller screens */ +@media screen and (max-width: 1024px) { + #login, + #register { + width: 90%; + padding: 20px; + } +} + +h2 { + margin-bottom: 16px; +} + +/* Add Responsiveness for smaller screens */ +@media screen and (max-width: 1024px) { + .sidebar { + width: 60px; /* Smaller sidebar on mobile */ + padding: 10px 0; + } +} + +/* User Sidebar */ +.user-sidebar { + position: fixed; + right: 0; + width: 250px; + background-color: #202225; + padding: 20px; + box-shadow: -2px 0 10px rgba(0, 0, 0, 0.6); + height: 100vh; + display: flex; + flex-direction: column; + gap: 10px; +} + +.user-sidebar h2 { + color: white; +} + +.user-sidebar p { + color: #b9bbbe; + font-size: 14px; +} + +.user-sidebar img { + width: 60px; + height: 60px; + border-radius: 50%; +} + +#adminModal, +#modModal { + padding: 16px; + border-radius: 0.5rem; + background-color: #202225; + position: absolute; + right: 16px; + top: 16px; + z-index: +1; +} + +#adminModal input, +#modModal input { + padding: 10px 20px; + background-color: #5865f2; /* Discord blue */ + border: none; + color: #ffffff; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s ease; +} + +.settings { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + top: 8px; + left: 8px; + background-color: unset; + padding: unset; + border-radius: unset; + width: unset; +} + +.settings svg { + height: 32px; + width: 32px; +} + +.settings:hover { + background-color: unset; +} + +/* Additional Styling for Logout and other elements */ +p { + margin-top: 10px; + text-align: center; +} + +p a { + color: #5865f2; + text-decoration: none; +} + +p a:hover { + text-decoration: underline; +} + +.settingsModal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1; + background-color: #202225; + padding: 16px; + border-radius: 0.5rem; +} + +/* Left Sidebar */ +.sidebar { + width: 240px; + background-color: #202225; /* Slightly darker gray */ + padding: 20px 10px; + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 10px; + box-shadow: 2px 0 10px rgba(0, 0, 0, 0.6); + position: fixed; + height: 100%; + left: 0; +} + +.sidebar a { + color: #b9bbbe; /* Light gray links */ + text-decoration: none; + display: block; + padding: 10px 15px; + border-radius: 4px; + transition: background-color 0.2s ease; +} + +.sidebar a:hover { + background-color: #3a3d42; +} + +/* Main Chat Area */ +.container { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: flex-end; + padding: 20px; + padding-left: 270px; /* Space for the sidebar */ + height: 100vh; + overflow: hidden; + position: relative; +} + +.highlight { + border: 2px solid orange; +} + +.mention { + color: #5865f2; + font-weight: bold; + padding: unset; +} + +/* Chat Messages */ +#messageList { + display: flex; + flex-direction: column-reverse; + gap: 10px; + overflow-y: auto; + max-height: 70vh; +} + +#messageList li span:not(.mention) { + padding: 12px; + border-radius: 10px; + background-color: #40444b; /* Dark gray background for messages */ + margin-bottom: 5px; + word-wrap: break-word; + max-width: 60%; + display: inline-block; + transition: background-color 0.3s ease; + font-family: 'Comic Sans MS', cursive; +} + +#messageList li.user { + background-color: #5865f2; /* Blue color for user messages */ + color: white; + align-self: flex-end; +} + +#messageList li span:hover { + background-color: #3a3d42; +} + +#messageList li b { + font-weight: bold; +} + +/* Message Timestamps */ +#messageList i { + font-size: 12px; + color: #6e737a; /* Lighter gray for timestamps */ +} + +/* Textarea for message input */ +#messageForm { + margin-top: 16px; + display: flex; + gap: 10px; + align-items: flex-end; + flex-direction: unset; +} + +textarea { + height: 36px; + padding: 10px; + background-color: #40444b; + color: #dcddde; + border: 1px solid #4f545c; + border-radius: 5px; + resize: none; + width: 80%; + font-size: 14px; + outline: none; + transition: border-color 0.2s ease; +} + +textarea:focus { + border-color: #5865f2; /* Blue outline on focus */ +} + +input[type="button"] { + padding: 10px 20px; + background-color: #5865f2; /* Discord blue */ + border: none; + color: #ffffff; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s ease; + width: 15%; +} + +input[type="button"]:hover { + background-color: #4752c4; +} + +input[type="button"]:active { + background-color: #3b44a1; +} + +input[type="button"]:disabled { + background-color: #202225; + cursor: not-allowed; +} + +/* file upload button */ +input[type="file"]::file-selector-button { + padding: 10px 20px; + background-color: #5865f2; /* Discord blue */ + border: none; + color: #ffffff; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s ease; + margin-right: 32px; +} + +/* file upload button hover state */ +input[type="file"]::file-selector-button:hover { + background-color: #4752c4; +} + +/* file upload button active state */ +input[type="file"]::file-selector-button:active { + background-color: #3b44a1; +} \ No newline at end of file