social_stream 0.11.2 → 0.11.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +0 -9
- data/base/app/assets/stylesheets/contacts.css.scss +8 -3
- data/base/app/assets/stylesheets/frontpage.css.scss +1 -1
- data/base/app/assets/stylesheets/search.css.scss +17 -1
- data/base/app/controllers/contacts_controller.rb +2 -5
- data/base/app/models/contact.rb +3 -2
- data/base/app/views/activities/_options.html.erb +1 -1
- data/base/app/views/contacts/_form.html.erb +14 -7
- data/base/app/views/frontpage/_social_networks.html.erb +3 -0
- data/base/app/views/groups/_new.html.erb +2 -2
- data/base/app/views/groups/_show.html.erb +1 -2
- data/base/app/views/groups/index.html.erb +1 -1
- data/base/app/views/layouts/_header_signed_out.erb +1 -3
- data/base/app/views/notifications/activities/_follow_subject.html.erb +5 -2
- data/base/app/views/notifications/activities/_like_subject.html.erb +6 -2
- data/base/app/views/notifications/activities/_make-friend_subject.html.erb +5 -2
- data/base/app/views/notifications/activities/_post_subject.html.erb +6 -1
- data/base/app/views/notifications/activities/_update_body.html.erb +5 -1
- data/base/app/views/notifications/activities/_update_subject.html.erb +5 -1
- data/base/app/views/posts/_post_focus_search.html.erb +37 -3
- data/base/app/views/posts/_post_global_search.html.erb +2 -0
- data/base/app/views/profiles/_contact.html.erb +1 -1
- data/base/app/views/search/index.js.erb +2 -0
- data/base/app/views/toolbar/_home.html.erb +4 -0
- data/base/config/locales/en.yml +26 -0
- data/base/config/locales/es.yml +26 -0
- data/base/lib/mailboxer/notification_decoder.rb +8 -6
- data/base/lib/social_stream-base.rb +1 -1
- data/base/lib/social_stream/base/version.rb +1 -1
- data/base/lib/social_stream/migrations/components.rb +1 -1
- data/base/lib/social_stream/toolbar_config/base.rb +12 -1
- data/base/social_stream-base.gemspec +1 -1
- data/base/spec/controllers/contacts_controller_spec.rb +4 -4
- data/events/app/assets/javascripts/social_stream-events.js +2 -2
- data/events/app/assets/stylesheets/social_stream-events.css +3 -0
- data/events/{app → vendor}/assets/javascripts/jquery.ad-gallery.js +0 -0
- data/events/{app → vendor}/assets/javascripts/jquery.ad-gallery.pack.js +0 -0
- data/events/{app → vendor}/assets/stylesheets/ad_next.png +0 -0
- data/events/{app → vendor}/assets/stylesheets/ad_prev.png +0 -0
- data/events/{app → vendor}/assets/stylesheets/ad_scroll_back.png +0 -0
- data/events/{app → vendor}/assets/stylesheets/ad_scroll_forward.png +0 -0
- data/events/{app → vendor}/assets/stylesheets/fullcalendar.css +0 -0
- data/events/{app → vendor}/assets/stylesheets/fullcalendar.print.css +0 -0
- data/events/{app → vendor}/assets/stylesheets/jquery.ad-gallery.css +0 -0
- data/events/{app → vendor}/assets/stylesheets/loader.gif +0 -0
- data/events/vendor/assets/stylesheets/social_stream-events.css +273 -0
- data/lib/social_stream.rb +0 -4
- data/lib/social_stream/version.rb +1 -1
- data/presence/.gitignore +4 -0
- data/presence/Gemfile +4 -0
- data/presence/Rakefile +1 -0
- data/presence/app/assets/audio/chat/onMessageAudio.mp3 +0 -0
- data/presence/app/assets/audio/chat/onMessageAudio.wav +0 -0
- data/presence/app/assets/images/arrow.png +0 -0
- data/presence/app/assets/images/black_arrow3.png +0 -0
- data/presence/app/assets/images/status/available.png +0 -0
- data/presence/app/assets/images/status/away.png +0 -0
- data/presence/app/assets/images/status/dnd.png +0 -0
- data/presence/app/assets/javascripts/jquery-ui-1.8.14.custom.min.js +789 -0
- data/presence/app/assets/javascripts/jquery.tools.min.js +17 -0
- data/presence/app/assets/javascripts/jquery.tools.tooltip.js +11 -0
- data/presence/app/assets/javascripts/jquery.ui.chatbox.js +260 -0
- data/presence/app/assets/javascripts/social_stream-presence.js +2 -0
- data/presence/app/assets/javascripts/store.js +27 -0
- data/presence/app/assets/javascripts/strophe.js +3612 -0
- data/presence/app/assets/javascripts/xmpp_client.js +498 -0
- data/presence/app/assets/stylesheets/chat.css +231 -0
- data/presence/app/assets/stylesheets/jquery.ui.chatbox.css +54 -0
- data/presence/app/assets/stylesheets/social_stream-presence.css +3 -0
- data/presence/app/controllers/xmpp_controller.rb +190 -0
- data/presence/app/helpers/xmpp_helper.rb +33 -0
- data/presence/app/views/xmpp/_chat.html.erb +59 -0
- data/presence/app/views/xmpp/_chat_connecting.html.erb +8 -0
- data/presence/app/views/xmpp/_chat_contacts.html.erb +46 -0
- data/presence/app/views/xmpp/_chat_off.html.erb +35 -0
- data/presence/app/views/xmpp/active_users.html.erb +33 -0
- data/presence/app/views/xmpp/chat.html.erb +13 -0
- data/presence/app/views/xmpp/index.html +19 -0
- data/presence/app/views/xmpp/test.html.erb +11 -0
- data/presence/config/routes.rb +15 -0
- data/presence/db/migrate/20110711111408_add_connected_column_to_user.rb +9 -0
- data/presence/db/migrate/20110928135031_add_status_column_to_user.rb +9 -0
- data/presence/ejabberd/conf/ejabberd.cfg +625 -0
- data/presence/ejabberd/conf/ejabberdctl.cfg +154 -0
- data/presence/ejabberd/conf/inetrc +3 -0
- data/presence/ejabberd/conf/server.pem +37 -0
- data/presence/ejabberd/conf/ssconfig.cfg +29 -0
- data/presence/ejabberd/ejabberd_scripts/authentication_script +117 -0
- data/presence/ejabberd/ejabberd_scripts/authentication_script_org +114 -0
- data/presence/ejabberd/ejabberd_scripts/compile_module +34 -0
- data/presence/ejabberd/ejabberd_scripts/generate_random_password +18 -0
- data/presence/ejabberd/ejabberd_scripts/kill_authentication_script.sh +13 -0
- data/presence/ejabberd/ejabberd_scripts/reset_connection_script +55 -0
- data/presence/ejabberd/ejabberd_scripts/reset_logs.sh +23 -0
- data/presence/ejabberd/ejabberd_scripts/set_connection_script +48 -0
- data/presence/ejabberd/ejabberd_scripts/set_presence_script +48 -0
- data/presence/ejabberd/ejabberd_scripts/show_config.sh +30 -0
- data/presence/ejabberd/ejabberd_scripts/start_ejabberd.sh +68 -0
- data/presence/ejabberd/ejabberd_scripts/stop_ejabberd.sh +12 -0
- data/presence/ejabberd/ejabberd_scripts/synchronize_presence_script +57 -0
- data/presence/ejabberd/ejabberd_scripts/unset_connection_script +48 -0
- data/presence/ejabberd/mod_admin_extra/mod_admin_extra.beam +0 -0
- data/presence/ejabberd/mod_admin_extra/mod_admin_extra.erl +1560 -0
- data/presence/ejabberd/mod_sspresence/mod_sspresence.erl +230 -0
- data/presence/lib/social_stream-presence.rb +13 -0
- data/presence/lib/social_stream/migrations/presence.rb +9 -0
- data/presence/lib/social_stream/presence/config.rb +9 -0
- data/presence/lib/social_stream/presence/engine.rb +49 -0
- data/presence/lib/social_stream/presence/models/buddy_manager.rb +67 -0
- data/presence/social_stream-presence.gemspec +28 -0
- data/presence/spec/demo/.gitignore +5 -0
- data/presence/spec/demo/.rspec +1 -0
- data/presence/spec/demo/README +261 -0
- data/presence/spec/demo/Rakefile +7 -0
- data/presence/spec/demo/app/assets/images/rails.png +0 -0
- data/presence/spec/demo/app/assets/javascripts/application.js +9 -0
- data/presence/spec/demo/app/assets/stylesheets/application.css +7 -0
- data/presence/spec/demo/app/controllers/application_controller.rb +3 -0
- data/presence/spec/demo/app/helpers/application_helper.rb +2 -0
- data/presence/spec/demo/app/mailers/.gitkeep +0 -0
- data/presence/spec/demo/app/models/.gitkeep +0 -0
- data/presence/spec/demo/app/views/layouts/application.html.erb +14 -0
- data/presence/spec/demo/autotest/discover.rb +2 -0
- data/presence/spec/demo/config.ru +4 -0
- data/presence/spec/demo/config/application.rb +41 -0
- data/presence/spec/demo/config/boot.rb +6 -0
- data/presence/spec/demo/config/database.yml +25 -0
- data/presence/spec/demo/config/environment.rb +5 -0
- data/presence/spec/demo/config/environments/development.rb +27 -0
- data/presence/spec/demo/config/environments/production.rb +54 -0
- data/presence/spec/demo/config/environments/test.rb +39 -0
- data/presence/spec/demo/config/initializers/backtrace_silencers.rb +7 -0
- data/presence/spec/demo/config/initializers/inflections.rb +10 -0
- data/presence/spec/demo/config/initializers/mime_types.rb +5 -0
- data/presence/spec/demo/config/initializers/secret_token.rb +7 -0
- data/presence/spec/demo/config/initializers/session_store.rb +8 -0
- data/presence/spec/demo/config/initializers/wrap_parameters.rb +12 -0
- data/presence/spec/demo/config/locales/en.yml +5 -0
- data/presence/spec/demo/config/routes.rb +58 -0
- data/presence/spec/demo/db/seeds.rb +7 -0
- data/presence/spec/demo/doc/README_FOR_APP +2 -0
- data/presence/spec/demo/lib/tasks/.gitkeep +0 -0
- data/presence/spec/demo/log/.gitkeep +0 -0
- data/presence/spec/demo/public/404.html +26 -0
- data/presence/spec/demo/public/422.html +26 -0
- data/presence/spec/demo/public/500.html +26 -0
- data/presence/spec/demo/public/favicon.ico +0 -0
- data/presence/spec/demo/public/index.html +241 -0
- data/presence/spec/demo/public/robots.txt +5 -0
- data/presence/spec/demo/script/rails +6 -0
- data/presence/spec/demo/spec/spec_helper.rb +27 -0
- data/presence/spec/demo/test/fixtures/.gitkeep +0 -0
- data/presence/spec/demo/test/functional/.gitkeep +0 -0
- data/presence/spec/demo/test/integration/.gitkeep +0 -0
- data/presence/spec/demo/test/performance/browsing_test.rb +12 -0
- data/presence/spec/demo/test/test_helper.rb +13 -0
- data/presence/spec/demo/test/unit/.gitkeep +0 -0
- data/presence/spec/demo/vendor/assets/stylesheets/.gitkeep +0 -0
- data/presence/spec/demo/vendor/plugins/.gitkeep +0 -0
- data/presence/spec/discover.rb +2 -0
- data/presence/spec/spec_helper.rb +27 -0
- data/social_stream.gemspec +9 -2
- data/spec/dummy/config/environments/{test_pg.rb → test_mysql.rb} +6 -2
- data/spec/dummy/config/environments/test_postgres.rb +39 -0
- data/spec/spec_helper.rb +1 -4
- metadata +135 -21
- data/base/app/views/groups/_new_activity.html.erb +0 -1
- data/base/app/views/groups/_new_activity_fields.html.erb +0 -7
Binary file
|
@@ -0,0 +1,1560 @@
|
|
1
|
+
%%%-------------------------------------------------------------------
|
2
|
+
%%% File : mod_admin_extra.erl
|
3
|
+
%%% Author : Badlop <badlop@process-one.net>
|
4
|
+
%%% Purpose : Contributed administrative functions and commands
|
5
|
+
%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
|
6
|
+
%%%
|
7
|
+
%%%
|
8
|
+
%%% ejabberd, Copyright (C) 2002-2008 ProcessOne
|
9
|
+
%%%
|
10
|
+
%%% This program is free software; you can redistribute it and/or
|
11
|
+
%%% modify it under the terms of the GNU General Public License as
|
12
|
+
%%% published by the Free Software Foundation; either version 2 of the
|
13
|
+
%%% License, or (at your option) any later version.
|
14
|
+
%%%
|
15
|
+
%%% This program is distributed in the hope that it will be useful,
|
16
|
+
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
+
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
18
|
+
%%% General Public License for more details.
|
19
|
+
%%%
|
20
|
+
%%% You should have received a copy of the GNU General Public License
|
21
|
+
%%% along with this program; if not, write to the Free Software
|
22
|
+
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
23
|
+
%%% 02111-1307 USA
|
24
|
+
%%%
|
25
|
+
%%%-------------------------------------------------------------------
|
26
|
+
|
27
|
+
-module(mod_admin_extra).
|
28
|
+
-author('badlop@process-one.net').
|
29
|
+
|
30
|
+
-behaviour(gen_mod).
|
31
|
+
|
32
|
+
-export([start/2, stop/1,
|
33
|
+
%% Node
|
34
|
+
compile/1,
|
35
|
+
load_config/1,
|
36
|
+
get_cookie/0,
|
37
|
+
remove_node/1,
|
38
|
+
export2odbc/2,
|
39
|
+
%% Accounts
|
40
|
+
set_password/3,
|
41
|
+
check_password_hash/4,
|
42
|
+
delete_old_users/1,
|
43
|
+
delete_old_users_vhost/2,
|
44
|
+
ban_account/3,
|
45
|
+
num_active_users/2,
|
46
|
+
%% Sessions
|
47
|
+
num_resources/2,
|
48
|
+
resource_num/3,
|
49
|
+
kick_session/4,
|
50
|
+
status_num/2, status_num/1,
|
51
|
+
status_list/2, status_list/1,
|
52
|
+
connected_users_info/0,
|
53
|
+
connected_users_vhost/1,
|
54
|
+
set_presence/7,
|
55
|
+
user_sessions_info/2,
|
56
|
+
%% Vcard
|
57
|
+
set_nickname/3,
|
58
|
+
get_vcard/3,
|
59
|
+
get_vcard/4,
|
60
|
+
get_vcard_multi/4,
|
61
|
+
set_vcard/4,
|
62
|
+
set_vcard/5,
|
63
|
+
%% Roster
|
64
|
+
add_rosteritem/7,
|
65
|
+
delete_rosteritem/4,
|
66
|
+
process_rosteritems/5,
|
67
|
+
get_roster/2,
|
68
|
+
push_roster/3,
|
69
|
+
push_roster_all/1,
|
70
|
+
push_alltoall/2,
|
71
|
+
%% mod_last
|
72
|
+
set_last/4,
|
73
|
+
%% mod_private
|
74
|
+
private_get/4,
|
75
|
+
private_set/3,
|
76
|
+
%% mod_shared_roster
|
77
|
+
srg_create/5,
|
78
|
+
srg_delete/2,
|
79
|
+
srg_list/1,
|
80
|
+
srg_get_info/2,
|
81
|
+
srg_get_members/2,
|
82
|
+
srg_user_add/4,
|
83
|
+
srg_user_del/4,
|
84
|
+
%% Stanza
|
85
|
+
send_message_headline/4,
|
86
|
+
send_message_chat/3,
|
87
|
+
send_stanza_c2s/4,
|
88
|
+
privacy_set/3,
|
89
|
+
%% Stats
|
90
|
+
stats/1, stats/2
|
91
|
+
]).
|
92
|
+
|
93
|
+
-include("ejabberd.hrl").
|
94
|
+
-include("ejabberd_commands.hrl").
|
95
|
+
-include("mod_roster.hrl").
|
96
|
+
-include("jlib.hrl").
|
97
|
+
|
98
|
+
%% Copied from ejabberd_sm.erl
|
99
|
+
-record(session, {sid, usr, us, priority, info}).
|
100
|
+
|
101
|
+
|
102
|
+
%%%
|
103
|
+
%%% gen_mod
|
104
|
+
%%%
|
105
|
+
|
106
|
+
start(_Host, _Opts) ->
|
107
|
+
ejabberd_commands:register_commands(commands()).
|
108
|
+
|
109
|
+
stop(_Host) ->
|
110
|
+
ejabberd_commands:unregister_commands(commands()).
|
111
|
+
|
112
|
+
|
113
|
+
%%%
|
114
|
+
%%% Register commands
|
115
|
+
%%%
|
116
|
+
|
117
|
+
commands() ->
|
118
|
+
Vcard1FieldsString = "Some vcard field names in get/set_vcard are:\n"
|
119
|
+
" FN - Full Name\n"
|
120
|
+
" NICKNAME - Nickname\n"
|
121
|
+
" BDAY - Birthday\n"
|
122
|
+
" TITLE - Work: Position\n",
|
123
|
+
" ROLE - Work: Role",
|
124
|
+
|
125
|
+
Vcard2FieldsString = "Some vcard field names and subnames in get/set_vcard2 are:\n"
|
126
|
+
" N FAMILY - Family name\n"
|
127
|
+
" N GIVEN - Given name\n"
|
128
|
+
" N MIDDLE - Middle name\n"
|
129
|
+
" ADR CTRY - Address: Country\n"
|
130
|
+
" ADR LOCALITY - Address: City\n"
|
131
|
+
" EMAIL USERID - E-Mail Address\n"
|
132
|
+
" ORG ORGNAME - Work: Company\n"
|
133
|
+
" ORG ORGUNIT - Work: Department",
|
134
|
+
|
135
|
+
VcardXEP = "For a full list of vCard fields check XEP-0054: vcard-temp at "
|
136
|
+
"http://www.xmpp.org/extensions/xep-0054.html",
|
137
|
+
|
138
|
+
[
|
139
|
+
#ejabberd_commands{name = compile, tags = [erlang],
|
140
|
+
desc = "Recompile and reload Erlang source code file",
|
141
|
+
module = ?MODULE, function = compile,
|
142
|
+
args = [{file, string}],
|
143
|
+
result = {res, rescode}},
|
144
|
+
#ejabberd_commands{name = load_config, tags = [server],
|
145
|
+
desc = "Load ejabberd configuration file",
|
146
|
+
module = ?MODULE, function = load_config,
|
147
|
+
args = [{file, string}],
|
148
|
+
result = {res, rescode}},
|
149
|
+
#ejabberd_commands{name = get_cookie, tags = [erlang],
|
150
|
+
desc = "Get the Erlang cookie of this node",
|
151
|
+
module = ?MODULE, function = get_cookie,
|
152
|
+
args = [],
|
153
|
+
result = {cookie, string}},
|
154
|
+
#ejabberd_commands{name = remove_node, tags = [erlang],
|
155
|
+
desc = "Remove an ejabberd node from Mnesia clustering config",
|
156
|
+
module = ?MODULE, function = remove_node,
|
157
|
+
args = [{node, string}],
|
158
|
+
result = {res, rescode}},
|
159
|
+
#ejabberd_commands{name = export2odbc, tags = [mnesia],
|
160
|
+
desc = "Export Mnesia tables to files in directory",
|
161
|
+
module = ?MODULE, function = export2odbc,
|
162
|
+
args = [{host, string}, {path, string}],
|
163
|
+
result = {res, rescode}},
|
164
|
+
|
165
|
+
#ejabberd_commands{name = num_active_users, tags = [accounts, stats],
|
166
|
+
desc = "Get number of users active in the last days",
|
167
|
+
module = ?MODULE, function = num_active_users,
|
168
|
+
args = [{host, string}, {days, integer}],
|
169
|
+
result = {users, integer}},
|
170
|
+
#ejabberd_commands{name = delete_old_users, tags = [accounts, purge],
|
171
|
+
desc = "Delete users that didn't log in last days, or that never logged",
|
172
|
+
module = ?MODULE, function = delete_old_users,
|
173
|
+
args = [{days, integer}],
|
174
|
+
result = {res, restuple}},
|
175
|
+
#ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge],
|
176
|
+
desc = "Delete users that didn't log in last days in vhost, or that never logged",
|
177
|
+
module = ?MODULE, function = delete_old_users_vhost,
|
178
|
+
args = [{host, string}, {days, integer}],
|
179
|
+
result = {res, restuple}},
|
180
|
+
|
181
|
+
#ejabberd_commands{name = check_account, tags = [accounts],
|
182
|
+
desc = "Check if an account exists or not",
|
183
|
+
module = ejabberd_auth, function = is_user_exists,
|
184
|
+
args = [{user, string}, {host, string}],
|
185
|
+
result = {res, rescode}},
|
186
|
+
#ejabberd_commands{name = check_password, tags = [accounts],
|
187
|
+
desc = "Check if a password is correct",
|
188
|
+
module = ejabberd_auth, function = check_password,
|
189
|
+
args = [{user, string}, {host, string}, {password, string}],
|
190
|
+
result = {res, rescode}},
|
191
|
+
#ejabberd_commands{name = check_password_hash, tags = [accounts],
|
192
|
+
desc = "Check if the password hash is correct",
|
193
|
+
longdesc = "Allowed hash methods: md5, sha.",
|
194
|
+
module = ?MODULE, function = check_password_hash,
|
195
|
+
args = [{user, string}, {host, string}, {passwordhash, string}, {hashmethod, string}],
|
196
|
+
result = {res, rescode}},
|
197
|
+
#ejabberd_commands{name = change_password, tags = [accounts],
|
198
|
+
desc = "Change the password of an account",
|
199
|
+
module = ?MODULE, function = set_password,
|
200
|
+
args = [{user, string}, {host, string}, {newpass, string}],
|
201
|
+
result = {res, rescode}},
|
202
|
+
#ejabberd_commands{name = ban_account, tags = [accounts],
|
203
|
+
desc = "Ban an account: kick sessions and set random password",
|
204
|
+
module = ?MODULE, function = ban_account,
|
205
|
+
args = [{user, string}, {host, string}, {reason, string}],
|
206
|
+
result = {res, rescode}},
|
207
|
+
|
208
|
+
#ejabberd_commands{name = num_resources, tags = [session],
|
209
|
+
desc = "Get the number of resources of a user",
|
210
|
+
module = ?MODULE, function = num_resources,
|
211
|
+
args = [{user, string}, {host, string}],
|
212
|
+
result = {resources, integer}},
|
213
|
+
#ejabberd_commands{name = resource_num, tags = [session],
|
214
|
+
desc = "Resource string of a session number",
|
215
|
+
module = ?MODULE, function = resource_num,
|
216
|
+
args = [{user, string}, {host, string}, {num, integer}],
|
217
|
+
result = {resource, string}},
|
218
|
+
#ejabberd_commands{name = kick_session, tags = [session],
|
219
|
+
desc = "Kick a user session",
|
220
|
+
module = ?MODULE, function = kick_session,
|
221
|
+
args = [{user, string}, {host, string}, {resource, string}, {reason, string}],
|
222
|
+
result = {res, rescode}},
|
223
|
+
#ejabberd_commands{name = status_num_host, tags = [session, stats],
|
224
|
+
desc = "Number of logged users with this status in host",
|
225
|
+
module = ?MODULE, function = status_num,
|
226
|
+
args = [{host, string}, {status, string}],
|
227
|
+
result = {users, integer}},
|
228
|
+
#ejabberd_commands{name = status_num, tags = [session, stats],
|
229
|
+
desc = "Number of logged users with this status",
|
230
|
+
module = ?MODULE, function = status_num,
|
231
|
+
args = [{status, string}],
|
232
|
+
result = {users, integer}},
|
233
|
+
#ejabberd_commands{name = status_list_host, tags = [session],
|
234
|
+
desc = "List of users logged in host with their statuses",
|
235
|
+
module = ?MODULE, function = status_list,
|
236
|
+
args = [{host, string}, {status, string}],
|
237
|
+
result = {users, {list,
|
238
|
+
{userstatus, {tuple, [
|
239
|
+
{user, string},
|
240
|
+
{host, string},
|
241
|
+
{resource, string},
|
242
|
+
{priority, integer},
|
243
|
+
{status, string}
|
244
|
+
]}}
|
245
|
+
}}},
|
246
|
+
#ejabberd_commands{name = status_list, tags = [session],
|
247
|
+
desc = "List of logged users with this status",
|
248
|
+
module = ?MODULE, function = status_list,
|
249
|
+
args = [{status, string}],
|
250
|
+
result = {users, {list,
|
251
|
+
{userstatus, {tuple, [
|
252
|
+
{user, string},
|
253
|
+
{host, string},
|
254
|
+
{resource, string},
|
255
|
+
{priority, integer},
|
256
|
+
{status, string}
|
257
|
+
]}}
|
258
|
+
}}},
|
259
|
+
#ejabberd_commands{name = connected_users_info,
|
260
|
+
tags = [session],
|
261
|
+
desc = "List all established sessions and their information",
|
262
|
+
module = ?MODULE, function = connected_users_info,
|
263
|
+
args = [],
|
264
|
+
result = {connected_users_info,
|
265
|
+
{list,
|
266
|
+
{sessions, {tuple,
|
267
|
+
[{jid, string},
|
268
|
+
{connection, string},
|
269
|
+
{ip, string},
|
270
|
+
{port, integer},
|
271
|
+
{priority, integer},
|
272
|
+
{node, string},
|
273
|
+
{uptime, integer}
|
274
|
+
]}}
|
275
|
+
}}},
|
276
|
+
#ejabberd_commands{name = connected_users_vhost,
|
277
|
+
tags = [session],
|
278
|
+
desc = "Get the list of established sessions in a vhost",
|
279
|
+
module = ?MODULE, function = connected_users_vhost,
|
280
|
+
args = [{host, string}],
|
281
|
+
result = {connected_users_vhost, {list, {sessions, string}}}},
|
282
|
+
#ejabberd_commands{name = user_sessions_info,
|
283
|
+
tags = [session],
|
284
|
+
desc = "Get information about all sessions of a user",
|
285
|
+
module = ?MODULE, function = user_sessions_info,
|
286
|
+
args = [{user, string}, {host, string}],
|
287
|
+
result = {sessions_info,
|
288
|
+
{list,
|
289
|
+
{session, {tuple,
|
290
|
+
[{connection, string},
|
291
|
+
{ip, string},
|
292
|
+
{port, integer},
|
293
|
+
{priority, integer},
|
294
|
+
{node, string},
|
295
|
+
{uptime, integer},
|
296
|
+
{status, string},
|
297
|
+
{resource, string},
|
298
|
+
{statustext, string}
|
299
|
+
]}}
|
300
|
+
}}},
|
301
|
+
|
302
|
+
#ejabberd_commands{name = set_presence,
|
303
|
+
tags = [session],
|
304
|
+
desc = "Set presence of a session",
|
305
|
+
module = ?MODULE, function = set_presence,
|
306
|
+
args = [{user, string}, {host, string},
|
307
|
+
{resource, string}, {type, string},
|
308
|
+
{show, string}, {status, string},
|
309
|
+
{priority, string}],
|
310
|
+
result = {res, rescode}},
|
311
|
+
|
312
|
+
#ejabberd_commands{name = set_nickname, tags = [vcard],
|
313
|
+
desc = "Set nickname in a user's vCard",
|
314
|
+
module = ?MODULE, function = set_nickname,
|
315
|
+
args = [{user, string}, {host, string}, {nickname, string}],
|
316
|
+
result = {res, rescode}},
|
317
|
+
|
318
|
+
#ejabberd_commands{name = get_vcard, tags = [vcard],
|
319
|
+
desc = "Get content from a vCard field",
|
320
|
+
longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
|
321
|
+
module = ?MODULE, function = get_vcard,
|
322
|
+
args = [{user, string}, {host, string}, {name, string}],
|
323
|
+
result = {content, string}},
|
324
|
+
#ejabberd_commands{name = get_vcard2, tags = [vcard],
|
325
|
+
desc = "Get content from a vCard field",
|
326
|
+
longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
|
327
|
+
module = ?MODULE, function = get_vcard,
|
328
|
+
args = [{user, string}, {host, string}, {name, string}, {subname, string}],
|
329
|
+
result = {content, string}},
|
330
|
+
#ejabberd_commands{name = get_vcard2_multi, tags = [vcard],
|
331
|
+
desc = "Get multiple contents from a vCard field (requires exmpp installed)",
|
332
|
+
longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
|
333
|
+
module = ?MODULE, function = get_vcard_multi,
|
334
|
+
args = [{user, string}, {host, string}, {name, string}, {subname, string}],
|
335
|
+
result = {contents, {list, string}}},
|
336
|
+
|
337
|
+
#ejabberd_commands{name = set_vcard, tags = [vcard],
|
338
|
+
desc = "Set content in a vCard field",
|
339
|
+
longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
|
340
|
+
module = ?MODULE, function = set_vcard,
|
341
|
+
args = [{user, string}, {host, string}, {name, string}, {content, string}],
|
342
|
+
result = {res, rescode}},
|
343
|
+
#ejabberd_commands{name = set_vcard2, tags = [vcard],
|
344
|
+
desc = "Set content in a vCard subfield",
|
345
|
+
longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
|
346
|
+
module = ?MODULE, function = set_vcard,
|
347
|
+
args = [{user, string}, {host, string}, {name, string}, {subname, string}, {content, string}],
|
348
|
+
result = {res, rescode}},
|
349
|
+
#ejabberd_commands{name = set_vcard2_multi, tags = [vcard],
|
350
|
+
desc = "Set multiple contents in a vCard subfield",
|
351
|
+
longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
|
352
|
+
module = ?MODULE, function = set_vcard,
|
353
|
+
args = [{user, string}, {host, string}, {name, string}, {subname, string}, {contents, {list, string}}],
|
354
|
+
result = {res, rescode}},
|
355
|
+
|
356
|
+
#ejabberd_commands{name = add_rosteritem, tags = [roster],
|
357
|
+
desc = "Add an item to a user's roster",
|
358
|
+
module = ?MODULE, function = add_rosteritem,
|
359
|
+
args = [{localuser, string}, {localserver, string},
|
360
|
+
{user, string}, {server, string},
|
361
|
+
{nick, string}, {group, string},
|
362
|
+
{subs, string}],
|
363
|
+
result = {res, rescode}},
|
364
|
+
%%{"", "subs= none, from, to or both"},
|
365
|
+
%%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
|
366
|
+
%%{"", "will add mike@server.com to peter@localhost roster"},
|
367
|
+
#ejabberd_commands{name = delete_rosteritem, tags = [roster],
|
368
|
+
desc = "Delete an item from a user's roster",
|
369
|
+
module = ?MODULE, function = delete_rosteritem,
|
370
|
+
args = [{localuser, string}, {localserver, string},
|
371
|
+
{user, string}, {server, string}],
|
372
|
+
result = {res, rescode}},
|
373
|
+
#ejabberd_commands{name = process_rosteritems, tags = [roster],
|
374
|
+
desc = "List or delete rosteritems that match filtering options",
|
375
|
+
longdesc = "Explanation of each argument:\n"
|
376
|
+
" - action: what to do with each rosteritem that "
|
377
|
+
"matches all the filtering options\n"
|
378
|
+
" - subs: subscription type\n"
|
379
|
+
" - asks: pending subscription\n"
|
380
|
+
" - users: the JIDs of the local user\n"
|
381
|
+
" - contacts: the JIDs of the contact in the roster\n"
|
382
|
+
"\n"
|
383
|
+
"Allowed values in the arguments:\n"
|
384
|
+
" ACTION = list | delete\n"
|
385
|
+
" SUBS = SUB[:SUB]* | any\n"
|
386
|
+
" SUB = none | from | to | both\n"
|
387
|
+
" ASKS = ASK[:ASK]* | any\n"
|
388
|
+
" ASK = none | out | in\n"
|
389
|
+
" USERS = JID[:JID]* | any\n"
|
390
|
+
" CONTACTS = JID[:JID]* | any\n"
|
391
|
+
" JID = characters valid in a JID, and can use the "
|
392
|
+
"globs: *, ?, ! and [...]\n"
|
393
|
+
"\n"
|
394
|
+
"This example will list roster items with subscription "
|
395
|
+
"'none', 'from' or 'to' that have any ask property, of "
|
396
|
+
"local users which JID is in the virtual host "
|
397
|
+
"'example.org' and that the contact JID is either a "
|
398
|
+
"bare server name (without user part) or that has a "
|
399
|
+
"user part and the server part contains the word 'icq'"
|
400
|
+
":\n list none:from:to any *@example.org *:*@*icq*",
|
401
|
+
module = ?MODULE, function = process_rosteritems,
|
402
|
+
args = [{action, string}, {subs, string},
|
403
|
+
{asks, string}, {users, string},
|
404
|
+
{contacts, string}],
|
405
|
+
result = {res, rescode}},
|
406
|
+
#ejabberd_commands{name = get_roster, tags = [roster],
|
407
|
+
desc = "Get roster of a local user",
|
408
|
+
module = ?MODULE, function = get_roster,
|
409
|
+
args = [{user, string}, {host, string}],
|
410
|
+
result = {contacts, {list, {contact, {tuple, [
|
411
|
+
{jid, string},
|
412
|
+
{nick, string},
|
413
|
+
{subscription, string},
|
414
|
+
{ask, string},
|
415
|
+
{group, string}
|
416
|
+
]}}}}},
|
417
|
+
#ejabberd_commands{name = push_roster, tags = [roster],
|
418
|
+
desc = "Push template roster from file to a user",
|
419
|
+
module = ?MODULE, function = push_roster,
|
420
|
+
args = [{file, string}, {user, string}, {host, string}],
|
421
|
+
result = {res, rescode}},
|
422
|
+
#ejabberd_commands{name = push_roster_all, tags = [roster],
|
423
|
+
desc = "Push template roster from file to all those users",
|
424
|
+
module = ?MODULE, function = push_roster_all,
|
425
|
+
args = [{file, string}],
|
426
|
+
result = {res, rescode}},
|
427
|
+
#ejabberd_commands{name = push_alltoall, tags = [roster],
|
428
|
+
desc = "Add all the users to all the users of Host in Group",
|
429
|
+
module = ?MODULE, function = push_alltoall,
|
430
|
+
args = [{host, string}, {group, string}],
|
431
|
+
result = {res, rescode}},
|
432
|
+
|
433
|
+
#ejabberd_commands{name = set_last, tags = [last],
|
434
|
+
desc = "Set last activity information",
|
435
|
+
longdesc = "Timestamp is the seconds since"
|
436
|
+
"1970-01-01 00:00:00 UTC, for example: date +%s",
|
437
|
+
module = ?MODULE, function = set_last,
|
438
|
+
args = [{user, string}, {host, string}, {timestamp, integer}, {status, string}],
|
439
|
+
result = {res, rescode}},
|
440
|
+
|
441
|
+
#ejabberd_commands{name = private_get, tags = [private],
|
442
|
+
desc = "Get some information from a user private storage",
|
443
|
+
module = ?MODULE, function = private_get,
|
444
|
+
args = [{user, string}, {host, string}, {element, string}, {ns, string}],
|
445
|
+
result = {res, string}},
|
446
|
+
#ejabberd_commands{name = private_set, tags = [private],
|
447
|
+
desc = "Set to the user private storage",
|
448
|
+
module = ?MODULE, function = private_set,
|
449
|
+
args = [{user, string}, {host, string}, {element, string}],
|
450
|
+
result = {res, rescode}},
|
451
|
+
|
452
|
+
#ejabberd_commands{name = srg_create, tags = [shared_roster_group],
|
453
|
+
desc = "Create a Shared Roster Group",
|
454
|
+
longdesc = "If you want to specify several group "
|
455
|
+
"identifiers in the Display argument,\n"
|
456
|
+
"put \\ \" around the argument and\nseparate the "
|
457
|
+
"identifiers with \\ \\ n\n"
|
458
|
+
"For example:\n"
|
459
|
+
" ejabberdctl srg_create group3 localhost "
|
460
|
+
"name desc \\\"group1\\\\ngroup2\\\"",
|
461
|
+
module = ?MODULE, function = srg_create,
|
462
|
+
args = [{group, string}, {host, string},
|
463
|
+
{name, string}, {description, string}, {display, string}],
|
464
|
+
result = {res, rescode}},
|
465
|
+
#ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
|
466
|
+
desc = "Delete a Shared Roster Group",
|
467
|
+
module = ?MODULE, function = srg_delete,
|
468
|
+
args = [{group, string}, {host, string}],
|
469
|
+
result = {res, rescode}},
|
470
|
+
#ejabberd_commands{name = srg_list, tags = [shared_roster_group],
|
471
|
+
desc = "List the Shared Roster Groups in Host",
|
472
|
+
module = ?MODULE, function = srg_list,
|
473
|
+
args = [{host, string}],
|
474
|
+
result = {groups, {list, {id, string}}}},
|
475
|
+
#ejabberd_commands{name = srg_get_info, tags = [shared_roster_group],
|
476
|
+
desc = "Get info of a Shared Roster Group",
|
477
|
+
module = ?MODULE, function = srg_get_info,
|
478
|
+
args = [{group, string}, {host, string}],
|
479
|
+
result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
|
480
|
+
#ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
|
481
|
+
desc = "Get members of a Shared Roster Group",
|
482
|
+
module = ?MODULE, function = srg_get_members,
|
483
|
+
args = [{group, string}, {host, string}],
|
484
|
+
result = {members, {list, {member, string}}}},
|
485
|
+
#ejabberd_commands{name = srg_user_add, tags = [shared_roster_group],
|
486
|
+
desc = "Add the JID user@host to the Shared Roster Group",
|
487
|
+
module = ?MODULE, function = srg_user_add,
|
488
|
+
args = [{user, string}, {host, string}, {group, string}, {grouphost, string}],
|
489
|
+
result = {res, rescode}},
|
490
|
+
#ejabberd_commands{name = srg_user_del, tags = [shared_roster_group],
|
491
|
+
desc = "Delete this JID user@host from the Shared Roster Group",
|
492
|
+
module = ?MODULE, function = srg_user_del,
|
493
|
+
args = [{user, string}, {host, string}, {group, string}, {grouphost, string}],
|
494
|
+
result = {res, rescode}},
|
495
|
+
|
496
|
+
#ejabberd_commands{name = send_message_chat, tags = [stanza],
|
497
|
+
desc = "Send a chat message to a local or remote bare of full JID",
|
498
|
+
module = ?MODULE, function = send_message_chat,
|
499
|
+
args = [{from, string}, {to, string}, {body, string}],
|
500
|
+
result = {res, rescode}},
|
501
|
+
#ejabberd_commands{name = send_message_headline, tags = [stanza],
|
502
|
+
desc = "Send a headline message to a local or remote bare of full JID",
|
503
|
+
module = ?MODULE, function = send_message_headline,
|
504
|
+
args = [{from, string}, {to, string},
|
505
|
+
{subject, string}, {body, string}],
|
506
|
+
result = {res, rescode}},
|
507
|
+
#ejabberd_commands{name = send_stanza_c2s, tags = [stanza],
|
508
|
+
desc = "Send a stanza as if sent from a c2s session",
|
509
|
+
module = ?MODULE, function = send_stanza_c2s,
|
510
|
+
args = [{user, string}, {host, string}, {resource, string}, {stanza, string}],
|
511
|
+
result = {res, rescode}},
|
512
|
+
#ejabberd_commands{name = privacy_set, tags = [stanza],
|
513
|
+
desc = "Send a IQ set privacy stanza for a local account",
|
514
|
+
module = ?MODULE, function = privacy_set,
|
515
|
+
args = [{user, string}, {host, string}, {xmlquery, string}],
|
516
|
+
result = {res, rescode}},
|
517
|
+
|
518
|
+
#ejabberd_commands{name = stats, tags = [stats],
|
519
|
+
desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds",
|
520
|
+
module = ?MODULE, function = stats,
|
521
|
+
args = [{name, string}],
|
522
|
+
result = {stat, integer}},
|
523
|
+
#ejabberd_commands{name = stats_host, tags = [stats],
|
524
|
+
desc = "Get statistical value for this host: registeredusers onlineusers",
|
525
|
+
module = ?MODULE, function = stats,
|
526
|
+
args = [{name, string}, {host, string}],
|
527
|
+
result = {stat, integer}}
|
528
|
+
].
|
529
|
+
|
530
|
+
|
531
|
+
%%%
|
532
|
+
%%% Node
|
533
|
+
%%%
|
534
|
+
|
535
|
+
compile(File) ->
|
536
|
+
case compile:file(File) of
|
537
|
+
ok -> ok;
|
538
|
+
_ -> error
|
539
|
+
end.
|
540
|
+
|
541
|
+
load_config(Path) ->
|
542
|
+
ok = ejabberd_config:load_file(Path).
|
543
|
+
|
544
|
+
get_cookie() ->
|
545
|
+
atom_to_list(erlang:get_cookie()).
|
546
|
+
|
547
|
+
remove_node(Node) ->
|
548
|
+
mnesia:del_table_copy(schema, list_to_atom(Node)),
|
549
|
+
ok.
|
550
|
+
|
551
|
+
export2odbc(Host, Directory) ->
|
552
|
+
Tables = [
|
553
|
+
{export_last, last},
|
554
|
+
{export_offline, offline},
|
555
|
+
{export_passwd, passwd},
|
556
|
+
{export_private_storage, private_storage},
|
557
|
+
{export_roster, roster},
|
558
|
+
{export_vcard, vcard},
|
559
|
+
{export_vcard_search, vcard_search}],
|
560
|
+
Export = fun({TableFun, Table}) ->
|
561
|
+
Filename = filename:join([Directory, atom_to_list(Table)++".txt"]),
|
562
|
+
io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]),
|
563
|
+
Res = (catch ejd2odbc:TableFun(Host, Filename)),
|
564
|
+
io:format(" Result: ~p~n", [Res])
|
565
|
+
end,
|
566
|
+
lists:foreach(Export, Tables),
|
567
|
+
ok.
|
568
|
+
|
569
|
+
|
570
|
+
%%%
|
571
|
+
%%% Accounts
|
572
|
+
%%%
|
573
|
+
|
574
|
+
set_password(User, Host, Password) ->
|
575
|
+
case ejabberd_auth:set_password(User, Host, Password) of
|
576
|
+
ok ->
|
577
|
+
ok;
|
578
|
+
_ ->
|
579
|
+
error
|
580
|
+
end.
|
581
|
+
|
582
|
+
%% Copied some code from ejabberd_commands.erl
|
583
|
+
check_password_hash(User, Host, PasswordHash, HashMethod) ->
|
584
|
+
AccountPass = ejabberd_auth:get_password_s(User, Host),
|
585
|
+
AccountPassHash = case HashMethod of
|
586
|
+
"md5" -> get_md5(AccountPass);
|
587
|
+
"sha" -> get_sha(AccountPass);
|
588
|
+
_ -> undefined
|
589
|
+
end,
|
590
|
+
case AccountPassHash of
|
591
|
+
undefined -> error;
|
592
|
+
PasswordHash -> ok;
|
593
|
+
_ -> error
|
594
|
+
end.
|
595
|
+
get_md5(AccountPass) ->
|
596
|
+
lists:flatten([io_lib:format("~.16B", [X])
|
597
|
+
|| X <- binary_to_list(crypto:md5(AccountPass))]).
|
598
|
+
get_sha(AccountPass) ->
|
599
|
+
lists:flatten([io_lib:format("~.16B", [X])
|
600
|
+
|| X <- binary_to_list(crypto:sha(AccountPass))]).
|
601
|
+
|
602
|
+
num_active_users(Host, Days) ->
|
603
|
+
list_last_activity(Host, true, Days).
|
604
|
+
|
605
|
+
%% Code based on ejabberd/src/web/ejabberd_web_admin.erl
|
606
|
+
list_last_activity(Host, Integral, Days) ->
|
607
|
+
{MegaSecs, Secs, _MicroSecs} = now(),
|
608
|
+
TimeStamp = MegaSecs * 1000000 + Secs,
|
609
|
+
TS = TimeStamp - Days * 86400,
|
610
|
+
case catch mnesia:dirty_select(
|
611
|
+
last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
|
612
|
+
[{'>', '$1', TS}],
|
613
|
+
[{'trunc', {'/',
|
614
|
+
{'-', TimeStamp, '$1'},
|
615
|
+
86400}}]}]) of
|
616
|
+
{'EXIT', _Reason} ->
|
617
|
+
[];
|
618
|
+
Vals ->
|
619
|
+
Hist = histogram(Vals, Integral),
|
620
|
+
if
|
621
|
+
Hist == [] ->
|
622
|
+
0;
|
623
|
+
true ->
|
624
|
+
Left = Days - length(Hist),
|
625
|
+
Tail = if
|
626
|
+
Integral ->
|
627
|
+
lists:duplicate(Left, lists:last(Hist));
|
628
|
+
true ->
|
629
|
+
lists:duplicate(Left, 0)
|
630
|
+
end,
|
631
|
+
lists:nth(Days, Hist ++ Tail)
|
632
|
+
end
|
633
|
+
end.
|
634
|
+
histogram(Values, Integral) ->
|
635
|
+
histogram(lists:sort(Values), Integral, 0, 0, []).
|
636
|
+
histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
|
637
|
+
histogram(T, Integral, Current, Count + 1, Hist);
|
638
|
+
histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
|
639
|
+
if
|
640
|
+
Integral ->
|
641
|
+
histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
|
642
|
+
true ->
|
643
|
+
histogram(Values, Integral, Current + 1, 0, [Count | Hist])
|
644
|
+
end;
|
645
|
+
histogram([], _Integral, _Current, Count, Hist) ->
|
646
|
+
if
|
647
|
+
Count > 0 ->
|
648
|
+
lists:reverse([Count | Hist]);
|
649
|
+
true ->
|
650
|
+
lists:reverse(Hist)
|
651
|
+
end.
|
652
|
+
|
653
|
+
|
654
|
+
delete_old_users(Days) ->
|
655
|
+
%% Get the list of registered users
|
656
|
+
Users = ejabberd_auth:dirty_get_registered_users(),
|
657
|
+
|
658
|
+
{removed, N, UR} = delete_old_users(Days, Users),
|
659
|
+
{ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
|
660
|
+
|
661
|
+
delete_old_users_vhost(Host, Days) ->
|
662
|
+
%% Get the list of registered users
|
663
|
+
Users = ejabberd_auth:get_vh_registered_users(Host),
|
664
|
+
|
665
|
+
{removed, N, UR} = delete_old_users(Days, Users),
|
666
|
+
{ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
|
667
|
+
|
668
|
+
delete_old_users(Days, Users) ->
|
669
|
+
%% Convert older time
|
670
|
+
SecOlder = Days*24*60*60,
|
671
|
+
|
672
|
+
%% Get current time
|
673
|
+
{MegaSecs, Secs, _MicroSecs} = now(),
|
674
|
+
TimeStamp_now = MegaSecs * 1000000 + Secs,
|
675
|
+
|
676
|
+
%% For a user, remove if required and answer true
|
677
|
+
F = fun({LUser, LServer}) ->
|
678
|
+
%% Check if the user is logged
|
679
|
+
case ejabberd_sm:get_user_resources(LUser, LServer) of
|
680
|
+
%% If it isnt
|
681
|
+
[] ->
|
682
|
+
%% Look for his last_activity
|
683
|
+
case (get_lastactivity_module(LServer)):get_last_info(LUser, LServer) of
|
684
|
+
%% If it is
|
685
|
+
%% existent:
|
686
|
+
{ok, TimeStamp, _Status} ->
|
687
|
+
%% get his age
|
688
|
+
Sec = TimeStamp_now - TimeStamp,
|
689
|
+
%% If he is
|
690
|
+
if
|
691
|
+
%% younger than SecOlder:
|
692
|
+
Sec < SecOlder ->
|
693
|
+
%% do nothing
|
694
|
+
false;
|
695
|
+
%% older:
|
696
|
+
true ->
|
697
|
+
%% remove the user
|
698
|
+
ejabberd_auth:remove_user(LUser, LServer),
|
699
|
+
true
|
700
|
+
end;
|
701
|
+
%% nonexistent:
|
702
|
+
not_found ->
|
703
|
+
%% remove the user
|
704
|
+
ejabberd_auth:remove_user(LUser, LServer),
|
705
|
+
true
|
706
|
+
end;
|
707
|
+
%% Else
|
708
|
+
_ ->
|
709
|
+
%% do nothing
|
710
|
+
false
|
711
|
+
end
|
712
|
+
end,
|
713
|
+
%% Apply the function to every user in the list
|
714
|
+
Users_removed = lists:filter(F, Users),
|
715
|
+
{removed, length(Users_removed), Users_removed}.
|
716
|
+
|
717
|
+
get_lastactivity_module(Server) ->
|
718
|
+
case lists:member(mod_last, gen_mod:loaded_modules(Server)) of
|
719
|
+
true -> mod_last;
|
720
|
+
_ -> mod_last_odbc
|
721
|
+
end.
|
722
|
+
|
723
|
+
|
724
|
+
%%
|
725
|
+
%% Ban account
|
726
|
+
|
727
|
+
ban_account(User, Host, ReasonText) ->
|
728
|
+
Reason = prepare_reason(ReasonText),
|
729
|
+
kick_sessions(User, Host, Reason),
|
730
|
+
set_random_password(User, Host, Reason),
|
731
|
+
ok.
|
732
|
+
|
733
|
+
kick_sessions(User, Server, Reason) ->
|
734
|
+
lists:map(
|
735
|
+
fun(Resource) ->
|
736
|
+
kick_this_session(User, Server, Resource, Reason)
|
737
|
+
end,
|
738
|
+
get_resources(User, Server)).
|
739
|
+
|
740
|
+
get_resources(User, Server) ->
|
741
|
+
lists:map(
|
742
|
+
fun(Session) ->
|
743
|
+
element(3, Session#session.usr)
|
744
|
+
end,
|
745
|
+
get_sessions(User, Server)).
|
746
|
+
|
747
|
+
get_sessions(User, Server) ->
|
748
|
+
LUser = jlib:nodeprep(User),
|
749
|
+
LServer = jlib:nameprep(Server),
|
750
|
+
Sessions = mnesia:dirty_index_read(session, {LUser, LServer}, #session.us),
|
751
|
+
true = is_list(Sessions),
|
752
|
+
Sessions.
|
753
|
+
|
754
|
+
set_random_password(User, Server, Reason) ->
|
755
|
+
NewPass = build_random_password(Reason),
|
756
|
+
set_password_auth(User, Server, NewPass).
|
757
|
+
|
758
|
+
build_random_password(Reason) ->
|
759
|
+
Date = jlib:timestamp_to_iso(calendar:universal_time()),
|
760
|
+
RandomString = randoms:get_string(),
|
761
|
+
"BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason.
|
762
|
+
|
763
|
+
set_password_auth(User, Server, Password) ->
|
764
|
+
ok = ejabberd_auth:set_password(User, Server, Password).
|
765
|
+
|
766
|
+
prepare_reason([]) ->
|
767
|
+
"Kicked by administrator";
|
768
|
+
prepare_reason([Reason]) ->
|
769
|
+
Reason;
|
770
|
+
prepare_reason(Reason) when is_list(Reason) ->
|
771
|
+
Reason;
|
772
|
+
prepare_reason(StringList) ->
|
773
|
+
string:join(StringList, "_").
|
774
|
+
|
775
|
+
|
776
|
+
%%%
|
777
|
+
%%% Sessions
|
778
|
+
%%%
|
779
|
+
|
780
|
+
num_resources(User, Host) ->
|
781
|
+
length(ejabberd_sm:get_user_resources(User, Host)).
|
782
|
+
|
783
|
+
resource_num(User, Host, Num) ->
|
784
|
+
Resources = ejabberd_sm:get_user_resources(User, Host),
|
785
|
+
case (0<Num) and (Num=<length(Resources)) of
|
786
|
+
true ->
|
787
|
+
lists:nth(Num, Resources);
|
788
|
+
false ->
|
789
|
+
lists:flatten(io_lib:format("Error: Wrong resource number: ~p", [Num]))
|
790
|
+
end.
|
791
|
+
|
792
|
+
kick_session(User, Server, Resource, ReasonText) ->
|
793
|
+
kick_this_session(User, Server, Resource, prepare_reason(ReasonText)),
|
794
|
+
ok.
|
795
|
+
|
796
|
+
kick_this_session(User, Server, Resource, Reason) ->
|
797
|
+
ejabberd_router:route(
|
798
|
+
jlib:make_jid("", "", ""),
|
799
|
+
jlib:make_jid(User, Server, Resource),
|
800
|
+
{xmlelement, "broadcast", [], [{exit, Reason}]}).
|
801
|
+
|
802
|
+
|
803
|
+
status_num(Host, Status) ->
|
804
|
+
length(get_status_list(Host, Status)).
|
805
|
+
status_num(Status) ->
|
806
|
+
status_num("all", Status).
|
807
|
+
status_list(Host, Status) ->
|
808
|
+
Res = get_status_list(Host, Status),
|
809
|
+
[{U, S, R, P, St} || {U, S, R, P, St} <- Res].
|
810
|
+
status_list(Status) ->
|
811
|
+
status_list("all", Status).
|
812
|
+
|
813
|
+
|
814
|
+
get_status_list(Host, Status_required) ->
|
815
|
+
%% Get list of all logged users
|
816
|
+
Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
|
817
|
+
%% Reformat the list
|
818
|
+
Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
|
819
|
+
Fhost = case Host of
|
820
|
+
"all" ->
|
821
|
+
%% All hosts are requested, so dont filter at all
|
822
|
+
fun(_, _) -> true end;
|
823
|
+
_ ->
|
824
|
+
%% Filter the list, only Host is interesting
|
825
|
+
fun(A, B) -> A == B end
|
826
|
+
end,
|
827
|
+
Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
|
828
|
+
%% For each Pid, get its presence
|
829
|
+
Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
|
830
|
+
%% Filter by status
|
831
|
+
Fstatus = case Status_required of
|
832
|
+
"all" ->
|
833
|
+
fun(_, _) -> true end;
|
834
|
+
_ ->
|
835
|
+
fun(A, B) -> A == B end
|
836
|
+
end,
|
837
|
+
[{User, Server, Resource, Priority, stringize(Status_text)}
|
838
|
+
|| {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
|
839
|
+
apply(Fstatus, [Status, Status_required])].
|
840
|
+
|
841
|
+
connected_users_info() ->
|
842
|
+
USRIs = dirty_get_sessions_list2(),
|
843
|
+
CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
|
844
|
+
lists:map(
|
845
|
+
fun([{U, S, R}, {Now, Pid}, Priority, Info]) ->
|
846
|
+
Conn = proplists:get_value(conn, Info),
|
847
|
+
{Ip, Port} = proplists:get_value(ip, Info),
|
848
|
+
IPS = inet_parse:ntoa(Ip),
|
849
|
+
NodeS = atom_to_list(node(Pid)),
|
850
|
+
Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
|
851
|
+
calendar:now_to_local_time(Now)),
|
852
|
+
{[U, $@, S, $/, R], atom_to_list(Conn), IPS, Port, Priority, NodeS, Uptime}
|
853
|
+
end,
|
854
|
+
USRIs).
|
855
|
+
|
856
|
+
connected_users_vhost(Host) ->
|
857
|
+
USRs = ejabberd_sm:get_vh_session_list(Host),
|
858
|
+
[ [U, $@, S, $/, R] || {U, S, R} <- USRs].
|
859
|
+
|
860
|
+
%% Code copied from ejabberd_sm.erl and customized
|
861
|
+
dirty_get_sessions_list2() ->
|
862
|
+
mnesia:dirty_select(
|
863
|
+
session,
|
864
|
+
[{#session{usr = '$1', sid = '$2', priority = '$3', info = '$4', _ = '_'},
|
865
|
+
[],
|
866
|
+
[['$1', '$2', '$3', '$4']]}]).
|
867
|
+
|
868
|
+
%% Make string more print-friendly
|
869
|
+
stringize(String) ->
|
870
|
+
%% Replace newline characters with other code
|
871
|
+
element(2, regexp:gsub(String, "\n", "\\n")).
|
872
|
+
|
873
|
+
set_presence(User, Host, Resource, Type, Show, Status, Priority) ->
|
874
|
+
Pid = ejabberd_sm:get_session_pid(User, Host, Resource),
|
875
|
+
USR = User ++ "@" ++ Host ++ "/" ++ Resource,
|
876
|
+
US = User ++ "@" ++ Host,
|
877
|
+
Message = {route_xmlstreamelement,
|
878
|
+
{xmlelement, "presence",
|
879
|
+
[{"from", USR}, {"to", US}, {"type", Type}],
|
880
|
+
[{xmlelement, "show", [], [{xmlcdata, Show}]},
|
881
|
+
{xmlelement, "status", [], [{xmlcdata, Status}]},
|
882
|
+
{xmlelement, "priority", [], [{xmlcdata, Priority}]}]}},
|
883
|
+
Pid ! Message.
|
884
|
+
|
885
|
+
user_sessions_info(User, Host) ->
|
886
|
+
CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
|
887
|
+
US = {User, Host},
|
888
|
+
Sessions = case catch mnesia:dirty_index_read(session, US, #session.us) of
|
889
|
+
{'EXIT', _Reason} ->
|
890
|
+
[];
|
891
|
+
Ss ->
|
892
|
+
Ss
|
893
|
+
end,
|
894
|
+
lists:map(
|
895
|
+
fun(Session) ->
|
896
|
+
{_U, _S, Resource} = Session#session.usr,
|
897
|
+
{Now, Pid} = Session#session.sid,
|
898
|
+
{_U, _Resource, Status, StatusText} = ejabberd_c2s:get_presence(Pid),
|
899
|
+
Info = Session#session.info,
|
900
|
+
Priority = Session#session.priority,
|
901
|
+
Conn = proplists:get_value(conn, Info),
|
902
|
+
{Ip, Port} = proplists:get_value(ip, Info),
|
903
|
+
IPS = inet_parse:ntoa(Ip),
|
904
|
+
NodeS = atom_to_list(node(Pid)),
|
905
|
+
Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
|
906
|
+
calendar:now_to_local_time(Now)),
|
907
|
+
{atom_to_list(Conn), IPS, Port, Priority, NodeS, Uptime, Status, Resource, StatusText}
|
908
|
+
end,
|
909
|
+
Sessions).
|
910
|
+
|
911
|
+
|
912
|
+
%%%
|
913
|
+
%%% Vcard
|
914
|
+
%%%
|
915
|
+
|
916
|
+
set_nickname(User, Host, Nickname) ->
|
917
|
+
R = mod_vcard:process_sm_iq(
|
918
|
+
{jid, User, Host, "", User, Host, ""},
|
919
|
+
{jid, User, Host, "", User, Host, ""},
|
920
|
+
{iq, "", set, "", "en",
|
921
|
+
{xmlelement, "vCard",
|
922
|
+
[{"xmlns", "vcard-temp"}], [
|
923
|
+
{xmlelement, "NICKNAME", [], [{xmlcdata, Nickname}]}
|
924
|
+
]
|
925
|
+
}}),
|
926
|
+
case R of
|
927
|
+
{iq, [], result, [], _L, []} ->
|
928
|
+
ok;
|
929
|
+
_ ->
|
930
|
+
error
|
931
|
+
end.
|
932
|
+
|
933
|
+
get_vcard(User, Host, Name) ->
|
934
|
+
[Res | _] = get_vcard_content(User, Host, [Name]),
|
935
|
+
Res.
|
936
|
+
|
937
|
+
get_vcard(User, Host, Name, Subname) ->
|
938
|
+
[Res | _] = get_vcard_content(User, Host, [Name, Subname]),
|
939
|
+
Res.
|
940
|
+
|
941
|
+
get_vcard_multi(User, Host, Name, Subname) ->
|
942
|
+
get_vcard_content(User, Host, [Name, Subname]).
|
943
|
+
|
944
|
+
set_vcard(User, Host, Name, SomeContent) ->
|
945
|
+
set_vcard_content(User, Host, [Name], SomeContent).
|
946
|
+
|
947
|
+
set_vcard(User, Host, Name, Subname, SomeContent) ->
|
948
|
+
set_vcard_content(User, Host, [Name, Subname], SomeContent).
|
949
|
+
|
950
|
+
|
951
|
+
%%
|
952
|
+
%% Internal vcard
|
953
|
+
|
954
|
+
get_module_resource(Server) ->
|
955
|
+
case gen_mod:get_module_opt(Server, ?MODULE, module_resource, none) of
|
956
|
+
none -> atom_to_list(?MODULE);
|
957
|
+
R when is_list(R) -> R
|
958
|
+
end.
|
959
|
+
|
960
|
+
get_vcard_content(User, Server, Data) ->
|
961
|
+
[{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
|
962
|
+
JID = jlib:make_jid(User, Server, get_module_resource(Server)),
|
963
|
+
IQ = #iq{type = get, xmlns = ?NS_VCARD},
|
964
|
+
IQr = Module:Function(JID, JID, IQ),
|
965
|
+
case IQr#iq.sub_el of
|
966
|
+
[A1] ->
|
967
|
+
case get_vcard(Data, A1) of
|
968
|
+
[] -> throw(error_no_value_found_in_vcard);
|
969
|
+
ElemList -> [xml:get_tag_cdata(Elem) || Elem <- ElemList]
|
970
|
+
end;
|
971
|
+
[] ->
|
972
|
+
throw(error_no_vcard_found)
|
973
|
+
end.
|
974
|
+
|
975
|
+
get_vcard([Data1, Data2], A1) ->
|
976
|
+
case get_subtag(A1, Data1) of
|
977
|
+
false -> false;
|
978
|
+
A2List -> lists:flatten([get_vcard([Data2], A2) || A2 <- A2List])
|
979
|
+
end;
|
980
|
+
|
981
|
+
get_vcard([Data], A1) ->
|
982
|
+
get_subtag(A1, Data).
|
983
|
+
|
984
|
+
get_subtag(Xmlelement, Name) ->
|
985
|
+
case code:ensure_loaded(exmpp_xml) of
|
986
|
+
{error, _} ->
|
987
|
+
[get_subtag_xml(Xmlelement, Name)];
|
988
|
+
{module, exmpp_xml} ->
|
989
|
+
get_subtag_exmpp(Xmlelement, Name)
|
990
|
+
end.
|
991
|
+
|
992
|
+
get_subtag_xml(Xmlelement, Name) ->
|
993
|
+
xml:get_subtag(Xmlelement, Name).
|
994
|
+
|
995
|
+
get_subtag_exmpp(Xmlelement, Name) ->
|
996
|
+
Xmlel = exmpp_xml:xmlelement_to_xmlel(Xmlelement),
|
997
|
+
XmlelList = exmpp_xml:get_elements(Xmlel, Name),
|
998
|
+
[exmpp_xml:xmlel_to_xmlelement(Xmlel2) || Xmlel2 <- XmlelList].
|
999
|
+
|
1000
|
+
set_vcard_content(User, Server, Data, SomeContent) ->
|
1001
|
+
ContentList = case SomeContent of
|
1002
|
+
[Char | _] when not is_list(Char) -> [SomeContent];
|
1003
|
+
[Char | _] when is_list(Char) -> SomeContent
|
1004
|
+
end,
|
1005
|
+
[{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
|
1006
|
+
JID = jlib:make_jid(User, Server, get_module_resource(Server)),
|
1007
|
+
IQ = #iq{type = get, xmlns = ?NS_VCARD},
|
1008
|
+
IQr = Module:Function(JID, JID, IQ),
|
1009
|
+
|
1010
|
+
%% Get old vcard
|
1011
|
+
A4 = case IQr#iq.sub_el of
|
1012
|
+
[A1] ->
|
1013
|
+
{_, _, _, A2} = A1,
|
1014
|
+
update_vcard_els(Data, ContentList, A2);
|
1015
|
+
[] ->
|
1016
|
+
update_vcard_els(Data, ContentList, [])
|
1017
|
+
end,
|
1018
|
+
|
1019
|
+
%% Build new vcard
|
1020
|
+
SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
|
1021
|
+
IQ2 = #iq{type=set, sub_el = SubEl},
|
1022
|
+
|
1023
|
+
Module:Function(JID, JID, IQ2),
|
1024
|
+
ok.
|
1025
|
+
|
1026
|
+
update_vcard_els(Data, ContentList, Els1) ->
|
1027
|
+
Els2 = lists:keysort(2, Els1),
|
1028
|
+
[Data1 | Data2] = Data,
|
1029
|
+
NewEls = case Data2 of
|
1030
|
+
[] ->
|
1031
|
+
[{xmlelement, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList];
|
1032
|
+
[D2] ->
|
1033
|
+
OldEl = case lists:keysearch(Data1, 2, Els2) of
|
1034
|
+
{value, A} -> A;
|
1035
|
+
false -> {xmlelement, Data1, [], []}
|
1036
|
+
end,
|
1037
|
+
{xmlelement, _, _, ContentOld1} = OldEl,
|
1038
|
+
Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]} || Content <- ContentList],
|
1039
|
+
ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2],
|
1040
|
+
ContentOld3 = lists:keysort(2, ContentOld2),
|
1041
|
+
ContentNew = lists:keymerge(2, Content2, ContentOld3),
|
1042
|
+
[{xmlelement, Data1, [], ContentNew}]
|
1043
|
+
end,
|
1044
|
+
Els3 = lists:keydelete(Data1, 2, Els2),
|
1045
|
+
lists:keymerge(2, NewEls, Els3).
|
1046
|
+
|
1047
|
+
|
1048
|
+
%%%
|
1049
|
+
%%% Roster
|
1050
|
+
%%%
|
1051
|
+
|
1052
|
+
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) ->
|
1053
|
+
case add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, list_to_atom(Subs), []) of
|
1054
|
+
{atomic, ok} ->
|
1055
|
+
push_roster_item(LocalUser, LocalServer, User, Server, {add, Nick, Subs, Group}),
|
1056
|
+
ok;
|
1057
|
+
_ ->
|
1058
|
+
error
|
1059
|
+
end.
|
1060
|
+
|
1061
|
+
add_rosteritem(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) ->
|
1062
|
+
subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs).
|
1063
|
+
|
1064
|
+
subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) ->
|
1065
|
+
mnesia:transaction(
|
1066
|
+
fun() ->
|
1067
|
+
mnesia:write({roster,
|
1068
|
+
{LU,LS,{User,Server,[]}}, % uj
|
1069
|
+
{LU,LS}, % user
|
1070
|
+
{User,Server,[]}, % jid
|
1071
|
+
Nick, % name: "Mom", []
|
1072
|
+
Subscription, % subscription: none, to=you see him, from=he sees you, both
|
1073
|
+
none, % ask: out=send request, in=somebody requests you, none
|
1074
|
+
[Group], % groups: ["Family"]
|
1075
|
+
Xattrs, % xattrs: [{"category","conference"}]
|
1076
|
+
[] % xs: []
|
1077
|
+
})
|
1078
|
+
end).
|
1079
|
+
|
1080
|
+
delete_rosteritem(LocalUser, LocalServer, User, Server) ->
|
1081
|
+
case unsubscribe(LocalUser, LocalServer, User, Server) of
|
1082
|
+
{atomic, ok} ->
|
1083
|
+
push_roster_item(LocalUser, LocalServer, User, Server, remove),
|
1084
|
+
ok;
|
1085
|
+
_ ->
|
1086
|
+
error
|
1087
|
+
end.
|
1088
|
+
|
1089
|
+
unsubscribe(LU, LS, User, Server) ->
|
1090
|
+
mnesia:transaction(
|
1091
|
+
fun() ->
|
1092
|
+
mnesia:delete({roster, {LU, LS, {User, Server, []}}})
|
1093
|
+
end).
|
1094
|
+
|
1095
|
+
|
1096
|
+
%% -----------------------------
|
1097
|
+
%% Get Roster
|
1098
|
+
%% -----------------------------
|
1099
|
+
|
1100
|
+
get_roster(User, Server) ->
|
1101
|
+
Items = ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]),
|
1102
|
+
make_roster_xmlrpc(Items).
|
1103
|
+
|
1104
|
+
%% Note: if a contact is in several groups, the contact is returned
|
1105
|
+
%% several times, each one in a different group.
|
1106
|
+
make_roster_xmlrpc(Roster) ->
|
1107
|
+
lists:foldl(
|
1108
|
+
fun(Item, Res) ->
|
1109
|
+
JIDS = jlib:jid_to_string(Item#roster.jid),
|
1110
|
+
Nick = Item#roster.name,
|
1111
|
+
Subs = atom_to_list(Item#roster.subscription),
|
1112
|
+
Ask = atom_to_list(Item#roster.ask),
|
1113
|
+
Groups = case Item#roster.groups of
|
1114
|
+
[] -> [""];
|
1115
|
+
Gs -> Gs
|
1116
|
+
end,
|
1117
|
+
ItemsX = [{JIDS, Nick, Subs, Ask, Group}
|
1118
|
+
|| Group <- Groups],
|
1119
|
+
ItemsX ++ Res
|
1120
|
+
end,
|
1121
|
+
[],
|
1122
|
+
Roster).
|
1123
|
+
|
1124
|
+
|
1125
|
+
%%-----------------------------
|
1126
|
+
%% Push Roster from file
|
1127
|
+
%%-----------------------------
|
1128
|
+
|
1129
|
+
push_roster(File, User, Server) ->
|
1130
|
+
{ok, [Roster]} = file:consult(File),
|
1131
|
+
subscribe_roster({User, Server, "", User}, Roster).
|
1132
|
+
|
1133
|
+
push_roster_all(File) ->
|
1134
|
+
{ok, [Roster]} = file:consult(File),
|
1135
|
+
subscribe_all(Roster).
|
1136
|
+
|
1137
|
+
subscribe_all(Roster) ->
|
1138
|
+
subscribe_all(Roster, Roster).
|
1139
|
+
subscribe_all([], _) ->
|
1140
|
+
ok;
|
1141
|
+
subscribe_all([User1 | Users], Roster) ->
|
1142
|
+
subscribe_roster(User1, Roster),
|
1143
|
+
subscribe_all(Users, Roster).
|
1144
|
+
|
1145
|
+
subscribe_roster(_, []) ->
|
1146
|
+
ok;
|
1147
|
+
%% Do not subscribe a user to itself
|
1148
|
+
subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
|
1149
|
+
subscribe_roster({Name, Server, Group, Nick}, Roster);
|
1150
|
+
%% Subscribe Name2 to Name1
|
1151
|
+
subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
|
1152
|
+
subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
|
1153
|
+
subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
|
1154
|
+
|
1155
|
+
push_alltoall(S, G) ->
|
1156
|
+
Users = ejabberd_auth:get_vh_registered_users(S),
|
1157
|
+
Users2 = build_list_users(G, Users, []),
|
1158
|
+
subscribe_all(Users2),
|
1159
|
+
ok.
|
1160
|
+
|
1161
|
+
build_list_users(_Group, [], Res) ->
|
1162
|
+
Res;
|
1163
|
+
build_list_users(Group, [{User, Server}|Users], Res) ->
|
1164
|
+
build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
|
1165
|
+
|
1166
|
+
%% @spec(LU, LS, U, S, Action) -> ok
|
1167
|
+
%% Action = {add, Nick, Subs, Group} | remove
|
1168
|
+
%% @doc Push to the roster of account LU@LS the contact U@S.
|
1169
|
+
%% The specific action to perform is defined in Action.
|
1170
|
+
push_roster_item(LU, LS, U, S, Action) ->
|
1171
|
+
lists:foreach(fun(R) ->
|
1172
|
+
push_roster_item(LU, LS, R, U, S, Action)
|
1173
|
+
end, ejabberd_sm:get_user_resources(LU, LS)).
|
1174
|
+
|
1175
|
+
push_roster_item(LU, LS, R, U, S, Action) ->
|
1176
|
+
LJID = jlib:make_jid(LU, LS, R),
|
1177
|
+
BroadcastEl = build_broadcast(U, S, Action),
|
1178
|
+
ejabberd_router:route(LJID, LJID, BroadcastEl),
|
1179
|
+
Item = build_roster_item(U, S, Action),
|
1180
|
+
ResIQ = build_iq_roster_push(Item),
|
1181
|
+
ejabberd_router:route(LJID, LJID, ResIQ).
|
1182
|
+
|
1183
|
+
build_roster_item(U, S, {add, Nick, Subs, Group}) ->
|
1184
|
+
{xmlelement, "item",
|
1185
|
+
[{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))},
|
1186
|
+
{"name", Nick},
|
1187
|
+
{"subscription", Subs}],
|
1188
|
+
[{xmlelement, "group", [], [{xmlcdata, Group}]}]
|
1189
|
+
};
|
1190
|
+
build_roster_item(U, S, remove) ->
|
1191
|
+
{xmlelement, "item",
|
1192
|
+
[{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))},
|
1193
|
+
{"subscription", "remove"}],
|
1194
|
+
[]
|
1195
|
+
}.
|
1196
|
+
|
1197
|
+
build_iq_roster_push(Item) ->
|
1198
|
+
{xmlelement, "iq",
|
1199
|
+
[{"type", "set"}, {"id", "push"}],
|
1200
|
+
[{xmlelement, "query",
|
1201
|
+
[{"xmlns", ?NS_ROSTER}],
|
1202
|
+
[Item]
|
1203
|
+
}
|
1204
|
+
]
|
1205
|
+
}.
|
1206
|
+
|
1207
|
+
build_broadcast(U, S, {add, _Nick, Subs, _Group}) ->
|
1208
|
+
build_broadcast(U, S, list_to_atom(Subs));
|
1209
|
+
build_broadcast(U, S, remove) ->
|
1210
|
+
build_broadcast(U, S, none);
|
1211
|
+
%% @spec (U::string(), S::string(), Subs::atom()) -> any()
|
1212
|
+
%% Subs = both | from | to | none
|
1213
|
+
build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) ->
|
1214
|
+
{xmlelement, "broadcast", [],
|
1215
|
+
[{item, {U, S, ""}, SubsAtom}]
|
1216
|
+
}.
|
1217
|
+
|
1218
|
+
%%%
|
1219
|
+
%%% Last Activity
|
1220
|
+
%%%
|
1221
|
+
|
1222
|
+
set_last(User, Server, Timestamp, Status) ->
|
1223
|
+
Mod = get_lastactivity_module(Server),
|
1224
|
+
Mod:store_last_info(User, Server, Timestamp, Status).
|
1225
|
+
|
1226
|
+
%%%
|
1227
|
+
%%% Private Storage
|
1228
|
+
%%%
|
1229
|
+
|
1230
|
+
%% Example usage:
|
1231
|
+
%% $ ejabberdctl private_set badlop localhost "\<aa\ xmlns=\'bb\'\>Cluth\</aa\>"
|
1232
|
+
%% $ ejabberdctl private_get badlop localhost aa bb
|
1233
|
+
%% <aa xmlns='bb'>Cluth</aa>
|
1234
|
+
|
1235
|
+
private_get(Username, Host, Element, Ns) ->
|
1236
|
+
From = jlib:make_jid(Username, Host, ""),
|
1237
|
+
To = jlib:make_jid(Username, Host, ""),
|
1238
|
+
IQ = {iq, "", get, ?NS_PRIVATE, "",
|
1239
|
+
{xmlelement,"query",
|
1240
|
+
[{"xmlns",?NS_PRIVATE}],
|
1241
|
+
[{xmlelement, Element, [{"xmlns", Ns}], []}]}},
|
1242
|
+
ResIq = mod_private:process_sm_iq(From, To, IQ),
|
1243
|
+
[{xmlelement,"query",
|
1244
|
+
[{"xmlns","jabber:iq:private"}],
|
1245
|
+
[SubEl]}] = ResIq#iq.sub_el,
|
1246
|
+
xml:element_to_string(SubEl).
|
1247
|
+
|
1248
|
+
private_set(Username, Host, ElementString) ->
|
1249
|
+
case xml_stream:parse_element(ElementString) of
|
1250
|
+
{error, Error} ->
|
1251
|
+
io:format("Error found parsing the element:~n ~p~nError: ~p~n",
|
1252
|
+
[ElementString, Error]),
|
1253
|
+
error;
|
1254
|
+
Xml ->
|
1255
|
+
private_set2(Username, Host, Xml)
|
1256
|
+
end.
|
1257
|
+
|
1258
|
+
private_set2(Username, Host, Xml) ->
|
1259
|
+
From = jlib:make_jid(Username, Host, ""),
|
1260
|
+
To = jlib:make_jid(Username, Host, ""),
|
1261
|
+
IQ = {iq, "", set, ?NS_PRIVATE, "",
|
1262
|
+
{xmlelement,"query",
|
1263
|
+
[{"xmlns",?NS_PRIVATE}],
|
1264
|
+
[Xml]}},
|
1265
|
+
mod_private:process_sm_iq(From, To, IQ),
|
1266
|
+
ok.
|
1267
|
+
|
1268
|
+
%%%
|
1269
|
+
%%% Shared Roster Groups
|
1270
|
+
%%%
|
1271
|
+
|
1272
|
+
srg_create(Group, Host, Name, Description, Display) ->
|
1273
|
+
{ok, DisplayList} = case Display of
|
1274
|
+
[] -> {ok, []};
|
1275
|
+
_ -> regexp:split(Display, "\\\\n")
|
1276
|
+
end,
|
1277
|
+
Opts = [{name, Name},
|
1278
|
+
{displayed_groups, DisplayList},
|
1279
|
+
{description, Description}],
|
1280
|
+
{atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
|
1281
|
+
ok.
|
1282
|
+
|
1283
|
+
srg_delete(Group, Host) ->
|
1284
|
+
{atomic, ok} = mod_shared_roster:delete_group(Host, Group),
|
1285
|
+
ok.
|
1286
|
+
|
1287
|
+
srg_list(Host) ->
|
1288
|
+
lists:sort(mod_shared_roster:list_groups(Host)).
|
1289
|
+
|
1290
|
+
srg_get_info(Group, Host) ->
|
1291
|
+
Opts = mod_shared_roster:get_group_opts(Host,Group),
|
1292
|
+
[{io_lib:format("~p", [Title]),
|
1293
|
+
io_lib:format("~p", [Value])} || {Title, Value} <- Opts].
|
1294
|
+
|
1295
|
+
srg_get_members(Group, Host) ->
|
1296
|
+
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
|
1297
|
+
[jlib:jid_to_string(jlib:make_jid(MUser, MServer, ""))
|
1298
|
+
|| {MUser, MServer} <- Members].
|
1299
|
+
|
1300
|
+
srg_user_add(User, Host, Group, GroupHost) ->
|
1301
|
+
{atomic, ok} = mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group),
|
1302
|
+
ok.
|
1303
|
+
|
1304
|
+
srg_user_del(User, Host, Group, GroupHost) ->
|
1305
|
+
{atomic, ok} = mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group),
|
1306
|
+
ok.
|
1307
|
+
|
1308
|
+
|
1309
|
+
%%%
|
1310
|
+
%%% Stanza
|
1311
|
+
%%%
|
1312
|
+
|
1313
|
+
%% @doc Send a chat message to a Jabber account.
|
1314
|
+
%% @spec (From::string(), To::string(), Body::string()) -> ok
|
1315
|
+
send_message_chat(From, To, Body) ->
|
1316
|
+
Packet = build_packet(message_chat, [Body]),
|
1317
|
+
send_packet_all_resources(From, To, Packet).
|
1318
|
+
|
1319
|
+
%% @doc Send a headline message to a Jabber account.
|
1320
|
+
%% @spec (From::string(), To::string(), Subject::string(), Body::string()) -> ok
|
1321
|
+
send_message_headline(From, To, Subject, Body) ->
|
1322
|
+
Packet = build_packet(message_headline, [Subject, Body]),
|
1323
|
+
send_packet_all_resources(From, To, Packet).
|
1324
|
+
|
1325
|
+
%% @doc Send a packet to a Jabber account.
|
1326
|
+
%% If a resource was specified in the JID,
|
1327
|
+
%% the packet is sent only to that specific resource.
|
1328
|
+
%% If no resource was specified in the JID,
|
1329
|
+
%% and the user is remote or local but offline,
|
1330
|
+
%% the packet is sent to the bare JID.
|
1331
|
+
%% If the user is local and is online in several resources,
|
1332
|
+
%% the packet is sent to all its resources.
|
1333
|
+
send_packet_all_resources(FromJIDString, ToJIDString, Packet) ->
|
1334
|
+
FromJID = jlib:string_to_jid(FromJIDString),
|
1335
|
+
ToJID = jlib:string_to_jid(ToJIDString),
|
1336
|
+
ToUser = ToJID#jid.user,
|
1337
|
+
ToServer = ToJID#jid.server,
|
1338
|
+
case ToJID#jid.resource of
|
1339
|
+
"" ->
|
1340
|
+
send_packet_all_resources(FromJID, ToUser, ToServer, Packet);
|
1341
|
+
Res ->
|
1342
|
+
send_packet_all_resources(FromJID, ToUser, ToServer, Res, Packet)
|
1343
|
+
end.
|
1344
|
+
|
1345
|
+
send_packet_all_resources(FromJID, ToUser, ToServer, Packet) ->
|
1346
|
+
case ejabberd_sm:get_user_resources(ToUser, ToServer) of
|
1347
|
+
[] ->
|
1348
|
+
send_packet_all_resources(FromJID, ToUser, ToServer, "", Packet);
|
1349
|
+
ToResources ->
|
1350
|
+
lists:foreach(
|
1351
|
+
fun(ToResource) ->
|
1352
|
+
send_packet_all_resources(FromJID, ToUser, ToServer,
|
1353
|
+
ToResource, Packet)
|
1354
|
+
end,
|
1355
|
+
ToResources)
|
1356
|
+
end.
|
1357
|
+
|
1358
|
+
send_packet_all_resources(FromJID, ToU, ToS, ToR, Packet) ->
|
1359
|
+
ToJID = jlib:make_jid(ToU, ToS, ToR),
|
1360
|
+
ejabberd_router:route(FromJID, ToJID, Packet).
|
1361
|
+
|
1362
|
+
|
1363
|
+
build_packet(message_chat, [Body]) ->
|
1364
|
+
{xmlelement, "message",
|
1365
|
+
[{"type", "chat"}, {"id", randoms:get_string()}],
|
1366
|
+
[{xmlelement, "body", [], [{xmlcdata, Body}]}]
|
1367
|
+
};
|
1368
|
+
build_packet(message_headline, [Subject, Body]) ->
|
1369
|
+
{xmlelement, "message",
|
1370
|
+
[{"type", "headline"}, {"id", randoms:get_string()}],
|
1371
|
+
[{xmlelement, "subject", [], [{xmlcdata, Subject}]},
|
1372
|
+
{xmlelement, "body", [], [{xmlcdata, Body}]}
|
1373
|
+
]
|
1374
|
+
}.
|
1375
|
+
|
1376
|
+
send_stanza_c2s(Username, Host, Resource, Stanza) ->
|
1377
|
+
C2sPid = ejabberd_sm:get_session_pid(Username, Host, Resource),
|
1378
|
+
XmlEl = xml_stream:parse_element(Stanza),
|
1379
|
+
p1_fsm:send_event(C2sPid, {xmlstreamelement, XmlEl}).
|
1380
|
+
|
1381
|
+
privacy_set(Username, Host, QueryS) ->
|
1382
|
+
From = jlib:string_to_jid(Username ++ "@" ++ Host),
|
1383
|
+
To = jlib:string_to_jid(Host),
|
1384
|
+
QueryEl = xml_stream:parse_element(QueryS),
|
1385
|
+
StanzaEl = {xmlelement, "iq", [{"type", "set"}], [QueryEl]},
|
1386
|
+
IQ = jlib:iq_query_info(StanzaEl),
|
1387
|
+
{result, []} = ejabberd_hooks:run_fold(
|
1388
|
+
privacy_iq_set,
|
1389
|
+
Host,
|
1390
|
+
{error, ?ERR_FEATURE_NOT_IMPLEMENTED},
|
1391
|
+
[From, To, IQ]
|
1392
|
+
),
|
1393
|
+
ok.
|
1394
|
+
|
1395
|
+
%%%
|
1396
|
+
%%% Stats
|
1397
|
+
%%%
|
1398
|
+
|
1399
|
+
stats(Name) ->
|
1400
|
+
case Name of
|
1401
|
+
"uptimeseconds" -> trunc(element(1, erlang:statistics(wall_clock))/1000);
|
1402
|
+
"registeredusers" -> length(ejabberd_auth:dirty_get_registered_users());
|
1403
|
+
"onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list());
|
1404
|
+
"onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list())
|
1405
|
+
end.
|
1406
|
+
|
1407
|
+
stats(Name, Host) ->
|
1408
|
+
case Name of
|
1409
|
+
"registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
|
1410
|
+
"onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
|
1411
|
+
end.
|
1412
|
+
|
1413
|
+
|
1414
|
+
|
1415
|
+
%%-----------------------------
|
1416
|
+
%% Purge roster items
|
1417
|
+
%%-----------------------------
|
1418
|
+
|
1419
|
+
process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
|
1420
|
+
Action = case ActionS of
|
1421
|
+
"list" -> list;
|
1422
|
+
"delete" -> delete
|
1423
|
+
end,
|
1424
|
+
|
1425
|
+
Subs = lists:foldl(
|
1426
|
+
fun(any, _) -> [none, from, to, both];
|
1427
|
+
(Sub, Subs) -> [Sub | Subs]
|
1428
|
+
end,
|
1429
|
+
[],
|
1430
|
+
[list_to_atom(S) || S <- string:tokens(SubsS, ":")]
|
1431
|
+
),
|
1432
|
+
|
1433
|
+
Asks = lists:foldl(
|
1434
|
+
fun(any, _) -> [none, out, in];
|
1435
|
+
(Ask, Asks) -> [Ask | Asks]
|
1436
|
+
end,
|
1437
|
+
[],
|
1438
|
+
[list_to_atom(S) || S <- string:tokens(AsksS, ":")]
|
1439
|
+
),
|
1440
|
+
|
1441
|
+
Users = lists:foldl(
|
1442
|
+
fun("any", _) -> ["*", "*@*"];
|
1443
|
+
(U, Us) -> [U | Us]
|
1444
|
+
end,
|
1445
|
+
[],
|
1446
|
+
[S || S <- string:tokens(UsersS, ":")]
|
1447
|
+
),
|
1448
|
+
|
1449
|
+
Contacts = lists:foldl(
|
1450
|
+
fun("any", _) -> ["*", "*@*"];
|
1451
|
+
(U, Us) -> [U | Us]
|
1452
|
+
end,
|
1453
|
+
[],
|
1454
|
+
[S || S <- string:tokens(ContactsS, ":")]
|
1455
|
+
),
|
1456
|
+
|
1457
|
+
case rosteritem_purge({Action, Subs, Asks, Users, Contacts}) of
|
1458
|
+
{atomic, ok} ->
|
1459
|
+
ok;
|
1460
|
+
{error, Reason} ->
|
1461
|
+
io:format("Error purging rosteritems: ~p~n", [Reason]),
|
1462
|
+
error;
|
1463
|
+
{badrpc, Reason} ->
|
1464
|
+
io:format("BadRPC purging rosteritems: ~p~n", [Reason]),
|
1465
|
+
error
|
1466
|
+
end.
|
1467
|
+
|
1468
|
+
%% @spec ({Action::atom(), Subs::[atom()], Asks::[atom()], User::string(), Contact::string()}) -> {atomic, ok}
|
1469
|
+
rosteritem_purge(Options) ->
|
1470
|
+
Num_rosteritems = mnesia:table_info(roster, size),
|
1471
|
+
io:format("There are ~p roster items in total.~n", [Num_rosteritems]),
|
1472
|
+
Key = mnesia:dirty_first(roster),
|
1473
|
+
ok = rip(Key, Options, {0, Num_rosteritems, 0, 0}),
|
1474
|
+
{atomic, ok}.
|
1475
|
+
|
1476
|
+
rip('$end_of_table', _Options, Counters) ->
|
1477
|
+
print_progress_line(Counters),
|
1478
|
+
ok;
|
1479
|
+
rip(Key, Options, {Pr, NT, NV, ND}) ->
|
1480
|
+
Key_next = mnesia:dirty_next(roster, Key),
|
1481
|
+
{Action, _, _, _, _} = Options,
|
1482
|
+
ND2 = case decide_rip(Key, Options) of
|
1483
|
+
true ->
|
1484
|
+
apply_action(Action, Key),
|
1485
|
+
ND+1;
|
1486
|
+
false ->
|
1487
|
+
ND
|
1488
|
+
end,
|
1489
|
+
NV2 = NV+1,
|
1490
|
+
Pr2 = print_progress_line({Pr, NT, NV2, ND2}),
|
1491
|
+
rip(Key_next, Options, {Pr2, NT, NV2, ND2}).
|
1492
|
+
|
1493
|
+
apply_action(list, Key) ->
|
1494
|
+
{User, Server, JID} = Key,
|
1495
|
+
{RUser, RServer, _} = JID,
|
1496
|
+
io:format("Matches: ~s@~s ~s@~s~n", [User, Server, RUser, RServer]);
|
1497
|
+
apply_action(delete, Key) ->
|
1498
|
+
apply_action(list, Key),
|
1499
|
+
mnesia:dirty_delete(roster, Key).
|
1500
|
+
|
1501
|
+
print_progress_line({Pr, NT, NV, ND}) ->
|
1502
|
+
Pr2 = trunc((NV/NT)*100),
|
1503
|
+
case Pr == Pr2 of
|
1504
|
+
true ->
|
1505
|
+
ok;
|
1506
|
+
false ->
|
1507
|
+
io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND])
|
1508
|
+
end,
|
1509
|
+
Pr2.
|
1510
|
+
|
1511
|
+
decide_rip(Key, {_Action, Subs, Asks, User, Contact}) ->
|
1512
|
+
case catch mnesia:dirty_read(roster, Key) of
|
1513
|
+
[RI] ->
|
1514
|
+
lists:member(RI#roster.subscription, Subs)
|
1515
|
+
andalso lists:member(RI#roster.ask, Asks)
|
1516
|
+
andalso decide_rip_jid(RI#roster.us, User)
|
1517
|
+
andalso decide_rip_jid(RI#roster.jid, Contact);
|
1518
|
+
_ ->
|
1519
|
+
false
|
1520
|
+
end.
|
1521
|
+
|
1522
|
+
%% Returns true if the server of the JID is included in the servers
|
1523
|
+
decide_rip_jid({UName, UServer, _UResource}, Match_list) ->
|
1524
|
+
decide_rip_jid({UName, UServer}, Match_list);
|
1525
|
+
decide_rip_jid({UName, UServer}, Match_list) ->
|
1526
|
+
lists:any(
|
1527
|
+
fun(Match_string) ->
|
1528
|
+
MJID = jlib:string_to_jid(Match_string),
|
1529
|
+
MName = MJID#jid.luser,
|
1530
|
+
MServer = MJID#jid.lserver,
|
1531
|
+
Is_server = is_glob_match(UServer, MServer),
|
1532
|
+
case MName of
|
1533
|
+
[] when UName == [] ->
|
1534
|
+
Is_server;
|
1535
|
+
[] ->
|
1536
|
+
false;
|
1537
|
+
_ ->
|
1538
|
+
Is_server
|
1539
|
+
andalso is_glob_match(UName, MName)
|
1540
|
+
end
|
1541
|
+
end,
|
1542
|
+
Match_list).
|
1543
|
+
|
1544
|
+
%% Copied from ejabberd-2.0.0/src/acl.erl
|
1545
|
+
is_regexp_match(String, RegExp) ->
|
1546
|
+
case regexp:first_match(String, RegExp) of
|
1547
|
+
nomatch ->
|
1548
|
+
false;
|
1549
|
+
{match, _, _} ->
|
1550
|
+
true;
|
1551
|
+
{error, ErrDesc} ->
|
1552
|
+
io:format(
|
1553
|
+
"Wrong regexp ~p in ACL: ~p",
|
1554
|
+
[RegExp, lists:flatten(regexp:format_error(ErrDesc))]),
|
1555
|
+
false
|
1556
|
+
end.
|
1557
|
+
is_glob_match(String, [$! | Glob]) ->
|
1558
|
+
not is_regexp_match(String, regexp:sh_to_awk(Glob));
|
1559
|
+
is_glob_match(String, Glob) ->
|
1560
|
+
is_regexp_match(String, regexp:sh_to_awk(Glob)).
|