public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ejabberd] f45: Ver. 2.1.3
@ 2026-09-23 19:06 Peter Lemenkov
0 siblings, 0 replies; only message in thread
From: Peter Lemenkov @ 2026-09-23 19:06 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/ejabberd
Branch : f45
Commit : 57a13caf3704ac7be5089275125b0e6b640e3289
Author : Peter Lemenkov <peter@fedoraproject.org>
Date : 2010-03-12T15:54:06+00:00
Stats : +2120/-1699 in 20 file(s)
URL : https://src.fedoraproject.org/rpms/ejabberd/c/57a13caf3704ac7be5089275125b0e6b640e3289?branch=f45
Log:
Ver. 2.1.3
---
diff --git a/.cvsignore b/.cvsignore
index 79517e7..ff96921 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1 +1 @@
-ejabberd-2.1.2.tar.gz
+ejabberd-2.1.3.tar.gz
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
new file mode 100644
index 0000000..09ae80c
--- /dev/null
+++ b/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch
@@ -0,0 +1,26 @@
+From 53f8a296b0f83ea9dafc3aed8b49c34941f31454 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Tue, 16 Feb 2010 16:03:38 +0300
+Subject: [PATCH 1/9] Fix PAM service example name to match actual one
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/ejabberd.cfg.example | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example
+index 4087947..b351cfc 100644
+--- a/src/ejabberd.cfg.example
++++ b/src/ejabberd.cfg.example
+@@ -235,7 +235,7 @@
+ %% Authentication using PAM
+ %%
+ %%{auth_method, pam}.
+-%%{pam_service, "pamservicename"}.
++%%{pam_service, "ejabberd"}.
+
+ %%
+ %% Authentication using LDAP
+--
+1.6.6.1
+
diff --git a/ejabberd-0002-Mention-mod_ctlextra-as-an-ejabberd-module.patch b/ejabberd-0002-Mention-mod_ctlextra-as-an-ejabberd-module.patch
new file mode 100644
index 0000000..2767291
--- /dev/null
+++ b/ejabberd-0002-Mention-mod_ctlextra-as-an-ejabberd-module.patch
@@ -0,0 +1,25 @@
+From 6365359078925b436d1d3238a2dd6d6c11271fac Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Tue, 16 Feb 2010 16:05:53 +0300
+Subject: [PATCH 2/9] Mention mod_ctlextra as an ejabberd module
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/ejabberd.app | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/src/ejabberd.app b/src/ejabberd.app
+index 2f5f123..cc7e1f8 100644
+--- a/src/ejabberd.app
++++ b/src/ejabberd.app
+@@ -70,6 +70,7 @@
+ mod_caps,
+ mod_configure2,
+ mod_configure,
++ mod_ctlextra,
+ mod_disco,
+ mod_echo,
+ mod_http_bind,
+--
+1.6.6.1
+
diff --git a/ejabberd-0003-Fixed-delays-in-s2s-connections.patch b/ejabberd-0003-Fixed-delays-in-s2s-connections.patch
new file mode 100644
index 0000000..19101dd
--- /dev/null
+++ b/ejabberd-0003-Fixed-delays-in-s2s-connections.patch
@@ -0,0 +1,54 @@
+From 019bf239560925edd809bb7922a49b3737412067 Mon Sep 17 00:00:00 2001
+From: Sergei Golovan <sgolovan@nes.ru>
+Date: Tue, 16 Feb 2010 16:07:37 +0300
+Subject: [PATCH 3/9] 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 | 6 +++---
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
+index f221302..b55c805 100644
+--- a/src/ejabberd_s2s_in.erl
++++ b/src/ejabberd_s2s_in.erl
+@@ -352,7 +352,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
+ case {ejabberd_s2s:allow_host(To, From),
+ lists:member(LTo, ejabberd_router:dirty_get_all_domains())} of
+ {true, true} ->
+- ejabberd_s2s_out:terminate_if_waiting_delay(To, From),
++ %%ejabberd_s2s_out:terminate_if_waiting_delay(To, From),
+ ejabberd_s2s_out:start(To, From,
+ {verify, self(),
+ Key, StateData#state.streamid}),
+diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
+index ac7eee2..048d018 100644
+--- a/src/ejabberd_s2s_out.erl
++++ b/src/ejabberd_s2s_out.erl
+@@ -90,7 +90,7 @@
+ [From, Host, Type])).
+ -endif.
+
+--define(FSMTIMEOUT, 30000).
++-define(FSMTIMEOUT, 60000).
+
+ %% We do not block on send anymore.
+ -define(TCP_SEND_TIMEOUT, 15000).
+@@ -226,8 +226,8 @@ open_socket(init, StateData) ->
+ {error, _Reason} ->
+ ?INFO_MSG("s2s connection: ~s -> ~s (remote server not found)",
+ [StateData#state.myname, StateData#state.server]),
+- wait_before_reconnect(StateData)
+- %%{stop, normal, StateData}
++ %%wait_before_reconnect(StateData)
++ {stop, normal, StateData}
+ end;
+ open_socket(stop, StateData) ->
+ ?INFO_MSG("s2s connection: ~s -> ~s (stopped in open socket)",
+--
+1.6.6.1
+
diff --git a/ejabberd-0004-Introducing-mod_admin_extra.patch b/ejabberd-0004-Introducing-mod_admin_extra.patch
new file mode 100644
index 0000000..47d10a0
--- /dev/null
+++ b/ejabberd-0004-Introducing-mod_admin_extra.patch
@@ -0,0 +1,1318 @@
+From 766c7603bff9d7548d43229f855e9ac7b79c63cd Mon Sep 17 00:00:00 2001
+From: Badlop <badlop@process-one.net>
+Date: Tue, 16 Feb 2010 16:12:17 +0300
+Subject: [PATCH 4/9] 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:
+https://forge.process-one.net/browse/ejabberd-modules/mod_admin_extra
+(trunk, revision 976).
+---
+ src/ejabberd.app | 1 +
+ src/mod_admin_extra.erl | 1279 +++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 1280 insertions(+), 0 deletions(-)
+ create mode 100644 src/mod_admin_extra.erl
+
+diff --git a/src/ejabberd.app b/src/ejabberd.app
+index cc7e1f8..ae8ce83 100644
+--- a/src/ejabberd.app
++++ b/src/ejabberd.app
+@@ -105,6 +105,7 @@
+ mod_vcard_ldap,
+ mod_vcard_odbc,
+ 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..9f3ca14
+--- /dev/null
++++ b/src/mod_admin_extra.erl
+@@ -0,0 +1,1279 @@
++%%%-------------------------------------------------------------------
++%%% File : mod_admin_extra.erl
++%%% Author : Badlop <badlop@process-one.net>
++%%% Purpose : Contributed administrative functions and commands
++%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
++%%%
++%%%
++%%% 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,
++ %% Vcard
++ set_nickname/3,
++ get_vcard/3,
++ get_vcard/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_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,
++ %% 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",
++ 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",
++ 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 acount 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 = 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 = 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_vcard2,
++ args = [{user, string}, {host, string}, {name, string}, {subname, string}, {content, string}],
++ result = {res, rescode}},
++
++ #ejabberd_commands{name = add_rosteritem, tags = [roster],
++ desc = "Add an item to a user's roster",
++ 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",
++ 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},
++ {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 = 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 = 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.
++
++
++-record(last_activity, {us, timestamp, status}).
++
++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 mnesia:dirty_read(last_activity, {LUser, LServer}) of
++ %% If it is
++ %% existent:
++ [#last_activity{timestamp = TimeStamp}] ->
++ %% 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:
++ [] ->
++ %% 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}.
++
++
++%%
++%% 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<Num) and (Num=<length(Resources)) of
++ true ->
++ 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])].
++
++%% Make string more print-friendly
++stringize(String) ->
++ %% Replace newline characters with other code
++ element(2, regexp:gsub(String, "\n", "\\n")).
++
++
++%%%
++%%% 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) ->
++ get_vcard_content(User, Host, [Name]).
++
++get_vcard(User, Host, Name, Subname) ->
++ get_vcard_content(User, Host, [Name, Subname]).
++
++set_vcard(User, Host, Name, Content) ->
++ set_vcard_content(User, Host, [Name], Content).
++
++set_vcard(User, Host, Name, Subname, Content) ->
++ set_vcard_content(User, Host, [Name, Subname], Content).
++
++
++%%
++%% Internal vcard
++
++get_vcard_content(User, Server, Data) ->
++ [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
++ JID = jlib:make_jid(User, 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
++ false -> "Error: no_value";
++ Elem -> xml:get_tag_cdata(Elem)
++ end;
++ [] ->
++ "Error: no_vcard"
++ end.
++
++get_vcard([Data1, Data2], A1) ->
++ case xml:get_subtag(A1, Data1) of
++ false -> false;
++ A2 -> get_vcard([Data2], A2)
++ end;
++
++get_vcard([Data], A1) ->
++ xml:get_subtag(A1, Data).
++
++set_vcard_content(User, Server, Data, Content) ->
++ [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
++ JID = jlib:make_jid(User, 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, Content, A2);
++ [] ->
++ update_vcard_els(Data, Content, [])
++ 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, Content, Els1) ->
++ Els2 = lists:keysort(2, Els1),
++ [Data1 | Data2] = Data,
++ NewEl = case Data2 of
++ [] ->
++ {xmlelement, Data1, [], [{xmlcdata,Content}]};
++ [D2] ->
++ OldEl = case lists:keysearch(Data1, 2, Els2) of
++ {value, A} -> A;
++ false -> {xmlelement, Data1, [], []}
++ end,
++ {xmlelement, _, _, ContentOld1} = OldEl,
++ Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]}],
++ ContentOld2 = lists:keysort(2, ContentOld1),
++ ContentOld3 = lists:keydelete(D2, 2, ContentOld2),
++ ContentNew = lists:keymerge(2, Content2, ContentOld3),
++ {xmlelement, Data1, [], ContentNew}
++ end,
++ Els3 = lists:keydelete(Data1, 2, Els2),
++ lists:keymerge(2, [NewEl], 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) ->
++ mnesia:transaction(
++ fun() ->
++ mnesia:write({roster,
++ {LU,LS,{User,Server,[]}}, % uj
++ {LU,LS}, % user
++ {User,Server,[]}, % jid
++ Nick, % name: "Mom", []
++ Subscription, % subscription: none, to=you see him, from=he sees you, both
++ none, % ask: out=send request, in=somebody requests you, none
++ [Group], % groups: ["Family"]
++ Xattrs, % xattrs: [{"category","conference"}]
++ [] % xs: []
++ })
++ end).
++
++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) ->
++ mnesia:transaction(
++ fun() ->
++ mnesia:delete({roster, {LU, LS, {User, Server, []}}})
++ end).
++
++
++%% -----------------------------
++%% Get Roster
++%% -----------------------------
++
++get_roster(User, Server) ->
++ {ok, Roster} = get_roster2(User, Server),
++ make_roster_xmlrpc(Roster).
++
++get_roster2(User, Server) ->
++ Modules = gen_mod:loaded_modules(Server),
++ Roster = case lists:member(mod_roster, Modules) of
++ true ->
++ mod_roster:get_user_roster([], {User, Server});
++ false ->
++ case lists:member(mod_roster_odbc, Modules) of
++ true ->
++ mod_roster_odbc:get_user_roster([], {User, Server});
++ false ->
++ {error, "Neither mod_roster or mod_roster_odbc are enabled"}
++ end
++ end,
++ {ok, Roster}.
++
++%% 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,
++ Groups = case Item#roster.groups of
++ [] -> [""];
++ Gs -> Gs
++ end,
++ ItemsX = [{JIDS, Nick, 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) ->
++ Item = build_roster_item(U, S, Action),
++ ResIQ = build_iq_roster_push(Item),
++ LJID = jlib:make_jid(LU, LS, R),
++ 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]
++ }
++ ]
++ }.
++
++
++%%%
++%%% Shared Roster Groups
++%%%
++
++srg_create(Group, Host, Name, Description, Display) ->
++ {ok, DisplayList} = regexp:split(Display, "\\\\n"),
++ 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"}],
++ [{xmlelement, "body", [], [{xmlcdata, Body}]}]
++ };
++build_packet(message_headline, [Subject, Body]) ->
++ {xmlelement, "message",
++ [{"type", "headline"}],
++ [{xmlelement, "subject", [], [{xmlcdata, Subject}]},
++ {xmlelement, "body", [], [{xmlcdata, Body}]}
++ ]
++ }.
++
++%%%
++%%% 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 regexp:first_match(String, RegExp) of
++ nomatch ->
++ false;
++ {match, _, _} ->
++ true;
++ {error, ErrDesc} ->
++ io:format(
++ "Wrong regexp ~p in ACL: ~p",
++ [RegExp, lists:flatten(regexp:format_error(ErrDesc))]),
++ false
++ end.
++is_glob_match(String, Glob) ->
++ is_regexp_match(String, regexp:sh_to_awk(Glob)).
+--
+1.6.6.1
+
diff --git a/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch b/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
new file mode 100644
index 0000000..eafbd02
--- /dev/null
+++ b/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
@@ -0,0 +1,48 @@
+From 1a2f7fc0f4ba4ff9f9cf1ee409ce461000685eef Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Tue, 16 Feb 2010 16:30:05 +0300
+Subject: [PATCH 5/9] Fedora-specific changes to ejabberdctl
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/ejabberdctl.template | 8 ++++----
+ 1 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template
+index 9ede77d..69c74d6 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.6.6.1
+
diff --git a/ejabberd-0006-Support-to-authenticate-against-SASL-GSSAPI-http-www.patch b/ejabberd-0006-Support-to-authenticate-against-SASL-GSSAPI-http-www.patch
new file mode 100644
index 0000000..e2b3e7d
--- /dev/null
+++ b/ejabberd-0006-Support-to-authenticate-against-SASL-GSSAPI-http-www.patch
@@ -0,0 +1,425 @@
+From 764adfa6bce78bc8615053a476c2bd38bdab349d Mon Sep 17 00:00:00 2001
+From: Badlop <badlop@process-one.net>
+Date: Tue, 29 Sep 2009 15:10:15 +0200
+Subject: [PATCH 6/9] Support to authenticate against SASL GSSAPI http://www.ejabberd.im/cyrsasl_gssapi
+
+---
+ src/cyrsasl.erl | 34 ++++++-----
+ src/cyrsasl_anonymous.erl | 6 +-
+ src/cyrsasl_digest.erl | 4 +-
+ src/cyrsasl_gssapi.erl | 143 +++++++++++++++++++++++++++++++++++++++++++++
+ src/cyrsasl_plain.erl | 5 +-
+ src/ejabberd.hrl | 7 ++
+ src/ejabberd_c2s.erl | 8 ++-
+ src/ejabberd_net.erl | 39 ++++++++++++
+ 8 files changed, 224 insertions(+), 22 deletions(-)
+ create mode 100644 src/cyrsasl_gssapi.erl
+ create mode 100644 src/ejabberd_net.erl
+
+diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl
+index 121ef6d..192d5b5 100644
+--- a/src/cyrsasl.erl
++++ b/src/cyrsasl.erl
+@@ -30,19 +30,20 @@
+ -export([start/0,
+ register_mechanism/3,
+ listmech/1,
+- server_new/7,
++ server_new/8,
+ server_start/3,
+ server_step/2]).
+
+ -record(sasl_mechanism, {mechanism, module, require_plain_password}).
+--record(sasl_state, {service, myname, realm,
+- get_password, check_password, check_password_digest,
+- mech_mod, mech_state}).
++-record(sasl_state, {service, myname,
++ mech_mod, mech_state, ctx}).
++
++-include("ejabberd.hrl").
+
+ -export([behaviour_info/1]).
+
+ behaviour_info(callbacks) ->
+- [{mech_new, 4}, {mech_step, 2}];
++ [{mech_new, 1}, {mech_step, 2}];
+ behaviour_info(_Other) ->
+ undefined.
+
+@@ -50,6 +51,7 @@ start() ->
+ ets:new(sasl_mechanism, [named_table,
+ public,
+ {keypos, #sasl_mechanism.mechanism}]),
++ cyrsasl_gssapi:start([]),
+ cyrsasl_plain:start([]),
+ cyrsasl_digest:start([]),
+ cyrsasl_anonymous:start([]),
+@@ -113,24 +115,26 @@ listmech(Host) ->
+ filter_anonymous(Host, Mechs).
+
+ server_new(Service, ServerFQDN, UserRealm, _SecFlags,
+- GetPassword, CheckPassword, CheckPasswordDigest) ->
++ GetPassword, CheckPassword, CheckPasswordDigest, FQDN) ->
++ Ctx = #sasl_ctx{
++ host = ServerFQDN,
++ realm = UserRealm,
++ get_password = GetPassword,
++ check_password = CheckPassword,
++ check_password_digest= CheckPasswordDigest,
++ fqdn = FQDN
++ },
++
+ #sasl_state{service = Service,
+ myname = ServerFQDN,
+- realm = UserRealm,
+- get_password = GetPassword,
+- check_password = CheckPassword,
+- check_password_digest= CheckPasswordDigest}.
++ ctx = Ctx}.
+
+ 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.ctx),
+ server_step(State#sasl_state{mech_mod = Module,
+ mech_state = MechState},
+ ClientIn);
+diff --git a/src/cyrsasl_anonymous.erl b/src/cyrsasl_anonymous.erl
+index 2b3ba36..ec75f40 100644
+--- a/src/cyrsasl_anonymous.erl
++++ b/src/cyrsasl_anonymous.erl
+@@ -27,12 +27,14 @@
+
+ -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]).
+
+ -behaviour(cyrsasl).
+
+ -record(state, {server}).
+
++-include("ejabberd.hrl").
++
+ start(_Opts) ->
+ cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, false),
+ ok.
+@@ -40,7 +42,7 @@ start(_Opts) ->
+ stop() ->
+ ok.
+
+-mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
++mech_new(#sasl_ctx{host=Host}) ->
+ {ok, #state{server = Host}}.
+
+ mech_step(State, _ClientIn) ->
+diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl
+index 2a7ce2a..a354778 100644
+--- a/src/cyrsasl_digest.erl
++++ b/src/cyrsasl_digest.erl
+@@ -29,7 +29,7 @@
+
+ -export([start/1,
+ stop/0,
+- mech_new/4,
++ mech_new/1,
+ mech_step/2]).
+
+ -include("ejabberd.hrl").
+@@ -45,7 +45,7 @@ start(_Opts) ->
+ stop() ->
+ ok.
+
+-mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) ->
++mech_new(#sasl_ctx{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..d292565
+--- /dev/null
++++ b/src/cyrsasl_gssapi.erl
+@@ -0,0 +1,143 @@
++%%%----------------------------------------------------------------------
++%%% File : cyrsasl_gssapi.erl
++%%% Author : Mikael Magnusson <mikma@users.sourceforge.net>
++%%% Purpose : GSSAPI SASL mechanism
++%%% Created : 1 June 2007 by Mikael Magnusson <mikma@users.sourceforge.net>
++%%% Id : $Id: $
++%%%----------------------------------------------------------------------
++%%%
++%%% Copyright (C) 2007 Mikael Magnusson <mikma@users.sourceforge.net>
++%%%
++%%% 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, "<Kerberos realm>"}.
++%%%
++%%% environment variables:
++%%% KRB5_KTNAME
++%%%
++
++-module(cyrsasl_gssapi).
++-author('mikma@users.sourceforge.net').
++-vsn('$Revision: $ ').
++
++-include("ejabberd.hrl").
++
++-export([start/1,
++ stop/0,
++ mech_new/1,
++ mech_step/2]).
++
++-behaviour(cyrsasl).
++
++-define(SERVER, cyrsasl_gssapi).
++-define(MSG, ?DEBUG).
++
++-record(state, {sasl,
++ needsmore=true,
++ step=0,
++ host,
++ authid,
++ authzid,
++ authrealm}).
++
++start(_Opts) ->
++ ChildSpec =
++ {?SERVER,
++ {esasl, start_link, [{local, ?SERVER}]},
++ transient,
++ 1000,
++ worker,
++ [esasl]},
++
++ {ok, _Pid} = supervisor:start_child(ejabberd_sup, ChildSpec),
++
++ cyrsasl:register_mechanism("GSSAPI", ?MODULE, false).
++
++stop() ->
++ esasl:stop(?SERVER),
++ supervisor:terminate_child(ejabberd_sup, ?SERVER),
++ supervisor:delete_child(ejabberd_sup, ?SERVER).
++
++mech_new(#sasl_ctx{host=Host, fqdn=FQDN}) ->
++ ?MSG("mech_new ~p ~p~n", [Host, FQDN]),
++ {ok, Sasl} = esasl:server_start(?SERVER, "GSSAPI", "xmpp", FQDN),
++ {ok, #state{sasl=Sasl,host=Host}}.
++
++mech_step(State, ClientIn) when is_list(ClientIn) ->
++ catch do_step(State, ClientIn).
++
++do_step(#state{needsmore=false}=State, _) ->
++ check_user(State);
++do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
++ ?MSG("mech_step~n", []),
++ case esasl:step(Sasl, list_to_binary(ClientIn)) of
++ {ok, RspAuth} ->
++ ?MSG("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} ->
++ ?MSG("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) ->
++ ?MSG("continue~n", []),
++ {continue, RspAuth, State#state{needsmore=false,step=Step+1}}.
++
++check_user(#state{authid=Authid,authzid=Authzid,
++ authrealm=Auth_realm,host=Host}) ->
++ Realm = ejabberd_config:get_local_option({sasl_realm, Host}),
++
++ if Realm =/= Auth_realm ->
++ ?MSG("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 ->
++ ?MSG("bad user ~p~n",[Authid]),
++ throw({error, "not-authorized"});
++ true ->
++ ok
++ end,
++
++ ?MSG("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 129fb8b..d920463 100644
+--- a/src/cyrsasl_plain.erl
++++ b/src/cyrsasl_plain.erl
+@@ -27,10 +27,11 @@
+ -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]).
+
+ -behaviour(cyrsasl).
+
++-include("ejabberd.hrl").
+ -record(state, {check_password}).
+
+ start(_Opts) ->
+@@ -40,7 +41,7 @@ start(_Opts) ->
+ stop() ->
+ ok.
+
+-mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
++mech_new(#sasl_ctx{check_password=CheckPassword}) ->
+ {ok, #state{check_password = CheckPassword}}.
+
+ mech_step(State, ClientIn) ->
+diff --git a/src/ejabberd.hrl b/src/ejabberd.hrl
+index e1f0cfd..39a41d5 100644
+--- a/src/ejabberd.hrl
++++ b/src/ejabberd.hrl
+@@ -59,3 +59,10 @@
+ -define(CRITICAL_MSG(Format, Args),
+ ejabberd_logger:critical_msg(?MODULE,?LINE,Format, Args)).
+
++-record(sasl_ctx, {
++ host,
++ realm,
++ get_password,
++ check_password,
++ check_password_digest,
++ fqdn}).
+diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
+index 6df6766..8edc1a0 100644
+--- a/src/ejabberd_c2s.erl
++++ b/src/ejabberd_c2s.erl
+@@ -69,6 +69,7 @@
+ -record(state, {socket,
+ sockmod,
+ socket_monitor,
++ fqdn,
+ xml_socket,
+ streamid,
+ sasl_state,
+@@ -206,9 +207,11 @@ init([{SockMod, Socket}, Opts]) ->
+ Socket
+ end,
+ SocketMonitor = SockMod:monitor(Socket1),
++ {ok, FQDN} = ejabberd_net:gethostname(Socket),
+ {ok, wait_for_stream, #state{socket = Socket1,
+ sockmod = SockMod,
+ socket_monitor = SocketMonitor,
++ fqdn = FQDN,
+ xml_socket = XMLSocket,
+ zlib = Zlib,
+ tls = TLS,
+@@ -252,6 +255,8 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
+ send_header(StateData, Server, "1.0", DefaultLang),
+ case StateData#state.authenticated of
+ false ->
++ FQDN = StateData#state.fqdn,
++ ?INFO_MSG("FQDN: ~p~n", [FQDN]),
+ SASLState =
+ cyrsasl:server_new(
+ "jabber", Server, "", [],
+@@ -266,7 +271,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,
++ FQDN),
+ Mechs = lists:map(
+ fun(S) ->
+ {xmlelement, "mechanism", [],
+diff --git a/src/ejabberd_net.erl b/src/ejabberd_net.erl
+new file mode 100644
+index 0000000..e9ab70a
+--- /dev/null
++++ b/src/ejabberd_net.erl
+@@ -0,0 +1,39 @@
++%%%----------------------------------------------------------------------
++%%% File : ejabberd_net.erl
++%%% Author : Mikael Magnusson <mikma@users.sourceforge.net>
++%%% Purpose : Serve C2S connection
++%%% Created : 6 June 2007 by Mikael Magnusson <mikma@users.sourceforge.net>
++%%% Id : $Id: $
++%%%----------------------------------------------------------------------
++
++-module(ejabberd_net).
++-author('mikma@users.sourceforge.net').
++%% -update_info({update, 0}).
++
++-export([gethostname/1]).
++
++-include("ejabberd.hrl").
++-include_lib("kernel/include/inet.hrl").
++
++%% Copied from ejabberd_socket.erl of ejabberd 2.0.3
++-record(socket_state, {sockmod, socket, receiver}).
++
++%%
++%% gethostname(Socket)
++%%
++gethostname(Socket) ->
++ ?INFO_MSG("gethostname ~p~n", [Socket]),
++%% {ok, "skinner.hem.za.org"}.
++
++ case ejabberd_config:get_local_option({sasl_fqdn, ?MYNAME}) of
++ undefined ->
++ {ok, {Addr, _Port}} = inet:sockname(Socket#socket_state.socket),
++ case inet:gethostbyaddr(Addr) of
++ {ok, HostEnt} when is_record(HostEnt, hostent) ->
++ {ok, HostEnt#hostent.h_name};
++ {error, What} ->
++ ?ERROR_MSG("Error in gethostname:~nSocket: ~p~nError: ~p", [What]),
++ error
++ end;
++ F -> {ok, F}
++ end.
+--
+1.6.6.1
+
diff --git a/ejabberd-0007-Fixed-typo.patch b/ejabberd-0007-Fixed-typo.patch
new file mode 100644
index 0000000..919212e
--- /dev/null
+++ b/ejabberd-0007-Fixed-typo.patch
@@ -0,0 +1,26 @@
+From d4e215baf7edfb1fe534d91fc83eaeb9426589a0 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Fri, 5 Mar 2010 17:48:51 +0300
+Subject: [PATCH 7/9] Fixed typo
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/ejabberd_net.erl | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/src/ejabberd_net.erl b/src/ejabberd_net.erl
+index e9ab70a..d887c23 100644
+--- a/src/ejabberd_net.erl
++++ b/src/ejabberd_net.erl
+@@ -32,7 +32,7 @@ gethostname(Socket) ->
+ {ok, HostEnt} when is_record(HostEnt, hostent) ->
+ {ok, HostEnt#hostent.h_name};
+ {error, What} ->
+- ?ERROR_MSG("Error in gethostname:~nSocket: ~p~nError: ~p", [What]),
++ ?ERROR_MSG("Error in gethostname:~nSocket: ~p~nError: ~p at Addr ~p", [Socket, What, Addr]),
+ error
+ end;
+ F -> {ok, F}
+--
+1.6.6.1
+
diff --git a/ejabberd-0008-Quick-fix-for-error-nxdomain.patch b/ejabberd-0008-Quick-fix-for-error-nxdomain.patch
new file mode 100644
index 0000000..3ea7835
--- /dev/null
+++ b/ejabberd-0008-Quick-fix-for-error-nxdomain.patch
@@ -0,0 +1,27 @@
+From 2bbffda3b07c07b6abf5d28e0b6af4094b56d981 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Fri, 5 Mar 2010 21:13:35 +0300
+Subject: [PATCH 8/9] Quick fix for {error,nxdomain}
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/ejabberd_net.erl | 3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+diff --git a/src/ejabberd_net.erl b/src/ejabberd_net.erl
+index d887c23..b6943e1 100644
+--- a/src/ejabberd_net.erl
++++ b/src/ejabberd_net.erl
+@@ -31,6 +31,9 @@ gethostname(Socket) ->
+ case inet:gethostbyaddr(Addr) of
+ {ok, HostEnt} when is_record(HostEnt, hostent) ->
+ {ok, HostEnt#hostent.h_name};
++ {error, nxdomain} ->
++ % Quick fix
++ {ok, inet_parse:ntoa(Addr)};
+ {error, What} ->
+ ?ERROR_MSG("Error in gethostname:~nSocket: ~p~nError: ~p at Addr ~p", [Socket, What, Addr]),
+ error
+--
+1.6.6.1
+
diff --git a/ejabberd-0009-Replace-MSG-with-DEBUG.patch b/ejabberd-0009-Replace-MSG-with-DEBUG.patch
new file mode 100644
index 0000000..dd6f963
--- /dev/null
+++ b/ejabberd-0009-Replace-MSG-with-DEBUG.patch
@@ -0,0 +1,87 @@
+From 94d7e120d3ad6072774286df2d4b26ea2fcd34f7 Mon Sep 17 00:00:00 2001
+From: Badlop <badlop@process-one.net>
+Date: Mon, 8 Mar 2010 22:35:12 +0100
+Subject: [PATCH 9/9] Replace MSG with DEBUG
+
+---
+ src/cyrsasl_gssapi.erl | 17 ++++++++---------
+ 1 files changed, 8 insertions(+), 9 deletions(-)
+
+diff --git a/src/cyrsasl_gssapi.erl b/src/cyrsasl_gssapi.erl
+index d292565..24a3796 100644
+--- a/src/cyrsasl_gssapi.erl
++++ b/src/cyrsasl_gssapi.erl
+@@ -51,7 +51,6 @@
+ -behaviour(cyrsasl).
+
+ -define(SERVER, cyrsasl_gssapi).
+--define(MSG, ?DEBUG).
+
+ -record(state, {sasl,
+ needsmore=true,
+@@ -80,7 +79,7 @@ stop() ->
+ supervisor:delete_child(ejabberd_sup, ?SERVER).
+
+ mech_new(#sasl_ctx{host=Host, fqdn=FQDN}) ->
+- ?MSG("mech_new ~p ~p~n", [Host, FQDN]),
++ ?DEBUG("mech_new ~p ~p~n", [Host, FQDN]),
+ {ok, Sasl} = esasl:server_start(?SERVER, "GSSAPI", "xmpp", FQDN),
+ {ok, #state{sasl=Sasl,host=Host}}.
+
+@@ -90,10 +89,10 @@ mech_step(State, ClientIn) when is_list(ClientIn) ->
+ do_step(#state{needsmore=false}=State, _) ->
+ check_user(State);
+ do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
+- ?MSG("mech_step~n", []),
++ ?DEBUG("mech_step~n", []),
+ case esasl:step(Sasl, list_to_binary(ClientIn)) of
+ {ok, RspAuth} ->
+- ?MSG("ok~n", []),
++ ?DEBUG("ok~n", []),
+ {ok, Display_name} = esasl:property_get(Sasl, gssapi_display_name),
+ {ok, Authzid} = esasl:property_get(Sasl, authzid),
+ {Authid, [$@ | Auth_realm]} =
+@@ -103,7 +102,7 @@ do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
+ authrealm=Auth_realm},
+ handle_step_ok(State1, binary_to_list(RspAuth));
+ {needsmore, RspAuth} ->
+- ?MSG("needsmore~n", []),
++ ?DEBUG("needsmore~n", []),
+ if (Step > 0) and (ClientIn =:= []) and (RspAuth =:= <<>>) ->
+ {error, "not-authorized"};
+ true ->
+@@ -117,7 +116,7 @@ do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
+ handle_step_ok(State, []) ->
+ check_user(State);
+ handle_step_ok(#state{step=Step}=State, RspAuth) ->
+- ?MSG("continue~n", []),
++ ?DEBUG("continue~n", []),
+ {continue, RspAuth, State#state{needsmore=false,step=Step+1}}.
+
+ check_user(#state{authid=Authid,authzid=Authzid,
+@@ -125,7 +124,7 @@ check_user(#state{authid=Authid,authzid=Authzid,
+ Realm = ejabberd_config:get_local_option({sasl_realm, Host}),
+
+ if Realm =/= Auth_realm ->
+- ?MSG("bad realm ~p (expected ~p)~n",[Auth_realm, Realm]),
++ ?DEBUG("bad realm ~p (expected ~p)~n",[Auth_realm, Realm]),
+ throw({error, "not-authorized"});
+ true ->
+ ok
+@@ -133,11 +132,11 @@ check_user(#state{authid=Authid,authzid=Authzid,
+
+ case ejabberd_auth:is_user_exists(Authid, Host) of
+ false ->
+- ?MSG("bad user ~p~n",[Authid]),
++ ?DEBUG("bad user ~p~n",[Authid]),
+ throw({error, "not-authorized"});
+ true ->
+ ok
+ end,
+
+- ?MSG("GSSAPI authenticated ~p ~p~n", [Authid, Authzid]),
++ ?DEBUG("GSSAPI authenticated ~p ~p~n", [Authid, Authzid]),
+ {ok, [{username, Authid}, {authzid, Authzid}]}.
+--
+1.6.6.1
+
diff --git a/ejabberd-c2s_dos_fix.diff b/ejabberd-c2s_dos_fix.diff
deleted file mode 100644
index 2fe5f26..0000000
--- a/ejabberd-c2s_dos_fix.diff
+++ /dev/null
@@ -1,83 +0,0 @@
-Index: branches/ejabberd-2.1.x/src/ejabberd_c2s.erl
-===================================================================
-diff -u -N -r2911 -r2936
---- branches/ejabberd-2.1.x/src/ejabberd_c2s.erl (.../ejabberd_c2s.erl) (revision 2911)
-+++ branches/ejabberd-2.1.x/src/ejabberd_c2s.erl (.../ejabberd_c2s.erl) (revision 2936)
-@@ -28,8 +28,10 @@
- -author('alexey@process-one.net').
- -update_info({update, 0}).
-
---behaviour(gen_fsm).
-+-define(GEN_FSM, p1_fsm).
-
-+-behaviour(?GEN_FSM).
-+
- %% External exports
- -export([start/2,
- stop/1,
-@@ -104,8 +106,8 @@
-
- %% Module start with or without supervisor:
- -ifdef(NO_TRANSIENT_SUPERVISORS).
---define(SUPERVISOR_START, gen_fsm:start(ejabberd_c2s, [SockData, Opts],
-- ?FSMOPTS)).
-+-define(SUPERVISOR_START, ?GEN_FSM:start(ejabberd_c2s, [SockData, Opts],
-+ fsm_limit_opts() ++ ?FSMOPTS)).
- -else.
- -define(SUPERVISOR_START, supervisor:start_child(ejabberd_c2s_sup,
- [SockData, Opts])).
-@@ -140,17 +142,18 @@
- ?SUPERVISOR_START.
-
- start_link(SockData, Opts) ->
-- gen_fsm:start_link(ejabberd_c2s, [SockData, Opts], ?FSMOPTS).
-+ ?GEN_FSM:start_link(ejabberd_c2s, [SockData, Opts],
-+ fsm_limit_opts() ++ ?FSMOPTS).
-
- socket_type() ->
- xml_stream.
-
- %% Return Username, Resource and presence information
- get_presence(FsmRef) ->
-- gen_fsm:sync_send_all_state_event(FsmRef, {get_presence}, 1000).
-+ ?GEN_FSM:sync_send_all_state_event(FsmRef, {get_presence}, 1000).
-
- stop(FsmRef) ->
-- gen_fsm:send_event(FsmRef, closed).
-+ ?GEN_FSM:send_event(FsmRef, closed).
-
- %%%----------------------------------------------------------------------
- %%% Callback functions from gen_fsm
-@@ -221,7 +224,7 @@
-
- %% Return list of all available resources of contacts,
- get_subscribed(FsmRef) ->
-- gen_fsm:sync_send_all_state_event(FsmRef, get_subscribed, 1000).
-+ ?GEN_FSM:sync_send_all_state_event(FsmRef, get_subscribed, 1000).
-
- %%----------------------------------------------------------------------
- %% Func: StateName/2
-@@ -903,7 +906,7 @@
- session_established(timeout, StateData) ->
- %% TODO: Options must be stored in state:
- Options = [],
-- proc_lib:hibernate(gen_fsm, enter_loop,
-+ proc_lib:hibernate(?GEN_FSM, enter_loop,
- [?MODULE, Options, session_established, StateData]),
- fsm_next_state(session_established, StateData);
-
-@@ -2153,6 +2156,14 @@
- end
- end.
-
-+fsm_limit_opts() ->
-+ case ejabberd_config:get_local_option(max_fsm_queue) of
-+ N when is_integer(N) ->
-+ [{max_queue, N}];
-+ _ ->
-+ []
-+ end.
-+
- %%%----------------------------------------------------------------------
- %%% JID Set memory footprint reduction code
- %%%----------------------------------------------------------------------
diff --git a/ejabberd-ejabberd_cfg_pam_name.diff b/ejabberd-ejabberd_cfg_pam_name.diff
deleted file mode 100644
index 3b9ed7d..0000000
--- a/ejabberd-ejabberd_cfg_pam_name.diff
+++ /dev/null
@@ -1,11 +0,0 @@
---- src/ejabberd.cfg.example 2009-11-20 15:07:57.317704955 +0300
-+++ src/ejabberd.cfg.example 2009-11-20 15:08:11.522704968 +0300
-@@ -235,7 +235,7 @@
- %% Authentication using PAM
- %%
- %%{auth_method, pam}.
--%%{pam_service, "pamservicename"}.
-+%%{pam_service, "ejabberd"}.
-
- %%
- %% Authentication using LDAP
diff --git a/ejabberd-ejabberdctl_fix.diff b/ejabberd-ejabberdctl_fix.diff
deleted file mode 100644
index 515f894..0000000
--- a/ejabberd-ejabberdctl_fix.diff
+++ /dev/null
@@ -1,42 +0,0 @@
---- src/ejabberdctl.template~ 2010-01-29 15:03:48.000000000 +0300
-+++ src/ejabberdctl.template 2010-01-29 15:07:58.867786945 +0300
-@@ -9,10 +9,10 @@
-
- # 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
-
- # Control number of connections identifiers
- # using flock if available. Expects a linux-style
-@@ -53,7 +53,7 @@
- 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@
-@@ -69,7 +69,7 @@
- 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
-@@ -77,7 +77,7 @@
- fi
- if [ "$EXEC_CMD" = "false" ] ; then
- echo "This command can only be run by root or the user $INSTALLUSER" >&2
-- exit 1
-+ exit 4
- fi
-
- NAME=-name
diff --git a/ejabberd-fixed_delays_in_s2s.patch b/ejabberd-fixed_delays_in_s2s.patch
deleted file mode 100644
index c520d6f..0000000
--- a/ejabberd-fixed_delays_in_s2s.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-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).
-
-Index: src/ejabberd_s2s_in.erl
-===================================================================
---- src/ejabberd_s2s_in.erl 2009-04-01 17:23:52.000000000 +0200
-+++ src/ejabberd_s2s_in.erl 2009-04-05 22:51:20.000000000 +0200
-@@ -352,7 +352,7 @@
- case {ejabberd_s2s:allow_host(To, From),
- lists:member(LTo, ejabberd_router:dirty_get_all_domains())} of
- {true, true} ->
-- ejabberd_s2s_out:terminate_if_waiting_delay(To, From),
-+ %%ejabberd_s2s_out:terminate_if_waiting_delay(To, From),
- ejabberd_s2s_out:start(To, From,
- {verify, self(),
- Key, StateData#state.streamid}),
-Index: src/ejabberd_s2s_out.erl
-===================================================================
---- src/ejabberd_s2s_out.erl 2009-11-06 22:53:19.000000000 +0300
-+++ src/ejabberd_s2s_out.erl 2009-11-20 15:59:53.445703654 +0300
-@@ -90,7 +90,7 @@
- [From, Host, Type])).
- -endif.
-
---define(FSMTIMEOUT, 30000).
-+-define(FSMTIMEOUT, 60000).
-
- %% We do not block on send anymore.
- -define(TCP_SEND_TIMEOUT, 15000).
-@@ -226,8 +226,8 @@
- {error, _Reason} ->
- ?INFO_MSG("s2s connection: ~s -> ~s (remote server not found)",
- [StateData#state.myname, StateData#state.server]),
-- wait_before_reconnect(StateData)
-- %%{stop, normal, StateData}
-+ %%wait_before_reconnect(StateData)
-+ {stop, normal, StateData}
- end;
- open_socket(stop, StateData) ->
- ?INFO_MSG("s2s connection: ~s -> ~s (stopped in open socket)",
diff --git a/ejabberd-mod_admin_extra.patch b/ejabberd-mod_admin_extra.patch
deleted file mode 100644
index 0e5ea0f..0000000
--- a/ejabberd-mod_admin_extra.patch
+++ /dev/null
@@ -1,1305 +0,0 @@
-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:
-https://forge.process-one.net/browse/ejabberd-modules/mod_admin_extra
-(trunk, revision 976).
-
-diff --git a/src/ejabberd.app b/src/ejabberd.app
-index e0e943e..f017015 100644
---- a/src/ejabberd.app
-+++ b/src/ejabberd.app
-@@ -104,6 +104,7 @@
- mod_vcard_ldap,
- mod_vcard_odbc,
- 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..9f3ca14
---- /dev/null
-+++ b/src/mod_admin_extra.erl
-@@ -0,0 +1,1279 @@
-+%%%-------------------------------------------------------------------
-+%%% File : mod_admin_extra.erl
-+%%% Author : Badlop <badlop@process-one.net>
-+%%% Purpose : Contributed administrative functions and commands
-+%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
-+%%%
-+%%%
-+%%% 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,
-+ %% Vcard
-+ set_nickname/3,
-+ get_vcard/3,
-+ get_vcard/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_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,
-+ %% 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",
-+ 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",
-+ 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 acount 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 = 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 = 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_vcard2,
-+ args = [{user, string}, {host, string}, {name, string}, {subname, string}, {content, string}],
-+ result = {res, rescode}},
-+
-+ #ejabberd_commands{name = add_rosteritem, tags = [roster],
-+ desc = "Add an item to a user's roster",
-+ 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",
-+ 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},
-+ {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 = 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 = 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.
-+
-+
-+-record(last_activity, {us, timestamp, status}).
-+
-+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 mnesia:dirty_read(last_activity, {LUser, LServer}) of
-+ %% If it is
-+ %% existent:
-+ [#last_activity{timestamp = TimeStamp}] ->
-+ %% 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:
-+ [] ->
-+ %% 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}.
-+
-+
-+%%
-+%% 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<Num) and (Num=<length(Resources)) of
-+ true ->
-+ 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])].
-+
-+%% Make string more print-friendly
-+stringize(String) ->
-+ %% Replace newline characters with other code
-+ element(2, regexp:gsub(String, "\n", "\\n")).
-+
-+
-+%%%
-+%%% 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) ->
-+ get_vcard_content(User, Host, [Name]).
-+
-+get_vcard(User, Host, Name, Subname) ->
-+ get_vcard_content(User, Host, [Name, Subname]).
-+
-+set_vcard(User, Host, Name, Content) ->
-+ set_vcard_content(User, Host, [Name], Content).
-+
-+set_vcard(User, Host, Name, Subname, Content) ->
-+ set_vcard_content(User, Host, [Name, Subname], Content).
-+
-+
-+%%
-+%% Internal vcard
-+
-+get_vcard_content(User, Server, Data) ->
-+ [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
-+ JID = jlib:make_jid(User, 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
-+ false -> "Error: no_value";
-+ Elem -> xml:get_tag_cdata(Elem)
-+ end;
-+ [] ->
-+ "Error: no_vcard"
-+ end.
-+
-+get_vcard([Data1, Data2], A1) ->
-+ case xml:get_subtag(A1, Data1) of
-+ false -> false;
-+ A2 -> get_vcard([Data2], A2)
-+ end;
-+
-+get_vcard([Data], A1) ->
-+ xml:get_subtag(A1, Data).
-+
-+set_vcard_content(User, Server, Data, Content) ->
-+ [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
-+ JID = jlib:make_jid(User, 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, Content, A2);
-+ [] ->
-+ update_vcard_els(Data, Content, [])
-+ 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, Content, Els1) ->
-+ Els2 = lists:keysort(2, Els1),
-+ [Data1 | Data2] = Data,
-+ NewEl = case Data2 of
-+ [] ->
-+ {xmlelement, Data1, [], [{xmlcdata,Content}]};
-+ [D2] ->
-+ OldEl = case lists:keysearch(Data1, 2, Els2) of
-+ {value, A} -> A;
-+ false -> {xmlelement, Data1, [], []}
-+ end,
-+ {xmlelement, _, _, ContentOld1} = OldEl,
-+ Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]}],
-+ ContentOld2 = lists:keysort(2, ContentOld1),
-+ ContentOld3 = lists:keydelete(D2, 2, ContentOld2),
-+ ContentNew = lists:keymerge(2, Content2, ContentOld3),
-+ {xmlelement, Data1, [], ContentNew}
-+ end,
-+ Els3 = lists:keydelete(Data1, 2, Els2),
-+ lists:keymerge(2, [NewEl], 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) ->
-+ mnesia:transaction(
-+ fun() ->
-+ mnesia:write({roster,
-+ {LU,LS,{User,Server,[]}}, % uj
-+ {LU,LS}, % user
-+ {User,Server,[]}, % jid
-+ Nick, % name: "Mom", []
-+ Subscription, % subscription: none, to=you see him, from=he sees you, both
-+ none, % ask: out=send request, in=somebody requests you, none
-+ [Group], % groups: ["Family"]
-+ Xattrs, % xattrs: [{"category","conference"}]
-+ [] % xs: []
-+ })
-+ end).
-+
-+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) ->
-+ mnesia:transaction(
-+ fun() ->
-+ mnesia:delete({roster, {LU, LS, {User, Server, []}}})
-+ end).
-+
-+
-+%% -----------------------------
-+%% Get Roster
-+%% -----------------------------
-+
-+get_roster(User, Server) ->
-+ {ok, Roster} = get_roster2(User, Server),
-+ make_roster_xmlrpc(Roster).
-+
-+get_roster2(User, Server) ->
-+ Modules = gen_mod:loaded_modules(Server),
-+ Roster = case lists:member(mod_roster, Modules) of
-+ true ->
-+ mod_roster:get_user_roster([], {User, Server});
-+ false ->
-+ case lists:member(mod_roster_odbc, Modules) of
-+ true ->
-+ mod_roster_odbc:get_user_roster([], {User, Server});
-+ false ->
-+ {error, "Neither mod_roster or mod_roster_odbc are enabled"}
-+ end
-+ end,
-+ {ok, Roster}.
-+
-+%% 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,
-+ Groups = case Item#roster.groups of
-+ [] -> [""];
-+ Gs -> Gs
-+ end,
-+ ItemsX = [{JIDS, Nick, 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) ->
-+ Item = build_roster_item(U, S, Action),
-+ ResIQ = build_iq_roster_push(Item),
-+ LJID = jlib:make_jid(LU, LS, R),
-+ 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]
-+ }
-+ ]
-+ }.
-+
-+
-+%%%
-+%%% Shared Roster Groups
-+%%%
-+
-+srg_create(Group, Host, Name, Description, Display) ->
-+ {ok, DisplayList} = regexp:split(Display, "\\\\n"),
-+ 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"}],
-+ [{xmlelement, "body", [], [{xmlcdata, Body}]}]
-+ };
-+build_packet(message_headline, [Subject, Body]) ->
-+ {xmlelement, "message",
-+ [{"type", "headline"}],
-+ [{xmlelement, "subject", [], [{xmlcdata, Subject}]},
-+ {xmlelement, "body", [], [{xmlcdata, Body}]}
-+ ]
-+ }.
-+
-+%%%
-+%%% 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 regexp:first_match(String, RegExp) of
-+ nomatch ->
-+ false;
-+ {match, _, _} ->
-+ true;
-+ {error, ErrDesc} ->
-+ io:format(
-+ "Wrong regexp ~p in ACL: ~p",
-+ [RegExp, lists:flatten(regexp:format_error(ErrDesc))]),
-+ false
-+ end.
-+is_glob_match(String, Glob) ->
-+ is_regexp_match(String, regexp:sh_to_awk(Glob)).
diff --git a/ejabberd-mod_ctlextra_mentioning_in_ejabberd_app.diff b/ejabberd-mod_ctlextra_mentioning_in_ejabberd_app.diff
deleted file mode 100644
index 1abb948..0000000
--- a/ejabberd-mod_ctlextra_mentioning_in_ejabberd_app.diff
+++ /dev/null
@@ -1,12 +0,0 @@
-Index: src/ejabberd.app
-===================================================================
---- src/ejabberd.app 2009-04-01 17:23:51.000000000 +0200
-+++ src/ejabberd.app 2009-04-05 22:51:20.000000000 +0200
-@@ -70,6 +70,7 @@
- mod_caps,
- mod_configure2,
- mod_configure,
-+ mod_ctlextra,
- mod_disco,
- mod_echo,
- mod_http_bind,
diff --git a/ejabberd.spec b/ejabberd.spec
index 3386704..3511f21 100644
--- a/ejabberd.spec
+++ b/ejabberd.spec
@@ -11,8 +11,8 @@
%endif
Name: ejabberd
-Version: 2.1.2
-Release: 2%{?dist}
+Version: 2.1.3
+Release: 1%{?dist}
Summary: A distributed, fault-tolerant Jabber/XMPP server
Group: Applications/Internet
@@ -39,21 +39,24 @@ Source9: ejabberdctl.pam
Source10: ejabberdctl.apps
Source11: ejabberd.pam
-# BZ# 439583, 452326, 451554, 465196, 502361
-Patch2: ejabberd-ejabberdctl_fix.diff
# Use ejabberd as an example for PAM service name
-Patch3: ejabberd-ejabberd_cfg_pam_name.diff
+Patch1: ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch
# Mention mod_ctlextra as an ejabberd module
-Patch5: ejabberd-mod_ctlextra_mentioning_in_ejabberd_app.diff
+Patch2: ejabberd-0002-Mention-mod_ctlextra-as-an-ejabberd-module.patch
# fixed delays in s2s connections
-Patch8: ejabberd-fixed_delays_in_s2s.patch
+Patch3: ejabberd-0003-Fixed-delays-in-s2s-connections.patch
# Introducing mod_admin_extra
-Patch10: ejabberd-mod_admin_extra.patch
-# see https://support.process-one.net/browse/EJAB-1173
-# https://forge.process-one.net/rdiff/ejabberd/branches/ejabberd-2.1.x/src/ejabberd_c2s.erl?r1=2911&r2=2936&u&N
-Patch11: ejabberd-c2s_dos_fix.diff
-# See this link: http://lists.jabber.ru/pipermail/ejabberd/2010-January/005819.html
-Patch12: ejabberdctl-support-concurrent-connections.patch
+Patch4: ejabberd-0004-Introducing-mod_admin_extra.patch
+# BZ# 439583, 452326, 451554, 465196, 502361
+Patch5: ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
+# http://www.ejabberd.im/cyrsasl_gssapi
+Patch6: ejabberd-0006-Support-to-authenticate-against-SASL-GSSAPI-http-www.patch
+# Typo in GSSAPI modules (sent upstream)
+Patch7: ejabberd-0007-Fixed-typo.patch
+# Quick fix for {error,nxdomain} issue
+Patch8: ejabberd-0008-Quick-fix-for-error-nxdomain.patch
+# Cleanup redundant defines
+Patch9: ejabberd-0009-Replace-MSG-with-DEBUG.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
@@ -79,9 +82,14 @@ Provides: user(%{name}) = %{uid}
Provides: group(%{name}) = %{uid}
Requires: erlang
+Requires: erlang-esasl
Requires: usermode
# for flock in ejabberdctl
+%if %{defined fedora}
Requires: util-linux-ng
+%else
+Requires: util-linux
+%endif
%description
@@ -99,13 +107,16 @@ Documentation for ejabberd.
%prep
%setup -q
-%patch3 -p0 -b .pam_name
-%patch5 -p0 -b .mod_ctlextra
-%patch8 -p0 -b .s2s
-%patch10 -p1 -b .mod_admin_extra
-%patch11 -p2 -b .EJAB_1173
-%patch12 -p1 -b .concurrent
-%patch2 -p0 -b .fix_ctl
+
+%patch1 -p1 -b .pam_name
+%patch2 -p1 -b .mod_ctlextra
+%patch3 -p1 -b .s2s_delays
+%patch4 -p1 -b .mod_admin_extra
+%patch5 -p1 -b .fedora_specific
+%patch6 -p1 -b .gssapi
+%patch7 -p1 -b .gssapi_typo
+%patch8 -p1 -b .nxdomain_fix
+%patch9 -p1 -b .gssapi_cleanup
dos2unix src/odbc/mssql2000.sql
@@ -117,7 +128,7 @@ cp %{S:7} src
%build
pushd src
-%configure --enable-odbc --enable-debug --enable-pam
+%configure --enable-odbc --enable-pam
# doesn't builds on SMP currently
make
popd
@@ -180,37 +191,42 @@ rm -rf %{buildroot}%{_docdir}/%{name}
%{__fe_groupadd} %{uid} -r %{name} &>/dev/null || :
%{__fe_useradd} %{uid} -r -s /sbin/nologin -d /var/lib/ejabberd -M \
-c 'ejabberd' -g %{name} %{name} &>/dev/null || :
-# we should backup DB in every upgrade
-if ejabberdctl status >/dev/null ; then
- # Use timestamp to make database restoring easier
- TIME=$(date +%Y-%m-%dT%H:%M:%S)
- BACKUPDIR=$(mktemp -d -p /var/tmp/ ejabberd-$TIME.XXXXXX)
- chown ejabberd:ejabberd $BACKUPDIR
- BACKUP=$BACKUPDIR/ejabberd-database
- ejabberdctl backup $BACKUP
- # Change ownership to root:root because ejabberd user might be
- # removed on package removal.
- chown -R root:root $BACKUPDIR
- chmod 700 $BACKUPDIR
- echo
- echo The ejabberd database has been backed up to $BACKUP.
- echo
-fi
-# 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 -pf /var/lib/ejabberd/{,spool/}.erlang.cookie
- echo
- echo The ejabberd cookie file was moved.
- echo Please remove old one from /var/lib/ejabberd/.erlang.cookie
- echo
+
+if [ $1 -gt 1 ]; then
+ # we should backup DB in every upgrade
+ if ejabberdctl status >/dev/null ; then
+ # Use timestamp to make database restoring easier
+ TIME=$(date +%Y-%m-%dT%H:%M:%S)
+ BACKUPDIR=$(mktemp -d -p /var/tmp/ ejabberd-$TIME.XXXXXX)
+ chown ejabberd:ejabberd $BACKUPDIR
+ BACKUP=$BACKUPDIR/ejabberd-database
+ ejabberdctl backup $BACKUP
+ # Change ownership to root:root because ejabberd user might be
+ # removed on package removal.
+ chown -R root:root $BACKUPDIR
+ chmod 700 $BACKUPDIR
+ echo
+ echo The ejabberd database has been backed up to $BACKUP.
+ echo
+ fi
+
+ # 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
+ echo
+ echo The ejabberd cookie file was moved.
+ echo Please delete old one from /var/lib/ejabberd/.erlang.cookie
+ echo
+ fi
fi
%post
/sbin/chkconfig --add %{name}
+# Create SSL certificate with default values if it doesn't exist
(cd /etc/ejabberd
if [ ! -f ejabberd.pem ]
then
@@ -298,6 +314,7 @@ rm -rf %{buildroot}
%{_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}/priv/bin/captcha.sh
%attr(4750,root,ejabberd) %{_libdir}/%{name}/priv/bin/epam
%{_libdir}/%{name}/priv/lib/ejabberd_zlib_drv.so
@@ -345,14 +362,32 @@ rm -rf %{buildroot}
%doc doc/release_notes_2.0.5.txt
%doc doc/release_notes_2.1.0.txt
%doc doc/release_notes_2.1.1.txt
+%doc doc/release_notes_2.1.2.txt
+%doc doc/release_notes_2.1.3.txt
%doc doc/webadmmain.png
%doc doc/webadmmainru.png
%doc doc/yozhikheader.png
%changelog
+* Fri Mar 12 2010 Peter Lemenkov <lemenkov@gmail.com> 2.1.3-1
+- Ver. 2.1.3
+- Patches rebased
+
+* Fri Mar 5 2010 Peter Lemenkov <lemenkov@gmail.com> 2.1.2-4
+- Fixed issue with {erorr,nxdomain}
+
+* Tue Feb 16 2010 Peter Lemenkov <lemenkov@gmail.com> 2.1.2-3
+- Do not try to backup DB on every fresh install
+- Do not force copying old erlang cookie file
+- Add missing release notes for ver. 2.1.2
+- Require erlang-esasl for krb5 support
+- No such %%configure option - --enable-debug
+- Patches were rebased and renumbered
+- Add new BR util-linux(-ng)
+
* Fri Jan 29 2010 Peter Lemenkov <lemenkov@gmail.com> 2.1.2-2
- Fixed BZ #559925 (EJAB-1173)
-- Changed order of rpmbuild targets in sthis spec to more natural one.
+- Changed order of rpmbuild targets in this spec to more natural one.
* Mon Jan 18 2010 Peter Lemenkov <lemenkov@gmail.com> 2.1.2-1
- Ver. 2.1.2
diff --git a/ejabberdctl-support-concurrent-connections.patch b/ejabberdctl-support-concurrent-connections.patch
deleted file mode 100644
index 313e844..0000000
--- a/ejabberdctl-support-concurrent-connections.patch
+++ /dev/null
@@ -1,156 +0,0 @@
-From 5990c7f1e5b2fa106806d9ed03a4a59cfa75959a Mon Sep 17 00:00:00 2001
-From: Martin Langhoff <martin@laptop.org>
-Date: Sun, 24 Jan 2010 14:39:15 +0100
-Subject: [PATCH] ejabberdctl: support concurrent connections with bound conn names
-
-If flock is available, ejabberdctl will use it to grab one
-of a bound number of connection names. This allows concurrent
-connections while using a bound number of atoms.
-
-Using PID, timestamps or random strings for transient connection IDs
-(which would avoid the need for flock) uses an unbound number of atoms.
-This can effectively DoS servers, as these connection names are
-not garbage collected.
----
- src/Makefile.in | 10 ++++++
- src/ejabberdctl.template | 75 +++++++++++++++++++++++++++++++++++++++++----
- 2 files changed, 78 insertions(+), 7 deletions(-)
-
-diff --git a/src/Makefile.in b/src/Makefile.in
-index 8485caa..02f05e8 100644
---- a/src/Makefile.in
-+++ b/src/Makefile.in
-@@ -110,6 +110,9 @@ MSGSDIR = $(PRIVDIR)/msgs
- # /var/lib/ejabberd/
- SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd
-
-+# /var/lock/ejabberdctl
-+CTLLOCKDIR = $(DESTDIR)@localstatedir@/lock/ejabberdctl
-+
- # /var/lib/ejabberd/.erlang.cookie
- COOKIEFILE = $(SPOOLDIR)/.erlang.cookie
-
-@@ -230,6 +233,12 @@ install: all
- install -d -m 750 $(O_USER) $(SPOOLDIR)
- $(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT)
- chmod -R 750 $(SPOOLDIR)
-+
-+ # ejabberdctl lock directory
-+ install -d -m 750 $(O_USER) $(CTLLOCKDIR)
-+ $(CHOWN_COMMAND) -R @INSTALLUSER@ $(CTLLOCKDIR) >$(CHOWN_OUTPUT)
-+ chmod -R 750 $(CTLLOCKDIR)
-+
- [ ! -f $(COOKIEFILE) ] || { $(CHOWN_COMMAND) @INSTALLUSER@ $(COOKIEFILE) >$(CHOWN_OUTPUT) ; chmod 400 $(COOKIEFILE) ; }
- #
- # Log directory
-@@ -265,6 +274,7 @@ uninstall-all: uninstall-binary
- rm -rf $(ETCDIR)
- rm -rf $(EJABBERDDIR)
- rm -rf $(SPOOLDIR)
-+ rm -rf $(CTLLOCKDIR)
- rm -rf $(LOGDIR)
-
- clean: clean-recursive clean-local
-diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template
-index 438481b..d2aa5fe 100644
---- a/src/ejabberdctl.template
-+++ b/src/ejabberdctl.template
-@@ -14,6 +14,13 @@ ERLANG_NODE=$NODE@$HOST
- ERL=@erl@
- INSTALLUSER=@installuser@
-
-+# Control number of connections identifiers
-+# using flock if available. Expects a linux-style
-+# flock that can lock a file descriptor.
-+MAXCONNID=100
-+CONNLOCKDIR=@LOCALSTATEDIR@/lock/ejabberdctl
-+FLOCK=`type -p flock`
-+
- # parse command line parameters
- ARGS=
- while [ $# -ne 0 ] ; do
-@@ -228,13 +235,55 @@ help ()
- ctl ()
- {
- COMMAND=$@
-- $EXEC_CMD "$ERL \
-- $NAME ctl-${ERLANG_NODE} \
-- -noinput \
-- -hidden \
-- -pa $EJABBERD_EBIN_PATH \
-- -s ejabberd_ctl -extra $ERLANG_NODE $COMMAND"
-- result=$?
-+
-+ if [ ! "$FLOCK" ];then
-+ # no flock, simply invoke ctlexec()
-+ CTL_CONN="ctl-${EJABBERD_NODE}"
-+ ctlexec $CTL_CONN $COMMAND
-+ result=$?
-+ else
-+ # we have flock so we get a lock
-+ # on one of a limited number of
-+ # conn names -- this allows
-+ # concurrent invokations using a bound
-+ # number of atoms
-+ for N in $(seq 1 $MAXCONNID); do
-+ CTL_CONN="ejabberdctl-$N"
-+ CTL_LOCKFILE="$CONNLOCKDIR/$CTL_CONN"
-+ (
-+ if flock --nb 200; then
-+ # see the '200' file descriptor
-+ # at the bottom of the block
-+ ctlexec $CTL_CONN $COMMAND
-+ ssresult=$?
-+ # segregate from possible flock exit(1)
-+ ssresult=$(expr $ssresult \* 10)
-+ exit $ssresult
-+ else
-+ exit 1
-+ fi
-+ ) 200>"$CTL_LOCKFILE"
-+ result=$?
-+ if [ $result -eq 1 ]; then
-+ # means we errored out in flock
-+ # rather than in the exec - stay in the loop
-+ # trying other conn names...
-+ badlock=1
-+ else
-+ badlock=""
-+ break;
-+ fi
-+ done
-+ result=$(expr $result / 10)
-+ fi
-+
-+ if [ "$badlock" ];then
-+ echo "Ran out of connections to try. Your ejabberd processes" >&2
-+ echo "may be stuck or this is a very busy server. For very" >&2
-+ echo "busy servers, consider raising MAXCONNIDS" >&2
-+ exit 1;
-+ fi
-+
- case $result in
- 0) :;;
- 1) :;;
-@@ -244,6 +293,18 @@ ctl ()
- return $result
- }
-
-+ctlexec ()
-+{
-+ CONN_NAME=$1; shift
-+ COMMAND=$@
-+ $EXEC_CMD "$ERL \
-+ $NAME ${CONN_NAME} \
-+ -noinput \
-+ -hidden \
-+ -pa $EJABBERD_EBIN_PATH \
-+ -s ejabberd_ctl -extra $ERLANG_NODE $COMMAND"
-+}
-+
- # display ctl usage
- usage ()
- {
---
-1.6.2.5
-
diff --git a/import.log b/import.log
index 7248f46..1d51932 100644
--- a/import.log
+++ b/import.log
@@ -17,3 +17,4 @@ ejabberd-2_1_0-2_fc12:HEAD:ejabberd-2.1.0-2.fc12.src.rpm:1260471420
ejabberd-2_1_1-1_fc12:HEAD:ejabberd-2.1.1-1.fc12.src.rpm:1261656263
ejabberd-2_1_2-1_fc12:HEAD:ejabberd-2.1.2-1.fc12.src.rpm:1264423410
ejabberd-2_1_2-2_fc12:HEAD:ejabberd-2.1.2-2.fc12.src.rpm:1264770769
+ejabberd-2_1_3-1_fc12:HEAD:ejabberd-2.1.3-1.fc12.src.rpm:1268409107
diff --git a/sources b/sources
index d2fe465..fa758f8 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-9102802ae19312c26f85ceb977b519aa ejabberd-2.1.2.tar.gz
+e5c87eda5312a6e8a53df0f9b4844b69 ejabberd-2.1.3.tar.gz
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-23 19:06 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 19:06 [rpms/ejabberd] f45: Ver. 2.1.3 Peter Lemenkov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox