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

processone / ejabberd / 873

18 Dec 2024 03:24PM UTC coverage: 32.87% (+0.2%) from 32.698%
873

push

github

jsautret
Merge branch 'master' of github.com:processone/ejabberd

25 of 154 new or added lines in 10 files covered. (16.23%)

1847 existing lines in 9 files now uncovered.

14620 of 44478 relevant lines covered (32.87%)

618.14 hits per line

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

9.98
/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-2024   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_v3/2,
47
         status_list/1, status_list_v3/1, connected_users_info/0,
48
         connected_users_vhost/1, set_presence/7,
49
         get_presence/2, user_sessions_info/2, get_last/2, set_last/4,
50

51
         % Accounts
52
         set_password/3, check_password_hash/4, delete_old_users/1,
53
         delete_old_users_vhost/2, check_password/3,
54
         ban_account/3, ban_account_v2/3, get_ban_details/2, unban_account/2,
55

56
         % vCard
57
         set_nickname/3, get_vcard/3,
58
         get_vcard/4, get_vcard_multi/4, set_vcard/4,
59
         set_vcard/5,
60

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

67
         % Private storage
68
         private_get/4, private_set/3,
69

70
         % Shared roster
71
         srg_create/5, srg_add/2,
72
         srg_delete/2, srg_list/1, srg_get_info/2,
73
         srg_set_info/4,
74
         srg_get_displayed/2, srg_add_displayed/3, srg_del_displayed/3,
75
         srg_get_members/2, srg_user_add/4, srg_user_del/4,
76

77
         % Send message
78
         send_message/5, send_stanza/3, send_stanza_c2s/4,
79

80
         % Privacy list
81
         privacy_set/3,
82

83
         % Stats
84
         stats/1, stats/2
85
        ]).
86
-export([web_menu_main/2, web_page_main/2,
87
         web_menu_host/3, web_page_host/3,
88
         web_menu_hostuser/4, web_page_hostuser/4,
89
         web_menu_hostnode/4, web_page_hostnode/4,
90
         web_menu_node/3, web_page_node/3]).
91

92
-import(ejabberd_web_admin, [make_command/4, make_table/2]).
93

94
-include("ejabberd_commands.hrl").
95
-include("ejabberd_http.hrl").
96
-include("ejabberd_web_admin.hrl").
97
-include("mod_roster.hrl").
98
-include("mod_privacy.hrl").
99
-include("ejabberd_sm.hrl").
100
-include_lib("xmpp/include/scram.hrl").
101
-include_lib("xmpp/include/xmpp.hrl").
102

103
%%%
104
%%% gen_mod
105
%%%
106

107
start(_Host, _Opts) ->
108
    ejabberd_commands:register_commands(?MODULE, get_commands_spec()),
3✔
109
    {ok, [{hook, webadmin_menu_main, web_menu_main, 50, global},
3✔
110
          {hook, webadmin_page_main, web_page_main, 50, global},
111
          {hook, webadmin_menu_host, web_menu_host, 50},
112
          {hook, webadmin_page_host, web_page_host, 50},
113
          {hook, webadmin_menu_hostuser, web_menu_hostuser, 50},
114
          {hook, webadmin_page_hostuser, web_page_hostuser, 50},
115
          {hook, webadmin_menu_hostnode, web_menu_hostnode, 50},
116
          {hook, webadmin_page_hostnode, web_page_hostnode, 50},
117
          {hook, webadmin_menu_node, web_menu_node, 50, global},
118
          {hook, webadmin_page_node, web_page_node, 50, global}]}.
119

120
stop(Host) ->
121
    case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
3✔
122
        false ->
123
            ejabberd_commands:unregister_commands(get_commands_spec());
1✔
124
        true ->
125
            ok
2✔
126
    end.
127

128
reload(_Host, _NewOpts, _OldOpts) ->
UNCOV
129
    ok.
×
130

131
depends(_Host, _Opts) ->
132
    [].
6✔
133

134
%%%
135
%%% Register commands
136
%%%
137

138
get_commands_spec() ->
139
    Vcard1FieldsString = "Some vcard field names in `get`/`set_vcard` are:\n\n"
4✔
140
        "* FN           - Full Name\n"
141
        "* NICKNAME     - Nickname\n"
142
        "* BDAY         - Birthday\n"
143
        "* TITLE        - Work: Position\n"
144
        "* ROLE         - Work: Role\n",
145

146
    Vcard2FieldsString = "Some vcard field names and subnames in `get`/`set_vcard2` are:\n\n"
4✔
147
        "* N FAMILY     - Family name\n"
148
        "* N GIVEN      - Given name\n"
149
        "* N MIDDLE     - Middle name\n"
150
        "* ADR CTRY     - Address: Country\n"
151
        "* ADR LOCALITY - Address: City\n"
152
        "* TEL HOME     - Telephone: Home\n"
153
        "* TEL CELL     - Telephone: Cellphone\n"
154
        "* TEL WORK     - Telephone: Work\n"
155
        "* TEL VOICE    - Telephone: Voice\n"
156
        "* EMAIL USERID - E-Mail Address\n"
157
        "* ORG ORGNAME  - Work: Company\n"
158
        "* ORG ORGUNIT  - Work: Department\n",
159

160
    VcardXEP = "For a full list of vCard fields check [XEP-0054: vcard-temp]"
4✔
161
        "(https://xmpp.org/extensions/xep-0054.html)",
162

163
    [
4✔
164
     #ejabberd_commands{name = compile, tags = [erlang],
165
                        desc = "Recompile and reload Erlang source code file",
166
                        module = ?MODULE, function = compile,
167
                        args = [{file, string}],
168
                        args_example = ["/home/me/srcs/ejabberd/mod_example.erl"],
169
                        args_desc = ["Filename of erlang source file to compile"],
170
                        result = {res, rescode},
171
                        result_example = ok},
172
     #ejabberd_commands{name = get_cookie, tags = [erlang],
173
                        desc = "Get the Erlang cookie of this node",
174
                        module = ?MODULE, function = get_cookie,
175
                        args = [],
176
                        result = {cookie, string},
177
                        result_example = "MWTAVMODFELNLSMYXPPD",
178
                        result_desc = "Erlang cookie used for authentication by ejabberd"},
179
    #ejabberd_commands{name = restart_module, tags = [erlang],
180
                        desc = "Stop an ejabberd module, reload code and start",
181
                        module = ?MODULE, function = restart_module,
182
                        args = [{host, binary}, {module, binary}],
183
                        args_example = ["myserver.com","mod_admin_extra"],
184
                        args_desc = ["Server name", "Module to restart"],
185
                        result = {res, integer},
186
                        result_example = 0,
187
                        result_desc = "Returns integer code:\n"
188
                                      " - `0`: code reloaded, module restarted\n"
189
                                      " - `1`: error: module not loaded\n"
190
                                      " - `2`: code not reloaded, but module restarted"},
191
     #ejabberd_commands{name = delete_old_users, tags = [accounts, purge],
192
                        desc = "Delete users that didn't log in last days, or that never logged",
193
                        longdesc = "To protect admin accounts, configure this for example:\n"
194
                            "``` yaml\n"
195
                            "access_rules:\n"
196
                            "  protect_old_users:\n"
197
                            "    - allow: admin\n"
198
                            "    - deny: all\n"
199
                            "```\n",
200
                        module = ?MODULE, function = delete_old_users,
201
                        args = [{days, integer}],
202
                        args_example = [30],
203
                        args_desc = ["Last login age in days of accounts that should be removed"],
204
                        result = {res, restuple},
205
                        result_example = {ok, <<"Deleted 2 users: [\"oldman@myserver.com\", \"test@myserver.com\"]">>},
206
                        result_desc = "Result tuple"},
207
     #ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge],
208
                        desc = "Delete users that didn't log in last days in vhost, or that never logged",
209
                        longdesc = "To protect admin accounts, configure this for example:\n"
210
                            "``` yaml\n"
211
                            "access_rules:\n"
212
                            "  delete_old_users:\n"
213
                            "    - deny: admin\n"
214
                            "    - allow: all\n"
215
                            "```\n",
216
                        module = ?MODULE, function = delete_old_users_vhost,
217
                        args = [{host, binary}, {days, integer}],
218
                        args_example = [<<"myserver.com">>, 30],
219
                        args_desc = ["Server name",
220
                                     "Last login age in days of accounts that should be removed"],
221
                        result = {res, restuple},
222
                        result_example = {ok, <<"Deleted 2 users: [\"oldman@myserver.com\", \"test@myserver.com\"]">>},
223
                        result_desc = "Result tuple"},
224
     #ejabberd_commands{name = check_account, tags = [accounts],
225
                        desc = "Check if an account exists or not",
226
                        module = ejabberd_auth, function = user_exists,
227
                        args = [{user, binary}, {host, binary}],
228
                        args_example = [<<"peter">>, <<"myserver.com">>],
229
                        args_desc = ["User name to check", "Server to check"],
230
                        result = {res, rescode},
231
                        result_example = ok},
232
     #ejabberd_commands{name = check_password, tags = [accounts],
233
                        desc = "Check if a password is correct",
234
                        module = ?MODULE, function = check_password,
235
                        args = [{user, binary}, {host, binary}, {password, binary}],
236
                        args_example = [<<"peter">>, <<"myserver.com">>, <<"secret">>],
237
                        args_desc = ["User name to check", "Server to check", "Password to check"],
238
                        result = {res, rescode},
239
                        result_example = ok},
240
     #ejabberd_commands{name = check_password_hash, tags = [accounts],
241
                        desc = "Check if the password hash is correct",
242
                        longdesc = "Allows hash methods from the Erlang/OTP "
243
                        "[crypto](https://www.erlang.org/doc/man/crypto) application.",
244
                        module = ?MODULE, function = check_password_hash,
245
                        args = [{user, binary}, {host, binary}, {passwordhash, binary},
246
                                {hashmethod, binary}],
247
                        args_example = [<<"peter">>, <<"myserver.com">>,
248
                                        <<"5ebe2294ecd0e0f08eab7690d2a6ee69">>, <<"md5">>],
249
                        args_desc = ["User name to check", "Server to check",
250
                                     "Password's hash value", "Name of hash method"],
251
                        result = {res, rescode},
252
                        result_example = ok},
253
     #ejabberd_commands{name = change_password, tags = [accounts],
254
                        desc = "Change the password of an account",
255
                        module = ?MODULE, function = set_password,
256
                        args = [{user, binary}, {host, binary}, {newpass, binary}],
257
                        args_example = [<<"peter">>, <<"myserver.com">>, <<"blank">>],
258
                        args_desc = ["User name", "Server name",
259
                                     "New password for user"],
260
                        result = {res, rescode},
261
                        result_example = ok},
262

263
     #ejabberd_commands{name = ban_account, tags = [accounts],
264
                        desc = "Ban an account: kick sessions and set random password",
265
                        longdesc = "This simply sets a random password.",
266
                        module = ?MODULE, function = ban_account,
267
                        args = [{user, binary}, {host, binary}, {reason, binary}],
268
                        args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
269
                        args_desc = ["User name to ban", "Server name",
270
                                     "Reason for banning user"],
271
                        result = {res, rescode},
272
                        result_example = ok},
273
     #ejabberd_commands{name = ban_account, tags = [accounts],
274
                        desc = "Ban an account",
275
                        longdesc = "This command kicks the account sessions, "
276
                        "sets a random password, and stores ban details in the "
277
                        "account private storage. "
278
                        "This command requires _`mod_private`_ to be enabled. "
279
                        "Check also _`get_ban_details`_ API "
280
                        "and _`unban_account`_ API.",
281
                        module = ?MODULE, function = ban_account_v2,
282
                        version = 2,
283
                        note = "improved in 24.06",
284
                        args = [{user, binary}, {host, binary}, {reason, binary}],
285
                        args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
286
                        args_desc = ["User name to ban", "Server name",
287
                                     "Reason for banning user"],
288
                        result = {res, rescode},
289
                        result_example = ok},
290
     #ejabberd_commands{name = get_ban_details, tags = [accounts],
291
                        desc = "Get ban details about an account",
292
                        longdesc = "Check _`ban_account`_ API.",
293
                        module = ?MODULE, function = get_ban_details,
294
                        version = 2,
295
                        note = "added in 24.06",
296
                        args = [{user, binary}, {host, binary}],
297
                        args_example = [<<"attacker">>, <<"myserver.com">>],
298
                        args_desc = ["User name to unban", "Server name"],
299
                        result = {ban_details, {list,
300
                                          {detail, {tuple, [{name, string},
301
                                                            {value, string}
302
                                                           ]}}
303
                                         }},
304
                        result_example = [{"reason", "Spamming other users"},
305
                                          {"bandate", "2024-04-22T09:16:47.975312Z"},
306
                                          {"lastdate", "2024-04-22T08:39:12Z"},
307
                                          {"lastreason", "Connection reset by peer"}]},
308
     #ejabberd_commands{name = unban_account, tags = [accounts],
309
                        desc = "Revert the ban from an account: set back the old password",
310
                        longdesc = "Check _`ban_account`_ API.",
311
                        module = ?MODULE, function = unban_account,
312
                        version = 2,
313
                        note = "added in 24.06",
314
                        args = [{user, binary}, {host, binary}],
315
                        args_example = [<<"gooduser">>, <<"myserver.com">>],
316
                        args_desc = ["User name to unban", "Server name"],
317
                        result = {res, rescode},
318
                        result_example = ok},
319

320
     #ejabberd_commands{name = num_resources, tags = [session],
321
                        desc = "Get the number of resources of a user",
322
                        module = ?MODULE, function = num_resources,
323
                        args = [{user, binary}, {host, binary}],
324
                        args_example = [<<"peter">>, <<"myserver.com">>],
325
                        args_desc = ["User name", "Server name"],
326
                        result = {resources, integer},
327
                        result_example = 5,
328
                        result_desc = "Number of active resources for a user"},
329
     #ejabberd_commands{name = resource_num, tags = [session],
330
                        desc = "Resource string of a session number",
331
                        module = ?MODULE, function = resource_num,
332
                        args = [{user, binary}, {host, binary}, {num, integer}],
333
                        args_example = [<<"peter">>, <<"myserver.com">>, 2],
334
                        args_desc = ["User name", "Server name", "ID of resource to return"],
335
                        result = {resource, string},
336
                        result_example = <<"Psi">>,
337
                        result_desc = "Name of user resource"},
338
     #ejabberd_commands{name = kick_session, tags = [session],
339
                        desc = "Kick a user session",
340
                        module = ?MODULE, function = kick_session,
341
                        args = [{user, binary}, {host, binary}, {resource, binary}, {reason, binary}],
342
                        args_example = [<<"peter">>, <<"myserver.com">>, <<"Psi">>,
343
                                        <<"Stuck connection">>],
344
                        args_desc = ["User name", "Server name", "User's resource",
345
                                     "Reason for closing session"],
346
                        result = {res, rescode},
347
                        result_example = ok},
348
     #ejabberd_commands{name = status_num_host, tags = [session, statistics],
349
                        desc = "Number of logged users with this status in host",
350
                        policy = admin,
351
                        module = ?MODULE, function = status_num,
352
                        args = [{host, binary}, {status, binary}],
353
                        args_example = [<<"myserver.com">>, <<"dnd">>],
354
                        args_desc = ["Server name", "Status type to check"],
355
                        result = {users, integer},
356
                        result_example = 23,
357
                        result_desc = "Number of connected sessions with given status type"},
358
     #ejabberd_commands{name = status_num, tags = [session, statistics],
359
                        desc = "Number of logged users with this status",
360
                        policy = admin,
361
                        module = ?MODULE, function = status_num,
362
                        args = [{status, binary}],
363
                        args_example = [<<"dnd">>],
364
                        args_desc = ["Status type to check"],
365
                        result = {users, integer},
366
                        result_example = 23,
367
                        result_desc = "Number of connected sessions with given status type"},
368
     #ejabberd_commands{name = status_list_host, tags = [session],
369
                        desc = "List of users logged in host with their statuses",
370
                        module = ?MODULE, function = status_list,
371
                        args = [{host, binary}, {status, binary}],
372
                        args_example = [<<"myserver.com">>, <<"dnd">>],
373
                        args_desc = ["Server name", "Status type to check"],
374
                        result_example = [{<<"peter">>,<<"myserver.com">>,<<"tka">>,6,<<"Busy">>}],
375
                        result = {users, {list,
376
                                          {userstatus, {tuple, [
377
                                                                {user, string},
378
                                                                {host, string},
379
                                                                {resource, string},
380
                                                                {priority, integer},
381
                                                                {status, string}
382
                                                               ]}}
383
                                         }}},
384
     #ejabberd_commands{name = status_list_host, tags = [session],
385
                        desc = "List of users logged in host with their statuses",
386
                        module = ?MODULE, function = status_list_v3,
387
                        version = 3,
388
                        note = "updated in 24.12",
389
                        args = [{host, binary}, {status, binary}],
390
                        args_example = [<<"myserver.com">>, <<"dnd">>],
391
                        args_desc = ["Server name", "Status type to check"],
392
                        result_example = [{<<"peter@myserver.com/tka">>,6,<<"Busy">>}],
393
                        result = {users, {list,
394
                                          {userstatus, {tuple, [{jid, string},
395
                                                                {priority, integer},
396
                                                                {status, string}
397
                                                               ]}}
398
                                         }}},
399
     #ejabberd_commands{name = status_list, tags = [session],
400
                        desc = "List of logged users with this status",
401
                        module = ?MODULE, function = status_list,
402
                        args = [{status, binary}],
403
                        args_example = [<<"dnd">>],
404
                        args_desc = ["Status type to check"],
405
                        result_example = [{<<"peter">>,<<"myserver.com">>,<<"tka">>,6,<<"Busy">>}],
406
                        result = {users, {list,
407
                                          {userstatus, {tuple, [
408
                                                                {user, string},
409
                                                                {host, string},
410
                                                                {resource, string},
411
                                                                {priority, integer},
412
                                                                {status, string}
413
                                                               ]}}
414
                                         }}},
415
     #ejabberd_commands{name = status_list, tags = [session],
416
                        desc = "List of logged users with this status",
417
                        module = ?MODULE, function = status_list_v3,
418
                        version = 3,
419
                        note = "updated in 24.12",
420
                        args = [{status, binary}],
421
                        args_example = [<<"dnd">>],
422
                        args_desc = ["Status type to check"],
423
                        result_example = [{<<"peter@myserver.com/tka">>,6,<<"Busy">>}],
424
                        result = {users, {list,
425
                                          {userstatus, {tuple, [{jid, string},
426
                                                                {priority, integer},
427
                                                                {status, string}
428
                                                               ]}}
429
                                         }}},
430
     #ejabberd_commands{name = connected_users_info,
431
                        tags = [session],
432
                        desc = "List all established sessions and their information",
433
                        module = ?MODULE, function = connected_users_info,
434
                        args = [],
435
                        result_example = [{"user1@myserver.com/tka",
436
                                            "c2s", "127.0.0.1", 42656,8, "ejabberd@localhost",
437
                                           231, <<"dnd">>, <<"tka">>, <<>>}],
438
                        result = {connected_users_info,
439
                                  {list,
440
                                   {session, {tuple,
441
                                              [{jid, string},
442
                                               {connection, string},
443
                                               {ip, string},
444
                                               {port, integer},
445
                                               {priority, integer},
446
                                               {node, string},
447
                                               {uptime, integer},
448
                                               {status, string},
449
                                               {resource, string},
450
                                               {statustext, string}
451
                                              ]}}
452
                                  }}},
453

454
     #ejabberd_commands{name = connected_users_vhost,
455
                        tags = [session],
456
                        desc = "Get the list of established sessions in a vhost",
457
                        module = ?MODULE, function = connected_users_vhost,
458
                        args_example = [<<"myexample.com">>],
459
                        args_desc = ["Server name"],
460
                        args = [{host, binary}],
461
                        result_example = [<<"user1@myserver.com/tka">>, <<"user2@localhost/tka">>],
462
                        result_desc = "List of sessions full JIDs",
463
                        result = {connected_users_vhost, {list, {sessions, string}}}},
464
     #ejabberd_commands{name = user_sessions_info,
465
                        tags = [session],
466
                        desc = "Get information about all sessions of a user",
467
                        module = ?MODULE, function = user_sessions_info,
468
                        args = [{user, binary}, {host, binary}],
469
                        args_example = [<<"peter">>, <<"myserver.com">>],
470
                        args_desc = ["User name", "Server name"],
471
                        result_example = [{"c2s", "127.0.0.1", 42656,8, "ejabberd@localhost",
472
                                           231, <<"dnd">>, <<"tka">>, <<>>}],
473
                        result = {sessions_info,
474
                                  {list,
475
                                   {session, {tuple,
476
                                              [{connection, string},
477
                                               {ip, string},
478
                                               {port, integer},
479
                                               {priority, integer},
480
                                               {node, string},
481
                                               {uptime, integer},
482
                                               {status, string},
483
                                               {resource, string},
484
                                               {statustext, string}
485
                                              ]}}
486
                                  }}},
487

488
     #ejabberd_commands{name = get_presence, tags = [session],
489
                        desc =
490
                            "Retrieve the resource with highest priority, "
491
                            "and its presence (show and status message) "
492
                            "for a given user.",
493
                        longdesc =
494
                            "The `jid` value contains the user JID "
495
                            "with resource.\n\nThe `show` value contains "
496
                            "the user presence flag. It can take "
497
                            "limited values:\n\n - `available`\n - `chat` "
498
                            "(Free for chat)\n - `away`\n - `dnd` (Do "
499
                            "not disturb)\n - `xa` (Not available, "
500
                            "extended away)\n - `unavailable` (Not "
501
                            "connected)\n\n`status` is a free text "
502
                            "defined by the user client.",
503
                        module = ?MODULE, function = get_presence,
504
                        args = [{user, binary}, {host, binary}],
505
                        args_rename = [{server, host}],
506
                        args_example = [<<"peter">>, <<"myexample.com">>],
507
                        args_desc = ["User name", "Server name"],
508
                        result_example = {<<"user1@myserver.com/tka">>, <<"dnd">>, <<"Busy">>},
509
                        result =
510
                            {presence,
511
                             {tuple,
512
                              [{jid, string}, {show, string},
513
                               {status, string}]}}},
514
     #ejabberd_commands{name = set_presence,
515
                        tags = [session],
516
                        desc = "Set presence of a session",
517
                        module = ?MODULE, function = set_presence,
518
                        args = [{user, binary}, {host, binary},
519
                                {resource, binary}, {type, binary},
520
                                {show, binary}, {status, binary},
521
                                {priority, binary}],
522
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"tka1">>,
523
                                        <<"available">>,<<"away">>,<<"BB">>, <<"7">>],
524
                        args_desc = ["User name", "Server name", "Resource",
525
                                        "Type: `available`, `error`, `probe`...",
526
                                        "Show: `away`, `chat`, `dnd`, `xa`.", "Status text",
527
                                        "Priority, provide this value as an integer"],
528
                        result = {res, rescode}},
529
     #ejabberd_commands{name = set_presence,
530
                        tags = [session],
531
                        desc = "Set presence of a session",
532
                        module = ?MODULE, function = set_presence,
533
                        version = 1,
534
                        note = "updated in 24.02",
535
                        args = [{user, binary}, {host, binary},
536
                                {resource, binary}, {type, binary},
537
                                {show, binary}, {status, binary},
538
                                {priority, integer}],
539
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"tka1">>,
540
                                        <<"available">>,<<"away">>,<<"BB">>, 7],
541
                        args_desc = ["User name", "Server name", "Resource",
542
                                        "Type: `available`, `error`, `probe`...",
543
                                        "Show: `away`, `chat`, `dnd`, `xa`.", "Status text",
544
                                        "Priority, provide this value as an integer"],
545
                        result = {res, rescode}},
546

547
     #ejabberd_commands{name = set_nickname, tags = [vcard],
548
                        desc = "Set nickname in a user's vCard",
549
                        module = ?MODULE, function = set_nickname,
550
                        args = [{user, binary}, {host, binary}, {nickname, binary}],
551
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"User 1">>],
552
                        args_desc = ["User name", "Server name", "Nickname"],
553
                        result = {res, rescode}},
554
     #ejabberd_commands{name = get_vcard, tags = [vcard],
555
                        desc = "Get content from a vCard field",
556
                        longdesc = Vcard1FieldsString ++ "\n" ++ VcardXEP,
557
                        module = ?MODULE, function = get_vcard,
558
                        args = [{user, binary}, {host, binary}, {name, binary}],
559
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"NICKNAME">>],
560
                        args_desc = ["User name", "Server name", "Field name"],
561
                        result_example = "User 1",
562
                        result_desc = "Field content",
563
                        result = {content, string}},
564
     #ejabberd_commands{name = get_vcard2, tags = [vcard],
565
                        desc = "Get content from a vCard subfield",
566
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
567
                        module = ?MODULE, function = get_vcard,
568
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}],
569
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"N">>, <<"FAMILY">>],
570
                        args_desc = ["User name", "Server name", "Field name", "Subfield name"],
571
                        result_example = "Schubert",
572
                        result_desc = "Field content",
573
                        result = {content, string}},
574
     #ejabberd_commands{name = get_vcard2_multi, tags = [vcard],
575
                        desc = "Get multiple contents from a vCard field",
576
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
577
                        module = ?MODULE, function = get_vcard_multi,
578
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}],
579
                        result = {contents, {list, {value, string}}}},
580

581
     #ejabberd_commands{name = set_vcard, tags = [vcard],
582
                        desc = "Set content in a vCard field",
583
                        longdesc = Vcard1FieldsString ++ "\n" ++ VcardXEP,
584
                        module = ?MODULE, function = set_vcard,
585
                        args = [{user, binary}, {host, binary}, {name, binary}, {content, binary}],
586
                        args_example = [<<"user1">>,<<"myserver.com">>, <<"URL">>, <<"www.example.com">>],
587
                        args_desc = ["User name", "Server name", "Field name", "Value"],
588
                        result = {res, rescode}},
589
     #ejabberd_commands{name = set_vcard2, tags = [vcard],
590
                        desc = "Set content in a vCard subfield",
591
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
592
                        module = ?MODULE, function = set_vcard,
593
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {content, binary}],
594
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"TEL">>, <<"NUMBER">>, <<"123456">>],
595
                        args_desc = ["User name", "Server name", "Field name", "Subfield name", "Value"],
596
                        result = {res, rescode}},
597
     #ejabberd_commands{name = set_vcard2_multi, tags = [vcard],
598
                        desc = "Set multiple contents in a vCard subfield",
599
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
600
                        module = ?MODULE, function = set_vcard,
601
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {contents, {list, {value, binary}}}],
602
                        result = {res, rescode}},
603

604
     #ejabberd_commands{name = add_rosteritem, tags = [roster],
605
                        desc = "Add an item to a user's roster (supports ODBC)",
606
                        longdesc = "Group can be several groups separated by `;` for example: `g1;g2;g3`",
607
                        module = ?MODULE, function = add_rosteritem,
608
                        args = [{localuser, binary}, {localhost, binary},
609
                                {user, binary}, {host, binary},
610
                                {nick, binary}, {group, binary},
611
                                {subs, binary}],
612
                        args_rename = [{localserver, localhost}, {server, host}],
613
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>,
614
                                <<"User 2">>, <<"Friends">>, <<"both">>],
615
                        args_desc = ["User name", "Server name", "Contact user name", "Contact server name",
616
                                "Nickname", "Group", "Subscription"],
617
                        result = {res, rescode}},
618
     #ejabberd_commands{name = add_rosteritem, tags = [roster],
619
                        desc = "Add an item to a user's roster (supports ODBC)",
620
                        module = ?MODULE, function = add_rosteritem,
621
                        version = 1,
622
                        note = "updated in 24.02",
623
                        args = [{localuser, binary}, {localhost, binary},
624
                                {user, binary}, {host, binary},
625
                                {nick, binary}, {groups, {list, {group, binary}}},
626
                                {subs, binary}],
627
                        args_rename = [{localserver, localhost}, {server, host}],
628
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>,
629
                                <<"User 2">>, [<<"Friends">>, <<"Team 1">>], <<"both">>],
630
                        args_desc = ["User name", "Server name", "Contact user name", "Contact server name",
631
                                "Nickname", "Groups", "Subscription"],
632
                        result = {res, rescode}},
633
     %%{"", "subs= none, from, to or both"},
634
     %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
635
     %%{"", "will add mike@server.com to peter@localhost roster"},
636
     #ejabberd_commands{name = delete_rosteritem, tags = [roster],
637
                        desc = "Delete an item from a user's roster (supports ODBC)",
638
                        module = ?MODULE, function = delete_rosteritem,
639
                        args = [{localuser, binary}, {localhost, binary},
640
                                {user, binary}, {host, binary}],
641
                        args_rename = [{localserver, localhost}, {server, host}],
642
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>],
643
                        args_desc = ["User name", "Server name", "Contact user name", "Contact server name"],
644
                        result = {res, rescode}},
645
     #ejabberd_commands{name = process_rosteritems, tags = [roster],
646
                        desc = "List/delete rosteritems that match filter",
647
                        longdesc = "Explanation of each argument:\n\n"
648
                        "* `action`: what to do with each rosteritem that "
649
                        "matches all the filtering options\n"
650
                        "* `subs`: subscription type\n"
651
                        "* `asks`: pending subscription\n"
652
                        "* `users`: the JIDs of the local user\n"
653
                        "* `contacts`: the JIDs of the contact in the roster\n"
654
                        "\n"
655
                        "**Mnesia backend:**\n"
656
                        "\n"
657
                        "Allowed values in the arguments:\n\n"
658
                        "* `action` = `list` | `delete`\n"
659
                        "* `subs` = `any` | SUB[:SUB]*\n"
660
                        "* `asks` = `any` | ASK[:ASK]*\n"
661
                        "* `users` = `any` | JID[:JID]*\n"
662
                        "* `contacts` = `any` | JID[:JID]*\n"
663
                        "\nwhere\n\n"
664
                        "* SUB = `none` | `from `| `to` | `both`\n"
665
                        "* ASK = `none` | `out` | `in`\n"
666
                        "* JID = characters valid in a JID, and can use the "
667
                        "globs: `*`, `?`, `!` and `[...]`\n"
668
                        "\n"
669
                        "This example will list roster items with subscription "
670
                        "`none`, `from` or `to` that have any ask property, of "
671
                        "local users which JID is in the virtual host "
672
                        "`example.org` and that the contact JID is either a "
673
                        "bare server name (without user part) or that has a "
674
                        "user part and the server part contains the word `icq`"
675
                        ":\n  `list none:from:to any *@example.org *:*@*icq*`"
676
                        "\n\n"
677
                        "**SQL backend:**\n"
678
                        "\n"
679
                        "Allowed values in the arguments:\n\n"
680
                        "* `action` = `list` | `delete`\n"
681
                        "* `subs` = `any` | SUB\n"
682
                        "* `asks` = `any` | ASK\n"
683
                        "* `users` = JID\n"
684
                        "* `contacts` = JID\n"
685
                        "\nwhere\n\n"
686
                        "* SUB = `none` | `from` | `to` | `both`\n"
687
                        "* ASK = `none` | `out` | `in`\n"
688
                        "* JID = characters valid in a JID, and can use the "
689
                        "globs: `_` and `%`\n"
690
                        "\n"
691
                        "This example will list roster items with subscription "
692
                        "`to` that have any ask property, of "
693
                        "local users which JID is in the virtual host "
694
                        "`example.org` and that the contact JID's "
695
                        "server part contains the word `icq`"
696
                        ":\n  `list to any %@example.org %@%icq%`",
697
                        module = mod_roster, function = process_rosteritems,
698
                        args = [{action, string}, {subs, string},
699
                                {asks, string}, {users, string},
700
                                {contacts, string}],
701
                        result = {response,
702
                                  {list,
703
                                   {pairs, {tuple,
704
                                               [{user, string},
705
                                                {contact, string}
706
                                               ]}}
707
                                  }}},
708
     #ejabberd_commands{name = get_roster, tags = [roster],
709
                        desc = "Get list of contacts in a local user roster",
710
                        longdesc =
711
                            "`subscription` can be: `none`, `from`, `to`, `both`.\n\n"
712
                            "`pending` can be: `in`, `out`, `none`.",
713
                        note = "improved in 23.10",
714
                        policy = user,
715
                        module = ?MODULE, function = get_roster,
716
                        args = [],
717
                        args_rename = [{server, host}],
718
                        result_example = [{<<"user2@localhost">>, <<"User 2">>, <<"none">>, <<"subscribe">>, [<<"Group1">>]}],
719
                        result = {contacts, {list, {contact, {tuple, [
720
                                                                      {jid, string},
721
                                                                      {nick, string},
722
                                                                      {subscription, string},
723
                                                                      {pending, string},
724
                                                                      {groups, {list, {group, string}}}
725
                                                                     ]}}}}},
726
     #ejabberd_commands{name = get_roster_count, tags = [roster],
727
                        desc = "Get number of contacts in a local user roster",
728
                        note = "added in 24.06",
729
                        policy = user,
730
                        module = ?MODULE, function = get_roster_count,
731
                        args = [],
732
                        args_example = [<<"sun">>, <<"localhost">>],
733
                        args_rename = [{server, host}],
734
                        result_example = 5,
735
                        result_desc = "Number",
736
                        result = {value, integer}},
737
     #ejabberd_commands{name = push_roster, tags = [roster],
738
                        desc = "Push template roster from file to a user",
739
                        longdesc = "The text file must contain an erlang term: a list "
740
                            "of tuples with username, servername, group and nick. For example:\n"
741
                            "`[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n"
742
                            " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].`\n\n"
743
                            "If there are problems parsing UTF8 character encoding, "
744
                            "provide the corresponding string with the `<<\"STRING\"/utf8>>` syntax, for example:\n"
745
                            "`[{\"user2\", \"localhost\", \"Workers\", <<\"User 2\"/utf8>>}]`.",
746
                        module = ?MODULE, function = push_roster,
747
                        args = [{file, binary}, {user, binary}, {host, binary}],
748
                        args_example = [<<"/home/ejabberd/roster.txt">>, <<"user1">>, <<"localhost">>],
749
                        args_desc = ["File path", "User name", "Server name"],
750
                        result = {res, rescode}},
751
     #ejabberd_commands{name = push_roster_all, tags = [roster],
752
                        desc = "Push template roster from file to all those users",
753
                        longdesc = "The text file must contain an erlang term: a list "
754
                            "of tuples with username, servername, group and nick. Example:\n"
755
                            "`[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n"
756
                            " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].`",
757
                        module = ?MODULE, function = push_roster_all,
758
                        args = [{file, binary}],
759
                        args_example = [<<"/home/ejabberd/roster.txt">>],
760
                        args_desc = ["File path"],
761
                        result = {res, rescode}},
762
     #ejabberd_commands{name = push_alltoall, tags = [roster],
763
                        desc = "Add all the users to all the users of Host in Group",
764
                        module = ?MODULE, function = push_alltoall,
765
                        args = [{host, binary}, {group, binary}],
766
                        args_example = [<<"myserver.com">>,<<"Everybody">>],
767
                        args_desc = ["Server name", "Group name"],
768
                        result = {res, rescode}},
769

770
     #ejabberd_commands{name = get_last, tags = [last],
771
                        desc = "Get last activity information",
772
                        longdesc = "Timestamp is UTC and "
773
                            "[XEP-0082](https://xmpp.org/extensions/xep-0082.html)"
774
                            " format, for example: "
775
                            "`2017-02-23T22:25:28.063062Z     ONLINE`",
776
                        module = ?MODULE, function = get_last,
777
                        args = [{user, binary}, {host, binary}],
778
                        args_example = [<<"user1">>,<<"myserver.com">>],
779
                        args_desc = ["User name", "Server name"],
780
                        result_example = {<<"2017-06-30T14:32:16.060684Z">>, "ONLINE"},
781
                        result_desc = "Last activity timestamp and status",
782
                        result = {last_activity,
783
                                  {tuple, [{timestamp, string},
784
                                           {status, string}
785
                                          ]}}},
786
     #ejabberd_commands{name = set_last, tags = [last],
787
                        desc = "Set last activity information",
788
                        longdesc = "Timestamp is the seconds since "
789
                        "`1970-01-01 00:00:00 UTC`. For example value see `date +%s`",
790
                        module = ?MODULE, function = set_last,
791
                        args = [{user, binary}, {host, binary}, {timestamp, integer}, {status, binary}],
792
                        args_example = [<<"user1">>,<<"myserver.com">>, 1500045311, <<"GoSleeping">>],
793
                        args_desc = ["User name", "Server name", "Number of seconds since epoch", "Status message"],
794
                        result = {res, rescode}},
795

796
     #ejabberd_commands{name = private_get, tags = [private],
797
                        desc = "Get some information from a user private storage",
798
                        module = ?MODULE, function = private_get,
799
                        args = [{user, binary}, {host, binary}, {element, binary}, {ns, binary}],
800
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"storage">>, <<"storage:rosternotes">>],
801
                        args_desc = ["User name", "Server name", "Element name", "Namespace"],
802
                        result = {res, string}},
803
     #ejabberd_commands{name = private_set, tags = [private],
804
                        desc = "Set to the user private storage",
805
                        module = ?MODULE, function = private_set,
806
                        args = [{user, binary}, {host, binary}, {element, binary}],
807
                        args_example = [<<"user1">>,<<"myserver.com">>,
808
                            <<"<storage xmlns='storage:rosternotes'/>">>],
809
                        args_desc = ["User name", "Server name", "XML storage element"],
810
                        result = {res, rescode}},
811

812
     #ejabberd_commands{name = srg_create, tags = [shared_roster_group],
813
                        desc = "Create a Shared Roster Group",
814
                        longdesc = "If you want to specify several group "
815
                        "identifiers in the Display argument,\n"
816
                        "put `\\ \"` around the argument and\nseparate the "
817
                        "identifiers with `\\ \\ n`\n"
818
                        "For example:\n"
819
                        "  `ejabberdctl srg_create group3 myserver.com "
820
                        "name desc \\\"group1\\\\ngroup2\\\"`",
821
                        note = "changed in 21.07",
822
                        module = ?MODULE, function = srg_create,
823
                        args = [{group, binary}, {host, binary},
824
                                {label, binary}, {description, binary}, {display, binary}],
825
                        args_rename = [{name, label}],
826
                        args_example = [<<"group3">>, <<"myserver.com">>, <<"Group3">>,
827
                                <<"Third group">>, <<"group1\\\\ngroup2">>],
828
                        args_desc = ["Group identifier", "Group server name", "Group name",
829
                                "Group description", "Groups to display"],
830
                        result = {res, rescode}},
831
     #ejabberd_commands{name = srg_create, tags = [shared_roster_group],
832
                        desc = "Create a Shared Roster Group",
833
                        module = ?MODULE, function = srg_create,
834
                        version = 1,
835
                        note = "updated in 24.02",
836
                        args = [{group, binary}, {host, binary},
837
                                {label, binary}, {description, binary}, {display, {list, {group, binary}}}],
838
                        args_rename = [{name, label}],
839
                        args_example = [<<"group3">>, <<"myserver.com">>, <<"Group3">>,
840
                                <<"Third group">>, [<<"group1">>, <<"group2">>]],
841
                        args_desc = ["Group identifier", "Group server name", "Group name",
842
                                "Group description", "List of groups to display"],
843
                        result = {res, rescode}},
844
     #ejabberd_commands{name = srg_add, tags = [shared_roster_group],
845
                        desc = "Add/Create a Shared Roster Group (without details)",
846
                        module = ?MODULE, function = srg_add,
847
                        note = "added in 24.06",
848
                        args = [{group, binary}, {host, binary}],
849
                        args_example = [<<"group3">>, <<"myserver.com">>],
850
                        args_desc = ["Group identifier", "Group server name"],
851
                        result = {res, rescode}},
852
     #ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
853
                        desc = "Delete a Shared Roster Group",
854
                        module = ?MODULE, function = srg_delete,
855
                        args = [{group, binary}, {host, binary}],
856
                        args_example = [<<"group3">>, <<"myserver.com">>],
857
                        args_desc = ["Group identifier", "Group server name"],
858
                        result = {res, rescode}},
859
     #ejabberd_commands{name = srg_list, tags = [shared_roster_group],
860
                        desc = "List the Shared Roster Groups in Host",
861
                        module = ?MODULE, function = srg_list,
862
                        args = [{host, binary}],
863
                        args_example = [<<"myserver.com">>],
864
                        args_desc = ["Server name"],
865
                        result_example = [<<"group1">>, <<"group2">>],
866
                        result_desc = "List of group identifiers",
867
                        result = {groups, {list, {id, string}}}},
868
     #ejabberd_commands{name = srg_get_info, tags = [shared_roster_group],
869
                        desc = "Get info of a Shared Roster Group",
870
                        module = ?MODULE, function = srg_get_info,
871
                        args = [{group, binary}, {host, binary}],
872
                        args_example = [<<"group3">>, <<"myserver.com">>],
873
                        args_desc = ["Group identifier", "Group server name"],
874
                        result_example = [{<<"name">>, "Group 3"}, {<<"displayed_groups">>, "group1"}],
875
                        result_desc = "List of group information, as key and value",
876
                        result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
877
     #ejabberd_commands{name = srg_set_info, tags = [shared_roster_group],
878
                        desc = "Set info of a Shared Roster Group",
879
                        module = ?MODULE, function = srg_set_info,
880
                        note = "added in 24.06",
881
                        args = [{group, binary}, {host, binary}, {key, binary}, {value, binary}],
882
                        args_example = [<<"group3">>, <<"myserver.com">>, <<"label">>, <<"Family">>],
883
                        args_desc = ["Group identifier", "Group server name",
884
                                     "Information key: label, description",
885
                                     "Information value"],
886
                        result = {res, rescode}},
887

888
     #ejabberd_commands{name = srg_get_displayed, tags = [shared_roster_group],
889
                        desc = "Get displayed groups of a Shared Roster Group",
890
                        module = ?MODULE, function = srg_get_displayed,
891
                        note = "added in 24.06",
892
                        args = [{group, binary}, {host, binary}],
893
                        args_example = [<<"group3">>, <<"myserver.com">>],
894
                        args_desc = ["Group identifier", "Group server name"],
895
                        result_example = [<<"group1">>, <<"group2">>],
896
                        result_desc = "List of groups to display",
897
                        result = {display, {list, {group, binary}}}},
898
     #ejabberd_commands{name = srg_add_displayed, tags = [shared_roster_group],
899
                        desc = "Add a group to displayed_groups of a Shared Roster Group",
900
                        module = ?MODULE, function = srg_add_displayed,
901
                        note = "added in 24.06",
902
                        args = [{group, binary}, {host, binary},
903
                                {add, binary}],
904
                        args_example = [<<"group3">>, <<"myserver.com">>, <<"group1">>],
905
                        args_desc = ["Group identifier", "Group server name",
906
                                "Group to add to displayed_groups"],
907
                        result = {res, rescode}},
908
     #ejabberd_commands{name = srg_del_displayed, tags = [shared_roster_group],
909
                        desc = "Delete a group from displayed_groups of a Shared Roster Group",
910
                        module = ?MODULE, function = srg_del_displayed,
911
                        note = "added in 24.06",
912
                        args = [{group, binary}, {host, binary},
913
                                {del, binary}],
914
                        args_example = [<<"group3">>, <<"myserver.com">>, <<"group1">>],
915
                        args_desc = ["Group identifier", "Group server name",
916
                                "Group to delete from displayed_groups"],
917
                        result = {res, rescode}},
918

919
     #ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
920
                        desc = "Get members of a Shared Roster Group",
921
                        module = ?MODULE, function = srg_get_members,
922
                        args = [{group, binary}, {host, binary}],
923
                        args_example = [<<"group3">>, <<"myserver.com">>],
924
                        args_desc = ["Group identifier", "Group server name"],
925
                        result_example = [<<"user1@localhost">>, <<"user2@localhost">>],
926
                        result_desc = "List of group identifiers",
927
                        result = {members, {list, {member, string}}}},
928
     #ejabberd_commands{name = srg_user_add, tags = [shared_roster_group],
929
                        desc = "Add the JID user@host to the Shared Roster Group",
930
                        module = ?MODULE, function = srg_user_add,
931
                        args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}],
932
                        args_example = [<<"user1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
933
                        args_desc = ["Username", "User server name", "Group identifier", "Group server name"],
934
                        result = {res, rescode}},
935
     #ejabberd_commands{name = srg_user_del, tags = [shared_roster_group],
936
                        desc = "Delete this JID user@host from the Shared Roster Group",
937
                        module = ?MODULE, function = srg_user_del,
938
                        args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}],
939
                        args_example = [<<"user1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
940
                        args_desc = ["Username", "User server name", "Group identifier", "Group server name"],
941
                        result = {res, rescode}},
942

943
     #ejabberd_commands{name = get_offline_count,
944
                        tags = [offline],
945
                        desc = "Get the number of unread offline messages",
946
                        policy = user,
947
                        module = mod_offline, function = count_offline_messages,
948
                        args = [],
949
                        args_rename = [{server, host}],
950
                        result_example = 5,
951
                        result_desc = "Number",
952
                        result = {value, integer}},
953
     #ejabberd_commands{name = get_offline_messages,
954
                        tags = [internal, offline],
955
                        desc = "Get the offline messages",
956
                        policy = user,
957
                        module = mod_offline, function = get_offline_messages,
958
                        args = [],
959
                        result = {queue, {list, {messages, {tuple, [{time, string},
960
                                                                    {from, string},
961
                                                                    {to, string},
962
                                                                    {packet, string}
963
                                                                   ]}}}}},
964

965
     #ejabberd_commands{name = send_message, tags = [stanza],
966
                        desc = "Send a message to a local or remote bare of full JID",
967
                        longdesc = "When sending a groupchat message to a MUC room, "
968
                        "`from` must be the full JID of a room occupant, "
969
                        "or the bare JID of a MUC service admin, "
970
                        "or the bare JID of a MUC/Sub subscribed user.",
971
                        module = ?MODULE, function = send_message,
972
                        args = [{type, binary}, {from, binary}, {to, binary},
973
                                {subject, binary}, {body, binary}],
974
                        args_example = [<<"headline">>, <<"admin@localhost">>, <<"user1@localhost">>,
975
                                <<"Restart">>, <<"In 5 minutes">>],
976
                        args_desc = ["Message type: `normal`, `chat`, `headline`, `groupchat`", "Sender JID",
977
                                "Receiver JID", "Subject, or empty string", "Body"],
978
                        result = {res, rescode}},
979
     #ejabberd_commands{name = send_stanza_c2s, tags = [stanza],
980
                        desc = "Send a stanza from an existing C2S session",
981
                        longdesc = "`user`@`host`/`resource` must be an existing C2S session."
982
                        " As an alternative, use _`send_stanza`_ API instead.",
983
                        module = ?MODULE, function = send_stanza_c2s,
984
                        args = [{user, binary}, {host, binary}, {resource, binary}, {stanza, binary}],
985
                        args_example = [<<"admin">>, <<"myserver.com">>, <<"bot">>,
986
                                <<"<message to='user1@localhost'><ext attr='value'/></message>">>],
987
                        args_desc = ["Username", "Server name", "Resource", "Stanza"],
988
                        result = {res, rescode}},
989
     #ejabberd_commands{name = send_stanza, tags = [stanza],
990
                        desc = "Send a stanza; provide From JID and valid To JID",
991
                        module = ?MODULE, function = send_stanza,
992
                        args = [{from, binary}, {to, binary}, {stanza, binary}],
993
                        args_example = [<<"admin@localhost">>, <<"user1@localhost">>,
994
                                <<"<message><ext attr='value'/></message>">>],
995
                        args_desc = ["Sender JID", "Destination JID", "Stanza"],
996
                        result = {res, rescode}},
997
     #ejabberd_commands{name = privacy_set, tags = [stanza],
998
                        desc = "Send a IQ set privacy stanza for a local account",
999
                        module = ?MODULE, function = privacy_set,
1000
                        args = [{user, binary}, {host, binary}, {xmlquery, binary}],
1001
                        args_example = [<<"user1">>, <<"myserver.com">>,
1002
                                <<"<query xmlns='jabber:iq:privacy'>...">>],
1003
                        args_desc = ["Username", "Server name", "Query XML element"],
1004
                        result = {res, rescode}},
1005

1006
     #ejabberd_commands{name = stats, tags = [statistics],
1007
                        desc = "Get some statistical value for the whole ejabberd server",
1008
                        longdesc = "Allowed statistics `name` are: `registeredusers`, "
1009
                            "`onlineusers`, `onlineusersnode`, `uptimeseconds`, `processes`.",
1010
                        policy = admin,
1011
                        module = ?MODULE, function = stats,
1012
                        args = [{name, binary}],
1013
                        args_example = [<<"registeredusers">>],
1014
                        args_desc = ["Statistic name"],
1015
                        result_example = 6,
1016
                        result_desc = "Integer statistic value",
1017
                        result = {stat, integer}},
1018
     #ejabberd_commands{name = stats_host, tags = [statistics],
1019
                        desc = "Get some statistical value for this host",
1020
                        longdesc = "Allowed statistics `name` are: `registeredusers`, `onlineusers`.",
1021
                        policy = admin,
1022
                        module = ?MODULE, function = stats,
1023
                        args = [{name, binary}, {host, binary}],
1024
                        args_example = [<<"registeredusers">>, <<"example.com">>],
1025
                        args_desc = ["Statistic name", "Server JID"],
1026
                        result_example = 6,
1027
                        result_desc = "Integer statistic value",
1028
                        result = {stat, integer}}
1029
    ].
1030

1031

1032
%%%
1033
%%% Adminsys
1034
%%%
1035

1036
compile(File) ->
UNCOV
1037
    Ebin = filename:join(code:lib_dir(ejabberd), "ebin"),
×
UNCOV
1038
    case ext_mod:compile_erlang_file(Ebin, File) of
×
1039
        {ok, Module} ->
UNCOV
1040
            code:purge(Module),
×
UNCOV
1041
            code:load_file(Module),
×
UNCOV
1042
            ok;
×
1043
        _ ->
UNCOV
1044
            error
×
1045
    end.
1046

1047
get_cookie() ->
UNCOV
1048
    atom_to_list(erlang:get_cookie()).
×
1049

1050
restart_module(Host, Module) when is_binary(Module) ->
UNCOV
1051
    restart_module(Host, misc:binary_to_atom(Module));
×
1052
restart_module(Host, Module) when is_atom(Module) ->
UNCOV
1053
    case gen_mod:is_loaded(Host, Module) of
×
1054
        false ->
1055
            % not a running module, force code reload anyway
UNCOV
1056
            code:purge(Module),
×
UNCOV
1057
            code:delete(Module),
×
UNCOV
1058
            code:load_file(Module),
×
UNCOV
1059
            1;
×
1060
        true ->
UNCOV
1061
            gen_mod:stop_module(Host, Module),
×
UNCOV
1062
            case code:soft_purge(Module) of
×
1063
                true ->
UNCOV
1064
                    code:delete(Module),
×
UNCOV
1065
                    code:load_file(Module),
×
UNCOV
1066
                    gen_mod:start_module(Host, Module),
×
UNCOV
1067
                    0;
×
1068
                false ->
UNCOV
1069
                    gen_mod:start_module(Host, Module),
×
UNCOV
1070
                    2
×
1071
            end
1072
    end.
1073

1074
%%%
1075
%%% Accounts
1076
%%%
1077

1078
set_password(User, Host, Password) ->
1079
    Fun = fun () -> ejabberd_auth:set_password(User, Host, Password) end,
5✔
1080
    user_action(User, Host, Fun, ok).
5✔
1081

1082
check_password(User, Host, Password) ->
UNCOV
1083
    ejabberd_auth:check_password(User, <<>>, Host, Password).
×
1084

1085
%% Copied some code from ejabberd_commands.erln
1086
check_password_hash(User, Host, PasswordHash, HashMethod) ->
1087
    AccountPass = ejabberd_auth:get_password_s(User, Host),
×
UNCOV
1088
    Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
×
1089
                   proplists:get_value(hashs, crypto:supports())),
1090
    MethodAllowed = lists:member(HashMethod, Methods),
×
1091
    AccountPassHash = case {AccountPass, MethodAllowed} of
×
1092
                          {A, _} when is_tuple(A) -> scrammed;
×
1093
                          {_, true} -> get_hash(AccountPass, HashMethod);
×
1094
                          {_, false} ->
1095
                              ?ERROR_MSG("Check_password_hash called "
×
1096
                                         "with hash method: ~p", [HashMethod]),
×
UNCOV
1097
                              undefined
×
1098
                      end,
1099
    case AccountPassHash of
×
1100
        scrammed ->
1101
            ?ERROR_MSG("Passwords are scrammed, and check_password_hash cannot work.", []),
×
UNCOV
1102
            throw(passwords_scrammed_command_cannot_work);
×
1103
        undefined -> throw(unkown_hash_method);
×
1104
        PasswordHash -> ok;
×
UNCOV
1105
        _ -> false
×
1106
    end.
1107

1108
get_hash(AccountPass, Method) ->
UNCOV
1109
    iolist_to_binary([io_lib:format("~2.16.0B", [X])
×
UNCOV
1110
          || X <- binary_to_list(
×
1111
              crypto:hash(binary_to_atom(Method, latin1), AccountPass))]).
1112

1113
delete_old_users(Days) ->
1114
    %% Get the list of registered users
1115
    Users = ejabberd_auth:get_users(),
1✔
1116

1117
    {removed, N, UR} = delete_old_users(Days, Users),
1✔
1118
    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
1✔
1119

1120
delete_old_users_vhost(Host, Days) ->
1121
    %% Get the list of registered users
1122
    Users = ejabberd_auth:get_users(Host),
×
1123

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

1127
delete_old_users(Days, Users) ->
1128
    SecOlder = Days*24*60*60,
1✔
1129
    TimeStamp_now = erlang:system_time(second),
1✔
1130
    TimeStamp_oldest = TimeStamp_now - SecOlder,
1✔
1131
    F = fun({LUser, LServer}) ->
1✔
1132
            case catch delete_or_not(LUser, LServer, TimeStamp_oldest) of
6✔
1133
                true ->
UNCOV
1134
                    ejabberd_auth:remove_user(LUser, LServer),
×
1135
                    true;
×
1136
                _ ->
1137
                    false
6✔
1138
            end
1139
        end,
1140
    Users_removed = lists:filter(F, Users),
1✔
1141
    {removed, length(Users_removed), Users_removed}.
1✔
1142

1143
delete_or_not(LUser, LServer, TimeStamp_oldest) ->
1144
    deny = acl:match_rule(LServer, protect_old_users, jid:make(LUser, LServer)),
6✔
1145
    [] = ejabberd_sm:get_user_resources(LUser, LServer),
6✔
1146
    case mod_last:get_last_info(LUser, LServer) of
4✔
1147
        {ok, TimeStamp, _Status} ->
UNCOV
1148
            if TimeStamp_oldest < TimeStamp ->
×
UNCOV
1149
                    false;
×
1150
                true ->
UNCOV
1151
                    true
×
1152
            end;
1153
        not_found ->
UNCOV
1154
            true
×
1155
    end.
1156

1157
%%
1158
%% Ban account v0
1159

1160
ban_account(User, Host, ReasonText) ->
UNCOV
1161
    Reason = prepare_reason(ReasonText),
×
UNCOV
1162
    kick_sessions(User, Host, Reason),
×
UNCOV
1163
    set_random_password(User, Host, Reason),
×
UNCOV
1164
    ok.
×
1165

1166
kick_sessions(User, Server, Reason) ->
UNCOV
1167
    lists:map(
×
1168
      fun(Resource) ->
1169
              kick_this_session(User, Server, Resource, Reason)
×
1170
      end,
1171
      ejabberd_sm:get_user_resources(User, Server)).
1172

1173
set_random_password(User, Server, Reason) ->
UNCOV
1174
    NewPass = build_random_password(Reason),
×
UNCOV
1175
    set_password_auth(User, Server, NewPass).
×
1176

1177
build_random_password(Reason) ->
UNCOV
1178
    {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:universal_time(),
×
UNCOV
1179
    Date = str:format("~4..0B~2..0B~2..0BT~2..0B:~2..0B:~2..0B",
×
1180
                      [Year, Month, Day, Hour, Minute, Second]),
UNCOV
1181
    RandomString = p1_rand:get_string(),
×
1182
    <<"BANNED_ACCOUNT--", Date/binary, "--", RandomString/binary, "--", Reason/binary>>.
×
1183

1184
set_password_auth(User, Server, Password) ->
1185
    ok = ejabberd_auth:set_password(User, Server, Password).
×
1186

1187
prepare_reason([]) ->
1188
    <<"Kicked by administrator">>;
×
1189
prepare_reason([Reason]) ->
UNCOV
1190
    Reason;
×
1191
prepare_reason(Reason) when is_binary(Reason) ->
UNCOV
1192
    Reason.
×
1193

1194
%%
1195
%% Ban account v2
1196

1197
ban_account_v2(User, Host, ReasonText) ->
1198
    case gen_mod:is_loaded(Host, mod_private) of
×
1199
        false ->
UNCOV
1200
            mod_private_is_required_but_disabled;
×
1201
        true ->
UNCOV
1202
            case is_banned(User, Host) of
×
1203
                true ->
UNCOV
1204
                    account_was_already_banned;
×
1205
                false ->
UNCOV
1206
                    ban_account_v2_b(User, Host, ReasonText)
×
1207
            end
1208
    end.
1209

1210
ban_account_v2_b(User, Host, ReasonText) ->
UNCOV
1211
    Reason = prepare_reason(ReasonText),
×
1212
    Pass = ejabberd_auth:get_password_s(User, Host),
×
1213
    Last = get_last(User, Host),
×
UNCOV
1214
    BanDate = xmpp_util:encode_timestamp(erlang:timestamp()),
×
1215
    Hash = get_hash_value(User, Host),
×
1216
    BanPrivateXml = build_ban_xmlel(Reason, Pass, Last, BanDate, Hash),
×
UNCOV
1217
    ok = private_set2(User, Host, BanPrivateXml),
×
UNCOV
1218
    ok = set_random_password_v2(User, Host),
×
1219
    kick_sessions(User, Host, Reason),
×
UNCOV
1220
    ok.
×
1221

1222
get_hash_value(User, Host) ->
1223
    Cookie = misc:atom_to_binary(erlang:get_cookie()),
1,176✔
1224
    misc:term_to_base64(crypto:hash(sha256, <<User/binary, Host/binary, Cookie/binary>>)).
1,176✔
1225

1226
set_random_password_v2(User, Server) ->
UNCOV
1227
    NewPass = p1_rand:get_string(),
×
UNCOV
1228
    ok = ejabberd_auth:set_password(User, Server, NewPass).
×
1229

1230
build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate, Hash) ->
UNCOV
1231
    PassEls = build_pass_els(Pass),
×
1232
    #xmlel{name = <<"banned">>,
×
1233
           attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}],
1234
           children = [#xmlel{name = <<"reason">>, attrs = [], children = [{xmlcdata, Reason}]},
1235
                       #xmlel{name = <<"password">>, attrs = [], children = PassEls},
1236
                       #xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]},
1237
                       #xmlel{name = <<"lastreason">>, attrs = [], children = [{xmlcdata, LastReason}]},
1238
                       #xmlel{name = <<"bandate">>, attrs = [], children = [{xmlcdata, BanDate}]},
1239
                       #xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, Hash}]}
1240
                       ]}.
1241

1242
build_pass_els(Pass) when is_binary(Pass) ->
UNCOV
1243
    [{xmlcdata, Pass}];
×
1244
build_pass_els(#scram{storedkey = StoredKey,
1245
                      serverkey = ServerKey,
1246
                      salt = Salt,
1247
                      hash = Hash,
1248
                      iterationcount = IterationCount}) ->
1249
    [#xmlel{name = <<"storedkey">>, attrs = [], children = [{xmlcdata, StoredKey}]},
×
1250
     #xmlel{name = <<"serverkey">>, attrs = [], children = [{xmlcdata, ServerKey}]},
1251
     #xmlel{name = <<"salt">>, attrs = [], children = [{xmlcdata, Salt}]},
1252
     #xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, misc:atom_to_binary(Hash)}]},
1253
     #xmlel{name = <<"iterationcount">>, attrs = [], children = [{xmlcdata, integer_to_binary(IterationCount)}]}
1254
    ].
1255

1256
%%
1257
%% Get ban details
1258

1259
get_ban_details(User, Host) ->
1260
    case private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>) of
1,306✔
1261
        [El] ->
1262
            get_ban_details(User, Host, El);
1,176✔
1263
        [] ->
1264
            []
130✔
1265
    end.
1266

1267
get_ban_details(User, Host, El) ->
1268
    Reason = fxml:get_subtag_cdata(El, <<"reason">>),
1,176✔
1269
    LastDate = fxml:get_subtag_cdata(El, <<"lastdate">>),
1,176✔
1270
    LastReason = fxml:get_subtag_cdata(El, <<"lastreason">>),
1,176✔
1271
    BanDate = fxml:get_subtag_cdata(El, <<"bandate">>),
1,176✔
1272
    Hash = fxml:get_subtag_cdata(El, <<"hash">>),
1,176✔
1273
    case Hash == get_hash_value(User, Host) of
1,176✔
1274
        true ->
UNCOV
1275
            [{"reason", Reason},
×
1276
             {"bandate", BanDate},
1277
             {"lastdate", LastDate},
1278
             {"lastreason", LastReason}];
1279
        false ->
1280
            []
1,176✔
1281
    end.
1282

1283
is_banned(User, Host) ->
UNCOV
1284
    case lists:keyfind("bandate", 1, get_ban_details(User, Host)) of
×
1285
        {_, BanDate} when BanDate /= <<>> ->
UNCOV
1286
            true;
×
1287
        _ ->
UNCOV
1288
            false
×
1289
    end.
1290

1291
%%
1292
%% Unban account
1293

1294
unban_account(User, Host) ->
UNCOV
1295
    case gen_mod:is_loaded(Host, mod_private) of
×
1296
        false ->
UNCOV
1297
            mod_private_is_required_but_disabled;
×
1298
        true ->
UNCOV
1299
            case is_banned(User, Host) of
×
1300
                false ->
UNCOV
1301
                    account_was_not_banned;
×
1302
                true ->
UNCOV
1303
                    unban_account2(User, Host)
×
1304
            end
1305
    end.
1306

1307
unban_account2(User, Host) ->
UNCOV
1308
    OldPass = get_oldpass(User, Host),
×
1309
    ok = ejabberd_auth:set_password(User, Host, OldPass),
×
UNCOV
1310
    UnBanPrivateXml = build_unban_xmlel(),
×
UNCOV
1311
    private_set2(User, Host, UnBanPrivateXml).
×
1312

1313
get_oldpass(User, Host) ->
UNCOV
1314
    [El] = private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>),
×
UNCOV
1315
    Pass = fxml:get_subtag(El, <<"password">>),
×
UNCOV
1316
    get_pass(Pass).
×
1317

1318
get_pass(#xmlel{children = [{xmlcdata, Pass}]}) ->
UNCOV
1319
    Pass;
×
1320
get_pass(#xmlel{children = ScramEls} = Pass) when is_list(ScramEls) ->
UNCOV
1321
    StoredKey = fxml:get_subtag_cdata(Pass, <<"storedkey">>),
×
1322
    ServerKey = fxml:get_subtag_cdata(Pass, <<"serverkey">>),
×
UNCOV
1323
    Salt = fxml:get_subtag_cdata(Pass, <<"salt">>),
×
UNCOV
1324
    Hash = fxml:get_subtag_cdata(Pass, <<"hash">>),
×
UNCOV
1325
    IterationCount = fxml:get_subtag_cdata(Pass, <<"iterationcount">>),
×
UNCOV
1326
    #scram{storedkey = StoredKey,
×
1327
           serverkey = ServerKey,
1328
           salt = Salt,
1329
           hash = binary_to_existing_atom(Hash, latin1),
1330
           iterationcount = binary_to_integer(IterationCount)}.
1331

1332
build_unban_xmlel() ->
1333
    #xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}]}.
×
1334

1335
%%%
1336
%%% Sessions
1337
%%%
1338

1339
num_resources(User, Host) ->
UNCOV
1340
    length(ejabberd_sm:get_user_resources(User, Host)).
×
1341

1342
resource_num(User, Host, Num) ->
1343
    Resources = ejabberd_sm:get_user_resources(User, Host),
×
1344
    case (0<Num) and (Num=<length(Resources)) of
×
1345
        true ->
UNCOV
1346
            lists:nth(Num, Resources);
×
1347
        false ->
1348
            throw({bad_argument,
×
1349
                   lists:flatten(io_lib:format("Wrong resource number: ~p", [Num]))})
1350
    end.
1351

1352
kick_session(User, Server, Resource, ReasonText) ->
1353
    kick_this_session(User, Server, Resource, prepare_reason(ReasonText)),
×
UNCOV
1354
    ok.
×
1355

1356
kick_this_session(User, Server, Resource, Reason) ->
1357
    ejabberd_sm:route(jid:make(User, Server, Resource),
×
1358
                      {exit, Reason}).
1359

1360
status_num(Host, Status) ->
UNCOV
1361
    length(get_status_list(Host, Status)).
×
1362
status_num(Status) ->
UNCOV
1363
    status_num(<<"all">>, Status).
×
1364
status_list(Host, Status) ->
UNCOV
1365
    Res = get_status_list(Host, Status),
×
UNCOV
1366
    [{U, S, R, num_prio(P), St} || {U, S, R, P, St} <- Res].
×
1367
status_list(Status) ->
UNCOV
1368
    status_list(<<"all">>, Status).
×
1369

1370
status_list_v3(ArgHost, Status) ->
NEW
1371
    List = status_list(ArgHost, Status),
×
NEW
1372
    [{jid:encode(jid:make(User, Host, Resource)), Priority, StatusText}
×
NEW
1373
     || {User, Host, Resource, Priority, StatusText} <- List].
×
1374

1375
status_list_v3(Status) ->
NEW
1376
    status_list_v3(<<"all">>, Status).
×
1377

1378

1379
get_status_list(Host, Status_required) ->
1380
    %% Get list of all logged users
UNCOV
1381
    Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
×
1382
    %% Reformat the list
UNCOV
1383
    Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
×
UNCOV
1384
    Fhost = case Host of
×
1385
                <<"all">> ->
1386
                    %% All hosts are requested, so don't filter at all
UNCOV
1387
                    fun(_, _) -> true end;
×
1388
                _ ->
1389
                    %% Filter the list, only Host is interesting
1390
                    fun(A, B) -> A == B end
×
1391
            end,
UNCOV
1392
    Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
×
1393
    %% For each Pid, get its presence
UNCOV
1394
    Sessions4 = [ {catch get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
×
1395
    %% Filter by status
1396
    Fstatus = case Status_required of
×
1397
                  <<"all">> ->
UNCOV
1398
                      fun(_, _) -> true end;
×
1399
                  _ ->
UNCOV
1400
                      fun(A, B) -> A == B end
×
1401
              end,
UNCOV
1402
    [{User, Server, Resource, num_prio(Priority), stringize(Status_text)}
×
1403
     || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
×
UNCOV
1404
        apply(Fstatus, [Status, Status_required])].
×
1405

1406
connected_users_info() ->
1407
    lists:filtermap(
×
1408
      fun({U, S, R}) ->
UNCOV
1409
            case user_session_info(U, S, R) of
×
1410
                offline ->
UNCOV
1411
                    false;
×
1412
                Info ->
1413
                    Jid = jid:encode(jid:make(U, S, R)),
×
1414
                    {true, erlang:insert_element(1, Info, Jid)}
×
1415
            end
1416
      end,
1417
      ejabberd_sm:dirty_get_sessions_list()).
1418

1419
connected_users_vhost(Host) ->
UNCOV
1420
    USRs = ejabberd_sm:get_vh_session_list(Host),
×
UNCOV
1421
    [ jid:encode(jid:make(USR)) || USR <- USRs].
×
1422

1423
%% Make string more print-friendly
1424
stringize(String) ->
1425
    %% Replace newline characters with other code
1426
    ejabberd_regexp:greplace(String, <<"\n">>, <<"\\n">>).
×
1427

1428
get_presence(Pid) ->
1429
    try get_presence2(Pid) of
×
1430
        {_, _, _, _} = Res ->
UNCOV
1431
            Res
×
1432
    catch
UNCOV
1433
        _:_ -> {<<"">>, <<"">>, <<"offline">>, <<"">>}
×
1434
    end.
1435
get_presence2(Pid) ->
1436
    Pres = #presence{from = From} = ejabberd_c2s:get_presence(Pid),
×
UNCOV
1437
    Show = case Pres of
×
1438
               #presence{type = unavailable} -> <<"unavailable">>;
×
UNCOV
1439
               #presence{show = undefined} -> <<"available">>;
×
1440
               #presence{show = S} -> atom_to_binary(S, utf8)
×
1441
           end,
1442
    Status = xmpp:get_text(Pres#presence.status),
×
UNCOV
1443
    {From#jid.user, From#jid.resource, Show, Status}.
×
1444

1445
get_presence(U, S) ->
1446
    Pids = [ejabberd_sm:get_session_pid(U, S, R)
×
UNCOV
1447
            || R <- ejabberd_sm:get_user_resources(U, S)],
×
UNCOV
1448
    OnlinePids = [Pid || Pid <- Pids, Pid=/=none],
×
1449
    case OnlinePids of
×
1450
        [] ->
1451
            {jid:encode({U, S, <<>>}), <<"unavailable">>, <<"">>};
×
1452
        [SessionPid|_] ->
1453
            {_User, Resource, Show, Status} = get_presence(SessionPid),
×
UNCOV
1454
            FullJID = jid:encode({U, S, Resource}),
×
1455
            {FullJID, Show, Status}
×
1456
    end.
1457

1458
set_presence(User, Host, Resource, Type, Show, Status, Priority) when is_binary(Priority) ->
UNCOV
1459
    set_presence(User, Host, Resource, Type, Show, Status, binary_to_integer(Priority));
×
1460

1461
set_presence(User, Host, Resource, Type, Show, Status, Priority) ->
1462
    Pres = #presence{
×
1463
        from = jid:make(User, Host, Resource),
1464
        to = jid:make(User, Host),
1465
        type = misc:binary_to_atom(Type),
1466
        status = xmpp:mk_text(Status),
1467
        show = misc:binary_to_atom(Show),
1468
        priority = Priority,
1469
        sub_els = []},
UNCOV
1470
    case ejabberd_sm:get_session_pid(User, Host, Resource) of
×
1471
        none -> throw({error, "User session not found"});
×
UNCOV
1472
        Ref -> ejabberd_c2s:set_presence(Ref, Pres)
×
1473
    end.
1474

1475
user_sessions_info(User, Host) ->
1476
    lists:filtermap(fun(Resource) ->
15✔
UNCOV
1477
                            case user_session_info(User, Host, Resource) of
×
1478
                                offline -> false;
×
1479
                                Info -> {true, Info}
×
1480
                            end
1481
                    end, ejabberd_sm:get_user_resources(User, Host)).
1482

1483
user_session_info(User, Host, Resource) ->
1484
    CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
×
1485
    case ejabberd_sm:get_user_info(User, Host, Resource) of
×
1486
        offline ->
UNCOV
1487
            offline;
×
1488
        Info ->
1489
            Now = proplists:get_value(ts, Info),
×
1490
            Pid = proplists:get_value(pid, Info),
×
1491
            {_U, _Resource, Status, StatusText} = get_presence(Pid),
×
UNCOV
1492
            Priority = proplists:get_value(priority, Info),
×
1493
            Conn = proplists:get_value(conn, Info),
×
UNCOV
1494
            {Ip, Port} = proplists:get_value(ip, Info),
×
1495
            IPS = inet_parse:ntoa(Ip),
×
1496
            NodeS = atom_to_list(node(Pid)),
×
1497
            Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
×
1498
                                    calendar:now_to_local_time(Now)),
UNCOV
1499
            {atom_to_list(Conn), IPS, Port, num_prio(Priority), NodeS, Uptime, Status, Resource, StatusText}
×
1500
    end.
1501

1502

1503
%%%
1504
%%% Vcard
1505
%%%
1506

1507
set_nickname(User, Host, Nickname) ->
UNCOV
1508
    VCard = xmpp:encode(#vcard_temp{nickname = Nickname}),
×
UNCOV
1509
    case mod_vcard:set_vcard(User, jid:nameprep(Host), VCard) of
×
1510
        {error, badarg} ->
UNCOV
1511
            error;
×
1512
        ok ->
1513
            ok
×
1514
    end.
1515

1516
get_vcard(User, Host, Name) ->
UNCOV
1517
    [Res | _] = get_vcard_content(User, Host, [Name]),
×
UNCOV
1518
    Res.
×
1519

1520
get_vcard(User, Host, Name, Subname) ->
1521
    [Res | _] = get_vcard_content(User, Host, [Name, Subname]),
×
UNCOV
1522
    Res.
×
1523

1524
get_vcard_multi(User, Host, Name, Subname) ->
UNCOV
1525
    get_vcard_content(User, Host, [Name, Subname]).
×
1526

1527
set_vcard(User, Host, Name, SomeContent) ->
UNCOV
1528
    set_vcard_content(User, Host, [Name], SomeContent).
×
1529

1530
set_vcard(User, Host, Name, Subname, SomeContent) ->
1531
    set_vcard_content(User, Host, [Name, Subname], SomeContent).
×
1532

1533
%%
1534
%% Room vcard
1535

1536
is_muc_service(Domain) ->
1537
    try mod_muc_admin:get_room_serverhost(Domain) of
×
1538
        Domain -> false;
×
1539
        Service when is_binary(Service) -> true
×
1540
    catch _:{unregistered_route, _} ->
1541
            throw(error_wrong_hostname)
×
1542
    end.
1543

1544
get_room_vcard(Name, Service) ->
UNCOV
1545
    case mod_muc_admin:get_room_options(Name, Service) of
×
1546
        [] ->
UNCOV
1547
            throw(error_no_vcard_found);
×
1548
        Opts ->
UNCOV
1549
            case lists:keyfind(<<"vcard">>, 1, Opts) of
×
1550
                false ->
1551
                    throw(error_no_vcard_found);
×
1552
                {_, VCardRaw} ->
1553
                    [fxml_stream:parse_element(VCardRaw)]
×
1554
            end
1555
    end.
1556

1557
%%
1558
%% Internal vcard
1559

1560
get_vcard_content(User, Server, Data) ->
UNCOV
1561
    case get_vcard_element(User, Server) of
×
1562
        [El|_] ->
1563
            case get_vcard(Data, El) of
×
1564
                [false] -> throw(error_no_value_found_in_vcard);
×
UNCOV
1565
                ElemList -> ?DEBUG("ELS ~p", [ElemList]), [fxml:get_tag_cdata(Elem) || Elem <- ElemList]
×
1566
            end;
1567
        [] ->
UNCOV
1568
            throw(error_no_vcard_found);
×
1569
        error ->
1570
            throw(database_failure)
×
1571
    end.
1572

1573
get_vcard_element(User, Server) ->
UNCOV
1574
    case is_muc_service(Server) of
×
1575
        true ->
UNCOV
1576
           get_room_vcard(User, Server);
×
1577
        false ->
UNCOV
1578
           mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server))
×
1579
    end.
1580

1581
get_vcard([<<"TEL">>, TelType], {_, _, _, OldEls}) ->
UNCOV
1582
    {TakenEl, _NewEls} = take_vcard_tel(TelType, OldEls, [], not_found),
×
1583
    [TakenEl];
×
1584

1585
get_vcard([Data1, Data2], A1) ->
UNCOV
1586
    case get_subtag(A1, Data1) of
×
1587
        [false] -> [false];
×
1588
        A2List ->
1589
            lists:flatten([get_vcard([Data2], A2) || A2 <- A2List])
×
1590
    end;
1591

1592
get_vcard([Data], A1) ->
1593
    get_subtag(A1, Data).
×
1594

1595
get_subtag(Xmlelement, Name) ->
UNCOV
1596
    [fxml:get_subtag(Xmlelement, Name)].
×
1597

1598
set_vcard_content(User, Server, Data, SomeContent) ->
UNCOV
1599
    ContentList = case SomeContent of
×
UNCOV
1600
        [Bin | _] when is_binary(Bin) -> SomeContent;
×
UNCOV
1601
        Bin when is_binary(Bin) -> [SomeContent]
×
1602
    end,
1603
    %% Get old vcard
UNCOV
1604
    A4 = case mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server)) of
×
1605
             [A1] ->
1606
                 {_, _, _, A2} = A1,
×
1607
                 update_vcard_els(Data, ContentList, A2);
×
1608
             [] ->
UNCOV
1609
                 update_vcard_els(Data, ContentList, []);
×
1610
             error ->
UNCOV
1611
                 throw(database_failure)
×
1612
         end,
1613
    %% Build new vcard
UNCOV
1614
    SubEl = {xmlel, <<"vCard">>, [{<<"xmlns">>,<<"vcard-temp">>}], A4},
×
UNCOV
1615
    mod_vcard:set_vcard(User, jid:nameprep(Server), SubEl).
×
1616

1617
take_vcard_tel(TelType, [{xmlel, <<"TEL">>, _, SubEls}=OldEl | OldEls], NewEls, Taken) ->
1618
    {Taken2, NewEls2} = case lists:keymember(TelType, 2, SubEls) of
×
UNCOV
1619
        true -> {fxml:get_subtag(OldEl, <<"NUMBER">>), NewEls};
×
1620
        false -> {Taken, [OldEl | NewEls]}
×
1621
    end,
UNCOV
1622
    take_vcard_tel(TelType, OldEls, NewEls2, Taken2);
×
1623
take_vcard_tel(TelType, [OldEl | OldEls], NewEls, Taken) ->
1624
    take_vcard_tel(TelType, OldEls, [OldEl | NewEls], Taken);
×
1625
take_vcard_tel(_TelType, [], NewEls, Taken) ->
UNCOV
1626
    {Taken, NewEls}.
×
1627

1628
update_vcard_els([<<"TEL">>, TelType], [TelValue], OldEls) ->
1629
    {_, NewEls} = take_vcard_tel(TelType, OldEls, [], not_found),
×
UNCOV
1630
    NewEl = {xmlel,<<"TEL">>,[],
×
1631
             [{xmlel,TelType,[],[]},
1632
              {xmlel,<<"NUMBER">>,[],[{xmlcdata,TelValue}]}]},
UNCOV
1633
    [NewEl | NewEls];
×
1634

1635
update_vcard_els(Data, ContentList, Els1) ->
UNCOV
1636
    Els2 = lists:keysort(2, Els1),
×
UNCOV
1637
    [Data1 | Data2] = Data,
×
1638
    NewEls = case Data2 of
×
1639
                [] ->
UNCOV
1640
                    [{xmlel, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList];
×
1641
                [D2] ->
1642
                    OldEl = case lists:keysearch(Data1, 2, Els2) of
×
1643
                                {value, A} -> A;
×
UNCOV
1644
                                false -> {xmlel, Data1, [], []}
×
1645
                            end,
1646
                    {xmlel, _, _, ContentOld1} = OldEl,
×
UNCOV
1647
                    Content2 = [{xmlel, D2, [], [{xmlcdata,Content}]} || Content <- ContentList],
×
1648
                    ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2],
×
1649
                    ContentOld3 = lists:keysort(2, ContentOld2),
×
UNCOV
1650
                    ContentNew = lists:keymerge(2, Content2, ContentOld3),
×
1651
                    [{xmlel, Data1, [], ContentNew}]
×
1652
            end,
1653
    Els3 = lists:keydelete(Data1, 2, Els2),
×
UNCOV
1654
    lists:keymerge(2, NewEls, Els3).
×
1655

1656

1657
%%%
1658
%%% Roster
1659
%%%
1660

1661
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) when is_binary(Group) ->
1662
    add_rosteritem(LocalUser, LocalServer, User, Server, Nick, [Group], Subs);
×
1663
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Groups, Subs) ->
1664
    case {jid:make(LocalUser, LocalServer), jid:make(User, Server)} of
×
1665
        {error, _} ->
1666
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1667
        {_, error} ->
1668
            throw({error, "Invalid 'user'/'server'"});
×
1669
        {Jid, _Jid2} ->
UNCOV
1670
            RosterItem = build_roster_item(User, Server, {add, Nick, Subs, Groups}),
×
1671
            case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of
×
1672
                ok -> ok;
×
UNCOV
1673
                _ -> error
×
1674
            end
1675
    end.
1676

1677
subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) ->
1678
    case {jid:make(LU, LS), jid:make(User, Server)} of
×
1679
        {error, _} ->
1680
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1681
        {_, error} ->
1682
            throw({error, "Invalid 'user'/'server'"});
×
1683
        {_Jid, _Jid2} ->
1684
            ItemEl = build_roster_item(User, Server, {add, Nick, Subscription, Group}),
×
1685
            mod_roster:set_items(LU, LS, #roster_query{items = [ItemEl]})
×
1686
    end.
1687

1688
delete_rosteritem(LocalUser, LocalServer, User, Server) ->
1689
    case {jid:make(LocalUser, LocalServer), jid:make(User, Server)} of
×
1690
        {error, _} ->
1691
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1692
        {_, error} ->
1693
            throw({error, "Invalid 'user'/'server'"});
×
1694
        {Jid, _Jid2} ->
1695
            RosterItem = build_roster_item(User, Server, remove),
×
1696
            case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of
×
UNCOV
1697
                ok -> ok;
×
UNCOV
1698
                _ -> error
×
1699
            end
1700
    end.
1701

1702
%% -----------------------------
1703
%% Get Roster
1704
%% -----------------------------
1705

1706
get_roster(User, Server) ->
UNCOV
1707
    case jid:make(User, Server) of
×
1708
        error ->
UNCOV
1709
            throw({error, "Invalid 'user'/'server'"});
×
1710
        #jid{luser = U, lserver = S} ->
UNCOV
1711
            Items = ejabberd_hooks:run_fold(roster_get, S, [], [{U, S}]),
×
1712
            make_roster_xmlrpc(Items)
×
1713
    end.
1714

1715
make_roster_xmlrpc(Roster) ->
UNCOV
1716
    lists:map(
×
1717
      fun(#roster_item{jid = JID, name = Nick, subscription = Sub, ask = Ask, groups = Groups}) ->
UNCOV
1718
              JIDS = jid:encode(JID),
×
UNCOV
1719
              Subs = atom_to_list(Sub),
×
1720
              Asks = atom_to_list(Ask),
×
UNCOV
1721
              {JIDS, Nick, Subs, Asks, Groups}
×
1722
      end,
1723
      Roster).
1724

1725
get_roster_count(User, Server) ->
1726
    case jid:make(User, Server) of
30✔
1727
        error ->
UNCOV
1728
            throw({error, "Invalid 'user'/'server'"});
×
1729
        #jid{luser = U, lserver = S} ->
1730
            Items = ejabberd_hooks:run_fold(roster_get, S, [], [{U, S}]),
30✔
1731
            length(Items)
30✔
1732
    end.
1733

1734
%%-----------------------------
1735
%% Push Roster from file
1736
%%-----------------------------
1737

1738
push_roster(File, User, Server) ->
1739
    {ok, [Roster]} = file:consult(File),
×
1740
    subscribe_roster({User, Server, <<>>, User}, Roster).
×
1741

1742
push_roster_all(File) ->
UNCOV
1743
    {ok, [Roster]} = file:consult(File),
×
UNCOV
1744
    subscribe_all(Roster).
×
1745

1746
subscribe_all(Roster) ->
UNCOV
1747
    subscribe_all(Roster, Roster).
×
1748
subscribe_all([], _) ->
1749
    ok;
×
1750
subscribe_all([User1 | Users], Roster) ->
1751
    subscribe_roster(User1, Roster),
×
UNCOV
1752
    subscribe_all(Users, Roster).
×
1753

1754
subscribe_roster(_, []) ->
UNCOV
1755
    ok;
×
1756
%% Do not subscribe a user to itself
1757
subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
1758
    subscribe_roster({Name, Server, Group, Nick}, Roster);
×
1759
%% Subscribe Name2 to Name1
1760
subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
1761
    subscribe(iolist_to_binary(Name1), iolist_to_binary(Server1), iolist_to_binary(Name2), iolist_to_binary(Server2),
×
1762
        iolist_to_binary(Nick2), iolist_to_binary(Group2), <<"both">>, []),
1763
    subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
×
1764

1765
push_alltoall(S, G) ->
UNCOV
1766
    Users = ejabberd_auth:get_users(S),
×
UNCOV
1767
    Users2 = build_list_users(G, Users, []),
×
UNCOV
1768
    subscribe_all(Users2),
×
UNCOV
1769
    ok.
×
1770

1771
build_list_users(_Group, [], Res) ->
UNCOV
1772
    Res;
×
1773
build_list_users(Group, [{User, Server}|Users], Res) ->
UNCOV
1774
    build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
×
1775

1776
%% @spec(LU, LS, U, S, Action) -> ok
1777
%%       Action = {add, Nick, Subs, Group} | remove
1778
%% @doc Push to the roster of account LU@LS the contact U@S.
1779
%% The specific action to perform is defined in Action.
1780
push_roster_item(LU, LS, U, S, Action) ->
1781
    lists:foreach(fun(R) ->
×
1782
                          push_roster_item(LU, LS, R, U, S, Action)
×
1783
                  end, ejabberd_sm:get_user_resources(LU, LS)).
1784

1785
push_roster_item(LU, LS, R, U, S, Action) ->
1786
    LJID = jid:make(LU, LS, R),
×
UNCOV
1787
    BroadcastEl = build_broadcast(U, S, Action),
×
UNCOV
1788
    ejabberd_sm:route(LJID, BroadcastEl),
×
1789
    Item = build_roster_item(U, S, Action),
×
UNCOV
1790
    ResIQ = build_iq_roster_push(Item),
×
1791
    ejabberd_router:route(
×
1792
      xmpp:set_from_to(ResIQ, jid:remove_resource(LJID), LJID)).
1793

1794
build_roster_item(U, S, {add, Nick, Subs, Groups}) when is_list(Groups) ->
UNCOV
1795
    #roster_item{jid = jid:make(U, S),
×
1796
                 name = Nick,
1797
                 subscription = misc:binary_to_atom(Subs),
1798
                 groups = Groups};
1799
build_roster_item(U, S, {add, Nick, Subs, Group}) ->
1800
    Groups = binary:split(Group,<<";">>, [global, trim]),
×
UNCOV
1801
    #roster_item{jid = jid:make(U, S),
×
1802
                 name = Nick,
1803
                 subscription = misc:binary_to_atom(Subs),
1804
                 groups = Groups};
1805
build_roster_item(U, S, remove) ->
UNCOV
1806
    #roster_item{jid = jid:make(U, S), subscription = remove}.
×
1807

1808
build_iq_roster_push(Item) ->
1809
    #iq{type = set, id = <<"push">>,
×
1810
        sub_els = [#roster_query{items = [Item]}]}.
1811

1812
build_broadcast(U, S, {add, _Nick, Subs, _Group}) ->
UNCOV
1813
    build_broadcast(U, S, list_to_atom(binary_to_list(Subs)));
×
1814
build_broadcast(U, S, remove) ->
UNCOV
1815
    build_broadcast(U, S, none);
×
1816
%% @spec (U::binary(), S::binary(), Subs::atom()) -> any()
1817
%% Subs = both | from | to | none
1818
build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) ->
UNCOV
1819
    {item, {U, S, <<>>}, SubsAtom}.
×
1820

1821
%%%
1822
%%% Last Activity
1823
%%%
1824

1825
get_last(User, Server) ->
1826
    {Now, Status} = case ejabberd_sm:get_user_resources(User, Server) of
45✔
1827
        [] ->
1828
            case mod_last:get_last_info(User, Server) of
45✔
1829
                not_found ->
1830
                    {erlang:timestamp(), "NOT FOUND"};
×
1831
                {ok, Shift, Status1} ->
1832
                    {{Shift div 1000000, Shift rem 1000000, 0}, Status1}
45✔
1833
            end;
1834
        _ ->
UNCOV
1835
            {erlang:timestamp(), "ONLINE"}
×
1836
    end,
1837
    {xmpp_util:encode_timestamp(Now), Status}.
45✔
1838

1839
set_last(User, Server, Timestamp, Status) ->
UNCOV
1840
    case mod_last:store_last_info(User, Server, Timestamp, Status) of
×
UNCOV
1841
        {ok, _} -> ok;
×
1842
        Error -> Error
×
1843
    end.
1844

1845
%%%
1846
%%% Private Storage
1847
%%%
1848

1849
%% Example usage:
1850
%% $ ejabberdctl private_set badlop localhost "\<aa\ xmlns=\'bb\'\>Cluth\</aa\>"
1851
%% $ ejabberdctl private_get badlop localhost aa bb
1852
%% <aa xmlns='bb'>Cluth</aa>
1853

1854
private_get(Username, Host, Element, Ns) ->
1855
    Els = private_get2(Username, Host, Element, Ns),
×
UNCOV
1856
    binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))).
×
1857

1858
private_get2(Username, Host, Element, Ns) ->
1859
    case gen_mod:is_loaded(Host, mod_private) of
1,306✔
1860
        true -> private_get3(Username, Host, Element, Ns);
1,176✔
1861
        false -> []
130✔
1862
    end.
1863

1864
private_get3(Username, Host, Element, Ns) ->
1865
    ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]},
1,176✔
1866
    mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host),
1,176✔
1867
                               [{Ns, ElementXml}]).
1868

1869
private_set(Username, Host, ElementString) ->
UNCOV
1870
    case fxml_stream:parse_element(ElementString) of
×
1871
        {error, Error} ->
1872
            io:format("Error found parsing the element:~n  ~p~nError: ~p~n",
×
1873
                      [ElementString, Error]),
UNCOV
1874
            error;
×
1875
        Xml ->
UNCOV
1876
            private_set2(Username, Host, Xml)
×
1877
    end.
1878

1879
private_set2(Username, Host, Xml) ->
UNCOV
1880
    NS = fxml:get_tag_attr_s(<<"xmlns">>, Xml),
×
UNCOV
1881
    JID = jid:make(Username, Host),
×
1882
    mod_private:set_data(JID, [{NS, Xml}]).
×
1883

1884
%%%
1885
%%% Shared Roster Groups
1886
%%%
1887

1888
srg_create(Group, Host, Label, Description, Display) when is_binary(Display) ->
UNCOV
1889
    DisplayList = case Display of
×
UNCOV
1890
       <<>> -> [];
×
UNCOV
1891
       _ -> ejabberd_regexp:split(Display, <<"\\\\n">>)
×
1892
    end,
UNCOV
1893
    srg_create(Group, Host, Label, Description, DisplayList);
×
1894

1895
srg_create(Group, Host, Label, Description, DisplayList) ->
UNCOV
1896
    {_DispGroups, WrongDispGroups} = filter_groups_existence(Host, DisplayList),
×
1897
    case (WrongDispGroups -- [Group]) /= [] of
×
1898
        true ->
UNCOV
1899
            {wrong_displayed_groups, WrongDispGroups};
×
1900
        false ->
UNCOV
1901
            srg_create2(Group, Host, Label, Description, DisplayList)
×
1902
    end.
1903

1904
srg_create2(Group, Host, Label, Description, DisplayList) ->
UNCOV
1905
    Opts = [{label, Label},
×
1906
            {displayed_groups, DisplayList},
1907
            {description, Description}],
UNCOV
1908
    {atomic, _} = mod_shared_roster:create_group(Host, Group, Opts),
×
UNCOV
1909
    ok.
×
1910

1911
srg_add(Group, Host) ->
1912
    Opts = [{label, <<"">>},
×
1913
            {description, <<"">>},
1914
            {displayed_groups, []}
1915
           ],
1916
    {atomic, _} = mod_shared_roster:create_group(Host, Group, Opts),
×
UNCOV
1917
    ok.
×
1918

1919
srg_delete(Group, Host) ->
UNCOV
1920
    {atomic, _} = mod_shared_roster:delete_group(Host, Group),
×
UNCOV
1921
    ok.
×
1922

1923
srg_list(Host) ->
1924
    lists:sort(mod_shared_roster:list_groups(Host)).
×
1925

1926
srg_get_info(Group, Host) ->
UNCOV
1927
    Opts = case mod_shared_roster:get_group_opts(Host,Group) of
×
UNCOV
1928
        Os when is_list(Os) -> Os;
×
UNCOV
1929
        error -> []
×
1930
    end,
1931
    [{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
×
1932

1933
to_list([]) -> [];
×
UNCOV
1934
to_list([H|_]=List) when is_binary(H) -> lists:join(", ", [to_list(E) || E <- List]);
×
1935
to_list(E) when is_atom(E) -> atom_to_list(E);
×
UNCOV
1936
to_list(E) when is_binary(E) -> binary_to_list(E).
×
1937

1938
%% @format-begin
1939

1940
srg_set_info(Group, Host, Key, Value) ->
1941
    Opts =
×
1942
        case mod_shared_roster:get_group_opts(Host, Group) of
1943
            Os when is_list(Os) ->
UNCOV
1944
                Os;
×
1945
            error ->
UNCOV
1946
                []
×
1947
        end,
UNCOV
1948
    Opts2 = srg_set_info(Key, Value, Opts),
×
UNCOV
1949
    case mod_shared_roster:set_group_opts(Host, Group, Opts2) of
×
1950
        {atomic, ok} ->
1951
            ok;
×
1952
        Problem ->
UNCOV
1953
            ?INFO_MSG("Problem: ~n  ~p", [Problem]), %+++
×
1954
            error
×
1955
    end.
1956

1957
srg_set_info(<<"description">>, Value, Opts) ->
1958
    [{description, Value} | proplists:delete(description, Opts)];
×
1959
srg_set_info(<<"label">>, Value, Opts) ->
UNCOV
1960
    [{label, Value} | proplists:delete(label, Opts)];
×
1961
srg_set_info(<<"all_users">>, <<"true">>, Opts) ->
1962
    [{all_users, true} | proplists:delete(all_users, Opts)];
×
1963
srg_set_info(<<"online_users">>, <<"true">>, Opts) ->
UNCOV
1964
    [{online_users, true} | proplists:delete(online_users, Opts)];
×
1965
srg_set_info(<<"all_users">>, _, Opts) ->
1966
    proplists:delete(all_users, Opts);
×
1967
srg_set_info(<<"online_users">>, _, Opts) ->
UNCOV
1968
    proplists:delete(online_users, Opts);
×
1969
srg_set_info(Key, _Value, Opts) ->
1970
    ?ERROR_MSG("Unknown Key in srg_set_info: ~p", [Key]),
×
1971
    Opts.
×
1972

1973
srg_get_displayed(Group, Host) ->
UNCOV
1974
    Opts =
×
1975
        case mod_shared_roster:get_group_opts(Host, Group) of
1976
            Os when is_list(Os) ->
1977
                Os;
×
1978
            error ->
UNCOV
1979
                []
×
1980
        end,
UNCOV
1981
    proplists:get_value(displayed_groups, Opts, []).
×
1982

1983
srg_add_displayed(Group, Host, NewGroup) ->
UNCOV
1984
    Opts =
×
1985
        case mod_shared_roster:get_group_opts(Host, Group) of
1986
            Os when is_list(Os) ->
UNCOV
1987
                Os;
×
1988
            error ->
UNCOV
1989
                []
×
1990
        end,
1991
    {DispGroups, WrongDispGroups} = filter_groups_existence(Host, [NewGroup]),
×
UNCOV
1992
    case WrongDispGroups /= [] of
×
1993
        true ->
UNCOV
1994
            {wrong_displayed_groups, WrongDispGroups};
×
1995
        false ->
1996
            DisplayedOld = proplists:get_value(displayed_groups, Opts, []),
×
UNCOV
1997
            Opts2 =
×
1998
                [{displayed_groups, lists:flatten(DisplayedOld, DispGroups)}
1999
                 | proplists:delete(displayed_groups, Opts)],
2000
            case mod_shared_roster:set_group_opts(Host, Group, Opts2) of
×
2001
                {atomic, ok} ->
2002
                    ok;
×
2003
                Problem ->
2004
                    ?INFO_MSG("Problem: ~n  ~p", [Problem]), %+++
×
UNCOV
2005
                    error
×
2006
            end
2007
    end.
2008

2009
srg_del_displayed(Group, Host, OldGroup) ->
2010
    Opts =
×
2011
        case mod_shared_roster:get_group_opts(Host, Group) of
2012
            Os when is_list(Os) ->
2013
                Os;
×
2014
            error ->
UNCOV
2015
                []
×
2016
        end,
UNCOV
2017
    DisplayedOld = proplists:get_value(displayed_groups, Opts, []),
×
UNCOV
2018
    {DispGroups, OldDispGroups} = lists:partition(fun(G) -> G /= OldGroup end, DisplayedOld),
×
2019
    case OldDispGroups == [] of
×
2020
        true ->
2021
            {inexistent_displayed_groups, OldGroup};
×
2022
        false ->
2023
            Opts2 = [{displayed_groups, DispGroups} | proplists:delete(displayed_groups, Opts)],
×
UNCOV
2024
            case mod_shared_roster:set_group_opts(Host, Group, Opts2) of
×
2025
                {atomic, ok} ->
2026
                    ok;
×
2027
                Problem ->
UNCOV
2028
                    ?INFO_MSG("Problem: ~n  ~p", [Problem]), %+++
×
2029
                    error
×
2030
            end
2031
    end.
2032

2033
filter_groups_existence(Host, Groups) ->
2034
    lists:partition(fun(Group) -> error /= mod_shared_roster:get_group_opts(Host, Group) end,
×
2035
                    Groups).
2036
%% @format-end
2037

2038
srg_get_members(Group, Host) ->
2039
    Members = mod_shared_roster:get_group_explicit_users(Host,Group),
×
UNCOV
2040
    [jid:encode(jid:make(MUser, MServer))
×
UNCOV
2041
     || {MUser, MServer} <- Members].
×
2042

2043
srg_user_add(User, Host, Group, GroupHost) ->
2044
    mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group),
×
UNCOV
2045
    ok.
×
2046

2047
srg_user_del(User, Host, Group, GroupHost) ->
UNCOV
2048
    mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group),
×
UNCOV
2049
    ok.
×
2050

2051

2052
%%%
2053
%%% Stanza
2054
%%%
2055

2056
%% @doc Send a message to an XMPP account.
2057
-spec send_message(Type::binary(), From::binary(), To::binary(),
2058
                   Subject::binary(), Body::binary()) -> ok.
2059
send_message(Type, From, To, Subject, Body) ->
2060
    CodecOpts = ejabberd_config:codec_options(),
×
2061
    try xmpp:decode(
×
2062
          #xmlel{name = <<"message">>,
2063
                 attrs = [{<<"to">>, To},
2064
                          {<<"from">>, From},
2065
                          {<<"type">>, Type},
2066
                          {<<"id">>, p1_rand:get_string()}],
2067
                 children =
2068
                     [#xmlel{name = <<"subject">>,
2069
                             children = [{xmlcdata, Subject}]},
2070
                      #xmlel{name = <<"body">>,
2071
                             children = [{xmlcdata, Body}]}]},
2072
          ?NS_CLIENT, CodecOpts) of
2073
        #message{from = JID, subject = SubjectEl, body = BodyEl} = Msg ->
UNCOV
2074
            Msg2 = case {xmpp:get_text(SubjectEl), xmpp:get_text(BodyEl)} of
×
UNCOV
2075
                       {Subject, <<>>} -> Msg;
×
2076
                       {<<>>, Body} -> Msg#message{subject = []};
×
UNCOV
2077
                       _ -> Msg
×
2078
                   end,
UNCOV
2079
            State = #{jid => JID},
×
UNCOV
2080
            ejabberd_hooks:run_fold(user_send_packet, JID#jid.lserver, {Msg2, State}, []),
×
2081
            ejabberd_router:route(Msg2)
×
2082
    catch _:{xmpp_codec, Why} ->
2083
            {error, xmpp:format_error(Why)}
×
2084
    end.
2085

2086
send_stanza(FromString, ToString, Stanza) ->
2087
    try
×
UNCOV
2088
        #xmlel{} = El = fxml_stream:parse_element(Stanza),
×
UNCOV
2089
        From = jid:decode(FromString),
×
2090
        To = jid:decode(ToString),
×
2091
        CodecOpts = ejabberd_config:codec_options(),
×
UNCOV
2092
        Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
×
UNCOV
2093
        Pkt2 = xmpp:set_from_to(Pkt, From, To),
×
UNCOV
2094
        State = #{jid => From},
×
UNCOV
2095
        ejabberd_hooks:run_fold(user_send_packet, From#jid.lserver,
×
2096
                                {Pkt2, State}, []),
UNCOV
2097
        ejabberd_router:route(Pkt2)
×
2098
    catch _:{xmpp_codec, Why} ->
UNCOV
2099
            io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
×
UNCOV
2100
            {error, Why};
×
2101
          _:{badmatch, {error, {Code, Why}}} when is_integer(Code) ->
2102
            io:format("invalid xml: ~p~n", [Why]),
×
2103
            {error, Why};
×
2104
          _:{badmatch, {error, Why}} ->
UNCOV
2105
            io:format("invalid xml: ~p~n", [Why]),
×
UNCOV
2106
            {error, Why};
×
2107
          _:{bad_jid, S} ->
UNCOV
2108
            io:format("malformed JID: ~ts~n", [S]),
×
UNCOV
2109
            {error, "JID malformed"}
×
2110
    end.
2111

2112
-spec send_stanza_c2s(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
2113
send_stanza_c2s(Username, Host, Resource, Stanza) ->
UNCOV
2114
    try
×
UNCOV
2115
        #xmlel{} = El = fxml_stream:parse_element(Stanza),
×
2116
        CodecOpts = ejabberd_config:codec_options(),
×
2117
        Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
×
2118
        case ejabberd_sm:get_session_pid(Username, Host, Resource) of
×
2119
            Pid when is_pid(Pid) ->
UNCOV
2120
                ejabberd_c2s:send(Pid, Pkt);
×
2121
            _ ->
2122
                {error, no_session}
×
2123
        end
2124
    catch _:{badmatch, {error, Why} = Err} ->
2125
            io:format("invalid xml: ~p~n", [Why]),
×
UNCOV
2126
            Err;
×
2127
          _:{xmpp_codec, Why} ->
UNCOV
2128
            io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
×
2129
            {error, Why}
×
2130
    end.
2131

2132
privacy_set(Username, Host, QueryS) ->
2133
    Jid = jid:make(Username, Host),
×
2134
    QueryEl = fxml_stream:parse_element(QueryS),
×
2135
    SubEl = xmpp:decode(QueryEl),
×
2136
    IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
×
2137
             from = Jid, to = Jid},
UNCOV
2138
    Result = mod_privacy:process_iq(IQ),
×
2139
    Result#iq.type == result.
×
2140

2141
%%%
2142
%%% Stats
2143
%%%
2144

2145
stats(Name) ->
UNCOV
2146
    case Name of
×
2147
        <<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
×
2148
        <<"processes">> -> length(erlang:processes());
×
UNCOV
2149
        <<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ejabberd_option:hosts());
×
2150
        <<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
×
2151
        <<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
×
2152
    end.
2153

2154
stats(Name, Host) ->
UNCOV
2155
    case Name of
×
2156
        <<"registeredusers">> -> ejabberd_auth:count_users(Host);
×
2157
        <<"onlineusers">> -> length(ejabberd_sm:get_vh_session_list(Host))
×
2158
    end.
2159

2160

2161
user_action(User, Server, Fun, OK) ->
2162
    case ejabberd_auth:user_exists(User, Server) of
5✔
2163
        true ->
2164
            case catch Fun() of
5✔
2165
                OK -> ok;
5✔
UNCOV
2166
                {error, Error} -> throw(Error);
×
2167
                Error ->
2168
                    ?ERROR_MSG("Command returned: ~p", [Error]),
×
UNCOV
2169
                    1
×
2170
            end;
2171
        false ->
UNCOV
2172
            throw({not_found, "unknown_user"})
×
2173
    end.
2174

2175
num_prio(Priority) when is_integer(Priority) ->
2176
    Priority;
×
2177
num_prio(_) ->
2178
    -1.
×
2179

2180
%%%
2181
%%% Web Admin
2182
%%%
2183

2184
%% @format-begin
2185

2186
%%% Main
2187

2188
web_menu_main(Acc, _Lang) ->
2189
    Acc ++ [{<<"stats">>, <<"Statistics">>}].
30✔
2190

2191
web_page_main(_, #request{path = [<<"stats">>]} = R) ->
2192
    Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>)
×
2193
          ++ [make_command(stats_host, R, [], [{only, presentation}]),
2194
              make_command(incoming_s2s_number, R, [], [{only, presentation}]),
2195
              make_command(outgoing_s2s_number, R, [], [{only, presentation}]),
2196
              make_table([<<"stat name">>, {<<"stat value">>, right}],
2197
                         [{?C(<<"Registered Users:">>),
2198
                           make_command(stats,
2199
                                        R,
2200
                                        [{<<"name">>, <<"registeredusers">>}],
2201
                                        [{only, value}])},
2202
                          {?C(<<"Online Users:">>),
2203
                           make_command(stats,
2204
                                        R,
2205
                                        [{<<"name">>, <<"onlineusers">>}],
2206
                                        [{only, value}])},
2207
                          {?C(<<"S2S Connections Incoming:">>),
2208
                           make_command(incoming_s2s_number, R, [], [{only, value}])},
2209
                          {?C(<<"S2S Connections Outgoing:">>),
2210
                           make_command(outgoing_s2s_number, R, [], [{only, value}])}])],
2211
    {stop, Res};
×
2212
web_page_main(Acc, _) ->
UNCOV
2213
    Acc.
×
2214

2215
%%% Host
2216

2217
web_menu_host(Acc, _Host, _Lang) ->
2218
    Acc ++ [{<<"purge">>, <<"Purge">>}, {<<"stats">>, <<"Statistics">>}].
×
2219

2220
web_page_host(_, Host, #request{path = [<<"purge">>]} = R) ->
UNCOV
2221
    Head = [?XC(<<"h1">>, <<"Purge">>)],
×
UNCOV
2222
    Set = [ejabberd_web_admin:make_command(delete_old_users_vhost,
×
2223
                                           R,
2224
                                           [{<<"host">>, Host}],
2225
                                           [])],
UNCOV
2226
    {stop, Head ++ Set};
×
2227
web_page_host(_, Host, #request{path = [<<"stats">>]} = R) ->
UNCOV
2228
    Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>)
×
2229
          ++ [make_command(stats_host, R, [], [{only, presentation}]),
2230
              make_table([<<"stat name">>, {<<"stat value">>, right}],
2231
                         [{?C(<<"Registered Users:">>),
2232
                           make_command(stats_host,
2233
                                        R,
2234
                                        [{<<"host">>, Host}, {<<"name">>, <<"registeredusers">>}],
2235
                                        [{only, value},
2236
                                         {result_links, [{stat, arg_host, 3, <<"users">>}]}])},
2237
                          {?C(<<"Online Users:">>),
2238
                           make_command(stats_host,
2239
                                        R,
2240
                                        [{<<"host">>, Host}, {<<"name">>, <<"onlineusers">>}],
2241
                                        [{only, value},
2242
                                         {result_links,
2243
                                          [{stat, arg_host, 3, <<"online-users">>}]}])}])],
UNCOV
2244
    {stop, Res};
×
2245
web_page_host(Acc, _, _) ->
UNCOV
2246
    Acc.
×
2247

2248
%%% HostUser
2249

2250
web_menu_hostuser(Acc, _Host, _Username, _Lang) ->
UNCOV
2251
    Acc ++ [{<<"auth">>, <<"Authentication">>}, {<<"session">>, <<"Sessions">>}].
×
2252

2253
web_page_hostuser(_, Host, User, #request{path = [<<"auth">>]} = R) ->
UNCOV
2254
    Ban = make_command(ban_account,
×
2255
                       R,
2256
                       [{<<"user">>, User}, {<<"host">>, Host}],
2257
                       [{style, danger}]),
UNCOV
2258
    Unban = make_command(unban_account, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
×
UNCOV
2259
    Res = ?H1GLraw(<<"Authentication">>,
×
2260
                   <<"admin/configuration/authentication/">>,
2261
                   <<"Authentication">>)
2262
          ++ [make_command(register, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
2263
              make_command(check_account, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
2264
              ?X(<<"hr">>),
2265
              make_command(check_password, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
2266
              make_command(check_password_hash, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
2267
              make_command(change_password,
2268
                           R,
2269
                           [{<<"user">>, User}, {<<"host">>, Host}],
2270
                           [{style, danger}]),
2271
              ?X(<<"hr">>),
2272
              make_command(get_ban_details, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
2273
              Ban,
2274
              Unban,
2275
              ?X(<<"hr">>),
2276
              make_command(unregister,
2277
                           R,
2278
                           [{<<"user">>, User}, {<<"host">>, Host}],
2279
                           [{style, danger}])],
UNCOV
2280
    {stop, Res};
×
2281
web_page_hostuser(_, Host, User, #request{path = [<<"session">>]} = R) ->
UNCOV
2282
    Head = [?XC(<<"h1">>, <<"Sessions">>), ?BR],
×
UNCOV
2283
    Set = [make_command(resource_num, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
×
2284
           make_command(set_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
2285
           make_command(kick_user, R, [{<<"user">>, User}, {<<"host">>, Host}], [{style, danger}]),
2286
           make_command(kick_session,
2287
                        R,
2288
                        [{<<"user">>, User}, {<<"host">>, Host}],
2289
                        [{style, danger}])],
UNCOV
2290
    timer:sleep(100), % kicking sessions takes a while, let's delay the get commands
×
UNCOV
2291
    Get = [make_command(user_sessions_info,
×
2292
                        R,
2293
                        [{<<"user">>, User}, {<<"host">>, Host}],
2294
                        [{result_links, [{node, node, 5, <<>>}]}]),
2295
           make_command(user_resources, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
2296
           make_command(get_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []),
2297
           make_command(num_resources, R, [{<<"user">>, User}, {<<"host">>, Host}], [])],
UNCOV
2298
    {stop, Head ++ Get ++ Set};
×
2299
web_page_hostuser(Acc, _, _, _) ->
2300
    Acc.
×
2301

2302
%%% HostNode
2303

2304
web_menu_hostnode(Acc, _Host, _Username, _Lang) ->
UNCOV
2305
    Acc ++ [{<<"modules">>, <<"Modules">>}].
×
2306

2307
web_page_hostnode(_, Host, Node, #request{path = [<<"modules">>]} = R) ->
UNCOV
2308
    Res = ?H1GLraw(<<"Modules">>, <<"admin/configuration/modules/">>, <<"Modules Options">>)
×
2309
          ++ [ejabberd_cluster:call(Node,
2310
                                    ejabberd_web_admin,
2311
                                    make_command,
2312
                                    [restart_module, R, [{<<"host">>, Host}], []])],
UNCOV
2313
    {stop, Res};
×
2314
web_page_hostnode(Acc, _Host, _Node, _Request) ->
UNCOV
2315
    Acc.
×
2316

2317
%%% Node
2318

2319
web_menu_node(Acc, _Node, _Lang) ->
UNCOV
2320
    Acc ++ [{<<"stats">>, <<"Statistics">>}].
×
2321

2322
web_page_node(_, Node, #request{path = [<<"stats">>]} = R) ->
UNCOV
2323
    UpSecs =
×
2324
        ejabberd_cluster:call(Node,
2325
                              ejabberd_web_admin,
2326
                              make_command,
2327
                              [stats, R, [{<<"name">>, <<"uptimeseconds">>}], [{only, value}]]),
NEW
2328
    UpDaysBin =
×
2329
        integer_to_binary(binary_to_integer(fxml:get_tag_cdata(UpSecs))
2330
                          div 86400), % 24*60*60
2331
    UpDays =
×
2332
        #xmlel{name = <<"code">>,
2333
               attrs = [],
2334
               children = [{xmlcdata, UpDaysBin}]},
UNCOV
2335
    Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>)
×
2336
          ++ [make_command(stats, R, [], [{only, presentation}]),
2337
              make_table([<<"stat name">>, {<<"stat value">>, right}],
2338
                         [{?C(<<"Online Users in this node:">>),
2339
                           ejabberd_cluster:call(Node,
2340
                                                 ejabberd_web_admin,
2341
                                                 make_command,
2342
                                                 [stats,
2343
                                                  R,
2344
                                                  [{<<"name">>, <<"onlineusersnode">>}],
2345
                                                  [{only, value}]])},
2346
                          {?C(<<"Uptime Seconds:">>), UpSecs},
2347
                          {?C(<<"Uptime Seconds (rounded to days):">>), UpDays},
2348
                          {?C(<<"Processes:">>),
2349
                           ejabberd_cluster:call(Node,
2350
                                                 ejabberd_web_admin,
2351
                                                 make_command,
2352
                                                 [stats,
2353
                                                  R,
2354
                                                  [{<<"name">>, <<"processes">>}],
2355
                                                  [{only, value}]])}])],
UNCOV
2356
    {stop, Res};
×
2357
web_page_node(Acc, _, _) ->
UNCOV
2358
    Acc.
×
2359
%% @format-end
2360

2361
%%%
2362
%%% Document
2363
%%%
2364

2365
mod_options(_) -> [].
6✔
2366

2367
mod_doc() ->
UNCOV
2368
    #{desc =>
×
2369
          [?T("This module provides additional administrative commands."), "",
2370
           ?T("Details for some commands:"), "",
2371
           ?T("_`ban_account`_ API:"),
2372
           ?T("This command kicks all the connected sessions of the account "
2373
              "from the server. It also changes their password to a randomly "
2374
              "generated one, so they can't login anymore unless a server "
2375
              "administrator changes their password again. It is possible to "
2376
              "define the reason of the ban. The new password also includes "
2377
              "the reason and the date and time of the ban. See an example below."), "",
2378
           ?T("_`push_roster`_ API (and _`push_roster_all`_ API):"),
2379
           ?T("The roster file must be placed, if using Windows, on the "
2380
              "directory where you installed ejabberd: "
2381
              "`C:/Program Files/ejabberd` or similar. If you use other "
2382
              "Operating System, place the file on the same directory where "
2383
              "the .beam files are installed. See below an example roster file."), "",
2384
           ?T("_`srg_create`_ API:"),
2385
           ?T("If you want to put a group Name with blank spaces, use the "
2386
              "characters '\"\'' and '\'\"' to define when the Name starts and "
2387
              "ends. See an example below.")],
2388
      example =>
2389
          [{?T("With this configuration, vCards can only be modified with "
2390
               "mod_admin_extra commands:"),
2391
            ["acl:",
2392
             "  adminextraresource:",
2393
             "    - resource: \"modadminextraf8x,31ad\"",
2394
             "access_rules:",
2395
             "  vcard_set:",
2396
             "    - allow: adminextraresource",
2397
             "modules:",
2398
             "  mod_admin_extra: {}",
2399
             "  mod_vcard:",
2400
             "    access_set: vcard_set"]},
2401
           {?T("Content of roster file for _`push_roster`_ API:"),
2402
            ["[{<<\"bob\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Bob\">>},",
2403
             "{<<\"mart\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Mart\">>},",
2404
             "{<<\"Rich\">>, <<\"example.org\">>, <<\"bosses\">>, <<\"Rich\">>}]."]},
2405
           {?T("With this call, the sessions of the local account which JID is "
2406
              "'boby@example.org' will be kicked, and its password will be set "
2407
              "to something like "
2408
              "'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"),
2409
            ["ejabberdctl vhost example.org ban_account boby \"Spammed rooms\""]},
2410
           {?T("Call to _`srg_create`_ API using double-quotes and single-quotes:"),
2411
            ["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