2018-02-14 21:23:20 +01:00
|
|
|
// Matrix Construct
|
|
|
|
//
|
|
|
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
|
|
|
// Copyright (C) 2016-2018 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.
|
|
|
|
|
|
|
|
#include "rooms.h"
|
|
|
|
|
|
|
|
using namespace ircd;
|
|
|
|
|
|
|
|
resource::response
|
|
|
|
get__members(client &client,
|
|
|
|
const resource::request &request,
|
|
|
|
const m::room::id &room_id)
|
|
|
|
{
|
2019-09-18 21:20:40 +02:00
|
|
|
// Acquire the membership/not_membership constraints from query string
|
|
|
|
char membuf[2][4][32];
|
|
|
|
string_view memship[2][4];
|
|
|
|
const size_t memcount[2]
|
2019-07-07 10:30:20 +02:00
|
|
|
{
|
2019-09-18 21:20:40 +02:00
|
|
|
request.query.count("not_membership"),
|
|
|
|
request.query.count("membership")
|
2019-07-07 10:30:20 +02:00
|
|
|
};
|
|
|
|
|
2019-09-18 21:20:40 +02:00
|
|
|
for(size_t i(0); i < 4 && i < memcount[0]; ++i)
|
|
|
|
memship[0][i] = url::decode(membuf[0][i], request.query.at("not_membership", i));
|
|
|
|
|
|
|
|
for(size_t i(0); i < 4 && i < memcount[1]; ++i)
|
|
|
|
memship[1][i] = url::decode(membuf[1][i], request.query.at("membership", i));
|
|
|
|
|
|
|
|
// List of membership strings user does not want in response.
|
|
|
|
const vector_view<const string_view> not_memberships
|
|
|
|
{
|
|
|
|
memship[0], memcount[0]
|
|
|
|
};
|
|
|
|
|
|
|
|
// List of membership strings user wants in response
|
|
|
|
const vector_view<const string_view> memberships
|
2019-07-07 10:30:20 +02:00
|
|
|
{
|
2019-09-18 21:20:40 +02:00
|
|
|
memship[1], memcount[1]
|
2019-07-07 10:30:20 +02:00
|
|
|
};
|
|
|
|
|
2019-09-18 21:20:40 +02:00
|
|
|
// Acquire the at/since parameter from query string.
|
2019-09-23 23:06:52 +02:00
|
|
|
char atbuf[64];
|
2019-07-07 10:30:20 +02:00
|
|
|
const string_view at
|
|
|
|
{
|
|
|
|
url::decode(atbuf, request.query["at"])
|
|
|
|
};
|
|
|
|
|
2019-09-23 22:00:30 +02:00
|
|
|
// at is a /sync since token we gave the client. This is simply
|
|
|
|
// an event_idx sequence integer, except during phased-polylog sync
|
|
|
|
// when this is a negative integer. If this is phased sync, we can
|
|
|
|
// parse this token for the snapshot integer.
|
|
|
|
const auto &[since, snapshot]
|
|
|
|
{
|
|
|
|
split(at, '_')
|
|
|
|
};
|
|
|
|
|
2019-09-23 23:26:18 +02:00
|
|
|
const auto at_idx
|
2019-07-07 10:30:20 +02:00
|
|
|
{
|
2019-09-23 22:00:30 +02:00
|
|
|
snapshot?
|
|
|
|
lex_cast<m::event::idx>(snapshot):
|
|
|
|
since?
|
|
|
|
lex_cast<m::event::idx>(since):
|
2019-09-23 23:26:18 +02:00
|
|
|
-1UL
|
2019-07-07 10:30:20 +02:00
|
|
|
};
|
|
|
|
|
2019-03-14 21:55:44 +01:00
|
|
|
const m::room room
|
2018-02-14 21:23:20 +01:00
|
|
|
{
|
2019-09-23 23:26:18 +02:00
|
|
|
room_id
|
2018-02-14 21:23:20 +01:00
|
|
|
};
|
|
|
|
|
2019-09-23 23:26:18 +02:00
|
|
|
if(!exists(room))
|
2019-03-14 21:55:44 +01:00
|
|
|
throw m::NOT_FOUND
|
|
|
|
{
|
|
|
|
"Room %s does not exist.",
|
|
|
|
string_view{room_id}
|
|
|
|
};
|
|
|
|
|
2019-08-14 10:01:46 +02:00
|
|
|
if(!visible(room, request.user_id))
|
2019-03-14 21:55:44 +01:00
|
|
|
throw m::ACCESS_DENIED
|
|
|
|
{
|
|
|
|
"You do not have permission to view %s members.",
|
|
|
|
string_view{room_id}
|
|
|
|
};
|
2018-02-14 21:23:20 +01:00
|
|
|
|
2019-03-14 21:55:44 +01:00
|
|
|
resource::response::chunked response
|
2018-02-14 21:23:20 +01:00
|
|
|
{
|
2019-03-14 21:55:44 +01:00
|
|
|
client, http::OK
|
|
|
|
};
|
2018-02-14 21:23:20 +01:00
|
|
|
|
2019-03-14 21:55:44 +01:00
|
|
|
json::stack out
|
2018-02-14 21:23:20 +01:00
|
|
|
{
|
2019-03-14 21:55:44 +01:00
|
|
|
response.buf, response.flusher()
|
|
|
|
};
|
|
|
|
|
|
|
|
json::stack::object top
|
|
|
|
{
|
|
|
|
out
|
|
|
|
};
|
|
|
|
|
|
|
|
json::stack::array chunk
|
|
|
|
{
|
|
|
|
top, "chunk"
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::room::members members
|
|
|
|
{
|
|
|
|
room
|
2018-02-14 21:23:20 +01:00
|
|
|
};
|
2019-03-14 21:55:44 +01:00
|
|
|
|
2019-09-18 21:20:40 +02:00
|
|
|
// The room::members interface can perform an optimized iteration if we
|
|
|
|
// supply a single membership type; otherwise all memberships iterated.
|
|
|
|
const string_view &membership
|
|
|
|
{
|
|
|
|
// A single membership entry is given in the query string
|
|
|
|
memberships.size() == 1?
|
|
|
|
memberships.at(0):
|
|
|
|
string_view{}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Tests if a member matches all of the membership constraint params. Used
|
|
|
|
// in the members iteration closures below. Note that if a membership
|
|
|
|
// parameter was passed to for_each() all members are of that membership,
|
|
|
|
// and that membership is desired, so we don't have to run any match here.
|
|
|
|
const auto membership_match{[&memberships, ¬_memberships]
|
|
|
|
(const m::user::id &member, const m::event::idx &event_idx)
|
|
|
|
{
|
|
|
|
if(likely(!empty(not_memberships)))
|
|
|
|
{
|
|
|
|
if(m::membership(event_idx, not_memberships))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if(likely(!empty(memberships)))
|
|
|
|
{
|
|
|
|
if(!m::membership(event_idx, memberships))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}};
|
|
|
|
|
|
|
|
// prefetch loop
|
2019-09-23 23:26:18 +02:00
|
|
|
members.for_each(membership, [&membership, &membership_match, &at_idx]
|
2019-08-25 05:37:37 +02:00
|
|
|
(const m::user::id &member, const m::event::idx &event_idx)
|
|
|
|
{
|
2019-09-23 23:26:18 +02:00
|
|
|
if(event_idx > at_idx)
|
|
|
|
return true;
|
|
|
|
|
2019-09-24 23:58:48 +02:00
|
|
|
// Prefetch the content cell for the m::membership test
|
|
|
|
if(!membership)
|
|
|
|
m::prefetch(event_idx, "content");
|
2019-08-25 05:37:37 +02:00
|
|
|
|
2019-09-24 23:58:48 +02:00
|
|
|
// Prefetch the event JSON
|
2019-08-25 05:37:37 +02:00
|
|
|
m::prefetch(event_idx);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2019-09-18 21:20:40 +02:00
|
|
|
// stream to client
|
2019-09-23 23:26:18 +02:00
|
|
|
members.for_each(membership, [&membership, &membership_match, &at_idx, &chunk]
|
2019-07-24 04:31:40 +02:00
|
|
|
(const m::user::id &member, const m::event::idx &event_idx)
|
2019-03-14 21:55:44 +01:00
|
|
|
{
|
2019-09-23 23:26:18 +02:00
|
|
|
if(event_idx > at_idx)
|
|
|
|
return true;
|
|
|
|
|
2019-09-18 21:20:40 +02:00
|
|
|
if(!membership && !membership_match(member, event_idx))
|
2019-07-24 04:31:40 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
const m::event::fetch event
|
|
|
|
{
|
|
|
|
event_idx, std::nothrow
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!event.valid)
|
2019-07-07 10:30:20 +02:00
|
|
|
return true;
|
|
|
|
|
2019-03-14 21:55:44 +01:00
|
|
|
chunk.append(event);
|
2019-07-07 10:30:20 +02:00
|
|
|
return true;
|
2019-07-24 04:31:40 +02:00
|
|
|
});
|
2019-03-14 21:55:44 +01:00
|
|
|
|
2019-06-24 07:09:41 +02:00
|
|
|
return std::move(response);
|
2018-02-14 21:23:20 +01:00
|
|
|
}
|
2018-02-16 21:36:30 +01:00
|
|
|
|
|
|
|
resource::response
|
|
|
|
get__joined_members(client &client,
|
|
|
|
const resource::request &request,
|
|
|
|
const m::room::id &room_id)
|
|
|
|
{
|
2019-03-14 21:55:44 +01:00
|
|
|
const m::room room
|
2018-02-16 21:36:30 +01:00
|
|
|
{
|
|
|
|
room_id
|
|
|
|
};
|
|
|
|
|
2019-03-14 21:55:44 +01:00
|
|
|
if(!exists(room))
|
|
|
|
throw m::NOT_FOUND
|
|
|
|
{
|
|
|
|
"Room %s does not exist.",
|
|
|
|
string_view{room_id}
|
|
|
|
};
|
2018-02-16 21:36:30 +01:00
|
|
|
|
2019-08-14 10:01:46 +02:00
|
|
|
if(!visible(room, request.user_id))
|
2019-03-14 21:55:44 +01:00
|
|
|
throw m::ACCESS_DENIED
|
2018-02-16 21:36:30 +01:00
|
|
|
{
|
2019-03-14 21:55:44 +01:00
|
|
|
"You do not have permission to view %s joined members.",
|
|
|
|
string_view{room_id}
|
|
|
|
};
|
2018-02-16 21:36:30 +01:00
|
|
|
|
2019-03-14 21:55:44 +01:00
|
|
|
resource::response::chunked response
|
2018-02-16 21:36:30 +01:00
|
|
|
{
|
2019-03-14 21:55:44 +01:00
|
|
|
client, http::OK
|
2018-02-16 21:36:30 +01:00
|
|
|
};
|
|
|
|
|
2019-03-14 21:55:44 +01:00
|
|
|
json::stack out
|
2018-02-16 21:36:30 +01:00
|
|
|
{
|
2019-03-14 21:55:44 +01:00
|
|
|
response.buf, response.flusher()
|
|
|
|
};
|
|
|
|
|
|
|
|
json::stack::object top
|
|
|
|
{
|
|
|
|
out
|
2018-02-16 21:36:30 +01:00
|
|
|
};
|
2019-03-14 21:55:44 +01:00
|
|
|
|
|
|
|
json::stack::object joined
|
|
|
|
{
|
|
|
|
top, "joined"
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::room::members members
|
|
|
|
{
|
|
|
|
room
|
|
|
|
};
|
|
|
|
|
2019-08-25 05:37:37 +02:00
|
|
|
members.for_each("join", []
|
|
|
|
(const m::user::id &user_id, const m::event::idx &event_idx)
|
|
|
|
{
|
|
|
|
m::prefetch(event_idx);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2019-07-24 04:31:40 +02:00
|
|
|
members.for_each("join", [&joined, &room]
|
|
|
|
(const m::user::id &user_id, const m::event::idx &event_idx)
|
2019-03-14 21:55:44 +01:00
|
|
|
{
|
|
|
|
json::stack::object room_member
|
|
|
|
{
|
|
|
|
joined, user_id
|
|
|
|
};
|
|
|
|
|
|
|
|
m::get(std::nothrow, event_idx, "content", [&room_member]
|
|
|
|
(const json::object &content)
|
|
|
|
{
|
|
|
|
for(const auto &[key, val] : content)
|
|
|
|
json::stack::member
|
|
|
|
{
|
|
|
|
room_member, key, val
|
|
|
|
};
|
|
|
|
});
|
2019-07-24 04:31:40 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
2019-03-14 21:55:44 +01:00
|
|
|
|
2019-06-24 07:09:41 +02:00
|
|
|
return std::move(response);
|
2018-02-16 21:36:30 +01:00
|
|
|
}
|