diff --git a/CMakeLists.txt b/CMakeLists.txt index e761dad..d13fdc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,60 +1,60 @@ # SPDX-FileCopyrightText: 2023 g10 code Gmbh # SPDX-Contributor: Carl Schwan # SPDX-License-Identifier: BSD-2-Clause cmake_minimum_required(VERSION 3.16) project(gpgoljs) set(KF_MIN_VERSION "5.240.0") find_package(ECM ${KF_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ) find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(ECMQtDeclareLoggingCategory) include(ECMAddTests) include(KDECompilerSettings NO_POLICY_SCOPE) set(QT_MIN_VERSION "6.5") set(KF_MIN_VERSION "5.240") set(MIMETREEPARSER_VERSION "5.240.40") set(LIBKLEO_VERSION "5.240.46") set(KLDAP_VERSION "5.240.40") set(LIBKDEPIM_VERSION "5.240.40") set(PIMTEXTEDIT_VERSION "5.240.40") find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core HttpServer Widgets PrintSupport) set_package_properties(Qt6 PROPERTIES TYPE REQUIRED PURPOSE "Basic application components" ) find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Contacts Completion CoreAddons WidgetsAddons Config ColorScheme Codecs XmlGui GuiAddons JobWidgets Sonnet CalendarCore Archive) set_package_properties(KF6 PROPERTIES TYPE REQUIRED PURPOSE "Basic application components" ) find_package(KPim6Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED) find_package(KPim6LdapWidgets ${KLDAP_VERSION} CONFIG REQUIRED) find_package(KPim6Libkdepim ${LIBKDEPIM_LIB_VERSION} CONFIG REQUIRED) find_package(KPim6MimeTreeParserWidgets ${MIMETREEPARSER_VERSION} CONFIG REQUIRED) find_package(KPim6TextEdit ${PIMTEXTEDIT_VERSION} CONFIG REQUIRED) find_package(KF6TextAutoCorrectionCore CONFIG REQUIRED) if (BUILD_TESTING) find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test) endif() add_subdirectory(common) -add_subdirectory(broker) add_subdirectory(server) +add_subdirectory(client) diff --git a/broker/CMakeLists.txt b/broker/CMakeLists.txt deleted file mode 100644 index fc4ec1e..0000000 --- a/broker/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# SPDX-FileCopyrightText: 2023 g10 code GmbH -# SPDX-Contributor: Carl Schwan -# SPDX-License-Identifier: BSD-2-Clause - -add_executable(gpgol-broker) -target_sources(gpgol-broker PRIVATE - # Controllers - controllers/abstractcontroller.cpp - controllers/abstractcontroller.h - controllers/registrationcontroller.cpp - controllers/registrationcontroller.h - controllers/staticcontroller.h - controllers/staticcontroller.cpp - controllers/emailcontroller.cpp - controllers/emailcontroller.h - - # State - model/serverstate.cpp - model/serverstate.h - - # web sever - webserver.cpp - webserver.h - - main.cpp -) - -qt_add_resources(gpgol-broker - PREFIX - "/" - FILES - assets/certificate.crt - assets/private.key - - assets/document-decrypt-16.png - assets/document-decrypt-32.png - assets/document-decrypt-64.png - assets/document-decrypt-80.png - - assets/script.js - - web/index.html -) - -target_link_libraries(gpgol-broker PRIVATE Qt6::HttpServer Qt6::Core common) diff --git a/broker/controllers/emailcontroller.cpp b/broker/controllers/emailcontroller.cpp deleted file mode 100644 index 39e67e7..0000000 --- a/broker/controllers/emailcontroller.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-FileCopyrightText: 2023 g10 code GmbH -// SPDX-Contributor: Carl Schwan -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "emailcontroller.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "webserver.h" - -using namespace Qt::Literals::StringLiterals; - -QHttpServerResponse EmailController::abstractEmailAction(const QHttpServerRequest &request, const QString &action, QHttpServerRequest::Method method) -{ - - const auto server = checkAuthentification(request); - if (!server) { - return forbidden(); - } - - QNetworkRequest viewEmailRequest(QUrl(u"http://127.0.0.1:"_s + QString::number(server->port) + u'/' + action)); - viewEmailRequest.setHeader(QNetworkRequest::ContentTypeHeader, u"application/json"_s); - auto email = Utils::findHeader(request.headers(), "X-EMAIL"); - auto displayName = Utils::findHeader(request.headers(), "X-NAME"); - auto token = QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8(); - viewEmailRequest.setRawHeader("X-EMAIL", email); - viewEmailRequest.setRawHeader("X-TOKEN", token); - viewEmailRequest.setRawHeader("X-NAME", displayName); - - auto &serverState = ServerState::instance(); - serverState.composerRequest[token] = QString::fromUtf8(email); - - - auto &state = ServerState::instance(); - QNetworkReply *reply; - if (method == QHttpServerRequest::Method::Post) { - const auto body = request.body(); - reply = state.qnam.post(viewEmailRequest, body); - } else { - reply = state.qnam.deleteResource(viewEmailRequest); - } - - QObject::connect(reply, &QNetworkReply::finished, reply, [reply]() { - if (reply->error() != QNetworkReply::NoError) { - qWarning() << reply->error() << reply->errorString(); - } else { - qWarning() << "sent request to view message to server"; - } - }); - - return QHttpServerResponse(QJsonObject { - {"status"_L1, "ok"_L1}, - }); -} - -QHttpServerResponse EmailController::viewEmailAction(const QHttpServerRequest &request) -{ - if (request.method() != QHttpServerRequest::Method::Post) { - return badRequest(u"Endpoint only supports POST request"_s); - } - - return abstractEmailAction(request, u"view"_s); -} - -QHttpServerResponse EmailController::newEmailAction(const QHttpServerRequest &request) -{ - if (request.method() != QHttpServerRequest::Method::Post) { - return badRequest(u"Endpoint only supports POST request"_s); - } - - return abstractEmailAction(request, u"new"_s); -} - -QHttpServerResponse EmailController::draftAction(QString, const QHttpServerRequest &request) -{ - if (request.method() != QHttpServerRequest::Method::Post && request.method() != QHttpServerRequest::Method::Delete) { - return badRequest(u"Endpoint only supports POST request"_s); - } - - return abstractEmailAction(request, request.url().path(), request.method()); -} - -QHttpServerResponse EmailController::replyEmailAction(const QHttpServerRequest &request) -{ - if (request.method() != QHttpServerRequest::Method::Post) { - return badRequest(u"Endpoint only supports POST request"_s); - } - - return abstractEmailAction(request, u"reply"_s); -} - -QHttpServerResponse EmailController::forwardEmailAction(const QHttpServerRequest &request) -{ - if (request.method() != QHttpServerRequest::Method::Post) { - return badRequest(u"Endpoint only supports POST request"_s); - } - - return abstractEmailAction(request, u"forward"_s); -} - -QHttpServerResponse checkStatus(int port, const QByteArray &body) -{ - QNetworkRequest infoEmailRequest(QUrl(u"http://127.0.0.1:"_s + QString::number(port) + u"/info"_s)); - infoEmailRequest.setHeader(QNetworkRequest::ContentTypeHeader, u"application/json"_s); - - auto &state = ServerState::instance(); - QEventLoop eventLoop; - auto reply = state.qnam.post(infoEmailRequest, body); - QObject::connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit); - eventLoop.exec(); - - QJsonParseError error; - const auto resultBody = QJsonDocument::fromJson(reply->readAll(), &error); - if (resultBody.isNull()) { - return QHttpServerResponse(QHttpServerResponse::StatusCode::BadRequest); - } - - if (!resultBody.isObject()) { - return QHttpServerResponse(QHttpServerResponse::StatusCode::BadRequest); - } - - return QHttpServerResponse{resultBody.object()}; -} - -QHttpServerResponse EmailController::infoEmailAction(const QHttpServerRequest &request) -{ - if (request.method() != QHttpServerRequest::Method::Post) { - return badRequest(u"Endpoint only supports POST request"_s); - } - - const auto server = checkAuthentification(request); - if (!server) { - return forbidden(); - } - - return checkStatus(server->port, request.body()); -} - -QHttpServerResponse EmailController::socketWebAction(const QHttpServerRequest &request) -{ - const auto email = QString::fromUtf8(Utils::findHeader(request.headers(), "X-EMAIL")); - const auto token = Utils::findHeader(request.headers(), "X-TOKEN"); - const auto &serverState = ServerState::instance(); - - qDebug() << serverState.composerRequest << email << token; - if (serverState.composerRequest[token] != email) { - return forbidden(); - } - - WebServer::self().sendMessageToWebClient(email, request.body()); - - return QHttpServerResponse(QJsonObject{ - { "status"_L1, "OK"_L1 }, - }); -} diff --git a/broker/controllers/emailcontroller.h b/broker/controllers/emailcontroller.h deleted file mode 100644 index ead4a20..0000000 --- a/broker/controllers/emailcontroller.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: 2023 g10 code GmbH -// SPDX-Contributor: Carl Schwan -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "abstractcontroller.h" - -#include - -class EmailController : public AbstractController -{ -public: - // Request from the web client - static QHttpServerResponse viewEmailAction(const QHttpServerRequest &request); - static QHttpServerResponse infoEmailAction(const QHttpServerRequest &request); - static QHttpServerResponse newEmailAction(const QHttpServerRequest &request); - static QHttpServerResponse replyEmailAction(const QHttpServerRequest &request); - static QHttpServerResponse forwardEmailAction(const QHttpServerRequest &request); - static QHttpServerResponse draftAction(QString draft, const QHttpServerRequest &request); - - /// Forward request from the native client to the web client - static QHttpServerResponse socketWebAction(const QHttpServerRequest &request); - -private: - static QHttpServerResponse abstractEmailAction(const QHttpServerRequest &request, const QString &action, QHttpServerRequest::Method method = QHttpServerRequest::Method::Post); -}; diff --git a/broker/main.cpp b/broker/main.cpp deleted file mode 100644 index a1b7bcb..0000000 --- a/broker/main.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2023 g10 code GmbH -// SPDX-Contributor: Carl Schwan -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "webserver.h" - -using namespace Qt::Literals::StringLiterals; - -int main(int argc, char *argv[]) -{ - QCoreApplication app(argc, argv); - - auto &webServer = WebServer::self(); - - if (!webServer.run()) { - qWarning() << "Server failed to listen on a port."; - return 1; - } - - return app.exec(); -} diff --git a/broker/webserver.cpp b/broker/webserver.cpp deleted file mode 100644 index 4e7da5a..0000000 --- a/broker/webserver.cpp +++ /dev/null @@ -1,300 +0,0 @@ -// SPDX-FileCopyrightText: 2023 g10 code GmbH -// SPDX-Contributor: Carl Schwan -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "webserver.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "controllers/registrationcontroller.h" -#include "controllers/staticcontroller.h" -#include "controllers/emailcontroller.h" - -using namespace Qt::Literals::StringLiterals; - -WebServer WebServer::s_instance = WebServer(); - -WebServer &WebServer::self() -{ - return s_instance; -} - -WebServer::WebServer() - : QObject(nullptr) - , m_httpServer(new QHttpServer(this)) - , m_webSocketServer(new QWebSocketServer(u"GPGOL"_s, QWebSocketServer::SslMode::SecureMode, this)) -{ -} - -WebServer::~WebServer() = default; - -bool WebServer::run() -{ - QFile privateKeyFile(QStringLiteral(":/assets/private.key")); - if (!privateKeyFile.open(QIODevice::ReadOnly)) { - qWarning() << u"Couldn't open file for reading: %1"_s.arg(privateKeyFile.errorString()); - return false; - } - const QSslKey sslKey(&privateKeyFile, QSsl::Rsa); - privateKeyFile.close(); - - const auto sslCertificateChain = - QSslCertificate::fromPath(QStringLiteral(":/assets/certificate.crt")); - if (sslCertificateChain.isEmpty()) { - qWarning() << u"Couldn't retrieve SSL certificate from file."_s; - return false; - } - - // Static assets controller - m_httpServer->route(u"/home"_s, &StaticController::homeAction); - m_httpServer->route(u"/assets/"_s, &StaticController::assetsAction); - - // Registration controller - m_httpServer->route(u"/register"_s, &RegistrationController::registerAction); - - // Email controller - m_httpServer->route(u"/view"_s, &EmailController::viewEmailAction); - m_httpServer->route(u"/info"_s, &EmailController::infoEmailAction); - m_httpServer->route(u"/reply"_s, &EmailController::replyEmailAction); - m_httpServer->route(u"/forward"_s, &EmailController::forwardEmailAction); - m_httpServer->route(u"/new"_s, &EmailController::newEmailAction); - m_httpServer->route(u"/socket-web"_s, &EmailController::socketWebAction); - - m_httpServer->route(u"/draft/"_s, &EmailController::draftAction); - - m_httpServer->afterRequest([](QHttpServerResponse &&resp) { - resp.setHeader("Access-Control-Allow-Origin", "*"); - return std::move(resp); - }); - - m_httpServer->sslSetup(sslCertificateChain.front(), sslKey); - - const auto port = m_httpServer->listen(QHostAddress::Any, WebServer::Port); - if (!port) { - qWarning() << "Server failed to listen on a port."; - return false; - } - qWarning() << u"Running http server on https://127.0.0.1:%1/ (Press CTRL+C to quit)"_s.arg(port); - - - QSslConfiguration sslConfiguration; - sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); - sslConfiguration.setLocalCertificate(sslCertificateChain.front()); - sslConfiguration.setPrivateKey(sslKey); - m_webSocketServer->setSslConfiguration(sslConfiguration); - - if (m_webSocketServer->listen(QHostAddress::Any, WebServer::WebSocketPort)) { - qWarning() << u"Running websocket server on wss://127.0.0.1:%1/ (Press CTRL+C to quit)"_s.arg(WebServer::Port + 1); - connect(m_webSocketServer, &QWebSocketServer::newConnection, this, &WebServer::onNewConnection); - } - - - return true; -} - -void WebServer::onNewConnection() -{ - auto pSocket = m_webSocketServer->nextPendingConnection(); - if (!pSocket) { - return; - } - - qDebug() << "Client connected:" << pSocket->peerName() << pSocket->origin(); - - connect(pSocket, &QWebSocket::textMessageReceived, this, &WebServer::processTextMessage); - connect(pSocket, &QWebSocket::binaryMessageReceived, - this, &WebServer::processBinaryMessage); - connect(pSocket, &QWebSocket::disconnected, this, &WebServer::socketDisconnected); - - m_clients << pSocket; -} - -void WebServer::processTextMessage(QString message) -{ - auto webClient = qobject_cast(sender()); - if (webClient) { - QJsonParseError error; - const auto doc = QJsonDocument::fromJson(message.toUtf8(), &error); - if (error.error != QJsonParseError::NoError) { - qWarning() << "Error parsing json" << error.errorString(); - return; - } - - if (!doc.isObject()) { - qWarning() << "Invalid json received"; - return; - } - - const auto object = doc.object(); - if (!object.contains("command"_L1) || !object["command"_L1].isString() - || !object.contains("arguments"_L1) || !object["arguments"_L1].isObject()) { - qWarning() << "Invalid json received: no command or arguments set" ; - return; - } - - static QHash commandMapping { - { "register"_L1, WebServer::Command::Register }, - { "email-sent"_L1, WebServer::Command::EmailSent }, - }; - - const auto command = commandMapping[doc["command"_L1].toString()]; - - processCommand(command, object["arguments"_L1].toObject(), webClient); - } -} - -void WebServer::processCommand(Command command, const QJsonObject &arguments, QWebSocket *socket) -{ - switch (command) { - case Command::Register: { - const auto type = arguments["type"_L1].toString(); - qDebug() << "Register" << arguments; - if (type.isEmpty()) { - qWarning() << "empty client type given when registering"; - return; - } - - const auto emails = arguments["emails"_L1].toArray(); - if (type == "webclient"_L1) { - if (emails.isEmpty()) { - qWarning() << "empty email given"; - } - for (const auto &email : emails) { - m_webClientsMappingToEmail[email.toString()] = socket; - qWarning() << "email" << email.toString() << "mapped to a web client"; - - const auto nativeClient = m_nativeClientsMappingToEmail[email.toString()]; - if (nativeClient) { - QJsonDocument doc(QJsonObject{ - { "type"_L1, "connection"_L1 }, - { "payload"_L1, QJsonObject{ - { "client_type"_L1, "web_client"_L1 } - }} - }); - nativeClient->sendTextMessage(QString::fromUtf8(doc.toJson())); - } - } - } else { - if (emails.isEmpty()) { - qWarning() << "empty email given"; - } - for (const auto &email : emails) { - m_nativeClientsMappingToEmail[email.toString()] = socket; - qWarning() << "email" << email.toString() << "mapped to a native client"; - - const auto webClient = m_webClientsMappingToEmail[email.toString()]; - if (webClient) { - QJsonDocument doc(QJsonObject{ - { "type"_L1, "connection"_L1 }, - { "payload"_L1, QJsonObject{ - { "client_type"_L1, "native_client"_L1 } - }} - }); - webClient->sendTextMessage(QString::fromUtf8(doc.toJson())); - } - } - } - return; - } - case Command::EmailSent: { - const auto email = arguments["email"_L1].toString(); - const auto socket = m_nativeClientsMappingToEmail[email]; - if (!socket) { - return; - } - QJsonDocument doc(QJsonObject{ - { "type"_L1, "email-sent"_L1 }, - { "arguments"_L1, arguments }, - }); - socket->sendTextMessage(QString::fromUtf8(doc.toJson())); - return; - } - case Command::Undefined: - qWarning() << "Invalid json received: invalid command" ; - return; - } -} - -bool WebServer::sendMessageToWebClient(const QString &email, const QByteArray &payload) -{ - auto socket = m_webClientsMappingToEmail[email]; - if (!socket) { - return false; - } - - socket->sendTextMessage(QString::fromUtf8(payload)); - return true; -} - -void WebServer::processBinaryMessage(QByteArray message) -{ - qWarning() << "got binary message" << message; - QWebSocket *pClient = qobject_cast(sender()); - if (pClient) { - pClient->sendBinaryMessage(message); - } -} - -void WebServer::socketDisconnected() -{ - QWebSocket *pClient = qobject_cast(sender()); - if (pClient) { - qDebug() << "Client disconnected" << pClient; - // Web client was disconnected - { - const auto it = std::find_if(m_webClientsMappingToEmail.cbegin(), m_webClientsMappingToEmail.cend(), [pClient](QWebSocket *webSocket) { - return pClient == webSocket; - }); - - if (it != m_webClientsMappingToEmail.cend()) { - const auto email = it.key(); - const auto nativeClient = m_nativeClientsMappingToEmail[email]; - qDebug() << "webclient with email disconnected" << email << nativeClient; - if (nativeClient) { - QJsonDocument doc(QJsonObject{ - { "type"_L1, "disconnection"_L1 }, - }); - nativeClient->sendTextMessage(QString::fromUtf8(doc.toJson())); - } - - m_webClientsMappingToEmail.removeIf([pClient](auto socket) { - return pClient == socket.value(); - }); - } - } - - // Native client was disconnected - const auto emails = m_nativeClientsMappingToEmail.keys(); - for (const auto &email : emails) { - const auto webSocket = m_nativeClientsMappingToEmail[email]; - if (webSocket != pClient) { - qDebug() << "webSocket not equal" << email << webSocket << pClient; - continue; - } - - qDebug() << "native client for" << email << "was disconnected."; - - QJsonDocument doc(QJsonObject{ - { "type"_L1, "disconnection"_L1 }, - }); - sendMessageToWebClient(email, doc.toJson()); - } - - m_nativeClientsMappingToEmail.removeIf([pClient](auto socket) { - return pClient == socket.value(); - }); - - m_clients.removeAll(pClient); - } -} diff --git a/broker/webserver.h b/broker/webserver.h deleted file mode 100644 index 98db50e..0000000 --- a/broker/webserver.h +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: 2023 g10 code GmbH -// SPDX-Contributor: Carl Schwan -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include - -QT_FORWARD_DECLARE_CLASS(QWebSocketServer) -QT_FORWARD_DECLARE_CLASS(QWebSocket) - -class WebsocketRequestBackend; -class QHttpServer; - -class WebServer : public QObject -{ - Q_OBJECT -public: - /// Get singleton instance of WebServer - static WebServer &self(); - - /// WebServer destructor. - ~WebServer() override; - - /// Start web server. - bool run(); - - /// is a valid WebServer instance - bool isValid() const; - - bool sendMessageToWebClient(const QString &email, const QByteArray &payload); - bool sendMessageToNativeClient(const QString &email, const QByteArray &payload); - -private Q_SLOTS: - void onNewConnection(); - void processTextMessage(QString message); - void processBinaryMessage(QByteArray message); - void socketDisconnected(); - -private: - WebServer(); - - enum class Command { - Undefined, ///< Undefined command. - Register, ///< Registration of a client (native or web). - EmailSent, ///< Confirmation that an email was sent. - }; - - enum SpecialValues { - Port = 5656, - WebSocketPort = 5657, - }; - - void processCommand(Command command, const QJsonObject &arguments, QWebSocket *socket); - - QHttpServer * const m_httpServer; - QWebSocketServer * const m_webSocketServer; - QList m_clients; - QHash m_webClientsMappingToEmail; - QHash m_nativeClientsMappingToEmail; - - static WebServer s_instance; -}; - diff --git a/server/CMakeLists.txt b/client/CMakeLists.txt similarity index 90% copy from server/CMakeLists.txt copy to client/CMakeLists.txt index 5194fae..59e7517 100644 --- a/server/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,287 +1,287 @@ # SPDX-FileCopyrightText: 2023 g10 code GmbH # SPDX-Contributor: Carl Schwan # SPDX-License-Identifier: BSD-2-Clause -add_library(gpgol-server-static STATIC) +add_library(gpgol-client-static STATIC) -target_sources(gpgol-server-static PRIVATE +target_sources(gpgol-client-static PRIVATE websocketclient.cpp websocketclient.h webserver.h webserver.cpp # Identity identity/addressvalidationjob.cpp identity/addressvalidationjob.h identity/identitymanager.cpp identity/identitymanager.h identity/identitydialog.cpp identity/identitydialog.h identity/identity.cpp identity/identity.h identity/signature.h identity/signature.cpp identity/signatureconfigurator.cpp identity/signatureconfigurator.h identity/signaturerichtexteditor.cpp identity/signaturerichtexteditor_p.h # HTTP Controller controllers/emailcontroller.cpp controllers/emailcontroller.h # Draft draft/draft.cpp draft/draft.h draft/draftmanager.cpp draft/draftmanager.h # EWS integration ews/ewsattachment.cpp ews/ewsattachment.h ews/ewsattendee.cpp ews/ewsattendee.h ews/ewsclient_debug.cpp ews/ewsclient_debug.h ews/ewsid.cpp ews/ewsid.h ews/ewsitem.cpp ews/ewsitem.h ews/ewsitembase.cpp ews/ewsitembase.h ews/ewsitembase_p.h ews/ewsmailbox.cpp ews/ewsmailbox.h ews/ewsmailfactory.cpp ews/ewsmailfactory.h ews/ewsoccurrence.cpp ews/ewsoccurrence.h ews/ewspropertyfield.cpp ews/ewspropertyfield.h ews/ewsrecurrence.cpp ews/ewsrecurrence.h ews/ewsserverversion.cpp ews/ewsserverversion.h ews/ewstypes.cpp ews/ewstypes.h ews/ewsxml.cpp ews/ewsxml.h # Editor editor/addresseelineedit.cpp editor/addresseelineedit.h editor/addresseelineeditmanager.cpp editor/addresseelineeditmanager.h editor/composer.cpp editor/composer.h editor/composerviewbase.cpp editor/composerviewbase.h editor/composerwindow.cpp editor/composerwindow.h editor/composerwindowfactory.cpp editor/composerwindowfactory.h editor/cryptostateindicatorwidget.cpp editor/cryptostateindicatorwidget.h editor/kmcomposerglobalaction.cpp editor/kmcomposerglobalaction.h editor/nearexpirywarning.cpp editor/nearexpirywarning.h editor/mailtemplates.cpp editor/mailtemplates.h editor/recipient.cpp editor/recipient.h editor/recipientline.cpp editor/recipientline.h editor/recipientseditor.cpp editor/recipientseditor.h editor/util.h editor/util.cpp editor/kmailcompletion.cpp editor/kmailcompletion.h editor/richtextcomposerng.cpp editor/richtextcomposerng.h editor/richtextcomposersignatures.cpp editor/richtextcomposersignatures.h editor/nodehelper.cpp editor/nodehelper.h editor/signaturecontroller.cpp editor/signaturecontroller.h editor/spellcheckerconfigdialog.cpp editor/spellcheckerconfigdialog.h # Editor job editor/job/abstractencryptjob.h editor/job/autocryptheadersjob.h editor/job/contentjobbase.h editor/job/contentjobbase_p.h editor/job/encryptjob.h editor/job/inserttextfilejob.h editor/job/itipjob.h editor/job/jobbase.h editor/job/jobbase_p.h editor/job/maintextjob.h editor/job/multipartjob.h editor/job/protectedheadersjob.h editor/job/signencryptjob.h editor/job/signjob.h editor/job/singlepartjob.h editor/job/skeletonmessagejob.h editor/job/transparentjob.h editor/job/autocryptheadersjob.cpp editor/job/contentjobbase.cpp editor/job/encryptjob.cpp editor/job/inserttextfilejob.cpp editor/job/itipjob.cpp editor/job/jobbase.cpp editor/job/maintextjob.cpp editor/job/multipartjob.cpp editor/job/protectedheadersjob.cpp editor/job/saveasfilejob.cpp editor/job/saveasfilejob.h editor/job/signencryptjob.cpp editor/job/signjob.cpp editor/job/singlepartjob.cpp editor/job/skeletonmessagejob.cpp editor/job/transparentjob.cpp ## Editor Part editor/part/globalpart.h editor/part/infopart.h editor/part/itippart.h editor/part/messagepart.h editor/part/textpart.h editor/part/globalpart.cpp editor/part/infopart.cpp editor/part/itippart.cpp editor/part/messagepart.cpp editor/part/textpart.cpp ## Attachment editor/attachment/attachmentjob.cpp editor/attachment/attachmentjob.h editor/attachment/attachmentclipboardjob.cpp editor/attachment/attachmentclipboardjob.h editor/attachment/attachmentcompressjob.cpp editor/attachment/attachmentcompressjob.h editor/attachment/attachmentcontroller.cpp editor/attachment/attachmentcontroller.h editor/attachment/attachmentcontrollerbase.cpp editor/attachment/attachmentcontrollerbase.h editor/attachment/attachmentfromfolderjob.cpp editor/attachment/attachmentfromfolderjob.h editor/attachment/attachmentfrommimecontentjob.cpp editor/attachment/attachmentfrommimecontentjob.h editor/attachment/attachmentfromurlbasejob.cpp editor/attachment/attachmentfromurlbasejob.h editor/attachment/attachmentfromurljob.cpp editor/attachment/attachmentfromurljob.h editor/attachment/attachmentfromurlutils.cpp editor/attachment/attachmentfromurlutils.h editor/attachment/attachmentfrompublickeyjob.cpp editor/attachment/attachmentfrompublickeyjob.h editor/attachment/attachmentloadjob.cpp editor/attachment/attachmentloadjob.h editor/attachment/attachmentmodel.cpp editor/attachment/attachmentmodel.h editor/attachment/attachmentpart.cpp editor/attachment/attachmentpart.h editor/attachment/attachmentpropertiesdialog.cpp editor/attachment/attachmentpropertiesdialog.h editor/attachment/attachmentupdatejob.cpp editor/attachment/attachmentupdatejob.h editor/attachment/attachmentview.cpp editor/attachment/attachmentview.h ) -qt_add_resources(gpgol-server-static +qt_add_resources(gpgol-client-static PREFIX "/" FILES assets/certificate.crt ) -ki18n_wrap_ui(gpgol-server-static +ki18n_wrap_ui(gpgol-client-static editor/attachment/ui/attachmentpropertiesdialog.ui editor/attachment/ui/attachmentpropertiesdialog_readonly.ui ) -ecm_qt_declare_logging_category(gpgol-server-static_SRCS +ecm_qt_declare_logging_category(gpgol-client-static_SRCS HEADER websocket_debug.h IDENTIFIER WEBSOCKET_LOG - CATEGORY_NAME org.gpgol.server.websocket - DESCRIPTION "Websocket connection in the server" + CATEGORY_NAME org.gpgol.client.websocket + DESCRIPTION "Websocket connection in the client" EXPORT GPGOL ) -ecm_qt_declare_logging_category(gpgol-server-static_SRCS +ecm_qt_declare_logging_category(gpgol-client-static_SRCS HEADER ewsresource_debug.h IDENTIFIER EWSRES_LOG CATEGORY_NAME org.gpgol.ews DESCRIPTION "Ews mail client" EXPORT GPGOL ) -ecm_qt_declare_logging_category(gpgol-server-static_SRCS +ecm_qt_declare_logging_category(gpgol-client-static_SRCS HEADER ewscli_debug.h IDENTIFIER EWSCLI_LOG CATEGORY_NAME org.gpgol.ews.client - DESCRIPTION "ews client (gpgol-server)" + DESCRIPTION "ews client (gpgol-client)" EXPORT GPGOL ) -ecm_qt_declare_logging_category(gpgol-server-static_SRCS +ecm_qt_declare_logging_category(gpgol-client-static_SRCS HEADER editor_debug.h IDENTIFIER EDITOR_LOG CATEGORY_NAME org.gpgol.editor DESCRIPTION "mail composer" EXPORT GPGOL ) set(WARN_TOOMANY_RECIPIENTS_DEFAULT true) set(ALLOW_SEMICOLON_AS_ADDRESS_SEPARATOR_DEFAULT true) configure_file(editor/settings/messagecomposer.kcfg.in ${CMAKE_CURRENT_BINARY_DIR}/messagecomposer.kcfg) -kconfig_add_kcfg_files(gpgol-server-static editor/settings/messagecomposersettings.kcfgc) +kconfig_add_kcfg_files(gpgol-client-static editor/settings/messagecomposersettings.kcfgc) -install(FILES composerui.rc DESTINATION ${KDE_INSTALL_KXMLGUIDIR}/gpgol-server) +install(FILES composerui.rc DESTINATION ${KDE_INSTALL_KXMLGUIDIR}/gpgol-client) -target_sources(gpgol-server-static PUBLIC ${gpgol-server-static_SRCS}) +target_sources(gpgol-client-static PUBLIC ${gpgol-client-static_SRCS}) -target_link_libraries(gpgol-server-static PUBLIC +target_link_libraries(gpgol-client-static PUBLIC common Qt6::HttpServer Qt6::Widgets Qt6::PrintSupport KF6::JobWidgets KF6::CalendarCore KF6::ConfigCore KF6::ConfigGui KF6::Contacts KF6::Completion KF6::CoreAddons KF6::ColorScheme KF6::Codecs KF6::GuiAddons KF6::SonnetUi KF6::WidgetsAddons KF6::XmlGui KF6::Archive KF6::TextAutoCorrectionCore KPim6::MimeTreeParserWidgets KPim6::Libkleo KPim6::Libkdepim KPim6::LdapWidgets KPim6::PimTextEdit ) -add_executable(gpgol-server main.cpp) -target_link_libraries(gpgol-server PRIVATE gpgol-server-static) +add_executable(gpgol-client main.cpp) +target_link_libraries(gpgol-client PRIVATE gpgol-client-static) if (BUILD_TESTING) add_subdirectory(autotests) endif() diff --git a/broker/assets/certificate.crt b/client/assets/certificate.crt similarity index 100% rename from broker/assets/certificate.crt rename to client/assets/certificate.crt diff --git a/client/autotests/CMakeLists.txt b/client/autotests/CMakeLists.txt new file mode 100644 index 0000000..1ce31c0 --- /dev/null +++ b/client/autotests/CMakeLists.txt @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2023 g10 code GmbH +# SPDX-Contributor: Carl Schwan +# SPDX-License-Identifier: BSD-2-Clause + +add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/testdata" ) + +ecm_add_test(emailcontrollertest.cpp + LINK_LIBRARIES gpgol-client-static Qt::Test + NAME_PREFIX "client-http-" +) + +ecm_add_test(globalparttest.cpp + LINK_LIBRARIES gpgol-client-static Qt::Test + NAME_PREFIX "client-part-" +) + +ecm_add_test(infoparttest.cpp + LINK_LIBRARIES gpgol-client-static Qt::Test + NAME_PREFIX "client-part-" +) + +ecm_add_test(itipjobtest.cpp + LINK_LIBRARIES gpgol-client-static Qt::Test + NAME_PREFIX "client-job-" +) + +ecm_add_test(multipartjobtest.cpp + LINK_LIBRARIES gpgol-client-static Qt::Test + NAME_PREFIX "client-job-" +) + +ecm_add_test(attachmentjobtest.cpp + LINK_LIBRARIES gpgol-client-static Qt::Test + NAME_PREFIX "client-job-" +) diff --git a/server/autotests/attachmentjobtest.cpp b/client/autotests/attachmentjobtest.cpp similarity index 100% rename from server/autotests/attachmentjobtest.cpp rename to client/autotests/attachmentjobtest.cpp diff --git a/server/autotests/attachmentjobtest.h b/client/autotests/attachmentjobtest.h similarity index 100% rename from server/autotests/attachmentjobtest.h rename to client/autotests/attachmentjobtest.h diff --git a/server/autotests/attachments/ascii.txt b/client/autotests/attachments/ascii.txt similarity index 100% rename from server/autotests/attachments/ascii.txt rename to client/autotests/attachments/ascii.txt diff --git a/server/autotests/attachments/iso8859-2.txt b/client/autotests/attachments/iso8859-2.txt similarity index 100% rename from server/autotests/attachments/iso8859-2.txt rename to client/autotests/attachments/iso8859-2.txt diff --git a/server/autotests/attachments/utf16.txt b/client/autotests/attachments/utf16.txt similarity index 100% rename from server/autotests/attachments/utf16.txt rename to client/autotests/attachments/utf16.txt diff --git a/server/autotests/emailcontrollertest.cpp b/client/autotests/emailcontrollertest.cpp similarity index 100% rename from server/autotests/emailcontrollertest.cpp rename to client/autotests/emailcontrollertest.cpp diff --git a/server/autotests/globalparttest.cpp b/client/autotests/globalparttest.cpp similarity index 100% rename from server/autotests/globalparttest.cpp rename to client/autotests/globalparttest.cpp diff --git a/server/autotests/globalparttest.h b/client/autotests/globalparttest.h similarity index 100% rename from server/autotests/globalparttest.h rename to client/autotests/globalparttest.h diff --git a/server/autotests/infoparttest.cpp b/client/autotests/infoparttest.cpp similarity index 100% rename from server/autotests/infoparttest.cpp rename to client/autotests/infoparttest.cpp diff --git a/server/autotests/infoparttest.h b/client/autotests/infoparttest.h similarity index 100% rename from server/autotests/infoparttest.h rename to client/autotests/infoparttest.h diff --git a/server/autotests/itipjobtest.cpp b/client/autotests/itipjobtest.cpp similarity index 100% rename from server/autotests/itipjobtest.cpp rename to client/autotests/itipjobtest.cpp diff --git a/server/autotests/itipjobtest.h b/client/autotests/itipjobtest.h similarity index 100% rename from server/autotests/itipjobtest.h rename to client/autotests/itipjobtest.h diff --git a/server/autotests/multipartjobtest.cpp b/client/autotests/multipartjobtest.cpp similarity index 100% rename from server/autotests/multipartjobtest.cpp rename to client/autotests/multipartjobtest.cpp diff --git a/server/autotests/multipartjobtest.h b/client/autotests/multipartjobtest.h similarity index 100% rename from server/autotests/multipartjobtest.h rename to client/autotests/multipartjobtest.h diff --git a/server/autotests/qtest_messagecomposer.h b/client/autotests/qtest_messagecomposer.h similarity index 100% rename from server/autotests/qtest_messagecomposer.h rename to client/autotests/qtest_messagecomposer.h diff --git a/server/autotests/signencrypttest.cpp b/client/autotests/signencrypttest.cpp similarity index 100% rename from server/autotests/signencrypttest.cpp rename to client/autotests/signencrypttest.cpp diff --git a/server/autotests/signencrypttest.h b/client/autotests/signencrypttest.h similarity index 100% rename from server/autotests/signencrypttest.h rename to client/autotests/signencrypttest.h diff --git a/server/autotests/testdata/encrypted.mbox b/client/autotests/testdata/encrypted.mbox similarity index 100% rename from server/autotests/testdata/encrypted.mbox rename to client/autotests/testdata/encrypted.mbox diff --git a/server/autotests/testdata/plaintext.mbox b/client/autotests/testdata/plaintext.mbox similarity index 100% rename from server/autotests/testdata/plaintext.mbox rename to client/autotests/testdata/plaintext.mbox diff --git a/server/composerui.rc b/client/composerui.rc similarity index 100% rename from server/composerui.rc rename to client/composerui.rc diff --git a/server/controllers/emailcontroller.cpp b/client/controllers/emailcontroller.cpp similarity index 100% copy from server/controllers/emailcontroller.cpp copy to client/controllers/emailcontroller.cpp diff --git a/server/controllers/emailcontroller.h b/client/controllers/emailcontroller.h similarity index 100% copy from server/controllers/emailcontroller.h copy to client/controllers/emailcontroller.h diff --git a/server/draft/draft.cpp b/client/draft/draft.cpp similarity index 100% rename from server/draft/draft.cpp rename to client/draft/draft.cpp diff --git a/server/draft/draft.h b/client/draft/draft.h similarity index 100% rename from server/draft/draft.h rename to client/draft/draft.h diff --git a/server/draft/draftmanager.cpp b/client/draft/draftmanager.cpp similarity index 100% rename from server/draft/draftmanager.cpp rename to client/draft/draftmanager.cpp diff --git a/server/draft/draftmanager.h b/client/draft/draftmanager.h similarity index 100% rename from server/draft/draftmanager.h rename to client/draft/draftmanager.h diff --git a/server/editor/addresseelineedit.cpp b/client/editor/addresseelineedit.cpp similarity index 100% rename from server/editor/addresseelineedit.cpp rename to client/editor/addresseelineedit.cpp diff --git a/server/editor/addresseelineedit.h b/client/editor/addresseelineedit.h similarity index 100% rename from server/editor/addresseelineedit.h rename to client/editor/addresseelineedit.h diff --git a/server/editor/addresseelineeditmanager.cpp b/client/editor/addresseelineeditmanager.cpp similarity index 100% rename from server/editor/addresseelineeditmanager.cpp rename to client/editor/addresseelineeditmanager.cpp diff --git a/server/editor/addresseelineeditmanager.h b/client/editor/addresseelineeditmanager.h similarity index 100% rename from server/editor/addresseelineeditmanager.h rename to client/editor/addresseelineeditmanager.h diff --git a/server/editor/attachment/attachmentclipboardjob.cpp b/client/editor/attachment/attachmentclipboardjob.cpp similarity index 100% rename from server/editor/attachment/attachmentclipboardjob.cpp rename to client/editor/attachment/attachmentclipboardjob.cpp diff --git a/server/editor/attachment/attachmentclipboardjob.h b/client/editor/attachment/attachmentclipboardjob.h similarity index 100% rename from server/editor/attachment/attachmentclipboardjob.h rename to client/editor/attachment/attachmentclipboardjob.h diff --git a/server/editor/attachment/attachmentcompressjob.cpp b/client/editor/attachment/attachmentcompressjob.cpp similarity index 100% rename from server/editor/attachment/attachmentcompressjob.cpp rename to client/editor/attachment/attachmentcompressjob.cpp diff --git a/server/editor/attachment/attachmentcompressjob.h b/client/editor/attachment/attachmentcompressjob.h similarity index 100% rename from server/editor/attachment/attachmentcompressjob.h rename to client/editor/attachment/attachmentcompressjob.h diff --git a/server/editor/attachment/attachmentcontroller.cpp b/client/editor/attachment/attachmentcontroller.cpp similarity index 100% rename from server/editor/attachment/attachmentcontroller.cpp rename to client/editor/attachment/attachmentcontroller.cpp diff --git a/server/editor/attachment/attachmentcontroller.h b/client/editor/attachment/attachmentcontroller.h similarity index 100% rename from server/editor/attachment/attachmentcontroller.h rename to client/editor/attachment/attachmentcontroller.h diff --git a/server/editor/attachment/attachmentcontrollerbase.cpp b/client/editor/attachment/attachmentcontrollerbase.cpp similarity index 100% rename from server/editor/attachment/attachmentcontrollerbase.cpp rename to client/editor/attachment/attachmentcontrollerbase.cpp diff --git a/server/editor/attachment/attachmentcontrollerbase.h b/client/editor/attachment/attachmentcontrollerbase.h similarity index 100% rename from server/editor/attachment/attachmentcontrollerbase.h rename to client/editor/attachment/attachmentcontrollerbase.h diff --git a/server/editor/attachment/attachmentfromfolderjob.cpp b/client/editor/attachment/attachmentfromfolderjob.cpp similarity index 100% rename from server/editor/attachment/attachmentfromfolderjob.cpp rename to client/editor/attachment/attachmentfromfolderjob.cpp diff --git a/server/editor/attachment/attachmentfromfolderjob.h b/client/editor/attachment/attachmentfromfolderjob.h similarity index 100% rename from server/editor/attachment/attachmentfromfolderjob.h rename to client/editor/attachment/attachmentfromfolderjob.h diff --git a/server/editor/attachment/attachmentfrommimecontentjob.cpp b/client/editor/attachment/attachmentfrommimecontentjob.cpp similarity index 100% rename from server/editor/attachment/attachmentfrommimecontentjob.cpp rename to client/editor/attachment/attachmentfrommimecontentjob.cpp diff --git a/server/editor/attachment/attachmentfrommimecontentjob.h b/client/editor/attachment/attachmentfrommimecontentjob.h similarity index 100% rename from server/editor/attachment/attachmentfrommimecontentjob.h rename to client/editor/attachment/attachmentfrommimecontentjob.h diff --git a/server/editor/attachment/attachmentfrompublickeyjob.cpp b/client/editor/attachment/attachmentfrompublickeyjob.cpp similarity index 100% rename from server/editor/attachment/attachmentfrompublickeyjob.cpp rename to client/editor/attachment/attachmentfrompublickeyjob.cpp diff --git a/server/editor/attachment/attachmentfrompublickeyjob.h b/client/editor/attachment/attachmentfrompublickeyjob.h similarity index 100% rename from server/editor/attachment/attachmentfrompublickeyjob.h rename to client/editor/attachment/attachmentfrompublickeyjob.h diff --git a/server/editor/attachment/attachmentfromurlbasejob.cpp b/client/editor/attachment/attachmentfromurlbasejob.cpp similarity index 100% rename from server/editor/attachment/attachmentfromurlbasejob.cpp rename to client/editor/attachment/attachmentfromurlbasejob.cpp diff --git a/server/editor/attachment/attachmentfromurlbasejob.h b/client/editor/attachment/attachmentfromurlbasejob.h similarity index 100% rename from server/editor/attachment/attachmentfromurlbasejob.h rename to client/editor/attachment/attachmentfromurlbasejob.h diff --git a/server/editor/attachment/attachmentfromurljob.cpp b/client/editor/attachment/attachmentfromurljob.cpp similarity index 100% rename from server/editor/attachment/attachmentfromurljob.cpp rename to client/editor/attachment/attachmentfromurljob.cpp diff --git a/server/editor/attachment/attachmentfromurljob.h b/client/editor/attachment/attachmentfromurljob.h similarity index 100% rename from server/editor/attachment/attachmentfromurljob.h rename to client/editor/attachment/attachmentfromurljob.h diff --git a/server/editor/attachment/attachmentfromurlutils.cpp b/client/editor/attachment/attachmentfromurlutils.cpp similarity index 100% rename from server/editor/attachment/attachmentfromurlutils.cpp rename to client/editor/attachment/attachmentfromurlutils.cpp diff --git a/server/editor/attachment/attachmentfromurlutils.h b/client/editor/attachment/attachmentfromurlutils.h similarity index 100% rename from server/editor/attachment/attachmentfromurlutils.h rename to client/editor/attachment/attachmentfromurlutils.h diff --git a/server/editor/attachment/attachmentjob.cpp b/client/editor/attachment/attachmentjob.cpp similarity index 100% rename from server/editor/attachment/attachmentjob.cpp rename to client/editor/attachment/attachmentjob.cpp diff --git a/server/editor/attachment/attachmentjob.h b/client/editor/attachment/attachmentjob.h similarity index 100% rename from server/editor/attachment/attachmentjob.h rename to client/editor/attachment/attachmentjob.h diff --git a/server/editor/attachment/attachmentloadjob.cpp b/client/editor/attachment/attachmentloadjob.cpp similarity index 100% rename from server/editor/attachment/attachmentloadjob.cpp rename to client/editor/attachment/attachmentloadjob.cpp diff --git a/server/editor/attachment/attachmentloadjob.h b/client/editor/attachment/attachmentloadjob.h similarity index 100% rename from server/editor/attachment/attachmentloadjob.h rename to client/editor/attachment/attachmentloadjob.h diff --git a/server/editor/attachment/attachmentmodel.cpp b/client/editor/attachment/attachmentmodel.cpp similarity index 100% rename from server/editor/attachment/attachmentmodel.cpp rename to client/editor/attachment/attachmentmodel.cpp diff --git a/server/editor/attachment/attachmentmodel.h b/client/editor/attachment/attachmentmodel.h similarity index 100% rename from server/editor/attachment/attachmentmodel.h rename to client/editor/attachment/attachmentmodel.h diff --git a/server/editor/attachment/attachmentpart.cpp b/client/editor/attachment/attachmentpart.cpp similarity index 100% rename from server/editor/attachment/attachmentpart.cpp rename to client/editor/attachment/attachmentpart.cpp diff --git a/server/editor/attachment/attachmentpart.h b/client/editor/attachment/attachmentpart.h similarity index 100% rename from server/editor/attachment/attachmentpart.h rename to client/editor/attachment/attachmentpart.h diff --git a/server/editor/attachment/attachmentpropertiesdialog.cpp b/client/editor/attachment/attachmentpropertiesdialog.cpp similarity index 100% rename from server/editor/attachment/attachmentpropertiesdialog.cpp rename to client/editor/attachment/attachmentpropertiesdialog.cpp diff --git a/server/editor/attachment/attachmentpropertiesdialog.h b/client/editor/attachment/attachmentpropertiesdialog.h similarity index 100% rename from server/editor/attachment/attachmentpropertiesdialog.h rename to client/editor/attachment/attachmentpropertiesdialog.h diff --git a/server/editor/attachment/attachmentupdatejob.cpp b/client/editor/attachment/attachmentupdatejob.cpp similarity index 100% rename from server/editor/attachment/attachmentupdatejob.cpp rename to client/editor/attachment/attachmentupdatejob.cpp diff --git a/server/editor/attachment/attachmentupdatejob.h b/client/editor/attachment/attachmentupdatejob.h similarity index 100% rename from server/editor/attachment/attachmentupdatejob.h rename to client/editor/attachment/attachmentupdatejob.h diff --git a/server/editor/attachment/attachmentview.cpp b/client/editor/attachment/attachmentview.cpp similarity index 100% rename from server/editor/attachment/attachmentview.cpp rename to client/editor/attachment/attachmentview.cpp diff --git a/server/editor/attachment/attachmentview.h b/client/editor/attachment/attachmentview.h similarity index 100% rename from server/editor/attachment/attachmentview.h rename to client/editor/attachment/attachmentview.h diff --git a/server/editor/attachment/ui/attachmentpropertiesdialog.ui b/client/editor/attachment/ui/attachmentpropertiesdialog.ui similarity index 100% rename from server/editor/attachment/ui/attachmentpropertiesdialog.ui rename to client/editor/attachment/ui/attachmentpropertiesdialog.ui diff --git a/server/editor/attachment/ui/attachmentpropertiesdialog_readonly.ui b/client/editor/attachment/ui/attachmentpropertiesdialog_readonly.ui similarity index 100% rename from server/editor/attachment/ui/attachmentpropertiesdialog_readonly.ui rename to client/editor/attachment/ui/attachmentpropertiesdialog_readonly.ui diff --git a/server/editor/attachmentaddedfromexternalwarning.cpp b/client/editor/attachmentaddedfromexternalwarning.cpp similarity index 100% rename from server/editor/attachmentaddedfromexternalwarning.cpp rename to client/editor/attachmentaddedfromexternalwarning.cpp diff --git a/server/editor/attachmentaddedfromexternalwarning.h b/client/editor/attachmentaddedfromexternalwarning.h similarity index 100% rename from server/editor/attachmentaddedfromexternalwarning.h rename to client/editor/attachmentaddedfromexternalwarning.h diff --git a/server/editor/attachmentmissingwarning.cpp b/client/editor/attachmentmissingwarning.cpp similarity index 100% rename from server/editor/attachmentmissingwarning.cpp rename to client/editor/attachmentmissingwarning.cpp diff --git a/server/editor/attachmentmissingwarning.h b/client/editor/attachmentmissingwarning.h similarity index 100% rename from server/editor/attachmentmissingwarning.h rename to client/editor/attachmentmissingwarning.h diff --git a/server/editor/composer.cpp b/client/editor/composer.cpp similarity index 100% rename from server/editor/composer.cpp rename to client/editor/composer.cpp diff --git a/server/editor/composer.h b/client/editor/composer.h similarity index 100% rename from server/editor/composer.h rename to client/editor/composer.h diff --git a/server/editor/composerviewbase.cpp b/client/editor/composerviewbase.cpp similarity index 100% rename from server/editor/composerviewbase.cpp rename to client/editor/composerviewbase.cpp diff --git a/server/editor/composerviewbase.h b/client/editor/composerviewbase.h similarity index 100% rename from server/editor/composerviewbase.h rename to client/editor/composerviewbase.h diff --git a/server/editor/composerwindow.cpp b/client/editor/composerwindow.cpp similarity index 100% rename from server/editor/composerwindow.cpp rename to client/editor/composerwindow.cpp diff --git a/server/editor/composerwindow.h b/client/editor/composerwindow.h similarity index 100% rename from server/editor/composerwindow.h rename to client/editor/composerwindow.h diff --git a/server/editor/composerwindowfactory.cpp b/client/editor/composerwindowfactory.cpp similarity index 100% rename from server/editor/composerwindowfactory.cpp rename to client/editor/composerwindowfactory.cpp diff --git a/server/editor/composerwindowfactory.h b/client/editor/composerwindowfactory.h similarity index 100% rename from server/editor/composerwindowfactory.h rename to client/editor/composerwindowfactory.h diff --git a/server/editor/cryptostateindicatorwidget.cpp b/client/editor/cryptostateindicatorwidget.cpp similarity index 100% rename from server/editor/cryptostateindicatorwidget.cpp rename to client/editor/cryptostateindicatorwidget.cpp diff --git a/server/editor/cryptostateindicatorwidget.h b/client/editor/cryptostateindicatorwidget.h similarity index 100% rename from server/editor/cryptostateindicatorwidget.h rename to client/editor/cryptostateindicatorwidget.h diff --git a/server/editor/externaleditorwarning.cpp b/client/editor/externaleditorwarning.cpp similarity index 100% rename from server/editor/externaleditorwarning.cpp rename to client/editor/externaleditorwarning.cpp diff --git a/server/editor/externaleditorwarning.h b/client/editor/externaleditorwarning.h similarity index 100% rename from server/editor/externaleditorwarning.h rename to client/editor/externaleditorwarning.h diff --git a/server/editor/incorrectidentityfolderwarning.cpp b/client/editor/incorrectidentityfolderwarning.cpp similarity index 100% rename from server/editor/incorrectidentityfolderwarning.cpp rename to client/editor/incorrectidentityfolderwarning.cpp diff --git a/server/editor/incorrectidentityfolderwarning.h b/client/editor/incorrectidentityfolderwarning.h similarity index 100% rename from server/editor/incorrectidentityfolderwarning.h rename to client/editor/incorrectidentityfolderwarning.h diff --git a/server/editor/job/abstractencryptjob.h b/client/editor/job/abstractencryptjob.h similarity index 100% rename from server/editor/job/abstractencryptjob.h rename to client/editor/job/abstractencryptjob.h diff --git a/server/editor/job/attachmentclipboardjob.cpp b/client/editor/job/attachmentclipboardjob.cpp similarity index 100% rename from server/editor/job/attachmentclipboardjob.cpp rename to client/editor/job/attachmentclipboardjob.cpp diff --git a/server/editor/job/attachmentclipboardjob.h b/client/editor/job/attachmentclipboardjob.h similarity index 100% rename from server/editor/job/attachmentclipboardjob.h rename to client/editor/job/attachmentclipboardjob.h diff --git a/server/editor/job/attachmentfrompublickeyjob.cpp b/client/editor/job/attachmentfrompublickeyjob.cpp similarity index 100% rename from server/editor/job/attachmentfrompublickeyjob.cpp rename to client/editor/job/attachmentfrompublickeyjob.cpp diff --git a/server/editor/job/attachmentfrompublickeyjob.h b/client/editor/job/attachmentfrompublickeyjob.h similarity index 100% rename from server/editor/job/attachmentfrompublickeyjob.h rename to client/editor/job/attachmentfrompublickeyjob.h diff --git a/server/editor/job/attachmentjob.cpp b/client/editor/job/attachmentjob.cpp similarity index 100% rename from server/editor/job/attachmentjob.cpp rename to client/editor/job/attachmentjob.cpp diff --git a/server/editor/job/attachmentjob.h b/client/editor/job/attachmentjob.h similarity index 100% rename from server/editor/job/attachmentjob.h rename to client/editor/job/attachmentjob.h diff --git a/server/editor/job/autocryptheadersjob.cpp b/client/editor/job/autocryptheadersjob.cpp similarity index 100% rename from server/editor/job/autocryptheadersjob.cpp rename to client/editor/job/autocryptheadersjob.cpp diff --git a/server/editor/job/autocryptheadersjob.h b/client/editor/job/autocryptheadersjob.h similarity index 100% rename from server/editor/job/autocryptheadersjob.h rename to client/editor/job/autocryptheadersjob.h diff --git a/server/editor/job/contentjobbase.cpp b/client/editor/job/contentjobbase.cpp similarity index 100% rename from server/editor/job/contentjobbase.cpp rename to client/editor/job/contentjobbase.cpp diff --git a/server/editor/job/contentjobbase.h b/client/editor/job/contentjobbase.h similarity index 100% rename from server/editor/job/contentjobbase.h rename to client/editor/job/contentjobbase.h diff --git a/server/editor/job/contentjobbase_p.h b/client/editor/job/contentjobbase_p.h similarity index 100% rename from server/editor/job/contentjobbase_p.h rename to client/editor/job/contentjobbase_p.h diff --git a/server/editor/job/encryptjob.cpp b/client/editor/job/encryptjob.cpp similarity index 100% rename from server/editor/job/encryptjob.cpp rename to client/editor/job/encryptjob.cpp diff --git a/server/editor/job/encryptjob.h b/client/editor/job/encryptjob.h similarity index 100% rename from server/editor/job/encryptjob.h rename to client/editor/job/encryptjob.h diff --git a/server/editor/job/inserttextfilejob.cpp b/client/editor/job/inserttextfilejob.cpp similarity index 100% rename from server/editor/job/inserttextfilejob.cpp rename to client/editor/job/inserttextfilejob.cpp diff --git a/server/editor/job/inserttextfilejob.h b/client/editor/job/inserttextfilejob.h similarity index 100% rename from server/editor/job/inserttextfilejob.h rename to client/editor/job/inserttextfilejob.h diff --git a/server/editor/job/itipjob.cpp b/client/editor/job/itipjob.cpp similarity index 100% rename from server/editor/job/itipjob.cpp rename to client/editor/job/itipjob.cpp diff --git a/server/editor/job/itipjob.h b/client/editor/job/itipjob.h similarity index 100% rename from server/editor/job/itipjob.h rename to client/editor/job/itipjob.h diff --git a/server/editor/job/jobbase.cpp b/client/editor/job/jobbase.cpp similarity index 100% rename from server/editor/job/jobbase.cpp rename to client/editor/job/jobbase.cpp diff --git a/server/editor/job/jobbase.h b/client/editor/job/jobbase.h similarity index 100% rename from server/editor/job/jobbase.h rename to client/editor/job/jobbase.h diff --git a/server/editor/job/jobbase_p.h b/client/editor/job/jobbase_p.h similarity index 100% rename from server/editor/job/jobbase_p.h rename to client/editor/job/jobbase_p.h diff --git a/server/editor/job/maintextjob.cpp b/client/editor/job/maintextjob.cpp similarity index 100% rename from server/editor/job/maintextjob.cpp rename to client/editor/job/maintextjob.cpp diff --git a/server/editor/job/maintextjob.h b/client/editor/job/maintextjob.h similarity index 100% rename from server/editor/job/maintextjob.h rename to client/editor/job/maintextjob.h diff --git a/server/editor/job/multipartjob.cpp b/client/editor/job/multipartjob.cpp similarity index 100% rename from server/editor/job/multipartjob.cpp rename to client/editor/job/multipartjob.cpp diff --git a/server/editor/job/multipartjob.h b/client/editor/job/multipartjob.h similarity index 100% rename from server/editor/job/multipartjob.h rename to client/editor/job/multipartjob.h diff --git a/server/editor/job/protectedheadersjob.cpp b/client/editor/job/protectedheadersjob.cpp similarity index 100% rename from server/editor/job/protectedheadersjob.cpp rename to client/editor/job/protectedheadersjob.cpp diff --git a/server/editor/job/protectedheadersjob.h b/client/editor/job/protectedheadersjob.h similarity index 100% rename from server/editor/job/protectedheadersjob.h rename to client/editor/job/protectedheadersjob.h diff --git a/server/editor/job/saveasfilejob.cpp b/client/editor/job/saveasfilejob.cpp similarity index 100% rename from server/editor/job/saveasfilejob.cpp rename to client/editor/job/saveasfilejob.cpp diff --git a/server/editor/job/saveasfilejob.h b/client/editor/job/saveasfilejob.h similarity index 100% rename from server/editor/job/saveasfilejob.h rename to client/editor/job/saveasfilejob.h diff --git a/server/editor/job/signencryptjob.cpp b/client/editor/job/signencryptjob.cpp similarity index 100% rename from server/editor/job/signencryptjob.cpp rename to client/editor/job/signencryptjob.cpp diff --git a/server/editor/job/signencryptjob.h b/client/editor/job/signencryptjob.h similarity index 100% rename from server/editor/job/signencryptjob.h rename to client/editor/job/signencryptjob.h diff --git a/server/editor/job/signjob.cpp b/client/editor/job/signjob.cpp similarity index 100% rename from server/editor/job/signjob.cpp rename to client/editor/job/signjob.cpp diff --git a/server/editor/job/signjob.h b/client/editor/job/signjob.h similarity index 100% rename from server/editor/job/signjob.h rename to client/editor/job/signjob.h diff --git a/server/editor/job/singlepartjob.cpp b/client/editor/job/singlepartjob.cpp similarity index 100% rename from server/editor/job/singlepartjob.cpp rename to client/editor/job/singlepartjob.cpp diff --git a/server/editor/job/singlepartjob.h b/client/editor/job/singlepartjob.h similarity index 100% rename from server/editor/job/singlepartjob.h rename to client/editor/job/singlepartjob.h diff --git a/server/editor/job/skeletonmessagejob.cpp b/client/editor/job/skeletonmessagejob.cpp similarity index 100% rename from server/editor/job/skeletonmessagejob.cpp rename to client/editor/job/skeletonmessagejob.cpp diff --git a/server/editor/job/skeletonmessagejob.h b/client/editor/job/skeletonmessagejob.h similarity index 100% rename from server/editor/job/skeletonmessagejob.h rename to client/editor/job/skeletonmessagejob.h diff --git a/server/editor/job/transparentjob.cpp b/client/editor/job/transparentjob.cpp similarity index 100% rename from server/editor/job/transparentjob.cpp rename to client/editor/job/transparentjob.cpp diff --git a/server/editor/job/transparentjob.h b/client/editor/job/transparentjob.h similarity index 100% rename from server/editor/job/transparentjob.h rename to client/editor/job/transparentjob.h diff --git a/server/editor/kleo_util.h b/client/editor/kleo_util.h similarity index 100% rename from server/editor/kleo_util.h rename to client/editor/kleo_util.h diff --git a/server/editor/kmailcompletion.cpp b/client/editor/kmailcompletion.cpp similarity index 100% rename from server/editor/kmailcompletion.cpp rename to client/editor/kmailcompletion.cpp diff --git a/server/editor/kmailcompletion.h b/client/editor/kmailcompletion.h similarity index 100% rename from server/editor/kmailcompletion.h rename to client/editor/kmailcompletion.h diff --git a/server/editor/kmcomposerglobalaction.cpp b/client/editor/kmcomposerglobalaction.cpp similarity index 100% rename from server/editor/kmcomposerglobalaction.cpp rename to client/editor/kmcomposerglobalaction.cpp diff --git a/server/editor/kmcomposerglobalaction.h b/client/editor/kmcomposerglobalaction.h similarity index 100% rename from server/editor/kmcomposerglobalaction.h rename to client/editor/kmcomposerglobalaction.h diff --git a/server/editor/mailtemplates.cpp b/client/editor/mailtemplates.cpp similarity index 100% rename from server/editor/mailtemplates.cpp rename to client/editor/mailtemplates.cpp diff --git a/server/editor/mailtemplates.h b/client/editor/mailtemplates.h similarity index 100% rename from server/editor/mailtemplates.h rename to client/editor/mailtemplates.h diff --git a/server/editor/nearexpirywarning.cpp b/client/editor/nearexpirywarning.cpp similarity index 100% rename from server/editor/nearexpirywarning.cpp rename to client/editor/nearexpirywarning.cpp diff --git a/server/editor/nearexpirywarning.h b/client/editor/nearexpirywarning.h similarity index 100% rename from server/editor/nearexpirywarning.h rename to client/editor/nearexpirywarning.h diff --git a/server/editor/nodehelper.cpp b/client/editor/nodehelper.cpp similarity index 100% rename from server/editor/nodehelper.cpp rename to client/editor/nodehelper.cpp diff --git a/server/editor/nodehelper.h b/client/editor/nodehelper.h similarity index 100% rename from server/editor/nodehelper.h rename to client/editor/nodehelper.h diff --git a/server/editor/part/globalpart.cpp b/client/editor/part/globalpart.cpp similarity index 100% rename from server/editor/part/globalpart.cpp rename to client/editor/part/globalpart.cpp diff --git a/server/editor/part/globalpart.h b/client/editor/part/globalpart.h similarity index 100% rename from server/editor/part/globalpart.h rename to client/editor/part/globalpart.h diff --git a/server/editor/part/infopart.cpp b/client/editor/part/infopart.cpp similarity index 100% rename from server/editor/part/infopart.cpp rename to client/editor/part/infopart.cpp diff --git a/server/editor/part/infopart.h b/client/editor/part/infopart.h similarity index 100% rename from server/editor/part/infopart.h rename to client/editor/part/infopart.h diff --git a/server/editor/part/itippart.cpp b/client/editor/part/itippart.cpp similarity index 100% rename from server/editor/part/itippart.cpp rename to client/editor/part/itippart.cpp diff --git a/server/editor/part/itippart.h b/client/editor/part/itippart.h similarity index 100% rename from server/editor/part/itippart.h rename to client/editor/part/itippart.h diff --git a/server/editor/part/messagepart.cpp b/client/editor/part/messagepart.cpp similarity index 100% rename from server/editor/part/messagepart.cpp rename to client/editor/part/messagepart.cpp diff --git a/server/editor/part/messagepart.h b/client/editor/part/messagepart.h similarity index 100% rename from server/editor/part/messagepart.h rename to client/editor/part/messagepart.h diff --git a/server/editor/part/textpart.cpp b/client/editor/part/textpart.cpp similarity index 100% rename from server/editor/part/textpart.cpp rename to client/editor/part/textpart.cpp diff --git a/server/editor/part/textpart.h b/client/editor/part/textpart.h similarity index 100% rename from server/editor/part/textpart.h rename to client/editor/part/textpart.h diff --git a/server/editor/recipient.cpp b/client/editor/recipient.cpp similarity index 100% rename from server/editor/recipient.cpp rename to client/editor/recipient.cpp diff --git a/server/editor/recipient.h b/client/editor/recipient.h similarity index 100% rename from server/editor/recipient.h rename to client/editor/recipient.h diff --git a/server/editor/recipientline.cpp b/client/editor/recipientline.cpp similarity index 100% rename from server/editor/recipientline.cpp rename to client/editor/recipientline.cpp diff --git a/server/editor/recipientline.h b/client/editor/recipientline.h similarity index 100% rename from server/editor/recipientline.h rename to client/editor/recipientline.h diff --git a/server/editor/recipientseditor.cpp b/client/editor/recipientseditor.cpp similarity index 100% rename from server/editor/recipientseditor.cpp rename to client/editor/recipientseditor.cpp diff --git a/server/editor/recipientseditor.h b/client/editor/recipientseditor.h similarity index 100% rename from server/editor/recipientseditor.h rename to client/editor/recipientseditor.h diff --git a/server/editor/richtextcomposerng.cpp b/client/editor/richtextcomposerng.cpp similarity index 100% rename from server/editor/richtextcomposerng.cpp rename to client/editor/richtextcomposerng.cpp diff --git a/server/editor/richtextcomposerng.h b/client/editor/richtextcomposerng.h similarity index 100% rename from server/editor/richtextcomposerng.h rename to client/editor/richtextcomposerng.h diff --git a/server/editor/richtextcomposersignatures.cpp b/client/editor/richtextcomposersignatures.cpp similarity index 100% rename from server/editor/richtextcomposersignatures.cpp rename to client/editor/richtextcomposersignatures.cpp diff --git a/server/editor/richtextcomposersignatures.h b/client/editor/richtextcomposersignatures.h similarity index 100% rename from server/editor/richtextcomposersignatures.h rename to client/editor/richtextcomposersignatures.h diff --git a/server/editor/settings/messagecomposer.kcfg.in b/client/editor/settings/messagecomposer.kcfg.in similarity index 100% rename from server/editor/settings/messagecomposer.kcfg.in rename to client/editor/settings/messagecomposer.kcfg.in diff --git a/server/editor/settings/messagecomposersettings.kcfgc b/client/editor/settings/messagecomposersettings.kcfgc similarity index 100% rename from server/editor/settings/messagecomposersettings.kcfgc rename to client/editor/settings/messagecomposersettings.kcfgc diff --git a/server/editor/signaturecontroller.cpp b/client/editor/signaturecontroller.cpp similarity index 100% rename from server/editor/signaturecontroller.cpp rename to client/editor/signaturecontroller.cpp diff --git a/server/editor/signaturecontroller.h b/client/editor/signaturecontroller.h similarity index 100% rename from server/editor/signaturecontroller.h rename to client/editor/signaturecontroller.h diff --git a/server/editor/spellcheckerconfigdialog.cpp b/client/editor/spellcheckerconfigdialog.cpp similarity index 100% rename from server/editor/spellcheckerconfigdialog.cpp rename to client/editor/spellcheckerconfigdialog.cpp diff --git a/server/editor/spellcheckerconfigdialog.h b/client/editor/spellcheckerconfigdialog.h similarity index 100% rename from server/editor/spellcheckerconfigdialog.h rename to client/editor/spellcheckerconfigdialog.h diff --git a/server/editor/toomanyrecipientswarning.cpp b/client/editor/toomanyrecipientswarning.cpp similarity index 100% rename from server/editor/toomanyrecipientswarning.cpp rename to client/editor/toomanyrecipientswarning.cpp diff --git a/server/editor/toomanyrecipientswarning.h b/client/editor/toomanyrecipientswarning.h similarity index 100% rename from server/editor/toomanyrecipientswarning.h rename to client/editor/toomanyrecipientswarning.h diff --git a/server/editor/urlrequester.cpp b/client/editor/urlrequester.cpp similarity index 100% rename from server/editor/urlrequester.cpp rename to client/editor/urlrequester.cpp diff --git a/server/editor/urlrequester.h b/client/editor/urlrequester.h similarity index 100% rename from server/editor/urlrequester.h rename to client/editor/urlrequester.h diff --git a/server/editor/util.cpp b/client/editor/util.cpp similarity index 100% rename from server/editor/util.cpp rename to client/editor/util.cpp diff --git a/server/editor/util.h b/client/editor/util.h similarity index 100% rename from server/editor/util.h rename to client/editor/util.h diff --git a/server/editor/util_p.h b/client/editor/util_p.h similarity index 100% rename from server/editor/util_p.h rename to client/editor/util_p.h diff --git a/server/ews/ewsattachment.cpp b/client/ews/ewsattachment.cpp similarity index 100% rename from server/ews/ewsattachment.cpp rename to client/ews/ewsattachment.cpp diff --git a/server/ews/ewsattachment.h b/client/ews/ewsattachment.h similarity index 100% rename from server/ews/ewsattachment.h rename to client/ews/ewsattachment.h diff --git a/server/ews/ewsattendee.cpp b/client/ews/ewsattendee.cpp similarity index 100% rename from server/ews/ewsattendee.cpp rename to client/ews/ewsattendee.cpp diff --git a/server/ews/ewsattendee.h b/client/ews/ewsattendee.h similarity index 100% rename from server/ews/ewsattendee.h rename to client/ews/ewsattendee.h diff --git a/server/ews/ewsclient_debug.cpp b/client/ews/ewsclient_debug.cpp similarity index 100% rename from server/ews/ewsclient_debug.cpp rename to client/ews/ewsclient_debug.cpp diff --git a/server/ews/ewsclient_debug.h b/client/ews/ewsclient_debug.h similarity index 100% rename from server/ews/ewsclient_debug.h rename to client/ews/ewsclient_debug.h diff --git a/server/ews/ewsid.cpp b/client/ews/ewsid.cpp similarity index 100% rename from server/ews/ewsid.cpp rename to client/ews/ewsid.cpp diff --git a/server/ews/ewsid.h b/client/ews/ewsid.h similarity index 100% rename from server/ews/ewsid.h rename to client/ews/ewsid.h diff --git a/server/ews/ewsitem.cpp b/client/ews/ewsitem.cpp similarity index 100% rename from server/ews/ewsitem.cpp rename to client/ews/ewsitem.cpp diff --git a/server/ews/ewsitem.h b/client/ews/ewsitem.h similarity index 100% rename from server/ews/ewsitem.h rename to client/ews/ewsitem.h diff --git a/server/ews/ewsitembase.cpp b/client/ews/ewsitembase.cpp similarity index 100% rename from server/ews/ewsitembase.cpp rename to client/ews/ewsitembase.cpp diff --git a/server/ews/ewsitembase.h b/client/ews/ewsitembase.h similarity index 100% rename from server/ews/ewsitembase.h rename to client/ews/ewsitembase.h diff --git a/server/ews/ewsitembase_p.h b/client/ews/ewsitembase_p.h similarity index 100% rename from server/ews/ewsitembase_p.h rename to client/ews/ewsitembase_p.h diff --git a/server/ews/ewsmailbox.cpp b/client/ews/ewsmailbox.cpp similarity index 100% rename from server/ews/ewsmailbox.cpp rename to client/ews/ewsmailbox.cpp diff --git a/server/ews/ewsmailbox.h b/client/ews/ewsmailbox.h similarity index 100% rename from server/ews/ewsmailbox.h rename to client/ews/ewsmailbox.h diff --git a/server/ews/ewsmailfactory.cpp b/client/ews/ewsmailfactory.cpp similarity index 100% rename from server/ews/ewsmailfactory.cpp rename to client/ews/ewsmailfactory.cpp diff --git a/server/ews/ewsmailfactory.h b/client/ews/ewsmailfactory.h similarity index 100% rename from server/ews/ewsmailfactory.h rename to client/ews/ewsmailfactory.h diff --git a/server/ews/ewsoccurrence.cpp b/client/ews/ewsoccurrence.cpp similarity index 100% rename from server/ews/ewsoccurrence.cpp rename to client/ews/ewsoccurrence.cpp diff --git a/server/ews/ewsoccurrence.h b/client/ews/ewsoccurrence.h similarity index 100% rename from server/ews/ewsoccurrence.h rename to client/ews/ewsoccurrence.h diff --git a/server/ews/ewspropertyfield.cpp b/client/ews/ewspropertyfield.cpp similarity index 100% rename from server/ews/ewspropertyfield.cpp rename to client/ews/ewspropertyfield.cpp diff --git a/server/ews/ewspropertyfield.h b/client/ews/ewspropertyfield.h similarity index 100% rename from server/ews/ewspropertyfield.h rename to client/ews/ewspropertyfield.h diff --git a/server/ews/ewsrecurrence.cpp b/client/ews/ewsrecurrence.cpp similarity index 100% rename from server/ews/ewsrecurrence.cpp rename to client/ews/ewsrecurrence.cpp diff --git a/server/ews/ewsrecurrence.h b/client/ews/ewsrecurrence.h similarity index 100% rename from server/ews/ewsrecurrence.h rename to client/ews/ewsrecurrence.h diff --git a/server/ews/ewsserverversion.cpp b/client/ews/ewsserverversion.cpp similarity index 100% rename from server/ews/ewsserverversion.cpp rename to client/ews/ewsserverversion.cpp diff --git a/server/ews/ewsserverversion.h b/client/ews/ewsserverversion.h similarity index 100% rename from server/ews/ewsserverversion.h rename to client/ews/ewsserverversion.h diff --git a/server/ews/ewstypes.cpp b/client/ews/ewstypes.cpp similarity index 100% rename from server/ews/ewstypes.cpp rename to client/ews/ewstypes.cpp diff --git a/server/ews/ewstypes.h b/client/ews/ewstypes.h similarity index 100% rename from server/ews/ewstypes.h rename to client/ews/ewstypes.h diff --git a/server/ews/ewsxml.cpp b/client/ews/ewsxml.cpp similarity index 100% rename from server/ews/ewsxml.cpp rename to client/ews/ewsxml.cpp diff --git a/server/ews/ewsxml.h b/client/ews/ewsxml.h similarity index 100% rename from server/ews/ewsxml.h rename to client/ews/ewsxml.h diff --git a/server/identity/addressvalidationjob.cpp b/client/identity/addressvalidationjob.cpp similarity index 100% rename from server/identity/addressvalidationjob.cpp rename to client/identity/addressvalidationjob.cpp diff --git a/server/identity/addressvalidationjob.h b/client/identity/addressvalidationjob.h similarity index 100% rename from server/identity/addressvalidationjob.h rename to client/identity/addressvalidationjob.h diff --git a/server/identity/identity.cpp b/client/identity/identity.cpp similarity index 100% rename from server/identity/identity.cpp rename to client/identity/identity.cpp diff --git a/server/identity/identity.h b/client/identity/identity.h similarity index 100% rename from server/identity/identity.h rename to client/identity/identity.h diff --git a/server/identity/identitydialog.cpp b/client/identity/identitydialog.cpp similarity index 100% rename from server/identity/identitydialog.cpp rename to client/identity/identitydialog.cpp diff --git a/server/identity/identitydialog.h b/client/identity/identitydialog.h similarity index 100% rename from server/identity/identitydialog.h rename to client/identity/identitydialog.h diff --git a/server/identity/identityfolderrequester.cpp b/client/identity/identityfolderrequester.cpp similarity index 100% rename from server/identity/identityfolderrequester.cpp rename to client/identity/identityfolderrequester.cpp diff --git a/server/identity/identityfolderrequester.h b/client/identity/identityfolderrequester.h similarity index 100% rename from server/identity/identityfolderrequester.h rename to client/identity/identityfolderrequester.h diff --git a/server/identity/identitymanager.cpp b/client/identity/identitymanager.cpp similarity index 100% rename from server/identity/identitymanager.cpp rename to client/identity/identitymanager.cpp diff --git a/server/identity/identitymanager.h b/client/identity/identitymanager.h similarity index 100% rename from server/identity/identitymanager.h rename to client/identity/identitymanager.h diff --git a/server/identity/kleo_util.h b/client/identity/kleo_util.h similarity index 100% rename from server/identity/kleo_util.h rename to client/identity/kleo_util.h diff --git a/server/identity/signature.cpp b/client/identity/signature.cpp similarity index 100% rename from server/identity/signature.cpp rename to client/identity/signature.cpp diff --git a/server/identity/signature.h b/client/identity/signature.h similarity index 100% rename from server/identity/signature.h rename to client/identity/signature.h diff --git a/server/identity/signatureconfigurator.cpp b/client/identity/signatureconfigurator.cpp similarity index 100% rename from server/identity/signatureconfigurator.cpp rename to client/identity/signatureconfigurator.cpp diff --git a/server/identity/signatureconfigurator.h b/client/identity/signatureconfigurator.h similarity index 100% rename from server/identity/signatureconfigurator.h rename to client/identity/signatureconfigurator.h diff --git a/server/identity/signaturerichtexteditor.cpp b/client/identity/signaturerichtexteditor.cpp similarity index 100% rename from server/identity/signaturerichtexteditor.cpp rename to client/identity/signaturerichtexteditor.cpp diff --git a/server/identity/signaturerichtexteditor_p.h b/client/identity/signaturerichtexteditor_p.h similarity index 100% rename from server/identity/signaturerichtexteditor_p.h rename to client/identity/signaturerichtexteditor_p.h diff --git a/server/main.cpp b/client/main.cpp similarity index 100% copy from server/main.cpp copy to client/main.cpp diff --git a/server/qnam.h b/client/qnam.h similarity index 100% rename from server/qnam.h rename to client/qnam.h diff --git a/server/webserver.cpp b/client/webserver.cpp similarity index 100% copy from server/webserver.cpp copy to client/webserver.cpp diff --git a/server/webserver.h b/client/webserver.h similarity index 100% copy from server/webserver.h copy to client/webserver.h diff --git a/server/websocketclient.cpp b/client/websocketclient.cpp similarity index 100% rename from server/websocketclient.cpp rename to client/websocketclient.cpp diff --git a/server/websocketclient.h b/client/websocketclient.h similarity index 100% rename from server/websocketclient.h rename to client/websocketclient.h diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 5194fae..265e642 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,287 +1,45 @@ # SPDX-FileCopyrightText: 2023 g10 code GmbH # SPDX-Contributor: Carl Schwan # SPDX-License-Identifier: BSD-2-Clause -add_library(gpgol-server-static STATIC) - -target_sources(gpgol-server-static PRIVATE - websocketclient.cpp - websocketclient.h - webserver.h - webserver.cpp - - # Identity - identity/addressvalidationjob.cpp - identity/addressvalidationjob.h - identity/identitymanager.cpp - identity/identitymanager.h - identity/identitydialog.cpp - identity/identitydialog.h - identity/identity.cpp - identity/identity.h - identity/signature.h - identity/signature.cpp - identity/signatureconfigurator.cpp - identity/signatureconfigurator.h - identity/signaturerichtexteditor.cpp - identity/signaturerichtexteditor_p.h - - # HTTP Controller +add_executable(gpgol-server) +target_sources(gpgol-server PRIVATE + # Controllers + controllers/abstractcontroller.cpp + controllers/abstractcontroller.h + controllers/registrationcontroller.cpp + controllers/registrationcontroller.h + controllers/staticcontroller.h + controllers/staticcontroller.cpp controllers/emailcontroller.cpp controllers/emailcontroller.h - # Draft - draft/draft.cpp - draft/draft.h - draft/draftmanager.cpp - draft/draftmanager.h - - # EWS integration - ews/ewsattachment.cpp - ews/ewsattachment.h - ews/ewsattendee.cpp - ews/ewsattendee.h - ews/ewsclient_debug.cpp - ews/ewsclient_debug.h - ews/ewsid.cpp - ews/ewsid.h - ews/ewsitem.cpp - ews/ewsitem.h - ews/ewsitembase.cpp - ews/ewsitembase.h - ews/ewsitembase_p.h - ews/ewsmailbox.cpp - ews/ewsmailbox.h - ews/ewsmailfactory.cpp - ews/ewsmailfactory.h - ews/ewsoccurrence.cpp - ews/ewsoccurrence.h - ews/ewspropertyfield.cpp - ews/ewspropertyfield.h - ews/ewsrecurrence.cpp - ews/ewsrecurrence.h - ews/ewsserverversion.cpp - ews/ewsserverversion.h - ews/ewstypes.cpp - ews/ewstypes.h - ews/ewsxml.cpp - ews/ewsxml.h - - # Editor - editor/addresseelineedit.cpp - editor/addresseelineedit.h - editor/addresseelineeditmanager.cpp - editor/addresseelineeditmanager.h - editor/composer.cpp - editor/composer.h - editor/composerviewbase.cpp - editor/composerviewbase.h - editor/composerwindow.cpp - editor/composerwindow.h - editor/composerwindowfactory.cpp - editor/composerwindowfactory.h - editor/cryptostateindicatorwidget.cpp - editor/cryptostateindicatorwidget.h - editor/kmcomposerglobalaction.cpp - editor/kmcomposerglobalaction.h - editor/nearexpirywarning.cpp - editor/nearexpirywarning.h - editor/mailtemplates.cpp - editor/mailtemplates.h - editor/recipient.cpp - editor/recipient.h - editor/recipientline.cpp - editor/recipientline.h - editor/recipientseditor.cpp - editor/recipientseditor.h - editor/util.h - editor/util.cpp - editor/kmailcompletion.cpp - editor/kmailcompletion.h - editor/richtextcomposerng.cpp - editor/richtextcomposerng.h - editor/richtextcomposersignatures.cpp - editor/richtextcomposersignatures.h - editor/nodehelper.cpp - editor/nodehelper.h - editor/signaturecontroller.cpp - editor/signaturecontroller.h - editor/spellcheckerconfigdialog.cpp - editor/spellcheckerconfigdialog.h - - # Editor job - editor/job/abstractencryptjob.h - editor/job/autocryptheadersjob.h - editor/job/contentjobbase.h - editor/job/contentjobbase_p.h - editor/job/encryptjob.h - editor/job/inserttextfilejob.h - editor/job/itipjob.h - editor/job/jobbase.h - editor/job/jobbase_p.h - editor/job/maintextjob.h - editor/job/multipartjob.h - editor/job/protectedheadersjob.h - editor/job/signencryptjob.h - editor/job/signjob.h - editor/job/singlepartjob.h - editor/job/skeletonmessagejob.h - editor/job/transparentjob.h - editor/job/autocryptheadersjob.cpp - editor/job/contentjobbase.cpp - editor/job/encryptjob.cpp - editor/job/inserttextfilejob.cpp - editor/job/itipjob.cpp - editor/job/jobbase.cpp - editor/job/maintextjob.cpp - editor/job/multipartjob.cpp - editor/job/protectedheadersjob.cpp - editor/job/saveasfilejob.cpp - editor/job/saveasfilejob.h - editor/job/signencryptjob.cpp - editor/job/signjob.cpp - editor/job/singlepartjob.cpp - editor/job/skeletonmessagejob.cpp - editor/job/transparentjob.cpp + # State + model/serverstate.cpp + model/serverstate.h - ## Editor Part - editor/part/globalpart.h - editor/part/infopart.h - editor/part/itippart.h - editor/part/messagepart.h - editor/part/textpart.h - editor/part/globalpart.cpp - editor/part/infopart.cpp - editor/part/itippart.cpp - editor/part/messagepart.cpp - editor/part/textpart.cpp + # web sever + webserver.cpp + webserver.h - ## Attachment - editor/attachment/attachmentjob.cpp - editor/attachment/attachmentjob.h - editor/attachment/attachmentclipboardjob.cpp - editor/attachment/attachmentclipboardjob.h - editor/attachment/attachmentcompressjob.cpp - editor/attachment/attachmentcompressjob.h - editor/attachment/attachmentcontroller.cpp - editor/attachment/attachmentcontroller.h - editor/attachment/attachmentcontrollerbase.cpp - editor/attachment/attachmentcontrollerbase.h - editor/attachment/attachmentfromfolderjob.cpp - editor/attachment/attachmentfromfolderjob.h - editor/attachment/attachmentfrommimecontentjob.cpp - editor/attachment/attachmentfrommimecontentjob.h - editor/attachment/attachmentfromurlbasejob.cpp - editor/attachment/attachmentfromurlbasejob.h - editor/attachment/attachmentfromurljob.cpp - editor/attachment/attachmentfromurljob.h - editor/attachment/attachmentfromurlutils.cpp - editor/attachment/attachmentfromurlutils.h - editor/attachment/attachmentfrompublickeyjob.cpp - editor/attachment/attachmentfrompublickeyjob.h - editor/attachment/attachmentloadjob.cpp - editor/attachment/attachmentloadjob.h - editor/attachment/attachmentmodel.cpp - editor/attachment/attachmentmodel.h - editor/attachment/attachmentpart.cpp - editor/attachment/attachmentpart.h - editor/attachment/attachmentpropertiesdialog.cpp - editor/attachment/attachmentpropertiesdialog.h - editor/attachment/attachmentupdatejob.cpp - editor/attachment/attachmentupdatejob.h - editor/attachment/attachmentview.cpp - editor/attachment/attachmentview.h + main.cpp ) -qt_add_resources(gpgol-server-static +qt_add_resources(gpgol-server PREFIX "/" FILES assets/certificate.crt -) + assets/private.key + assets/document-decrypt-16.png + assets/document-decrypt-32.png + assets/document-decrypt-64.png + assets/document-decrypt-80.png -ki18n_wrap_ui(gpgol-server-static - editor/attachment/ui/attachmentpropertiesdialog.ui - editor/attachment/ui/attachmentpropertiesdialog_readonly.ui -) + assets/script.js -ecm_qt_declare_logging_category(gpgol-server-static_SRCS - HEADER websocket_debug.h - IDENTIFIER WEBSOCKET_LOG - CATEGORY_NAME org.gpgol.server.websocket - DESCRIPTION "Websocket connection in the server" - EXPORT GPGOL + web/index.html ) -ecm_qt_declare_logging_category(gpgol-server-static_SRCS - HEADER ewsresource_debug.h - IDENTIFIER EWSRES_LOG - CATEGORY_NAME org.gpgol.ews - DESCRIPTION "Ews mail client" - EXPORT GPGOL -) - -ecm_qt_declare_logging_category(gpgol-server-static_SRCS - HEADER ewscli_debug.h - IDENTIFIER EWSCLI_LOG - CATEGORY_NAME org.gpgol.ews.client - DESCRIPTION "ews client (gpgol-server)" - EXPORT GPGOL -) - -ecm_qt_declare_logging_category(gpgol-server-static_SRCS - HEADER editor_debug.h - IDENTIFIER EDITOR_LOG - CATEGORY_NAME org.gpgol.editor - DESCRIPTION "mail composer" - EXPORT GPGOL -) - -set(WARN_TOOMANY_RECIPIENTS_DEFAULT true) -set(ALLOW_SEMICOLON_AS_ADDRESS_SEPARATOR_DEFAULT true) - -configure_file(editor/settings/messagecomposer.kcfg.in ${CMAKE_CURRENT_BINARY_DIR}/messagecomposer.kcfg) - -kconfig_add_kcfg_files(gpgol-server-static editor/settings/messagecomposersettings.kcfgc) - -install(FILES composerui.rc DESTINATION ${KDE_INSTALL_KXMLGUIDIR}/gpgol-server) - -target_sources(gpgol-server-static PUBLIC ${gpgol-server-static_SRCS}) - -target_link_libraries(gpgol-server-static PUBLIC - common - - Qt6::HttpServer - Qt6::Widgets - Qt6::PrintSupport - - KF6::JobWidgets - KF6::CalendarCore - KF6::ConfigCore - KF6::ConfigGui - KF6::Contacts - KF6::Completion - KF6::CoreAddons - KF6::ColorScheme - KF6::Codecs - KF6::GuiAddons - KF6::SonnetUi - KF6::WidgetsAddons - KF6::XmlGui - KF6::Archive - KF6::TextAutoCorrectionCore - - KPim6::MimeTreeParserWidgets - KPim6::Libkleo - KPim6::Libkdepim - KPim6::LdapWidgets - KPim6::PimTextEdit -) - -add_executable(gpgol-server main.cpp) -target_link_libraries(gpgol-server PRIVATE gpgol-server-static) - -if (BUILD_TESTING) - add_subdirectory(autotests) -endif() +target_link_libraries(gpgol-server PRIVATE Qt6::HttpServer Qt6::Core common) diff --git a/broker/assets/document-decrypt-16.png b/server/assets/document-decrypt-16.png similarity index 100% rename from broker/assets/document-decrypt-16.png rename to server/assets/document-decrypt-16.png diff --git a/broker/assets/document-decrypt-32.png b/server/assets/document-decrypt-32.png similarity index 100% rename from broker/assets/document-decrypt-32.png rename to server/assets/document-decrypt-32.png diff --git a/broker/assets/document-decrypt-64.png b/server/assets/document-decrypt-64.png similarity index 100% rename from broker/assets/document-decrypt-64.png rename to server/assets/document-decrypt-64.png diff --git a/broker/assets/document-decrypt-80.png b/server/assets/document-decrypt-80.png similarity index 100% rename from broker/assets/document-decrypt-80.png rename to server/assets/document-decrypt-80.png diff --git a/broker/assets/icon-128.png b/server/assets/icon-128.png similarity index 100% rename from broker/assets/icon-128.png rename to server/assets/icon-128.png diff --git a/broker/assets/icon-16.png b/server/assets/icon-16.png similarity index 100% rename from broker/assets/icon-16.png rename to server/assets/icon-16.png diff --git a/broker/assets/icon-32.png b/server/assets/icon-32.png similarity index 100% rename from broker/assets/icon-32.png rename to server/assets/icon-32.png diff --git a/broker/assets/icon-64.png b/server/assets/icon-64.png similarity index 100% rename from broker/assets/icon-64.png rename to server/assets/icon-64.png diff --git a/broker/assets/icon-80.png b/server/assets/icon-80.png similarity index 100% rename from broker/assets/icon-80.png rename to server/assets/icon-80.png diff --git a/broker/assets/logo-filled.png b/server/assets/logo-filled.png similarity index 100% rename from broker/assets/logo-filled.png rename to server/assets/logo-filled.png diff --git a/broker/assets/private.key b/server/assets/private.key similarity index 100% rename from broker/assets/private.key rename to server/assets/private.key diff --git a/broker/assets/script.js b/server/assets/script.js similarity index 100% rename from broker/assets/script.js rename to server/assets/script.js diff --git a/server/autotests/CMakeLists.txt b/server/autotests/CMakeLists.txt deleted file mode 100644 index ce4fc43..0000000 --- a/server/autotests/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-FileCopyrightText: 2023 g10 code GmbH -# SPDX-Contributor: Carl Schwan -# SPDX-License-Identifier: BSD-2-Clause - -add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/testdata" ) - -ecm_add_test(emailcontrollertest.cpp - LINK_LIBRARIES gpgol-server-static Qt::Test - NAME_PREFIX "server-http-" -) - -ecm_add_test(globalparttest.cpp - LINK_LIBRARIES gpgol-server-static Qt::Test - NAME_PREFIX "server-part-" -) - -ecm_add_test(infoparttest.cpp - LINK_LIBRARIES gpgol-server-static Qt::Test - NAME_PREFIX "server-part-" -) - -ecm_add_test(itipjobtest.cpp - LINK_LIBRARIES gpgol-server-static Qt::Test - NAME_PREFIX "server-job-" -) - -ecm_add_test(multipartjobtest.cpp - LINK_LIBRARIES gpgol-server-static Qt::Test - NAME_PREFIX "server-job-" -) - -ecm_add_test(attachmentjobtest.cpp - LINK_LIBRARIES gpgol-server-static Qt::Test - NAME_PREFIX "server-job-" -) diff --git a/broker/controllers/abstractcontroller.cpp b/server/controllers/abstractcontroller.cpp similarity index 100% rename from broker/controllers/abstractcontroller.cpp rename to server/controllers/abstractcontroller.cpp diff --git a/broker/controllers/abstractcontroller.h b/server/controllers/abstractcontroller.h similarity index 100% rename from broker/controllers/abstractcontroller.h rename to server/controllers/abstractcontroller.h diff --git a/broker/controllers/draftcontroller.h b/server/controllers/draftcontroller.h similarity index 100% rename from broker/controllers/draftcontroller.h rename to server/controllers/draftcontroller.h diff --git a/server/controllers/emailcontroller.cpp b/server/controllers/emailcontroller.cpp index 7188bec..39e67e7 100644 --- a/server/controllers/emailcontroller.cpp +++ b/server/controllers/emailcontroller.cpp @@ -1,178 +1,163 @@ // SPDX-FileCopyrightText: 2023 g10 code GmbH // SPDX-Contributor: Carl Schwan // SPDX-License-Identifier: GPL-2.0-or-later #include "emailcontroller.h" -#include -#include -#include #include -#include -#include -#include -#include -#include - -#include "editor/composerwindow.h" -#include "editor/composerwindowfactory.h" -#include "draft/draftmanager.h" +#include +#include +#include +#include +#include +#include +#include #include +#include "webserver.h" + using namespace Qt::Literals::StringLiterals; -QHttpServerResponse EmailController::viewEmailAction(const QHttpServerRequest &request) +QHttpServerResponse EmailController::abstractEmailAction(const QHttpServerRequest &request, const QString &action, QHttpServerRequest::Method method) { - const auto email = QString::fromUtf8(Utils::findHeader(request.headers(), "X-EMAIL")); - const auto displayName = QString::fromUtf8(Utils::findHeader(request.headers(), "X-NAME")); - const auto bearerToken = Utils::findHeader(request.headers(), "X-TOKEN"); - - const auto content = request.body(); - KMime::Message::Ptr message(new KMime::Message()); - message->setContent(KMime::CRLFtoLF(content)); - message->parse(); - - auto dialog = new MimeTreeParser::Widgets::MessageViewerDialog({ message }); - dialog->setAttribute(Qt::WA_DeleteOnClose); - auto toolBar = dialog->toolBar(); - toolBar->show(); - - // spacer - QWidget* spacer = new QWidget(); - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - toolBar->addWidget(spacer); - - // reply - auto replyAction = new QAction(QIcon::fromTheme(u"mail-reply-sender-symbolic"_s), i18nc("@action:button", "Reply"), toolBar); - QObject::connect(replyAction, &QAction::triggered, dialog, [message, email, displayName, bearerToken](bool) { - auto dialog = ComposerWindowFactory::self().create(email, displayName, bearerToken); - dialog->reply(message); - dialog->show(); - }); - toolBar->addAction(replyAction); - auto widget = qobject_cast(toolBar->widgetForAction(replyAction)); - widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - // forward - auto forwardAction = new QAction(QIcon::fromTheme(u"mail-forward-symbolic"_s), i18nc("@action:button", "Forward"), toolBar); - QObject::connect(forwardAction, &QAction::triggered, dialog, [message, email, displayName, bearerToken](bool) { - auto dialog = ComposerWindowFactory::self().create(email, displayName, bearerToken); - dialog->reply(message); - dialog->show(); - }); - toolBar->addAction(forwardAction); - widget = qobject_cast(toolBar->widgetForAction(forwardAction)); - widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - dialog->show(); + const auto server = checkAuthentification(request); + if (!server) { + return forbidden(); + } + + QNetworkRequest viewEmailRequest(QUrl(u"http://127.0.0.1:"_s + QString::number(server->port) + u'/' + action)); + viewEmailRequest.setHeader(QNetworkRequest::ContentTypeHeader, u"application/json"_s); + auto email = Utils::findHeader(request.headers(), "X-EMAIL"); + auto displayName = Utils::findHeader(request.headers(), "X-NAME"); + auto token = QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8(); + viewEmailRequest.setRawHeader("X-EMAIL", email); + viewEmailRequest.setRawHeader("X-TOKEN", token); + viewEmailRequest.setRawHeader("X-NAME", displayName); + + auto &serverState = ServerState::instance(); + serverState.composerRequest[token] = QString::fromUtf8(email); + + + auto &state = ServerState::instance(); + QNetworkReply *reply; + if (method == QHttpServerRequest::Method::Post) { + const auto body = request.body(); + reply = state.qnam.post(viewEmailRequest, body); + } else { + reply = state.qnam.deleteResource(viewEmailRequest); + } + + QObject::connect(reply, &QNetworkReply::finished, reply, [reply]() { + if (reply->error() != QNetworkReply::NoError) { + qWarning() << reply->error() << reply->errorString(); + } else { + qWarning() << "sent request to view message to server"; + } + }); return QHttpServerResponse(QJsonObject { - { "status"_L1, "200"_L1 } + {"status"_L1, "ok"_L1}, }); } -QHttpServerResponse EmailController::infoEmailAction(const QHttpServerRequest &request) +QHttpServerResponse EmailController::viewEmailAction(const QHttpServerRequest &request) { - qDebug() << "request received"; - const auto content = request.body(); - KMime::Message::Ptr message(new KMime::Message()); - message->setContent(KMime::CRLFtoLF(content)); - message->parse(); - - MimeTreeParser::ObjectTreeParser treeParser; - treeParser.parseObjectTree(message.get()); + if (request.method() != QHttpServerRequest::Method::Post) { + return badRequest(u"Endpoint only supports POST request"_s); + } - return QHttpServerResponse(QJsonObject { - { "status"_L1, "200"_L1 }, - { "encrypted"_L1, treeParser.hasEncryptedParts() }, - { "signed"_L1, treeParser.hasSignedParts() }, - { "drafts"_L1, DraftManager::self().toJson() }, - }); + return abstractEmailAction(request, u"view"_s); } QHttpServerResponse EmailController::newEmailAction(const QHttpServerRequest &request) { - const auto email = QString::fromUtf8(Utils::findHeader(request.headers(), "X-EMAIL")); - const auto displayName = QString::fromUtf8(Utils::findHeader(request.headers(), "X-NAME")); - const auto bearerToken = Utils::findHeader(request.headers(), "X-TOKEN"); + if (request.method() != QHttpServerRequest::Method::Post) { + return badRequest(u"Endpoint only supports POST request"_s); + } - auto dialog = ComposerWindowFactory::self().create(email, displayName, bearerToken); - dialog->show(); + return abstractEmailAction(request, u"new"_s); +} - return QHttpServerResponse(QJsonObject { - { "status"_L1, "200"_L1 } - }); +QHttpServerResponse EmailController::draftAction(QString, const QHttpServerRequest &request) +{ + if (request.method() != QHttpServerRequest::Method::Post && request.method() != QHttpServerRequest::Method::Delete) { + return badRequest(u"Endpoint only supports POST request"_s); + } + + return abstractEmailAction(request, request.url().path(), request.method()); } QHttpServerResponse EmailController::replyEmailAction(const QHttpServerRequest &request) { - const auto email = QString::fromUtf8(Utils::findHeader(request.headers(), "X-EMAIL")); - const auto displayName = QString::fromUtf8(Utils::findHeader(request.headers(), "X-NAME")); - const auto bearerToken = Utils::findHeader(request.headers(), "X-TOKEN"); + if (request.method() != QHttpServerRequest::Method::Post) { + return badRequest(u"Endpoint only supports POST request"_s); + } - const auto content = request.body(); - KMime::Message::Ptr message(new KMime::Message()); - message->setContent(KMime::CRLFtoLF(content)); - message->parse(); + return abstractEmailAction(request, u"reply"_s); +} - auto dialog = ComposerWindowFactory::self().create(email, displayName, bearerToken); - dialog->reply(message); - dialog->show(); +QHttpServerResponse EmailController::forwardEmailAction(const QHttpServerRequest &request) +{ + if (request.method() != QHttpServerRequest::Method::Post) { + return badRequest(u"Endpoint only supports POST request"_s); + } - return QHttpServerResponse(QJsonObject { - { "status"_L1, "200"_L1 } - }); + return abstractEmailAction(request, u"forward"_s); } -QHttpServerResponse EmailController::forwardEmailAction(const QHttpServerRequest &request) +QHttpServerResponse checkStatus(int port, const QByteArray &body) { - const auto email = QString::fromUtf8(Utils::findHeader(request.headers(), "X-EMAIL")); - const auto displayName = QString::fromUtf8(Utils::findHeader(request.headers(), "X-NAME")); - const auto bearerToken = Utils::findHeader(request.headers(), "X-TOKEN"); + QNetworkRequest infoEmailRequest(QUrl(u"http://127.0.0.1:"_s + QString::number(port) + u"/info"_s)); + infoEmailRequest.setHeader(QNetworkRequest::ContentTypeHeader, u"application/json"_s); + + auto &state = ServerState::instance(); + QEventLoop eventLoop; + auto reply = state.qnam.post(infoEmailRequest, body); + QObject::connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit); + eventLoop.exec(); + + QJsonParseError error; + const auto resultBody = QJsonDocument::fromJson(reply->readAll(), &error); + if (resultBody.isNull()) { + return QHttpServerResponse(QHttpServerResponse::StatusCode::BadRequest); + } - const auto content = request.body(); - KMime::Message::Ptr message(new KMime::Message()); - message->setContent(KMime::CRLFtoLF(content)); - message->parse(); + if (!resultBody.isObject()) { + return QHttpServerResponse(QHttpServerResponse::StatusCode::BadRequest); + } - auto dialog = ComposerWindowFactory::self().create(email, displayName, bearerToken); - dialog->forward(message); - dialog->show(); + return QHttpServerResponse{resultBody.object()}; +} - return QHttpServerResponse(QJsonObject { - { "status"_L1, "200"_L1 } - }); +QHttpServerResponse EmailController::infoEmailAction(const QHttpServerRequest &request) +{ + if (request.method() != QHttpServerRequest::Method::Post) { + return badRequest(u"Endpoint only supports POST request"_s); + } + + const auto server = checkAuthentification(request); + if (!server) { + return forbidden(); + } + + return checkStatus(server->port, request.body()); } -QHttpServerResponse EmailController::draftAction(QString draftId, const QHttpServerRequest &request) +QHttpServerResponse EmailController::socketWebAction(const QHttpServerRequest &request) { const auto email = QString::fromUtf8(Utils::findHeader(request.headers(), "X-EMAIL")); - const auto displayName = QString::fromUtf8(Utils::findHeader(request.headers(), "X-NAME")); - const auto bearerToken = Utils::findHeader(request.headers(), "X-TOKEN"); - - const auto draft = DraftManager::self().draftById(draftId.toUtf8()); + const auto token = Utils::findHeader(request.headers(), "X-TOKEN"); + const auto &serverState = ServerState::instance(); - if (!draft.isValid()) { - return QHttpServerResponse(QJsonObject { - { "status"_L1, "404"_L1 } - }, QHttpServerResponse::StatusCode::NotFound); + qDebug() << serverState.composerRequest << email << token; + if (serverState.composerRequest[token] != email) { + return forbidden(); } - if (request.method() == QHttpServerRequest::Method::Post) { - // POST - auto dialog = ComposerWindowFactory::self().create(email, displayName, bearerToken); - dialog->setMessage(draft.mime()); - dialog->show(); - } else { - // DELETE - if (!DraftManager::self().remove(draft)) { - return QHttpServerResponse(QJsonObject { - { "status"_L1, "500"_L1 } - }, QHttpServerResponse::StatusCode::InternalServerError); - } - } + WebServer::self().sendMessageToWebClient(email, request.body()); - return QHttpServerResponse(QJsonObject { - { "status"_L1, "200"_L1 } + return QHttpServerResponse(QJsonObject{ + { "status"_L1, "OK"_L1 }, }); } diff --git a/server/controllers/emailcontroller.h b/server/controllers/emailcontroller.h index 6553ec3..ead4a20 100644 --- a/server/controllers/emailcontroller.h +++ b/server/controllers/emailcontroller.h @@ -1,19 +1,27 @@ // SPDX-FileCopyrightText: 2023 g10 code GmbH // SPDX-Contributor: Carl Schwan // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "abstractcontroller.h" + #include -#include -class EmailController +class EmailController : public AbstractController { public: + // Request from the web client static QHttpServerResponse viewEmailAction(const QHttpServerRequest &request); static QHttpServerResponse infoEmailAction(const QHttpServerRequest &request); static QHttpServerResponse newEmailAction(const QHttpServerRequest &request); - static QHttpServerResponse forwardEmailAction(const QHttpServerRequest &request); static QHttpServerResponse replyEmailAction(const QHttpServerRequest &request); - static QHttpServerResponse draftAction(QString draftId, const QHttpServerRequest &request); -}; \ No newline at end of file + static QHttpServerResponse forwardEmailAction(const QHttpServerRequest &request); + static QHttpServerResponse draftAction(QString draft, const QHttpServerRequest &request); + + /// Forward request from the native client to the web client + static QHttpServerResponse socketWebAction(const QHttpServerRequest &request); + +private: + static QHttpServerResponse abstractEmailAction(const QHttpServerRequest &request, const QString &action, QHttpServerRequest::Method method = QHttpServerRequest::Method::Post); +}; diff --git a/broker/controllers/registrationcontroller.cpp b/server/controllers/registrationcontroller.cpp similarity index 100% rename from broker/controllers/registrationcontroller.cpp rename to server/controllers/registrationcontroller.cpp diff --git a/broker/controllers/registrationcontroller.h b/server/controllers/registrationcontroller.h similarity index 100% rename from broker/controllers/registrationcontroller.h rename to server/controllers/registrationcontroller.h diff --git a/broker/controllers/staticcontroller.cpp b/server/controllers/staticcontroller.cpp similarity index 100% rename from broker/controllers/staticcontroller.cpp rename to server/controllers/staticcontroller.cpp diff --git a/broker/controllers/staticcontroller.h b/server/controllers/staticcontroller.h similarity index 100% rename from broker/controllers/staticcontroller.h rename to server/controllers/staticcontroller.h diff --git a/server/main.cpp b/server/main.cpp index 2663051..a1b7bcb 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -1,50 +1,30 @@ -// SPDX-FileCopyrightText: 2023 g10 code Gmbh +// SPDX-FileCopyrightText: 2023 g10 code GmbH // SPDX-Contributor: Carl Schwan // SPDX-License-Identifier: GPL-2.0-or-later -#include "controllers/emailcontroller.h" - -#include -#include -#include +#include +#include #include #include -#include -#include -#include +#include #include -#include -#include +#include +#include -#include "websocketclient.h" #include "webserver.h" -#include "qnam.h" using namespace Qt::Literals::StringLiterals; -using namespace std::chrono; int main(int argc, char *argv[]) { - QApplication app(argc, argv); - app.setQuitOnLastWindowClosed(false); - - KLocalizedString::setApplicationDomain(QByteArrayLiteral("gpgol")); + QCoreApplication app(argc, argv); - QObject::connect(qnam, &QNetworkAccessManager::sslErrors, qnam, [](QNetworkReply *reply, const QList &) { - reply->ignoreSslErrors(); - }); + auto &webServer = WebServer::self(); - WebServer server; - server.run(); - if (!server.running()) { + if (!webServer.run()) { qWarning() << "Server failed to listen on a port."; return 1; } - const auto port = server.port(); - - WebsocketClient::self(QUrl(u"wss://localhost:5657/"_s), port); - auto keyCache = Kleo::KeyCache::mutableInstance(); - keyCache->startKeyListing(); return app.exec(); } diff --git a/broker/manifest.xml b/server/manifest.xml similarity index 100% rename from broker/manifest.xml rename to server/manifest.xml diff --git a/broker/model/serverstate.cpp b/server/model/serverstate.cpp similarity index 100% rename from broker/model/serverstate.cpp rename to server/model/serverstate.cpp diff --git a/broker/model/serverstate.h b/server/model/serverstate.h similarity index 100% rename from broker/model/serverstate.h rename to server/model/serverstate.h diff --git a/broker/web/index.html b/server/web/index.html similarity index 100% rename from broker/web/index.html rename to server/web/index.html diff --git a/server/webserver.cpp b/server/webserver.cpp index b67066a..4e7da5a 100644 --- a/server/webserver.cpp +++ b/server/webserver.cpp @@ -1,46 +1,300 @@ -// SPDX-FileCopyrightText: 2024 g10 code Gmbh +// SPDX-FileCopyrightText: 2023 g10 code GmbH // SPDX-Contributor: Carl Schwan // SPDX-License-Identifier: GPL-2.0-or-later #include "webserver.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "controllers/registrationcontroller.h" +#include "controllers/staticcontroller.h" #include "controllers/emailcontroller.h" using namespace Qt::Literals::StringLiterals; -WebServer::WebServer(QObject *parent) - : QObject(parent) +WebServer WebServer::s_instance = WebServer(); + +WebServer &WebServer::self() { - m_server.route(u"/view"_s, &EmailController::viewEmailAction); - m_server.route(u"/info"_s, &EmailController::infoEmailAction); - m_server.route(u"/new"_s, &EmailController::newEmailAction); - m_server.route(u"/forward"_s, &EmailController::forwardEmailAction); - m_server.route(u"/reply"_s, &EmailController::replyEmailAction); - m_server.route(u"/draft/"_s, &EmailController::draftAction); + return s_instance; +} + +WebServer::WebServer() + : QObject(nullptr) + , m_httpServer(new QHttpServer(this)) + , m_webSocketServer(new QWebSocketServer(u"GPGOL"_s, QWebSocketServer::SslMode::SecureMode, this)) +{ +} + +WebServer::~WebServer() = default; + +bool WebServer::run() +{ + QFile privateKeyFile(QStringLiteral(":/assets/private.key")); + if (!privateKeyFile.open(QIODevice::ReadOnly)) { + qWarning() << u"Couldn't open file for reading: %1"_s.arg(privateKeyFile.errorString()); + return false; + } + const QSslKey sslKey(&privateKeyFile, QSsl::Rsa); + privateKeyFile.close(); + + const auto sslCertificateChain = + QSslCertificate::fromPath(QStringLiteral(":/assets/certificate.crt")); + if (sslCertificateChain.isEmpty()) { + qWarning() << u"Couldn't retrieve SSL certificate from file."_s; + return false; + } + + // Static assets controller + m_httpServer->route(u"/home"_s, &StaticController::homeAction); + m_httpServer->route(u"/assets/"_s, &StaticController::assetsAction); + + // Registration controller + m_httpServer->route(u"/register"_s, &RegistrationController::registerAction); + + // Email controller + m_httpServer->route(u"/view"_s, &EmailController::viewEmailAction); + m_httpServer->route(u"/info"_s, &EmailController::infoEmailAction); + m_httpServer->route(u"/reply"_s, &EmailController::replyEmailAction); + m_httpServer->route(u"/forward"_s, &EmailController::forwardEmailAction); + m_httpServer->route(u"/new"_s, &EmailController::newEmailAction); + m_httpServer->route(u"/socket-web"_s, &EmailController::socketWebAction); + + m_httpServer->route(u"/draft/"_s, &EmailController::draftAction); - m_server.afterRequest([](QHttpServerResponse &&resp) { + m_httpServer->afterRequest([](QHttpServerResponse &&resp) { resp.setHeader("Access-Control-Allow-Origin", "*"); return std::move(resp); }); + + m_httpServer->sslSetup(sslCertificateChain.front(), sslKey); + + const auto port = m_httpServer->listen(QHostAddress::Any, WebServer::Port); + if (!port) { + qWarning() << "Server failed to listen on a port."; + return false; + } + qWarning() << u"Running http server on https://127.0.0.1:%1/ (Press CTRL+C to quit)"_s.arg(port); + + + QSslConfiguration sslConfiguration; + sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); + sslConfiguration.setLocalCertificate(sslCertificateChain.front()); + sslConfiguration.setPrivateKey(sslKey); + m_webSocketServer->setSslConfiguration(sslConfiguration); + + if (m_webSocketServer->listen(QHostAddress::Any, WebServer::WebSocketPort)) { + qWarning() << u"Running websocket server on wss://127.0.0.1:%1/ (Press CTRL+C to quit)"_s.arg(WebServer::Port + 1); + connect(m_webSocketServer, &QWebSocketServer::newConnection, this, &WebServer::onNewConnection); + } + + + return true; } -void WebServer::run() +void WebServer::onNewConnection() { - m_port = m_server.listen(); - if (!m_port) { - qWarning() << "Server failed to listen on a port."; - m_running = false; + auto pSocket = m_webSocketServer->nextPendingConnection(); + if (!pSocket) { + return; + } + + qDebug() << "Client connected:" << pSocket->peerName() << pSocket->origin(); + + connect(pSocket, &QWebSocket::textMessageReceived, this, &WebServer::processTextMessage); + connect(pSocket, &QWebSocket::binaryMessageReceived, + this, &WebServer::processBinaryMessage); + connect(pSocket, &QWebSocket::disconnected, this, &WebServer::socketDisconnected); + + m_clients << pSocket; +} + +void WebServer::processTextMessage(QString message) +{ + auto webClient = qobject_cast(sender()); + if (webClient) { + QJsonParseError error; + const auto doc = QJsonDocument::fromJson(message.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "Error parsing json" << error.errorString(); + return; + } + + if (!doc.isObject()) { + qWarning() << "Invalid json received"; + return; + } + + const auto object = doc.object(); + if (!object.contains("command"_L1) || !object["command"_L1].isString() + || !object.contains("arguments"_L1) || !object["arguments"_L1].isObject()) { + qWarning() << "Invalid json received: no command or arguments set" ; + return; + } + + static QHash commandMapping { + { "register"_L1, WebServer::Command::Register }, + { "email-sent"_L1, WebServer::Command::EmailSent }, + }; + + const auto command = commandMapping[doc["command"_L1].toString()]; + + processCommand(command, object["arguments"_L1].toObject(), webClient); + } +} + +void WebServer::processCommand(Command command, const QJsonObject &arguments, QWebSocket *socket) +{ + switch (command) { + case Command::Register: { + const auto type = arguments["type"_L1].toString(); + qDebug() << "Register" << arguments; + if (type.isEmpty()) { + qWarning() << "empty client type given when registering"; + return; + } + + const auto emails = arguments["emails"_L1].toArray(); + if (type == "webclient"_L1) { + if (emails.isEmpty()) { + qWarning() << "empty email given"; + } + for (const auto &email : emails) { + m_webClientsMappingToEmail[email.toString()] = socket; + qWarning() << "email" << email.toString() << "mapped to a web client"; + + const auto nativeClient = m_nativeClientsMappingToEmail[email.toString()]; + if (nativeClient) { + QJsonDocument doc(QJsonObject{ + { "type"_L1, "connection"_L1 }, + { "payload"_L1, QJsonObject{ + { "client_type"_L1, "web_client"_L1 } + }} + }); + nativeClient->sendTextMessage(QString::fromUtf8(doc.toJson())); + } + } + } else { + if (emails.isEmpty()) { + qWarning() << "empty email given"; + } + for (const auto &email : emails) { + m_nativeClientsMappingToEmail[email.toString()] = socket; + qWarning() << "email" << email.toString() << "mapped to a native client"; + + const auto webClient = m_webClientsMappingToEmail[email.toString()]; + if (webClient) { + QJsonDocument doc(QJsonObject{ + { "type"_L1, "connection"_L1 }, + { "payload"_L1, QJsonObject{ + { "client_type"_L1, "native_client"_L1 } + }} + }); + webClient->sendTextMessage(QString::fromUtf8(doc.toJson())); + } + } + } + return; + } + case Command::EmailSent: { + const auto email = arguments["email"_L1].toString(); + const auto socket = m_nativeClientsMappingToEmail[email]; + if (!socket) { + return; + } + QJsonDocument doc(QJsonObject{ + { "type"_L1, "email-sent"_L1 }, + { "arguments"_L1, arguments }, + }); + socket->sendTextMessage(QString::fromUtf8(doc.toJson())); return; } - qWarning() << u"Running on http://127.0.0.1:%1/ (Press CTRL+C to quit)"_s.arg(m_port); - m_running = true; + case Command::Undefined: + qWarning() << "Invalid json received: invalid command" ; + return; + } +} + +bool WebServer::sendMessageToWebClient(const QString &email, const QByteArray &payload) +{ + auto socket = m_webClientsMappingToEmail[email]; + if (!socket) { + return false; + } + + socket->sendTextMessage(QString::fromUtf8(payload)); + return true; } -int WebServer::port() const +void WebServer::processBinaryMessage(QByteArray message) { - return m_port; + qWarning() << "got binary message" << message; + QWebSocket *pClient = qobject_cast(sender()); + if (pClient) { + pClient->sendBinaryMessage(message); + } } -bool WebServer::running() const +void WebServer::socketDisconnected() { - return m_running; + QWebSocket *pClient = qobject_cast(sender()); + if (pClient) { + qDebug() << "Client disconnected" << pClient; + // Web client was disconnected + { + const auto it = std::find_if(m_webClientsMappingToEmail.cbegin(), m_webClientsMappingToEmail.cend(), [pClient](QWebSocket *webSocket) { + return pClient == webSocket; + }); + + if (it != m_webClientsMappingToEmail.cend()) { + const auto email = it.key(); + const auto nativeClient = m_nativeClientsMappingToEmail[email]; + qDebug() << "webclient with email disconnected" << email << nativeClient; + if (nativeClient) { + QJsonDocument doc(QJsonObject{ + { "type"_L1, "disconnection"_L1 }, + }); + nativeClient->sendTextMessage(QString::fromUtf8(doc.toJson())); + } + + m_webClientsMappingToEmail.removeIf([pClient](auto socket) { + return pClient == socket.value(); + }); + } + } + + // Native client was disconnected + const auto emails = m_nativeClientsMappingToEmail.keys(); + for (const auto &email : emails) { + const auto webSocket = m_nativeClientsMappingToEmail[email]; + if (webSocket != pClient) { + qDebug() << "webSocket not equal" << email << webSocket << pClient; + continue; + } + + qDebug() << "native client for" << email << "was disconnected."; + + QJsonDocument doc(QJsonObject{ + { "type"_L1, "disconnection"_L1 }, + }); + sendMessageToWebClient(email, doc.toJson()); + } + + m_nativeClientsMappingToEmail.removeIf([pClient](auto socket) { + return pClient == socket.value(); + }); + + m_clients.removeAll(pClient); + } } diff --git a/server/webserver.h b/server/webserver.h index 44aeab8..98db50e 100644 --- a/server/webserver.h +++ b/server/webserver.h @@ -1,33 +1,67 @@ -// SPDX-FileCopyrightText: 2024 g10 code Gmbh +// SPDX-FileCopyrightText: 2023 g10 code GmbH // SPDX-Contributor: Carl Schwan // SPDX-License-Identifier: GPL-2.0-or-later -#include +#pragma once + #include +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QWebSocketServer) +QT_FORWARD_DECLARE_CLASS(QWebSocket) + +class WebsocketRequestBackend; +class QHttpServer; -/// The webserver of the native client -/// -/// This webserver will receive webrequests from the outlook web client via -/// the broker. class WebServer : public QObject { Q_OBJECT public: - /// Default contructor. - WebServer(QObject *parent = nullptr); + /// Get singleton instance of WebServer + static WebServer &self(); - /// Start webserver. - void run(); + /// WebServer destructor. + ~WebServer() override; - /// \return the port of the webserver. Qt will randomly assign a free port to this - /// process. - int port() const; + /// Start web server. + bool run(); - /// \return whether the webserver is running. - bool running() const; + /// is a valid WebServer instance + bool isValid() const; + + bool sendMessageToWebClient(const QString &email, const QByteArray &payload); + bool sendMessageToNativeClient(const QString &email, const QByteArray &payload); + +private Q_SLOTS: + void onNewConnection(); + void processTextMessage(QString message); + void processBinaryMessage(QByteArray message); + void socketDisconnected(); private: - int m_port; - bool m_running = false; - QHttpServer m_server; + WebServer(); + + enum class Command { + Undefined, ///< Undefined command. + Register, ///< Registration of a client (native or web). + EmailSent, ///< Confirmation that an email was sent. + }; + + enum SpecialValues { + Port = 5656, + WebSocketPort = 5657, + }; + + void processCommand(Command command, const QJsonObject &arguments, QWebSocket *socket); + + QHttpServer * const m_httpServer; + QWebSocketServer * const m_webSocketServer; + QList m_clients; + QHash m_webClientsMappingToEmail; + QHash m_nativeClientsMappingToEmail; + + static WebServer s_instance; }; +