construct/matrix/pretty.cc

841 lines
17 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2019 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.
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<string_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<bool>(auth[0]) && !std::get<std::exception_ptr>(auth[0])? ' ':
!std::get<bool>(auth[0]) && std::get<std::exception_ptr>(auth[0])? 'X': '?',
std::get<bool>(auth[1]) && !std::get<std::exception_ptr>(auth[1])? ' ':
!std::get<bool>(auth[1]) && std::get<std::exception_ptr>(auth[1])? 'X': '?',
std::get<bool>(auth[2]) && !std::get<std::exception_ptr>(auth[2])? ' ':
!std::get<bool>(auth[2]) && std::get<std::exception_ptr>(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;
}