social_stream 0.11.2 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)).