mirror of
https://github.com/matrix-construct/construct
synced 2024-11-25 08:12:37 +01:00
Checkpoint matrix with preliminary federation client and keyserver related.
This commit is contained in:
parent
3a9696fb6c
commit
20869309a2
16 changed files with 1160 additions and 252 deletions
|
@ -67,8 +67,9 @@ struct _name_ \
|
|||
namespace ircd::m
|
||||
{
|
||||
IRCD_M_EXCEPTION(error, UNKNOWN, http::INTERNAL_SERVER_ERROR);
|
||||
IRCD_M_EXCEPTION(error, NOT_FOUND, http::NOT_FOUND);
|
||||
IRCD_M_EXCEPTION(error, BAD_REQUEST, http::BAD_REQUEST);
|
||||
IRCD_M_EXCEPTION(error, BAD_JSON, http::BAD_REQUEST);
|
||||
IRCD_M_EXCEPTION(error, NOT_FOUND, http::NOT_FOUND);
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
|
@ -102,6 +102,7 @@ struct ircd::m::event
|
|||
|
||||
// Queue of contexts waiting to see the next inserted event
|
||||
static ctx::view<const event> inserted;
|
||||
static id::buf head;
|
||||
|
||||
static const_iterator find(const id &);
|
||||
static void insert(json::iov &);
|
||||
|
|
|
@ -113,4 +113,8 @@ struct ircd::m::filter
|
|||
|
||||
using super_type::tuple;
|
||||
using super_type::operator=;
|
||||
|
||||
static size_t size(const string_view &filter_id);
|
||||
|
||||
filter(const string_view &filter_id, const mutable_buffer &);
|
||||
};
|
||||
|
|
87
include/ircd/m/keys.h
Normal file
87
include/ircd/m/keys.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* charybdis: 21st Century IRC++d
|
||||
*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_M_KEYS_H
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wsubobject-linkage"
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
struct key;
|
||||
}
|
||||
|
||||
namespace ircd::my
|
||||
{
|
||||
extern ed25519::pk public_key;
|
||||
extern ed25519::sk secret_key;
|
||||
extern std::string public_key_b64;
|
||||
}
|
||||
|
||||
namespace ircd::m::name
|
||||
{
|
||||
constexpr const char *const server_name {"server_name"};
|
||||
constexpr const char *const verify_keys {"verify_keys"};
|
||||
constexpr const char *const old_verify_keys {"old_verify_keys"};
|
||||
// constexpr const char *const signatures {"signatures"};
|
||||
constexpr const char *const tls_fingerprints {"tls_fingerprints"};
|
||||
constexpr const char *const valid_until_ts {"valid_until_ts"};
|
||||
}
|
||||
|
||||
/// 2.2.1.1 Publishing Keys
|
||||
///
|
||||
/// Key Type, Description
|
||||
/// server_name String, DNS name of the homeserver.
|
||||
/// verify_keys Object, Public keys of the homeserver for verifying digital signatures.
|
||||
/// old_verify_keys Object, The public keys that the server used to use and when it stopped using them.
|
||||
/// signatures Object, Digital signatures for this object signed using the verify_keys.
|
||||
/// tls_fingerprints Array of Objects, Hashes of X.509 TLS certificates used by this this server encoded as Unpadded Base64.
|
||||
/// valid_until_ts Integer, POSIX timestamp when the list of valid keys should be refreshed.
|
||||
///
|
||||
struct ircd::m::key
|
||||
:json::tuple
|
||||
<
|
||||
json::property<name::old_verify_keys, json::object>,
|
||||
json::property<name::server_name, string_view>,
|
||||
json::property<name::signatures, json::object>,
|
||||
json::property<name::tls_fingerprints, json::array>,
|
||||
json::property<name::valid_until_ts, time_t>,
|
||||
json::property<name::verify_keys, json::object>
|
||||
>
|
||||
{
|
||||
static room keys;
|
||||
|
||||
using super_type::tuple;
|
||||
using super_type::operator=;
|
||||
};
|
||||
|
||||
namespace ircd::m::keys
|
||||
{
|
||||
using closure = std::function<void (const key &)>;
|
||||
|
||||
bool get(const string_view &server_name, const closure &);
|
||||
void set(const key &);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
|
@ -43,13 +43,15 @@ struct ircd::m::room
|
|||
|
||||
id room_id;
|
||||
|
||||
bool membership(const m::id::user &, const string_view &membership = "join") const;
|
||||
event::id head(event::id::buf &) const;
|
||||
|
||||
event::id::buf send(json::iov &event);
|
||||
event::id::buf send(const json::members &event);
|
||||
|
||||
bool is_member(const m::id::user &, const string_view &membership = "join");
|
||||
void membership(const m::id::user &, json::iov &content);
|
||||
void leave(const m::id::user &, json::iov &content);
|
||||
void join(const m::id::user &, json::iov &content);
|
||||
|
||||
void create(const m::id::user &sender, const m::id::user &creator, json::iov &content);
|
||||
|
||||
room(const id &room_id)
|
||||
|
@ -60,19 +62,3 @@ struct ircd::m::room
|
|||
:room_id{}
|
||||
{}
|
||||
};
|
||||
|
||||
struct ircd::m::room::members
|
||||
{
|
||||
id room_id;
|
||||
|
||||
bool is_member(const m::id::user &) const;
|
||||
bool membership(const m::id::user &, const string_view & = "join") const;
|
||||
|
||||
members(const id &room_id)
|
||||
:room_id{room_id}
|
||||
{}
|
||||
|
||||
members(const room &room)
|
||||
:room_id{room.room_id}
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -29,15 +29,15 @@ namespace ircd {
|
|||
namespace m {
|
||||
|
||||
struct session
|
||||
:client
|
||||
{
|
||||
std::shared_ptr<ircd::client> client;
|
||||
std::string access_token;
|
||||
std::deque<std::string> tape;
|
||||
std::multimap<string_view, string_view> resource;
|
||||
|
||||
json::object operator()(parse::buffer &pb, request &);
|
||||
|
||||
session(const host_port &);
|
||||
session(const hostport &);
|
||||
};
|
||||
|
||||
} // namespace m
|
||||
|
|
|
@ -28,9 +28,6 @@
|
|||
namespace ircd::m
|
||||
{
|
||||
struct user;
|
||||
|
||||
extern user me;
|
||||
extern room my_room;
|
||||
}
|
||||
|
||||
struct ircd::m::user
|
||||
|
|
534
ircd/matrix.cc
534
ircd/matrix.cc
|
@ -24,8 +24,8 @@
|
|||
// m/session.h
|
||||
//
|
||||
|
||||
ircd::m::session::session(const host_port &host_port)
|
||||
:client{host_port}
|
||||
ircd::m::session::session(const hostport &host_port)
|
||||
:client{std::make_shared<ircd::client>(host_port)}
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -33,29 +33,111 @@ ircd::json::object
|
|||
ircd::m::session::operator()(parse::buffer &pb,
|
||||
request &r)
|
||||
{
|
||||
parse::capstan pc
|
||||
const json::member origin
|
||||
{
|
||||
pb, read_closure(*this)
|
||||
"origin", my_host()
|
||||
};
|
||||
|
||||
const http::line::header headers[]
|
||||
const json::member destination
|
||||
{
|
||||
{ "Content-Type"s, "application/json"s }
|
||||
//host(remote_hostport(*client->sock))
|
||||
"destination", "zemos.net"
|
||||
};
|
||||
|
||||
const json::member method
|
||||
{
|
||||
"method", r.method
|
||||
};
|
||||
|
||||
const std::string uri
|
||||
{
|
||||
std::string {"/"} + std::string{r.path} +
|
||||
(r.query? (std::string{"?"} + std::string{r.query}) : std::string{})
|
||||
};
|
||||
|
||||
json::iov iov;
|
||||
const json::iov::push pushed[]
|
||||
{
|
||||
{ iov, json::member { "uri", uri } },
|
||||
{ iov, origin },
|
||||
{ iov, method },
|
||||
{ iov, destination },
|
||||
};
|
||||
|
||||
const json::iov::add_if content
|
||||
{
|
||||
iov, r.content.size() > 2, json::member
|
||||
{
|
||||
"content", r.content
|
||||
}
|
||||
};
|
||||
|
||||
size_t headers{2};
|
||||
http::line::header header[3]
|
||||
{
|
||||
{ "Content-Type", "application/json" },
|
||||
{ "User-Agent", "IRCd" }
|
||||
};
|
||||
|
||||
char x_matrix_buf[2048];
|
||||
if(startswith(r.path, "_matrix/federation"))
|
||||
{
|
||||
// These buffers can be comfortably large if they're not on a stack and
|
||||
// nothing in this procedure has a yield; the assertion is tripped if so
|
||||
static char request_object_buffer[4096];
|
||||
static char signature_buffer[128];
|
||||
ctx::critical_assertion ca;
|
||||
|
||||
const auto request_object
|
||||
{
|
||||
json::stringify(request_object_buffer, iov)
|
||||
};
|
||||
|
||||
std::cout << request_object << std::endl;
|
||||
|
||||
const ed25519::sig sig
|
||||
{
|
||||
my::secret_key.sign(request_object)
|
||||
};
|
||||
|
||||
const auto signature
|
||||
{
|
||||
b64encode_unpadded(signature_buffer, sig)
|
||||
};
|
||||
|
||||
const auto x_matrix_len
|
||||
{
|
||||
fmt::sprintf(x_matrix_buf, "X-Matrix origin=%s,key=\"ed25519:pk2\",sig=\"%s\"",
|
||||
string_view{origin.second},
|
||||
signature)
|
||||
};
|
||||
|
||||
const string_view x_matrix
|
||||
{
|
||||
x_matrix_buf, size_t(x_matrix_len)
|
||||
};
|
||||
|
||||
header[headers++] = { "Authorization", x_matrix };
|
||||
}
|
||||
|
||||
http::request
|
||||
{
|
||||
host(remote_addr(*this)),
|
||||
string_view{destination.second}, //host(remote(*client)),
|
||||
r.method,
|
||||
r.path,
|
||||
r.query,
|
||||
r.content,
|
||||
write_closure(*this),
|
||||
{ headers, headers + (sizeof(headers) / sizeof(http::line::header)) }
|
||||
write_closure(*client),
|
||||
{ header, headers }
|
||||
};
|
||||
|
||||
http::code status;
|
||||
json::object object;
|
||||
parse::capstan pc
|
||||
{
|
||||
pb, read_closure(*client)
|
||||
};
|
||||
|
||||
http::response
|
||||
{
|
||||
pc,
|
||||
|
@ -81,36 +163,56 @@ ircd::m::session::operator()(parse::buffer &pb,
|
|||
namespace ircd::m
|
||||
{
|
||||
std::map<std::string, ircd::module> modules;
|
||||
ircd::listener *listener;
|
||||
ircd::net::listener *listener;
|
||||
|
||||
static void leave_ircd_room();
|
||||
static void join_ircd_room();
|
||||
void bootstrap();
|
||||
static void init_keys(const std::string &secret_key_file);
|
||||
static void bootstrap();
|
||||
}
|
||||
|
||||
const ircd::m::user::id::buf
|
||||
ircd_user_id
|
||||
{
|
||||
"ircd", "cdc.z" //TODO: hostname
|
||||
};
|
||||
|
||||
ircd::m::user
|
||||
ircd::m::me
|
||||
{
|
||||
"@ircd:cdc.z"
|
||||
ircd_user_id
|
||||
};
|
||||
|
||||
const ircd::m::room::id::buf
|
||||
ircd_room_id
|
||||
{
|
||||
"ircd", ircd::my_host()
|
||||
};
|
||||
|
||||
ircd::m::room
|
||||
ircd::m::my_room
|
||||
{
|
||||
ircd::m::room::id{"!ircd:cdc.z"}
|
||||
ircd_room_id
|
||||
};
|
||||
|
||||
ircd::m::room
|
||||
ircd::m::filter::filters
|
||||
ircd::string_view
|
||||
ircd::m::my_host()
|
||||
{
|
||||
ircd::m::room::id{"!filters:cdc.z"}
|
||||
};
|
||||
return "cdc.z:8447"; //me.user_id.host();
|
||||
}
|
||||
|
||||
ircd::m::init::init()
|
||||
try
|
||||
{
|
||||
init_keys("charybdis.sk");
|
||||
|
||||
const string_view prefixes[]
|
||||
{
|
||||
"client_", "key_",
|
||||
};
|
||||
|
||||
for(const auto &name : mods::available())
|
||||
if(startswith(name, "client_"))
|
||||
if(startswith_any(name, std::begin(prefixes), std::end(prefixes)))
|
||||
modules.emplace(name, name);
|
||||
|
||||
if(db::sequence(*event::events) == 0)
|
||||
|
@ -123,14 +225,14 @@ try
|
|||
{ "name", "Chat Matrix" },
|
||||
{ "host", "0.0.0.0" },
|
||||
{ "port", 8447 },
|
||||
{ "ssl_certificate_file", "/home/jason/zemos.net.tls.crt" },
|
||||
{ "ssl_certificate_chain_file", "/home/jason/zemos.net.tls.crt" },
|
||||
{ "ssl_certificate_file", "/home/jason/zemos.net.tls2.crt" },
|
||||
{ "ssl_certificate_chain_file", "/home/jason/zemos.net.tls2.crt" },
|
||||
{ "ssl_tmp_dh_file", "/home/jason/zemos.net.tls.dh" },
|
||||
{ "ssl_private_key_file_pem", "/home/jason/zemos.net.tls.key" },
|
||||
{ "ssl_private_key_file_pem", "/home/jason/zemos.net.tls2.key" },
|
||||
})};
|
||||
|
||||
//TODO: conf obviously
|
||||
listener = new ircd::listener{options};
|
||||
listener = new ircd::net::listener{options};
|
||||
|
||||
join_ircd_room();
|
||||
}
|
||||
|
@ -174,6 +276,11 @@ ircd::m::leave_ircd_room()
|
|||
my_room.leave(me.user_id, content);
|
||||
}
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
static void bootstrap_keys();
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::bootstrap()
|
||||
{
|
||||
|
@ -192,6 +299,174 @@ ircd::m::bootstrap()
|
|||
user::accounts.join(me.user_id, content);
|
||||
user::sessions.create(me.user_id, me.user_id, content);
|
||||
filter::filters.create(me.user_id, me.user_id, content);
|
||||
bootstrap_keys();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/keys.h
|
||||
//
|
||||
|
||||
const ircd::m::room::id::buf
|
||||
keys_room_id
|
||||
{
|
||||
"keys", ircd::my_host()
|
||||
};
|
||||
|
||||
ircd::m::room
|
||||
ircd::m::key::keys
|
||||
{
|
||||
keys_room_id
|
||||
};
|
||||
|
||||
ircd::ed25519::sk
|
||||
ircd::my::secret_key
|
||||
{};
|
||||
|
||||
ircd::ed25519::pk
|
||||
ircd::my::public_key
|
||||
{};
|
||||
|
||||
std::string
|
||||
ircd::my::public_key_b64
|
||||
{};
|
||||
|
||||
static void
|
||||
ircd::m::init_keys(const std::string &sk_file)
|
||||
{
|
||||
my::secret_key = ed25519::sk
|
||||
{
|
||||
sk_file, &my::public_key
|
||||
};
|
||||
|
||||
my::public_key_b64 = b64encode_unpadded(my::public_key);
|
||||
|
||||
log::info("My ed25519 public key is: %s",
|
||||
my::public_key_b64);
|
||||
}
|
||||
|
||||
namespace ircd
|
||||
{
|
||||
size_t certbytes(const mutable_raw_buffer &buf, const std::string &certfile);
|
||||
}
|
||||
|
||||
static void
|
||||
ircd::m::bootstrap_keys()
|
||||
{
|
||||
json::iov content;
|
||||
key::keys.create(me.user_id, me.user_id, content);
|
||||
|
||||
key my_key;
|
||||
json::val<name::server_name>(my_key) = my_host();
|
||||
json::val<name::old_verify_keys>(my_key) = "{}";
|
||||
|
||||
const auto valid_until
|
||||
{
|
||||
ircd::time<milliseconds>() + duration_cast<milliseconds>(hours(2160)).count()
|
||||
};
|
||||
json::val<name::valid_until_ts>(my_key) = valid_until;
|
||||
|
||||
static char verify_keys_buf[256];
|
||||
json::val<name::verify_keys>(my_key) = json::stringify(verify_keys_buf, json::members
|
||||
{
|
||||
{ "ed25519:pk2", json::members
|
||||
{
|
||||
{ "key", my::public_key_b64 }
|
||||
}}
|
||||
});
|
||||
|
||||
//static unsigned char pembuf[4096];
|
||||
//const auto cbsz{ircd::certbytes(pembuf, "/home/jason/zemos.net.tls.crt")};
|
||||
|
||||
static std::array<uint8_t, 32> tls_hash;
|
||||
a2u(tls_hash, "C259B83ABED34D81B31F773737574FBD966CE33BDED708BF502CA1D4CEC3D318");
|
||||
|
||||
static char tls_b64_buf[256];
|
||||
const json::members tlsfps
|
||||
{
|
||||
{ "sha256", b64encode_unpadded(tls_b64_buf, tls_hash) }
|
||||
};
|
||||
|
||||
const json::value tlsfp[1]
|
||||
{
|
||||
{ tlsfps }
|
||||
};
|
||||
|
||||
static char tls_fingerprints_buf[256];
|
||||
json::val<name::tls_fingerprints>(my_key) = json::stringify(tls_fingerprints_buf, json::value
|
||||
{
|
||||
tlsfp, 1
|
||||
});
|
||||
|
||||
const std::string presig
|
||||
{
|
||||
json::string(my_key)
|
||||
};
|
||||
|
||||
const ed25519::sig sig
|
||||
{
|
||||
my::secret_key.sign(const_raw_buffer{presig})
|
||||
};
|
||||
|
||||
static char signature[128], signatures[256];
|
||||
json::val<name::signatures>(my_key) = json::stringify(signatures, json::members
|
||||
{
|
||||
{ my_host(), json::members
|
||||
{
|
||||
{ "ed25519:pk2", b64encode_unpadded(signature, sig) }
|
||||
}}
|
||||
});
|
||||
|
||||
keys::set(my_key);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::keys::set(const key &key)
|
||||
{
|
||||
const auto &state_key
|
||||
{
|
||||
at<name::server_name>(key)
|
||||
};
|
||||
|
||||
const m::user::id::buf sender
|
||||
{
|
||||
"ircd", at<name::server_name>(key)
|
||||
};
|
||||
|
||||
const auto content
|
||||
{
|
||||
json::string(key)
|
||||
};
|
||||
|
||||
json::iov event;
|
||||
json::iov::push members[]
|
||||
{
|
||||
{ event, json::member { "type", "ircd.key" }},
|
||||
{ event, json::member { "state_key", state_key }},
|
||||
{ event, json::member { "sender", sender }},
|
||||
{ event, json::member { "content", content }}
|
||||
};
|
||||
|
||||
key::keys.send(event);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::keys::get(const string_view &server_name,
|
||||
const closure &closure)
|
||||
{
|
||||
const m::event::query<m::event::where::equal> query
|
||||
{
|
||||
{ "room_id", key::keys.room_id },
|
||||
{ "type", "ircd.key" },
|
||||
{ "state_key", server_name },
|
||||
};
|
||||
|
||||
return m::events::test(query, [&closure]
|
||||
(const auto &event)
|
||||
{
|
||||
closure(json::val<name::content>(event));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -249,6 +524,65 @@ ircd::m::dbs::init_modules()
|
|||
modules.emplace(name, name);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/filter.h
|
||||
//
|
||||
|
||||
const ircd::m::room::id::buf
|
||||
filters_room_id
|
||||
{
|
||||
"filters", ircd::my_host()
|
||||
};
|
||||
|
||||
ircd::m::room
|
||||
ircd::m::filter::filters
|
||||
{
|
||||
filters_room_id
|
||||
};
|
||||
|
||||
ircd::m::filter::filter(const string_view &filter_id,
|
||||
const mutable_buffer &buf)
|
||||
{
|
||||
const m::event::query<m::event::where::equal> query
|
||||
{
|
||||
{ "room_id", filters.room_id },
|
||||
{ "type", "ircd.filter" },
|
||||
{ "state_key", filter_id },
|
||||
};
|
||||
|
||||
size_t len{0};
|
||||
m::events::test(query, [&buf, &len]
|
||||
(const auto &event)
|
||||
{
|
||||
len = copy(buf, json::val<name::content>(event));
|
||||
return true;
|
||||
});
|
||||
|
||||
new (this) filter{json::object{buf}};
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::filter::size(const string_view &filter_id)
|
||||
{
|
||||
const m::event::query<m::event::where::equal> query
|
||||
{
|
||||
{ "room_id", filters.room_id },
|
||||
{ "type", "ircd.filter" },
|
||||
{ "state_key", filter_id },
|
||||
};
|
||||
|
||||
size_t ret{0};
|
||||
m::events::test(query, [&ret]
|
||||
(const auto &event)
|
||||
{
|
||||
ret = json::val<name::content>(event).size();
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/room.h
|
||||
|
@ -310,12 +644,12 @@ void
|
|||
ircd::m::room::membership(const m::id::user &user_id,
|
||||
json::iov &content)
|
||||
{
|
||||
const string_view membership
|
||||
const string_view &membership
|
||||
{
|
||||
content.at("membership")
|
||||
};
|
||||
|
||||
if(is_member(user_id, membership))
|
||||
if(this->membership(user_id, membership))
|
||||
throw m::ALREADY_MEMBER
|
||||
{
|
||||
"Member '%s' is already '%s'.", string_view{user_id}, membership
|
||||
|
@ -340,15 +674,56 @@ ircd::m::room::membership(const m::id::user &user_id,
|
|||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::is_member(const m::id::user &user_id,
|
||||
const string_view &membership)
|
||||
ircd::m::room::membership(const m::id::user &user_id,
|
||||
const string_view &membership)
|
||||
const
|
||||
{
|
||||
const members members
|
||||
const event::query<event::where::equal> member_event
|
||||
{
|
||||
room_id
|
||||
{ "room_id", room_id },
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id },
|
||||
};
|
||||
|
||||
return members.membership(user_id, membership);
|
||||
if(!membership)
|
||||
return m::events::test(member_event);
|
||||
|
||||
const event::query<event::where::test> membership_test{[&membership]
|
||||
(const auto &event)
|
||||
{
|
||||
const json::object &content
|
||||
{
|
||||
json::at<m::name::content>(event)
|
||||
};
|
||||
|
||||
const auto &existing_membership
|
||||
{
|
||||
unquote(content.at("membership"))
|
||||
};
|
||||
|
||||
return membership == existing_membership;
|
||||
}};
|
||||
|
||||
return m::events::test(member_event && membership_test);
|
||||
}
|
||||
|
||||
ircd::m::event::id
|
||||
ircd::m::room::head(event::id::buf &buf)
|
||||
const
|
||||
{
|
||||
const event::query<event::where::equal> query
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
};
|
||||
|
||||
events::test(query, [&buf]
|
||||
(const auto &event)
|
||||
{
|
||||
buf = json::val<name::event_id>(event);
|
||||
return true;
|
||||
});
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
|
@ -385,69 +760,33 @@ ircd::m::room::send(json::iov &event)
|
|||
return generated_event_id;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::members::membership(const m::id::user &user_id,
|
||||
const string_view &membership)
|
||||
const
|
||||
{
|
||||
if(membership.empty())
|
||||
return is_member(user_id);
|
||||
|
||||
const event::query<event::where::equal> member_event
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id },
|
||||
};
|
||||
|
||||
const event::query<event::where::test> membership_test{[&membership]
|
||||
(const auto &event)
|
||||
{
|
||||
const json::object &content
|
||||
{
|
||||
json::at<m::name::content>(event)
|
||||
};
|
||||
|
||||
const auto &existing_membership
|
||||
{
|
||||
unquote(content.at("membership"))
|
||||
};
|
||||
|
||||
return membership == existing_membership;
|
||||
}};
|
||||
|
||||
return m::events::test(member_event && membership_test);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::members::is_member(const m::id::user &user_id)
|
||||
const
|
||||
{
|
||||
const event::query<event::where::equal> member_event
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id },
|
||||
};
|
||||
|
||||
return m::events::test(member_event);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/user.h
|
||||
//
|
||||
|
||||
const ircd::m::room::id::buf
|
||||
accounts_room_id
|
||||
{
|
||||
"accounts", ircd::my_host()
|
||||
};
|
||||
|
||||
ircd::m::room
|
||||
ircd::m::user::accounts
|
||||
{
|
||||
ircd::m::room::id{"!accounts:cdc.z"}
|
||||
accounts_room_id
|
||||
};
|
||||
|
||||
const ircd::m::room::id::buf
|
||||
sessions_room_id
|
||||
{
|
||||
"sessions", ircd::my_host()
|
||||
};
|
||||
|
||||
ircd::m::room
|
||||
ircd::m::user::sessions
|
||||
{
|
||||
ircd::m::room::id{"!sessions:cdc.z"}
|
||||
sessions_room_id
|
||||
};
|
||||
|
||||
/// Register the user by joining them to the accounts room.
|
||||
|
@ -554,31 +893,7 @@ bool
|
|||
ircd::m::user::is_active()
|
||||
const
|
||||
{
|
||||
const auto &room_id{accounts.room_id};
|
||||
const m::event::query<event::where::equal> member_event
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id },
|
||||
};
|
||||
|
||||
const m::event::query<event::where::test> is_joined{[]
|
||||
(const auto &event)
|
||||
{
|
||||
const json::object &content
|
||||
{
|
||||
json::val<m::name::content>(event)
|
||||
};
|
||||
|
||||
const auto &membership
|
||||
{
|
||||
unquote(content["membership"])
|
||||
};
|
||||
|
||||
return membership == "join";
|
||||
}};
|
||||
|
||||
return events::test(member_event && is_joined);
|
||||
return accounts.membership(user_id);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1068,12 +1383,16 @@ ircd::ctx::view<const ircd::m::event>
|
|||
ircd::m::event::inserted
|
||||
{};
|
||||
|
||||
ircd::m::event::id::buf
|
||||
ircd::m::event::head
|
||||
{};
|
||||
|
||||
void
|
||||
ircd::m::event::insert(json::iov &iov)
|
||||
{
|
||||
const id::event::buf generated_event_id
|
||||
{
|
||||
iov.has("event_id")? id::event::buf{} : id::event::buf{id::generate, "cdc.z"}
|
||||
iov.has("event_id")? id::event::buf{} : id::event::buf{id::generate, my_host()}
|
||||
};
|
||||
|
||||
const json::iov::add_if event_id
|
||||
|
@ -1083,7 +1402,7 @@ ircd::m::event::insert(json::iov &iov)
|
|||
|
||||
const json::iov::set origin_server_ts
|
||||
{
|
||||
iov, { "origin_server_ts", time<milliseconds>() }
|
||||
iov, { "origin_server_ts", ircd::time<milliseconds>() }
|
||||
};
|
||||
|
||||
const m::event event
|
||||
|
@ -1108,9 +1427,8 @@ ircd::m::event::insert(json::iov &iov)
|
|||
};
|
||||
|
||||
append_indexes(event, txn);
|
||||
|
||||
txn(*event::events);
|
||||
|
||||
event::head = json::at<name::event_id>(event);
|
||||
event::inserted.notify(event);
|
||||
}
|
||||
|
||||
|
|
|
@ -120,9 +120,9 @@ try
|
|||
// Sets up the query to find the access_token in the sessions rooms
|
||||
const m::event::query<m::event::where::equal> query
|
||||
{
|
||||
{ "type", "ircd.access_token" },
|
||||
{ "state_key", access_token },
|
||||
{ "room_id", "!sessions:cdc.z" },
|
||||
{ "type", "ircd.access_token" },
|
||||
{ "state_key", access_token },
|
||||
{ "room_id", m::user::sessions.room_id },
|
||||
};
|
||||
|
||||
const bool result
|
||||
|
|
|
@ -81,6 +81,16 @@ client_module_LTLIBRARIES = \
|
|||
client/client_voip_turnserver.la \
|
||||
###
|
||||
|
||||
# This puts the source in key/ but the installed
|
||||
# library is key_X.so in the main modules dir.
|
||||
key_moduledir = @moduledir@
|
||||
key_key_server_la_SOURCES = key/server.cc
|
||||
key_key_query_la_SOURCES = key/query.cc
|
||||
key_module_LTLIBRARIES = \
|
||||
key/key_server.la \
|
||||
key/key_query.la \
|
||||
###
|
||||
|
||||
if JS
|
||||
server_moduledir = @moduledir@
|
||||
server_server_console_la_SOURCES = server/console.cc
|
||||
|
|
|
@ -26,7 +26,7 @@ struct room
|
|||
{
|
||||
static constexpr const auto base_url
|
||||
{
|
||||
"_matrix/client/r0/rooms/"
|
||||
"_matrix/client/r0/rooms"
|
||||
};
|
||||
|
||||
using resource::resource;
|
||||
|
@ -45,6 +45,102 @@ mapi::header IRCD_MODULE
|
|||
"registers the resource 'client/rooms'"
|
||||
};
|
||||
|
||||
resource::response
|
||||
get_messages(client &client,
|
||||
const resource::request &request,
|
||||
const string_view ¶ms,
|
||||
const m::room::id &room_id)
|
||||
{
|
||||
const m::event::query<m::event::where::equal> event_in_room
|
||||
{
|
||||
{ "room_id", room_id }
|
||||
};
|
||||
|
||||
const m::event::query<m::event::where::test> event_not_state
|
||||
{
|
||||
[](const auto &event)
|
||||
{
|
||||
return !defined(json::val<m::name::state_key>(event));
|
||||
}
|
||||
};
|
||||
|
||||
const auto query
|
||||
{
|
||||
event_in_room && event_not_state
|
||||
};
|
||||
|
||||
const size_t count
|
||||
{
|
||||
std::min(m::events::count(query), 128UL)
|
||||
};
|
||||
|
||||
if(!count)
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"No messages."
|
||||
};
|
||||
|
||||
size_t j(0);
|
||||
json::value ret[count];
|
||||
m::events::for_each(query, [&count, &j, &ret]
|
||||
(const auto &event)
|
||||
{
|
||||
if(j < count)
|
||||
ret[j++] = event;
|
||||
});
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, json::members
|
||||
{
|
||||
{ "chunk", json::value { ret, j } }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
resource::response
|
||||
get_members(client &client,
|
||||
const resource::request &request,
|
||||
const string_view ¶ms,
|
||||
const m::room::id &room_id)
|
||||
{
|
||||
|
||||
const m::event::query<m::event::where::equal> query
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", "" },
|
||||
};
|
||||
|
||||
const auto count
|
||||
{
|
||||
m::events::count(query)
|
||||
};
|
||||
|
||||
if(!count)
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"No members."
|
||||
};
|
||||
|
||||
size_t j(0);
|
||||
json::value ret[count];
|
||||
m::events::for_each(query, [&count, &j, &ret]
|
||||
(const auto &event)
|
||||
{
|
||||
if(j < count)
|
||||
ret[j++] = event;
|
||||
});
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, json::members
|
||||
{
|
||||
{ "chunk", json::value { ret, j } }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
resource::response
|
||||
get_state(client &client,
|
||||
const resource::request &request,
|
||||
|
@ -310,3 +406,46 @@ resource::method method_put
|
|||
method_put.REQUIRES_AUTH
|
||||
}
|
||||
};
|
||||
|
||||
resource::response
|
||||
post_receipt(client &client,
|
||||
const resource::request &request,
|
||||
const string_view ¶ms,
|
||||
const m::room::id &room_id)
|
||||
{
|
||||
string_view token[4];
|
||||
if(tokens(params, '/', token) != 4)
|
||||
throw m::BAD_REQUEST{"receipt type and event_id required"};
|
||||
|
||||
const string_view &receipt_type{token[2]};
|
||||
const string_view &event_id{token[3]};
|
||||
std::cout << "type: " << receipt_type << " eid: " << event_id << std::endl;
|
||||
}
|
||||
|
||||
resource::response
|
||||
post_rooms(client &client, const resource::request &request)
|
||||
{
|
||||
const auto params
|
||||
{
|
||||
lstrip(request.head.path, room::base_url)
|
||||
};
|
||||
|
||||
string_view token[2];
|
||||
if(tokens(params, '/', token) != 2)
|
||||
throw m::BAD_REQUEST{"/rooms command required"};
|
||||
|
||||
m::room::id::buf room_id;
|
||||
urldecode(token[0], room_id);
|
||||
const string_view &cmd{token[1]};
|
||||
|
||||
if(cmd == "receipt")
|
||||
return post_receipt(client, request, params, room_id);
|
||||
}
|
||||
|
||||
resource::method method_POST
|
||||
{
|
||||
rooms_resource, "POST", post_rooms,
|
||||
{
|
||||
method_put.REQUIRES_AUTH
|
||||
}
|
||||
};
|
||||
|
|
|
@ -39,18 +39,6 @@ resource sync_resource
|
|||
sync_description
|
||||
};
|
||||
|
||||
struct syncpoll
|
||||
{
|
||||
static std::multimap<std::string, syncpoll> polling;
|
||||
static std::multimap<steady_point, decltype(polling)::iterator> pollout;
|
||||
|
||||
std::weak_ptr<ircd::client> client;
|
||||
decltype(pollout)::iterator it { std::end(pollout) };
|
||||
};
|
||||
|
||||
decltype(syncpoll::polling) syncpoll::polling {};
|
||||
decltype(syncpoll::pollout) syncpoll::pollout {};
|
||||
|
||||
void longpoll(client &client, const resource::request &request, const steady_point &timeout);
|
||||
|
||||
void synchronizer_worker();
|
||||
|
@ -88,71 +76,11 @@ mapi::header IRCD_MODULE
|
|||
};
|
||||
|
||||
resource::response
|
||||
sync_now(client &client,
|
||||
const resource::request &request,
|
||||
const string_view &filter_id,
|
||||
const bool &full_state,
|
||||
const string_view &set_presence)
|
||||
{
|
||||
|
||||
json::value events[1];
|
||||
|
||||
const json::members timeline
|
||||
{
|
||||
{ "events", { events, 1 } }
|
||||
};
|
||||
|
||||
const json::members state
|
||||
{
|
||||
{ "events", { events, 1 } }
|
||||
};
|
||||
|
||||
const json::members join
|
||||
{
|
||||
{ "timeline", timeline },
|
||||
{ "state", state },
|
||||
};
|
||||
|
||||
const json::object leave{};
|
||||
const json::object invite{};
|
||||
const json::members rooms
|
||||
{
|
||||
{ "leave", leave },
|
||||
{ "join", join },
|
||||
{ "invite", invite },
|
||||
};
|
||||
|
||||
const string_view next_batch{};
|
||||
const json::object presence{};
|
||||
|
||||
const m::event::id head_event_id
|
||||
{
|
||||
"$12382382:cdc.z"
|
||||
};
|
||||
|
||||
const json::members content
|
||||
{
|
||||
{ "event_id", head_event_id }
|
||||
};
|
||||
|
||||
m::user::sessions.send(
|
||||
{
|
||||
{ "type", "ircd.tape.head" },
|
||||
{ "state_key", request.query.at("access_token") },
|
||||
{ "sender", request.user_id },
|
||||
{ "content", content },
|
||||
});
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, json::members
|
||||
{
|
||||
{ "next_batch", next_batch },
|
||||
{ "rooms", rooms },
|
||||
{ "presence", presence }
|
||||
}
|
||||
};
|
||||
}
|
||||
initial_sync(client &client,
|
||||
const resource::request &request,
|
||||
const string_view &filter_id,
|
||||
const bool &full_state,
|
||||
const string_view &set_presence);
|
||||
|
||||
resource::response
|
||||
sync(client &client, const resource::request &request)
|
||||
|
@ -193,15 +121,9 @@ sync(client &client, const resource::request &request)
|
|||
request.query["set_presence"]
|
||||
};
|
||||
|
||||
// Start a new spool for client
|
||||
if(!since)
|
||||
return sync_now(client, request, filter_id, full_state, set_presence);
|
||||
|
||||
// The !sessions:your.host room is where the ircd.tape.head event holds
|
||||
// the state we use to calculate the last event the user has seen.
|
||||
const m::room::state sessions
|
||||
{
|
||||
m::user::sessions
|
||||
};
|
||||
return initial_sync(client, request, filter_id, full_state, set_presence);
|
||||
|
||||
// The ircd.tape.head
|
||||
const m::event::query<m::event::where::equal> query
|
||||
|
@ -254,24 +176,49 @@ resource::method get_sync
|
|||
/// Input
|
||||
///
|
||||
///
|
||||
|
||||
struct syncpoll
|
||||
{
|
||||
static std::list<syncpoll> polling;
|
||||
static std::multimap<steady_point, decltype(polling)::iterator> pollout;
|
||||
|
||||
std::string user_id;
|
||||
std::string since;
|
||||
std::string access_token; // can get rid of this and use some session id
|
||||
std::weak_ptr<ircd::client> client;
|
||||
decltype(pollout)::iterator it { std::end(pollout) };
|
||||
};
|
||||
|
||||
decltype(syncpoll::polling) syncpoll::polling {};
|
||||
decltype(syncpoll::pollout) syncpoll::pollout {};
|
||||
|
||||
void
|
||||
longpoll(client &client,
|
||||
const resource::request &request,
|
||||
const steady_point &timeout)
|
||||
{
|
||||
static auto &polling{syncpoll::polling};
|
||||
static auto &pollout{syncpoll::pollout};
|
||||
|
||||
const auto it
|
||||
{
|
||||
syncpoll::polling.emplace(request.user_id, syncpoll{weak_from(client)})
|
||||
polling.emplace(polling.end(), syncpoll
|
||||
{
|
||||
std::string{request.user_id},
|
||||
std::string{request.query.at("since")},
|
||||
std::string{request.query.at("access_token")},
|
||||
weak_from(client)
|
||||
})
|
||||
};
|
||||
|
||||
syncpoll &data
|
||||
{
|
||||
it->second
|
||||
*it
|
||||
};
|
||||
|
||||
data.it = syncpoll::pollout.emplace(timeout, it);
|
||||
data.it = pollout.emplace(timeout, it);
|
||||
|
||||
if(syncpoll::pollout.size() == 1)
|
||||
if(pollout.size() == 1)
|
||||
notify(synchronizer_timeout_context);
|
||||
}
|
||||
|
||||
|
@ -279,7 +226,7 @@ longpoll(client &client,
|
|||
// Timeout worker stack
|
||||
//
|
||||
|
||||
void synchronizer_timeout(const std::string &user_id, const syncpoll &sp);
|
||||
void synchronizer_timeout(const syncpoll &sp);
|
||||
|
||||
/// This function is the base of an ircd::context which yields until a client
|
||||
/// is due to timeout. This worker reaps timed out clients from the lists.
|
||||
|
@ -302,9 +249,8 @@ try
|
|||
continue;
|
||||
}
|
||||
|
||||
const auto &user_id{iterator->first};
|
||||
const auto &data{iterator->second};
|
||||
synchronizer_timeout(user_id, data);
|
||||
const auto &data{*iterator};
|
||||
synchronizer_timeout(data);
|
||||
polling.erase(iterator);
|
||||
pollout.erase(std::begin(pollout));
|
||||
}
|
||||
|
@ -322,8 +268,7 @@ catch(const ircd::ctx::interrupted &e)
|
|||
/// TODO: The http error response should not yield this context. If the sendq
|
||||
/// TODO: is backed up the client should be dc'ed.
|
||||
void
|
||||
synchronizer_timeout(const std::string &user_id,
|
||||
const syncpoll &sp)
|
||||
synchronizer_timeout(const syncpoll &sp)
|
||||
try
|
||||
{
|
||||
const life_guard<client> client
|
||||
|
@ -345,6 +290,7 @@ catch(const std::exception &e)
|
|||
// Main worker stack
|
||||
//
|
||||
|
||||
bool update_sync(const syncpoll &data, const m::event &event, const m::room &);
|
||||
void synchronize(const m::event &, const m::room::id &);
|
||||
void synchronize(const m::event &);
|
||||
|
||||
|
@ -354,16 +300,23 @@ try
|
|||
{
|
||||
while(1) try
|
||||
{
|
||||
const auto &event
|
||||
std::unique_lock<decltype(m::event::inserted)> lock
|
||||
{
|
||||
m::event::inserted.wait()
|
||||
m::event::inserted
|
||||
};
|
||||
|
||||
synchronize(event);
|
||||
// reference to the event on the inserter's stack
|
||||
const auto &event
|
||||
{
|
||||
m::event::inserted.wait(lock)
|
||||
};
|
||||
|
||||
if(!syncpoll::polling.empty())
|
||||
synchronize(event);
|
||||
}
|
||||
catch(const timeout &e)
|
||||
{
|
||||
ircd::log::debug("Synchronizer worker timeout");
|
||||
ircd::log::debug("Synchronizer worker: %s", e.what());
|
||||
}
|
||||
}
|
||||
catch(const ircd::ctx::interrupted &e)
|
||||
|
@ -374,9 +327,6 @@ catch(const ircd::ctx::interrupted &e)
|
|||
void
|
||||
synchronize(const m::event &event)
|
||||
{
|
||||
static auto &polling{syncpoll::polling};
|
||||
static auto &pollout{syncpoll::pollout};
|
||||
|
||||
const auto &room_id
|
||||
{
|
||||
json::val<m::name::room_id>(event)
|
||||
|
@ -388,33 +338,288 @@ synchronize(const m::event &event)
|
|||
return;
|
||||
}
|
||||
|
||||
std::cout << event << std::endl;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void
|
||||
synchronize(const m::event &event,
|
||||
const m::room::id &room_id)
|
||||
{
|
||||
std::cout << event << std::endl;
|
||||
static auto &polling{syncpoll::polling};
|
||||
static auto &pollout{syncpoll::pollout};
|
||||
|
||||
const m::room room
|
||||
{
|
||||
room_id
|
||||
};
|
||||
|
||||
for(auto it(std::begin(polling)); it != std::end(polling);)
|
||||
{
|
||||
const auto &data{*it};
|
||||
if(!room.membership(data.user_id))
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(update_sync(data, event, room))
|
||||
{
|
||||
pollout.erase(data.it);
|
||||
polling.erase(it++);
|
||||
}
|
||||
else ++it;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
update_sync_room(client &client,
|
||||
const m::room &room,
|
||||
const string_view &since,
|
||||
const m::event &event)
|
||||
{
|
||||
std::vector<std::string> state;
|
||||
if(defined(json::val<m::name::state_key>(event)))
|
||||
state.emplace_back(json::string(event));
|
||||
|
||||
const auto state_serial
|
||||
{
|
||||
json::string(state.data(), state.data() + state.size())
|
||||
};
|
||||
|
||||
std::vector<std::string> timeline;
|
||||
if(!defined(json::val<m::name::state_key>(event)))
|
||||
timeline.emplace_back(json::string(event));
|
||||
|
||||
const auto timeline_serial
|
||||
{
|
||||
json::string(timeline.data(), timeline.data() + timeline.size())
|
||||
};
|
||||
|
||||
const json::members body
|
||||
{
|
||||
{ "state", json::member { "events", state_serial } },
|
||||
{ "timeline", json::member { "events", timeline_serial } }
|
||||
};
|
||||
|
||||
return json::string(body);
|
||||
}
|
||||
|
||||
std::string
|
||||
update_sync_rooms(client &client,
|
||||
const m::user::id &user_id,
|
||||
const m::room &room,
|
||||
const string_view &since,
|
||||
const m::event &event)
|
||||
{
|
||||
|
||||
std::vector<std::string> r[3];
|
||||
std::vector<json::member> m[3];
|
||||
r[0].emplace_back(update_sync_room(client, room, since, event));
|
||||
m[0].emplace_back(room.room_id, r[0].back());
|
||||
|
||||
const std::string join{json::string(m[0].data(), m[0].data() + m[0].size())};
|
||||
const std::string leave{json::string(m[1].data(), m[1].data() + m[1].size())};
|
||||
const std::string invite{json::string(m[2].data(), m[2].data() + m[2].size())};
|
||||
return json::string(json::members
|
||||
{
|
||||
{ "join", join },
|
||||
{ "leave", leave },
|
||||
{ "invite", invite },
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
handle_event(const m::event &event,
|
||||
const syncpoll &request)
|
||||
update_sync(const syncpoll &data,
|
||||
const m::event &event,
|
||||
const m::room &room)
|
||||
try
|
||||
{
|
||||
const life_guard<const client> client
|
||||
const life_guard<client> client
|
||||
{
|
||||
request.client
|
||||
data.client
|
||||
};
|
||||
|
||||
// if(request.timeout < now<steady_point>())
|
||||
// return false;
|
||||
const auto rooms
|
||||
{
|
||||
update_sync_rooms(*client, data.user_id, room, data.since, event)
|
||||
};
|
||||
|
||||
const auto presence
|
||||
{
|
||||
"{}"
|
||||
};
|
||||
|
||||
const string_view next_batch
|
||||
{
|
||||
at<m::name::event_id>(event)
|
||||
};
|
||||
|
||||
resource::response
|
||||
{
|
||||
*client, json::members
|
||||
{
|
||||
{ "next_batch", next_batch },
|
||||
{ "rooms", rooms },
|
||||
{ "presence", presence }
|
||||
}
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
catch(const std::bad_weak_ptr &e)
|
||||
{
|
||||
log::error("%s", e.what());
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
initial_sync_room(client &client,
|
||||
const resource::request &request,
|
||||
const m::room &room,
|
||||
const bool &full_state)
|
||||
{
|
||||
std::vector<std::string> state;
|
||||
{
|
||||
const m::event::query<m::event::where::equal> state_query
|
||||
{
|
||||
{ "room_id", room.room_id },
|
||||
{ "state_key", "" },
|
||||
};
|
||||
|
||||
m::events::for_each(state_query, [&state](const auto &event)
|
||||
{
|
||||
state.emplace_back(json::string(event));
|
||||
});
|
||||
}
|
||||
|
||||
const auto state_serial
|
||||
{
|
||||
json::string(state.data(), state.data() + state.size())
|
||||
};
|
||||
|
||||
std::vector<std::string> timeline;
|
||||
{
|
||||
const m::event::query<m::event::where::equal> timeline_query
|
||||
{
|
||||
{ "room_id", room.room_id },
|
||||
};
|
||||
|
||||
m::events::query(timeline_query, [&timeline](const auto &event)
|
||||
{
|
||||
if(timeline.size() > 10)
|
||||
return true;
|
||||
|
||||
if(!defined(json::val<m::name::state_key>(event)))
|
||||
timeline.emplace_back(json::string(event));
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
const auto timeline_serial
|
||||
{
|
||||
json::string(timeline.data(), timeline.data() + timeline.size())
|
||||
};
|
||||
|
||||
const json::members body
|
||||
{
|
||||
{ "state", json::member { "events", state_serial } },
|
||||
{ "timeline", json::member { "events", timeline_serial } }
|
||||
};
|
||||
|
||||
return json::string(body);
|
||||
}
|
||||
|
||||
std::string
|
||||
initial_sync_rooms(client &client,
|
||||
const resource::request &request,
|
||||
const string_view &filter_id,
|
||||
const bool &full_state)
|
||||
{
|
||||
const m::event::query<m::event::where::equal> query
|
||||
{
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", request.user_id },
|
||||
};
|
||||
|
||||
std::array<std::vector<std::string>, 3> r;
|
||||
std::array<std::vector<json::member>, 3> m;
|
||||
m::events::for_each(query, [&r, &m, &client, &request, &full_state](const auto &event)
|
||||
{
|
||||
const auto &content{json::val<m::name::content>(event)};
|
||||
const auto &membership{unquote(content["membership"])};
|
||||
const m::room::id &room_id{json::val<m::name::room_id>(event)};
|
||||
const auto i
|
||||
{
|
||||
membership == "join"? 0:
|
||||
membership == "leave"? 1:
|
||||
membership == "invite"? 2:
|
||||
-1
|
||||
};
|
||||
|
||||
r.at(i).emplace_back(initial_sync_room(client, request, room_id, full_state));
|
||||
m.at(i).emplace_back(room_id, r.at(i).back());
|
||||
});
|
||||
|
||||
const std::string join{json::string(m[0].data(), m[0].data() + m[0].size())};
|
||||
const std::string leave{json::string(m[1].data(), m[1].data() + m[1].size())};
|
||||
const std::string invite{json::string(m[2].data(), m[2].data() + m[2].size())};
|
||||
return json::string(json::members
|
||||
{
|
||||
{ "join", join },
|
||||
{ "leave", leave },
|
||||
{ "invite", invite },
|
||||
});
|
||||
}
|
||||
|
||||
resource::response
|
||||
initial_sync(client &client,
|
||||
const resource::request &request,
|
||||
const string_view &filter_id,
|
||||
const bool &full_state,
|
||||
const string_view &set_presence)
|
||||
{
|
||||
char buf[4096] {0};
|
||||
json::mutable_object body{buf};
|
||||
body.insert({"foo","bar"});
|
||||
body.insert({"baz","bam"});
|
||||
|
||||
std::cout << buf << std::endl;
|
||||
|
||||
const std::string rooms
|
||||
{
|
||||
initial_sync_rooms(client, request, filter_id, full_state)
|
||||
};
|
||||
|
||||
const auto presence
|
||||
{
|
||||
"{}"
|
||||
};
|
||||
|
||||
const string_view next_batch
|
||||
{
|
||||
m::event::head
|
||||
};
|
||||
|
||||
const json::members content
|
||||
{
|
||||
{ "event_id", next_batch }
|
||||
};
|
||||
|
||||
m::user::sessions.send(
|
||||
{
|
||||
{ "type", "ircd.tape.head" },
|
||||
{ "state_key", request.query.at("access_token") },
|
||||
{ "sender", request.user_id },
|
||||
{ "content", content },
|
||||
});
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, json::members
|
||||
{
|
||||
{ "next_batch", next_batch },
|
||||
{ "rooms", rooms },
|
||||
{ "presence", presence }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -130,8 +130,6 @@ post_filter(client &client, const resource::request::object<const m::filter> &re
|
|||
json::val<m::name::state>(room)
|
||||
};
|
||||
|
||||
std::cout << json::get<m::name::limit>(state) << std::endl;
|
||||
|
||||
const auto &presence
|
||||
{
|
||||
// (5.2) The presence updates to include.
|
||||
|
|
27
modules/key/query.cc
Normal file
27
modules/key/query.cc
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using namespace ircd;
|
||||
|
||||
mapi::header IRCD_MODULE
|
||||
{
|
||||
"Hosts the key server resource"
|
||||
};
|
130
modules/key/server.cc
Normal file
130
modules/key/server.cc
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using namespace ircd;
|
||||
|
||||
mapi::header IRCD_MODULE
|
||||
{
|
||||
"federation 2.2.1.1: Publishing Keys"
|
||||
};
|
||||
|
||||
struct server
|
||||
:resource
|
||||
{
|
||||
static constexpr const auto base_url
|
||||
{
|
||||
"_matrix/key/v2/server/"
|
||||
};
|
||||
|
||||
using resource::resource;
|
||||
}
|
||||
server_resource
|
||||
{
|
||||
server::base_url, resource::opts
|
||||
{
|
||||
resource::DIRECTORY,
|
||||
"federation 2.2.1.1: Publishing Keys"
|
||||
}
|
||||
};
|
||||
|
||||
resource::response
|
||||
handle_get(client &client,
|
||||
const resource::request &request)
|
||||
{
|
||||
char key_id_buf[256];
|
||||
const auto key_id
|
||||
{
|
||||
urldecode(split(request.head.path, server_resource.base_url).second, key_id_buf)
|
||||
};
|
||||
|
||||
std::string my_key;
|
||||
m::keys::get(my_host(), [&my_key](const auto &key)
|
||||
{
|
||||
my_key = json::string(key);
|
||||
});
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, json::object{my_key}
|
||||
};
|
||||
}
|
||||
|
||||
resource::method method_get
|
||||
{
|
||||
server_resource, "GET", handle_get
|
||||
};
|
||||
|
||||
// __attribute__((constructor))
|
||||
static
|
||||
void foop()
|
||||
{
|
||||
using namespace ircd;
|
||||
|
||||
uint8_t seed_buf[ed25519::SEED_SZ + 10];
|
||||
const auto seed
|
||||
{
|
||||
b64decode(seed_buf, "YJDBA9Xnr2sVqXD9Vj7XVUnmFZcZrlw8Md7kMW+3XA1")
|
||||
};
|
||||
|
||||
ed25519::pk pk;
|
||||
ed25519::sk sk{&pk, seed};
|
||||
|
||||
const auto SERVER_NAME {"domain"};
|
||||
const auto KEY_ID {"ed25519:1"};
|
||||
|
||||
const auto test{[&](const std::string &object)
|
||||
{
|
||||
const auto sig
|
||||
{
|
||||
sk.sign(const_raw_buffer{object})
|
||||
};
|
||||
|
||||
char sigb64_buf[128];
|
||||
const auto sigb64
|
||||
{
|
||||
b64encode_unpadded(sigb64_buf, sig)
|
||||
};
|
||||
|
||||
std::cout << "sig: " << sigb64 << std::endl;
|
||||
|
||||
ed25519::sig unsig; const auto unsigb64
|
||||
{
|
||||
b64decode(unsig, sigb64)
|
||||
};
|
||||
|
||||
return pk.verify(const_raw_buffer{object}, unsig);
|
||||
}};
|
||||
|
||||
std::cout <<
|
||||
test(std::string{json::object
|
||||
{
|
||||
"{}"
|
||||
}})
|
||||
<< std::endl;
|
||||
|
||||
std::cout <<
|
||||
test(json::string(json::members
|
||||
{
|
||||
{ "one", 1 },
|
||||
{ "two", "Two" }
|
||||
}))
|
||||
<< std::endl;
|
||||
}
|
|
@ -48,7 +48,12 @@ init_0()
|
|||
resource::response
|
||||
get_root(client &client, const resource::request &request)
|
||||
{
|
||||
auto it(files.find(request.head.path));
|
||||
const auto &path
|
||||
{
|
||||
request.head.path?: "index.html"
|
||||
};
|
||||
|
||||
auto it(files.find(path));
|
||||
if(it == end(files))
|
||||
throw http::error{http::NOT_FOUND};
|
||||
|
||||
|
|
Loading…
Reference in a new issue