mirror of
https://github.com/matrix-construct/construct
synced 2024-09-26 18:38:52 +02:00
ircd:Ⓜ️ Split m.cc; distribute inits; modules: Checkpoint matrix.
This commit is contained in:
parent
d8bddbbff0
commit
80931e4e86
19 changed files with 2404 additions and 1861 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 &);
|
||||
|
|
|
@ -225,5 +225,4 @@ namespace ircd
|
|||
#include "net/net.h"
|
||||
#include "server.h"
|
||||
#include "client.h"
|
||||
#include "m/m.h"
|
||||
#include "resource.h"
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
371
ircd/m/event.cc
Normal 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;
|
||||
}
|
71
ircd/m/io.cc
71
ircd/m/io.cc
|
@ -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
546
ircd/m/keys.cc
Normal 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;
|
||||
}
|
1575
ircd/m/m.cc
1575
ircd/m/m.cc
File diff suppressed because it is too large
Load diff
637
ircd/m/room.cc
Normal file
637
ircd/m/room.cc
Normal 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;
|
||||
}
|
605
ircd/m/vm.cc
605
ircd/m/vm.cc
|
@ -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 "?????";
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -71,7 +71,7 @@ try
|
|||
room_id
|
||||
};
|
||||
|
||||
room.create(event, content);
|
||||
//room.create(event, content);
|
||||
|
||||
return resource::response
|
||||
{
|
||||
|
|
55
modules/client/directory/room.cc
Normal file
55
modules/client/directory/room.cc
Normal 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"
|
||||
};
|
|
@ -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 '.'
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue