0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2025-01-13 08:23:56 +01:00

ircd/modules: Preliminary matrix scaffold.

This commit is contained in:
Jason Volk 2017-08-23 15:10:28 -06:00
parent a57070a9cf
commit a3117391b5
28 changed files with 1892 additions and 500 deletions

42
include/ircd/m.h Normal file
View file

@ -0,0 +1,42 @@
/*
* 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_H
namespace ircd {
namespace m {
} // namespace m
} // namespace ircd
#include "m/error.h"
#include "m/id.h"
#include "m/event.h"
#include "m/request.h"
#include "m/session.h"
namespace ircd {
} // namespace ircd

84
include/ircd/m/error.h Normal file
View file

@ -0,0 +1,84 @@
/*
* 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_ERROR_H
namespace ircd {
namespace m {
struct error
:http::error
{
template<class... args> error(const http::code &, const string_view &errcode, const char *const &fmt, args&&...);
template<class... args> error(const string_view &errcode, const char *const &fmt, args&&...);
error(const http::code &, const json::doc &doc = {});
error(const http::code &, const json::obj &obj);
};
} // namespace m
} // namespace ircd
inline
ircd::m::error::error(const http::code &c,
const json::obj &obj)
:http::error{c, std::string{obj}}
{}
inline
ircd::m::error::error(const http::code &c,
const json::doc &doc)
:http::error{c, std::string{doc}}
{}
template<class... args>
ircd::m::error::error(const string_view &errcode,
const char *const &fmt,
args&&... a)
:error
{
http::BAD_REQUEST, errcode, fmt, std::forward<args>(a)...
}{}
template<class... args>
ircd::m::error::error(const http::code &status,
const string_view &errcode,
const char *const &fmt,
args&&... a)
:http::error
{
status, [&]() -> std::string
{
char estr[256]; const auto estr_len
{
fmt::snprintf{estr, sizeof(estr), fmt, std::forward<args>(a)...}
};
return json::obj
{
{ "errcode", errcode },
{ "error", string_view(estr, estr_len) }
};
}()
}{}

89
include/ircd/m/event.h Normal file
View file

@ -0,0 +1,89 @@
/*
* 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.
*
*/
#include <ircd/json/object.h>
#pragma once
#define HAVE_IRCD_M_EVENT_H
namespace ircd {
namespace m {
struct event
:json::object
<
string_view,
time_t,
string_view,
string_view,
string_view,
string_view
>
{
IRCD_MEMBERS
(
"content",
"origin_server_ts",
"sender",
"type",
"unsigned",
"state_key"
)
template<class... A>
event(A&&... a)
:object{std::make_tuple(std::forward<A>(a)...)}
{}
};
struct sync
:json::object
<
string_view,
string_view,
string_view,
string_view
>
{
IRCD_MEMBERS
(
"account_data",
"next_batch",
"rooms",
"presence",
)
sync(const json::doc &doc)
{
}
template<class... A>
sync(A&&... a)
:object{std::make_tuple(std::forward<A>(a)...)}
{}
};
} // namespace m
} // namespace ircd

102
include/ircd/m/id.h Normal file
View file

@ -0,0 +1,102 @@
/*
* 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_ID_H
namespace ircd {
namespace m {
const size_t USER_ID_BUFSIZE = 256;
const size_t ACCESS_TOKEN_BUFSIZE = 256;
inline bool
username_valid(const string_view &username)
{
return true;
}
inline bool
mxid_valid(const string_view &mxid)
{
const auto userhost(split(mxid, ':'));
const auto &user(userhost.first);
const auto &host(userhost.second);
if(user.empty() || host.empty())
return false;
if(!startswith(user, '@') || user.size() == 1)
return false;
return true;
}
inline string_view
username_generate(char *const &buf,
const size_t &max)
{
const uint32_t num(rand() % 100000U);
const size_t len(snprintf(buf, max, "@guest%u", num));
return { buf, len };
}
inline string_view
access_token_generate(char *const &buf,
const size_t &max)
{
const int num[] { rand(), rand(), rand() };
const size_t len(snprintf(buf, max, "charybdis%d%d%d", num[0], num[1], num[2]));
return { buf, len };
}
struct id
{
string_view user;
string_view host;
template<size_t size>
id(string_view user, string_view host)
:user{std::move(user)}
,host{std::move(host)}
{}
id(const string_view &user, const string_view &host, char *const &buf, const size_t &max)
:user{user}
,host{host}
{
char gen_buf[USER_ID_BUFSIZE];
fmt::snprintf(buf, max, "%s%s:%s",
user.empty() || startswith(user, '@')? "" : "@",
!user.empty()? user : username_generate(gen_buf, sizeof(gen_buf)),
host);
}
template<size_t size>
id(const string_view &user, const string_view &host, char (&buf)[size])
:id{user, host, buf, size}
{}
};
} // namespace m
} // namespace ircd

75
include/ircd/m/request.h Normal file
View file

@ -0,0 +1,75 @@
/*
* 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_REQUEST_H
namespace ircd {
namespace m {
struct request
:json::obj
{
string_view method;
string_view path;
string_view query;
string_view access_token;
request(const string_view &method,
const string_view &path,
const string_view &query = {},
std::initializer_list<json::obj::member> body = {});
request(const string_view &method,
const string_view &path,
const string_view &query,
const json::doc &content);
};
} // namespace m
} // namespace ircd
inline
ircd::m::request::request(const string_view &method,
const string_view &path,
const string_view &query,
std::initializer_list<json::obj::member> body)
:json::obj{std::move(body)}
,method{method}
,path{path}
,query{query}
{
}
inline
ircd::m::request::request(const string_view &method,
const string_view &path,
const string_view &query,
const json::doc &content)
:json::obj{content}
,method{method}
,path{path}
,query{query}
{
}

44
include/ircd/m/session.h Normal file
View file

@ -0,0 +1,44 @@
/*
* 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_SESSION_H
namespace ircd {
namespace m {
struct session
:client
{
std::string access_token;
std::deque<std::string> tape;
std::multimap<string_view, string_view> resource;
json::doc operator()(parse::buffer &pb, request &);
session(const host_port &);
};
} // namespace m
} // namespace ircd

View file

@ -1,239 +0,0 @@
/*
* 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_MATRIX_H
namespace ircd {
namespace m {
struct error
:http::error
{
template<class... args> error(const http::code &, const string_view &errcode, const char *const &fmt, args&&...);
template<class... args> error(const string_view &errcode, const char *const &fmt, args&&...);
error(const http::code &, const json::doc &doc = {});
error(const http::code &, const json::obj &obj);
};
struct member
{
};
template<const char *const &name>
struct mem
:member
{
std::string value;
operator const std::string &() const { return value; }
mem(const json::obj &obj)
:value{obj[name]}
{}
mem(const json::doc &doc)
:value{doc[name]}
{}
mem() = default;
friend std::ostream &operator<<(std::ostream &s, const mem &m)
{
s << m.value;
return s;
}
};
struct session
{
static constexpr auto _user_id { "user_id" };
static constexpr auto _access_token { "access_token" };
static constexpr auto _home_server { "home_server" };
static constexpr auto _device_id { "device_id" };
mem<_user_id> user_id;
mem<_access_token> access_token;
mem<_home_server> home_server;
mem<_device_id> device_id;
session(const json::obj &obj)
:user_id{obj}
,access_token{obj}
,home_server{obj}
,device_id{obj}
{}
session() = default;
};
struct request
:json::obj
{
struct quote;
struct versions;
struct sync;
struct login;
string_view method;
string_view resource;
string_view access_token;
request(const string_view &method,
const string_view &resource,
std::initializer_list<json::obj::member> body = {})
:json::obj{std::move(body)}
,method{method}
,resource{resource}
{}
request(const string_view &method,
const string_view &resource,
const json::doc &content)
:json::obj{content}
,method{method}
,resource{resource}
{}
};
struct request::sync
:request
{
/*
bool full_state;
string_view since;
string_view filter;
string_view set_presence;
milliseconds timeout;
*/
sync(std::initializer_list<json::obj::member> body = {})
:request{"GET", "/_matrix/client/r0/sync", std::move(body)}
{}
};
struct request::login
:request
{
/*
string_view user;
string_view password;
*/
login(std::initializer_list<json::obj::member> body = {})
:request{"POST", "/_matrix/client/r0/login", std::move(body)}
{}
};
struct request::quote
:request
{
quote(const string_view &method,
const string_view &resource,
const json::doc &content)
:request{method, resource, content}
{}
};
struct client
:ircd::client
{
IRCD_EXCEPTION(ircd::error, error)
std::unique_ptr<session> sess;
// Synchronize server state
void sync(request::sync &r);
// Account login
session login(request::login &);
// Account registration
void reg(const string_view &user,
const string_view &pass,
const string_view &type = "m.login.dummy");
void quote(request::quote &);
client(const host_port &);
};
using doc_closure = std::function<void (const json::doc &)>;
using arr_closure = std::function<void (const json::arr &)>;
struct request::versions
:request
{
versions(client &, const doc_closure & = nullptr, std::initializer_list<json::obj::member> body = {});
};
} // namespace m
} // namespace ircd
inline
ircd::m::error::error(const http::code &c,
const json::obj &obj)
:http::error{c, std::string{obj}}
{}
inline
ircd::m::error::error(const http::code &c,
const json::doc &doc)
:http::error{c, std::string{doc}}
{}
template<class... args>
ircd::m::error::error(const string_view &errcode,
const char *const &fmt,
args&&... a)
:error
{
http::BAD_REQUEST, errcode, fmt, std::forward<args>(a)...
}{}
template<class... args>
ircd::m::error::error(const http::code &status,
const string_view &errcode,
const char *const &fmt,
args&&... a)
:http::error
{
status, [&]
{
char estr[256]; const auto estr_len
{
fmt::snprintf(estr, sizeof(estr), fmt, std::forward<args>(a)...)
};
return std::string
{
json::obj
{
{ "errcode", errcode },
{ "error", string_view(estr, estr_len) }
}
};
}()
}{}

View file

@ -182,4 +182,3 @@ struct line {};
#include "js.h"
#include "client.h"
#include "mods.h"
#include "matrix.h"

View file

@ -19,231 +19,50 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace ircd {
namespace m {
#include <ircd/m.h>
db::handle accounts;
} // namespace m
} // namespace ircd
ircd::m::client::client(const host_port &host_port)
:ircd::client{host_port}
ircd::m::session::session(const host_port &host_port)
:client{host_port}
{
}
ircd::m::request::versions::versions(client &client,
const doc_closure &closure,
std::initializer_list<json::obj::member> body)
:request
ircd::json::doc
ircd::m::session::operator()(parse::buffer &pb,
request &r)
{
"GET", "/_matrix/client/versions", std::move(body)
}
{
const auto handle([&](const http::code &code,
const json::doc &doc)
parse::capstan pc
{
switch(code)
{
case http::OK:
if(closure)
closure(doc);
break;
default:
throw m::error(code, doc);
}
});
char buf[1024];
parse::buffer pb{buf};
parse::capstan pc{pb, read_closure(client)};
const auto handler([&pc, &handle]
(const http::response::head &head)
{
const http::response::content content{pc, head};
const auto status(http::status(head.status));
const json::doc doc{content};
handle(status, doc);
});
pb, read_closure(*this)
};
http::request
{
host(remote_addr(client)), method, resource, std::string(*this), write_closure(client),
host(remote_addr(*this)),
r.method,
r.path,
r.query,
std::string(r),
write_closure(*this),
{
{ "Content-Type"s, "application/json"s }
}
};
http::code status;
json::doc doc;
http::response
{
pc, nullptr, handler
pc,
nullptr,
[&pc, &status, &doc](const http::response::head &head)
{
status = http::status(head.status);
doc = http::response::content{pc, head};
}
};
}
void
ircd::m::client::reg(const string_view &user,
const string_view &pass,
const string_view &type)
{
const json::obj auth
{
{ "type", type }
};
const json::obj obj
{
{ "password", pass },
{ "username", user },
{ "auth", &auth },
};
const auto handle([&](const http::code &code,
const json::doc &doc)
{
switch(code)
{
case http::OK:
std::cout << "OK!" << std::endl;
std::cout << doc << std::endl;
break;
default:
throw m::error(code, doc);
}
});
char buf[4096];
parse::buffer pb{buf};
parse::capstan pc{pb, read_closure(*this)};
const auto handler([&pc, &handle]
(const http::response::head &head)
{
const http::response::content content{pc, head};
const auto status(http::status(head.status));
const json::doc doc{content};
handle(status, doc);
});
http::request
{
host(remote_addr(*this)), "POST"s, "/_matrix/client/r0/register"s, std::string(obj), write_closure(*this),
{
{ "Content-Type"s, "application/json"s }
}
};
http::response
{
pc, nullptr, handler
};
}
ircd::m::session
ircd::m::client::login(request::login &r)
{
session ret;
const auto handle([&](const http::code &code,
const json::doc &doc)
{
switch(code)
{
case http::OK:
ret = json::obj{doc};
this->sess.reset(new session{doc});
break;
default:
throw m::error(code, doc);
}
});
char buf[4096];
parse::buffer pb{buf};
parse::capstan pc{pb, read_closure(*this)};
const auto handler([&pc, &handle]
(const http::response::head &head)
{
const http::response::content content{pc, head};
const auto status(http::status(head.status));
const json::doc doc{content};
handle(status, doc);
});
http::request
{
host(remote_addr(*this)), r.method, r.resource, std::string(r), write_closure(*this),
{
{ "Content-Type"s, "application/json"s }
}
};
http::response
{
pc, nullptr, handler
};
return ret;
}
void
ircd::m::client::sync(request::sync &r)
{
if(!sess)
throw error("No active session");
static const auto urlfmt
{
"%s?access_token=%s"
};
char url[1024]; const auto urllen
{
fmt::snprintf(url, sizeof(url), urlfmt, r.resource, string(sess->access_token))
};
http::request
{
host(remote_addr(*this)), r.method, url, {}, write_closure(*this),
{
{ "Content-Type"s, "application/json"s }
}
};
char buf[4096];
parse::buffer pb{buf};
parse::capstan pc{pb, read_closure(*this)};
http::response
{
pc, nullptr, [this, &pc](const auto &head)
{
http::response::content content
{
pc, head
};
switch(http::status(head.status))
{
case http::OK:
{
const json::obj d(content, true);
const json::arr a(d["account_data.events[0]"]);
std::cout << a << std::endl;
break;
}
default:
throw m::error(http::status(head.status), json::doc(content));
}
}
};
}
void
ircd::m::client::quote(request::quote &r)
{
if(status < 200 || status >= 300)
throw m::error(status, doc);
return doc;
}

View file

@ -19,6 +19,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <ircd/m.h>
namespace ircd {
IRCD_INIT_PRIORITY(STD_CONTAINER)

View file

@ -28,13 +28,35 @@ AM_LDFLAGS += \
# This puts the source in client/ but the installed
# library is client_X.so in the main modules dir.
client_moduledir=@moduledir@
client_client_versions_la_SOURCES = client/versions.cc
client_client_register_la_SOURCES = client/register.cc
client_client_login_la_SOURCES = client/login.cc
client_module_LTLIBRARIES = \
client/client_versions.la \
client/client_register.la \
client/client_login.la
client_client_versions_la_SOURCES = client/versions.cc
client_client_account_la_SOURCES = client/account.cc
client_client_register_la_SOURCES = client/register.cc
client_client_login_la_SOURCES = client/login.cc
client_client_logout_la_SOURCES = client/logout.cc
client_client_sync_la_SOURCES = client/sync.cc
client_client_room_la_SOURCES = client/room.cc
client_client_publicrooms_la_SOURCES = client/publicrooms.cc
client_client_createroom_la_SOURCES = client/createroom.cc
client_client_pushrules_la_SOURCES = client/pushrules.cc
client_client_user_la_SOURCES = client/user.cc
client_client_voip_turnserver_la_SOURCES = client/voip/turnserver.cc
client_module_LTLIBRARIES = \
client/client_versions.la \
client/client_account.la \
client/client_register.la \
client/client_login.la \
client/client_logout.la \
client/client_sync.la \
client/client_room.la \
client/client_publicrooms.la \
client/client_createroom.la \
client/client_pushrules.la \
client/client_user.la \
client/client_voip_turnserver.la
moduledir=@moduledir@
matrix_la_SOURCES = matrix.cc
root_la_SOURCES = root.cc
module_LTLIBRARIES = \
matrix.la \
root.la

153
modules/client/account.cc Normal file
View file

@ -0,0 +1,153 @@
/*
* 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 token_descriptor
{
"token",
"An index of access_token to user_id",
{
// readable key // readable value
typeid(string_view), typeid(string_view)
}
};
const database::descriptor account_registered_descriptor
{
"registered",
"A UNIX epoch timestamp sampled when the account was created.",
{
// readable key // binary value
typeid(string_view), typeid(time_t)
}
};
const database::description account_description
{
{ "default" },
token_descriptor,
account_registered_descriptor,
{ "access_token" },
{ "access_token.text" },
{ "password" },
{ "password.text" },
{ "password.hash" },
{ "password.hash.sha256" },
};
std::shared_ptr<database> account_database
{
std::make_shared<database>("account"s, ""s, account_description)
};
extern database *const account
{
account_database.get()
};
struct account
:resource
{
resource deactivate
{
"_matrix/client/r0/account/deactivate",
"Deactivate the user's account, removing all ability for the user to login again. (3.3.3)"
};
resource password
{
"_matrix/client/r0/account/password",
"Changes the password for an account on this homeserver. (3.3.4)"
};
using resource::resource;
}
account_resource
{
"_matrix/client/r0/account",
"Account management (3.3)"
};
resource::response
account_password(client &client, const resource::request &request)
try
{
const auto new_password
{
request.at("new_password")
};
const auto type
{
unquote(request.at("auth.type"))
};
const auto session
{
request["auth.session"]
};
if(!type.empty() && type != "m.login.token")
{
throw m::error
{
"M_UNSUPPORTED", "Login type is not supported."
};
}
//db::handle hand(account, "foo");
//database::snapshot snap(hand);
/*
account(db::MERGE, "jzk", std::string
{
json::obj
{{
"password",
{
{ "plaintext", new_password }
}
}}
});
*/
return resource::response
{
client
};
}
catch(const db::not_found &e)
{
throw m::error
{
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
};
}
resource::method post
{
account_resource.password, "POST", account_password
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/login' to handle requests"
};

30
modules/client/account.h Normal file
View file

@ -0,0 +1,30 @@
/*
* 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.
*/
ircd::import_shared<ircd::database> account_database
{
"client_account", "account_database"
};
extern ircd::database *const account
{
account_database.get()
};

View file

@ -0,0 +1,88 @@
/*
* 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;
import<database *> room_database
{
"client_room", "room"
};
extern database *const room
{
room_database
};
resource createroom
{
"_matrix/client/r0/createRoom",
"Create a new room with various configuration options. (7.1.1)"
};
resource::response
room_create(client &client, const resource::request &request)
try
{
const auto name
{
unquote(request["name"])
};
const auto visibility
{
unquote(request["visibility"])
};
std::string room_id {"!foo@bar.com"};
return resource::response
{
client, http::CREATED, json::obj
{
{ "room_id", room_id }
}
};
}
catch(const db::not_found &e)
{
throw m::error
{
http::CONFLICT, "M_ROOM_IN_USE", "The desired room name is in use."
};
throw m::error
{
http::CONFLICT, "M_ROOM_ALIAS_IN_USE", "An alias of the desired room is in use."
};
}
resource::method post
{
createroom, "POST", room_create,
{
post.REQUIRES_AUTH
}
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/createRoom' to handle requests"
};

View file

@ -19,75 +19,132 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
using namespace ircd;
#include "account.h"
mods::sym_ref<db::handle> accounts_ref
{
"client_register",
"accounts"
};
using namespace ircd;
resource login_resource
{
"/_matrix/client/r0/login",
"_matrix/client/r0/login",
"Authenticates the user by password, and issues an access token "
"they can use to authorize themself in subsequent requests. (3.2.2)"
};
const auto home_server
{
// "The hostname of the homeserver on which the account has been registered."
"cdc.z"
};
using object = db::object<account>;
template<class T = string_view> using value = db::value<T, account>;
resource::response
login(client &client, const resource::request &request)
try
login_password(client &client, const resource::request &request)
{
const auto user
{
// "The fully qualified user ID or just local part of the user ID, to log in."
unquote(request.at("user"))
};
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")
};
const auto type
{
unquote(request["type"])
};
if(!type.empty() && type != "m.login.password")
{
value<> password_text("password.text", user_id);
if(password_text != password)
throw m::error
{
"M_UNSUPPORTED", "Login type is not supported."
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
};
}
db::handle &accounts(accounts_ref);
accounts(user, [&password](const json::doc &user)
{
if(user["password"] != password)
throw db::not_found();
});
// "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 access_token(123456);
const auto home_server("cdc.z");
const auto device_id("ABCDEF");
// Generate access token
char access_token[m::ACCESS_TOKEN_BUFSIZE];
m::access_token_generate(access_token, sizeof(access_token));
// 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;
// Send response to user
return resource::response
{
client,
{
{ "user_id", user },
{ "user_id", user_id },
{ "access_token", access_token },
{ "home_server", home_server },
{ "device_id", device_id },
}
};
}
catch(const db::not_found &e)
resource::response
login_token(client &client, const resource::request &request)
{
throw m::error
const auto token
{
http::FORBIDDEN, "M_FORBIDDEN", "Access denied."
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 },
}
};
}
resource::response
login(client &client, const resource::request &request)
{
const auto type
{
// "Required. The login type being used. Currently only "m.login.password" is supported."
unquote(request.at("type"))
};
if(type == "m.login.password")
return login_password(client, request);
else if(type == "m.login.token")
return login_token(client, request);
else
throw m::error
{
"M_UNSUPPORTED", "Login type is not supported."
};
}
resource::method post

65
modules/client/logout.cc Normal file
View file

@ -0,0 +1,65 @@
/*
* 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.
*/
#include "account.h"
using namespace ircd;
using object = db::object<account>;
template<class T = string_view> using value = db::value<T, account>;
resource logout_resource
{
"_matrix/client/r0/logout",
"Invalidates an existing access token, so that it can no longer be used for "
"authorization. (3.2.3)"
};
resource::response
logout(client &client, const resource::request &request)
{
const auto &access_token(request.query.at("access_token"));
const auto it(resource::tokens.find(access_token));
if(unlikely(it == end(resource::tokens)))
throw http::error{http::INTERNAL_SERVER_ERROR};
resource::tokens.erase(it);
return resource::response
{
client, json::obj
{
{ }
}
};
}
resource::method post
{
logout_resource, "POST", logout,
{
post.REQUIRES_AUTH
}
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/logout' to handle requests"
};

View file

@ -0,0 +1,96 @@
/*
* 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.
*/
#include "room.h"
using namespace ircd;
resource publicrooms_resource
{
"_matrix/client/r0/publicRooms",
"Lists the public rooms on the server. "
"This API returns paginated responses. (7.5)"
};
resource::response
get_publicrooms(client &client, const resource::request &request)
{
json::doc test
{
R"({"content":"hello","origin_server_ts":12345,"sender":"me"})"
};
m::event ev
{
"some content", 0, "some sender", "some type", "some unsigned", "statie"
};
json::for_each(ev, []
(const auto &key, const auto &val)
{
std::cout << key << " => " << val << std::endl;
});
std::cout << std::endl;
json::rfor_each(ev, []
(const string_view &key, const auto &val)
{
std::cout << key << " => " << val << std::endl;
});
std::cout << std::endl;
json::until(ev, []
(const string_view &key, const auto &val)
{
std::cout << key << " => " << val << std::endl;
return true;
});
std::cout << std::endl;
json::runtil(ev, []
(const string_view &key, const auto &val)
{
std::cout << key << " => " << val << std::endl;
return true;
});
std::cout << std::endl;
std::cout << index(ev, "origin_server_ts") << std::endl;
std::cout << std::endl;
return resource::response
{
client, json::obj
{
{ }
}
};
}
resource::method post
{
publicrooms_resource, "GET", get_publicrooms
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/publicrooms' to manage Matrix rooms"
};

View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using namespace ircd;
resource pushrules
{
"_matrix/client/r0/pushrules", R"(
Retrieve all push rulesets for this user. Clients can "drill-down" on the rulesets by
suffixing a scope to this path e.g. /pushrules/global/. This will return a subset of this data
under the specified key e.g. the global key. (11.10.1.4.6)
)"
};
resource::response
get_pushrules(client &client, const resource::request &request)
try
{
return resource::response
{
client, json::obj
{
{ }
}
};
}
catch(...)
{
throw;
}
resource::method get
{
pushrules, "GET", get_pushrules
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/pushrules' to handle requests"
};

View file

@ -19,37 +19,145 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "account.h"
using namespace ircd;
db::handle accounts
using object = db::object<account>;
template<class T = string_view> using value = db::value<T, account>;
const auto home_server
{
"accounts"
// "The hostname of the homeserver on which the account has been registered."
"cdc.z"
};
resource::response
handle_post(client &client,
resource::request &request)
{
const auto access_token("abcdef");
const auto home_server("cdc.z");
const auto user_id("foo@bar");
const auto refresh_token(12345);
const auto kind
{
request.query["kind"]
};
if(kind.empty() || kind == "guest")
{
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::obj
{
{ "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"))
};
if(type != "m.login.dummy")
throw m::error
{
"M_UNSUPPORTED", "Registration '%s' not supported.", type
};
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"))
};
if(!username.empty() && !m::username_valid(username))
throw m::error
{
"M_INVALID_USERNAME", "The desired user ID is not a valid user name."
};
const auto pass
{
// "Required. The desired password for the account."
request.at("pass")
};
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)))
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, pass },
{ db::SET, access_token_text, access_token },
});
// Send response to user
return resource::response
{
client,
client, http::CREATED, json::obj
{
{ "access_token", access_token },
{ "home_server", home_server },
{ "user_id", user_id },
{ "refresh_token", refresh_token },
}
};
}
resource register_resource
{
"/_matrix/client/r0/register",
"_matrix/client/r0/register",
"Register for an account on this homeserver. (3.3.1)"
};

173
modules/client/room.cc Normal file
View file

@ -0,0 +1,173 @@
/*
* 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 room_created_descriptor
{
"created",
R"(### developer note:
A UNIX epoch timestamp sampled when the room was created.
)",
{
// readable key // binary value
typeid(string_view), typeid(time_t)
}
};
const database::descriptor room_creator_descriptor
{
"creator",
R"(### protocol note:
The user_id of the room creator. This is set by the homeserver.
)"
};
const database::descriptor room_topic_descriptor
{
"topic",
R"(### protocol note:
If this is included, an m.room.topic event will be sent into the room to indicate the
topic for the room. See Room Events for more information on m.room.topic.
)"
};
const database::descriptor room_visibility_descriptor
{
"visibility",
R"(### protocol note:
Rooms default to private visibility if this key is not included.
* "public" visibility indicates that the room will be shown in the published room list.
* "private" visibility will hide the room from the published room list.
One of: ["public", "private"]
)"
};
const database::descriptor room_join_rules_descriptor
{
"join_rules",
R"(### protocol note:
A room may be public meaning anyone can join the room without any prior action.
Alternatively, it can be invite meaning that a user who wishes to join the room must first
receive an invite to the room from someone already inside of the room. Currently, knock and private
are reserved keywords which are not implemented.
The type of rules used for users wishing to join this room.
One of: ["public", "knock", "invite", "private"]
)"
};
const database::descriptor room_history_visibility_descriptor
{
"history_visibility",
R"(### protocol note:
* "world_readable" All events while this is the m.room.history_visibility value may be shared
by any participating homeserver with anyone, regardless of whether they have ever joined the room.
* "shared" Previous events are always accessible to newly joined members. All events in the room
are accessible, even those sent when the member was not a part of the room.
* "invited" Events are accessible to newly joined members from the point they were invited onwards.
Events stop being accessible when the member's state changes to something other than invite or join.
* "joined" Events are accessible to newly joined members from the point they joined the room onwards.
Events stop being accessible when the member's state changes to something other than join.
)"
};
const database::descriptor room_alias_descriptor
{
"alias",
R"(### protocol note:
The desired room alias local part. If this is included, a room alias will be created and mapped to
the newly created room. The alias will belong on the same homeserver which created the room.
For example, if this was set to "foo" and sent to the homeserver "example.com" the complete
room alias would be #foo:example.com.
### developer note:
The alias column on the room's primary row has a comma separated list of aliases.
For each of those aliases this column has a key indexed for it; the value for that key is
the primary room's name. This is a cross-reference that must be kept in sync.
)"
};
const database::descriptor room_federate_descriptor
{
"federate",
R"(### protocol note:
Whether users on other servers can join this room. Defaults to true if key does not exist.
)",
{
// readable key // binary value
typeid(string_view), typeid(bool)
}
};
const database::description room_description
{
{ "default" },
room_created_descriptor,
room_creator_descriptor,
room_topic_descriptor,
room_visibility_descriptor,
room_join_rules_descriptor,
room_history_visibility_descriptor,
room_alias_descriptor,
room_federate_descriptor,
};
std::shared_ptr<database> room_database
{
std::make_shared<database>("room"s, ""s, room_description)
};
extern database *const room
{
room_database.get()
};
struct room
:resource
{
using resource::resource;
}
room_resource
{
"_matrix/client/r0/room",
"Rooms (7.0)"
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/room' to manage Matrix rooms"
};

30
modules/client/room.h Normal file
View file

@ -0,0 +1,30 @@
/*
* 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.
*/
ircd::import_shared<ircd::database> room_database
{
"client_room", "room_database"
};
extern ircd::database *const room
{
room_database.get()
};

97
modules/client/sync.cc Normal file
View file

@ -0,0 +1,97 @@
/*
* 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.
*/
#include "account.h"
using namespace ircd;
using object = db::object<account>;
template<class T = string_view> using value = db::value<T, account>;
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)"
};
resource::response
sync(client &client, const resource::request &request)
{
const auto filter
{
// 6.2.1 The ID of a filter created using the filter API or a filter JSON object
// encoded as a string. The server will detect whether it is an ID or a JSON object
// by whether the first character is a "{" open brace. Passing the JSON inline is best
// suited to one off requests. Creating a filter using the filter API is recommended
// for clients that reuse the same filter multiple times, for example in long poll requests.
request["filter"]
};
const auto since
{
// 6.2.1 A point in time to continue a sync from.
request["since"]
};
const auto full_state
{
// 6.2.1 Controls whether to include the full state for all rooms the user is a member of.
// If this is set to true, then all state events will be returned, even if since is non-empty.
// The timeline will still be limited by the since parameter. In this case, the timeout
// parameter will be ignored and the query will return immediately, possibly with an
// empty timeline. If false, and since is non-empty, only state which has changed since
// the point indicated by since will be returned. By default, this is false.
request.get<bool>("full_state", false)
};
const auto set_presence
{
// 6.2.1 Controls whether the client is automatically marked as online by polling this API.
// If this parameter is omitted then the client is automatically marked as online when it
// uses this API. Otherwise if the parameter is set to "offline" then the client is not
// marked as being online when it uses this API. One of: ["offline"]
request.get("set_presence", "offline")
};
const auto timeout
{
// 6.2.1 The maximum time to poll in milliseconds before returning this request.
request.get<time_t>("timeout", -1)
};
return {};
}
resource::method get_sync
{
sync_resource, "GET", sync,
{
get_sync.REQUIRES_AUTH
}
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/sync' to handle requests."
};

147
modules/client/user.cc Normal file
View file

@ -0,0 +1,147 @@
/*
* 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.
*/
#include "account.h"
using namespace ircd;
resource user_resource
{
"_matrix/client/r0/user",
"User resource",
{
resource::DIRECTORY
}
};
namespace ircd
{
static string_view extract_user_id(const auto &path, const auto &suffix);
}
ircd::string_view
ircd::extract_user_id(const auto &path,
const auto &suffix)
{
// Extract the user_id from _matrix/client/r0/user/$user/filter
return lstrip(between(path, user_resource.path, suffix), '/');
}
resource::response
get_filter(client &client, const resource::request &request)
try
{
const auto user_id
{
extract_user_id(request.head.path, "/filter")
};
return resource::response
{
client, json::obj
{
{ }
}
};
}
catch(...)
{
throw;
}
resource::method get
{
user_resource, "GET", get_filter
};
// (5.2) Uploads a new filter definition to the homeserver. Returns a filter ID that
// may be used in future requests to restrict which events are returned to the client.
resource::response
post_filter(client &client, const resource::request &request)
try
{
const auto user_id
{
// (5.2) Required. The id of the user uploading the filter. The access
// token must be authorized to make requests for this user id.
extract_user_id(request.head.path, "/filter")
};
const auto event_fields
{
// (5.2) List of event fields to include. If this list is absent then all fields are
// included. The entries may include '.' charaters to indicate sub-fields. So
// ['content.body'] will include the 'body' field of the 'content' object. A literal '.'
// character in a field name may be escaped using a '\'. A server may include more
// fields than were requested.
request["event_fields"]
};
const auto event_format
{
// (5.2) The format to use for events. 'client' will return the events in a format suitable
// for clients. 'federation' will return the raw event as receieved over federation.
// The default is 'client'. One of: ["client", "federation"]
request["event_format"]
};
const auto account_data
{
// (5.2) The user account data that isn't associated with rooms to include.
request["account_data"]
};
const auto room
{
// (5.2) Filters to be applied to room data.
request["room"]
};
const auto presence
{
// (5.2) The presence updates to include.
request["presence"]
};
std::cout << "do filter: " << request.content << std::endl;
return resource::response
{
client, http::CREATED, json::obj
{
{ "filter_id", "abc321" }
}
};
}
catch(...)
{
throw;
}
resource::method post
{
user_resource, "POST", post_filter
};
mapi::header IRCD_MODULE
{
"registers the resource 'client/user' to handle requests"
};

View file

@ -23,7 +23,7 @@ using namespace ircd;
resource versions_resource
{
"/_matrix/client/versions",
"_matrix/client/versions",
"Gets the versions of the specification supported by the server (2.1)"
};
@ -35,7 +35,7 @@ resource::method getter
{
static const json::doc object
{
R"({"versions":["r0.0.1"]})"
R"({"versions":["r2.0.0"]})"
};
return resource::response { client, object };

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using namespace ircd;
mapi::header IRCD_MODULE
{
"registers the resource 'client/voip/turnserver' to handle requests."
};
resource turnserver_resource
{
"_matrix/client/r0/voip/turnServer",
"This API provides credentials for the client to use when initiating calls."
"(11.3.3)"
};
resource::response
get_turnserver(client &client, const resource::request &request)
{
return resource::response
{
client, json::obj
{
{ },
}
};
}
resource::method turnserver_get
{
turnserver_resource, "GET", get_turnserver,
{
//get_turnserver.REQUIRES_AUTH
}
};

32
modules/ircd.js Normal file
View file

@ -0,0 +1,32 @@
/*
* 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.
*/
'use strict';
var ircd =
{
};
ircd.opts =
{
};

70
modules/matrix.cc Normal file
View file

@ -0,0 +1,70 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <ircd/listen.h>
using namespace ircd;
mapi::header IRCD_MODULE
{
"Chat Matrix Protocol"
};
std::map<std::string, module> modules;
//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_account.so"s, "client_account.so"s);
modules.emplace("client_room.so"s, "client_room.so"s);
for(const auto &name : mods::available())
if(name != "matrix.so")
modules.emplace(name, name);
root_module = &modules.at("root.so"s);
return true;
}());
listener matrices
{
std::string { json::obj
{
{ "name", "Chat Matrix" },
{ "host", "0.0.0.0" },
{
"ssl_certificate_chain_file", "/home/jason/.synapse/zemos.net.crt"
},
{
"ssl_tmp_dh_file", "/home/jason/.synapse/cdc.z.tls.dh"
},
{
"ssl_private_key_file_pem", "/home/jason/.synapse/cdc.z.tls.key"
},
{
"port", 6667
}
}}
};

95
modules/root.cc Normal file
View file

@ -0,0 +1,95 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <iterator>
using namespace ircd;
mapi::header IRCD_MODULE
{
"Chat Matrix Protocol"
};
IRCD_INIT_PRIORITY(STD_CONTAINER)
std::map<std::string, std::string, iless> files
{
};
__attribute__((constructor))
void
init_0()
{
for(const auto &file : fs::ls("/home/jason/charybdis/charybdis/modules/static"))
{
const auto name(token_last(file, "/"));
files.emplace(std::string(name), file);
}
}
resource::response
get_root(client &client, const resource::request &request)
{
auto it(files.find(request.head.path));
if(it == end(files))
throw http::error{http::NOT_FOUND};
const auto &filename(it->second);
std::ifstream file(filename);
std::noskipws(file);
const std::string content
{
std::istream_iterator<char>{file}, std::istream_iterator<char>{}
};
string_view content_type; switch(hash(rsplit(filename, '.').second))
{
case hash("css"): content_type = "text/css; charset=utf-8"; break;
case hash("js"): content_type = "application/javascript; charset=utf-8"; break;
case hash("html"): content_type = "text/html; charset=utf-8"; break;
case hash("ico"): content_type = "image/x-icon"; break;
case hash("svg"): content_type = "image/svg+xml"; break;
case hash("png"): content_type = "image/png"; break;
case hash("woff2"): content_type = "application/font-woff2"; break;
case hash("woff"): content_type = "application/font-woff"; break;
case hash("eot"): content_type = "application/vnd.ms-fontobject"; break;
case hash("otf"):
case hash("ttf"): content_type = "application/font-sfnt"; break;
default: content_type = "text/plain; charset=utf-8"; break;
}
return resource::response
{
client, string_view{content}, content_type
};
}
resource root_resource
{
"", "Root resource",
{
root_resource.DIRECTORY
}
};
resource::method root_get
{
root_resource, "GET", get_root
};