Page MenuHomePhorge

D25.1726899665.diff
No OneTemporary

D25.1726899665.diff

diff --git a/src/client/actions/send.hpp b/src/client/actions/send.hpp
--- a/src/client/actions/send.hpp
+++ b/src/client/actions/send.hpp
@@ -19,6 +19,7 @@
ClientResult processResponse(ClientModel m, SendMessageResponse r);
ClientResult updateClient(ClientModel m, SendToDeviceMessageAction a);
+ ClientResult updateClient(ClientModel m, SendMultipleToDeviceMessagesAction a);
ClientResult processResponse(ClientModel m, SendToDeviceResponse r);
ClientResult updateClient(ClientModel m, SaveLocalEchoAction a);
diff --git a/src/client/actions/send.cpp b/src/client/actions/send.cpp
--- a/src/client/actions/send.cpp
+++ b/src/client/actions/send.cpp
@@ -9,6 +9,7 @@
#include <debug.hpp>
#include <types.hpp>
+#include <immer-utils.hpp>
#include "send.hpp"
#include "status-utils.hpp"
@@ -112,15 +113,10 @@
if (type == "m.room_key" && !a.event.encrypted()) {
kzo.client.err() << "Trying to send room key event unencrypted! Rejecting." << std::endl;
- return { std::move(m), [](auto &&) {
- return EffectStatus{
- /* succ = */ false,
- json{
- {"errorCode", "MOE_KAZV_MXC_SENDING_ROOM_KEY_EVENT_UNENCRYPTED"},
- {"error", "Cannot send room key event unencrypted"},
- }
- };
- }};
+ return { std::move(m), failEffect(
+ "MOE_KAZV_MXC_SENDING_ROOM_KEY_EVENT_UNENCRYPTED",
+ "Cannot send room key event unencrypted"
+ )};
}
auto txnId = a.txnId.has_value() ? a.txnId.value() : getTxnId(a.event, m);
@@ -147,6 +143,68 @@
return { std::move(m), lager::noop };
}
+ ClientResult updateClient(ClientModel m, SendMultipleToDeviceMessagesAction a)
+ {
+ std::optional<std::string> type;
+ std::optional<std::string> txnId;
+ immer::map<std::string, immer::map<std::string, JsonWrap>> userToDeviceToContentMap;
+ for (auto [userId, deviceToEventMap] : a.userToDeviceToEventMap) {
+ userToDeviceToContentMap = std::move(userToDeviceToContentMap).set(userId, immer::map<std::string, JsonWrap>());
+ for (auto [deviceId, event] : deviceToEventMap) {
+ auto origJson = event.originalJson().get();
+ if (!origJson.contains("type") || !origJson.contains("content")) {
+ return { std::move(m), failEffect(
+ "MOE_KAZV_MXC_INVALID_EVENT_FORMAT",
+ "Invalid event format"
+ )};
+ }
+ if (!type.has_value()) {
+ type = origJson["type"];
+ } else {
+ if (type.value() != origJson["type"]) {
+ return { std::move(m), failEffect(
+ "MOE_KAZV_MXC_TYPE_NOT_SAME",
+ "The to-device messages' type must be the same"
+ )};
+ }
+ }
+ if (origJson["type"] == "m.room_key" && !event.encrypted()) {
+ kzo.client.err() << "Trying to send room key event unencrypted! Rejecting." << std::endl;
+ return { std::move(m), failEffect(
+ "MOE_KAZV_MXC_SENDING_ROOM_KEY_EVENT_UNENCRYPTED",
+ "Cannot send room key event unencrypted"
+ )};
+ }
+ if (!txnId.has_value()) {
+ txnId = getTxnId(event, m);
+ }
+ auto content = origJson["content"];
+ userToDeviceToContentMap = setIn(
+ std::move(userToDeviceToContentMap),
+ std::move(content),
+ userId, deviceId
+ );
+ }
+ }
+
+ if (!type) {
+ // nothing to send
+ return { std::move(m), lager::noop };
+ }
+
+ auto job = m.job<SendToDeviceJob>()
+ .make(type.value(), txnId.value(), userToDeviceToContentMap)
+ // XXX not extracting devicesToSend from the map
+ // those triggers should be deprecated in favour of
+ // then()-continuation anyway
+ .withData(json{{"devicesToSend", json::object()},
+ {"txnId", txnId.value()}});
+
+ m.addJob(std::move(job));
+
+ return { std::move(m), lager::noop };
+ }
+
ClientResult processResponse(ClientModel m, SendToDeviceResponse r)
{
auto devicesToSend = r.dataJson("devicesToSend");
diff --git a/src/client/client-model.hpp b/src/client/client-model.hpp
--- a/src/client/client-model.hpp
+++ b/src/client/client-model.hpp
@@ -444,6 +444,19 @@
std::optional<std::string> txnId{std::nullopt};
};
+ /**
+ * Send multiple to device messages.
+ *
+ * Due to limitations of the spec, the type of the Events must be the same.
+ */
+ struct SendMultipleToDeviceMessagesAction
+ {
+ /// A map from user id to device id to the event.
+ immer::map<std::string, immer::map<std::string, Event>> userToDeviceToEventMap;
+ /// An optional transaction id. Will be generated if not provided.
+ std::optional<std::string> txnId{std::nullopt};
+ };
+
struct UploadIdentityKeysAction
{
};
diff --git a/src/client/clientfwd.hpp b/src/client/clientfwd.hpp
--- a/src/client/clientfwd.hpp
+++ b/src/client/clientfwd.hpp
@@ -60,6 +60,7 @@
struct DownloadThumbnailAction;
struct SendToDeviceMessageAction;
+ struct SendMultipleToDeviceMessagesAction;
struct UploadIdentityKeysAction;
struct GenerateAndUploadOneTimeKeysAction;
@@ -124,6 +125,7 @@
DownloadThumbnailAction,
SendToDeviceMessageAction,
+ SendMultipleToDeviceMessagesAction,
UploadIdentityKeysAction,
GenerateAndUploadOneTimeKeysAction,
diff --git a/src/client/status-utils.hpp b/src/client/status-utils.hpp
--- a/src/client/status-utils.hpp
+++ b/src/client/status-utils.hpp
@@ -42,4 +42,19 @@
return EffectStatus(/* succ = */ false, data);
};
}
+
+ /**
+ * An effect that returns a failed EffectStatus with the given
+ * error code and message.
+ */
+ inline auto failEffect(std::string errorCode, std::string errorMsg)
+ {
+ auto d = json{
+ {"error", errorMsg},
+ {"errorCode", errorCode},
+ };
+ return [data=JsonWrap(d)](auto &&) {
+ return EffectStatus(/* succ = */ false, data);
+ };
+ }
}
diff --git a/src/tests/client/send-test.cpp b/src/tests/client/send-test.cpp
--- a/src/tests/client/send-test.cpp
+++ b/src/tests/client/send-test.cpp
@@ -138,6 +138,106 @@
io.run();
}
+TEST_CASE("SendMultipleToDeviceMessagesAction", "[client][send]")
+{
+ boost::asio::io_context io;
+ SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
+
+ ClientModel m = makeClient(
+ withCrypto(makeCrypto({}))
+ | withRoom(makeRoom(
+ withRoomId("!exampleroomid:example.com")
+ | withRoomEncrypted(true)
+ ))
+ );
+
+ auto store = createTestClientStoreFrom(m, ph);
+
+ SECTION("Reject unencrypted m.room_key event")
+ {
+ store.dispatch(SendMultipleToDeviceMessagesAction{
+ {{"@foo:example.com",
+ {{"device1", Event{json{{"content", {{"foo", "bar"}}}, {"type", "m.room_key"}}}}},
+ }},
+ })
+ .then([](auto status) {
+ REQUIRE(!status.success());
+ REQUIRE(status.dataStr("errorCode") == "MOE_KAZV_MXC_SENDING_ROOM_KEY_EVENT_UNENCRYPTED");
+ });
+ }
+
+ SECTION("Make a job")
+ {
+ store.dispatch(SendMultipleToDeviceMessagesAction{
+ {{"@foo:example.com",
+ {{"device1", Event{json{{"content", {{"foo", "1"}}}, {"type", "moe.kazv.mxc.some-type"}}}},
+ {"device2", Event{json{{"content", {{"foo", "2"}}}, {"type", "moe.kazv.mxc.some-type"}}}}},
+ }},
+ })
+ .then([&store](auto status) {
+ REQUIRE(status.success());
+ auto next = store.reader().make().get();
+ assert1Job(next);
+ for1stJob(next, [](const BaseJob &job) {
+ auto expectedBody = json{
+ {"messages", {
+ {"@foo:example.com", {
+ {"device1", {{"foo", "1"}}},
+ {"device2", {{"foo", "2"}}},
+ }},
+ }},
+ };
+ REQUIRE(json::parse(std::get<Bytes>(job.requestBody())) == expectedBody);
+ REQUIRE(job.url().find("/sendToDevice/moe.kazv.mxc.some-type/") != std::string::npos);
+ });
+ });
+ }
+
+ SECTION("Make no job if there is nothing to send")
+ {
+ store.dispatch(SendMultipleToDeviceMessagesAction{
+ {{"@foo:example.com",
+ {},
+ }},
+ })
+ .then([&store](auto status) {
+ REQUIRE(status.success());
+ auto next = store.reader().make().get();
+ REQUIRE(next.nextJobs.empty());
+ });
+ }
+
+ SECTION("Reject if events are not of the same type")
+ {
+ store.dispatch(SendMultipleToDeviceMessagesAction{
+ {{"@foo:example.com",
+ {{"device1", Event{json{{"content", {{"foo", "1"}}}, {"type", "moe.kazv.mxc.some-type-2"}}}},
+ {"device2", Event{json{{"content", {{"foo", "2"}}}, {"type", "moe.kazv.mxc.some-type"}}}}},
+ }},
+ })
+ .then([](auto status) {
+ REQUIRE(!status.success());
+ REQUIRE(status.dataStr("errorCode") == "MOE_KAZV_MXC_TYPE_NOT_SAME");
+ });
+ }
+
+ SECTION("Reject if some event is not well-formed")
+ {
+ store.dispatch(SendMultipleToDeviceMessagesAction{
+ {{"@foo:example.com",
+ {{"device1", Event{json{{"type", "moe.kazv.mxc.some-type"}}}},
+ {"device2", Event{json{{"content", {{"foo", "2"}}}, {"type", "moe.kazv.mxc.some-type"}}}}},
+ }},
+ })
+ .then([](auto status) {
+ REQUIRE(!status.success());
+ REQUIRE(status.dataStr("errorCode") == "MOE_KAZV_MXC_INVALID_EVENT_FORMAT");
+ });
+ }
+
+ io.run();
+}
+
TEST_CASE("UpdateLocalEchoStatusAction: update one does not exist", "[client][send]")
{
ClientModel loggedInModel = makeClient(

File Metadata

Mime Type
text/plain
Expires
Fri, Sep 20, 11:21 PM (21 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16888
Default Alt Text
D25.1726899665.diff (10 KB)

Event Timeline