Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34504029
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
30 KB
Subscribers
None
View Options
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
Details
Attached
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
Attached To
rOJ GpgOL.js
Event Timeline
Log In to Comment