mirror of
https://github.com/matrix-construct/construct
synced 2024-10-01 13:18:58 +02:00
m: Improve mxid class
This commit is contained in:
parent
892ad69f5b
commit
96d121c82a
2 changed files with 350 additions and 65 deletions
|
@ -25,78 +25,127 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#define HAVE_IRCD_M_ID_H
|
#define HAVE_IRCD_M_ID_H
|
||||||
|
|
||||||
namespace ircd {
|
namespace ircd::m
|
||||||
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;
|
IRCD_M_EXCEPTION(error, INVALID_MXID, http::BAD_REQUEST)
|
||||||
|
IRCD_M_EXCEPTION(INVALID_MXID, BAD_SIGIL, http::BAD_REQUEST)
|
||||||
|
|
||||||
|
IRCD_OVERLOAD(generate)
|
||||||
|
|
||||||
|
struct id;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
//
|
||||||
mxid_valid(const string_view &mxid)
|
// Interface to a string representing an mxid. The m::id itself is just a
|
||||||
|
// string_view over some existing data. m::id::buf is an m::id with an
|
||||||
|
// internal array providing the buffer.
|
||||||
|
//
|
||||||
|
struct ircd::m::id
|
||||||
|
:string_view
|
||||||
{
|
{
|
||||||
const auto userhost(split(mxid, ':'));
|
struct event;
|
||||||
const auto &user(userhost.first);
|
struct user;
|
||||||
const auto &host(userhost.second);
|
struct room;
|
||||||
if(user.empty() || host.empty())
|
struct alias;
|
||||||
return false;
|
template<class T, size_t SIZE = 256> struct buf;
|
||||||
|
|
||||||
if(!startswith(user, '@') || user.size() == 1)
|
enum sigil
|
||||||
return false;
|
{
|
||||||
|
EVENT = '$',
|
||||||
return true;
|
USER = '@',
|
||||||
|
ROOM = '!',
|
||||||
|
ALIAS = '#',
|
||||||
}
|
}
|
||||||
|
sigil;
|
||||||
|
|
||||||
inline string_view
|
// Checks
|
||||||
username_generate(char *const &buf,
|
bool valid() const; // Fully qualified mxid
|
||||||
const size_t &max)
|
bool valid_local() const; // Local part is valid (may or may not have host)
|
||||||
{
|
|
||||||
const uint32_t num(rand() % 100000U);
|
|
||||||
const size_t len(snprintf(buf, max, "@guest%u", num));
|
|
||||||
return { buf, len };
|
|
||||||
}
|
|
||||||
|
|
||||||
inline string_view
|
// Extract elements
|
||||||
access_token_generate(char *const &buf,
|
string_view local() const { return split(*this, ':').first; }
|
||||||
const size_t &max)
|
string_view host() const { return split(*this, ':').second; }
|
||||||
{
|
string_view name() const { return lstrip(local(), '@'); }
|
||||||
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
|
private:
|
||||||
{
|
static string_view generate_random_timebased(const enum sigil &, char *const &buf, const size_t &max);
|
||||||
string_view user;
|
static string_view generate_random_prefixed(const enum sigil &, const string_view &prefix, char *const &buf, const size_t &max);
|
||||||
string_view host;
|
|
||||||
|
|
||||||
template<size_t size>
|
public:
|
||||||
id(string_view user, string_view host)
|
IRCD_USING_OVERLOAD(generate, m::generate);
|
||||||
:user{std::move(user)}
|
|
||||||
,host{std::move(host)}
|
|
||||||
{}
|
|
||||||
|
|
||||||
id(const string_view &user, const string_view &host, char *const &buf, const size_t &max)
|
id() = default;
|
||||||
:user{user}
|
id(const string_view &id);
|
||||||
,host{host}
|
id(const enum sigil &, const string_view &id);
|
||||||
{
|
id(const enum sigil &, char *const &buf, const size_t &max, const string_view &id);
|
||||||
char gen_buf[USER_ID_BUFSIZE];
|
id(const enum sigil &, char *const &buf, const size_t &max, const string_view &name, const string_view &host);
|
||||||
fmt::snprintf(buf, max, "%s%s:%s",
|
id(const enum sigil &, char *const &buf, const size_t &max, const generate_t &, const string_view &host);
|
||||||
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::m
|
||||||
} // namespace ircd
|
{
|
||||||
|
bool valid_sigil(const char &c);
|
||||||
|
bool valid_sigil(const string_view &id);
|
||||||
|
enum id::sigil sigil(const char &c);
|
||||||
|
enum id::sigil sigil(const string_view &id);
|
||||||
|
const char *reflect(const enum id::sigil &);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ID object backed by an internal buffer. Useful for creating or composing
|
||||||
|
// a new ID.
|
||||||
|
//
|
||||||
|
template<class T,
|
||||||
|
size_t MAX = 256>
|
||||||
|
struct ircd::m::id::buf
|
||||||
|
:T
|
||||||
|
{
|
||||||
|
static constexpr const size_t &SIZE{MAX};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<char, SIZE> b;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<class... args>
|
||||||
|
buf(args&&... a)
|
||||||
|
:T{b.data(), b.size(), std::forward<args>(a)...}
|
||||||
|
{}
|
||||||
|
|
||||||
|
buf() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// convenience typedefs
|
||||||
|
//
|
||||||
|
|
||||||
|
struct ircd::m::id::event
|
||||||
|
:ircd::m::id
|
||||||
|
{
|
||||||
|
using buf = m::id::buf<event>;
|
||||||
|
template<class... args> event(args&&... a) :m::id{EVENT, std::forward<args>(a)...} {}
|
||||||
|
event() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ircd::m::id::user
|
||||||
|
:ircd::m::id
|
||||||
|
{
|
||||||
|
using buf = m::id::buf<user>;
|
||||||
|
template<class... args> user(args&&... a) :m::id{USER, std::forward<args>(a)...} {}
|
||||||
|
user() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ircd::m::id::room
|
||||||
|
:ircd::m::id
|
||||||
|
{
|
||||||
|
using buf = m::id::buf<room>;
|
||||||
|
template<class... args> room(args&&... a) :m::id{ROOM, std::forward<args>(a)...} {}
|
||||||
|
room() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ircd::m::id::alias
|
||||||
|
:ircd::m::id
|
||||||
|
{
|
||||||
|
using buf = m::id::buf<alias>;
|
||||||
|
template<class... args> alias(args&&... a) :m::id{ALIAS, std::forward<args>(a)...} {}
|
||||||
|
alias() = default;
|
||||||
|
};
|
||||||
|
|
236
ircd/matrix.cc
236
ircd/matrix.cc
|
@ -66,3 +66,239 @@ ircd::m::session::operator()(parse::buffer &pb,
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// m/id.h
|
||||||
|
//
|
||||||
|
|
||||||
|
ircd::m::id::id(const string_view &id)
|
||||||
|
:string_view{id}
|
||||||
|
,sigil{m::sigil(id)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::m::id::id(const enum sigil &sigil,
|
||||||
|
const string_view &id)
|
||||||
|
:string_view{id}
|
||||||
|
,sigil{sigil}
|
||||||
|
{
|
||||||
|
if(!valid())
|
||||||
|
throw INVALID_MXID("Not a valid '%s' mxid", reflect(sigil));
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::m::id::id(const enum sigil &sigil,
|
||||||
|
char *const &buf,
|
||||||
|
const size_t &max,
|
||||||
|
const string_view &id)
|
||||||
|
:string_view{buf}
|
||||||
|
,sigil{sigil}
|
||||||
|
{
|
||||||
|
strlcpy(buf, id, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::m::id::id(const enum sigil &sigil,
|
||||||
|
char *const &buf,
|
||||||
|
const size_t &max,
|
||||||
|
const string_view &name,
|
||||||
|
const string_view &host)
|
||||||
|
:string_view{[&]() -> string_view
|
||||||
|
{
|
||||||
|
if(!max)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
size_t len(0);
|
||||||
|
if(!startswith(name, sigil))
|
||||||
|
buf[len++] = char(sigil);
|
||||||
|
|
||||||
|
const auto has_sep
|
||||||
|
{
|
||||||
|
std::count(std::begin(name), std::end(name), ':')
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!has_sep && host.empty())
|
||||||
|
{
|
||||||
|
len += strlcpy(buf + len, name, max - len);
|
||||||
|
}
|
||||||
|
else if(!has_sep && !host.empty())
|
||||||
|
{
|
||||||
|
len += fmt::snprintf(buf + len, max - len, "%s:%s",
|
||||||
|
name,
|
||||||
|
host);
|
||||||
|
}
|
||||||
|
else if(has_sep == 1 && !host.empty() && !split(name, ':').second.empty())
|
||||||
|
{
|
||||||
|
len += strlcpy(buf + len, name, max - len);
|
||||||
|
}
|
||||||
|
else if(has_sep && !host.empty())
|
||||||
|
{
|
||||||
|
throw INVALID_MXID("Not a valid '%s' mxid", reflect(sigil));
|
||||||
|
}
|
||||||
|
|
||||||
|
return { buf, len };
|
||||||
|
}()}
|
||||||
|
,sigil{sigil}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::m::id::id(const enum sigil &sigil,
|
||||||
|
char *const &buf,
|
||||||
|
const size_t &max,
|
||||||
|
const generate_t &,
|
||||||
|
const string_view &host)
|
||||||
|
:string_view{[&]
|
||||||
|
{
|
||||||
|
char name[64]; switch(sigil)
|
||||||
|
{
|
||||||
|
case sigil::USER:
|
||||||
|
generate_random_prefixed(sigil::USER, "guest", name, sizeof(name));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case sigil::ALIAS:
|
||||||
|
generate_random_prefixed(sigil::ALIAS, "", name, sizeof(name));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
generate_random_timebased(sigil, name, sizeof(name));
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t len
|
||||||
|
{
|
||||||
|
fmt::snprintf(buf, max, "%s:%s",
|
||||||
|
name,
|
||||||
|
host)
|
||||||
|
};
|
||||||
|
|
||||||
|
return string_view { buf, len };
|
||||||
|
}()}
|
||||||
|
,sigil{sigil}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::m::id::valid()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
const auto parts(split(*this, ':'));
|
||||||
|
const auto &local(parts.first);
|
||||||
|
const auto &host(parts.second);
|
||||||
|
|
||||||
|
// this valid() requires a full canonical mxid with a host
|
||||||
|
if(host.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// local requires a sigil plus at least one character
|
||||||
|
if(local.size() < 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// local requires the correct sigil type
|
||||||
|
if(!startswith(local, sigil))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::m::id::valid_local()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
const auto parts(split(*this, ':'));
|
||||||
|
const auto &local(parts.first);
|
||||||
|
|
||||||
|
// local requires a sigil plus at least one character
|
||||||
|
if(local.size() < 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// local requires the correct sigil type
|
||||||
|
if(!startswith(local, sigil))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::string_view
|
||||||
|
ircd::m::id::generate_random_prefixed(const enum sigil &sigil,
|
||||||
|
const string_view &prefix,
|
||||||
|
char *const &buf,
|
||||||
|
const size_t &max)
|
||||||
|
{
|
||||||
|
const uint32_t num(rand::integer());
|
||||||
|
const size_t len(fmt::snprintf(buf, max, "%c%s%u", char(sigil), prefix, num));
|
||||||
|
return { buf, len };
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::string_view
|
||||||
|
ircd::m::id::generate_random_timebased(const enum sigil &sigil,
|
||||||
|
char *const &buf,
|
||||||
|
const size_t &max)
|
||||||
|
{
|
||||||
|
const auto utime(microtime());
|
||||||
|
const size_t len(snprintf(buf, max, "%c%zd%06d", char(sigil), utime.first, utime.second));
|
||||||
|
return { buf, len };
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ircd::m::id::sigil
|
||||||
|
ircd::m::sigil(const string_view &s)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return sigil(s.at(0));
|
||||||
|
}
|
||||||
|
catch(const std::out_of_range &e)
|
||||||
|
{
|
||||||
|
throw BAD_SIGIL("sigil undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ircd::m::id::sigil
|
||||||
|
ircd::m::sigil(const char &c)
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case '$': return id::EVENT;
|
||||||
|
case '@': return id::USER;
|
||||||
|
case '#': return id::ALIAS;
|
||||||
|
case '!': return id::ROOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw BAD_SIGIL("'%c' is not a valid sigil", c);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ircd::m::reflect(const enum id::sigil &c)
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case id::EVENT: return "EVENT";
|
||||||
|
case id::USER: return "USER";
|
||||||
|
case id::ALIAS: return "ALIAS";
|
||||||
|
case id::ROOM: return "ROOM";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "?????";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::m::valid_sigil(const string_view &s)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return valid_sigil(s.at(0));
|
||||||
|
}
|
||||||
|
catch(const std::out_of_range &e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::m::valid_sigil(const char &c)
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case id::EVENT:
|
||||||
|
case id::USER:
|
||||||
|
case id::ALIAS:
|
||||||
|
case id::ROOM:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue