Software / code / prosody
Comparison
plugins/mod_admin_shell.lua @ 11885:197642f9972f
mod_admin_shell: New table based implementation of c2s and s2s:show()
Nicer and more readable.
Thanks jonas’ and prosody@ for JID length stats to inform column widths.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Wed, 10 Nov 2021 17:59:35 +0100 |
| parent | 11850:bfa85965106e |
| child | 11886:b0b258e092da |
comparison
equal
deleted
inserted
replaced
| 11884:248477e45c64 | 11885:197642f9972f |
|---|---|
| 39 local t_insert = table.insert; | 39 local t_insert = table.insert; |
| 40 local t_concat = table.concat; | 40 local t_concat = table.concat; |
| 41 | 41 |
| 42 local format_number = require "util.human.units".format; | 42 local format_number = require "util.human.units".format; |
| 43 local format_table = require "util.human.io".table; | 43 local format_table = require "util.human.io".table; |
| 44 | |
| 45 local function capitalize(s) | |
| 46 return (s:gsub("^%a", string.upper):gsub("_", " ")); | |
| 47 end | |
| 44 | 48 |
| 45 local commands = module:shared("commands") | 49 local commands = module:shared("commands") |
| 46 local def_env = module:shared("env"); | 50 local def_env = module:shared("env"); |
| 47 local default_env_mt = { __index = def_env }; | 51 local default_env_mt = { __index = def_env }; |
| 48 | 52 |
| 203 print [[xmpp - Commands for sending XMPP stanzas]] | 207 print [[xmpp - Commands for sending XMPP stanzas]] |
| 204 print [[debug - Commands for debugging the server]] | 208 print [[debug - Commands for debugging the server]] |
| 205 print [[config - Reloading the configuration, etc.]] | 209 print [[config - Reloading the configuration, etc.]] |
| 206 print [[console - Help regarding the console itself]] | 210 print [[console - Help regarding the console itself]] |
| 207 elseif section == "c2s" then | 211 elseif section == "c2s" then |
| 208 print [[c2s:show(jid) - Show all client sessions with the specified JID (or all if no JID given)]] | 212 print [[c2s:show(jid, columns) - Show all client sessions with the specified JID (or all if no JID given)]] |
| 209 print [[c2s:show_insecure() - Show all unencrypted client connections]] | 213 print [[c2s:show_tls(jid) - Show TLS cipher info for encrypted sessions]] |
| 210 print [[c2s:show_secure() - Show all encrypted client connections]] | |
| 211 print [[c2s:show_tls() - Show TLS cipher info for encrypted sessions]] | |
| 212 print [[c2s:count() - Count sessions without listing them]] | 214 print [[c2s:count() - Count sessions without listing them]] |
| 213 print [[c2s:close(jid) - Close all sessions for the specified JID]] | 215 print [[c2s:close(jid) - Close all sessions for the specified JID]] |
| 214 print [[c2s:closeall() - Close all active c2s connections ]] | 216 print [[c2s:closeall() - Close all active c2s connections ]] |
| 215 elseif section == "s2s" then | 217 elseif section == "s2s" then |
| 216 print [[s2s:show(domain) - Show all s2s connections for the given domain (or all if no domain given)]] | 218 print [[s2s:show(domain, columns) - Show all s2s connections for the given domain (or all if no domain given)]] |
| 217 print [[s2s:show_tls(domain) - Show TLS cipher info for encrypted sessions]] | 219 print [[s2s:show_tls(domain) - Show TLS cipher info for encrypted sessions]] |
| 218 print [[s2s:close(from, to) - Close a connection from one domain to another]] | 220 print [[s2s:close(from, to) - Close a connection from one domain to another]] |
| 219 print [[s2s:closeall(host) - Close all the incoming/outgoing s2s sessions to specified host]] | 221 print [[s2s:closeall(host) - Close all the incoming/outgoing s2s sessions to specified host]] |
| 220 elseif section == "http" then | 222 elseif section == "http" then |
| 221 print [[http:list(hosts) - Show HTTP endpoints]] | 223 print [[http:list(hosts) - Show HTTP endpoints]] |
| 580 function def_env.config:reload() | 582 function def_env.config:reload() |
| 581 local ok, err = prosody.reload_config(); | 583 local ok, err = prosody.reload_config(); |
| 582 return ok, (ok and "Config reloaded (you may need to reload modules to take effect)") or tostring(err); | 584 return ok, (ok and "Config reloaded (you may need to reload modules to take effect)") or tostring(err); |
| 583 end | 585 end |
| 584 | 586 |
| 585 local function common_info(session, line) | |
| 586 if session.id then | |
| 587 line[#line+1] = "["..session.id.."]" | |
| 588 else | |
| 589 line[#line+1] = "["..session.type..(tostring(session):match("%x*$")).."]" | |
| 590 end | |
| 591 end | |
| 592 | |
| 593 local function session_flags(session, line) | |
| 594 line = line or {}; | |
| 595 common_info(session, line); | |
| 596 if session.type == "c2s" then | |
| 597 local status, priority = "unavailable", tostring(session.priority or "-"); | |
| 598 if session.presence then | |
| 599 status = session.presence:get_child_text("show") or "available"; | |
| 600 end | |
| 601 line[#line+1] = status.."("..priority..")"; | |
| 602 end | |
| 603 if session.cert_identity_status == "valid" then | |
| 604 line[#line+1] = "(authenticated)"; | |
| 605 end | |
| 606 if session.dialback_key then | |
| 607 line[#line+1] = "(dialback)"; | |
| 608 end | |
| 609 if session.external_auth then | |
| 610 line[#line+1] = "(SASL)"; | |
| 611 end | |
| 612 if session.secure then | |
| 613 line[#line+1] = "(encrypted)"; | |
| 614 end | |
| 615 if session.compressed then | |
| 616 line[#line+1] = "(compressed)"; | |
| 617 end | |
| 618 if session.smacks then | |
| 619 line[#line+1] = "(sm)"; | |
| 620 end | |
| 621 if session.state then | |
| 622 if type(session.csi_counter) == "number" then | |
| 623 line[#line+1] = string.format("(csi:%s queue #%d)", session.state, session.csi_counter); | |
| 624 else | |
| 625 line[#line+1] = string.format("(csi:%s)", session.state); | |
| 626 end | |
| 627 end | |
| 628 if session.ip and session.ip:match(":") then | |
| 629 line[#line+1] = "(IPv6)"; | |
| 630 end | |
| 631 if session.remote then | |
| 632 line[#line+1] = "(remote)"; | |
| 633 end | |
| 634 if session.incoming and session.outgoing then | |
| 635 line[#line+1] = "(bidi)"; | |
| 636 elseif session.is_bidi or session.bidi_session then | |
| 637 line[#line+1] = "(bidi)"; | |
| 638 end | |
| 639 if session.bosh_version then | |
| 640 line[#line+1] = "(bosh)"; | |
| 641 end | |
| 642 if session.websocket_request then | |
| 643 line[#line+1] = "(websocket)"; | |
| 644 end | |
| 645 return table.concat(line, " "); | |
| 646 end | |
| 647 | |
| 648 local function tls_info(session, line) | |
| 649 line = line or {}; | |
| 650 common_info(session, line); | |
| 651 if session.secure then | |
| 652 local sock = session.conn and session.conn.socket and session.conn:socket(); | |
| 653 if sock then | |
| 654 local info = sock.info and sock:info(); | |
| 655 if info then | |
| 656 line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); | |
| 657 else | |
| 658 -- TLS session might not be ready yet | |
| 659 line[#line+1] = "(cipher info unavailable)"; | |
| 660 end | |
| 661 if sock.getsniname then | |
| 662 local name = sock:getsniname(); | |
| 663 if name then | |
| 664 line[#line+1] = ("(SNI:%q)"):format(name); | |
| 665 end | |
| 666 end | |
| 667 if sock.getalpn then | |
| 668 local proto = sock:getalpn(); | |
| 669 if proto then | |
| 670 line[#line+1] = ("(ALPN:%q)"):format(proto); | |
| 671 end | |
| 672 end | |
| 673 end | |
| 674 else | |
| 675 line[#line+1] = "(insecure)"; | |
| 676 end | |
| 677 return table.concat(line, " "); | |
| 678 end | |
| 679 | |
| 680 def_env.c2s = {}; | 587 def_env.c2s = {}; |
| 681 | 588 |
| 682 local function get_jid(session) | 589 local function get_jid(session) |
| 683 if session.username then | 590 if session.username then |
| 684 return session.full_jid or jid_join(session.username, session.host, session.resource); | 591 return session.full_jid or jid_join(session.username, session.host, session.resource); |
| 698 c2s:append(array.collect(values(module:shared"/*/bosh/sessions"))); | 605 c2s:append(array.collect(values(module:shared"/*/bosh/sessions"))); |
| 699 c2s:unique(); | 606 c2s:unique(); |
| 700 return c2s; | 607 return c2s; |
| 701 end | 608 end |
| 702 | 609 |
| 610 local function _sort_by_jid(a, b) | |
| 611 if a.host == b.host then | |
| 612 if a.username == b.username then return (a.resource or "") > (b.resource or ""); end | |
| 613 return (a.username or "") > (b.username or ""); | |
| 614 end | |
| 615 return _sort_hosts(a.host or "", b.host or ""); | |
| 616 end | |
| 617 | |
| 703 local function show_c2s(callback) | 618 local function show_c2s(callback) |
| 704 get_c2s():sort(function(a, b) | 619 get_c2s():sort(_sort_by_jid):map(function (session) |
| 705 if a.host == b.host then | |
| 706 if a.username == b.username then | |
| 707 return (a.resource or "") > (b.resource or ""); | |
| 708 end | |
| 709 return (a.username or "") > (b.username or ""); | |
| 710 end | |
| 711 return _sort_hosts(a.host or "", b.host or ""); | |
| 712 end):map(function (session) | |
| 713 callback(get_jid(session), session) | 620 callback(get_jid(session), session) |
| 714 end); | 621 end); |
| 715 end | 622 end |
| 716 | 623 |
| 717 function def_env.c2s:count() | 624 function def_env.c2s:count() |
| 718 local c2s = get_c2s(); | 625 local c2s = get_c2s(); |
| 719 return true, "Total: ".. #c2s .." clients"; | 626 return true, "Total: ".. #c2s .." clients"; |
| 720 end | 627 end |
| 721 | 628 |
| 722 function def_env.c2s:show(match_jid, annotate) | 629 local function get_s2s_hosts(session) --> local,remote |
| 723 local print, count = self.session.print, 0; | 630 if session.direction == "outgoing" then |
| 724 annotate = annotate or session_flags; | 631 return session.host or session.from_host, session.to_host; |
| 725 local curr_host = false; | 632 elseif session.direction == "incoming" then |
| 726 show_c2s(function (jid, session) | 633 return session.host or session.to_host, session.from_host; |
| 727 if curr_host ~= session.host then | 634 end |
| 728 curr_host = session.host; | 635 end |
| 729 print(curr_host or "(not connected to any host yet)"); | 636 |
| 730 end | 637 local available_columns = { |
| 731 if (not match_jid) or jid:match(match_jid) then | 638 jid = { |
| 732 count = count + 1; | 639 title = "JID"; |
| 733 print(annotate(session, { " ", jid })); | 640 width = 32; |
| 734 end | 641 key = "full_jid"; |
| 735 end); | 642 mapper = function(full_jid, session) return full_jid or get_jid(session) end; |
| 736 return true, "Total: "..count.." clients"; | 643 }; |
| 737 end | 644 host = { |
| 738 | 645 title = "Host"; |
| 739 function def_env.c2s:show_insecure(match_jid) | 646 key = "host"; |
| 740 local print, count = self.session.print, 0; | 647 width = 22; |
| 741 show_c2s(function (jid, session) | 648 mapper = function(host, session) |
| 742 if ((not match_jid) or jid:match(match_jid)) and not session.secure then | 649 if host ~= "" then return host; end |
| 743 count = count + 1; | 650 return get_s2s_hosts(session) or "?"; |
| 744 print(jid); | 651 end; |
| 745 end | 652 }; |
| 746 end); | 653 remote = { |
| 747 return true, "Total: "..count.." insecure client connections"; | 654 title = "Remote"; |
| 748 end | 655 width = 22; |
| 749 | 656 mapper = function(_, session) |
| 750 function def_env.c2s:show_secure(match_jid) | 657 return select(2, get_s2s_hosts(session)); |
| 751 local print, count = self.session.print, 0; | 658 end; |
| 752 show_c2s(function (jid, session) | 659 }; |
| 753 if ((not match_jid) or jid:match(match_jid)) and session.secure then | 660 dir = { |
| 754 count = count + 1; | 661 title = "Dir"; |
| 755 print(jid); | 662 width = 3; |
| 756 end | 663 key = "direction"; |
| 757 end); | 664 mapper = function (dir) |
| 758 return true, "Total: "..count.." secure client connections"; | 665 if dir == "outgoing" then return "-->"; end |
| 666 if dir == "incoming" then return "<--"; end | |
| 667 return "" | |
| 668 end; | |
| 669 }; | |
| 670 id = { title = "Session ID"; width = 20; key = "id" }; | |
| 671 type = { title = "Type"; width = #"c2s_unauthed"; key = "type" }; | |
| 672 method = { | |
| 673 title = "Method"; | |
| 674 width = 10; | |
| 675 mapper = function(_, session) | |
| 676 if session.bosh_version then | |
| 677 return "BOSH"; | |
| 678 elseif session.websocket_request then | |
| 679 return "WebSocket"; | |
| 680 else | |
| 681 return "TCP"; | |
| 682 end | |
| 683 end; | |
| 684 }; | |
| 685 ipv = { | |
| 686 title = "IPv"; | |
| 687 width = 4; | |
| 688 key = "ip"; | |
| 689 mapper = function(ip) return ip:find(":") and "IPv6" or "IPv4"; end; | |
| 690 }; | |
| 691 ip = { title = "IP address"; width = 40; key = "ip" }; | |
| 692 status = { | |
| 693 title = "Status"; | |
| 694 width = 11; | |
| 695 key = "presence"; | |
| 696 mapper = function(p) | |
| 697 if not p or p == "" then return "unavailable"; end | |
| 698 return p:get_child_text("show") or "available"; | |
| 699 end; | |
| 700 }; | |
| 701 secure = { | |
| 702 title = "Security"; | |
| 703 key = "conn"; | |
| 704 width = 11; | |
| 705 mapper = function(conn, session) | |
| 706 if not session.secure then return "insecure"; end | |
| 707 if conn == "" or not conn:ssl() then return "secure" end | |
| 708 local sock = conn ~= "" and conn:socket(); | |
| 709 if not sock then return "unknown TLS"; end | |
| 710 local tls_info = sock.info and sock:info(); | |
| 711 return tls_info and tls_info.protocol or "unknown TLS"; | |
| 712 end; | |
| 713 }; | |
| 714 encryption = { | |
| 715 title = "Encryption"; | |
| 716 width = 30; | |
| 717 key = "conn"; | |
| 718 mapper = function(conn) | |
| 719 local sock = conn ~= "" and conn:socket(); | |
| 720 local info = sock and sock.info and sock:info(); | |
| 721 if info then return info.cipher end | |
| 722 return "" | |
| 723 end; | |
| 724 }; | |
| 725 cert = { | |
| 726 title = "Certificate"; | |
| 727 key = "cert_identity_status"; | |
| 728 mapper = function(cert_status, session) | |
| 729 if cert_status ~= "" then return capitalize(cert_status); end | |
| 730 if session.cert_chain_status == "Invalid" then | |
| 731 local cert_errors = set.new(session.cert_chain_errors[1]); | |
| 732 if cert_errors:contains("certificate has expired") then | |
| 733 return "Expired"; | |
| 734 elseif cert_errors:contains("self signed certificate") then | |
| 735 return "Self-signed"; | |
| 736 end | |
| 737 return "Untrusted"; | |
| 738 elseif session.cert_identity_status == "invalid" then | |
| 739 return "Mismatched"; | |
| 740 end | |
| 741 return "Not validated"; | |
| 742 end; | |
| 743 }; | |
| 744 sni = { | |
| 745 title = "SNI"; | |
| 746 width = 22; | |
| 747 mapper = function(_, session) | |
| 748 if not session.conn then return "" end | |
| 749 local sock = session.conn:socket(); | |
| 750 return sock and sock.getsniname and sock:getsniname() or ""; | |
| 751 end; | |
| 752 }; | |
| 753 alpn = { | |
| 754 title = "ALPN"; | |
| 755 width = 11; | |
| 756 mapper = function(_, session) | |
| 757 if not session.conn then return "" end | |
| 758 local sock = session.conn:socket(); | |
| 759 return sock and sock.getalpn and sock:getalpn() or ""; | |
| 760 end; | |
| 761 }; | |
| 762 smacks = { | |
| 763 title = "SM"; | |
| 764 key = "smacks"; | |
| 765 width = 11; | |
| 766 mapper = function(smacks_xmlns, session) | |
| 767 if smacks_xmlns == "" then return "no"; end | |
| 768 if session.hibernating then return "hibernating"; end | |
| 769 return "yes"; | |
| 770 end; | |
| 771 }; | |
| 772 smacks_queue = { | |
| 773 title = "SM Queue"; | |
| 774 key = "outgoing_stanza_queue"; | |
| 775 width = 8; | |
| 776 align = "right"; | |
| 777 mapper = function (queue) | |
| 778 return tostring(#queue); | |
| 779 end | |
| 780 }; | |
| 781 csi = { | |
| 782 title = "CSI State"; | |
| 783 key = "state"; | |
| 784 -- TODO include counter | |
| 785 }; | |
| 786 s2s_sasl = { | |
| 787 title = "SASL"; | |
| 788 key = "external_auth"; | |
| 789 width = 10; | |
| 790 mapper = capitalize | |
| 791 }; | |
| 792 dialback = { | |
| 793 title = "Dialback"; | |
| 794 key = "dialback_key"; | |
| 795 width = 13; | |
| 796 mapper = function (dialback_key, session) | |
| 797 if dialback_key == "" then | |
| 798 if session.type == "s2sin" or session.type == "s2sout" then | |
| 799 return "Not used"; | |
| 800 end | |
| 801 return "Not initiated"; | |
| 802 elseif session.type == "s2sin_unauthed" or session.type == "s2sout_unauthed" then | |
| 803 return "Initiated"; | |
| 804 else | |
| 805 return "Completed"; | |
| 806 end | |
| 807 end | |
| 808 }; | |
| 809 }; | |
| 810 | |
| 811 local function get_colspec(colspec, default) | |
| 812 local columns = {}; | |
| 813 for i, col in pairs(colspec or default) do | |
| 814 if type(col) == "string" then | |
| 815 columns[i] = available_columns[col] or { title = capitalize(col); width = 20; key = col }; | |
| 816 elseif type(col) ~= "table" then | |
| 817 return false, ("argument %d: expected string|table but got %s"):format(i, type(col)); | |
| 818 else | |
| 819 columns[i] = col; | |
| 820 end | |
| 821 end | |
| 822 | |
| 823 return columns; | |
| 824 end | |
| 825 | |
| 826 function def_env.c2s:show(match_jid, colspec) | |
| 827 local print = self.session.print; | |
| 828 local columns = get_colspec(colspec, { "id"; "jid"; "ipv"; "status"; "secure"; "smacks"; "csi" }); | |
| 829 local row = format_table(columns, 120); | |
| 830 | |
| 831 local function match(session) | |
| 832 local jid = get_jid(session) | |
| 833 return (not match_jid) or jid:match(match_jid) | |
| 834 end | |
| 835 | |
| 836 print(row()); | |
| 837 | |
| 838 for _, session in ipairs(get_c2s():filter(match):sort(_sort_by_jid)) do | |
| 839 print(row(session)); | |
| 840 end | |
| 841 return true; | |
| 759 end | 842 end |
| 760 | 843 |
| 761 function def_env.c2s:show_tls(match_jid) | 844 function def_env.c2s:show_tls(match_jid) |
| 762 return self:show(match_jid, tls_info); | 845 return self:show(match_jid, { "jid"; "id"; "secure"; "encryption" }); |
| 763 end | 846 end |
| 764 | 847 |
| 765 local function build_reason(text, condition) | 848 local function build_reason(text, condition) |
| 766 if text or condition then | 849 if text or condition then |
| 767 return { | 850 return { |
| 792 return true, "Total: "..count.." sessions closed"; | 875 return true, "Total: "..count.." sessions closed"; |
| 793 end | 876 end |
| 794 | 877 |
| 795 | 878 |
| 796 def_env.s2s = {}; | 879 def_env.s2s = {}; |
| 797 function def_env.s2s:show(match_jid, annotate) | 880 local function _sort_s2s(a, b) |
| 881 local a_local, a_remote = get_s2s_hosts(a); | |
| 882 local b_local, b_remote = get_s2s_hosts(b); | |
| 883 if (a_local or "") == (b_local or "") then return _sort_hosts(a_remote or "", b_remote or ""); end | |
| 884 return _sort_hosts(a_local or "", b_local or ""); | |
| 885 end | |
| 886 | |
| 887 function def_env.s2s:show(match_jid, colspec) | |
| 798 local print = self.session.print; | 888 local print = self.session.print; |
| 799 annotate = annotate or session_flags; | 889 local columns = get_colspec(colspec, { "id"; "host"; "dir"; "remote"; "ipv"; "secure"; "s2s_sasl"; "dialback" }); |
| 800 | 890 local row = format_table(columns, 132); |
| 801 local count_in, count_out = 0,0; | 891 |
| 802 local s2s_list = { }; | 892 local function match(session) |
| 803 | 893 local host, remote = get_s2s_hosts(session); |
| 804 local s2s_sessions = module:shared"/*/s2s/sessions"; | 894 return not match_jid or (host or ""):match(match_jid) or (remote or ""):match(match_jid); |
| 805 for _, session in pairs(s2s_sessions) do | 895 end |
| 806 local remotehost, localhost, direction; | 896 |
| 807 if session.direction == "outgoing" then | 897 local s2s_sessions = array(iterators.values(module:shared"/*/s2s/sessions")):filter(match):sort(_sort_s2s); |
| 808 direction = "->"; | 898 |
| 809 count_out = count_out + 1; | 899 print(row()); |
| 810 remotehost, localhost = session.to_host or "?", session.from_host or "?"; | 900 |
| 811 else | 901 for _, session in ipairs(s2s_sessions) do |
| 812 direction = "<-"; | 902 print(row(session)); |
| 813 count_in = count_in + 1; | 903 end |
| 814 remotehost, localhost = session.from_host or "?", session.to_host or "?"; | 904 return true; -- TODO counts |
| 815 end | |
| 816 local sess_lines = { l = localhost, r = remotehost, | |
| 817 annotate(session, { "", direction, remotehost or "?" })}; | |
| 818 | |
| 819 if (not match_jid) or remotehost:match(match_jid) or localhost:match(match_jid) then | |
| 820 table.insert(s2s_list, sess_lines); | |
| 821 -- luacheck: ignore 421/print | |
| 822 local print = function (s) table.insert(sess_lines, " "..s); end | |
| 823 if session.sendq then | |
| 824 print("There are "..#session.sendq.." queued outgoing stanzas for this connection"); | |
| 825 end | |
| 826 if session.type == "s2sout_unauthed" then | |
| 827 if session.notopen then | |
| 828 print("The <stream> has not yet been opened"); | |
| 829 elseif not session.dialback_key then | |
| 830 print("Dialback has not been initiated yet"); | |
| 831 elseif session.dialback_key then | |
| 832 print("Dialback has been requested, but no result received"); | |
| 833 end | |
| 834 end | |
| 835 if session.type == "s2sin_unauthed" then | |
| 836 print("Connection not yet authenticated"); | |
| 837 elseif session.type == "s2sin" then | |
| 838 for name in pairs(session.hosts) do | |
| 839 if name ~= session.from_host then | |
| 840 print("also hosts "..tostring(name)); | |
| 841 end | |
| 842 end | |
| 843 end | |
| 844 end | |
| 845 end | |
| 846 | |
| 847 -- Sort by local host, then remote host | |
| 848 table.sort(s2s_list, function(a,b) | |
| 849 if a.l == b.l then return _sort_hosts(a.r, b.r); end | |
| 850 return _sort_hosts(a.l, b.l); | |
| 851 end); | |
| 852 local lasthost; | |
| 853 for _, sess_lines in ipairs(s2s_list) do | |
| 854 if sess_lines.l ~= lasthost then print(sess_lines.l); lasthost=sess_lines.l end | |
| 855 for _, line in ipairs(sess_lines) do print(line); end | |
| 856 end | |
| 857 return true, "Total: "..count_out.." outgoing, "..count_in.." incoming connections"; | |
| 858 end | 905 end |
| 859 | 906 |
| 860 function def_env.s2s:show_tls(match_jid) | 907 function def_env.s2s:show_tls(match_jid) |
| 861 return self:show(match_jid, tls_info); | 908 return self:show(match_jid, { "id"; "host"; "dir"; "remote"; "secure"; "encryption"; "cert" }); |
| 862 end | 909 end |
| 863 | 910 |
| 864 local function print_subject(print, subject) | 911 local function print_subject(print, subject) |
| 865 for _, entry in ipairs(subject) do | 912 for _, entry in ipairs(subject) do |
| 866 print( | 913 print( |