diff --git a/include/xmpp_codec.hrl b/include/xmpp_codec.hrl index 88c94a76b..caabb101d 100644 --- a/include/xmpp_codec.hrl +++ b/include/xmpp_codec.hrl @@ -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() | diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index b166f2b66..63dc2dfe2 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -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 - {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}; - {ok, PutURL, GetURL} -> - SlotEl = slot_el(PutURL, GetURL, XMLNS), - IQ#iq{type = result, sub_el = [SlotEl]}; - {error, Error} -> - IQ#iq{type = error, sub_el = [SubEl, Error]} - end; + 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), + Slot = mk_slot(Slot, State, XMLNS), + {xmpp:make_iq_result(IQ, Slot), NewState}; + {ok, PutURL, GetURL} -> + Slot = mk_slot(PutURL, GetURL, XMLNS), + xmpp:make_iq_result(IQ, Slot); {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), - 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)}]}; - pass -> - #xmlel{name = <<"thumbnail">>, - attrs = [{<<"xmlns">>, ?NS_THUMBS_1}, - {<<"uri">>, URI}, - {<<"media-type">>, ContentType}]} - end. + xmpp:encode( + case identify(Path) of + {ok, #media_info{height = H, width = W}} -> + #thumbnail{'media-type' = ContentType, uri = URI, + height = H, width = W}; + pass -> + #thumbnail{uri = URI, 'media-type' = ContentType} + end). %%-------------------------------------------------------------------- %% Remove user. diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl index 35e266ddf..056edbedb 100644 --- a/src/mod_http_upload_quota.erl +++ b/src/mod_http_upload_quota.erl @@ -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"). diff --git a/src/xmpp_codec.erl b/src/xmpp_codec.erl index 11bd741f4..8d87712df 100644 --- a/src/xmpp_codec.erl +++ b/src/xmpp_codec.erl @@ -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, + <>); +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, + <>); +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, <>); +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, + <>); +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, <>); +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, diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec index 9cb14282c..293f12d17 100644 --- a/tools/xmpp_codec.spec +++ b/tools/xmpp_codec.spec @@ -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),