// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2019 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. The // full license for this software is available in the LICENSE file. std::ostream & ircd::m::pretty_detailed(std::ostream &out, const event &event, const event::idx &event_idx) { const bool cached { event_idx && m::cached(event_idx) }; const auto cached_keys { event_idx? m::cached_keys(event_idx, m::event::keys::selection{}): m::event::keys::selection{m::event::keys::include{}} }; const bool full_json { event_idx && has(m::dbs::event_json, byte_view(event_idx)) }; out << pretty(event) << std::endl; if(event_idx) out << std::setw(16) << std::right << "SEQUENCE" << " " << event_idx << std::endl; if(event.source) { char pbuf[64]; out << std::setw(16) << std::right << "JSON SIZE" << " " << pretty(pbuf, iec(size(string_view{event.source}))) << std::endl; } if(cached || cached_keys.count()) { out << std::setw(16) << std::right << "CACHED" << " "; if(cached) out << " _json"; for(const auto &key : m::event::keys{cached_keys}) out << " " << key; out << std::endl; } if(m::room::auth::is_power_event(event)) out << std::setw(16) << std::right << "POWER EVENT" << " " << std::endl; const m::event::prev prev{event}; const m::event::auth auth{event}; if(auth.auth_events_count() || prev.prev_events_count()) out << std::setw(16) << std::right << "REFERENCES" << " " << (auth.auth_events_count() + prev.prev_events_count()) << std::endl; const m::event::refs &refs{event_idx}; if(refs.count()) out << std::setw(16) << std::right << "REFERENCED BY" << " " << refs.count() << std::endl; const m::event::horizon horizon{event.event_id}; if(horizon.count()) out << std::setw(16) << std::right << "HORIZONS" << " " << horizon.count() << std::endl; out << std::endl; for(size_t i(0); i < auth.auth_events_count(); ++i) { const m::event::id &id{auth.auth_event(i)}; const m::event::fetch event{std::nothrow, id}; if(!event.valid) { out << "x-> AUTH " << id << std::endl; continue; } out << "--> AUTH " << " " << std::setw(9) << std::right << event.event_idx << " " << pretty_oneline(event, false) << std::endl; } for(size_t i(0); i < prev.prev_events_count(); ++i) { const m::event::id &id{prev.prev_event(i)}; const m::event::fetch event{std::nothrow, id}; if(!event.valid) { out << "x-> PREV " << id << std::endl; continue; } out << "--> PREV " << " " << std::setw(9) << std::right << event.event_idx << " " << pretty_oneline(event, false) << std::endl; } horizon.for_each([&out] (const auto &, const auto &event_idx) { const m::event::fetch event { std::nothrow, event_idx }; out << "<-- HORIZON " << " " << std::setw(9) << std::right << event.event_idx << " "; if(event.valid) out << pretty_oneline(event, false); else out << "Not Found."; out << std::endl; return true; }); if(event_idx) out << std::setw(16) << std::left << "---" << " " << std::setw(9) << std::right << event_idx << " " << pretty_oneline(event, false) << std::endl; const auto refcnt(refs.count()); if(refcnt) { refs.for_each([&out] (const m::event::idx &idx, const auto &type) { const m::event::fetch event{idx}; out << "<-- " << std::setw(12) << std::left << trunc(reflect(type), 12) << " " << std::setw(9) << std::right << idx << " " << pretty_oneline(event, false) << std::endl; return true; }); } out << std::endl; if(event.source && !json::valid(event.source, std::nothrow)) out << std::setw(9) << std::left << "!!! ERROR" << " " << "JSON SOURCE INVALID" << std::endl; const m::event::conforms conforms { event }; if(!conforms.clean()) out << std::setw(9) << std::left << "!!! ERROR" << " " << conforms << std::endl; if(!verify_hash(event)) { char buf[512]; out << std::setw(9) << std::left << "!!! ERROR" << " " << "HASH MISMATCH :" << b64::encode_unpadded(buf, hash(event)) << std::endl; } { const auto &[authed, failmsg](m::room::auth::check_static(event)); if(!authed) out << std::setw(9) << std::left << "!!! ERROR" << " " << "STATICALLY UNAUTHORIZED :" << what(failmsg) << std::endl; } { const auto &[authed, failmsg](m::room::auth::check_relative(event)); if(!authed) out << std::setw(9) << std::left << "!!! ERROR" << " " << "RELATIVELY UNAUTHORIZED :" << what(failmsg) << std::endl; } { const auto &[authed, failmsg](m::room::auth::check_present(event)); if(!authed) out << std::setw(9) << std::left << "!!! ERROR" << " " << "PRESENTLY UNAUTHORIZED :" << what(failmsg) << std::endl; } try { if(!verify(event)) out << std::setw(9) << std::left << "!!! ERROR" << " " << "SIGNATURE FAILED" << std::endl; } catch(const std::exception &e) { out << std::setw(9) << std::left << "!!! ERROR" << " " << "SIGNATURE FAILED :" << e.what() << std::endl; } return out; } std::ostream & ircd::m::pretty_stateline(std::ostream &out, const event &event, const event::idx &event_idx) { const room room { json::get<"room_id"_>(event) }; const room::state &state { room }; const bool active { event_idx? state.has(event_idx): false }; const bool redacted { event_idx? bool(m::redacted(event_idx)): false }; const bool power { m::room::auth::is_power_event(event) }; const room::auth::passfail auth[] { event_idx? room::auth::check_static(event): room::auth::passfail{false, {}}, event_idx && m::exists(event.event_id)? room::auth::check_relative(event): room::auth::passfail{false, {}}, event_idx? room::auth::check_present(event): room::auth::passfail{false, {}}, }; char buf[32]; const string_view flags { fmt::sprintf { buf, "%c %c%c%c%c%c", active? '*' : ' ', power? '@' : ' ', redacted? 'R' : ' ', std::get(auth[0]) && !std::get(auth[0])? ' ': !std::get(auth[0]) && std::get(auth[0])? 'X': '?', std::get(auth[1]) && !std::get(auth[1])? ' ': !std::get(auth[1]) && std::get(auth[1])? 'X': '?', std::get(auth[2]) && !std::get(auth[2])? ' ': !std::get(auth[2]) && std::get(auth[2])? 'X': '?', } }; const auto &type { json::get<"type"_>(event) }; const auto &state_key { json::get<"state_key"_>(event) }; const auto &depth { json::get<"depth"_>(event) }; const json::string &content { type == "m.room.member"? m::membership(event): type == "m.room.history_visibility"? json::get<"content"_>(event).get("history_visibility"): type == "m.room.join_rules"? json::get<"content"_>(event).get("join_rule"): type == "m.room.name"? json::get<"content"_>(event).get("name"): type == "m.room.canonical_alias"? json::get<"content"_>(event).get("alias"): type == "m.room.avatar"? json::get<"content"_>(event).get("url"): json::string{} }; char smbuf[48]; if(event.event_id.version() == "1") { out << smalldate(smbuf, json::get<"origin_server_ts"_>(event) / 1000L) << std::right << " " << std::setw(9) << json::get<"depth"_>(event) << std::right << " [ " << std::setw(30) << type << std::left << " | " << std::setw(50) << trunc(state_key, 50) << std::left << "]" << flags << " " << std::setw(10) << event_idx << std::left << " " << std::setw(72) << string_view{event.event_id} << std::left << ' ' << std::left << trunc(content, 80) ; } else { out << std::left << smalldate(smbuf, json::get<"origin_server_ts"_>(event) / 1000L) << ' ' << string_view{event.event_id} << std::right << " " << std::setw(9) << json::get<"depth"_>(event) << std::right << " [ " << std::setw(50) << type << std::left << " | " << std::setw(60) << trunc(state_key, 60) << std::left << "]" << flags << " " << std::setw(10) << event_idx << ' ' << std::left << trunc(content, 80) ; } if(std::get<1>(auth[0])) out << ":" << trunc(what(std::get<1>(auth[0])), 72); out << std::endl; return out; } std::string ircd::m::pretty(const event &event) { std::string ret; std::stringstream s; pubsetbuf(s, ret, 4096); pretty(s, event); resizebuf(s, ret); return ret; } std::ostream & ircd::m::pretty(std::ostream &s, const event &event) { const auto out{[&s] (const string_view &key, auto&& val) { if(defined(json::value(val))) s << std::setw(16) << std::right << key << " :" << val << std::endl; }}; const string_view top_keys[] { "event_id", "room_id", "sender", "origin", "depth", "type", "state_key", "redacts", }; if(!json::get<"event_id"_>(event) && event.event_id) s << std::setw(16) << std::right << "(event_id)" << " :" << string_view{event.event_id} << std::endl; json::for_each(event, top_keys, out); if(json::get<"type"_>(event) == "m.room.member") s << std::setw(16) << std::right << "membership" << " :" << m::membership(event) << std::endl; const auto &ts{json::get<"origin_server_ts"_>(event)}; { char buf[128]; s << std::setw(16) << std::right << "origin_server_ts" << " :" << timef(buf, ts / 1000L, ircd::localtime) << " (" << ts << ")" << std::endl; } const json::object &contents{json::get<"content"_>(event)}; if(!contents.empty()) s << std::setw(16) << std::right << "content" << " :" << size(contents) << " keys; " << size(string_view{contents}) << " bytes." << std::endl; const auto &hashes{json::get<"hashes"_>(event)}; for(const auto &hash : hashes) { s << std::setw(16) << std::right << "[hash]" << " :" << hash.first << " " << json::string(hash.second) << std::endl; } const auto &signatures{json::get<"signatures"_>(event)}; for(const auto &signature : signatures) { s << std::setw(16) << std::right << "[signature]" << " :" << signature.first << " "; for(const auto &key : json::object{signature.second}) s << key.first << " "; s << std::endl; } const m::event::auth auth{event}; pretty(s, auth); const m::event::prev prev{event}; pretty(s, prev); if(!contents.empty()) for(const json::object::member &content : contents) s << std::setw(16) << std::right << "[content]" << " :" << std::setw(7) << std::left << reflect(json::type(content.second)) << " " << std::setw(5) << std::right << size(string_view{content.second}) << " bytes " << ':' << content.first << std::endl; return s; } std::string ircd::m::pretty_oneline(const event &event, const int &fmt) { std::string ret; std::stringstream s; pubsetbuf(s, ret, 4096); pretty_oneline(s, event, fmt); resizebuf(s, ret); return ret; } std::ostream & ircd::m::pretty_oneline(std::ostream &s, const event &event, const int &fmt) { if(defined(json::get<"room_id"_>(event))) s << json::get<"room_id"_>(event) << ' '; else s << "* "; char sdbuf[48]; if(event.event_id && event.event_id.version() != "1") s << event.event_id << ' '; else if(!event.event_id) try { s << m::event::id::v4{sdbuf, event} << ' '; } catch(const std::exception &e) { s << "$[" << e.what() << "] "; } if(json::get<"origin_server_ts"_>(event) != json::undefined_number) s << smalldate(sdbuf, json::get<"origin_server_ts"_>(event) / 1000L) << ' '; else s << "* "; if(json::get<"depth"_>(event) != json::undefined_number) s << json::get<"depth"_>(event) << ' '; else s << "* "; const m::event::auth auth(event); for(size_t i(0); i < auth.auth_events_count(); ++i) s << 'A'; const m::event::prev prev(event); for(size_t i(0); i < prev.prev_events_count(); ++i) s << 'P'; if(auth.auth_events_count() || prev.prev_events_count()) s << ' '; if(event.event_id && event.event_id.version() == "1") s << event.event_id << ' '; if(fmt >= 2) { const auto &hashes{json::get<"hashes"_>(event)}; s << "[ "; for(const auto &hash : hashes) s << hash.first << ' '; s << "] "; const auto &signatures{json::get<"signatures"_>(event)}; s << "[ "; for(const auto &signature : signatures) { s << signature.first << "[ "; for(const auto &key : json::object{signature.second}) s << key.first << ' '; s << "] "; } s << "] "; } if(defined(json::get<"type"_>(event))) s << json::get<"type"_>(event) << ' '; else s << "* "; const auto &state_key { json::get<"state_key"_>(event) }; if(defined(state_key) && empty(state_key)) s << "\"\"" << ' '; else if(defined(state_key)) s << state_key << ' '; else s << "*" << ' '; const string_view &membership { json::get<"type"_>(event) == "m.room.member"? m::membership(event): "*"_sv }; s << membership << ' '; if(defined(json::get<"redacts"_>(event))) s << json::get<"redacts"_>(event) << ' '; else s << "* "; if(defined(json::get<"origin"_>(event)) && defined(json::get<"sender"_>(event))) if(at<"origin"_>(event) != user::id(at<"sender"_>(event)).host()) s << ':' << json::get<"origin"_>(event) << ' '; if(defined(json::get<"sender"_>(event))) s << json::get<"sender"_>(event) << ' '; else s << "@*:* "; const json::object &contents { fmt >= 1? json::get<"content"_>(event): json::object{} }; if(!contents.empty()) { s << "+" << string_view{contents}.size() << " bytes :"; for(const auto &content : contents) s << content.first << ' '; } return s; } std::string ircd::m::pretty_msgline(const event &event, const pretty_opts &opts) { std::string ret; std::stringstream s; pubsetbuf(s, ret, 4096); pretty_msgline(s, event, opts); resizebuf(s, ret); return ret; } std::ostream & ircd::m::pretty_msgline(std::ostream &s, const event &event, const pretty_opts &opts) { if(opts.show_event_idx) s << opts.event_idx << ' '; if(opts.show_depth) s << json::get<"depth"_>(event) << ' '; if(opts.show_origin_server_ts) { char sdbuf[48]; if(json::get<"origin_server_ts"_>(event) != json::undefined_number) s << smalldate(sdbuf, json::get<"origin_server_ts"_>(event) / 1000L) << ' '; } if(opts.show_origin_server_ts_ago) { char buf[48]; const system_point p(milliseconds(json::get<"origin_server_ts"_>(event))); if(json::get<"origin_server_ts"_>(event) != json::undefined_number) s << ago(buf, p, 0x3) << ' '; } if(opts.show_event_id) s << event.event_id << ' '; if(opts.show_sender) s << json::get<"sender"_>(event) << ' '; if(opts.show_state_key) { const auto &state_key { json::get<"state_key"_>(event) }; if(defined(state_key) && empty(state_key)) s << "\"\"" << ' '; else if(defined(state_key)) s << state_key << ' '; else s << "*" << ' '; } const json::object &content { json::get<"content"_>(event) }; if(opts.show_content) switch(hash(json::get<"type"_>(event))) { case "m.room.message"_: { const room::message msg { content }; const auto &type { json::get<"msgtype"_>(msg) }; const auto &body { msg.body() }; if(opts.show_msgtype) s << type << ' '; if(opts.body_delim) s << opts.body_delim; s << msg.body(); break; } default: s << string_view{content}; break; } return s; } std::string ircd::m::pretty(const event::auth &auth) { std::string ret; std::stringstream s; pubsetbuf(s, ret, 4096); pretty(s, auth); resizebuf(s, ret); return ret; } std::ostream & ircd::m::pretty(std::ostream &s, const event::auth &auth) { for(size_t i(0); i < auth.auth_events_count(); ++i) { const auto &[event_id, ref_hash] { auth.auth_events(i) }; s << std::setw(16) << std::right << "[auth event]" << " :" << event_id; for(const auto &[algorithm, digest] : ref_hash) { s << ' ' << json::string(algorithm); if(digest) s << ": " << json::string(digest); } s << std::endl; } return s; } std::ostream & ircd::m::pretty_oneline(std::ostream &s, const event::auth &auth) { const auto &auth_events{json::get<"auth_events"_>(auth)}; s << "A[ "; for(const json::array auth_event : auth_events) s << json::string(auth_event[0]) << ' '; s << "] "; return s; } std::string ircd::m::pretty(const event::prev &prev) { std::string ret; std::stringstream s; pubsetbuf(s, ret, 4096); pretty(s, prev); resizebuf(s, ret); return ret; } std::ostream & ircd::m::pretty(std::ostream &s, const event::prev &prev) { for(size_t i(0); i < prev.prev_events_count(); ++i) { const auto &[event_id, ref_hash] { prev.prev_events(i) }; s << std::setw(16) << std::right << "[prev_event]" << " :" << event_id; for(const auto &[algorithm, digest] : ref_hash) { s << ' ' << json::string(algorithm); if(digest) s << ": " << json::string(digest); } s << std::endl; } return s; } std::ostream & ircd::m::pretty_oneline(std::ostream &s, const event::prev &prev) { const auto &prev_events{json::get<"prev_events"_>(prev)}; s << "E[ "; for(const json::array prev_event : prev_events) s << json::string(prev_event[0]) << ' '; s << "] "; return s; }