Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 409425 | Differences between
and this patch

Collapse All | Expand All

(-)a/src/mod_admin_extra.erl (+1568 lines)
Line 0 Link Here
1
%%%-------------------------------------------------------------------
2
%%% File    : mod_admin_extra.erl
3
%%% Author  : Badlop <badlop@process-one.net>
4
%%% Purpose : Contributed administrative functions and commands
5
%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
6
%%%
7
%%%
8
%%% ejabberd, Copyright (C) 2002-2008   ProcessOne
9
%%%
10
%%% This program is free software; you can redistribute it and/or
11
%%% modify it under the terms of the GNU General Public License as
12
%%% published by the Free Software Foundation; either version 2 of the
13
%%% License, or (at your option) any later version.
14
%%%
15
%%% This program is distributed in the hope that it will be useful,
16
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
%%% General Public License for more details.
19
%%%
20
%%% You should have received a copy of the GNU General Public License
21
%%% along with this program; if not, write to the Free Software
22
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23
%%% 02111-1307 USA
24
%%%
25
%%%-------------------------------------------------------------------
26
27
-module(mod_admin_extra).
28
-author('badlop@process-one.net').
29
30
-behaviour(gen_mod).
31
32
-export([start/2, stop/1,
33
	 %% Node
34
	 compile/1,
35
	 load_config/1,
36
	 get_cookie/0,
37
	 remove_node/1,
38
	 export2odbc/2,
39
	 %% Accounts
40
	 set_password/3,
41
	 check_password_hash/4,
42
	 delete_old_users/1,
43
	 delete_old_users_vhost/2,
44
	 ban_account/3,
45
	 num_active_users/2,
46
	 %% Sessions
47
	 num_resources/2,
48
	 resource_num/3,
49
	 kick_session/4,
50
	 status_num/2, status_num/1,
51
	 status_list/2, status_list/1,
52
	 connected_users_info/0,
53
	 connected_users_vhost/1,
54
	 set_presence/7,
55
	 user_sessions_info/2,
56
	 %% Vcard
57
	 set_nickname/3,
58
	 get_vcard/3,
59
	 get_vcard/4,
60
	 get_vcard_multi/4,
61
	 set_vcard/4,
62
	 set_vcard/5,
63
	 %% Roster
64
	 add_rosteritem/7,
65
	 delete_rosteritem/4,
66
	 process_rosteritems/5,
67
	 get_roster/2,
68
	 push_roster/3,
69
	 push_roster_all/1,
70
	 push_alltoall/2,
71
	 %% mod_last
72
	 set_last/4,
73
	 %% mod_private
74
	 private_get/4,
75
	 private_set/3,
76
	 %% mod_shared_roster
77
	 srg_create/5,
78
	 srg_delete/2,
79
	 srg_list/1,
80
	 srg_get_info/2,
81
	 srg_get_members/2,
82
	 srg_user_add/4,
83
	 srg_user_del/4,
84
	 %% Stanza
85
	 send_message_headline/4,
86
	 send_message_chat/3,
87
	 send_stanza_c2s/4,
88
	 privacy_set/3,
89
	 %% Stats
90
	 stats/1, stats/2
91
	]).
92
93
-include("ejabberd.hrl").
94
-include("ejabberd_commands.hrl").
95
-include("mod_roster.hrl").
96
-include("jlib.hrl").
97
98
%% Copied from ejabberd_sm.erl
99
-record(session, {sid, usr, us, priority, info}).
100
101
102
%%%
103
%%% gen_mod
104
%%%
105
106
start(_Host, _Opts) ->
107
    ejabberd_commands:register_commands(commands()).
108
109
stop(_Host) ->
110
    ejabberd_commands:unregister_commands(commands()).
111
112
113
%%%
114
%%% Register commands
115
%%%
116
117
commands() ->
118
    Vcard1FieldsString = "Some vcard field names in get/set_vcard are:\n"
119
	" FN		- Full Name\n"
120
	" NICKNAME	- Nickname\n"
121
	" BDAY		- Birthday\n"
122
	" TITLE		- Work: Position\n"
123
	" ROLE		- Work: Role",
124
125
    Vcard2FieldsString = "Some vcard field names and subnames in get/set_vcard2 are:\n"
126
	" N FAMILY	- Family name\n"
127
	" N GIVEN	- Given name\n"
128
	" N MIDDLE	- Middle name\n"
129
	" ADR CTRY	- Address: Country\n"
130
	" ADR LOCALITY	- Address: City\n"
131
	" EMAIL USERID	- E-Mail Address\n"
132
	" ORG ORGNAME	- Work: Company\n"
133
	" ORG ORGUNIT	- Work: Department",
134
135
    VcardXEP = "For a full list of vCard fields check XEP-0054: vcard-temp at "
136
	"http://www.xmpp.org/extensions/xep-0054.html",
137
138
    [
139
     #ejabberd_commands{name = compile, tags = [erlang],
140
			desc = "Recompile and reload Erlang source code file",
141
			module = ?MODULE, function = compile,
142
			args = [{file, string}],
143
			result = {res, rescode}},
144
     #ejabberd_commands{name = load_config, tags = [server],
145
			desc = "Load ejabberd configuration file",
146
			module = ?MODULE, function = load_config,
147
			args = [{file, string}],
148
			result = {res, rescode}},
149
     #ejabberd_commands{name = get_cookie, tags = [erlang],
150
			desc = "Get the Erlang cookie of this node",
151
			module = ?MODULE, function = get_cookie,
152
			args = [],
153
			result = {cookie, string}},
154
     #ejabberd_commands{name = remove_node, tags = [erlang],
155
			desc = "Remove an ejabberd node from Mnesia clustering config",
156
			module = ?MODULE, function = remove_node,
157
			args = [{node, string}],
158
			result = {res, rescode}},
159
     #ejabberd_commands{name = export2odbc, tags = [mnesia],
160
			desc = "Export Mnesia tables to files in directory",
161
			module = ?MODULE, function = export2odbc,
162
			args = [{host, string}, {path, string}],
163
			result = {res, rescode}},
164
165
     #ejabberd_commands{name = num_active_users, tags = [accounts, stats],
166
			desc = "Get number of users active in the last days",
167
			module = ?MODULE, function = num_active_users,
168
			args = [{host, string}, {days, integer}],
169
			result = {users, integer}},
170
     #ejabberd_commands{name = delete_old_users, tags = [accounts, purge],
171
			desc = "Delete users that didn't log in last days, or that never logged",
172
			module = ?MODULE, function = delete_old_users,
173
			args = [{days, integer}],
174
			result = {res, restuple}},
175
     #ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge],
176
			desc = "Delete users that didn't log in last days in vhost, or that never logged",
177
			module = ?MODULE, function = delete_old_users_vhost,
178
			args = [{host, string}, {days, integer}],
179
			result = {res, restuple}},
180
181
     #ejabberd_commands{name = check_account, tags = [accounts],
182
			desc = "Check if an account exists or not",
183
			module = ejabberd_auth, function = is_user_exists,
184
			args = [{user, string}, {host, string}],
185
			result = {res, rescode}},
186
     #ejabberd_commands{name = check_password, tags = [accounts],
187
			desc = "Check if a password is correct",
188
			module = ejabberd_auth, function = check_password,
189
			args = [{user, string}, {host, string}, {password, string}],
190
			result = {res, rescode}},
191
     #ejabberd_commands{name = check_password_hash, tags = [accounts],
192
			desc = "Check if the password hash is correct",
193
			longdesc = "Allowed hash methods: md5, sha.",
194
			module = ?MODULE, function = check_password_hash,
195
			args = [{user, string}, {host, string}, {passwordhash, string}, {hashmethod, string}],
196
			result = {res, rescode}},
197
     #ejabberd_commands{name = change_password, tags = [accounts],
198
			desc = "Change the password of an account",
199
			module = ?MODULE, function = set_password,
200
			args = [{user, string}, {host, string}, {newpass, string}],
201
			result = {res, rescode}},
202
     #ejabberd_commands{name = ban_account, tags = [accounts],
203
			desc = "Ban an account: kick sessions and set random password",
204
			module = ?MODULE, function = ban_account,
205
			args = [{user, string}, {host, string}, {reason, string}],
206
			result = {res, rescode}},
207
208
     #ejabberd_commands{name = num_resources, tags = [session],
209
			desc = "Get the number of resources of a user",
210
			module = ?MODULE, function = num_resources,
211
			args = [{user, string}, {host, string}],
212
			result = {resources, integer}},
213
     #ejabberd_commands{name = resource_num, tags = [session],
214
			desc = "Resource string of a session number",
215
			module = ?MODULE, function = resource_num,
216
			args = [{user, string}, {host, string}, {num, integer}],
217
			result = {resource, string}},
218
     #ejabberd_commands{name = kick_session, tags = [session],
219
			desc = "Kick a user session",
220
			module = ?MODULE, function = kick_session,
221
			args = [{user, string}, {host, string}, {resource, string}, {reason, string}],
222
			result = {res, rescode}},
223
     #ejabberd_commands{name = status_num_host, tags = [session, stats],
224
			desc = "Number of logged users with this status in host",
225
			module = ?MODULE, function = status_num,
226
			args = [{host, string}, {status, string}],
227
			result = {users, integer}},
228
     #ejabberd_commands{name = status_num, tags = [session, stats],
229
			desc = "Number of logged users with this status",
230
			module = ?MODULE, function = status_num,
231
			args = [{status, string}],
232
			result = {users, integer}},
233
     #ejabberd_commands{name = status_list_host, tags = [session],
234
			desc = "List of users logged in host with their statuses",
235
			module = ?MODULE, function = status_list,
236
			args = [{host, string}, {status, string}],
237
			result = {users, {list,
238
					  {userstatus, {tuple, [
239
								{user, string},
240
								{host, string},
241
								{resource, string},
242
								{priority, integer},
243
								{status, string}
244
							       ]}}
245
					 }}},
246
     #ejabberd_commands{name = status_list, tags = [session],
247
			desc = "List of logged users with this status",
248
			module = ?MODULE, function = status_list,
249
			args = [{status, string}],
250
			result = {users, {list,
251
					  {userstatus, {tuple, [
252
								{user, string},
253
								{host, string},
254
								{resource, string},
255
								{priority, integer},
256
								{status, string}
257
							       ]}}
258
					 }}},
259
     #ejabberd_commands{name = connected_users_info,
260
			tags = [session],
261
			desc = "List all established sessions and their information",
262
			module = ?MODULE, function = connected_users_info,
263
			args = [],
264
			result = {connected_users_info,
265
				  {list,
266
				   {sessions, {tuple,
267
					       [{jid, string},
268
						{connection, string},
269
						{ip, string},
270
						{port, integer},
271
						{priority, integer},
272
						{node, string},
273
						{uptime, integer}
274
					       ]}}
275
				  }}},
276
     #ejabberd_commands{name = connected_users_vhost,
277
                       tags = [session],
278
                       desc = "Get the list of established sessions in a vhost",
279
                       module = ?MODULE, function = connected_users_vhost,
280
                       args = [{host, string}],
281
                       result = {connected_users_vhost, {list, {sessions, string}}}},
282
     #ejabberd_commands{name = user_sessions_info,
283
			tags = [session],
284
			desc = "Get information about all sessions of a user",
285
			module = ?MODULE, function = user_sessions_info,
286
			args = [{user, string}, {host, string}],
287
			result = {sessions_info,
288
				  {list,
289
				   {session, {tuple,
290
					      [{connection, string},
291
					       {ip, string},
292
					       {port, integer},
293
					       {priority, integer},
294
					       {node, string},
295
					       {uptime, integer},
296
					       {status, string},
297
					       {resource, string},
298
					       {statustext, string}
299
					      ]}}
300
				  }}},
301
302
     #ejabberd_commands{name = set_presence,
303
			tags = [session],
304
			desc = "Set presence of a session",
305
			module = ?MODULE, function = set_presence,
306
			args = [{user, string}, {host, string},
307
				{resource, string}, {type, string},
308
				{show, string}, {status, string},
309
				{priority, string}],
310
			result = {res, rescode}},
311
312
     #ejabberd_commands{name = set_nickname, tags = [vcard],
313
			desc = "Set nickname in a user's vCard",
314
			module = ?MODULE, function = set_nickname,
315
			args = [{user, string}, {host, string}, {nickname, string}],
316
			result = {res, rescode}},
317
318
     #ejabberd_commands{name = get_vcard, tags = [vcard],
319
			desc = "Get content from a vCard field",
320
			longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
321
			module = ?MODULE, function = get_vcard,
322
			args = [{user, string}, {host, string}, {name, string}],
323
			result = {content, string}},
324
     #ejabberd_commands{name = get_vcard2, tags = [vcard],
325
			desc = "Get content from a vCard field",
326
			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
327
			module = ?MODULE, function = get_vcard,
328
			args = [{user, string}, {host, string}, {name, string}, {subname, string}],
329
			result = {content, string}},
330
     #ejabberd_commands{name = get_vcard2_multi, tags = [vcard],
331
			desc = "Get multiple contents from a vCard field (requires exmpp installed)",
332
			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
333
			module = ?MODULE, function = get_vcard_multi,
334
			args = [{user, string}, {host, string}, {name, string}, {subname, string}],
335
			result = {contents, {list, string}}},
336
337
     #ejabberd_commands{name = set_vcard, tags = [vcard],
338
			desc = "Set content in a vCard field",
339
			longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
340
			module = ?MODULE, function = set_vcard,
341
			args = [{user, string}, {host, string}, {name, string}, {content, string}],
342
			result = {res, rescode}},
343
     #ejabberd_commands{name = set_vcard2, tags = [vcard],
344
			desc = "Set content in a vCard subfield",
345
			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
346
			module = ?MODULE, function = set_vcard,
347
			args = [{user, string}, {host, string}, {name, string}, {subname, string}, {content, string}],
348
			result = {res, rescode}},
349
     #ejabberd_commands{name = set_vcard2_multi, tags = [vcard],
350
			desc = "Set multiple contents in a vCard subfield",
351
			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
352
			module = ?MODULE, function = set_vcard,
353
			args = [{user, string}, {host, string}, {name, string}, {subname, string}, {contents, {list, string}}],
354
			result = {res, rescode}},
355
356
     #ejabberd_commands{name = add_rosteritem, tags = [roster],
357
			desc = "Add an item to a user's roster (supports ODBC)",
358
			module = ?MODULE, function = add_rosteritem,
359
			args = [{localuser, string}, {localserver, string},
360
				{user, string}, {server, string},
361
				{nick, string}, {group, string},
362
				{subs, string}],
363
			result = {res, rescode}},
364
     %%{"", "subs= none, from, to or both"},
365
     %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
366
     %%{"", "will add mike@server.com to peter@localhost roster"},
367
     #ejabberd_commands{name = delete_rosteritem, tags = [roster],
368
			desc = "Delete an item from a user's roster (supports ODBC)",
369
			module = ?MODULE, function = delete_rosteritem,
370
			args = [{localuser, string}, {localserver, string},
371
				{user, string}, {server, string}],
372
			result = {res, rescode}},
373
     #ejabberd_commands{name = process_rosteritems, tags = [roster],
374
			desc = "List or delete rosteritems that match filtering options",
375
			longdesc = "Explanation of each argument:\n"
376
			" - action: what to do with each rosteritem that "
377
			"matches all the filtering options\n"
378
			" - subs: subscription type\n"
379
			" - asks: pending subscription\n"
380
			" - users: the JIDs of the local user\n"
381
			" - contacts: the JIDs of the contact in the roster\n"
382
			"\n"
383
			"Allowed values in the arguments:\n"
384
			"  ACTION = list | delete\n"
385
			"  SUBS = SUB[:SUB]* | any\n"
386
			"  SUB = none | from | to | both\n"
387
			"  ASKS = ASK[:ASK]* | any\n"
388
			"  ASK = none | out | in\n"
389
			"  USERS = JID[:JID]* | any\n"
390
			"  CONTACTS = JID[:JID]* | any\n"
391
			"  JID = characters valid in a JID, and can use the "
392
			"globs: *, ?, ! and [...]\n"
393
			"\n"
394
			"This example will list roster items with subscription "
395
			"'none', 'from' or 'to' that have any ask property, of "
396
			"local users which JID is in the virtual host "
397
			"'example.org' and that the contact JID is either a "
398
			"bare server name (without user part) or that has a "
399
			"user part and the server part contains the word 'icq'"
400
			":\n  list none:from:to any *@example.org *:*@*icq*",
401
			module = ?MODULE, function = process_rosteritems,
402
			args = [{action, string}, {subs, string},
403
				{asks, string}, {users, string},
404
				{contacts, string}],
405
			result = {res, rescode}},
406
     #ejabberd_commands{name = get_roster, tags = [roster],
407
			desc = "Get roster of a local user",
408
			module = ?MODULE, function = get_roster,
409
			args = [{user, string}, {host, string}],
410
			result = {contacts, {list, {contact, {tuple, [
411
								      {jid, string},
412
								      {nick, string},
413
								      {subscription, string},
414
								      {ask, string},
415
								      {group, string}
416
								     ]}}}}},
417
     #ejabberd_commands{name = push_roster, tags = [roster],
418
			desc = "Push template roster from file to a user",
419
			module = ?MODULE, function = push_roster,
420
			args = [{file, string}, {user, string}, {host, string}],
421
			result = {res, rescode}},
422
     #ejabberd_commands{name = push_roster_all, tags = [roster],
423
			desc = "Push template roster from file to all those users",
424
			module = ?MODULE, function = push_roster_all,
425
			args = [{file, string}],
426
			result = {res, rescode}},
427
     #ejabberd_commands{name = push_alltoall, tags = [roster],
428
			desc = "Add all the users to all the users of Host in Group",
429
			module = ?MODULE, function = push_alltoall,
430
			args = [{host, string}, {group, string}],
431
			result = {res, rescode}},
432
433
     #ejabberd_commands{name = set_last, tags = [last],
434
			desc = "Set last activity information",
435
			longdesc = "Timestamp is the seconds since"
436
			"1970-01-01 00:00:00 UTC, for example: date +%s",
437
			module = ?MODULE, function = set_last,
438
			args = [{user, string}, {host, string}, {timestamp, integer}, {status, string}],
439
			result = {res, rescode}},
440
441
     #ejabberd_commands{name = private_get, tags = [private],
442
			desc = "Get some information from a user private storage",
443
			module = ?MODULE, function = private_get,
444
			args = [{user, string}, {host, string}, {element, string}, {ns, string}],
445
			result = {res, string}},
446
     #ejabberd_commands{name = private_set, tags = [private],
447
			desc = "Set to the user private storage",
448
			module = ?MODULE, function = private_set,
449
			args = [{user, string}, {host, string}, {element, string}],
450
			result = {res, rescode}},
451
452
     #ejabberd_commands{name = srg_create, tags = [shared_roster_group],
453
			desc = "Create a Shared Roster Group",
454
			longdesc = "If you want to specify several group "
455
			"identifiers in the Display argument,\n"
456
			"put  \\ \" around the argument and\nseparate the "
457
			"identifiers with \\ \\ n\n"
458
			"For example:\n"
459
			"  ejabberdctl srg_create group3 localhost "
460
			"name desc \\\"group1\\\\ngroup2\\\"",
461
			module = ?MODULE, function = srg_create,
462
			args = [{group, string}, {host, string},
463
				{name, string}, {description, string}, {display, string}],
464
			result = {res, rescode}},
465
     #ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
466
			desc = "Delete a Shared Roster Group",
467
			module = ?MODULE, function = srg_delete,
468
			args = [{group, string}, {host, string}],
469
			result = {res, rescode}},
470
     #ejabberd_commands{name = srg_list, tags = [shared_roster_group],
471
			desc = "List the Shared Roster Groups in Host",
472
			module = ?MODULE, function = srg_list,
473
			args = [{host, string}],
474
			result = {groups, {list, {id, string}}}},
475
     #ejabberd_commands{name = srg_get_info, tags = [shared_roster_group],
476
			desc = "Get info of a Shared Roster Group",
477
			module = ?MODULE, function = srg_get_info,
478
			args = [{group, string}, {host, string}],
479
			result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
480
     #ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
481
			desc = "Get members of a Shared Roster Group",
482
			module = ?MODULE, function = srg_get_members,
483
			args = [{group, string}, {host, string}],
484
			result = {members, {list, {member, string}}}},
485
     #ejabberd_commands{name = srg_user_add, tags = [shared_roster_group],
486
			desc = "Add the JID user@host to the Shared Roster Group",
487
			module = ?MODULE, function = srg_user_add,
488
			args = [{user, string}, {host, string}, {group, string}, {grouphost, string}],
489
			result = {res, rescode}},
490
     #ejabberd_commands{name = srg_user_del, tags = [shared_roster_group],
491
			desc = "Delete this JID user@host from the Shared Roster Group",
492
			module = ?MODULE, function = srg_user_del,
493
			args = [{user, string}, {host, string}, {group, string}, {grouphost, string}],
494
			result = {res, rescode}},
495
496
     #ejabberd_commands{name = send_message_chat, tags = [stanza],
497
			desc = "Send a chat message to a local or remote bare of full JID",
498
			module = ?MODULE, function = send_message_chat,
499
			args = [{from, string}, {to, string}, {body, string}],
500
			result = {res, rescode}},
501
     #ejabberd_commands{name = send_message_headline, tags = [stanza],
502
			desc = "Send a headline message to a local or remote bare of full JID",
503
			module = ?MODULE, function = send_message_headline,
504
			args = [{from, string}, {to, string},
505
				{subject, string}, {body, string}],
506
			result = {res, rescode}},
507
     #ejabberd_commands{name = send_stanza_c2s, tags = [stanza],
508
			desc = "Send a stanza as if sent from a c2s session",
509
			module = ?MODULE, function = send_stanza_c2s,
510
			args = [{user, string}, {host, string}, {resource, string}, {stanza, string}],
511
			result = {res, rescode}},
512
     #ejabberd_commands{name = privacy_set, tags = [stanza],
513
			desc = "Send a IQ set privacy stanza for a local account",
514
			module = ?MODULE, function = privacy_set,
515
			args = [{user, string}, {host, string}, {xmlquery, string}],
516
			result = {res, rescode}},
517
518
     #ejabberd_commands{name = stats, tags = [stats],
519
			desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds",
520
			module = ?MODULE, function = stats,
521
			args = [{name, string}],
522
			result = {stat, integer}},
523
     #ejabberd_commands{name = stats_host, tags = [stats],
524
			desc = "Get statistical value for this host: registeredusers onlineusers",
525
			module = ?MODULE, function = stats,
526
			args = [{name, string}, {host, string}],
527
			result = {stat, integer}}
528
    ].
529
530
531
%%%
532
%%% Node
533
%%%
534
535
compile(File) ->
536
    case compile:file(File) of
537
	ok -> ok;
538
	_ -> error
539
    end.
540
541
load_config(Path) ->
542
    ok = ejabberd_config:load_file(Path).
543
544
get_cookie() ->
545
    atom_to_list(erlang:get_cookie()).
546
547
remove_node(Node) ->
548
    mnesia:del_table_copy(schema, list_to_atom(Node)),
549
    ok.
550
551
export2odbc(Host, Directory) ->
552
    Tables = [
553
	      {export_last, last},
554
	      {export_offline, offline},
555
	      {export_passwd, passwd},
556
	      {export_private_storage, private_storage},
557
	      {export_roster, roster},
558
	      {export_vcard, vcard},
559
	      {export_vcard_search, vcard_search}],
560
    Export = fun({TableFun, Table}) ->
561
		     Filename = filename:join([Directory, atom_to_list(Table)++".txt"]),
562
		     io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]),
563
		     Res = (catch ejd2odbc:TableFun(Host, Filename)),
564
		     io:format("  Result: ~p~n", [Res])
565
	     end,
566
    lists:foreach(Export, Tables),
567
    ok.
568
569
570
%%%
571
%%% Accounts
572
%%%
573
574
set_password(User, Host, Password) ->
575
    case ejabberd_auth:set_password(User, Host, Password) of
576
	ok ->
577
	    ok;
578
	_ ->
579
	    error
580
    end.
581
582
%% Copied some code from ejabberd_commands.erl
583
check_password_hash(User, Host, PasswordHash, HashMethod) ->
584
    AccountPass = ejabberd_auth:get_password_s(User, Host),
585
    AccountPassHash = case HashMethod of
586
			  "md5" -> get_md5(AccountPass);
587
			  "sha" -> get_sha(AccountPass);
588
			  _ -> undefined
589
		      end,
590
    case AccountPassHash of
591
	undefined -> error;
592
	PasswordHash -> ok;
593
	_ -> error
594
    end.
595
get_md5(AccountPass) ->
596
    lists:flatten([io_lib:format("~.16B", [X])
597
		   || X <- binary_to_list(crypto:md5(AccountPass))]).
598
get_sha(AccountPass) ->
599
    lists:flatten([io_lib:format("~.16B", [X])
600
		   || X <- binary_to_list(crypto:sha(AccountPass))]).
601
602
num_active_users(Host, Days) ->
603
    list_last_activity(Host, true, Days).
604
605
%% Code based on ejabberd/src/web/ejabberd_web_admin.erl
606
list_last_activity(Host, Integral, Days) ->
607
    {MegaSecs, Secs, _MicroSecs} = now(),
608
    TimeStamp = MegaSecs * 1000000 + Secs,
609
    TS = TimeStamp - Days * 86400,
610
    case catch mnesia:dirty_select(
611
		 last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
612
				  [{'>', '$1', TS}],
613
				  [{'trunc', {'/',
614
					      {'-', TimeStamp, '$1'},
615
					      86400}}]}]) of
616
							      {'EXIT', _Reason} ->
617
		 [];
618
	       Vals ->
619
		 Hist = histogram(Vals, Integral),
620
		 if
621
		     Hist == [] ->
622
			 0;
623
		     true ->
624
			 Left = Days - length(Hist),
625
			 Tail = if
626
				    Integral ->
627
					lists:duplicate(Left, lists:last(Hist));
628
				    true ->
629
					lists:duplicate(Left, 0)
630
				end,
631
			 lists:nth(Days, Hist ++ Tail)
632
		 end
633
	 end.
634
histogram(Values, Integral) ->
635
    histogram(lists:sort(Values), Integral, 0, 0, []).
636
histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
637
    histogram(T, Integral, Current, Count + 1, Hist);
638
histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
639
    if
640
	Integral ->
641
	    histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
642
	true ->
643
	    histogram(Values, Integral, Current + 1, 0, [Count | Hist])
644
    end;
645
histogram([], _Integral, _Current, Count, Hist) ->
646
    if
647
	Count > 0 ->
648
	    lists:reverse([Count | Hist]);
649
	true ->
650
	    lists:reverse(Hist)
651
    end.
652
653
654
delete_old_users(Days) ->
655
    %% Get the list of registered users
656
    Users = ejabberd_auth:dirty_get_registered_users(),
657
658
    {removed, N, UR} = delete_old_users(Days, Users),
659
    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
660
661
delete_old_users_vhost(Host, Days) ->
662
    %% Get the list of registered users
663
    Users = ejabberd_auth:get_vh_registered_users(Host),
664
665
    {removed, N, UR} = delete_old_users(Days, Users),
666
    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
667
668
delete_old_users(Days, Users) ->
669
    %% Convert older time
670
    SecOlder = Days*24*60*60,
671
672
    %% Get current time
673
    {MegaSecs, Secs, _MicroSecs} = now(),
674
    TimeStamp_now = MegaSecs * 1000000 + Secs,
675
676
    %% For a user, remove if required and answer true
677
    F = fun({LUser, LServer}) ->
678
		%% Check if the user is logged
679
		case ejabberd_sm:get_user_resources(LUser, LServer) of
680
		    %% If it isnt
681
		    [] ->
682
			%% Look for his last_activity
683
			case (get_lastactivity_module(LServer)):get_last_info(LUser, LServer) of
684
			    %% If it is
685
			    %% existent:
686
			    {ok, TimeStamp, _Status} ->
687
				%% get his age
688
				Sec = TimeStamp_now - TimeStamp,
689
				%% If he is
690
				if
691
				    %% younger than SecOlder:
692
				    Sec < SecOlder ->
693
					%% do nothing
694
					false;
695
				    %% older:
696
				    true ->
697
					%% remove the user
698
					ejabberd_auth:remove_user(LUser, LServer),
699
					true
700
				end;
701
			    %% nonexistent:
702
			    not_found ->
703
				%% remove the user
704
				ejabberd_auth:remove_user(LUser, LServer),
705
				true
706
			end;
707
		    %% Else
708
		    _ ->
709
			%% do nothing
710
			false
711
		end
712
	end,
713
    %% Apply the function to every user in the list
714
    Users_removed = lists:filter(F, Users),
715
    {removed, length(Users_removed), Users_removed}.
716
717
get_lastactivity_module(Server) ->
718
    case lists:member(mod_last, gen_mod:loaded_modules(Server)) of
719
        true -> mod_last;
720
        _ -> mod_last_odbc
721
    end.
722
723
724
%%
725
%% Ban account
726
727
ban_account(User, Host, ReasonText) ->
728
    Reason = prepare_reason(ReasonText),
729
    kick_sessions(User, Host, Reason),
730
    set_random_password(User, Host, Reason),
731
    ok.
732
733
kick_sessions(User, Server, Reason) ->
734
    lists:map(
735
      fun(Resource) ->
736
	      kick_this_session(User, Server, Resource, Reason)
737
      end,
738
      get_resources(User, Server)).
739
740
get_resources(User, Server) ->
741
    lists:map(
742
      fun(Session) ->
743
	      element(3, Session#session.usr)
744
      end,
745
      get_sessions(User, Server)).
746
747
get_sessions(User, Server) ->
748
    LUser = jlib:nodeprep(User),
749
    LServer = jlib:nameprep(Server),
750
    Sessions =  mnesia:dirty_index_read(session, {LUser, LServer}, #session.us),
751
    true = is_list(Sessions),
752
    Sessions.
753
754
set_random_password(User, Server, Reason) ->
755
    NewPass = build_random_password(Reason),
756
    set_password_auth(User, Server, NewPass).
757
758
build_random_password(Reason) ->
759
    Date = jlib:timestamp_to_iso(calendar:universal_time()),
760
    RandomString = randoms:get_string(),
761
    "BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason.
762
763
set_password_auth(User, Server, Password) ->
764
    ok = ejabberd_auth:set_password(User, Server, Password).
765
766
prepare_reason([]) ->
767
    "Kicked by administrator";
768
prepare_reason([Reason]) ->
769
    Reason;
770
prepare_reason(Reason) when is_list(Reason) ->
771
    Reason;
772
prepare_reason(StringList) ->
773
    string:join(StringList, "_").
774
775
776
%%%
777
%%% Sessions
778
%%%
779
780
num_resources(User, Host) ->
781
    length(ejabberd_sm:get_user_resources(User, Host)).
782
783
resource_num(User, Host, Num) ->
784
    Resources = ejabberd_sm:get_user_resources(User, Host),
785
    case (0<Num) and (Num=<length(Resources)) of
786
	true ->
787
	    lists:nth(Num, Resources);
788
	false ->
789
	    lists:flatten(io_lib:format("Error: Wrong resource number: ~p", [Num]))
790
    end.
791
792
kick_session(User, Server, Resource, ReasonText) ->
793
    kick_this_session(User, Server, Resource, prepare_reason(ReasonText)),
794
    ok.
795
796
kick_this_session(User, Server, Resource, Reason) ->
797
    ejabberd_router:route(
798
      jlib:make_jid("", "", ""),
799
      jlib:make_jid(User, Server, Resource),
800
      {xmlelement, "broadcast", [], [{exit, Reason}]}).
801
802
803
status_num(Host, Status) ->
804
    length(get_status_list(Host, Status)).
805
status_num(Status) ->
806
    status_num("all", Status).
807
status_list(Host, Status) ->
808
    Res = get_status_list(Host, Status),
809
    [{U, S, R, P, St} || {U, S, R, P, St} <- Res].
810
status_list(Status) ->
811
    status_list("all", Status).
812
813
814
get_status_list(Host, Status_required) ->
815
    %% Get list of all logged users
816
    Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
817
    %% Reformat the list
818
    Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
819
    Fhost = case Host of
820
		"all" ->
821
		    %% All hosts are requested, so dont filter at all
822
		    fun(_, _) -> true end;
823
		_ ->
824
		    %% Filter the list, only Host is interesting
825
		    fun(A, B) -> A == B end
826
	    end,
827
    Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
828
    %% For each Pid, get its presence
829
    Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
830
    %% Filter by status
831
    Fstatus = case Status_required of
832
		  "all" ->
833
		      fun(_, _) -> true end;
834
		  _ ->
835
		      fun(A, B) -> A == B end
836
	      end,
837
    [{User, Server, Resource, Priority, stringize(Status_text)}
838
     || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
839
	apply(Fstatus, [Status, Status_required])].
840
841
connected_users_info() ->
842
    USRIs = dirty_get_sessions_list2(),
843
    CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
844
    lists:map(
845
      fun([{U, S, R}, {Now, Pid}, Priority, Info]) ->
846
	      Conn = proplists:get_value(conn, Info),
847
	      {Ip, Port} = proplists:get_value(ip, Info),
848
	      IPS = inet_parse:ntoa(Ip),
849
	      NodeS = atom_to_list(node(Pid)),
850
	      Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
851
				      calendar:now_to_local_time(Now)),
852
	      {[U, $@, S, $/, R], atom_to_list(Conn), IPS, Port, Priority, NodeS, Uptime}
853
      end,
854
      USRIs).
855
856
connected_users_vhost(Host) ->
857
    USRs = ejabberd_sm:get_vh_session_list(Host),
858
    [ [U, $@, S, $/, R] || {U, S, R} <- USRs].
859
860
%% Code copied from ejabberd_sm.erl and customized
861
dirty_get_sessions_list2() ->
862
    mnesia:dirty_select(
863
      session,
864
      [{#session{usr = '$1', sid = '$2', priority = '$3', info = '$4', _ = '_'},
865
	[],
866
	[['$1', '$2', '$3', '$4']]}]).
867
868
%% Make string more print-friendly
869
stringize(String) ->
870
    %% Replace newline characters with other code
871
    ejabberd_regexp:greplace(String, "\n", "\\n").
872
873
set_presence(User, Host, Resource, Type, Show, Status, Priority) ->
874
    Pid = ejabberd_sm:get_session_pid(User, Host, Resource),
875
    USR = User ++ "@" ++ Host ++ "/" ++ Resource,
876
    US = User ++ "@" ++ Host,
877
    Message = {route_xmlstreamelement,
878
	       {xmlelement, "presence",
879
		[{"from", USR}, {"to", US}, {"type", Type}],
880
		[{xmlelement, "show", [], [{xmlcdata, Show}]},
881
		 {xmlelement, "status", [], [{xmlcdata, Status}]},
882
		 {xmlelement, "priority", [], [{xmlcdata, Priority}]}]}},
883
    Pid ! Message.
884
885
user_sessions_info(User, Host) ->
886
    CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
887
    US = {User, Host},
888
    Sessions = case catch mnesia:dirty_index_read(session, US, #session.us) of
889
		   {'EXIT', _Reason} ->
890
		       [];
891
		   Ss ->
892
		       Ss
893
	       end,
894
    lists:map(
895
      fun(Session) ->
896
	      {_U, _S, Resource} = Session#session.usr,
897
	      {Now, Pid} = Session#session.sid,
898
	      {_U, _Resource, Status, StatusText} = ejabberd_c2s:get_presence(Pid),
899
	      Info = Session#session.info,
900
	      Priority = Session#session.priority,
901
	      Conn = proplists:get_value(conn, Info),
902
	      {Ip, Port} = proplists:get_value(ip, Info),
903
	      IPS = inet_parse:ntoa(Ip),
904
	      NodeS = atom_to_list(node(Pid)),
905
	      Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
906
				      calendar:now_to_local_time(Now)),
907
	      {atom_to_list(Conn), IPS, Port, Priority, NodeS, Uptime, Status, Resource, StatusText}
908
      end,
909
      Sessions).
910
911
912
%%%
913
%%% Vcard
914
%%%
915
916
set_nickname(User, Host, Nickname) ->
917
    R = mod_vcard:process_sm_iq(
918
	  {jid, User, Host, "", User, Host, ""},
919
	  {jid, User, Host, "", User, Host, ""},
920
	  {iq, "", set, "", "en",
921
	   {xmlelement, "vCard",
922
	    [{"xmlns", "vcard-temp"}], [
923
					{xmlelement, "NICKNAME", [], [{xmlcdata, Nickname}]}
924
				       ]
925
	   }}),
926
    case R of
927
	{iq, [], result, [], _L, []} ->
928
	    ok;
929
	_ ->
930
	    error
931
    end.
932
933
get_vcard(User, Host, Name) ->
934
    [Res | _] = get_vcard_content(User, Host, [Name]),
935
    Res.
936
937
get_vcard(User, Host, Name, Subname) ->
938
    [Res | _] = get_vcard_content(User, Host, [Name, Subname]),
939
    Res.
940
941
get_vcard_multi(User, Host, Name, Subname) ->
942
    get_vcard_content(User, Host, [Name, Subname]).
943
944
set_vcard(User, Host, Name, SomeContent) ->
945
    set_vcard_content(User, Host, [Name], SomeContent).
946
947
set_vcard(User, Host, Name, Subname, SomeContent) ->
948
    set_vcard_content(User, Host, [Name, Subname], SomeContent).
949
950
951
%%
952
%% Internal vcard
953
954
get_module_resource(Server) ->
955
    case gen_mod:get_module_opt(Server, ?MODULE, module_resource, none) of
956
	none -> atom_to_list(?MODULE);
957
	R when is_list(R) -> R
958
    end.
959
960
get_vcard_content(User, Server, Data) ->
961
    [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
962
    JID = jlib:make_jid(User, Server, get_module_resource(Server)),
963
    IQ = #iq{type = get, xmlns = ?NS_VCARD},
964
    IQr = Module:Function(JID, JID, IQ),
965
    case IQr#iq.sub_el of
966
	[A1] ->
967
	    case get_vcard(Data, A1) of
968
		[] -> throw(error_no_value_found_in_vcard);
969
		ElemList -> [xml:get_tag_cdata(Elem) || Elem <- ElemList]
970
	    end;
971
	[] ->
972
	    throw(error_no_vcard_found)
973
    end.
974
975
get_vcard([Data1, Data2], A1) ->
976
    case get_subtag(A1, Data1) of
977
    	false -> false;
978
	A2List -> lists:flatten([get_vcard([Data2], A2) || A2 <- A2List])
979
    end;
980
981
get_vcard([Data], A1) ->
982
    get_subtag(A1, Data).
983
984
get_subtag(Xmlelement, Name) ->
985
    case code:ensure_loaded(exmpp_xml) of
986
	{error, _} ->
987
	    [get_subtag_xml(Xmlelement, Name)];
988
	{module, exmpp_xml} ->
989
	    get_subtag_exmpp(Xmlelement, Name)
990
    end.
991
992
get_subtag_xml(Xmlelement, Name) ->
993
    xml:get_subtag(Xmlelement, Name).
994
995
get_subtag_exmpp(Xmlelement, Name) ->
996
    Xmlel = exmpp_xml:xmlelement_to_xmlel(Xmlelement),
997
    XmlelList = exmpp_xml:get_elements(Xmlel, Name),
998
    [exmpp_xml:xmlel_to_xmlelement(Xmlel2) || Xmlel2 <- XmlelList].
999
1000
set_vcard_content(User, Server, Data, SomeContent) ->
1001
    ContentList = case SomeContent of
1002
	[Char | _] when not is_list(Char) -> [SomeContent];
1003
	[Char | _] when is_list(Char) -> SomeContent
1004
    end,
1005
    [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
1006
    JID = jlib:make_jid(User, Server, get_module_resource(Server)),
1007
    IQ = #iq{type = get, xmlns = ?NS_VCARD},
1008
    IQr = Module:Function(JID, JID, IQ),
1009
1010
    %% Get old vcard
1011
    A4 = case IQr#iq.sub_el of
1012
	     [A1] ->
1013
		 {_, _, _, A2} = A1,
1014
		 update_vcard_els(Data, ContentList, A2);
1015
	     [] ->
1016
		 update_vcard_els(Data, ContentList, [])
1017
	 end,
1018
1019
    %% Build new vcard
1020
    SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
1021
    IQ2 = #iq{type=set, sub_el = SubEl},
1022
1023
    Module:Function(JID, JID, IQ2),
1024
    ok.
1025
1026
update_vcard_els(Data, ContentList, Els1) ->
1027
    Els2 = lists:keysort(2, Els1),
1028
    [Data1 | Data2] = Data,
1029
    NewEls = case Data2 of
1030
		[] ->
1031
		    [{xmlelement, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList];
1032
		[D2] ->
1033
		    OldEl = case lists:keysearch(Data1, 2, Els2) of
1034
				{value, A} -> A;
1035
				false -> {xmlelement, Data1, [], []}
1036
			    end,
1037
		    {xmlelement, _, _, ContentOld1} = OldEl,
1038
		    Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]} || Content <- ContentList],
1039
		    ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2],
1040
		    ContentOld3 = lists:keysort(2, ContentOld2),
1041
		    ContentNew = lists:keymerge(2, Content2, ContentOld3),
1042
		    [{xmlelement, Data1, [], ContentNew}]
1043
	    end,
1044
    Els3 = lists:keydelete(Data1, 2, Els2),
1045
    lists:keymerge(2, NewEls, Els3).
1046
1047
1048
%%%
1049
%%% Roster
1050
%%%
1051
1052
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) ->
1053
    case add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, list_to_atom(Subs), []) of
1054
	{atomic, ok} ->
1055
	    push_roster_item(LocalUser, LocalServer, User, Server, {add, Nick, Subs, Group}),
1056
	    ok;
1057
	_ ->
1058
	    error
1059
    end.
1060
1061
add_rosteritem(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) ->
1062
    subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs).
1063
1064
subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) ->
1065
    SubscriptionS = case is_atom(Subscription) of
1066
	true -> atom_to_list(Subscription);
1067
	false -> Subscription
1068
    end,
1069
    ItemEl = build_roster_item(User, Server, {add, Nick, SubscriptionS, Group}),
1070
    {ok, M} = loaded_module(LS,[mod_roster_odbc,mod_roster]),
1071
    M:set_items(
1072
	LU, LS,
1073
	{xmlelement,"query",
1074
            [{"xmlns","jabber:iq:roster"}],
1075
            [ItemEl]}).
1076
1077
delete_rosteritem(LocalUser, LocalServer, User, Server) ->
1078
    case unsubscribe(LocalUser, LocalServer, User, Server) of
1079
	{atomic, ok} ->
1080
	    push_roster_item(LocalUser, LocalServer, User, Server, remove),
1081
	    ok;
1082
	_  ->
1083
	    error
1084
    end.
1085
1086
unsubscribe(LU, LS, User, Server) ->
1087
    ItemEl = build_roster_item(User, Server, remove),
1088
    {ok, M} = loaded_module(LS,[mod_roster_odbc,mod_roster]),
1089
    M:set_items(
1090
	LU, LS,
1091
	{xmlelement,"query",
1092
            [{"xmlns","jabber:iq:roster"}],
1093
            [ItemEl]}).
1094
1095
loaded_module(Domain,Options) ->
1096
    LoadedModules = gen_mod:loaded_modules(Domain),
1097
    case lists:filter(fun(Module) ->
1098
                              lists:member(Module, LoadedModules)
1099
                      end, Options) of
1100
        [M|_] -> {ok, M};
1101
        [] -> {error,not_found}
1102
    end.
1103
1104
%% -----------------------------
1105
%% Get Roster
1106
%% -----------------------------
1107
1108
get_roster(User, Server) ->
1109
    Items = ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]),
1110
    make_roster_xmlrpc(Items).
1111
1112
%% Note: if a contact is in several groups, the contact is returned
1113
%% several times, each one in a different group.
1114
make_roster_xmlrpc(Roster) ->
1115
    lists:foldl(
1116
      fun(Item, Res) ->
1117
	      JIDS = jlib:jid_to_string(Item#roster.jid),
1118
	      Nick = Item#roster.name,
1119
	      Subs = atom_to_list(Item#roster.subscription),
1120
	      Ask = atom_to_list(Item#roster.ask),
1121
	      Groups = case Item#roster.groups of
1122
			   [] -> [""];
1123
			   Gs -> Gs
1124
		       end,
1125
	      ItemsX = [{JIDS, Nick, Subs, Ask, Group}
1126
			|| Group <- Groups],
1127
	      ItemsX ++ Res
1128
      end,
1129
      [],
1130
      Roster).
1131
1132
1133
%%-----------------------------
1134
%% Push Roster from file
1135
%%-----------------------------
1136
1137
push_roster(File, User, Server) ->
1138
    {ok, [Roster]} = file:consult(File),
1139
    subscribe_roster({User, Server, "", User}, Roster).
1140
1141
push_roster_all(File) ->
1142
    {ok, [Roster]} = file:consult(File),
1143
    subscribe_all(Roster).
1144
1145
subscribe_all(Roster) ->
1146
    subscribe_all(Roster, Roster).
1147
subscribe_all([], _) ->
1148
    ok;
1149
subscribe_all([User1 | Users], Roster) ->
1150
    subscribe_roster(User1, Roster),
1151
    subscribe_all(Users, Roster).
1152
1153
subscribe_roster(_, []) ->
1154
    ok;
1155
%% Do not subscribe a user to itself
1156
subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
1157
    subscribe_roster({Name, Server, Group, Nick}, Roster);
1158
%% Subscribe Name2 to Name1
1159
subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
1160
    subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
1161
    subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
1162
1163
push_alltoall(S, G) ->
1164
    Users = ejabberd_auth:get_vh_registered_users(S),
1165
    Users2 = build_list_users(G, Users, []),
1166
    subscribe_all(Users2),
1167
    ok.
1168
1169
build_list_users(_Group, [], Res) ->
1170
    Res;
1171
build_list_users(Group, [{User, Server}|Users], Res) ->
1172
    build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
1173
1174
%% @spec(LU, LS, U, S, Action) -> ok
1175
%%       Action = {add, Nick, Subs, Group} | remove
1176
%% @doc Push to the roster of account LU@LS the contact U@S.
1177
%% The specific action to perform is defined in Action.
1178
push_roster_item(LU, LS, U, S, Action) ->
1179
    lists:foreach(fun(R) ->
1180
			  push_roster_item(LU, LS, R, U, S, Action)
1181
		  end, ejabberd_sm:get_user_resources(LU, LS)).
1182
1183
push_roster_item(LU, LS, R, U, S, Action) ->
1184
    LJID = jlib:make_jid(LU, LS, R),
1185
    BroadcastEl = build_broadcast(U, S, Action),
1186
    ejabberd_router:route(LJID, LJID, BroadcastEl),
1187
    Item = build_roster_item(U, S, Action),
1188
    ResIQ = build_iq_roster_push(Item),
1189
    ejabberd_router:route(LJID, LJID, ResIQ).
1190
1191
build_roster_item(U, S, {add, Nick, Subs, Group}) ->
1192
    {xmlelement, "item",
1193
     [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))},
1194
      {"name", Nick},
1195
      {"subscription", Subs}],
1196
     [{xmlelement, "group", [], [{xmlcdata, Group}]}]
1197
    };
1198
build_roster_item(U, S, remove) ->
1199
    {xmlelement, "item",
1200
     [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))},
1201
      {"subscription", "remove"}],
1202
     []
1203
    }.
1204
1205
build_iq_roster_push(Item) ->
1206
    {xmlelement, "iq",
1207
     [{"type", "set"}, {"id", "push"}],
1208
     [{xmlelement, "query",
1209
       [{"xmlns", ?NS_ROSTER}],
1210
       [Item]
1211
      }
1212
     ]
1213
    }.
1214
1215
build_broadcast(U, S, {add, _Nick, Subs, _Group}) ->
1216
    build_broadcast(U, S, list_to_atom(Subs));
1217
build_broadcast(U, S, remove) ->
1218
    build_broadcast(U, S, none);
1219
%% @spec (U::string(), S::string(), Subs::atom()) -> any()
1220
%% Subs = both | from | to | none
1221
build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) ->
1222
    {xmlelement, "broadcast", [],
1223
     [{item, {U, S, ""}, SubsAtom}]
1224
    }.
1225
1226
%%%
1227
%%% Last Activity
1228
%%%
1229
1230
set_last(User, Server, Timestamp, Status) ->
1231
    Mod = get_lastactivity_module(Server),
1232
    Mod:store_last_info(User, Server, Timestamp, Status).
1233
1234
%%%
1235
%%% Private Storage
1236
%%%
1237
1238
%% Example usage:
1239
%% $ ejabberdctl private_set badlop localhost "\<aa\ xmlns=\'bb\'\>Cluth\</aa\>"
1240
%% $ ejabberdctl private_get badlop localhost aa bb
1241
%% <aa xmlns='bb'>Cluth</aa>
1242
1243
private_get(Username, Host, Element, Ns) ->
1244
    From = jlib:make_jid(Username, Host, ""),
1245
    To = jlib:make_jid(Username, Host, ""),
1246
    IQ = {iq, "", get, ?NS_PRIVATE, "",
1247
	  {xmlelement,"query",
1248
	   [{"xmlns",?NS_PRIVATE}],
1249
	   [{xmlelement, Element, [{"xmlns", Ns}], []}]}},
1250
    ResIq = mod_private:process_sm_iq(From, To, IQ),
1251
    [{xmlelement,"query",
1252
      [{"xmlns","jabber:iq:private"}],
1253
      [SubEl]}] = ResIq#iq.sub_el,
1254
    xml:element_to_string(SubEl).
1255
1256
private_set(Username, Host, ElementString) ->
1257
    case xml_stream:parse_element(ElementString) of
1258
	{error, Error} ->
1259
	    io:format("Error found parsing the element:~n  ~p~nError: ~p~n",
1260
		      [ElementString, Error]),
1261
	    error;
1262
	Xml ->
1263
	    private_set2(Username, Host, Xml)
1264
    end.
1265
1266
private_set2(Username, Host, Xml) ->
1267
    From = jlib:make_jid(Username, Host, ""),
1268
    To = jlib:make_jid(Username, Host, ""),
1269
    IQ = {iq, "", set, ?NS_PRIVATE, "",
1270
	  {xmlelement,"query",
1271
	   [{"xmlns",?NS_PRIVATE}],
1272
	   [Xml]}},
1273
    mod_private:process_sm_iq(From, To, IQ),
1274
    ok.
1275
1276
%%%
1277
%%% Shared Roster Groups
1278
%%%
1279
1280
srg_create(Group, Host, Name, Description, Display) ->
1281
    DisplayList = case Display of
1282
	[] -> [];
1283
	_ -> ejabberd_regexp:split(Display, "\\\\n")
1284
    end,
1285
    Opts = [{name, Name},
1286
	    {displayed_groups, DisplayList},
1287
	    {description, Description}],
1288
    {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
1289
    ok.
1290
1291
srg_delete(Group, Host) ->
1292
    {atomic, ok} = mod_shared_roster:delete_group(Host, Group),
1293
    ok.
1294
1295
srg_list(Host) ->
1296
    lists:sort(mod_shared_roster:list_groups(Host)).
1297
1298
srg_get_info(Group, Host) ->
1299
    Opts = mod_shared_roster:get_group_opts(Host,Group),
1300
    [{io_lib:format("~p", [Title]),
1301
      io_lib:format("~p", [Value])} || {Title, Value} <- Opts].
1302
1303
srg_get_members(Group, Host) ->
1304
    Members = mod_shared_roster:get_group_explicit_users(Host,Group),
1305
    [jlib:jid_to_string(jlib:make_jid(MUser, MServer, ""))
1306
     || {MUser, MServer} <- Members].
1307
1308
srg_user_add(User, Host, Group, GroupHost) ->
1309
    {atomic, ok} = mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group),
1310
    ok.
1311
1312
srg_user_del(User, Host, Group, GroupHost) ->
1313
    {atomic, ok} = mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group),
1314
    ok.
1315
1316
1317
%%%
1318
%%% Stanza
1319
%%%
1320
1321
%% @doc Send a chat message to a Jabber account.
1322
%% @spec (From::string(), To::string(), Body::string()) -> ok
1323
send_message_chat(From, To, Body) ->
1324
    Packet = build_packet(message_chat, [Body]),
1325
    send_packet_all_resources(From, To, Packet).
1326
1327
%% @doc Send a headline message to a Jabber account.
1328
%% @spec (From::string(), To::string(), Subject::string(), Body::string()) -> ok
1329
send_message_headline(From, To, Subject, Body) ->
1330
    Packet = build_packet(message_headline, [Subject, Body]),
1331
    send_packet_all_resources(From, To, Packet).
1332
1333
%% @doc Send a packet to a Jabber account.
1334
%% If a resource was specified in the JID,
1335
%% the packet is sent only to that specific resource.
1336
%% If no resource was specified in the JID,
1337
%% and the user is remote or local but offline,
1338
%% the packet is sent to the bare JID.
1339
%% If the user is local and is online in several resources,
1340
%% the packet is sent to all its resources.
1341
send_packet_all_resources(FromJIDString, ToJIDString, Packet) ->
1342
    FromJID = jlib:string_to_jid(FromJIDString),
1343
    ToJID = jlib:string_to_jid(ToJIDString),
1344
    ToUser = ToJID#jid.user,
1345
    ToServer = ToJID#jid.server,
1346
    case ToJID#jid.resource of
1347
	"" ->
1348
	    send_packet_all_resources(FromJID, ToUser, ToServer, Packet);
1349
	Res ->
1350
	    send_packet_all_resources(FromJID, ToUser, ToServer, Res, Packet)
1351
    end.
1352
1353
send_packet_all_resources(FromJID, ToUser, ToServer, Packet) ->
1354
    case ejabberd_sm:get_user_resources(ToUser, ToServer) of
1355
	[] ->
1356
	    send_packet_all_resources(FromJID, ToUser, ToServer, "", Packet);
1357
	ToResources ->
1358
	    lists:foreach(
1359
	      fun(ToResource) ->
1360
		      send_packet_all_resources(FromJID, ToUser, ToServer,
1361
						ToResource, Packet)
1362
	      end,
1363
	      ToResources)
1364
    end.
1365
1366
send_packet_all_resources(FromJID, ToU, ToS, ToR, Packet) ->
1367
    ToJID = jlib:make_jid(ToU, ToS, ToR),
1368
    ejabberd_router:route(FromJID, ToJID, Packet).
1369
1370
1371
build_packet(message_chat, [Body]) ->
1372
    {xmlelement, "message",
1373
     [{"type", "chat"}, {"id", randoms:get_string()}],
1374
     [{xmlelement, "body", [], [{xmlcdata, Body}]}]
1375
    };
1376
build_packet(message_headline, [Subject, Body]) ->
1377
    {xmlelement, "message",
1378
     [{"type", "headline"}, {"id", randoms:get_string()}],
1379
     [{xmlelement, "subject", [], [{xmlcdata, Subject}]},
1380
      {xmlelement, "body", [], [{xmlcdata, Body}]}
1381
     ]
1382
    }.
1383
1384
send_stanza_c2s(Username, Host, Resource, Stanza) ->
1385
    C2sPid = ejabberd_sm:get_session_pid(Username, Host, Resource),
1386
    XmlEl = xml_stream:parse_element(Stanza),
1387
    p1_fsm:send_event(C2sPid, {xmlstreamelement, XmlEl}).
1388
1389
privacy_set(Username, Host, QueryS) ->
1390
    From = jlib:string_to_jid(Username ++ "@" ++ Host),
1391
    To = jlib:string_to_jid(Host),
1392
    QueryEl = xml_stream:parse_element(QueryS),
1393
    StanzaEl = {xmlelement, "iq", [{"type", "set"}], [QueryEl]},
1394
    IQ = jlib:iq_query_info(StanzaEl),
1395
    ejabberd_hooks:run_fold(
1396
		     privacy_iq_set,
1397
		     Host,
1398
		     {error, ?ERR_FEATURE_NOT_IMPLEMENTED},
1399
		     [From, To, IQ]
1400
		    ),
1401
    ok.
1402
1403
%%%
1404
%%% Stats
1405
%%%
1406
1407
stats(Name) ->
1408
    case Name of
1409
	"uptimeseconds" -> trunc(element(1, erlang:statistics(wall_clock))/1000);
1410
	"registeredusers" -> length(ejabberd_auth:dirty_get_registered_users());
1411
	"onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list());
1412
	"onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list())
1413
    end.
1414
1415
stats(Name, Host) ->
1416
    case Name of
1417
	"registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
1418
	"onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
1419
    end.
1420
1421
1422
1423
%%-----------------------------
1424
%% Purge roster items
1425
%%-----------------------------
1426
1427
process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
1428
    Action = case ActionS of
1429
		 "list" -> list;
1430
		 "delete" -> delete
1431
	     end,
1432
1433
    Subs = lists:foldl(
1434
	     fun(any, _) -> [none, from, to, both];
1435
		(Sub, Subs) -> [Sub | Subs]
1436
	     end,
1437
	     [],
1438
	     [list_to_atom(S) || S <- string:tokens(SubsS, ":")]
1439
	    ),
1440
1441
    Asks = lists:foldl(
1442
	     fun(any, _) -> [none, out, in];
1443
		(Ask, Asks) -> [Ask | Asks]
1444
	     end,
1445
	     [],
1446
	     [list_to_atom(S) || S <- string:tokens(AsksS, ":")]
1447
	    ),
1448
1449
    Users = lists:foldl(
1450
	      fun("any", _) -> ["*", "*@*"];
1451
		 (U, Us) -> [U | Us]
1452
	      end,
1453
	      [],
1454
	      [S || S <- string:tokens(UsersS, ":")]
1455
	     ),
1456
1457
    Contacts = lists:foldl(
1458
		 fun("any", _) -> ["*", "*@*"];
1459
		    (U, Us) -> [U | Us]
1460
		 end,
1461
		 [],
1462
		 [S || S <- string:tokens(ContactsS, ":")]
1463
		),
1464
1465
    case rosteritem_purge({Action, Subs, Asks, Users, Contacts}) of
1466
	{atomic, ok} ->
1467
	    ok;
1468
	{error, Reason} ->
1469
	    io:format("Error purging rosteritems: ~p~n", [Reason]),
1470
	    error;
1471
	{badrpc, Reason} ->
1472
	    io:format("BadRPC purging rosteritems: ~p~n", [Reason]),
1473
	    error
1474
    end.
1475
1476
%% @spec ({Action::atom(), Subs::[atom()], Asks::[atom()], User::string(), Contact::string()}) -> {atomic, ok}
1477
rosteritem_purge(Options) ->
1478
    Num_rosteritems = mnesia:table_info(roster, size),
1479
    io:format("There are ~p roster items in total.~n", [Num_rosteritems]),
1480
    Key = mnesia:dirty_first(roster),
1481
    ok = rip(Key, Options, {0, Num_rosteritems, 0, 0}),
1482
    {atomic, ok}.
1483
1484
rip('$end_of_table', _Options, Counters) ->
1485
    print_progress_line(Counters),
1486
    ok;
1487
rip(Key, Options, {Pr, NT, NV, ND}) ->
1488
    Key_next = mnesia:dirty_next(roster, Key),
1489
    {Action, _, _, _, _} = Options,
1490
    ND2 = case decide_rip(Key, Options) of
1491
	      true ->
1492
		  apply_action(Action, Key),
1493
		  ND+1;
1494
	      false ->
1495
		  ND
1496
	  end,
1497
    NV2 = NV+1,
1498
    Pr2 = print_progress_line({Pr, NT, NV2, ND2}),
1499
    rip(Key_next, Options, {Pr2, NT, NV2, ND2}).
1500
1501
apply_action(list, Key) ->
1502
    {User, Server, JID} = Key,
1503
    {RUser, RServer, _} = JID,
1504
    io:format("Matches: ~s@~s ~s@~s~n", [User, Server, RUser, RServer]);
1505
apply_action(delete, Key) ->
1506
    apply_action(list, Key),
1507
    mnesia:dirty_delete(roster, Key).
1508
1509
print_progress_line({Pr, NT, NV, ND}) ->
1510
    Pr2 = trunc((NV/NT)*100),
1511
    case Pr == Pr2 of
1512
	true ->
1513
	    ok;
1514
	false ->
1515
	    io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND])
1516
    end,
1517
    Pr2.
1518
1519
decide_rip(Key, {_Action, Subs, Asks, User, Contact}) ->
1520
    case catch mnesia:dirty_read(roster, Key) of
1521
	[RI] ->
1522
	    lists:member(RI#roster.subscription, Subs)
1523
		andalso lists:member(RI#roster.ask, Asks)
1524
		andalso decide_rip_jid(RI#roster.us, User)
1525
		andalso decide_rip_jid(RI#roster.jid, Contact);
1526
	_ ->
1527
	    false
1528
    end.
1529
1530
%% Returns true if the server of the JID is included in the servers
1531
decide_rip_jid({UName, UServer, _UResource}, Match_list) ->
1532
    decide_rip_jid({UName, UServer}, Match_list);
1533
decide_rip_jid({UName, UServer}, Match_list) ->
1534
    lists:any(
1535
      fun(Match_string) ->
1536
	      MJID = jlib:string_to_jid(Match_string),
1537
	      MName = MJID#jid.luser,
1538
	      MServer = MJID#jid.lserver,
1539
	      Is_server = is_glob_match(UServer, MServer),
1540
	      case MName of
1541
		  [] when UName == [] ->
1542
		      Is_server;
1543
		  [] ->
1544
		      false;
1545
		  _ ->
1546
		      Is_server
1547
			  andalso is_glob_match(UName, MName)
1548
	      end
1549
      end,
1550
      Match_list).
1551
1552
%% Copied from ejabberd-2.0.0/src/acl.erl
1553
is_regexp_match(String, RegExp) ->
1554
    case ejabberd_regexp:run(String, RegExp) of
1555
	nomatch ->
1556
	    false;
1557
	match ->
1558
	    true;
1559
	{error, ErrDesc} ->
1560
	    io:format(
1561
	      "Wrong regexp ~p in ACL: ~p",
1562
	      [RegExp, ErrDesc]),
1563
	    false
1564
    end.
1565
is_glob_match(String, [$! | Glob]) ->
1566
    not is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob));
1567
is_glob_match(String, Glob) ->
1568
    is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
(-)a/src/mod_ecomm_test.erl (+427 lines)
Line 0 Link Here
1
%%%-------------------------------------------------------------------
2
%%% File    : mod_ecomm_test.erl
3
%%% Author  : Badlop <badlop@process-one.net>
4
%%% Purpose : Simple commands for testing
5
%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
6
%%%
7
%%%
8
%%% ejabberd, Copyright (C) 2002-2008   ProcessOne
9
%%%
10
%%% This program is free software; you can redistribute it and/or
11
%%% modify it under the terms of the GNU General Public License as
12
%%% published by the Free Software Foundation; either version 2 of the
13
%%% License, or (at your option) any later version.
14
%%%
15
%%% This program is distributed in the hope that it will be useful,
16
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
%%% General Public License for more details.
19
%%%
20
%%% You should have received a copy of the GNU General Public License
21
%%% along with this program; if not, write to the Free Software
22
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23
%%% 02111-1307 USA
24
%%%
25
%%%-------------------------------------------------------------------
26
27
-module(mod_ecomm_test).
28
-author('badlop@process-one.net').
29
30
-behaviour(gen_mod).
31
32
-export([start/2, stop/1,
33
	 %% Take: test arguments
34
	 take_integer/1,
35
	 take_string/1,
36
	 take_integer_string/2,
37
	 take_tuple_2integer/1,
38
	 take_tuple_2string/1,
39
	 take_list_integer/1,
40
	 take_list_string/1,
41
	 %% Echo: test arguments and result
42
	 echo_integer/1,
43
	 echo_string/1,
44
	 echo_integer_string/2,
45
	 echo_list_integer/1,
46
	 echo_list_string/1,
47
	 echo_integer_list_string/2,
48
	 echo_isatils/4,
49
	 %% Tell: test result
50
	 tell_atom/1,
51
	 tell_rescode/1,
52
	 tell_restuple/1,
53
	 tell_tuple_3integer/0,
54
	 tell_tuple_3string/0,
55
	 tell_tuple_3atom/0,
56
	 tell_tuple_3list/0,
57
	 tell_list_3integer/0,
58
	 tell_list_3string/0,
59
	 tell_list_3atom/0,
60
	 tell_list_3tuple/0,
61
	 %% Realistic
62
	 this_crashes/1,
63
	 this_wrong_args/1,
64
	 this_wrong_return/0,
65
	 pow/2, seq/2, substrs/1, splitjid/1, splitjids/1
66
	]).
67
68
-include("ejabberd.hrl").
69
-include("ejabberd_commands.hrl").
70
-include("jlib.hrl").
71
72
start(_Host, _Opts) ->
73
    ejabberd_commands:register_commands(commands()).
74
75
stop(_Host) ->
76
    ejabberd_commands:unregister_commands(commands()).
77
78
%%%
79
%%% ejabberd commands
80
%%%
81
82
commands() ->
83
    [
84
85
     #ejabberd_commands{name = take_integer, tags = [test],
86
		       desc = "Take Integer in args, give Integer zero",
87
		       module = ?MODULE, function = take_integer,
88
		       args = [{thisinteger, integer}],
89
		       result = {zero, integer}},
90
91
     #ejabberd_commands{name = take_string, tags = [test],
92
		       desc = "Take String, give Integer zero",
93
		       module = ?MODULE, function = take_string,
94
		       args = [{thisstring, string}],
95
		       result = {zero, integer}},
96
97
     #ejabberd_commands{name = take_integer_string, tags = [test],
98
		       desc = "Take integer and string, give Integer zero",
99
		       module = ?MODULE, function = take_integer_string,
100
		       args = [{thisinteger, integer}, {thisstring, string}],
101
		       result = {zero, integer}},
102
103
     %% Not supported by ejabberd_ctl
104
     #ejabberd_commands{name = take_tuple_2integer, tags = [test],
105
		       desc = "Take Tuple of two integers, give Integer zero",
106
		       module = ?MODULE, function = take_tuple_2integer,
107
		       args = [{thistuple, {tuple, [{thisinteger1, integer}, {thisinteger2, integer}]}}],
108
		       result = {zero, integer}},
109
     %% Not supported by ejabberd_ctl
110
     #ejabberd_commands{name = take_tuple_2string, tags = [test],
111
		       desc = "Take Tuple of two strings, give Integer zero",
112
		       module = ?MODULE, function = take_tuple_2string,
113
		       args = [{thistuple, {tuple, [{thisstring1, string}, {thisstring2, string}]}}],
114
		       result = {zero, integer}},
115
     %% Not supported by ejabberd_ctl
116
     #ejabberd_commands{name = take_list_integer, tags = [test],
117
		       desc = "Take List of integers, give Integer zero",
118
		       module = ?MODULE, function = take_list_integer,
119
		       args = [{thislist, {list, {thisinteger, integer}}}],
120
		       result = {zero, integer}},
121
     %% Not supported by ejabberd_ctl
122
     #ejabberd_commands{name = take_list_string, tags = [test],
123
		       desc = "Take List of strings, give Integer zero",
124
		       module = ?MODULE, function = take_list_string,
125
		       args = [{thislist, {list, {thisstring, string}}}],
126
		       result = {zero, integer}},
127
128
     #ejabberd_commands{name = echo_integer, tags = [test],
129
		       desc = "Echo Integer",
130
		       module = ?MODULE, function = echo_integer,
131
		       args = [{thisinteger, integer}],
132
		       result = {thatinteger, integer}},
133
     #ejabberd_commands{name = echo_string, tags = [test],
134
		       desc = "Echo String",
135
		       module = ?MODULE, function = echo_string,
136
		       args = [{thisstring, string}],
137
		       result = {thatstring, string}},
138
     #ejabberd_commands{name = echo_integer_string, tags = [test],
139
		       desc = "Echo integer and string, in result as a tuple",
140
		       module = ?MODULE, function = echo_integer_string,
141
		       args = [{thisinteger, integer}, {thisstring, string}],
142
		       result = {thistuple, {tuple, [{thisinteger, integer}, {thisstring, string}]}}},
143
     %% Not supported by ejabberd_ctl
144
     #ejabberd_commands{name = echo_list_integer, tags = [test],
145
		       desc = "Echo List of integers",
146
		       module = ?MODULE, function = echo_list_integer,
147
		       args = [{thislist, {list, {thisinteger, integer}}}],
148
		       result = {thatlist, {list, {thatinteger, integer}}}},
149
     %% Not supported by ejabberd_ctl
150
     #ejabberd_commands{name = echo_list_string, tags = [test],
151
		       desc = "Echo List of strings",
152
		       module = ?MODULE, function = echo_list_string,
153
		       args = [{thislist, {list, {thisstring, string}}}],
154
		       result = {thatlist, {list, {thatstring, string}}}},
155
     %% Not supported by ejabberd_ctl
156
     #ejabberd_commands{name = echo_integer_list_string, tags = [test],
157
		       desc = "Echo an integer and List of strings",
158
		       module = ?MODULE, function = echo_integer_list_string,
159
		       args = [{thisinteger, integer}, {thislist, {list, {thisstring, string}}}],
160
		       result = {thistuple, {tuple, [{thatinteger, integer}, {thatlist, {list, {thatstring, string}}}]}}},
161
     %% Not supported by ejabberd_ctl
162
     #ejabberd_commands{name = echo_isatils, tags = [test],
163
		       desc = "Echo integer, string, atom and tuple of integer and list of strings",
164
		       module = ?MODULE, function = echo_isatils,
165
		       args = [{thisinteger, integer},
166
			       {thisstring, string},
167
			       {thisatom, atom},
168
			       {thistuple, {tuple, [
169
						    {listlen, integer},
170
						    {thislist, {list, {contentstring, string}}}
171
						   ]}}
172
			      ],
173
		       result = {results, {tuple, [{thatinteger, integer},
174
						   {thatstring, string},
175
						   {thatatom, atom},
176
						   {thattuple, {tuple, [
177
									{listlen, integer},
178
									{thatlist, {list, {contentstring, string}}}
179
								       ]}}
180
						  ]}}},
181
182
     #ejabberd_commands{name = tell_atom, tags = [test],
183
		       desc = "Tell Atom, give Integer zero",
184
		       module = ?MODULE, function = tell_atom,
185
		       args = [{thisinteger, integer}],
186
		       result = {thisatom, atom}},
187
     #ejabberd_commands{name = tell_rescode, tags = [test],
188
		       desc = "Tell rescode",
189
		       module = ?MODULE, function = tell_rescode,
190
		       args = [{thisinteger, integer}],
191
		       result = {res, rescode}},
192
     #ejabberd_commands{name = tell_restuple, tags = [test],
193
		       desc = "Tell restuple",
194
		       module = ?MODULE, function = tell_restuple,
195
		       args = [{thisinteger, integer}],
196
		       result = {res, restuple}},
197
     #ejabberd_commands{name = tell_tuple_3integer, tags = [test],
198
		       desc = "Tell a tuple with 3 integers",
199
		       module = ?MODULE, function = tell_tuple_3integer,
200
		       args = [],
201
		       result = {thattuple, {tuple, [{first, integer},
202
						     {second, integer},
203
						     {third, integer}]}}},
204
     #ejabberd_commands{name = tell_tuple_3string, tags = [test],
205
		       desc = "Tell a tuple with 3 strings",
206
		       module = ?MODULE, function = tell_tuple_3string,
207
		       args = [],
208
		       result = {thattuple, {tuple, [{first, string},
209
						     {second, string},
210
						     {third, string}]}}},
211
     #ejabberd_commands{name = tell_tuple_3atom, tags = [test],
212
		       desc = "Tell a tuple with 3 atoms",
213
		       module = ?MODULE, function = tell_tuple_3atom,
214
		       args = [],
215
		       result = {thattuple, {tuple, [{first, atom},
216
						     {second, atom},
217
						     {third, atom}]}}},
218
     #ejabberd_commands{name = tell_tuple_3list, tags = [test],
219
		       desc = "Tell a tuple with 3 lists",
220
		       module = ?MODULE, function = tell_tuple_3list,
221
		       args = [],
222
		       result = {thattuple, {tuple,
223
					     [{first, {list,
224
						       {thisinteger, integer}}},
225
					      {second, {list,
226
							{thisstring, string}}},
227
					      {third, {list,
228
						       {thisatom, atom}}}]}}},
229
230
     #ejabberd_commands{name = tell_list_3integer, tags = [test],
231
		       desc = "Tell a list with 3 integers",
232
		       module = ?MODULE, function = tell_list_3integer,
233
		       args = [],
234
		       result = {thatlist, {list, {thisinteger, integer}}}},
235
     #ejabberd_commands{name = tell_list_3string, tags = [test],
236
		       desc = "Tell a list with 3 strings",
237
		       module = ?MODULE, function = tell_list_3string,
238
		       args = [],
239
		       result = {thatlist, {list, {thisstring, string}}}},
240
     #ejabberd_commands{name = tell_list_3atom, tags = [test],
241
		       desc = "Tell a list with 3 atoms",
242
		       module = ?MODULE, function = tell_list_3atom,
243
		       args = [],
244
		       result = {thatlist, {list, {thisatom, atom}}}},
245
     #ejabberd_commands{name = tell_list_3tuple, tags = [test],
246
		       desc = "Tell a list with 3 tuples",
247
		       module = ?MODULE, function = tell_list_3tuple,
248
		       args = [],
249
		       result = {thatlist, {list, {thistuple,
250
						   {tuple,
251
						    [{thisinteger, integer},
252
						     {thistring, string},
253
						     {thisatom, atom}]}}}}},
254
255
     #ejabberd_commands{name = this_crashes, tags = [test],
256
		       desc = "This command crashes: test+5",
257
		       module = ?MODULE, function = this_crashes,
258
		       args = [{aninteger, integer}],
259
		       result = {result, integer}},
260
     #ejabberd_commands{name = this_wrong_args, tags = [test],
261
		       desc = "This problematic command defines 2 arguments but function expects 1",
262
		       module = ?MODULE, function = this_wrong_args,
263
		       args = [{a, integer}, {b, integer}],
264
		       result = {result, integer}},
265
     #ejabberd_commands{name = this_wrong_return, tags = [test],
266
		       desc = "This problematic command doesn't give a proper return",
267
		       module = ?MODULE, function = this_wrong_return,
268
		       args = [],
269
		       result = {result, integer}},
270
271
     #ejabberd_commands{name = pow, tags = [test],
272
			desc = "Return the power of base for exponent",
273
			longdesc = "This is an example command. The formula is:\n"
274
			" power = base ^ exponent",
275
			module = ?MODULE, function = pow,
276
			args = [{base, integer}, {exponent, integer}],
277
			result = {power, integer}},
278
279
     #ejabberd_commands{name = seq, tags = [test],
280
		       desc = "Return list of integers between two integers",
281
		       module = ?MODULE, function = seq,
282
		       args = [{from, integer}, {to, integer}],
283
		       result = {sequence, {list, {intermediate, integer}}}},
284
285
     #ejabberd_commands{name = substrs, tags = [test],
286
		       desc = "Return list of substrings of length increasing",
287
		       module = ?MODULE, function = substrs,
288
		       args = [{word, string}],
289
		       result = {substrings, {list, {miniword, string}}}},
290
291
     #ejabberd_commands{name = splitjid, tags = [test],
292
		       desc = "Split JID in parts: user, server, resource",
293
		       module = ?MODULE, function = splitjid,
294
		       args = [{jid, string}],
295
		       result = {jidparts, {tuple, [{user, string},
296
						    {server, string},
297
						    {resource, string}]}}},
298
299
     %% Not supported by ejabberd_ctl because uses 'list' in the arguments
300
     #ejabberd_commands{name = splitjids, tags = [test],
301
		       desc = "Split JIDs in parts: user, server, resource",
302
		       module = ?MODULE, function = splitjids,
303
		       args = [{jids, {list, {jid, string}}}],
304
		       result = {jidsparts,
305
				 {list, {jidparts,
306
					 {tuple, [{user, string},
307
						  {server, string},
308
						  {resource, string}]}}}}}
309
310
    ].
311
312
%%%
313
%%% Take
314
%%%
315
316
take_integer(A) when is_integer(A) -> 0.
317
take_string(A) when is_list(A) -> 0.
318
take_integer_string(A, B)
319
  when is_integer(A) and is_list(B) ->
320
    0.
321
take_tuple_2integer({A, B})
322
  when is_integer(A) and is_integer(B) ->
323
    0.
324
take_tuple_2string({A, B})
325
  when is_list(A) and is_list(B) ->
326
    0.
327
take_list_integer(L)
328
  when is_list(L) ->
329
    true = lists:all(fun(A) -> is_integer(A) end, L),
330
    0.
331
take_list_string(L)
332
  when is_list(L) ->
333
    true = lists:all(fun(A) -> is_list(A) end, L),
334
    0.
335
336
%%%
337
%%% Echo
338
%%%
339
340
echo_integer(A) when is_integer(A) -> A.
341
echo_string(A) when is_list(A) -> A.
342
echo_integer_string(A, B) when is_integer(A) and is_list(B) -> {A, B}.
343
echo_list_integer(L)
344
  when is_list(L) ->
345
    true = lists:all(fun(A) -> is_integer(A) end, L),
346
    L.
347
echo_list_string(L)
348
  when is_list(L) ->
349
    true = lists:all(fun(A) -> is_list(A) end, L),
350
    L.
351
echo_integer_list_string(I, L)
352
  when is_integer(I) and is_list(L) ->
353
    true = lists:all(fun(A) -> is_list(A) end, L),
354
    {I, L}.
355
echo_isatils(I, S, A, {II, L})
356
  when is_integer(I) and is_list(S) and is_atom(A) and is_integer(II) and is_list(L) ->
357
    true = lists:all(fun(SS) -> is_list(SS) end, L),
358
    {I, S, A, {I, L}}.
359
360
361
%%%
362
%%% Tell
363
%%%
364
365
tell_atom(0) -> zero;
366
tell_atom(1) -> one;
367
tell_atom(A) when is_integer(A) -> greater_than_one.
368
369
tell_rescode(0) -> ok;
370
tell_rescode(1) -> true;
371
tell_rescode(2) -> error;
372
tell_rescode(3) -> false;
373
tell_rescode(4) -> whatever.
374
375
tell_restuple(0) -> {ok, "All OK"};
376
tell_restuple(1) -> {true, "Successful result"};
377
tell_restuple(2) -> {error, "This is an error message"}.
378
379
tell_tuple_3integer() -> {123, 456, 789}.
380
tell_tuple_3string() -> {"Tell", "me", "a tuple please"}.
381
tell_tuple_3atom() -> {ok, works, perfectly}.
382
tell_tuple_3list() -> {[1, 23, 456], ["Tell", "me"], [all, is, ok]}.
383
384
tell_list_3integer() -> [123, 456, 789].
385
tell_list_3string() -> ["Tell", "me", "a tuple please"].
386
tell_list_3atom() -> [ok, works, perfectly].
387
tell_list_3tuple() ->
388
    [{123, "abcdefghijkl", first},
389
     {593, "this string", morning},
390
     {999, "Sleeping dog", not_seen}].
391
392
393
%%%
394
%%% Realistic
395
%%%
396
397
%% This function will crash for sure
398
this_crashes(Integer) ->
399
    test + Integer.
400
401
this_wrong_args(Integer) ->
402
    Integer + 1.
403
404
this_wrong_return() ->
405
    "this is a string".
406
407
pow(Base, Exponent) ->
408
    PowFloat = math:pow(Base, Exponent),
409
    round(PowFloat).
410
411
seq(From, To) ->
412
    lists:seq(From, To).
413
414
%% For "stick" returns: s st sti stic stick
415
substrs(Word) ->
416
    Lengths = lists:seq(1, string:len(Word)),
417
    [string:substr(Word, 1, Length) || Length <- Lengths].
418
419
splitjid(String) ->
420
    JID = jlib:string_to_jid(String),
421
    {JID#jid.user,
422
     JID#jid.server,
423
     JID#jid.resource}.
424
425
splitjids(Strings) ->
426
    [splitjid(String) || String <- Strings].
427

Return to bug 409425