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

processone / ejabberd / 712

30 Apr 2024 04:00PM UTC coverage: 31.234% (+0.05%) from 31.181%
712

push

github

badlop
Remove unused format_status/2 callback that is deprecated in OTP 27

13572 of 43452 relevant lines covered (31.23%)

618.51 hits per line

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

3.65
/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/1, connected_users_info/0,
47
         connected_users_vhost/1, set_presence/7,
48
         get_presence/2, user_sessions_info/2, get_last/2, set_last/4,
49

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

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

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

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

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

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

77
         % Privacy list
78
         privacy_set/3,
79

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

84

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

92
%%%
93
%%% gen_mod
94
%%%
95

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

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

107
reload(_Host, _NewOpts, _OldOpts) ->
108
    ok.
×
109

110
depends(_Host, _Opts) ->
111
    [].
×
112

113
%%%
114
%%% Register commands
115
%%%
116

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

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

139
    VcardXEP = "For a full list of vCard fields check [XEP-0054: vcard-temp]"
×
140
        "(https://xmpp.org/extensions/xep-0054.html)",
141

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

242
     #ejabberd_commands{name = ban_account, tags = [accounts],
243
                        desc = "Ban an account: kick sessions and set random password",
244
                        longdesc = "This simply sets a random password.",
245
                        module = ?MODULE, function = ban_account,
246
                        args = [{user, binary}, {host, binary}, {reason, binary}],
247
                        args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
248
                        args_desc = ["User name to ban", "Server name",
249
                                     "Reason for banning user"],
250
                        result = {res, rescode},
251
                        result_example = ok},
252
     #ejabberd_commands{name = ban_account, tags = [accounts],
253
                        desc = "Ban an account",
254
                        longdesc = "This command kicks the account sessions, "
255
                        "sets a random password, and stores ban details in the "
256
                        "account private storage. "
257
                        "This command requires mod_private to be enabled. "
258
                        "Check also _`get_ban_details`_ API "
259
                        "and `_unban_account`_ API.",
260
                        module = ?MODULE, function = ban_account_v2,
261
                        version = 2,
262
                        note = "improved in 24.xx",
263
                        args = [{user, binary}, {host, binary}, {reason, binary}],
264
                        args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
265
                        args_desc = ["User name to ban", "Server name",
266
                                     "Reason for banning user"],
267
                        result = {res, rescode},
268
                        result_example = ok},
269
     #ejabberd_commands{name = get_ban_details, tags = [accounts],
270
                        desc = "Get ban details about an account",
271
                        longdesc = "Check _`ban_account`_ API.",
272
                        module = ?MODULE, function = get_ban_details,
273
                        version = 2,
274
                        note = "added in 24.xx",
275
                        args = [{user, binary}, {host, binary}],
276
                        args_example = [<<"attacker">>, <<"myserver.com">>],
277
                        args_desc = ["User name to unban", "Server name"],
278
                        result = {ban_details, {list,
279
                                          {detail, {tuple, [{name, string},
280
                                                            {value, string}
281
                                                           ]}}
282
                                         }},
283
                        result_example = [{"reason", "Spamming other users"},
284
                                          {"bandate", "2024-04-22T09:16:47.975312Z"},
285
                                          {"lastdate", "2024-04-22T08:39:12Z"},
286
                                          {"lastreason", "Connection reset by peer"}]},
287
     #ejabberd_commands{name = unban_account, tags = [accounts],
288
                        desc = "Revert the ban from an account: set back the old password",
289
                        longdesc = "Check _`ban_account`_ API.",
290
                        module = ?MODULE, function = unban_account,
291
                        version = 2,
292
                        note = "added in 24.xx",
293
                        args = [{user, binary}, {host, binary}],
294
                        args_example = [<<"gooduser">>, <<"myserver.com">>],
295
                        args_desc = ["User name to unban", "Server name"],
296
                        result = {res, rescode},
297
                        result_example = ok},
298

299
     #ejabberd_commands{name = num_resources, tags = [session],
300
                        desc = "Get the number of resources of a user",
301
                        module = ?MODULE, function = num_resources,
302
                        args = [{user, binary}, {host, binary}],
303
                        args_example = [<<"peter">>, <<"myserver.com">>],
304
                        args_desc = ["User name", "Server name"],
305
                        result = {resources, integer},
306
                        result_example = 5,
307
                        result_desc = "Number of active resources for a user"},
308
     #ejabberd_commands{name = resource_num, tags = [session],
309
                        desc = "Resource string of a session number",
310
                        module = ?MODULE, function = resource_num,
311
                        args = [{user, binary}, {host, binary}, {num, integer}],
312
                        args_example = [<<"peter">>, <<"myserver.com">>, 2],
313
                        args_desc = ["User name", "Server name", "ID of resource to return"],
314
                        result = {resource, string},
315
                        result_example = <<"Psi">>,
316
                        result_desc = "Name of user resource"},
317
     #ejabberd_commands{name = kick_session, tags = [session],
318
                        desc = "Kick a user session",
319
                        module = ?MODULE, function = kick_session,
320
                        args = [{user, binary}, {host, binary}, {resource, binary}, {reason, binary}],
321
                        args_example = [<<"peter">>, <<"myserver.com">>, <<"Psi">>,
322
                                        <<"Stuck connection">>],
323
                        args_desc = ["User name", "Server name", "User's resource",
324
                                     "Reason for closing session"],
325
                        result = {res, rescode},
326
                        result_example = ok},
327
     #ejabberd_commands{name = status_num_host, tags = [session, statistics],
328
                        desc = "Number of logged users with this status in host",
329
                        policy = admin,
330
                        module = ?MODULE, function = status_num,
331
                        args = [{host, binary}, {status, binary}],
332
                        args_example = [<<"myserver.com">>, <<"dnd">>],
333
                        args_desc = ["Server name", "Status type to check"],
334
                        result = {users, integer},
335
                        result_example = 23,
336
                        result_desc = "Number of connected sessions with given status type"},
337
     #ejabberd_commands{name = status_num, tags = [session, statistics],
338
                        desc = "Number of logged users with this status",
339
                        policy = admin,
340
                        module = ?MODULE, function = status_num,
341
                        args = [{status, binary}],
342
                        args_example = [<<"dnd">>],
343
                        args_desc = ["Status type to check"],
344
                        result = {users, integer},
345
                        result_example = 23,
346
                        result_desc = "Number of connected sessions with given status type"},
347
     #ejabberd_commands{name = status_list_host, tags = [session],
348
                        desc = "List of users logged in host with their statuses",
349
                        module = ?MODULE, function = status_list,
350
                        args = [{host, binary}, {status, binary}],
351
                        args_example = [<<"myserver.com">>, <<"dnd">>],
352
                        args_desc = ["Server name", "Status type to check"],
353
                        result_example = [{<<"peter">>,<<"myserver.com">>,<<"tka">>,6,<<"Busy">>}],
354
                        result = {users, {list,
355
                                          {userstatus, {tuple, [
356
                                                                {user, string},
357
                                                                {host, string},
358
                                                                {resource, string},
359
                                                                {priority, integer},
360
                                                                {status, string}
361
                                                               ]}}
362
                                         }}},
363
     #ejabberd_commands{name = status_list, tags = [session],
364
                        desc = "List of logged users with this status",
365
                        module = ?MODULE, function = status_list,
366
                        args = [{status, binary}],
367
                        args_example = [<<"dnd">>],
368
                        args_desc = ["Status type to check"],
369
                        result_example = [{<<"peter">>,<<"myserver.com">>,<<"tka">>,6,<<"Busy">>}],
370
                        result = {users, {list,
371
                                          {userstatus, {tuple, [
372
                                                                {user, string},
373
                                                                {host, string},
374
                                                                {resource, string},
375
                                                                {priority, integer},
376
                                                                {status, string}
377
                                                               ]}}
378
                                         }}},
379
     #ejabberd_commands{name = connected_users_info,
380
                        tags = [session],
381
                        desc = "List all established sessions and their information",
382
                        module = ?MODULE, function = connected_users_info,
383
                        args = [],
384
                        result_example = [{"user1@myserver.com/tka",
385
                                            "c2s", "127.0.0.1", 42656,8, "ejabberd@localhost",
386
                                           231, <<"dnd">>, <<"tka">>, <<>>}],
387
                        result = {connected_users_info,
388
                                  {list,
389
                                   {session, {tuple,
390
                                              [{jid, string},
391
                                               {connection, string},
392
                                               {ip, string},
393
                                               {port, integer},
394
                                               {priority, integer},
395
                                               {node, string},
396
                                               {uptime, integer},
397
                                               {status, string},
398
                                               {resource, string},
399
                                               {statustext, string}
400
                                              ]}}
401
                                  }}},
402

403
     #ejabberd_commands{name = connected_users_vhost,
404
                        tags = [session],
405
                        desc = "Get the list of established sessions in a vhost",
406
                        module = ?MODULE, function = connected_users_vhost,
407
                        args_example = [<<"myexample.com">>],
408
                        args_desc = ["Server name"],
409
                        result_example = [<<"user1@myserver.com/tka">>, <<"user2@localhost/tka">>],
410
                        args = [{host, binary}],
411
                        result = {connected_users_vhost, {list, {sessions, string}}}},
412
     #ejabberd_commands{name = user_sessions_info,
413
                        tags = [session],
414
                        desc = "Get information about all sessions of a user",
415
                        module = ?MODULE, function = user_sessions_info,
416
                        args = [{user, binary}, {host, binary}],
417
                        args_example = [<<"peter">>, <<"myserver.com">>],
418
                        args_desc = ["User name", "Server name"],
419
                        result_example = [{"c2s", "127.0.0.1", 42656,8, "ejabberd@localhost",
420
                                           231, <<"dnd">>, <<"tka">>, <<>>}],
421
                        result = {sessions_info,
422
                                  {list,
423
                                   {session, {tuple,
424
                                              [{connection, string},
425
                                               {ip, string},
426
                                               {port, integer},
427
                                               {priority, integer},
428
                                               {node, string},
429
                                               {uptime, integer},
430
                                               {status, string},
431
                                               {resource, string},
432
                                               {statustext, string}
433
                                              ]}}
434
                                  }}},
435

436
     #ejabberd_commands{name = get_presence, tags = [session],
437
                        desc =
438
                            "Retrieve the resource with highest priority, "
439
                            "and its presence (show and status message) "
440
                            "for a given user.",
441
                        longdesc =
442
                            "The `jid` value contains the user JID "
443
                            "with resource.\n\nThe `show` value contains "
444
                            "the user presence flag. It can take "
445
                            "limited values:\n\n - `available`\n - `chat` "
446
                            "(Free for chat)\n - `away`\n - `dnd` (Do "
447
                            "not disturb)\n - `xa` (Not available, "
448
                            "extended away)\n - `unavailable` (Not "
449
                            "connected)\n\n`status` is a free text "
450
                            "defined by the user client.",
451
                        module = ?MODULE, function = get_presence,
452
                        args = [{user, binary}, {host, binary}],
453
                        args_rename = [{server, host}],
454
                        args_example = [<<"peter">>, <<"myexample.com">>],
455
                        args_desc = ["User name", "Server name"],
456
                        result_example = {<<"user1@myserver.com/tka">>, <<"dnd">>, <<"Busy">>},
457
                        result =
458
                            {presence,
459
                             {tuple,
460
                              [{jid, string}, {show, string},
461
                               {status, string}]}}},
462
     #ejabberd_commands{name = set_presence,
463
                        tags = [session],
464
                        desc = "Set presence of a session",
465
                        module = ?MODULE, function = set_presence,
466
                        args = [{user, binary}, {host, binary},
467
                                {resource, binary}, {type, binary},
468
                                {show, binary}, {status, binary},
469
                                {priority, binary}],
470
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"tka1">>,
471
                                        <<"available">>,<<"away">>,<<"BB">>, <<"7">>],
472
                        args_desc = ["User name", "Server name", "Resource",
473
                                        "Type: `available`, `error`, `probe`...",
474
                                        "Show: `away`, `chat`, `dnd`, `xa`.", "Status text",
475
                                        "Priority, provide this value as an integer"],
476
                        result = {res, rescode}},
477
     #ejabberd_commands{name = set_presence,
478
                        tags = [session],
479
                        desc = "Set presence of a session",
480
                        module = ?MODULE, function = set_presence,
481
                        version = 1,
482
                        note = "updated in 24.02",
483
                        args = [{user, binary}, {host, binary},
484
                                {resource, binary}, {type, binary},
485
                                {show, binary}, {status, binary},
486
                                {priority, integer}],
487
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"tka1">>,
488
                                        <<"available">>,<<"away">>,<<"BB">>, 7],
489
                        args_desc = ["User name", "Server name", "Resource",
490
                                        "Type: `available`, `error`, `probe`...",
491
                                        "Show: `away`, `chat`, `dnd`, `xa`.", "Status text",
492
                                        "Priority, provide this value as an integer"],
493
                        result = {res, rescode}},
494

495
     #ejabberd_commands{name = set_nickname, tags = [vcard],
496
                        desc = "Set nickname in a user's vCard",
497
                        module = ?MODULE, function = set_nickname,
498
                        args = [{user, binary}, {host, binary}, {nickname, binary}],
499
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"User 1">>],
500
                        args_desc = ["User name", "Server name", "Nickname"],
501
                        result = {res, rescode}},
502
     #ejabberd_commands{name = get_vcard, tags = [vcard],
503
                        desc = "Get content from a vCard field",
504
                        longdesc = Vcard1FieldsString ++ "\n" ++ VcardXEP,
505
                        module = ?MODULE, function = get_vcard,
506
                        args = [{user, binary}, {host, binary}, {name, binary}],
507
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"NICKNAME">>],
508
                        args_desc = ["User name", "Server name", "Field name"],
509
                        result_example = "User 1",
510
                        result_desc = "Field content",
511
                        result = {content, string}},
512
     #ejabberd_commands{name = get_vcard2, tags = [vcard],
513
                        desc = "Get content from a vCard subfield",
514
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
515
                        module = ?MODULE, function = get_vcard,
516
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}],
517
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"N">>, <<"FAMILY">>],
518
                        args_desc = ["User name", "Server name", "Field name", "Subfield name"],
519
                        result_example = "Schubert",
520
                        result_desc = "Field content",
521
                        result = {content, string}},
522
     #ejabberd_commands{name = get_vcard2_multi, tags = [vcard],
523
                        desc = "Get multiple contents from a vCard field",
524
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
525
                        module = ?MODULE, function = get_vcard_multi,
526
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}],
527
                        result = {contents, {list, {value, string}}}},
528

529
     #ejabberd_commands{name = set_vcard, tags = [vcard],
530
                        desc = "Set content in a vCard field",
531
                        longdesc = Vcard1FieldsString ++ "\n" ++ VcardXEP,
532
                        module = ?MODULE, function = set_vcard,
533
                        args = [{user, binary}, {host, binary}, {name, binary}, {content, binary}],
534
                        args_example = [<<"user1">>,<<"myserver.com">>, <<"URL">>, <<"www.example.com">>],
535
                        args_desc = ["User name", "Server name", "Field name", "Value"],
536
                        result = {res, rescode}},
537
     #ejabberd_commands{name = set_vcard2, tags = [vcard],
538
                        desc = "Set content in a vCard subfield",
539
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
540
                        module = ?MODULE, function = set_vcard,
541
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {content, binary}],
542
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"TEL">>, <<"NUMBER">>, <<"123456">>],
543
                        args_desc = ["User name", "Server name", "Field name", "Subfield name", "Value"],
544
                        result = {res, rescode}},
545
     #ejabberd_commands{name = set_vcard2_multi, tags = [vcard],
546
                        desc = "Set multiple contents in a vCard subfield",
547
                        longdesc = Vcard2FieldsString ++ "\n" ++ VcardXEP,
548
                        module = ?MODULE, function = set_vcard,
549
                        args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {contents, {list, {value, binary}}}],
550
                        result = {res, rescode}},
551

552
     #ejabberd_commands{name = add_rosteritem, tags = [roster],
553
                        desc = "Add an item to a user's roster (supports ODBC)",
554
                        longdesc = "Group can be several groups separated by `;` for example: `g1;g2;g3`",
555
                        module = ?MODULE, function = add_rosteritem,
556
                        args = [{localuser, binary}, {localhost, binary},
557
                                {user, binary}, {host, binary},
558
                                {nick, binary}, {group, binary},
559
                                {subs, binary}],
560
                        args_rename = [{localserver, localhost}, {server, host}],
561
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>,
562
                                <<"User 2">>, <<"Friends">>, <<"both">>],
563
                        args_desc = ["User name", "Server name", "Contact user name", "Contact server name",
564
                                "Nickname", "Group", "Subscription"],
565
                        result = {res, rescode}},
566
     #ejabberd_commands{name = add_rosteritem, tags = [roster],
567
                        desc = "Add an item to a user's roster (supports ODBC)",
568
                        module = ?MODULE, function = add_rosteritem,
569
                        version = 1,
570
                        note = "updated in 24.02",
571
                        args = [{localuser, binary}, {localhost, binary},
572
                                {user, binary}, {host, binary},
573
                                {nick, binary}, {groups, {list, {group, binary}}},
574
                                {subs, binary}],
575
                        args_rename = [{localserver, localhost}, {server, host}],
576
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>,
577
                                <<"User 2">>, [<<"Friends">>, <<"Team 1">>], <<"both">>],
578
                        args_desc = ["User name", "Server name", "Contact user name", "Contact server name",
579
                                "Nickname", "Groups", "Subscription"],
580
                        result = {res, rescode}},
581
     %%{"", "subs= none, from, to or both"},
582
     %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
583
     %%{"", "will add mike@server.com to peter@localhost roster"},
584
     #ejabberd_commands{name = delete_rosteritem, tags = [roster],
585
                        desc = "Delete an item from a user's roster (supports ODBC)",
586
                        module = ?MODULE, function = delete_rosteritem,
587
                        args = [{localuser, binary}, {localhost, binary},
588
                                {user, binary}, {host, binary}],
589
                        args_rename = [{localserver, localhost}, {server, host}],
590
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>],
591
                        args_desc = ["User name", "Server name", "Contact user name", "Contact server name"],
592
                        result = {res, rescode}},
593
     #ejabberd_commands{name = process_rosteritems, tags = [roster],
594
                        desc = "List/delete rosteritems that match filter",
595
                        longdesc = "Explanation of each argument:\n\n"
596
                        "* `action`: what to do with each rosteritem that "
597
                        "matches all the filtering options\n"
598
                        "* `subs`: subscription type\n"
599
                        "* `asks`: pending subscription\n"
600
                        "* `users`: the JIDs of the local user\n"
601
                        "* `contacts`: the JIDs of the contact in the roster\n"
602
                        "\n"
603
                        "**Mnesia backend:**\n"
604
                        "\n"
605
                        "Allowed values in the arguments:\n\n"
606
                        "* `action` = `list` | `delete`\n"
607
                        "* `subs` = `any` | SUB[:SUB]*\n"
608
                        "* `asks` = `any` | ASK[:ASK]*\n"
609
                        "* `users` = `any` | JID[:JID]*\n"
610
                        "* `contacts` = `any` | JID[:JID]*\n"
611
                        "\nwhere\n\n"
612
                        "* SUB = `none` | `from `| `to` | `both`\n"
613
                        "* ASK = `none` | `out` | `in`\n"
614
                        "* JID = characters valid in a JID, and can use the "
615
                        "globs: `*`, `?`, `!` and `[...]`\n"
616
                        "\n"
617
                        "This example will list roster items with subscription "
618
                        "`none`, `from` or `to` that have any ask property, of "
619
                        "local users which JID is in the virtual host "
620
                        "`example.org` and that the contact JID is either a "
621
                        "bare server name (without user part) or that has a "
622
                        "user part and the server part contains the word `icq`"
623
                        ":\n  `list none:from:to any *@example.org *:*@*icq*`"
624
                        "\n\n"
625
                        "**SQL backend:**\n"
626
                        "\n"
627
                        "Allowed values in the arguments:\n\n"
628
                        "* `action` = `list` | `delete`\n"
629
                        "* `subs` = `any` | SUB\n"
630
                        "* `asks` = `any` | ASK\n"
631
                        "* `users` = JID\n"
632
                        "* `contacts` = JID\n"
633
                        "\nwhere\n\n"
634
                        "* SUB = `none` | `from` | `to` | `both`\n"
635
                        "* ASK = `none` | `out` | `in`\n"
636
                        "* JID = characters valid in a JID, and can use the "
637
                        "globs: `_` and `%`\n"
638
                        "\n"
639
                        "This example will list roster items with subscription "
640
                        "`to` that have any ask property, of "
641
                        "local users which JID is in the virtual host "
642
                        "`example.org` and that the contact JID's "
643
                        "server part contains the word `icq`"
644
                        ":\n  `list to any %@example.org %@%icq%`",
645
                        module = mod_roster, function = process_rosteritems,
646
                        args = [{action, string}, {subs, string},
647
                                {asks, string}, {users, string},
648
                                {contacts, string}],
649
                        result = {response,
650
                                  {list,
651
                                   {pairs, {tuple,
652
                                               [{user, string},
653
                                                {contact, string}
654
                                               ]}}
655
                                  }}},
656
     #ejabberd_commands{name = get_roster, tags = [roster],
657
                        desc = "Get list of contacts in a local user roster",
658
                        longdesc =
659
                            "`subscription` can be: `none`, `from`, `to`, `both`.\n\n"
660
                            "`pending` can be: `in`, `out`, `none`.",
661
                        note = "improved in 23.10",
662
                        policy = user,
663
                        module = ?MODULE, function = get_roster,
664
                        args = [],
665
                        args_rename = [{server, host}],
666
                        result = {contacts, {list, {contact, {tuple, [
667
                                                                      {jid, string},
668
                                                                      {nick, string},
669
                                                                      {subscription, string},
670
                                                                      {pending, string},
671
                                                                      {groups, {list, {group, string}}}
672
                                                                     ]}}}}},
673
     #ejabberd_commands{name = push_roster, tags = [roster],
674
                        desc = "Push template roster from file to a user",
675
                        longdesc = "The text file must contain an erlang term: a list "
676
                            "of tuples with username, servername, group and nick. For example:\n"
677
                            "`[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n"
678
                            " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].`\n\n"
679
                            "If there are problems parsing UTF8 character encoding, "
680
                            "provide the corresponding string with the `<<\"STRING\"/utf8>>` syntax, for example:\n"
681
                            "`[{\"user2\", \"localhost\", \"Workers\", <<\"User 2\"/utf8>>}]`.",
682
                        module = ?MODULE, function = push_roster,
683
                        args = [{file, binary}, {user, binary}, {host, binary}],
684
                        args_example = [<<"/home/ejabberd/roster.txt">>, <<"user1">>, <<"localhost">>],
685
                        args_desc = ["File path", "User name", "Server name"],
686
                        result = {res, rescode}},
687
     #ejabberd_commands{name = push_roster_all, tags = [roster],
688
                        desc = "Push template roster from file to all those users",
689
                        longdesc = "The text file must contain an erlang term: a list "
690
                            "of tuples with username, servername, group and nick. Example:\n"
691
                            "`[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n"
692
                            " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].`",
693
                        module = ?MODULE, function = push_roster_all,
694
                        args = [{file, binary}],
695
                        args_example = [<<"/home/ejabberd/roster.txt">>],
696
                        args_desc = ["File path"],
697
                        result = {res, rescode}},
698
     #ejabberd_commands{name = push_alltoall, tags = [roster],
699
                        desc = "Add all the users to all the users of Host in Group",
700
                        module = ?MODULE, function = push_alltoall,
701
                        args = [{host, binary}, {group, binary}],
702
                        args_example = [<<"myserver.com">>,<<"Everybody">>],
703
                        args_desc = ["Server name", "Group name"],
704
                        result = {res, rescode}},
705

706
     #ejabberd_commands{name = get_last, tags = [last],
707
                        desc = "Get last activity information",
708
                        longdesc = "Timestamp is UTC and "
709
                            "[XEP-0082](https://xmpp.org/extensions/xep-0082.html)"
710
                            " format, for example: "
711
                            "`2017-02-23T22:25:28.063062Z     ONLINE`",
712
                        module = ?MODULE, function = get_last,
713
                        args = [{user, binary}, {host, binary}],
714
                        args_example = [<<"user1">>,<<"myserver.com">>],
715
                        args_desc = ["User name", "Server name"],
716
                        result_example = {<<"2017-06-30T14:32:16.060684Z">>, "ONLINE"},
717
                        result_desc = "Last activity timestamp and status",
718
                        result = {last_activity,
719
                                  {tuple, [{timestamp, string},
720
                                           {status, string}
721
                                          ]}}},
722
     #ejabberd_commands{name = set_last, tags = [last],
723
                        desc = "Set last activity information",
724
                        longdesc = "Timestamp is the seconds since "
725
                        "`1970-01-01 00:00:00 UTC`. For example value see `date +%s`",
726
                        module = ?MODULE, function = set_last,
727
                        args = [{user, binary}, {host, binary}, {timestamp, integer}, {status, binary}],
728
                        args_example = [<<"user1">>,<<"myserver.com">>, 1500045311, <<"GoSleeping">>],
729
                        args_desc = ["User name", "Server name", "Number of seconds since epoch", "Status message"],
730
                        result = {res, rescode}},
731

732
     #ejabberd_commands{name = private_get, tags = [private],
733
                        desc = "Get some information from a user private storage",
734
                        module = ?MODULE, function = private_get,
735
                        args = [{user, binary}, {host, binary}, {element, binary}, {ns, binary}],
736
                        args_example = [<<"user1">>,<<"myserver.com">>,<<"storage">>, <<"storage:rosternotes">>],
737
                        args_desc = ["User name", "Server name", "Element name", "Namespace"],
738
                        result = {res, string}},
739
     #ejabberd_commands{name = private_set, tags = [private],
740
                        desc = "Set to the user private storage",
741
                        module = ?MODULE, function = private_set,
742
                        args = [{user, binary}, {host, binary}, {element, binary}],
743
                        args_example = [<<"user1">>,<<"myserver.com">>,
744
                            <<"<storage xmlns='storage:rosternotes'/>">>],
745
                        args_desc = ["User name", "Server name", "XML storage element"],
746
                        result = {res, rescode}},
747

748
     #ejabberd_commands{name = srg_create, tags = [shared_roster_group],
749
                        desc = "Create a Shared Roster Group",
750
                        longdesc = "If you want to specify several group "
751
                        "identifiers in the Display argument,\n"
752
                        "put `\\ \"` around the argument and\nseparate the "
753
                        "identifiers with `\\ \\ n`\n"
754
                        "For example:\n"
755
                        "  `ejabberdctl srg_create group3 myserver.com "
756
                        "name desc \\\"group1\\\\ngroup2\\\"`",
757
                        note = "changed in 21.07",
758
                        module = ?MODULE, function = srg_create,
759
                        args = [{group, binary}, {host, binary},
760
                                {label, binary}, {description, binary}, {display, binary}],
761
                        args_rename = [{name, label}],
762
                        args_example = [<<"group3">>, <<"myserver.com">>, <<"Group3">>,
763
                                <<"Third group">>, <<"group1\\\\ngroup2">>],
764
                        args_desc = ["Group identifier", "Group server name", "Group name",
765
                                "Group description", "Groups to display"],
766
                        result = {res, rescode}},
767
     #ejabberd_commands{name = srg_create, tags = [shared_roster_group],
768
                        desc = "Create a Shared Roster Group",
769
                        module = ?MODULE, function = srg_create,
770
                        version = 1,
771
                        note = "updated in 24.02",
772
                        args = [{group, binary}, {host, binary},
773
                                {label, binary}, {description, binary}, {display, {list, {group, binary}}}],
774
                        args_rename = [{name, label}],
775
                        args_example = [<<"group3">>, <<"myserver.com">>, <<"Group3">>,
776
                                <<"Third group">>, [<<"group1">>, <<"group2">>]],
777
                        args_desc = ["Group identifier", "Group server name", "Group name",
778
                                "Group description", "List of groups to display"],
779
                        result = {res, rescode}},
780
     #ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
781
                        desc = "Delete a Shared Roster Group",
782
                        module = ?MODULE, function = srg_delete,
783
                        args = [{group, binary}, {host, binary}],
784
                        args_example = [<<"group3">>, <<"myserver.com">>],
785
                        args_desc = ["Group identifier", "Group server name"],
786
                        result = {res, rescode}},
787
     #ejabberd_commands{name = srg_list, tags = [shared_roster_group],
788
                        desc = "List the Shared Roster Groups in Host",
789
                        module = ?MODULE, function = srg_list,
790
                        args = [{host, binary}],
791
                        args_example = [<<"myserver.com">>],
792
                        args_desc = ["Server name"],
793
                        result_example = [<<"group1">>, <<"group2">>],
794
                        result_desc = "List of group identifiers",
795
                        result = {groups, {list, {id, string}}}},
796
     #ejabberd_commands{name = srg_get_info, tags = [shared_roster_group],
797
                        desc = "Get info of a Shared Roster Group",
798
                        module = ?MODULE, function = srg_get_info,
799
                        args = [{group, binary}, {host, binary}],
800
                        args_example = [<<"group3">>, <<"myserver.com">>],
801
                        args_desc = ["Group identifier", "Group server name"],
802
                        result_example = [{<<"name">>, "Group 3"}, {<<"displayed_groups">>, "group1"}],
803
                        result_desc = "List of group information, as key and value",
804
                        result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
805
     #ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
806
                        desc = "Get members of a Shared Roster Group",
807
                        module = ?MODULE, function = srg_get_members,
808
                        args = [{group, binary}, {host, binary}],
809
                        args_example = [<<"group3">>, <<"myserver.com">>],
810
                        args_desc = ["Group identifier", "Group server name"],
811
                        result_example = [<<"user1@localhost">>, <<"user2@localhost">>],
812
                        result_desc = "List of group identifiers",
813
                        result = {members, {list, {member, string}}}},
814
     #ejabberd_commands{name = srg_user_add, tags = [shared_roster_group],
815
                        desc = "Add the JID user@host to the Shared Roster Group",
816
                        module = ?MODULE, function = srg_user_add,
817
                        args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}],
818
                        args_example = [<<"user1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
819
                        args_desc = ["Username", "User server name", "Group identifier", "Group server name"],
820
                        result = {res, rescode}},
821
     #ejabberd_commands{name = srg_user_del, tags = [shared_roster_group],
822
                        desc = "Delete this JID user@host from the Shared Roster Group",
823
                        module = ?MODULE, function = srg_user_del,
824
                        args = [{user, binary}, {host, binary}, {group, binary}, {grouphost, binary}],
825
                        args_example = [<<"user1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
826
                        args_desc = ["Username", "User server name", "Group identifier", "Group server name"],
827
                        result = {res, rescode}},
828

829
     #ejabberd_commands{name = get_offline_count,
830
                        tags = [offline],
831
                        desc = "Get the number of unread offline messages",
832
                        policy = user,
833
                        module = mod_offline, function = count_offline_messages,
834
                        args = [],
835
                        args_rename = [{server, host}],
836
                        result_example = 5,
837
                        result_desc = "Number",
838
                        result = {value, integer}},
839
     #ejabberd_commands{name = send_message, tags = [stanza],
840
                        desc = "Send a message to a local or remote bare of full JID",
841
                        longdesc = "When sending a groupchat message to a MUC room, "
842
                        "`from` must be the full JID of a room occupant, "
843
                        "or the bare JID of a MUC service admin, "
844
                        "or the bare JID of a MUC/Sub subscribed user.",
845
                        module = ?MODULE, function = send_message,
846
                        args = [{type, binary}, {from, binary}, {to, binary},
847
                                {subject, binary}, {body, binary}],
848
                        args_example = [<<"headline">>, <<"admin@localhost">>, <<"user1@localhost">>,
849
                                <<"Restart">>, <<"In 5 minutes">>],
850
                        args_desc = ["Message type: `normal`, `chat`, `headline`, `groupchat`", "Sender JID",
851
                                "Receiver JID", "Subject, or empty string", "Body"],
852
                        result = {res, rescode}},
853
     #ejabberd_commands{name = send_stanza_c2s, tags = [stanza],
854
                        desc = "Send a stanza from an existing C2S session",
855
                        longdesc = "`user`@`host`/`resource` must be an existing C2S session."
856
                        " As an alternative, use _`send_stanza`_ API instead.",
857
                        module = ?MODULE, function = send_stanza_c2s,
858
                        args = [{user, binary}, {host, binary}, {resource, binary}, {stanza, binary}],
859
                        args_example = [<<"admin">>, <<"myserver.com">>, <<"bot">>,
860
                                <<"<message to='user1@localhost'><ext attr='value'/></message>">>],
861
                        args_desc = ["Username", "Server name", "Resource", "Stanza"],
862
                        result = {res, rescode}},
863
     #ejabberd_commands{name = send_stanza, tags = [stanza],
864
                        desc = "Send a stanza; provide From JID and valid To JID",
865
                        module = ?MODULE, function = send_stanza,
866
                        args = [{from, binary}, {to, binary}, {stanza, binary}],
867
                        args_example = [<<"admin@localhost">>, <<"user1@localhost">>,
868
                                <<"<message><ext attr='value'/></message>">>],
869
                        args_desc = ["Sender JID", "Destination JID", "Stanza"],
870
                        result = {res, rescode}},
871
     #ejabberd_commands{name = privacy_set, tags = [stanza],
872
                        desc = "Send a IQ set privacy stanza for a local account",
873
                        module = ?MODULE, function = privacy_set,
874
                        args = [{user, binary}, {host, binary}, {xmlquery, binary}],
875
                        args_example = [<<"user1">>, <<"myserver.com">>,
876
                                <<"<query xmlns='jabber:iq:privacy'>...">>],
877
                        args_desc = ["Username", "Server name", "Query XML element"],
878
                        result = {res, rescode}},
879

880
     #ejabberd_commands{name = stats, tags = [statistics],
881
                        desc = "Get some statistical value for the whole ejabberd server",
882
                        longdesc = "Allowed statistics `name` are: `registeredusers`, "
883
                            "`onlineusers`, `onlineusersnode`, `uptimeseconds`, `processes`.",
884
                        policy = admin,
885
                        module = ?MODULE, function = stats,
886
                        args = [{name, binary}],
887
                        args_example = [<<"registeredusers">>],
888
                        args_desc = ["Statistic name"],
889
                        result_example = 6,
890
                        result_desc = "Integer statistic value",
891
                        result = {stat, integer}},
892
     #ejabberd_commands{name = stats_host, tags = [statistics],
893
                        desc = "Get some statistical value for this host",
894
                        longdesc = "Allowed statistics `name` are: `registeredusers`, `onlineusers`.",
895
                        policy = admin,
896
                        module = ?MODULE, function = stats,
897
                        args = [{name, binary}, {host, binary}],
898
                        args_example = [<<"registeredusers">>, <<"example.com">>],
899
                        args_desc = ["Statistic name", "Server JID"],
900
                        result_example = 6,
901
                        result_desc = "Integer statistic value",
902
                        result = {stat, integer}}
903
    ].
904

905

906
%%%
907
%%% Adminsys
908
%%%
909

910
compile(File) ->
911
    Ebin = filename:join(code:lib_dir(ejabberd), "ebin"),
×
912
    case ext_mod:compile_erlang_file(Ebin, File) of
×
913
        {ok, Module} ->
914
            code:purge(Module),
×
915
            code:load_file(Module),
×
916
            ok;
×
917
        _ ->
918
            error
×
919
    end.
920

921
get_cookie() ->
922
    atom_to_list(erlang:get_cookie()).
×
923

924
restart_module(Host, Module) when is_binary(Module) ->
925
    restart_module(Host, misc:binary_to_atom(Module));
×
926
restart_module(Host, Module) when is_atom(Module) ->
927
    case gen_mod:is_loaded(Host, Module) of
×
928
        false ->
929
            % not a running module, force code reload anyway
930
            code:purge(Module),
×
931
            code:delete(Module),
×
932
            code:load_file(Module),
×
933
            1;
×
934
        true ->
935
            gen_mod:stop_module(Host, Module),
×
936
            case code:soft_purge(Module) of
×
937
                true ->
938
                    code:delete(Module),
×
939
                    code:load_file(Module),
×
940
                    gen_mod:start_module(Host, Module),
×
941
                    0;
×
942
                false ->
943
                    gen_mod:start_module(Host, Module),
×
944
                    2
×
945
            end
946
    end.
947

948
%%%
949
%%% Accounts
950
%%%
951

952
set_password(User, Host, Password) ->
953
    Fun = fun () -> ejabberd_auth:set_password(User, Host, Password) end,
×
954
    user_action(User, Host, Fun, ok).
×
955

956
check_password(User, Host, Password) ->
957
    ejabberd_auth:check_password(User, <<>>, Host, Password).
×
958

959
%% Copied some code from ejabberd_commands.erln
960
check_password_hash(User, Host, PasswordHash, HashMethod) ->
961
    AccountPass = ejabberd_auth:get_password_s(User, Host),
×
962
    Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
×
963
                   proplists:get_value(hashs, crypto:supports())),
964
    MethodAllowed = lists:member(HashMethod, Methods),
×
965
    AccountPassHash = case {AccountPass, MethodAllowed} of
×
966
                          {A, _} when is_tuple(A) -> scrammed;
×
967
                          {_, true} -> get_hash(AccountPass, HashMethod);
×
968
                          {_, false} ->
969
                              ?ERROR_MSG("Check_password_hash called "
×
970
                                         "with hash method: ~p", [HashMethod]),
×
971
                              undefined
×
972
                      end,
973
    case AccountPassHash of
×
974
        scrammed ->
975
            ?ERROR_MSG("Passwords are scrammed, and check_password_hash cannot work.", []),
×
976
            throw(passwords_scrammed_command_cannot_work);
×
977
        undefined -> throw(unkown_hash_method);
×
978
        PasswordHash -> ok;
×
979
        _ -> false
×
980
    end.
981

982
get_hash(AccountPass, Method) ->
983
    iolist_to_binary([io_lib:format("~2.16.0B", [X])
×
984
          || X <- binary_to_list(
×
985
              crypto:hash(binary_to_atom(Method, latin1), AccountPass))]).
986

987
delete_old_users(Days) ->
988
    %% Get the list of registered users
989
    Users = ejabberd_auth:get_users(),
×
990

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

994
delete_old_users_vhost(Host, Days) ->
995
    %% Get the list of registered users
996
    Users = ejabberd_auth:get_users(Host),
×
997

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

1001
delete_old_users(Days, Users) ->
1002
    SecOlder = Days*24*60*60,
×
1003
    TimeStamp_now = erlang:system_time(second),
×
1004
    TimeStamp_oldest = TimeStamp_now - SecOlder,
×
1005
    F = fun({LUser, LServer}) ->
×
1006
            case catch delete_or_not(LUser, LServer, TimeStamp_oldest) of
×
1007
                true ->
1008
                    ejabberd_auth:remove_user(LUser, LServer),
×
1009
                    true;
×
1010
                _ ->
1011
                    false
×
1012
            end
1013
        end,
1014
    Users_removed = lists:filter(F, Users),
×
1015
    {removed, length(Users_removed), Users_removed}.
×
1016

1017
delete_or_not(LUser, LServer, TimeStamp_oldest) ->
1018
    deny = acl:match_rule(LServer, protect_old_users, jid:make(LUser, LServer)),
×
1019
    [] = ejabberd_sm:get_user_resources(LUser, LServer),
×
1020
    case mod_last:get_last_info(LUser, LServer) of
×
1021
        {ok, TimeStamp, _Status} ->
1022
            if TimeStamp_oldest < TimeStamp ->
×
1023
                    false;
×
1024
                true ->
1025
                    true
×
1026
            end;
1027
        not_found ->
1028
            true
×
1029
    end.
1030

1031
%%
1032
%% Ban account v0
1033

1034
ban_account(User, Host, ReasonText) ->
1035
    Reason = prepare_reason(ReasonText),
×
1036
    kick_sessions(User, Host, Reason),
×
1037
    set_random_password(User, Host, Reason),
×
1038
    ok.
×
1039

1040
kick_sessions(User, Server, Reason) ->
1041
    lists:map(
×
1042
      fun(Resource) ->
1043
              kick_this_session(User, Server, Resource, Reason)
×
1044
      end,
1045
      ejabberd_sm:get_user_resources(User, Server)).
1046

1047
set_random_password(User, Server, Reason) ->
1048
    NewPass = build_random_password(Reason),
×
1049
    set_password_auth(User, Server, NewPass).
×
1050

1051
build_random_password(Reason) ->
1052
    {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:universal_time(),
×
1053
    Date = str:format("~4..0B~2..0B~2..0BT~2..0B:~2..0B:~2..0B",
×
1054
                      [Year, Month, Day, Hour, Minute, Second]),
1055
    RandomString = p1_rand:get_string(),
×
1056
    <<"BANNED_ACCOUNT--", Date/binary, "--", RandomString/binary, "--", Reason/binary>>.
×
1057

1058
set_password_auth(User, Server, Password) ->
1059
    ok = ejabberd_auth:set_password(User, Server, Password).
×
1060

1061
prepare_reason([]) ->
1062
    <<"Kicked by administrator">>;
×
1063
prepare_reason([Reason]) ->
1064
    Reason;
×
1065
prepare_reason(Reason) when is_binary(Reason) ->
1066
    Reason.
×
1067

1068
%%
1069
%% Ban account v2
1070

1071
ban_account_v2(User, Host, ReasonText) ->
1072
    case gen_mod:is_loaded(Host, mod_private) of
×
1073
        false ->
1074
            mod_private_is_required_but_disabled;
×
1075
        true ->
1076
            case is_banned(User, Host) of
×
1077
                true ->
1078
                    account_was_already_banned;
×
1079
                false ->
1080
                    ban_account_v2_b(User, Host, ReasonText)
×
1081
            end
1082
    end.
1083

1084
ban_account_v2_b(User, Host, ReasonText) ->
1085
    Reason = prepare_reason(ReasonText),
×
1086
    Pass = ejabberd_auth:get_password_s(User, Host),
×
1087
    Last = get_last(User, Host),
×
1088
    BanDate = xmpp_util:encode_timestamp(erlang:timestamp()),
×
1089
    Hash = get_hash_value(User, Host),
×
1090
    BanPrivateXml = build_ban_xmlel(Reason, Pass, Last, BanDate, Hash),
×
1091
    ok = private_set2(User, Host, BanPrivateXml),
×
1092
    ok = set_random_password_v2(User, Host),
×
1093
    kick_sessions(User, Host, Reason),
×
1094
    ok.
×
1095

1096
get_hash_value(User, Host) ->
1097
    Cookie = misc:atom_to_binary(erlang:get_cookie()),
1,176✔
1098
    misc:term_to_base64(crypto:hash(sha256, <<User/binary, Host/binary, Cookie/binary>>)).
1,176✔
1099

1100
set_random_password_v2(User, Server) ->
1101
    NewPass = p1_rand:get_string(),
×
1102
    ok = ejabberd_auth:set_password(User, Server, NewPass).
×
1103

1104
build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate, Hash) ->
1105
    PassEls = build_pass_els(Pass),
×
1106
    #xmlel{name = <<"banned">>,
×
1107
           attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}],
1108
           children = [#xmlel{name = <<"reason">>, attrs = [], children = [{xmlcdata, Reason}]},
1109
                       #xmlel{name = <<"password">>, attrs = [], children = PassEls},
1110
                       #xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]},
1111
                       #xmlel{name = <<"lastreason">>, attrs = [], children = [{xmlcdata, LastReason}]},
1112
                       #xmlel{name = <<"bandate">>, attrs = [], children = [{xmlcdata, BanDate}]},
1113
                       #xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, Hash}]}
1114
                       ]}.
1115

1116
build_pass_els(Pass) when is_binary(Pass) ->
1117
    [{xmlcdata, Pass}];
×
1118
build_pass_els(#scram{storedkey = StoredKey,
1119
                      serverkey = ServerKey,
1120
                      salt = Salt,
1121
                      hash = Hash,
1122
                      iterationcount = IterationCount}) ->
1123
    [#xmlel{name = <<"storedkey">>, attrs = [], children = [{xmlcdata, StoredKey}]},
×
1124
     #xmlel{name = <<"serverkey">>, attrs = [], children = [{xmlcdata, ServerKey}]},
1125
     #xmlel{name = <<"salt">>, attrs = [], children = [{xmlcdata, Salt}]},
1126
     #xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, misc:atom_to_binary(Hash)}]},
1127
     #xmlel{name = <<"iterationcount">>, attrs = [], children = [{xmlcdata, integer_to_binary(IterationCount)}]}
1128
    ].
1129

1130
%%
1131
%% Get ban details
1132

1133
get_ban_details(User, Host) ->
1134
    case private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>) of
1,294✔
1135
        [El] ->
1136
            get_ban_details(User, Host, El);
1,176✔
1137
        [] ->
1138
            []
118✔
1139
    end.
1140

1141
get_ban_details(User, Host, El) ->
1142
    Reason = fxml:get_subtag_cdata(El, <<"reason">>),
1,176✔
1143
    LastDate = fxml:get_subtag_cdata(El, <<"lastdate">>),
1,176✔
1144
    LastReason = fxml:get_subtag_cdata(El, <<"lastreason">>),
1,176✔
1145
    BanDate = fxml:get_subtag_cdata(El, <<"bandate">>),
1,176✔
1146
    Hash = fxml:get_subtag_cdata(El, <<"hash">>),
1,176✔
1147
    case Hash == get_hash_value(User, Host) of
1,176✔
1148
        true ->
1149
            [{"reason", Reason},
×
1150
             {"bandate", BanDate},
1151
             {"lastdate", LastDate},
1152
             {"lastreason", LastReason}];
1153
        false ->
1154
            []
1,176✔
1155
    end.
1156

1157
is_banned(User, Host) ->
1158
    case lists:keyfind("bandate", 1, get_ban_details(User, Host)) of
×
1159
        {_, BanDate} when BanDate /= <<>> ->
1160
            true;
×
1161
        _ ->
1162
            false
×
1163
    end.
1164

1165
%%
1166
%% Unban account
1167

1168
unban_account(User, Host) ->
1169
    case gen_mod:is_loaded(Host, mod_private) of
×
1170
        false ->
1171
            mod_private_is_required_but_disabled;
×
1172
        true ->
1173
            case is_banned(User, Host) of
×
1174
                false ->
1175
                    account_was_not_banned;
×
1176
                true ->
1177
                    unban_account2(User, Host)
×
1178
            end
1179
    end.
1180

1181
unban_account2(User, Host) ->
1182
    OldPass = get_oldpass(User, Host),
×
1183
    ok = ejabberd_auth:set_password(User, Host, OldPass),
×
1184
    UnBanPrivateXml = build_unban_xmlel(),
×
1185
    private_set2(User, Host, UnBanPrivateXml).
×
1186

1187
get_oldpass(User, Host) ->
1188
    [El] = private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>),
×
1189
    Pass = fxml:get_subtag(El, <<"password">>),
×
1190
    get_pass(Pass).
×
1191

1192
get_pass(#xmlel{children = [{xmlcdata, Pass}]}) ->
1193
    Pass;
×
1194
get_pass(#xmlel{children = ScramEls} = Pass) when is_list(ScramEls) ->
1195
    StoredKey = fxml:get_subtag_cdata(Pass, <<"storedkey">>),
×
1196
    ServerKey = fxml:get_subtag_cdata(Pass, <<"serverkey">>),
×
1197
    Salt = fxml:get_subtag_cdata(Pass, <<"salt">>),
×
1198
    Hash = fxml:get_subtag_cdata(Pass, <<"hash">>),
×
1199
    IterationCount = fxml:get_subtag_cdata(Pass, <<"iterationcount">>),
×
1200
    #scram{storedkey = StoredKey,
×
1201
           serverkey = ServerKey,
1202
           salt = Salt,
1203
           hash = binary_to_existing_atom(Hash, latin1),
1204
           iterationcount = binary_to_integer(IterationCount)}.
1205

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

1209
%%%
1210
%%% Sessions
1211
%%%
1212

1213
num_resources(User, Host) ->
1214
    length(ejabberd_sm:get_user_resources(User, Host)).
×
1215

1216
resource_num(User, Host, Num) ->
1217
    Resources = ejabberd_sm:get_user_resources(User, Host),
×
1218
    case (0<Num) and (Num=<length(Resources)) of
×
1219
        true ->
1220
            lists:nth(Num, Resources);
×
1221
        false ->
1222
            throw({bad_argument,
×
1223
                   lists:flatten(io_lib:format("Wrong resource number: ~p", [Num]))})
1224
    end.
1225

1226
kick_session(User, Server, Resource, ReasonText) ->
1227
    kick_this_session(User, Server, Resource, prepare_reason(ReasonText)),
×
1228
    ok.
×
1229

1230
kick_this_session(User, Server, Resource, Reason) ->
1231
    ejabberd_sm:route(jid:make(User, Server, Resource),
×
1232
                      {exit, Reason}).
1233

1234
status_num(Host, Status) ->
1235
    length(get_status_list(Host, Status)).
×
1236
status_num(Status) ->
1237
    status_num(<<"all">>, Status).
×
1238
status_list(Host, Status) ->
1239
    Res = get_status_list(Host, Status),
×
1240
    [{U, S, R, num_prio(P), St} || {U, S, R, P, St} <- Res].
×
1241
status_list(Status) ->
1242
    status_list(<<"all">>, Status).
×
1243

1244

1245
get_status_list(Host, Status_required) ->
1246
    %% Get list of all logged users
1247
    Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
×
1248
    %% Reformat the list
1249
    Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
×
1250
    Fhost = case Host of
×
1251
                <<"all">> ->
1252
                    %% All hosts are requested, so don't filter at all
1253
                    fun(_, _) -> true end;
×
1254
                _ ->
1255
                    %% Filter the list, only Host is interesting
1256
                    fun(A, B) -> A == B end
×
1257
            end,
1258
    Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
×
1259
    %% For each Pid, get its presence
1260
    Sessions4 = [ {catch get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
×
1261
    %% Filter by status
1262
    Fstatus = case Status_required of
×
1263
                  <<"all">> ->
1264
                      fun(_, _) -> true end;
×
1265
                  _ ->
1266
                      fun(A, B) -> A == B end
×
1267
              end,
1268
    [{User, Server, Resource, num_prio(Priority), stringize(Status_text)}
×
1269
     || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
×
1270
        apply(Fstatus, [Status, Status_required])].
×
1271

1272
connected_users_info() ->
1273
    lists:filtermap(
×
1274
      fun({U, S, R}) ->
1275
            case user_session_info(U, S, R) of
×
1276
                offline ->
1277
                    false;
×
1278
                Info ->
1279
                    Jid = jid:encode(jid:make(U, S, R)),
×
1280
                    {true, erlang:insert_element(1, Info, Jid)}
×
1281
            end
1282
      end,
1283
      ejabberd_sm:dirty_get_sessions_list()).
1284

1285
connected_users_vhost(Host) ->
1286
    USRs = ejabberd_sm:get_vh_session_list(Host),
×
1287
    [ jid:encode(jid:make(USR)) || USR <- USRs].
×
1288

1289
%% Make string more print-friendly
1290
stringize(String) ->
1291
    %% Replace newline characters with other code
1292
    ejabberd_regexp:greplace(String, <<"\n">>, <<"\\n">>).
×
1293

1294
get_presence(Pid) ->
1295
    try get_presence2(Pid) of
×
1296
        {_, _, _, _} = Res ->
1297
            Res
×
1298
    catch
1299
        _:_ -> {<<"">>, <<"">>, <<"offline">>, <<"">>}
×
1300
    end.
1301
get_presence2(Pid) ->
1302
    Pres = #presence{from = From} = ejabberd_c2s:get_presence(Pid),
×
1303
    Show = case Pres of
×
1304
               #presence{type = unavailable} -> <<"unavailable">>;
×
1305
               #presence{show = undefined} -> <<"available">>;
×
1306
               #presence{show = S} -> atom_to_binary(S, utf8)
×
1307
           end,
1308
    Status = xmpp:get_text(Pres#presence.status),
×
1309
    {From#jid.user, From#jid.resource, Show, Status}.
×
1310

1311
get_presence(U, S) ->
1312
    Pids = [ejabberd_sm:get_session_pid(U, S, R)
×
1313
            || R <- ejabberd_sm:get_user_resources(U, S)],
×
1314
    OnlinePids = [Pid || Pid <- Pids, Pid=/=none],
×
1315
    case OnlinePids of
×
1316
        [] ->
1317
            {jid:encode({U, S, <<>>}), <<"unavailable">>, <<"">>};
×
1318
        [SessionPid|_] ->
1319
            {_User, Resource, Show, Status} = get_presence(SessionPid),
×
1320
            FullJID = jid:encode({U, S, Resource}),
×
1321
            {FullJID, Show, Status}
×
1322
    end.
1323

1324
set_presence(User, Host, Resource, Type, Show, Status, Priority) when is_binary(Priority) ->
1325
    set_presence(User, Host, Resource, Type, Show, Status, binary_to_integer(Priority));
×
1326

1327
set_presence(User, Host, Resource, Type, Show, Status, Priority) ->
1328
    Pres = #presence{
×
1329
        from = jid:make(User, Host, Resource),
1330
        to = jid:make(User, Host),
1331
        type = misc:binary_to_atom(Type),
1332
        status = xmpp:mk_text(Status),
1333
        show = misc:binary_to_atom(Show),
1334
        priority = Priority,
1335
        sub_els = []},
1336
    Ref = ejabberd_sm:get_session_pid(User, Host, Resource),
×
1337
    ejabberd_c2s:set_presence(Ref, Pres).
×
1338

1339
user_sessions_info(User, Host) ->
1340
    lists:filtermap(fun(Resource) ->
×
1341
                            case user_session_info(User, Host, Resource) of
×
1342
                                offline -> false;
×
1343
                                Info -> {true, Info}
×
1344
                            end
1345
                    end, ejabberd_sm:get_user_resources(User, Host)).
1346

1347
user_session_info(User, Host, Resource) ->
1348
    CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
×
1349
    case ejabberd_sm:get_user_info(User, Host, Resource) of
×
1350
        offline ->
1351
            offline;
×
1352
        Info ->
1353
            Now = proplists:get_value(ts, Info),
×
1354
            Pid = proplists:get_value(pid, Info),
×
1355
            {_U, _Resource, Status, StatusText} = get_presence(Pid),
×
1356
            Priority = proplists:get_value(priority, Info),
×
1357
            Conn = proplists:get_value(conn, Info),
×
1358
            {Ip, Port} = proplists:get_value(ip, Info),
×
1359
            IPS = inet_parse:ntoa(Ip),
×
1360
            NodeS = atom_to_list(node(Pid)),
×
1361
            Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
×
1362
                                    calendar:now_to_local_time(Now)),
1363
            {atom_to_list(Conn), IPS, Port, num_prio(Priority), NodeS, Uptime, Status, Resource, StatusText}
×
1364
    end.
1365

1366

1367
%%%
1368
%%% Vcard
1369
%%%
1370

1371
set_nickname(User, Host, Nickname) ->
1372
    VCard = xmpp:encode(#vcard_temp{nickname = Nickname}),
×
1373
    case mod_vcard:set_vcard(User, jid:nameprep(Host), VCard) of
×
1374
        {error, badarg} ->
1375
            error;
×
1376
        ok ->
1377
            ok
×
1378
    end.
1379

1380
get_vcard(User, Host, Name) ->
1381
    [Res | _] = get_vcard_content(User, Host, [Name]),
×
1382
    Res.
×
1383

1384
get_vcard(User, Host, Name, Subname) ->
1385
    [Res | _] = get_vcard_content(User, Host, [Name, Subname]),
×
1386
    Res.
×
1387

1388
get_vcard_multi(User, Host, Name, Subname) ->
1389
    get_vcard_content(User, Host, [Name, Subname]).
×
1390

1391
set_vcard(User, Host, Name, SomeContent) ->
1392
    set_vcard_content(User, Host, [Name], SomeContent).
×
1393

1394
set_vcard(User, Host, Name, Subname, SomeContent) ->
1395
    set_vcard_content(User, Host, [Name, Subname], SomeContent).
×
1396

1397
%%
1398
%% Room vcard
1399

1400
is_muc_service(Domain) ->
1401
    try mod_muc_admin:get_room_serverhost(Domain) of
×
1402
        Domain -> false;
×
1403
        Service when is_binary(Service) -> true
×
1404
    catch _:{unregistered_route, _} ->
1405
            throw(error_wrong_hostname)
×
1406
    end.
1407

1408
get_room_vcard(Name, Service) ->
1409
    case mod_muc_admin:get_room_options(Name, Service) of
×
1410
        [] ->
1411
            throw(error_no_vcard_found);
×
1412
        Opts ->
1413
            case lists:keyfind(<<"vcard">>, 1, Opts) of
×
1414
                false ->
1415
                    throw(error_no_vcard_found);
×
1416
                {_, VCardRaw} ->
1417
                    [fxml_stream:parse_element(VCardRaw)]
×
1418
            end
1419
    end.
1420

1421
%%
1422
%% Internal vcard
1423

1424
get_vcard_content(User, Server, Data) ->
1425
    case get_vcard_element(User, Server) of
×
1426
        [El|_] ->
1427
            case get_vcard(Data, El) of
×
1428
                [false] -> throw(error_no_value_found_in_vcard);
×
1429
                ElemList -> ?DEBUG("ELS ~p", [ElemList]), [fxml:get_tag_cdata(Elem) || Elem <- ElemList]
×
1430
            end;
1431
        [] ->
1432
            throw(error_no_vcard_found);
×
1433
        error ->
1434
            throw(database_failure)
×
1435
    end.
1436

1437
get_vcard_element(User, Server) ->
1438
    case is_muc_service(Server) of
×
1439
        true ->
1440
           get_room_vcard(User, Server);
×
1441
        false ->
1442
           mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server))
×
1443
    end.
1444

1445
get_vcard([<<"TEL">>, TelType], {_, _, _, OldEls}) ->
1446
    {TakenEl, _NewEls} = take_vcard_tel(TelType, OldEls, [], not_found),
×
1447
    [TakenEl];
×
1448

1449
get_vcard([Data1, Data2], A1) ->
1450
    case get_subtag(A1, Data1) of
×
1451
        [false] -> [false];
×
1452
        A2List ->
1453
            lists:flatten([get_vcard([Data2], A2) || A2 <- A2List])
×
1454
    end;
1455

1456
get_vcard([Data], A1) ->
1457
    get_subtag(A1, Data).
×
1458

1459
get_subtag(Xmlelement, Name) ->
1460
    [fxml:get_subtag(Xmlelement, Name)].
×
1461

1462
set_vcard_content(User, Server, Data, SomeContent) ->
1463
    ContentList = case SomeContent of
×
1464
        [Bin | _] when is_binary(Bin) -> SomeContent;
×
1465
        Bin when is_binary(Bin) -> [SomeContent]
×
1466
    end,
1467
    %% Get old vcard
1468
    A4 = case mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server)) of
×
1469
             [A1] ->
1470
                 {_, _, _, A2} = A1,
×
1471
                 update_vcard_els(Data, ContentList, A2);
×
1472
             [] ->
1473
                 update_vcard_els(Data, ContentList, []);
×
1474
             error ->
1475
                 throw(database_failure)
×
1476
         end,
1477
    %% Build new vcard
1478
    SubEl = {xmlel, <<"vCard">>, [{<<"xmlns">>,<<"vcard-temp">>}], A4},
×
1479
    mod_vcard:set_vcard(User, jid:nameprep(Server), SubEl).
×
1480

1481
take_vcard_tel(TelType, [{xmlel, <<"TEL">>, _, SubEls}=OldEl | OldEls], NewEls, Taken) ->
1482
    {Taken2, NewEls2} = case lists:keymember(TelType, 2, SubEls) of
×
1483
        true -> {fxml:get_subtag(OldEl, <<"NUMBER">>), NewEls};
×
1484
        false -> {Taken, [OldEl | NewEls]}
×
1485
    end,
1486
    take_vcard_tel(TelType, OldEls, NewEls2, Taken2);
×
1487
take_vcard_tel(TelType, [OldEl | OldEls], NewEls, Taken) ->
1488
    take_vcard_tel(TelType, OldEls, [OldEl | NewEls], Taken);
×
1489
take_vcard_tel(_TelType, [], NewEls, Taken) ->
1490
    {Taken, NewEls}.
×
1491

1492
update_vcard_els([<<"TEL">>, TelType], [TelValue], OldEls) ->
1493
    {_, NewEls} = take_vcard_tel(TelType, OldEls, [], not_found),
×
1494
    NewEl = {xmlel,<<"TEL">>,[],
×
1495
             [{xmlel,TelType,[],[]},
1496
              {xmlel,<<"NUMBER">>,[],[{xmlcdata,TelValue}]}]},
1497
    [NewEl | NewEls];
×
1498

1499
update_vcard_els(Data, ContentList, Els1) ->
1500
    Els2 = lists:keysort(2, Els1),
×
1501
    [Data1 | Data2] = Data,
×
1502
    NewEls = case Data2 of
×
1503
                [] ->
1504
                    [{xmlel, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList];
×
1505
                [D2] ->
1506
                    OldEl = case lists:keysearch(Data1, 2, Els2) of
×
1507
                                {value, A} -> A;
×
1508
                                false -> {xmlel, Data1, [], []}
×
1509
                            end,
1510
                    {xmlel, _, _, ContentOld1} = OldEl,
×
1511
                    Content2 = [{xmlel, D2, [], [{xmlcdata,Content}]} || Content <- ContentList],
×
1512
                    ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2],
×
1513
                    ContentOld3 = lists:keysort(2, ContentOld2),
×
1514
                    ContentNew = lists:keymerge(2, Content2, ContentOld3),
×
1515
                    [{xmlel, Data1, [], ContentNew}]
×
1516
            end,
1517
    Els3 = lists:keydelete(Data1, 2, Els2),
×
1518
    lists:keymerge(2, NewEls, Els3).
×
1519

1520

1521
%%%
1522
%%% Roster
1523
%%%
1524

1525
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) when is_binary(Group) ->
1526
    add_rosteritem(LocalUser, LocalServer, User, Server, Nick, [Group], Subs);
×
1527
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Groups, Subs) ->
1528
    case {jid:make(LocalUser, LocalServer), jid:make(User, Server)} of
×
1529
        {error, _} ->
1530
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1531
        {_, error} ->
1532
            throw({error, "Invalid 'user'/'server'"});
×
1533
        {Jid, _Jid2} ->
1534
            RosterItem = build_roster_item(User, Server, {add, Nick, Subs, Groups}),
×
1535
            case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of
×
1536
                ok -> ok;
×
1537
                _ -> error
×
1538
            end
1539
    end.
1540

1541
subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) ->
1542
    case {jid:make(LU, LS), jid:make(User, Server)} of
×
1543
        {error, _} ->
1544
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1545
        {_, error} ->
1546
            throw({error, "Invalid 'user'/'server'"});
×
1547
        {_Jid, _Jid2} ->
1548
            ItemEl = build_roster_item(User, Server, {add, Nick, Subscription, Group}),
×
1549
            mod_roster:set_items(LU, LS, #roster_query{items = [ItemEl]})
×
1550
    end.
1551

1552
delete_rosteritem(LocalUser, LocalServer, User, Server) ->
1553
    case {jid:make(LocalUser, LocalServer), jid:make(User, Server)} of
×
1554
        {error, _} ->
1555
            throw({error, "Invalid 'localuser'/'localserver'"});
×
1556
        {_, error} ->
1557
            throw({error, "Invalid 'user'/'server'"});
×
1558
        {Jid, _Jid2} ->
1559
            RosterItem = build_roster_item(User, Server, remove),
×
1560
            case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of
×
1561
                ok -> ok;
×
1562
                _ -> error
×
1563
            end
1564
    end.
1565

1566
%% -----------------------------
1567
%% Get Roster
1568
%% -----------------------------
1569

1570
get_roster(User, Server) ->
1571
    case jid:make(User, Server) of
×
1572
        error ->
1573
            throw({error, "Invalid 'user'/'server'"});
×
1574
        #jid{luser = U, lserver = S} ->
1575
            Items = ejabberd_hooks:run_fold(roster_get, S, [], [{U, S}]),
×
1576
            make_roster_xmlrpc(Items)
×
1577
    end.
1578

1579
make_roster_xmlrpc(Roster) ->
1580
    lists:map(
×
1581
      fun(#roster_item{jid = JID, name = Nick, subscription = Sub, ask = Ask, groups = Groups}) ->
1582
              JIDS = jid:encode(JID),
×
1583
              Subs = atom_to_list(Sub),
×
1584
              Asks = atom_to_list(Ask),
×
1585
              {JIDS, Nick, Subs, Asks, Groups}
×
1586
      end,
1587
      Roster).
1588

1589
%%-----------------------------
1590
%% Push Roster from file
1591
%%-----------------------------
1592

1593
push_roster(File, User, Server) ->
1594
    {ok, [Roster]} = file:consult(File),
×
1595
    subscribe_roster({User, Server, <<>>, User}, Roster).
×
1596

1597
push_roster_all(File) ->
1598
    {ok, [Roster]} = file:consult(File),
×
1599
    subscribe_all(Roster).
×
1600

1601
subscribe_all(Roster) ->
1602
    subscribe_all(Roster, Roster).
×
1603
subscribe_all([], _) ->
1604
    ok;
×
1605
subscribe_all([User1 | Users], Roster) ->
1606
    subscribe_roster(User1, Roster),
×
1607
    subscribe_all(Users, Roster).
×
1608

1609
subscribe_roster(_, []) ->
1610
    ok;
×
1611
%% Do not subscribe a user to itself
1612
subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
1613
    subscribe_roster({Name, Server, Group, Nick}, Roster);
×
1614
%% Subscribe Name2 to Name1
1615
subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
1616
    subscribe(iolist_to_binary(Name1), iolist_to_binary(Server1), iolist_to_binary(Name2), iolist_to_binary(Server2),
×
1617
        iolist_to_binary(Nick2), iolist_to_binary(Group2), <<"both">>, []),
1618
    subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
×
1619

1620
push_alltoall(S, G) ->
1621
    Users = ejabberd_auth:get_users(S),
×
1622
    Users2 = build_list_users(G, Users, []),
×
1623
    subscribe_all(Users2),
×
1624
    ok.
×
1625

1626
build_list_users(_Group, [], Res) ->
1627
    Res;
×
1628
build_list_users(Group, [{User, Server}|Users], Res) ->
1629
    build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
×
1630

1631
%% @spec(LU, LS, U, S, Action) -> ok
1632
%%       Action = {add, Nick, Subs, Group} | remove
1633
%% @doc Push to the roster of account LU@LS the contact U@S.
1634
%% The specific action to perform is defined in Action.
1635
push_roster_item(LU, LS, U, S, Action) ->
1636
    lists:foreach(fun(R) ->
×
1637
                          push_roster_item(LU, LS, R, U, S, Action)
×
1638
                  end, ejabberd_sm:get_user_resources(LU, LS)).
1639

1640
push_roster_item(LU, LS, R, U, S, Action) ->
1641
    LJID = jid:make(LU, LS, R),
×
1642
    BroadcastEl = build_broadcast(U, S, Action),
×
1643
    ejabberd_sm:route(LJID, BroadcastEl),
×
1644
    Item = build_roster_item(U, S, Action),
×
1645
    ResIQ = build_iq_roster_push(Item),
×
1646
    ejabberd_router:route(
×
1647
      xmpp:set_from_to(ResIQ, jid:remove_resource(LJID), LJID)).
1648

1649
build_roster_item(U, S, {add, Nick, Subs, Groups}) when is_list(Groups) ->
1650
    #roster_item{jid = jid:make(U, S),
×
1651
                 name = Nick,
1652
                 subscription = misc:binary_to_atom(Subs),
1653
                 groups = Groups};
1654
build_roster_item(U, S, {add, Nick, Subs, Group}) ->
1655
    Groups = binary:split(Group,<<";">>, [global, trim]),
×
1656
    #roster_item{jid = jid:make(U, S),
×
1657
                 name = Nick,
1658
                 subscription = misc:binary_to_atom(Subs),
1659
                 groups = Groups};
1660
build_roster_item(U, S, remove) ->
1661
    #roster_item{jid = jid:make(U, S), subscription = remove}.
×
1662

1663
build_iq_roster_push(Item) ->
1664
    #iq{type = set, id = <<"push">>,
×
1665
        sub_els = [#roster_query{items = [Item]}]}.
1666

1667
build_broadcast(U, S, {add, _Nick, Subs, _Group}) ->
1668
    build_broadcast(U, S, list_to_atom(binary_to_list(Subs)));
×
1669
build_broadcast(U, S, remove) ->
1670
    build_broadcast(U, S, none);
×
1671
%% @spec (U::binary(), S::binary(), Subs::atom()) -> any()
1672
%% Subs = both | from | to | none
1673
build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) ->
1674
    {item, {U, S, <<>>}, SubsAtom}.
×
1675

1676
%%%
1677
%%% Last Activity
1678
%%%
1679

1680
get_last(User, Server) ->
1681
    {Now, Status} = case ejabberd_sm:get_user_resources(User, Server) of
×
1682
        [] ->
1683
            case mod_last:get_last_info(User, Server) of
×
1684
                not_found ->
1685
                    {erlang:timestamp(), "NOT FOUND"};
×
1686
                {ok, Shift, Status1} ->
1687
                    {{Shift div 1000000, Shift rem 1000000, 0}, Status1}
×
1688
            end;
1689
        _ ->
1690
            {erlang:timestamp(), "ONLINE"}
×
1691
    end,
1692
    {xmpp_util:encode_timestamp(Now), Status}.
×
1693

1694
set_last(User, Server, Timestamp, Status) ->
1695
    case mod_last:store_last_info(User, Server, Timestamp, Status) of
×
1696
        {ok, _} -> ok;
×
1697
        Error -> Error
×
1698
    end.
1699

1700
%%%
1701
%%% Private Storage
1702
%%%
1703

1704
%% Example usage:
1705
%% $ ejabberdctl private_set badlop localhost "\<aa\ xmlns=\'bb\'\>Cluth\</aa\>"
1706
%% $ ejabberdctl private_get badlop localhost aa bb
1707
%% <aa xmlns='bb'>Cluth</aa>
1708

1709
private_get(Username, Host, Element, Ns) ->
1710
    Els = private_get2(Username, Host, Element, Ns),
×
1711
    binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))).
×
1712

1713
private_get2(Username, Host, Element, Ns) ->
1714
    case gen_mod:is_loaded(Host, mod_private) of
1,294✔
1715
        true -> private_get3(Username, Host, Element, Ns);
1,176✔
1716
        false -> []
118✔
1717
    end.
1718

1719
private_get3(Username, Host, Element, Ns) ->
1720
    ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]},
1,176✔
1721
    mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host),
1,176✔
1722
                               [{Ns, ElementXml}]).
1723

1724
private_set(Username, Host, ElementString) ->
1725
    case fxml_stream:parse_element(ElementString) of
×
1726
        {error, Error} ->
1727
            io:format("Error found parsing the element:~n  ~p~nError: ~p~n",
×
1728
                      [ElementString, Error]),
1729
            error;
×
1730
        Xml ->
1731
            private_set2(Username, Host, Xml)
×
1732
    end.
1733

1734
private_set2(Username, Host, Xml) ->
1735
    NS = fxml:get_tag_attr_s(<<"xmlns">>, Xml),
×
1736
    JID = jid:make(Username, Host),
×
1737
    mod_private:set_data(JID, [{NS, Xml}]).
×
1738

1739
%%%
1740
%%% Shared Roster Groups
1741
%%%
1742

1743
srg_create(Group, Host, Label, Description, Display) when is_binary(Display) ->
1744
    DisplayList = case Display of
×
1745
       <<>> -> [];
×
1746
       _ -> ejabberd_regexp:split(Display, <<"\\\\n">>)
×
1747
    end,
1748
    srg_create(Group, Host, Label, Description, DisplayList);
×
1749

1750
srg_create(Group, Host, Label, Description, DisplayList) ->
1751
    Opts = [{label, Label},
×
1752
            {displayed_groups, DisplayList},
1753
            {description, Description}],
1754
    {atomic, _} = mod_shared_roster:create_group(Host, Group, Opts),
×
1755
    ok.
×
1756

1757
srg_delete(Group, Host) ->
1758
    {atomic, _} = mod_shared_roster:delete_group(Host, Group),
×
1759
    ok.
×
1760

1761
srg_list(Host) ->
1762
    lists:sort(mod_shared_roster:list_groups(Host)).
×
1763

1764
srg_get_info(Group, Host) ->
1765
    Opts = case mod_shared_roster:get_group_opts(Host,Group) of
×
1766
        Os when is_list(Os) -> Os;
×
1767
        error -> []
×
1768
    end,
1769
    [{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
×
1770

1771
to_list([]) -> [];
×
1772
to_list([H|T]) -> [to_list(H)|to_list(T)];
×
1773
to_list(E) when is_atom(E) -> atom_to_list(E);
×
1774
to_list(E) -> binary_to_list(E).
×
1775

1776
srg_get_members(Group, Host) ->
1777
    Members = mod_shared_roster:get_group_explicit_users(Host,Group),
×
1778
    [jid:encode(jid:make(MUser, MServer))
×
1779
     || {MUser, MServer} <- Members].
×
1780

1781
srg_user_add(User, Host, Group, GroupHost) ->
1782
    mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group),
×
1783
    ok.
×
1784

1785
srg_user_del(User, Host, Group, GroupHost) ->
1786
    mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group),
×
1787
    ok.
×
1788

1789

1790
%%%
1791
%%% Stanza
1792
%%%
1793

1794
%% @doc Send a message to an XMPP account.
1795
-spec send_message(Type::binary(), From::binary(), To::binary(),
1796
                   Subject::binary(), Body::binary()) -> ok.
1797
send_message(Type, From, To, Subject, Body) ->
1798
    CodecOpts = ejabberd_config:codec_options(),
×
1799
    try xmpp:decode(
×
1800
          #xmlel{name = <<"message">>,
1801
                 attrs = [{<<"to">>, To},
1802
                          {<<"from">>, From},
1803
                          {<<"type">>, Type},
1804
                          {<<"id">>, p1_rand:get_string()}],
1805
                 children =
1806
                     [#xmlel{name = <<"subject">>,
1807
                             children = [{xmlcdata, Subject}]},
1808
                      #xmlel{name = <<"body">>,
1809
                             children = [{xmlcdata, Body}]}]},
1810
          ?NS_CLIENT, CodecOpts) of
1811
        #message{from = JID, subject = SubjectEl, body = BodyEl} = Msg ->
1812
            Msg2 = case {xmpp:get_text(SubjectEl), xmpp:get_text(BodyEl)} of
×
1813
                       {Subject, <<>>} -> Msg;
×
1814
                       {<<>>, Body} -> Msg#message{subject = []};
×
1815
                       _ -> Msg
×
1816
                   end,
1817
            State = #{jid => JID},
×
1818
            ejabberd_hooks:run_fold(user_send_packet, JID#jid.lserver, {Msg2, State}, []),
×
1819
            ejabberd_router:route(Msg2)
×
1820
    catch _:{xmpp_codec, Why} ->
1821
            {error, xmpp:format_error(Why)}
×
1822
    end.
1823

1824
send_stanza(FromString, ToString, Stanza) ->
1825
    try
×
1826
        #xmlel{} = El = fxml_stream:parse_element(Stanza),
×
1827
        From = jid:decode(FromString),
×
1828
        To = jid:decode(ToString),
×
1829
        CodecOpts = ejabberd_config:codec_options(),
×
1830
        Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
×
1831
        Pkt2 = xmpp:set_from_to(Pkt, From, To),
×
1832
        State = #{jid => From},
×
1833
        ejabberd_hooks:run_fold(user_send_packet, From#jid.lserver,
×
1834
                                {Pkt2, State}, []),
1835
        ejabberd_router:route(Pkt2)
×
1836
    catch _:{xmpp_codec, Why} ->
1837
            io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
×
1838
            {error, Why};
×
1839
          _:{badmatch, {error, {Code, Why}}} when is_integer(Code) ->
1840
            io:format("invalid xml: ~p~n", [Why]),
×
1841
            {error, Why};
×
1842
          _:{badmatch, {error, Why}} ->
1843
            io:format("invalid xml: ~p~n", [Why]),
×
1844
            {error, Why};
×
1845
          _:{bad_jid, S} ->
1846
            io:format("malformed JID: ~ts~n", [S]),
×
1847
            {error, "JID malformed"}
×
1848
    end.
1849

1850
-spec send_stanza_c2s(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
1851
send_stanza_c2s(Username, Host, Resource, Stanza) ->
1852
    try
×
1853
        #xmlel{} = El = fxml_stream:parse_element(Stanza),
×
1854
        CodecOpts = ejabberd_config:codec_options(),
×
1855
        Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
×
1856
        case ejabberd_sm:get_session_pid(Username, Host, Resource) of
×
1857
            Pid when is_pid(Pid) ->
1858
                ejabberd_c2s:send(Pid, Pkt);
×
1859
            _ ->
1860
                {error, no_session}
×
1861
        end
1862
    catch _:{badmatch, {error, Why} = Err} ->
1863
            io:format("invalid xml: ~p~n", [Why]),
×
1864
            Err;
×
1865
          _:{xmpp_codec, Why} ->
1866
            io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
×
1867
            {error, Why}
×
1868
    end.
1869

1870
privacy_set(Username, Host, QueryS) ->
1871
    Jid = jid:make(Username, Host),
×
1872
    QueryEl = fxml_stream:parse_element(QueryS),
×
1873
    SubEl = xmpp:decode(QueryEl),
×
1874
    IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
×
1875
             from = Jid, to = Jid},
1876
    Result = mod_privacy:process_iq(IQ),
×
1877
    Result#iq.type == result.
×
1878

1879
%%%
1880
%%% Stats
1881
%%%
1882

1883
stats(Name) ->
1884
    case Name of
×
1885
        <<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
×
1886
        <<"processes">> -> length(erlang:processes());
×
1887
        <<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ejabberd_option:hosts());
×
1888
        <<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
×
1889
        <<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
×
1890
    end.
1891

1892
stats(Name, Host) ->
1893
    case Name of
×
1894
        <<"registeredusers">> -> ejabberd_auth:count_users(Host);
×
1895
        <<"onlineusers">> -> length(ejabberd_sm:get_vh_session_list(Host))
×
1896
    end.
1897

1898

1899
user_action(User, Server, Fun, OK) ->
1900
    case ejabberd_auth:user_exists(User, Server) of
×
1901
        true ->
1902
            case catch Fun() of
×
1903
                OK -> ok;
×
1904
                {error, Error} -> throw(Error);
×
1905
                Error ->
1906
                    ?ERROR_MSG("Command returned: ~p", [Error]),
×
1907
                    1
×
1908
            end;
1909
        false ->
1910
            throw({not_found, "unknown_user"})
×
1911
    end.
1912

1913
num_prio(Priority) when is_integer(Priority) ->
1914
    Priority;
×
1915
num_prio(_) ->
1916
    -1.
×
1917

1918
mod_options(_) -> [].
×
1919

1920
mod_doc() ->
1921
    #{desc =>
×
1922
          [?T("This module provides additional administrative commands."), "",
1923
           ?T("Details for some commands:"), "",
1924
           ?T("- 'ban-acount':"),
1925
           ?T("This command kicks all the connected sessions of the account "
1926
              "from the server. It also changes their password to a randomly "
1927
              "generated one, so they can't login anymore unless a server "
1928
              "administrator changes their password again. It is possible to "
1929
              "define the reason of the ban. The new password also includes "
1930
              "the reason and the date and time of the ban. See an example below."),
1931
           ?T("- 'pushroster': (and 'pushroster-all')"),
1932
           ?T("The roster file must be placed, if using Windows, on the "
1933
              "directory where you installed ejabberd: "
1934
              "`C:/Program Files/ejabberd` or similar. If you use other "
1935
              "Operating System, place the file on the same directory where "
1936
              "the .beam files are installed. See below an example roster file."),
1937
           ?T("- 'srg-create':"),
1938
           ?T("If you want to put a group Name with blankspaces, use the "
1939
              "characters \"\' and \'\" to define when the Name starts and "
1940
              "ends. See an example below.")],
1941
      example =>
1942
          [{?T("With this configuration, vCards can only be modified with "
1943
               "mod_admin_extra commands:"),
1944
            ["acl:",
1945
             "  adminextraresource:",
1946
             "    - resource: \"modadminextraf8x,31ad\"",
1947
             "access_rules:",
1948
             "  vcard_set:",
1949
             "    - allow: adminextraresource",
1950
             "modules:",
1951
             "  mod_admin_extra: {}",
1952
             "  mod_vcard:",
1953
             "    access_set: vcard_set"]},
1954
           {?T("Content of roster file for 'pushroster' command:"),
1955
            ["[{<<\"bob\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Bob\">>},",
1956
             "{<<\"mart\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Mart\">>},",
1957
             "{<<\"Rich\">>, <<\"example.org\">>, <<\"bosses\">>, <<\"Rich\">>}]."]},
1958
           {?T("With this call, the sessions of the local account which JID is "
1959
              "boby@example.org will be kicked, and its password will be set "
1960
              "to something like "
1961
              "'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"),
1962
            ["ejabberdctl vhost example.org ban-account boby \"Spammed rooms\""]},
1963
           {?T("Call to srg-create using double-quotes and single-quotes:"),
1964
            ["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