/* * Copyright (C) 2016 Charybdis Development Team * Copyright (C) 2016 Jason Volk * * 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::m::session::session(const host_port &host_port) :client{host_port} { } ircd::json::object ircd::m::session::operator()(parse::buffer &pb, request &r) { parse::capstan pc { pb, read_closure(*this) }; http::request { 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::object object; http::response { pc, nullptr, [&pc, &status, &object](const http::response::head &head) { status = http::status(head.status); object = http::response::content{pc, head}; } }; if(status < 200 || status >= 300) throw m::error(status, 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; }