diff --git a/include/ircd/json/obj.h b/include/ircd/json/obj.h index 667b2077a..4b31a11c2 100644 --- a/include/ircd/json/obj.h +++ b/include/ircd/json/obj.h @@ -248,7 +248,7 @@ ircd::json::operator<(const obj::member &a, const string_view &b) inline bool ircd::json::operator!=(const obj::member &a, const string_view &b) { - return string_view(a.first.string, a.first.len) == b; + return string_view(a.first.string, a.first.len) != b; } inline bool diff --git a/include/ircd/matrix.h b/include/ircd/matrix.h index 50cb77e88..2847df0b5 100644 --- a/include/ircd/matrix.h +++ b/include/ircd/matrix.h @@ -29,11 +29,211 @@ namespace ircd { namespace m { struct error -:ircd::error +:http::error { - error(const json::doc &doc); - error(const json::obj &obj); + template error(const http::code &, const string_view &errcode, const char *const &fmt, args&&...); + template 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 +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 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 body = {}) + :request{"GET", "/_matrix/client/r0/sync", std::move(body)} + {} +}; + +struct request::login +:request +{ + /* + string_view user; + string_view password; + */ + + login(std::initializer_list 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 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; +using arr_closure = std::function; + +struct request::versions +:request +{ + versions(client &, const doc_closure & = nullptr, std::initializer_list 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 +ircd::m::error::error(const string_view &errcode, + const char *const &fmt, + args&&... a) +:error +{ + http::BAD_REQUEST, errcode, fmt, std::forward(a)... +}{} + +template +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(a)...) + }; + + return std::string + { + json::obj + { + { "errcode", errcode }, + { "error", string_view(estr, estr_len) } + } + }; + }() +}{} diff --git a/include/ircd/resource.h b/include/ircd/resource.h index d9b4918b8..8d7baa7dc 100644 --- a/include/ircd/resource.h +++ b/include/ircd/resource.h @@ -44,8 +44,10 @@ struct resource protected: decltype(resources)::const_iterator resources_it; + void call_method(client &, method &, resource::request &); + public: - void operator()(client &, parse::capstan &, const http::request::head &) const; + void operator()(client &, parse::capstan &, const http::request::head &); resource(const char *const &name, const char *const &description = ""); @@ -53,18 +55,25 @@ struct resource virtual ~resource() noexcept; }; -struct resource::response -{ - response(client &, const json::doc &doc, const http::code &code = http::OK); - response(client &, const json::obj &obj, const http::code &code = http::OK); - response() = default; - ~response() noexcept; -}; - struct resource::request +:json::doc { const http::request::head &head; http::request::content &content; + + request(const http::request::head &head, http::request::content &content) + :json::doc{content} + ,head{head} + ,content{content} + {} +}; + +struct resource::response +{ + response(client &, const json::doc &doc, const http::code & = http::OK); + response(client &, const json::obj &obj, const http::code & = http::OK); + response() = default; + ~response() noexcept; }; struct resource::method diff --git a/include/ircd/stdinc.h b/include/ircd/stdinc.h index 87a8fac70..428b7eef9 100644 --- a/include/ircd/stdinc.h +++ b/include/ircd/stdinc.h @@ -171,7 +171,6 @@ struct line {}; #include "rfc1459.h" #include "json.h" #include "http.h" -#include "matrix.h" #include "fmt.h" #include "path.h" #include "ctx.h" @@ -183,3 +182,4 @@ struct line {}; #include "js.h" #include "client.h" #include "mods.h" +#include "matrix.h" diff --git a/ircd/Makefile.am b/ircd/Makefile.am index ccf426e00..5c4436800 100644 --- a/ircd/Makefile.am +++ b/ircd/Makefile.am @@ -41,10 +41,11 @@ libircd_la_SOURCES = \ fmt.cc \ db.cc \ locale.cc \ - json.cc \ http.cc \ + json.cc \ matrix.cc \ - parse.cc + parse.cc \ + conf.cc if JS libircd_la_SOURCES += \ diff --git a/ircd/client.cc b/ircd/client.cc index e8f9f136e..77f0c2b45 100644 --- a/ircd/client.cc +++ b/ircd/client.cc @@ -181,6 +181,7 @@ ircd::read_closure(client &client) { try { + const char *const got(start); read(client, start, stop); } catch(const boost::system::system_error &e) @@ -317,7 +318,7 @@ try string(remote_addr(client)).c_str(), std::string(head.resource).c_str()); - const auto &resource(*resource::resources.at(head.resource)); + auto &resource(*resource::resources.at(head.resource)); resource(client, pc, head); } catch(const std::out_of_range &e) diff --git a/ircd/conf.cc b/ircd/conf.cc new file mode 100644 index 000000000..1ae5cfe0e --- /dev/null +++ b/ircd/conf.cc @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 Charybdis Development Team + * Copyright (C) 2017 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. + */ + +namespace ircd { + +} // namespace ircd diff --git a/ircd/matrix.cc b/ircd/matrix.cc index bbc251058..d795981b5 100644 --- a/ircd/matrix.cc +++ b/ircd/matrix.cc @@ -22,30 +22,193 @@ namespace ircd { namespace m { +db::handle accounts; + } // namespace m } // namespace ircd -void -ircd::test(const string_view &what) -try +ircd::m::client::client(const host_port &host_port) +:ircd::client{host_port} { - const auto tok(tokens(what, " ")); - const std::string method(tok.at(2)); - const std::string rcontent(tok.size() >= 5? tok.at(4) : string_view{}); +} - char url[256] {0}; - snprintf(url, sizeof(url), "/_matrix/client/%s", std::string(tok.at(3)).c_str()); - - const std::string host{tok.at(0)}; - const auto port(lex_cast(tok.at(1))); - ircd::client client +ircd::m::request::versions::versions(client &client, + const doc_closure &closure, + std::initializer_list body) +:request +{ + "GET", "/_matrix/client/versions", std::move(body) +} +{ + const auto handle([&](const http::code &code, + const json::doc &doc) { - { host, port } + 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); + }); + + http::request + { + host(remote_addr(client)), method, resource, std::string(*this), write_closure(client), + { + { "Content-Type"s, "application/json"s } + } + }; + + http::response + { + pc, nullptr, handler + }; +} + +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, method, url, rcontent, write_closure(client), + host(remote_addr(*this)), r.method, url, {}, write_closure(*this), { { "Content-Type"s, "application/json"s } } @@ -53,33 +216,34 @@ try char buf[4096]; parse::buffer pb{buf}; - parse::capstan pc{pb, read_closure(client)}; + parse::capstan pc{pb, read_closure(*this)}; http::response { - pc, nullptr, [&pc](const auto &head) + pc, nullptr, [this, &pc](const auto &head) { - http::response::content content{pc, head}; - const json::doc cdoc{content}; + http::response::content content + { + pc, head + }; - for(const auto &member : cdoc) - std::cout << member.first << " --> " << member.second << std::endl; + 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)); + } } }; } -catch(const std::exception &e) -{ - log::error("test: %s", e.what()); - throw; -} -ircd::m::error::error(const json::obj &obj) -:ircd::error{generate_skip} +void +ircd::m::client::quote(request::quote &r) { - serialize(obj, begin(buf), end(buf)); -} - -ircd::m::error::error(const json::doc &doc) -:ircd::error{generate_skip} -{ - print(buf, sizeof(buf), doc); } diff --git a/ircd/resource.cc b/ircd/resource.cc index 1737e25c8..f6ccb386e 100644 --- a/ircd/resource.cc +++ b/ircd/resource.cc @@ -56,20 +56,39 @@ void ircd::resource::operator()(client &client, parse::capstan &pc, const http::request::head &head) -const try +try { - const auto &method(*methods.at(head.method)); + auto &method(*methods.at(head.method)); http::request::content content{pc, head}; resource::request request { head, content }; - method(client, request); + call_method(client, method, request); } catch(const std::out_of_range &e) { - throw http::error(http::METHOD_NOT_ALLOWED); + throw http::error + { + http::METHOD_NOT_ALLOWED + }; +} + +void +ircd::resource::call_method(client &client, + method &method, + resource::request &request) +try +{ + method(client, request); +} +catch(const json::error &e) +{ + throw m::error + { + "M_BAD_JSON", "Required JSON field: %s", e.what() + }; } ircd::resource::method::method(struct resource &resource, @@ -99,11 +118,20 @@ noexcept } ircd::resource::response::response(client &client, - const json::obj &doc, + const json::obj &obj, const http::code &code) +try { - char cbuf[1024]; - response(client, serialize(doc, cbuf, cbuf + sizeof(cbuf)), code); + char cbuf[1024], *out(cbuf); + const auto doc(serialize(obj, out, cbuf + sizeof(cbuf))); + response(client, doc, code); +} +catch(const json::error &e) +{ + throw m::error + { + http::INTERNAL_SERVER_ERROR, "M_NOT_JSON", "Generator Protection: %s", e.what() + }; } ircd::resource::response::response(client &client,