From 145e11bc3af88258d588f8a21c394bafce40a27f Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Aug 31 2014 14:52:59 +0000 Subject: Ejabberd ver. 14.07 Signed-off-by: Peter Lemenkov --- diff --git a/.gitignore b/.gitignore index b2b84eb..157f4d1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ ejabberd-2.1.5.tar.gz /processone-ejabberd-v2.1.12-0-gc058687.tar.gz /processone-ejabberd-v2.1.13-0-g5feeacf.tar.gz /ejabberd-v2.1.13.tar.gz +/ejabberd-14.07.tar.gz +/ejabberd-src-deps.tar diff --git a/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch b/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch index 8883f15..1816b94 100644 --- a/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch +++ b/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch @@ -1,26 +1,19 @@ -From b3a61330f7328507e1608e437a152e806ef520d1 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Tue, 16 Feb 2010 16:03:38 +0300 -Subject: [PATCH 01/10] Fix PAM service example name to match actual one +Subject: [PATCH] Fix PAM service example name to match actual one Signed-off-by: Peter Lemenkov ---- - src/ejabberd.cfg.example | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example -index a4068ad..9b24a4a 100644 ---- a/src/ejabberd.cfg.example -+++ b/src/ejabberd.cfg.example -@@ -243,7 +243,7 @@ - %% Authentication using PAM - %% - %%{auth_method, pam}. --%%{pam_service, "pamservicename"}. -+%%{pam_service, "ejabberd"}. +diff --git a/ejabberd.yml.example b/ejabberd.yml.example +index ad5ca9e..028704b 100644 +--- a/ejabberd.yml.example ++++ b/ejabberd.yml.example +@@ -245,7 +245,7 @@ auth_method: internal + ## Authentication using PAM + ## + ## auth_method: pam +-## pam_service: "pamservicename" ++## pam_service: "ejabberd" - %% - %% Authentication using LDAP --- -1.8.3.1 - + ## + ## Authentication using LDAP diff --git a/ejabberd-0002-Fixed-delays-in-s2s-connections.patch b/ejabberd-0002-Fixed-delays-in-s2s-connections.patch deleted file mode 100644 index 8572111..0000000 --- a/ejabberd-0002-Fixed-delays-in-s2s-connections.patch +++ /dev/null @@ -1,53 +0,0 @@ -From ec26218c6f2374f4e39e50c194150065cc5da275 Mon Sep 17 00:00:00 2001 -From: Sergei Golovan -Date: Tue, 16 Feb 2010 16:07:37 +0300 -Subject: [PATCH 02/10] Fixed delays in s2s connections. - -Patch by Sergei Golovan increases timeouts in S2S and removes horrible 5-minute -delay between remote server connection attempts after a falure (in case of -server is down it would be reasonable, but what happens much more often is a -network split when the delay is inadmissible). ---- - src/ejabberd_s2s_in.erl | 2 +- - src/ejabberd_s2s_out.erl | 5 +++-- - 2 files changed, 4 insertions(+), 3 deletions(-) - -diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl -index 6ae4f34..a54e9ab 100644 ---- a/src/ejabberd_s2s_in.erl -+++ b/src/ejabberd_s2s_in.erl -@@ -414,7 +414,7 @@ stream_established({xmlstreamelement, El}, StateData) -> - case {ejabberd_s2s:allow_host(LTo, LFrom), - lists:member(LTo, ejabberd_router:dirty_get_all_domains())} of - {true, true} -> -- ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom), -+ %%ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom), - ejabberd_s2s_out:start(LTo, LFrom, - {verify, self(), - Key, StateData#state.streamid}), -diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl -index 0dedb4c..9376d16 100644 ---- a/src/ejabberd_s2s_out.erl -+++ b/src/ejabberd_s2s_out.erl -@@ -93,7 +93,7 @@ - [From, Host, Type])). - -endif. - ---define(FSMTIMEOUT, 30000). -+-define(FSMTIMEOUT, 60000). - - %% We do not block on send anymore. - -define(TCP_SEND_TIMEOUT, 15000). -@@ -245,7 +245,8 @@ open_socket(init, StateData) -> - NewStateData = StateData#state{bridge={Mod, Fun}}, - {next_state, relay_to_bridge, NewStateData}; - _ -> -- wait_before_reconnect(StateData) -+ %%wait_before_reconnect(StateData) -+ {stop, normal, StateData} - end - end; - open_socket(closed, StateData) -> --- -1.8.3.1 - diff --git a/ejabberd-0002-Introducing-mod_admin_extra.patch b/ejabberd-0002-Introducing-mod_admin_extra.patch new file mode 100644 index 0000000..586cefb --- /dev/null +++ b/ejabberd-0002-Introducing-mod_admin_extra.patch @@ -0,0 +1,1608 @@ +From: Badlop +Date: Tue, 16 Feb 2010 16:12:17 +0300 +Subject: [PATCH] Introducing mod_admin_extra + +Adds the mod_admin_extra module to ejabberd. +This module extends the functionality provided by ejabberdctl +by adding several new commands. + +The code is taken from the ProcessOne repository: + +wget https://raw.githubusercontent.com/processone/ejabberd-contrib/e23bf94/mod_admin_extra/src/mod_admin_extra.erl + +diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl +new file mode 100644 +index 0000000..63703ec +--- /dev/null ++++ b/src/mod_admin_extra.erl +@@ -0,0 +1,1590 @@ ++%%%------------------------------------------------------------------- ++%%% File : mod_admin_extra.erl ++%%% Author : Badlop ++%%% Purpose : Contributed administrative functions and commands ++%%% Created : 10 Aug 2008 by Badlop ++%%% ++%%% ++%%% ejabberd, Copyright (C) 2002-2008 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. ++%%% ++%%% 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., 59 Temple Place, Suite 330, Boston, MA ++%%% 02111-1307 USA ++%%% ++%%%------------------------------------------------------------------- ++ ++-module(mod_admin_extra). ++-author('badlop@process-one.net'). ++ ++-behaviour(gen_mod). ++ ++-export([start/2, stop/1, ++ %% Node ++ compile/1, ++ load_config/1, ++ get_cookie/0, ++ remove_node/1, ++ export2odbc/2, ++ %% Accounts ++ set_password/3, ++ check_password_hash/4, ++ delete_old_users/1, ++ delete_old_users_vhost/2, ++ ban_account/3, ++ num_active_users/2, ++ %% Sessions ++ num_resources/2, ++ resource_num/3, ++ 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, ++ user_sessions_info/2, ++ %% Vcard ++ set_nickname/3, ++ get_vcard/3, ++ get_vcard/4, ++ get_vcard_multi/4, ++ set_vcard/4, ++ set_vcard/5, ++ %% Roster ++ add_rosteritem/7, ++ delete_rosteritem/4, ++ process_rosteritems/5, ++ get_roster/2, ++ push_roster/3, ++ push_roster_all/1, ++ push_alltoall/2, ++ %% mod_last ++ get_last/2, ++ set_last/4, ++ %% mod_private ++ private_get/4, ++ private_set/3, ++ %% mod_shared_roster ++ srg_create/5, ++ srg_delete/2, ++ srg_list/1, ++ srg_get_info/2, ++ srg_get_members/2, ++ srg_user_add/4, ++ srg_user_del/4, ++ %% Stanza ++ send_message_headline/4, ++ send_message_chat/3, ++ send_stanza_c2s/4, ++ privacy_set/3, ++ %% Stats ++ stats/1, stats/2 ++ ]). ++ ++-include("ejabberd.hrl"). ++-include("ejabberd_commands.hrl"). ++-include("mod_roster.hrl"). ++-include("jlib.hrl"). ++ ++%% Copied from ejabberd_sm.erl ++-record(session, {sid, usr, us, priority, info}). ++ ++ ++%%% ++%%% gen_mod ++%%% ++ ++start(_Host, _Opts) -> ++ ejabberd_commands:register_commands(commands()). ++ ++stop(_Host) -> ++ ejabberd_commands:unregister_commands(commands()). ++ ++ ++%%% ++%%% Register commands ++%%% ++ ++commands() -> ++ 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", ++ ++ 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" ++ " 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}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = load_config, tags = [server], ++ desc = "Load ejabberd configuration file", ++ module = ?MODULE, function = load_config, ++ args = [{file, string}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = get_cookie, tags = [erlang], ++ desc = "Get the Erlang cookie of this node", ++ module = ?MODULE, function = get_cookie, ++ args = [], ++ result = {cookie, string}}, ++ #ejabberd_commands{name = remove_node, tags = [erlang], ++ desc = "Remove an ejabberd node from Mnesia clustering config", ++ module = ?MODULE, function = remove_node, ++ args = [{node, string}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = export2odbc, tags = [mnesia], %% Copied to ejabberd 2.1.x after 11 ++ desc = "Export Mnesia tables to files in directory", ++ module = ?MODULE, function = export2odbc, ++ args = [{host, string}, {path, string}], ++ result = {res, rescode}}, ++ ++ #ejabberd_commands{name = num_active_users, tags = [accounts, stats], ++ desc = "Get number of users active in the last days", ++ module = ?MODULE, function = num_active_users, ++ args = [{host, binary}, {days, integer}], ++ result = {users, integer}}, ++ #ejabberd_commands{name = delete_old_users, tags = [accounts, purge], ++ desc = "Delete users that didn't log in last days, or that never logged", ++ module = ?MODULE, function = delete_old_users, ++ args = [{days, integer}], ++ result = {res, restuple}}, ++ #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", ++ module = ?MODULE, function = delete_old_users_vhost, ++ args = [{host, binary}, {days, integer}], ++ result = {res, restuple}}, ++ ++ #ejabberd_commands{name = check_account, tags = [accounts], ++ desc = "Check if an account exists or not", ++ module = ejabberd_auth, function = is_user_exists, ++ args = [{user, binary}, {host, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = check_password, tags = [accounts], ++ desc = "Check if a password is correct", ++ module = ejabberd_auth, function = check_password, ++ args = [{user, binary}, {host, binary}, {password, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = check_password_hash, tags = [accounts], ++ desc = "Check if the password hash is correct", ++ longdesc = "Allowed hash methods: md5, sha.", ++ module = ?MODULE, function = check_password_hash, ++ args = [{user, binary}, {host, binary}, {passwordhash, binary}, {hashmethod, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = change_password, tags = [accounts], ++ desc = "Change the password of an account", ++ module = ?MODULE, function = set_password, ++ args = [{user, binary}, {host, binary}, {newpass, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = ban_account, tags = [accounts], ++ desc = "Ban an account: kick sessions and set random password", ++ module = ?MODULE, function = ban_account, ++ args = [{user, binary}, {host, binary}, {reason, binary}], ++ result = {res, rescode}}, ++ ++ #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}], ++ result = {resources, integer}}, ++ #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}], ++ result = {resource, string}}, ++ #ejabberd_commands{name = kick_session, tags = [session], ++ desc = "Kick a user session", ++ module = ?MODULE, function = kick_session, ++ args = [{user, binary}, {host, binary}, {resource, binary}, {reason, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = status_num_host, tags = [session, stats], ++ desc = "Number of logged users with this status in host", ++ module = ?MODULE, function = status_num, ++ args = [{host, binary}, {status, binary}], ++ result = {users, integer}}, ++ #ejabberd_commands{name = status_num, tags = [session, stats], ++ desc = "Number of logged users with this status", ++ module = ?MODULE, function = status_num, ++ args = [{status, binary}], ++ result = {users, integer}}, ++ #ejabberd_commands{name = status_list_host, tags = [session], ++ desc = "List of users logged in host with their statuses", ++ module = ?MODULE, function = status_list, ++ args = [{host, binary}, {status, binary}], ++ result = {users, {list, ++ {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, ++ args = [{status, binary}], ++ result = {users, {list, ++ {userstatus, {tuple, [ ++ {user, string}, ++ {host, string}, ++ {resource, string}, ++ {priority, integer}, ++ {status, string} ++ ]}} ++ }}}, ++ #ejabberd_commands{name = connected_users_info, ++ tags = [session], ++ desc = "List all established sessions and their information", ++ module = ?MODULE, function = connected_users_info, ++ args = [], ++ result = {connected_users_info, ++ {list, ++ {sessions, {tuple, ++ [{jid, string}, ++ {connection, string}, ++ {ip, string}, ++ {port, integer}, ++ {priority, integer}, ++ {node, string}, ++ {uptime, integer} ++ ]}} ++ }}}, ++ #ejabberd_commands{name = connected_users_vhost, ++ tags = [session], ++ desc = "Get the list of established sessions in a vhost", ++ module = ?MODULE, function = connected_users_vhost, ++ args = [{host, string}], ++ 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, string}, {host, string}], ++ result = {sessions_info, ++ {list, ++ {session, {tuple, ++ [{connection, string}, ++ {ip, string}, ++ {port, integer}, ++ {priority, integer}, ++ {node, string}, ++ {uptime, integer}, ++ {status, string}, ++ {resource, string}, ++ {statustext, string} ++ ]}} ++ }}}, ++ ++ #ejabberd_commands{name = set_presence, ++ tags = [session], ++ desc = "Set presence of a session", ++ module = ?MODULE, function = set_presence, ++ args = [{user, string}, {host, string}, ++ {resource, string}, {type, string}, ++ {show, string}, {status, string}, ++ {priority, string}], ++ result = {res, rescode}}, ++ ++ #ejabberd_commands{name = set_nickname, tags = [vcard], ++ desc = "Set nickname in a user's vCard", ++ module = ?MODULE, function = set_nickname, ++ args = [{user, string}, {host, string}, {nickname, string}], ++ result = {res, rescode}}, ++ ++ #ejabberd_commands{name = get_vcard, tags = [vcard], ++ desc = "Get content from a vCard field", ++ longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP, ++ module = ?MODULE, function = get_vcard, ++ args = [{user, binary}, {host, binary}, {name, binary}], ++ result = {content, string}}, ++ #ejabberd_commands{name = get_vcard2, tags = [vcard], ++ desc = "Get content from a vCard field", ++ longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, ++ module = ?MODULE, function = get_vcard, ++ args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}], ++ result = {content, string}}, ++ #ejabberd_commands{name = get_vcard2_multi, tags = [vcard], ++ desc = "Get multiple contents from a vCard field (requires exmpp installed)", ++ longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, ++ module = ?MODULE, function = get_vcard_multi, ++ args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}], ++ result = {contents, {list, string}}}, ++ ++ #ejabberd_commands{name = set_vcard, tags = [vcard], ++ desc = "Set content in a vCard field", ++ longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP, ++ module = ?MODULE, function = set_vcard, ++ args = [{user, binary}, {host, binary}, {name, binary}, {content, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = set_vcard2, tags = [vcard], ++ desc = "Set content in a vCard subfield", ++ longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, ++ module = ?MODULE, function = set_vcard, ++ args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {content, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = set_vcard2_multi, tags = [vcard], ++ desc = "Set multiple contents in a vCard subfield", ++ longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, ++ module = ?MODULE, function = set_vcard, ++ args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {contents, {list, binary}}], ++ result = {res, rescode}}, ++ ++ #ejabberd_commands{name = add_rosteritem, tags = [roster], ++ desc = "Add an item to a user's roster (supports ODBC)", ++ module = ?MODULE, function = add_rosteritem, ++ args = [{localuser, binary}, {localserver, binary}, ++ {user, binary}, {server, binary}, ++ {nick, binary}, {group, binary}, ++ {subs, binary}], ++ 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], ++ desc = "Delete an item from a user's roster (supports ODBC)", ++ module = ?MODULE, function = delete_rosteritem, ++ args = [{localuser, binary}, {localserver, binary}, ++ {user, binary}, {server, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = process_rosteritems, tags = [roster], ++ desc = "List or delete rosteritems that match filtering options", ++ 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" ++ "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*", ++ module = ?MODULE, 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", ++ module = ?MODULE, function = get_roster, ++ args = [{user, binary}, {host, binary}], ++ 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", ++ module = ?MODULE, function = push_roster, ++ args = [{file, string}, {user, string}, {host, string}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = push_roster_all, tags = [roster], ++ desc = "Push template roster from file to all those users", ++ module = ?MODULE, function = push_roster_all, ++ args = [{file, string}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = push_alltoall, tags = [roster], ++ desc = "Add all the users to all the users of Host in Group", ++ module = ?MODULE, function = push_alltoall, ++ args = [{host, string}, {group, string}], ++ result = {res, rescode}}, ++ ++ #ejabberd_commands{name = get_last, tags = [last], ++ desc = "Get last activity information", ++ longdesc = "Timestamp is the seconds since" ++ "1970-01-01 00:00:00 UTC, for example: date +%s", ++ module = ?MODULE, function = get_last, ++ args = [{user, binary}, {host, binary}], ++ result = {last_activity, string}}, ++ #ejabberd_commands{name = set_last, tags = [last], ++ desc = "Set last activity information", ++ longdesc = "Timestamp is the seconds since" ++ "1970-01-01 00:00:00 UTC, for example: date +%s", ++ module = ?MODULE, function = set_last, ++ args = [{user, string}, {host, string}, {timestamp, integer}, {status, string}], ++ result = {res, rescode}}, ++ ++ #ejabberd_commands{name = private_get, tags = [private], ++ desc = "Get some information from a user private storage", ++ module = ?MODULE, function = private_get, ++ args = [{user, string}, {host, string}, {element, string}, {ns, string}], ++ result = {res, string}}, ++ #ejabberd_commands{name = private_set, tags = [private], ++ desc = "Set to the user private storage", ++ module = ?MODULE, function = private_set, ++ args = [{user, string}, {host, string}, {element, string}], ++ 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 localhost " ++ "name desc \\\"group1\\\\ngroup2\\\"", ++ module = ?MODULE, function = srg_create, ++ args = [{group, binary}, {host, binary}, ++ {name, binary}, {description, binary}, {display, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = srg_delete, tags = [shared_roster_group], ++ desc = "Delete a Shared Roster Group", ++ module = ?MODULE, function = srg_delete, ++ args = [{group, binary}, {host, binary}], ++ 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, ++ args = [{host, binary}], ++ 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, ++ args = [{group, binary}, {host, binary}], ++ 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, ++ args = [{group, binary}, {host, binary}], ++ 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, ++ args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = srg_user_del, tags = [shared_roster_group], ++ desc = "Delete this JID user@host from the Shared Roster Group", ++ module = ?MODULE, function = srg_user_del, ++ args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}], ++ result = {res, rescode}}, ++ ++ #ejabberd_commands{name = send_message_chat, tags = [stanza], ++ desc = "Send a chat message to a local or remote bare of full JID", ++ module = ?MODULE, function = send_message_chat, ++ args = [{from, binary}, {to, binary}, {body, binary}], ++ result = {res, rescode}}, ++ #ejabberd_commands{name = send_message_headline, tags = [stanza], ++ desc = "Send a headline message to a local or remote bare of full JID", ++ module = ?MODULE, function = send_message_headline, ++ args = [{from, binary}, {to, binary}, ++ {subject, binary}, {body, binary}], ++ 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, ++ args = [{user, binary}, {host, binary}, {resource, binary}, {stanza, binary}], ++ 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, ++ args = [{user, binary}, {host, binary}, {xmlquery, binary}], ++ result = {res, rescode}}, ++ ++ #ejabberd_commands{name = stats, tags = [stats], ++ desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds", ++ module = ?MODULE, function = stats, ++ args = [{name, binary}], ++ result = {stat, integer}}, ++ #ejabberd_commands{name = stats_host, tags = [stats], ++ desc = "Get statistical value for this host: registeredusers onlineusers", ++ module = ?MODULE, function = stats, ++ args = [{name, binary}, {host, binary}], ++ result = {stat, integer}} ++ ]. ++ ++ ++%%% ++%%% Node ++%%% ++ ++compile(File) -> ++ case compile:file(File) of ++ ok -> ok; ++ _ -> error ++ end. ++ ++load_config(Path) -> ++ ok = ejabberd_config:load_file(Path). ++ ++get_cookie() -> ++ atom_to_list(erlang:get_cookie()). ++ ++remove_node(Node) -> ++ mnesia:del_table_copy(schema, list_to_atom(Node)), ++ ok. ++ ++export2odbc(Host, Directory) -> ++ Tables = [ ++ {export_last, last}, ++ {export_offline, offline}, ++ {export_passwd, passwd}, ++ {export_private_storage, private_storage}, ++ {export_roster, roster}, ++ {export_vcard, vcard}, ++ {export_vcard_search, vcard_search}], ++ Export = fun({TableFun, Table}) -> ++ Filename = filename:join([Directory, atom_to_list(Table)++".txt"]), ++ io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]), ++ Res = (catch ejd2odbc:TableFun(Host, Filename)), ++ io:format(" Result: ~p~n", [Res]) ++ end, ++ lists:foreach(Export, Tables), ++ ok. ++ ++ ++%%% ++%%% Accounts ++%%% ++ ++set_password(User, Host, Password) -> ++ case ejabberd_auth:set_password(User, Host, Password) of ++ ok -> ++ ok; ++ _ -> ++ error ++ end. ++ ++%% Copied some code from ejabberd_commands.erl ++check_password_hash(User, Host, PasswordHash, HashMethod) -> ++ AccountPass = ejabberd_auth:get_password_s(User, Host), ++ AccountPassHash = case HashMethod of ++ "md5" -> get_md5(AccountPass); ++ "sha" -> get_sha(AccountPass); ++ _ -> undefined ++ end, ++ case AccountPassHash of ++ undefined -> error; ++ PasswordHash -> ok; ++ _ -> error ++ end. ++get_md5(AccountPass) -> ++ lists:flatten([io_lib:format("~.16B", [X]) ++ || X <- binary_to_list(crypto:md5(AccountPass))]). ++get_sha(AccountPass) -> ++ lists:flatten([io_lib:format("~.16B", [X]) ++ || X <- binary_to_list(crypto:sha(AccountPass))]). ++ ++num_active_users(Host, Days) -> ++ list_last_activity(Host, true, Days). ++ ++%% Code based on ejabberd/src/web/ejabberd_web_admin.erl ++list_last_activity(Host, Integral, Days) -> ++ {MegaSecs, Secs, _MicroSecs} = now(), ++ TimeStamp = MegaSecs * 1000000 + Secs, ++ TS = TimeStamp - Days * 86400, ++ case catch mnesia:dirty_select( ++ last_activity, [{{last_activity, {'_', Host}, '$1', '_'}, ++ [{'>', '$1', TS}], ++ [{'trunc', {'/', ++ {'-', TimeStamp, '$1'}, ++ 86400}}]}]) of ++ {'EXIT', _Reason} -> ++ []; ++ Vals -> ++ Hist = histogram(Vals, Integral), ++ if ++ Hist == [] -> ++ 0; ++ true -> ++ Left = Days - length(Hist), ++ Tail = if ++ Integral -> ++ lists:duplicate(Left, lists:last(Hist)); ++ true -> ++ lists:duplicate(Left, 0) ++ end, ++ lists:nth(Days, Hist ++ Tail) ++ end ++ end. ++histogram(Values, Integral) -> ++ histogram(lists:sort(Values), Integral, 0, 0, []). ++histogram([H | T], Integral, Current, Count, Hist) when Current == H -> ++ histogram(T, Integral, Current, Count + 1, Hist); ++histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H -> ++ if ++ Integral -> ++ histogram(Values, Integral, Current + 1, Count, [Count | Hist]); ++ true -> ++ histogram(Values, Integral, Current + 1, 0, [Count | Hist]) ++ end; ++histogram([], _Integral, _Current, Count, Hist) -> ++ if ++ Count > 0 -> ++ lists:reverse([Count | Hist]); ++ true -> ++ lists:reverse(Hist) ++ end. ++ ++ ++delete_old_users(Days) -> ++ %% Get the list of registered users ++ Users = ejabberd_auth:dirty_get_registered_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 ++ Users = ejabberd_auth:get_vh_registered_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) -> ++ %% Convert older time ++ SecOlder = Days*24*60*60, ++ ++ %% Get current time ++ {MegaSecs, Secs, _MicroSecs} = now(), ++ TimeStamp_now = MegaSecs * 1000000 + Secs, ++ ++ %% For a user, remove if required and answer true ++ F = fun({LUser, LServer}) -> ++ %% Check if the user is logged ++ case ejabberd_sm:get_user_resources(LUser, LServer) of ++ %% If it isnt ++ [] -> ++ %% Look for his last_activity ++ case (get_lastactivity_module(LServer)):get_last_info(LUser, LServer) of ++ %% If it is ++ %% existent: ++ {ok, TimeStamp, _Status} -> ++ %% get his age ++ Sec = TimeStamp_now - TimeStamp, ++ %% If he is ++ if ++ %% younger than SecOlder: ++ Sec < SecOlder -> ++ %% do nothing ++ false; ++ %% older: ++ true -> ++ %% remove the user ++ ejabberd_auth:remove_user(LUser, LServer), ++ true ++ end; ++ %% nonexistent: ++ not_found -> ++ %% remove the user ++ ejabberd_auth:remove_user(LUser, LServer), ++ true ++ end; ++ %% Else ++ _ -> ++ %% do nothing ++ false ++ end ++ end, ++ %% Apply the function to every user in the list ++ Users_removed = lists:filter(F, Users), ++ {removed, length(Users_removed), Users_removed}. ++ ++get_lastactivity_module(Server) -> ++ case lists:member(mod_last, gen_mod:loaded_modules(Server)) of ++ true -> mod_last; ++ _ -> mod_last_odbc ++ 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, ++ get_resources(User, Server)). ++ ++get_resources(User, Server) -> ++ lists:map( ++ fun(Session) -> ++ element(3, Session#session.usr) ++ end, ++ get_sessions(User, Server)). ++ ++get_sessions(User, Server) -> ++ LUser = jlib:nodeprep(User), ++ LServer = jlib:nameprep(Server), ++ Sessions = mnesia:dirty_index_read(session, {LUser, LServer}, #session.us), ++ true = is_list(Sessions), ++ Sessions. ++ ++set_random_password(User, Server, Reason) -> ++ NewPass = build_random_password(Reason), ++ set_password_auth(User, Server, NewPass). ++ ++build_random_password(Reason) -> ++ Date = jlib:timestamp_to_iso(calendar:universal_time()), ++ RandomString = randoms:get_string(), ++ "BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason. ++ ++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 ++ lists:nth(Num, Resources); ++ false -> ++ lists:flatten(io_lib:format("Error: 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_router:route( ++ jlib:make_jid(<<>>, <<>>, <<>>), ++ jlib:make_jid(User, Server, Resource), ++ {broadcast, {exit, Reason}}). ++ ++ ++status_num(Host, Status) -> ++ length(get_status_list(Host, Status)). ++status_num(Status) -> ++ status_num(<<"all">>, Status). ++status_list(Host, Status) -> ++ Res = get_status_list(Host, Status), ++ [{U, S, R, P, St} || {U, S, R, P, St} <- Res]. ++status_list(Status) -> ++ 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 ++ <<"all">> -> ++ %% All hosts are requested, so dont 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 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3], ++ %% Filter by status ++ Fstatus = case Status_required of ++ <<"all">> -> ++ fun(_, _) -> true end; ++ _ -> ++ fun(A, B) -> A == B end ++ end, ++ [{User, Server, Resource, Priority, stringize(Status_text)} ++ || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4, ++ apply(Fstatus, [Status, Status_required])]. ++ ++connected_users_info() -> ++ USRIs = dirty_get_sessions_list2(), ++ CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}), ++ lists:map( ++ fun([{U, S, R}, {Now, Pid}, 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)), ++ {[U, $@, S, $/, R], atom_to_list(Conn), IPS, Port, Priority, NodeS, Uptime} ++ end, ++ USRIs). ++ ++connected_users_vhost(Host) -> ++ USRs = ejabberd_sm:get_vh_session_list(Host), ++ [ [U, $@, S, $/, R] || {U, S, R} <- USRs]. ++ ++%% Code copied from ejabberd_sm.erl and customized ++dirty_get_sessions_list2() -> ++ mnesia:dirty_select( ++ session, ++ [{#session{usr = '$1', sid = '$2', priority = '$3', info = '$4', _ = '_'}, ++ [], ++ [['$1', '$2', '$3', '$4']]}]). ++ ++%% Make string more print-friendly ++stringize(String) -> ++ %% Replace newline characters with other code ++ ejabberd_regexp:greplace(String, <<"\n">>, <<"\\n">>). ++ ++set_presence(User, Host, Resource, Type, Show, Status, Priority) -> ++ Pid = ejabberd_sm:get_session_pid(User, Host, Resource), ++ USR = User ++ "@" ++ Host ++ "/" ++ Resource, ++ US = User ++ "@" ++ Host, ++ Message = {route_xmlstreamelement, ++ {xmlel, <<"presence">>, ++ [{<<"from">>, USR}, {<<"to">>, US}, {<<"type">>, Type}], ++ [{xmlel, <<"show">>, [], [{xmlcdata, Show}]}, ++ {xmlel, <<"status">>, [], [{xmlcdata, Status}]}, ++ {xmlel, <<"priority">>, [], [{xmlcdata, Priority}]}]}}, ++ Pid ! Message. ++ ++user_sessions_info(User, Host) -> ++ CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}), ++ US = {User, Host}, ++ Sessions = case catch mnesia:dirty_index_read(session, US, #session.us) of ++ {'EXIT', _Reason} -> ++ []; ++ Ss -> ++ Ss ++ end, ++ lists:map( ++ fun(Session) -> ++ {_U, _S, Resource} = Session#session.usr, ++ {Now, Pid} = Session#session.sid, ++ {_U, _Resource, Status, StatusText} = ejabberd_c2s:get_presence(Pid), ++ Info = Session#session.info, ++ Priority = Session#session.priority, ++ 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, Priority, NodeS, Uptime, Status, Resource, StatusText} ++ end, ++ Sessions). ++ ++ ++%%% ++%%% Vcard ++%%% ++ ++set_nickname(User, Host, Nickname) -> ++ R = mod_vcard:process_sm_iq( ++ {jid, User, Host, <<>>, User, Host, <<>>}, ++ {jid, User, Host, <<>>, User, Host, <<>>}, ++ {iq, <<>>, set, <<>>, <<"en">>, ++ {xmlel, <<"vCard">>, [ ++ {<<"xmlns">>, <<"vcard-temp">>}], [ ++ {xmlel, <<"NICKNAME">>, [], [{xmlcdata, Nickname}]} ++ ] ++ }}), ++ case R of ++ {iq, [], result, [], _L, []} -> ++ ok; ++ _ -> ++ error ++ 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_module_resource(Server) -> ++ case gen_mod:get_module_opt(Server, ?MODULE, module_resource, fun(A) -> A end, none) of ++ none -> list_to_binary(atom_to_list(?MODULE)); ++ R when is_binary(R) -> R ++ end. ++ ++get_vcard_content(User, Server, Data) -> ++ [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}), ++ JID = jlib:make_jid(User, Server, get_module_resource(Server)), ++ IQ = #iq{type = get, xmlns = ?NS_VCARD}, ++ IQr = Module:Function(JID, JID, IQ), ++ case IQr#iq.sub_el of ++ [A1] -> ++ case get_vcard(Data, A1) of ++ [] -> throw(error_no_value_found_in_vcard); ++ ElemList -> [xml:get_tag_cdata(Elem) || Elem <- ElemList] ++ end; ++ [] -> ++ throw(error_no_vcard_found) ++ end. ++ ++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) -> ++ case code:ensure_loaded(exmpp_xml) of ++ {error, _} -> ++ [get_subtag_xml(Xmlelement, Name)]; ++ {module, exmpp_xml} -> ++ get_subtag_exmpp(Xmlelement, Name) ++ end. ++ ++get_subtag_xml(Xmlelement, Name) -> ++ xml:get_subtag(Xmlelement, Name). ++ ++get_subtag_exmpp(Xmlelement, Name) -> ++ Xmlel = exmpp_xml:xmlelement_to_xmlel(Xmlelement), ++ XmlelList = exmpp_xml:get_elements(Xmlel, Name), ++ [exmpp_xml:xmlel_to_xmlelement(Xmlel2) || Xmlel2 <- XmlelList]. ++ ++set_vcard_content(User, Server, Data, SomeContent) -> ++ ContentList = case SomeContent of ++ [Bin | _] when is_binary(Bin) -> SomeContent; ++ Bin when is_binary(Bin) -> [SomeContent] ++ end, ++ [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}), ++ JID = jlib:make_jid(User, Server, get_module_resource(Server)), ++ IQ = #iq{type = get, xmlns = ?NS_VCARD}, ++ IQr = Module:Function(JID, JID, IQ), ++ ++ %% Get old vcard ++ A4 = case IQr#iq.sub_el of ++ [A1] -> ++ {_, _, _, A2} = A1, ++ update_vcard_els(Data, ContentList, A2); ++ [] -> ++ update_vcard_els(Data, ContentList, []) ++ end, ++ ++ %% Build new vcard ++ SubEl = {xmlel, <<"vCard">>, [{<<"xmlns">>,<<"vcard-temp">>}], A4}, ++ IQ2 = #iq{type=set, sub_el = SubEl}, ++ ++ Module:Function(JID, JID, IQ2), ++ ok. ++ ++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; ++ 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) -> ++ case add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs, []) of ++ {atomic, ok} -> ++ push_roster_item(LocalUser, LocalServer, User, Server, {add, Nick, Subs, Group}), ++ ok; ++ _ -> ++ error ++ end. ++ ++add_rosteritem(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) -> ++ subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs). ++ ++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, ++ {xmlel, <<"query">>, ++ [{<<"xmlns">>, <<"jabber:iq:roster">>}], ++ [ItemEl]}). ++ ++delete_rosteritem(LocalUser, LocalServer, User, Server) -> ++ case unsubscribe(LocalUser, LocalServer, User, Server) of ++ {atomic, ok} -> ++ push_roster_item(LocalUser, LocalServer, User, Server, remove), ++ ok; ++ _ -> ++ error ++ end. ++ ++unsubscribe(LU, LS, User, Server) -> ++ ItemEl = build_roster_item(User, Server, remove), ++ mod_roster:set_items( ++ LU, LS, ++ {xmlel, <<"query">>, ++ [{<<"xmlns">>, <<"jabber:iq:roster">>}], ++ [ItemEl]}). ++ ++%% ----------------------------- ++%% 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 = jlib:jid_to_string(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 ++ [] -> [<<>>]; ++ 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), ++ 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(Name1, Server1, Name2, Server2, Nick2, Group2, <<"both">>, []), ++ subscribe_roster({Name1, Server1, Group1, Nick1}, Roster). ++ ++push_alltoall(S, G) -> ++ Users = ejabberd_auth:get_vh_registered_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 = jlib:make_jid(LU, LS, R), ++ BroadcastEl = build_broadcast(U, S, Action), ++ ejabberd_router:route(LJID, LJID, BroadcastEl), ++ Item = build_roster_item(U, S, Action), ++ ResIQ = build_iq_roster_push(Item), ++ ejabberd_router:route(LJID, LJID, ResIQ). ++ ++build_roster_item(U, S, {add, Nick, Subs, Group}) -> ++ {xmlel, <<"item">>, ++ [{<<"jid">>, jlib:jid_to_string(jlib:make_jid(U, S, <<>>))}, ++ {<<"name">>, Nick}, ++ {<<"subscription">>, Subs}], ++ [{xmlel, <<"group">>, [], [{xmlcdata, Group}]}] ++ }; ++build_roster_item(U, S, remove) -> ++ {xmlel, <<"item">>, ++ [{<<"jid">>, jlib:jid_to_string(jlib:make_jid(U, S, <<>>))}, ++ {<<"subscription">>, <<"remove">>}], ++ [] ++ }. ++ ++build_iq_roster_push(Item) -> ++ {xmlel, <<"iq">>, ++ [{<<"type">>, <<"set">>}, {<<"id">>, <<"push">>}], ++ [{xmlel, <<"query">>, ++ [{<<"xmlns">>, ?NS_ROSTER}], ++ [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); ++%% @spec (U::binary(), S::binary(), Subs::atom()) -> any() ++%% Subs = both | from | to | none ++build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) -> ++ {broadcast, {item, {U, S, <<>>}, SubsAtom}}. ++ ++%%% ++%%% Last Activity ++%%% ++ ++get_last(User, Server) -> ++ Mod = get_lastactivity_module(Server), ++ case ejabberd_sm:get_user_resources(User, Server) of ++ [] -> ++ case Mod:get_last_info(User, Server) of ++ not_found -> ++ "Never"; ++ {ok, Shift, _Status} -> ++ TimeStamp = {Shift div 1000000, ++ Shift rem 1000000, ++ 0}, ++ {{Year, Month, Day}, {Hour, Minute, Second}} = ++ calendar:now_to_local_time(TimeStamp), ++ lists:flatten( ++ io_lib:format( ++ "~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", ++ [Year, Month, Day, Hour, Minute, Second])) ++ end; ++ _ -> ++ "Online" ++ end. ++ ++set_last(User, Server, Timestamp, Status) -> ++ Mod = get_lastactivity_module(Server), ++ Mod:store_last_info(User, Server, Timestamp, Status). ++ ++%%% ++%%% Private Storage ++%%% ++ ++%% Example usage: ++%% $ ejabberdctl private_set badlop localhost "\Cluth\" ++%% $ ejabberdctl private_get badlop localhost aa bb ++%% Cluth ++ ++private_get(Username, Host, Element, Ns) -> ++ From = jlib:make_jid(Username, Host, <<>>), ++ To = jlib:make_jid(Username, Host, <<>>), ++ IQ = {iq, <<>>, get, ?NS_PRIVATE, <<>>, ++ {xmlel, <<"query">>, ++ [{<<"xmlns">>,?NS_PRIVATE}], ++ [{xmlel, Element, [{<<"xmlns">>, Ns}], []}]}}, ++ ResIq = mod_private:process_sm_iq(From, To, IQ), ++ [{xmlel, <<"query">>, ++ [{<<"xmlns">>, <<"jabber:iq:private">>}], ++ [SubEl]}] = ResIq#iq.sub_el, ++ xml:element_to_string(SubEl). ++ ++private_set(Username, Host, ElementString) -> ++ case xml_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) -> ++ From = jlib:make_jid(Username, Host, <<>>), ++ To = jlib:make_jid(Username, Host, <<>>), ++ IQ = {iq, <<>>, set, ?NS_PRIVATE, <<>>, ++ {xmlel, <<"query">>, ++ [{<<"xmlns">>, ?NS_PRIVATE}], ++ [Xml]}}, ++ mod_private:process_sm_iq(From, To, IQ), ++ ok. ++ ++%%% ++%%% Shared Roster Groups ++%%% ++ ++srg_create(Group, Host, Name, Description, Display) -> ++ DisplayList = case Display of ++ [] -> []; ++ _ -> ejabberd_regexp:split(Display, <<"\\\\n">>) ++ end, ++ Opts = [{name, Name}, ++ {displayed_groups, DisplayList}, ++ {description, Description}], ++ {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts), ++ ok. ++ ++srg_delete(Group, Host) -> ++ {atomic, ok} = 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, ++ [{io_lib:format("~p", [Title]), ++ io_lib:format("~p", [Value])} || {Title, Value} <- Opts]. ++ ++srg_get_members(Group, Host) -> ++ Members = mod_shared_roster:get_group_explicit_users(Host,Group), ++ [jlib:jid_to_string(jlib:make_jid(MUser, MServer, <<>>)) ++ || {MUser, MServer} <- Members]. ++ ++srg_user_add(User, Host, Group, GroupHost) -> ++ {atomic, ok} = mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group), ++ ok. ++ ++srg_user_del(User, Host, Group, GroupHost) -> ++ {atomic, ok} = mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group), ++ ok. ++ ++ ++%%% ++%%% Stanza ++%%% ++ ++%% @doc Send a chat message to a Jabber account. ++%% @spec (From::binary(), To::binary(), Body::binary()) -> ok ++send_message_chat(From, To, Body) -> ++ Packet = build_packet(message_chat, [Body]), ++ send_packet_all_resources(From, To, Packet). ++ ++%% @doc Send a headline message to a Jabber account. ++%% @spec (From::binary(), To::binary(), Subject::binary(), Body::binary()) -> ok ++send_message_headline(From, To, Subject, Body) -> ++ Packet = build_packet(message_headline, [Subject, Body]), ++ send_packet_all_resources(From, To, Packet). ++ ++%% @doc Send a packet to a Jabber account. ++%% If a resource was specified in the JID, ++%% the packet is sent only to that specific resource. ++%% If no resource was specified in the JID, ++%% and the user is remote or local but offline, ++%% the packet is sent to the bare JID. ++%% If the user is local and is online in several resources, ++%% the packet is sent to all its resources. ++send_packet_all_resources(FromJIDString, ToJIDString, Packet) -> ++ FromJID = jlib:string_to_jid(FromJIDString), ++ ToJID = jlib:string_to_jid(ToJIDString), ++ ToUser = ToJID#jid.user, ++ ToServer = ToJID#jid.server, ++ case ToJID#jid.resource of ++ <<>> -> ++ send_packet_all_resources(FromJID, ToUser, ToServer, Packet); ++ Res -> ++ send_packet_all_resources(FromJID, ToUser, ToServer, Res, Packet) ++ end. ++ ++send_packet_all_resources(FromJID, ToUser, ToServer, Packet) -> ++ case ejabberd_sm:get_user_resources(ToUser, ToServer) of ++ [] -> ++ send_packet_all_resources(FromJID, ToUser, ToServer, <<>>, Packet); ++ ToResources -> ++ lists:foreach( ++ fun(ToResource) -> ++ send_packet_all_resources(FromJID, ToUser, ToServer, ++ ToResource, Packet) ++ end, ++ ToResources) ++ end. ++ ++send_packet_all_resources(FromJID, ToU, ToS, ToR, Packet) -> ++ ToJID = jlib:make_jid(ToU, ToS, ToR), ++ ejabberd_router:route(FromJID, ToJID, Packet). ++ ++ ++build_packet(message_chat, [Body]) -> ++ {xmlel, <<"message">>, ++ [{<<"type">>, <<"chat">>}, {<<"id">>, randoms:get_string()}], ++ [{xmlel, <<"body">>, [], [{xmlcdata, Body}]}] ++ }; ++build_packet(message_headline, [Subject, Body]) -> ++ {xmlel, <<"message">>, ++ [{<<"type">>, <<"headline">>}, {<<"id">>, randoms:get_string()}], ++ [{xmlel, <<"subject">>, [], [{xmlcdata, Subject}]}, ++ {xmlel, <<"body">>, [], [{xmlcdata, Body}]} ++ ] ++ }. ++ ++send_stanza_c2s(Username, Host, Resource, Stanza) -> ++ C2sPid = ejabberd_sm:get_session_pid(Username, Host, Resource), ++ XmlEl = xml_stream:parse_element(Stanza), ++ p1_fsm:send_event(C2sPid, {xmlstreamelement, XmlEl}). ++ ++privacy_set(Username, Host, QueryS) -> ++ From = jlib:string_to_jid(Username ++ "@" ++ Host), ++ To = jlib:string_to_jid(Host), ++ QueryEl = xml_stream:parse_element(QueryS), ++ StanzaEl = {xmlel, <<"iq">>, [{<<"type">>, <<"set">>}], [QueryEl]}, ++ IQ = jlib:iq_query_info(StanzaEl), ++ ejabberd_hooks:run_fold( ++ privacy_iq_set, ++ Host, ++ {error, ?ERR_FEATURE_NOT_IMPLEMENTED}, ++ [From, To, IQ] ++ ), ++ ok. ++ ++%%% ++%%% Stats ++%%% ++ ++stats(Name) -> ++ case Name of ++ <<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000); ++ <<"registeredusers">> -> length(ejabberd_auth:dirty_get_registered_users()); ++ <<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list()); ++ <<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list()) ++ end. ++ ++stats(Name, Host) -> ++ case Name of ++ <<"registeredusers">> -> length(ejabberd_auth:get_vh_registered_users(Host)); ++ <<"onlineusers">> -> length(ejabberd_sm:get_vh_session_list(Host)) ++ end. ++ ++ ++ ++%%----------------------------- ++%% Purge roster items ++%%----------------------------- ++ ++process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) -> ++ Action = case ActionS of ++ "list" -> list; ++ "delete" -> delete ++ end, ++ ++ Subs = lists:foldl( ++ fun(any, _) -> [none, from, to, both]; ++ (Sub, Subs) -> [Sub | Subs] ++ end, ++ [], ++ [list_to_atom(S) || S <- string:tokens(SubsS, ":")] ++ ), ++ ++ Asks = lists:foldl( ++ fun(any, _) -> [none, out, in]; ++ (Ask, Asks) -> [Ask | Asks] ++ end, ++ [], ++ [list_to_atom(S) || S <- string:tokens(AsksS, ":")] ++ ), ++ ++ Users = lists:foldl( ++ fun("any", _) -> ["*", "*@*"]; ++ (U, Us) -> [U | Us] ++ end, ++ [], ++ [S || S <- string:tokens(UsersS, ":")] ++ ), ++ ++ Contacts = lists:foldl( ++ fun("any", _) -> ["*", "*@*"]; ++ (U, Us) -> [U | Us] ++ end, ++ [], ++ [S || S <- string:tokens(ContactsS, ":")] ++ ), ++ ++ case rosteritem_purge({Action, Subs, Asks, Users, Contacts}) of ++ {atomic, Res} -> ++ Res; ++ {error, Reason} -> ++ io:format("Error purging rosteritems: ~p~n", [Reason]), ++ error; ++ {badrpc, Reason} -> ++ io:format("BadRPC purging rosteritems: ~p~n", [Reason]), ++ error ++ end. ++ ++%% @spec ({Action::atom(), Subs::[atom()], Asks::[atom()], User::string(), Contact::string()}) -> {atomic, ok} ++rosteritem_purge(Options) -> ++ Num_rosteritems = mnesia:table_info(roster, size), ++ io:format("There are ~p roster items in total.~n", [Num_rosteritems]), ++ Key = mnesia:dirty_first(roster), ++ Res = rip(Key, Options, {0, Num_rosteritems, 0, 0}, []), ++ {atomic, Res}. ++ ++rip('$end_of_table', _Options, Counters, Res) -> ++ print_progress_line(Counters), ++ Res; ++rip(Key, Options, {Pr, NT, NV, ND}, Res) -> ++ Key_next = mnesia:dirty_next(roster, Key), ++ {Action, _, _, _, _} = Options, ++ {ND2, Res2} = case decide_rip(Key, Options) of ++ true -> ++ Jids = apply_action(Action, Key), ++ {ND+1, [Jids | Res]}; ++ false -> ++ {ND, Res} ++ end, ++ NV2 = NV+1, ++ Pr2 = print_progress_line({Pr, NT, NV2, ND2}), ++ rip(Key_next, Options, {Pr2, NT, NV2, ND2}, Res2). ++ ++apply_action(list, Key) -> ++ {User, Server, JID} = Key, ++ {RUser, RServer, _} = JID, ++ Jid1string = User ++ "@" ++ Server, ++ Jid2string = RUser ++ "@" ++ RServer, ++ io:format("Matches: ~s ~s~n", [Jid1string, Jid2string]), ++ {Jid1string, Jid2string}; ++apply_action(delete, Key) -> ++ R = apply_action(list, Key), ++ mnesia:dirty_delete(roster, Key), ++ R. ++ ++print_progress_line({Pr, NT, NV, ND}) -> ++ Pr2 = trunc((NV/NT)*100), ++ case Pr == Pr2 of ++ true -> ++ ok; ++ false -> ++ io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND]) ++ end, ++ Pr2. ++ ++decide_rip(Key, {_Action, Subs, Asks, User, Contact}) -> ++ case catch mnesia:dirty_read(roster, Key) of ++ [RI] -> ++ lists:member(RI#roster.subscription, Subs) ++ andalso lists:member(RI#roster.ask, Asks) ++ andalso decide_rip_jid(RI#roster.us, User) ++ andalso decide_rip_jid(RI#roster.jid, Contact); ++ _ -> ++ false ++ end. ++ ++%% Returns true if the server of the JID is included in the servers ++decide_rip_jid({UName, UServer, _UResource}, Match_list) -> ++ decide_rip_jid({UName, UServer}, Match_list); ++decide_rip_jid({UName, UServer}, Match_list) -> ++ lists:any( ++ fun(Match_string) -> ++ MJID = jlib:string_to_jid(Match_string), ++ MName = MJID#jid.luser, ++ MServer = MJID#jid.lserver, ++ Is_server = is_glob_match(UServer, MServer), ++ case MName of ++ [] when UName == [] -> ++ Is_server; ++ [] -> ++ false; ++ _ -> ++ Is_server ++ andalso is_glob_match(UName, MName) ++ end ++ end, ++ Match_list). ++ ++%% Copied from ejabberd-2.0.0/src/acl.erl ++is_regexp_match(String, RegExp) -> ++ case ejabberd_regexp:run(String, RegExp) of ++ nomatch -> ++ false; ++ match -> ++ true; ++ {error, ErrDesc} -> ++ io:format( ++ "Wrong regexp ~p in ACL: ~p", ++ [RegExp, ErrDesc]), ++ false ++ end. ++is_glob_match(String, [$! | Glob]) -> ++ not is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)); ++is_glob_match(String, Glob) -> ++ is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)). diff --git a/ejabberd-0003-Fedora-specific-changes-to-ejabberdctl.patch b/ejabberd-0003-Fedora-specific-changes-to-ejabberdctl.patch new file mode 100644 index 0000000..f9cf331 --- /dev/null +++ b/ejabberd-0003-Fedora-specific-changes-to-ejabberdctl.patch @@ -0,0 +1,19 @@ +From: Peter Lemenkov +Date: Tue, 16 Feb 2010 16:30:05 +0300 +Subject: [PATCH] Fedora-specific changes to ejabberdctl + +Signed-off-by: Peter Lemenkov + +diff --git a/ejabberdctl.template b/ejabberdctl.template +index a86694f..09b9947 100755 +--- a/ejabberdctl.template ++++ b/ejabberdctl.template +@@ -22,7 +22,7 @@ if [ "$INSTALLUSER" != "" ] ; then + EXEC_CMD="false" + for GID in `id -G`; do + if [ $GID -eq 0 ] ; then +- EXEC_CMD="su $INSTALLUSER -p -c" ++ EXEC_CMD="/sbin/runuser -s /bin/bash -p ejabberd -c" + fi + done + if [ `id -g` -eq `id -g $INSTALLUSER` ] ; then diff --git a/ejabberd-0003-Introducing-mod_admin_extra.patch b/ejabberd-0003-Introducing-mod_admin_extra.patch deleted file mode 100644 index 3f9d8a7..0000000 --- a/ejabberd-0003-Introducing-mod_admin_extra.patch +++ /dev/null @@ -1,1607 +0,0 @@ -From 363bfab713d9267e3186126d2df4162f24969d8c Mon Sep 17 00:00:00 2001 -From: Badlop -Date: Tue, 16 Feb 2010 16:12:17 +0300 -Subject: [PATCH 03/10] Introducing mod_admin_extra - -Adds the mod_admin_extra module to ejabberd. -This module extends the functionality provided by ejabberdctl -by adding several new commands. - -The code is taken from the ProcessOne repository: - -svn export -r 1125 https://svn.process-one.net/ejabberd-modules/mod_admin_extra/trunk/src/mod_admin_extra.erl ---- - src/ejabberd.app | 1 + - src/mod_admin_extra.erl | 1568 +++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 1569 insertions(+) - create mode 100644 src/mod_admin_extra.erl - -diff --git a/src/ejabberd.app b/src/ejabberd.app -index 1320597..4efc68d 100644 ---- a/src/ejabberd.app -+++ b/src/ejabberd.app -@@ -99,6 +99,7 @@ - mod_vcard, - mod_vcard_ldap, - mod_version, -+ mod_admin_extra, - node_buddy, - node_club, - node_default, -diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl -new file mode 100644 -index 0000000..1cef25a ---- /dev/null -+++ b/src/mod_admin_extra.erl -@@ -0,0 +1,1568 @@ -+%%%------------------------------------------------------------------- -+%%% File : mod_admin_extra.erl -+%%% Author : Badlop -+%%% Purpose : Contributed administrative functions and commands -+%%% Created : 10 Aug 2008 by Badlop -+%%% -+%%% -+%%% ejabberd, Copyright (C) 2002-2008 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. -+%%% -+%%% 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., 59 Temple Place, Suite 330, Boston, MA -+%%% 02111-1307 USA -+%%% -+%%%------------------------------------------------------------------- -+ -+-module(mod_admin_extra). -+-author('badlop@process-one.net'). -+ -+-behaviour(gen_mod). -+ -+-export([start/2, stop/1, -+ %% Node -+ compile/1, -+ load_config/1, -+ get_cookie/0, -+ remove_node/1, -+ export2odbc/2, -+ %% Accounts -+ set_password/3, -+ check_password_hash/4, -+ delete_old_users/1, -+ delete_old_users_vhost/2, -+ ban_account/3, -+ num_active_users/2, -+ %% Sessions -+ num_resources/2, -+ resource_num/3, -+ 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, -+ user_sessions_info/2, -+ %% Vcard -+ set_nickname/3, -+ get_vcard/3, -+ get_vcard/4, -+ get_vcard_multi/4, -+ set_vcard/4, -+ set_vcard/5, -+ %% Roster -+ add_rosteritem/7, -+ delete_rosteritem/4, -+ process_rosteritems/5, -+ get_roster/2, -+ push_roster/3, -+ push_roster_all/1, -+ push_alltoall/2, -+ %% mod_last -+ set_last/4, -+ %% mod_private -+ private_get/4, -+ private_set/3, -+ %% mod_shared_roster -+ srg_create/5, -+ srg_delete/2, -+ srg_list/1, -+ srg_get_info/2, -+ srg_get_members/2, -+ srg_user_add/4, -+ srg_user_del/4, -+ %% Stanza -+ send_message_headline/4, -+ send_message_chat/3, -+ send_stanza_c2s/4, -+ privacy_set/3, -+ %% Stats -+ stats/1, stats/2 -+ ]). -+ -+-include("ejabberd.hrl"). -+-include("ejabberd_commands.hrl"). -+-include("mod_roster.hrl"). -+-include("jlib.hrl"). -+ -+%% Copied from ejabberd_sm.erl -+-record(session, {sid, usr, us, priority, info}). -+ -+ -+%%% -+%%% gen_mod -+%%% -+ -+start(_Host, _Opts) -> -+ ejabberd_commands:register_commands(commands()). -+ -+stop(_Host) -> -+ ejabberd_commands:unregister_commands(commands()). -+ -+ -+%%% -+%%% Register commands -+%%% -+ -+commands() -> -+ 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", -+ -+ 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" -+ " 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}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = load_config, tags = [server], -+ desc = "Load ejabberd configuration file", -+ module = ?MODULE, function = load_config, -+ args = [{file, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = get_cookie, tags = [erlang], -+ desc = "Get the Erlang cookie of this node", -+ module = ?MODULE, function = get_cookie, -+ args = [], -+ result = {cookie, string}}, -+ #ejabberd_commands{name = remove_node, tags = [erlang], -+ desc = "Remove an ejabberd node from Mnesia clustering config", -+ module = ?MODULE, function = remove_node, -+ args = [{node, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = export2odbc, tags = [mnesia], -+ desc = "Export Mnesia tables to files in directory", -+ module = ?MODULE, function = export2odbc, -+ args = [{host, string}, {path, string}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = num_active_users, tags = [accounts, stats], -+ desc = "Get number of users active in the last days", -+ module = ?MODULE, function = num_active_users, -+ args = [{host, string}, {days, integer}], -+ result = {users, integer}}, -+ #ejabberd_commands{name = delete_old_users, tags = [accounts, purge], -+ desc = "Delete users that didn't log in last days, or that never logged", -+ module = ?MODULE, function = delete_old_users, -+ args = [{days, integer}], -+ result = {res, restuple}}, -+ #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", -+ module = ?MODULE, function = delete_old_users_vhost, -+ args = [{host, string}, {days, integer}], -+ result = {res, restuple}}, -+ -+ #ejabberd_commands{name = check_account, tags = [accounts], -+ desc = "Check if an account exists or not", -+ module = ejabberd_auth, function = is_user_exists, -+ args = [{user, string}, {host, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = check_password, tags = [accounts], -+ desc = "Check if a password is correct", -+ module = ejabberd_auth, function = check_password, -+ args = [{user, string}, {host, string}, {password, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = check_password_hash, tags = [accounts], -+ desc = "Check if the password hash is correct", -+ longdesc = "Allowed hash methods: md5, sha.", -+ module = ?MODULE, function = check_password_hash, -+ args = [{user, string}, {host, string}, {passwordhash, string}, {hashmethod, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = change_password, tags = [accounts], -+ desc = "Change the password of an account", -+ module = ?MODULE, function = set_password, -+ args = [{user, string}, {host, string}, {newpass, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = ban_account, tags = [accounts], -+ desc = "Ban an account: kick sessions and set random password", -+ module = ?MODULE, function = ban_account, -+ args = [{user, string}, {host, string}, {reason, string}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = num_resources, tags = [session], -+ desc = "Get the number of resources of a user", -+ module = ?MODULE, function = num_resources, -+ args = [{user, string}, {host, string}], -+ result = {resources, integer}}, -+ #ejabberd_commands{name = resource_num, tags = [session], -+ desc = "Resource string of a session number", -+ module = ?MODULE, function = resource_num, -+ args = [{user, string}, {host, string}, {num, integer}], -+ result = {resource, string}}, -+ #ejabberd_commands{name = kick_session, tags = [session], -+ desc = "Kick a user session", -+ module = ?MODULE, function = kick_session, -+ args = [{user, string}, {host, string}, {resource, string}, {reason, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = status_num_host, tags = [session, stats], -+ desc = "Number of logged users with this status in host", -+ module = ?MODULE, function = status_num, -+ args = [{host, string}, {status, string}], -+ result = {users, integer}}, -+ #ejabberd_commands{name = status_num, tags = [session, stats], -+ desc = "Number of logged users with this status", -+ module = ?MODULE, function = status_num, -+ args = [{status, string}], -+ result = {users, integer}}, -+ #ejabberd_commands{name = status_list_host, tags = [session], -+ desc = "List of users logged in host with their statuses", -+ module = ?MODULE, function = status_list, -+ args = [{host, string}, {status, string}], -+ result = {users, {list, -+ {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, -+ args = [{status, string}], -+ result = {users, {list, -+ {userstatus, {tuple, [ -+ {user, string}, -+ {host, string}, -+ {resource, string}, -+ {priority, integer}, -+ {status, string} -+ ]}} -+ }}}, -+ #ejabberd_commands{name = connected_users_info, -+ tags = [session], -+ desc = "List all established sessions and their information", -+ module = ?MODULE, function = connected_users_info, -+ args = [], -+ result = {connected_users_info, -+ {list, -+ {sessions, {tuple, -+ [{jid, string}, -+ {connection, string}, -+ {ip, string}, -+ {port, integer}, -+ {priority, integer}, -+ {node, string}, -+ {uptime, integer} -+ ]}} -+ }}}, -+ #ejabberd_commands{name = connected_users_vhost, -+ tags = [session], -+ desc = "Get the list of established sessions in a vhost", -+ module = ?MODULE, function = connected_users_vhost, -+ args = [{host, string}], -+ 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, string}, {host, string}], -+ result = {sessions_info, -+ {list, -+ {session, {tuple, -+ [{connection, string}, -+ {ip, string}, -+ {port, integer}, -+ {priority, integer}, -+ {node, string}, -+ {uptime, integer}, -+ {status, string}, -+ {resource, string}, -+ {statustext, string} -+ ]}} -+ }}}, -+ -+ #ejabberd_commands{name = set_presence, -+ tags = [session], -+ desc = "Set presence of a session", -+ module = ?MODULE, function = set_presence, -+ args = [{user, string}, {host, string}, -+ {resource, string}, {type, string}, -+ {show, string}, {status, string}, -+ {priority, string}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = set_nickname, tags = [vcard], -+ desc = "Set nickname in a user's vCard", -+ module = ?MODULE, function = set_nickname, -+ args = [{user, string}, {host, string}, {nickname, string}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = get_vcard, tags = [vcard], -+ desc = "Get content from a vCard field", -+ longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP, -+ module = ?MODULE, function = get_vcard, -+ args = [{user, string}, {host, string}, {name, string}], -+ result = {content, string}}, -+ #ejabberd_commands{name = get_vcard2, tags = [vcard], -+ desc = "Get content from a vCard field", -+ longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, -+ module = ?MODULE, function = get_vcard, -+ args = [{user, string}, {host, string}, {name, string}, {subname, string}], -+ result = {content, string}}, -+ #ejabberd_commands{name = get_vcard2_multi, tags = [vcard], -+ desc = "Get multiple contents from a vCard field (requires exmpp installed)", -+ longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, -+ module = ?MODULE, function = get_vcard_multi, -+ args = [{user, string}, {host, string}, {name, string}, {subname, string}], -+ result = {contents, {list, string}}}, -+ -+ #ejabberd_commands{name = set_vcard, tags = [vcard], -+ desc = "Set content in a vCard field", -+ longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP, -+ module = ?MODULE, function = set_vcard, -+ args = [{user, string}, {host, string}, {name, string}, {content, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = set_vcard2, tags = [vcard], -+ desc = "Set content in a vCard subfield", -+ longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, -+ module = ?MODULE, function = set_vcard, -+ args = [{user, string}, {host, string}, {name, string}, {subname, string}, {content, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = set_vcard2_multi, tags = [vcard], -+ desc = "Set multiple contents in a vCard subfield", -+ longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, -+ module = ?MODULE, function = set_vcard, -+ args = [{user, string}, {host, string}, {name, string}, {subname, string}, {contents, {list, string}}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = add_rosteritem, tags = [roster], -+ desc = "Add an item to a user's roster (supports ODBC)", -+ module = ?MODULE, function = add_rosteritem, -+ args = [{localuser, string}, {localserver, string}, -+ {user, string}, {server, string}, -+ {nick, string}, {group, string}, -+ {subs, string}], -+ 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], -+ desc = "Delete an item from a user's roster (supports ODBC)", -+ module = ?MODULE, function = delete_rosteritem, -+ args = [{localuser, string}, {localserver, string}, -+ {user, string}, {server, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = process_rosteritems, tags = [roster], -+ desc = "List or delete rosteritems that match filtering options", -+ 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" -+ "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*", -+ module = ?MODULE, function = process_rosteritems, -+ args = [{action, string}, {subs, string}, -+ {asks, string}, {users, string}, -+ {contacts, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = get_roster, tags = [roster], -+ desc = "Get roster of a local user", -+ module = ?MODULE, function = get_roster, -+ args = [{user, string}, {host, string}], -+ 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", -+ module = ?MODULE, function = push_roster, -+ args = [{file, string}, {user, string}, {host, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = push_roster_all, tags = [roster], -+ desc = "Push template roster from file to all those users", -+ module = ?MODULE, function = push_roster_all, -+ args = [{file, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = push_alltoall, tags = [roster], -+ desc = "Add all the users to all the users of Host in Group", -+ module = ?MODULE, function = push_alltoall, -+ args = [{host, string}, {group, string}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = set_last, tags = [last], -+ desc = "Set last activity information", -+ longdesc = "Timestamp is the seconds since" -+ "1970-01-01 00:00:00 UTC, for example: date +%s", -+ module = ?MODULE, function = set_last, -+ args = [{user, string}, {host, string}, {timestamp, integer}, {status, string}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = private_get, tags = [private], -+ desc = "Get some information from a user private storage", -+ module = ?MODULE, function = private_get, -+ args = [{user, string}, {host, string}, {element, string}, {ns, string}], -+ result = {res, string}}, -+ #ejabberd_commands{name = private_set, tags = [private], -+ desc = "Set to the user private storage", -+ module = ?MODULE, function = private_set, -+ args = [{user, string}, {host, string}, {element, string}], -+ 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 localhost " -+ "name desc \\\"group1\\\\ngroup2\\\"", -+ module = ?MODULE, function = srg_create, -+ args = [{group, string}, {host, string}, -+ {name, string}, {description, string}, {display, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = srg_delete, tags = [shared_roster_group], -+ desc = "Delete a Shared Roster Group", -+ module = ?MODULE, function = srg_delete, -+ args = [{group, string}, {host, string}], -+ 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, -+ args = [{host, string}], -+ 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, -+ args = [{group, string}, {host, string}], -+ 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, -+ args = [{group, string}, {host, string}], -+ 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, -+ args = [{user, string}, {host, string}, {group, string}, {grouphost, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = srg_user_del, tags = [shared_roster_group], -+ desc = "Delete this JID user@host from the Shared Roster Group", -+ module = ?MODULE, function = srg_user_del, -+ args = [{user, string}, {host, string}, {group, string}, {grouphost, string}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = send_message_chat, tags = [stanza], -+ desc = "Send a chat message to a local or remote bare of full JID", -+ module = ?MODULE, function = send_message_chat, -+ args = [{from, string}, {to, string}, {body, string}], -+ result = {res, rescode}}, -+ #ejabberd_commands{name = send_message_headline, tags = [stanza], -+ desc = "Send a headline message to a local or remote bare of full JID", -+ module = ?MODULE, function = send_message_headline, -+ args = [{from, string}, {to, string}, -+ {subject, string}, {body, string}], -+ 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, -+ args = [{user, string}, {host, string}, {resource, string}, {stanza, string}], -+ 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, -+ args = [{user, string}, {host, string}, {xmlquery, string}], -+ result = {res, rescode}}, -+ -+ #ejabberd_commands{name = stats, tags = [stats], -+ desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds", -+ module = ?MODULE, function = stats, -+ args = [{name, string}], -+ result = {stat, integer}}, -+ #ejabberd_commands{name = stats_host, tags = [stats], -+ desc = "Get statistical value for this host: registeredusers onlineusers", -+ module = ?MODULE, function = stats, -+ args = [{name, string}, {host, string}], -+ result = {stat, integer}} -+ ]. -+ -+ -+%%% -+%%% Node -+%%% -+ -+compile(File) -> -+ case compile:file(File) of -+ ok -> ok; -+ _ -> error -+ end. -+ -+load_config(Path) -> -+ ok = ejabberd_config:load_file(Path). -+ -+get_cookie() -> -+ atom_to_list(erlang:get_cookie()). -+ -+remove_node(Node) -> -+ mnesia:del_table_copy(schema, list_to_atom(Node)), -+ ok. -+ -+export2odbc(Host, Directory) -> -+ Tables = [ -+ {export_last, last}, -+ {export_offline, offline}, -+ {export_passwd, passwd}, -+ {export_private_storage, private_storage}, -+ {export_roster, roster}, -+ {export_vcard, vcard}, -+ {export_vcard_search, vcard_search}], -+ Export = fun({TableFun, Table}) -> -+ Filename = filename:join([Directory, atom_to_list(Table)++".txt"]), -+ io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]), -+ Res = (catch ejd2odbc:TableFun(Host, Filename)), -+ io:format(" Result: ~p~n", [Res]) -+ end, -+ lists:foreach(Export, Tables), -+ ok. -+ -+ -+%%% -+%%% Accounts -+%%% -+ -+set_password(User, Host, Password) -> -+ case ejabberd_auth:set_password(User, Host, Password) of -+ ok -> -+ ok; -+ _ -> -+ error -+ end. -+ -+%% Copied some code from ejabberd_commands.erl -+check_password_hash(User, Host, PasswordHash, HashMethod) -> -+ AccountPass = ejabberd_auth:get_password_s(User, Host), -+ AccountPassHash = case HashMethod of -+ "md5" -> get_md5(AccountPass); -+ "sha" -> get_sha(AccountPass); -+ _ -> undefined -+ end, -+ case AccountPassHash of -+ undefined -> error; -+ PasswordHash -> ok; -+ _ -> error -+ end. -+get_md5(AccountPass) -> -+ lists:flatten([io_lib:format("~.16B", [X]) -+ || X <- binary_to_list(crypto:md5(AccountPass))]). -+get_sha(AccountPass) -> -+ lists:flatten([io_lib:format("~.16B", [X]) -+ || X <- binary_to_list(crypto:sha(AccountPass))]). -+ -+num_active_users(Host, Days) -> -+ list_last_activity(Host, true, Days). -+ -+%% Code based on ejabberd/src/web/ejabberd_web_admin.erl -+list_last_activity(Host, Integral, Days) -> -+ {MegaSecs, Secs, _MicroSecs} = now(), -+ TimeStamp = MegaSecs * 1000000 + Secs, -+ TS = TimeStamp - Days * 86400, -+ case catch mnesia:dirty_select( -+ last_activity, [{{last_activity, {'_', Host}, '$1', '_'}, -+ [{'>', '$1', TS}], -+ [{'trunc', {'/', -+ {'-', TimeStamp, '$1'}, -+ 86400}}]}]) of -+ {'EXIT', _Reason} -> -+ []; -+ Vals -> -+ Hist = histogram(Vals, Integral), -+ if -+ Hist == [] -> -+ 0; -+ true -> -+ Left = Days - length(Hist), -+ Tail = if -+ Integral -> -+ lists:duplicate(Left, lists:last(Hist)); -+ true -> -+ lists:duplicate(Left, 0) -+ end, -+ lists:nth(Days, Hist ++ Tail) -+ end -+ end. -+histogram(Values, Integral) -> -+ histogram(lists:sort(Values), Integral, 0, 0, []). -+histogram([H | T], Integral, Current, Count, Hist) when Current == H -> -+ histogram(T, Integral, Current, Count + 1, Hist); -+histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H -> -+ if -+ Integral -> -+ histogram(Values, Integral, Current + 1, Count, [Count | Hist]); -+ true -> -+ histogram(Values, Integral, Current + 1, 0, [Count | Hist]) -+ end; -+histogram([], _Integral, _Current, Count, Hist) -> -+ if -+ Count > 0 -> -+ lists:reverse([Count | Hist]); -+ true -> -+ lists:reverse(Hist) -+ end. -+ -+ -+delete_old_users(Days) -> -+ %% Get the list of registered users -+ Users = ejabberd_auth:dirty_get_registered_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 -+ Users = ejabberd_auth:get_vh_registered_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) -> -+ %% Convert older time -+ SecOlder = Days*24*60*60, -+ -+ %% Get current time -+ {MegaSecs, Secs, _MicroSecs} = now(), -+ TimeStamp_now = MegaSecs * 1000000 + Secs, -+ -+ %% For a user, remove if required and answer true -+ F = fun({LUser, LServer}) -> -+ %% Check if the user is logged -+ case ejabberd_sm:get_user_resources(LUser, LServer) of -+ %% If it isnt -+ [] -> -+ %% Look for his last_activity -+ case (get_lastactivity_module(LServer)):get_last_info(LUser, LServer) of -+ %% If it is -+ %% existent: -+ {ok, TimeStamp, _Status} -> -+ %% get his age -+ Sec = TimeStamp_now - TimeStamp, -+ %% If he is -+ if -+ %% younger than SecOlder: -+ Sec < SecOlder -> -+ %% do nothing -+ false; -+ %% older: -+ true -> -+ %% remove the user -+ ejabberd_auth:remove_user(LUser, LServer), -+ true -+ end; -+ %% nonexistent: -+ not_found -> -+ %% remove the user -+ ejabberd_auth:remove_user(LUser, LServer), -+ true -+ end; -+ %% Else -+ _ -> -+ %% do nothing -+ false -+ end -+ end, -+ %% Apply the function to every user in the list -+ Users_removed = lists:filter(F, Users), -+ {removed, length(Users_removed), Users_removed}. -+ -+get_lastactivity_module(Server) -> -+ case lists:member(mod_last, gen_mod:loaded_modules(Server)) of -+ true -> mod_last; -+ _ -> mod_last_odbc -+ 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, -+ get_resources(User, Server)). -+ -+get_resources(User, Server) -> -+ lists:map( -+ fun(Session) -> -+ element(3, Session#session.usr) -+ end, -+ get_sessions(User, Server)). -+ -+get_sessions(User, Server) -> -+ LUser = jlib:nodeprep(User), -+ LServer = jlib:nameprep(Server), -+ Sessions = mnesia:dirty_index_read(session, {LUser, LServer}, #session.us), -+ true = is_list(Sessions), -+ Sessions. -+ -+set_random_password(User, Server, Reason) -> -+ NewPass = build_random_password(Reason), -+ set_password_auth(User, Server, NewPass). -+ -+build_random_password(Reason) -> -+ Date = jlib:timestamp_to_iso(calendar:universal_time()), -+ RandomString = randoms:get_string(), -+ "BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason. -+ -+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_list(Reason) -> -+ Reason; -+prepare_reason(StringList) -> -+ string:join(StringList, "_"). -+ -+ -+%%% -+%%% 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 -+ lists:nth(Num, Resources); -+ false -> -+ lists:flatten(io_lib:format("Error: 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_router:route( -+ jlib:make_jid("", "", ""), -+ jlib:make_jid(User, Server, Resource), -+ {xmlelement, "broadcast", [], [{exit, Reason}]}). -+ -+ -+status_num(Host, Status) -> -+ length(get_status_list(Host, Status)). -+status_num(Status) -> -+ status_num("all", Status). -+status_list(Host, Status) -> -+ Res = get_status_list(Host, Status), -+ [{U, S, R, P, St} || {U, S, R, P, St} <- Res]. -+status_list(Status) -> -+ 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 -+ "all" -> -+ %% All hosts are requested, so dont 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 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3], -+ %% Filter by status -+ Fstatus = case Status_required of -+ "all" -> -+ fun(_, _) -> true end; -+ _ -> -+ fun(A, B) -> A == B end -+ end, -+ [{User, Server, Resource, Priority, stringize(Status_text)} -+ || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4, -+ apply(Fstatus, [Status, Status_required])]. -+ -+connected_users_info() -> -+ USRIs = dirty_get_sessions_list2(), -+ CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}), -+ lists:map( -+ fun([{U, S, R}, {Now, Pid}, 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)), -+ {[U, $@, S, $/, R], atom_to_list(Conn), IPS, Port, Priority, NodeS, Uptime} -+ end, -+ USRIs). -+ -+connected_users_vhost(Host) -> -+ USRs = ejabberd_sm:get_vh_session_list(Host), -+ [ [U, $@, S, $/, R] || {U, S, R} <- USRs]. -+ -+%% Code copied from ejabberd_sm.erl and customized -+dirty_get_sessions_list2() -> -+ mnesia:dirty_select( -+ session, -+ [{#session{usr = '$1', sid = '$2', priority = '$3', info = '$4', _ = '_'}, -+ [], -+ [['$1', '$2', '$3', '$4']]}]). -+ -+%% Make string more print-friendly -+stringize(String) -> -+ %% Replace newline characters with other code -+ ejabberd_regexp:greplace(String, "\n", "\\n"). -+ -+set_presence(User, Host, Resource, Type, Show, Status, Priority) -> -+ Pid = ejabberd_sm:get_session_pid(User, Host, Resource), -+ USR = User ++ "@" ++ Host ++ "/" ++ Resource, -+ US = User ++ "@" ++ Host, -+ Message = {route_xmlstreamelement, -+ {xmlelement, "presence", -+ [{"from", USR}, {"to", US}, {"type", Type}], -+ [{xmlelement, "show", [], [{xmlcdata, Show}]}, -+ {xmlelement, "status", [], [{xmlcdata, Status}]}, -+ {xmlelement, "priority", [], [{xmlcdata, Priority}]}]}}, -+ Pid ! Message. -+ -+user_sessions_info(User, Host) -> -+ CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}), -+ US = {User, Host}, -+ Sessions = case catch mnesia:dirty_index_read(session, US, #session.us) of -+ {'EXIT', _Reason} -> -+ []; -+ Ss -> -+ Ss -+ end, -+ lists:map( -+ fun(Session) -> -+ {_U, _S, Resource} = Session#session.usr, -+ {Now, Pid} = Session#session.sid, -+ {_U, _Resource, Status, StatusText} = ejabberd_c2s:get_presence(Pid), -+ Info = Session#session.info, -+ Priority = Session#session.priority, -+ 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, Priority, NodeS, Uptime, Status, Resource, StatusText} -+ end, -+ Sessions). -+ -+ -+%%% -+%%% Vcard -+%%% -+ -+set_nickname(User, Host, Nickname) -> -+ R = mod_vcard:process_sm_iq( -+ {jid, User, Host, "", User, Host, ""}, -+ {jid, User, Host, "", User, Host, ""}, -+ {iq, "", set, "", "en", -+ {xmlelement, "vCard", -+ [{"xmlns", "vcard-temp"}], [ -+ {xmlelement, "NICKNAME", [], [{xmlcdata, Nickname}]} -+ ] -+ }}), -+ case R of -+ {iq, [], result, [], _L, []} -> -+ ok; -+ _ -> -+ error -+ 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_module_resource(Server) -> -+ case gen_mod:get_module_opt(Server, ?MODULE, module_resource, none) of -+ none -> atom_to_list(?MODULE); -+ R when is_list(R) -> R -+ end. -+ -+get_vcard_content(User, Server, Data) -> -+ [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}), -+ JID = jlib:make_jid(User, Server, get_module_resource(Server)), -+ IQ = #iq{type = get, xmlns = ?NS_VCARD}, -+ IQr = Module:Function(JID, JID, IQ), -+ case IQr#iq.sub_el of -+ [A1] -> -+ case get_vcard(Data, A1) of -+ [] -> throw(error_no_value_found_in_vcard); -+ ElemList -> [xml:get_tag_cdata(Elem) || Elem <- ElemList] -+ end; -+ [] -> -+ throw(error_no_vcard_found) -+ end. -+ -+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) -> -+ case code:ensure_loaded(exmpp_xml) of -+ {error, _} -> -+ [get_subtag_xml(Xmlelement, Name)]; -+ {module, exmpp_xml} -> -+ get_subtag_exmpp(Xmlelement, Name) -+ end. -+ -+get_subtag_xml(Xmlelement, Name) -> -+ xml:get_subtag(Xmlelement, Name). -+ -+get_subtag_exmpp(Xmlelement, Name) -> -+ Xmlel = exmpp_xml:xmlelement_to_xmlel(Xmlelement), -+ XmlelList = exmpp_xml:get_elements(Xmlel, Name), -+ [exmpp_xml:xmlel_to_xmlelement(Xmlel2) || Xmlel2 <- XmlelList]. -+ -+set_vcard_content(User, Server, Data, SomeContent) -> -+ ContentList = case SomeContent of -+ [Char | _] when not is_list(Char) -> [SomeContent]; -+ [Char | _] when is_list(Char) -> SomeContent -+ end, -+ [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}), -+ JID = jlib:make_jid(User, Server, get_module_resource(Server)), -+ IQ = #iq{type = get, xmlns = ?NS_VCARD}, -+ IQr = Module:Function(JID, JID, IQ), -+ -+ %% Get old vcard -+ A4 = case IQr#iq.sub_el of -+ [A1] -> -+ {_, _, _, A2} = A1, -+ update_vcard_els(Data, ContentList, A2); -+ [] -> -+ update_vcard_els(Data, ContentList, []) -+ end, -+ -+ %% Build new vcard -+ SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4}, -+ IQ2 = #iq{type=set, sub_el = SubEl}, -+ -+ Module:Function(JID, JID, IQ2), -+ ok. -+ -+update_vcard_els(Data, ContentList, Els1) -> -+ Els2 = lists:keysort(2, Els1), -+ [Data1 | Data2] = Data, -+ NewEls = case Data2 of -+ [] -> -+ [{xmlelement, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList]; -+ [D2] -> -+ OldEl = case lists:keysearch(Data1, 2, Els2) of -+ {value, A} -> A; -+ false -> {xmlelement, Data1, [], []} -+ end, -+ {xmlelement, _, _, ContentOld1} = OldEl, -+ Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]} || Content <- ContentList], -+ ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2], -+ ContentOld3 = lists:keysort(2, ContentOld2), -+ ContentNew = lists:keymerge(2, Content2, ContentOld3), -+ [{xmlelement, Data1, [], ContentNew}] -+ end, -+ Els3 = lists:keydelete(Data1, 2, Els2), -+ lists:keymerge(2, NewEls, Els3). -+ -+ -+%%% -+%%% Roster -+%%% -+ -+add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> -+ case add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, list_to_atom(Subs), []) of -+ {atomic, ok} -> -+ push_roster_item(LocalUser, LocalServer, User, Server, {add, Nick, Subs, Group}), -+ ok; -+ _ -> -+ error -+ end. -+ -+add_rosteritem(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) -> -+ subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs). -+ -+subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) -> -+ SubscriptionS = case is_atom(Subscription) of -+ true -> atom_to_list(Subscription); -+ false -> Subscription -+ end, -+ ItemEl = build_roster_item(User, Server, {add, Nick, SubscriptionS, Group}), -+ {ok, M} = loaded_module(LS,[mod_roster_odbc,mod_roster]), -+ M:set_items( -+ LU, LS, -+ {xmlelement,"query", -+ [{"xmlns","jabber:iq:roster"}], -+ [ItemEl]}). -+ -+delete_rosteritem(LocalUser, LocalServer, User, Server) -> -+ case unsubscribe(LocalUser, LocalServer, User, Server) of -+ {atomic, ok} -> -+ push_roster_item(LocalUser, LocalServer, User, Server, remove), -+ ok; -+ _ -> -+ error -+ end. -+ -+unsubscribe(LU, LS, User, Server) -> -+ ItemEl = build_roster_item(User, Server, remove), -+ {ok, M} = loaded_module(LS,[mod_roster_odbc,mod_roster]), -+ M:set_items( -+ LU, LS, -+ {xmlelement,"query", -+ [{"xmlns","jabber:iq:roster"}], -+ [ItemEl]}). -+ -+loaded_module(Domain,Options) -> -+ LoadedModules = gen_mod:loaded_modules(Domain), -+ case lists:filter(fun(Module) -> -+ lists:member(Module, LoadedModules) -+ end, Options) of -+ [M|_] -> {ok, M}; -+ [] -> {error,not_found} -+ 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 = jlib:jid_to_string(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 -+ [] -> [""]; -+ 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), -+ 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(Name1, Server1, Name2, Server2, Nick2, Group2, both, []), -+ subscribe_roster({Name1, Server1, Group1, Nick1}, Roster). -+ -+push_alltoall(S, G) -> -+ Users = ejabberd_auth:get_vh_registered_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 = jlib:make_jid(LU, LS, R), -+ BroadcastEl = build_broadcast(U, S, Action), -+ ejabberd_router:route(LJID, LJID, BroadcastEl), -+ Item = build_roster_item(U, S, Action), -+ ResIQ = build_iq_roster_push(Item), -+ ejabberd_router:route(LJID, LJID, ResIQ). -+ -+build_roster_item(U, S, {add, Nick, Subs, Group}) -> -+ {xmlelement, "item", -+ [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))}, -+ {"name", Nick}, -+ {"subscription", Subs}], -+ [{xmlelement, "group", [], [{xmlcdata, Group}]}] -+ }; -+build_roster_item(U, S, remove) -> -+ {xmlelement, "item", -+ [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))}, -+ {"subscription", "remove"}], -+ [] -+ }. -+ -+build_iq_roster_push(Item) -> -+ {xmlelement, "iq", -+ [{"type", "set"}, {"id", "push"}], -+ [{xmlelement, "query", -+ [{"xmlns", ?NS_ROSTER}], -+ [Item] -+ } -+ ] -+ }. -+ -+build_broadcast(U, S, {add, _Nick, Subs, _Group}) -> -+ build_broadcast(U, S, list_to_atom(Subs)); -+build_broadcast(U, S, remove) -> -+ build_broadcast(U, S, none); -+%% @spec (U::string(), S::string(), Subs::atom()) -> any() -+%% Subs = both | from | to | none -+build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) -> -+ {xmlelement, "broadcast", [], -+ [{item, {U, S, ""}, SubsAtom}] -+ }. -+ -+%%% -+%%% Last Activity -+%%% -+ -+set_last(User, Server, Timestamp, Status) -> -+ Mod = get_lastactivity_module(Server), -+ Mod:store_last_info(User, Server, Timestamp, Status). -+ -+%%% -+%%% Private Storage -+%%% -+ -+%% Example usage: -+%% $ ejabberdctl private_set badlop localhost "\Cluth\" -+%% $ ejabberdctl private_get badlop localhost aa bb -+%% Cluth -+ -+private_get(Username, Host, Element, Ns) -> -+ From = jlib:make_jid(Username, Host, ""), -+ To = jlib:make_jid(Username, Host, ""), -+ IQ = {iq, "", get, ?NS_PRIVATE, "", -+ {xmlelement,"query", -+ [{"xmlns",?NS_PRIVATE}], -+ [{xmlelement, Element, [{"xmlns", Ns}], []}]}}, -+ ResIq = mod_private:process_sm_iq(From, To, IQ), -+ [{xmlelement,"query", -+ [{"xmlns","jabber:iq:private"}], -+ [SubEl]}] = ResIq#iq.sub_el, -+ xml:element_to_string(SubEl). -+ -+private_set(Username, Host, ElementString) -> -+ case xml_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) -> -+ From = jlib:make_jid(Username, Host, ""), -+ To = jlib:make_jid(Username, Host, ""), -+ IQ = {iq, "", set, ?NS_PRIVATE, "", -+ {xmlelement,"query", -+ [{"xmlns",?NS_PRIVATE}], -+ [Xml]}}, -+ mod_private:process_sm_iq(From, To, IQ), -+ ok. -+ -+%%% -+%%% Shared Roster Groups -+%%% -+ -+srg_create(Group, Host, Name, Description, Display) -> -+ DisplayList = case Display of -+ [] -> []; -+ _ -> ejabberd_regexp:split(Display, "\\\\n") -+ end, -+ Opts = [{name, Name}, -+ {displayed_groups, DisplayList}, -+ {description, Description}], -+ {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts), -+ ok. -+ -+srg_delete(Group, Host) -> -+ {atomic, ok} = 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 = mod_shared_roster:get_group_opts(Host,Group), -+ [{io_lib:format("~p", [Title]), -+ io_lib:format("~p", [Value])} || {Title, Value} <- Opts]. -+ -+srg_get_members(Group, Host) -> -+ Members = mod_shared_roster:get_group_explicit_users(Host,Group), -+ [jlib:jid_to_string(jlib:make_jid(MUser, MServer, "")) -+ || {MUser, MServer} <- Members]. -+ -+srg_user_add(User, Host, Group, GroupHost) -> -+ {atomic, ok} = mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group), -+ ok. -+ -+srg_user_del(User, Host, Group, GroupHost) -> -+ {atomic, ok} = mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group), -+ ok. -+ -+ -+%%% -+%%% Stanza -+%%% -+ -+%% @doc Send a chat message to a Jabber account. -+%% @spec (From::string(), To::string(), Body::string()) -> ok -+send_message_chat(From, To, Body) -> -+ Packet = build_packet(message_chat, [Body]), -+ send_packet_all_resources(From, To, Packet). -+ -+%% @doc Send a headline message to a Jabber account. -+%% @spec (From::string(), To::string(), Subject::string(), Body::string()) -> ok -+send_message_headline(From, To, Subject, Body) -> -+ Packet = build_packet(message_headline, [Subject, Body]), -+ send_packet_all_resources(From, To, Packet). -+ -+%% @doc Send a packet to a Jabber account. -+%% If a resource was specified in the JID, -+%% the packet is sent only to that specific resource. -+%% If no resource was specified in the JID, -+%% and the user is remote or local but offline, -+%% the packet is sent to the bare JID. -+%% If the user is local and is online in several resources, -+%% the packet is sent to all its resources. -+send_packet_all_resources(FromJIDString, ToJIDString, Packet) -> -+ FromJID = jlib:string_to_jid(FromJIDString), -+ ToJID = jlib:string_to_jid(ToJIDString), -+ ToUser = ToJID#jid.user, -+ ToServer = ToJID#jid.server, -+ case ToJID#jid.resource of -+ "" -> -+ send_packet_all_resources(FromJID, ToUser, ToServer, Packet); -+ Res -> -+ send_packet_all_resources(FromJID, ToUser, ToServer, Res, Packet) -+ end. -+ -+send_packet_all_resources(FromJID, ToUser, ToServer, Packet) -> -+ case ejabberd_sm:get_user_resources(ToUser, ToServer) of -+ [] -> -+ send_packet_all_resources(FromJID, ToUser, ToServer, "", Packet); -+ ToResources -> -+ lists:foreach( -+ fun(ToResource) -> -+ send_packet_all_resources(FromJID, ToUser, ToServer, -+ ToResource, Packet) -+ end, -+ ToResources) -+ end. -+ -+send_packet_all_resources(FromJID, ToU, ToS, ToR, Packet) -> -+ ToJID = jlib:make_jid(ToU, ToS, ToR), -+ ejabberd_router:route(FromJID, ToJID, Packet). -+ -+ -+build_packet(message_chat, [Body]) -> -+ {xmlelement, "message", -+ [{"type", "chat"}, {"id", randoms:get_string()}], -+ [{xmlelement, "body", [], [{xmlcdata, Body}]}] -+ }; -+build_packet(message_headline, [Subject, Body]) -> -+ {xmlelement, "message", -+ [{"type", "headline"}, {"id", randoms:get_string()}], -+ [{xmlelement, "subject", [], [{xmlcdata, Subject}]}, -+ {xmlelement, "body", [], [{xmlcdata, Body}]} -+ ] -+ }. -+ -+send_stanza_c2s(Username, Host, Resource, Stanza) -> -+ C2sPid = ejabberd_sm:get_session_pid(Username, Host, Resource), -+ XmlEl = xml_stream:parse_element(Stanza), -+ p1_fsm:send_event(C2sPid, {xmlstreamelement, XmlEl}). -+ -+privacy_set(Username, Host, QueryS) -> -+ From = jlib:string_to_jid(Username ++ "@" ++ Host), -+ To = jlib:string_to_jid(Host), -+ QueryEl = xml_stream:parse_element(QueryS), -+ StanzaEl = {xmlelement, "iq", [{"type", "set"}], [QueryEl]}, -+ IQ = jlib:iq_query_info(StanzaEl), -+ ejabberd_hooks:run_fold( -+ privacy_iq_set, -+ Host, -+ {error, ?ERR_FEATURE_NOT_IMPLEMENTED}, -+ [From, To, IQ] -+ ), -+ ok. -+ -+%%% -+%%% Stats -+%%% -+ -+stats(Name) -> -+ case Name of -+ "uptimeseconds" -> trunc(element(1, erlang:statistics(wall_clock))/1000); -+ "registeredusers" -> length(ejabberd_auth:dirty_get_registered_users()); -+ "onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list()); -+ "onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list()) -+ end. -+ -+stats(Name, Host) -> -+ case Name of -+ "registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host)); -+ "onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host)) -+ end. -+ -+ -+ -+%%----------------------------- -+%% Purge roster items -+%%----------------------------- -+ -+process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) -> -+ Action = case ActionS of -+ "list" -> list; -+ "delete" -> delete -+ end, -+ -+ Subs = lists:foldl( -+ fun(any, _) -> [none, from, to, both]; -+ (Sub, Subs) -> [Sub | Subs] -+ end, -+ [], -+ [list_to_atom(S) || S <- string:tokens(SubsS, ":")] -+ ), -+ -+ Asks = lists:foldl( -+ fun(any, _) -> [none, out, in]; -+ (Ask, Asks) -> [Ask | Asks] -+ end, -+ [], -+ [list_to_atom(S) || S <- string:tokens(AsksS, ":")] -+ ), -+ -+ Users = lists:foldl( -+ fun("any", _) -> ["*", "*@*"]; -+ (U, Us) -> [U | Us] -+ end, -+ [], -+ [S || S <- string:tokens(UsersS, ":")] -+ ), -+ -+ Contacts = lists:foldl( -+ fun("any", _) -> ["*", "*@*"]; -+ (U, Us) -> [U | Us] -+ end, -+ [], -+ [S || S <- string:tokens(ContactsS, ":")] -+ ), -+ -+ case rosteritem_purge({Action, Subs, Asks, Users, Contacts}) of -+ {atomic, ok} -> -+ ok; -+ {error, Reason} -> -+ io:format("Error purging rosteritems: ~p~n", [Reason]), -+ error; -+ {badrpc, Reason} -> -+ io:format("BadRPC purging rosteritems: ~p~n", [Reason]), -+ error -+ end. -+ -+%% @spec ({Action::atom(), Subs::[atom()], Asks::[atom()], User::string(), Contact::string()}) -> {atomic, ok} -+rosteritem_purge(Options) -> -+ Num_rosteritems = mnesia:table_info(roster, size), -+ io:format("There are ~p roster items in total.~n", [Num_rosteritems]), -+ Key = mnesia:dirty_first(roster), -+ ok = rip(Key, Options, {0, Num_rosteritems, 0, 0}), -+ {atomic, ok}. -+ -+rip('$end_of_table', _Options, Counters) -> -+ print_progress_line(Counters), -+ ok; -+rip(Key, Options, {Pr, NT, NV, ND}) -> -+ Key_next = mnesia:dirty_next(roster, Key), -+ {Action, _, _, _, _} = Options, -+ ND2 = case decide_rip(Key, Options) of -+ true -> -+ apply_action(Action, Key), -+ ND+1; -+ false -> -+ ND -+ end, -+ NV2 = NV+1, -+ Pr2 = print_progress_line({Pr, NT, NV2, ND2}), -+ rip(Key_next, Options, {Pr2, NT, NV2, ND2}). -+ -+apply_action(list, Key) -> -+ {User, Server, JID} = Key, -+ {RUser, RServer, _} = JID, -+ io:format("Matches: ~s@~s ~s@~s~n", [User, Server, RUser, RServer]); -+apply_action(delete, Key) -> -+ apply_action(list, Key), -+ mnesia:dirty_delete(roster, Key). -+ -+print_progress_line({Pr, NT, NV, ND}) -> -+ Pr2 = trunc((NV/NT)*100), -+ case Pr == Pr2 of -+ true -> -+ ok; -+ false -> -+ io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND]) -+ end, -+ Pr2. -+ -+decide_rip(Key, {_Action, Subs, Asks, User, Contact}) -> -+ case catch mnesia:dirty_read(roster, Key) of -+ [RI] -> -+ lists:member(RI#roster.subscription, Subs) -+ andalso lists:member(RI#roster.ask, Asks) -+ andalso decide_rip_jid(RI#roster.us, User) -+ andalso decide_rip_jid(RI#roster.jid, Contact); -+ _ -> -+ false -+ end. -+ -+%% Returns true if the server of the JID is included in the servers -+decide_rip_jid({UName, UServer, _UResource}, Match_list) -> -+ decide_rip_jid({UName, UServer}, Match_list); -+decide_rip_jid({UName, UServer}, Match_list) -> -+ lists:any( -+ fun(Match_string) -> -+ MJID = jlib:string_to_jid(Match_string), -+ MName = MJID#jid.luser, -+ MServer = MJID#jid.lserver, -+ Is_server = is_glob_match(UServer, MServer), -+ case MName of -+ [] when UName == [] -> -+ Is_server; -+ [] -> -+ false; -+ _ -> -+ Is_server -+ andalso is_glob_match(UName, MName) -+ end -+ end, -+ Match_list). -+ -+%% Copied from ejabberd-2.0.0/src/acl.erl -+is_regexp_match(String, RegExp) -> -+ case ejabberd_regexp:run(String, RegExp) of -+ nomatch -> -+ false; -+ match -> -+ true; -+ {error, ErrDesc} -> -+ io:format( -+ "Wrong regexp ~p in ACL: ~p", -+ [RegExp, ErrDesc]), -+ false -+ end. -+is_glob_match(String, [$! | Glob]) -> -+ not is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)); -+is_glob_match(String, Glob) -> -+ is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)). --- -1.8.3.1 - diff --git a/ejabberd-0004-Fedora-specific-changes-to-ejabberdctl.patch b/ejabberd-0004-Fedora-specific-changes-to-ejabberdctl.patch deleted file mode 100644 index 20cf399..0000000 --- a/ejabberd-0004-Fedora-specific-changes-to-ejabberdctl.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 2e72b2ac86fcbc5902555621422db36684d42385 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Tue, 16 Feb 2010 16:30:05 +0300 -Subject: [PATCH 04/10] Fedora-specific changes to ejabberdctl - -Signed-off-by: Peter Lemenkov ---- - src/ejabberdctl.template | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template -index 461ec1a..b298e01 100644 ---- a/src/ejabberdctl.template -+++ b/src/ejabberdctl.template -@@ -9,10 +9,10 @@ ERL_MAX_ETS_TABLES=1400 - - # define default environment variables - NODE=ejabberd --HOST=localhost -+HOST=`hostname -s 2>/dev/null || echo "localhost"` - ERLANG_NODE=$NODE@$HOST - ERL=@erl@ --INSTALLUSER=@installuser@ -+INSTALLUSER=ejabberd - - # parse command line parameters - ARGS= -@@ -46,7 +46,7 @@ if [ "$LOGS_DIR" = "" ] ; then - LOGS_DIR=@LOCALSTATEDIR@/log/ejabberd - fi - if [ "$SPOOLDIR" = "" ] ; then -- SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd -+ SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd/spool - fi - if [ "$EJABBERD_DOC_PATH" = "" ] ; then - EJABBERD_DOC_PATH=@DOCDIR@ -@@ -62,7 +62,7 @@ EJID=`id -g $INSTALLUSER` - EXEC_CMD="false" - for GID in $GIDS; do - if [ $GID -eq 0 ] ; then -- EXEC_CMD="su ${INSTALLUSER} -p -c" -+ EXEC_CMD="/sbin/runuser -s /bin/bash -p ejabberd -c" - fi - done - if [ "$ID" -eq "$EJID" ] ; then --- -1.8.3.1 - diff --git a/ejabberd-0004-Install-.so-objects-with-0755-permissions.patch b/ejabberd-0004-Install-.so-objects-with-0755-permissions.patch new file mode 100644 index 0000000..dbf4fbf --- /dev/null +++ b/ejabberd-0004-Install-.so-objects-with-0755-permissions.patch @@ -0,0 +1,19 @@ +From: Peter Lemenkov +Date: Sat, 12 Jun 2010 14:14:52 +0400 +Subject: [PATCH] Install *.so objects with 0755 permissions + +Signed-off-by: Peter Lemenkov + +diff --git a/Makefile.in b/Makefile.in +index 018ccd7..58d3486 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -156,7 +156,7 @@ install: all + # + # Binary system libraries + $(INSTALL) -d $(SODIR) +- $(INSTALL) -m 644 $(DLLs) $(SODIR) ++ $(INSTALL) -m 755 $(DLLs) $(SODIR) + # + # Translated strings + $(INSTALL) -d $(MSGSDIR) diff --git a/ejabberd-0005-Clean-up-false-security-measure.patch b/ejabberd-0005-Clean-up-false-security-measure.patch new file mode 100644 index 0000000..a6a99fd --- /dev/null +++ b/ejabberd-0005-Clean-up-false-security-measure.patch @@ -0,0 +1,19 @@ +From: Peter Lemenkov +Date: Wed, 17 Jul 2013 14:56:09 +0400 +Subject: [PATCH] Clean up false security measure + +Signed-off-by: Peter Lemenkov + +diff --git a/Makefile.in b/Makefile.in +index 58d3486..de47be2 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -127,7 +127,7 @@ install: all + # + # Administration script + [ -d $(SBINDIR) ] || $(INSTALL) -d -m 755 $(SBINDIR) +- $(INSTALL) -m 550 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl ++ $(INSTALL) -m 755 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl + # + # Init script + $(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*" \ diff --git a/ejabberd-0005-Install-.so-objects-with-0755-permissions.patch b/ejabberd-0005-Install-.so-objects-with-0755-permissions.patch deleted file mode 100644 index af85b3c..0000000 --- a/ejabberd-0005-Install-.so-objects-with-0755-permissions.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 75f9fdbe72c77c1521edc7402c0d27883dadf46c Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Sat, 12 Jun 2010 14:14:52 +0400 -Subject: [PATCH 05/10] Install *.so objects with 0755 permissions - -Signed-off-by: Peter Lemenkov ---- - src/Makefile.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/Makefile.in b/src/Makefile.in -index 42af5b2..64ed856 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -237,7 +237,7 @@ install: all - # - # Binary system libraries - install -d $(SODIR) -- install -m 644 *.so $(SODIR) -+ install -m 755 *.so $(SODIR) - # - # Translated strings - install -d $(MSGSDIR) --- -1.8.3.1 - diff --git a/ejabberd-0006-Enable-polkit-support.patch b/ejabberd-0006-Enable-polkit-support.patch new file mode 100644 index 0000000..715c5a4 --- /dev/null +++ b/ejabberd-0006-Enable-polkit-support.patch @@ -0,0 +1,16 @@ +From: Peter Lemenkov +Date: Wed, 17 Jul 2013 14:51:04 +0400 +Subject: [PATCH] Enable polkit support + +Signed-off-by: Peter Lemenkov + +diff --git a/ejabberdctl.template b/ejabberdctl.template +index 09b9947..0fd33fe 100755 +--- a/ejabberdctl.template ++++ b/ejabberdctl.template +@@ -1,4 +1,4 @@ +-#!/bin/sh ++#!/usr/bin/pkexec /bin/sh + + # define default configuration + POLL=true diff --git a/ejabberd-0006-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch b/ejabberd-0006-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch deleted file mode 100644 index a9b4dda..0000000 --- a/ejabberd-0006-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch +++ /dev/null @@ -1,505 +0,0 @@ -From e49dbaca001a3d311a2f8a8e878c5b8b6fc385c0 Mon Sep 17 00:00:00 2001 -From: Badlop -Date: Thu, 15 Apr 2010 17:20:16 +0200 -Subject: [PATCH 06/10] Support SASL GSSAPI authentication (thanks to Mikael - Magnusson)(EJAB-831) - ---- - src/cyrsasl.erl | 71 +++++++++++++++----- - src/cyrsasl.hrl | 15 +++++ - src/cyrsasl_anonymous.erl | 6 +- - src/cyrsasl_digest.erl | 6 +- - src/cyrsasl_gssapi.erl | 166 ++++++++++++++++++++++++++++++++++++++++++++++ - src/cyrsasl_plain.erl | 6 +- - src/cyrsasl_scram.erl | 5 +- - src/ejabberd_c2s.erl | 12 +++- - src/ejabberd_socket.erl | 19 ++++++ - 9 files changed, 281 insertions(+), 25 deletions(-) - create mode 100644 src/cyrsasl.hrl - create mode 100644 src/cyrsasl_gssapi.erl - -diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl -index 9d1377f..802c679 100644 ---- a/src/cyrsasl.erl -+++ b/src/cyrsasl.erl -@@ -30,21 +30,36 @@ - -export([start/0, - register_mechanism/3, - listmech/1, -- server_new/7, -+ server_new/8, - server_start/3, - server_step/2]). - -+-include("cyrsasl.hrl"). - -include("ejabberd.hrl"). - -+%% @type saslmechanism() = {sasl_mechanism, Mechanism, Module, Require_Plain} -+%% Mechanism = string() -+%% Module = atom() -+%% Require_Plain = bool(). -+%% Registry entry of a supported SASL mechanism. -+ - -record(sasl_mechanism, {mechanism, module, password_type}). ---record(sasl_state, {service, myname, realm, -- get_password, check_password, check_password_digest, -- mech_mod, mech_state}). -+ -+%% @type saslstate() = {sasl_state, Service, Myname, Mech_Mod, Mech_State, Params} -+%% Service = string() -+%% Myname = string() -+%% Mech_Mod = atom() -+%% Mech_State = term(). -+%% Params = term(). -+%% State of this process. -+ -+-record(sasl_state, {service, myname, -+ mech_mod, mech_state, params}). - - -export([behaviour_info/1]). - - behaviour_info(callbacks) -> -- [{mech_new, 4}, {mech_step, 2}]; -+ [{mech_new, 1}, {mech_step, 2}]; - behaviour_info(_Other) -> - undefined. - -@@ -56,8 +71,30 @@ start() -> - cyrsasl_digest:start([]), - cyrsasl_scram:start([]), - cyrsasl_anonymous:start([]), -+ maybe_try_start_gssapi(), - ok. - -+maybe_try_start_gssapi() -> -+ case os:getenv("KRB5_KTNAME") of -+ false -> -+ ok; -+ _String -> -+ try_start_gssapi() -+ end. -+ -+try_start_gssapi() -> -+ case code:load_file(esasl) of -+ {module, _Module} -> -+ cyrsasl_gssapi:start([]); -+ {error, What} -> -+ ?ERROR_MSG("Support for GSSAPI not started because esasl.beam was not found: ~p", [What]) -+ end. -+ -+%% @spec (Mechanism, Module, Require_Plain) -> true -+%% Mechanism = string() -+%% Module = atom() -+%% Require_Plain = bool() -+ - register_mechanism(Mechanism, Module, PasswordType) -> - ets:insert(sasl_mechanism, - #sasl_mechanism{mechanism = Mechanism, -@@ -119,24 +156,28 @@ listmech(Host) -> - filter_anonymous(Host, Mechs). - - server_new(Service, ServerFQDN, UserRealm, _SecFlags, -- GetPassword, CheckPassword, CheckPasswordDigest) -> -+ GetPassword, CheckPassword, CheckPasswordDigest, Socket) -> -+ Params = #sasl_params{ -+ host = ServerFQDN, -+ realm = UserRealm, -+ get_password = GetPassword, -+ check_password = CheckPassword, -+ check_password_digest= CheckPasswordDigest, -+ socket = Socket -+ }, -+ - #sasl_state{service = Service, - myname = ServerFQDN, -- realm = UserRealm, -- get_password = GetPassword, -- check_password = CheckPassword, -- check_password_digest= CheckPasswordDigest}. -+ params = Params}. -+ - - server_start(State, Mech, ClientIn) -> - case lists:member(Mech, listmech(State#sasl_state.myname)) of - true -> - case ets:lookup(sasl_mechanism, Mech) of - [#sasl_mechanism{module = Module}] -> -- {ok, MechState} = Module:mech_new( -- State#sasl_state.myname, -- State#sasl_state.get_password, -- State#sasl_state.check_password, -- State#sasl_state.check_password_digest), -+ {ok, MechState} = -+ Module:mech_new(State#sasl_state.params), - server_step(State#sasl_state{mech_mod = Module, - mech_state = MechState}, - ClientIn); -diff --git a/src/cyrsasl.hrl b/src/cyrsasl.hrl -new file mode 100644 -index 0000000..b4cc3e3 ---- /dev/null -+++ b/src/cyrsasl.hrl -@@ -0,0 +1,15 @@ -+%% @type saslparams() = {sasl_params, Host, Realm, GetPassword, CheckPassword, CheckPasswordDigest} -+%% Host = string() -+%% Realm = string() -+%% GetPassword = function() -+%% CheckPassword = function() -+%% CheckPasswordDigest = any(). -+%% Parameters for SASL. -+ -+-record(sasl_params, { -+ host, -+ realm, -+ get_password, -+ check_password, -+ check_password_digest, -+ socket}). -diff --git a/src/cyrsasl_anonymous.erl b/src/cyrsasl_anonymous.erl -index cb0b1e3..085e98c 100644 ---- a/src/cyrsasl_anonymous.erl -+++ b/src/cyrsasl_anonymous.erl -@@ -27,7 +27,9 @@ - - -module(cyrsasl_anonymous). - ---export([start/1, stop/0, mech_new/4, mech_step/2]). -+-export([start/1, stop/0, mech_new/1, mech_step/2]). -+ -+-include("cyrsasl.hrl"). - - -behaviour(cyrsasl). - -@@ -40,7 +42,7 @@ start(_Opts) -> - stop() -> - ok. - --mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) -> -+mech_new(#sasl_params{host=Host}) -> - {ok, #state{server = Host}}. - - mech_step(State, _ClientIn) -> -diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl -index 557e498..e4199f8 100644 ---- a/src/cyrsasl_digest.erl -+++ b/src/cyrsasl_digest.erl -@@ -29,10 +29,11 @@ - - -export([start/1, - stop/0, -- mech_new/4, -+ mech_new/1, - mech_step/2]). - - -include("ejabberd.hrl"). -+-include("cyrsasl.hrl"). - - -behaviour(cyrsasl). - -@@ -47,7 +48,8 @@ start(_Opts) -> - stop() -> - ok. - --mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) -> -+mech_new(#sasl_params{host=Host, get_password=GetPassword, -+ check_password_digest=CheckPasswordDigest}) -> - {ok, #state{step = 1, - nonce = randoms:get_string(), - host = Host, -diff --git a/src/cyrsasl_gssapi.erl b/src/cyrsasl_gssapi.erl -new file mode 100644 -index 0000000..11d9955 ---- /dev/null -+++ b/src/cyrsasl_gssapi.erl -@@ -0,0 +1,166 @@ -+%%%---------------------------------------------------------------------- -+%%% File : cyrsasl_gssapi.erl -+%%% Author : Mikael Magnusson -+%%% Purpose : GSSAPI SASL mechanism -+%%% Created : 1 June 2007 by Mikael Magnusson -+%%% Id : $Id: $ -+%%%---------------------------------------------------------------------- -+%%% -+%%% Copyright (C) 2007-2009 Mikael Magnusson -+%%% -+%%% Permission is hereby granted, free of charge, to any person -+%%% obtaining a copy of this software and associated documentation -+%%% files (the "Software"), to deal in the Software without -+%%% restriction, including without limitation the rights to use, copy, -+%%% modify, merge, publish, distribute, sublicense, and/or sell copies -+%%% of the Software, and to permit persons to whom the Software is -+%%% furnished to do so, subject to the following conditions: -+%%% -+%%% The above copyright notice and this permission notice shall be -+%%% included in all copies or substantial portions of the Software. -+%%% -+%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -+%%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -+%%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -+%%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -+%%% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -+%%% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -+%%% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -+%%% SOFTWARE. -+%%% -+ -+%%% -+%%% configuration options: -+%%% {sasl_realm, ""}. -+%%% -+%%% environment variables: -+%%% KRB5_KTNAME -+%%% -+ -+-module(cyrsasl_gssapi). -+-author('mikma@users.sourceforge.net'). -+-vsn('$Revision: $ '). -+ -+-export([start/1, -+ stop/0, -+ mech_new/1, -+ mech_step/2]). -+ -+-include("ejabberd.hrl"). -+-include("cyrsasl.hrl"). -+ -+-behaviour(cyrsasl). -+ -+-define(SERVER, ?MODULE). -+-define(SERVICE, "xmpp"). -+ -+-record(state, {sasl, -+ needsmore=true, -+ step=0, -+ host, -+ realm, -+ authid, -+ authzid, -+ authrealm, -+ error}). -+ -+start(_Opts) -> -+ ChildSpec = -+ {?SERVER, -+ {esasl, start_link, [{local, ?SERVER}]}, -+ transient, -+ 1000, -+ worker, -+ [esasl]}, -+ -+ case supervisor:start_child(ejabberd_sup, ChildSpec) of -+ {ok, _Pid} -> -+ cyrsasl:register_mechanism("GSSAPI", ?MODULE, false); -+ {error, Error} = E -> -+ ?ERROR_MSG("esasl failed: ~p", [Error]), -+ E -+ end. -+ -+ -+stop() -> -+ catch esasl:stop(?SERVER), -+ supervisor:terminate_child(ejabberd_sup, ?SERVER), -+ supervisor:delete_child(ejabberd_sup, ?SERVER). -+ -+mech_new(#sasl_params{host=Host, realm=Realm, socket=Socket}) -> -+ case ejabberd_socket:gethostname(Socket) of -+ {ok, FQDN} -> -+ ?DEBUG("mech_new ~p ~p ~p~n", [Host, Realm, FQDN]), -+ case esasl:server_start(?SERVER, "GSSAPI", ?SERVICE, FQDN) of -+ {ok, Sasl} -> -+ {ok, #state{sasl=Sasl,host=Host,realm=Realm}}; -+ {error, {gsasl_error, Error}} -> -+ {ok, Str} = esasl:str_error(?SERVER, Error), -+ ?DEBUG("esasl error: ~p", [Str]), -+ {ok, #state{needsmore=error,error="internal-server-error"}}; -+ {error, Error} -> -+ ?DEBUG("esasl error: ~p", [Error]), -+ {ok, #state{needsmore=error,error="internal-server-error"}} -+ end; -+ {error, Error} -> -+ ?DEBUG("gethostname error: ~p", [Error]), -+ {ok, #state{needsmore=error,error="internal-server-error"}} -+ end. -+ -+mech_step(State, ClientIn) when is_list(ClientIn) -> -+ catch do_step(State, ClientIn). -+ -+do_step(#state{needsmore=error,error=Error}=_State, _) -> -+ {error, Error}; -+do_step(#state{needsmore=false}=State, _) -> -+ check_user(State); -+do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) -> -+ ?DEBUG("mech_step~n", []), -+ case esasl:step(Sasl, list_to_binary(ClientIn)) of -+ {ok, RspAuth} -> -+ ?DEBUG("ok~n", []), -+ {ok, Display_name} = esasl:property_get(Sasl, gssapi_display_name), -+ {ok, Authzid} = esasl:property_get(Sasl, authzid), -+ {Authid, [$@ | Auth_realm]} = -+ lists:splitwith(fun(E)->E =/= $@ end, Display_name), -+ State1 = State#state{authid=Authid, -+ authzid=Authzid, -+ authrealm=Auth_realm}, -+ handle_step_ok(State1, binary_to_list(RspAuth)); -+ {needsmore, RspAuth} -> -+ ?DEBUG("needsmore~n", []), -+ if (Step > 0) and (ClientIn =:= []) and (RspAuth =:= <<>>) -> -+ {error, "not-authorized"}; -+ true -> -+ {continue, binary_to_list(RspAuth), -+ State#state{step=Step+1}} -+ end; -+ {error, _} -> -+ {error, "not-authorized"} -+ end. -+ -+handle_step_ok(State, []) -> -+ check_user(State); -+handle_step_ok(#state{step=Step}=State, RspAuth) -> -+ ?DEBUG("continue~n", []), -+ {continue, RspAuth, State#state{needsmore=false,step=Step+1}}. -+ -+check_user(#state{authid=Authid,authzid=Authzid, -+ authrealm=Auth_realm,host=Host,realm=Realm}) -> -+ if Realm =/= Auth_realm -> -+ ?DEBUG("bad realm ~p (expected ~p)~n",[Auth_realm, Realm]), -+ throw({error, "not-authorized"}); -+ true -> -+ ok -+ end, -+ -+ case ejabberd_auth:is_user_exists(Authid, Host) of -+ false -> -+ ?DEBUG("bad user ~p~n",[Authid]), -+ throw({error, "not-authorized"}); -+ true -> -+ ok -+ end, -+ -+ ?DEBUG("GSSAPI authenticated ~p ~p~n", [Authid, Authzid]), -+ {ok, [{username, Authid}, {authzid, Authzid}]}. -diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl -index 7192cd1..7bf41b3 100644 ---- a/src/cyrsasl_plain.erl -+++ b/src/cyrsasl_plain.erl -@@ -27,7 +27,9 @@ - -module(cyrsasl_plain). - -author('alexey@process-one.net'). - ---export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]). -+-export([start/1, stop/0, mech_new/1, mech_step/2, parse/1]). -+ -+-include("cyrsasl.hrl"). - - -behaviour(cyrsasl). - -@@ -40,7 +42,7 @@ start(_Opts) -> - stop() -> - ok. - --mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) -> -+mech_new(#sasl_params{check_password = CheckPassword}) -> - {ok, #state{check_password = CheckPassword}}. - - mech_step(State, ClientIn) -> -diff --git a/src/cyrsasl_scram.erl b/src/cyrsasl_scram.erl -index dc671b2..d65cc6f 100644 ---- a/src/cyrsasl_scram.erl -+++ b/src/cyrsasl_scram.erl -@@ -29,10 +29,11 @@ - - -export([start/1, - stop/0, -- mech_new/4, -+ mech_new/1, - mech_step/2]). - - -include("ejabberd.hrl"). -+-include("cyrsasl.hrl"). - - -behaviour(cyrsasl). - -@@ -48,7 +49,7 @@ start(_Opts) -> - stop() -> - ok. - --mech_new(_Host, GetPassword, _CheckPassword, _CheckPasswordDigest) -> -+mech_new(#sasl_params{get_password=GetPassword}) -> - {ok, #state{step = 2, get_password = GetPassword}}. - - mech_step(#state{step = 2} = State, ClientIn) -> -diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl -index ed26400..11ae5dd 100644 ---- a/src/ejabberd_c2s.erl -+++ b/src/ejabberd_c2s.erl -@@ -305,9 +305,16 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> - send_header(StateData, Server, "1.0", DefaultLang), - case StateData#state.authenticated of - false -> -+ Realm = -+ case ejabberd_config:get_local_option({sasl_realm, Server}) of -+ undefined -> -+ ""; -+ Realm0 -> -+ Realm0 -+ end, - SASLState = - cyrsasl:server_new( -- "jabber", Server, "", [], -+ "jabber", Server, Realm, [], - fun(U) -> - ejabberd_auth:get_password_with_authmodule( - U, Server) -@@ -319,7 +326,8 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> - fun(U, P, D, DG) -> - ejabberd_auth:check_password_with_authmodule( - U, Server, P, D, DG) -- end), -+ end, -+ StateData#state.socket), - Mechs = lists:map( - fun(S) -> - {xmlelement, "mechanism", [], -diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl -index 836e7d9..59e4034 100644 ---- a/src/ejabberd_socket.erl -+++ b/src/ejabberd_socket.erl -@@ -44,9 +44,11 @@ - get_peer_certificate/1, - get_verify_result/1, - close/1, -+ gethostname/1, - sockname/1, peername/1]). - - -include("ejabberd.hrl"). -+-include_lib("kernel/include/inet.hrl"). - - -record(socket_state, {sockmod, socket, receiver}). - -@@ -228,6 +230,23 @@ peername(#socket_state{sockmod = SockMod, socket = Socket}) -> - SockMod:peername(Socket) - end. - -+gethostname(#socket_state{socket = Socket} = State) -> -+ ?DEBUG("gethostname ~p~n", [Socket]), -+ -+ case sockname(State) of -+ {ok, {Addr, _Port}} -> -+ case inet:gethostbyaddr(Addr) of -+ {ok, HostEnt} when is_record(HostEnt, hostent) -> -+ ?DEBUG("gethostname result ~p~n", -+ [HostEnt#hostent.h_name]), -+ {ok, HostEnt#hostent.h_name}; -+ {error, _Reason} = E -> -+ E -+ end; -+ {error, _Reason} = E -> -+ E -+ end. -+ - %%==================================================================== - %% Internal functions - %%==================================================================== --- -1.8.3.1 - diff --git a/ejabberd-0007-Disable-INET_DIST_INTERFACE-by-default.patch b/ejabberd-0007-Disable-INET_DIST_INTERFACE-by-default.patch deleted file mode 100644 index d0d1d40..0000000 --- a/ejabberd-0007-Disable-INET_DIST_INTERFACE-by-default.patch +++ /dev/null @@ -1,26 +0,0 @@ -From a8910615b82e7af8cb32916792970de0b53e5872 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Sat, 18 Jun 2011 23:24:28 +0400 -Subject: [PATCH 07/10] Disable INET_DIST_INTERFACE by default - -Signed-off-by: Peter Lemenkov ---- - src/ejabberdctl.cfg.example | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/ejabberdctl.cfg.example b/src/ejabberdctl.cfg.example -index b72058a..5ee43cf 100644 ---- a/src/ejabberdctl.cfg.example -+++ b/src/ejabberdctl.cfg.example -@@ -59,7 +59,7 @@ - # - # Default: {127,0,0,1} - # --INET_DIST_INTERFACE={127,0,0,1} -+#INET_DIST_INTERFACE={127,0,0,1} - - #. - #' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections --- -1.8.3.1 - diff --git a/ejabberd-0007-Install-into-BINDIR-instead-of-SBINDIR.patch b/ejabberd-0007-Install-into-BINDIR-instead-of-SBINDIR.patch new file mode 100644 index 0000000..7a0e178 --- /dev/null +++ b/ejabberd-0007-Install-into-BINDIR-instead-of-SBINDIR.patch @@ -0,0 +1,45 @@ +From: Peter Lemenkov +Date: Wed, 17 Jul 2013 14:53:49 +0400 +Subject: [PATCH] Install into BINDIR instead of SBINDIR + +Signed-off-by: Peter Lemenkov + +diff --git a/Makefile.in b/Makefile.in +index de47be2..037d2ff 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -11,8 +11,8 @@ DESTDIR = + # /etc/ejabberd/ + ETCDIR = $(DESTDIR)@sysconfdir@/ejabberd + +-# /sbin/ +-SBINDIR = $(DESTDIR)@sbindir@ ++# /bin/ ++BINDIR = $(DESTDIR)@bindir@ + + # /lib/ejabberd/ + EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd +@@ -126,11 +126,11 @@ install: all + $(INSTALL) -b -m 644 $(G_USER) inetrc $(ETCDIR)/inetrc + # + # Administration script +- [ -d $(SBINDIR) ] || $(INSTALL) -d -m 755 $(SBINDIR) +- $(INSTALL) -m 755 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl ++ [ -d $(BINDIR) ] || $(INSTALL) -d -m 755 $(BINDIR) ++ $(INSTALL) -m 755 $(G_USER) ejabberdctl.example $(BINDIR)/ejabberdctl + # + # Init script +- $(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*" \ ++ $(SED) -e "s*@ctlscriptpath@*$(BINDIR)*" \ + -e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \ + > ejabberd.init + chmod 755 ejabberd.init +@@ -191,7 +191,7 @@ install: all + uninstall: uninstall-binary + + uninstall-binary: +- rm -f $(SBINDIR)/ejabberdctl ++ rm -f $(BINDIR)/ejabberdctl + rm -fr $(DOCDIR) + rm -f $(BEAMDIR)/*.beam + rm -f $(BEAMDIR)/*.app diff --git a/ejabberd-0008-Clean-up-false-security-measure.patch b/ejabberd-0008-Clean-up-false-security-measure.patch deleted file mode 100644 index a44f130..0000000 --- a/ejabberd-0008-Clean-up-false-security-measure.patch +++ /dev/null @@ -1,26 +0,0 @@ -From c827055ee650243c2af546753743f692ae0fe758 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Wed, 17 Jul 2013 14:56:09 +0400 -Subject: [PATCH 08/10] Clean up false security measure - -Signed-off-by: Peter Lemenkov ---- - src/Makefile.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/Makefile.in b/src/Makefile.in -index 64ed856..1a1fa41 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -202,7 +202,7 @@ install: all - # - # Administration script - [ -d $(SBINDIR) ] || install -d -m 755 $(SBINDIR) -- install -m 550 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl -+ install -m 755 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl - # - # Init script - sed -e "s*@ctlscriptpath@*$(SBINDIR)*" \ --- -1.8.3.1 - diff --git a/ejabberd-0008-Disable-Erlang-version-check.patch b/ejabberd-0008-Disable-Erlang-version-check.patch new file mode 100644 index 0000000..3c6d723 --- /dev/null +++ b/ejabberd-0008-Disable-Erlang-version-check.patch @@ -0,0 +1,38 @@ +From: Peter Lemenkov +Date: Sat, 30 Aug 2014 12:21:45 +0400 +Subject: [PATCH] Disable Erlang version check + +Signed-off-by: Peter Lemenkov + +diff --git a/configure.ac b/configure.ac +index e7bb43b..d24c543 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3,8 +3,6 @@ + + AC_PREREQ(2.53) + AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo community` | sed 's/-g.*//' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +-REQUIRE_ERLANG_MIN="5.9.1 (Erlang/OTP R15B01)" +-REQUIRE_ERLANG_MAX="9.0.0 (No Max)" + + # Checks for programs. + AC_PROG_MAKE_SET +@@ -32,18 +30,6 @@ AC_PATH_TOOL(ERLC, erlc, , [${extra_erl_path}$PATH]) + AC_ERLANG_NEED_ERL + AC_ERLANG_NEED_ERLC + +-AC_ARG_ENABLE(erlang-version-check, +-[AC_HELP_STRING([--enable-erlang-version-check], +- [Check Erlang/OTP version @<:@default=yes@:>@])]) +-case "$enable_erlang_version_check" in +- yes|'') +- ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX]) +- ;; +- no) +- ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX],[warn]) +- ;; +-esac +- + # Checks and sets ERLANG_ROOT_DIR and ERLANG_LIB_DIR variable + AC_ERLANG_SUBST_ROOT_DIR + # AC_ERLANG_SUBST_LIB_DIR diff --git a/ejabberd-0009-Enable-polkit-support.patch b/ejabberd-0009-Enable-polkit-support.patch deleted file mode 100644 index fd31fd1..0000000 --- a/ejabberd-0009-Enable-polkit-support.patch +++ /dev/null @@ -1,23 +0,0 @@ -From f2420ac96bb52eeb5a01111cabb4f5580db42142 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Wed, 17 Jul 2013 14:51:04 +0400 -Subject: [PATCH 09/10] Enable polkit support - -Signed-off-by: Peter Lemenkov ---- - src/ejabberdctl.template | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template -index b298e01..fa6c5c2 100644 ---- a/src/ejabberdctl.template -+++ b/src/ejabberdctl.template -@@ -1,4 +1,4 @@ --#!/bin/sh -+#!/usr/bin/pkexec /bin/sh - - # define default configuration - POLL=true --- -1.8.3.1 - diff --git a/ejabberd-0009-Fix-permissions-for-captcha-script.patch b/ejabberd-0009-Fix-permissions-for-captcha-script.patch new file mode 100644 index 0000000..d694c57 --- /dev/null +++ b/ejabberd-0009-Fix-permissions-for-captcha-script.patch @@ -0,0 +1,19 @@ +From: Peter Lemenkov +Date: Sun, 31 Aug 2014 13:21:48 +0400 +Subject: [PATCH] Fix permissions for captcha script + +Signed-off-by: Peter Lemenkov + +diff --git a/Makefile.in b/Makefile.in +index 037d2ff..d972c98 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -150,7 +150,7 @@ install: all + # + # Binary C programs + $(INSTALL) -d $(PBINDIR) +- $(INSTALL) -m 750 $(O_USER) tools/captcha.sh $(PBINDIR) ++ $(INSTALL) -m 755 $(O_USER) tools/captcha.sh $(PBINDIR) + -[ -f deps/p1_pam/priv/bin/epam ] \ + && $(INSTALL) -m 750 $(O_USER) deps/p1_pam/priv/bin/epam $(PBINDIR) + # diff --git a/ejabberd-0010-Enable-systemd-notification-if-available.patch b/ejabberd-0010-Enable-systemd-notification-if-available.patch new file mode 100644 index 0000000..8a04f37 --- /dev/null +++ b/ejabberd-0010-Enable-systemd-notification-if-available.patch @@ -0,0 +1,18 @@ +From: Peter Lemenkov +Date: Sun, 31 Aug 2014 16:08:17 +0400 +Subject: [PATCH] Enable systemd notification if available + +Signed-off-by: Peter Lemenkov + +diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl +index 379f728..9aa6801 100644 +--- a/src/ejabberd_app.erl ++++ b/src/ejabberd_app.erl +@@ -66,6 +66,7 @@ start(normal, _Args) -> + maybe_add_nameservers(), + start_modules(), + ejabberd_listener:start_listeners(), ++ {module, sd_notify} == code:load_file(sd_notify) andalso sd_notify:sd_notify(0, "READY=1"), + ?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]), + Sup; + start(_, _) -> diff --git a/ejabberd-0010-Install-into-BINDIR-instead-of-SBINDIR.patch b/ejabberd-0010-Install-into-BINDIR-instead-of-SBINDIR.patch deleted file mode 100644 index 3868b01..0000000 --- a/ejabberd-0010-Install-into-BINDIR-instead-of-SBINDIR.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 729db839b762a472444bacff22a1cb8870635272 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Wed, 17 Jul 2013 14:53:49 +0400 -Subject: [PATCH 10/10] Install into BINDIR instead of SBINDIR - -Signed-off-by: Peter Lemenkov ---- - src/Makefile.in | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - -diff --git a/src/Makefile.in b/src/Makefile.in -index 1a1fa41..1578183 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -92,7 +92,7 @@ DESTDIR = - ETCDIR = $(DESTDIR)@sysconfdir@/ejabberd - - # /sbin/ --SBINDIR = $(DESTDIR)@sbindir@ -+BINDIR = $(DESTDIR)@bindir@ - - # /lib/ejabberd/ - EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd -@@ -201,11 +201,11 @@ install: all - install -b -m 644 $(G_USER) inetrc $(ETCDIR)/inetrc - # - # Administration script -- [ -d $(SBINDIR) ] || install -d -m 755 $(SBINDIR) -- install -m 755 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl -+ [ -d $(BINDIR) ] || install -d -m 755 $(BINDIR) -+ install -m 755 $(G_USER) ejabberdctl.example $(BINDIR)/ejabberdctl - # - # Init script -- sed -e "s*@ctlscriptpath@*$(SBINDIR)*" \ -+ sed -e "s*@ctlscriptpath@*$(BINDIR)*" \ - -e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \ - > ejabberd.init - chmod 755 ejabberd.init -@@ -273,7 +273,7 @@ install: all - uninstall: uninstall-binary - - uninstall-binary: -- rm -f $(SBINDIR)/ejabberdctl -+ rm -f $(BINDIR)/ejabberdctl - rm -fr $(DOCDIR) - rm -f $(BEAMDIR)/*.beam - rm -f $(BEAMDIR)/*.app --- -1.8.3.1 - diff --git a/ejabberd.init b/ejabberd.init deleted file mode 100644 index 71cadea..0000000 --- a/ejabberd.init +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash -# -# ejabberd Start and stop ejabberd. - -# chkconfig: - 40 60 -# description: ejabberd -# processname: ejabberd -# pidfile: /var/run/ejabberd.pid - -### BEGIN INIT INFO -# Provides: ejabberd -# Required-Start: network -# Required-Stop: network -# Default-Start: -# Default-Stop: 0 1 6 -# Short-Description: Start and stop ejabberd -# Description: A distributed, fault-tolerant Jabber/XMPP server -### END INIT INFO - -. /etc/rc.d/init.d/functions - -if [ -r /etc/sysconfig/ejabberd ]; then - . /etc/sysconfig/ejabberd -fi - -if [ ! "$CONFIG_FILE" ]; then - CONFIG_FILE=/etc/ejabberd/ejabberd.cfg -fi - -# we're using symlinked consolehelper to check user's rights -progctl=/usr/bin/ejabberdctl - -start() { - echo -n $"Starting ejabberd: " - if [ "$ULIMIT_MAX_FILES" ]; then - ulimit -n $ULIMIT_MAX_FILES - fi - - # check whether ejabberd was already started - if $progctl status > /dev/null 2>&1 ; then - echo -n "already running" && warning && echo - return 0 - fi - - daemon $progctl start --config $CONFIG_FILE \ - --ctl-config /etc/ejabberd/ejabberdctl.cfg \ - --logs "/var/log/ejabberd" \ - --spool "/var/lib/ejabberd/spool" \ - 2>/dev/null - RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ejabberd - echo - - # it takes some time to actually start necessary nodes - sleep 5 - - return $RETVAL -} - -stop() { - # Stop daemons. - echo -n "Shutting down ejabberd: " - - # check whether ejabberd was already stopped - if ! $progctl status > /dev/null 2>&1 ; then - echo -n "already stopped" && warning && echo - return 0 - fi - - daemon $progctl stop 2>/dev/null - RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ejabberd - echo - - # it takes some time to actually stop necessary nodes - sleep 5 - - return $RETVAL -} - -restart() { - stop - sleep 5 - start -} - -# See how we were called. -case "$1" in - start) - start - ;; - stop) - stop - ;; - restart|force-reload) - restart - ;; - condrestart|try-restart) - [ -f /var/lock/subsys/ejabberd ] && restart || : - ;; - status) - $progctl status - ;; - *) - echo "Usage: ejabberd {start|stop|restart|force-reload|condrestart|try-restart|status}" - exit 2 -esac - -exit $? - - diff --git a/ejabberd.logrotate b/ejabberd.logrotate index cf5274a..9921889 100644 --- a/ejabberd.logrotate +++ b/ejabberd.logrotate @@ -4,6 +4,6 @@ create 0640 ejabberd ejabberd sharedscripts postrotate - runuser -s /bin/bash - ejabberd -c "/usr/sbin/ejabberdctl reopen-log" >/dev/null 2>/dev/null || true + runuser -s /bin/bash - ejabberd -c "/usr/bin/ejabberdctl reopen-log" >/dev/null 2>/dev/null || true endscript } diff --git a/ejabberd.service b/ejabberd.service index ea16733..2fe9048 100644 --- a/ejabberd.service +++ b/ejabberd.service @@ -1,6 +1,7 @@ [Unit] Description=A distributed, fault-tolerant Jabber/XMPP server -After=network.target +Requires=network-online.target +After=network-online.target [Service] Type=oneshot @@ -9,11 +10,11 @@ Group=ejabberd LimitNOFILE=16000 RestartSec=5 ExecStart=/usr/bin/bash /usr/bin/ejabberdctl \ - --config /etc/ejabberd/ejabberd.cfg \ + --config /etc/ejabberd/ejabberd.yml \ --ctl-config /etc/ejabberd/ejabberdctl.cfg \ --logs "/var/log/ejabberd" \ - --spool "/var/lib/ejabberd/spool" start -ExecStop=/usr/sbin/ejabberdctl stop + --spool "/var/lib/ejabberd" start +ExecStop=/usr/bin/ejabberdctl stop RemainAfterExit=yes [Install] diff --git a/ejabberd.spec b/ejabberd.spec index 52c8d73..485d856 100644 --- a/ejabberd.spec +++ b/ejabberd.spec @@ -1,4 +1,5 @@ %global _hardened_build 1 + # FIXME non-standard directory for storing *.so objects %{?filter_setup: %filter_provides_in %{_libdir}/ejabberd/priv/lib/.*\.so$ @@ -15,15 +16,14 @@ # see https://bugzilla.redhat.com/bugzilla/250253 %global _with_hevea 0 %else -# FIXME Hevea is deadly broken currently -%global _with_hevea 0 +%global _with_hevea 1 %endif %endif Name: ejabberd -Version: 2.1.13 -Release: 10%{?dist} +Version: 14.07 +Release: 1%{?dist} Summary: A distributed, fault-tolerant Jabber/XMPP server Group: Applications/Internet @@ -32,10 +32,8 @@ URL: http://www.ejabberd.im/ %if 0%{?el7}%{?fedora} VCS: scm:git:https://github.com/processone/ejabberd.git %endif -Source0: https://github.com/processone/%{name}/archive/v%{version}/%{name}-v%{version}.tar.gz -Source1: ejabberd.init +Source0: https://github.com/processone/%{name}/archive/%{version}/%{name}-%{version}.tar.gz Source2: ejabberd.logrotate -Source3: ejabberd.sysconfig # Support for systemd Source4: ejabberd.service @@ -45,42 +43,46 @@ Source5: ejabberd.tmpfiles.conf Source9: ejabberdctl.pam Source11: ejabberd.pam -# usermode support for old systems -Source10: ejabberdctl.apps # polkit support Source12: ejabberdctl.polkit.actions Source13: ejabberdctl.polkit.rules +Source14: ejabberd-src-deps.tar + # Use ejabberd as an example for PAM service name (fedora/epel-specific) Patch1: ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch -# fixed delays in s2s connections -Patch2: ejabberd-0002-Fixed-delays-in-s2s-connections.patch # Introducing mod_admin_extra -Patch3: ejabberd-0003-Introducing-mod_admin_extra.patch +Patch2: ejabberd-0002-Introducing-mod_admin_extra.patch # BZ# 439583, 452326, 451554, 465196, 502361 (fedora/epel-specific) -Patch4: ejabberd-0004-Fedora-specific-changes-to-ejabberdctl.patch -# Fix so-lib permissions while installing (fedora/epel-specific) -Patch5: ejabberd-0005-Install-.so-objects-with-0755-permissions.patch -# Backported from upstream -Patch6: ejabberd-0006-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch -# Disable IP restriction for ejabberdctl (seems that it doesn't work well) -Patch7: ejabberd-0007-Disable-INET_DIST_INTERFACE-by-default.patch -# Don't try to make system-wide scripts unreadable for users (fedora/epel-specific) -Patch8: ejabberd-0008-Clean-up-false-security-measure.patch +Patch3: ejabberd-0003-Fedora-specific-changes-to-ejabberdctl.patch +# Fedora-specific +Patch4: ejabberd-0004-Install-.so-objects-with-0755-permissions.patch +# Fedora-specific +Patch5: ejabberd-0005-Clean-up-false-security-measure.patch # polkit support -Patch9: ejabberd-0009-Enable-polkit-support.patch -# polkit support -Patch10:ejabberd-0010-Install-into-BINDIR-instead-of-SBINDIR.patch - -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Patch6: ejabberd-0006-Enable-polkit-support.patch +# Fedora-specific +Patch7: ejabberd-0007-Install-into-BINDIR-instead-of-SBINDIR.patch +# Fedora-specific +Patch8: ejabberd-0008-Disable-Erlang-version-check.patch +# Fedora-specific +Patch9: ejabberd-0009-Fix-permissions-for-captcha-script.patch +# Fedora-specific +Patch10:ejabberd-0010-Enable-systemd-notification-if-available.patch BuildRequires: expat-devel BuildRequires: openssl-devel >= 0.9.8 BuildRequires: pam-devel +BuildRequires: libyaml-devel BuildRequires: erlang +BuildRequires: erlang-rebar +# FIXME +#BuildRequires: erlang-jiffy +#BuildRequires: erlang-lager +#BuildRequires: erlang-ibrowse +#BuildRequires: erlang-xmlrpc %if 0%{?_with_hevea} BuildRequires: hevea -BuildRequires: texlive BuildRequires: texlive-comment %endif BuildRequires: autoconf @@ -90,61 +92,22 @@ BuildRequires: automake Requires(pre): shadow-utils Requires(post): /usr/bin/openssl -%if 0%{?el5}%{?el6} -Requires(post): /sbin/chkconfig -Requires(preun): /sbin/chkconfig -Requires(preun): /sbin/service -Requires(postun): /sbin/service -%else Requires(post): systemd Requires(preun): systemd Requires(postun): systemd -%endif Provides: user(%{name}) Provides: group(%{name}) -#Error:erlang(exmpp_jid:domain_as_list/1) -#Error:erlang(exmpp_jid:make/2) -#Error:erlang(exmpp_jid:node_as_list/1) -#Error:erlang(exmpp_jid:parse/1) -#Error:erlang(exmpp_jid:prep_domain_as_list/1) -#Error:erlang(exmpp_jid:prep_node_as_list/1) -#Error:erlang(exmpp_jid:prep_resource_as_list/1) -#Error:erlang(exmpp_jid:resource_as_list/1) -#Error:erlang(exmpp:start/0) -#Error:erlang(exmpp_xml:document_to_list/1) -#Error:erlang(exmpp_xml:foreach/2) -#Error:erlang(exmpp_xml:get_attribute/3) -#Error:erlang(exmpp_xml:get_attribute_as_list/3) -#Error:erlang(exmpp_xml:get_element/2) -#Error:erlang(exmpp_xml:get_elements/2) -#Error:erlang(exmpp_xml:parse/2) -#Error:erlang(exmpp_xml:start_parser/1) -#Error:erlang(exmpp_xml:stop_parser/1) -#Error:erlang(exmpp_xml:xmlelement_to_xmlel/1) -#Error:erlang(exmpp_xml:xmlel_to_xmlelement/1) -#Error:erlang(mysql_conn:fetch/3) -#Error:erlang(mysql_conn:start/6) -#Error:erlang(mysql_conn:stop/1) -#Error:erlang(mysql:get_result_affected_rows/1) -#Error:erlang(mysql:get_result_field_info/1) -#Error:erlang(mysql:get_result_reason/1) -#Error:erlang(mysql:get_result_rows/1) -#Error:erlang(release_handler_1:eval_script/3) -#Error:erlang(ssl:seed/1) Requires: erlang -#Error:erlang(esasl:str_error/2) -Requires: erlang-esasl -%if 0%{?el5}%{?el6} -Requires: usermode -%else +# FIXME see also patch #10 +#Requires: erlang-sd_notify # for /usr/bin/pkexec Requires: polkit -%endif # for flock in ejabberdctl Requires: util-linux %{?__erlang_drv_version:Requires: %{__erlang_drv_version}} +%{?__erlang_nif_version:Requires: %{__erlang_nif_version}} %description @@ -155,10 +118,8 @@ Windows NT/2000/XP). %package doc Summary: Documentation for ejabberd -%if 0%{?el6}%{?el7}%{?fedora} BuildArch: noarch Obsoletes: %{name}-doc < 2.1.4 -%endif # docdir owner Requires: %{name} = %{version}-%{release} Group: Documentation @@ -170,100 +131,67 @@ Documentation for ejabberd. %setup -q %patch1 -p1 -b .pam_name -%patch2 -p1 -b .s2s_delays -%patch3 -p1 -b .mod_admin_extra -%patch4 -p1 -b .fedora_specific -%patch5 -p1 -b .fix_perms -%patch6 -p1 -b .gssapi -%patch7 -p1 -b .disable_ip_restriction_for_ejabberdctl -%patch8 -p1 -b .dont_hide -%if 0%{?el7}%{?fedora} -%patch9 -p1 -b .use_polkit -%patch10 -p1 -b .usr_bin -%endif +%patch2 -p1 -b .mod_admin_extra +%patch3 -p1 -b .fedora_specific +%patch4 -p1 -b .so_lib_755 +%patch5 -p1 -b .false_security +%patch6 -p1 -b .use_polkit +%patch7 -p1 -b .use_bindir +%patch8 -p1 -b .no_ver_check +%patch9 -p1 -b .captcha_perms +#%patch10 -p1 -b .systemd_notify + +tar xvf %{S:14} %build -pushd src autoreconf -ivf -%configure --enable-odbc --enable-pam -# doesn't build on SMP currently + +# Disabled: --enable-hipe --enable-roster-gateway-workaround --enable-transient_supervisors --enable-full-xml --enable-mssql --enable-tools --enable-riak +%configure --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-json --enable-iconv --enable-debug --enable-http --enable-lager make -popd %if 0%{?_with_hevea} pushd doc # remove pre-built docs -rm -f dev.html features.html features.pdf guide.html guide.pdf +rm -f dev.html features.html +# See this link - http://thread.gmane.org/gmane.linux.redhat.fedora.devel/198954/focus=198957 +make release make html pdf popd %endif %install -rm -rf %{buildroot} - -pushd src make install DESTDIR=%{buildroot} -popd # fix example SSL certificate path to real one, which we created recently (see above) -%{__perl} -pi -e 's!/path/to/ssl.pem!/etc/ejabberd/ejabberd.pem!g' %{buildroot}/etc/ejabberd/ejabberd.cfg +%{__perl} -pi -e 's!/path/to/ssl.pem!/etc/ejabberd/ejabberd.pem!g' %{buildroot}/etc/ejabberd/ejabberd.yml # fix captcha path -%{__perl} -pi -e 's!/lib/ejabberd/priv/bin/captcha.sh!%{_libdir}/%{name}/priv/bin/captcha.sh!g' %{buildroot}/etc/ejabberd/ejabberd.cfg - -mkdir -p %{buildroot}/var/log/ejabberd -mkdir -p %{buildroot}/var/lib/ejabberd/spool +%{__perl} -pi -e 's!/lib/ejabberd/priv/bin/captcha.sh!%{_libdir}/%{name}/priv/bin/captcha.sh!g' %{buildroot}/etc/ejabberd/ejabberd.yml install -D -p -m 0644 %{S:9} %{buildroot}%{_sysconfdir}/pam.d/ejabberdctl install -D -p -m 0644 %{S:11} %{buildroot}%{_sysconfdir}/pam.d/ejabberd -%if 0%{?el5} -# No password-auth PAM scheme in EL5 - see rhbz #758601 -sed -i -e "s,password-auth,system-auth,g" %{buildroot}%{_sysconfdir}/pam.d/ejabberd -%endif - -%if 0%{?el5}%{?el6} -# install init-script -install -D -p -m 0755 %{S:1} %{buildroot}%{_initrddir}/ejabberd -# install sysconfig file -install -D -p -m 0644 %{S:3} %{buildroot}%{_sysconfdir}/sysconfig/ejabberd -%else # install systemd entry install -D -m 0644 -p %{S:4} %{buildroot}%{_unitdir}/%{name}.service install -D -m 0644 -p %{S:5} %{buildroot}%{_tmpfilesdir}/%{name}.conf -%endif # install config for logrotate install -D -p -m 0644 %{S:2} %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd -# create room for necessary data +# create room for additional files (such as SQL schemas) install -d %{buildroot}%{_datadir}/%{name} # install sql-scripts for creating db schemes for various RDBMS -install -p -m 0644 src/odbc/mssql2000.sql %{buildroot}%{_datadir}/%{name} -install -p -m 0644 src/odbc/mssql2005.sql %{buildroot}%{_datadir}/%{name} -install -p -m 0644 src/odbc/mysql.sql %{buildroot}%{_datadir}/%{name} -install -p -m 0644 src/odbc/pg.sql %{buildroot}%{_datadir}/%{name} - -# Fix permissions for captcha script -# In fact, we can also chown root:ejabberd here, but I'm not sure -# that we should care about the possibility of reading by someone -# for this *default* sript, which is not intended to be changed -chmod 755 %{buildroot}%{_libdir}/%{name}/priv/bin/captcha.sh - -%if 0%{?el5}%{?el6} -# Use usermode on old systems -mkdir -p %{buildroot}%{_bindir} -ln -s consolehelper %{buildroot}%{_bindir}/ejabberdctl -install -D -p -m 0644 %{S:10} %{buildroot}%{_sysconfdir}/security/console.apps/ejabberdctl -%else -# Use polkit +install -p -m 0644 sql/mssql2000.sql %{buildroot}%{_datadir}/%{name} +install -p -m 0644 sql/mssql2005.sql %{buildroot}%{_datadir}/%{name} +install -p -m 0644 sql/mssql2012.sql %{buildroot}%{_datadir}/%{name} +install -p -m 0644 sql/mysql.sql %{buildroot}%{_datadir}/%{name} +install -p -m 0644 sql/pg.sql %{buildroot}%{_datadir}/%{name} + +# Install polkit-related files install -D -p -m 0644 %{S:12} %{buildroot}%{_datadir}/polkit-1/actions/ejabberdctl.policy install -D -p -m 0644 %{S:13} %{buildroot}%{_datadir}/polkit-1/rules.d/51-ejabberdctl.rules -%endif - -# Remove installed doc-files -rm -rf %{buildroot}%{_defaultdocdir} %pre @@ -293,25 +221,18 @@ if [ $1 -gt 1 ]; then # fix cookie path (since ver. 2.1.0 cookie stored in /var/lib/ejabberd/spool # rather than in /var/lib/ejabberd - if [ -f /var/lib/ejabberd/.erlang.cookie ]; then - cp -pu /var/lib/ejabberd/{,spool/}.erlang.cookie + if [ -f /var/lib/ejabberd/spool/.erlang.cookie ]; then + cp -pu /var/lib/ejabberd/{spool/,}.erlang.cookie echo - echo The ejabberd cookie file was moved. - echo Please delete old one from /var/lib/ejabberd/.erlang.cookie + echo The ejabberd cookie file was moved again. + echo Please delete old one from /var/lib/ejabberd/spool/.erlang.cookie echo fi fi %post -%if 0%{?el5}%{?el6} -if [ $1 -eq 1 ]; then - # Initial installation - /sbin/chkconfig --add %{name} || : -fi -%else %systemd_post %{name}.service -%endif # Create SSL certificate with default values if it doesn't exist (cd /etc/ejabberd @@ -336,111 +257,56 @@ fi) %preun -%if 0%{?el5}%{?el6} -if [ $1 -eq 0 ]; then - # Package removal, not upgrade - /sbin/service %{name} stop >/dev/null 2>&1 || : - /sbin/chkconfig --del %{name} || : -fi -%else %systemd_preun %{name}.service -%endif %postun -%if 0%{?el5}%{?el6} -if [ $1 -ge 1 ]; then - # Package upgrade, not uninstall - /sbin/service %{name} condrestart >/dev/null 2>&1 -fi -%else %systemd_postun_with_restart %{name}.service -%endif - - -%clean -rm -rf %{buildroot} %files %doc COPYING %attr(750,ejabberd,ejabberd) %dir %{_sysconfdir}/ejabberd -%attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/ejabberd.cfg +%attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/ejabberd.yml %attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/ejabberdctl.cfg %attr(640,ejabberd,ejabberd) %config(noreplace) %{_sysconfdir}/ejabberd/inetrc -%if 0%{?el5}%{?el6} -%config(noreplace) %{_sysconfdir}/sysconfig/%{name} -%{_initrddir}/%{name} -%else %{_unitdir}/%{name}.service %{_tmpfilesdir}/%{name}.conf -%endif %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} %config(noreplace) %{_sysconfdir}/pam.d/%{name} %config(noreplace) %{_sysconfdir}/pam.d/ejabberdctl -%if 0%{?el5}%{?el6} -%{_sbindir}/ejabberdctl -%config(noreplace) %{_sysconfdir}/security/console.apps/ejabberdctl -%else %{_datadir}/polkit-1/actions/ejabberdctl.policy %{_datadir}/polkit-1/rules.d/51-ejabberdctl.rules -%endif %{_bindir}/ejabberdctl %dir %{_libdir}/%{name} %dir %{_libdir}/%{name}/ebin %dir %{_libdir}/%{name}/include -%dir %{_libdir}/%{name}/include/eldap -%dir %{_libdir}/%{name}/include/mod_muc -%dir %{_libdir}/%{name}/include/mod_proxy65 -%dir %{_libdir}/%{name}/include/mod_pubsub -%dir %{_libdir}/%{name}/include/web %dir %{_libdir}/%{name}/priv %dir %{_libdir}/%{name}/priv/bin %dir %{_libdir}/%{name}/priv/lib %dir %{_libdir}/%{name}/priv/msgs -%{_libdir}/%{name}/ebin/%{name}.app +%{_libdir}/%{name}/ebin/*.app %{_libdir}/%{name}/ebin/*.beam -%{_libdir}/%{name}/include/XmppAddr.hrl -%{_libdir}/%{name}/include/adhoc.hrl -%{_libdir}/%{name}/include/cyrsasl.hrl -%{_libdir}/%{name}/include/ejabberd.hrl -%{_libdir}/%{name}/include/ejabberd_commands.hrl -%{_libdir}/%{name}/include/ejabberd_config.hrl -%{_libdir}/%{name}/include/ejabberd_ctl.hrl -%{_libdir}/%{name}/include/eldap/ELDAPv3.hrl -%{_libdir}/%{name}/include/eldap/eldap.hrl -%{_libdir}/%{name}/include/jlib.hrl -%{_libdir}/%{name}/include/mod_muc/mod_muc_room.hrl -%{_libdir}/%{name}/include/mod_privacy.hrl -%{_libdir}/%{name}/include/mod_proxy65/mod_proxy65.hrl -%{_libdir}/%{name}/include/mod_pubsub/pubsub.hrl -%{_libdir}/%{name}/include/mod_roster.hrl -%{_libdir}/%{name}/include/web/ejabberd_http.hrl -%{_libdir}/%{name}/include/web/ejabberd_web_admin.hrl -%{_libdir}/%{name}/include/web/http_bind.hrl +%{_libdir}/%{name}/include/*.hrl %{_libdir}/%{name}/priv/bin/captcha.sh %attr(4750,root,ejabberd) %{_libdir}/%{name}/priv/bin/epam -%{_libdir}/%{name}/priv/lib/ejabberd_zlib_drv.so -%{_libdir}/%{name}/priv/lib/expat_erl.so -%{_libdir}/%{name}/priv/lib/iconv_erl.so -%{_libdir}/%{name}/priv/lib/sha_drv.so -%{_libdir}/%{name}/priv/lib/stringprep_drv.so -%{_libdir}/%{name}/priv/lib/tls_drv.so +%{_libdir}/%{name}/priv/lib/*.so + %{_libdir}/%{name}/priv/msgs/*.msg %dir %{_datadir}/%{name} %{_datadir}/%{name}/mssql2000.sql %{_datadir}/%{name}/mssql2005.sql +%{_datadir}/%{name}/mssql2012.sql %{_datadir}/%{name}/mysql.sql %{_datadir}/%{name}/pg.sql %attr(750,ejabberd,ejabberd) %dir /var/lib/ejabberd -%attr(750,ejabberd,ejabberd) %dir /var/lib/ejabberd/spool %attr(750,ejabberd,ejabberd) %dir /var/lock/ejabberdctl %attr(750,ejabberd,ejabberd) %dir /var/log/ejabberd @@ -454,6 +320,14 @@ rm -rf %{buildroot} %changelog +* Sat Aug 30 2014 Peter Lemenkov - 14.07-1 +- Ver. 14.07 +- Dropped support for EPEL5, EPEL6 +- Dropped initial untested support for GSSAPI - nobody is interested in testing + and further developing this +- Fixed https://bugzilla.redhat.com/1089475 +- Fixed https://bugzilla.redhat.com/1117450 + * Thu Aug 28 2014 Peter Lemenkov - 2.1.13-10 - Rebuild with Erlang 17.2.1 diff --git a/ejabberd.sysconfig b/ejabberd.sysconfig deleted file mode 100644 index 20a8a7a..0000000 --- a/ejabberd.sysconfig +++ /dev/null @@ -1,13 +0,0 @@ -## Settings for ejabberd - -## Where should ejabberd find its configuration file? -# -CONFIG_FILE=/etc/ejabberd/ejabberd.cfg - -## ULIMIT_MAX_FILES alters the number of files that ejabberd is -## allowed to have open at once. If it is unset the system default -## (usually 1024) will be used. ejabberd will want over twice as many -## open files as it has active connections, so if you have a few -## hundred or more users you will want to set this. -# -ULIMIT_MAX_FILES=1024 diff --git a/ejabberdctl.apps b/ejabberdctl.apps deleted file mode 100644 index 30eab2c..0000000 --- a/ejabberdctl.apps +++ /dev/null @@ -1,5 +0,0 @@ -USER=root -PROGRAM=/usr/sbin/ejabberdctl -SESSION=yes -FALLBACK=no -GUI=no diff --git a/sources b/sources index 4c10b8d..b1728e6 100644 --- a/sources +++ b/sources @@ -1 +1,2 @@ -2a7c3b711b4f7091f811c51b52beb735 ejabberd-v2.1.13.tar.gz +a26b8acfc8fd15d94874befa06da15be ejabberd-14.07.tar.gz +0362024d73b2c8ee38246be5a168869b ejabberd-src-deps.tar