]> granicus.if.org Git - ejabberd/commitdiff
Rewrite mod_http_upload to use XML generator
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Sat, 30 Jul 2016 14:48:52 +0000 (17:48 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Sat, 30 Jul 2016 14:48:52 +0000 (17:48 +0300)
include/xmpp_codec.hrl
src/mod_http_upload.erl
src/mod_http_upload_quota.erl
src/xmpp_codec.erl
tools/xmpp_codec.spec

index 88c94a76b5d34f55d5a2b61658a9a6bcf8efc474..caabb101d911b706d4d43b19bfae94a9f05a389e 100644 (file)
                       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{}.
                          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{}.
 -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{}.
                         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() |
                         feature_csi() |
                         privacy_query() |
                         delay() |
+                        thumbnail() |
                         vcard_tel() |
                         vcard_geo() |
                         vcard_photo() |
                         mam_result() |
                         rsm_first() |
                         stat() |
+                        upload_request() |
                         xdata_field() |
                         adhoc_command() |
                         sm_failed() |
index b166f2b6644af504c86a3b0526c1adee55977c73..63dc2dfe296d3db2ef176400b95f0f353fbf4ffe 100644 (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
-                       {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.
index 35e266ddf2cc86c35abec46e312ee120e0cd9c4d..056edbedb12f728efbfebec6424fb844c3fca0f6 100644 (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").
 
index 11bd741f41dbaf4a5159c2e8e58f3496dff91ae5..8d87712dfa307628da9776d9544a188522b2539b 100644 (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,
index 9cb14282ca1c7aa1c699a44c9386bb38d5f0b106..293f12d17417cef71a6ee227aea8258fdb2b94e4 100644 (file)
           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),