mirror of
https://github.com/matrix-construct/construct
synced 2024-12-26 15:33:54 +01:00
ircd::m modules: Matrix reinterface checkpoint.
This commit is contained in:
parent
a89a8dfa5f
commit
1cea631f60
24 changed files with 1135 additions and 463 deletions
|
@ -25,32 +25,32 @@
|
|||
#pragma once
|
||||
#define HAVE_IRCD_M_H
|
||||
|
||||
#include "json/parse.h"
|
||||
|
||||
namespace ircd {
|
||||
namespace m {
|
||||
|
||||
} // namespace m
|
||||
} // namespace ircd
|
||||
|
||||
#include "m/error.h"
|
||||
#include "m/id.h"
|
||||
#include "m/db.h"
|
||||
#include "m/event.h"
|
||||
#include "m/events.h"
|
||||
#include "m/room.h"
|
||||
#include "m/timeline.h"
|
||||
#include "m/request.h"
|
||||
#include "m/accounts.h"
|
||||
#include "m/session.h"
|
||||
|
||||
namespace ircd {
|
||||
namespace m {
|
||||
|
||||
namespace ircd::m::dbs
|
||||
{
|
||||
struct init
|
||||
{
|
||||
db::init db;
|
||||
init();
|
||||
~init() noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
struct init
|
||||
{
|
||||
dbs::init dbs;
|
||||
|
||||
init();
|
||||
~init() noexcept;
|
||||
};
|
||||
|
||||
} // namespace m
|
||||
} // namespace ircd
|
||||
}
|
||||
|
|
|
@ -43,35 +43,40 @@
|
|||
// implied by the total 65 KB limit on events.
|
||||
//
|
||||
|
||||
namespace ircd {
|
||||
namespace m {
|
||||
namespace ircd::m
|
||||
{
|
||||
struct event;
|
||||
}
|
||||
|
||||
struct event
|
||||
namespace ircd::m::name
|
||||
{
|
||||
constexpr const char *const event_id {"event_id"};
|
||||
constexpr const char *const content {"content"};
|
||||
constexpr const char *const origin_server_ts {"origin_server_ts"};
|
||||
constexpr const char *const sender {"sender"};
|
||||
constexpr const char *const type {"type"};
|
||||
constexpr const char *const room_id {"room_id"};
|
||||
constexpr const char *const state_key {"state_key"};
|
||||
constexpr const char *const prev_ids {"prev_ids"};
|
||||
constexpr const char *const unsigned_ {"unsigned"};
|
||||
constexpr const char *const signatures {"signatures"};
|
||||
}
|
||||
|
||||
struct ircd::m::event
|
||||
:json::tuple
|
||||
<
|
||||
string_view,
|
||||
time_t,
|
||||
string_view,
|
||||
string_view,
|
||||
string_view,
|
||||
string_view
|
||||
json::member<name::event_id, string_view>,
|
||||
json::member<name::content, string_view>,
|
||||
json::member<name::origin_server_ts, time_t>,
|
||||
json::member<name::sender, string_view>,
|
||||
json::member<name::type, string_view>,
|
||||
json::member<name::room_id, string_view>,
|
||||
json::member<name::state_key, string_view>,
|
||||
json::member<name::prev_ids, string_view>,
|
||||
json::member<name::unsigned_, string_view>,
|
||||
json::member<name::signatures, string_view>
|
||||
>
|
||||
{
|
||||
IRCD_MEMBERS
|
||||
(
|
||||
"content",
|
||||
"origin_server_ts",
|
||||
"sender",
|
||||
"type",
|
||||
"unsigned",
|
||||
"state_key"
|
||||
)
|
||||
|
||||
template<class... A>
|
||||
explicit event(const json::object &obj)
|
||||
:tuple{json::make_tuple<decltype(*this)>(obj)}
|
||||
{}
|
||||
using super_type::tuple;
|
||||
using super_type::operator=;
|
||||
};
|
||||
|
||||
} // namespace m
|
||||
} // namespace ircd
|
||||
|
|
|
@ -23,10 +23,47 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_M_ACCOUNTS_H
|
||||
#define HAVE_IRCD_M_EVENTS_H
|
||||
|
||||
namespace ircd {
|
||||
namespace m {
|
||||
namespace ircd::m::events
|
||||
{
|
||||
IRCD_M_EXCEPTION(m::error, INVALID_TRANSITION, http::BAD_REQUEST)
|
||||
|
||||
} // namespace m
|
||||
} // namespace ircd
|
||||
struct transition;
|
||||
|
||||
using transition_list = std::list<struct transition *>;
|
||||
extern transition_list transitions;
|
||||
|
||||
extern database *events;
|
||||
using cursor = db::cursor<events, m::event>;
|
||||
using const_iterator = cursor::const_iterator;
|
||||
using iterator = const_iterator;
|
||||
using where = cursor::where_type;
|
||||
|
||||
const_iterator find(const id &);
|
||||
const_iterator begin();
|
||||
const_iterator end();
|
||||
|
||||
void insert(event &);
|
||||
void insert(const event &);
|
||||
}
|
||||
|
||||
struct ircd::m::events::transition
|
||||
{
|
||||
struct method
|
||||
{
|
||||
std::function<bool (const event &)> valid;
|
||||
std::function<void (const event &) noexcept> effects;
|
||||
};
|
||||
|
||||
const char *name;
|
||||
struct method method;
|
||||
unique_const_iterator<events::transition_list> it;
|
||||
|
||||
virtual bool valid(const event &event) const;
|
||||
virtual void effects(const event &e);
|
||||
|
||||
transition(const char *const &name, struct method method);
|
||||
transition(const char *const &name);
|
||||
virtual ~transition() noexcept;
|
||||
};
|
74
include/ircd/m/room.h
Normal file
74
include/ircd/m/room.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* charybdis: 21st Century IRC++d
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_M_ROOM_H
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
IRCD_M_EXCEPTION(m::error, CONFLICT, http::CONFLICT);
|
||||
IRCD_M_EXCEPTION(m::error, NOT_MODIFIED, http::NOT_MODIFIED);
|
||||
IRCD_M_EXCEPTION(CONFLICT, ALREADY_MEMBER, http::CONFLICT);
|
||||
|
||||
struct room;
|
||||
}
|
||||
|
||||
namespace ircd::m::rooms
|
||||
{
|
||||
}
|
||||
|
||||
struct ircd::m::room
|
||||
{
|
||||
struct alias;
|
||||
|
||||
string_view room_id;
|
||||
|
||||
using event_closure = std::function<void (const event &)>;
|
||||
void for_each(const events::where &, const event_closure &) const;
|
||||
void for_each(const event_closure &) const;
|
||||
|
||||
using event_closure_bool = std::function<bool (const event &)>;
|
||||
size_t count(const events::where &, const event_closure_bool &) const;
|
||||
size_t count(const events::where &) const;
|
||||
bool any(const events::where &, const event_closure_bool &) const;
|
||||
bool any(const events::where &) const;
|
||||
|
||||
bool is_member(const m::id::user &, const string_view &membership = "join");
|
||||
|
||||
void membership(const m::id::user &, const json::builder &content);
|
||||
void join(const m::id::user &, const json::builder &content);
|
||||
|
||||
room(const id::room &room_id);
|
||||
room(const id::alias &alias);
|
||||
};
|
||||
|
||||
inline
|
||||
ircd::m::room::room(const id::room &room_id)
|
||||
:room_id{room_id}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::room::room(const id::alias &alias)
|
||||
:room_id{}
|
||||
{}
|
|
@ -23,22 +23,23 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_M_DB_H
|
||||
#define HAVE_IRCD_M_TIMELINE_H
|
||||
|
||||
namespace ircd {
|
||||
namespace m {
|
||||
namespace db {
|
||||
|
||||
extern database *events;
|
||||
extern database *accounts;
|
||||
extern database *rooms;
|
||||
|
||||
struct init
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The timeline class represents a sequence of events. This is the preferred
|
||||
// interface to the events DB. The timeline can be used to compose a selection
|
||||
// of events and run algorithms over them. The timeline interface can also be
|
||||
// used to insert new events. Timelines form the basis for composing higher
|
||||
// level structures like rooms.
|
||||
//
|
||||
struct timeline
|
||||
{
|
||||
init();
|
||||
~init() noexcept;
|
||||
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
} // namespace m
|
||||
} // namespace ircd
|
|
@ -97,11 +97,7 @@ try
|
|||
client::init _client_; // Client/Socket Networking
|
||||
db::init _db_; // RocksDB
|
||||
js::init _js_; // SpiderMonkey
|
||||
|
||||
module matrix
|
||||
{
|
||||
"matrix"
|
||||
};
|
||||
m::init _matrix_; // Matrix
|
||||
|
||||
// This is the main program loop. Right now all it does is sleep until notified to
|
||||
// break with a clean shutdown. Other subsystems may spawn their own main loops, but
|
||||
|
|
478
ircd/matrix.cc
478
ircd/matrix.cc
|
@ -21,6 +21,11 @@
|
|||
|
||||
#include <ircd/m.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/session.h
|
||||
//
|
||||
|
||||
ircd::m::session::session(const host_port &host_port)
|
||||
:client{host_port}
|
||||
{
|
||||
|
@ -67,6 +72,479 @@ ircd::m::session::operator()(parse::buffer &pb,
|
|||
return object;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m.h
|
||||
//
|
||||
|
||||
namespace ircd {
|
||||
namespace m {
|
||||
|
||||
std::map<std::string, ircd::module> modules;
|
||||
ircd::listener *listener;
|
||||
|
||||
void bootstrap();
|
||||
|
||||
} // namespace m
|
||||
} // namespace ircd
|
||||
|
||||
ircd::m::init::init()
|
||||
{
|
||||
if(db::sequence(*events::events) == 0)
|
||||
bootstrap();
|
||||
|
||||
for(const auto &name : mods::available())
|
||||
if(startswith(name, "client_"))
|
||||
modules.emplace(name, name);
|
||||
|
||||
modules.emplace("root.so"s, "root.so"s);
|
||||
|
||||
listener = new ircd::listener
|
||||
{
|
||||
//TODO: conf obviously
|
||||
json::string
|
||||
({
|
||||
{ "name", "Chat Matrix" },
|
||||
{ "host", "0.0.0.0" },
|
||||
{ "port", 8447 },
|
||||
{ "ssl_certificate_file", "/home/jason/zemos.net.tls.crt" },
|
||||
{ "ssl_certificate_chain_file", "/home/jason/zemos.net.tls.crt" },
|
||||
{ "ssl_tmp_dh_file", "/home/jason/zemos.net.tls.dh" },
|
||||
{ "ssl_private_key_file_pem", "/home/jason/zemos.net.tls.key" },
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
ircd::m::init::~init()
|
||||
noexcept
|
||||
{
|
||||
delete listener;
|
||||
modules.clear();
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::bootstrap()
|
||||
{
|
||||
assert(events::events);
|
||||
assert(db::sequence(*events::events) == 0);
|
||||
|
||||
ircd::log::notice
|
||||
(
|
||||
"This appears to be your first time running IRCd because the events "
|
||||
"database is empty. I will be bootstrapping it with initial events now..."
|
||||
);
|
||||
|
||||
// ircd.create event
|
||||
const m::id::id::room::buf room_id{"ircd", "localhost"};
|
||||
const m::id::id::user::buf user_id{"ircd", "localhost"};
|
||||
{
|
||||
const auto type{"m.room.create"};
|
||||
|
||||
char content[512];
|
||||
json::print(content, sizeof(content),
|
||||
{
|
||||
{ "creator", user_id },
|
||||
});
|
||||
|
||||
m::event event
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", type },
|
||||
{ "state_key", "" },
|
||||
{ "sender", user_id },
|
||||
{ "content", content },
|
||||
};
|
||||
|
||||
m::events::insert(event);
|
||||
}
|
||||
|
||||
{
|
||||
const auto type{"m.room.member"};
|
||||
|
||||
char content[512];
|
||||
json::print(content, sizeof(content),
|
||||
{
|
||||
{ "membership", "join" },
|
||||
});
|
||||
|
||||
m::event event
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", type },
|
||||
{ "state_key", user_id },
|
||||
{ "sender", user_id },
|
||||
{ "content", content },
|
||||
};
|
||||
|
||||
m::events::insert(event);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/db.h
|
||||
//
|
||||
|
||||
namespace ircd::m::dbs
|
||||
{
|
||||
std::map<std::string, ircd::module> modules;
|
||||
std::map<std::string, import_shared<database>> databases;
|
||||
|
||||
void init_modules();
|
||||
void init_databases();
|
||||
}
|
||||
|
||||
ircd::m::dbs::init::init()
|
||||
{
|
||||
init_modules();
|
||||
init_databases();
|
||||
|
||||
ircd::m::events::events = databases.at("events").get();
|
||||
}
|
||||
|
||||
ircd::m::dbs::init::~init()
|
||||
noexcept
|
||||
{
|
||||
ircd::m::events::events = nullptr;
|
||||
|
||||
databases.clear();
|
||||
modules.clear();
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::dbs::init_databases()
|
||||
{
|
||||
for(const auto &pair : modules)
|
||||
{
|
||||
const auto &name(pair.first);
|
||||
const auto dbname(mods::unpostfixed(name));
|
||||
const std::string shortname(lstrip(dbname, "db_"));
|
||||
const std::string symname(shortname + "_database"s);
|
||||
databases.emplace(shortname, import_shared<database>
|
||||
{
|
||||
dbname, symname
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::dbs::init_modules()
|
||||
{
|
||||
for(const auto &name : mods::available())
|
||||
if(startswith(name, "db_"))
|
||||
modules.emplace(name, name);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/events.h
|
||||
//
|
||||
|
||||
namespace ircd::m::events
|
||||
{
|
||||
void write(const event &);
|
||||
}
|
||||
|
||||
ircd::database *ircd::m::events::events;
|
||||
|
||||
void
|
||||
ircd::m::events::insert(const event &a)
|
||||
{
|
||||
event b(a);
|
||||
insert(b);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::events::insert(event &event)
|
||||
{
|
||||
if(!json::val<name::type>(event))
|
||||
throw BAD_JSON("Required event field: '%s'", name::type);
|
||||
|
||||
if(!json::val<name::sender>(event))
|
||||
throw BAD_JSON("Required event field: '%s'", name::sender);
|
||||
|
||||
bool has_event_id
|
||||
{
|
||||
!json::val<name::event_id>(event).empty()
|
||||
};
|
||||
|
||||
const id::event::buf generated_event_id
|
||||
{
|
||||
has_event_id? id::event::buf{} : id::event::buf{id::generate, "cdc.z"}
|
||||
};
|
||||
|
||||
if(!has_event_id)
|
||||
json::val<name::event_id>(event) = generated_event_id;
|
||||
|
||||
const scope remove_our_event_id([&]
|
||||
{
|
||||
if(!has_event_id)
|
||||
json::val<name::event_id>(event) = {};
|
||||
});
|
||||
|
||||
bool has_origin_server_ts
|
||||
{
|
||||
json::val<name::origin_server_ts>(event) != 0
|
||||
};
|
||||
|
||||
if(!has_origin_server_ts)
|
||||
json::val<name::origin_server_ts>(event) = time<milliseconds>();
|
||||
|
||||
for(const auto &transition : events::transitions)
|
||||
if(!transition->valid(event))
|
||||
throw INVALID_TRANSITION("Event insertion refused: '%s'",
|
||||
transition->name);
|
||||
|
||||
write(event);
|
||||
}
|
||||
|
||||
ircd::m::events::const_iterator
|
||||
ircd::m::events::find(const id &id)
|
||||
{
|
||||
cursor cur{"!room_id$event_id"};
|
||||
return cur.find(id);
|
||||
}
|
||||
|
||||
ircd::m::events::const_iterator
|
||||
ircd::m::events::begin()
|
||||
{
|
||||
cursor cur{"!room_id$event_id"};
|
||||
return cur.begin();
|
||||
}
|
||||
|
||||
ircd::m::events::const_iterator
|
||||
ircd::m::events::end()
|
||||
{
|
||||
cursor cur{"!room_id$event_id"};
|
||||
return cur.end();
|
||||
}
|
||||
|
||||
ircd::m::events::transition_list
|
||||
ircd::m::events::transitions
|
||||
{};
|
||||
|
||||
ircd::m::events::transition::transition(const char *const &name)
|
||||
:name{name}
|
||||
,it
|
||||
{
|
||||
events::transitions, events::transitions.emplace(events::transitions.end(), this)
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::events::transition::transition(const char *const &name,
|
||||
struct method method)
|
||||
:name{name}
|
||||
,method{std::move(method)}
|
||||
,it
|
||||
{
|
||||
events::transitions, events::transitions.emplace(events::transitions.end(), this)
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::events::transition::~transition()
|
||||
noexcept
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::events::transition::valid(const event &event)
|
||||
const
|
||||
{
|
||||
return method.valid(event);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::events::transition::effects(const event &event)
|
||||
{
|
||||
method.effects(event);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::events::write(const event &event)
|
||||
{
|
||||
const auto &master_index
|
||||
{
|
||||
at<name::event_id>(event)
|
||||
};
|
||||
|
||||
constexpr const size_t num_indexes(1);
|
||||
constexpr const size_t num_deltas
|
||||
{
|
||||
event.size() + num_indexes
|
||||
};
|
||||
|
||||
size_t i(0);
|
||||
db::delta deltas[num_deltas];
|
||||
for_each(event, [&i, &deltas, &master_index]
|
||||
(const auto &key, const auto &val)
|
||||
{
|
||||
deltas[i++] =
|
||||
{
|
||||
key, // col
|
||||
master_index, // key
|
||||
byte_view<>{val}, // val
|
||||
};
|
||||
});
|
||||
|
||||
char buf[id::event::buf::SIZE + id::room::buf::SIZE];
|
||||
if(!json::val<name::room_id>(event).empty())
|
||||
{
|
||||
strlcpy(buf, json::val<name::room_id>(event).data(), sizeof(buf));
|
||||
strlcat(buf, json::val<name::event_id>(event).data(), sizeof(buf));
|
||||
deltas[i++] = db::delta
|
||||
{
|
||||
"!room_id$event_id", // col
|
||||
buf, // key
|
||||
};
|
||||
}
|
||||
|
||||
(*events)(begin(deltas), begin(deltas) + i);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/room.h
|
||||
//
|
||||
|
||||
void
|
||||
ircd::m::room::join(const m::id::user &user_id,
|
||||
const json::builder &content)
|
||||
{
|
||||
const json::builder content_with_membership
|
||||
{
|
||||
&content,
|
||||
{ "membership", "join" }
|
||||
};
|
||||
|
||||
membership(user_id, content_with_membership);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::membership(const m::id::user &user_id,
|
||||
const json::builder &content)
|
||||
{
|
||||
if(is_member(user_id, content.at("membership")))
|
||||
throw m::ALREADY_MEMBER
|
||||
{
|
||||
"Already a member with this membership."
|
||||
};
|
||||
|
||||
char cbuf[512];
|
||||
m::events::insert(m::event
|
||||
{
|
||||
{ "room_id", room_id },
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id },
|
||||
{ "sender", user_id },
|
||||
{ "content", json::stringify(cbuf, sizeof(cbuf), content) }
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::is_member(const m::id::user &user_id,
|
||||
const string_view &membership)
|
||||
{
|
||||
const m::events::where::equal query
|
||||
{
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id }
|
||||
};
|
||||
|
||||
return count(query, [](const auto &event)
|
||||
{
|
||||
const json::object &content
|
||||
{
|
||||
json::val<m::name::content>(event)
|
||||
};
|
||||
|
||||
const auto &membership
|
||||
{
|
||||
unquote(content["membership"])
|
||||
};
|
||||
|
||||
return membership == membership;
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::any(const events::where &where)
|
||||
const
|
||||
{
|
||||
events::cursor cursor{"!room_id$event_id"};
|
||||
cursor.where = &where;
|
||||
return bool(cursor.find(room_id));
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::any(const events::where &where,
|
||||
const event_closure_bool &closure)
|
||||
const
|
||||
{
|
||||
events::cursor cursor{"!room_id$event_id"};
|
||||
cursor.where = &where;
|
||||
|
||||
for(auto it(cursor.find(room_id)); bool(it); ++it)
|
||||
if(closure(*it))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::count(const events::where &where)
|
||||
const
|
||||
{
|
||||
events::cursor cursor{"!room_id$event_id"};
|
||||
cursor.where = &where;
|
||||
|
||||
size_t i(0);
|
||||
for(auto it(cursor.find(room_id)); bool(it); ++it, i++)
|
||||
return i;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::count(const events::where &where,
|
||||
const event_closure_bool &closure)
|
||||
const
|
||||
{
|
||||
events::cursor cursor{"!room_id$event_id"};
|
||||
cursor.where = &where;
|
||||
|
||||
size_t i(0);
|
||||
for(auto it(cursor.find(room_id)); bool(it); ++it)
|
||||
{
|
||||
const m::event &e(*it);
|
||||
i += closure(e);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::for_each(const events::where &where,
|
||||
const event_closure &closure)
|
||||
const
|
||||
{
|
||||
events::cursor cursor{"!room_id$event_id"};
|
||||
cursor.where = &where;
|
||||
auto it(cursor.find(room_id));
|
||||
if(!it)
|
||||
return;
|
||||
|
||||
for(; bool(it); ++it)
|
||||
closure(*it);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::for_each(const event_closure &closure)
|
||||
const
|
||||
{
|
||||
const m::events::where::noop where{};
|
||||
for_each(where, closure);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/id.h
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
#
|
||||
#AM_CXXFLAGS = \
|
||||
# -fno-implicit-templates
|
||||
#
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/include \
|
||||
@JS_CPPFLAGS@ \
|
||||
@BOOST_CPPFLAGS@ \
|
||||
-include $(top_srcdir)/include/ircd/ircd.h \
|
||||
-include $(top_srcdir)/include/ircd/mapi.h \
|
||||
-include $(top_srcdir)/include/ircd/m.h \
|
||||
-include $(top_srcdir)/include/ircd/db/object.h
|
||||
-include $(top_srcdir)/include/ircd/m.h
|
||||
|
||||
AM_LDFLAGS = \
|
||||
-L$(top_srcdir)/ircd \
|
||||
|
@ -26,10 +30,8 @@ AM_LDFLAGS += \
|
|||
# -export-symbols-regex *
|
||||
|
||||
moduledir=@moduledir@
|
||||
matrix_la_SOURCES = matrix.cc
|
||||
root_la_SOURCES = root.cc
|
||||
module_LTLIBRARIES = \
|
||||
matrix.la \
|
||||
root.la \
|
||||
###
|
||||
|
||||
|
@ -37,12 +39,8 @@ module_LTLIBRARIES = \
|
|||
# library is client_X.so in the main modules dir.
|
||||
db_moduledir = @moduledir@
|
||||
db_db_events_la_SOURCES = db/events.cc
|
||||
db_db_accounts_la_SOURCES = db/accounts.cc
|
||||
db_db_rooms_la_SOURCES = db/rooms.cc
|
||||
db_module_LTLIBRARIES = \
|
||||
db/db_events.la \
|
||||
db/db_accounts.la \
|
||||
db/db_rooms.la \
|
||||
###
|
||||
|
||||
# This puts the source in client/ but the installed
|
||||
|
|
|
@ -106,5 +106,5 @@ resource::method post
|
|||
|
||||
mapi::header IRCD_MODULE
|
||||
{
|
||||
"registers the resource 'client/login' to handle requests"
|
||||
"registers the resource 'client/account' to handle requests"
|
||||
};
|
||||
|
|
|
@ -41,13 +41,43 @@ try
|
|||
unquote(request["visibility"])
|
||||
};
|
||||
|
||||
std::string room_id {"!foo@bar.com"};
|
||||
const m::id::room::buf room_id
|
||||
{
|
||||
m::id::generate, "localhost"
|
||||
};
|
||||
|
||||
const m::id::user::buf sender_id
|
||||
{
|
||||
m::id::generate, "localhost"
|
||||
};
|
||||
|
||||
const m::id::event::buf create_event_id
|
||||
{
|
||||
m::id::generate, "localhost",
|
||||
};
|
||||
|
||||
const time_t origin_server_ts
|
||||
{
|
||||
time(NULL)
|
||||
};
|
||||
/*
|
||||
db::object<m::db::events, m::event> event
|
||||
{
|
||||
create_event_id
|
||||
};
|
||||
|
||||
db::write
|
||||
({
|
||||
{ event["type"], "m.room.create" },
|
||||
{ event["room_id"], room_id },
|
||||
{ event["origin_server_ts"], binary_view(origin_server_ts) },
|
||||
});
|
||||
*/
|
||||
return resource::response
|
||||
{
|
||||
client, http::CREATED, json::index
|
||||
{
|
||||
{ "room_id", room_id }
|
||||
{ "room_id", string_view{room_id} }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,12 +27,53 @@ resource events_resource
|
|||
"Events (6.2.3) (10.x)"
|
||||
};
|
||||
|
||||
const m::id::room::buf accounts_room_id
|
||||
{
|
||||
"accounts", "cdc.z"
|
||||
};
|
||||
|
||||
const m::id::room::buf locops_room_id
|
||||
{
|
||||
"locops", "cdc.z"
|
||||
};
|
||||
|
||||
const m::id::room::buf ircd_room_id
|
||||
{
|
||||
"ircd", "localhost"
|
||||
};
|
||||
|
||||
resource::response
|
||||
get_events(client &client, const resource::request &request)
|
||||
{
|
||||
m::room room{accounts_room_id};
|
||||
std::vector<std::string> ret;
|
||||
|
||||
room.for_each([&ret](const auto &event)
|
||||
{
|
||||
ret.emplace_back(json::string(event));
|
||||
});
|
||||
|
||||
std::vector<json::object> jo(ret.size());
|
||||
std::transform(ret.begin(), ret.end(), jo.begin(), []
|
||||
(const auto &string) -> json::object
|
||||
{
|
||||
return string;
|
||||
});
|
||||
|
||||
char buf[16384];
|
||||
char *start{buf};
|
||||
char *const stop{buf + sizeof(buf)};
|
||||
const auto chunk
|
||||
{
|
||||
json::serialize(jo, start, stop)
|
||||
};
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, json::object {}
|
||||
client, json::index
|
||||
{
|
||||
{ "chunk", string_view{chunk} }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -34,93 +34,109 @@ const auto home_server
|
|||
"cdc.z"
|
||||
};
|
||||
|
||||
using object = db::object<m::db::accounts>;
|
||||
template<class T = string_view> using value = db::value<m::db::accounts, T>;
|
||||
namespace name
|
||||
{
|
||||
extern constexpr auto password{"password"};
|
||||
extern constexpr auto medium{"medium"};
|
||||
extern constexpr auto type{"type"};
|
||||
extern constexpr auto user{"user"};
|
||||
extern constexpr auto address{"address"};
|
||||
}
|
||||
|
||||
struct body
|
||||
:json::tuple
|
||||
<
|
||||
json::member<name::password, string_view>,
|
||||
json::member<name::medium, time_t>,
|
||||
json::member<name::type, string_view>,
|
||||
json::member<name::user, string_view>,
|
||||
json::member<name::address, string_view>
|
||||
>
|
||||
{
|
||||
using super_type::tuple;
|
||||
};
|
||||
|
||||
// Generate !accounts:host which is the MXID of the room where the members
|
||||
// are the actual account registrations themselves for this homeserver.
|
||||
const m::id::room::buf accounts_room_id
|
||||
{
|
||||
"accounts", home_server
|
||||
};
|
||||
|
||||
// Handle to the accounts room
|
||||
m::room accounts_room
|
||||
{
|
||||
accounts_room_id
|
||||
};
|
||||
|
||||
resource::response
|
||||
post_login_password(client &client, const resource::request &request)
|
||||
post_login_password(client &client,
|
||||
const resource::request &request,
|
||||
const resource::request::body<body> &body)
|
||||
{
|
||||
const auto user
|
||||
const m::id::user::buf user_id
|
||||
{
|
||||
// "The fully qualified user ID or just local part of the user ID, to log in."
|
||||
unquote(request.at("user"))
|
||||
unquote(at<name::user>(body)), home_server
|
||||
};
|
||||
|
||||
char user_id[m::USER_ID_BUFSIZE]; m::id
|
||||
{
|
||||
user, home_server, user_id
|
||||
};
|
||||
|
||||
const auto password
|
||||
{
|
||||
// "Required. The user's password."
|
||||
request.at("password")
|
||||
};
|
||||
|
||||
value<> password_text("password.text", user_id);
|
||||
if(password_text != password)
|
||||
if(!user_id.valid() || user_id.host() != home_server)
|
||||
throw m::error
|
||||
{
|
||||
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
|
||||
};
|
||||
|
||||
// "An access token for the account. This access token can then be used to "
|
||||
// "authorize other requests. The access token may expire at some point, and if "
|
||||
// "so, it SHOULD come with a refresh_token. There is no specific error message to "
|
||||
// "indicate that a request has failed because an access token has expired; "
|
||||
// "instead, if a client has reason to believe its access token is valid, and "
|
||||
// "it receives an auth error, they should attempt to refresh for a new token "
|
||||
// "on failure, and retry the request with the new token."
|
||||
value<> access_token_text{"access_token.text", user_id};
|
||||
const auto &supplied_password
|
||||
{
|
||||
at<name::password>(body)
|
||||
};
|
||||
|
||||
// Generate access token
|
||||
char access_token[m::ACCESS_TOKEN_BUFSIZE];
|
||||
m::access_token_generate(access_token, sizeof(access_token));
|
||||
const auto check{[&supplied_password]
|
||||
(const auto &event)
|
||||
{
|
||||
const json::object &content
|
||||
{
|
||||
json::val<m::name::content>(event)
|
||||
};
|
||||
|
||||
// Write access token to database
|
||||
access_token_text = access_token;
|
||||
ircd::resource::tokens.emplace(access_token, &client); //TODO: XXX
|
||||
value<> token_to_user_id{"token", access_token_text};
|
||||
token_to_user_id = user_id;
|
||||
const auto &membership
|
||||
{
|
||||
unquote(content.at("membership"))
|
||||
};
|
||||
|
||||
if(membership != "join")
|
||||
return false;
|
||||
|
||||
const auto &correct_password
|
||||
{
|
||||
content.at("password")
|
||||
};
|
||||
|
||||
if(supplied_password != correct_password)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}};
|
||||
|
||||
const m::events::where::equal query
|
||||
{
|
||||
{ "type", "m.room.member" },
|
||||
{ "state_key", user_id }
|
||||
};
|
||||
|
||||
if(!accounts_room.any(query, check))
|
||||
throw m::error
|
||||
{
|
||||
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
|
||||
};
|
||||
|
||||
// Send response to user
|
||||
return resource::response
|
||||
{
|
||||
client,
|
||||
{
|
||||
{ "user_id", user_id },
|
||||
{ "access_token", access_token },
|
||||
{ "home_server", home_server },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
resource::response
|
||||
post_login_token(client &client, const resource::request &request)
|
||||
{
|
||||
const auto token
|
||||
{
|
||||
unquote(request.at("token"))
|
||||
};
|
||||
|
||||
const value<> user_id
|
||||
{
|
||||
"token", token
|
||||
};
|
||||
|
||||
if(!user_id)
|
||||
throw m::error
|
||||
{
|
||||
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
|
||||
};
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client,
|
||||
{
|
||||
{ "user_id", string_view(user_id) },
|
||||
{ "access_token", token },
|
||||
{ "user_id", string_view{user_id} },
|
||||
{ "home_server", home_server },
|
||||
// { "access_token", access_token },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -128,16 +144,20 @@ post_login_token(client &client, const resource::request &request)
|
|||
resource::response
|
||||
post_login(client &client, const resource::request &request)
|
||||
{
|
||||
const resource::request::body<body> body
|
||||
{
|
||||
request
|
||||
};
|
||||
|
||||
// x.x.x Required. The login type being used.
|
||||
// Currently only "m.login.password" is supported.
|
||||
const auto type
|
||||
{
|
||||
// "Required. The login type being used. Currently only "m.login.password" is supported."
|
||||
unquote(request.at("type"))
|
||||
unquote(at<name::type>(body))
|
||||
};
|
||||
|
||||
if(type == "m.login.password")
|
||||
return post_login_password(client, request);
|
||||
else if(type == "m.login.token")
|
||||
return post_login_token(client, request);
|
||||
return post_login_password(client, request, body);
|
||||
else
|
||||
throw m::error
|
||||
{
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
|
||||
using namespace ircd;
|
||||
|
||||
using object = db::object<m::db::accounts>;
|
||||
template<class T = string_view> using value = db::value<m::db::accounts, T>;
|
||||
|
||||
resource logout_resource
|
||||
{
|
||||
"_matrix/client/r0/logout",
|
||||
|
|
|
@ -21,51 +21,79 @@
|
|||
|
||||
using namespace ircd;
|
||||
|
||||
using object = db::object<m::db::accounts>;
|
||||
template<class T = string_view> using value = db::value<m::db::accounts, T>;
|
||||
|
||||
const auto home_server
|
||||
{
|
||||
// "The hostname of the homeserver on which the account has been registered."
|
||||
"cdc.z"
|
||||
};
|
||||
|
||||
namespace name
|
||||
{
|
||||
constexpr auto username{"username"};
|
||||
constexpr auto bind_email{"bind_email"};
|
||||
constexpr auto password{"password"};
|
||||
constexpr auto auth{"auth"};
|
||||
}
|
||||
|
||||
struct body
|
||||
:json::tuple
|
||||
<
|
||||
json::member<name::username, string_view>,
|
||||
json::member<name::bind_email, bool>,
|
||||
json::member<name::password, string_view>,
|
||||
json::member<name::auth, json::object>
|
||||
>
|
||||
{
|
||||
using super_type::tuple;
|
||||
};
|
||||
|
||||
// Generate !accounts:host which is the MXID of the room where the members
|
||||
// are the account registrations on this homeserver.
|
||||
const m::id::room::buf accounts_room_id
|
||||
{
|
||||
"accounts", home_server
|
||||
};
|
||||
|
||||
m::room accounts_room
|
||||
{
|
||||
accounts_room_id
|
||||
};
|
||||
|
||||
static void
|
||||
join_accounts_room(const m::id::user &user_id,
|
||||
const json::members &contents)
|
||||
try
|
||||
{
|
||||
const json::builder content
|
||||
{
|
||||
nullptr, &contents
|
||||
};
|
||||
|
||||
accounts_room.join(user_id, content);
|
||||
}
|
||||
catch(const m::ALREADY_MEMBER &e)
|
||||
{
|
||||
throw m::error
|
||||
{
|
||||
http::CONFLICT, "M_USER_IN_USE", "The desired user ID is already in use."
|
||||
};
|
||||
}
|
||||
|
||||
resource::response
|
||||
handle_post(client &client,
|
||||
resource::request &request)
|
||||
handle_post_kind_user(client &client,
|
||||
resource::request &request,
|
||||
const resource::request::body<body> &body)
|
||||
{
|
||||
const auto kind
|
||||
// 3.3.1 Additional authentication information for the user-interactive authentication API.
|
||||
const auto &auth
|
||||
{
|
||||
request.query["kind"]
|
||||
at<name::auth>(body)
|
||||
};
|
||||
|
||||
if(kind.empty() || kind == "guest")
|
||||
// 3.3.1 Required. The login type that the client is attempting to complete.
|
||||
const auto &type
|
||||
{
|
||||
char user_id[m::USER_ID_BUFSIZE]; m::id
|
||||
{
|
||||
"randy12345", home_server, user_id
|
||||
};
|
||||
char access_token[m::ACCESS_TOKEN_BUFSIZE];
|
||||
m::access_token_generate(access_token, sizeof(access_token));
|
||||
ircd::resource::tokens.emplace(access_token, &client); //TODO: XXX
|
||||
value<> token_to_user_id{"token", access_token};
|
||||
token_to_user_id = user_id;
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, http::CREATED, json::index
|
||||
{
|
||||
{ "access_token", access_token },
|
||||
{ "home_server", home_server },
|
||||
{ "user_id", user_id },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const auto type
|
||||
{
|
||||
// "Required. The login type that the client is attempting to complete."
|
||||
unquote(request.at("auth.type"))
|
||||
unquote(auth.at("type"))
|
||||
};
|
||||
|
||||
if(type != "m.login.dummy")
|
||||
|
@ -74,71 +102,61 @@ handle_post(client &client,
|
|||
"M_UNSUPPORTED", "Registration '%s' not supported.", type
|
||||
};
|
||||
|
||||
const auto username
|
||||
// 3.3.1 The local part of the desired Matrix ID. If omitted, the homeserver MUST
|
||||
// generate a Matrix ID local part.
|
||||
const auto &username
|
||||
{
|
||||
// "The local part of the desired Matrix ID. If omitted, the homeserver MUST "
|
||||
// "generate a Matrix ID local part."
|
||||
unquote(request.get("username"))
|
||||
unquote(get<name::username>(body))
|
||||
};
|
||||
|
||||
if(!username.empty() && !m::username_valid(username))
|
||||
// Generate canonical mxid
|
||||
const m::id::user::buf user_id
|
||||
{
|
||||
username, home_server
|
||||
};
|
||||
|
||||
if(!user_id.valid())
|
||||
throw m::error
|
||||
{
|
||||
"M_INVALID_USERNAME", "The desired user ID is not a valid user name."
|
||||
};
|
||||
|
||||
const auto password
|
||||
{
|
||||
// "Required. The desired password for the account."
|
||||
request.at("password")
|
||||
};
|
||||
|
||||
const auto bind_email
|
||||
{
|
||||
// "If true, the server binds the email used for authentication to the "
|
||||
// "Matrix ID with the ID Server."
|
||||
request.get<bool>("bind_email", false)
|
||||
};
|
||||
|
||||
// Generate fully qualified user id and randomize username if missing
|
||||
char user_id[m::USER_ID_BUFSIZE]; m::id
|
||||
{
|
||||
username, home_server, user_id
|
||||
};
|
||||
|
||||
// Atomic commitment to registration
|
||||
value<time_t> registered{"registered", user_id};
|
||||
{
|
||||
time_t expected(0); // unregistered == empty == 0
|
||||
if(!registered.compare_exchange(expected, time(nullptr)))
|
||||
if(user_id.host() != home_server)
|
||||
throw m::error
|
||||
{
|
||||
http::CONFLICT, "M_USER_IN_USE", "The desired user ID is already taken."
|
||||
"M_INVALID_USERNAME", "Can only register with host '%s'", home_server
|
||||
};
|
||||
}
|
||||
|
||||
// "An access token for the account. This access token can then be used to "
|
||||
// "authorize other requests. The access token may expire at some point, and if "
|
||||
// "so, it SHOULD come with a refresh_token. There is no specific error message to "
|
||||
// "indicate that a request has failed because an access token has expired; "
|
||||
// "instead, if a client has reason to believe its access token is valid, and "
|
||||
// "it receives an auth error, they should attempt to refresh for a new token "
|
||||
// "on failure, and retry the request with the new token."
|
||||
value<> access_token_text{"access_token.text", user_id};
|
||||
// 3.3.1 Required. The desired password for the account.
|
||||
const auto &password
|
||||
{
|
||||
at<name::password>(body)
|
||||
};
|
||||
|
||||
// Prepare to store password
|
||||
value<> password_text("password.text", user_id);
|
||||
if(password.size() > 255)
|
||||
throw m::error
|
||||
{
|
||||
"M_INVALID_PASSWORD", "The desired password is too long"
|
||||
};
|
||||
|
||||
// Generate access token
|
||||
char access_token[m::ACCESS_TOKEN_BUFSIZE];
|
||||
m::access_token_generate(access_token, sizeof(access_token));
|
||||
ircd::resource::tokens.emplace(access_token, &client); //TODO: XXX
|
||||
// 3.3.1 If true, the server binds the email used for authentication to the
|
||||
// Matrix ID with the ID Server. Defaults to false.
|
||||
const auto &bind_email
|
||||
{
|
||||
get<name::bind_email>(body, false)
|
||||
};
|
||||
|
||||
// Batch transaction to database
|
||||
db::write
|
||||
({
|
||||
{ db::SET, password_text, password },
|
||||
{ db::SET, access_token_text, access_token },
|
||||
// Register the user by joining them to the accounts room. Join the user
|
||||
// to the accounts room by issuing a join event. The content will store
|
||||
// keys from the registration options including the password - do not
|
||||
// expose this to clients //TODO: store hashed pass
|
||||
// Once this call completes the join was successful and the user
|
||||
// is registered, otherwise throws.
|
||||
char content[384];
|
||||
join_accounts_room(user_id,
|
||||
{
|
||||
{ "password", password },
|
||||
{ "bind_email", bind_email },
|
||||
});
|
||||
|
||||
// Send response to user
|
||||
|
@ -146,13 +164,60 @@ handle_post(client &client,
|
|||
{
|
||||
client, http::CREATED, json::index
|
||||
{
|
||||
{ "access_token", access_token },
|
||||
{ "home_server", home_server },
|
||||
{ "user_id", user_id },
|
||||
{ "home_server", home_server },
|
||||
// { "access_token", access_token },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
resource::response
|
||||
handle_post_kind_guest(client &client,
|
||||
resource::request &request,
|
||||
const resource::request::body<body> &body)
|
||||
{
|
||||
const m::id::user::buf user_id
|
||||
{
|
||||
m::generate, home_server
|
||||
};
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, http::CREATED, json::index
|
||||
{
|
||||
{ "user_id", user_id },
|
||||
{ "home_server", home_server },
|
||||
//{ "access_token", access_token },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
resource::response
|
||||
handle_post(client &client,
|
||||
resource::request &request)
|
||||
{
|
||||
const resource::request::body<body> body
|
||||
{
|
||||
request
|
||||
};
|
||||
|
||||
const auto kind
|
||||
{
|
||||
request.query["kind"]
|
||||
};
|
||||
|
||||
if(kind == "user")
|
||||
return handle_post_kind_user(client, request, body);
|
||||
|
||||
if(kind.empty() || kind == "guest")
|
||||
return handle_post_kind_guest(client, request, body);
|
||||
|
||||
throw m::error
|
||||
{
|
||||
http::BAD_REQUEST, "M_UNKNOWN", "Unknown 'kind' of registration specified in query."
|
||||
};
|
||||
}
|
||||
|
||||
resource register_resource
|
||||
{
|
||||
"_matrix/client/r0/register",
|
||||
|
|
|
@ -21,16 +21,16 @@
|
|||
|
||||
using namespace ircd;
|
||||
|
||||
//using object = db::object<m::db::accounts>;
|
||||
//template<class T = string_view> using value = db::value<T, m::db::accounts>;
|
||||
|
||||
resource sync_resource
|
||||
{
|
||||
"_matrix/client/r0/sync",
|
||||
"Synchronise the client's state with the latest state on the server. "
|
||||
"Clients use this API when they first log in to get an initial snapshot of "
|
||||
"the state on the server, and then continue to call this API to get "
|
||||
"incremental deltas to the state, and to receive new messages. (6.2)"
|
||||
|
||||
R"(
|
||||
Synchronise the client's state with the latest state on the server.
|
||||
Clients use this API when they first log in to get an initial snapshot of
|
||||
the state on the server, and then continue to call this API to get
|
||||
incremental deltas to the state, and to receive new messages. (6.2)
|
||||
)"
|
||||
};
|
||||
|
||||
resource::response
|
||||
|
|
|
@ -21,16 +21,6 @@
|
|||
|
||||
using namespace ircd;
|
||||
|
||||
const database::descriptor accounts_token_descriptor
|
||||
{
|
||||
"token",
|
||||
"An index of access_token to user_id",
|
||||
{
|
||||
// readable key // readable value
|
||||
typeid(string_view), typeid(string_view)
|
||||
}
|
||||
};
|
||||
|
||||
const database::descriptor accounts_registered_descriptor
|
||||
{
|
||||
"registered",
|
||||
|
@ -44,10 +34,7 @@ const database::descriptor accounts_registered_descriptor
|
|||
const database::description accounts_description
|
||||
{
|
||||
{ "default" },
|
||||
accounts_token_descriptor,
|
||||
accounts_registered_descriptor,
|
||||
{ "access_token" },
|
||||
{ "access_token.text" },
|
||||
{ "password" },
|
||||
{ "password.text" },
|
||||
{ "password.hash" },
|
||||
|
@ -63,4 +50,3 @@ mapi::header IRCD_MODULE
|
|||
{
|
||||
"Hosts the 'accounts' database"
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,30 @@
|
|||
|
||||
using namespace ircd;
|
||||
|
||||
const database::descriptor events_event_id_descriptor
|
||||
{
|
||||
// name
|
||||
"event_id",
|
||||
|
||||
// explanation
|
||||
R"(### protocol note:
|
||||
|
||||
10.1
|
||||
The id of event.
|
||||
|
||||
10.4
|
||||
MUST NOT exceed 255 bytes.
|
||||
|
||||
### developer note:
|
||||
key is event_id. This is redundant data but we have to have it for now.
|
||||
)",
|
||||
|
||||
// typing (key, value)
|
||||
{
|
||||
typeid(string_view), typeid(string_view)
|
||||
}
|
||||
};
|
||||
|
||||
const database::descriptor events_type_descriptor
|
||||
{
|
||||
// name
|
||||
|
@ -191,9 +215,118 @@ const database::descriptor events_origin_server_ts_descriptor
|
|||
}
|
||||
};
|
||||
|
||||
const database::descriptor events_prev_ids_descriptor
|
||||
{
|
||||
// name
|
||||
"prev_ids",
|
||||
|
||||
// explanation
|
||||
R"(### protocol note:
|
||||
|
||||
FEDERATION 4.1 (INCONSISTENT)
|
||||
List of (String, String, Object) Triplets
|
||||
The originating homeserver, PDU ids and hashes of the most recent PDUs the homeserver was
|
||||
aware of for the room when it made this PDU. ["blue.example.com","99d16afbc8", {"sha256":
|
||||
"abase64encodedsha256hashshouldbe43byteslong"}]
|
||||
|
||||
### developer note:
|
||||
key is event_id
|
||||
|
||||
)",
|
||||
|
||||
// typing (key, value)
|
||||
{
|
||||
typeid(string_view), typeid(string_view)
|
||||
}
|
||||
};
|
||||
|
||||
const database::descriptor events_unsigned_descriptor
|
||||
{
|
||||
// name
|
||||
"unsigned",
|
||||
|
||||
// explanation
|
||||
R"(### protocol note:
|
||||
|
||||
### developer note:
|
||||
key is event_id
|
||||
|
||||
)",
|
||||
|
||||
// typing (key, value)
|
||||
{
|
||||
typeid(string_view), typeid(string_view)
|
||||
}
|
||||
};
|
||||
|
||||
const database::descriptor events_signatures_descriptor
|
||||
{
|
||||
// name
|
||||
"signatures",
|
||||
|
||||
// explanation
|
||||
R"(### protocol note:
|
||||
|
||||
### developer note:
|
||||
key is event_id
|
||||
|
||||
)",
|
||||
|
||||
// typing (key, value)
|
||||
{
|
||||
typeid(string_view), typeid(string_view)
|
||||
}
|
||||
};
|
||||
|
||||
const database::descriptor index_room_id_to_event_id
|
||||
{
|
||||
// name
|
||||
"!room_id$event_id",
|
||||
|
||||
// explanation
|
||||
R"(### developer note:
|
||||
|
||||
key is "!room_id$event_id"
|
||||
the prefix transform is in effect. this column indexes events by
|
||||
room_id offering an iterable bound of the index prefixed by room_id
|
||||
|
||||
)",
|
||||
|
||||
// typing (key, value)
|
||||
{
|
||||
typeid(string_view), typeid(string_view)
|
||||
},
|
||||
|
||||
// options
|
||||
{},
|
||||
|
||||
// comparator
|
||||
{},
|
||||
|
||||
// prefix transform
|
||||
{
|
||||
"!room_id$event_id"s,
|
||||
[](const string_view &key)
|
||||
{
|
||||
return key.find('$') != key.npos;
|
||||
},
|
||||
[](const string_view &key)
|
||||
{
|
||||
return split(key, '$').first;
|
||||
}
|
||||
}
|
||||
|
||||
// hooks
|
||||
// {
|
||||
|
||||
|
||||
// }
|
||||
};
|
||||
|
||||
const database::description events_description
|
||||
{
|
||||
{ "default" },
|
||||
events_event_id_descriptor,
|
||||
events_type_descriptor,
|
||||
events_content_descriptor,
|
||||
events_room_id_descriptor,
|
||||
|
@ -201,6 +334,10 @@ const database::description events_description
|
|||
events_state_key_descriptor,
|
||||
events_origin_descriptor,
|
||||
events_origin_server_ts_descriptor,
|
||||
events_prev_ids_descriptor,
|
||||
events_unsigned_descriptor,
|
||||
events_signatures_descriptor,
|
||||
index_room_id_to_event_id,
|
||||
};
|
||||
|
||||
std::shared_ptr<database> events_database
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
const database::descriptor rooms_head_descriptor
|
||||
{
|
||||
// name
|
||||
"head",
|
||||
|
||||
// notes
|
||||
R"(
|
||||
### developer note:
|
||||
|
||||
The latest event for a room.
|
||||
|
||||
key is room_id
|
||||
value is event_id
|
||||
|
||||
)",
|
||||
|
||||
// typing for key and value
|
||||
{
|
||||
typeid(string_view), typeid(string_view)
|
||||
}
|
||||
};
|
||||
|
||||
const database::description rooms_description
|
||||
{
|
||||
{ "default" },
|
||||
rooms_head_descriptor,
|
||||
};
|
||||
|
||||
std::shared_ptr<database> rooms_database
|
||||
{
|
||||
std::make_shared<database>("room"s, ""s, rooms_description)
|
||||
};
|
||||
|
||||
mapi::header IRCD_MODULE
|
||||
{
|
||||
"Hosts the 'rooms' database"
|
||||
};
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
using namespace ircd;
|
||||
|
||||
mapi::header IRCD_MODULE
|
||||
{
|
||||
"Chat Matrix Protocol"
|
||||
};
|
||||
|
||||
std::map<std::string, module> modules;
|
||||
|
||||
void test();
|
||||
|
||||
//TODO: XXX very temporary stuff around here
|
||||
// The path root (serves static assets for web etc); this pointer
|
||||
// exists for now to easily find and reload that specifically.
|
||||
module *root_module;
|
||||
const auto _init_([]
|
||||
{
|
||||
// These modules host databases and have to be loaded first.
|
||||
modules.emplace("client_events.so"s, "client_events.so"s);
|
||||
modules.emplace("client_account.so"s, "client_account.so"s);
|
||||
modules.emplace("client_rooms.so"s, "client_rooms.so"s);
|
||||
|
||||
for(const auto &name : mods::available())
|
||||
if(name != "matrix.so")
|
||||
modules.emplace(name, name);
|
||||
|
||||
root_module = &modules.at("root.so"s);
|
||||
|
||||
test();
|
||||
|
||||
return true;
|
||||
}());
|
||||
|
||||
listener matrices
|
||||
{
|
||||
std::string { json::index
|
||||
{
|
||||
{ "name", "Chat Matrix" },
|
||||
{ "host", "0.0.0.0" },
|
||||
{
|
||||
"ssl_certificate_file", "/home/jason/newcert.pem"
|
||||
},
|
||||
{
|
||||
"ssl_certificate_chain_file", "/home/jason/newcert.pem"
|
||||
},
|
||||
{
|
||||
"ssl_tmp_dh_file", "/home/jason/dh1024.pem"
|
||||
},
|
||||
{
|
||||
"ssl_private_key_file_pem", "/home/jason/privkey.pem"
|
||||
},
|
||||
{
|
||||
"port", 8448
|
||||
}
|
||||
}}
|
||||
};
|
||||
|
||||
void test()
|
||||
{
|
||||
json::object test
|
||||
{
|
||||
R"({"content":"hello","origin_server_ts":12345,"sender":"@foo:bar.com"})"
|
||||
};
|
||||
/*
|
||||
const m::event ev0
|
||||
{
|
||||
"some content", 0, "some sender", "some type", "some unsigned", "statie"
|
||||
};
|
||||
*/
|
||||
const m::event ev
|
||||
{
|
||||
test
|
||||
};
|
||||
|
||||
std::cout << "size: " << sizeof(ev) << std::endl;
|
||||
|
||||
json::for_each(ev, []
|
||||
(auto&& key, auto&& val)
|
||||
{
|
||||
std::cout << key << " => " << val << std::endl;
|
||||
});
|
||||
|
||||
std::cout << "----" << std::endl;
|
||||
|
||||
json::rfor_each(ev, []
|
||||
(const string_view &key, auto&& val)
|
||||
{
|
||||
std::cout << key << " => " << val << std::endl;
|
||||
});
|
||||
|
||||
std::cout << "----" << std::endl;
|
||||
|
||||
json::until(ev, []
|
||||
(const string_view &key, auto&& val)
|
||||
{
|
||||
std::cout << key << " => " << val << std::endl;
|
||||
return true;
|
||||
});
|
||||
|
||||
std::cout << "----" << std::endl;
|
||||
|
||||
json::runtil(ev, []
|
||||
(const string_view &key, auto&& val)
|
||||
{
|
||||
std::cout << key << " => " << val << std::endl;
|
||||
return true;
|
||||
});
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << json::indexof(ev, "origin_server_ts") << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
|
@ -25,7 +25,7 @@ using namespace ircd;
|
|||
|
||||
mapi::header IRCD_MODULE
|
||||
{
|
||||
"Chat Matrix Protocol"
|
||||
"Web root content resource",
|
||||
};
|
||||
|
||||
IRCD_INIT_PRIORITY(STD_CONTAINER)
|
||||
|
|
Loading…
Reference in a new issue