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
|
#pragma once
|
||||||
#define HAVE_IRCD_M_H
|
#define HAVE_IRCD_M_H
|
||||||
|
|
||||||
#include "json/parse.h"
|
|
||||||
|
|
||||||
namespace ircd {
|
|
||||||
namespace m {
|
|
||||||
|
|
||||||
} // namespace m
|
|
||||||
} // namespace ircd
|
|
||||||
|
|
||||||
#include "m/error.h"
|
#include "m/error.h"
|
||||||
#include "m/id.h"
|
#include "m/id.h"
|
||||||
#include "m/db.h"
|
|
||||||
#include "m/event.h"
|
#include "m/event.h"
|
||||||
|
#include "m/events.h"
|
||||||
|
#include "m/room.h"
|
||||||
|
#include "m/timeline.h"
|
||||||
#include "m/request.h"
|
#include "m/request.h"
|
||||||
#include "m/accounts.h"
|
|
||||||
#include "m/session.h"
|
#include "m/session.h"
|
||||||
|
|
||||||
namespace ircd {
|
namespace ircd::m::dbs
|
||||||
namespace m {
|
|
||||||
|
|
||||||
struct init
|
|
||||||
{
|
{
|
||||||
db::init db;
|
struct init
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
~init() noexcept;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
init();
|
namespace ircd::m
|
||||||
~init() noexcept;
|
{
|
||||||
};
|
struct init
|
||||||
|
{
|
||||||
|
dbs::init dbs;
|
||||||
|
|
||||||
} // namespace m
|
init();
|
||||||
} // namespace ircd
|
~init() noexcept;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -43,35 +43,40 @@
|
||||||
// implied by the total 65 KB limit on events.
|
// implied by the total 65 KB limit on events.
|
||||||
//
|
//
|
||||||
|
|
||||||
namespace ircd {
|
namespace ircd::m
|
||||||
namespace 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
|
:json::tuple
|
||||||
<
|
<
|
||||||
string_view,
|
json::member<name::event_id, string_view>,
|
||||||
time_t,
|
json::member<name::content, string_view>,
|
||||||
string_view,
|
json::member<name::origin_server_ts, time_t>,
|
||||||
string_view,
|
json::member<name::sender, string_view>,
|
||||||
string_view,
|
json::member<name::type, string_view>,
|
||||||
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
|
using super_type::tuple;
|
||||||
(
|
using super_type::operator=;
|
||||||
"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)}
|
|
||||||
{}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace m
|
|
||||||
} // namespace ircd
|
|
||||||
|
|
|
@ -23,10 +23,47 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#define HAVE_IRCD_M_ACCOUNTS_H
|
#define HAVE_IRCD_M_EVENTS_H
|
||||||
|
|
||||||
namespace ircd {
|
namespace ircd::m::events
|
||||||
namespace m {
|
{
|
||||||
|
IRCD_M_EXCEPTION(m::error, INVALID_TRANSITION, http::BAD_REQUEST)
|
||||||
|
|
||||||
} // namespace m
|
struct transition;
|
||||||
} // namespace ircd
|
|
||||||
|
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
|
#pragma once
|
||||||
#define HAVE_IRCD_M_DB_H
|
#define HAVE_IRCD_M_TIMELINE_H
|
||||||
|
|
||||||
namespace ircd {
|
namespace ircd {
|
||||||
namespace m {
|
namespace m {
|
||||||
namespace db {
|
|
||||||
|
|
||||||
extern database *events;
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
extern database *accounts;
|
//
|
||||||
extern database *rooms;
|
// 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
|
||||||
struct init
|
// 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 m
|
||||||
} // namespace ircd
|
} // namespace ircd
|
|
@ -97,11 +97,7 @@ try
|
||||||
client::init _client_; // Client/Socket Networking
|
client::init _client_; // Client/Socket Networking
|
||||||
db::init _db_; // RocksDB
|
db::init _db_; // RocksDB
|
||||||
js::init _js_; // SpiderMonkey
|
js::init _js_; // SpiderMonkey
|
||||||
|
m::init _matrix_; // Matrix
|
||||||
module matrix
|
|
||||||
{
|
|
||||||
"matrix"
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is the main program loop. Right now all it does is sleep until notified to
|
// 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
|
// 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>
|
#include <ircd/m.h>
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// m/session.h
|
||||||
|
//
|
||||||
|
|
||||||
ircd::m::session::session(const host_port &host_port)
|
ircd::m::session::session(const host_port &host_port)
|
||||||
:client{host_port}
|
:client{host_port}
|
||||||
{
|
{
|
||||||
|
@ -67,6 +72,479 @@ ircd::m::session::operator()(parse::buffer &pb,
|
||||||
return object;
|
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
|
// m/id.h
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
|
#
|
||||||
|
#AM_CXXFLAGS = \
|
||||||
|
# -fno-implicit-templates
|
||||||
|
#
|
||||||
|
|
||||||
AM_CPPFLAGS = \
|
AM_CPPFLAGS = \
|
||||||
-I$(top_srcdir)/include \
|
-I$(top_srcdir)/include \
|
||||||
@JS_CPPFLAGS@ \
|
@JS_CPPFLAGS@ \
|
||||||
@BOOST_CPPFLAGS@ \
|
@BOOST_CPPFLAGS@ \
|
||||||
-include $(top_srcdir)/include/ircd/ircd.h \
|
-include $(top_srcdir)/include/ircd/ircd.h \
|
||||||
-include $(top_srcdir)/include/ircd/mapi.h \
|
-include $(top_srcdir)/include/ircd/mapi.h \
|
||||||
-include $(top_srcdir)/include/ircd/m.h \
|
-include $(top_srcdir)/include/ircd/m.h
|
||||||
-include $(top_srcdir)/include/ircd/db/object.h
|
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
AM_LDFLAGS = \
|
||||||
-L$(top_srcdir)/ircd \
|
-L$(top_srcdir)/ircd \
|
||||||
|
@ -26,10 +30,8 @@ AM_LDFLAGS += \
|
||||||
# -export-symbols-regex *
|
# -export-symbols-regex *
|
||||||
|
|
||||||
moduledir=@moduledir@
|
moduledir=@moduledir@
|
||||||
matrix_la_SOURCES = matrix.cc
|
|
||||||
root_la_SOURCES = root.cc
|
root_la_SOURCES = root.cc
|
||||||
module_LTLIBRARIES = \
|
module_LTLIBRARIES = \
|
||||||
matrix.la \
|
|
||||||
root.la \
|
root.la \
|
||||||
###
|
###
|
||||||
|
|
||||||
|
@ -37,12 +39,8 @@ module_LTLIBRARIES = \
|
||||||
# library is client_X.so in the main modules dir.
|
# library is client_X.so in the main modules dir.
|
||||||
db_moduledir = @moduledir@
|
db_moduledir = @moduledir@
|
||||||
db_db_events_la_SOURCES = db/events.cc
|
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_module_LTLIBRARIES = \
|
||||||
db/db_events.la \
|
db/db_events.la \
|
||||||
db/db_accounts.la \
|
|
||||||
db/db_rooms.la \
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# This puts the source in client/ but the installed
|
# This puts the source in client/ but the installed
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
@ -106,5 +106,5 @@ resource::method post
|
||||||
|
|
||||||
mapi::header IRCD_MODULE
|
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"])
|
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
|
return resource::response
|
||||||
{
|
{
|
||||||
client, http::CREATED, json::index
|
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)"
|
"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
|
resource::response
|
||||||
get_events(client &client, const resource::request &request)
|
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
|
return resource::response
|
||||||
{
|
{
|
||||||
client, json::object {}
|
client, json::index
|
||||||
|
{
|
||||||
|
{ "chunk", string_view{chunk} }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
@ -34,93 +34,109 @@ const auto home_server
|
||||||
"cdc.z"
|
"cdc.z"
|
||||||
};
|
};
|
||||||
|
|
||||||
using object = db::object<m::db::accounts>;
|
namespace name
|
||||||
template<class T = string_view> using value = db::value<m::db::accounts, T>;
|
{
|
||||||
|
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
|
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(at<name::user>(body)), home_server
|
||||||
unquote(request.at("user"))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
char user_id[m::USER_ID_BUFSIZE]; m::id
|
if(!user_id.valid() || user_id.host() != home_server)
|
||||||
{
|
|
||||||
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)
|
|
||||||
throw m::error
|
throw m::error
|
||||||
{
|
{
|
||||||
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
|
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
|
||||||
};
|
};
|
||||||
|
|
||||||
// "An access token for the account. This access token can then be used to "
|
const auto &supplied_password
|
||||||
// "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 "
|
at<name::password>(body)
|
||||||
// "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};
|
|
||||||
|
|
||||||
// Generate access token
|
const auto check{[&supplied_password]
|
||||||
char access_token[m::ACCESS_TOKEN_BUFSIZE];
|
(const auto &event)
|
||||||
m::access_token_generate(access_token, sizeof(access_token));
|
{
|
||||||
|
const json::object &content
|
||||||
|
{
|
||||||
|
json::val<m::name::content>(event)
|
||||||
|
};
|
||||||
|
|
||||||
// Write access token to database
|
const auto &membership
|
||||||
access_token_text = access_token;
|
{
|
||||||
ircd::resource::tokens.emplace(access_token, &client); //TODO: XXX
|
unquote(content.at("membership"))
|
||||||
value<> token_to_user_id{"token", access_token_text};
|
};
|
||||||
token_to_user_id = user_id;
|
|
||||||
|
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
|
// Send response to user
|
||||||
return resource::response
|
return resource::response
|
||||||
{
|
{
|
||||||
client,
|
client,
|
||||||
{
|
{
|
||||||
{ "user_id", user_id },
|
{ "user_id", string_view{user_id} },
|
||||||
{ "access_token", access_token },
|
{ "home_server", home_server },
|
||||||
{ "home_server", home_server },
|
// { "access_token", access_token },
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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 },
|
|
||||||
{ "home_server", home_server },
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -128,16 +144,20 @@ post_login_token(client &client, const resource::request &request)
|
||||||
resource::response
|
resource::response
|
||||||
post_login(client &client, const resource::request &request)
|
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
|
const auto type
|
||||||
{
|
{
|
||||||
// "Required. The login type being used. Currently only "m.login.password" is supported."
|
unquote(at<name::type>(body))
|
||||||
unquote(request.at("type"))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if(type == "m.login.password")
|
if(type == "m.login.password")
|
||||||
return post_login_password(client, request);
|
return post_login_password(client, request, body);
|
||||||
else if(type == "m.login.token")
|
|
||||||
return post_login_token(client, request);
|
|
||||||
else
|
else
|
||||||
throw m::error
|
throw m::error
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
@ -21,9 +21,6 @@
|
||||||
|
|
||||||
using namespace ircd;
|
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
|
resource logout_resource
|
||||||
{
|
{
|
||||||
"_matrix/client/r0/logout",
|
"_matrix/client/r0/logout",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
@ -21,51 +21,79 @@
|
||||||
|
|
||||||
using namespace ircd;
|
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
|
const auto home_server
|
||||||
{
|
{
|
||||||
// "The hostname of the homeserver on which the account has been registered."
|
// "The hostname of the homeserver on which the account has been registered."
|
||||||
"cdc.z"
|
"cdc.z"
|
||||||
};
|
};
|
||||||
|
|
||||||
resource::response
|
namespace name
|
||||||
handle_post(client &client,
|
|
||||||
resource::request &request)
|
|
||||||
{
|
{
|
||||||
const auto kind
|
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
|
||||||
{
|
{
|
||||||
request.query["kind"]
|
nullptr, &contents
|
||||||
};
|
};
|
||||||
|
|
||||||
if(kind.empty() || kind == "guest")
|
accounts_room.join(user_id, content);
|
||||||
|
}
|
||||||
|
catch(const m::ALREADY_MEMBER &e)
|
||||||
|
{
|
||||||
|
throw m::error
|
||||||
{
|
{
|
||||||
char user_id[m::USER_ID_BUFSIZE]; m::id
|
http::CONFLICT, "M_USER_IN_USE", "The desired user ID is already in use."
|
||||||
{
|
};
|
||||||
"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
|
resource::response
|
||||||
{
|
handle_post_kind_user(client &client,
|
||||||
client, http::CREATED, json::index
|
resource::request &request,
|
||||||
{
|
const resource::request::body<body> &body)
|
||||||
{ "access_token", access_token },
|
{
|
||||||
{ "home_server", home_server },
|
// 3.3.1 Additional authentication information for the user-interactive authentication API.
|
||||||
{ "user_id", user_id },
|
const auto &auth
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto type
|
|
||||||
{
|
{
|
||||||
// "Required. The login type that the client is attempting to complete."
|
at<name::auth>(body)
|
||||||
unquote(request.at("auth.type"))
|
};
|
||||||
|
|
||||||
|
// 3.3.1 Required. The login type that the client is attempting to complete.
|
||||||
|
const auto &type
|
||||||
|
{
|
||||||
|
unquote(auth.at("type"))
|
||||||
};
|
};
|
||||||
|
|
||||||
if(type != "m.login.dummy")
|
if(type != "m.login.dummy")
|
||||||
|
@ -74,71 +102,61 @@ handle_post(client &client,
|
||||||
"M_UNSUPPORTED", "Registration '%s' not supported.", type
|
"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 "
|
unquote(get<name::username>(body))
|
||||||
// "generate a Matrix ID local part."
|
|
||||||
unquote(request.get("username"))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
throw m::error
|
||||||
{
|
{
|
||||||
"M_INVALID_USERNAME", "The desired user ID is not a valid user name."
|
"M_INVALID_USERNAME", "The desired user ID is not a valid user name."
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto password
|
if(user_id.host() != home_server)
|
||||||
|
throw m::error
|
||||||
|
{
|
||||||
|
"M_INVALID_USERNAME", "Can only register with host '%s'", home_server
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3.3.1 Required. The desired password for the account.
|
||||||
|
const auto &password
|
||||||
{
|
{
|
||||||
// "Required. The desired password for the account."
|
at<name::password>(body)
|
||||||
request.at("password")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto bind_email
|
if(password.size() > 255)
|
||||||
|
throw m::error
|
||||||
|
{
|
||||||
|
"M_INVALID_PASSWORD", "The desired password is too long"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
{
|
{
|
||||||
// "If true, the server binds the email used for authentication to the "
|
get<name::bind_email>(body, false)
|
||||||
// "Matrix ID with the ID Server."
|
|
||||||
request.get<bool>("bind_email", false)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate fully qualified user id and randomize username if missing
|
// Register the user by joining them to the accounts room. Join the user
|
||||||
char user_id[m::USER_ID_BUFSIZE]; m::id
|
// 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,
|
||||||
{
|
{
|
||||||
username, home_server, user_id
|
{ "password", password },
|
||||||
};
|
{ "bind_email", bind_email },
|
||||||
|
|
||||||
// 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)))
|
|
||||||
throw m::error
|
|
||||||
{
|
|
||||||
http::CONFLICT, "M_USER_IN_USE", "The desired user ID is already taken."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// "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};
|
|
||||||
|
|
||||||
// Prepare to store password
|
|
||||||
value<> password_text("password.text", user_id);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Batch transaction to database
|
|
||||||
db::write
|
|
||||||
({
|
|
||||||
{ db::SET, password_text, password },
|
|
||||||
{ db::SET, access_token_text, access_token },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send response to user
|
// Send response to user
|
||||||
|
@ -146,13 +164,60 @@ handle_post(client &client,
|
||||||
{
|
{
|
||||||
client, http::CREATED, json::index
|
client, http::CREATED, json::index
|
||||||
{
|
{
|
||||||
{ "access_token", access_token },
|
|
||||||
{ "home_server", home_server },
|
|
||||||
{ "user_id", user_id },
|
{ "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
|
resource register_resource
|
||||||
{
|
{
|
||||||
"_matrix/client/r0/register",
|
"_matrix/client/r0/register",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
@ -21,16 +21,16 @@
|
||||||
|
|
||||||
using namespace ircd;
|
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
|
resource sync_resource
|
||||||
{
|
{
|
||||||
"_matrix/client/r0/sync",
|
"_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 "
|
R"(
|
||||||
"the state on the server, and then continue to call this API to get "
|
Synchronise the client's state with the latest state on the server.
|
||||||
"incremental deltas to the state, and to receive new messages. (6.2)"
|
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
|
resource::response
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Charybdis Development Team
|
* Copyright (C) 2017 Charybdis Development Team
|
||||||
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,16 +21,6 @@
|
||||||
|
|
||||||
using namespace ircd;
|
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
|
const database::descriptor accounts_registered_descriptor
|
||||||
{
|
{
|
||||||
"registered",
|
"registered",
|
||||||
|
@ -44,10 +34,7 @@ const database::descriptor accounts_registered_descriptor
|
||||||
const database::description accounts_description
|
const database::description accounts_description
|
||||||
{
|
{
|
||||||
{ "default" },
|
{ "default" },
|
||||||
accounts_token_descriptor,
|
|
||||||
accounts_registered_descriptor,
|
accounts_registered_descriptor,
|
||||||
{ "access_token" },
|
|
||||||
{ "access_token.text" },
|
|
||||||
{ "password" },
|
{ "password" },
|
||||||
{ "password.text" },
|
{ "password.text" },
|
||||||
{ "password.hash" },
|
{ "password.hash" },
|
||||||
|
@ -63,4 +50,3 @@ mapi::header IRCD_MODULE
|
||||||
{
|
{
|
||||||
"Hosts the 'accounts' database"
|
"Hosts the 'accounts' database"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,30 @@
|
||||||
|
|
||||||
using namespace ircd;
|
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
|
const database::descriptor events_type_descriptor
|
||||||
{
|
{
|
||||||
// name
|
// 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
|
const database::description events_description
|
||||||
{
|
{
|
||||||
{ "default" },
|
{ "default" },
|
||||||
|
events_event_id_descriptor,
|
||||||
events_type_descriptor,
|
events_type_descriptor,
|
||||||
events_content_descriptor,
|
events_content_descriptor,
|
||||||
events_room_id_descriptor,
|
events_room_id_descriptor,
|
||||||
|
@ -201,6 +334,10 @@ const database::description events_description
|
||||||
events_state_key_descriptor,
|
events_state_key_descriptor,
|
||||||
events_origin_descriptor,
|
events_origin_descriptor,
|
||||||
events_origin_server_ts_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
|
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
|
mapi::header IRCD_MODULE
|
||||||
{
|
{
|
||||||
"Chat Matrix Protocol"
|
"Web root content resource",
|
||||||
};
|
};
|
||||||
|
|
||||||
IRCD_INIT_PRIORITY(STD_CONTAINER)
|
IRCD_INIT_PRIORITY(STD_CONTAINER)
|
||||||
|
|
Loading…
Reference in a new issue