0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-27 16:04:15 +01:00
construct/include/ircd/m/id.h

384 lines
11 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 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. The
// full license for this software is available in the LICENSE file.
#pragma once
#define HAVE_IRCD_M_ID_H
namespace ircd::m
{
struct id;
IRCD_M_EXCEPTION(error, INVALID_MXID, http::BAD_REQUEST)
IRCD_M_EXCEPTION(INVALID_MXID, BAD_SIGIL, http::BAD_REQUEST)
bool my(const id &);
bool is_sigil(const char &c) noexcept;
bool has_sigil(const string_view &) noexcept;
}
/// (Appendix 4.2) Common Identifier Format
///
/// The Matrix protocol uses a common format to assign unique identifiers to
/// a number of entities, including users, events and rooms. Each identifier
/// takes the form: `&localpart:domain` where & represents a 'sigil' character;
/// domain is the server name of the homeserver which allocated the identifier,
/// and localpart is an identifier allocated by that homeserver. The precise
/// grammar defining the allowable format of an identifier depends on the type
/// of identifier.
///
/// This structure is an 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
{
struct event;
struct user;
struct room;
struct room_alias;
struct group;
struct device;
enum sigil :char;
template<class T> struct buf;
struct input;
struct output;
struct parser;
struct printer;
struct valid;
struct parser static const parser;
struct printer static const printer;
struct valid static const valid;
static constexpr const size_t &MAX_SIZE
{
RB_MXID_MAXLEN // set by ./configure; should be 255
};
public:
// Extract elements
string_view local() const; // The full localpart including sigil
string_view host() const; // The full server part including port
string_view localname() const; // The localpart not including sigil
string_view hostname() const; // The server part not including port
uint16_t port() const; // Just the port number or 0 if none
bool literal() const; // Whether the hostname() is IP literal
// Rewrites the ID so the local and host parts are swapped; for indexing.
// e.g. `$foo:bar.com` becomes `bar.com$foo`; unswap() reverses to normal.
static id unswap(const string_view &, const mutable_buffer &);
static string_view swap(const id &, const mutable_buffer &);
string_view swap(const mutable_buffer &) const;
IRCD_USING_OVERLOAD(generate, m::generate);
explicit id(const enum sigil &, const id &) noexcept;
id(const enum sigil &, const mutable_buffer &, const generate_t &, const string_view &host);
id(const enum sigil &, const mutable_buffer &, const string_view &local, const string_view &host);
id(const enum sigil &, const mutable_buffer &, const string_view &id);
id(const enum sigil &, const string_view &id);
id(const string_view &id);
id() = default;
};
// Utilities
namespace ircd::m
{
// Check if any sigil
bool is_sigil(const char &c) noexcept;
bool has_sigil(const string_view &) noexcept;
// Interpret sigil type (or throw)
id::sigil sigil(const char &c);
id::sigil sigil(const string_view &id);
string_view reflect(const id::sigil &);
// Full ID checks
void validate(const id::sigil &, const string_view &); // valid() | throws
bool valid(const id::sigil &, const string_view &) noexcept;
bool valid_local(const id::sigil &, const string_view &) noexcept; // Local part is valid
bool valid_local_only(const id::sigil &, const string_view &) noexcept; // No :host
}
/// Singleton in m::id
struct ircd::m::id::valid
{
bool operator()(std::nothrow_t, const id::sigil &sigil, const string_view &id) const noexcept;
void operator()(const id::sigil &sigil, const string_view &id) const;
bool operator()(std::nothrow_t, const string_view &id) const noexcept;
void operator()(const string_view &id) const;
};
/// (4.2) The sigil characters
enum ircd::m::id::sigil
:char
{
USER = '@', ///< User ID (4.2.1)
EVENT = '$', ///< Event ID (4.2.2)
ROOM = '!', ///< Room ID (4.2.2)
ROOM_ALIAS = '#', ///< Room alias (4.2.3)
GROUP = '+', ///< Group ID (experimental)
DEVICE = '%', ///< Device ID (experimental)
NODE = ':', ///< Node ID (experimental)
};
/// (Appendix 4.2.1) User Identifiers
///
/// Users within Matrix are uniquely identified by their Matrix user ID. The
/// user ID is namespaced to the homeserver which allocated the account and
/// has the form: `@localpart:domain` The localpart of a user ID is an opaque
/// identifier for that user. It MUST NOT be empty, and MUST contain only the
/// characters a-z, 0-9, ., _, =, -, and /. The domain of a user ID is the
/// server name of the homeserver which allocated the account. The length of
/// a user ID, including the @ sigil and the domain, MUST NOT exceed 255
/// characters.
///
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;
};
/// (Appendix 4.2.2) Room IDs and Event IDs
///
/// An event has exactly one event ID. An event ID has the format:
/// `$opaque_id:domain` The domain of an event ID is the server name of the
/// homeserver which created the event. The domain is used only for namespacing
/// to avoid the risk of clashes of identifiers between different homeservers.
/// There is no implication that the event in question is still available at
/// the corresponding homeserver. Event IDs are case-sensitive. They are not
/// meant to be human readable.
///
struct ircd::m::id::event
:ircd::m::id
{
struct v3;
struct v4;
using buf = m::id::buf<event>;
using closure = std::function<void (const id::event &)>;
using closure_bool = std::function<bool (const id::event &)>;
string_view version() const; // static view
template<class... args>
event(args&&... a)
:m::id{EVENT, std::forward<args>(a)...}
{}
event() = default;
};
// Forward declaration for m::id::event::v3/v4 ctors which hash an m::event
// to compute its event_id. The m::id::event::v3/v4 objects could be placed
// elsewhere below event.h to avoid circularity, but it is probably more
// intuitive for developers to to find these objects filed here instead.
namespace ircd::m
{
struct event;
}
/// Version 3 event_id
///
/// This object is constructed on either a fully qualified existing event_id
/// to which it validates and maintains a string_view similar to other m::id
/// constructions, or the event data itself from which it creates a version 3
/// event_id into the buffer to maintain a string_view like the former.
struct ircd::m::id::event::v3
:ircd::m::id::event
{
static bool is(const string_view &) noexcept;
v3(const mutable_buffer &out, const m::event &);
v3(const string_view &id);
v3() = default;
};
/// Version 4 event_id
///
/// The version 4 event_id is similar to the version 3 event_id except for
/// being RFC4648 (base64 URL-safe) encoded. The v3 event_id is deprecated
/// by Matrix-spec. See v3 notes.
struct ircd::m::id::event::v4
:ircd::m::id::event
{
static bool is(const string_view &) noexcept;
v4(const mutable_buffer &out, const m::event &);
v4(const string_view &id);
v4() = default;
};
/// (Appendix 4.2.2) Room IDs and Event IDs
///
/// A room has exactly one room ID. A room ID has the format:
/// `!opaque_id:domain` The domain of a room ID is the server name of the
/// homeserver which created the room. The domain is used only for namespacing
/// to avoid the risk of clashes of identifiers between different homeservers.
/// There is no implication that the room in question is still available at
/// the corresponding homeserver. Room IDs are case-sensitive. They are not
/// meant to be human readable.
///
struct ircd::m::id::room
:ircd::m::id
{
using buf = m::id::buf<room>;
using closure = std::function<void (const id::room &)>;
using closure_bool = std::function<bool (const id::room &)>;
template<class... args>
room(args&&... a)
:m::id{ROOM, std::forward<args>(a)...}
{}
room() = default;
};
/// (Appendix 4.2.3) Room Aliases
/// A room may have zero or more aliases. A room alias has the format:
/// `#room_alias:domain` The domain of a room alias is the server name of the
/// homeserver which created the alias. Other servers may contact this
/// homeserver to look up the alias. Room aliases MUST NOT exceed 255 bytes
/// (including the # sigil and the domain).
///
struct ircd::m::id::room_alias
:ircd::m::id
{
using buf = m::id::buf<room_alias>;
template<class... args>
room_alias(args&&... a)
:m::id{ROOM_ALIAS, std::forward<args>(a)...}
{}
room_alias() = default;
};
/// Group ID (EXPERIMENTAL)
///
struct ircd::m::id::group
:ircd::m::id
{
using buf = m::id::buf<group>;
template<class... args>
group(args&&... a)
:m::id{GROUP, std::forward<args>(a)...}
{}
group() = default;
};
/// Device ID (EXPERIMENTAL)
///
struct ircd::m::id::device
:ircd::m::id
{
using buf = m::id::buf<device>;
template<class... args>
device(args&&... a)
:m::id{DEVICE, std::forward<args>(a)...}
{}
device() = default;
};
/// ID object backed by an internal buffer of default worst-case size.
///
template<class T>
struct ircd::m::id::buf
:T
{
static constexpr const size_t SIZE
{
m::id::MAX_SIZE + 1
};
private:
fixed_buffer<mutable_buffer, SIZE> b;
public:
operator const fixed_buffer<mutable_buffer, SIZE> &() const
{
return b;
}
operator mutable_buffer()
{
return b;
}
/// Due to the normal semantics of this object in relation to its parent,
/// if you write directly to this as a mutable_buffer you can call
/// assigned() to update this.
buf &assigned(const T &t)
{
assert(string_view{t}.data() == b.data());
static_cast<string_view &>(*this) = t;
return *this;
}
// Universal reference with perfect forwarding will be too greedy here by
// overriding some of the other semantics. const reference pack is fine.
template<class... args>
buf(const args &...a)
:T{b, a...}
{}
buf() = default;
buf(const buf &other)
:T{}
{
static_cast<string_view &>(*this) =
{
b.data(), buffer::copy(b, string_view{other})
};
}
buf(buf &&other) noexcept
:T{}
{
static_cast<string_view &>(*this) =
{
b.data(), buffer::copy(b, string_view{other})
};
}
buf &operator=(const buf &other)
{
this->~buf();
static_cast<string_view &>(*this) =
{
b.data(), buffer::copy(b, string_view{other})
};
return *this;
}
};
inline
ircd::m::id::id(const id::sigil &sigil,
const id &id)
noexcept
:string_view{id}
{
assert(this->empty() || this->front() == sigil);
}