From 28265d71c3ad0d98f463c6c979eb814b64fde3dd Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mar 12 2010 16:04:15 +0000 Subject: 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 +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 +--- + 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 +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 +--- + 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 +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 +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 ++%%% Purpose : Contributed administrative functions and commands ++%%% Created : 10 Aug 2008 by Badlop ++%%% ++%%% ++%%% ejabberd, Copyright (C) 2002-2008 ProcessOne ++%%% ++%%% This program is free software; you can redistribute it and/or ++%%% modify it under the terms of the GNU General Public License as ++%%% published by the Free Software Foundation; either version 2 of the ++%%% License, or (at your option) any later version. ++%%% ++%%% This program is distributed in the hope that it will be useful, ++%%% but WITHOUT ANY WARRANTY; without even the implied warranty of ++%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++%%% General Public License for more details. ++%%% ++%%% You should have received a copy of the GNU General Public License ++%%% along with this program; if not, write to the Free Software ++%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++%%% 02111-1307 USA ++%%% ++%%%------------------------------------------------------------------- ++ ++-module(mod_admin_extra). ++-author('badlop@process-one.net'). ++ ++-behaviour(gen_mod). ++ ++-export([start/2, stop/1, ++ %% Node ++ compile/1, ++ load_config/1, ++ get_cookie/0, ++ remove_node/1, ++ export2odbc/2, ++ %% Accounts ++ set_password/3, ++ check_password_hash/4, ++ delete_old_users/1, ++ delete_old_users_vhost/2, ++ ban_account/3, ++ num_active_users/2, ++ %% Sessions ++ num_resources/2, ++ resource_num/3, ++ kick_session/4, ++ status_num/2, status_num/1, ++ status_list/2, status_list/1, ++ %% 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 ++ 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 +Date: Tue, 16 Feb 2010 16:30:05 +0300 +Subject: [PATCH 5/9] Fedora-specific changes to ejabberdctl + +Signed-off-by: Peter Lemenkov +--- + 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 +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 ++%%% Purpose : GSSAPI SASL mechanism ++%%% Created : 1 June 2007 by Mikael Magnusson ++%%% Id : $Id: $ ++%%%---------------------------------------------------------------------- ++%%% ++%%% Copyright (C) 2007 Mikael Magnusson ++%%% ++%%% Permission is hereby granted, free of charge, to any person ++%%% obtaining a copy of this software and associated documentation ++%%% files (the "Software"), to deal in the Software without ++%%% restriction, including without limitation the rights to use, copy, ++%%% modify, merge, publish, distribute, sublicense, and/or sell copies ++%%% of the Software, and to permit persons to whom the Software is ++%%% furnished to do so, subject to the following conditions: ++%%% ++%%% The above copyright notice and this permission notice shall be ++%%% included in all copies or substantial portions of the Software. ++%%% ++%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++%%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++%%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++%%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++%%% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++%%% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++%%% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++%%% SOFTWARE. ++%%% ++ ++%%% ++%%% configuration options: ++%%% {sasl_realm, ""}. ++%%% ++%%% environment variables: ++%%% KRB5_KTNAME ++%%% ++ ++-module(cyrsasl_gssapi). ++-author('mikma@users.sourceforge.net'). ++-vsn('$Revision: $ '). ++ ++-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 ++%%% Purpose : Serve C2S connection ++%%% Created : 6 June 2007 by Mikael Magnusson ++%%% 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 +Date: Fri, 5 Mar 2010 17:48:51 +0300 +Subject: [PATCH 7/9] Fixed typo + +Signed-off-by: Peter Lemenkov +--- + 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 +Date: Fri, 5 Mar 2010 21:13:35 +0300 +Subject: [PATCH 8/9] Quick fix for {error,nxdomain} + +Signed-off-by: Peter Lemenkov +--- + 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 +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 -+%%% Purpose : Contributed administrative functions and commands -+%%% Created : 10 Aug 2008 by Badlop -+%%% -+%%% -+%%% ejabberd, Copyright (C) 2002-2008 ProcessOne -+%%% -+%%% This program is free software; you can redistribute it and/or -+%%% modify it under the terms of the GNU General Public License as -+%%% published by the Free Software Foundation; either version 2 of the -+%%% License, or (at your option) any later version. -+%%% -+%%% This program is distributed in the hope that it will be useful, -+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+%%% General Public License for more details. -+%%% -+%%% You should have received a copy of the GNU General Public License -+%%% along with this program; if not, write to the Free Software -+%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+%%% 02111-1307 USA -+%%% -+%%%------------------------------------------------------------------- -+ -+-module(mod_admin_extra). -+-author('badlop@process-one.net'). -+ -+-behaviour(gen_mod). -+ -+-export([start/2, stop/1, -+ %% Node -+ compile/1, -+ load_config/1, -+ get_cookie/0, -+ remove_node/1, -+ export2odbc/2, -+ %% Accounts -+ set_password/3, -+ check_password_hash/4, -+ delete_old_users/1, -+ delete_old_users_vhost/2, -+ ban_account/3, -+ num_active_users/2, -+ %% Sessions -+ num_resources/2, -+ resource_num/3, -+ kick_session/4, -+ status_num/2, status_num/1, -+ status_list/2, status_list/1, -+ %% 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 -+ 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 2.1.3-1 +- Ver. 2.1.3 +- Patches rebased + +* Fri Mar 5 2010 Peter Lemenkov 2.1.2-4 +- Fixed issue with {erorr,nxdomain} + +* Tue Feb 16 2010 Peter Lemenkov 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 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 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 -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 17c7a5d..5c635b5 100644 --- a/import.log +++ b/import.log @@ -16,3 +16,4 @@ ejabberd-2_1_0-2_fc12:F-11:ejabberd-2.1.0-2.fc12.src.rpm:1260472235 ejabberd-2_1_1-1_fc12:F-11:ejabberd-2.1.1-1.fc12.src.rpm:1261656564 ejabberd-2_1_2-1_fc12:F-11:ejabberd-2.1.2-1.fc12.src.rpm:1264423698 ejabberd-2_1_2-2_fc12:F-11:ejabberd-2.1.2-2.fc12.src.rpm:1264770903 +ejabberd-2_1_3-1_fc12:F-11:ejabberd-2.1.3-1.fc12.src.rpm:1268409828 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