// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // 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. #include "rooms.h" using namespace ircd::m; using namespace ircd; static bool append_event(const m::resource::request &request, json::stack::array &, const m::event::idx &event_idx); static m::resource::response get__state(client &client, const m::resource::request &request, const m::room::state &state); m::resource::response put__state(client &client, const m::resource::request &request, const room::id &room_id) { if(request.parv.size() < 3) throw m::NEED_MORE_PARAMS { "'type' path parameter required." }; char type_buf[m::event::TYPE_MAX_SIZE]; const string_view &type { url::decode(type_buf, request.parv[2]) }; char skey_buf[m::event::STATE_KEY_MAX_SIZE]; const string_view &state_key { request.parv.size() > 3? url::decode(skey_buf, request.parv[3]): string_view{""} // "" is significant }; const json::object &content { request.content }; const m::vm::copts copts { // Bridges are authorized to set their own time. .ts = request.bridge_id? request.query.get("ts", milliseconds::min()): milliseconds::min(), }; const m::room room { room_id, &copts }; const auto event_id { m::send(room, request.user_id, type, state_key, content) }; return m::resource::response { client, json::members { { "event_id", event_id } } }; } m::resource::response get__state(client &client, const m::resource::request &request, const room::id &room_id) { char type_buf[m::event::TYPE_MAX_SIZE]; const string_view &type { request.parv.size() > 2? url::decode(type_buf, request.parv[2]): string_view{} }; char skey_buf[m::event::STATE_KEY_MAX_SIZE]; const string_view &state_key { request.parv.size() > 3? url::decode(skey_buf, request.parv[3]): string_view{} }; // (non-standard) Allow an event_id to be passed in the query string // for reference framing. m::event::id::buf event_id; if(request.query["event_id"]) url::decode(event_id, request.query["event_id"]); const m::room room { room_id, event_id }; if(!exists(room)) throw m::NOT_FOUND { "Room %s not found.", string_view{room_id} }; if(!visible(room, request.user_id)) throw m::ACCESS_DENIED { "You are not permitted to view %s", string_view{room_id} }; const m::room::state state { room }; if(!type) return get__state(client, request, state); const m::event::fetch event { state.get(type, state_key) }; if(!visible(event, request.user_id)) throw m::ACCESS_DENIED { "You are not permitted to view this state in %s", string_view{room_id} }; const json::object content { json::get<"content"_>(event) }; return m::resource::response { client, content }; } m::resource::response get__state(client &client, const m::resource::request &request, const m::room::state &state) { m::resource::response::chunked response { client, http::OK }; json::stack out { response.buf, response.flusher() }; json::stack::array top { out }; state.for_each([&request, &top] (const m::event::idx &event_idx) { append_event(request, top, event_idx); }); return response; } bool append_event(const m::resource::request &request, json::stack::array &array, const event::idx &event_idx) { const m::event::fetch event { std::nothrow, event_idx }; if(unlikely(!event.valid)) return false; if(!visible(event, request.user_id)) return false; m::event::append { array, event, { .event_idx = event_idx, .user_id = request.user_id, .query_txnid = false, .query_prev_state = false, .query_redacted = false, }, }; return true; }