Page MenuHome GnuPG

No OneTemporary

diff --git a/broker/assets/script.js b/broker/assets/script.js
index a4cddf4..3861b30 100644
--- a/broker/assets/script.js
+++ b/broker/assets/script.js
@@ -1,278 +1,278 @@
// SPDX-FileCopyrightText: 2023 g10 code GmbH
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
function downloadViaRest(callback) {
const context = {
isRest: true
};
Office.context.mailbox.getCallbackTokenAsync(context, (tokenResults) => {
if (tokenResults.status === Office.AsyncResultStatus.Failed) {
console.error('Failed to get rest api auth token');
return;
}
const request =
'<?xml version="1.0" encoding="utf-8"?>' +
'<soap:Envelope xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"' +
' xmlns:xsd="https://www.w3.org/2001/XMLSchema"' +
' xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"' +
' xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">' +
' <soap:Header>' +
' <RequestServerVersion Version="Exchange2013" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" soap:mustUnderstand="0" />' +
' </soap:Header>' +
' <soap:Body>' +
' <GetItem xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">' +
' <ItemShape>' +
' <t:BaseShape>IdOnly</t:BaseShape>' +
' <t:IncludeMimeContent>true</t:IncludeMimeContent>' +
' </ItemShape>' +
' <ItemIds><t:ItemId Id="' + Office.context.mailbox.item.itemId + '"/></ItemIds>' +
' </GetItem>' +
' </soap:Body>' +
'</soap:Envelope>';
Office.context.mailbox.makeEwsRequestAsync(request, (asyncResult) => {
const parser = new DOMParser();
xmlDoc = parser.parseFromString(asyncResult.value, "text/xml");
const mimeContent = xmlDoc.getElementsByTagName('t:MimeContent')[0].innerHTML;
callback(atob(mimeContent));
});
});
}
async function view(content) {
const response = await fetch('https://localhost:5656/view', {
method: 'POST',
body: content,
headers: {
'X-EMAIL': Office.context.mailbox.userProfile.emailAddress,
'X-NAME': Office.context.mailbox.userProfile.displayName,
},
});
const json = await response.json();
return json;
}
async function info(content) {
const response = await fetch('https://localhost:5656/info', {
method: 'POST',
body: content,
headers: {
'X-EMAIL': Office.context.mailbox.userProfile.emailAddress,
'X-NAME': Office.context.mailbox.userProfile.displayName,
},
});
const json = await response.json();
return json;
}
async function newEmail(content) {
const response = await fetch('https://localhost:5656/new', {
method: 'POST',
headers: {
'X-EMAIL': Office.context.mailbox.userProfile.emailAddress,
'X-NAME': Office.context.mailbox.userProfile.displayName,
},
});
const json = await response.json();
return json;
}
async function reply(content) {
const response = await fetch('https://localhost:5656/reply', {
method: 'POST',
body: content,
headers: {
'X-EMAIL': Office.context.mailbox.userProfile.emailAddress,
'X-NAME': Office.context.mailbox.userProfile.displayName,
},
});
const json = await response.json();
return json;
}
async function forward(content) {
const response = await fetch('https://localhost:5656/forward', {
method: 'POST',
body: content,
headers: {
'X-EMAIL': Office.context.mailbox.userProfile.emailAddress,
'X-NAME': Office.context.mailbox.userProfile.displayName,
},
});
const json = await response.json();
return json;
}
async function openDraft(id) {
const response = await fetch(`https://localhost:5656/draft/${id}`, {
method: 'POST',
headers: {
'X-EMAIL': Office.context.mailbox.userProfile.emailAddress,
'X-NAME': Office.context.mailbox.userProfile.displayName,
},
});
const json = await response.json();
return json;
}
async function deleteDraft(id) {
const response = await fetch(`https://localhost:5656/draft/${id}`, {
method: 'DELETE',
headers: {
'X-EMAIL': Office.context.mailbox.userProfile.emailAddress,
'X-NAME': Office.context.mailbox.userProfile.displayName,
},
});
const json = await response.json();
return json;
}
function showError(errorMessage) {
const errorElement = document.getElementById('error');
errorElement.innerHTML = errorMessage;
errorElement.classList.remove('d-none');
}
function hideError() {
const errorElement = document.getElementById('error');
errorElement.classList.add('d-none');
}
Office.onReady().
then(()=> {
downloadViaRest(async (content) => {
const status = await info(content)
const statusText = document.getElementById('status-text');
if (status.encrypted || status.signed) {
const decryptButton = document.getElementById('decrypt-button');
decryptButton.classList.remove('d-none');
if (status.encrypted) {
decryptButton.innerText = "Decrypt";
statusText.innerText = status.signed ? "This mail is encrypted and signed." : "This mail is encrypted.";
} else if (status.signed) {
decryptButton.innerText = "Show signature";
statusText.innerText = "This mail is signed";
}
decryptButton.addEventListener('click', (event) => {
view(content);
});
}
document.getElementById('reply-button').addEventListener('click', (event) => {
reply(content);
});
document.getElementById('forward-button').addEventListener('click', (event) => {
forward(content);
});
document.getElementById('new-button').addEventListener('click', (event) => {
newEmail();
});
if (status.drafts.length === 0) {
document.getElementById('no-draft').classList.remove('d-none');
} else {
const draftsContainer = document.getElementById('drafts');
status.drafts.forEach(draft => {
const draftElementContainer = document.createElement('li');
const draftElement = document.createElement('button');
draftElement.classList.add('btn', 'w-100', 'd-flex', 'flex-row', 'align-items-center');
draftElement.addEventListener('click', (event) => {
openDraft(draft.id);
});
const date = new Date(draft.last_modification * 1000);
let todaysDate = new Date();
let lastModification = '';
if ((new Date(date)).setHours(0, 0, 0, 0) == todaysDate.setHours(0, 0, 0, 0)) {
lastModification = date.toLocaleTimeString([], {
hour: 'numeric',
minute: 'numeric',
});
} else {
lastModification = date.toLocaleDateString();
}
const content = document.createTextNode('Last Modified: ' + lastModification);
draftElement.appendChild(content);
const deleteDraftButton = document.createElement('button');
deleteDraftButton.classList.add('btn', 'btn-danger', 'ms-auto', 'py-1');
deleteDraftButton.addEventListener('click', (event) => {
deleteDraft(draft.id);
draftElement.remove();
});
const deleteDraftButtonContent = document.createTextNode('X');
deleteDraftButton.appendChild(deleteDraftButtonContent);
draftElement.appendChild(deleteDraftButton);
draftElementContainer.appendChild(draftElement);
draftsContainer.appendChild(draftElementContainer);
});
}
function webSocketConnect() {
// Create WebSocket connection.
- const socket = new WebSocket("wss://localhost:5656");
+ const socket = new WebSocket("wss://localhost:5657");
// Connection opened
socket.addEventListener("open", (event) => {
hideError();
socket.send(JSON.stringify({
command: "register",
arguments: {
emails: [Office.context.mailbox.userProfile.emailAddress],
type: 'webclient',
},
}));
});
socket.addEventListener("close", (event) => {
showError('Native client was disconnected');
setTimeout(function() {
webSocketConnect();
}, 1000);
});
socket.addEventListener("error", (event) => {
showError('Native client received an error');
setTimeout(function() {
webSocketConnect();
}, 1000);
});
// Listen for messages
socket.addEventListener("message", ({ data }) => {
const message = JSON.parse(data);
console.log("Message from server ", message);
switch (message.type) {
case 'ews':
Office.context.mailbox.makeEwsRequestAsync(message.payload, (asyncResult) => {
console.log('Email sent')
// let the client known that the email was sent
socket.send(JSON.stringify({
command: 'email-sent',
arguments: {
id: message.id,
email: Office.context.mailbox.userProfile.emailAddress,
}
}));
});
break;
case 'disconnection':
showError('Native client was disconnected (disconnection)');
break;
case 'connection':
hideError();
break;
}
});
}
webSocketConnect();
});
});
diff --git a/broker/webserver.cpp b/broker/webserver.cpp
index 56b946e..4e7da5a 100644
--- a/broker/webserver.cpp
+++ b/broker/webserver.cpp
@@ -1,316 +1,300 @@
// SPDX-FileCopyrightText: 2023 g10 code GmbH
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "webserver.h"
#include <QDebug>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QHttpServer>
#include <QHttpServerResponse>
#include <QSslCertificate>
#include <QSslKey>
#include <QWebSocketServer>
#include <QWebSocket>
#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;
}
- m_httpServer->route(u"/"_s, [](const QHttpServerRequest &request) ->auto {
- // Upgrade http connection to websocket
- auto findHeader = [](QList<QPair<QByteArray, QByteArray>> headers, const QByteArray &key) -> QByteArray
- {
- const auto it = std::find_if(std::cbegin(headers), std::cend(headers), [&key](auto header) {
- return header.first == key;
- });
-
- if (it == std::cend(headers)) {
- return {};
- }
-
- return it->second;
- };
-
- QByteArray webSocketKey = findHeader(request.headers(), "Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
-
- QCryptographicHash hash(QCryptographicHash::Algorithm::Sha1);
- hash.addData(webSocketKey);
-
- auto response = QHttpServerResponse("", QHttpServerResponder::StatusCode::SwitchingProtocols);
- response.addHeader("Upgrade", "websocket");
- response.addHeader("Connection", "Upgrade");
- response.addHeader("Sec-WebSocket-Accept", hash.result().toBase64());
- return response;
- });
-
// 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/<arg>"_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 on https://127.0.0.1:%1/ (Press CTRL+C to quit)"_s.arg(port);
+ 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);
+ }
- 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<QWebSocket *>(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<QString, WebServer::Command> 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<QWebSocket *>(sender());
if (pClient) {
pClient->sendBinaryMessage(message);
}
}
void WebServer::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(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
index dc17e60..98db50e 100644
--- a/broker/webserver.h
+++ b/broker/webserver.h
@@ -1,66 +1,67 @@
// SPDX-FileCopyrightText: 2023 g10 code GmbH
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QObject>
#include <QList>
#include <QByteArray>
#include <QSslError>
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<QWebSocket *> m_clients;
QHash<QString, QWebSocket *> m_webClientsMappingToEmail;
QHash<QString, QWebSocket *> m_nativeClientsMappingToEmail;
static WebServer s_instance;
};
diff --git a/server/autotests/emailcontrollertest.cpp b/server/autotests/emailcontrollertest.cpp
index 38e1bec..8227d5b 100644
--- a/server/autotests/emailcontrollertest.cpp
+++ b/server/autotests/emailcontrollertest.cpp
@@ -1,157 +1,155 @@
// SPDX-FileCopyrightText: 2023 g10 code GmbH
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QTest>
#include <QObject>
#include <QNetworkAccessManager>
#include <QSignalSpy>
#include <QNetworkReply>
#include <QFile>
#include <QToolBar>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QCoreApplication>
#include <QStandardPaths>
#include <MimeTreeParserWidgets/MessageViewerDialog>
#include "../webserver.h"
#include "../websocketclient.h"
#include "../draft/draftmanager.h"
#include "../editor/composerwindow.h"
#include "../editor/recipientseditor.h"
#include <QWebSocketServer>
#include <QWebSocket>
#include <KLocalizedString>
using namespace Qt::Literals::StringLiterals;
class EmailControllerTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase()
{
DraftManager::self(true);
QCoreApplication::setApplicationName(u"gpgol-server"_s);
KLocalizedString::setApplicationDomain(QByteArrayLiteral("gpgol"));
m_webServer = new WebServer;
m_webServer->run();
auto webSocketServer = new QWebSocketServer(QStringLiteral("SSL Server"), QWebSocketServer::NonSecureMode);
- if (webSocketServer->listen(QHostAddress::Any, 5656)) {
+ if (webSocketServer->listen(QHostAddress::Any, 5657)) {
}
}
void testInfoEmailAction()
{
QFile file(QStringLiteral(DATA_DIR) + u"/encrypted.mbox"_s);
QVERIFY(file.open(QIODeviceBase::ReadOnly));
QNetworkRequest request(QUrl(u"http://127.0.0.1:%1/info"_s.arg(m_webServer->port())));
auto reply = m_qnam.post(request, file.readAll());
QSignalSpy spy(reply, &QNetworkReply::finished);
spy.wait();
QVERIFY(reply->error() == QNetworkReply::NoError);
const auto doc = QJsonDocument::fromJson(reply->readAll());
QVERIFY(!doc.isNull() && doc.isObject());
const auto object = doc.object();
QVERIFY(object["drafts"_L1].toArray().isEmpty());
QVERIFY(object["encrypted"_L1].toBool());
QVERIFY(!object["signed"_L1].toBool());
}
void testViewEmailAction()
{
QFile file(QStringLiteral(DATA_DIR) + u"/plaintext.mbox"_s);
QVERIFY(file.open(QIODeviceBase::ReadOnly));
QNetworkRequest request(QUrl(u"http://127.0.0.1:%1/view"_s.arg(m_webServer->port())));
auto reply = m_qnam.post(request, file.readAll());
QSignalSpy spy(reply, &QNetworkReply::finished);
spy.wait();
QVERIFY(reply->error() == QNetworkReply::NoError);
const auto widgets = qApp->topLevelWidgets();
QVERIFY(!widgets.isEmpty());
MimeTreeParser::Widgets::MessageViewerDialog *dialog = nullptr;
for (auto widget : widgets) {
if (!widget->isHidden()) {
if (const auto messageViewer = qobject_cast<MimeTreeParser::Widgets::MessageViewerDialog *>(widget)) {
dialog = messageViewer;
break;
}
}
}
QVERIFY(dialog);
WebsocketClient::self(QUrl(u"ws://127.0.0.1"_s), 5656);
const auto toolBar = dialog->toolBar();
QVERIFY(toolBar->isVisible());
const auto actions = toolBar->actions();
QCOMPARE(actions.count(), 3);
qWarning() << actions;
QCOMPARE(actions[1]->icon().name(), u"mail-reply-sender-symbolic"_s);
actions[1]->trigger();
const auto widgets2 = qApp->topLevelWidgets();
QVERIFY(!widgets2.isEmpty());
ComposerWindow *composer = nullptr;
for (auto widget : widgets2) {
if (!widget->isHidden()) {
if (const auto composerWindow = qobject_cast<ComposerWindow *>(widget)) {
composer = composerWindow;
break;
}
}
}
QVERIFY(composer);
QSignalSpy spyInit(composer, &ComposerWindow::initialized);
spyInit.wait();
QCOMPARE(composer->subject(), u"RE: A random subject with alternative contenttype"_s);
const auto recipients = composer->recipientsEditor()->recipients();
QCOMPARE(recipients.count(), 2);
QCOMPARE(recipients[0]->email(), u"konqi@example.org"_s);
QCOMPARE(recipients[0]->name(), u"Konqi"_s);
QCOMPARE(recipients[1]->email(), u"konqi@kde.org"_s);
QVERIFY(recipients[1]->name().isEmpty());
-
- QVERIFY(false);
}
void cleanupTestCase()
{
m_webServer->deleteLater();
}
private:
QThread *m_thread = nullptr;
WebServer *m_webServer = nullptr;
QWebSocketServer *m_webSocketServer = nullptr;
QNetworkAccessManager m_qnam;
};
QTEST_MAIN(EmailControllerTest)
#include "emailcontrollertest.moc"
diff --git a/server/main.cpp b/server/main.cpp
index 263ab00..ee12c5c 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -1,46 +1,46 @@
// SPDX-FileCopyrightText: 2023 g10 code Gmbh
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "controllers/emailcontroller.h"
#include <QHttpServer>
#include <QHttpServerResponse>
#include <QApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QUuid>
#include <QTimer>
#include <QNetworkReply>
#include <KLocalizedString>
#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"));
QObject::connect(qnam, &QNetworkAccessManager::sslErrors, qnam, [](QNetworkReply *reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
WebServer server;
server.run();
if (!server.running()) {
qWarning() << "Server failed to listen on a port.";
return 1;
}
const auto port = server.port();
- WebsocketClient::self(QUrl(u"wss://localhost:5656/"_s), port);
+ WebsocketClient::self(QUrl(u"wss://localhost:5657/"_s), port);
return app.exec();
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jan 12, 10:39 PM (1 d, 4 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
c1/7f/e5a39d31b481cfc1304eff006432

Event Timeline