1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : adhoc.erl |
3 |
|
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se> |
4 |
|
%%% Purpose : Provide helper functions for ad-hoc commands (XEP-0050) |
5 |
|
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%---------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(adhoc). |
27 |
|
-author('henoch@dtek.chalmers.se'). |
28 |
|
-xep([{xep, 50}, {version, "1.2"}]). |
29 |
|
-export([parse_request/1, |
30 |
|
produce_response/2, |
31 |
|
produce_response/4, |
32 |
|
produce_response/1]). |
33 |
|
|
34 |
|
-include("mongoose.hrl"). |
35 |
|
-include("jlib.hrl"). |
36 |
|
-include("adhoc.hrl"). |
37 |
|
|
38 |
|
-type request() :: #adhoc_request{}. |
39 |
|
-type response() :: #adhoc_response{}. |
40 |
|
|
41 |
|
-export_type([request/0, response/0]). |
42 |
|
|
43 |
|
%% @doc Parse an ad-hoc request. Return either an adhoc_request record or |
44 |
|
%% an {error, ErrorType} tuple. |
45 |
|
-spec parse_request(jlib:iq()) -> request() | {error, exml:element()}. |
46 |
|
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) -> |
47 |
5 |
?LOG_DEBUG(#{what => adhoc_parse_request, |
48 |
|
text => <<"entering parse_request...">>, |
49 |
5 |
sub_el => SubEl}), |
50 |
5 |
Node = xml:get_tag_attr_s(<<"node">>, SubEl), |
51 |
5 |
SessionID = xml:get_tag_attr_s(<<"sessionid">>, SubEl), |
52 |
5 |
Action = xml:get_tag_attr_s(<<"action">>, SubEl), |
53 |
5 |
XData = find_xdata_el(SubEl), |
54 |
5 |
#xmlel{children = AllEls} = SubEl, |
55 |
5 |
Others = case XData of |
56 |
|
false -> |
57 |
3 |
AllEls; |
58 |
|
_ -> |
59 |
2 |
lists:delete(XData, AllEls) |
60 |
|
end, |
61 |
|
|
62 |
5 |
#adhoc_request{lang = Lang, |
63 |
|
node = Node, |
64 |
|
session_id = SessionID, |
65 |
|
action = Action, |
66 |
|
xdata = XData, |
67 |
|
others = Others}; |
68 |
|
parse_request(_) -> |
69 |
:-( |
{error, mongoose_xmpp_errors:bad_request()}. |
70 |
|
|
71 |
|
%% @doc Borrowed from mod_vcard.erl |
72 |
|
-spec find_xdata_el(exml:element()) -> false | exml:element(). |
73 |
|
find_xdata_el(#xmlel{children = SubEls}) -> |
74 |
5 |
find_xdata_el1(SubEls). |
75 |
|
|
76 |
|
%% @private |
77 |
|
find_xdata_el1([]) -> |
78 |
3 |
false; |
79 |
|
find_xdata_el1([XE = #xmlel{attrs = Attrs} | Els]) -> |
80 |
2 |
case xml:get_attr_s(<<"xmlns">>, Attrs) of |
81 |
|
?NS_XDATA -> |
82 |
2 |
XE; |
83 |
|
_ -> |
84 |
:-( |
find_xdata_el1(Els) |
85 |
|
end; |
86 |
|
find_xdata_el1([_ | Els]) -> |
87 |
:-( |
find_xdata_el1(Els). |
88 |
|
|
89 |
|
%% @doc Produce a <command/> node to use as response from an adhoc_response |
90 |
|
%% record, filling in values for language, node and session id from |
91 |
|
%% the request. |
92 |
|
-spec produce_response(request(), response() | atom()) -> #xmlel{}. |
93 |
|
produce_response(Request, Status) when is_atom(Status) -> |
94 |
2 |
produce_response(Request, Status, <<>>, []); |
95 |
|
produce_response(#adhoc_request{lang = Lang, |
96 |
|
node = Node, |
97 |
|
session_id = SessionID}, |
98 |
|
Response) -> |
99 |
:-( |
produce_response(Response#adhoc_response{lang = Lang, |
100 |
|
node = Node, |
101 |
|
session_id = SessionID}). |
102 |
|
|
103 |
|
%% @doc Produce a <command/> node to use as response from an adhoc_response |
104 |
|
%% record, filling in values for language, node and session id from |
105 |
|
%% the request. |
106 |
|
-spec produce_response(request(), Status :: atom(), DefaultAction :: binary(), |
107 |
|
Elements :: [exml:element()]) -> exml:element(). |
108 |
|
produce_response(Request, Status, DefaultAction, Elements) -> |
109 |
4 |
#adhoc_request{lang = Lang, node = Node, session_id = SessionID} = Request, |
110 |
4 |
produce_response(#adhoc_response{lang = Lang, node = Node, session_id = SessionID, |
111 |
|
status = Status, default_action = DefaultAction, |
112 |
|
elements = Elements}). |
113 |
|
|
114 |
|
|
115 |
|
%% @doc Produce a <command/> node to use as response from an adhoc_response |
116 |
|
%% record. |
117 |
|
-spec produce_response(response()) -> exml:element(). |
118 |
|
produce_response(#adhoc_response{lang = _Lang, |
119 |
|
node = Node, |
120 |
|
session_id = ProvidedSessionID, |
121 |
|
status = Status, |
122 |
|
default_action = DefaultAction, |
123 |
|
actions = Actions, |
124 |
|
notes = Notes, |
125 |
|
elements = Elements}) -> |
126 |
5 |
SessionID = if is_binary(ProvidedSessionID), ProvidedSessionID /= <<"">> -> |
127 |
:-( |
ProvidedSessionID; |
128 |
|
true -> |
129 |
5 |
USec = os:system_time(microsecond), |
130 |
5 |
TS = calendar:system_time_to_rfc3339(USec, [{offset, "Z"}, |
131 |
|
{unit, microsecond}]), |
132 |
5 |
list_to_binary(TS) |
133 |
|
end, |
134 |
5 |
ActionsEls = case Actions of |
135 |
|
[] -> |
136 |
5 |
[]; |
137 |
|
_ -> |
138 |
:-( |
ActionsElAttrs = case DefaultAction of |
139 |
:-( |
<<"">> -> []; |
140 |
:-( |
_ -> [{<<"execute">>, DefaultAction}] |
141 |
|
end, |
142 |
:-( |
[#xmlel{name = <<"actions">>, attrs = ActionsElAttrs, |
143 |
:-( |
children = [#xmlel{name = Action} || Action <- Actions]}] |
144 |
|
end, |
145 |
5 |
NotesEls = lists:map(fun({Type, Text}) -> |
146 |
1 |
#xmlel{name = <<"note">>, |
147 |
|
attrs = [{<<"type">>, Type}], |
148 |
|
children = [#xmlcdata{content = Text}]} |
149 |
|
end, Notes), |
150 |
5 |
#xmlel{name = <<"command">>, |
151 |
|
attrs = [{<<"xmlns">>, ?NS_COMMANDS}, |
152 |
|
{<<"sessionid">>, SessionID}, |
153 |
|
{<<"node">>, Node}, |
154 |
|
{<<"status">>, list_to_binary(atom_to_list(Status))}], |
155 |
|
children = ActionsEls ++ NotesEls ++ Elements}. |