-record(feature_register, {}).
-type feature_register() :: #feature_register{}.
+-record(adhoc_note, {type = info :: 'error' | 'info' | 'warn',
+ data = <<>> :: binary()}).
+-type adhoc_note() :: #adhoc_note{}.
+
-record(address, {type :: 'bcc' | 'cc' | 'noreply' | 'ofrom' | 'replyroom' | 'replyto' | 'to',
jid :: any(),
desc :: binary(),
sid :: binary()}).
-type bytestreams() :: #bytestreams{}.
+-record(adhoc_actions, {execute :: 'complete' | 'next' | 'prev',
+ prev = false :: boolean(),
+ next = false :: boolean(),
+ complete = false :: boolean()}).
+-type adhoc_actions() :: #adhoc_actions{}.
+
-record(vcard_org, {name :: binary(),
units = [] :: [binary()]}).
-type vcard_org() :: #vcard_org{}.
fields = [] :: [#xdata_field{}]}).
-type xdata() :: #xdata{}.
+-record(adhoc_command, {node :: binary(),
+ action = execute :: 'cancel' | 'complete' | 'execute' | 'next' | 'prev',
+ sid :: binary(),
+ status :: 'canceled' | 'completed' | 'executing',
+ lang :: binary(),
+ actions :: #adhoc_actions{},
+ notes = [] :: [#adhoc_note{}],
+ xdata :: #xdata{}}).
+-type adhoc_command() :: #adhoc_command{}.
+
-record(search, {instructions :: binary(),
first :: binary(),
last :: binary(),
pubsub_options() |
compress() |
bytestreams() |
+ adhoc_actions() |
muc_history() |
identity() |
feature_csi() |
pubsub() |
muc_owner() |
muc_actor() |
- carbons_private() |
- mix_leave() |
- muc_subscribe() |
+ adhoc_note() |
rosterver_feature() |
muc_invite() |
vcard_xupdate() |
bookmark_conference() |
offline() |
time() |
- muc_unique() |
- sasl_response() |
- pubsub_subscribe() |
- presence() |
- message() |
sm_enable() |
starttls_failure() |
sasl_challenge() |
- gone() |
x_conference() |
private() |
compress_failure() |
sasl_failure() |
bookmark_storage() |
vcard_name() |
- sm_resume() |
- carbons_enable() |
- expire() |
- muc_unsubscribe() |
- pubsub_unsubscribe() |
muc_decline() |
- chatstate() |
sasl_auth() |
p1_push() |
legacy_auth() |
rsm_first() |
stat() |
xdata_field() |
+ adhoc_command() |
sm_failed() |
ping() |
- disco_item() |
privacy_item() |
+ disco_item() |
caps() |
muc() |
stream_features() |
error() |
stream_error() |
muc_user() |
- vcard_adr().
+ vcard_adr() |
+ carbons_private() |
+ mix_leave() |
+ muc_subscribe() |
+ muc_unique() |
+ sasl_response() |
+ pubsub_subscribe() |
+ presence() |
+ message() |
+ gone() |
+ sm_resume() |
+ carbons_enable() |
+ expire() |
+ muc_unsubscribe() |
+ pubsub_unsubscribe() |
+ chatstate().
-behaviour(gen_mod).
--export([start/2, stop/1, process_local_iq/3,
- process_sm_iq/3, get_local_commands/5,
+-export([start/2, stop/1, process_local_iq/1,
+ process_sm_iq/1, get_local_commands/5,
get_local_identity/5, get_local_features/5,
get_sm_commands/5, get_sm_identity/5, get_sm_features/5,
ping_item/4, ping_command/4, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
-
--include("jlib.hrl").
-
--include("adhoc.hrl").
+-include("xmpp.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
%-------------------------------------------------------------------------
get_local_commands(Acc, _From,
- #jid{server = Server, lserver = LServer} = _To, <<"">>,
+ #jid{server = Server, lserver = LServer} = _To, undefined,
Lang) ->
Display = gen_mod:get_module_opt(LServer, ?MODULE,
report_commands_node,
{result, I} -> I;
_ -> []
end,
- Nodes = [#xmlel{name = <<"item">>,
- attrs =
- [{<<"jid">>, Server}, {<<"node">>, ?NS_COMMANDS},
- {<<"name">>,
- translate:translate(Lang, <<"Commands">>)}],
- children = []}],
+ Nodes = [#disco_item{jid = jid:make(Server),
+ node = ?NS_COMMANDS,
+ name = translate:translate(Lang, <<"Commands">>)}],
{result, Items ++ Nodes}
end;
get_local_commands(_Acc, From,
%-------------------------------------------------------------------------
get_sm_commands(Acc, _From,
- #jid{lserver = LServer} = To, <<"">>, Lang) ->
+ #jid{lserver = LServer} = To, undefined, Lang) ->
Display = gen_mod:get_module_opt(LServer, ?MODULE,
report_commands_node,
fun(B) when is_boolean(B) -> B end,
{result, I} -> I;
_ -> []
end,
- Nodes = [#xmlel{name = <<"item">>,
- attrs =
- [{<<"jid">>, jid:to_string(To)},
- {<<"node">>, ?NS_COMMANDS},
- {<<"name">>,
- translate:translate(Lang, <<"Commands">>)}],
- children = []}],
+ Nodes = [#disco_item{jid = To,
+ node = ?NS_COMMANDS,
+ name = translate:translate(Lang, <<"Commands">>)}],
{result, Items ++ Nodes}
end;
get_sm_commands(_Acc, From,
%% On disco info request to the ad-hoc node, return automation/command-list.
get_local_identity(Acc, _From, _To, ?NS_COMMANDS,
Lang) ->
- [#xmlel{name = <<"identity">>,
- attrs =
- [{<<"category">>, <<"automation">>},
- {<<"type">>, <<"command-list">>},
- {<<"name">>,
- translate:translate(Lang, <<"Commands">>)}],
- children = []}
+ [#identity{category = <<"automation">>,
+ type = <<"command-list">>,
+ name = translate:translate(Lang, <<"Commands">>)}
| Acc];
get_local_identity(Acc, _From, _To, <<"ping">>, Lang) ->
- [#xmlel{name = <<"identity">>,
- attrs =
- [{<<"category">>, <<"automation">>},
- {<<"type">>, <<"command-node">>},
- {<<"name">>, translate:translate(Lang, <<"Ping">>)}],
- children = []}
+ [#identity{category = <<"automation">>,
+ type = <<"command-node">>,
+ name = translate:translate(Lang, <<"Ping">>)}
| Acc];
get_local_identity(Acc, _From, _To, _Node, _Lang) ->
Acc.
%% On disco info request to the ad-hoc node, return automation/command-list.
get_sm_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) ->
- [#xmlel{name = <<"identity">>,
- attrs =
- [{<<"category">>, <<"automation">>},
- {<<"type">>, <<"command-list">>},
- {<<"name">>,
- translate:translate(Lang, <<"Commands">>)}],
- children = []}
+ [#identity{category = <<"automation">>,
+ type = <<"command-list">>,
+ name = translate:translate(Lang, <<"Commands">>)}
| Acc];
get_sm_identity(Acc, _From, _To, _Node, _Lang) -> Acc.
%-------------------------------------------------------------------------
-process_local_iq(From, To, IQ) ->
- process_adhoc_request(From, To, IQ,
- adhoc_local_commands).
+process_local_iq(IQ) ->
+ process_adhoc_request(IQ, adhoc_local_commands).
-process_sm_iq(From, To, IQ) ->
- process_adhoc_request(From, To, IQ, adhoc_sm_commands).
+process_sm_iq(IQ) ->
+ process_adhoc_request(IQ, adhoc_sm_commands).
-process_adhoc_request(From, To,
- #iq{sub_el = SubEl, lang = Lang} = IQ, Hook) ->
- ?DEBUG("About to parse ~p...", [IQ]),
- case adhoc:parse_request(IQ) of
- {error, Error} ->
- IQ#iq{type = error, sub_el = [SubEl, Error]};
- #adhoc_request{} = AdhocRequest ->
- Host = To#jid.lserver,
- case ejabberd_hooks:run_fold(Hook, Host, empty,
- [From, To, AdhocRequest])
- of
- ignore -> ignore;
- empty ->
- Txt = <<"No hook has processed this command">>,
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)]};
- {error, Error} ->
- IQ#iq{type = error, sub_el = [SubEl, Error]};
- Command -> IQ#iq{type = result, sub_el = [Command]}
- end
- end.
+process_adhoc_request(#iq{from = From, to = To,
+ type = set, lang = Lang,
+ sub_els = [#adhoc_command{} = SubEl]} = IQ, Hook) ->
+ Host = To#jid.lserver,
+ case ejabberd_hooks:run_fold(Hook, Host, empty, [From, To, SubEl]) of
+ ignore ->
+ ignore;
+ empty ->
+ Txt = <<"No hook has processed this command">>,
+ xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
+ {error, Error} ->
+ xmpp:make_error(IQ, Error);
+ Command ->
+ xmpp:make_iq_result(IQ, Command)
+ end;
+process_adhoc_request(#iq{} = IQ, _Hooks) ->
+ xmpp:make_error(IQ, xmpp:err_bad_request()).
ping_item(Acc, _From, #jid{server = Server} = _To,
Lang) ->
{result, I} -> I;
_ -> []
end,
- Nodes = [#xmlel{name = <<"item">>,
- attrs =
- [{<<"jid">>, Server}, {<<"node">>, <<"ping">>},
- {<<"name">>, translate:translate(Lang, <<"Ping">>)}],
- children = []}],
+ Nodes = [#disco_item{jid = jid:make(Server),
+ node = <<"ping">>,
+ name = translate:translate(Lang, <<"Ping">>)}],
{result, Items ++ Nodes}.
ping_command(_Acc, _From, _To,
- #adhoc_request{lang = Lang, node = <<"ping">>,
- sessionid = _Sessionid, action = Action} =
- Request) ->
- if Action == <<"">>; Action == <<"execute">> ->
- adhoc:produce_response(Request,
- #adhoc_response{status = completed,
- notes =
- [{<<"info">>,
- translate:translate(Lang,
- <<"Pong">>)}]});
+ #adhoc_command{lang = Lang, node = <<"ping">>,
+ action = Action} = Request) ->
+ if Action == execute ->
+ xmpp_util:make_adhoc_response(
+ Request,
+ #adhoc_command{
+ status = completed,
+ notes = [#adhoc_note{
+ type = info,
+ data = translate:translate(Lang, <<"Pong">>)}]});
true ->
Txt = <<"Incorrect value of 'action' attribute">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)}
+ {error, xmpp:err_bad_request(Txt, Lang)}
end;
ping_command(Acc, _From, _To, _Request) -> Acc.
-include("ejabberd.hrl").
-include("logger.hrl").
--include("jlib.hrl").
--include("adhoc.hrl").
+-include("xmpp.hrl").
-include("mod_announce.hrl").
-callback init(binary(), gen_mod:opts()) -> any().
-define(NS_ADMINL(Sub), [<<"http:">>, <<"jabber.org">>, <<"protocol">>,
<<"admin">>, <<Sub>>]).
+tokenize(undefined) -> [];
tokenize(Node) -> str:tokens(Node, <<"/#">>).
start(Host, Opts) ->
{wait, Proc}.
%% Announcing via messages to a custom resource
-announce(From, #jid{luser = <<>>} = To, #xmlel{name = <<"message">>} = Packet) ->
+announce(From, #jid{luser = <<>>} = To, #message{} = Packet) ->
Proc = gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME),
case To#jid.lresource of
<<"announce/all">> ->
%%-------------------------------------------------------------------------
%% Announcing via ad-hoc commands
-define(INFO_COMMAND(Lang, Node),
- [#xmlel{name = <<"identity">>,
- attrs = [{<<"category">>, <<"automation">>},
- {<<"type">>, <<"command-node">>},
- {<<"name">>, get_title(Lang, Node)}]}]).
+ [#identity{category = <<"automation">>,
+ type = <<"command-node">>,
+ name = get_title(Lang, Node)}]).
disco_identity(Acc, _From, _To, Node, Lang) ->
LNode = tokenize(Node),
-define(INFO_RESULT(Allow, Feats, Lang),
case Allow of
deny ->
- {error, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
+ {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)};
allow ->
{result, Feats}
end).
acl:match_rule(global, Access2, From)} of
{deny, deny} ->
Txt = <<"Denied by ACL">>,
- {error, ?ERRT_FORBIDDEN(Lang, Txt)};
+ {error, xmpp:err_forbidden(Txt, Lang)};
_ ->
{result, []}
end
%%-------------------------------------------------------------------------
-define(NODE_TO_ITEM(Lang, Server, Node),
-(
- #xmlel{
- name = <<"item">>,
- attrs = [
- {<<"jid">>, Server},
- {<<"node">>, Node},
- {<<"name">>, get_title(Lang, Node)}
- ]
- }
-)).
+ #disco_item{jid = jid:make(Server),
+ node = Node,
+ name = get_title(Lang, Node)}).
-define(ITEMS_RESULT(Allow, Items, Lang),
case Allow of
deny ->
- {error, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
+ {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)};
allow ->
{result, Items}
end).
-disco_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, <<>>, Lang) ->
+disco_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, undefined, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
false ->
Acc;
commands_result(Allow, From, To, Request) ->
case Allow of
deny ->
- Lang = Request#adhoc_request.lang,
- {error, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
+ Lang = Request#adhoc_command.lang,
+ {error, xmpp:err_forbidden(<<"Denied by ACL">>, Lang)};
allow ->
announce_commands(From, To, Request)
end.
announce_commands(Acc, From, #jid{lserver = LServer} = To,
- #adhoc_request{ node = Node} = Request) ->
+ #adhoc_command{node = Node} = Request) ->
LNode = tokenize(Node),
F = fun() ->
Access = get_access(global),
%%-------------------------------------------------------------------------
announce_commands(From, To,
- #adhoc_request{lang = Lang,
+ #adhoc_command{lang = Lang,
node = Node,
- action = Action,
- xdata = XData} = Request) ->
- %% If the "action" attribute is not present, it is
- %% understood as "execute". If there was no <actions/>
- %% element in the first response (which there isn't in our
- %% case), "execute" and "complete" are equivalent.
- ActionIsExecute = lists:member(Action, [<<>>, <<"execute">>, <<"complete">>]),
- if Action == <<"cancel">> ->
+ sid = SID,
+ xdata = XData,
+ action = Action} = Request) ->
+ ActionIsExecute = Action == execute orelse Action == complete,
+ if Action == cancel ->
%% User cancels request
- adhoc:produce_response(Request, #adhoc_response{status = canceled});
- XData == false, ActionIsExecute ->
+ #adhoc_command{status = canceled, lang = Lang, node = Node,
+ sid = SID};
+ XData == undefined, ActionIsExecute ->
%% User requests form
- Elements = generate_adhoc_form(Lang, Node, To#jid.lserver),
- adhoc:produce_response(Request,
- #adhoc_response{status = executing,elements = [Elements]});
- XData /= false, ActionIsExecute ->
- %% User returns form.
- case jlib:parse_xdata_submit(XData) of
- invalid ->
- {error, ?ERRT_BAD_REQUEST(Lang, <<"Incorrect data form">>)};
- Fields ->
- handle_adhoc_form(From, To, Request, Fields)
- end;
+ Form = generate_adhoc_form(Lang, Node, To#jid.lserver),
+ #adhoc_command{status = executing, lang = Lang, node = Node,
+ sid = SID, xdata = Form};
+ XData /= undefined, ActionIsExecute ->
+ handle_adhoc_form(From, To, Request);
true ->
- Txt = <<"Incorrect action or data form">>,
- {error, ?ERRT_BAD_REQUEST(Lang, Txt)}
+ Txt = <<"Unexpected action">>,
+ {error, xmpp:err_bad_request(Txt, Lang)}
end.
--define(VVALUE(Val),
-(
- #xmlel{
- name = <<"value">>,
- children = [{xmlcdata, Val}]
- }
-)).
-
-define(TVFIELD(Type, Var, Val),
-(
- #xmlel{
- name = <<"field">>,
- attrs = [{<<"type">>, Type}, {<<"var">>, Var}],
- children = vvaluel(Val)
- }
-)).
-
--define(HFIELD(), ?TVFIELD(<<"hidden">>, <<"FORM_TYPE">>, ?NS_ADMIN)).
+ #xdata_field{type = Type, var = Var, values = vvaluel(Val)}).
vvaluel(Val) ->
case Val of
<<>> -> [];
- _ -> [?VVALUE(Val)]
+ _ -> [Val]
end.
generate_adhoc_form(Lang, Node, ServerHost) ->
true ->
{<<>>, <<>>}
end,
- #xmlel{
- name = <<"x">>,
- attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
- children = [
- ?HFIELD(),
- #xmlel{name = <<"title">>, children = [{xmlcdata, get_title(Lang, Node)}]}
- ]
- ++
- if (LNode == ?NS_ADMINL("delete-motd"))
- or (LNode == ?NS_ADMINL("delete-motd-allhosts")) ->
- [#xmlel{
- name = <<"field">>,
- attrs = [
- {<<"var">>, <<"confirm">>},
- {<<"type">>, <<"boolean">>},
- {<<"label">>,
- translate:translate(Lang, <<"Really delete message of the day?">>)}
- ],
- children = [
- #xmlel{name = <<"value">>, children = [{xmlcdata, <<"true">>}]}
- ]
- }
- ];
- true ->
- [#xmlel{
- name = <<"field">>,
- attrs = [
- {<<"var">>, <<"subject">>},
- {<<"type">>, <<"text-single">>},
- {<<"label">>, translate:translate(Lang, <<"Subject">>)}],
- children = vvaluel(OldSubject)
- },
- #xmlel{
- name = <<"field">>,
- attrs = [
- {<<"var">>, <<"body">>},
- {<<"type">>, <<"text-multi">>},
- {<<"label">>, translate:translate(Lang, <<"Message body">>)}],
- children = vvaluel(OldBody)
- }
- ]
-
- end}.
+ Fs = if (LNode == ?NS_ADMINL("delete-motd"))
+ or (LNode == ?NS_ADMINL("delete-motd-allhosts")) ->
+ [#xdata_field{type = boolean,
+ var = <<"confirm">>,
+ label = translate:translate(
+ Lang, <<"Really delete message of the day?">>),
+ values = [<<"true">>]}];
+ true ->
+ [#xdata_field{type = 'text-single',
+ var = <<"subject">>,
+ label = translate:translate(Lang, <<"Subject">>),
+ values = vvaluel(OldSubject)},
+ #xdata_field{type = 'text-multi',
+ var = <<"body">>,
+ label = translate:translate(Lang, <<"Message body">>),
+ values = vvaluel(OldBody)}]
+ end,
+ #xdata{type = form,
+ title = get_title(Lang, Node),
+ fields = [#xdata_field{type = hidden, var = <<"FORM_TYPE">>,
+ values = [?NS_ADMIN]}|Fs]}.
join_lines([]) ->
<<>>;
iolist_to_binary(lists:reverse(tl(Acc))).
handle_adhoc_form(From, #jid{lserver = LServer} = To,
- #adhoc_request{lang = Lang,
- node = Node,
- sessionid = SessionID},
- Fields) ->
- Confirm = case lists:keysearch(<<"confirm">>, 1, Fields) of
- {value, {<<"confirm">>, [<<"true">>]}} ->
- true;
- {value, {<<"confirm">>, [<<"1">>]}} ->
- true;
- _ ->
- false
+ #adhoc_command{lang = Lang, node = Node,
+ sid = SessionID, xdata = XData}) ->
+ Confirm = case xmpp_util:get_xdata_values(<<"confirm">>, XData) of
+ [<<"true">>] -> true;
+ [<<"1">>] -> true;
+ _ -> false
end,
- Subject = case lists:keysearch(<<"subject">>, 1, Fields) of
- {value, {<<"subject">>, SubjectLines}} ->
- %% There really shouldn't be more than one
- %% subject line, but can we stop them?
- join_lines(SubjectLines);
- _ ->
- <<>>
- end,
- Body = case lists:keysearch(<<"body">>, 1, Fields) of
- {value, {<<"body">>, BodyLines}} ->
- join_lines(BodyLines);
- _ ->
- <<>>
- end,
- Response = #adhoc_response{lang = Lang,
- node = Node,
- sessionid = SessionID,
- status = completed},
- Packet = #xmlel{
- name = <<"message">>,
- attrs = [{<<"type">>, <<"headline">>}],
- children = if Subject /= <<>> ->
- [#xmlel{name = <<"subject">>, children = [{xmlcdata, Subject}]}];
- true ->
- []
- end
- ++
- if Body /= <<>> ->
- [#xmlel{name = <<"body">>, children = [{xmlcdata, Body}]}];
- true ->
- []
- end
- },
+ Subject = join_lines(xmpp_util:get_xdata_values(<<"subject">>, XData)),
+ Body = join_lines(xmpp_util:get_xdata_values(<<"body">>, XData)),
+ Response = #adhoc_command{lang = Lang, node = Node, sid = SessionID,
+ status = completed},
+ Packet = #message{type = headline,
+ body = xmpp:mk_text(Body),
+ subject = xmpp:mk_text(Subject)},
Proc = gen_mod:get_module_proc(LServer, ?PROCNAME),
case {Node, Body} of
{?NS_ADMIN_DELETE_MOTD, _} ->
if Confirm ->
Proc ! {announce_motd_delete, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
true ->
- adhoc:produce_response(Response)
+ Response
end;
{?NS_ADMIN_DELETE_MOTD_ALLHOSTS, _} ->
if Confirm ->
Proc ! {announce_all_hosts_motd_delete, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
true ->
- adhoc:produce_response(Response)
+ Response
end;
{_, <<>>} ->
%% An announce message with no body is definitely an operator error.
%% Throw an error and give him/her a chance to send message again.
- {error, ?ERRT_NOT_ACCEPTABLE(Lang,
- <<"No body provided for announce message">>)};
+ {error, xmpp:err_not_acceptable(
+ <<"No body provided for announce message">>, Lang)};
%% Now send the packet to ?PROCNAME.
%% We don't use direct announce_* functions because it
%% leads to large delay in response and <iq/> queries processing
{?NS_ADMIN_ANNOUNCE, _} ->
Proc ! {announce_online, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
{?NS_ADMIN_ANNOUNCE_ALLHOSTS, _} ->
Proc ! {announce_all_hosts_online, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
{?NS_ADMIN_ANNOUNCE_ALL, _} ->
Proc ! {announce_all, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
{?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS, _} ->
Proc ! {announce_all_hosts_all, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
{?NS_ADMIN_SET_MOTD, _} ->
Proc ! {announce_motd, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
{?NS_ADMIN_SET_MOTD_ALLHOSTS, _} ->
Proc ! {announce_all_hosts_motd, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
{?NS_ADMIN_EDIT_MOTD, _} ->
Proc ! {announce_motd_update, From, To, Packet},
- adhoc:produce_response(Response);
+ Response;
{?NS_ADMIN_EDIT_MOTD_ALLHOSTS, _} ->
Proc ! {announce_all_hosts_motd_update, From, To, Packet},
- adhoc:produce_response(Response);
- _ ->
+ Response;
+ Junk ->
%% This can't happen, as we haven't registered any other
%% command nodes.
- {error, ?ERR_INTERNAL_SERVER_ERROR}
+ ?ERROR_MSG("got unexpected node/body = ~p", [Junk]),
+ {error, xmpp:err_internal_server_error()}
end.
get_title(Lang, <<"announce">>) ->
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
- Local = jid:make(<<>>, To#jid.server, <<>>),
+ Local = jid:make(To#jid.server),
lists:foreach(
fun({User, Server}) ->
- Dest = jid:make(User, Server, <<>>),
+ Dest = jid:make(User, Server),
ejabberd_router:route(Local, Dest, Packet)
end, ejabberd_auth:get_vh_registered_users(Host))
end.
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
- Local = jid:make(<<>>, To#jid.server, <<>>),
+ Local = jid:make(To#jid.server),
lists:foreach(
fun({User, Server}) ->
- Dest = jid:make(User, Server, <<>>),
+ Dest = jid:make(User, Server),
ejabberd_router:route(Local, Dest, Packet)
end, ejabberd_auth:dirty_get_registered_users())
end.
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
announce_online1(ejabberd_sm:get_vh_session_list(Host),
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
announce_online1(ejabberd_sm:dirty_get_sessions_list(),
end.
announce_online1(Sessions, Server, Packet) ->
- Local = jid:make(<<>>, Server, <<>>),
+ Local = jid:make(Server),
lists:foreach(
fun({U, S, R}) ->
Dest = jid:make(U, S, R),
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd(Host, Packet)
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
Hosts = ?MYHOSTS,
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_update(Host, Packet)
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
Hosts = ?MYHOSTS,
announce_motd_update(LServer, Packet) ->
announce_motd_delete(LServer),
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:set_motd(LServer, Packet).
+ Mod:set_motd(LServer, xmpp:encode(Packet)).
announce_motd_delete(From, To, Packet) ->
Host = To#jid.lserver,
Access = get_access(Host),
case acl:match_rule(Host, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
announce_motd_delete(Host)
Access = get_access(global),
case acl:match_rule(global, Access, From) of
deny ->
- Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
+ Lang = xmpp:get_lang(Packet),
Txt = <<"Denied by ACL">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, Txt)),
+ Err = xmpp:make_error(Packet, xmpp:err_forbidden(Txt, Lang)),
ejabberd_router:route(To, From, Err);
allow ->
Hosts = ?MYHOSTS,
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:get_motd(LServer) of
{ok, Packet} ->
- case Mod:is_motd_user(LUser, LServer) of
- false ->
- Local = jid:make(<<>>, LServer, <<>>),
- ejabberd_router:route(Local, JID, Packet),
- Mod:set_motd_user(LUser, LServer);
- true ->
- ok
+ try xmpp:decode(Packet, [ignore_els]) of
+ Msg ->
+ case Mod:is_motd_user(LUser, LServer) of
+ false ->
+ Local = jid:make(LServer),
+ ejabberd_router:route(Local, JID, Msg),
+ Mod:set_motd_user(LUser, LServer);
+ true ->
+ ok
+ end
+ catch _:{xmpp_codec, Why} ->
+ ?ERROR_MSG("failed to decode motd packet ~p: ~s",
+ [Packet, xmpp:format_error(Why)])
end;
error ->
ok
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:get_motd(LServer) of
{ok, Packet} ->
- {fxml:get_subtag_cdata(Packet, <<"subject">>),
- fxml:get_subtag_cdata(Packet, <<"body">>)};
+ try xmpp:decode(Packet, [ignore_els]) of
+ #message{body = Body, subject = Subject} ->
+ {xmpp:get_text(Subject), xmpp:get_text(Body)}
+ catch _:{xmpp_codec, Why} ->
+ ?ERROR_MSG("failed to decode motd packet ~p: ~s",
+ [Packet, xmpp:format_error(Why)])
+ end;
error ->
{<<>>, <<>>}
end.
%% This function is similar to others, but doesn't perform any ACL verification
send_announcement_to_all(Host, SubjectS, BodyS) ->
- SubjectEls = if SubjectS /= <<>> ->
- [#xmlel{name = <<"subject">>, children = [{xmlcdata, SubjectS}]}];
- true ->
- []
- end,
- BodyEls = if BodyS /= <<>> ->
- [#xmlel{name = <<"body">>, children = [{xmlcdata, BodyS}]}];
- true ->
- []
- end,
- Packet = #xmlel{
- name = <<"message">>,
- attrs = [{<<"type">>, <<"headline">>}],
- children = SubjectEls ++ BodyEls
- },
+ Packet = #message{type = headline,
+ body = xmpp:mk_text(BodyS),
+ subject = xmpp:mk_text(SubjectS)},
Sessions = ejabberd_sm:dirty_get_sessions_list(),
- Local = jid:make(<<>>, Host, <<>>),
+ Local = jid:make(Host),
lists:foreach(
fun({U, S, R}) ->
Dest = jid:make(U, S, R),
decode({xmlel, _name, _attrs, _} = _el, Opts) ->
IgnoreEls = proplists:get_bool(ignore_els, Opts),
case {_name, get_attr(<<"xmlns">>, _attrs)} of
+ {<<"command">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ decode_adhoc_command(<<"http://jabber.org/protocol/commands">>,
+ IgnoreEls, _el);
+ {<<"note">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ decode_adhoc_command_notes(<<"http://jabber.org/protocol/commands">>,
+ IgnoreEls, _el);
+ {<<"actions">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ decode_adhoc_command_actions(<<"http://jabber.org/protocol/commands">>,
+ IgnoreEls, _el);
+ {<<"complete">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ decode_adhoc_command_complete(<<"http://jabber.org/protocol/commands">>,
+ IgnoreEls, _el);
+ {<<"next">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ decode_adhoc_command_next(<<"http://jabber.org/protocol/commands">>,
+ IgnoreEls, _el);
+ {<<"prev">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ decode_adhoc_command_prev(<<"http://jabber.org/protocol/commands">>,
+ IgnoreEls, _el);
{<<"client-id">>, <<"urn:xmpp:sid:0">>} ->
decode_client_id(<<"urn:xmpp:sid:0">>, IgnoreEls, _el);
{<<"stanza-id">>, <<"urn:xmpp:sid:0">>} ->
is_known_tag({xmlel, _name, _attrs, _} = _el) ->
case {_name, get_attr(<<"xmlns">>, _attrs)} of
+ {<<"command">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ true;
+ {<<"note">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ true;
+ {<<"actions">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ true;
+ {<<"complete">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ true;
+ {<<"next">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ true;
+ {<<"prev">>,
+ <<"http://jabber.org/protocol/commands">>} ->
+ true;
{<<"client-id">>, <<"urn:xmpp:sid:0">>} -> true;
{<<"stanza-id">>, <<"urn:xmpp:sid:0">>} -> true;
{<<"addresses">>,
[{<<"xmlns">>, <<"urn:xmpp:sid:0">>}]);
encode({client_id, _} = Client_id) ->
encode_client_id(Client_id,
- [{<<"xmlns">>, <<"urn:xmpp:sid:0">>}]).
+ [{<<"xmlns">>, <<"urn:xmpp:sid:0">>}]);
+encode({adhoc_actions, _, _, _, _} = Actions) ->
+ encode_adhoc_command_actions(Actions,
+ [{<<"xmlns">>,
+ <<"http://jabber.org/protocol/commands">>}]);
+encode({adhoc_note, _, _} = Note) ->
+ encode_adhoc_command_notes(Note,
+ [{<<"xmlns">>,
+ <<"http://jabber.org/protocol/commands">>}]);
+encode({adhoc_command, _, _, _, _, _, _, _, _} =
+ Command) ->
+ encode_adhoc_command(Command,
+ [{<<"xmlns">>,
+ <<"http://jabber.org/protocol/commands">>}]).
get_name({last, _, _}) -> <<"query">>;
get_name({version, _, _, _}) -> <<"query">>;
get_name({address, _, _, _, _, _}) -> <<"address">>;
get_name({addresses, _}) -> <<"addresses">>;
get_name({stanza_id, _, _}) -> <<"stanza-id">>;
-get_name({client_id, _}) -> <<"client-id">>.
+get_name({client_id, _}) -> <<"client-id">>;
+get_name({adhoc_actions, _, _, _, _}) -> <<"actions">>;
+get_name({adhoc_note, _, _}) -> <<"note">>;
+get_name({adhoc_command, _, _, _, _, _, _, _, _}) ->
+ <<"command">>.
get_ns({last, _, _}) -> <<"jabber:iq:last">>;
get_ns({version, _, _, _}) -> <<"jabber:iq:version">>;
get_ns({addresses, _}) ->
<<"http://jabber.org/protocol/address">>;
get_ns({stanza_id, _, _}) -> <<"urn:xmpp:sid:0">>;
-get_ns({client_id, _}) -> <<"urn:xmpp:sid:0">>.
+get_ns({client_id, _}) -> <<"urn:xmpp:sid:0">>;
+get_ns({adhoc_actions, _, _, _, _}) ->
+ <<"http://jabber.org/protocol/commands">>;
+get_ns({adhoc_note, _, _}) ->
+ <<"http://jabber.org/protocol/commands">>;
+get_ns({adhoc_command, _, _, _, _, _, _, _, _}) ->
+ <<"http://jabber.org/protocol/commands">>.
dec_int(Val) -> dec_int(Val, infinity, infinity).
pp(addresses, 1) -> [list];
pp(stanza_id, 2) -> [by, id];
pp(client_id, 1) -> [id];
+pp(adhoc_actions, 4) -> [execute, prev, next, complete];
+pp(adhoc_note, 2) -> [type, data];
+pp(adhoc_command, 8) ->
+ [node, action, sid, status, lang, actions, notes,
+ xdata];
pp(_, _) -> no.
join([], _Sep) -> <<>>;
M = jlib:binary_to_integer(M1),
if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end.
+decode_adhoc_command(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"command">>, _attrs, _els}) ->
+ {Xdata, Notes, Actions} =
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ undefined, [], undefined),
+ {Node, Lang, Sid, Status, Action} =
+ decode_adhoc_command_attrs(__TopXMLNS, _attrs,
+ undefined, undefined, undefined, undefined,
+ undefined),
+ {adhoc_command, Node, Action, Sid, Status, Lang,
+ Actions, Notes, Xdata}.
+
+decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, [],
+ Xdata, Notes, Actions) ->
+ {Xdata, lists:reverse(Notes), Actions};
+decode_adhoc_command_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"actions">>, _attrs, _} = _el | _els],
+ Xdata, Notes, Actions) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata, Notes,
+ decode_adhoc_command_actions(__TopXMLNS,
+ __IgnoreEls,
+ _el));
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata, Notes,
+ decode_adhoc_command_actions(<<"http://jabber.org/protocol/commands">>,
+ __IgnoreEls,
+ _el));
+ _ ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata, Notes, Actions)
+ end;
+decode_adhoc_command_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"x">>, _attrs, _} = _el | _els], Xdata,
+ Notes, Actions) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"jabber:x:data">> ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ decode_xdata(<<"jabber:x:data">>,
+ __IgnoreEls, _el),
+ Notes, Actions);
+ _ ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata, Notes, Actions)
+ end;
+decode_adhoc_command_els(__TopXMLNS, __IgnoreEls,
+ [{xmlel, <<"note">>, _attrs, _} = _el | _els], Xdata,
+ Notes, Actions) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata,
+ [decode_adhoc_command_notes(__TopXMLNS,
+ __IgnoreEls, _el)
+ | Notes],
+ Actions);
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata,
+ [decode_adhoc_command_notes(<<"http://jabber.org/protocol/commands">>,
+ __IgnoreEls, _el)
+ | Notes],
+ Actions);
+ _ ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata, Notes, Actions)
+ end;
+decode_adhoc_command_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Xdata, Notes, Actions) ->
+ decode_adhoc_command_els(__TopXMLNS, __IgnoreEls, _els,
+ Xdata, Notes, Actions).
+
+decode_adhoc_command_attrs(__TopXMLNS,
+ [{<<"node">>, _val} | _attrs], _Node, Lang, Sid,
+ Status, Action) ->
+ decode_adhoc_command_attrs(__TopXMLNS, _attrs, _val,
+ Lang, Sid, Status, Action);
+decode_adhoc_command_attrs(__TopXMLNS,
+ [{<<"xml:lang">>, _val} | _attrs], Node, _Lang, Sid,
+ Status, Action) ->
+ decode_adhoc_command_attrs(__TopXMLNS, _attrs, Node,
+ _val, Sid, Status, Action);
+decode_adhoc_command_attrs(__TopXMLNS,
+ [{<<"sessionid">>, _val} | _attrs], Node, Lang, _Sid,
+ Status, Action) ->
+ decode_adhoc_command_attrs(__TopXMLNS, _attrs, Node,
+ Lang, _val, Status, Action);
+decode_adhoc_command_attrs(__TopXMLNS,
+ [{<<"status">>, _val} | _attrs], Node, Lang, Sid,
+ _Status, Action) ->
+ decode_adhoc_command_attrs(__TopXMLNS, _attrs, Node,
+ Lang, Sid, _val, Action);
+decode_adhoc_command_attrs(__TopXMLNS,
+ [{<<"action">>, _val} | _attrs], Node, Lang, Sid,
+ Status, _Action) ->
+ decode_adhoc_command_attrs(__TopXMLNS, _attrs, Node,
+ Lang, Sid, Status, _val);
+decode_adhoc_command_attrs(__TopXMLNS, [_ | _attrs],
+ Node, Lang, Sid, Status, Action) ->
+ decode_adhoc_command_attrs(__TopXMLNS, _attrs, Node,
+ Lang, Sid, Status, Action);
+decode_adhoc_command_attrs(__TopXMLNS, [], Node, Lang,
+ Sid, Status, Action) ->
+ {decode_adhoc_command_attr_node(__TopXMLNS, Node),
+ 'decode_adhoc_command_attr_xml:lang'(__TopXMLNS, Lang),
+ decode_adhoc_command_attr_sessionid(__TopXMLNS, Sid),
+ decode_adhoc_command_attr_status(__TopXMLNS, Status),
+ decode_adhoc_command_attr_action(__TopXMLNS, Action)}.
+
+encode_adhoc_command({adhoc_command, Node, Action, Sid,
+ Status, Lang, Actions, Notes, Xdata},
+ _xmlns_attrs) ->
+ _els =
+ lists:reverse('encode_adhoc_command_$xdata'(Xdata,
+ 'encode_adhoc_command_$notes'(Notes,
+ 'encode_adhoc_command_$actions'(Actions,
+ [])))),
+ _attrs = encode_adhoc_command_attr_action(Action,
+ encode_adhoc_command_attr_status(Status,
+ encode_adhoc_command_attr_sessionid(Sid,
+ 'encode_adhoc_command_attr_xml:lang'(Lang,
+ encode_adhoc_command_attr_node(Node,
+ _xmlns_attrs))))),
+ {xmlel, <<"command">>, _attrs, _els}.
+
+'encode_adhoc_command_$xdata'(undefined, _acc) -> _acc;
+'encode_adhoc_command_$xdata'(Xdata, _acc) ->
+ [encode_xdata(Xdata,
+ [{<<"xmlns">>, <<"jabber:x:data">>}])
+ | _acc].
+
+'encode_adhoc_command_$notes'([], _acc) -> _acc;
+'encode_adhoc_command_$notes'([Notes | _els], _acc) ->
+ 'encode_adhoc_command_$notes'(_els,
+ [encode_adhoc_command_notes(Notes, [])
+ | _acc]).
+
+'encode_adhoc_command_$actions'(undefined, _acc) ->
+ _acc;
+'encode_adhoc_command_$actions'(Actions, _acc) ->
+ [encode_adhoc_command_actions(Actions, []) | _acc].
+
+decode_adhoc_command_attr_node(__TopXMLNS, undefined) ->
+ erlang:error({xmpp_codec,
+ {missing_attr, <<"node">>, <<"command">>, __TopXMLNS}});
+decode_adhoc_command_attr_node(__TopXMLNS, _val) ->
+ _val.
+
+encode_adhoc_command_attr_node(_val, _acc) ->
+ [{<<"node">>, _val} | _acc].
+
+'decode_adhoc_command_attr_xml:lang'(__TopXMLNS,
+ undefined) ->
+ undefined;
+'decode_adhoc_command_attr_xml:lang'(__TopXMLNS,
+ _val) ->
+ _val.
+
+'encode_adhoc_command_attr_xml:lang'(undefined, _acc) ->
+ _acc;
+'encode_adhoc_command_attr_xml:lang'(_val, _acc) ->
+ [{<<"xml:lang">>, _val} | _acc].
+
+decode_adhoc_command_attr_sessionid(__TopXMLNS,
+ undefined) ->
+ undefined;
+decode_adhoc_command_attr_sessionid(__TopXMLNS, _val) ->
+ _val.
+
+encode_adhoc_command_attr_sessionid(undefined, _acc) ->
+ _acc;
+encode_adhoc_command_attr_sessionid(_val, _acc) ->
+ [{<<"sessionid">>, _val} | _acc].
+
+decode_adhoc_command_attr_status(__TopXMLNS,
+ undefined) ->
+ undefined;
+decode_adhoc_command_attr_status(__TopXMLNS, _val) ->
+ case catch dec_enum(_val,
+ [canceled, completed, executing])
+ of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"status">>, <<"command">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_adhoc_command_attr_status(undefined, _acc) ->
+ _acc;
+encode_adhoc_command_attr_status(_val, _acc) ->
+ [{<<"status">>, enc_enum(_val)} | _acc].
+
+decode_adhoc_command_attr_action(__TopXMLNS,
+ undefined) ->
+ execute;
+decode_adhoc_command_attr_action(__TopXMLNS, _val) ->
+ case catch dec_enum(_val,
+ [cancel, complete, execute, next, prev])
+ of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"action">>, <<"command">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_adhoc_command_attr_action(execute, _acc) -> _acc;
+encode_adhoc_command_attr_action(_val, _acc) ->
+ [{<<"action">>, enc_enum(_val)} | _acc].
+
+decode_adhoc_command_notes(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"note">>, _attrs, _els}) ->
+ Data = decode_adhoc_command_notes_els(__TopXMLNS,
+ __IgnoreEls, _els, <<>>),
+ Type = decode_adhoc_command_notes_attrs(__TopXMLNS,
+ _attrs, undefined),
+ {adhoc_note, Type, Data}.
+
+decode_adhoc_command_notes_els(__TopXMLNS, __IgnoreEls,
+ [], Data) ->
+ decode_adhoc_command_notes_cdata(__TopXMLNS, Data);
+decode_adhoc_command_notes_els(__TopXMLNS, __IgnoreEls,
+ [{xmlcdata, _data} | _els], Data) ->
+ decode_adhoc_command_notes_els(__TopXMLNS, __IgnoreEls,
+ _els, <<Data/binary, _data/binary>>);
+decode_adhoc_command_notes_els(__TopXMLNS, __IgnoreEls,
+ [_ | _els], Data) ->
+ decode_adhoc_command_notes_els(__TopXMLNS, __IgnoreEls,
+ _els, Data).
+
+decode_adhoc_command_notes_attrs(__TopXMLNS,
+ [{<<"type">>, _val} | _attrs], _Type) ->
+ decode_adhoc_command_notes_attrs(__TopXMLNS, _attrs,
+ _val);
+decode_adhoc_command_notes_attrs(__TopXMLNS,
+ [_ | _attrs], Type) ->
+ decode_adhoc_command_notes_attrs(__TopXMLNS, _attrs,
+ Type);
+decode_adhoc_command_notes_attrs(__TopXMLNS, [],
+ Type) ->
+ decode_adhoc_command_notes_attr_type(__TopXMLNS, Type).
+
+encode_adhoc_command_notes({adhoc_note, Type, Data},
+ _xmlns_attrs) ->
+ _els = encode_adhoc_command_notes_cdata(Data, []),
+ _attrs = encode_adhoc_command_notes_attr_type(Type,
+ _xmlns_attrs),
+ {xmlel, <<"note">>, _attrs, _els}.
+
+decode_adhoc_command_notes_attr_type(__TopXMLNS,
+ undefined) ->
+ info;
+decode_adhoc_command_notes_attr_type(__TopXMLNS,
+ _val) ->
+ case catch dec_enum(_val, [info, warn, error]) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"type">>, <<"note">>, __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_adhoc_command_notes_attr_type(info, _acc) ->
+ _acc;
+encode_adhoc_command_notes_attr_type(_val, _acc) ->
+ [{<<"type">>, enc_enum(_val)} | _acc].
+
+decode_adhoc_command_notes_cdata(__TopXMLNS, <<>>) ->
+ <<>>;
+decode_adhoc_command_notes_cdata(__TopXMLNS, _val) ->
+ _val.
+
+encode_adhoc_command_notes_cdata(<<>>, _acc) -> _acc;
+encode_adhoc_command_notes_cdata(_val, _acc) ->
+ [{xmlcdata, _val} | _acc].
+
+decode_adhoc_command_actions(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"actions">>, _attrs, _els}) ->
+ {Next, Complete, Prev} =
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, false, false,
+ false),
+ Execute = decode_adhoc_command_actions_attrs(__TopXMLNS,
+ _attrs, undefined),
+ {adhoc_actions, Execute, Prev, Next, Complete}.
+
+decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, [], Next, Complete, Prev) ->
+ {Next, Complete, Prev};
+decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls,
+ [{xmlel, <<"prev">>, _attrs, _} = _el | _els],
+ Next, Complete, Prev) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, Next, Complete,
+ decode_adhoc_command_prev(__TopXMLNS,
+ __IgnoreEls,
+ _el));
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, Next, Complete,
+ decode_adhoc_command_prev(<<"http://jabber.org/protocol/commands">>,
+ __IgnoreEls,
+ _el));
+ _ ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, Next, Complete,
+ Prev)
+ end;
+decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls,
+ [{xmlel, <<"next">>, _attrs, _} = _el | _els],
+ Next, Complete, Prev) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els,
+ decode_adhoc_command_next(__TopXMLNS,
+ __IgnoreEls,
+ _el),
+ Complete, Prev);
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els,
+ decode_adhoc_command_next(<<"http://jabber.org/protocol/commands">>,
+ __IgnoreEls,
+ _el),
+ Complete, Prev);
+ _ ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, Next, Complete,
+ Prev)
+ end;
+decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls,
+ [{xmlel, <<"complete">>, _attrs, _} = _el
+ | _els],
+ Next, Complete, Prev) ->
+ case get_attr(<<"xmlns">>, _attrs) of
+ <<"">>
+ when __TopXMLNS ==
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, Next,
+ decode_adhoc_command_complete(__TopXMLNS,
+ __IgnoreEls,
+ _el),
+ Prev);
+ <<"http://jabber.org/protocol/commands">> ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, Next,
+ decode_adhoc_command_complete(<<"http://jabber.org/protocol/commands">>,
+ __IgnoreEls,
+ _el),
+ Prev);
+ _ ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, Next, Complete,
+ Prev)
+ end;
+decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, [_ | _els], Next, Complete,
+ Prev) ->
+ decode_adhoc_command_actions_els(__TopXMLNS,
+ __IgnoreEls, _els, Next, Complete, Prev).
+
+decode_adhoc_command_actions_attrs(__TopXMLNS,
+ [{<<"execute">>, _val} | _attrs],
+ _Execute) ->
+ decode_adhoc_command_actions_attrs(__TopXMLNS, _attrs,
+ _val);
+decode_adhoc_command_actions_attrs(__TopXMLNS,
+ [_ | _attrs], Execute) ->
+ decode_adhoc_command_actions_attrs(__TopXMLNS, _attrs,
+ Execute);
+decode_adhoc_command_actions_attrs(__TopXMLNS, [],
+ Execute) ->
+ decode_adhoc_command_actions_attr_execute(__TopXMLNS,
+ Execute).
+
+encode_adhoc_command_actions({adhoc_actions, Execute,
+ Prev, Next, Complete},
+ _xmlns_attrs) ->
+ _els =
+ lists:reverse('encode_adhoc_command_actions_$next'(Next,
+ 'encode_adhoc_command_actions_$complete'(Complete,
+ 'encode_adhoc_command_actions_$prev'(Prev,
+ [])))),
+ _attrs =
+ encode_adhoc_command_actions_attr_execute(Execute,
+ _xmlns_attrs),
+ {xmlel, <<"actions">>, _attrs, _els}.
+
+'encode_adhoc_command_actions_$next'(false, _acc) ->
+ _acc;
+'encode_adhoc_command_actions_$next'(Next, _acc) ->
+ [encode_adhoc_command_next(Next, []) | _acc].
+
+'encode_adhoc_command_actions_$complete'(false, _acc) ->
+ _acc;
+'encode_adhoc_command_actions_$complete'(Complete,
+ _acc) ->
+ [encode_adhoc_command_complete(Complete, []) | _acc].
+
+'encode_adhoc_command_actions_$prev'(false, _acc) ->
+ _acc;
+'encode_adhoc_command_actions_$prev'(Prev, _acc) ->
+ [encode_adhoc_command_prev(Prev, []) | _acc].
+
+decode_adhoc_command_actions_attr_execute(__TopXMLNS,
+ undefined) ->
+ undefined;
+decode_adhoc_command_actions_attr_execute(__TopXMLNS,
+ _val) ->
+ case catch dec_enum(_val, [complete, next, prev]) of
+ {'EXIT', _} ->
+ erlang:error({xmpp_codec,
+ {bad_attr_value, <<"execute">>, <<"actions">>,
+ __TopXMLNS}});
+ _res -> _res
+ end.
+
+encode_adhoc_command_actions_attr_execute(undefined,
+ _acc) ->
+ _acc;
+encode_adhoc_command_actions_attr_execute(_val, _acc) ->
+ [{<<"execute">>, enc_enum(_val)} | _acc].
+
+decode_adhoc_command_complete(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"complete">>, _attrs, _els}) ->
+ true.
+
+encode_adhoc_command_complete(true, _xmlns_attrs) ->
+ _els = [],
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"complete">>, _attrs, _els}.
+
+decode_adhoc_command_next(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"next">>, _attrs, _els}) ->
+ true.
+
+encode_adhoc_command_next(true, _xmlns_attrs) ->
+ _els = [],
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"next">>, _attrs, _els}.
+
+decode_adhoc_command_prev(__TopXMLNS, __IgnoreEls,
+ {xmlel, <<"prev">>, _attrs, _els}) ->
+ true.
+
+encode_adhoc_command_prev(true, _xmlns_attrs) ->
+ _els = [],
+ _attrs = _xmlns_attrs,
+ {xmlel, <<"prev">>, _attrs, _els}.
+
decode_client_id(__TopXMLNS, __IgnoreEls,
{xmlel, <<"client-id">>, _attrs, _els}) ->
Id = decode_client_id_attrs(__TopXMLNS, _attrs,
%% API
-export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1,
is_standalone_chat_state/1, get_xdata_values/2,
- has_xdata_var/2]).
+ has_xdata_var/2, make_adhoc_response/1, make_adhoc_response/2]).
-include("xmpp.hrl").
has_xdata_var(Var, #xdata{fields = Fields}) ->
lists:keymember(Var, #xdata_field.var, Fields).
+-spec make_adhoc_response(adhoc_command(), adhoc_command()) -> adhoc_command().
+make_adhoc_response(#adhoc_command{lang = Lang, node = Node, sid = SID},
+ Command) ->
+ Command#adhoc_command{lang = Lang, node = Node, sid = SID}.
+
+-spec make_adhoc_response(adhoc_command()) -> adhoc_command().
+make_adhoc_response(#adhoc_command{sid = undefined} = Command) ->
+ SID = jlib:now_to_utc_string(p1_time_compat:timestamp()),
+ Command#adhoc_command{sid = SID};
+make_adhoc_response(Command) ->
+ Command.
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
result = {client_id, '$id'},
attrs = [#attr{name = <<"id">>, required = true}]}).
+-xml(adhoc_command_prev,
+ #elem{name = <<"prev">>,
+ xmlns = <<"http://jabber.org/protocol/commands">>,
+ result = true}).
+-xml(adhoc_command_next,
+ #elem{name = <<"next">>,
+ xmlns = <<"http://jabber.org/protocol/commands">>,
+ result = true}).
+-xml(adhoc_command_complete,
+ #elem{name = <<"complete">>,
+ xmlns = <<"http://jabber.org/protocol/commands">>,
+ result = true}).
+
+-xml(adhoc_command_actions,
+ #elem{name = <<"actions">>,
+ xmlns = <<"http://jabber.org/protocol/commands">>,
+ result = {adhoc_actions, '$execute', '$prev', '$next', '$complete'},
+ attrs = [#attr{name = <<"execute">>,
+ dec = {dec_enum, [[complete, next, prev]]},
+ enc = {enc_enum, []}}],
+ refs = [#ref{name = adhoc_command_prev, min = 0, max = 1,
+ default = false, label = '$prev'},
+ #ref{name = adhoc_command_next, min = 0, max = 1,
+ default = false, label = '$next'},
+ #ref{name = adhoc_command_complete, min = 0, max = 1,
+ default = false, label = '$complete'}]}).
+
+-xml(adhoc_command_notes,
+ #elem{name = <<"note">>,
+ xmlns = <<"http://jabber.org/protocol/commands">>,
+ result = {adhoc_note, '$type', '$data'},
+ attrs = [#attr{name = <<"type">>, default = info,
+ dec = {dec_enum, [[info, warn, error]]},
+ enc = {enc_enum, []}}],
+ cdata = #cdata{default = <<"">>, label = '$data'}}).
+
+-xml(adhoc_command,
+ #elem{name = <<"command">>,
+ xmlns = <<"http://jabber.org/protocol/commands">>,
+ result = {adhoc_command, '$node', '$action', '$sid',
+ '$status', '$lang', '$actions', '$notes', '$xdata'},
+ attrs = [#attr{name = <<"node">>, required = true},
+ #attr{name = <<"xml:lang">>, label = '$lang'},
+ #attr{name = <<"sessionid">>, label = '$sid'},
+ #attr{name = <<"status">>,
+ dec = {dec_enum, [[canceled, completed, executing]]},
+ enc = {enc_enum, []}},
+ #attr{name = <<"action">>, default = execute,
+ dec = {dec_enum, [[cancel, complete,
+ execute, next, prev]]},
+ enc = {enc_enum, []}}],
+ refs = [#ref{name = adhoc_command_actions, min = 0, max = 1,
+ label = '$actions'},
+ #ref{name = xdata, min = 0, max = 1},
+ #ref{name = adhoc_command_notes, label = '$notes'}]}).
+
dec_tzo(Val) ->
[H1, M1] = str:tokens(Val, <<":">>),
H = jlib:binary_to_integer(H1),