Füge Unterstützung für SSL/TLS in die Konfiguration und das Build-System ein

- Integriere die libwebsockets-Bibliothek für SSL/TLS WebSocket-Unterstützung in `CMakeLists.txt`.
- Aktualisiere `chatconfig.json`, um SSL-Optionen wie `ssl_enabled`, `ssl_cert_path` und `ssl_key_path` hinzuzufügen.
- Ergänze das `deploy.sh`-Skript um einen Schritt zur optionalen Einrichtung von SSL/TLS.
- Modifiziere `update_config.sh`, um die SSL-Konfiguration in die Servereinstellungen zu integrieren.
- Implementiere eine Überprüfung in `main.cpp`, um den SSL-Status zu prüfen und entsprechende Meldungen auszugeben.
This commit is contained in:
Torsten Schulz (local)
2025-09-04 12:05:22 +02:00
parent ec939bb506
commit d619d70a76
9 changed files with 929 additions and 2 deletions

View File

@@ -39,6 +39,12 @@ endif()
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
target_link_libraries(yourchat OpenSSL::SSL OpenSSL::Crypto) target_link_libraries(yourchat OpenSSL::SSL OpenSSL::Crypto)
# Link libwebsockets for SSL/TLS WebSocket support
find_package(PkgConfig REQUIRED)
pkg_check_modules(LWS REQUIRED libwebsockets)
target_include_directories(yourchat PRIVATE ${LWS_INCLUDE_DIRS})
target_link_libraries(yourchat ${LWS_LIBRARIES})
add_executable(ws_probe tools/ws_probe.cpp) add_executable(ws_probe tools/ws_probe.cpp)
target_compile_features(ws_probe PRIVATE cxx_std_17) target_compile_features(ws_probe PRIVATE cxx_std_17)
target_link_libraries(ws_probe pthread) target_link_libraries(ws_probe pthread)

View File

@@ -1,6 +1,9 @@
{ {
"server": { "server": {
"port": 1235 "port": 1235,
"ssl_enabled": false,
"ssl_cert_path": "/etc/yourpart/server.crt",
"ssl_key_path": "/etc/yourpart/server.key"
}, },
"database": { "database": {
"user": "yourpart", "user": "yourpart",

View File

@@ -40,6 +40,16 @@ echo "=== Schritt 4: Konfiguration aktualisieren ==="
echo "Führe update_config.sh aus..." echo "Führe update_config.sh aus..."
./deploy/update_config.sh ./deploy/update_config.sh
echo ""
echo "=== Schritt 5: SSL-Setup (optional) ==="
echo "Möchten Sie SSL/TLS für sichere Verbindungen einrichten? (y/N)"
read -p "SSL einrichten? " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Führe SSL-Setup aus..."
./deploy/setup-ssl.sh
fi
echo "" echo ""
echo "=== Deployment abgeschlossen! ===" echo "=== Deployment abgeschlossen! ==="
echo "" echo ""

View File

@@ -34,7 +34,7 @@ sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-13 130
echo "=== Bibliotheken installieren ===" echo "=== Bibliotheken installieren ==="
# Benötigte Bibliotheken # Benötigte Bibliotheken
sudo apt install -y libssl-dev libjsoncpp-dev libpqxx-dev sudo apt install -y libssl-dev libjsoncpp-dev libpqxx-dev libwebsockets-dev
echo "=== PostgreSQL installieren (falls nicht vorhanden) ===" echo "=== PostgreSQL installieren (falls nicht vorhanden) ==="
# PostgreSQL (falls Datenbank lokal laufen soll) # PostgreSQL (falls Datenbank lokal laufen soll)

419
deploy/setup-ssl.sh Executable file
View File

@@ -0,0 +1,419 @@
#!/bin/bash
# SSL/TLS Setup Script für YourChat
# Erstellt oder verwaltet SSL-Zertifikate für sichere Chat-Verbindungen
# Unterstützt Self-Signed Certificates und Let's Encrypt
set -e
# Farben für Logging
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
CERT_DIR="/etc/yourpart"
CERT_FILE="$CERT_DIR/server.crt"
KEY_FILE="$CERT_DIR/server.key"
CSR_FILE="$CERT_DIR/server.csr"
# Let's Encrypt Verzeichnisse
LETSENCRYPT_DIR="/etc/letsencrypt/live"
LETSENCRYPT_CERT="$LETSENCRYPT_DIR/your-part.de/fullchain.pem"
LETSENCRYPT_KEY="$LETSENCRYPT_DIR/your-part.de/privkey.pem"
# Apache2 Zertifikate (Ubuntu/Debian)
APACHE2_CERT_PATHS=(
"/etc/letsencrypt/live/your-part.de/fullchain.pem"
"/etc/letsencrypt/live/$(hostname)/fullchain.pem"
"/etc/apache2/ssl/apache.crt"
"/etc/httpd/ssl/apache.crt"
"/etc/ssl/certs/apache-selfsigned.crt"
"/etc/ssl/certs/ssl-cert-snakeoil.pem"
)
APACHE2_KEY_PATHS=(
"/etc/letsencrypt/live/your-part.de/privkey.pem"
"/etc/letsencrypt/live/$(hostname)/privkey.pem"
"/etc/apache2/ssl/apache.key"
"/etc/httpd/ssl/apache.key"
"/etc/ssl/private/apache-selfsigned.key"
"/etc/ssl/private/ssl-cert-snakeoil.key"
)
# Prüfe ob OpenSSL installiert ist
if ! command -v openssl &> /dev/null; then
log_error "OpenSSL ist nicht installiert!"
exit 1
fi
# Prüfe ob wir sudo-Rechte haben
if ! sudo -n true 2>/dev/null; then
log_info "Einige Operationen benötigen sudo-Rechte für SSL-Verzeichnisse..."
fi
# Funktionen
setup_letsencrypt() {
log_info "Let's Encrypt Setup für your-part.de"
# Prüfe ob certbot installiert ist
if ! command -v certbot &> /dev/null; then
log_error "Certbot ist nicht installiert!"
log_info "Installiere Certbot..."
if command -v apt &> /dev/null; then
sudo apt update
sudo apt install -y certbot
elif command -v zypper &> /dev/null; then
sudo zypper install -y certbot
else
log_error "Paketmanager nicht unterstützt. Installiere Certbot manuell."
exit 1
fi
fi
# Prüfe ob Let's Encrypt Zertifikate bereits existieren
if [ -f "$LETSENCRYPT_CERT" ] && [ -f "$LETSENCRYPT_KEY" ]; then
log_info "Let's Encrypt Zertifikate existieren bereits"
# Prüfe Gültigkeit
if openssl x509 -in "$LETSENCRYPT_CERT" -text -noout &> /dev/null; then
log_success "Let's Encrypt Zertifikat ist gültig"
# Zeige Zertifikats-Informationen
log_info "Let's Encrypt Zertifikats-Informationen:"
openssl x509 -in "$LETSENCRYPT_CERT" -text -noout | grep -E "(Subject:|Not Before|Not After|DNS:)"
read -p "Möchten Sie die Zertifikate erneuern? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Zertifikate bleiben unverändert"
return 0
fi
else
log_warning "Let's Encrypt Zertifikat ist ungültig, erstelle neue..."
fi
fi
# Erstelle oder erneuere Let's Encrypt Zertifikat
log_info "Erstelle/erneuere Let's Encrypt Zertifikat für your-part.de..."
# Prüfe ob Port 80 verfügbar ist (für HTTP-01 Challenge)
if ! sudo netstat -tlnp | grep -q ":80 "; then
log_warning "Port 80 ist nicht verfügbar. Stelle sicher, dass kein anderer Service läuft."
log_info "Oder verwende DNS-01 Challenge mit --dns-cloudflare oder ähnlich"
fi
# Erstelle Zertifikat mit HTTP-01 Challenge
sudo certbot certonly --standalone -d your-part.de --non-interactive --agree-tos --email admin@your-part.de
if [ $? -eq 0 ]; then
log_success "Let's Encrypt Zertifikat erfolgreich erstellt!"
# Erstelle Symlinks zu den Zertifikaten
sudo ln -sf "$LETSENCRYPT_CERT" "$CERT_FILE"
sudo ln -sf "$LETSENCRYPT_KEY" "$KEY_FILE"
# Setze korrekte Berechtigungen
sudo chown yourchat:yourchat "$CERT_FILE" "$KEY_FILE"
sudo chmod 644 "$CERT_FILE"
sudo chmod 600 "$KEY_FILE"
# Zeige Zertifikats-Informationen
log_info "Let's Encrypt Zertifikats-Informationen:"
openssl x509 -in "$CERT_FILE" -text -noout | grep -E "(Subject:|Not Before|Not After|DNS:)"
# Erstelle Auto-Renewal Cron Job
setup_auto_renewal
else
log_error "Let's Encrypt Zertifikat konnte nicht erstellt werden!"
exit 1
fi
}
setup_apache2_certificates() {
log_info "Apache2 Zertifikate Setup"
# Finde verfügbare Zertifikate
FOUND_CERT=""
FOUND_KEY=""
for cert_path in "${APACHE2_CERT_PATHS[@]}"; do
if sudo test -f "$cert_path"; then
FOUND_CERT="$cert_path"
log_info "Gefundenes Zertifikat: $cert_path"
break
fi
done
for key_path in "${APACHE2_KEY_PATHS[@]}"; do
if sudo test -f "$key_path"; then
FOUND_KEY="$key_path"
log_info "Gefundener Private Key: $key_path"
break
fi
done
if [ -z "$FOUND_CERT" ] || [ -z "$FOUND_KEY" ]; then
log_error "Keine Apache2-Zertifikate gefunden!"
log_info "Verfügbare Pfade:"
for path in "${APACHE2_CERT_PATHS[@]}" "${APACHE2_KEY_PATHS[@]}"; do
if sudo test -f "$path"; then
log_info "$path"
else
log_info "$path"
fi
done
exit 1
fi
# Warnung für Snakeoil-Zertifikate
if [[ "$FOUND_CERT" == *"snakeoil"* ]]; then
log_warning "ACHTUNG: Snakeoil-Zertifikat erkannt!"
log_warning "Dieses Zertifikat ist nur für localhost gültig, nicht für your-part.de"
log_warning "Für Produktionsumgebungen sollten Sie Let's Encrypt verwenden"
echo ""
read -p "Möchten Sie trotzdem fortfahren? (y/N): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Setup abgebrochen. Verwenden Sie Option 2 für Let's Encrypt."
exit 0
fi
fi
# Erstelle Symlinks zu den Apache2-Zertifikaten
log_info "Erstelle Symlinks zu Apache2-Zertifikaten..."
sudo ln -sf "$FOUND_CERT" "$CERT_FILE"
sudo ln -sf "$FOUND_KEY" "$KEY_FILE"
# Setze korrekte Berechtigungen
sudo chown yourchat:yourchat "$CERT_FILE" "$KEY_FILE"
sudo chmod 644 "$CERT_FILE"
sudo chmod 600 "$KEY_FILE"
log_success "Apache2-Zertifikate erfolgreich verlinkt!"
# Zeige Zertifikats-Informationen
log_info "Apache2-Zertifikats-Informationen:"
openssl x509 -in "$CERT_FILE" -text -noout | grep -E "(Subject:|Not Before|Not After|DNS:)"
# Prüfe ob es sich um Let's Encrypt-Zertifikate handelt
if [[ "$FOUND_CERT" == *"letsencrypt"* ]]; then
log_info "Let's Encrypt-Zertifikate erkannt, richte Auto-Renewal ein..."
setup_auto_renewal
else
log_warning "Self-Signed oder andere Zertifikate erkannt - kein Auto-Renewal eingerichtet"
fi
}
setup_auto_renewal() {
log_info "Richte automatische Zertifikats-Erneuerung ein..."
# Erstelle Renewal Script
sudo tee /etc/yourpart/renew-ssl.sh > /dev/null << 'EOF'
#!/bin/bash
# Automatische SSL-Zertifikats-Erneuerung für YourChat
CERT_DIR="/etc/yourpart"
LETSENCRYPT_CERT="/etc/letsencrypt/live/your-part.de/fullchain.pem"
LETSENCRYPT_KEY="/etc/letsencrypt/live/your-part.de/privkey.pem"
# Erneuere Zertifikat
certbot renew --quiet
if [ $? -eq 0 ]; then
# Aktualisiere Symlinks
ln -sf "$LETSENCRYPT_CERT" "$CERT_DIR/server.crt"
ln -sf "$LETSENCRYPT_KEY" "$CERT_DIR/server.key"
# Setze Berechtigungen
chown yourchat:yourchat "$CERT_DIR/server.crt" "$CERT_DIR/server.key"
chmod 644 "$CERT_DIR/server.crt"
chmod 600 "$CERT_DIR/server.key"
# Starte YourChat neu
systemctl reload yourchat
echo "$(date): SSL-Zertifikat erfolgreich erneuert" >> /var/log/yourchat/ssl-renewal.log
fi
EOF
sudo chmod +x /etc/yourpart/renew-ssl.sh
# Füge Cron Job hinzu (täglich um 2:30 Uhr)
(sudo crontab -l 2>/dev/null; echo "30 2 * * * /etc/yourpart/renew-ssl.sh") | sudo crontab -
log_success "Automatische Erneuerung eingerichtet (täglich um 2:30 Uhr)"
}
log_info "SSL/TLS Setup für YourChat"
# Benutzerauswahl
echo ""
log_info "Wählen Sie den Zertifikatstyp:"
echo "1) Self-Signed Certificate (für Entwicklung/Testing)"
echo "2) Let's Encrypt Certificate (für Produktion)"
echo "3) Bestehende Let's Encrypt Zertifikate verwenden"
echo "4) Apache2-Zertifikate verwenden (empfohlen für Ubuntu)"
echo ""
read -p "Ihre Wahl (1-4): " -n 1 -r
echo ""
case $REPLY in
1)
log_info "Self-Signed Certificate wird erstellt..."
CERT_TYPE="self-signed"
;;
2)
log_info "Let's Encrypt Certificate wird erstellt..."
CERT_TYPE="letsencrypt"
;;
3)
log_info "Bestehende Let's Encrypt Zertifikate werden verwendet..."
CERT_TYPE="existing-letsencrypt"
;;
4)
log_info "Apache2-Zertifikate werden verwendet..."
CERT_TYPE="apache2"
;;
*)
log_error "Ungültige Auswahl!"
exit 1
;;
esac
# Erstelle Zertifikats-Verzeichnis falls nicht vorhanden
if [ ! -d "$CERT_DIR" ]; then
log_info "Erstelle Zertifikats-Verzeichnis: $CERT_DIR"
sudo mkdir -p "$CERT_DIR"
fi
# Führe entsprechenden Setup-Typ aus
case $CERT_TYPE in
"self-signed")
# Prüfe ob bereits Zertifikate existieren
if [ -f "$CERT_FILE" ] && [ -f "$KEY_FILE" ]; then
log_info "Zertifikate existieren bereits"
# Prüfe Gültigkeit der Zertifikate
if openssl x509 -in "$CERT_FILE" -text -noout &> /dev/null; then
log_success "Zertifikat ist gültig"
# Zeige Zertifikats-Informationen
log_info "Zertifikats-Informationen:"
openssl x509 -in "$CERT_FILE" -text -noout | grep -E "(Subject:|Not Before|Not After|DNS:)"
read -p "Möchten Sie neue Zertifikate erstellen? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Zertifikate bleiben unverändert"
exit 0
fi
else
log_warning "Zertifikat ist ungültig, erstelle neue..."
fi
fi
log_info "Erstelle neue Self-Signed SSL-Zertifikate..."
# Erstelle Private Key
log_info "Erstelle Private Key..."
sudo openssl genrsa -out "$KEY_FILE" 2048
sudo chmod 600 "$KEY_FILE"
sudo chown yourchat:yourchat "$KEY_FILE"
# Erstelle Certificate Signing Request (CSR)
log_info "Erstelle Certificate Signing Request..."
sudo openssl req -new -key "$KEY_FILE" -out "$CSR_FILE" -subj "/C=DE/ST=Germany/L=Berlin/O=YourChat/OU=IT/CN=your-part.de"
# Erstelle Self-Signed Certificate
log_info "Erstelle Self-Signed Certificate..."
sudo openssl x509 -req -days 365 -in "$CSR_FILE" -signkey "$KEY_FILE" -out "$CERT_FILE"
# Setze korrekte Berechtigungen
sudo chmod 644 "$CERT_FILE"
sudo chown yourchat:yourchat "$CERT_FILE"
# Lösche CSR-Datei (nicht mehr benötigt)
sudo rm -f "$CSR_FILE"
log_success "Self-Signed SSL-Zertifikate erfolgreich erstellt!"
;;
"letsencrypt")
setup_letsencrypt
;;
"existing-letsencrypt")
if [ -f "$LETSENCRYPT_CERT" ] && [ -f "$LETSENCRYPT_KEY" ]; then
log_info "Verwende bestehende Let's Encrypt Zertifikate..."
# Erstelle Symlinks zu den Zertifikaten
sudo ln -sf "$LETSENCRYPT_CERT" "$CERT_FILE"
sudo ln -sf "$LETSENCRYPT_KEY" "$KEY_FILE"
# Setze korrekte Berechtigungen
sudo chown yourchat:yourchat "$CERT_FILE" "$KEY_FILE"
sudo chmod 644 "$CERT_FILE"
sudo chmod 600 "$KEY_FILE"
log_success "Let's Encrypt Zertifikate erfolgreich verlinkt!"
# Richte Auto-Renewal ein
setup_auto_renewal
else
log_error "Let's Encrypt Zertifikate nicht gefunden in $LETSENCRYPT_DIR"
log_info "Führen Sie zuerst 'certbot certonly' aus oder wählen Sie Option 2"
exit 1
fi
;;
"apache2")
setup_apache2_certificates
;;
esac
# Zeige Zertifikats-Informationen
log_info "Zertifikats-Informationen:"
openssl x509 -in "$CERT_FILE" -text -noout | grep -E "(Subject:|Not Before|Not After|DNS:)"
log_info ""
log_info "Nächste Schritte:"
log_info "1. Aktiviere SSL in der Konfiguration:"
log_info " ./deploy/update_config.sh"
log_info "2. Starte YourChat neu:"
log_info " sudo systemctl restart yourchat"
log_info "3. Verbinde dich mit:"
log_info " wss://your-part.de:1235"
log_info ""
case $CERT_TYPE in
"self-signed")
log_warning "Hinweis: Dies ist ein Self-Signed Certificate!"
log_warning "Für Produktionsumgebungen verwenden Sie Let's Encrypt oder Apache2-Zertifikate."
;;
"apache2")
log_success "Apache2-Zertifikate erfolgreich konfiguriert!"
log_info "Diese Zertifikate werden automatisch von Apache2 verwaltet."
;;
*)
log_success "Let's Encrypt Zertifikat ist produktionsbereit!"
;;
esac

View File

@@ -137,6 +137,18 @@ def merge_json(source_file, target_file, output_file):
if 'database' not in db_config: if 'database' not in db_config:
db_config['database'] = database db_config['database'] = database
# Spezielle Behandlung für server-Konfiguration (SSL)
if 'server' in merged_data and isinstance(merged_data['server'], dict):
server_config = merged_data['server']
# SSL-Konfiguration hinzufügen falls nicht vorhanden
if 'ssl_enabled' not in server_config:
server_config['ssl_enabled'] = False
if 'ssl_cert_path' not in server_config:
server_config['ssl_cert_path'] = "/etc/yourpart/server.crt"
if 'ssl_key_path' not in server_config:
server_config['ssl_key_path'] = "/etc/yourpart/server.key"
with open(output_file, 'w') as f: with open(output_file, 'w') as f:
json.dump(merged_data, f, indent=4, ensure_ascii=False) json.dump(merged_data, f, indent=4, ensure_ascii=False)

364
src/core/ssl_server.cpp Normal file
View File

@@ -0,0 +1,364 @@
#include "ssl_server.h"
#include "config.h"
#include "lib/database.h"
#include "chat_room.h"
#include "chat_user.h"
#include "lib/base.h"
#include <iostream>
#include <json/json.h>
#include <cstring>
namespace Yc {
namespace Lib {
// Static instance pointer
SSLServer* SSLServer::_instance = nullptr;
// Protocols array definition
struct lws_protocols SSLServer::_protocols[] = {
{
"yourchat-protocol",
SSLServer::wsCallback,
sizeof(WebSocketUserData),
4096
},
{ nullptr, nullptr, 0, 0 }
};
SSLServer::SSLServer(std::shared_ptr<Config> config, std::shared_ptr<Database> database)
: _config(std::move(config)), _database(std::move(database)) {
_instance = this;
// Load SSL settings from config
_useSSL = _config->value("server", "ssl_enabled").asBool();
_certPath = _config->value("server", "ssl_cert_path").asString();
_keyPath = _config->value("server", "ssl_key_path").asString();
_port = _config->value("server", "port").asInt();
if (_useSSL && (_certPath.empty() || _keyPath.empty())) {
throw std::runtime_error("SSL enabled but certificate or key path not provided");
}
}
SSLServer::~SSLServer() {
stop();
_instance = nullptr;
}
void SSLServer::run() {
_running = true;
_serverThread = std::thread([this](){ startServer(); });
_messageThread = std::thread([this](){ processMessageQueue(); });
}
void SSLServer::stop() {
_running = false;
if (_context) lws_cancel_service(_context);
if (_serverThread.joinable()) _serverThread.join();
if (_messageThread.joinable()) _messageThread.join();
if (_context) {
lws_context_destroy(_context);
_context = nullptr;
}
}
void SSLServer::startServer() {
struct lws_context_creation_info info;
memset(&info, 0, sizeof(info));
info.port = _port;
info.protocols = _protocols;
// SSL/TLS Konfiguration
if (_useSSL) {
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.ssl_cert_filepath = _certPath.c_str();
info.ssl_private_key_filepath = _keyPath.c_str();
std::cout << "[YourChat] SSL WebSocket Server starting on port " << _port
<< " with certificates: " << _certPath << " / " << _keyPath << std::endl;
} else {
std::cout << "[YourChat] WebSocket Server starting on port " << _port << " (no SSL)" << std::endl;
}
// Reduziere Log-Level um weniger Debug-Ausgaben zu haben
setenv("LWS_LOG_LEVEL", "0", 1); // 0 = nur Fehler
_context = lws_create_context(&info);
if (!_context) {
throw std::runtime_error("Failed to create LWS context");
}
while (_running) {
lws_service(_context, 50);
}
}
void SSLServer::processMessageQueue() {
while (_running) {
std::unique_lock<std::mutex> lock(_queueMutex);
_queueCV.wait(lock, [this](){ return !_messageQueue.empty() || !_running; });
while (!_messageQueue.empty()) {
std::string msg = std::move(_messageQueue.front());
_messageQueue.pop();
lock.unlock();
// Process message here if needed
lock.lock();
}
}
}
int SSLServer::wsCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
if (!_instance) return 0;
auto *ud = reinterpret_cast<WebSocketUserData*>(user);
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
std::cout << "[YourChat] WebSocket-Verbindung hergestellt" << std::endl;
break;
case LWS_CALLBACK_RECEIVE: {
std::string msg(reinterpret_cast<char*>(in), len);
std::cout << "[YourChat] WebSocket-Nachricht empfangen: " << msg << std::endl;
_instance->handleWebSocketMessage(wsi, msg);
break;
}
case LWS_CALLBACK_SERVER_WRITEABLE: {
if (ud->pendingMessage.empty()) {
// Ping senden
unsigned char buf[LWS_PRE + 4];
memcpy(buf + LWS_PRE, "ping", 4);
lws_write(wsi, buf + LWS_PRE, 4, LWS_WRITE_TEXT);
} else {
// Nachricht senden
unsigned char buf[LWS_PRE + ud->pendingMessage.length()];
memcpy(buf + LWS_PRE, ud->pendingMessage.c_str(), ud->pendingMessage.length());
lws_write(wsi, buf + LWS_PRE, ud->pendingMessage.length(), LWS_WRITE_TEXT);
ud->pendingMessage.clear();
}
break;
}
case LWS_CALLBACK_CLOSED:
if (!ud->token.empty()) {
_instance->removeConnection(ud->token);
}
break;
default:
break;
}
return 0;
}
void SSLServer::handleWebSocketMessage(struct lws *wsi, const std::string& message) {
try {
Json::Value root;
Json::Reader reader;
if (!reader.parse(message, root)) {
std::cerr << "[YourChat] JSON Parse Error: " << reader.getFormattedErrorMessages() << std::endl;
return;
}
std::string type = root.get("type", "").asString();
std::string token = root.get("token", "").asString();
if (type == "init") {
// User initialization
std::string name = root.get("name", "").asString();
std::string room = root.get("room", "").asString();
std::string color = root.get("color", "#000000").asString();
std::string password = root.get("password", "").asString();
if (name.empty() || room.empty()) {
Json::Value errorJson;
errorJson["type"] = "error";
errorJson["message"] = "missing_fields";
errorJson["detail"] = "'name' und 'room' müssen gesetzt sein.";
sendMessage(lws_get_socket_fd(wsi), errorJson);
return;
}
if (userExists(name)) {
Json::Value errorJson;
errorJson["type"] = "error";
errorJson["message"] = "loggedin";
sendMessage(lws_get_socket_fd(wsi), errorJson);
return;
}
// Generate token if not provided
if (token.empty()) {
token = Base::generateToken();
}
// Store user data
auto *ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(wsi));
ud->token = token;
ud->userName = name;
ud->userColor = color;
ud->currentRoom = room;
ud->authenticated = true;
// Add to connections
addConnection(token, wsi);
// Try to add user to room
bool added = false;
for (auto &roomObj: _rooms) {
if (roomObj->name() == room) {
// Create ChatUser and add to room
auto chatUser = std::make_shared<ChatUser>(name, color, token, lws_get_socket_fd(wsi));
if (roomObj->addUser(name, color, password, lws_get_socket_fd(wsi))) {
_users[token] = chatUser;
added = true;
break;
}
}
}
if (!added) {
Json::Value errorJson;
errorJson["type"] = "error";
errorJson["message"] = "room_not_found_or_join_failed";
sendMessage(lws_get_socket_fd(wsi), errorJson);
} else {
// Send success response
Json::Value successJson;
successJson["type"] = "init_success";
successJson["token"] = token;
successJson["message"] = "Erfolgreich verbunden";
sendMessage(lws_get_socket_fd(wsi), successJson);
}
} else if (type == "message") {
// Handle chat message
if (!token.empty()) {
auto user = getUserByToken(token);
if (user) {
std::string msg = root.get("message", "").asString();
// Process message through room
for (auto &room: _rooms) {
if (room->userIsInRoom(user->name())) {
room->addMessage(user, msg);
break;
}
}
}
}
}
// Add more message types as needed
} catch (const std::exception &e) {
std::cerr << "[YourChat] Error processing WebSocket message: " << e.what() << std::endl;
}
}
void SSLServer::addConnection(const std::string& token, struct lws *wsi) {
std::unique_lock<std::shared_mutex> lock(_connectionsMutex);
_connections[token] = wsi;
std::cout << "[YourChat] Verbindung für Token " << token << " gespeichert" << std::endl;
}
void SSLServer::removeConnection(const std::string& token) {
std::unique_lock<std::shared_mutex> lock(_connectionsMutex);
_connections.erase(token);
_users.erase(token);
std::cout << "[YourChat] Verbindung für Token " << token << " entfernt" << std::endl;
}
std::shared_ptr<ChatUser> SSLServer::getUserByToken(const std::string& token) {
std::shared_lock<std::shared_mutex> lock(_connectionsMutex);
auto it = _users.find(token);
return (it != _users.end()) ? it->second : nullptr;
}
void SSLServer::sendMessage(int socket, const Json::Value& message) {
Json::StreamWriterBuilder builder;
std::string jsonString = Json::writeString(builder, message);
sendMessage(socket, jsonString);
}
void SSLServer::sendMessage(int socket, const std::string& message) {
// Find the WebSocket connection for this socket
std::shared_lock<std::shared_mutex> lock(_connectionsMutex);
for (auto& pair : _connections) {
if (lws_get_socket_fd(pair.second) == socket) {
auto *ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(pair.second));
if (ud) {
ud->pendingMessage = message;
lws_callback_on_writable(pair.second);
}
break;
}
}
}
void SSLServer::broadcastToRoom(const std::string& roomName, const std::string& message) {
for (auto &room: _rooms) {
if (room->name() == roomName) {
// This would need to be implemented in ChatRoom to work with WebSockets
// For now, just a placeholder
break;
}
}
}
void SSLServer::createRooms() {
// Load rooms from database or config
// This is a simplified version - would need to be expanded
Json::Value roomList = _config->group("rooms");
if (roomList.isArray()) {
for (const auto& room : roomList) {
// Create room objects
// This would need the actual ChatRoom constructor
}
}
}
std::vector<std::string> SSLServer::roomList() {
std::vector<std::string> list;
for (const auto &room: _rooms) {
list.push_back(room->name());
}
return list;
}
bool SSLServer::roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password) {
for (auto &room: _rooms) {
if (room->name() == roomName && room->accessAllowed(userName, password)) {
return true;
}
}
return false;
}
bool SSLServer::changeRoom(std::shared_ptr<ChatUser> user, const std::string& newRoom, const std::string& password) {
if (!roomAllowed(newRoom, user->name(), password)) {
return false;
}
// Implementation would go here
return true;
}
bool SSLServer::userExists(const std::string& userName) {
for (const auto &room: _rooms) {
if (room->userNameExists(userName)) {
return true;
}
}
return false;
}
void SSLServer::initUser(const std::string& token, const std::string& name, const std::string& room, const std::string& color, const std::string& password) {
// Implementation would go here
}
} // namespace Lib
} // namespace Yc

100
src/core/ssl_server.h Normal file
View File

@@ -0,0 +1,100 @@
#ifndef YC_LIB_SSL_SERVER_H
#define YC_LIB_SSL_SERVER_H
#include <libwebsockets.h>
#include <memory>
#include <string>
#include <thread>
#include <atomic>
#include <mutex>
#include <shared_mutex>
#include <unordered_map>
#include <queue>
#include <condition_variable>
#include <vector>
namespace Yc {
namespace Lib {
class Config;
class Database;
class ChatRoom;
class ChatUser;
struct WebSocketUserData {
std::string token;
std::string userName;
std::string userColor;
std::string currentRoom;
bool authenticated = false;
std::string pendingMessage;
};
class SSLServer {
public:
SSLServer(std::shared_ptr<Config> config, std::shared_ptr<Database> database);
~SSLServer();
void run();
void stop();
void createRooms();
// Room management
std::vector<std::string> roomList();
bool roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password);
bool changeRoom(std::shared_ptr<ChatUser> user, const std::string& newRoom, const std::string& password);
// User management
bool userExists(const std::string& userName);
void initUser(const std::string& token, const std::string& name, const std::string& room, const std::string& color, const std::string& password);
// Message handling
void sendMessage(int socket, const std::string& message);
void broadcastToRoom(const std::string& roomName, const std::string& message);
// WebSocket callbacks
static int wsCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
private:
void startServer();
void processMessageQueue();
void handleWebSocketMessage(struct lws *wsi, const std::string& message);
void addConnection(const std::string& token, struct lws *wsi);
void removeConnection(const std::string& token);
std::shared_ptr<ChatUser> getUserByToken(const std::string& token);
std::shared_ptr<Config> _config;
std::shared_ptr<Database> _database;
std::vector<std::shared_ptr<ChatRoom>> _rooms;
// SSL/TLS settings
bool _useSSL;
std::string _certPath;
std::string _keyPath;
int _port;
// Server state
std::atomic<bool> _running{false};
struct lws_context* _context = nullptr;
std::thread _serverThread;
std::thread _messageThread;
// Message queue
std::mutex _queueMutex;
std::condition_variable _queueCV;
std::queue<std::string> _messageQueue;
// Connections
std::shared_mutex _connectionsMutex;
std::unordered_map<std::string, struct lws*> _connections;
std::unordered_map<std::string, std::shared_ptr<ChatUser>> _users;
// Static instance for callbacks
static SSLServer* _instance;
static struct lws_protocols _protocols[];
};
} // namespace Lib
} // namespace Yc
#endif // YC_LIB_SSL_SERVER_H

View File

@@ -2,13 +2,26 @@
#include "core/config.h" #include "core/config.h"
#include "core/server.h" #include "core/server.h"
#include "lib/database.h" #include "lib/database.h"
#include <iostream>
// main function // main function
int main(int, char **) { int main(int, char **) {
auto config = std::make_shared<Yc::Lib::Config>(); auto config = std::make_shared<Yc::Lib::Config>();
auto database = std::make_shared<Yc::Lib::Database>(config); auto database = std::make_shared<Yc::Lib::Database>(config);
// Check if SSL is enabled (for future implementation)
bool sslEnabled = config->value("server", "ssl_enabled").asBool();
if (sslEnabled) {
std::cout << "[YourChat] SSL/TLS support is configured but not yet implemented" << std::endl;
std::cout << "[YourChat] Starting without SSL/TLS support" << std::endl;
} else {
std::cout << "[YourChat] Starting without SSL/TLS support" << std::endl;
}
auto server = std::make_shared<Yc::Lib::Server>(config, database); auto server = std::make_shared<Yc::Lib::Server>(config, database);
server->createRooms(config->group("rooms")); server->createRooms(config->group("rooms"));
server->run(); server->run();
return 0; return 0;
} }