Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F112427
D25.1732311387.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
D25.1732311387.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 22, 1:36 PM (56 m, 48 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38801
Default Alt Text
D25.1732311387.diff (10 KB)
Attached To
Mode
D25: Add action to send different to-device messages to multiple devices
Attached
Detach File
Event Timeline
Log In to Comment