xmpp.chapril.org-ejabberd/src/mod_admin_extra.erl

1652 lines
62 KiB
Erlang
Raw Normal View History

%%%-------------------------------------------------------------------
%%% File : mod_admin_extra.erl
%%% Author : Badlop <badlop@process-one.net>
%%% Purpose : Contributed administrative functions and commands
%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
%%%
%%%
2020-01-28 13:34:02 +01:00
%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
2015-02-25 15:17:31 +01:00
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%-------------------------------------------------------------------
-module(mod_admin_extra).
-author('badlop@process-one.net').
-behaviour(gen_mod).
2015-02-10 17:09:03 +01:00
-include("logger.hrl").
-include("translate.hrl").
2015-02-10 17:09:03 +01:00
-export([start/2, stop/1, reload/3, mod_options/1,
get_commands_spec/0, depends/2, mod_doc/0]).
2016-11-30 10:31:36 +01:00
% Commands API
-export([
% Adminsys
compile/1, get_cookie/0,
2016-11-30 10:31:36 +01:00
restart_module/2,
% Sessions
num_resources/2, resource_num/3,
2015-06-01 14:38:27 +02:00
kick_session/4, status_num/2, status_num/1,
status_list/2, status_list/1, connected_users_info/0,
connected_users_vhost/1, set_presence/7,
get_presence/2, user_sessions_info/2, get_last/2, set_last/4,
2016-11-30 10:31:36 +01:00
% Accounts
set_password/3, check_password_hash/4, delete_old_users/1,
delete_old_users_vhost/2, ban_account/3, check_password/3,
% vCard
set_nickname/3, get_vcard/3,
2015-06-01 14:38:27 +02:00
get_vcard/4, get_vcard_multi/4, set_vcard/4,
2016-11-30 10:31:36 +01:00
set_vcard/5,
% Roster
add_rosteritem/7, delete_rosteritem/4,
get_roster/2, push_roster/3,
2016-11-30 10:31:36 +01:00
push_roster_all/1, push_alltoall/2,
2018-02-16 09:52:29 +01:00
push_roster_item/5, build_roster_item/3,
2016-11-30 10:31:36 +01:00
% Private storage
private_get/4, private_set/3,
% Shared roster
srg_create/5,
2015-06-01 14:38:27 +02:00
srg_delete/2, srg_list/1, srg_get_info/2,
srg_get_members/2, srg_user_add/4, srg_user_del/4,
2016-11-30 10:31:36 +01:00
% Send message
send_message/5, send_stanza/3, send_stanza_c2s/4,
% Privacy list
privacy_set/3,
% Stats
stats/1, stats/2
]).
-include("ejabberd_commands.hrl").
-include("mod_roster.hrl").
-include("mod_privacy.hrl").
-include("ejabberd_sm.hrl").
-include("xmpp.hrl").
%%%
%%% gen_mod
%%%
start(_Host, _Opts) ->
ejabberd_commands:register_commands(get_commands_spec()).
stop(Host) ->
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
false ->
ejabberd_commands:unregister_commands(get_commands_spec());
true ->
ok
end.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
depends(_Host, _Opts) ->
[].
%%%
%%% Register commands
%%%
get_commands_spec() ->
Vcard1FieldsString = "Some vcard field names in get/set_vcard are:\n"
" FN - Full Name\n"
" NICKNAME - Nickname\n"
" BDAY - Birthday\n"
" TITLE - Work: Position\n"
" ROLE - Work: Role",
2016-04-01 12:24:00 +02:00
Vcard2FieldsString = "Some vcard field names and subnames in get/set_vcard2 are:\n"
" N FAMILY - Family name\n"
" N GIVEN - Given name\n"
" N MIDDLE - Middle name\n"
" ADR CTRY - Address: Country\n"
" ADR LOCALITY - Address: City\n"
" TEL HOME - Telephone: Home\n"
" TEL CELL - Telephone: Cellphone\n"
" TEL WORK - Telephone: Work\n"
" TEL VOICE - Telephone: Voice\n"
" EMAIL USERID - E-Mail Address\n"
" ORG ORGNAME - Work: Company\n"
" ORG ORGUNIT - Work: Department",
VcardXEP = "For a full list of vCard fields check XEP-0054: vcard-temp at "
"http://www.xmpp.org/extensions/xep-0054.html",
[
#ejabberd_commands{name = compile, tags = [erlang],
desc = "Recompile and reload Erlang source code file",
module = ?MODULE, function = compile,
args = [{file, string}],
args_example = ["/home/me/srcs/ejabberd/mod_example.erl"],
args_desc = ["Filename of erlang source file to compile"],
result = {res, rescode},
result_example = ok,
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = get_cookie, tags = [erlang],
desc = "Get the Erlang cookie of this node",
module = ?MODULE, function = get_cookie,
args = [],
result = {cookie, string},
result_example = "MWTAVMODFELNLSMYXPPD",
result_desc = "Erlang cookie used for authentication by ejabberd"},
2016-11-30 10:31:36 +01:00
#ejabberd_commands{name = restart_module, tags = [erlang],
desc = "Stop an ejabberd module, reload code and start",
module = ?MODULE, function = restart_module,
args = [{host, binary}, {module, binary}],
args_example = ["myserver.com","mod_admin_extra"],
args_desc = ["Server name", "Module to restart"],
result = {res, integer},
result_example = 0,
result_desc = "Returns integer code:\n"
" - 0: code reloaded, module restarted\n"
" - 1: error: module not loaded\n"
" - 2: code not reloaded, but module restarted"},
#ejabberd_commands{name = delete_old_users, tags = [accounts, purge],
desc = "Delete users that didn't log in last days, or that never logged",
longdesc = "To protect admin accounts, configure this for example:\n"
"access_rules:\n"
" protect_old_users:\n"
" - allow: admin\n"
" - deny: all\n",
module = ?MODULE, function = delete_old_users,
args = [{days, integer}],
args_example = [30],
args_desc = ["Last login age in days of accounts that should be removed"],
result = {res, restuple},
result_example = {ok, <<"Deleted 2 users: [\"oldman@myserver.com\", \"test@myserver.com\"]">>},
result_desc = "Result tuple"},
#ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge],
desc = "Delete users that didn't log in last days in vhost, or that never logged",
longdesc = "To protect admin accounts, configure this for example:\n"
"access_rules:\n"
" delete_old_users:\n"
" - deny: admin\n"
" - allow: all\n",
module = ?MODULE, function = delete_old_users_vhost,
args = [{host, binary}, {days, integer}],
args_example = [<<"myserver.com">>, 30],
args_desc = ["Server name",
"Last login age in days of accounts that should be removed"],
result = {res, restuple},
result_example = {ok, <<"Deleted 2 users: [\"oldman@myserver.com\", \"test@myserver.com\"]">>},
result_desc = "Result tuple"},
#ejabberd_commands{name = check_account, tags = [accounts],
2016-04-01 12:24:00 +02:00
desc = "Check if an account exists or not",
2017-05-11 14:49:06 +02:00
module = ejabberd_auth, function = user_exists,
args = [{user, binary}, {host, binary}],
args_example = [<<"peter">>, <<"myserver.com">>],
args_desc = ["User name to check", "Server to check"],
result = {res, rescode},
result_example = ok,
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = check_password, tags = [accounts],
2016-04-01 12:24:00 +02:00
desc = "Check if a password is correct",
2016-04-10 15:37:36 +02:00
module = ?MODULE, function = check_password,
args = [{user, binary}, {host, binary}, {password, binary}],
args_example = [<<"peter">>, <<"myserver.com">>, <<"secret">>],
args_desc = ["User name to check", "Server to check", "Password to check"],
result = {res, rescode},
result_example = ok,
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = check_password_hash, tags = [accounts],
desc = "Check if the password hash is correct",
longdesc = "Allows hash methods from crypto application",
module = ?MODULE, function = check_password_hash,
2016-11-10 11:20:57 +01:00
args = [{user, binary}, {host, binary}, {passwordhash, binary},
{hashmethod, binary}],
args_example = [<<"peter">>, <<"myserver.com">>,
<<"5ebe2294ecd0e0f08eab7690d2a6ee69">>, <<"md5">>],
args_desc = ["User name to check", "Server to check",
"Password's hash value", "Name of hash method"],
result = {res, rescode},
result_example = ok,
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = change_password, tags = [accounts],
desc = "Change the password of an account",
2016-04-01 12:24:00 +02:00
module = ?MODULE, function = set_password,
args = [{user, binary}, {host, binary}, {newpass, binary}],
args_example = [<<"peter">>, <<"myserver.com">>, <<"blank">>],
args_desc = ["User name", "Server name",
"New password for user"],
result = {res, rescode},
result_example = ok,
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = ban_account, tags = [accounts],
2016-04-01 12:24:00 +02:00
desc = "Ban an account: kick sessions and set random password",
module = ?MODULE, function = ban_account,
args = [{user, binary}, {host, binary}, {reason, binary}],
args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
args_desc = ["User name to ban", "Server name",
"Reason for banning user"],
result = {res, rescode},
result_example = ok,
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = num_resources, tags = [session],
desc = "Get the number of resources of a user",
module = ?MODULE, function = num_resources,
args = [{user, binary}, {host, binary}],
args_example = [<<"peter">>, <<"myserver.com">>],
args_desc = ["User name", "Server name"],
result = {resources, integer},
result_example = 5,
result_desc = "Number of active resources for a user"},
#ejabberd_commands{name = resource_num, tags = [session],
desc = "Resource string of a session number",
module = ?MODULE, function = resource_num,
args = [{user, binary}, {host, binary}, {num, integer}],
args_example = [<<"peter">>, <<"myserver.com">>, 2],
args_desc = ["User name", "Server name", "ID of resource to return"],
result = {resource, string},
result_example = <<"Psi">>,
result_desc = "Name of user resource"},
#ejabberd_commands{name = kick_session, tags = [session],
desc = "Kick a user session",
module = ?MODULE, function = kick_session,
2014-04-02 04:17:08 +02:00
args = [{user, binary}, {host, binary}, {resource, binary}, {reason, binary}],
args_example = [<<"peter">>, <<"myserver.com">>, <<"Psi">>,
<<"Stuck connection">>],
args_desc = ["User name", "Server name", "User's resource",
"Reason for closing session"],
result = {res, rescode},
result_example = ok,
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = status_num_host, tags = [session, stats],
desc = "Number of logged users with this status in host",
2016-11-30 10:31:36 +01:00
policy = admin,
module = ?MODULE, function = status_num,
2014-04-02 04:17:08 +02:00
args = [{host, binary}, {status, binary}],
args_example = [<<"myserver.com">>, <<"dnd">>],
args_desc = ["Server name", "Status type to check"],
result = {users, integer},
result_example = 23,
result_desc = "Number of connected sessions with given status type"},
#ejabberd_commands{name = status_num, tags = [session, stats],
desc = "Number of logged users with this status",
2016-11-30 10:31:36 +01:00
policy = admin,
module = ?MODULE, function = status_num,
2014-04-02 04:17:08 +02:00
args = [{status, binary}],
args_example = [<<"dnd">>],
args_desc = ["Status type to check"],
result = {users, integer},
result_example = 23,
result_desc = "Number of connected sessions with given status type"},
#ejabberd_commands{name = status_list_host, tags = [session],
desc = "List of users logged in host with their statuses",
module = ?MODULE, function = status_list,
2014-04-02 04:17:08 +02:00
args = [{host, binary}, {status, binary}],
args_example = [<<"myserver.com">>, <<"dnd">>],
args_desc = ["Server name", "Status type to check"],
result_example = [{<<"peter">>,<<"myserver.com">>,<<"tka">>,6,<<"Busy">>}],
result = {users, {list,
2016-04-01 12:24:00 +02:00
{userstatus, {tuple, [
{user, string},
{host, string},
{resource, string},
{priority, integer},
{status, string}
]}}
}}},
#ejabberd_commands{name = status_list, tags = [session],
desc = "List of logged users with this status",
module = ?MODULE, function = status_list,
2014-04-02 04:17:08 +02:00
args = [{status, binary}],
args_example = [<<"dnd">>],
args_desc = ["Status type to check"],
result_example = [{<<"peter">>,<<"myserver.com">>,<<"tka">>,6,<<"Busy">>}],
result = {users, {list,
{userstatus, {tuple, [
{user, string},
{host, string},
{resource, string},
{priority, integer},
{status, string}
]}}
}}},
#ejabberd_commands{name = connected_users_info,
tags = [session],
2016-04-01 12:24:00 +02:00
desc = "List all established sessions and their information",
module = ?MODULE, function = connected_users_info,
args = [],
result_example = [{"user1@myserver.com/tka",
"c2s", "127.0.0.1", 42656,8, "ejabberd@localhost",
231, <<"dnd">>, <<"tka">>, <<>>}],
result = {connected_users_info,
{list,
{session, {tuple,
[{jid, string},
{connection, string},
{ip, string},
{port, integer},
{priority, integer},
{node, string},
{uptime, integer},
{status, string},
{resource, string},
{statustext, string}
]}}
}}},
#ejabberd_commands{name = connected_users_vhost,
2016-11-30 10:31:36 +01:00
tags = [session],
desc = "Get the list of established sessions in a vhost",
module = ?MODULE, function = connected_users_vhost,
args_example = [<<"myexample.com">>],
args_desc = ["Server name"],
result_example = [<<"user1@myserver.com/tka">>, <<"user2@localhost/tka">>],
2016-11-30 10:31:36 +01:00
args = [{host, binary}],
result = {connected_users_vhost, {list, {sessions, string}}}},
#ejabberd_commands{name = user_sessions_info,
tags = [session],
desc = "Get information about all sessions of a user",
module = ?MODULE, function = user_sessions_info,
args = [{user, binary}, {host, binary}],
args_example = [<<"peter">>, <<"myserver.com">>],
args_desc = ["User name", "Server name"],
result_example = [{"c2s", "127.0.0.1", 42656,8, "ejabberd@localhost",
231, <<"dnd">>, <<"tka">>, <<>>}],
result = {sessions_info,
{list,
{session, {tuple,
[{connection, string},
{ip, string},
{port, integer},
{priority, integer},
{node, string},
{uptime, integer},
{status, string},
{resource, string},
{statustext, string}
]}}
}}},
2016-04-01 12:24:00 +02:00
2016-11-30 10:31:36 +01:00
#ejabberd_commands{name = get_presence, tags = [session],
desc =
"Retrieve the resource with highest priority, "
"and its presence (show and status message) "
"for a given user.",
longdesc =
"The 'jid' value contains the user jid "
"with resource.\nThe 'show' value contains "
"the user presence flag. It can take "
"limited values:\n - available\n - chat "
"(Free for chat)\n - away\n - dnd (Do "
"not disturb)\n - xa (Not available, "
"extended away)\n - unavailable (Not "
"connected)\n\n'status' is a free text "
"defined by the user client.",
module = ?MODULE, function = get_presence,
args = [{user, binary}, {host, binary}],
args_rename = [{server, host}],
args_example = [<<"peter">>, <<"myexample.com">>],
args_desc = ["User name", "Server name"],
result_example = {<<"user1@myserver.com/tka">>, <<"dnd">>, <<"Busy">>},
2016-11-30 10:31:36 +01:00
result =
{presence,
{tuple,
[{jid, string}, {show, string},
{status, string}]}}},
#ejabberd_commands{name = set_presence,
tags = [session],
desc = "Set presence of a session",
module = ?MODULE, function = set_presence,
2015-03-26 20:41:16 +01:00
args = [{user, binary}, {host, binary},
{resource, binary}, {type, binary},
{show, binary}, {status, binary},
2016-04-01 12:24:00 +02:00
{priority, binary}],
args_example = [<<"user1">>,<<"myserver.com">>,<<"tka1">>,
<<"available">>,<<"away">>,<<"BB">>, <<"7">>],
args_desc = ["User name", "Server name", "Resource",
"Type: available, error, probe...",
"Show: away, chat, dnd, xa.", "Status text",
"Priority, provide this value as an integer"],
result = {res, rescode}},
#ejabberd_commands{name = set_nickname, tags = [vcard],
desc = "Set nickname in a user's vCard",
module = ?MODULE, function = set_nickname,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {nickname, binary}],
args_example = [<<"user1">>,<<"myserver.com">>,<<"User 1">>],
args_desc = ["User name", "Server name", "Nickname"],
result = {res, rescode}},
#ejabberd_commands{name = get_vcard, tags = [vcard],
desc = "Get content from a vCard field",
2016-04-01 12:24:00 +02:00
longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
module = ?MODULE, function = get_vcard,
args = [{user, binary}, {host, binary}, {name, binary}],
args_example = [<<"user1">>,<<"myserver.com">>,<<"NICKNAME">>],
args_desc = ["User name", "Server name", "Field name"],
result_example = "User 1",
result_desc = "Field content",
result = {content, string}},
#ejabberd_commands{name = get_vcard2, tags = [vcard],
desc = "Get content from a vCard subfield",
2016-04-01 12:24:00 +02:00
longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
module = ?MODULE, function = get_vcard,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}],
args_example = [<<"user1">>,<<"myserver.com">>,<<"N">>, <<"FAMILY">>],
args_desc = ["User name", "Server name", "Field name", "Subfield name"],
result_example = "Schubert",
result_desc = "Field content",
result = {content, string}},
#ejabberd_commands{name = get_vcard2_multi, tags = [vcard],
2015-04-01 15:44:49 +02:00
desc = "Get multiple contents from a vCard field",
2016-04-01 12:24:00 +02:00
longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
module = ?MODULE, function = get_vcard_multi,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}],
result = {contents, {list, {value, string}}}},
2016-04-01 12:24:00 +02:00
#ejabberd_commands{name = set_vcard, tags = [vcard],
desc = "Set content in a vCard field",
2016-04-01 12:24:00 +02:00
longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
module = ?MODULE, function = set_vcard,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {name, binary}, {content, binary}],
args_example = [<<"user1">>,<<"myserver.com">>, <<"URL">>, <<"www.example.com">>],
args_desc = ["User name", "Server name", "Field name", "Value"],
result = {res, rescode}},
#ejabberd_commands{name = set_vcard2, tags = [vcard],
desc = "Set content in a vCard subfield",
2016-04-01 12:24:00 +02:00
longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
module = ?MODULE, function = set_vcard,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {content, binary}],
args_example = [<<"user1">>,<<"myserver.com">>,<<"TEL">>, <<"NUMBER">>, <<"123456">>],
args_desc = ["User name", "Server name", "Field name", "Subfield name", "Value"],
result = {res, rescode}},
#ejabberd_commands{name = set_vcard2_multi, tags = [vcard],
desc = "Set multiple contents in a vCard subfield",
2016-04-01 12:24:00 +02:00
longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
module = ?MODULE, function = set_vcard,
args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {contents, {list, {value, binary}}}],
result = {res, rescode}},
#ejabberd_commands{name = add_rosteritem, tags = [roster],
desc = "Add an item to a user's roster (supports ODBC)",
longdesc = "Group can be several groups separated by ; for example: \"g1;g2;g3\"",
module = ?MODULE, function = add_rosteritem,
args = [{localuser, binary}, {localhost, binary},
{user, binary}, {host, binary},
{nick, binary}, {group, binary},
{subs, binary}],
args_rename = [{localserver, localhost}, {server, host}],
args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>,
<<"User 2">>, <<"Friends">>, <<"both">>],
args_desc = ["User name", "Server name", "Contact user name", "Contact server name",
"Nickname", "Group", "Subscription"],
result = {res, rescode}},
%%{"", "subs= none, from, to or both"},
%%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
%%{"", "will add mike@server.com to peter@localhost roster"},
#ejabberd_commands{name = delete_rosteritem, tags = [roster],
2016-04-01 12:24:00 +02:00
desc = "Delete an item from a user's roster (supports ODBC)",
module = ?MODULE, function = delete_rosteritem,
args = [{localuser, binary}, {localhost, binary},
{user, binary}, {host, binary}],
args_rename = [{localserver, localhost}, {server, host}],
args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>],
args_desc = ["User name", "Server name", "Contact user name", "Contact server name"],
result = {res, rescode}},
#ejabberd_commands{name = process_rosteritems, tags = [roster],
desc = "List/delete rosteritems that match filter",
longdesc = "Explanation of each argument:\n"
" - action: what to do with each rosteritem that "
"matches all the filtering options\n"
" - subs: subscription type\n"
" - asks: pending subscription\n"
" - users: the JIDs of the local user\n"
" - contacts: the JIDs of the contact in the roster\n"
"\n"
" *** Mnesia: \n"
"\n"
"Allowed values in the arguments:\n"
" ACTION = list | delete\n"
" SUBS = SUB[:SUB]* | any\n"
" SUB = none | from | to | both\n"
" ASKS = ASK[:ASK]* | any\n"
" ASK = none | out | in\n"
" USERS = JID[:JID]* | any\n"
" CONTACTS = JID[:JID]* | any\n"
" JID = characters valid in a JID, and can use the "
"globs: *, ?, ! and [...]\n"
"\n"
"This example will list roster items with subscription "
"'none', 'from' or 'to' that have any ask property, of "
"local users which JID is in the virtual host "
"'example.org' and that the contact JID is either a "
"bare server name (without user part) or that has a "
"user part and the server part contains the word 'icq'"
":\n list none:from:to any *@example.org *:*@*icq*"
"\n\n"
" *** SQL:\n"
"\n"
"Allowed values in the arguments:\n"
" ACTION = list | delete\n"
" SUBS = any | none | from | to | both\n"
" ASKS = any | none | out | in\n"
" USERS = JID\n"
" CONTACTS = JID\n"
" JID = characters valid in a JID, and can use the "
"globs: _ and %\n"
"\n"
"This example will list roster items with subscription "
"'to' that have any ask property, of "
"local users which JID is in the virtual host "
"'example.org' and that the contact JID's "
"server part contains the word 'icq'"
":\n list to any %@example.org %@%icq%",
module = mod_roster, function = process_rosteritems,
args = [{action, string}, {subs, string},
{asks, string}, {users, string},
{contacts, string}],
result = {response,
{list,
{pairs, {tuple,
[{user, string},
{contact, string}
]}}
}}},
#ejabberd_commands{name = get_roster, tags = [roster],
desc = "Get roster of a local user",
policy = user,
module = ?MODULE, function = get_roster,
args = [],
args_rename = [{server, host}],
result = {contacts, {list, {contact, {tuple, [
{jid, string},
{nick, string},
{subscription, string},
{ask, string},
{group, string}
]}}}}},
#ejabberd_commands{name = push_roster, tags = [roster],
desc = "Push template roster from file to a user",
longdesc = "The text file must contain an erlang term: a list "
"of tuples with username, servername, group and nick. Example:\n"
2018-11-23 13:21:17 +01:00
"[{<<\"user1\">>, <<\"localhost\">>, <<\"Workers\">>, <<\"User 1\">>},\n"
" {<<\"user2\">>, <<\"localhost\">>, <<\"Workers\">>, <<\"User 2\">>}].\n"
"When using UTF8 character encoding add /utf8 to certain string. Example:\n"
"[{<<\"user2\">>, <<\"localhost\">>, <<\"Workers\"/utf8>>, <<\"User 2\"/utf8>>}].",
module = ?MODULE, function = push_roster,
args = [{file, binary}, {user, binary}, {host, binary}],
args_example = [<<"/home/ejabberd/roster.txt">>, <<"user1">>, <<"localhost">>],
args_desc = ["File path", "User name", "Server name"],
result = {res, rescode}},
#ejabberd_commands{name = push_roster_all, tags = [roster],
2016-04-01 12:24:00 +02:00
desc = "Push template roster from file to all those users",
longdesc = "The text file must contain an erlang term: a list "
"of tuples with username, servername, group and nick. Example:\n"
"[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n"
" {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].",
module = ?MODULE, function = push_roster_all,
args = [{file, binary}],
args_example = [<<"/home/ejabberd/roster.txt">>],
args_desc = ["File path"],
result = {res, rescode}},
#ejabberd_commands{name = push_alltoall, tags = [roster],
2016-04-01 12:24:00 +02:00
desc = "Add all the users to all the users of Host in Group",
module = ?MODULE, function = push_alltoall,
args = [{host, binary}, {group, binary}],
args_example = [<<"myserver.com">>,<<"Everybody">>],
args_desc = ["Server name", "Group name"],
result = {res, rescode}},
2016-04-01 12:24:00 +02:00
#ejabberd_commands{name = get_last, tags = [last],
desc = "Get last activity information",
longdesc = "Timestamp is UTC and XEP-0082 format, for example: "
"2017-02-23T22:25:28.063062Z ONLINE",
2016-04-01 12:24:00 +02:00
module = ?MODULE, function = get_last,
args = [{user, binary}, {host, binary}],
args_example = [<<"user1">>,<<"myserver.com">>],
args_desc = ["User name", "Server name"],
result_example = {<<"2017-06-30T14:32:16.060684Z">>, "ONLINE"},
result_desc = "Last activity timestamp and status",
result = {last_activity,
{tuple, [{timestamp, string},
{status, string}
]}}},
2016-04-01 12:24:00 +02:00
#ejabberd_commands{name = set_last, tags = [last],
desc = "Set last activity information",
longdesc = "Timestamp is the seconds since "
2016-04-01 12:24:00 +02:00
"1970-01-01 00:00:00 UTC, for example: date +%s",
module = ?MODULE, function = set_last,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {timestamp, integer}, {status, binary}],
2017-07-14 17:17:26 +02:00
args_example = [<<"user1">>,<<"myserver.com">>, 1500045311, <<"GoSleeping">>],
args_desc = ["User name", "Server name", "Number of seconds since epoch", "Status message"],
2016-04-01 12:24:00 +02:00
result = {res, rescode}},
#ejabberd_commands{name = private_get, tags = [private],
desc = "Get some information from a user private storage",
module = ?MODULE, function = private_get,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {element, binary}, {ns, binary}],
args_example = [<<"user1">>,<<"myserver.com">>,<<"storage">>, <<"storage:rosternotes">>],
args_desc = ["User name", "Server name", "Element name", "Namespace"],
result = {res, string}},
#ejabberd_commands{name = private_set, tags = [private],
desc = "Set to the user private storage",
module = ?MODULE, function = private_set,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {element, binary}],
args_example = [<<"user1">>,<<"myserver.com">>,
<<"<storage xmlns='storage:rosternotes'/>">>],
args_desc = ["User name", "Server name", "XML storage element"],
result = {res, rescode}},
#ejabberd_commands{name = srg_create, tags = [shared_roster_group],
desc = "Create a Shared Roster Group",
longdesc = "If you want to specify several group "
"identifiers in the Display argument,\n"
"put \\ \" around the argument and\nseparate the "
"identifiers with \\ \\ n\n"
"For example:\n"
" ejabberdctl srg_create group3 myserver.com "
"name desc \\\"group1\\\\ngroup2\\\"",
module = ?MODULE, function = srg_create,
2014-03-03 12:31:17 +01:00
args = [{group, binary}, {host, binary},
2016-04-01 12:24:00 +02:00
{name, binary}, {description, binary}, {display, binary}],
args_example = [<<"group3">>, <<"myserver.com">>, <<"Group3">>,
<<"Third group">>, <<"group1\\\\ngroup2">>],
args_desc = ["Group identifier", "Group server name", "Group name",
"Group description", "Groups to display"],
result = {res, rescode}},
#ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
desc = "Delete a Shared Roster Group",
module = ?MODULE, function = srg_delete,
2014-03-03 12:31:17 +01:00
args = [{group, binary}, {host, binary}],
args_example = [<<"group3">>, <<"myserver.com">>],
args_desc = ["Group identifier", "Group server name"],
result = {res, rescode}},
#ejabberd_commands{name = srg_list, tags = [shared_roster_group],
desc = "List the Shared Roster Groups in Host",
module = ?MODULE, function = srg_list,
2014-03-03 12:31:17 +01:00
args = [{host, binary}],
args_example = [<<"myserver.com">>],
args_desc = ["Server name"],
result_example = [<<"group1">>, <<"group2">>],
result_desc = "List of group identifiers",
result = {groups, {list, {id, string}}}},
#ejabberd_commands{name = srg_get_info, tags = [shared_roster_group],
desc = "Get info of a Shared Roster Group",
module = ?MODULE, function = srg_get_info,
2014-03-03 12:31:17 +01:00
args = [{group, binary}, {host, binary}],
args_example = [<<"group3">>, <<"myserver.com">>],
args_desc = ["Group identifier", "Group server name"],
result_example = [{<<"name">>, "Group 3"}, {<<"displayed_groups">>, "group1"}],
result_desc = "List of group informations, as key and value",
2016-04-01 12:24:00 +02:00
result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
#ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
desc = "Get members of a Shared Roster Group",
module = ?MODULE, function = srg_get_members,
2014-03-03 12:31:17 +01:00
args = [{group, binary}, {host, binary}],
args_example = [<<"group3">>, <<"myserver.com">>],
args_desc = ["Group identifier", "Group server name"],
result_example = [<<"user1@localhost">>, <<"user2@localhost">>],
result_desc = "List of group identifiers",
result = {members, {list, {member, string}}}},
#ejabberd_commands{name = srg_user_add, tags = [shared_roster_group],
desc = "Add the JID user@host to the Shared Roster Group",
module = ?MODULE, function = srg_user_add,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}],
args_example = [<<"user1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
args_desc = ["Username", "User server name", "Group identifier", "Group server name"],
result = {res, rescode}},
#ejabberd_commands{name = srg_user_del, tags = [shared_roster_group],
2016-04-01 12:24:00 +02:00
desc = "Delete this JID user@host from the Shared Roster Group",
module = ?MODULE, function = srg_user_del,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}],
args_example = [<<"user1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
args_desc = ["Username", "User server name", "Group identifier", "Group server name"],
result = {res, rescode}},
2016-04-01 12:24:00 +02:00
#ejabberd_commands{name = get_offline_count,
tags = [offline],
desc = "Get the number of unread offline messages",
2016-11-30 10:31:36 +01:00
policy = user,
2016-04-01 12:24:00 +02:00
module = mod_offline, function = count_offline_messages,
args = [],
args_rename = [{server, host}],
result_example = 5,
result_desc = "Number",
result = {value, integer}},
#ejabberd_commands{name = send_message, tags = [stanza],
2016-04-01 12:24:00 +02:00
desc = "Send a message to a local or remote bare of full JID",
longdesc = "When sending a groupchat message to a MUC room, "
"FROM must be the full JID of a room occupant, "
"or the bare JID of a MUC service admin, "
"or the bare JID of a MUC/Sub subscribed user.",
module = ?MODULE, function = send_message,
args = [{type, binary}, {from, binary}, {to, binary},
2015-02-10 17:09:03 +01:00
{subject, binary}, {body, binary}],
args_example = [<<"headline">>, <<"admin@localhost">>, <<"user1@localhost">>,
<<"Restart">>, <<"In 5 minutes">>],
args_desc = ["Message type: normal, chat, headline, groupchat", "Sender JID",
2017-07-14 17:17:26 +02:00
"Receiver JID", "Subject, or empty string", "Body"],
2015-02-10 17:09:03 +01:00
result = {res, rescode}},
#ejabberd_commands{name = send_stanza_c2s, tags = [stanza],
desc = "Send a stanza as if sent from a c2s session",
module = ?MODULE, function = send_stanza_c2s,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {resource, binary}, {stanza, binary}],
args_example = [<<"admin">>, <<"myserver.com">>, <<"bot">>,
<<"<message to='user1@localhost'><ext attr='value'/></message>">>],
args_desc = ["Username", "Server name", "Resource", "Stanza"],
result = {res, rescode}},
#ejabberd_commands{name = send_stanza, tags = [stanza],
desc = "Send a stanza; provide From JID and valid To JID",
module = ?MODULE, function = send_stanza,
args = [{from, binary}, {to, binary}, {stanza, binary}],
args_example = [<<"admin@localhost">>, <<"user1@localhost">>,
<<"<message><ext attr='value'/></message>">>],
args_desc = ["Sender JID", "Destination JID", "Stanza"],
result = {res, rescode}},
#ejabberd_commands{name = privacy_set, tags = [stanza],
desc = "Send a IQ set privacy stanza for a local account",
module = ?MODULE, function = privacy_set,
2016-04-01 12:24:00 +02:00
args = [{user, binary}, {host, binary}, {xmlquery, binary}],
args_example = [<<"user1">>, <<"myserver.com">>,
<<"<query xmlns='jabber:iq:privacy'>...">>],
args_desc = ["Username", "Server name", "Query XML element"],
result = {res, rescode}},
#ejabberd_commands{name = stats, tags = [stats],
2016-01-14 15:09:03 +01:00
desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds processes",
2016-11-30 10:31:36 +01:00
policy = admin,
module = ?MODULE, function = stats,
args = [{name, binary}],
args_example = [<<"registeredusers">>],
args_desc = ["Statistic name"],
result_example = 6,
result_desc = "Integer statistic value",
result = {stat, integer}},
#ejabberd_commands{name = stats_host, tags = [stats],
2016-04-01 12:24:00 +02:00
desc = "Get statistical value for this host: registeredusers onlineusers",
2016-11-30 10:31:36 +01:00
policy = admin,
module = ?MODULE, function = stats,
args = [{name, binary}, {host, binary}],
args_example = [<<"registeredusers">>, <<"example.com">>],
2017-07-14 17:17:26 +02:00
args_desc = ["Statistic name", "Server JID"],
result_example = 6,
result_desc = "Integer statistic value",
2016-04-01 12:24:00 +02:00
result = {stat, integer}}
Commands refactor, first pass. - add API versionning - changed error handling, based on exception - commands moved/merged from mod_admin_p1 to mod_admin_extra - command bufixes - add some elixir unit test cases Squashed commit of the following: commit dd59855b3486f78a9349756e4f102e79b3accff8 Merge: 14e8ffc 506e08e Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 30 11:43:18 2015 +0100 Merge branch '3.2.x' into api commit 14e8ffce78cbea6c8605371d1fc50a0c1d1e012c Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 27 16:35:17 2015 +0100 Added OAuth tests to ejabberd_commands commit f81c550c14628edfe4861c228576cb767924366a Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 27 16:34:55 2015 +0100 Added some mod_http_api tests commit 6a64578d5b2ba532a2feb6503ed98561e56d5d53 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Oct 26 15:29:36 2015 +0100 Fix get_last command test Previous version won't work with dst. commit 27e0cde9e9c1f001effe68f8424a365ad947c068 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 17:59:34 2015 +0200 Add tests on admin command policy commit 19dad8d54f54c9fabd454280483cccfb06c8e78a Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 16:49:36 2015 +0200 Added command related tests (http api & user policy) commit e0e596ab4a3f3a70aba5f374f028939ab794de33 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 16:49:16 2015 +0200 Fix command call. commit 128cd7d1ede3c47a34f8ec3a750c980ccad2c61d Merge: 60c4c4c 447313c Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 22 14:48:39 2015 +0200 Merge branch '3.2.x' into api commit 60c4c4c0751302524c14219c6bc8c56a6069a689 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 22 14:45:57 2015 +0200 Fix ejabberd_commands spec. commit 8e145c28c5da762c2b93ee32327eff1db94ebfed Merge: 397273a f13dc94 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 18:26:07 2015 +0200 Merge branch '3.2.x' into api commit 397273a23ed415feac87aed33da6452229793387 Merge: c30e89b f289e27 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 15:27:45 2015 +0200 Merge branch '3.2.x' into api commit c30e89bb8a0013bff37e61e4c6953350c9c1f313 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 12:47:02 2015 +0200 Merge mod_http_api commit 7b0db22b4acd48ff6fabce41c1b2525e6580a3c5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 16 11:55:48 2015 +0200 Fix exunit tests to run with common_test suites commit d8b1a89800ac7379a57a7eb4a09c3c93c3e1e5eb Merge: 2879ae8 63455b3 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 15 11:39:45 2015 +0200 Merge branch '3.2.x' into api commit 2879ae87ff3eee369ef3d780136b96ecff5285d1 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 14 14:53:44 2015 +0200 Fix update_roster command. commit a1d453dd7a3afda9861a8d747494a45057ad574b Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 13 16:14:28 2015 +0200 API commands refactor Moving and/or merging commands from mod_admin_p1 to mod_admin_extra commit b709ed26b0fc0ca4f3bdd5a59fa58ec7e3db97fa Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 7 15:10:01 2015 +0200 Add tests on commands commit 6711687bee9c672cb3d5aed0744e13420ecf6dbd Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 29 15:58:16 2015 +0200 Add ejabberd_commands tests commit df8682f419cf3877e77e36a19bca0fc55dc991f8 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Sep 28 14:54:39 2015 +0200 Added API versioning for ejabberdctl and rest commands commit cd017b0e3aac431bc3ee807ceb7f8641e1523ef5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Sep 18 11:21:45 2015 +0200 Better error handling of HTTP API commands. commit ca5cb6acd8e4643f9d6c484d2277b0d7e88471e5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 15 15:03:05 2015 +0200 add commands to mod_admin_extra: - get_offline_count - get_presence - change_password commit 7f583fa099e30ac2b0915669fd8f102ac565b833 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 15 15:02:16 2015 +0200 Improve REST API error handling commit 14753b1c02cdce434a786b7f80f6c09f0d210075 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Sep 14 10:51:17 2015 +0200 Change REST API return codes for integer type.
2016-03-31 13:53:31 +02:00
].
2016-04-01 12:24:00 +02:00
%%%
%%% Adminsys
%%%
compile(File) ->
Ebin = filename:join(code:lib_dir(ejabberd), "ebin"),
case ext_mod:compile_erlang_file(Ebin, File) of
{ok, Module} ->
code:purge(Module),
code:load_file(Module),
ok;
_ ->
error
end.
get_cookie() ->
atom_to_list(erlang:get_cookie()).
2016-11-30 10:31:36 +01:00
restart_module(Host, Module) when is_binary(Module) ->
restart_module(Host, misc:binary_to_atom(Module));
2016-11-30 10:31:36 +01:00
restart_module(Host, Module) when is_atom(Module) ->
case gen_mod:is_loaded(Host, Module) of
false ->
2016-11-30 10:31:36 +01:00
% not a running module, force code reload anyway
code:purge(Module),
code:delete(Module),
code:load_file(Module),
1;
true ->
2016-11-30 10:31:36 +01:00
gen_mod:stop_module(Host, Module),
case code:soft_purge(Module) of
true ->
code:delete(Module),
code:load_file(Module),
gen_mod:start_module(Host, Module),
2016-11-30 10:31:36 +01:00
0;
false ->
gen_mod:start_module(Host, Module),
2016-11-30 10:31:36 +01:00
2
end
end.
Commands refactor, first pass. - add API versionning - changed error handling, based on exception - commands moved/merged from mod_admin_p1 to mod_admin_extra - command bufixes - add some elixir unit test cases Squashed commit of the following: commit dd59855b3486f78a9349756e4f102e79b3accff8 Merge: 14e8ffc 506e08e Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 30 11:43:18 2015 +0100 Merge branch '3.2.x' into api commit 14e8ffce78cbea6c8605371d1fc50a0c1d1e012c Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 27 16:35:17 2015 +0100 Added OAuth tests to ejabberd_commands commit f81c550c14628edfe4861c228576cb767924366a Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 27 16:34:55 2015 +0100 Added some mod_http_api tests commit 6a64578d5b2ba532a2feb6503ed98561e56d5d53 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Oct 26 15:29:36 2015 +0100 Fix get_last command test Previous version won't work with dst. commit 27e0cde9e9c1f001effe68f8424a365ad947c068 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 17:59:34 2015 +0200 Add tests on admin command policy commit 19dad8d54f54c9fabd454280483cccfb06c8e78a Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 16:49:36 2015 +0200 Added command related tests (http api & user policy) commit e0e596ab4a3f3a70aba5f374f028939ab794de33 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 16:49:16 2015 +0200 Fix command call. commit 128cd7d1ede3c47a34f8ec3a750c980ccad2c61d Merge: 60c4c4c 447313c Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 22 14:48:39 2015 +0200 Merge branch '3.2.x' into api commit 60c4c4c0751302524c14219c6bc8c56a6069a689 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 22 14:45:57 2015 +0200 Fix ejabberd_commands spec. commit 8e145c28c5da762c2b93ee32327eff1db94ebfed Merge: 397273a f13dc94 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 18:26:07 2015 +0200 Merge branch '3.2.x' into api commit 397273a23ed415feac87aed33da6452229793387 Merge: c30e89b f289e27 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 15:27:45 2015 +0200 Merge branch '3.2.x' into api commit c30e89bb8a0013bff37e61e4c6953350c9c1f313 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 12:47:02 2015 +0200 Merge mod_http_api commit 7b0db22b4acd48ff6fabce41c1b2525e6580a3c5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 16 11:55:48 2015 +0200 Fix exunit tests to run with common_test suites commit d8b1a89800ac7379a57a7eb4a09c3c93c3e1e5eb Merge: 2879ae8 63455b3 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 15 11:39:45 2015 +0200 Merge branch '3.2.x' into api commit 2879ae87ff3eee369ef3d780136b96ecff5285d1 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 14 14:53:44 2015 +0200 Fix update_roster command. commit a1d453dd7a3afda9861a8d747494a45057ad574b Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 13 16:14:28 2015 +0200 API commands refactor Moving and/or merging commands from mod_admin_p1 to mod_admin_extra commit b709ed26b0fc0ca4f3bdd5a59fa58ec7e3db97fa Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 7 15:10:01 2015 +0200 Add tests on commands commit 6711687bee9c672cb3d5aed0744e13420ecf6dbd Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 29 15:58:16 2015 +0200 Add ejabberd_commands tests commit df8682f419cf3877e77e36a19bca0fc55dc991f8 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Sep 28 14:54:39 2015 +0200 Added API versioning for ejabberdctl and rest commands commit cd017b0e3aac431bc3ee807ceb7f8641e1523ef5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Sep 18 11:21:45 2015 +0200 Better error handling of HTTP API commands. commit ca5cb6acd8e4643f9d6c484d2277b0d7e88471e5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 15 15:03:05 2015 +0200 add commands to mod_admin_extra: - get_offline_count - get_presence - change_password commit 7f583fa099e30ac2b0915669fd8f102ac565b833 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 15 15:02:16 2015 +0200 Improve REST API error handling commit 14753b1c02cdce434a786b7f80f6c09f0d210075 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Sep 14 10:51:17 2015 +0200 Change REST API return codes for integer type.
2016-03-31 13:53:31 +02:00
%%%
%%% Accounts
%%%
2016-04-01 12:24:00 +02:00
set_password(User, Host, Password) ->
Fun = fun () -> ejabberd_auth:set_password(User, Host, Password) end,
user_action(User, Host, Fun, ok).
Commands refactor, first pass. - add API versionning - changed error handling, based on exception - commands moved/merged from mod_admin_p1 to mod_admin_extra - command bufixes - add some elixir unit test cases Squashed commit of the following: commit dd59855b3486f78a9349756e4f102e79b3accff8 Merge: 14e8ffc 506e08e Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 30 11:43:18 2015 +0100 Merge branch '3.2.x' into api commit 14e8ffce78cbea6c8605371d1fc50a0c1d1e012c Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 27 16:35:17 2015 +0100 Added OAuth tests to ejabberd_commands commit f81c550c14628edfe4861c228576cb767924366a Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 27 16:34:55 2015 +0100 Added some mod_http_api tests commit 6a64578d5b2ba532a2feb6503ed98561e56d5d53 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Oct 26 15:29:36 2015 +0100 Fix get_last command test Previous version won't work with dst. commit 27e0cde9e9c1f001effe68f8424a365ad947c068 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 17:59:34 2015 +0200 Add tests on admin command policy commit 19dad8d54f54c9fabd454280483cccfb06c8e78a Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 16:49:36 2015 +0200 Added command related tests (http api & user policy) commit e0e596ab4a3f3a70aba5f374f028939ab794de33 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 23 16:49:16 2015 +0200 Fix command call. commit 128cd7d1ede3c47a34f8ec3a750c980ccad2c61d Merge: 60c4c4c 447313c Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 22 14:48:39 2015 +0200 Merge branch '3.2.x' into api commit 60c4c4c0751302524c14219c6bc8c56a6069a689 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 22 14:45:57 2015 +0200 Fix ejabberd_commands spec. commit 8e145c28c5da762c2b93ee32327eff1db94ebfed Merge: 397273a f13dc94 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 18:26:07 2015 +0200 Merge branch '3.2.x' into api commit 397273a23ed415feac87aed33da6452229793387 Merge: c30e89b f289e27 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 15:27:45 2015 +0200 Merge branch '3.2.x' into api commit c30e89bb8a0013bff37e61e4c6953350c9c1f313 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 21 12:47:02 2015 +0200 Merge mod_http_api commit 7b0db22b4acd48ff6fabce41c1b2525e6580a3c5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Oct 16 11:55:48 2015 +0200 Fix exunit tests to run with common_test suites commit d8b1a89800ac7379a57a7eb4a09c3c93c3e1e5eb Merge: 2879ae8 63455b3 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Thu Oct 15 11:39:45 2015 +0200 Merge branch '3.2.x' into api commit 2879ae87ff3eee369ef3d780136b96ecff5285d1 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 14 14:53:44 2015 +0200 Fix update_roster command. commit a1d453dd7a3afda9861a8d747494a45057ad574b Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Oct 13 16:14:28 2015 +0200 API commands refactor Moving and/or merging commands from mod_admin_p1 to mod_admin_extra commit b709ed26b0fc0ca4f3bdd5a59fa58ec7e3db97fa Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Wed Oct 7 15:10:01 2015 +0200 Add tests on commands commit 6711687bee9c672cb3d5aed0744e13420ecf6dbd Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 29 15:58:16 2015 +0200 Add ejabberd_commands tests commit df8682f419cf3877e77e36a19bca0fc55dc991f8 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Sep 28 14:54:39 2015 +0200 Added API versioning for ejabberdctl and rest commands commit cd017b0e3aac431bc3ee807ceb7f8641e1523ef5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Fri Sep 18 11:21:45 2015 +0200 Better error handling of HTTP API commands. commit ca5cb6acd8e4643f9d6c484d2277b0d7e88471e5 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 15 15:03:05 2015 +0200 add commands to mod_admin_extra: - get_offline_count - get_presence - change_password commit 7f583fa099e30ac2b0915669fd8f102ac565b833 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Tue Sep 15 15:02:16 2015 +0200 Improve REST API error handling commit 14753b1c02cdce434a786b7f80f6c09f0d210075 Author: Jerome Sautret <jerome.sautret@process-one.net> Date: Mon Sep 14 10:51:17 2015 +0200 Change REST API return codes for integer type.
2016-03-31 13:53:31 +02:00
2016-04-10 15:37:36 +02:00
check_password(User, Host, Password) ->
ejabberd_auth:check_password(User, <<>>, Host, Password).
%% Copied some code from ejabberd_commands.erl
check_password_hash(User, Host, PasswordHash, HashMethod) ->
AccountPass = ejabberd_auth:get_password_s(User, Host),
Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
proplists:get_value(hashs, crypto:supports())),
MethodAllowed = lists:member(HashMethod, Methods),
AccountPassHash = case {AccountPass, MethodAllowed} of
{A, _} when is_tuple(A) -> scrammed;
{_, true} -> get_hash(AccountPass, HashMethod);
{_, false} ->
2019-06-24 19:32:34 +02:00
?ERROR_MSG("Check_password_hash called "
"with hash method: ~p", [HashMethod]),
2016-11-30 10:31:36 +01:00
undefined
end,
case AccountPassHash of
scrammed ->
?ERROR_MSG("Passwords are scrammed, and check_password_hash cannot work.", []),
throw(passwords_scrammed_command_cannot_work);
undefined -> throw(unkown_hash_method);
PasswordHash -> ok;
_ -> false
end.
get_hash(AccountPass, Method) ->
iolist_to_binary([io_lib:format("~2.16.0B", [X])
|| X <- binary_to_list(
crypto:hash(binary_to_atom(Method, latin1), AccountPass))]).
delete_old_users(Days) ->
%% Get the list of registered users
Use cache for authentication backends The commit introduces the following API incompatibilities: In ejabberd_auth.erl: * dirty_get_registered_users/0 is renamed to get_users/0 * get_vh_registered_users/1 is renamed to get_users/1 * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is renamed to count_users/1 * get_vh_registered_users_number/2 is renamed to count_users/2 In ejabberd_auth callbacks * plain_password_required/0 is replaced by plain_password_required/1 where the argument is a virtual host * store_type/0 is replaced by store_type/1 where the argument is a virtual host * set_password/3 is now an optional callback * remove_user/3 callback is no longer needed * remove_user/2 now should return `ok | {error, atom()}` * is_user_exists/2 now must only be implemented for backends with `external` store type * check_password/6 is no longer needed * check_password/4 now must only be implemented for backends with `external` store type * try_register/3 is now an optional callback and should return `ok | {error, atom()}` * dirty_get_registered_users/0 is no longer needed * get_vh_registered_users/1 is no longer needed * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is no longer needed * get_vh_registered_users_number/2 is renamed to count_users/2 * get_password_s/2 is no longer needed * get_password/2 now must only be implemented for backends with `plain` or `scram` store type Additionally, the commit introduces two new callbacks: * use_cache/1 where the argument is a virtual host * cache_nodes/1 where the argument is a virtual host New options are also introduced: `auth_use_cache`, `auth_cache_missed`, `auth_cache_life_time` and `auth_cache_size`.
2017-05-11 13:37:21 +02:00
Users = ejabberd_auth:get_users(),
{removed, N, UR} = delete_old_users(Days, Users),
{ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
delete_old_users_vhost(Host, Days) ->
%% Get the list of registered users
Use cache for authentication backends The commit introduces the following API incompatibilities: In ejabberd_auth.erl: * dirty_get_registered_users/0 is renamed to get_users/0 * get_vh_registered_users/1 is renamed to get_users/1 * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is renamed to count_users/1 * get_vh_registered_users_number/2 is renamed to count_users/2 In ejabberd_auth callbacks * plain_password_required/0 is replaced by plain_password_required/1 where the argument is a virtual host * store_type/0 is replaced by store_type/1 where the argument is a virtual host * set_password/3 is now an optional callback * remove_user/3 callback is no longer needed * remove_user/2 now should return `ok | {error, atom()}` * is_user_exists/2 now must only be implemented for backends with `external` store type * check_password/6 is no longer needed * check_password/4 now must only be implemented for backends with `external` store type * try_register/3 is now an optional callback and should return `ok | {error, atom()}` * dirty_get_registered_users/0 is no longer needed * get_vh_registered_users/1 is no longer needed * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is no longer needed * get_vh_registered_users_number/2 is renamed to count_users/2 * get_password_s/2 is no longer needed * get_password/2 now must only be implemented for backends with `plain` or `scram` store type Additionally, the commit introduces two new callbacks: * use_cache/1 where the argument is a virtual host * cache_nodes/1 where the argument is a virtual host New options are also introduced: `auth_use_cache`, `auth_cache_missed`, `auth_cache_life_time` and `auth_cache_size`.
2017-05-11 13:37:21 +02:00
Users = ejabberd_auth:get_users(Host),
{removed, N, UR} = delete_old_users(Days, Users),
{ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
delete_old_users(Days, Users) ->
SecOlder = Days*24*60*60,
TimeStamp_now = erlang:system_time(second),
TimeStamp_oldest = TimeStamp_now - SecOlder,
F = fun({LUser, LServer}) ->
case catch delete_or_not(LUser, LServer, TimeStamp_oldest) of
true ->
ejabberd_auth:remove_user(LUser, LServer),
true;
_ ->
false
end
end,
Users_removed = lists:filter(F, Users),
{removed, length(Users_removed), Users_removed}.
delete_or_not(LUser, LServer, TimeStamp_oldest) ->
deny = acl:match_rule(LServer, protect_old_users, jid:make(LUser, LServer)),
[] = ejabberd_sm:get_user_resources(LUser, LServer),
case mod_last:get_last_info(LUser, LServer) of
{ok, TimeStamp, _Status} ->
if TimeStamp_oldest < TimeStamp ->
false;
true ->
true
end;
not_found ->
true
end.
%%
%% Ban account
ban_account(User, Host, ReasonText) ->
Reason = prepare_reason(ReasonText),
kick_sessions(User, Host, Reason),
set_random_password(User, Host, Reason),
ok.
kick_sessions(User, Server, Reason) ->
lists:map(
fun(Resource) ->
kick_this_session(User, Server, Resource, Reason)
end,
ejabberd_sm:get_user_resources(User, Server)).
set_random_password(User, Server, Reason) ->
NewPass = build_random_password(Reason),
set_password_auth(User, Server, NewPass).
build_random_password(Reason) ->
{{Year, Month, Day}, {Hour, Minute, Second}} = calendar:universal_time(),
Date = str:format("~4..0B~2..0B~2..0BT~2..0B:~2..0B:~2..0B",
[Year, Month, Day, Hour, Minute, Second]),
RandomString = p1_rand:get_string(),
2015-03-26 20:41:16 +01:00
<<"BANNED_ACCOUNT--", Date/binary, "--", RandomString/binary, "--", Reason/binary>>.
set_password_auth(User, Server, Password) ->
ok = ejabberd_auth:set_password(User, Server, Password).
prepare_reason([]) ->
<<"Kicked by administrator">>;
prepare_reason([Reason]) ->
Reason;
prepare_reason(Reason) when is_binary(Reason) ->
Reason.
%%%
%%% Sessions
%%%
num_resources(User, Host) ->
length(ejabberd_sm:get_user_resources(User, Host)).
resource_num(User, Host, Num) ->
Resources = ejabberd_sm:get_user_resources(User, Host),
case (0<Num) and (Num=<length(Resources)) of
true ->
lists:nth(Num, Resources);
false ->
throw({bad_argument,
lists:flatten(io_lib:format("Wrong resource number: ~p", [Num]))})
end.
kick_session(User, Server, Resource, ReasonText) ->
kick_this_session(User, Server, Resource, prepare_reason(ReasonText)),
ok.
kick_this_session(User, Server, Resource, Reason) ->
ejabberd_sm:route(jid:make(User, Server, Resource),
{exit, Reason}).
status_num(Host, Status) ->
length(get_status_list(Host, Status)).
status_num(Status) ->
2014-04-02 04:17:08 +02:00
status_num(<<"all">>, Status).
status_list(Host, Status) ->
Res = get_status_list(Host, Status),
[{U, S, R, num_prio(P), St} || {U, S, R, P, St} <- Res].
status_list(Status) ->
2014-04-02 04:17:08 +02:00
status_list(<<"all">>, Status).
get_status_list(Host, Status_required) ->
%% Get list of all logged users
Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
%% Reformat the list
Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
Fhost = case Host of
2014-04-02 04:17:08 +02:00
<<"all">> ->
2018-01-27 17:35:38 +01:00
%% All hosts are requested, so don't filter at all
fun(_, _) -> true end;
_ ->
%% Filter the list, only Host is interesting
fun(A, B) -> A == B end
end,
Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
%% For each Pid, get its presence
Sessions4 = [ {catch get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
%% Filter by status
Fstatus = case Status_required of
2014-04-02 04:17:08 +02:00
<<"all">> ->
fun(_, _) -> true end;
_ ->
fun(A, B) -> A == B end
end,
[{User, Server, Resource, num_prio(Priority), stringize(Status_text)}
|| {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
apply(Fstatus, [Status, Status_required])].
connected_users_info() ->
lists:filtermap(
fun({U, S, R}) ->
case user_session_info(U, S, R) of
offline ->
false;
Info ->
Jid = jid:encode(jid:make(U, S, R)),
{true, erlang:insert_element(1, Info, Jid)}
end
end,
ejabberd_sm:dirty_get_sessions_list()).
connected_users_vhost(Host) ->
USRs = ejabberd_sm:get_vh_session_list(Host),
[ jid:encode(jid:make(USR)) || USR <- USRs].
%% Make string more print-friendly
stringize(String) ->
%% Replace newline characters with other code
2014-04-02 04:17:08 +02:00
ejabberd_regexp:greplace(String, <<"\n">>, <<"\\n">>).
get_presence(Pid) ->
try get_presence2(Pid) of
{_, _, _, _} = Res ->
Res
catch
_:_ -> {<<"">>, <<"">>, <<"offline">>, <<"">>}
end.
get_presence2(Pid) ->
Pres = #presence{from = From} = ejabberd_c2s:get_presence(Pid),
Show = case Pres of
#presence{type = unavailable} -> <<"unavailable">>;
#presence{show = undefined} -> <<"available">>;
#presence{show = S} -> atom_to_binary(S, utf8)
end,
Status = xmpp:get_text(Pres#presence.status),
{From#jid.user, From#jid.resource, Show, Status}.
2016-11-30 10:31:36 +01:00
get_presence(U, S) ->
Pids = [ejabberd_sm:get_session_pid(U, S, R)
|| R <- ejabberd_sm:get_user_resources(U, S)],
OnlinePids = [Pid || Pid <- Pids, Pid=/=none],
case OnlinePids of
[] ->
{jid:encode({U, S, <<>>}), <<"unavailable">>, <<"">>};
2016-11-30 10:31:36 +01:00
[SessionPid|_] ->
{_User, Resource, Show, Status} = get_presence(SessionPid),
FullJID = jid:encode({U, S, Resource}),
2016-11-30 10:31:36 +01:00
{FullJID, Show, Status}
end.
2016-05-31 11:47:08 +02:00
set_presence(User, Host, Resource, Type, Show, Status, Priority)
when is_integer(Priority) ->
BPriority = integer_to_binary(Priority),
set_presence(User, Host, Resource, Type, Show, Status, BPriority);
set_presence(User, Host, Resource, Type, Show, Status, Priority0) ->
Priority = if is_integer(Priority0) -> Priority0;
true -> binary_to_integer(Priority0)
end,
Pres = #presence{
from = jid:make(User, Host, Resource),
to = jid:make(User, Host),
type = misc:binary_to_atom(Type),
status = xmpp:mk_text(Status),
show = misc:binary_to_atom(Show),
priority = Priority,
sub_els = []},
Ref = ejabberd_sm:get_session_pid(User, Host, Resource),
ejabberd_c2s:set_presence(Ref, Pres).
user_sessions_info(User, Host) ->
lists:filtermap(fun(Resource) ->
case user_session_info(User, Host, Resource) of
offline -> false;
Info -> {true, Info}
end
end, ejabberd_sm:get_user_resources(User, Host)).
user_session_info(User, Host, Resource) ->
CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
case ejabberd_sm:get_user_info(User, Host, Resource) of
offline ->
offline;
Info ->
Now = proplists:get_value(ts, Info),
Pid = proplists:get_value(pid, Info),
{_U, _Resource, Status, StatusText} = get_presence(Pid),
Priority = proplists:get_value(priority, Info),
Conn = proplists:get_value(conn, Info),
{Ip, Port} = proplists:get_value(ip, Info),
IPS = inet_parse:ntoa(Ip),
NodeS = atom_to_list(node(Pid)),
Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
calendar:now_to_local_time(Now)),
{atom_to_list(Conn), IPS, Port, num_prio(Priority), NodeS, Uptime, Status, Resource, StatusText}
end.
%%%
%%% Vcard
%%%
2016-04-01 12:24:00 +02:00
set_nickname(User, Host, Nickname) ->
VCard = xmpp:encode(#vcard_temp{nickname = Nickname}),
case mod_vcard:set_vcard(User, jid:nameprep(Host), VCard) of
{error, badarg} ->
error;
ok ->
ok
2016-04-01 12:24:00 +02:00
end.
get_vcard(User, Host, Name) ->
[Res | _] = get_vcard_content(User, Host, [Name]),
Res.
get_vcard(User, Host, Name, Subname) ->
[Res | _] = get_vcard_content(User, Host, [Name, Subname]),
Res.
get_vcard_multi(User, Host, Name, Subname) ->
get_vcard_content(User, Host, [Name, Subname]).
set_vcard(User, Host, Name, SomeContent) ->
set_vcard_content(User, Host, [Name], SomeContent).
set_vcard(User, Host, Name, Subname, SomeContent) ->
set_vcard_content(User, Host, [Name, Subname], SomeContent).
%%
%% Internal vcard
get_vcard_content(User, Server, Data) ->
case mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server)) of
[El|_] ->
case get_vcard(Data, El) of
[false] -> throw(error_no_value_found_in_vcard);
2016-02-03 19:03:17 +01:00
ElemList -> ?DEBUG("ELS ~p", [ElemList]), [fxml:get_tag_cdata(Elem) || Elem <- ElemList]
end;
[] ->
throw(error_no_vcard_found);
error ->
throw(database_failure)
end.
get_vcard([<<"TEL">>, TelType], {_, _, _, OldEls}) ->
{TakenEl, _NewEls} = take_vcard_tel(TelType, OldEls, [], not_found),
[TakenEl];
get_vcard([Data1, Data2], A1) ->
case get_subtag(A1, Data1) of
[false] -> [false];
A2List ->
lists:flatten([get_vcard([Data2], A2) || A2 <- A2List])
end;
get_vcard([Data], A1) ->
get_subtag(A1, Data).
get_subtag(Xmlelement, Name) ->
2016-02-03 19:03:17 +01:00
[fxml:get_subtag(Xmlelement, Name)].
set_vcard_content(User, Server, Data, SomeContent) ->
ContentList = case SomeContent of
[Bin | _] when is_binary(Bin) -> SomeContent;
Bin when is_binary(Bin) -> [SomeContent]
end,
%% Get old vcard
A4 = case mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server)) of
[A1] ->
{_, _, _, A2} = A1,
update_vcard_els(Data, ContentList, A2);
[] ->
update_vcard_els(Data, ContentList, []);
error ->
throw(database_failure)
end,
%% Build new vcard
SubEl = {xmlel, <<"vCard">>, [{<<"xmlns">>,<<"vcard-temp">>}], A4},
2018-10-30 23:07:30 +01:00
mod_vcard:set_vcard(User, jid:nameprep(Server), SubEl).
take_vcard_tel(TelType, [{xmlel, <<"TEL">>, _, SubEls}=OldEl | OldEls], NewEls, Taken) ->
{Taken2, NewEls2} = case lists:keymember(TelType, 2, SubEls) of
2016-02-03 19:03:17 +01:00
true -> {fxml:get_subtag(OldEl, <<"NUMBER">>), NewEls};
false -> {Taken, [OldEl | NewEls]}
end,
take_vcard_tel(TelType, OldEls, NewEls2, Taken2);
take_vcard_tel(TelType, [OldEl | OldEls], NewEls, Taken) ->
take_vcard_tel(TelType, OldEls, [OldEl | NewEls], Taken);
take_vcard_tel(_TelType, [], NewEls, Taken) ->
{Taken, NewEls}.
update_vcard_els([<<"TEL">>, TelType], [TelValue], OldEls) ->
{_, NewEls} = take_vcard_tel(TelType, OldEls, [], not_found),
NewEl = {xmlel,<<"TEL">>,[],
2016-04-01 12:24:00 +02:00
[{xmlel,TelType,[],[]},
{xmlel,<<"NUMBER">>,[],[{xmlcdata,TelValue}]}]},
[NewEl | NewEls];
update_vcard_els(Data, ContentList, Els1) ->
Els2 = lists:keysort(2, Els1),
[Data1 | Data2] = Data,
NewEls = case Data2 of
[] ->
[{xmlel, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList];
[D2] ->
OldEl = case lists:keysearch(Data1, 2, Els2) of
{value, A} -> A;
2014-04-02 04:17:08 +02:00
false -> {xmlel, Data1, [], []}
end,
{xmlel, _, _, ContentOld1} = OldEl,
Content2 = [{xmlel, D2, [], [{xmlcdata,Content}]} || Content <- ContentList],
ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2],
ContentOld3 = lists:keysort(2, ContentOld2),
ContentNew = lists:keymerge(2, Content2, ContentOld3),
[{xmlel, Data1, [], ContentNew}]
end,
Els3 = lists:keydelete(Data1, 2, Els2),
lists:keymerge(2, NewEls, Els3).
%%%
%%% Roster
%%%
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) ->
Jid = jid:make(LocalUser, LocalServer),
RosterItem = build_roster_item(User, Server, {add, Nick, Subs, Group}),
case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of
ok -> ok;
_ -> error
end.
subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) ->
ItemEl = build_roster_item(User, Server, {add, Nick, Subscription, Group}),
mod_roster:set_items(LU, LS, #roster_query{items = [ItemEl]}).
delete_rosteritem(LocalUser, LocalServer, User, Server) ->
Jid = jid:make(LocalUser, LocalServer),
RosterItem = build_roster_item(User, Server, remove),
case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of
ok -> ok;
_ -> error
end.
%% -----------------------------
%% Get Roster
%% -----------------------------
get_roster(User, Server) ->
Items = ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]),
make_roster_xmlrpc(Items).
%% Note: if a contact is in several groups, the contact is returned
%% several times, each one in a different group.
make_roster_xmlrpc(Roster) ->
lists:foldl(
fun(Item, Res) ->
JIDS = jid:encode(Item#roster.jid),
Nick = Item#roster.name,
Subs = atom_to_list(Item#roster.subscription),
Ask = atom_to_list(Item#roster.ask),
Groups = case Item#roster.groups of
2014-04-02 04:17:08 +02:00
[] -> [<<>>];
Gs -> Gs
end,
ItemsX = [{JIDS, Nick, Subs, Ask, Group} || Group <- Groups],
ItemsX ++ Res
end,
[],
Roster).
%%-----------------------------
%% Push Roster from file
%%-----------------------------
push_roster(File, User, Server) ->
{ok, [Roster]} = file:consult(File),
2014-04-02 04:17:08 +02:00
subscribe_roster({User, Server, <<>>, User}, Roster).
push_roster_all(File) ->
{ok, [Roster]} = file:consult(File),
subscribe_all(Roster).
subscribe_all(Roster) ->
subscribe_all(Roster, Roster).
subscribe_all([], _) ->
ok;
subscribe_all([User1 | Users], Roster) ->
subscribe_roster(User1, Roster),
subscribe_all(Users, Roster).
subscribe_roster(_, []) ->
ok;
%% Do not subscribe a user to itself
subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
subscribe_roster({Name, Server, Group, Nick}, Roster);
%% Subscribe Name2 to Name1
subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
subscribe(iolist_to_binary(Name1), iolist_to_binary(Server1), iolist_to_binary(Name2), iolist_to_binary(Server2),
iolist_to_binary(Nick2), iolist_to_binary(Group2), <<"both">>, []),
subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
push_alltoall(S, G) ->
Use cache for authentication backends The commit introduces the following API incompatibilities: In ejabberd_auth.erl: * dirty_get_registered_users/0 is renamed to get_users/0 * get_vh_registered_users/1 is renamed to get_users/1 * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is renamed to count_users/1 * get_vh_registered_users_number/2 is renamed to count_users/2 In ejabberd_auth callbacks * plain_password_required/0 is replaced by plain_password_required/1 where the argument is a virtual host * store_type/0 is replaced by store_type/1 where the argument is a virtual host * set_password/3 is now an optional callback * remove_user/3 callback is no longer needed * remove_user/2 now should return `ok | {error, atom()}` * is_user_exists/2 now must only be implemented for backends with `external` store type * check_password/6 is no longer needed * check_password/4 now must only be implemented for backends with `external` store type * try_register/3 is now an optional callback and should return `ok | {error, atom()}` * dirty_get_registered_users/0 is no longer needed * get_vh_registered_users/1 is no longer needed * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is no longer needed * get_vh_registered_users_number/2 is renamed to count_users/2 * get_password_s/2 is no longer needed * get_password/2 now must only be implemented for backends with `plain` or `scram` store type Additionally, the commit introduces two new callbacks: * use_cache/1 where the argument is a virtual host * cache_nodes/1 where the argument is a virtual host New options are also introduced: `auth_use_cache`, `auth_cache_missed`, `auth_cache_life_time` and `auth_cache_size`.
2017-05-11 13:37:21 +02:00
Users = ejabberd_auth:get_users(S),
Users2 = build_list_users(G, Users, []),
subscribe_all(Users2),
ok.
build_list_users(_Group, [], Res) ->
Res;
build_list_users(Group, [{User, Server}|Users], Res) ->
build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
%% @spec(LU, LS, U, S, Action) -> ok
%% Action = {add, Nick, Subs, Group} | remove
%% @doc Push to the roster of account LU@LS the contact U@S.
%% The specific action to perform is defined in Action.
push_roster_item(LU, LS, U, S, Action) ->
lists:foreach(fun(R) ->
push_roster_item(LU, LS, R, U, S, Action)
end, ejabberd_sm:get_user_resources(LU, LS)).
push_roster_item(LU, LS, R, U, S, Action) ->
LJID = jid:make(LU, LS, R),
BroadcastEl = build_broadcast(U, S, Action),
ejabberd_sm:route(LJID, BroadcastEl),
Item = build_roster_item(U, S, Action),
ResIQ = build_iq_roster_push(Item),
ejabberd_router:route(
xmpp:set_from_to(ResIQ, jid:remove_resource(LJID), LJID)).
build_roster_item(U, S, {add, Nick, Subs, Group}) ->
Groups = binary:split(Group,<<";">>, [global]),
#roster_item{jid = jid:make(U, S),
name = Nick,
subscription = misc:binary_to_atom(Subs),
groups = Groups};
build_roster_item(U, S, remove) ->
#roster_item{jid = jid:make(U, S), subscription = remove}.
build_iq_roster_push(Item) ->
#iq{type = set, id = <<"push">>,
sub_els = [#roster_query{items = [Item]}]}.
build_broadcast(U, S, {add, _Nick, Subs, _Group}) ->
build_broadcast(U, S, list_to_atom(binary_to_list(Subs)));
build_broadcast(U, S, remove) ->
build_broadcast(U, S, none);
2014-04-02 04:17:08 +02:00
%% @spec (U::binary(), S::binary(), Subs::atom()) -> any()
%% Subs = both | from | to | none
build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) ->
{item, {U, S, <<>>}, SubsAtom}.
%%%
%%% Last Activity
%%%
get_last(User, Server) ->
{Now, Status} = case ejabberd_sm:get_user_resources(User, Server) of
2016-04-01 12:24:00 +02:00
[] ->
case mod_last:get_last_info(User, Server) of
not_found ->
{erlang:timestamp(), "NOT FOUND"};
{ok, Shift, Status1} ->
{{Shift div 1000000, Shift rem 1000000, 0}, Status1}
2016-04-01 12:24:00 +02:00
end;
_ ->
{erlang:timestamp(), "ONLINE"}
end,
{xmpp_util:encode_timestamp(Now), Status}.
set_last(User, Server, Timestamp, Status) ->
case mod_last:store_last_info(User, Server, Timestamp, Status) of
{ok, _} -> ok;
Error -> Error
end.
%%%
%%% Private Storage
%%%
%% Example usage:
%% $ ejabberdctl private_set badlop localhost "\<aa\ xmlns=\'bb\'\>Cluth\</aa\>"
%% $ ejabberdctl private_get badlop localhost aa bb
%% <aa xmlns='bb'>Cluth</aa>
private_get(Username, Host, Element, Ns) ->
ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]},
Els = mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host),
[{Ns, ElementXml}]),
binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))).
private_set(Username, Host, ElementString) ->
2016-02-03 19:03:17 +01:00
case fxml_stream:parse_element(ElementString) of
{error, Error} ->
io:format("Error found parsing the element:~n ~p~nError: ~p~n",
[ElementString, Error]),
error;
Xml ->
private_set2(Username, Host, Xml)
end.
private_set2(Username, Host, Xml) ->
NS = fxml:get_tag_attr_s(<<"xmlns">>, Xml),
JID = jid:make(Username, Host),
mod_private:set_data(JID, [{NS, Xml}]).
%%%
%%% Shared Roster Groups
%%%
srg_create(Group, Host, Name, Description, Display) ->
DisplayList = case Display of
<<>> -> [];
2014-03-03 12:31:17 +01:00
_ -> ejabberd_regexp:split(Display, <<"\\\\n">>)
end,
Opts = [{name, Name},
{displayed_groups, DisplayList},
{description, Description}],
{atomic, _} = mod_shared_roster:create_group(Host, Group, Opts),
ok.
srg_delete(Group, Host) ->
{atomic, _} = mod_shared_roster:delete_group(Host, Group),
ok.
srg_list(Host) ->
lists:sort(mod_shared_roster:list_groups(Host)).
srg_get_info(Group, Host) ->
Opts = case mod_shared_roster:get_group_opts(Host,Group) of
Os when is_list(Os) -> Os;
error -> []
end,
[{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
to_list([]) -> [];
to_list([H|T]) -> [to_list(H)|to_list(T)];
to_list(E) when is_atom(E) -> atom_to_list(E);
to_list(E) -> binary_to_list(E).
srg_get_members(Group, Host) ->
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
[jid:encode(jid:make(MUser, MServer))
|| {MUser, MServer} <- Members].
srg_user_add(User, Host, Group, GroupHost) ->
mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group),
ok.
srg_user_del(User, Host, Group, GroupHost) ->
mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group),
ok.
%%%
%%% Stanza
%%%
%% @doc Send a message to a Jabber account.
%% @spec (Type::binary(), From::binary(), To::binary(), Subject::binary(), Body::binary()) -> ok
send_message(Type, From, To, Subject, Body) ->
2019-11-29 09:27:57 +01:00
CodecOpts = ejabberd_config:codec_options(),
try xmpp:decode(
#xmlel{name = <<"message">>,
attrs = [{<<"to">>, To},
{<<"from">>, From},
{<<"type">>, Type},
{<<"id">>, p1_rand:get_string()}],
children =
[#xmlel{name = <<"subject">>,
children = [{xmlcdata, Subject}]},
#xmlel{name = <<"body">>,
children = [{xmlcdata, Body}]}]},
?NS_CLIENT, CodecOpts) of
#message{from = JID} = Msg ->
State = #{jid => JID},
ejabberd_hooks:run_fold(user_send_packet, JID#jid.lserver, {Msg, State}, []),
ejabberd_router:route(Msg)
catch _:{xmpp_codec, Why} ->
{error, xmpp:format_error(Why)}
end.
2016-01-19 16:16:04 +01:00
send_stanza(FromString, ToString, Stanza) ->
try
#xmlel{} = El = fxml_stream:parse_element(Stanza),
From = jid:decode(FromString),
To = jid:decode(ToString),
2019-06-21 20:06:32 +02:00
CodecOpts = ejabberd_config:codec_options(),
Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
ejabberd_router:route(xmpp:set_from_to(Pkt, From, To))
catch _:{xmpp_codec, Why} ->
io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
{error, Why};
_:{badmatch, {error, {Code, Why}}} when is_integer(Code) ->
io:format("invalid xml: ~p~n", [Why]),
{error, Why};
_:{badmatch, {error, Why}} ->
io:format("invalid xml: ~p~n", [Why]),
{error, Why};
_:{bad_jid, S} ->
io:format("malformed JID: ~ts~n", [S]),
{error, "JID malformed"}
2016-01-19 16:16:04 +01:00
end.
-spec send_stanza_c2s(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
send_stanza_c2s(Username, Host, Resource, Stanza) ->
try
#xmlel{} = El = fxml_stream:parse_element(Stanza),
CodecOpts = ejabberd_config:codec_options(),
Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
case ejabberd_sm:get_session_pid(Username, Host, Resource) of
Pid when is_pid(Pid) ->
ejabberd_c2s:send(Pid, Pkt);
_ ->
{error, no_session}
end
catch _:{badmatch, {error, Why} = Err} ->
io:format("invalid xml: ~p~n", [Why]),
Err;
_:{xmpp_codec, Why} ->
io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
{error, Why}
2016-01-19 16:16:04 +01:00
end.
privacy_set(Username, Host, QueryS) ->
Jid = jid:make(Username, Host),
2016-02-03 19:03:17 +01:00
QueryEl = fxml_stream:parse_element(QueryS),
SubEl = xmpp:decode(QueryEl),
IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
from = Jid, to = Jid},
Result = mod_privacy:process_iq(IQ),
Result#iq.type == result.
%%%
%%% Stats
%%%
stats(Name) ->
case Name of
2014-05-02 22:01:36 +02:00
<<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
2016-01-14 15:09:03 +01:00
<<"processes">> -> length(erlang:processes());
2019-06-14 11:33:26 +02:00
<<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ejabberd_option:hosts());
2014-05-02 22:01:36 +02:00
<<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
<<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
end.
stats(Name, Host) ->
case Name of
Use cache for authentication backends The commit introduces the following API incompatibilities: In ejabberd_auth.erl: * dirty_get_registered_users/0 is renamed to get_users/0 * get_vh_registered_users/1 is renamed to get_users/1 * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is renamed to count_users/1 * get_vh_registered_users_number/2 is renamed to count_users/2 In ejabberd_auth callbacks * plain_password_required/0 is replaced by plain_password_required/1 where the argument is a virtual host * store_type/0 is replaced by store_type/1 where the argument is a virtual host * set_password/3 is now an optional callback * remove_user/3 callback is no longer needed * remove_user/2 now should return `ok | {error, atom()}` * is_user_exists/2 now must only be implemented for backends with `external` store type * check_password/6 is no longer needed * check_password/4 now must only be implemented for backends with `external` store type * try_register/3 is now an optional callback and should return `ok | {error, atom()}` * dirty_get_registered_users/0 is no longer needed * get_vh_registered_users/1 is no longer needed * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is no longer needed * get_vh_registered_users_number/2 is renamed to count_users/2 * get_password_s/2 is no longer needed * get_password/2 now must only be implemented for backends with `plain` or `scram` store type Additionally, the commit introduces two new callbacks: * use_cache/1 where the argument is a virtual host * cache_nodes/1 where the argument is a virtual host New options are also introduced: `auth_use_cache`, `auth_cache_missed`, `auth_cache_life_time` and `auth_cache_size`.
2017-05-11 13:37:21 +02:00
<<"registeredusers">> -> ejabberd_auth:count_users(Host);
2014-05-02 22:01:36 +02:00
<<"onlineusers">> -> length(ejabberd_sm:get_vh_session_list(Host))
end.
user_action(User, Server, Fun, OK) ->
2017-05-11 14:49:06 +02:00
case ejabberd_auth:user_exists(User, Server) of
true ->
case catch Fun() of
OK -> ok;
{error, Error} -> throw(Error);
Error ->
?ERROR_MSG("Command returned: ~p", [Error]),
1
end;
false ->
throw({not_found, "unknown_user"})
end.
2015-06-01 14:38:27 +02:00
num_prio(Priority) when is_integer(Priority) ->
Priority;
num_prio(_) ->
-1.
mod_options(_) -> [].
mod_doc() ->
#{desc =>
[?T("This module provides additional administrative commands."), "",
?T("Details for some commands:"), "",
?T("- 'ban-acount':"),
?T("This command kicks all the connected sessions of the
account from the server. It also changes their password to
a randomly generated one, so they can't login anymore
unless a server administrator changes their password
again. It is possible to define the reason of the ban. The
new password also includes the reason and the date and time
of the ban. For example, if this command is called:
'ejabberdctl vhost example.org ban-account boby \"Spammed
rooms\"', then the sessions of the local account which JID
is boby@example.org will be kicked, and its password will
be set to something like this:
'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"),
?T("- 'pushroster' (and 'pushroster-all'):"),
?T("The roster file must be placed, if using Windows, on
the directory where you installed ejabberd: C:/Program
Files/ejabberd or similar. If you use other Operating
System, place the file on the same directory where the
.beam files are installed. See below an example roster
file."),
?T("- 'srg-create':"),
?T("If you want to put a group Name with blankspaces, use
the characters \"\' and \\'\" to define when the Name
starts and ends. For example: 'ejabberdctl srg-create g1
example.org \"\'Group number 1\\'\" this_is_g1 g1'")],
opts =>
[{module_resource,
#{value => ?T("Resource"),
desc =>
?T("Indicate the resource that the XMPP stanzas must
use in the FROM or TO JIDs. This is only useful in
the 'get_vcard*' and 'set_vcard*' commands. The
default value is 'mod_admin_extra'.")}}],
example =>
[{?T("With this configuration, vCards can only be modified
with mod_admin_extra commands:"),
["acl:",
" adminextraresource:",
" - resource: \"modadminextraf8x,31ad\"",
"access_rules:",
" vcard_set:",
" - allow: adminextraresource",
"modules:",
" mod_admin_extra:",
" module_resource: \"modadminextraf8x,31ad\"",
" mod_vcard:",
" access_set: vcard_set"]},
{?T("Content of roster file for 'pushroster' command:"),
["[{<<\"bob\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Bob\">>},",
"{<<\"mart\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Mart\">>},",
"{<<\"Rich\">>, <<\"example.org\">>, <<\"bosses\">>, <<\"Rich\">>}]."]}]}.