2020-03-29 01:10:32 +01:00
|
|
|
// The Construct
|
|
|
|
//
|
|
|
|
// Copyright (C) The Construct Developers, Authors & Contributors
|
|
|
|
// Copyright (C) 2016-2020 Jason Volk <jason@zemos.net>
|
|
|
|
//
|
|
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
|
|
// copyright notice and this permission notice is present in all copies. The
|
|
|
|
// full license for this software is available in the LICENSE file.
|
|
|
|
|
|
|
|
namespace ircd::m
|
|
|
|
{
|
2023-02-02 01:21:22 +01:00
|
|
|
static string_view make_state_key(const mutable_buffer &, const string_view &, const string_view &, const event::idx &);
|
|
|
|
|
|
|
|
static resource::response _get_room_keys_keys(client &, const resource::request &, const room::state &, const event::idx &, const string_view &, const string_view &);
|
|
|
|
static void _get_room_keys_keys(client &, const resource::request &, const room::state &, const event::idx &, const string_view &, json::stack::object &);
|
2020-03-29 01:10:32 +01:00
|
|
|
static resource::response get_room_keys_keys(client &, const resource::request &);
|
|
|
|
extern resource::method room_keys_keys_get;
|
|
|
|
|
2020-06-10 03:22:24 +02:00
|
|
|
static event::id::buf put_room_keys_keys_key(client &, const resource::request &, const room::id &, const string_view &, const event::idx &, const json::object &);
|
2020-03-29 01:10:32 +01:00
|
|
|
static resource::response put_room_keys_keys(client &, const resource::request &);
|
|
|
|
extern resource::method room_keys_keys_put;
|
|
|
|
|
|
|
|
static resource::response delete_room_keys_keys(client &, const resource::request &);
|
|
|
|
extern resource::method room_keys_keys_delete;
|
|
|
|
|
|
|
|
extern resource room_keys_keys;
|
|
|
|
}
|
|
|
|
|
|
|
|
ircd::mapi::header
|
|
|
|
IRCD_MODULE
|
|
|
|
{
|
|
|
|
"Client (undocumented) :e2e Room Keys Keys"
|
|
|
|
};
|
|
|
|
|
|
|
|
decltype(ircd::m::room_keys_keys)
|
|
|
|
ircd::m::room_keys_keys
|
|
|
|
{
|
|
|
|
"/_matrix/client/unstable/room_keys/keys",
|
|
|
|
{
|
|
|
|
"(undocumented) Room Keys Keys",
|
|
|
|
resource::DIRECTORY,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// DELETE
|
|
|
|
//
|
|
|
|
|
|
|
|
decltype(ircd::m::room_keys_keys_delete)
|
|
|
|
ircd::m::room_keys_keys_delete
|
|
|
|
{
|
|
|
|
room_keys_keys, "DELETE", delete_room_keys_keys,
|
|
|
|
{
|
2021-02-19 15:44:27 +01:00
|
|
|
room_keys_keys_delete.REQUIRES_AUTH |
|
|
|
|
room_keys_keys_delete.RATE_LIMITED
|
2020-03-29 01:10:32 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ircd::m::resource::response
|
|
|
|
ircd::m::delete_room_keys_keys(client &client,
|
|
|
|
const resource::request &request)
|
|
|
|
{
|
2023-02-02 01:37:00 +01:00
|
|
|
char room_id_buf[room::id::buf::SIZE];
|
|
|
|
const string_view &room_id
|
|
|
|
{
|
|
|
|
request.parv.size() > 0?
|
|
|
|
url::decode(room_id_buf, request.parv[0]):
|
|
|
|
string_view{}
|
|
|
|
};
|
|
|
|
|
|
|
|
char session_id_buf[256];
|
|
|
|
const string_view &session_id
|
|
|
|
{
|
|
|
|
request.parv.size() > 1?
|
|
|
|
url::decode(session_id_buf, request.parv[1]):
|
|
|
|
string_view{}
|
|
|
|
};
|
|
|
|
|
|
|
|
const event::idx version
|
|
|
|
{
|
|
|
|
request.query.at<event::idx>("version")
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::user::room user_room
|
|
|
|
{
|
|
|
|
request.user_id
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::room::state state
|
|
|
|
{
|
|
|
|
user_room
|
|
|
|
};
|
|
|
|
|
|
|
|
char state_key_buf[event::STATE_KEY_MAX_SIZE];
|
|
|
|
const string_view state_key
|
|
|
|
{
|
|
|
|
make_state_key(state_key_buf, room_id, session_id, version)
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto event_id
|
|
|
|
{
|
|
|
|
m::event_id(state.get("ircd.room_keys.key", state_key))
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto redact_id
|
|
|
|
{
|
|
|
|
m::redact(user_room, request.user_id, event_id, "deleted by client")
|
|
|
|
};
|
2020-03-29 01:10:32 +01:00
|
|
|
|
|
|
|
return resource::response
|
|
|
|
{
|
2023-02-02 01:37:00 +01:00
|
|
|
client, http::OK
|
2020-03-29 01:10:32 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// PUT
|
|
|
|
//
|
|
|
|
|
|
|
|
decltype(ircd::m::room_keys_keys_put)
|
|
|
|
ircd::m::room_keys_keys_put
|
|
|
|
{
|
|
|
|
room_keys_keys, "PUT", put_room_keys_keys,
|
|
|
|
{
|
2020-06-08 21:36:56 +02:00
|
|
|
// Flags
|
2021-02-19 15:44:27 +01:00
|
|
|
room_keys_keys_put.REQUIRES_AUTH |
|
|
|
|
room_keys_keys_put.RATE_LIMITED,
|
2020-06-08 21:36:56 +02:00
|
|
|
|
|
|
|
// timeout //TODO: XXX designated
|
|
|
|
30s,
|
|
|
|
|
|
|
|
// Payload maximum
|
|
|
|
1_MiB,
|
2020-03-29 01:10:32 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ircd::m::resource::response
|
|
|
|
ircd::m::put_room_keys_keys(client &client,
|
|
|
|
const resource::request &request)
|
|
|
|
{
|
2020-03-31 20:43:10 +02:00
|
|
|
char room_id_buf[room::id::buf::SIZE];
|
|
|
|
const string_view &room_id
|
|
|
|
{
|
|
|
|
request.parv.size() > 0?
|
|
|
|
url::decode(room_id_buf, request.parv[0]):
|
|
|
|
string_view{}
|
|
|
|
};
|
|
|
|
|
|
|
|
char session_id_buf[256];
|
|
|
|
const string_view &session_id
|
|
|
|
{
|
|
|
|
request.parv.size() > 1?
|
|
|
|
url::decode(session_id_buf, request.parv[1]):
|
|
|
|
string_view{}
|
|
|
|
};
|
|
|
|
|
|
|
|
const event::idx version
|
|
|
|
{
|
|
|
|
request.query.at<event::idx>("version")
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!room_id && !session_id)
|
|
|
|
{
|
|
|
|
const json::object &rooms
|
|
|
|
{
|
|
|
|
request["rooms"]
|
|
|
|
};
|
|
|
|
|
|
|
|
for(const auto &[room_id, sessions] : rooms)
|
|
|
|
for(const auto &[session_id, session] : json::object(sessions))
|
|
|
|
put_room_keys_keys_key(client, request, room_id, session_id, version, session);
|
|
|
|
}
|
|
|
|
else if(!session_id)
|
|
|
|
{
|
|
|
|
const json::object &sessions
|
|
|
|
{
|
|
|
|
request["sessions"]
|
|
|
|
};
|
|
|
|
|
|
|
|
for(const auto &[session_id, session] : sessions)
|
|
|
|
put_room_keys_keys_key(client, request, room_id, session_id, version, session);
|
|
|
|
}
|
|
|
|
else put_room_keys_keys_key(client, request, room_id, session_id, version, request);
|
2020-03-29 01:10:32 +01:00
|
|
|
|
|
|
|
return resource::response
|
|
|
|
{
|
2020-03-31 20:43:10 +02:00
|
|
|
client, http::OK
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-06-10 03:22:24 +02:00
|
|
|
ircd::m::event::id::buf
|
2020-03-31 20:43:10 +02:00
|
|
|
ircd::m::put_room_keys_keys_key(client &client,
|
|
|
|
const resource::request &request,
|
|
|
|
const room::id &room_id,
|
|
|
|
const string_view &session_id,
|
|
|
|
const event::idx &version,
|
|
|
|
const json::object &content)
|
|
|
|
{
|
|
|
|
const m::user::room user_room
|
|
|
|
{
|
|
|
|
request.user_id
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::room::type events
|
|
|
|
{
|
|
|
|
user_room, "ircd.room_keys.version"
|
|
|
|
};
|
|
|
|
|
|
|
|
events.for_each([&version]
|
|
|
|
(const auto &, const auto &, const event::idx &_event_idx)
|
|
|
|
{
|
|
|
|
if(m::redacted(_event_idx))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if(_event_idx != version)
|
|
|
|
throw http::error
|
|
|
|
{
|
|
|
|
"%lu is not the most recent key version",
|
|
|
|
http::FORBIDDEN,
|
|
|
|
version
|
|
|
|
};
|
|
|
|
|
|
|
|
return false; // false to break after this first hit
|
|
|
|
});
|
|
|
|
|
|
|
|
char state_key_buf[event::STATE_KEY_MAX_SIZE];
|
2023-02-02 01:21:22 +01:00
|
|
|
const string_view state_key
|
2020-03-31 20:43:10 +02:00
|
|
|
{
|
2023-02-02 01:21:22 +01:00
|
|
|
make_state_key(state_key_buf, room_id, session_id, version)
|
|
|
|
};
|
2020-03-31 20:43:10 +02:00
|
|
|
|
|
|
|
const auto event_id
|
|
|
|
{
|
|
|
|
send(user_room, request.user_id, "ircd.room_keys.key", state_key, content)
|
2020-03-29 01:10:32 +01:00
|
|
|
};
|
2020-06-10 03:22:24 +02:00
|
|
|
|
|
|
|
return event_id;
|
2020-03-29 01:10:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// GET
|
|
|
|
//
|
|
|
|
|
|
|
|
decltype(ircd::m::room_keys_keys_get)
|
|
|
|
ircd::m::room_keys_keys_get
|
|
|
|
{
|
|
|
|
room_keys_keys, "GET", get_room_keys_keys,
|
|
|
|
{
|
2021-02-19 15:44:27 +01:00
|
|
|
room_keys_keys_get.REQUIRES_AUTH |
|
|
|
|
room_keys_keys_get.RATE_LIMITED
|
2020-03-29 01:10:32 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ircd::m::resource::response
|
|
|
|
ircd::m::get_room_keys_keys(client &client,
|
|
|
|
const resource::request &request)
|
|
|
|
{
|
2020-03-31 21:12:08 +02:00
|
|
|
char room_id_buf[room::id::buf::SIZE];
|
|
|
|
const string_view &room_id
|
|
|
|
{
|
|
|
|
request.parv.size() > 0?
|
|
|
|
url::decode(room_id_buf, request.parv[0]):
|
|
|
|
string_view{}
|
|
|
|
};
|
2020-03-29 01:10:32 +01:00
|
|
|
|
2020-03-31 21:12:08 +02:00
|
|
|
char session_id_buf[256];
|
|
|
|
const string_view &session_id
|
2020-03-29 01:10:32 +01:00
|
|
|
{
|
2020-03-31 21:12:08 +02:00
|
|
|
request.parv.size() > 1?
|
|
|
|
url::decode(session_id_buf, request.parv[1]):
|
|
|
|
string_view{}
|
2020-03-29 01:10:32 +01:00
|
|
|
};
|
2020-03-31 21:12:08 +02:00
|
|
|
|
|
|
|
const event::idx version
|
|
|
|
{
|
|
|
|
request.query.at<event::idx>("version")
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::user::room user_room
|
|
|
|
{
|
|
|
|
request.user_id
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::room::state state
|
|
|
|
{
|
|
|
|
user_room
|
|
|
|
};
|
|
|
|
|
|
|
|
if(room_id && session_id)
|
2023-02-02 01:21:22 +01:00
|
|
|
return _get_room_keys_keys(client, request, state, version, room_id, session_id);
|
2020-03-31 21:12:08 +02:00
|
|
|
|
|
|
|
resource::response::chunked response
|
|
|
|
{
|
|
|
|
client, http::OK
|
|
|
|
};
|
|
|
|
|
|
|
|
json::stack out
|
|
|
|
{
|
|
|
|
response.buf, response.flusher()
|
|
|
|
};
|
|
|
|
|
|
|
|
json::stack::object top
|
|
|
|
{
|
|
|
|
out
|
|
|
|
};
|
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
json::stack::object rooms
|
|
|
|
{
|
|
|
|
top, "rooms"
|
|
|
|
};
|
|
|
|
|
2020-03-31 21:12:08 +02:00
|
|
|
if(room_id)
|
|
|
|
{
|
2023-02-02 01:21:22 +01:00
|
|
|
_get_room_keys_keys(client, request, state, version, room_id, rooms);
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
m::room::id::buf last_room;
|
|
|
|
state.for_each("ircd.room_keys.key", [&client, &request, &state, &version, &rooms, &last_room]
|
|
|
|
(const string_view &, const string_view &state_key, const event::idx &)
|
|
|
|
{
|
|
|
|
const auto &room_id
|
2020-03-31 21:12:08 +02:00
|
|
|
{
|
2023-02-02 01:21:22 +01:00
|
|
|
token(state_key, ":::", 0)
|
2020-03-31 21:12:08 +02:00
|
|
|
};
|
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
if(!m::valid(id::ROOM, room_id))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if(room_id == last_room)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
_get_room_keys_keys(client, request, state, version, room_id, rooms);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ircd::m::_get_room_keys_keys(client &client,
|
|
|
|
const resource::request &request,
|
|
|
|
const m::room::state &state,
|
|
|
|
const event::idx &version,
|
|
|
|
const string_view &room_id,
|
|
|
|
json::stack::object &rooms)
|
|
|
|
{
|
|
|
|
json::stack::object room
|
|
|
|
{
|
|
|
|
rooms, room_id
|
|
|
|
};
|
|
|
|
|
|
|
|
json::stack::object sessions
|
|
|
|
{
|
|
|
|
room, "sessions"
|
|
|
|
};
|
|
|
|
|
|
|
|
state.for_each("ircd.room_keys.key", [&room_id, &version, &sessions]
|
|
|
|
(const string_view &type, const string_view &state_key, const event::idx &event_idx)
|
|
|
|
{
|
|
|
|
string_view part[3]; const auto parts
|
2020-03-31 21:12:08 +02:00
|
|
|
{
|
2023-02-02 01:21:22 +01:00
|
|
|
tokens(state_key, ":::", part)
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto &_room_id{part[0]};
|
|
|
|
const auto &_session_id{part[1]};
|
|
|
|
const auto &_version{part[2]};
|
|
|
|
if(!m::valid(id::ROOM, _room_id))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if(_room_id != room_id)
|
|
|
|
return true;
|
2020-03-31 21:12:08 +02:00
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
if(_version != lex_cast<event::idx>(version))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
m::get(std::nothrow, event_idx, "content", [&sessions, &_session_id]
|
|
|
|
(const json::object &session)
|
|
|
|
{
|
|
|
|
json::stack::member
|
2020-03-31 21:12:08 +02:00
|
|
|
{
|
2023-02-02 01:21:22 +01:00
|
|
|
sessions, _session_id, session
|
2020-03-31 21:12:08 +02:00
|
|
|
};
|
2023-02-02 01:21:22 +01:00
|
|
|
});
|
2020-03-31 21:12:08 +02:00
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
2020-03-31 21:12:08 +02:00
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
ircd::m::resource::response
|
|
|
|
ircd::m::_get_room_keys_keys(client &client,
|
|
|
|
const resource::request &request,
|
|
|
|
const m::room::state &state,
|
|
|
|
const event::idx &version,
|
|
|
|
const string_view &room_id,
|
|
|
|
const string_view &session_id)
|
|
|
|
{
|
|
|
|
char state_key_buf[event::STATE_KEY_MAX_SIZE];
|
|
|
|
const string_view state_key
|
|
|
|
{
|
|
|
|
make_state_key(state_key_buf, room_id, session_id, version)
|
|
|
|
};
|
2020-03-31 21:12:08 +02:00
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
const auto event_idx
|
|
|
|
{
|
|
|
|
state.get("ircd.room_keys.key", state_key)
|
|
|
|
};
|
2020-03-31 21:12:08 +02:00
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
m::get(event_idx, "content", [&client]
|
|
|
|
(const json::object &content)
|
|
|
|
{
|
|
|
|
resource::response
|
|
|
|
{
|
|
|
|
client, content
|
|
|
|
};
|
|
|
|
});
|
2020-03-31 21:12:08 +02:00
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
return {}; // responded from closure or thrown
|
|
|
|
}
|
2020-03-31 21:12:08 +02:00
|
|
|
|
2023-02-02 01:21:22 +01:00
|
|
|
ircd::string_view
|
|
|
|
ircd::m::make_state_key(const mutable_buffer &buf,
|
|
|
|
const string_view &room_id,
|
|
|
|
const string_view &session_id,
|
|
|
|
const event::idx &version)
|
|
|
|
{
|
|
|
|
return fmt::sprintf
|
|
|
|
{
|
|
|
|
buf, "%s:::%s:::%u",
|
|
|
|
room_id,
|
|
|
|
session_id,
|
|
|
|
version,
|
|
|
|
};
|
2020-03-29 01:10:32 +01:00
|
|
|
}
|