0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-09-26 02:18:53 +02:00

ircd:Ⓜ️ Split m.cc; distribute inits; modules: Checkpoint matrix.

This commit is contained in:
Jason Volk 2017-12-12 13:33:14 -07:00
parent d8bddbbff0
commit 80931e4e86
19 changed files with 2404 additions and 1861 deletions

View file

@ -1444,29 +1444,6 @@ try
break;
}
case hash("trace"):
{
const auto args
{
tokens_after(line, ' ', 0)
};
m::event::id event_id
{
token(args, ' ', 0)
};
m::vm::trace(event_id, []
(const auto &event, auto &next)
{
std::cout << m::pretty_oneline(event) << std::endl;
std::cout << m::pretty(m::event::prev{event}) << std::endl;
return true;
});
break;
}
case hash("directory"):
{
if(!moi)

View file

@ -84,6 +84,8 @@ struct ircd::m::keys
json::property<name::verify_keys, json::object>
>
{
struct init;
using key_closure = std::function<void (const string_view &)>; // remember to unquote()!!!
using keys_closure = std::function<void (const keys &)>;
@ -102,4 +104,18 @@ struct ircd::m::keys
using super_type::operator=;
};
struct ircd::m::keys::init
{
json::object conf;
void certificate();
void signing();
public:
void bootstrap();
init(const json::object &conf);
~init() noexcept;
};
#pragma GCC diagnostic pop

View file

@ -25,13 +25,24 @@
#pragma once
#define HAVE_IRCD_M_H
#include <ircd/server.h>
/// Matrix Protocol System
namespace ircd::m
{
extern struct log::log log;
struct init;
namespace dbs
{
struct init;
}
extern struct user me;
extern struct room my_room;
extern struct room control;
extern struct log::log log;
IRCD_OVERLOAD(generate)
}
namespace ircd::m::self
@ -65,22 +76,24 @@ namespace ircd
#include "keys.h"
#include "txn.h"
namespace ircd::m::dbs
struct ircd::m::dbs::init
{
struct init
{
init();
~init() noexcept;
};
}
init();
~init() noexcept;
};
namespace ircd::m
struct ircd::m::init
{
struct init
{
dbs::init dbs;
json::object conf;
init();
~init() noexcept;
};
}
void bootstrap();
void listeners();
void modules();
dbs::init _dbs;
keys::init _keys;
public:
init();
~init() noexcept;
};

View file

@ -35,44 +35,68 @@ namespace ircd::m
struct room;
bool my(const room &);
bool exists(const id::room &);
event::id::buf send(const id::room &, const m::id::user &sender, const string_view &type, const json::iov &content);
event::id::buf send(const id::room &, const m::id::user &sender, const string_view &type, const json::members &content);
event::id::buf send(const id::room &, const m::id::user &sender, const string_view &type, const string_view &state_key, const json::iov &content);
event::id::buf send(const id::room &, const m::id::user &sender, const string_view &type, const string_view &state_key, const json::members &content);
event::id::buf send(room, const m::id::user &sender, const string_view &type, const json::iov &content);
event::id::buf send(room, const m::id::user &sender, const string_view &type, const json::members &content);
event::id::buf send(room, const m::id::user &sender, const string_view &type, const string_view &state_key, const json::iov &content);
event::id::buf send(room, const m::id::user &sender, const string_view &type, const string_view &state_key, const json::members &content);
event::id::buf message(const id::room &, const m::id::user &sender, const json::members &content);
event::id::buf message(const id::room &, const m::id::user &sender, const string_view &body, const string_view &msgtype = "m.text");
event::id::buf membership(const id::room &, const m::id::user &, const string_view &membership);
event::id::buf leave(const id::room &, const m::id::user &);
event::id::buf join(const id::room &, const m::id::user &);
event::id::buf message(room, const m::id::user &sender, const json::members &content);
event::id::buf message(room, const m::id::user &sender, const string_view &body, const string_view &msgtype = "m.text");
event::id::buf membership(room, const m::id::user &, const string_view &membership);
event::id::buf leave(room, const m::id::user &);
event::id::buf join(room, const m::id::user &);
room create(const id::room &, const id::user &creator, const id::room &parent, const string_view &type);
room create(const id::room &, const id::user &creator, const string_view &type = {});
}
/// Interface to a room.
///
/// This is a lightweight object which uses a room_id and an optional event_id
/// to provide an interface to a matrix room. This object itself isn't the
/// actual room data since that takes the form of events in the database;
/// this is just a handle with aforementioned string_view's used by its member
/// functions.
///
/// This object allows the programmer to represent the room either at its
/// present state, or if an event_id is given, at the point of that event.
///
/// Many convenience functions are provided outside of this class to
/// accomplish general tasks using rooms in one statement. Additionally,
/// several sub-classes provide functionality even more specific than this
/// interface too. If a subclass is provided, for example: `struct members`,
/// such an interface may employ optimized tactics for its specific task.
///
struct ircd::m::room
{
struct alias;
struct state;
struct members;
struct fetch;
struct branch;
using id = m::id::room;
using alias = m::id::room_alias;
id room_id;
event::id event_id;
operator const id &() const { return room_id; }
// observer
bool test(const string_view &type, const event::closure_bool &view) const;
void for_each(const string_view &type, const event::closure &view) const;
bool test(const string_view &type, const event::closure_bool &view) const;
bool has(const string_view &type, const string_view &state_key) const;
bool has(const string_view &type) const;
bool get(const string_view &type, const string_view &state_key, const event::closure &) const;
bool get(const string_view &type, const event::closure &view) const;
bool get(const event::closure &view) const;
bool prev(const string_view &type, const string_view &state_key, const event::closure &) const;
bool prev(const string_view &type, const event::closure &view) const;
bool prev(const event::closure &view) const;
// observer misc
bool membership(const m::id::user &, const string_view &membership = "join") const;
@ -82,15 +106,53 @@ struct ircd::m::room
// modify
event::id::buf send(json::iov &event);
event::id::buf send(json::iov &event, const json::iov &content);
// modify misc
event::id::buf message(json::iov &event, const json::iov &content);
event::id::buf membership(json::iov &event, const json::iov &content);
event::id::buf create(json::iov &event, json::iov &content);
room(const id::alias &alias);
room(const id &room_id)
room(const alias &, const event::id &event_id = {});
room(const id &room_id, const event::id &event_id = {})
:room_id{room_id}
,event_id{event_id}
{}
};
/// Interface to the members of a room.
///
/// This interface focuses specifically on room membership and its routines
/// are optimized for this area of room functionality.
///
struct ircd::m::room::members
{
struct origins;
m::room room;
bool until(const string_view &membership, const event::closure_bool &view) const;
bool until(const event::closure_bool &view) const;
members(m::room room)
:room{room}
{}
};
/// Interface to the origins of members of a room
///
/// This interface focuses even more specifically on the servers (origins) for
/// the members of a room. As multiple users from the same origin may be
/// members of a room -- all in different membership states etc -- this
/// interface distills that fact away from what would otherwise burden users
/// of the more general room::members interface.
///
struct ircd::m::room::members::origins
{
using closure = std::function<void (const string_view &)>;
using closure_bool = std::function<bool (const string_view &)>;
m::room room;
bool until(const closure_bool &view) const;
origins(m::room room)
:room{room}
{}
};
@ -160,18 +222,4 @@ struct ircd::m::room::state
friend std::string pretty_oneline(const room::state &);
};
struct ircd::m::room::branch
{
event::id::buf event_id;
unique_buffer<mutable_buffer> buf;
json::object pdu;
branch() = default;
branch(const event::id &event_id)
:event_id{event_id}
,buf{64_KiB}
,pdu{}
{}
};
#pragma GCC diagnostic pop

View file

@ -80,6 +80,8 @@ namespace ircd::m::vm
bool test(const query<> &, const closure_bool &);
bool test(const query<> &);
bool until(const query<> &, const closure_bool &);
bool until(const query<> &);
void for_each(const query<> &, const closure &);
void for_each(const closure &);
size_t count(const query<> &, const closure_bool &);

View file

@ -225,5 +225,4 @@ namespace ircd
#include "net/net.h"
#include "server.h"
#include "client.h"
#include "m/m.h"
#include "resource.h"

View file

@ -75,8 +75,11 @@ libircd_la_SOURCES = \
lexical.cc \
locale.cc \
logger.cc \
m/event.cc \
m/id.cc \
m/io.cc \
m/keys.cc \
m/room.cc \
m/vm.cc \
m/m.cc \
mods.cc \

View file

@ -26,8 +26,6 @@
*/
#include <ircd/asio.h>
#include <ircd/m/m.h>
#include <ircd/resource.h>
namespace ircd {

371
ircd/m/event.cc Normal file
View file

@ -0,0 +1,371 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 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.
*/
#include <ircd/m/m.h>
ircd::database *
ircd::m::event::events
{};
ircd::m::event::event(const id &id,
const mutable_buffer &buf)
{
fetch tab
{
id, buf
};
new (this) event{tab};
}
ircd::m::event::event(fetch &tab)
{
io::acquire(tab);
if(bool(tab.error))
std::rethrow_exception(tab.error);
new (this) super_type{tab.pdu};
}
ircd::m::event::temporality
ircd::m::temporality(const event &event,
const int64_t &rel)
{
const auto &depth
{
json::get<"depth"_>(event)
};
return depth > rel? event::temporality::FUTURE:
depth == rel? event::temporality::PRESENT:
event::temporality::PAST;
}
ircd::m::event::lineage
ircd::m::lineage(const event &event)
{
const json::array prev[]
{
json::get<"prev_events"_>(event),
json::get<"auth_events"_>(event),
json::get<"prev_state"_>(event),
};
const auto count{std::accumulate(begin(prev), end(prev), size_t(0), []
(auto ret, const auto &array)
{
return ret += array.count();
})};
return count > 1? event::lineage::MERGE:
count == 1? event::lineage::FORWARD:
event::lineage::ROOT;
}
ircd::string_view
ircd::m::reflect(const event::lineage &lineage)
{
switch(lineage)
{
case event::lineage::MERGE: return "MERGE";
case event::lineage::FORWARD: return "FORWARD";
case event::lineage::ROOT: return "ROOT";
}
return "?????";
}
ircd::string_view
ircd::m::reflect(const event::temporality &temporality)
{
switch(temporality)
{
case event::temporality::FUTURE: return "FUTURE";
case event::temporality::PRESENT: return "PRESENT";
case event::temporality::PAST: return "PAST";
}
return "?????";
}
size_t
ircd::m::degree(const event &event)
{
return degree(event::prev{event});
}
size_t
ircd::m::degree(const event::prev &prev)
{
size_t ret{0};
json::for_each(prev, [&ret]
(const auto &, const json::array &prevs)
{
ret += prevs.count();
});
return ret;
}
size_t
ircd::m::count(const event::prev &prev)
{
size_t ret{0};
m::for_each(prev, [&ret](const event::id &event_id)
{
++ret;
});
return ret;
}
void
ircd::m::for_each(const event::prev &prev,
const std::function<void (const event::id &)> &closure)
{
json::for_each(prev, [&closure]
(const auto &key, const json::array &prevs)
{
for(const json::array &prev : prevs)
{
const event::id &id{unquote(prev[0])};
closure(id);
}
});
}
std::string
ircd::m::pretty(const event::prev &prev)
{
std::string ret;
std::stringstream s;
pubsetbuf(s, ret, 2048);
const auto out{[&s]
(const string_view &key, auto&& val)
{
if(json::defined(val))
s << key << ": " << val << std::endl;
}};
const auto &auth_events{json::get<"auth_events"_>(prev)};
for(const json::array auth_event : auth_events)
out("auth_event", unquote(auth_event[0]));
const auto &prev_states{json::get<"prev_state"_>(prev)};
for(const json::array prev_state : prev_states)
out("prev_state", unquote(prev_state[0]));
const auto &prev_events{json::get<"prev_events"_>(prev)};
for(const json::array prev_event : prev_events)
out("prev_event", unquote(prev_event[0]));
resizebuf(s, ret);
return ret;
}
std::string
ircd::m::pretty_oneline(const event::prev &prev)
{
std::string ret;
std::stringstream s;
pubsetbuf(s, ret, 1024);
const auto &auth_events{json::get<"auth_events"_>(prev)};
s << "A[ ";
for(const json::array auth_event : auth_events)
s << unquote(auth_event[0]) << " ";
s << "] ";
const auto &prev_states{json::get<"prev_state"_>(prev)};
s << "S[ ";
for(const json::array prev_state : prev_states)
s << unquote(prev_state[0]) << " ";
s << "] ";
const auto &prev_events{json::get<"prev_events"_>(prev)};
s << "E[ ";
for(const json::array prev_event : prev_events)
s << unquote(prev_event[0]) << " ";
s << "] ";
resizebuf(s, ret);
return ret;
}
std::string
ircd::m::pretty(const event &event)
{
std::string ret;
std::stringstream s;
pubsetbuf(s, ret, 2048);
const auto out{[&s]
(const string_view &key, auto&& val)
{
if(json::defined(val))
s << std::setw(16) << std::right << key << ": " << val << std::endl;
}};
const string_view top_keys[]
{
"origin",
"event_id",
"room_id",
"sender",
"type",
"depth",
"state_key",
"membership",
};
json::for_each(event, top_keys, out);
const auto &hashes{json::get<"hashes"_>(event)};
for(const auto &hash : hashes)
{
s << std::setw(16) << std::right << "[hash]" << ": "
<< hash.first
//<< " "
//<< hash.second
<< std::endl;
}
const auto &signatures{json::get<"signatures"_>(event)};
for(const auto &signature : signatures)
{
s << std::setw(16) << std::right << "[signature]" << ": "
<< signature.first << " ";
for(const auto &key : json::object{signature.second})
s << key.first << " ";
s << std::endl;
}
const json::object &contents{json::get<"content"_>(event)};
if(!contents.empty())
{
s << std::setw(16) << std::right << "[content]" << ": ";
for(const auto &content : contents)
s << content.first << ", ";
s << std::endl;
}
const auto &auth_events{json::get<"auth_events"_>(event)};
for(const json::array auth_event : auth_events)
out("[auth_event]", unquote(auth_event[0]));
const auto &prev_states{json::get<"prev_state"_>(event)};
for(const json::array prev_state : prev_states)
out("[prev_state]", unquote(prev_state[0]));
const auto &prev_events{json::get<"prev_events"_>(event)};
for(const json::array prev_event : prev_events)
out("[prev_event]", unquote(prev_event[0]));
resizebuf(s, ret);
return ret;
}
std::string
ircd::m::pretty_oneline(const event &event)
{
std::string ret;
std::stringstream s;
pubsetbuf(s, ret, 1024);
const auto out{[&s]
(const string_view &key, auto&& val)
{
if(defined(val))
s << val << " ";
else
s << "* ";
}};
const string_view top_keys[]
{
"origin",
"event_id",
"room_id",
"sender",
"depth",
};
s << ':';
json::for_each(event, top_keys, out);
const auto &auth_events{json::get<"auth_events"_>(event)};
s << "pa:" << auth_events.count() << " ";
const auto &prev_states{json::get<"prev_state"_>(event)};
s << "ps:" << prev_states.count() << " ";
const auto &prev_events{json::get<"prev_events"_>(event)};
s << "pe:" << prev_events.count() << " ";
const auto &hashes{json::get<"hashes"_>(event)};
s << "[ ";
for(const auto &hash : hashes)
s << hash.first << " ";
s << "] ";
const auto &signatures{json::get<"signatures"_>(event)};
s << "[ ";
for(const auto &signature : signatures)
{
s << signature.first << "[ ";
for(const auto &key : json::object{signature.second})
s << key.first << " ";
s << "] ";
}
s << "] ";
out("type", json::get<"type"_>(event));
const auto &state_key
{
json::get<"state_key"_>(event)
};
if(defined(state_key) && empty(state_key))
s << "\"\"" << " ";
else if(defined(state_key))
s << state_key << " ";
else
s << "*" << " ";
out("membership", json::get<"membership"_>(event));
const json::object &contents{json::get<"content"_>(event)};
if(!contents.empty())
{
s << "+" << string_view{contents}.size() << " bytes :";
for(const auto &content : contents)
s << content.first << " ";
}
resizebuf(s, ret);
return ret;
}

View file

@ -549,10 +549,6 @@ ircd::m::io::session::operator()(parse::buffer &pb,
};
}
//
// response
//
ircd::m::io::response::response(server &server,
parse::buffer &pb)
{
@ -586,34 +582,6 @@ ircd::m::io::response::response(server &server,
throw m::error(status, object);
}
//
// request
//
namespace ircd::m::name
{
// constexpr const char *const content {"content"};
constexpr const char *const destination {"destination"};
constexpr const char *const method {"method"};
// constexpr const char *const origin {"origin"};
constexpr const char *const uri {"uri"};
}
struct ircd::m::io::request::authorization
:json::tuple
<
json::property<name::content, string_view>,
json::property<name::destination, string_view>,
json::property<name::method, string_view>,
json::property<name::origin, string_view>,
json::property<name::uri, string_view>
>
{
string_view generate(const mutable_buffer &out);
using super_type::tuple;
};
void
ircd::m::io::request::operator()(const vector_view<const http::header> &addl_headers)
const
@ -659,6 +627,30 @@ const
};
}
namespace ircd::m::name
{
// constexpr const char *const content {"content"};
constexpr const char *const destination {"destination"};
constexpr const char *const method {"method"};
// constexpr const char *const origin {"origin"};
constexpr const char *const uri {"uri"};
}
struct ircd::m::io::request::authorization
:json::tuple
<
json::property<name::content, string_view>,
json::property<name::destination, string_view>,
json::property<name::method, string_view>,
json::property<name::origin, string_view>,
json::property<name::uri, string_view>
>
{
string_view generate(const mutable_buffer &out);
using super_type::tuple;
};
ircd::string_view
ircd::m::io::request::generate_authorization(const mutable_buffer &out)
const
@ -731,16 +723,13 @@ ircd::m::io::verify_x_matrix_authorization(const string_view &x_matrix,
for(const auto &token : tokens)
{
const auto &key_value
const auto &kv{split(token, '=')};
const auto &val{unquote(kv.second)};
switch(hash(kv.first))
{
split(token, '=')
};
switch(hash(key_value.first))
{
case hash("origin"): origin = unquote(key_value.second); break;
case hash("key"): key = unquote(key_value.second); break;
case hash("sig"): sig = unquote(key_value.second); break;
case hash("origin"): origin = val; break;
case hash("key"): key = val; break;
case hash("sig"): sig = val; break;
}
}

546
ircd/m/keys.cc Normal file
View file

@ -0,0 +1,546 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 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.
*/
#include <ircd/m/m.h>
const ircd::m::room::id::buf
keys_room_id
{
"keys", ircd::my_host()
};
/// The keys room is where the public key data for each server is stored as
/// state indexed by the server name.
ircd::m::room
ircd::m::keys::room
{
keys_room_id
};
ircd::ed25519::sk
ircd::m::self::secret_key
{};
ircd::ed25519::pk
ircd::m::self::public_key
{};
std::string
ircd::m::self::public_key_b64
{};
std::string
ircd::m::self::public_key_id
{};
std::string
ircd::m::self::tls_cert_der
{};
std::string
ircd::m::self::tls_cert_der_sha256_b64
{};
//
// init
//
ircd::m::keys::init::init(const json::object &conf)
:conf{conf}
{
certificate();
signing();
}
ircd::m::keys::init::~init()
noexcept
{
}
void
ircd::m::keys::init::certificate()
{
const std::string cert_file
{
unquote(conf.at("tls_certificate_path"))
};
if(!fs::exists(cert_file))
throw fs::error("Failed to find SSL certificate @ `%s'", cert_file);
const auto cert_pem
{
fs::read(cert_file)
};
const unique_buffer<mutable_raw_buffer> der_buf
{
8_KiB
};
const auto cert_der
{
openssl::cert2d(der_buf, cert_pem)
};
const fixed_buffer<const_raw_buffer, crh::sha256::digest_size> hash
{
sha256{cert_der}
};
self::tls_cert_der_sha256_b64 =
{
b64encode_unpadded(hash)
};
log.info("Certificate `%s' (PEM: %zu bytes; DER: %zu bytes) sha256b64: %s",
cert_file,
cert_pem.size(),
ircd::size(cert_der),
self::tls_cert_der_sha256_b64);
}
void
ircd::m::keys::init::signing()
{
const std::string sk_file
{
unquote(conf.get("signing_key_path", "construct.sk"))
};
if(fs::exists(sk_file))
log.info("Using ed25519 secret key @ `%s'", sk_file);
else
log.notice("Creating new ed25519 secret key @ `%s'", sk_file);
self::secret_key = ed25519::sk
{
sk_file, &self::public_key
};
self::public_key_b64 = b64encode_unpadded(self::public_key);
const fixed_buffer<const_raw_buffer, sha256::digest_size> hash
{
sha256{const_raw_buffer{self::public_key}}
};
const auto public_key_hash_b64
{
b64encode_unpadded(hash)
};
self::public_key_id = fmt::snstringf
{
BUFSIZE, "ed25519:%s", public_key_hash_b64
};
log.info("Current key is '%s' and the public key is: %s",
self::public_key_id,
self::public_key_b64);
}
void
ircd::m::keys::init::bootstrap()
{
create(keys::room, me.user_id);
send(keys::room, me.user_id, "m.room.name", "",
{
{ "name", "Key Room" }
});
const json::strung verify_keys
{
json::members
{{
string_view{self::public_key_id},
{
{ "key", self::public_key_b64 }
}
}}
};
keys my_key;
json::get<"verify_keys"_>(my_key) = verify_keys;
json::get<"server_name"_>(my_key) = my_host();
json::get<"old_verify_keys"_>(my_key) = "{}";
json::get<"valid_until_ts"_>(my_key) = ircd::time<milliseconds>() + duration_cast<milliseconds>(hours(2160)).count();
const json::members tlsfps
{
{ "sha256", self::tls_cert_der_sha256_b64 }
};
const json::value tlsfp[1]
{
{ tlsfps }
};
const json::strung tls_fingerprints{json::value
{
tlsfp, 1
}};
json::get<"tls_fingerprints"_>(my_key) = tls_fingerprints;
const auto presig
{
json::strung(my_key)
};
const ed25519::sig sig
{
self::secret_key.sign(const_raw_buffer{presig})
};
static char signature[256];
const json::strung signatures{json::members
{
{ my_host(), json::members
{
{ string_view{self::public_key_id}, b64encode_unpadded(signature, sig) }
}}
}};
json::get<"signatures"_>(my_key) = signatures;
keys::set(my_key);
}
//
// keys
//
void
ircd::m::keys::get(const string_view &server_name,
const string_view &key_id,
const key_closure &closure)
try
{
get(server_name, [&key_id, &closure](const keys &keys)
{
const json::object vks
{
at<"verify_keys"_>(keys)
};
const json::object vkk
{
vks.at(key_id)
};
// The key is not unquote() because some types of keys may be
// more complex than just a string one day; think: RLWE.
const string_view &key
{
vkk.at("key")
};
closure(key);
});
}
catch(const json::not_found &e)
{
throw m::NOT_FOUND
{
"Failed to find key '%s' for '%s': %s",
key_id,
server_name,
e.what()
};
}
void
ircd::m::keys::get(const string_view &server_name,
const keys_closure &closure)
{
assert(!server_name.empty());
if(get_local(server_name, closure))
return;
if(server_name == my_host())
throw m::NOT_FOUND
{
"keys for '%s' (that's myself) not found", server_name
};
log.debug("Keys for %s not cached; querying network...",
server_name);
char url[1024]; const auto url_len
{
fmt::snprintf(url, sizeof(url), "_matrix/key/v2/server/")
};
//TODO: XXX
const unique_buffer<mutable_buffer> buffer
{
8192
};
ircd::parse::buffer pb{mutable_buffer{buffer}};
m::request request{"GET", url, {}, {}};
m::session session{server_name};
const json::object response
{
session(pb, request)
};
const m::keys &keys
{
response
};
if(!verify(keys))
throw m::error
{
http::UNAUTHORIZED, "M_INVALID_SIGNATURE",
"Failed to verify keys for '%s'", server_name
};
log.debug("Verified keys from '%s'",
server_name);
set(keys);
closure(keys);
}
void
ircd::m::keys::get(const string_view &server_name,
const string_view &key_id,
const string_view &query_server,
const keys_closure &closure)
try
{
assert(!server_name.empty());
assert(!query_server.empty());
char key_id_buf[1024];
char server_name_buf[1024];
char url[1024]; const auto url_len
{
fmt::snprintf(url, sizeof(url), "_matrix/key/v2/query/%s/%s/",
url::encode(server_name, server_name_buf),
url::encode(key_id, key_id_buf))
};
//TODO: XXX
const unique_buffer<mutable_buffer> buffer
{
8192
};
// Make request and receive response synchronously.
// This ircd::ctx will block here fetching.
ircd::parse::buffer pb{mutable_buffer{buffer}};
m::request request{"GET", url, {}, {}};
m::session session{server_name};
const json::object response
{
session(pb, request)
};
const json::array &keys
{
response.at("server_keys")
};
log::debug("Fetched %zu candidate keys seeking '%s' for '%s' from '%s' (%s)",
keys.count(),
empty(key_id)? "*" : key_id,
server_name,
query_server,
string(net::remote(session.server)));
bool ret{false};
for(auto it(begin(keys)); it != end(keys); ++it)
{
const m::keys &keys{*it};
const auto &_server_name
{
at<"server_name"_>(keys)
};
if(!verify(keys))
throw m::error
{
http::UNAUTHORIZED, "M_INVALID_SIGNATURE",
"Failed to verify keys for '%s' from '%s'",
_server_name,
query_server
};
log.debug("Verified keys for '%s' from '%s'",
_server_name,
query_server);
set(keys);
const json::object vks{json::get<"verify_keys"_>(keys)};
if(_server_name == server_name)
{
closure(keys);
ret = true;
}
}
if(!ret)
throw m::NOT_FOUND
{
"Failed to get any keys for '%s' from '%s' (got %zu total keys otherwise)",
server_name,
query_server,
keys.count()
};
}
catch(const json::not_found &e)
{
throw m::NOT_FOUND
{
"Failed to find key '%s' for '%s' when querying '%s': %s",
key_id,
server_name,
query_server,
e.what()
};
}
bool
ircd::m::keys::get_local(const string_view &server_name,
const keys_closure &closure)
{
const m::vm::query<m::vm::where::equal> query
{
{ "room_id", keys::room.room_id },
{ "type", "ircd.key" },
{ "state_key", server_name },
};
const auto have
{
[&closure](const auto &event)
{
closure(json::get<"content"_>(event));
return true;
}
};
return m::vm::test(query, have);
}
void
ircd::m::keys::set(const keys &keys)
{
const auto &state_key
{
unquote(at<"server_name"_>(keys))
};
const m::user::id::buf sender
{
"ircd", unquote(at<"server_name"_>(keys))
};
const json::strung content
{
keys
};
json::iov event;
json::iov::push members[]
{
{ event, { "type", "ircd.key" }},
{ event, { "state_key", state_key }},
{ event, { "sender", sender }},
{ event, { "content", content }}
};
keys::room.send(event);
}
/// Verify this key data (with itself).
bool
ircd::m::keys::verify(const keys &keys)
noexcept try
{
const auto &valid_until_ts
{
at<"valid_until_ts"_>(keys)
};
if(valid_until_ts < ircd::time<milliseconds>())
throw ircd::error("Key was valid until %s", timestr(valid_until_ts));
const json::object &verify_keys
{
at<"verify_keys"_>(keys)
};
const string_view &key_id
{
begin(verify_keys)->first
};
const json::object &key
{
begin(verify_keys)->second
};
const ed25519::pk pk
{
[&key](auto &pk)
{
b64decode(pk, unquote(key.at("key")));
}
};
const json::object &signatures
{
at<"signatures"_>(keys)
};
const string_view &server_name
{
unquote(at<"server_name"_>(keys))
};
const json::object &server_signatures
{
signatures.at(server_name)
};
const ed25519::sig sig{[&server_signatures, &key_id](auto &sig)
{
b64decode(sig, unquote(server_signatures.at(key_id)));
}};
///TODO: XXX
m::keys copy{keys};
at<"signatures"_>(copy) = string_view{};
const json::strung preimage{copy};
return pk.verify(const_raw_buffer{preimage}, sig);
}
catch(const std::exception &e)
{
log.error("key verification for '%s' failed: %s",
json::get<"server_name"_>(keys, "<no server name>"_sv),
e.what());
return false;
}

File diff suppressed because it is too large Load diff

637
ircd/m/room.cc Normal file
View file

@ -0,0 +1,637 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 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.
*/
#include <ircd/m/m.h>
const ircd::m::room::id::buf
init_room_id
{
"init", ircd::my_host()
};
ircd::m::room
ircd::m::create(const id::room &room_id,
const id::user &creator,
const string_view &type)
{
return create(room_id, creator, init_room_id, type);
}
ircd::m::room
ircd::m::create(const id::room &room_id,
const id::user &creator,
const id::room &parent,
const string_view &type)
{
json::iov event;
json::iov content;
const json::iov::push push[]
{
{ event, { "sender", creator }},
{ content, { "creator", creator }},
};
const json::iov::add_if _parent
{
content, !parent.empty() && parent.local() != "init",
{
"parent", parent
}
};
const json::iov::add_if _type
{
content, !type.empty() && type != "room",
{
"type", type
}
};
json::iov::set _set[]
{
{ event, { "depth", 1 }},
{ event, { "type", "m.room.create" }},
{ event, { "state_key", "" }},
};
room room
{
room_id
};
room.send(event, content);
return room;
}
ircd::m::event::id::buf
ircd::m::join(m::room room,
const m::id::user &user_id)
{
return membership(room, user_id, "join");
}
ircd::m::event::id::buf
ircd::m::leave(m::room room,
const m::id::user &user_id)
{
return membership(room, user_id, "leave");
}
ircd::m::event::id::buf
ircd::m::membership(m::room room,
const m::id::user &user_id,
const string_view &membership)
{
json::iov event;
json::iov content;
json::iov::push push[]
{
{ event, { "type", "m.room.member" }},
{ event, { "sender", user_id }},
{ event, { "state_key", user_id }},
{ event, { "membership", membership }},
{ content, { "membership", membership }},
};
/*
if(this->membership(user_id, membership))
throw m::ALREADY_MEMBER
{
"Member '%s' is already '%s'.", string_view{user_id}, membership
};
*/
return room.send(event, content);
}
ircd::m::event::id::buf
ircd::m::message(m::room room,
const m::id::user &sender,
const string_view &body,
const string_view &msgtype)
{
return message(room, sender,
{
{ "body", body },
{ "msgtype", msgtype }
});
}
ircd::m::event::id::buf
ircd::m::message(m::room room,
const m::id::user &sender,
const json::members &contents)
{
return send(room, sender, "m.room.message", contents);
}
ircd::m::event::id::buf
ircd::m::send(m::room room,
const m::id::user &sender,
const string_view &type,
const string_view &state_key,
const json::members &contents)
{
size_t i(0);
json::iov content;
json::iov::push _content[contents.size()];
for(const auto &member : contents)
new (_content + i++) json::iov::push(content, member);
return send(room, sender, type, state_key, content);
}
ircd::m::event::id::buf
ircd::m::send(m::room room,
const m::id::user &sender,
const string_view &type,
const string_view &state_key,
const json::iov &content)
{
json::iov event;
const json::iov::push push[]
{
{ event, { "sender", sender }},
{ event, { "type", type }},
{ event, { "state_key", state_key }},
};
return room.send(event, content);
}
ircd::m::event::id::buf
ircd::m::send(m::room room,
const m::id::user &sender,
const string_view &type,
const json::members &contents)
{
size_t i(0);
json::iov content;
json::iov::push _content[contents.size()];
for(const auto &member : contents)
new (_content + i++) json::iov::push(content, member);
return send(room, sender, type, content);
}
ircd::m::event::id::buf
ircd::m::send(m::room room,
const m::id::user &sender,
const string_view &type,
const json::iov &content)
{
json::iov event;
const json::iov::push push[]
{
{ event, { "sender", sender }},
{ event, { "type", type }},
};
return room.send(event, content);
}
bool
ircd::m::exists(const id::room &room_id)
{
const vm::query<vm::where::equal> query
{
{ "room_id", room_id },
};
return m::vm::test(query);
}
bool
ircd::m::my(const room &room)
{
return my(room.room_id);
}
//
// room
//
ircd::m::room::room(const alias &alias,
const event::id &event_id)
:room_id{}
,event_id{event_id}
{
assert(0); //TODO: translate
}
ircd::m::event::id::buf
ircd::m::room::message(json::iov &event,
const json::iov &content)
{
const json::iov::set _type[]
{
{ event, { "type", "m.room.message" }},
};
const json::strung c //TODO: child iov
{
content
};
const json::iov::set_if _content[]
{
{ event, !content.empty(), { "content", string_view{c} }},
};
return send(event);
}
ircd::m::event::id::buf
ircd::m::room::send(json::iov &event,
const json::iov &content)
{
const json::strung c //TODO: child iov
{
content
};
const json::iov::set_if _content[]
{
{ event, !content.empty(), { "content", string_view{c} }},
};
return send(event);
}
ircd::m::event::id::buf
ircd::m::room::send(json::iov &event)
{
const json::iov::set room_id
{
event, { "room_id", this->room_id }
};
//std::cout << this->room_id << " at " << this->maxdepth() << std::endl;
// TODO: XXX
// commitment to room here @ exclusive acquisition of depth
const json::iov::defaults depth
{
event, { "depth", int64_t(this->maxdepth()) + 1 }
};
return m::vm::commit(event);
}
bool
ircd::m::room::membership(const m::id::user &user_id,
const string_view &membership)
const
{
const vm::query<vm::where::equal> member_event
{
{ "room_id", room_id },
{ "type", "m.room.member" },
{ "state_key", user_id },
};
if(!membership)
return m::vm::test(member_event);
const vm::query<vm::where::test> membership_test{[&membership]
(const auto &event)
{
const json::object &content
{
json::at<"content"_>(event)
};
const auto &existing_membership
{
unquote(content.at("membership"))
};
return membership == existing_membership;
}};
return m::vm::test(member_event && membership_test);
}
bool
ircd::m::room::prev(const event::closure &closure)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room_id },
};
vm::cursor cursor
{
"prev_event_id for event_id in room_id", &query
};
auto it(cursor.begin(room_id));
if(!it)
return false;
closure(*it);
return true;
}
bool
ircd::m::room::get(const event::closure &closure)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room_id },
};
vm::cursor cursor
{
"event_id in room_id", &query
};
auto it(cursor.begin(room_id));
if(!it)
return false;
closure(*it);
return true;
}
bool
ircd::m::room::get(const string_view &type,
const event::closure &closure)
const
{
return get(type, string_view{}, closure);
}
bool
ircd::m::room::get(const string_view &type,
const string_view &state_key,
const event::closure &closure)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room_id },
{ "type", type },
{ "state_key", state_key },
};
return m::vm::test(query, [&closure]
(const auto &event)
{
closure(event);
return true;
});
}
bool
ircd::m::room::has(const string_view &type)
const
{
return test(type, [](const auto &event)
{
return true;
});
}
bool
ircd::m::room::has(const string_view &type,
const string_view &state_key)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room_id },
{ "type", type },
{ "state_key", state_key },
};
return m::vm::test(query);
}
void
ircd::m::room::for_each(const string_view &type,
const event::closure &closure)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room_id },
{ "type", type },
};
return m::vm::for_each(query, closure);
}
bool
ircd::m::room::test(const string_view &type,
const event::closure_bool &closure)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room_id },
{ "type", type },
};
return m::vm::test(query, closure);
}
/// academic search
uint64_t
ircd::m::room::maxdepth()
const
{
event::id::buf buf;
return maxdepth(buf);
}
/// academic search
uint64_t
ircd::m::room::maxdepth(event::id::buf &buf)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room_id },
};
int64_t depth{0};
vm::for_each(query, [&buf, &depth]
(const auto &event)
{
if(json::get<"depth"_>(event) > depth)
{
depth = json::get<"depth"_>(event);
buf = json::get<"event_id"_>(event);
}
});
return depth;
}
//
// room::members
//
bool
ircd::m::room::members::until(const event::closure_bool &view)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room.room_id },
{ "type", "m.room.member" },
};
return m::vm::until(query, [&view]
(const auto &event)
{
return view(event);
});
}
bool
ircd::m::room::members::until(const string_view &membership,
const event::closure_bool &view)
const
{
const vm::query<vm::where::equal> query
{
{ "room_id", room.room_id },
{ "type", "m.room.member" },
{ "membership", membership },
};
return m::vm::until(query, [&view]
(const auto &event)
{
return view(event);
});
}
//
// room::members::origins
//
bool
ircd::m::room::members::origins::until(const closure_bool &view)
const
{
db::index index
{
*event::events, "origin in room_id"
};
auto it(index.begin(room.room_id));
for(; bool(it); ++it)
{
const string_view &key(it->first);
const string_view &origin(split(key, ":::").second); //TODO: XXX
if(!view(origin))
return false;
}
return true;
}
//
// room::state
//
ircd::m::room::state::state(const room::id &room_id,
const event::id &event_id,
const mutable_buffer &buf)
{
fetch tab
{
event_id, room_id, buf
};
new (this) state{tab};
}
ircd::m::room::state::state(fetch &tab)
{
io::acquire(tab);
if(bool(tab.error))
std::rethrow_exception(tab.error);
new (this) state{tab.pdus};
}
ircd::m::room::state::state(const json::array &pdus)
{
for(const json::object &pdu : pdus)
{
const m::event &event{pdu};
json::set(*this, at<"type"_>(event), event);
}
}
std::string
ircd::m::pretty(const room::state &state)
{
std::string ret;
std::stringstream s;
pubsetbuf(s, ret, 2048);
const auto out{[&s]
(const string_view &key, const auto &event)
{
if(!json::get<"event_id"_>(event))
return;
s << std::setw(28) << std::right << key
<< " : " << at<"event_id"_>(event)
<< " " << json::get<"sender"_>(event)
<< " " << json::get<"depth"_>(event)
<< " " << pretty_oneline(event::prev{event})
<< std::endl;
}};
json::for_each(state, out);
resizebuf(s, ret);
return ret;
}
std::string
ircd::m::pretty_oneline(const room::state &state)
{
std::string ret;
std::stringstream s;
pubsetbuf(s, ret, 1024);
const auto out{[&s]
(const string_view &key, const auto &event)
{
if(!json::get<"event_id"_>(event))
return;
s << key << " ";
}};
json::for_each(state, out);
resizebuf(s, ret);
return ret;
}

View file

@ -21,15 +21,6 @@
#include <ircd/m/m.h>
///////////////////////////////////////////////////////////////////////////////
//
// m/vm.h
//
namespace ircd::m::vm
{
}
decltype(ircd::m::vm::log)
ircd::m::vm::log
{
@ -46,77 +37,6 @@ ircd::m::vm::pipe
{
};
void
ircd::m::vm::trace(const id::event &event_id,
const tracer &closure)
{
std::multimap<uint64_t, room::branch> tree;
room::branch root{event_id};
event::fetch tab{root.event_id, root.buf};
io::acquire(tab);
root.pdu = tab.pdu;
const event event{root.pdu};
const auto &depth{at<"depth"_>(event)};
tree.emplace(depth, std::move(root));
for(int64_t d(depth); d > 0; --d)
{
const auto pit(tree.equal_range(d));
if(pit.first == pit.second)
if(tree.lower_bound(d - 1) == end(tree))
break;
for(auto it(pit.first); it != pit.second; ++it)
{
room::branch &b(it->second);
const m::event event{b.pdu};
const json::array &prev_events
{
json::get<"prev_events"_>(event)
};
const auto count
{
prev_events.count()
};
room::branch child[count];
event::fetch tab[count];
for(size_t i(0); i < count; ++i)
{
const json::array &prev_event{prev_events[i]};
child[i] = room::branch { unquote(prev_event[0]) };
tab[i] = { child[i].event_id, child[i].buf };
}
io::acquire({tab, count});
for(size_t i(0); i < count; ++i)
{
child[i].pdu = tab[i].pdu;
if(tab[i].error)
continue;
event::id::buf tmp;
const m::event event{child[i].pdu};
if(!closure(event, tmp))
return;
const auto &depth{at<"depth"_>(event)};
tree.emplace(depth, std::move(child[i]));
}
}
}
for(const auto &pit : tree)
{
const auto &depth(pit.first);
const auto &branch(pit.second);
std::cout << pretty_oneline(m::event{branch.pdu}) << std::endl;
}
}
void
ircd::m::vm::statefill(const room::id &room_id,
const event::id &event_id)
@ -488,16 +408,19 @@ ircd::m::vm::join(const room::id &room_id,
/// Insert a new event originating from this server.
///
/// Figure 1:
/// in .
/// ___:::::::__V <-- this function
/// | ||||||| //
/// in . <-- injection
/// ___:::::::__//
/// | ||||||| // <-- this function
/// | \\|// //|
/// | ||| // |
/// | ||// |
/// | ||| // | | acceleration
/// | |||// | |
/// | |||/ | V
/// | ||| |
/// | !!! |
/// | * | <----- IRCd core
/// | |//|||\\| |
/// |/|/|/|\|\|\| <---- release commitment propagation cone
/// | * | <----- nozzle
/// | ///|||\\\ |
/// |/|/|/|\|\|\| <---- propagation cone
/// _/|/|/|/|\|\|\|\_
/// out
///
/// This function takes an event object vector and adds our origin and event_id
@ -688,10 +611,6 @@ namespace ircd::m::vm
void write(const event &, db::iov &txn);
void write(eval &);
int _query(eval &, const query<> &, const closure_bool &);
int _query_where(eval &, const query<where::equal> &where, const closure_bool &closure);
int _query_where(eval &, const query<where::logical_and> &where, const closure_bool &closure);
bool evaluate(eval &, const vector_view<port> &, const size_t &i);
enum fault evaluate(eval &, const event &);
enum fault evaluate(eval &, const vector_view<const event> &);
@ -780,6 +699,13 @@ ircd::m::vm::eval::operator()(const vector_view<const event> &events)
return fault::ACCEPT;
}
namespace ircd::m
{
//TODO: XXX
void _index_special0(const event &event, db::iov &iov, const string_view &prev_event_id);
void _index_special1(const event &event, db::iov &iov, const string_view &prev_event_id);
}
enum ircd::m::vm::fault
ircd::m::vm::evaluate(eval &eval,
const vector_view<const event> &events)
@ -808,6 +734,43 @@ ircd::m::vm::evaluate(eval &eval,
{
if(p[i].w)
{
const auto &event{*p[i].event};
if(defined(json::get<"state_key"_>(event)))
{
event::id::buf prev_event_id;
const query<where::equal> q
{
{ "room_id", at<"room_id"_>(event) },
{ "type", at<"type"_>(event) },
{ "state_key", at<"state_key"_>(event) },
};
if(test(q, [&prev_event_id](const auto &event)
{
prev_event_id = at<"event_id"_>(event);
std::cout << "got prev S: " << prev_event_id << std::endl;
return true;
}))
_index_special0(event, eval.txn, prev_event_id);
}
{
event::id::buf prev_event_id;
const query<where::equal> q
{
{ "room_id", at<"room_id"_>(event) },
{ "depth", at<"depth"_>(event) - 1 },
};
if(test(q, [&prev_event_id](const auto &event)
{
prev_event_id = at<"event_id"_>(event);
std::cout << "got prev E: " << prev_event_id << std::endl;
return true;
}))
_index_special1(event, eval.txn, prev_event_id);
}
write(*p[i].event, eval.txn);
++eval.cs;
}
@ -987,95 +950,7 @@ ircd::m::vm::check_fault_throw(eval &eval)
}
}
*/
int
ircd::m::vm::test(eval &eval,
const query<> &where)
{
return test(eval, where, [](const auto &event)
{
return true;
});
}
int
ircd::m::vm::test(eval &eval,
const query<> &clause,
const closure_bool &closure)
{
return _query(eval, clause, [&closure](const auto &event)
{
return closure(event);
});
}
int
ircd::m::vm::_query(eval &eval,
const query<> &clause,
const closure_bool &closure)
{
switch(clause.type)
{
case where::equal:
{
const auto &c
{
dynamic_cast<const query<where::equal> &>(clause)
};
return _query_where(eval, c, closure);
}
case where::logical_and:
{
const auto &c
{
dynamic_cast<const query<where::logical_and> &>(clause)
};
return _query_where(eval, c, closure);
}
default:
return -1;
}
}
int
ircd::m::vm::_query_where(eval &eval,
const query<where::equal> &where,
const closure_bool &closure)
{
const int ret
{
eval.capstan.test(where)
};
log.debug("eval(%p): Query [%s]: %s -> %d",
&eval,
"where equal",
pretty_oneline(where.value),
ret);
return ret;
}
int
ircd::m::vm::_query_where(eval &eval,
const query<where::logical_and> &where,
const closure_bool &closure)
{
const auto &lhs{*where.a}, &rhs{*where.b};
const auto reclosure{[&lhs, &rhs, &closure]
(const auto &event)
{
if(!rhs(event))
return false;
return closure(event);
}};
return _query(eval, lhs, reclosure);
}
ircd::ctx::view<const ircd::m::event>
ircd::m::vm::inserted
@ -1336,12 +1211,12 @@ ircd::m::vm::write(const event &event,
append_indexes(event, txn);
}
//
// Query
//
namespace ircd::m::vm
{
int _query(eval &, const query<> &, const closure_bool &);
int _query_where(eval &, const query<where::equal> &where, const closure_bool &closure);
int _query_where(eval &, const query<where::logical_and> &where, const closure_bool &closure);
bool _query(const query<> &, const closure_bool &);
bool _query_event_id(const query<> &, const closure_bool &);
@ -1373,6 +1248,28 @@ ircd::m::vm::exists(const event::id &event_id)
return has(column, event_id);
}
int
ircd::m::vm::test(eval &eval,
const query<> &where)
{
return test(eval, where, [](const auto &event)
{
return true;
});
}
int
ircd::m::vm::test(eval &eval,
const query<> &clause,
const closure_bool &closure)
{
return _query(eval, clause, [&closure](const auto &event)
{
return closure(event);
});
}
bool
ircd::m::vm::test(const query<> &where)
{
@ -1396,6 +1293,29 @@ ircd::m::vm::test(const query<> &clause,
return ret;
}
bool
ircd::m::vm::until(const query<> &where)
{
return until(where, [](const auto &event)
{
return true;
});
}
bool
ircd::m::vm::until(const query<> &clause,
const closure_bool &closure)
{
bool ret{true};
_query(clause, [&ret, &closure](const auto &event)
{
ret = closure(event);
return ret;
});
return ret;
}
size_t
ircd::m::vm::count(const query<> &where)
{
@ -1471,6 +1391,75 @@ ircd::m::vm::for_each(const query<> &clause,
});
}
int
ircd::m::vm::_query(eval &eval,
const query<> &clause,
const closure_bool &closure)
{
switch(clause.type)
{
case where::equal:
{
const auto &c
{
dynamic_cast<const query<where::equal> &>(clause)
};
return _query_where(eval, c, closure);
}
case where::logical_and:
{
const auto &c
{
dynamic_cast<const query<where::logical_and> &>(clause)
};
return _query_where(eval, c, closure);
}
default:
return -1;
}
}
int
ircd::m::vm::_query_where(eval &eval,
const query<where::equal> &where,
const closure_bool &closure)
{
const int ret
{
eval.capstan.test(where)
};
log.debug("eval(%p): Query [%s]: %s -> %d",
&eval,
"where equal",
pretty_oneline(where.value),
ret);
return ret;
}
int
ircd::m::vm::_query_where(eval &eval,
const query<where::logical_and> &where,
const closure_bool &closure)
{
const auto &lhs{*where.a}, &rhs{*where.b};
const auto reclosure{[&lhs, &rhs, &closure]
(const auto &event)
{
if(!rhs(event))
return false;
return closure(event);
}};
return _query(eval, lhs, reclosure);
}
bool
ircd::m::vm::_query(const query<> &clause,
const closure_bool &closure)
@ -1596,9 +1585,8 @@ int
ircd::m::vm::_query_where_room_id_at_event_id(const query<where::equal> &where,
const closure_bool &closure)
{
std::cout << "where room id at event id?" << std::endl;
const auto &value{where.value};
const auto &room_id{json::get<"room_id"_>(value)};
const room::id &room_id{json::get<"room_id"_>(value)};
const auto &event_id{json::get<"event_id"_>(value)};
const auto &state_key{json::get<"state_key"_>(value)};
@ -1686,10 +1674,10 @@ ircd::m::vm::_query_in_room_id(const query<> &query,
bool
ircd::m::vm::_query_for_type_state_key_in_room_id(const query<> &query,
const closure_bool &closure,
const room::id &room_id,
const string_view &type,
const string_view &state_key)
const closure_bool &closure,
const room::id &room_id,
const string_view &type,
const string_view &state_key)
{
cursor cursor
{
@ -1724,6 +1712,23 @@ ircd::m::vm::_query_for_type_state_key_in_room_id(const query<> &query,
return false;
}
ircd::string_view
ircd::m::vm::reflect(const where &w)
{
switch(w)
{
case where::noop: return "noop";
case where::test: return "test";
case where::equal: return "equal";
case where::not_equal: return "not_equal";
case where::logical_or: return "logical_or";
case where::logical_and: return "logical_and";
case where::logical_not: return "logical_not";
}
return "?????";
}
namespace ircd::m
{
struct indexer;
@ -1733,13 +1738,16 @@ namespace ircd::m
struct ircd::m::indexer
{
//TODO: collapse
struct concat;
struct concat_s; //TODO: special
struct concat_v;
struct concat_2v;
struct concat_3vs; //TODO: special
std::string name;
virtual void operator()(const event &, db::iov &iov) const = 0;
virtual void operator()(const event &, db::iov &iov) const {}
indexer(std::string name)
:name{std::move(name)}
@ -1764,14 +1772,13 @@ ircd::m::append_indexes(const event &event,
}
}
struct ircd::m::indexer::concat
:indexer
{
std::string col_a;
std::string col_b;
void operator()(const event &, db::iov &) const override;
void operator()(const event &, db::iov &) const final override;
concat(std::string col_a, std::string col_b)
:indexer
@ -1820,6 +1827,62 @@ const
};
}
struct ircd::m::indexer::concat_s
:indexer
{
std::string col_a;
std::string col_b;
void operator()(const event &, db::iov &) const final override;
concat_s(std::string col_a, std::string col_b)
:indexer
{
fmt::snstringf(512, "%s in %s", col_a, col_b)
}
,col_a{col_a}
,col_b{col_b}
{}
};
void
ircd::m::indexer::concat_s::operator()(const event &event,
db::iov &iov)
const
{
if(!iov.has(db::op::SET, col_a) || !iov.has(db::op::SET, col_b))
return;
static const size_t buf_max
{
1024
};
char index[buf_max];
index[0] = '\0';
const auto function
{
[&index](auto &val)
{
strlcat(index, byte_view<string_view>{val}, buf_max);
}
};
at(event, col_b, function);
strlcat(index, ":::", buf_max); //TODO: special
at(event, col_a, function);
db::iov::append
{
iov, db::delta
{
name, // col
index, // key
{}, // val
}
};
}
struct ircd::m::indexer::concat_v
:indexer
{
@ -1827,7 +1890,8 @@ struct ircd::m::indexer::concat_v
std::string col_b;
std::string col_c;
void operator()(const event &, db::iov &) const override;
void operator()(const event &, db::iov &) const final override;
void operator()(const event &, db::iov &, const string_view &) const;
concat_v(std::string col_a, std::string col_b, std::string col_c)
:indexer
@ -1883,6 +1947,41 @@ const
};
}
void
ircd::m::indexer::concat_v::operator()(const event &event,
db::iov &iov,
const string_view &val)
const
{
static const size_t buf_max
{
1024
};
char index[buf_max];
index[0] = '\0';
const auto concat
{
[&index](auto &val)
{
strlcat(index, byte_view<string_view>{val}, buf_max);
}
};
at(event, col_c, concat);
at(event, col_b, concat);
db::iov::append
{
iov, db::delta
{
name, // col
index, // key
val, // val
}
};
}
struct ircd::m::indexer::concat_2v
:indexer
{
@ -1891,7 +1990,7 @@ struct ircd::m::indexer::concat_2v
std::string col_b1;
std::string col_c;
void operator()(const event &, db::iov &) const override;
void operator()(const event &, db::iov &) const final override;
concat_2v(std::string col_a, std::string col_b0, std::string col_b1, std::string col_c)
:indexer
@ -1950,28 +2049,108 @@ const
};
}
struct ircd::m::indexer::concat_3vs
:indexer
{
std::string col_a;
std::string col_b0;
std::string col_b1;
std::string col_b2;
std::string col_c;
void operator()(const event &, db::iov &, const string_view &prev_event_id) const;
concat_3vs(std::string col_a, std::string col_b0, std::string col_b1, std::string col_b2, std::string col_c)
:indexer
{
fmt::snstringf(512, "%s for %s,%s,%s in %s", col_a, col_b0, col_b1, col_b2, col_c)
}
,col_a{col_a}
,col_b0{col_b0}
,col_b1{col_b1}
,col_b2{col_b2}
,col_c{col_c}
{}
}
const concat_3vs
{
"prev_event_id", "type", "state_key", "event_id", "room_id"
};
// Non-participating
void
ircd::m::_index_special0(const event &event,
db::iov &iov,
const string_view &prev_event_id)
{
concat_3vs(event, iov, prev_event_id);
}
// Non-participating
void
ircd::m::_index_special1(const event &event,
db::iov &iov,
const string_view &prev_event_id)
{
static const ircd::m::indexer::concat_v idxr
{
"prev_event_id", "event_id", "room_id"
};
idxr(event, iov, prev_event_id);
}
// Non-participating
void
ircd::m::indexer::concat_3vs::operator()(const event &event,
db::iov &iov,
const string_view &prev_event_id)
const
{
if(!iov.has(db::op::SET, col_c) ||
!iov.has(db::op::SET, col_b0) ||
!iov.has(db::op::SET, col_b1) ||
!iov.has(db::op::SET, col_b2))
return;
static const size_t buf_max
{
2048
};
char index[buf_max];
index[0] = '\0';
const auto concat
{
[&index](auto &val)
{
strlcat(index, byte_view<string_view>{val}, buf_max);
}
};
at(event, col_c, concat);
strlcat(index, "..", buf_max); //TODO: special
at(event, col_b0, concat);
at(event, col_b1, concat);
at(event, col_b2, concat);
db::iov::append
{
iov, db::delta
{
name, // col
index, // key
prev_event_id, // val
}
};
}
std::set<std::shared_ptr<ircd::m::indexer>> ircd::m::indexers
{{
std::make_shared<ircd::m::indexer::concat>("event_id", "sender"),
std::make_shared<ircd::m::indexer::concat>("event_id", "room_id"),
std::make_shared<ircd::m::indexer::concat_v>("event_id", "room_id", "type"),
std::make_shared<ircd::m::indexer::concat_s>("origin", "room_id"),
std::make_shared<ircd::m::indexer::concat_v>("event_id", "room_id", "sender"),
std::make_shared<ircd::m::indexer::concat_2v>("event_id", "type", "state_key", "room_id"),
std::make_shared<ircd::m::indexer::concat_3vs>("prev_event_id", "type", "state_key", "event_id", "room_id"),
}};
ircd::string_view
ircd::m::vm::reflect(const where &w)
{
switch(w)
{
case where::noop: return "noop";
case where::test: return "test";
case where::equal: return "equal";
case where::not_equal: return "not_equal";
case where::logical_or: return "logical_or";
case where::logical_and: return "logical_and";
case where::logical_not: return "logical_not";
}
return "?????";
}

View file

@ -11,6 +11,7 @@ AM_CPPFLAGS = \
@JS_CPPFLAGS@ \
@BOOST_CPPFLAGS@ \
-include $(top_srcdir)/include/ircd/ircd.h \
-include $(top_srcdir)/include/ircd/m/m.h \
-include $(top_srcdir)/include/ircd/mapi.h \
###
@ -65,6 +66,7 @@ client_client_pushrules_la_SOURCES = client/pushrules.cc
client_client_user_la_SOURCES = client/user.cc
client_client_join_la_SOURCES = client/join.cc
client_client_voip_turnserver_la_SOURCES = client/voip/turnserver.cc
client_client_directory_room_la_SOURCES = client/directory/room.cc
client_module_LTLIBRARIES = \
client/client_versions.la \
client/client_events.la \
@ -80,6 +82,7 @@ client_module_LTLIBRARIES = \
client/client_user.la \
client/client_join.la \
client/client_voip_turnserver.la \
client/client_directory_room.la \
###
# This puts the source in key/ but the installed

View file

@ -71,7 +71,7 @@ try
room_id
};
room.create(event, content);
//room.create(event, content);
return resource::response
{

View file

@ -0,0 +1,55 @@
/*
* 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;
resource directory_room_resource
{
"/_matrix/client/r0/directory/room",
{
"(7.2) Room aliases",
directory_room_resource.DIRECTORY
}
};
resource::response
get_directory_room(client &client, const resource::request &request)
{
m::room::alias::buf room_alias
{
url::decode(request.parv[0], room_alias)
};
return resource::response
{
client, http::NOT_FOUND
};
}
resource::method directory_room_get
{
directory_room_resource, "GET", get_directory_room
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/directory/room' to manage the Matrix room directory"
};

View file

@ -94,6 +94,14 @@ post_filter(client &client, const resource::request::object<const m::filter> &re
url::decode(request.parv[0], user_id)
};
if(user_id != request.user_id)
throw m::ACCESS_DENIED
{
"Trying to post a filter for `%s' but you are `%s'",
user_id,
request.user_id
};
// (5.2) List of event fields to include. If this list is absent then all fields are
// included. The entries may include '.' charaters to indicate sub-fields. So
// ['content.body'] will include the 'body' field of the 'content' object. A literal '.'

View file

@ -438,6 +438,57 @@ const database::descriptor event_id_in_room_id
event_id_in,
};
/// prefix transform for origin in
///
/// This transform expects a concatenation ending with an origin which means
/// the prefix can be the same for multiple origins; therefor we can find
/// or iterate "origin in X" where X is some repeated prefix
///
/// TODO: strings will have character conflicts. must address
const ircd::db::prefix_transform origin_in
{
"origin in",
[](const string_view &key)
{
return has(key, ":::");
//return key.find(':') != key.npos;
},
[](const string_view &key)
{
return split(key, ":::").first;
//return rsplit(key, ':').first;
}
};
const database::descriptor origin_in_room_id
{
// name
"origin in room_id",
// explanation
R"(### developer note:
key is "!room_id:origin"
the prefix transform is in effect. this column indexes origins in a
room_id offering an iterable bound of the index prefixed by room_id
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
},
// options
{},
// comparator - sorts from highest to lowest
{}, //ircd::db::reverse_cmp_string_view{},
// prefix transform
origin_in,
};
/// prefix transform for room_id
///
/// This transform expects a concatenation ending with a room_id which means
@ -457,31 +508,6 @@ const ircd::db::prefix_transform room_id_in
}
};
const database::descriptor event_id_for_room_id_in_type
{
// name
"event_id for room_id in type",
// explanation
R"(### developer note:
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
},
// options
{},
// comparator
{},
// prefix transform
room_id_in,
};
const database::descriptor event_id_for_room_id_in_sender
{
// name
@ -513,6 +539,8 @@ const database::descriptor event_id_for_room_id_in_sender
/// in that order with prefix being the room_id (this may change to room_id+
/// type
///
/// TODO: arbitrary type strings will have character conflicts. must address
/// TODO: with grammars.
const ircd::db::prefix_transform type_state_key_in_room_id
{
"type,state_key in room_id",
@ -550,26 +578,88 @@ const database::descriptor event_id_for_type_state_key_in_room_id
// prefix transform
type_state_key_in_room_id
};
/*
const database::descriptor event_id_timeline_prev
const database::descriptor prev_event_id_for_event_id_in_room_id
{
// name
"event_id_timeline_prev",
"prev_event_id for event_id in room_id",
// explanation
R"(### protocol note:
R"(### developer note:
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
},
// options
{},
// comparator
{},
// prefix transform
event_id_in
};
/// prefix transform for event_id in room_id,type,state_key
///
/// This transform is special for concatenating room_id with type and state_key
/// and event_id in that order with prefix being the room_id,type,state_key. This
/// will index multiple event_ids with the same type,state_key in a room which
/// allows for a temporal depth to the database; event_id for type,state_key only
/// resolves to a single latest event and overwrites itself as per the room state
/// algorithm whereas this can map all of them and then allows for tracing.
///
/// TODO: arbitrary type strings will have character conflicts. must address
/// TODO: with grammars.
const ircd::db::prefix_transform event_id_in_room_id_type_state_key
{
"event_id in room_id,type_state_key",
[](const string_view &key)
{
return has(key, '$');
},
[](const string_view &key)
{
return split(key, '$').first;
}
};
*/
const database::descriptor prev_event_id_for_type_state_key_event_id_in_room_id
{
// name
"prev_event_id for type,state_key,event_id in room_id",
// explanation
R"(### developer note:
)",
// typing (key, value)
{
typeid(string_view), typeid(string_view)
},
// options
{},
// comparator
{},
// prefix transform
event_id_in_room_id_type_state_key
};
const database::description events_description
{
{ "default" },
// These columns directly represent event fields indexed by event_id and
// the value is the actual event values. Some values may be JSON, like
// content.
events_event_id_descriptor,
events_type_descriptor,
events_content_descriptor,
@ -586,14 +676,44 @@ const database::description events_description
events_membership_descriptor,
events_prev_events_descriptor,
events_prev_state_descriptor,
// These columns are metadata composed from the event data. Each column has
// a different approach specific to what the query is and value being sought.
// (sender, event_id) => ()
// All events for a sender
// * broad but useful in cases
event_id_in_sender,
// (room_id, event_id) => ()
// All events for a room
// * broad but useful in cases
// * ?eliminate for prev_event?
event_id_in_room_id,
event_id_for_room_id_in_type,
// (room_id, origin) => ()
// All origins for a room
origin_in_room_id,
// (sender, room_id) => (event_id)
// The _last written_ event from a sender in a room
event_id_for_room_id_in_sender,
// (room_id, type, state_key) => (event_id)
// The _last written_ event of type + state_key in a room
// * Proper for room state algorithm, but only works in the present based
// on our subjective tape order.
event_id_for_type_state_key_in_room_id,
// event_id_timeline_prev,
// event_id_timeline_next,
// event_id_for_type_timeline_prev,
// (room_id, event_id) => (prev event_id)
// Events in a room resolving to the previous event in a room in
// our subjective euclidean tape order.
prev_event_id_for_event_id_in_room_id,
// (room_id, type, state_key, event_id) => (prev event_id)
// Events of a (type, state_key) in a room resolving to the previous event
// of (type, state_key) in a room in our subjective euclidean tape order.
prev_event_id_for_type_state_key_event_id_in_room_id,
};
std::shared_ptr<database> events_database