0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-28 23:08:20 +02:00

m: Improve mxid class

This commit is contained in:
Jason Volk 2017-09-07 04:07:58 -07:00
parent 892ad69f5b
commit 96d121c82a
2 changed files with 350 additions and 65 deletions

View file

@ -25,78 +25,127 @@
#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)
namespace ircd::m
{
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, ':'));
const auto &user(userhost.first);
const auto &host(userhost.second);
if(user.empty() || host.empty())
return false;
struct event;
struct user;
struct room;
struct alias;
template<class T, size_t SIZE = 256> struct buf;
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}
enum sigil
{
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);
EVENT = '$',
USER = '@',
ROOM = '!',
ALIAS = '#',
}
sigil;
template<size_t size>
id(const string_view &user, const string_view &host, char (&buf)[size])
:id{user, host, buf, size}
{}
// Checks
bool valid() const; // Fully qualified mxid
bool valid_local() const; // Local part is valid (may or may not have host)
// Extract elements
string_view local() const { return split(*this, ':').first; }
string_view host() const { return split(*this, ':').second; }
string_view name() const { return lstrip(local(), '@'); }
private:
static string_view generate_random_timebased(const enum sigil &, char *const &buf, const size_t &max);
static string_view generate_random_prefixed(const enum sigil &, const string_view &prefix, char *const &buf, const size_t &max);
public:
IRCD_USING_OVERLOAD(generate, m::generate);
id() = default;
id(const string_view &id);
id(const enum sigil &, const string_view &id);
id(const enum sigil &, char *const &buf, const size_t &max, const string_view &id);
id(const enum sigil &, char *const &buf, const size_t &max, const string_view &name, const string_view &host);
id(const enum sigil &, char *const &buf, const size_t &max, const generate_t &, const string_view &host);
};
} // namespace m
} // namespace ircd
namespace ircd::m
{
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;
};

View file

@ -66,3 +66,239 @@ ircd::m::session::operator()(parse::buffer &pb,
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;
}