25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-28 16:34:13 +01:00

Rewrite mod_http_upload to use XML generator

This commit is contained in:
Evgeniy Khramtsov 2016-07-30 17:48:52 +03:00
parent d2d3b961eb
commit 792e6a7c1c
5 changed files with 784 additions and 138 deletions

View File

@ -91,6 +91,12 @@
since :: any()}).
-type muc_history() :: #muc_history{}.
-record(thumbnail, {uri :: binary(),
'media-type' = <<>> :: binary(),
width :: non_neg_integer(),
height :: non_neg_integer()}).
-type thumbnail() :: #thumbnail{}.
-record(pubsub_affiliation, {node :: binary(),
type :: 'member' | 'none' | 'outcast' | 'owner' | 'publish-only' | 'publisher'}).
-type pubsub_affiliation() :: #pubsub_affiliation{}.
@ -387,6 +393,11 @@
items = [] :: [#pubsub_item{}]}).
-type pubsub_retract() :: #pubsub_retract{}.
-record(upload_slot, {get :: binary(),
put :: binary(),
xmlns :: binary()}).
-type upload_slot() :: #upload_slot{}.
-record(mix_participant, {jid :: any(),
nick :: binary()}).
-type mix_participant() :: #mix_participant{}.
@ -405,6 +416,12 @@
-record(block_list, {items = [] :: [any()]}).
-type block_list() :: #block_list{}.
-record(upload_request, {filename :: binary(),
size :: non_neg_integer(),
'content-type' = <<>> :: binary(),
xmlns :: binary()}).
-type upload_request() :: #upload_request{}.
-record(xdata_option, {label :: binary(),
value :: binary()}).
-type xdata_option() :: #xdata_option{}.
@ -837,15 +854,16 @@
version() |
pubsub_affiliation() |
mam_fin() |
sm_a() |
bob_data() |
media() |
sm_a() |
carbons_sent() |
mam_archived() |
p1_rebind() |
sasl_abort() |
carbons_received() |
pubsub_retract() |
upload_slot() |
mix_participant() |
compressed() |
block_list() |
@ -887,6 +905,7 @@
feature_csi() |
privacy_query() |
delay() |
thumbnail() |
vcard_tel() |
vcard_geo() |
vcard_photo() |
@ -932,6 +951,7 @@
mam_result() |
rsm_first() |
stat() |
upload_request() |
xdata_field() |
adhoc_command() |
sm_failed() |

View File

@ -92,7 +92,7 @@
-include("ejabberd.hrl").
-include("ejabberd_http.hrl").
-include("jlib.hrl").
-include("xmpp.hrl").
-include("logger.hrl").
-record(state,
@ -360,9 +360,8 @@ handle_cast(Request, State) ->
-spec handle_info(timeout | _, state()) -> {noreply, state()}.
handle_info({route, From, To, #xmlel{name = <<"iq">>} = Stanza}, State) ->
Request = jlib:iq_query_info(Stanza),
{Reply, NewState} = case process_iq(From, Request, State) of
handle_info({route, From, To, #iq{} = IQ}, State) ->
{Reply, NewState} = case process_iq(From, IQ, State) of
R when is_record(R, iq) ->
{R, State};
{R, S} ->
@ -371,7 +370,7 @@ handle_info({route, From, To, #xmlel{name = <<"iq">>} = Stanza}, State) ->
{none, State}
end,
if Reply /= none ->
ejabberd_router:route(To, From, jlib:iq_to_xml(Reply));
ejabberd_router:route(To, From, Reply);
true ->
ok
end,
@ -531,89 +530,48 @@ expand_host(Subject, Host) ->
%% XMPP request handling.
-spec process_iq(jid(), iq_request() | reply | invalid, state())
-> {iq_reply(), state()} | iq_reply() | not_request.
-spec process_iq(jid(), iq(), state()) -> {iq(), state()} | iq() | not_request.
process_iq(_From,
#iq{type = get, xmlns = ?NS_DISCO_INFO, lang = Lang} = IQ,
#iq{type = get, lang = Lang, sub_els = [#disco_info{}]} = IQ,
#state{server_host = ServerHost, name = Name}) ->
AddInfo = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
[ServerHost, ?MODULE, <<"">>, <<"">>]),
IQ#iq{type = result,
sub_el = [#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}],
children = iq_disco_info(ServerHost, Lang, Name)
++ AddInfo}]};
process_iq(From,
#iq{type = get, xmlns = XMLNS, lang = Lang, sub_el = SubEl} = IQ,
#state{server_host = ServerHost, access = Access} = State)
when XMLNS == ?NS_HTTP_UPLOAD;
XMLNS == ?NS_HTTP_UPLOAD_OLD ->
xmpp:make_iq_result(IQ, iq_disco_info(ServerHost, Lang, Name, AddInfo));
process_iq(From, #iq{type = get, lang = Lang,
sub_els = [#upload_request{filename = File,
size = Size,
'content-type' = CType,
xmlns = XMLNS}]} = IQ,
#state{server_host = ServerHost, access = Access} = State) ->
case acl:match_rule(ServerHost, Access, From) of
allow ->
case parse_request(SubEl, Lang) of
{ok, File, Size, ContentType} ->
case create_slot(State, From, File, Size, ContentType,
Lang) of
ContentType = yield_content_type(CType),
case create_slot(State, From, File, Size, ContentType, Lang) of
{ok, Slot} ->
{ok, Timer} = timer:send_after(?SLOT_TIMEOUT,
{slot_timed_out,
Slot}),
NewState = add_slot(Slot, Size, Timer, State),
SlotEl = slot_el(Slot, State, XMLNS),
{IQ#iq{type = result, sub_el = [SlotEl]}, NewState};
Slot = mk_slot(Slot, State, XMLNS),
{xmpp:make_iq_result(IQ, Slot), NewState};
{ok, PutURL, GetURL} ->
SlotEl = slot_el(PutURL, GetURL, XMLNS),
IQ#iq{type = result, sub_el = [SlotEl]};
Slot = mk_slot(PutURL, GetURL, XMLNS),
xmpp:make_iq_result(IQ, Slot);
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]}
end;
{error, Error} ->
?DEBUG("Cannot parse request from ~s",
[jid:to_string(From)]),
IQ#iq{type = error, sub_el = [SubEl, Error]}
xmpp:make_error(IQ, Error)
end;
deny ->
?DEBUG("Denying HTTP upload slot request from ~s",
[jid:to_string(From)]),
Txt = <<"Denied by ACL">>,
IQ#iq{type = error, sub_el = [SubEl, ?ERRT_FORBIDDEN(Lang, Txt)]}
xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang))
end;
process_iq(_From, #iq{sub_el = SubEl} = IQ, _State) ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
process_iq(_From, reply, _State) ->
not_request;
process_iq(_From, invalid, _State) ->
process_iq(_From, #iq{type = T} = IQ, _State) when T == get; T == set ->
xmpp:make_error(IQ, xmpp:err_not_allowed());
process_iq(_From, #iq{}, _State) ->
not_request.
-spec parse_request(xmlel(), binary())
-> {ok, binary(), pos_integer(), binary()} | {error, xmlel()}.
parse_request(#xmlel{name = <<"request">>, attrs = Attrs} = Request, Lang) ->
case fxml:get_attr(<<"xmlns">>, Attrs) of
{value, XMLNS} when XMLNS == ?NS_HTTP_UPLOAD;
XMLNS == ?NS_HTTP_UPLOAD_OLD ->
case {fxml:get_subtag_cdata(Request, <<"filename">>),
fxml:get_subtag_cdata(Request, <<"size">>),
fxml:get_subtag_cdata(Request, <<"content-type">>)} of
{File, SizeStr, ContentType} when byte_size(File) > 0 ->
case catch jlib:binary_to_integer(SizeStr) of
Size when is_integer(Size), Size > 0 ->
{ok, File, Size, yield_content_type(ContentType)};
_ ->
Text = <<"Please specify file size.">>,
{error, ?ERRT_BAD_REQUEST(Lang, Text)}
end;
_ ->
Text = <<"Please specify file name.">>,
{error, ?ERRT_BAD_REQUEST(Lang, Text)}
end;
_ ->
Text = <<"No or invalid XML namespace">>,
{error, ?ERRT_BAD_REQUEST(Lang, Text)}
end;
parse_request(_El, _Lang) -> {error, ?ERR_BAD_REQUEST}.
-spec create_slot(state(), jid(), binary(), pos_integer(), binary(), binary())
-> {ok, slot()} | {ok, binary(), binary()} | {error, xmlel()}.
@ -624,7 +582,7 @@ create_slot(#state{service_url = undefined, max_size = MaxSize},
" Bytes.">>,
?INFO_MSG("Rejecting file ~s from ~s (too large: ~B bytes)",
[File, jid:to_string(JID), Size]),
{error, ?ERRT_NOT_ACCEPTABLE(Lang, Text)};
{error, xmpp:err_not_acceptable(Text, Lang)};
create_slot(#state{service_url = undefined,
jid_in_url = JIDinURL,
secret_length = SecretLength,
@ -642,8 +600,8 @@ create_slot(#state{service_url = undefined,
[jid:to_string(JID), File]),
{ok, [UserStr, RandStr, FileStr]};
deny ->
{error, ?ERR_SERVICE_UNAVAILABLE};
#xmlel{} = Error ->
{error, xmpp:err_service_unavailable()};
#error{} = Error ->
{error, Error}
end;
create_slot(#state{service_url = ServiceURL},
@ -669,28 +627,28 @@ create_slot(#state{service_url = ServiceURL},
?ERROR_MSG("Can't parse data received for ~s from <~s>: ~p",
[jid:to_string(JID), ServiceURL, Lines]),
Txt = <<"Failed to parse HTTP response">>,
{error, ?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)}
{error, xmpp:err_service_unavailable(Txt, Lang)}
end;
{ok, {402, _Body}} ->
?INFO_MSG("Got status code 402 for ~s from <~s>",
[jid:to_string(JID), ServiceURL]),
{error, ?ERR_RESOURCE_CONSTRAINT};
{error, xmpp:err_resource_constraint()};
{ok, {403, _Body}} ->
?INFO_MSG("Got status code 403 for ~s from <~s>",
[jid:to_string(JID), ServiceURL]),
{error, ?ERR_NOT_ALLOWED};
{error, xmpp:err_not_allowed()};
{ok, {413, _Body}} ->
?INFO_MSG("Got status code 413 for ~s from <~s>",
[jid:to_string(JID), ServiceURL]),
{error, ?ERR_NOT_ACCEPTABLE};
{error, xmpp:err_not_acceptable()};
{ok, {Code, _Body}} ->
?ERROR_MSG("Got unexpected status code for ~s from <~s>: ~B",
[jid:to_string(JID), ServiceURL, Code]),
{error, ?ERR_SERVICE_UNAVAILABLE};
{error, xmpp:err_service_unavailable()};
{error, Reason} ->
?ERROR_MSG("Error requesting upload slot for ~s from <~s>: ~p",
[jid:to_string(JID), ServiceURL, Reason]),
{error, ?ERR_SERVICE_UNAVAILABLE}
{error, xmpp:err_service_unavailable()}
end.
-spec add_slot(slot(), pos_integer(), timer:tref(), state()) -> state().
@ -710,19 +668,12 @@ del_slot(Slot, #state{slots = Slots} = State) ->
NewSlots = maps:remove(Slot, Slots),
State#state{slots = NewSlots}.
-spec slot_el(slot() | binary(), state() | binary(), binary()) -> xmlel().
-spec mk_slot(slot() | binary(), state() | binary(), binary()) -> xmlel().
slot_el(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS) ->
mk_slot(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS) ->
PutURL = str:join([PutPrefix | Slot], <<$/>>),
GetURL = str:join([GetPrefix | Slot], <<$/>>),
slot_el(PutURL, GetURL, XMLNS);
slot_el(PutURL, GetURL, XMLNS) ->
#xmlel{name = <<"slot">>,
attrs = [{<<"xmlns">>, XMLNS}],
children = [#xmlel{name = <<"put">>,
children = [{xmlcdata, PutURL}]},
#xmlel{name = <<"get">>,
children = [{xmlcdata, GetURL}]}]}.
#upload_slot{get = GetURL, put = PutURL, xmlns = XMLNS}.
-spec make_user_string(jid(), sha1 | node) -> binary().
@ -762,44 +713,30 @@ map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
yield_content_type(Type) -> Type.
-spec iq_disco_info(binary(), binary(), binary()) -> [xmlel()].
-spec iq_disco_info(binary(), binary(), binary(), [xdata()]) -> [xmlel()].
iq_disco_info(Host, Lang, Name) ->
iq_disco_info(Host, Lang, Name, AddInfo) ->
Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size,
fun(I) when is_integer(I), I > 0 -> I;
(infinity) -> infinity
end,
104857600) of
infinity ->
[];
AddInfo;
MaxSize ->
MaxSizeStr = jlib:integer_to_binary(MaxSize),
Fields = [#xmlel{name = <<"field">>,
attrs = [{<<"type">>, <<"hidden">>},
{<<"var">>, <<"FORM_TYPE">>}],
children = [#xmlel{name = <<"value">>,
children =
[{xmlcdata,
?NS_HTTP_UPLOAD}]}]},
#xmlel{name = <<"field">>,
attrs = [{<<"var">>, <<"max-file-size">>}],
children = [#xmlel{name = <<"value">>,
children =
[{xmlcdata,
MaxSizeStr}]}]}],
[#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA},
{<<"type">>, <<"result">>}],
children = Fields}]
Fields = [#xdata_field{type = hidden,
var = <<"FORM_TYPE">>,
values = [?NS_HTTP_UPLOAD]},
#xdata_field{var = <<"max-file-size">>,
values = [MaxSizeStr]}],
[#xdata{type = result, fields = Fields}|AddInfo]
end,
[#xmlel{name = <<"identity">>,
attrs = [{<<"category">>, <<"store">>},
{<<"type">>, <<"file">>},
{<<"name">>, translate:translate(Lang, Name)}]},
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, ?NS_HTTP_UPLOAD}]},
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, ?NS_HTTP_UPLOAD_OLD}]} | Form].
#disco_info{identities = [#identity{category = <<"store">>,
type = <<"file">>,
name = translate:translate(Lang, Name)}],
features = [?NS_HTTP_UPLOAD, ?NS_HTTP_UPLOAD_OLD],
xdata = Form}.
%% HTTP request handling.
@ -984,20 +921,14 @@ convert(Path, #media_info{type = T, width = W, height = H}) ->
thumb_el(Path, URI) ->
ContentType = guess_content_type(Path),
xmpp:encode(
case identify(Path) of
{ok, #media_info{height = H, width = W}} ->
#xmlel{name = <<"thumbnail">>,
attrs = [{<<"xmlns">>, ?NS_THUMBS_1},
{<<"media-type">>, ContentType},
{<<"uri">>, URI},
{<<"height">>, jlib:integer_to_binary(H)},
{<<"width">>, jlib:integer_to_binary(W)}]};
#thumbnail{'media-type' = ContentType, uri = URI,
height = H, width = W};
pass ->
#xmlel{name = <<"thumbnail">>,
attrs = [{<<"xmlns">>, ?NS_THUMBS_1},
{<<"uri">>, URI},
{<<"media-type">>, ContentType}]}
end.
#thumbnail{uri = URI, 'media-type' = ContentType}
end).
%%--------------------------------------------------------------------
%% Remove user.

View File

@ -53,7 +53,7 @@
%% ejabberd_hooks callback.
-export([handle_slot_request/5]).
-include("jlib.hrl").
-include("jid.hrl").
-include("logger.hrl").
-include_lib("kernel/include/file.hrl").

View File

@ -15,6 +15,56 @@ decode(_el) -> decode(_el, []).
decode({xmlel, _name, _attrs, _} = _el, Opts) ->
IgnoreEls = proplists:get_bool(ignore_els, Opts),
case {_name, get_attr(<<"xmlns">>, _attrs)} of
{<<"thumbnail">>, <<"urn:xmpp:thumbs:1">>} ->
decode_thumbnail(<<"urn:xmpp:thumbs:1">>, IgnoreEls,
_el);
{<<"slot">>, <<"urn:xmpp:http:upload">>} ->
decode_upload_slot(<<"urn:xmpp:http:upload">>,
IgnoreEls, _el);
{<<"slot">>,
<<"eu:siacs:conversations:http:upload">>} ->
decode_upload_slot(<<"eu:siacs:conversations:http:upload">>,
IgnoreEls, _el);
{<<"put">>, <<"urn:xmpp:http:upload">>} ->
decode_upload_put(<<"urn:xmpp:http:upload">>, IgnoreEls,
_el);
{<<"put">>, <<"eu:siacs:conversations:http:upload">>} ->
decode_upload_put(<<"eu:siacs:conversations:http:upload">>,
IgnoreEls, _el);
{<<"get">>, <<"urn:xmpp:http:upload">>} ->
decode_upload_get(<<"urn:xmpp:http:upload">>, IgnoreEls,
_el);
{<<"get">>, <<"eu:siacs:conversations:http:upload">>} ->
decode_upload_get(<<"eu:siacs:conversations:http:upload">>,
IgnoreEls, _el);
{<<"request">>, <<"urn:xmpp:http:upload">>} ->
decode_upload_request(<<"urn:xmpp:http:upload">>,
IgnoreEls, _el);
{<<"request">>,
<<"eu:siacs:conversations:http:upload">>} ->
decode_upload_request(<<"eu:siacs:conversations:http:upload">>,
IgnoreEls, _el);
{<<"content-type">>, <<"urn:xmpp:http:upload">>} ->
decode_upload_content_type(<<"urn:xmpp:http:upload">>,
IgnoreEls, _el);
{<<"content-type">>,
<<"eu:siacs:conversations:http:upload">>} ->
decode_upload_content_type(<<"eu:siacs:conversations:http:upload">>,
IgnoreEls, _el);
{<<"size">>, <<"urn:xmpp:http:upload">>} ->
decode_upload_size(<<"urn:xmpp:http:upload">>,
IgnoreEls, _el);
{<<"size">>,
<<"eu:siacs:conversations:http:upload">>} ->
decode_upload_size(<<"eu:siacs:conversations:http:upload">>,
IgnoreEls, _el);
{<<"filename">>, <<"urn:xmpp:http:upload">>} ->
decode_upload_filename(<<"urn:xmpp:http:upload">>,
IgnoreEls, _el);
{<<"filename">>,
<<"eu:siacs:conversations:http:upload">>} ->
decode_upload_filename(<<"eu:siacs:conversations:http:upload">>,
IgnoreEls, _el);
{<<"address">>, <<"urn:xmpp:sic:0">>} ->
decode_sic(<<"urn:xmpp:sic:0">>, IgnoreEls, _el);
{<<"address">>, <<"urn:xmpp:sic:1">>} ->
@ -1319,6 +1369,34 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
is_known_tag({xmlel, _name, _attrs, _} = _el) ->
case {_name, get_attr(<<"xmlns">>, _attrs)} of
{<<"thumbnail">>, <<"urn:xmpp:thumbs:1">>} -> true;
{<<"slot">>, <<"urn:xmpp:http:upload">>} -> true;
{<<"slot">>,
<<"eu:siacs:conversations:http:upload">>} ->
true;
{<<"put">>, <<"urn:xmpp:http:upload">>} -> true;
{<<"put">>, <<"eu:siacs:conversations:http:upload">>} ->
true;
{<<"get">>, <<"urn:xmpp:http:upload">>} -> true;
{<<"get">>, <<"eu:siacs:conversations:http:upload">>} ->
true;
{<<"request">>, <<"urn:xmpp:http:upload">>} -> true;
{<<"request">>,
<<"eu:siacs:conversations:http:upload">>} ->
true;
{<<"content-type">>, <<"urn:xmpp:http:upload">>} ->
true;
{<<"content-type">>,
<<"eu:siacs:conversations:http:upload">>} ->
true;
{<<"size">>, <<"urn:xmpp:http:upload">>} -> true;
{<<"size">>,
<<"eu:siacs:conversations:http:upload">>} ->
true;
{<<"filename">>, <<"urn:xmpp:http:upload">>} -> true;
{<<"filename">>,
<<"eu:siacs:conversations:http:upload">>} ->
true;
{<<"address">>, <<"urn:xmpp:sic:0">>} -> true;
{<<"address">>, <<"urn:xmpp:sic:1">>} -> true;
{<<"port">>, <<"urn:xmpp:sic:1">>} -> true;
@ -2626,7 +2704,14 @@ encode({media, _, _, _} = Media) ->
encode({oob_x, _, _, _} = X) ->
encode_oob_x(X, [{<<"xmlns">>, <<"jabber:x:oob">>}]);
encode({sic, _, _, _} = Address) ->
encode_sic(Address, []).
encode_sic(Address, []);
encode({upload_request, _, _, _, _} = Request) ->
encode_upload_request(Request, []);
encode({upload_slot, _, _, _} = Slot) ->
encode_upload_slot(Slot, []);
encode({thumbnail, _, _, _, _} = Thumbnail) ->
encode_thumbnail(Thumbnail,
[{<<"xmlns">>, <<"urn:xmpp:thumbs:1">>}]).
get_name({last, _, _}) -> <<"query">>;
get_name({version, _, _, _}) -> <<"query">>;
@ -2820,7 +2905,10 @@ get_name({xcaptcha, _}) -> <<"captcha">>;
get_name({media_uri, _, _}) -> <<"uri">>;
get_name({media, _, _, _}) -> <<"media">>;
get_name({oob_x, _, _, _}) -> <<"x">>;
get_name({sic, _, _, _}) -> <<"address">>.
get_name({sic, _, _, _}) -> <<"address">>;
get_name({upload_request, _, _, _, _}) -> <<"request">>;
get_name({upload_slot, _, _, _}) -> <<"slot">>;
get_name({thumbnail, _, _, _, _}) -> <<"thumbnail">>.
get_ns({last, _, _}) -> <<"jabber:iq:last">>;
get_ns({version, _, _, _}) -> <<"jabber:iq:version">>;
@ -3089,7 +3177,11 @@ get_ns({media_uri, _, _}) ->
get_ns({media, _, _, _}) ->
<<"urn:xmpp:media-element">>;
get_ns({oob_x, _, _, _}) -> <<"jabber:x:oob">>;
get_ns({sic, _, _, Xmlns}) -> Xmlns.
get_ns({sic, _, _, Xmlns}) -> Xmlns;
get_ns({upload_request, _, _, _, Xmlns}) -> Xmlns;
get_ns({upload_slot, _, _, Xmlns}) -> Xmlns;
get_ns({thumbnail, _, _, _, _}) ->
<<"urn:xmpp:thumbs:1">>.
dec_int(Val) -> dec_int(Val, infinity, infinity).
@ -3339,6 +3431,10 @@ pp(media_uri, 2) -> [type, uri];
pp(media, 3) -> [height, width, uri];
pp(oob_x, 3) -> [url, desc, sid];
pp(sic, 3) -> [ip, port, xmlns];
pp(upload_request, 4) ->
[filename, size, 'content-type', xmlns];
pp(upload_slot, 3) -> [get, put, xmlns];
pp(thumbnail, 4) -> [uri, 'media-type', width, height];
pp(_, _) -> no.
enc_ip({0, 0, 0, 0, 0, 65535, A, B}) ->
@ -3394,6 +3490,533 @@ dec_tzo(Val) ->
M = jlib:binary_to_integer(M1),
if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end.
decode_thumbnail(__TopXMLNS, __IgnoreEls,
{xmlel, <<"thumbnail">>, _attrs, _els}) ->
{Uri, Media_type, Width, Height} =
decode_thumbnail_attrs(__TopXMLNS, _attrs, undefined,
undefined, undefined, undefined),
{thumbnail, Uri, Media_type, Width, Height}.
decode_thumbnail_attrs(__TopXMLNS,
[{<<"uri">>, _val} | _attrs], _Uri, Media_type, Width,
Height) ->
decode_thumbnail_attrs(__TopXMLNS, _attrs, _val,
Media_type, Width, Height);
decode_thumbnail_attrs(__TopXMLNS,
[{<<"media-type">>, _val} | _attrs], Uri, _Media_type,
Width, Height) ->
decode_thumbnail_attrs(__TopXMLNS, _attrs, Uri, _val,
Width, Height);
decode_thumbnail_attrs(__TopXMLNS,
[{<<"width">>, _val} | _attrs], Uri, Media_type, _Width,
Height) ->
decode_thumbnail_attrs(__TopXMLNS, _attrs, Uri,
Media_type, _val, Height);
decode_thumbnail_attrs(__TopXMLNS,
[{<<"height">>, _val} | _attrs], Uri, Media_type, Width,
_Height) ->
decode_thumbnail_attrs(__TopXMLNS, _attrs, Uri,
Media_type, Width, _val);
decode_thumbnail_attrs(__TopXMLNS, [_ | _attrs], Uri,
Media_type, Width, Height) ->
decode_thumbnail_attrs(__TopXMLNS, _attrs, Uri,
Media_type, Width, Height);
decode_thumbnail_attrs(__TopXMLNS, [], Uri, Media_type,
Width, Height) ->
{decode_thumbnail_attr_uri(__TopXMLNS, Uri),
'decode_thumbnail_attr_media-type'(__TopXMLNS,
Media_type),
decode_thumbnail_attr_width(__TopXMLNS, Width),
decode_thumbnail_attr_height(__TopXMLNS, Height)}.
encode_thumbnail({thumbnail, Uri, Media_type, Width,
Height},
_xmlns_attrs) ->
_els = [],
_attrs = encode_thumbnail_attr_height(Height,
encode_thumbnail_attr_width(Width,
'encode_thumbnail_attr_media-type'(Media_type,
encode_thumbnail_attr_uri(Uri,
_xmlns_attrs)))),
{xmlel, <<"thumbnail">>, _attrs, _els}.
decode_thumbnail_attr_uri(__TopXMLNS, undefined) ->
erlang:error({xmpp_codec,
{missing_attr, <<"uri">>, <<"thumbnail">>,
__TopXMLNS}});
decode_thumbnail_attr_uri(__TopXMLNS, _val) -> _val.
encode_thumbnail_attr_uri(_val, _acc) ->
[{<<"uri">>, _val} | _acc].
'decode_thumbnail_attr_media-type'(__TopXMLNS,
undefined) ->
<<>>;
'decode_thumbnail_attr_media-type'(__TopXMLNS, _val) ->
_val.
'encode_thumbnail_attr_media-type'(<<>>, _acc) -> _acc;
'encode_thumbnail_attr_media-type'(_val, _acc) ->
[{<<"media-type">>, _val} | _acc].
decode_thumbnail_attr_width(__TopXMLNS, undefined) ->
undefined;
decode_thumbnail_attr_width(__TopXMLNS, _val) ->
case catch dec_int(_val, 0, infinity) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_attr_value, <<"width">>, <<"thumbnail">>,
__TopXMLNS}});
_res -> _res
end.
encode_thumbnail_attr_width(undefined, _acc) -> _acc;
encode_thumbnail_attr_width(_val, _acc) ->
[{<<"width">>, enc_int(_val)} | _acc].
decode_thumbnail_attr_height(__TopXMLNS, undefined) ->
undefined;
decode_thumbnail_attr_height(__TopXMLNS, _val) ->
case catch dec_int(_val, 0, infinity) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_attr_value, <<"height">>, <<"thumbnail">>,
__TopXMLNS}});
_res -> _res
end.
encode_thumbnail_attr_height(undefined, _acc) -> _acc;
encode_thumbnail_attr_height(_val, _acc) ->
[{<<"height">>, enc_int(_val)} | _acc].
decode_upload_slot(__TopXMLNS, __IgnoreEls,
{xmlel, <<"slot">>, _attrs, _els}) ->
{Put, Get} = decode_upload_slot_els(__TopXMLNS,
__IgnoreEls, _els, undefined,
undefined),
Xmlns = decode_upload_slot_attrs(__TopXMLNS, _attrs,
undefined),
{upload_slot, Get, Put, Xmlns}.
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, [], Put,
Get) ->
{Put, Get};
decode_upload_slot_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"get">>, _attrs, _} = _el | _els], Put,
Get) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"eu:siacs:conversations:http:upload">>;
__TopXMLNS == <<"urn:xmpp:http:upload">> ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
Put,
decode_upload_get(__TopXMLNS, __IgnoreEls,
_el));
<<"urn:xmpp:http:upload">> ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
Put,
decode_upload_get(<<"urn:xmpp:http:upload">>,
__IgnoreEls, _el));
<<"eu:siacs:conversations:http:upload">> ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
Put,
decode_upload_get(<<"eu:siacs:conversations:http:upload">>,
__IgnoreEls, _el));
_ ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
Put, Get)
end;
decode_upload_slot_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"put">>, _attrs, _} = _el | _els], Put,
Get) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"eu:siacs:conversations:http:upload">>;
__TopXMLNS == <<"urn:xmpp:http:upload">> ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
decode_upload_put(__TopXMLNS, __IgnoreEls,
_el),
Get);
<<"urn:xmpp:http:upload">> ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
decode_upload_put(<<"urn:xmpp:http:upload">>,
__IgnoreEls, _el),
Get);
<<"eu:siacs:conversations:http:upload">> ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
decode_upload_put(<<"eu:siacs:conversations:http:upload">>,
__IgnoreEls, _el),
Get);
_ ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
Put, Get)
end;
decode_upload_slot_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Put, Get) ->
decode_upload_slot_els(__TopXMLNS, __IgnoreEls, _els,
Put, Get).
decode_upload_slot_attrs(__TopXMLNS,
[{<<"xmlns">>, _val} | _attrs], _Xmlns) ->
decode_upload_slot_attrs(__TopXMLNS, _attrs, _val);
decode_upload_slot_attrs(__TopXMLNS, [_ | _attrs],
Xmlns) ->
decode_upload_slot_attrs(__TopXMLNS, _attrs, Xmlns);
decode_upload_slot_attrs(__TopXMLNS, [], Xmlns) ->
decode_upload_slot_attr_xmlns(__TopXMLNS, Xmlns).
encode_upload_slot({upload_slot, Get, Put, Xmlns},
_xmlns_attrs) ->
_els = lists:reverse('encode_upload_slot_$put'(Put,
'encode_upload_slot_$get'(Get,
[]))),
_attrs = encode_upload_slot_attr_xmlns(Xmlns,
_xmlns_attrs),
{xmlel, <<"slot">>, _attrs, _els}.
'encode_upload_slot_$put'(undefined, _acc) -> _acc;
'encode_upload_slot_$put'(Put, _acc) ->
[encode_upload_put(Put, []) | _acc].
'encode_upload_slot_$get'(undefined, _acc) -> _acc;
'encode_upload_slot_$get'(Get, _acc) ->
[encode_upload_get(Get, []) | _acc].
decode_upload_slot_attr_xmlns(__TopXMLNS, undefined) ->
undefined;
decode_upload_slot_attr_xmlns(__TopXMLNS, _val) -> _val.
encode_upload_slot_attr_xmlns(undefined, _acc) -> _acc;
encode_upload_slot_attr_xmlns(_val, _acc) ->
[{<<"xmlns">>, _val} | _acc].
decode_upload_put(__TopXMLNS, __IgnoreEls,
{xmlel, <<"put">>, _attrs, _els}) ->
Cdata = decode_upload_put_els(__TopXMLNS, __IgnoreEls,
_els, <<>>),
Cdata.
decode_upload_put_els(__TopXMLNS, __IgnoreEls, [],
Cdata) ->
decode_upload_put_cdata(__TopXMLNS, Cdata);
decode_upload_put_els(__TopXMLNS, __IgnoreEls,
[{xmlcdata, _data} | _els], Cdata) ->
decode_upload_put_els(__TopXMLNS, __IgnoreEls, _els,
<<Cdata/binary, _data/binary>>);
decode_upload_put_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Cdata) ->
decode_upload_put_els(__TopXMLNS, __IgnoreEls, _els,
Cdata).
encode_upload_put(Cdata, _xmlns_attrs) ->
_els = encode_upload_put_cdata(Cdata, []),
_attrs = _xmlns_attrs,
{xmlel, <<"put">>, _attrs, _els}.
decode_upload_put_cdata(__TopXMLNS, <<>>) ->
erlang:error({xmpp_codec,
{missing_cdata, <<>>, <<"put">>, __TopXMLNS}});
decode_upload_put_cdata(__TopXMLNS, _val) -> _val.
encode_upload_put_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
decode_upload_get(__TopXMLNS, __IgnoreEls,
{xmlel, <<"get">>, _attrs, _els}) ->
Cdata = decode_upload_get_els(__TopXMLNS, __IgnoreEls,
_els, <<>>),
Cdata.
decode_upload_get_els(__TopXMLNS, __IgnoreEls, [],
Cdata) ->
decode_upload_get_cdata(__TopXMLNS, Cdata);
decode_upload_get_els(__TopXMLNS, __IgnoreEls,
[{xmlcdata, _data} | _els], Cdata) ->
decode_upload_get_els(__TopXMLNS, __IgnoreEls, _els,
<<Cdata/binary, _data/binary>>);
decode_upload_get_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Cdata) ->
decode_upload_get_els(__TopXMLNS, __IgnoreEls, _els,
Cdata).
encode_upload_get(Cdata, _xmlns_attrs) ->
_els = encode_upload_get_cdata(Cdata, []),
_attrs = _xmlns_attrs,
{xmlel, <<"get">>, _attrs, _els}.
decode_upload_get_cdata(__TopXMLNS, <<>>) ->
erlang:error({xmpp_codec,
{missing_cdata, <<>>, <<"get">>, __TopXMLNS}});
decode_upload_get_cdata(__TopXMLNS, _val) -> _val.
encode_upload_get_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
decode_upload_request(__TopXMLNS, __IgnoreEls,
{xmlel, <<"request">>, _attrs, _els}) ->
{Content_type, Size, Filename} =
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
<<>>, error, error),
Xmlns = decode_upload_request_attrs(__TopXMLNS, _attrs,
undefined),
{upload_request, Filename, Size, Content_type, Xmlns}.
decode_upload_request_els(__TopXMLNS, __IgnoreEls, [],
Content_type, Size, Filename) ->
{Content_type,
case Size of
error ->
erlang:error({xmpp_codec,
{missing_tag, <<"size">>, __TopXMLNS}});
{value, Size1} -> Size1
end,
case Filename of
error ->
erlang:error({xmpp_codec,
{missing_tag, <<"filename">>, __TopXMLNS}});
{value, Filename1} -> Filename1
end};
decode_upload_request_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"filename">>, _attrs, _} = _el | _els],
Content_type, Size, Filename) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"eu:siacs:conversations:http:upload">>;
__TopXMLNS == <<"urn:xmpp:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type, Size,
{value,
decode_upload_filename(__TopXMLNS,
__IgnoreEls, _el)});
<<"urn:xmpp:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type, Size,
{value,
decode_upload_filename(<<"urn:xmpp:http:upload">>,
__IgnoreEls, _el)});
<<"eu:siacs:conversations:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type, Size,
{value,
decode_upload_filename(<<"eu:siacs:conversations:http:upload">>,
__IgnoreEls, _el)});
_ ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type, Size, Filename)
end;
decode_upload_request_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"size">>, _attrs, _} = _el | _els],
Content_type, Size, Filename) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"eu:siacs:conversations:http:upload">>;
__TopXMLNS == <<"urn:xmpp:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type,
{value,
decode_upload_size(__TopXMLNS, __IgnoreEls,
_el)},
Filename);
<<"urn:xmpp:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type,
{value,
decode_upload_size(<<"urn:xmpp:http:upload">>,
__IgnoreEls, _el)},
Filename);
<<"eu:siacs:conversations:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type,
{value,
decode_upload_size(<<"eu:siacs:conversations:http:upload">>,
__IgnoreEls, _el)},
Filename);
_ ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type, Size, Filename)
end;
decode_upload_request_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"content-type">>, _attrs, _} = _el | _els],
Content_type, Size, Filename) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">>
when __TopXMLNS ==
<<"eu:siacs:conversations:http:upload">>;
__TopXMLNS == <<"urn:xmpp:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
decode_upload_content_type(__TopXMLNS,
__IgnoreEls,
_el),
Size, Filename);
<<"urn:xmpp:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
decode_upload_content_type(<<"urn:xmpp:http:upload">>,
__IgnoreEls,
_el),
Size, Filename);
<<"eu:siacs:conversations:http:upload">> ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
decode_upload_content_type(<<"eu:siacs:conversations:http:upload">>,
__IgnoreEls,
_el),
Size, Filename);
_ ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type, Size, Filename)
end;
decode_upload_request_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Content_type, Size, Filename) ->
decode_upload_request_els(__TopXMLNS, __IgnoreEls, _els,
Content_type, Size, Filename).
decode_upload_request_attrs(__TopXMLNS,
[{<<"xmlns">>, _val} | _attrs], _Xmlns) ->
decode_upload_request_attrs(__TopXMLNS, _attrs, _val);
decode_upload_request_attrs(__TopXMLNS, [_ | _attrs],
Xmlns) ->
decode_upload_request_attrs(__TopXMLNS, _attrs, Xmlns);
decode_upload_request_attrs(__TopXMLNS, [], Xmlns) ->
decode_upload_request_attr_xmlns(__TopXMLNS, Xmlns).
encode_upload_request({upload_request, Filename, Size,
Content_type, Xmlns},
_xmlns_attrs) ->
_els =
lists:reverse('encode_upload_request_$content-type'(Content_type,
'encode_upload_request_$size'(Size,
'encode_upload_request_$filename'(Filename,
[])))),
_attrs = encode_upload_request_attr_xmlns(Xmlns,
_xmlns_attrs),
{xmlel, <<"request">>, _attrs, _els}.
'encode_upload_request_$content-type'(<<>>, _acc) ->
_acc;
'encode_upload_request_$content-type'(Content_type,
_acc) ->
[encode_upload_content_type(Content_type, []) | _acc].
'encode_upload_request_$size'(Size, _acc) ->
[encode_upload_size(Size, []) | _acc].
'encode_upload_request_$filename'(Filename, _acc) ->
[encode_upload_filename(Filename, []) | _acc].
decode_upload_request_attr_xmlns(__TopXMLNS,
undefined) ->
undefined;
decode_upload_request_attr_xmlns(__TopXMLNS, _val) ->
_val.
encode_upload_request_attr_xmlns(undefined, _acc) ->
_acc;
encode_upload_request_attr_xmlns(_val, _acc) ->
[{<<"xmlns">>, _val} | _acc].
decode_upload_content_type(__TopXMLNS, __IgnoreEls,
{xmlel, <<"content-type">>, _attrs, _els}) ->
Cdata = decode_upload_content_type_els(__TopXMLNS,
__IgnoreEls, _els, <<>>),
Cdata.
decode_upload_content_type_els(__TopXMLNS, __IgnoreEls,
[], Cdata) ->
decode_upload_content_type_cdata(__TopXMLNS, Cdata);
decode_upload_content_type_els(__TopXMLNS, __IgnoreEls,
[{xmlcdata, _data} | _els], Cdata) ->
decode_upload_content_type_els(__TopXMLNS, __IgnoreEls,
_els, <<Cdata/binary, _data/binary>>);
decode_upload_content_type_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Cdata) ->
decode_upload_content_type_els(__TopXMLNS, __IgnoreEls,
_els, Cdata).
encode_upload_content_type(Cdata, _xmlns_attrs) ->
_els = encode_upload_content_type_cdata(Cdata, []),
_attrs = _xmlns_attrs,
{xmlel, <<"content-type">>, _attrs, _els}.
decode_upload_content_type_cdata(__TopXMLNS, <<>>) ->
<<>>;
decode_upload_content_type_cdata(__TopXMLNS, _val) ->
_val.
encode_upload_content_type_cdata(<<>>, _acc) -> _acc;
encode_upload_content_type_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
decode_upload_size(__TopXMLNS, __IgnoreEls,
{xmlel, <<"size">>, _attrs, _els}) ->
Cdata = decode_upload_size_els(__TopXMLNS, __IgnoreEls,
_els, <<>>),
Cdata.
decode_upload_size_els(__TopXMLNS, __IgnoreEls, [],
Cdata) ->
decode_upload_size_cdata(__TopXMLNS, Cdata);
decode_upload_size_els(__TopXMLNS, __IgnoreEls,
[{xmlcdata, _data} | _els], Cdata) ->
decode_upload_size_els(__TopXMLNS, __IgnoreEls, _els,
<<Cdata/binary, _data/binary>>);
decode_upload_size_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Cdata) ->
decode_upload_size_els(__TopXMLNS, __IgnoreEls, _els,
Cdata).
encode_upload_size(Cdata, _xmlns_attrs) ->
_els = encode_upload_size_cdata(Cdata, []),
_attrs = _xmlns_attrs,
{xmlel, <<"size">>, _attrs, _els}.
decode_upload_size_cdata(__TopXMLNS, <<>>) ->
erlang:error({xmpp_codec,
{missing_cdata, <<>>, <<"size">>, __TopXMLNS}});
decode_upload_size_cdata(__TopXMLNS, _val) ->
case catch dec_int(_val, 0, infinity) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_cdata_value, <<>>, <<"size">>, __TopXMLNS}});
_res -> _res
end.
encode_upload_size_cdata(_val, _acc) ->
[{xmlcdata, enc_int(_val)} | _acc].
decode_upload_filename(__TopXMLNS, __IgnoreEls,
{xmlel, <<"filename">>, _attrs, _els}) ->
Cdata = decode_upload_filename_els(__TopXMLNS,
__IgnoreEls, _els, <<>>),
Cdata.
decode_upload_filename_els(__TopXMLNS, __IgnoreEls, [],
Cdata) ->
decode_upload_filename_cdata(__TopXMLNS, Cdata);
decode_upload_filename_els(__TopXMLNS, __IgnoreEls,
[{xmlcdata, _data} | _els], Cdata) ->
decode_upload_filename_els(__TopXMLNS, __IgnoreEls,
_els, <<Cdata/binary, _data/binary>>);
decode_upload_filename_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Cdata) ->
decode_upload_filename_els(__TopXMLNS, __IgnoreEls,
_els, Cdata).
encode_upload_filename(Cdata, _xmlns_attrs) ->
_els = encode_upload_filename_cdata(Cdata, []),
_attrs = _xmlns_attrs,
{xmlel, <<"filename">>, _attrs, _els}.
decode_upload_filename_cdata(__TopXMLNS, <<>>) ->
erlang:error({xmpp_codec,
{missing_cdata, <<>>, <<"filename">>, __TopXMLNS}});
decode_upload_filename_cdata(__TopXMLNS, _val) -> _val.
encode_upload_filename_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
decode_sic(__TopXMLNS, __IgnoreEls,
{xmlel, <<"address">>, _attrs, _els}) ->
{Ip, Port} = decode_sic_els(__TopXMLNS, __IgnoreEls,

View File

@ -3021,6 +3021,78 @@
refs = [#ref{name = sic_ip, min = 0, max = 1, label = '$ip'},
#ref{name = sip_port, min = 0, max = 1, label = '$port'}]}).
-xml(upload_filename,
#elem{name = <<"filename">>,
xmlns = [<<"urn:xmpp:http:upload">>,
<<"eu:siacs:conversations:http:upload">>],
result = '$cdata',
cdata = #cdata{required = true}}).
-xml(upload_size,
#elem{name = <<"size">>,
xmlns = [<<"urn:xmpp:http:upload">>,
<<"eu:siacs:conversations:http:upload">>],
result = '$cdata',
cdata = #cdata{required = true,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}}}).
-xml(upload_content_type,
#elem{name = <<"content-type">>,
xmlns = [<<"urn:xmpp:http:upload">>,
<<"eu:siacs:conversations:http:upload">>],
result = '$cdata',
cdata = #cdata{default = <<"">>}}).
-xml(upload_request,
#elem{name = <<"request">>,
xmlns = [<<"urn:xmpp:http:upload">>,
<<"eu:siacs:conversations:http:upload">>],
result = {upload_request, '$filename', '$size',
'$content-type', '$xmlns'},
attrs = [#attr{name = <<"xmlns">>}],
refs = [#ref{name = upload_filename, label = '$filename',
min = 1, max = 1},
#ref{name = upload_size, label = '$size', min = 1, max = 1},
#ref{name = upload_content_type, label = '$content-type',
min = 0, max = 1, default = <<"">>}]}).
-xml(upload_get,
#elem{name = <<"get">>,
xmlns = [<<"urn:xmpp:http:upload">>,
<<"eu:siacs:conversations:http:upload">>],
result = '$cdata',
cdata = #cdata{required = true}}).
-xml(upload_put,
#elem{name = <<"put">>,
xmlns = [<<"urn:xmpp:http:upload">>,
<<"eu:siacs:conversations:http:upload">>],
result = '$cdata',
cdata = #cdata{required = true}}).
-xml(upload_slot,
#elem{name = <<"slot">>,
xmlns = [<<"urn:xmpp:http:upload">>,
<<"eu:siacs:conversations:http:upload">>],
result = {upload_slot, '$get', '$put', '$xmlns'},
attrs = [#attr{name = <<"xmlns">>}],
refs = [#ref{name = upload_get, min = 0, max = 1, label = '$get'},
#ref{name = upload_put, min = 0, max = 1, label = '$put'}]}).
-xml(thumbnail,
#elem{name = <<"thumbnail">>,
xmlns = <<"urn:xmpp:thumbs:1">>,
result = {thumbnail, '$uri', '$media-type', '$width', '$height'},
attrs = [#attr{name = <<"uri">>, required = true},
#attr{name = <<"media-type">>, default = <<"">>},
#attr{name = <<"width">>,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
#attr{name = <<"height">>,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}}]}).
dec_tzo(Val) ->
[H1, M1] = str:tokens(Val, <<":">>),
H = jlib:binary_to_integer(H1),