QMessageServer is a chat server application that serves as both a WebSocket and HTTP/HTTPS server. The application provides access to a simple chat server with a login/register system and end-to-end encryption on the client-side. It is built using the Qt framework.
- WebSocket chat server
- HTTP and HTTPS server for user interface
- User registration and login system
- End-to-End Encryption
- Server-side SSL configuration
- Qt 5.15 or later
Clone the repository:
git clone https://github.com/fibur/QMessageServer.git
cd QMessageServer
Build the project using qmake and make:
mkdir build
cd build
cmake ..
cmake --build .
After building the project, you can start the server by executing the built binary. The server supports command-line options to configure different aspects such as ports, server IP, and SSL certificates:
./QMessageServer -chatServerPort 12345 -httpServerPort 8080 -httpsServerPort 8443 -serverIp localhost -sslCertificate path/to/certificate -sslPrivateKey path/to/privatekey
-chatServerPort
,-csp
,-cp
: Set the chat server port (default: 12345).-httpServerPort
,-httpPort
,-hp
: Set the HTTP server port (default: 8080).-httpsServerPort
,-httpsPort
,-hsp
: Set the HTTPS server port (default: 8443).-serverIp
,-ip
: Set the server IP address (default: localhost).-sslCertificate
,-sslcert
: Set the server's SSL certificate file path.-sslPrivateKey
,-sslprvkey
: Set the server's SSL private key file path.
Server loads HTML dynamically, from {workinkg-directory}
/html folder. You have to provide frontend by your own, or use content from the exampleHTML
folder, which provides full functionality, with simple UI. If you want to create it by your own, then below you can find basic informations about communication workflow.
The UserManager
class is responsible for managing the users of the chat application. This class takes care of creating, loading, saving, authorizing, and de-authorizing users. It also handles user authentication.
When a UserManager
object is created, it connects to an SQLite database (users.db
), and if the database doesn't exist, it creates one. It also creates a table named users
if it doesn't already exist.
The loadUsers
method is responsible for loading the users from the database into memory.
The saveUser
method saves a new user to the database if it does not exist already.
The authenticateUser
method checks if a user with the given name and password exists in memory.
UserManager
also handles user authorization by generating a unique token for an authenticated user and setting the last active time.
It also deauthorizes inactive users by setting their token to an empty string and their socket to null.
Unique IDs are generated for new users with generateUniqueID
.
Passwords are hashed using SHA-256 before being stored in the database.
Users can be looked up by their token, name, or ID using the respective methods.
The class provides a method to get a list of currently active users.
This document describes the communication process with a WebSocket server which uses the provided QWebSocket message handling method.
- Client sends a JSON request to the server via WebSocket.
- Server processes the request and sends back a JSON response.
The request sent by the client should be a JSON object containing at least an action
field. The action
field should be an integer corresponding to the type of request.
{
"action": 1,
"name": "john_doe",
"password": "password123"
}
The action
field in the request can have one of the following values:
0
orRequests.LoginRequest
: Login Request1
orRequests.RegisterRequest
: Register Request2
orRequests.LogoutRequest
: Logout Request3
orRequests.MessageRequest
: Message Request4
orRequests.AuthorizeRequest
: Authorize Request
The server utilizes an internal enum, HttpServer::Requests
, to map these values to the request types.
Client sends their username and password to login.
Fields:
action
: 0name
: usernamepassword
: password
Client sends their desired username and password to register a new account.
Fields:
action
: 1name
: desired usernamepassword
: desired password
Client sends a request to log out.
Fields:
action
: 2token
: authentication token
Client sends a message to another user.
Fields:
action
: 3token
: authentication tokentarget
: recipient user IDmessage
: message content
Client sends a token to authorize itself.
Fields:
action
: 4token
: authentication token
The server responds with a JSON object. The object always contains a valid
field which indicates whether the request was processed successfully or not.
Example response JSON for successful login:
{
"valid": true,
"event": 1, // HttpServer::Responses::LoginEvent or Responses.LoginEvent from enums.js
"token": "auth_token",
"username": "john_doe",
"users": [ /* list of users */ ]
}
Example response JSON for failed login:
{
"valid": false,
"error": "User or password is invalid."
}
If an error occurs during the processing of the request, the response JSON will contain a valid
field set to false
, and an error
field containing the error message.
The connection may be closed by the server if a user logs out or if a user logs in from another device. The client should be prepared to handle the connection being closed by the server.
All server files are provided dynamically from html folder from working dir. Additionally, server provides virtual file named enum.js
in root location, providing all needed enum values from C++ dynamically to avoid usage of magic numbers.
The HTTP server processes JavaScript files and replaces the following placeholders with actual values:
%SERVER_ADDRESS%
: Replaced with the WebSocket chat server's address.%SERVER_PORT%
: Replaced with the WebSocket chat server's port.%SERVER_PROTOCOL%
: Replaced with eitherws
(WebSocket) orwss
(WebSocket Secure) based on the server's configuration.
The HTTP server provides a list of action type enumerations at the URL http(s)://<address>/enums.js
. This is a static constant list of values behaving as an enum, providing all message action types.
Example usage:
// Load the enums from the server
fetch('http(s)://<address>/enums.js')
.then(response => response.json())
.then(enums => {
console.log(enums); // Logs the list of action types
});
Folder exampleHTML
contains fully functional HTML content example, which is compatible with server implementation. Feel free to adjust it to your needs.
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.