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.
Files changed (168) hide show
  1. data/Gemfile +0 -9
  2. data/base/app/assets/stylesheets/contacts.css.scss +8 -3
  3. data/base/app/assets/stylesheets/frontpage.css.scss +1 -1
  4. data/base/app/assets/stylesheets/search.css.scss +17 -1
  5. data/base/app/controllers/contacts_controller.rb +2 -5
  6. data/base/app/models/contact.rb +3 -2
  7. data/base/app/views/activities/_options.html.erb +1 -1
  8. data/base/app/views/contacts/_form.html.erb +14 -7
  9. data/base/app/views/frontpage/_social_networks.html.erb +3 -0
  10. data/base/app/views/groups/_new.html.erb +2 -2
  11. data/base/app/views/groups/_show.html.erb +1 -2
  12. data/base/app/views/groups/index.html.erb +1 -1
  13. data/base/app/views/layouts/_header_signed_out.erb +1 -3
  14. data/base/app/views/notifications/activities/_follow_subject.html.erb +5 -2
  15. data/base/app/views/notifications/activities/_like_subject.html.erb +6 -2
  16. data/base/app/views/notifications/activities/_make-friend_subject.html.erb +5 -2
  17. data/base/app/views/notifications/activities/_post_subject.html.erb +6 -1
  18. data/base/app/views/notifications/activities/_update_body.html.erb +5 -1
  19. data/base/app/views/notifications/activities/_update_subject.html.erb +5 -1
  20. data/base/app/views/posts/_post_focus_search.html.erb +37 -3
  21. data/base/app/views/posts/_post_global_search.html.erb +2 -0
  22. data/base/app/views/profiles/_contact.html.erb +1 -1
  23. data/base/app/views/search/index.js.erb +2 -0
  24. data/base/app/views/toolbar/_home.html.erb +4 -0
  25. data/base/config/locales/en.yml +26 -0
  26. data/base/config/locales/es.yml +26 -0
  27. data/base/lib/mailboxer/notification_decoder.rb +8 -6
  28. data/base/lib/social_stream-base.rb +1 -1
  29. data/base/lib/social_stream/base/version.rb +1 -1
  30. data/base/lib/social_stream/migrations/components.rb +1 -1
  31. data/base/lib/social_stream/toolbar_config/base.rb +12 -1
  32. data/base/social_stream-base.gemspec +1 -1
  33. data/base/spec/controllers/contacts_controller_spec.rb +4 -4
  34. data/events/app/assets/javascripts/social_stream-events.js +2 -2
  35. data/events/app/assets/stylesheets/social_stream-events.css +3 -0
  36. data/events/{app → vendor}/assets/javascripts/jquery.ad-gallery.js +0 -0
  37. data/events/{app → vendor}/assets/javascripts/jquery.ad-gallery.pack.js +0 -0
  38. data/events/{app → vendor}/assets/stylesheets/ad_next.png +0 -0
  39. data/events/{app → vendor}/assets/stylesheets/ad_prev.png +0 -0
  40. data/events/{app → vendor}/assets/stylesheets/ad_scroll_back.png +0 -0
  41. data/events/{app → vendor}/assets/stylesheets/ad_scroll_forward.png +0 -0
  42. data/events/{app → vendor}/assets/stylesheets/fullcalendar.css +0 -0
  43. data/events/{app → vendor}/assets/stylesheets/fullcalendar.print.css +0 -0
  44. data/events/{app → vendor}/assets/stylesheets/jquery.ad-gallery.css +0 -0
  45. data/events/{app → vendor}/assets/stylesheets/loader.gif +0 -0
  46. data/events/vendor/assets/stylesheets/social_stream-events.css +273 -0
  47. data/lib/social_stream.rb +0 -4
  48. data/lib/social_stream/version.rb +1 -1
  49. data/presence/.gitignore +4 -0
  50. data/presence/Gemfile +4 -0
  51. data/presence/Rakefile +1 -0
  52. data/presence/app/assets/audio/chat/onMessageAudio.mp3 +0 -0
  53. data/presence/app/assets/audio/chat/onMessageAudio.wav +0 -0
  54. data/presence/app/assets/images/arrow.png +0 -0
  55. data/presence/app/assets/images/black_arrow3.png +0 -0
  56. data/presence/app/assets/images/status/available.png +0 -0
  57. data/presence/app/assets/images/status/away.png +0 -0
  58. data/presence/app/assets/images/status/dnd.png +0 -0
  59. data/presence/app/assets/javascripts/jquery-ui-1.8.14.custom.min.js +789 -0
  60. data/presence/app/assets/javascripts/jquery.tools.min.js +17 -0
  61. data/presence/app/assets/javascripts/jquery.tools.tooltip.js +11 -0
  62. data/presence/app/assets/javascripts/jquery.ui.chatbox.js +260 -0
  63. data/presence/app/assets/javascripts/social_stream-presence.js +2 -0
  64. data/presence/app/assets/javascripts/store.js +27 -0
  65. data/presence/app/assets/javascripts/strophe.js +3612 -0
  66. data/presence/app/assets/javascripts/xmpp_client.js +498 -0
  67. data/presence/app/assets/stylesheets/chat.css +231 -0
  68. data/presence/app/assets/stylesheets/jquery.ui.chatbox.css +54 -0
  69. data/presence/app/assets/stylesheets/social_stream-presence.css +3 -0
  70. data/presence/app/controllers/xmpp_controller.rb +190 -0
  71. data/presence/app/helpers/xmpp_helper.rb +33 -0
  72. data/presence/app/views/xmpp/_chat.html.erb +59 -0
  73. data/presence/app/views/xmpp/_chat_connecting.html.erb +8 -0
  74. data/presence/app/views/xmpp/_chat_contacts.html.erb +46 -0
  75. data/presence/app/views/xmpp/_chat_off.html.erb +35 -0
  76. data/presence/app/views/xmpp/active_users.html.erb +33 -0
  77. data/presence/app/views/xmpp/chat.html.erb +13 -0
  78. data/presence/app/views/xmpp/index.html +19 -0
  79. data/presence/app/views/xmpp/test.html.erb +11 -0
  80. data/presence/config/routes.rb +15 -0
  81. data/presence/db/migrate/20110711111408_add_connected_column_to_user.rb +9 -0
  82. data/presence/db/migrate/20110928135031_add_status_column_to_user.rb +9 -0
  83. data/presence/ejabberd/conf/ejabberd.cfg +625 -0
  84. data/presence/ejabberd/conf/ejabberdctl.cfg +154 -0
  85. data/presence/ejabberd/conf/inetrc +3 -0
  86. data/presence/ejabberd/conf/server.pem +37 -0
  87. data/presence/ejabberd/conf/ssconfig.cfg +29 -0
  88. data/presence/ejabberd/ejabberd_scripts/authentication_script +117 -0
  89. data/presence/ejabberd/ejabberd_scripts/authentication_script_org +114 -0
  90. data/presence/ejabberd/ejabberd_scripts/compile_module +34 -0
  91. data/presence/ejabberd/ejabberd_scripts/generate_random_password +18 -0
  92. data/presence/ejabberd/ejabberd_scripts/kill_authentication_script.sh +13 -0
  93. data/presence/ejabberd/ejabberd_scripts/reset_connection_script +55 -0
  94. data/presence/ejabberd/ejabberd_scripts/reset_logs.sh +23 -0
  95. data/presence/ejabberd/ejabberd_scripts/set_connection_script +48 -0
  96. data/presence/ejabberd/ejabberd_scripts/set_presence_script +48 -0
  97. data/presence/ejabberd/ejabberd_scripts/show_config.sh +30 -0
  98. data/presence/ejabberd/ejabberd_scripts/start_ejabberd.sh +68 -0
  99. data/presence/ejabberd/ejabberd_scripts/stop_ejabberd.sh +12 -0
  100. data/presence/ejabberd/ejabberd_scripts/synchronize_presence_script +57 -0
  101. data/presence/ejabberd/ejabberd_scripts/unset_connection_script +48 -0
  102. data/presence/ejabberd/mod_admin_extra/mod_admin_extra.beam +0 -0
  103. data/presence/ejabberd/mod_admin_extra/mod_admin_extra.erl +1560 -0
  104. data/presence/ejabberd/mod_sspresence/mod_sspresence.erl +230 -0
  105. data/presence/lib/social_stream-presence.rb +13 -0
  106. data/presence/lib/social_stream/migrations/presence.rb +9 -0
  107. data/presence/lib/social_stream/presence/config.rb +9 -0
  108. data/presence/lib/social_stream/presence/engine.rb +49 -0
  109. data/presence/lib/social_stream/presence/models/buddy_manager.rb +67 -0
  110. data/presence/social_stream-presence.gemspec +28 -0
  111. data/presence/spec/demo/.gitignore +5 -0
  112. data/presence/spec/demo/.rspec +1 -0
  113. data/presence/spec/demo/README +261 -0
  114. data/presence/spec/demo/Rakefile +7 -0
  115. data/presence/spec/demo/app/assets/images/rails.png +0 -0
  116. data/presence/spec/demo/app/assets/javascripts/application.js +9 -0
  117. data/presence/spec/demo/app/assets/stylesheets/application.css +7 -0
  118. data/presence/spec/demo/app/controllers/application_controller.rb +3 -0
  119. data/presence/spec/demo/app/helpers/application_helper.rb +2 -0
  120. data/presence/spec/demo/app/mailers/.gitkeep +0 -0
  121. data/presence/spec/demo/app/models/.gitkeep +0 -0
  122. data/presence/spec/demo/app/views/layouts/application.html.erb +14 -0
  123. data/presence/spec/demo/autotest/discover.rb +2 -0
  124. data/presence/spec/demo/config.ru +4 -0
  125. data/presence/spec/demo/config/application.rb +41 -0
  126. data/presence/spec/demo/config/boot.rb +6 -0
  127. data/presence/spec/demo/config/database.yml +25 -0
  128. data/presence/spec/demo/config/environment.rb +5 -0
  129. data/presence/spec/demo/config/environments/development.rb +27 -0
  130. data/presence/spec/demo/config/environments/production.rb +54 -0
  131. data/presence/spec/demo/config/environments/test.rb +39 -0
  132. data/presence/spec/demo/config/initializers/backtrace_silencers.rb +7 -0
  133. data/presence/spec/demo/config/initializers/inflections.rb +10 -0
  134. data/presence/spec/demo/config/initializers/mime_types.rb +5 -0
  135. data/presence/spec/demo/config/initializers/secret_token.rb +7 -0
  136. data/presence/spec/demo/config/initializers/session_store.rb +8 -0
  137. data/presence/spec/demo/config/initializers/wrap_parameters.rb +12 -0
  138. data/presence/spec/demo/config/locales/en.yml +5 -0
  139. data/presence/spec/demo/config/routes.rb +58 -0
  140. data/presence/spec/demo/db/seeds.rb +7 -0
  141. data/presence/spec/demo/doc/README_FOR_APP +2 -0
  142. data/presence/spec/demo/lib/tasks/.gitkeep +0 -0
  143. data/presence/spec/demo/log/.gitkeep +0 -0
  144. data/presence/spec/demo/public/404.html +26 -0
  145. data/presence/spec/demo/public/422.html +26 -0
  146. data/presence/spec/demo/public/500.html +26 -0
  147. data/presence/spec/demo/public/favicon.ico +0 -0
  148. data/presence/spec/demo/public/index.html +241 -0
  149. data/presence/spec/demo/public/robots.txt +5 -0
  150. data/presence/spec/demo/script/rails +6 -0
  151. data/presence/spec/demo/spec/spec_helper.rb +27 -0
  152. data/presence/spec/demo/test/fixtures/.gitkeep +0 -0
  153. data/presence/spec/demo/test/functional/.gitkeep +0 -0
  154. data/presence/spec/demo/test/integration/.gitkeep +0 -0
  155. data/presence/spec/demo/test/performance/browsing_test.rb +12 -0
  156. data/presence/spec/demo/test/test_helper.rb +13 -0
  157. data/presence/spec/demo/test/unit/.gitkeep +0 -0
  158. data/presence/spec/demo/vendor/assets/stylesheets/.gitkeep +0 -0
  159. data/presence/spec/demo/vendor/plugins/.gitkeep +0 -0
  160. data/presence/spec/discover.rb +2 -0
  161. data/presence/spec/spec_helper.rb +27 -0
  162. data/social_stream.gemspec +9 -2
  163. data/spec/dummy/config/environments/{test_pg.rb → test_mysql.rb} +6 -2
  164. data/spec/dummy/config/environments/test_postgres.rb +39 -0
  165. data/spec/spec_helper.rb +1 -4
  166. metadata +135 -21
  167. data/base/app/views/groups/_new_activity.html.erb +0 -1
  168. data/base/app/views/groups/_new_activity_fields.html.erb +0 -7
@@ -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)).