• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

processone / ejabberd / 603

17 Oct 2023 01:57PM UTC coverage: 32.654% (-0.4%) from 33.021%
603

push

github

badlop
Fixing minor typos in CHANGELOG

13497 of 41333 relevant lines covered (32.65%)

646.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/mod_admin_extra.erl
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-2023   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 along
21
%%% with this program; if not, write to the Free Software Foundation, Inc.,
22
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
%%%
24
%%%-------------------------------------------------------------------
25

26
-module(mod_admin_extra).
27
-author('badlop@process-one.net').
28

29
-behaviour(gen_mod).
30

31
-include("logger.hrl").
32
-include("translate.hrl").
33

34
-export([start/2, stop/1, reload/3, mod_options/1,
35
         get_commands_spec/0, depends/2, mod_doc/0]).
36

37
% Commands API
38
-export([
39
         % Adminsys
40
         compile/1, get_cookie/0,
41
         restart_module/2,
42

43
         % Sessions
44
         num_resources/2, resource_num/3,
45
         kick_session/4, status_num/2, status_num/1,
46
         status_list/2, status_list/1, connected_users_info/0,
47
         connected_users_vhost/1, set_presence/7,
48
         get_presence/2, user_sessions_info/2, get_last/2, set_last/4,
49

50
         % Accounts
51
         set_password/3, check_password_hash/4, delete_old_users/1,
52
         delete_old_users_vhost/2, ban_account/3, check_password/3,
53

54
         % vCard
55
         set_nickname/3, get_vcard/3,
56
         get_vcard/4, get_vcard_multi/4, set_vcard/4,
57
         set_vcard/5,
58

59
         % Roster
60
         add_rosteritem/7, delete_rosteritem/4,
61
         get_roster/2, push_roster/3,
62
         push_roster_all/1, push_alltoall/2,
63
         push_roster_item/5, build_roster_item/3,
64

65
         % Private storage
66
         private_get/4, private_set/3,
67

68
         % Shared roster
69
         srg_create/5,
70
         srg_delete/2, srg_list/1, srg_get_info/2,
71
         srg_get_members/2, srg_user_add/4, srg_user_del/4,
72

73
         % Send message
74
         send_message/5, send_stanza/3, send_stanza_c2s/4,
75

76
         % Privacy list
77
         privacy_set/3,
78

79
         % Stats
80
         stats/1, stats/2
81
        ]).
82

83

84
-include("ejabberd_commands.hrl").
85
-include("mod_roster.hrl").
86
-include("mod_privacy.hrl").
87
-include("ejabberd_sm.hrl").
88
-include_lib("xmpp/include/xmpp.hrl").
89

90
%%%
91
%%% gen_mod
92
%%%
93

94
start(_Host, _Opts) ->
95
    ejabberd_commands:register_commands(?MODULE, get_commands_spec()).
×
96

97
stop(Host) ->
98
    case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
×
99
        false ->
100
            ejabberd_commands:unregister_commands(get_commands_spec());
×
101
        true ->
102
            ok
×
103
    end.
104

105
reload(_Host, _NewOpts, _OldOpts) ->
106
    ok.
×
107

108
depends(_Host, _Opts) ->
109
    [].
×
110

111
%%%
112
%%% Register commands
113
%%%
114

115
get_commands_spec() ->
116
    Vcard1FieldsString = "Some vcard field names in get/set_vcard are:\n\n"
×
117
        "* FN           - Full Name\n"
118
        "* NICKNAME     - Nickname\n"
119
        "* BDAY         - Birthday\n"
120
        "* TITLE        - Work: Position\n"
121
        "* ROLE         - Work: Role\n",
122

123
    Vcard2FieldsString = "Some vcard field names and subnames in get/set_vcard2 are:\n\n"
×
124
        "* N FAMILY     - Family name\n"
125
        "* N GIVEN      - Given name\n"
126
        "* N MIDDLE     - Middle name\n"
127
        "* ADR CTRY     - Address: Country\n"
128
        "* ADR LOCALITY - Address: City\n"
129
        "* TEL HOME     - Telephone: Home\n"
130
        "* TEL CELL     - Telephone: Cellphone\n"
131
        "* TEL WORK     - Telephone: Work\n"
132
        "* TEL VOICE    - Telephone: Voice\n"
133
        "* EMAIL USERID - E-Mail Address\n"
134
        "* ORG ORGNAME  - Work: Company\n"
135
        "* ORG ORGUNIT  - Work: Department\n",
136

137
    VcardXEP = "For a full list of vCard fields check XEP-0054: vcard-temp at "
×
138
        "https://xmpp.org/extensions/xep-0054.html",
139

140
    [
×
141
     #ejabberd_commands{name = compile, tags = [erlang],
142
                        desc = "Recompile and reload Erlang source code file",
143
                        module = ?MODULE, function = compile,
144
                        args = [{file, string}],
145
                        args_example = ["/home/me/srcs/ejabberd/mod_example.erl"],
146
                        args_desc = ["Filename of erlang source file to compile"],
147
                        result = {res, rescode},
148
                        result_example = ok,
149
                        result_desc = "Status code: 0 on success, 1 otherwise"},
150
     #ejabberd_commands{name = get_cookie, tags = [erlang],
151
                        desc = "Get the Erlang cookie of this node",
152
                        module = ?MODULE, function = get_cookie,
153
                        args = [],
154
                        result = {cookie, string},
155
                        result_example = "MWTAVMODFELNLSMYXPPD",
156
                        result_desc = "Erlang cookie used for authentication by ejabberd"},
157
    #ejabberd_commands{name = restart_module, tags = [erlang],
158
                        desc = "Stop an ejabberd module, reload code and start",
159
                        module = ?MODULE, function = restart_module,
160
                        args = [{host, binary}, {module, binary}],
161
                        args_example = ["myserver.com","mod_admin_extra"],
162
                        args_desc = ["Server name", "Module to restart"],
163
                        result = {res, integer},
164
                        result_example = 0,
165
                        result_desc = "Returns integer code:\n"
166
                                      " - 0: code reloaded, module restarted\n"
167
                                      " - 1: error: module not loaded\n"
168
                                      " - 2: code not reloaded, but module restarted"},
169
     #ejabberd_commands{name = delete_old_users, tags = [accounts, purge],
170
                        desc = "Delete users that didn't log in last days, or that never logged",
171
                        longdesc = "To protect admin accounts, configure this for example:\n"
172
                            "```\n"
173
                            "access_rules:\n"
174
                            "  protect_old_users:\n"
175
                            "    - allow: admin\n"
176
                            "    - deny: all\n"
177
                            "```\n",
178
                        module = ?MODULE, function = delete_old_users,
179
                        args = [{days, integer}],
180
                        args_example = [30],
181
                        args_desc = ["Last login age in days of accounts that should be removed"],
182
                        result = {res, restuple},
183
                        result_example = {ok, <<"Deleted 2 users: [\"oldman@myserver.com\", \"test@myserver.com\"]">>},
184
                        result_desc = "Result tuple"},
185
     #ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge],
186
                        desc = "Delete users that didn't log in last days in vhost, or that never logged",
187
                        longdesc = "To protect admin accounts, configure this for example:\n"
188
                            "```\n"
189
                            "access_rules:\n"
190
                            "  delete_old_users:\n"
191
                            "    - deny: admin\n"
192
                            "    - allow: all\n"
193
                            "```\n",
194
                        module = ?MODULE, function = delete_old_users_vhost,
195
                        args = [{host, binary}, {days, integer}],
196
                        args_example = [<<"myserver.com">>, 30],
197
                        args_desc = ["Server name",
198
                                     "Last login age in days of accounts that should be removed"],
199
                        result = {res, restuple},
200
                        result_example = {ok, <<"Deleted 2 users: [\"oldman@myserver.com\", \"test@myserver.com\"]">>},
201
                        result_desc = "Result tuple"},
202
     #ejabberd_commands{name = check_account, tags = [accounts],
203
                        desc = "Check if an account exists or not",
204
                        module = ejabberd_auth, function = user_exists,
205
                        args = [{user, binary}, {host, binary}],
206
                        args_example = [<<"peter">>, <<"myserver.com">>],
207
                        args_desc = ["User name to check", "Server to check"],
208
                        result = {res, rescode},
209
                        result_example = ok,
210
                        result_desc = "Status code: 0 on success, 1 otherwise"},
211
     #ejabberd_commands{name = check_password, tags = [accounts],
212
                        desc = "Check if a password is correct",
213
                        module = ?MODULE, function = check_password,
214
                        args = [{user, binary}, {host, binary}, {password, binary}],
215
                        args_example = [<<"peter">>, <<"myserver.com">>, <<"secret">>],
216
                        args_desc = ["User name to check", "Server to check", "Password to check"],
217
                        result = {res, rescode},
218
                        result_example = ok,
219
                        result_desc = "Status code: 0 on success, 1 otherwise"},
220
     #ejabberd_commands{name = check_password_hash, tags = [accounts],
221
                        desc = "Check if the password hash is correct",
222
                        longdesc = "Allows hash methods from the Erlang/OTP "
223
                        "[crypto](https://www.erlang.org/doc/man/crypto) application.",
224
                        module = ?MODULE, function = check_password_hash,
225
                        args = [{user, binary}, {host, binary}, {passwordhash, binary},
226
                                {hashmethod, binary}],
227
                        args_example = [<<"peter">>, <<"myserver.com">>,
228
                                        <<"5ebe2294ecd0e0f08eab7690d2a6ee69">>, <<"md5">>],
229
                        args_desc = ["User name to check", "Server to check",
230
                                     "Password's hash value", "Name of hash method"],
231
                        result = {res, rescode},
232
                        result_example = ok,
233
                        result_desc = "Status code: 0 on success, 1 otherwise"},
234
     #ejabberd_commands{name = change_password, tags = [accounts],
235
                        desc = "Change the password of an account",
236
                        module = ?MODULE, function = set_password,
237
                        args = [{user, binary}, {host, binary}, {newpass, binary}],
238
                        args_example = [<<"peter">>, <<"myserver.com">>, <<"blank">>],
239
                        args_desc = ["User name", "Server name",
240
                                     "New password for user"],
241
                        result = {res, rescode},
242
                        result_example = ok,
243
                        result_desc = "Status code: 0 on success, 1 otherwise"},
244
     #ejabberd_commands{name = ban_account, tags = [accounts],
245
                        desc = "Ban an account: kick sessions and set random password",
246
                        module = ?MODULE, function = ban_account,
247
                        args = [{user, binary}, {host, binary}, {reason, binary}],
248
                        args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
249
                        args_desc = ["User name to ban", "Server name",
250
                                     "Reason for banning user"],
251
                        result = {res, rescode},
252
                        result_example = ok,
253
                        result_desc = "Status code: 0 on success, 1 otherwise"},
254
     #ejabberd_commands{name = num_resources, tags = [session],
255
                        desc = "Get the number of resources of a user",
256
                        module = ?MODULE, function = num_resources,
257
                        args = [{user, binary}, {host, binary}],
258
                        args_example = [<<"peter">>, <<"myserver.com">>],
259
                        args_desc = ["User name", "Server name"],
260
                        result = {resources, integer},
261
                        result_example = 5,
262
                        result_desc = "Number of active resources for a user"},
263
     #ejabberd_commands{name = resource_num, tags = [session],
264
                        desc = "Resource string of a session number",
265
                        module = ?MODULE, function = resource_num,
266
                        args = [{user, binary}, {host, binary}, {num, integer}],
267
                        args_example = [<<"peter">>, <<"myserver.com">>, 2],
268
                        args_desc = ["User name", "Server name", "ID of resource to return"],
269
                        result = {resource, string},
270
                        result_example = <<"Psi">>,
271
                        result_desc = "Name of user resource"},
272
     #ejabberd_commands{name = kick_session, tags = [session],
273
                        desc = "Kick a user session",
274
                        module = ?MODULE, function = kick_session,
275
                        args = [{user, binary}, {host, binary}, {resource, binary}, {reason, binary}],
276
                        args_example = [<<"peter">>, <<"myserver.com">>, <<"Psi">>,
277
                                        <<"Stuck connection">>],
278
                        args_desc = ["User name", "Server name", "User's resource",
279
                                     "Reason for closing session"],
280
                        result = {res, rescode},
281
                        result_example = ok,
282
                        result_desc = "Status code: 0 on success, 1 otherwise"},
283
     #ejabberd_commands{name = status_num_host, tags = [session, statistics],
284
                        desc = "Number of logged users with this status in host",
285
                        policy = admin,
286
                        module = ?MODULE, function = status_num,
287
                        args = [{host, binary}, {status, binary}],
288
                        args_example = [<<"myserver.com">>, <<"dnd">>],
289
                        args_desc = ["Server name", "Status type to check"],
290
                        result = {users, integer},
291
                        result_example = 23,
292
                        result_desc = "Number of connected sessions with given status type"},
293
     #ejabberd_commands{name = status_num, tags = [session, statistics],
294
                        desc = "Number of logged users with this status",
295
                        policy = admin,
296
                        module = ?MODULE, function = status_num,
297
                        args = [{status, binary}],
298
                        args_example = [<<"dnd">>],
299
                        args_desc = ["Status type to check"],
300
                        result = {users, integer},
301
                        result_example = 23,
302
                        result_desc = "Number of connected sessions with given status type"},
303
     #ejabberd_commands{name = status_list_host, tags = [session],
304
                        desc = "List of users logged in host with their statuses",
305
                        module = ?MODULE, function = status_list,
306
                        args = [{host, binary}, {status, binary}],
307
                        args_example = [<<"myserver.com">>, <<"dnd">>],
308
                        args_desc = ["Server name", "Status type to check"],
309
                        result_example = [{<<"peter">>,<<"myserver.com">>,<<"tka">>,6,<<"Busy">>}],
310
                        result = {users, {list,
311
                                          {userstatus, {tuple, [
312
                                                                {user, string},
313
                                                                {host, string},
314
                                                                {resource, string},
315
                                                                {priority, integer},
316
                                                                {status, string}
317
                                                               ]}}
318
                                         }}},
319
     #ejabberd_commands{name = status_list, tags = [session],
320
                        desc = "List of logged users with this status",
321
                        module = ?MODULE, function = status_list,
322
                        args = [{status, binary}],
323
                        args_example = [<<"dnd">>],
324
                        args_desc = ["Status type to check"],
325
                        result_example = [{<<"peter">>,<<"myserver.com">>,<<"tka">>,6,<<"Busy">>}],
326
                        result = {users, {list,
327
                                          {userstatus, {tuple, [
328
                                                                {user, string},
329
                                                                {host, string},
330
                                                                {resource, string},
331
                                                                {priority, integer},
332
                                                                {status, string}
333
                                                               ]}}
334
                                         }}},
335
     #ejabberd_commands{name = connected_users_info,
336
                        tags = [session],
337
                        desc = "List all established sessions and their information",
338
                        module = ?MODULE, function = connected_users_info,
339
                        args = [],
340
                        result_example = [{"user1@myserver.com/tka",
341
                                            "c2s", "127.0.0.1", 42656,8, "ejabberd@localhost",
342
                                           231, <<"dnd">>, <<"tka">>, <<>>}],
343
                        result = {connected_users_info,
344
                                  {list,
345
                                   {session, {tuple,
346
                                              [{jid, string},
347
                                               {connection, string},
348
                                               {ip, string},
349
                                               {port, integer},
350
                                               {priority, integer},
351
                                               {node, string},
352
                                               {uptime, integer},
353
                                               {status, string},
354
                                               {resource, string},
355
                                               {statustext, string}
356
                                              ]}}
357
                                  }}},
358

359
     #ejabberd_commands{name = connected_users_vhost,
360
                        tags = [session],
361
                        desc = "Get the list of established sessions in a vhost",
362
                        module = ?MODULE, function = connected_users_vhost,
363
                        args_example = [<<"myexample.com">>],
364
                        args_desc = ["Server name"],
365
                        result_example = [<<"user1@myserver.com/tka">>, <<"user2@localhost/tka">>],
366
                        args = [{host, binary}],
367
                        result = {connected_users_vhost, {list, {sessions, string}}}},
368
     #ejabberd_commands{name = user_sessions_info,
369
                        tags = [session],
370
                        desc = "Get information about all sessions of a user",
371
                        module = ?MODULE, function = user_sessions_info,
372
                        args = [{user, binary}, {host, binary}],
373
                        args_example = [<<"peter">>, <<"myserver.com">>],
374
                        args_desc = ["User name", "Server name"],
375
                        result_example = [{"c2s", "127.0.0.1", 42656,8, "ejabberd@localhost",
376
                                           231, <<"dnd">>, <<"tka">>, <<>>}],
377
                        result = {sessions_info,
378
                                  {list,
379
                                   {session, {tuple,
380
                                              [{connection, string},
381
                                               {ip, string},
382
                                               {port, integer},
383
                                               {priority, integer},
384
                                               {node, string},
385
                                               {uptime, integer},
386
                                               {status, string},
387
                                               {resource, string},
388
                                               {statustext, string}
389
                                              ]}}
390
                                  }}},
391

392
     #ejabberd_commands{name = get_presence, tags = [session],
393
                        desc =
394
                            "Retrieve the resource with highest priority, "
395
                            "and its presence (show and status message) "
396
                            "for a given user.",
397
                        longdesc =
398
                            "The `jid` value contains the user JID "
399
                            "with resource.\n\nThe `show` value contains "
400
                            "the user presence flag. It can take "
401
                            "limited values:\n\n - `available`\n - `chat` "
402
                            "(Free for chat)\n - `away`\n - `dnd` (Do "
403
                            "not disturb)\n - `xa` (Not available, "
404
                            "extended away)\n - `unavailable` (Not "
405
                            "connected)\n\n`status` is a free text "
406
                            "defined by the user client.",
407
                        module = ?MODULE, function = get_presence,
408
                        args = [{user, binary}, {host, binary}],
409
                        args_rename = [{server, host}],
410
                        args_example = [<<"peter">>, <<"myexample.com">>],
411
                        args_desc = ["User name", "Server name"],
412
                        result_example = {<<"user1@myserver.com/tka">>, <<"dnd">>, <<"Busy">>},
413
                        result =
414
                            {presence,
415
                             {tuple,
416
                              [{jid, string}, {show, string},
417
                               {status, string}]}}},
418
     #ejabberd_commands{name = set_presence,
419
                        tags = [session],
420
                        desc = "Set presence of a session",
421
                        module = ?MODULE, function = set_presence,
422
                        args = [{user, binary}, {host, binary},
423
                                {resource, binary}, {type, binary},
424
                                {show, binary}, {status, binary},
425
                                {priority, binary}],
426
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"tka1">>,
427
                                        <<"available">>,<<"away">>,<<"BB">>, <<"7">>],
428
                        args_desc = ["User name", "Server name", "Resource",
429
                                        "Type: `available`, `error`, `probe`...",
430
                                        "Show: `away`, `chat`, `dnd`, `xa`.", "Status text",
431
                                        "Priority, provide this value as an integer"],
432
                        result = {res, rescode}},
433

434
     #ejabberd_commands{name = set_nickname, tags = [vcard],
435
                        desc = "Set nickname in a user's vCard",
436
                        module = ?MODULE, function = set_nickname,
437
                        args = [{user, binary}, {host, binary}, {nickname, binary}],
438
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"User 1">>],
439
                        args_desc = ["User name", "Server name", "Nickname"],
440
                        result = {res, rescode}},
441
     #ejabberd_commands{name = get_vcard, tags = [vcard],
442
                        desc = "Get content from a vCard field",
443
                        longdesc = Vcard1FieldsString ++ "\n" ++ VcardXEP,
444
                        module = ?MODULE, function = get_vcard,
445
                        args = [{user, binary}, {host, binary}, {name, binary}],
446
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"NICKNAME">>],
447
                        args_desc = ["User name", "Server name", "Field name"],
448
                        result_example = "User 1",
449
                        result_desc = "Field content",
450
                        result = {content, string}},
451
     #ejabberd_commands{name = get_vcard2, tags = [vcard],
452
                        desc = "Get content from a vCard subfield",
453
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
454
                        module = ?MODULE, function = get_vcard,
455
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}],
456
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"N">>, <<"FAMILY">>],
457
                        args_desc = ["User name", "Server name", "Field name", "Subfield name"],
458
                        result_example = "Schubert",
459
                        result_desc = "Field content",
460
                        result = {content, string}},
461
     #ejabberd_commands{name = get_vcard2_multi, tags = [vcard],
462
                        desc = "Get multiple contents from a vCard field",
463
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
464
                        module = ?MODULE, function = get_vcard_multi,
465
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}],
466
                        result = {contents, {list, {value, string}}}},
467

468
     #ejabberd_commands{name = set_vcard, tags = [vcard],
469
                        desc = "Set content in a vCard field",
470
                        longdesc = Vcard1FieldsString ++ "\n" ++ VcardXEP,
471
                        module = ?MODULE, function = set_vcard,
472
                        args = [{user, binary}, {host, binary}, {name, binary}, {content, binary}],
473
                        args_example = [<<"user1">>,<<"myserver.com">>, <<"URL">>, <<"www.example.com">>],
474
                        args_desc = ["User name", "Server name", "Field name", "Value"],
475
                        result = {res, rescode}},
476
     #ejabberd_commands{name = set_vcard2, tags = [vcard],
477
                        desc = "Set content in a vCard subfield",
478
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
479
                        module = ?MODULE, function = set_vcard,
480
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {content, binary}],
481
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"TEL">>, <<"NUMBER">>, <<"123456">>],
482
                        args_desc = ["User name", "Server name", "Field name", "Subfield name", "Value"],
483
                        result = {res, rescode}},
484
     #ejabberd_commands{name = set_vcard2_multi, tags = [vcard],
485
                        desc = "Set multiple contents in a vCard subfield",
486
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
487
                        module = ?MODULE, function = set_vcard,
488
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {contents, {list, {value, binary}}}],
489
                        result = {res, rescode}},
490

491
     #ejabberd_commands{name = add_rosteritem, tags = [roster],
492
                        desc = "Add an item to a user's roster (supports ODBC)",
493
                        longdesc = "Group can be several groups separated by `;` for example: `g1;g2;g3`",
494
                        module = ?MODULE, function = add_rosteritem,
495
                        args = [{localuser, binary}, {localhost, binary},
496
                                {user, binary}, {host, binary},
497
                                {nick, binary}, {group, binary},
498
                                {subs, binary}],
499
                        args_rename = [{localserver, localhost}, {server, host}],
500
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>,
501
                                <<"User 2">>, <<"Friends">>, <<"both">>],
502
                        args_desc = ["User name", "Server name", "Contact user name", "Contact server name",
503
                                "Nickname", "Group", "Subscription"],
504
                        result = {res, rescode}},
505
     %%{"", "subs= none, from, to or both"},
506
     %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
507
     %%{"", "will add mike@server.com to peter@localhost roster"},
508
     #ejabberd_commands{name = delete_rosteritem, tags = [roster],
509
                        desc = "Delete an item from a user's roster (supports ODBC)",
510
                        module = ?MODULE, function = delete_rosteritem,
511
                        args = [{localuser, binary}, {localhost, binary},
512
                                {user, binary}, {host, binary}],
513
                        args_rename = [{localserver, localhost}, {server, host}],
514
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>],
515
                        args_desc = ["User name", "Server name", "Contact user name", "Contact server name"],
516
                        result = {res, rescode}},
517
     #ejabberd_commands{name = process_rosteritems, tags = [roster],
518
                        desc = "List/delete rosteritems that match filter",
519
                        longdesc = "Explanation of each argument:\n"
520
                        " - action: what to do with each rosteritem that "
521
                        "matches all the filtering options\n"
522
                        " - subs: subscription type\n"
523
                        " - asks: pending subscription\n"
524
                        " - users: the JIDs of the local user\n"
525
                        " - contacts: the JIDs of the contact in the roster\n"
526
                        "\n"
527
                        " *** Mnesia: \n"
528
                        "\n"
529
                        "Allowed values in the arguments:\n"
530
                        "  ACTION = list | delete\n"
531
                        "  SUBS = SUB[:SUB]* | any\n"
532
                        "  SUB = none | from | to | both\n"
533
                        "  ASKS = ASK[:ASK]* | any\n"
534
                        "  ASK = none | out | in\n"
535
                        "  USERS = JID[:JID]* | any\n"
536
                        "  CONTACTS = JID[:JID]* | any\n"
537
                        "  JID = characters valid in a JID, and can use the "
538
                        "globs: *, ?, ! and [...]\n"
539
                        "\n"
540
                        "This example will list roster items with subscription "
541
                        "'none', 'from' or 'to' that have any ask property, of "
542
                        "local users which JID is in the virtual host "
543
                        "'example.org' and that the contact JID is either a "
544
                        "bare server name (without user part) or that has a "
545
                        "user part and the server part contains the word 'icq'"
546
                        ":\n  list none:from:to any *@example.org *:*@*icq*"
547
                        "\n\n"
548
                        " *** SQL:\n"
549
                        "\n"
550
                        "Allowed values in the arguments:\n"
551
                        "  ACTION = list | delete\n"
552
                        "  SUBS = any | none | from | to | both\n"
553
                        "  ASKS = any | none | out | in\n"
554
                        "  USERS = JID\n"
555
                        "  CONTACTS = JID\n"
556
                        "  JID = characters valid in a JID, and can use the "
557
                        "globs: _ and %\n"
558
                        "\n"
559
                        "This example will list roster items with subscription "
560
                        "'to' that have any ask property, of "
561
                        "local users which JID is in the virtual host "
562
                        "'example.org' and that the contact JID's "
563
                        "server part contains the word 'icq'"
564
                        ":\n  list to any %@example.org %@%icq%",
565
                        module = mod_roster, function = process_rosteritems,
566
                        args = [{action, string}, {subs, string},
567
                                {asks, string}, {users, string},
568
                                {contacts, string}],
569
                        result = {response,
570
                                  {list,
571
                                   {pairs, {tuple,
572
                                               [{user, string},
573
                                                {contact, string}
574
                                               ]}}
575
                                  }}},
576
     #ejabberd_commands{name = get_roster, tags = [roster],
577
                        desc = "Get list of contacts in a local user roster",
578
                        longdesc =
579
                            "Subscription can be: \"none\", \"from\", \"to\", \"both\". "
580
                            "Pending can be: \"in\", \"out\", \"none\".",
581
                        note = "improved in 23.10",
582
                        policy = user,
583
                        module = ?MODULE, function = get_roster,
584
                        args = [],
585
                        args_rename = [{server, host}],
586
                        result = {contacts, {list, {contact, {tuple, [
587
                                                                      {jid, string},
588
                                                                      {nick, string},
589
                                                                      {subscription, string},
590
                                                                      {pending, string},
591
                                                                      {groups, {list, {group, string}}}
592
                                                                     ]}}}}},
593
     #ejabberd_commands{name = push_roster, tags = [roster],
594
                        desc = "Push template roster from file to a user",
595
                        longdesc = "The text file must contain an erlang term: a list "
596
                            "of tuples with username, servername, group and nick. Example:\n"
597
                            "[{<<\"user1\">>, <<\"localhost\">>, <<\"Workers\">>, <<\"User 1\">>},\n"
598
                            " {<<\"user2\">>, <<\"localhost\">>, <<\"Workers\">>, <<\"User 2\">>}].\n"
599
                            "When using UTF8 character encoding add /utf8 to certain string. Example:\n"
600
                            "[{<<\"user2\">>, <<\"localhost\">>, <<\"Workers\"/utf8>>, <<\"User 2\"/utf8>>}].",
601
                        module = ?MODULE, function = push_roster,
602
                        args = [{file, binary}, {user, binary}, {host, binary}],
603
                        args_example = [<<"/home/ejabberd/roster.txt">>, <<"user1">>, <<"localhost">>],
604
                        args_desc = ["File path", "User name", "Server name"],
605
                        result = {res, rescode}},
606
     #ejabberd_commands{name = push_roster_all, tags = [roster],
607
                        desc = "Push template roster from file to all those users",
608
                        longdesc = "The text file must contain an erlang term: a list "
609
                            "of tuples with username, servername, group and nick. Example:\n"
610
                            "[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n"
611
                            " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].",
612
                        module = ?MODULE, function = push_roster_all,
613
                        args = [{file, binary}],
614
                        args_example = [<<"/home/ejabberd/roster.txt">>],
615
                        args_desc = ["File path"],
616
                        result = {res, rescode}},
617
     #ejabberd_commands{name = push_alltoall, tags = [roster],
618
                        desc = "Add all the users to all the users of Host in Group",
619
                        module = ?MODULE, function = push_alltoall,
620
                        args = [{host, binary}, {group, binary}],
621
                        args_example = [<<"myserver.com">>,<<"Everybody">>],
622
                        args_desc = ["Server name", "Group name"],
623
                        result = {res, rescode}},
624

625
     #ejabberd_commands{name = get_last, tags = [last],
626
                        desc = "Get last activity information",
627
                        longdesc = "Timestamp is UTC and XEP-0082 format, for example: "
628
                            "`2017-02-23T22:25:28.063062Z     ONLINE`",
629
                        module = ?MODULE, function = get_last,
630
                        args = [{user, binary}, {host, binary}],
631
                        args_example = [<<"user1">>,<<"myserver.com">>],
632
                        args_desc = ["User name", "Server name"],
633
                        result_example = {<<"2017-06-30T14:32:16.060684Z">>, "ONLINE"},
634
                        result_desc = "Last activity timestamp and status",
635
                        result = {last_activity,
636
                                  {tuple, [{timestamp, string},
637
                                           {status, string}
638
                                          ]}}},
639
     #ejabberd_commands{name = set_last, tags = [last],
640
                        desc = "Set last activity information",
641
                        longdesc = "Timestamp is the seconds since "
642
                        "`1970-01-01 00:00:00 UTC`. For example value see `date +%s`",
643
                        module = ?MODULE, function = set_last,
644
                        args = [{user, binary}, {host, binary}, {timestamp, integer}, {status, binary}],
645
                        args_example = [<<"user1">>,<<"myserver.com">>, 1500045311, <<"GoSleeping">>],
646
                        args_desc = ["User name", "Server name", "Number of seconds since epoch", "Status message"],
647
                        result = {res, rescode}},
648

649
     #ejabberd_commands{name = private_get, tags = [private],
650
                        desc = "Get some information from a user private storage",
651
                        module = ?MODULE, function = private_get,
652
                        args = [{user, binary}, {host, binary}, {element, binary}, {ns, binary}],
653
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"storage">>, <<"storage:rosternotes">>],
654
                        args_desc = ["User name", "Server name", "Element name", "Namespace"],
655
                        result = {res, string}},
656
     #ejabberd_commands{name = private_set, tags = [private],
657
                        desc = "Set to the user private storage",
658
                        module = ?MODULE, function = private_set,
659
                        args = [{user, binary}, {host, binary}, {element, binary}],
660
                        args_example = [<<"user1">>,<<"myserver.com">>,
661
                            <<"<storage xmlns='storage:rosternotes'/>">>],
662
                        args_desc = ["User name", "Server name", "XML storage element"],
663
                        result = {res, rescode}},
664

665
     #ejabberd_commands{name = srg_create, tags = [shared_roster_group],
666
                        desc = "Create a Shared Roster Group",
667
                        longdesc = "If you want to specify several group "
668
                        "identifiers in the Display argument,\n"
669
                        "put `\\ \"` around the argument and\nseparate the "
670
                        "identifiers with `\\ \\ n`\n"
671
                        "For example:\n"
672
                        "  `ejabberdctl srg_create group3 myserver.com "
673
                        "name desc \\\"group1\\\\ngroup2\\\"`",
674
                        note = "changed in 21.07",
675
                        module = ?MODULE, function = srg_create,
676
                        args = [{group, binary}, {host, binary},
677
                                {label, binary}, {description, binary}, {display, binary}],
678
                        args_rename = [{name, label}],
679
                        args_example = [<<"group3">>, <<"myserver.com">>, <<"Group3">>,
680
                                <<"Third group">>, <<"group1\\\\ngroup2">>],
681
                        args_desc = ["Group identifier", "Group server name", "Group name",
682
                                "Group description", "Groups to display"],
683
                        result = {res, rescode}},
684
     #ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
685
                        desc = "Delete a Shared Roster Group",
686
                        module = ?MODULE, function = srg_delete,
687
                        args = [{group, binary}, {host, binary}],
688
                        args_example = [<<"group3">>, <<"myserver.com">>],
689
                        args_desc = ["Group identifier", "Group server name"],
690
                        result = {res, rescode}},
691
     #ejabberd_commands{name = srg_list, tags = [shared_roster_group],
692
                        desc = "List the Shared Roster Groups in Host",
693
                        module = ?MODULE, function = srg_list,
694
                        args = [{host, binary}],
695
                        args_example = [<<"myserver.com">>],
696
                        args_desc = ["Server name"],
697
                        result_example = [<<"group1">>, <<"group2">>],
698
                        result_desc = "List of group identifiers",
699
                        result = {groups, {list, {id, string}}}},
700
     #ejabberd_commands{name = srg_get_info, tags = [shared_roster_group],
701
                        desc = "Get info of a Shared Roster Group",
702
                        module = ?MODULE, function = srg_get_info,
703
                        args = [{group, binary}, {host, binary}],
704
                        args_example = [<<"group3">>, <<"myserver.com">>],
705
                        args_desc = ["Group identifier", "Group server name"],
706
                        result_example = [{<<"name">>, "Group 3"}, {<<"displayed_groups">>, "group1"}],
707
                        result_desc = "List of group information, as key and value",
708
                        result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
709
     #ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
710
                        desc = "Get members of a Shared Roster Group",
711
                        module = ?MODULE, function = srg_get_members,
712
                        args = [{group, binary}, {host, binary}],
713
                        args_example = [<<"group3">>, <<"myserver.com">>],
714
                        args_desc = ["Group identifier", "Group server name"],
715
                        result_example = [<<"user1@localhost">>, <<"user2@localhost">>],
716
                        result_desc = "List of group identifiers",
717
                        result = {members, {list, {member, string}}}},
718
     #ejabberd_commands{name = srg_user_add, tags = [shared_roster_group],
719
                        desc = "Add the JID user@host to the Shared Roster Group",
720
                        module = ?MODULE, function = srg_user_add,
721
                        args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}],
722
                        args_example = [<<"user1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
723
                        args_desc = ["Username", "User server name", "Group identifier", "Group server name"],
724
                        result = {res, rescode}},
725
     #ejabberd_commands{name = srg_user_del, tags = [shared_roster_group],
726
                        desc = "Delete this JID user@host from the Shared Roster Group",
727
                        module = ?MODULE, function = srg_user_del,
728
                        args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}],
729
                        args_example = [<<"user1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
730
                        args_desc = ["Username", "User server name", "Group identifier", "Group server name"],
731
                        result = {res, rescode}},
732

733
     #ejabberd_commands{name = get_offline_count,
734
                        tags = [offline],
735
                        desc = "Get the number of unread offline messages",
736
                        policy = user,
737
                        module = mod_offline, function = count_offline_messages,
738
                        args = [],
739
                        args_rename = [{server, host}],
740
                        result_example = 5,
741
                        result_desc = "Number",
742
                        result = {value, integer}},
743
     #ejabberd_commands{name = send_message, tags = [stanza],
744
                        desc = "Send a message to a local or remote bare of full JID",
745
                        longdesc = "When sending a groupchat message to a MUC room, "
746
                        "`from` must be the full JID of a room occupant, "
747
                        "or the bare JID of a MUC service admin, "
748
                        "or the bare JID of a MUC/Sub subscribed user.",
749
                        module = ?MODULE, function = send_message,
750
                        args = [{type, binary}, {from, binary}, {to, binary},
751
                                {subject, binary}, {body, binary}],
752
                        args_example = [<<"headline">>, <<"admin@localhost">>, <<"user1@localhost">>,
753
                                <<"Restart">>, <<"In 5 minutes">>],
754
                        args_desc = ["Message type: `normal`, `chat`, `headline`, `groupchat`", "Sender JID",
755
                                "Receiver JID", "Subject, or empty string", "Body"],
756
                        result = {res, rescode}},
757
     #ejabberd_commands{name = send_stanza_c2s, tags = [stanza],
758
                        desc = "Send a stanza from an existing C2S session",
759
                        longdesc = "`user`@`host`/`resource` must be an existing C2S session."
760
                        " As an alternative, use http://./#send-stanza[send_stanza] instead.",
761
                        module = ?MODULE, function = send_stanza_c2s,
762
                        args = [{user, binary}, {host, binary}, {resource, binary}, {stanza, binary}],
763
                        args_example = [<<"admin">>, <<"myserver.com">>, <<"bot">>,
764
                                <<"<message to='user1@localhost'><ext attr='value'/></message>">>],
765
                        args_desc = ["Username", "Server name", "Resource", "Stanza"],
766
                        result = {res, rescode}},
767
     #ejabberd_commands{name = send_stanza, tags = [stanza],
768
                        desc = "Send a stanza; provide From JID and valid To JID",
769
                        module = ?MODULE, function = send_stanza,
770
                        args = [{from, binary}, {to, binary}, {stanza, binary}],
771
                        args_example = [<<"admin@localhost">>, <<"user1@localhost">>,
772
                                <<"<message><ext attr='value'/></message>">>],
773
                        args_desc = ["Sender JID", "Destination JID", "Stanza"],
774
                        result = {res, rescode}},
775
     #ejabberd_commands{name = privacy_set, tags = [stanza],
776
                        desc = "Send a IQ set privacy stanza for a local account",
777
                        module = ?MODULE, function = privacy_set,
778
                        args = [{user, binary}, {host, binary}, {xmlquery, binary}],
779
                        args_example = [<<"user1">>, <<"myserver.com">>,
780
                                <<"<query xmlns='jabber:iq:privacy'>...">>],
781
                        args_desc = ["Username", "Server name", "Query XML element"],
782
                        result = {res, rescode}},
783

784
     #ejabberd_commands{name = stats, tags = [statistics],
785
                        desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds processes",
786
                        policy = admin,
787
                        module = ?MODULE, function = stats,
788
                        args = [{name, binary}],
789
                        args_example = [<<"registeredusers">>],
790
                        args_desc = ["Statistic name"],
791
                        result_example = 6,
792
                        result_desc = "Integer statistic value",
793
                        result = {stat, integer}},
794
     #ejabberd_commands{name = stats_host, tags = [statistics],
795
                        desc = "Get statistical value for this host: registeredusers onlineusers",
796
                        policy = admin,
797
                        module = ?MODULE, function = stats,
798
                        args = [{name, binary}, {host, binary}],
799
                        args_example = [<<"registeredusers">>, <<"example.com">>],
800
                        args_desc = ["Statistic name", "Server JID"],
801
                        result_example = 6,
802
                        result_desc = "Integer statistic value",
803
                        result = {stat, integer}}
804
    ].
805

806

807
%%%
808
%%% Adminsys
809
%%%
810

811
compile(File) ->
812
    Ebin = filename:join(code:lib_dir(ejabberd), "ebin"),
×
813
    case ext_mod:compile_erlang_file(Ebin, File) of
×
814
        {ok, Module} ->
815
            code:purge(Module),
×
816
            code:load_file(Module),
×
817
            ok;
×
818
        _ ->
819
            error
×
820
    end.
821

822
get_cookie() ->
823
    atom_to_list(erlang:get_cookie()).
×
824

825
restart_module(Host, Module) when is_binary(Module) ->
826
    restart_module(Host, misc:binary_to_atom(Module));
×
827
restart_module(Host, Module) when is_atom(Module) ->
828
    case gen_mod:is_loaded(Host, Module) of
×
829
        false ->
830
            % not a running module, force code reload anyway
831
            code:purge(Module),
×
832
            code:delete(Module),
×
833
            code:load_file(Module),
×
834
            1;
×
835
        true ->
836
            gen_mod:stop_module(Host, Module),
×
837
            case code:soft_purge(Module) of
×
838
                true ->
839
                    code:delete(Module),
×
840
                    code:load_file(Module),
×
841
                    gen_mod:start_module(Host, Module),
×
842
                    0;
×
843
                false ->
844
                    gen_mod:start_module(Host, Module),
×
845
                    2
×
846
            end
847
    end.
848

849
%%%
850
%%% Accounts
851
%%%
852

853
set_password(User, Host, Password) ->
854
    Fun = fun () -> ejabberd_auth:set_password(User, Host, Password) end,
×
855
    user_action(User, Host, Fun, ok).
×
856

857
check_password(User, Host, Password) ->
858
    ejabberd_auth:check_password(User, <<>>, Host, Password).
×
859

860
%% Copied some code from ejabberd_commands.erl
861
check_password_hash(User, Host, PasswordHash, HashMethod) ->
862
    AccountPass = ejabberd_auth:get_password_s(User, Host),
×
863
    Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
×
864
                   proplists:get_value(hashs, crypto:supports())),
865
    MethodAllowed = lists:member(HashMethod, Methods),
×
866
    AccountPassHash = case {AccountPass, MethodAllowed} of
×
867
                          {A, _} when is_tuple(A) -> scrammed;
×
868
                          {_, true} -> get_hash(AccountPass, HashMethod);
×
869
                          {_, false} ->
870
                              ?ERROR_MSG("Check_password_hash called "
×
871
                                         "with hash method: ~p", [HashMethod]),
×
872
                              undefined
×
873
                      end,
874
    case AccountPassHash of
×
875
        scrammed ->
876
            ?ERROR_MSG("Passwords are scrammed, and check_password_hash cannot work.", []),
×
877
            throw(passwords_scrammed_command_cannot_work);
×
878
        undefined -> throw(unkown_hash_method);
×
879
        PasswordHash -> ok;
×
880
        _ -> false
×
881
    end.
882

883
get_hash(AccountPass, Method) ->
884
    iolist_to_binary([io_lib:format("~2.16.0B", [X])
×
885
          || X <- binary_to_list(
×
886
              crypto:hash(binary_to_atom(Method, latin1), AccountPass))]).
887

888
delete_old_users(Days) ->
889
    %% Get the list of registered users
890
    Users = ejabberd_auth:get_users(),
×
891

892
    {removed, N, UR} = delete_old_users(Days, Users),
×
893
    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
×
894

895
delete_old_users_vhost(Host, Days) ->
896
    %% Get the list of registered users
897
    Users = ejabberd_auth:get_users(Host),
×
898

899
    {removed, N, UR} = delete_old_users(Days, Users),
×
900
    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
×
901

902
delete_old_users(Days, Users) ->
903
    SecOlder = Days*24*60*60,
×
904
    TimeStamp_now = erlang:system_time(second),
×
905
    TimeStamp_oldest = TimeStamp_now - SecOlder,
×
906
    F = fun({LUser, LServer}) ->
×
907
            case catch delete_or_not(LUser, LServer, TimeStamp_oldest) of
×
908
                true ->
909
                    ejabberd_auth:remove_user(LUser, LServer),
×
910
                    true;
×
911
                _ ->
912
                    false
×
913
            end
914
        end,
915
    Users_removed = lists:filter(F, Users),
×
916
    {removed, length(Users_removed), Users_removed}.
×
917

918
delete_or_not(LUser, LServer, TimeStamp_oldest) ->
919
    deny = acl:match_rule(LServer, protect_old_users, jid:make(LUser, LServer)),
×
920
    [] = ejabberd_sm:get_user_resources(LUser, LServer),
×
921
    case mod_last:get_last_info(LUser, LServer) of
×
922
        {ok, TimeStamp, _Status} ->
923
            if TimeStamp_oldest < TimeStamp ->
×
924
                    false;
×
925
                true ->
926
                    true
×
927
            end;
928
        not_found ->
929
            true
×
930
    end.
931

932
%%
933
%% Ban account
934

935
ban_account(User, Host, ReasonText) ->
936
    Reason = prepare_reason(ReasonText),
×
937
    kick_sessions(User, Host, Reason),
×
938
    set_random_password(User, Host, Reason),
×
939
    ok.
×
940

941
kick_sessions(User, Server, Reason) ->
942
    lists:map(
×
943
      fun(Resource) ->
944
              kick_this_session(User, Server, Resource, Reason)
×
945
      end,
946
      ejabberd_sm:get_user_resources(User, Server)).
947

948
set_random_password(User, Server, Reason) ->
949
    NewPass = build_random_password(Reason),
×
950
    set_password_auth(User, Server, NewPass).
×
951

952
build_random_password(Reason) ->
953
    {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:universal_time(),
×
954
    Date = str:format("~4..0B~2..0B~2..0BT~2..0B:~2..0B:~2..0B",
×
955
                      [Year, Month, Day, Hour, Minute, Second]),
956
    RandomString = p1_rand:get_string(),
×
957
    <<"BANNED_ACCOUNT--", Date/binary, "--", RandomString/binary, "--", Reason/binary>>.
×
958

959
set_password_auth(User, Server, Password) ->
960
    ok = ejabberd_auth:set_password(User, Server, Password).
×
961

962
prepare_reason([]) ->
963
    <<"Kicked by administrator">>;
×
964
prepare_reason([Reason]) ->
965
    Reason;
×
966
prepare_reason(Reason) when is_binary(Reason) ->
967
    Reason.
×
968

969
%%%
970
%%% Sessions
971
%%%
972

973
num_resources(User, Host) ->
974
    length(ejabberd_sm:get_user_resources(User, Host)).
×
975

976
resource_num(User, Host, Num) ->
977
    Resources = ejabberd_sm:get_user_resources(User, Host),
×
978
    case (0<Num) and (Num=<length(Resources)) of
×
979
        true ->
980
            lists:nth(Num, Resources);
×
981
        false ->
982
            throw({bad_argument,
×
983
                   lists:flatten(io_lib:format("Wrong resource number: ~p", [Num]))})
984
    end.
985

986
kick_session(User, Server, Resource, ReasonText) ->
987
    kick_this_session(User, Server, Resource, prepare_reason(ReasonText)),
×
988
    ok.
×
989

990
kick_this_session(User, Server, Resource, Reason) ->
991
    ejabberd_sm:route(jid:make(User, Server, Resource),
×
992
                      {exit, Reason}).
993

994
status_num(Host, Status) ->
995
    length(get_status_list(Host, Status)).
×
996
status_num(Status) ->
997
    status_num(<<"all">>, Status).
×
998
status_list(Host, Status) ->
999
    Res = get_status_list(Host, Status),
×
1000
    [{U, S, R, num_prio(P), St} || {U, S, R, P, St} <- Res].
×
1001
status_list(Status) ->
1002
    status_list(<<"all">>, Status).
×
1003

1004

1005
get_status_list(Host, Status_required) ->
1006
    %% Get list of all logged users
1007
    Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
×
1008
    %% Reformat the list
1009
    Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
×
1010
    Fhost = case Host of
×
1011
                <<"all">> ->
1012
                    %% All hosts are requested, so don't filter at all
1013
                    fun(_, _) -> true end;
×
1014
                _ ->
1015
                    %% Filter the list, only Host is interesting
1016
                    fun(A, B) -> A == B end
×
1017
            end,
1018
    Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
×
1019
    %% For each Pid, get its presence
1020
    Sessions4 = [ {catch get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
×
1021
    %% Filter by status
1022
    Fstatus = case Status_required of
×
1023
                  <<"all">> ->
1024
                      fun(_, _) -> true end;
×
1025
                  _ ->
1026
                      fun(A, B) -> A == B end
×
1027
              end,
1028
    [{User, Server, Resource, num_prio(Priority), stringize(Status_text)}
×
1029
     || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
×
1030
        apply(Fstatus, [Status, Status_required])].
×
1031

1032
connected_users_info() ->
1033
    lists:filtermap(
×
1034
      fun({U, S, R}) ->
1035
            case user_session_info(U, S, R) of
×
1036
                offline ->
1037
                    false;
×
1038
                Info ->
1039
                    Jid = jid:encode(jid:make(U, S, R)),
×
1040
                    {true, erlang:insert_element(1, Info, Jid)}
×
1041
            end
1042
      end,
1043
      ejabberd_sm:dirty_get_sessions_list()).
1044

1045
connected_users_vhost(Host) ->
1046
    USRs = ejabberd_sm:get_vh_session_list(Host),
×
1047
    [ jid:encode(jid:make(USR)) || USR <- USRs].
×
1048

1049
%% Make string more print-friendly
1050
stringize(String) ->
1051
    %% Replace newline characters with other code
1052
    ejabberd_regexp:greplace(String, <<"\n">>, <<"\\n">>).
×
1053

1054
get_presence(Pid) ->
1055
    try get_presence2(Pid) of
×
1056
        {_, _, _, _} = Res ->
1057
            Res
×
1058
    catch
1059
        _:_ -> {<<"">>, <<"">>, <<"offline">>, <<"">>}
×
1060
    end.
1061
get_presence2(Pid) ->
1062
    Pres = #presence{from = From} = ejabberd_c2s:get_presence(Pid),
×
1063
    Show = case Pres of
×
1064
               #presence{type = unavailable} -> <<"unavailable">>;
×
1065
               #presence{show = undefined} -> <<"available">>;
×
1066
               #presence{show = S} -> atom_to_binary(S, utf8)
×
1067
           end,
1068
    Status = xmpp:get_text(Pres#presence.status),
×
1069
    {From#jid.user, From#jid.resource, Show, Status}.
×
1070

1071
get_presence(U, S) ->
1072
    Pids = [ejabberd_sm:get_session_pid(U, S, R)
×
1073
            || R <- ejabberd_sm:get_user_resources(U, S)],
×
1074
    OnlinePids = [Pid || Pid <- Pids, Pid=/=none],
×
1075
    case OnlinePids of
×
1076
        [] ->
1077
            {jid:encode({U, S, <<>>}), <<"unavailable">>, <<"">>};
×
1078
        [SessionPid|_] ->
1079
            {_User, Resource, Show, Status} = get_presence(SessionPid),
×
1080
            FullJID = jid:encode({U, S, Resource}),
×
1081
            {FullJID, Show, Status}
×
1082
    end.
1083

1084
set_presence(User, Host, Resource, Type, Show, Status, Priority)
1085
        when is_integer(Priority) ->
1086
    BPriority = integer_to_binary(Priority),
×
1087
    set_presence(User, Host, Resource, Type, Show, Status, BPriority);
×
1088
set_presence(User, Host, Resource, Type, Show, Status, Priority0) ->
1089
    Priority = if is_integer(Priority0) -> Priority0;
×
1090
                  true -> binary_to_integer(Priority0)
×
1091
               end,
1092
    Pres = #presence{
×
1093
        from = jid:make(User, Host, Resource),
1094
        to = jid:make(User, Host),
1095
        type = misc:binary_to_atom(Type),
1096
        status = xmpp:mk_text(Status),
1097
        show = misc:binary_to_atom(Show),
1098
        priority = Priority,
1099
        sub_els = []},
1100
    Ref = ejabberd_sm:get_session_pid(User, Host, Resource),
×
1101
    ejabberd_c2s:set_presence(Ref, Pres).
×
1102

1103
user_sessions_info(User, Host) ->
1104
    lists:filtermap(fun(Resource) ->
×
1105
                            case user_session_info(User, Host, Resource) of
×
1106
                                offline -> false;
×
1107
                                Info -> {true, Info}
×
1108
                            end
1109
                    end, ejabberd_sm:get_user_resources(User, Host)).
1110

1111
user_session_info(User, Host, Resource) ->
1112
    CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
×
1113
    case ejabberd_sm:get_user_info(User, Host, Resource) of
×
1114
        offline ->
1115
            offline;
×
1116
        Info ->
1117
            Now = proplists:get_value(ts, Info),
×
1118
            Pid = proplists:get_value(pid, Info),
×
1119
            {_U, _Resource, Status, StatusText} = get_presence(Pid),
×
1120
            Priority = proplists:get_value(priority, Info),
×
1121
            Conn = proplists:get_value(conn, Info),
×
1122
            {Ip, Port} = proplists:get_value(ip, Info),
×
1123
            IPS = inet_parse:ntoa(Ip),
×
1124
            NodeS = atom_to_list(node(Pid)),
×
1125
            Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
×
1126
                                    calendar:now_to_local_time(Now)),
1127
            {atom_to_list(Conn), IPS, Port, num_prio(Priority), NodeS, Uptime, Status, Resource, StatusText}
×
1128
    end.
1129

1130

1131
%%%
1132
%%% Vcard
1133
%%%
1134

1135
set_nickname(User, Host, Nickname) ->
1136
    VCard = xmpp:encode(#vcard_temp{nickname = Nickname}),
×
1137
    case mod_vcard:set_vcard(User, jid:nameprep(Host), VCard) of
×
1138
        {error, badarg} ->
1139
            error;
×
1140
        ok ->
1141
            ok
×
1142
    end.
1143

1144
get_vcard(User, Host, Name) ->
1145
    [Res | _] = get_vcard_content(User, Host, [Name]),
×
1146
    Res.
×
1147

1148
get_vcard(User, Host, Name, Subname) ->
1149
    [Res | _] = get_vcard_content(User, Host, [Name, Subname]),
×
1150
    Res.
×
1151

1152
get_vcard_multi(User, Host, Name, Subname) ->
1153
    get_vcard_content(User, Host, [Name, Subname]).
×
1154

1155
set_vcard(User, Host, Name, SomeContent) ->
1156
    set_vcard_content(User, Host, [Name], SomeContent).
×
1157

1158
set_vcard(User, Host, Name, Subname, SomeContent) ->
1159
    set_vcard_content(User, Host, [Name, Subname], SomeContent).
×
1160

1161
%%
1162
%% Room vcard
1163

1164
is_muc_service(Domain) ->
1165
    try mod_muc_admin:get_room_serverhost(Domain) of
×
1166
        Domain -> false;
×
1167
        Service when is_binary(Service) -> true
×
1168
    catch _:{unregistered_route, _} ->
1169
            throw(error_wrong_hostname)
×
1170
    end.
1171

1172
get_room_vcard(Name, Service) ->
1173
    case mod_muc_admin:get_room_options(Name, Service) of
×
1174
        [] ->
1175
            throw(error_no_vcard_found);
×
1176
        Opts ->
1177
            case lists:keyfind(<<"vcard">>, 1, Opts) of
×
1178
                false ->
1179
                    throw(error_no_vcard_found);
×
1180
                {_, VCardRaw} ->
1181
                    [fxml_stream:parse_element(VCardRaw)]
×
1182
            end
1183
    end.
1184

1185
%%
1186
%% Internal vcard
1187

1188
get_vcard_content(User, Server, Data) ->
1189
    case get_vcard_element(User, Server) of
×
1190
        [El|_] ->
1191
            case get_vcard(Data, El) of
×
1192
                [false] -> throw(error_no_value_found_in_vcard);
×
1193
                ElemList -> ?DEBUG("ELS ~p", [ElemList]), [fxml:get_tag_cdata(Elem) || Elem <- ElemList]
×
1194
            end;
1195
        [] ->
1196
            throw(error_no_vcard_found);
×
1197
        error ->
1198
            throw(database_failure)
×
1199
    end.
1200

1201
get_vcard_element(User, Server) ->
1202
    case is_muc_service(Server) of
×
1203
        true ->
1204
           get_room_vcard(User, Server);
×
1205
        false ->
1206
           mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server))
×
1207
    end.
1208

1209
get_vcard([<<"TEL">>, TelType], {_, _, _, OldEls}) ->
1210
    {TakenEl, _NewEls} = take_vcard_tel(TelType, OldEls, [], not_found),
×
1211
    [TakenEl];
×
1212

1213
get_vcard([Data1, Data2], A1) ->
1214
    case get_subtag(A1, Data1) of
×
1215
        [false] -> [false];
×
1216
        A2List ->
1217
            lists:flatten([get_vcard([Data2], A2) || A2 <- A2List])
×
1218
    end;
1219

1220
get_vcard([Data], A1) ->
1221
    get_subtag(A1, Data).
×
1222

1223
get_subtag(Xmlelement, Name) ->
1224
    [fxml:get_subtag(Xmlelement, Name)].
×
1225

1226
set_vcard_content(User, Server, Data, SomeContent) ->
1227
    ContentList = case SomeContent of
×
1228
        [Bin | _] when is_binary(Bin) -> SomeContent;
×
1229
        Bin when is_binary(Bin) -> [SomeContent]
×
1230
    end,
1231
    %% Get old vcard
1232
    A4 = case mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server)) of
×
1233
             [A1] ->
1234
                 {_, _, _, A2} = A1,
×
1235
                 update_vcard_els(Data, ContentList, A2);
×
1236
             [] ->
1237
                 update_vcard_els(Data, ContentList, []);
×
1238
             error ->
1239
                 throw(database_failure)
×
1240
         end,
1241
    %% Build new vcard
1242
    SubEl = {xmlel, <<"vCard">>, [{<<"xmlns">>,<<"vcard-temp">>}], A4},
×
1243
    mod_vcard:set_vcard(User, jid:nameprep(Server), SubEl).
×
1244

1245
take_vcard_tel(TelType, [{xmlel, <<"TEL">>, _, SubEls}=OldEl | OldEls], NewEls, Taken) ->
1246
    {Taken2, NewEls2} = case lists:keymember(TelType, 2, SubEls) of
×
1247
        true -> {fxml:get_subtag(OldEl, <<"NUMBER">>), NewEls};
×
1248
        false -> {Taken, [OldEl | NewEls]}
×
1249
    end,
1250
    take_vcard_tel(TelType, OldEls, NewEls2, Taken2);
×
1251
take_vcard_tel(TelType, [OldEl | OldEls], NewEls, Taken) ->
1252
    take_vcard_tel(TelType, OldEls, [OldEl | NewEls], Taken);
×
1253
take_vcard_tel(_TelType, [], NewEls, Taken) ->
1254
    {Taken, NewEls}.
×
1255

1256
update_vcard_els([<<"TEL">>, TelType], [TelValue], OldEls) ->
1257
    {_, NewEls} = take_vcard_tel(TelType, OldEls, [], not_found),
×
1258
    NewEl = {xmlel,<<"TEL">>,[],
×
1259
             [{xmlel,TelType,[],[]},
1260
              {xmlel,<<"NUMBER">>,[],[{xmlcdata,TelValue}]}]},
1261
    [NewEl | NewEls];
×
1262

1263
update_vcard_els(Data, ContentList, Els1) ->
1264
    Els2 = lists:keysort(2, Els1),
×
1265
    [Data1 | Data2] = Data,
×
1266
    NewEls = case Data2 of
×
1267
                [] ->
1268
                    [{xmlel, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList];
×
1269
                [D2] ->
1270
                    OldEl = case lists:keysearch(Data1, 2, Els2) of
×
1271
                                {value, A} -> A;
×
1272
                                false -> {xmlel, Data1, [], []}
×
1273
                            end,
1274
                    {xmlel, _, _, ContentOld1} = OldEl,
×
1275
                    Content2 = [{xmlel, D2, [], [{xmlcdata,Content}]} || Content <- ContentList],
×
1276
                    ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2],
×
1277
                    ContentOld3 = lists:keysort(2, ContentOld2),
×
1278
                    ContentNew = lists:keymerge(2, Content2, ContentOld3),
×
1279
                    [{xmlel, Data1, [], ContentNew}]
×
1280
            end,
1281
    Els3 = lists:keydelete(Data1, 2, Els2),
×
1282
    lists:keymerge(2, NewEls, Els3).
×
1283

1284

1285
%%%
1286
%%% Roster
1287
%%%
1288

1289
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) ->
1290
    case {jid:make(LocalUser, LocalServer), jid:make(User, Server)} of
×
1291
        {error, _} ->
1292
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1293
        {_, error} ->
1294
            throw({error, "Invalid 'user'/'server'"});
×
1295
        {Jid, _Jid2} ->
1296
            RosterItem = build_roster_item(User, Server, {add, Nick, Subs, Group}),
×
1297
            case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of
×
1298
                ok -> ok;
×
1299
                _ -> error
×
1300
            end
1301
    end.
1302

1303
subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) ->
1304
    case {jid:make(LU, LS), jid:make(User, Server)} of
×
1305
        {error, _} ->
1306
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1307
        {_, error} ->
1308
            throw({error, "Invalid 'user'/'server'"});
×
1309
        {_Jid, _Jid2} ->
1310
            ItemEl = build_roster_item(User, Server, {add, Nick, Subscription, Group}),
×
1311
            mod_roster:set_items(LU, LS, #roster_query{items = [ItemEl]})
×
1312
    end.
1313

1314
delete_rosteritem(LocalUser, LocalServer, User, Server) ->
1315
    case {jid:make(LocalUser, LocalServer), jid:make(User, Server)} of
×
1316
        {error, _} ->
1317
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1318
        {_, error} ->
1319
            throw({error, "Invalid 'user'/'server'"});
×
1320
        {Jid, _Jid2} ->
1321
            RosterItem = build_roster_item(User, Server, remove),
×
1322
            case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of
×
1323
                ok -> ok;
×
1324
                _ -> error
×
1325
            end
1326
    end.
1327

1328
%% -----------------------------
1329
%% Get Roster
1330
%% -----------------------------
1331

1332
get_roster(User, Server) ->
1333
    case jid:make(User, Server) of
×
1334
        error ->
1335
            throw({error, "Invalid 'user'/'server'"});
×
1336
        #jid{luser = U, lserver = S} ->
1337
            Items = ejabberd_hooks:run_fold(roster_get, S, [], [{U, S}]),
×
1338
            make_roster_xmlrpc(Items)
×
1339
    end.
1340

1341
make_roster_xmlrpc(Roster) ->
1342
    lists:map(
×
1343
      fun(#roster_item{jid = JID, name = Nick, subscription = Sub, ask = Ask, groups = Groups}) ->
1344
              JIDS = jid:encode(JID),
×
1345
              Subs = atom_to_list(Sub),
×
1346
              Asks = atom_to_list(Ask),
×
1347
              {JIDS, Nick, Subs, Asks, Groups}
×
1348
      end,
1349
      Roster).
1350

1351
%%-----------------------------
1352
%% Push Roster from file
1353
%%-----------------------------
1354

1355
push_roster(File, User, Server) ->
1356
    {ok, [Roster]} = file:consult(File),
×
1357
    subscribe_roster({User, Server, <<>>, User}, Roster).
×
1358

1359
push_roster_all(File) ->
1360
    {ok, [Roster]} = file:consult(File),
×
1361
    subscribe_all(Roster).
×
1362

1363
subscribe_all(Roster) ->
1364
    subscribe_all(Roster, Roster).
×
1365
subscribe_all([], _) ->
1366
    ok;
×
1367
subscribe_all([User1 | Users], Roster) ->
1368
    subscribe_roster(User1, Roster),
×
1369
    subscribe_all(Users, Roster).
×
1370

1371
subscribe_roster(_, []) ->
1372
    ok;
×
1373
%% Do not subscribe a user to itself
1374
subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
1375
    subscribe_roster({Name, Server, Group, Nick}, Roster);
×
1376
%% Subscribe Name2 to Name1
1377
subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
1378
    subscribe(iolist_to_binary(Name1), iolist_to_binary(Server1), iolist_to_binary(Name2), iolist_to_binary(Server2),
×
1379
        iolist_to_binary(Nick2), iolist_to_binary(Group2), <<"both">>, []),
1380
    subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
×
1381

1382
push_alltoall(S, G) ->
1383
    Users = ejabberd_auth:get_users(S),
×
1384
    Users2 = build_list_users(G, Users, []),
×
1385
    subscribe_all(Users2),
×
1386
    ok.
×
1387

1388
build_list_users(_Group, [], Res) ->
1389
    Res;
×
1390
build_list_users(Group, [{User, Server}|Users], Res) ->
1391
    build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
×
1392

1393
%% @spec(LU, LS, U, S, Action) -> ok
1394
%%       Action = {add, Nick, Subs, Group} | remove
1395
%% @doc Push to the roster of account LU@LS the contact U@S.
1396
%% The specific action to perform is defined in Action.
1397
push_roster_item(LU, LS, U, S, Action) ->
1398
    lists:foreach(fun(R) ->
×
1399
                          push_roster_item(LU, LS, R, U, S, Action)
×
1400
                  end, ejabberd_sm:get_user_resources(LU, LS)).
1401

1402
push_roster_item(LU, LS, R, U, S, Action) ->
1403
    LJID = jid:make(LU, LS, R),
×
1404
    BroadcastEl = build_broadcast(U, S, Action),
×
1405
    ejabberd_sm:route(LJID, BroadcastEl),
×
1406
    Item = build_roster_item(U, S, Action),
×
1407
    ResIQ = build_iq_roster_push(Item),
×
1408
    ejabberd_router:route(
×
1409
      xmpp:set_from_to(ResIQ, jid:remove_resource(LJID), LJID)).
1410

1411
build_roster_item(U, S, {add, Nick, Subs, Group}) ->
1412
    Groups = binary:split(Group,<<";">>, [global, trim]),
×
1413
    #roster_item{jid = jid:make(U, S),
×
1414
                 name = Nick,
1415
                 subscription = misc:binary_to_atom(Subs),
1416
                 groups = Groups};
1417
build_roster_item(U, S, remove) ->
1418
    #roster_item{jid = jid:make(U, S), subscription = remove}.
×
1419

1420
build_iq_roster_push(Item) ->
1421
    #iq{type = set, id = <<"push">>,
×
1422
        sub_els = [#roster_query{items = [Item]}]}.
1423

1424
build_broadcast(U, S, {add, _Nick, Subs, _Group}) ->
1425
    build_broadcast(U, S, list_to_atom(binary_to_list(Subs)));
×
1426
build_broadcast(U, S, remove) ->
1427
    build_broadcast(U, S, none);
×
1428
%% @spec (U::binary(), S::binary(), Subs::atom()) -> any()
1429
%% Subs = both | from | to | none
1430
build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) ->
1431
    {item, {U, S, <<>>}, SubsAtom}.
×
1432

1433
%%%
1434
%%% Last Activity
1435
%%%
1436

1437
get_last(User, Server) ->
1438
    {Now, Status} = case ejabberd_sm:get_user_resources(User, Server) of
×
1439
        [] ->
1440
            case mod_last:get_last_info(User, Server) of
×
1441
                not_found ->
1442
                    {erlang:timestamp(), "NOT FOUND"};
×
1443
                {ok, Shift, Status1} ->
1444
                    {{Shift div 1000000, Shift rem 1000000, 0}, Status1}
×
1445
            end;
1446
        _ ->
1447
            {erlang:timestamp(), "ONLINE"}
×
1448
    end,
1449
    {xmpp_util:encode_timestamp(Now), Status}.
×
1450

1451
set_last(User, Server, Timestamp, Status) ->
1452
    case mod_last:store_last_info(User, Server, Timestamp, Status) of
×
1453
        {ok, _} -> ok;
×
1454
        Error -> Error
×
1455
    end.
1456

1457
%%%
1458
%%% Private Storage
1459
%%%
1460

1461
%% Example usage:
1462
%% $ ejabberdctl private_set badlop localhost "\<aa\ xmlns=\'bb\'\>Cluth\</aa\>"
1463
%% $ ejabberdctl private_get badlop localhost aa bb
1464
%% <aa xmlns='bb'>Cluth</aa>
1465

1466
private_get(Username, Host, Element, Ns) ->
1467
    ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]},
×
1468
    Els = mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host),
×
1469
                               [{Ns, ElementXml}]),
1470
    binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))).
×
1471

1472
private_set(Username, Host, ElementString) ->
1473
    case fxml_stream:parse_element(ElementString) of
×
1474
        {error, Error} ->
1475
            io:format("Error found parsing the element:~n  ~p~nError: ~p~n",
×
1476
                      [ElementString, Error]),
1477
            error;
×
1478
        Xml ->
1479
            private_set2(Username, Host, Xml)
×
1480
    end.
1481

1482
private_set2(Username, Host, Xml) ->
1483
    NS = fxml:get_tag_attr_s(<<"xmlns">>, Xml),
×
1484
    JID = jid:make(Username, Host),
×
1485
    mod_private:set_data(JID, [{NS, Xml}]).
×
1486

1487
%%%
1488
%%% Shared Roster Groups
1489
%%%
1490

1491
srg_create(Group, Host, Label, Description, Display) ->
1492
    DisplayList = case Display of
×
1493
        <<>> -> [];
×
1494
        _ -> ejabberd_regexp:split(Display, <<"\\\\n">>)
×
1495
    end,
1496
    Opts = [{label, Label},
×
1497
            {displayed_groups, DisplayList},
1498
            {description, Description}],
1499
    {atomic, _} = mod_shared_roster:create_group(Host, Group, Opts),
×
1500
    ok.
×
1501

1502
srg_delete(Group, Host) ->
1503
    {atomic, _} = mod_shared_roster:delete_group(Host, Group),
×
1504
    ok.
×
1505

1506
srg_list(Host) ->
1507
    lists:sort(mod_shared_roster:list_groups(Host)).
×
1508

1509
srg_get_info(Group, Host) ->
1510
    Opts = case mod_shared_roster:get_group_opts(Host,Group) of
×
1511
        Os when is_list(Os) -> Os;
×
1512
        error -> []
×
1513
    end,
1514
    [{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
×
1515

1516
to_list([]) -> [];
×
1517
to_list([H|T]) -> [to_list(H)|to_list(T)];
×
1518
to_list(E) when is_atom(E) -> atom_to_list(E);
×
1519
to_list(E) -> binary_to_list(E).
×
1520

1521
srg_get_members(Group, Host) ->
1522
    Members = mod_shared_roster:get_group_explicit_users(Host,Group),
×
1523
    [jid:encode(jid:make(MUser, MServer))
×
1524
     || {MUser, MServer} <- Members].
×
1525

1526
srg_user_add(User, Host, Group, GroupHost) ->
1527
    mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group),
×
1528
    ok.
×
1529

1530
srg_user_del(User, Host, Group, GroupHost) ->
1531
    mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group),
×
1532
    ok.
×
1533

1534

1535
%%%
1536
%%% Stanza
1537
%%%
1538

1539
%% @doc Send a message to an XMPP account.
1540
-spec send_message(Type::binary(), From::binary(), To::binary(),
1541
                   Subject::binary(), Body::binary()) -> ok.
1542
send_message(Type, From, To, Subject, Body) ->
1543
    CodecOpts = ejabberd_config:codec_options(),
×
1544
    try xmpp:decode(
×
1545
          #xmlel{name = <<"message">>,
1546
                 attrs = [{<<"to">>, To},
1547
                          {<<"from">>, From},
1548
                          {<<"type">>, Type},
1549
                          {<<"id">>, p1_rand:get_string()}],
1550
                 children =
1551
                     [#xmlel{name = <<"subject">>,
1552
                             children = [{xmlcdata, Subject}]},
1553
                      #xmlel{name = <<"body">>,
1554
                             children = [{xmlcdata, Body}]}]},
1555
          ?NS_CLIENT, CodecOpts) of
1556
        #message{from = JID, subject = SubjectEl, body = BodyEl} = Msg ->
1557
            Msg2 = case {xmpp:get_text(SubjectEl), xmpp:get_text(BodyEl)} of
×
1558
                       {Subject, <<>>} -> Msg;
×
1559
                       {<<>>, Body} -> Msg#message{subject = []};
×
1560
                       _ -> Msg
×
1561
                   end,
1562
            State = #{jid => JID},
×
1563
            ejabberd_hooks:run_fold(user_send_packet, JID#jid.lserver, {Msg2, State}, []),
×
1564
            ejabberd_router:route(Msg2)
×
1565
    catch _:{xmpp_codec, Why} ->
1566
            {error, xmpp:format_error(Why)}
×
1567
    end.
1568

1569
send_stanza(FromString, ToString, Stanza) ->
1570
    try
×
1571
        #xmlel{} = El = fxml_stream:parse_element(Stanza),
×
1572
        From = jid:decode(FromString),
×
1573
        To = jid:decode(ToString),
×
1574
        CodecOpts = ejabberd_config:codec_options(),
×
1575
        Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
×
1576
        Pkt2 = xmpp:set_from_to(Pkt, From, To),
×
1577
        State = #{jid => From},
×
1578
        ejabberd_hooks:run_fold(user_send_packet, From#jid.lserver,
×
1579
                                {Pkt2, State}, []),
1580
        ejabberd_router:route(Pkt2)
×
1581
    catch _:{xmpp_codec, Why} ->
1582
            io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
×
1583
            {error, Why};
×
1584
          _:{badmatch, {error, {Code, Why}}} when is_integer(Code) ->
1585
            io:format("invalid xml: ~p~n", [Why]),
×
1586
            {error, Why};
×
1587
          _:{badmatch, {error, Why}} ->
1588
            io:format("invalid xml: ~p~n", [Why]),
×
1589
            {error, Why};
×
1590
          _:{bad_jid, S} ->
1591
            io:format("malformed JID: ~ts~n", [S]),
×
1592
            {error, "JID malformed"}
×
1593
    end.
1594

1595
-spec send_stanza_c2s(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
1596
send_stanza_c2s(Username, Host, Resource, Stanza) ->
1597
    try
×
1598
        #xmlel{} = El = fxml_stream:parse_element(Stanza),
×
1599
        CodecOpts = ejabberd_config:codec_options(),
×
1600
        Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
×
1601
        case ejabberd_sm:get_session_pid(Username, Host, Resource) of
×
1602
            Pid when is_pid(Pid) ->
1603
                ejabberd_c2s:send(Pid, Pkt);
×
1604
            _ ->
1605
                {error, no_session}
×
1606
        end
1607
    catch _:{badmatch, {error, Why} = Err} ->
1608
            io:format("invalid xml: ~p~n", [Why]),
×
1609
            Err;
×
1610
          _:{xmpp_codec, Why} ->
1611
            io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
×
1612
            {error, Why}
×
1613
    end.
1614

1615
privacy_set(Username, Host, QueryS) ->
1616
    Jid = jid:make(Username, Host),
×
1617
    QueryEl = fxml_stream:parse_element(QueryS),
×
1618
    SubEl = xmpp:decode(QueryEl),
×
1619
    IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
×
1620
             from = Jid, to = Jid},
1621
    Result = mod_privacy:process_iq(IQ),
×
1622
    Result#iq.type == result.
×
1623

1624
%%%
1625
%%% Stats
1626
%%%
1627

1628
stats(Name) ->
1629
    case Name of
×
1630
        <<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
×
1631
        <<"processes">> -> length(erlang:processes());
×
1632
        <<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ejabberd_option:hosts());
×
1633
        <<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
×
1634
        <<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
×
1635
    end.
1636

1637
stats(Name, Host) ->
1638
    case Name of
×
1639
        <<"registeredusers">> -> ejabberd_auth:count_users(Host);
×
1640
        <<"onlineusers">> -> length(ejabberd_sm:get_vh_session_list(Host))
×
1641
    end.
1642

1643

1644
user_action(User, Server, Fun, OK) ->
1645
    case ejabberd_auth:user_exists(User, Server) of
×
1646
        true ->
1647
            case catch Fun() of
×
1648
                OK -> ok;
×
1649
                {error, Error} -> throw(Error);
×
1650
                Error ->
1651
                    ?ERROR_MSG("Command returned: ~p", [Error]),
×
1652
                    1
×
1653
            end;
1654
        false ->
1655
            throw({not_found, "unknown_user"})
×
1656
    end.
1657

1658
num_prio(Priority) when is_integer(Priority) ->
1659
    Priority;
×
1660
num_prio(_) ->
1661
    -1.
×
1662

1663
mod_options(_) -> [].
×
1664

1665
mod_doc() ->
1666
    #{desc =>
×
1667
          [?T("This module provides additional administrative commands."), "",
1668
           ?T("Details for some commands:"), "",
1669
           ?T("- 'ban-acount':"),
1670
           ?T("This command kicks all the connected sessions of the account "
1671
              "from the server. It also changes their password to a randomly "
1672
              "generated one, so they can't login anymore unless a server "
1673
              "administrator changes their password again. It is possible to "
1674
              "define the reason of the ban. The new password also includes "
1675
              "the reason and the date and time of the ban. See an example below."),
1676
           ?T("- 'pushroster': (and 'pushroster-all')"),
1677
           ?T("The roster file must be placed, if using Windows, on the "
1678
              "directory where you installed ejabberd: "
1679
              "C:/Program Files/ejabberd or similar. If you use other "
1680
              "Operating System, place the file on the same directory where "
1681
              "the .beam files are installed. See below an example roster file."),
1682
           ?T("- 'srg-create':"),
1683
           ?T("If you want to put a group Name with blankspaces, use the "
1684
              "characters \"\' and \'\" to define when the Name starts and "
1685
              "ends. See an example below.")],
1686
      example =>
1687
          [{?T("With this configuration, vCards can only be modified with "
1688
               "mod_admin_extra commands:"),
1689
            ["acl:",
1690
             "  adminextraresource:",
1691
             "    - resource: \"modadminextraf8x,31ad\"",
1692
             "access_rules:",
1693
             "  vcard_set:",
1694
             "    - allow: adminextraresource",
1695
             "modules:",
1696
             "  mod_admin_extra: {}",
1697
             "  mod_vcard:",
1698
             "    access_set: vcard_set"]},
1699
           {?T("Content of roster file for 'pushroster' command:"),
1700
            ["[{<<\"bob\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Bob\">>},",
1701
             "{<<\"mart\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Mart\">>},",
1702
             "{<<\"Rich\">>, <<\"example.org\">>, <<\"bosses\">>, <<\"Rich\">>}]."]},
1703
           {?T("With this call, the sessions of the local account which JID is "
1704
              "boby@example.org will be kicked, and its password will be set "
1705
              "to something like "
1706
              "'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"),
1707
            ["ejabberdctl vhost example.org ban-account boby \"Spammed rooms\""]},
1708
           {?T("Call to srg-create using double-quotes and single-quotes:"),
1709
            ["ejabberdctl srg-create g1 example.org \"\'Group number 1\'\" this_is_g1 g1"]}]}.
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc