1: %% @doc This suite tests both old ejabberd_commands module, which is slowly getting deprecated,
    2: %% and the new mongoose_commands implementation.
    3: -module(commands_SUITE).
    4: -compile([export_all, nowarn_export_all]).
    5: 
    6: -include_lib("exml/include/exml.hrl").
    7: -include_lib("eunit/include/eunit.hrl").
    8: -include("ejabberd_commands.hrl").
    9: -include("jlib.hrl").
   10: 
   11: -define(PRT(X, Y), ct:pal("~p: ~p", [X, Y])).
   12: 
   13: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   14: %%%% suite configuration
   15: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   16: 
   17: all() ->
   18:     [
   19:      {group, old_commands},
   20:      {group, new_commands}
   21:     ].
   22: 
   23: groups() ->
   24:     [
   25:      {old_commands, [sequence],
   26:       [old_list,
   27:        old_exec,
   28:        old_access_ctl
   29:       ]
   30:      },
   31:      {new_commands, [sequence],
   32:       [new_type_checker,
   33:        new_reg_unreg,
   34:        new_failedreg,
   35:        new_list,
   36:        new_execute,
   37:        different_types,
   38:        errors_are_readable
   39:       ]
   40:      }
   41:     ].
   42: 
   43: init_per_suite(C) ->
   44:     application:ensure_all_started(jid),
   45:     ok = mnesia:start(),
   46:     C.
   47: 
   48: end_per_suite(_) ->
   49:     mnesia:stop(),
   50:     mnesia:delete_schema([node()]),
   51:     ok.
   52: 
   53: init_per_group(old_commands, C) ->
   54:     Pid = spawn(fun ec_holder/0),
   55:     [{helper_proc, Pid} | C];
   56: init_per_group(new_commands, C) ->
   57:     Pid = spawn(fun mc_holder/0),
   58:     [{helper_proc, Pid} | C].
   59: 
   60: end_per_group(old_commands, C) ->
   61:     ejabberd_commands:unregister_commands(commands_old()),
   62:     stop_helper_proc(C),
   63:     C;
   64: end_per_group(new_commands, C) ->
   65:     mongoose_commands:unregister(commands_new()),
   66:     stop_helper_proc(C),
   67:     C.
   68: 
   69: stop_helper_proc(C) ->
   70:     Pid = proplists:get_value(helper_proc, C),
   71:     Pid ! stop.
   72: 
   73: init_per_testcase(_, C) ->
   74:     [mongoose_config:set_opt(Key, Value) || {Key, Value} <- opts()],
   75:     meck:new(ejabberd_auth_dummy, [non_strict]),
   76:     meck:expect(ejabberd_auth_dummy, get_password_s, fun(_, _) -> <<"">> end),
   77:     meck:new(mongoose_domain_api),
   78:     meck:expect(mongoose_domain_api, get_domain_host_type, fun(H) -> {ok, H} end),
   79:     C.
   80: 
   81: end_per_testcase(_, _C) ->
   82:     [mongoose_config:unset_opt(Key) || {Key, _Value} <- opts()],
   83:     meck:unload().
   84: 
   85: opts() ->
   86:     [{{auth, <<"localhost">>}, #{methods => [dummy]}},
   87:      {{access, experts_only, <<"localhost">>}, [{allow, coder}, {allow, manager}, {deny, all}]},
   88:      {{acl, coder, <<"localhost">>}, [{user, <<"zenek">>}]}].
   89: 
   90: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   91: %%%% test methods
   92: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   93: 
   94: 
   95: old_list(_C) ->
   96:     %% list
   97:     Rlist = ejabberd_commands:list_commands(),
   98:     {command_one, _, "do nothing and return"} = proplists:lookup(command_one, Rlist),
   99:     %% get definition
  100:     Rget = ejabberd_commands:get_command_definition(command_one),
  101:     % we should get back exactly the definition we provided
  102:     [Cone | _] = commands_old(),
  103:     Cone = Rget,
  104:     %% get interface
  105:     {Argspec, Retspec} = ejabberd_commands:get_command_format(command_one),
  106:     [{msg, binary}] = Argspec,
  107:     {res, restuple} = Retspec,
  108:     %% list by tags
  109:     Tagcomm = ejabberd_commands:get_tags_commands(),
  110:     ?assertEqual(length(proplists:get_value("one", Tagcomm)), 1),
  111:     ?assertEqual(length(proplists:get_value("two", Tagcomm)), 2),
  112:     ?assertEqual(length(proplists:get_value("three", Tagcomm)), 1),
  113:     ok.
  114: 
  115: old_exec(_C) ->
  116:     %% execute
  117:     <<"bzzzz">> = ejabberd_commands:execute_command(command_one, [<<"bzzzz">>]),
  118:     Res = ejabberd_commands:execute_command(command_one, [123]),
  119:     ?PRT("invalid type ignored", Res), %% there is no arg type check
  120:     Res2 = ejabberd_commands:execute_command(command_two, [123]),
  121:     ?PRT("invalid return type ignored", Res2), %% nor return
  122:     %% execute unknown command
  123:     {error, command_unknown} = ejabberd_commands:execute_command(command_seven, [123]),
  124:     ok.
  125: 
  126: old_access_ctl(_C) ->
  127:     %% with no auth method it is all fine
  128:     checkauth(true, [], noauth),
  129:     %% noauth fails if first item is not 'all' (users)
  130:     checkauth(account_unprivileged, [{none, none, []}], noauth),
  131:     %% if here we allow all commands to noauth
  132:     checkauth(true, [{all, all, []}], noauth),
  133:     %% and here only command_one
  134:     checkauth(true, [{all, [command_one], []}], noauth),
  135:     %% so this'd fail
  136:     checkauth(account_unprivileged, [{all, [command_two], []}], noauth),
  137:     % now we provide a role name, this requires a user and triggers password and acl check
  138:     % this fails because password is bad
  139:     checkauth(invalid_account_data, [{some_acl_role, [command_one], []}], {<<"zenek">>, <<"localhost">>, <<"bbb">>}),
  140:     % this, because of acl
  141:     checkauth(account_unprivileged, [{some_acl_role, [command_one], []}], {<<"zenek">>, <<"localhost">>, <<"">>}),
  142:     % and this should work, because we define command_one as available to experts only, while acls in config
  143:     % (see ggo/1) state that experts-only funcs are available to coders and managers, and zenek is a coder, gah.
  144:     checkauth(true, [{experts_only, [command_one], []}], {<<"zenek">>, <<"localhost">>, <<"">>}),
  145:     ok.
  146: 
  147: 
  148: new_type_checker(_C) ->
  149:     true = t_check_type({msg, binary}, <<"zzz">>),
  150:     true = t_check_type({msg, integer}, 127),
  151:     {false, _} = t_check_type({{a, binary}, {b, integer}}, 127),
  152:     true = t_check_type({{a, binary}, {b, integer}}, {<<"z">>, 127}),
  153:     true = t_check_type({ok, {msg, integer}}, {ok, 127}),
  154:     true = t_check_type({ok, {msg, integer}, {val, binary}}, {ok, 127, <<"z">>}),
  155:     {false, _} = t_check_type({k, {msg, integer}, {val, binary}}, {ok, 127, <<"z">>}),
  156:     {false, _} = t_check_type({ok, {msg, integer}, {val, binary}}, {ok, 127, "z"}),
  157:     {false, _} = t_check_type({ok, {msg, integer}, {val, binary}}, {ok, 127, <<"z">>, 333}),
  158:     true = t_check_type([integer], []),
  159:     true = t_check_type([integer], [1, 2, 3]),
  160:     {false, _} = t_check_type([integer], [1, <<"z">>, 3]),
  161:     true = t_check_type([], [1, 2, 3]),
  162:     true = t_check_type([], []),
  163:     true = t_check_type({msg, boolean}, true),
  164:     true = t_check_type({msg, boolean}, false),
  165:     {false, _} = t_check_type({msg, boolean}, <<"true">>),
  166:     ok.
  167: 
  168: t_check_type(Spec, Value) ->
  169:     R = try mongoose_commands:check_type(argument, Spec, Value) of
  170:             true -> true
  171:         catch
  172:             E ->
  173:                 {false, E}
  174:         end,
  175:     R.
  176: 
  177: new_reg_unreg(_C) ->
  178:     L1 = length(commands_new()),
  179:     L2 = L1 + length(commands_new_temp()),
  180:     ?assertEqual(length(mongoose_commands:list(admin)), L1),
  181:     mongoose_commands:register(commands_new_temp()),
  182:     ?assertEqual(length(mongoose_commands:list(admin)), L2),
  183:     mongoose_commands:unregister(commands_new_temp()),
  184:     ?assertEqual(length(mongoose_commands:list(admin)), L1),
  185:     ok.
  186: 
  187: failedreg([]) -> ok;
  188: failedreg([Cmd|Tail]) ->
  189:     ?assertThrow({invalid_command_definition, _}, mongoose_commands:register([Cmd])),
  190:     failedreg(Tail).
  191: 
  192: new_failedreg(_C) ->
  193:     failedreg(commands_new_lame()).
  194: 
  195: 
  196: new_list(_C) ->
  197:     %% for admin
  198:     Rlist = mongoose_commands:list(admin),
  199:     [Cmd] = [C || C <- Rlist, mongoose_commands:name(C) == command_one],
  200:     command_one = mongoose_commands:name(Cmd),
  201:     <<"do nothing and return">> = mongoose_commands:desc(Cmd),
  202:     %% list by category
  203:     [_] = mongoose_commands:list(admin, <<"user">>),
  204:     [] = mongoose_commands:list(admin, <<"nocategory">>),
  205:     %% list by category and action
  206:     [_] = mongoose_commands:list(admin, <<"user">>, read),
  207:     [] = mongoose_commands:list(admin, <<"user">>, update),
  208:     %% get definition
  209:     Rget = mongoose_commands:get_command(admin, command_one),
  210:     command_one = mongoose_commands:name(Rget),
  211:     read = mongoose_commands:action(Rget),
  212:     [] = mongoose_commands:identifiers(Rget),
  213:     {error, denied, _} = mongoose_commands:get_command(ujid(), command_one),
  214:     %% list for a user
  215:     Ulist = mongoose_commands:list(ujid()),
  216:     [UCmd] = [UC || UC <- Ulist, mongoose_commands:name(UC) == command_foruser],
  217: 
  218:     command_foruser = mongoose_commands:name(UCmd),
  219:     URget = mongoose_commands:get_command(ujid(), command_foruser),
  220:     command_foruser = mongoose_commands:name(URget),
  221:     ok.
  222: 
  223: 
  224: new_execute(_C) ->
  225:     {ok, <<"bzzzz">>} = mongoose_commands:execute(admin, command_one, [<<"bzzzz">>]),
  226:     Cmd = mongoose_commands:get_command(admin, command_one),
  227:     {ok, <<"bzzzz">>} = mongoose_commands:execute(admin, Cmd, [<<"bzzzz">>]),
  228:     %% call with a map
  229:     {ok, <<"bzzzz">>} = mongoose_commands:execute(admin, command_one, #{msg => <<"bzzzz">>}),
  230:     %% command which returns just ok
  231:     ok = mongoose_commands:execute(admin, command_noreturn, [<<"bzzzz">>]),
  232:     %% this user has no permissions
  233:     {error, denied, _} = mongoose_commands:execute(ujid(), command_one, [<<"bzzzz">>]),
  234:     %% command is not registered
  235:     {error, not_implemented, _} = mongoose_commands:execute(admin, command_seven, [<<"bzzzz">>]),
  236:     %% invalid arguments
  237:     {error, type_error, _} = mongoose_commands:execute(admin, command_one, [123]),
  238:     {error, type_error, _} = mongoose_commands:execute(admin, command_one, []),
  239:     {error, type_error, _} = mongoose_commands:execute(admin, command_one, #{}),
  240:     {error, type_error, _} = mongoose_commands:execute(admin, command_one, #{msg => 123}),
  241:     {error, type_error, _} = mongoose_commands:execute(admin, command_one, #{notthis => <<"bzzzz">>}),
  242:     {error, type_error, _} = mongoose_commands:execute(admin, command_one, #{msg => <<"bzzzz">>, redundant => 123}),
  243:     %% backend func throws exception
  244:     {error, internal, _} = mongoose_commands:execute(admin, command_one, [<<"throw">>]),
  245:     %% backend func returns error
  246:     {error, internal, <<"byleco">>} = mongoose_commands:execute(admin, command_one, [<<"error">>]),
  247:     % user executes his command
  248:     {ok, <<"bzzzz">>} = mongoose_commands:execute(ujid(), command_foruser, #{msg => <<"bzzzz">>}),
  249:     % a caller arg
  250:     % called by admin
  251:     {ok, <<"admin@localhost/zbzzzz">>} = mongoose_commands:execute(admin,
  252:                                                                    command_withcaller,
  253:                                                                    #{caller => <<"admin@localhost/z">>,
  254:                                                                      msg => <<"bzzzz">>}),
  255:     % called by user
  256:     {ok, <<"zenek@localhost/zbzzzz">>} = mongoose_commands:execute(<<"zenek@localhost">>,
  257:                                                                    command_withcaller,
  258:                                                                    #{caller => <<"zenek@localhost/z">>,
  259:                                                                      msg => <<"bzzzz">>}),
  260:     % call by user but jids do not match
  261:     {error, denied, _} = mongoose_commands:execute(<<"wacek@localhost">>,
  262:                                                    command_withcaller,
  263:                                                    #{caller => <<"zenek@localhost/z">>,
  264:                                                      msg => <<"bzzzz">>}),
  265:     {ok, 30} = mongoose_commands:execute(admin, command_withoptargs, #{msg => <<"a">>}),
  266:     {ok, 18} = mongoose_commands:execute(admin, command_withoptargs, #{msg => <<"a">>, value => 6}),
  267:     ok.
  268: 
  269: different_types(_C) ->
  270:     mongoose_commands:register(commands_new_temp2()),
  271:     {ok, <<"response1">>} = mongoose_commands:execute(admin, command_two, [10, 15]),
  272:     {ok, <<"response2">>} = mongoose_commands:execute(admin, command_three, [10, <<"binary">>]),
  273:     mongoose_commands:unregister(commands_new_temp2()),
  274:     ok.
  275: 
  276: errors_are_readable(_C) ->
  277:     {error, internal, TextBin} = mongoose_commands:execute(admin, make_error, [<<"oops">>]),
  278:     Map = parse_binary_term(TextBin),
  279:     [<<"oops">>] = maps:get(args, Map),
  280:     admin = maps:get(caller, Map),
  281:     error = maps:get(class, Map),
  282:     make_error = maps:get(command_name, Map),
  283:     <<"oops">> = maps:get(reason, Map),
  284:     [_|_] = maps:get(stacktrace, Map),
  285:     command_failed = maps:get(what, Map),
  286:     ok.
  287: 
  288: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  289: %%%% definitions
  290: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  291: 
  292: commands_new() ->
  293:     [
  294:         [
  295:             {name, command_one},
  296:             {category, <<"user">>},
  297:             {desc, <<"do nothing and return">>},
  298:             {module, ?MODULE},
  299:             {function, cmd_one},
  300:             {action, read},
  301:             {args, [{msg, binary}]},
  302:             {result, {msg, binary}}
  303:         ],
  304:         [
  305:             {name, command_noreturn},
  306:             {category, <<"message">>},
  307:             {desc, <<"do nothing and return nothing">>},
  308:             {module, ?MODULE},
  309:             {function, cmd_one},
  310:             {action, create},
  311:             {args, [{msg, binary}]},
  312:             {result, ok}
  313:         ],
  314:         [
  315:             {name, command_foruser},
  316:             {category, <<"another">>},
  317:             {desc, <<"this is available for a user">>},
  318:             {module, ?MODULE},
  319:             {function, cmd_one},
  320:             {action, read},
  321:             {security_policy, [user]},
  322:             {args, [{msg, binary}]},
  323:             {result, {msg, binary}}
  324:         ],
  325:         [
  326:             {name, command_withoptargs},
  327:             {category, <<"yetanother">>},
  328:             {desc, <<"this is available for a user">>},
  329:             {module, ?MODULE},
  330:             {function, cmd_one_withvalue},
  331:             {action, read},
  332:             {security_policy, [user]},
  333:             {args, [{msg, binary}]},
  334:             {optargs, [{value, integer, 10}]},
  335:             {result, {nvalue, integer}}
  336:         ],
  337:         [
  338:             {name, command_withcaller},
  339:             {category, <<"another">>},
  340:             {desc, <<"this has a 'caller' argument, returns caller ++ msg">>},
  341:             {module, ?MODULE},
  342:             {function, cmd_concat},
  343:             {action, create},
  344:             {security_policy, [user]},
  345:             {args, [{caller, binary}, {msg, binary}]},
  346:             {result, {msg, binary}}
  347:         ],
  348:         [
  349:             {name, make_error},
  350:             {category, <<"testing">>},
  351:             {desc, <<"Just to test an error">>},
  352:             {module, erlang},
  353:             {function, error},
  354:             {action, read},
  355:             {args, [{error, binary}]},
  356:             {result, []}
  357:         ]
  358:     ].
  359: 
  360: commands_new_temp() ->
  361:     %% this is to check registering/unregistering commands
  362:     [
  363:         [
  364:             {name, command_temp},
  365:             {category, <<"user">>},
  366:             {desc, <<"do nothing and return">>},
  367:             {module, ?MODULE},
  368:             {function, cmd_one},
  369:             {action, create}, % different action
  370:             {args, [{msg, binary}]},
  371:             {result, {msg, binary}}
  372:         ],
  373:         [
  374:             {name, command_one_arity},
  375:             {category, <<"user">>},
  376:             {desc, <<"do nothing and return">>},
  377:             {module, ?MODULE},
  378:             {function, cmd_one},
  379:             {action, read},
  380:             {args, [{msg, binary}, {whatever, integer}]}, % different arity
  381:             {result, {msg, binary}}
  382:         ],
  383:         [
  384:             {name, command_one_two},
  385:             {category, <<"user">>},
  386:             {subcategory, <<"rosters">>}, % has subcategory
  387:             {desc, <<"do nothing and return">>},
  388:             {module, ?MODULE},
  389:             {function, cmd_one},
  390:             {action, read},
  391:             {args, [{msg, binary}]},
  392:             {result, {msg, binary}}
  393:         ],
  394:         [
  395:             {name, command_temp2},
  396:             {category, <<"user">>},
  397:             {desc, <<"this one specifies identifiers">>},
  398:             {module, ?MODULE},
  399:             {function, cmd_one},
  400:             {action, update},
  401:             {identifiers, [ident]},
  402:             {args, [{ident, integer}, {msg, binary}]},
  403:             {result, {msg, binary}}
  404:         ]
  405:     ].
  406: 
  407: commands_new_temp2() ->
  408:     %% This is for extra test with different arg types
  409:     [
  410:         [
  411:             {name, command_two},
  412:             {category, <<"animals">>},
  413:             {desc, <<"some">>},
  414:             {module, ?MODULE},
  415:             {function, the_same_types},
  416:             {action, read},
  417:             {args, [{one, integer}, {two, integer}]},
  418:             {result, {msg, binary}}
  419:     ],
  420:         [
  421:             {name, command_three},
  422:             {category, <<"music">>},
  423:             {desc, <<"two args, different types">>},
  424:             {module, ?MODULE},
  425:             {function, different_types},
  426:             {action, read},
  427:             {args, [{one, integer}, {two, binary}]},
  428:             {result, {msg, binary}}
  429:         ]
  430:     ].
  431: 
  432: commands_new_lame() ->
  433:     [
  434:         [
  435:             {name, command_one} % missing values
  436:         ],
  437:         [
  438:             {name, command_one},
  439:             {category, []} %% should be binary
  440:         ],
  441:         [
  442:             {name, command_one},
  443:             {category, <<"user">>},
  444:             {desc, <<"do nothing and return">>},
  445:             {module, ?MODULE},
  446:             {function, cmd_one},
  447:             {action, andnowforsomethingcompletelydifferent} %% not one of allowed values
  448:         ],
  449:         [
  450:             {name, command_one},
  451:             {category, <<"user">>},
  452:             {desc, <<"do nothing and return">>},
  453:             {module, ?MODULE},
  454:             {function, cmd_one},
  455:             {action, delete},
  456:             {args, [{msg, binary}, integer]}, %% args have to be a flat list of named arguments
  457:             {result, {msg, binary}}
  458:         ],
  459: %%        We do not crash if command is already registered because some modules are loaded more then once
  460: %%        [
  461: %%            {name, command_one}, %% everything is fine, but it is already registered
  462: %%            {category, another},
  463: %%            {desc, "do nothing and return"},
  464: %%            {module, ?MODULE},
  465: %%            {function, cmd_one},
  466: %%            {action, read},
  467: %%            {args, [{msg, binary}]},
  468: %%            {result, {msg, binary}}
  469: %%        ],
  470:         [
  471:             {name, command_one},
  472:             {category, <<"another">>},
  473:             {desc, <<"do nothing and return">>},
  474:             {module, ?MODULE},
  475:             {function, cmd_one},
  476:             {action, update}, %% an 'update' command has to specify identifiers
  477:             {args, [{msg, binary}]},
  478:             {result, {msg, binary}}
  479:         ],
  480:         [
  481:             {name, command_one},
  482:             {category, <<"another">>},
  483:             {desc, <<"do nothing and return">>},
  484:             {module, ?MODULE},
  485:             {function, cmd_one},
  486:             {action, update},
  487:             {identifiers, [1]}, %% ...and they must be atoms...
  488:             {args, [{msg, binary}]},
  489:             {result, {msg, binary}}
  490:         ],
  491:         [
  492:             {name, command_one},
  493:             {category, <<"another">>},
  494:             {desc, <<"do nothing and return">>},
  495:             {module, ?MODULE},
  496:             {function, cmd_one},
  497:             {action, update},
  498:             {identifiers, [ident]}, %% ...which are present in args
  499:             {args, [{msg, binary}]},
  500:             {result, {msg, binary}}
  501:         ],
  502:         [
  503:             {name, command_seven}, %% name is different...
  504:             {category, <<"user">>},
  505:             {desc, <<"do nothing and return">>},
  506:             {module, ?MODULE},
  507:             {function, cmd_one},
  508:             {action, read}, %% ...but another command with the same category and action and arity is already registered
  509:             {args, [{msg, binary}]},
  510:             {result, {msg, binary}}
  511:         ],
  512:         [
  513:             {name, command_seven},
  514:             {category, <<"user">>},
  515:             {desc, <<"do nothing and return">>},
  516:             {module, ?MODULE},
  517:             {function, cmd_one},
  518:             {action, delete},
  519:             {security_policy, [wrong]}, % invalid security definition
  520:             {args, [{msg, binary}]},
  521:             {result, {msg, binary}}
  522: %%        ],
  523: %%        [
  524: %%            {name, command_seven},
  525: %%            {category, user},
  526: %%            {desc, "do nothing and return"},
  527: %%            {module, ?MODULE},
  528: %%            {function, cmd_one},
  529: %%            {action, delete},
  530: %%            {security_policy, []}, % invalid security definition
  531: %%            {args, [{msg, binary}]},
  532: %%            {result, {msg, binary}}
  533:         ]
  534:     ].
  535: 
  536: commands_old() ->
  537:     [
  538:         #ejabberd_commands{name = command_one, tags = [one],
  539:                            desc = "do nothing and return",
  540:                            module = ?MODULE, function = cmd_one,
  541:                            args = [{msg, binary}],
  542:                            result = {res, restuple}},
  543:         #ejabberd_commands{name = command_two, tags = [two],
  544:                            desc = "this returns wrong type",
  545:                            module = ?MODULE, function = cmd_two,
  546:                            args = [{msg, binary}],
  547:                            result = {res, restuple}},
  548:         #ejabberd_commands{name = command_three, tags = [two, three],
  549:                            desc = "do nothing and return",
  550:                            module = ?MODULE, function = cmd_three,
  551:                            args = [{msg, binary}],
  552:                            result = {res, restuple}}
  553:     ].
  554: 
  555: cmd_one(<<"throw">>) ->
  556:     C = 12,
  557:     <<"A", C/binary>>;
  558: cmd_one(<<"error">>) ->
  559:     {error, internal, <<"byleco">>};
  560: cmd_one(M) ->
  561:     M.
  562: 
  563: cmd_one_withvalue(_Msg, Value) ->
  564:     Value * 3.
  565: 
  566: cmd_two(M) ->
  567:     M.
  568: 
  569: the_same_types(10, 15) ->
  570:     <<"response1">>;
  571: the_same_types(_, _) ->
  572:     <<"wrong response">>.
  573: 
  574: different_types(10, <<"binary">>) ->
  575:     <<"response2">>;
  576: different_types(_, _) ->
  577:     <<"wrong content">>.
  578: 
  579: cmd_concat(A, B) ->
  580:     <<A/binary, B/binary>>.
  581: 
  582: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  583: %%%% utilities
  584: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  585: 
  586: %% this is a bit stupid, but we need a process which would hold ets table
  587: ec_holder() ->
  588:     ejabberd_commands:init(),
  589:     ejabberd_commands:register_commands(commands_old()),
  590:     receive
  591:         _ -> ok
  592:     end.
  593: 
  594: mc_holder() ->
  595:     % we have to do it here to avoid race condition and random failures
  596:     {ok, Pid} = gen_hook:start_link(),
  597:     mongoose_commands:init(),
  598:     mongoose_commands:register(commands_new()),
  599:     receive
  600:         _ -> ok
  601:     end,
  602:     erlang:exit(Pid, kill).
  603: 
  604: checkauth(true, AccessCommands, Auth) ->
  605:     B = <<"bzzzz">>,
  606:     B = ejabberd_commands:execute_command(AccessCommands, Auth, command_one, [B]);
  607: checkauth(ErrMess, AccessCommands, Auth) ->
  608:     B = <<"bzzzz">>,
  609:     {error, ErrMess} = ejabberd_commands:execute_command(AccessCommands, Auth, command_one, [B]).
  610: 
  611: ujid() ->
  612:     <<"zenek@localhost/k">>.
  613: %%    #jid{user = <<"zenek">>, server = <<"localhost">>, resource = "k",
  614: %%         luser = <<"zenek">>, lserver = <<"localhost">>, lresource = "k"}.
  615: 
  616: parse_binary_term(TextBin) ->
  617:     {ok, Tokens, _} = erl_scan:string(binary_to_list(TextBin) ++ "."),
  618:     {ok, Abstract} = erl_parse:parse_exprs(Tokens),
  619:     {value, Value, _} = erl_eval:exprs(Abstract, erl_eval:new_bindings()),
  620:     Value.