Index: src/ejabberd.cfg.example =================================================================== --- src/ejabberd.cfg.example (Revision 565) +++ src/ejabberd.cfg.example (Arbeitskopie) @@ -124,7 +124,7 @@ {5269, ejabberd_s2s_in, [{shaper, s2s_shaper}, {max_stanza_size, 131072} ]}, - {5280, ejabberd_http, [http_poll, web_admin]}, + {5280, ejabberd_http, [http_poll, http_bind, web_admin]}, {8888, ejabberd_service, [{access, all}, {hosts, ["icq.localhost", "sms.localhost"], [{password, "secret"}]}]} Index: src/web/Makefile.in =================================================================== --- src/web/Makefile.in (Revision 565) +++ src/web/Makefile.in (Arbeitskopie) @@ -15,7 +15,8 @@ $(OUTDIR)/ejabberd_http.beam \ $(OUTDIR)/ejabberd_web.beam \ $(OUTDIR)/ejabberd_web_admin.beam \ - $(OUTDIR)/ejabberd_http_poll.beam + $(OUTDIR)/ejabberd_http_poll.beam \ + $(OUTDIR)/ejabberd_http_bind.beam all: $(OBJS) Index: src/web/ejabberd_http_bind.erl =================================================================== --- src/web/ejabberd_http_bind.erl (Revision 0) +++ src/web/ejabberd_http_bind.erl (Revision 0) @@ -0,0 +1,731 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_http_bind.erl +%%% Author : Stefan Strigler +%%% Purpose : HTTP Binding support (JEP-0124) +%%% Created : 21 Sep 2005 by Stefan Strigler +%%% Id : $Id: ejabberd_http_bind.erl,v 1.2 2006/01/16 10:47:50 zeank Exp $ +%%%---------------------------------------------------------------------- + +-module(ejabberd_http_bind). +-author('steve@zeank.in-berlin.de'). +-vsn('Revision: 1.3'). + +-behaviour(gen_fsm). + +%% External exports +-export([start_link/2, + init/1, + handle_event/3, + handle_sync_event/4, + code_change/4, + handle_info/3, + terminate/3, + send/2, + setopts/2, + controlling_process/2, + close/1, + process_request/1]). + +%%-define(ejabberd_debug, true). + +-include("ejabberd.hrl"). +-include("jlib.hrl"). +-include("ejabberd_http.hrl"). + +-record(http_bind, {id, pid, hold, wait}). + +%% http binding request +-record(hbr, {rid, + key, + in, + out}). + +-record(state, {id, + rid = error, + key, + output = "", + input = "", + waiting_input = false, + last_receiver, + last_poll, + ctime = 0, + timer, + req_list = [] % list of requests + }). + + +%-define(DBGFSM, true). + +-ifdef(DBGFSM). +-define(FSMOPTS, [{debug, [trace]}]). +-else. +-define(FSMOPTS, []). +-endif. + +-define(MAX_REQUESTS, 2). % number of simultaneous requests +-define(MIN_POLLING, "2"). % don't poll faster than that or we will shoot you +-define(MAX_WAIT, 60). % max num of secs to keep a request on hold +-define(MAX_INACTIVITY, 30000). % msecs to wait before terminating idle sessions +-define(CT, {"Content-Type", "text/xml; charset=utf-8"}). +-define(BAD_REQUEST, ?CT). + + +%%%---------------------------------------------------------------------- +%%% API +%%%---------------------------------------------------------------------- +start(ID, Key) -> + mnesia:create_table(http_bind, + [{ram_copies, [node()]}, + {attributes, record_info(fields, http_bind)}]), + supervisor:start_child(ejabberd_http_bind_sup, [ID, Key]). + +start_link(ID, Key) -> + gen_fsm:start_link(?MODULE, [ID, Key], ?FSMOPTS). + +send({http_bind, FsmRef}, Packet) -> + gen_fsm:sync_send_all_state_event(FsmRef, {send, Packet}). + +setopts({http_bind, FsmRef}, Opts) -> + case lists:member({active, once}, Opts) of + true -> + gen_fsm:sync_send_all_state_event(FsmRef, activate); + _ -> + ok + end. + +controlling_process(_Socket, _Pid) -> + ok. + +close({http_bind, FsmRef}) -> + catch gen_fsm:sync_send_all_state_event(FsmRef, close). + + +process_request(#request{path = [], + data = Data}) -> + case catch parse_request(Data) of + {ok, ID1, RID, Key, NewKey, Attrs, Packet} -> + XmppDomain = xml:get_attr_s("to",Attrs), + if + (ID1 == "") and (XmppDomain == "") -> + {200, [?CT], ""}; + true -> + ID = if + (ID1 == "") -> + %% create new session + NewID = sha:sha(term_to_binary({now(), make_ref()})), + {ok, Pid} = start(NewID, Key), + Wait = case + string:to_integer(xml:get_attr_s("wait",Attrs)) + of + {error, _} -> + ?MAX_WAIT; + {CWait, _} -> + if + (CWait > ?MAX_WAIT) -> + ?MAX_WAIT; + true -> + CWait + end + end, + Hold = case + string:to_integer( + xml:get_attr_s("hold",Attrs)) + of + {error, _} -> + (?MAX_REQUESTS - 1); + {CHold, _} -> + if + (CHold > (?MAX_REQUESTS - 1)) -> + (?MAX_REQUESTS - 1); + true -> + CHold + end + end, + mnesia:transaction( + fun() -> + mnesia:write(#http_bind{id = NewID, + pid = Pid, + wait = Wait, + hold = Hold}) + end), + InPacket = if + (XmppDomain /= "") -> + [""]; + true -> + "" + end, + NewID; + true -> + %% old session + Type = xml:get_attr_s("type",Attrs), + Wait = ?MAX_WAIT, + Hold = (?MAX_REQUESTS - 1), + if + (Type == "terminate") -> + %% terminate session + InPacket = Packet ++ ""; + true -> + InPacket = Packet + end, + ID1 + end, +%% ?DEBUG("~n InPacket: ~s ~n", [InPacket]), + case http_put(ID, RID, Key, NewKey, Hold, InPacket) of + {error, not_exists} -> + ?DEBUG("no session associated with sid: ~p", [ID]), + {404, [?BAD_REQUEST], ""}; + {error, bad_key} -> + ?DEBUG("bad key: ~s", Key), + case mnesia:dirty_read({http_bind, ID}) of + [] -> + {404, [?BAD_REQUEST], ""}; + [#http_bind{pid = FsmRef}] -> + gen_fsm:sync_send_all_state_event(FsmRef,stop), + {404, [?BAD_REQUEST], ""} + end; + {error, polling_too_frequently} -> + ?DEBUG("polling too frequently: ~p", [ID]), + case mnesia:dirty_read({http_bind, ID}) of + [] -> %% unlikely! (?) + {404, [?BAD_REQUEST], ""}; + [#http_bind{pid = FsmRef}] -> + gen_fsm:sync_send_all_state_event(FsmRef,stop), + {403, [?BAD_REQUEST], ""} + end; + {repeat, OutPacket} -> + ?DEBUG("http_put said 'repeat!' ...~nOutPacket: ~p", + [OutPacket]), + send_outpacket(ID, OutPacket); + ok -> + receive_loop(ID,ID1,RID,Wait,Hold,Attrs) + end + end; + _ -> + {400, [?BAD_REQUEST], ""} + end; +process_request(_Request) -> + {400, [], {xmlelement, "h1", [], + [{xmlcdata, "400 Bad Request"}]}}. + +receive_loop(ID,ID1,RID,Wait,Hold,Attrs) -> + receive + after 100 -> ok + end, + prepare_response(ID,ID1,RID,Wait,Hold,Attrs). + +prepare_response(ID,ID1,RID,Wait,Hold,Attrs) -> + case http_get(ID,RID) of + {error, not_exists} -> + ?DEBUG("no session associated with sid: ~s", ID), + {404, [?BAD_REQUEST], ""}; + {ok, keep_on_hold} -> + receive_loop(ID,ID1,RID,Wait,Hold,Attrs); + {ok, cancel} -> + %% actually it would be better if we could completely + %% cancel this request, but then we would have to hack + %% ejabberd_http and I'm too lazy now + {404, [?BAD_REQUEST], ""}; + {ok, OutPacket} -> + ?DEBUG("OutPacket: ~s", [OutPacket]), + if + ID == ID1 -> + send_outpacket(ID, OutPacket); + true -> + To = xml:get_attr_s("to",Attrs), + case xml_stream:parse_element( + OutPacket++"") of + El when element(1, El) == xmlelement -> + {xmlelement, _, OutAttrs, _} = El, + AuthID = xml:get_attr_s("id", OutAttrs), + StreamError = false; + {error, _} -> + AuthID = "", + StreamError = true + end, + if + To == "" -> + {200, [?CT], ""}; + StreamError == true -> + {200, [?CT], ""}; + true -> + {200, [?CT], + xml:element_to_string( + {xmlelement,"body", + [{"xmlns", + "http://jabber.org/protocol/httpbind"}, + {"sid",ID}, + {"wait", integer_to_list(Wait)}, + {"requests", integer_to_list(Hold+1)}, + {"inactivity", + integer_to_list(trunc(?MAX_INACTIVITY/1000))}, + {"polling", ?MIN_POLLING}, + {"authid", AuthID} + ],[]})} + end + end + end. + +send_outpacket(ID, OutPacket) -> + case OutPacket of + "" -> + {200, [?CT], ""}; + "" -> + case mnesia:dirty_read({http_bind, ID}) of + [#http_bind{pid = FsmRef}] -> + gen_fsm:sync_send_all_state_event(FsmRef,stop) + end, + {200, [?CT], ""}; + _ -> + case xml_stream:parse_element("" + ++ OutPacket + ++ "") + of + El when element(1, El) == xmlelement -> + {xmlelement, _, _, OEls} = El, + TypedEls = [xml:replace_tag_attr("xmlns", + "jabber:client",OEl) || + OEl <- OEls], + ?DEBUG(" --- outgoing data --- ~n~s~n --- END --- ~n", + [xml:element_to_string( + {xmlelement,"body", + [{"xmlns", + "http://jabber.org/protocol/httpbind"}], + TypedEls})] + ), + {200, [?CT], + xml:element_to_string( + {xmlelement,"body", + [{"xmlns", + "http://jabber.org/protocol/httpbind"}], + TypedEls})}; + {error, _E} -> + ?DEBUG("parse error: ~p", [_E]), + StreamErrCond = case xml_stream:parse_element( + ""++OutPacket) of + El when element(1, El) == xmlelement -> + {xmlelement, _Tag, _Attr, Els} = El, + [{xmlelement, SE, _, Cond} | _] = Els, + if + SE == "stream:error" -> + Cond; + true -> + null + end; + {error, _E} -> + null + end, + case mnesia:dirty_read({http_bind, ID}) of + [#http_bind{pid = FsmRef}] -> + gen_fsm:sync_send_all_state_event(FsmRef,stop); + _ -> + err %% hu? + end, + case StreamErrCond of + null -> + {200, [?CT], + ""}; + _ -> + {200, [?CT], + "" ++ + elements_to_string(StreamErrCond) ++ + ""} + end + end + end. + +%%%---------------------------------------------------------------------- +%%% Callback functions from gen_fsm +%%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +%% Func: init/1 +%% Returns: {ok, StateName, StateData} | +%% {ok, StateName, StateData, Timeout} | +%% ignore | +%% {stop, StopReason} +%%---------------------------------------------------------------------- +init([ID, Key]) -> + ?INFO_MSG("started: ~p", [{ID, Key}]), + Opts = [], % TODO + {ok, C2SPid} = ejabberd_c2s:start({?MODULE, {http_bind, self()}}, Opts), + ejabberd_c2s:become_controller(C2SPid), + Timer = erlang:start_timer(?MAX_INACTIVITY, self(), []), + {ok, loop, #state{id = ID, + key = Key, + timer = Timer}}. + +%%---------------------------------------------------------------------- +%% Func: StateName/2 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- + + +%%---------------------------------------------------------------------- +%% Func: StateName/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {reply, Reply, NextStateName, NextStateData} | +%% {reply, Reply, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} | +%% {stop, Reason, Reply, NewStateData} +%%---------------------------------------------------------------------- +%state_name(Event, From, StateData) -> +% Reply = ok, +% {reply, Reply, state_name, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_event/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- +handle_event(_Event, StateName, StateData) -> + {next_state, StateName, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_sync_event/4 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {reply, Reply, NextStateName, NextStateData} | +%% {reply, Reply, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} | +%% {stop, Reason, Reply, NewStateData} +%%---------------------------------------------------------------------- +handle_sync_event({send, Packet}, _From, StateName, StateData) -> + Output = [StateData#state.output | Packet], + Reply = ok, + {reply, Reply, StateName, StateData#state{output = Output}}; + +handle_sync_event(activate, From, StateName, StateData) -> + case StateData#state.input of + "" -> + {reply, ok, StateName, StateData#state{ + waiting_input = From}}; + Input -> + From ! {tcp, {http_bind, self()}, list_to_binary(Input)}, + {reply, ok, StateName, StateData#state{ + input = "", + waiting_input = false, + last_receiver = From}} + end; + +handle_sync_event(stop, _From, _StateName, StateData) -> + Reply = ok, + {stop, normal, Reply, StateData}; + +handle_sync_event({http_put, RID, Key, NewKey, Hold, Packet}, + _From, StateName, StateData) -> + %% check if RID valid + RidAllow = case RID of + error -> + false; + _ -> + case StateData#state.rid of + error -> + %% first request - nothing saved so far + true; + OldRID -> + ?DEBUG("state.rid/cur rid: ~p/~p", + [OldRID, RID]), + if + (OldRID < RID) and + (RID =< (OldRID + Hold + 1)) -> + true; + (RID =< OldRID) and + (RID > OldRID - Hold - 1) -> + repeat; + true -> + false + end + end + end, + %% check if key valid + KeyAllow = case RidAllow of + repeat -> + true; + false -> + false; + true -> + case StateData#state.key of + "" -> + true; + OldKey -> + NextKey = httpd_util:to_lower( + hex(binary_to_list( + crypto:sha(Key)))), + ?DEBUG("Key/OldKey/NextKey: ~s/~s/~s", + [Key, OldKey, NextKey]), + if + OldKey == NextKey -> + true; + true -> + ?DEBUG("wrong key: ~s",[Key]), + false + end + end + end, + {_,TSec,TMSec} = now(), + TNow = TSec*1000*1000 + TMSec, + LastPoll = if + Packet == "" -> + TNow; + true -> + 0 + end, + {MinPoll, _} = string:to_integer(?MIN_POLLING), + if + (Packet == "") and + (TNow - StateData#state.last_poll < MinPoll*1000*1000) -> + Reply = {error, polling_too_frequently}, + {reply, Reply, StateName, StateData}; + KeyAllow -> + case RidAllow of + false -> + Reply = {error, not_exists}, + {reply, Reply, StateName, StateData}; + repeat -> + ?DEBUG("REPEATING ~p", [RID]), + [Out | _XS] = [El#hbr.out || + El <- StateData#state.req_list, + El#hbr.rid == RID], + case Out of + [[] | OutPacket] -> + Reply = {repeat, OutPacket}; + _ -> + Reply = {repeat, Out} + end, + {reply, Reply, StateName, + StateData#state{input = "cancel", last_poll = LastPoll}}; + true -> + SaveKey = if + NewKey == "" -> + Key; + true -> + NewKey + end, + ?DEBUG(" -- SaveKey: ~s~n", [SaveKey]), + + %% save request + ReqList = [#hbr{rid=RID, + key=StateData#state.key, + in=StateData#state.input, + out=StateData#state.output + } | + [El || El <- StateData#state.req_list, + El#hbr.rid < RID, + El#hbr.rid > (RID - 1 - Hold)] + ], +%% ?DEBUG("reqlist: ~p", [ReqList]), + case StateData#state.waiting_input of + false -> + cancel_timer(StateData#state.timer), + Timer = erlang:start_timer( + ?MAX_INACTIVITY, self(), []), + Input = Packet ++ [StateData#state.input], + Reply = ok, + {reply, Reply, StateName, + StateData#state{input = Input, + rid = RID, + key = SaveKey, + ctime = TNow, + timer = Timer, + last_poll = LastPoll, + req_list = ReqList + }}; + {Receiver, _Tag} -> + Receiver ! {tcp, {http_bind, self()}, + list_to_binary(Packet)}, + cancel_timer(StateData#state.timer), + Timer = erlang:start_timer( + ?MAX_INACTIVITY, self(), []), + Reply = ok, + {reply, Reply, StateName, + StateData#state{waiting_input = false, + last_receiver = Receiver, + input = "", + rid = RID, + key = SaveKey, + ctime = TNow, + timer = Timer, + last_poll = LastPoll, + req_list = ReqList + }} + end + end; + true -> + Reply = {error, bad_key}, + {reply, Reply, StateName, StateData} + end; + +handle_sync_event({http_get, RID, Wait, Hold}, _From, StateName, StateData) -> + {_,TSec,TMSec} = now(), + TNow = TSec*1000*1000 + TMSec, + cancel_timer(StateData#state.timer), + Timer = erlang:start_timer(?MAX_INACTIVITY, self(), []), + if + (Hold > 0) and + (StateData#state.output == "") and + ((TNow - StateData#state.ctime) < (Wait*1000*1000)) and + (StateData#state.rid == RID) and + (StateData#state.input /= "cancel") -> + Output = StateData#state.output, + ReqList = StateData#state.req_list, + Reply = {ok, keep_on_hold}; + (StateData#state.input == "cancel") -> + Output = StateData#state.output, + ReqList = StateData#state.req_list, + Reply = {ok, cancel}; + true -> + case StateData#state.output of + [[]| OutPacket] -> + Reply = {ok, OutPacket}; + _ -> + Reply = {ok, StateData#state.output} + end, + %% save request + ReqList = [#hbr{rid=RID, + key=StateData#state.key, + in=StateData#state.input, + out=StateData#state.output + } | + [El || El <- StateData#state.req_list, + El#hbr.rid /= RID ] + ], + Output = "" + end, + {reply, Reply, StateName, StateData#state{ + input = "", + output = Output, + timer = Timer, + req_list = ReqList}}; + +handle_sync_event(_Event, _From, StateName, StateData) -> + Reply = ok, + {reply, Reply, StateName, StateData}. + +code_change(_OldVsn, StateName, StateData, _Extra) -> + {ok, StateName, StateData}. + +%%---------------------------------------------------------------------- +%% Func: handle_info/3 +%% Returns: {next_state, NextStateName, NextStateData} | +%% {next_state, NextStateName, NextStateData, Timeout} | +%% {stop, Reason, NewStateData} +%%---------------------------------------------------------------------- +handle_info({timeout, Timer, _}, _StateName, + #state{timer = Timer} = StateData) -> + ?DEBUG("ding dong", []), + {stop, normal, StateData}; + +handle_info(_, StateName, StateData) -> + {next_state, StateName, StateData}. + +%%---------------------------------------------------------------------- +%% Func: terminate/3 +%% Purpose: Shutdown the fsm +%% Returns: any +%%---------------------------------------------------------------------- +terminate(_Reason, _StateName, StateData) -> + ?DEBUG("terminate: deleting session ~s", [StateData#state.id]), + mnesia:transaction( + fun() -> + mnesia:delete({http_bind, StateData#state.id}) + end), + case StateData#state.waiting_input of + false -> + case StateData#state.last_receiver of + undefined -> ok; + Receiver -> Receiver ! {tcp_closed, {http_bind, self()}} + end; + {Receiver, _Tag} -> Receiver ! {tcp_closed, {http_bind, self()}} + end, + ok. + +%%%---------------------------------------------------------------------- +%%% Internal functions +%%%---------------------------------------------------------------------- + + +http_put(ID, RID, Key, NewKey, Hold, Packet) -> + case mnesia:dirty_read({http_bind, ID}) of + [] -> + {error, not_exists}; + [#http_bind{pid = FsmRef}] -> + gen_fsm:sync_send_all_state_event( + FsmRef, {http_put, RID, Key, NewKey, Hold, Packet}) + end. + +http_get(ID,RID) -> + case mnesia:dirty_read({http_bind, ID}) of + [] -> + {error, not_exists}; + [#http_bind{pid = FsmRef, wait = Wait, hold = Hold}] -> + gen_fsm:sync_send_all_state_event(FsmRef, + {http_get, RID, Wait, Hold}) + end. + + +parse_request(Data) -> + ?DEBUG("--- incoming data --- ~n~s~n --- END --- ", + [Data]), + case xml_stream:parse_element(Data) of + El when element(1, El) == xmlelement -> + {xmlelement, Name, Attrs, Els} = El, + ID = xml:get_attr_s("sid",Attrs), + {RID,_X} = string:to_integer(xml:get_attr_s("rid",Attrs)), + Key = xml:get_attr_s("key",Attrs), + NewKey = xml:get_attr_s("newkey",Attrs), + Xmlns = xml:get_attr_s("xmlns",Attrs), + Packet = [xml:element_to_string( + xml:remove_tag_attr("xmlns",E)) || + E <- Els], + if + Name /= "body" -> + {error, bad_request}; + Xmlns /= "http://jabber.org/protocol/httpbind" -> + {error, bad_request}; + true -> + {ok, ID, RID, Key, NewKey, Attrs, Packet} + end; + {error, _Reason} -> + {error, bad_request} + end. + +cancel_timer(Timer) -> + erlang:cancel_timer(Timer), + receive + {timeout, Timer, _} -> + ok + after 0 -> + ok + end. + +hex(Bin) when binary(Bin) -> hex(binary_to_list(Bin)); +hex([]) -> ""; +hex([H|T]) -> + [A,B] = if + H == 0 -> "00"; + H < 16 -> [$0,element(H,{$1,$2,$3,$4,$5,$6,$7,$8,$9,$a,$b,$c,$d,$e,$f})]; + true -> erlang:integer_to_list(H,16) + end, + [A,B|hex(T)]. + +elements_to_string([]) -> + []; +elements_to_string([El | Els]) -> + xml:element_to_string(El) ++ elements_to_string(Els). Index: src/web/ejabberd_web.erl =================================================================== --- src/web/ejabberd_web.erl (Revision 565) +++ src/web/ejabberd_web.erl (Arbeitskopie) @@ -49,7 +49,7 @@ {"value", Value}])). -process_get({_, true}, +process_get({_, _, true}, #request{auth = Auth, path = ["admin", "server", SHost | RPath], q = Query, @@ -94,7 +94,7 @@ {404, [], make_xhtml([?XC("h1", "Not found")])} end; -process_get({_, true}, +process_get({_, _, true}, #request{auth = Auth, path = ["admin" | RPath], q = Query, @@ -133,12 +133,19 @@ [{xmlcdata, "401 Unauthorized"}]}])} end; -process_get({true, _}, +process_get({true, _, _}, #request{path = ["http-poll" | RPath], q = _Query, lang = _Lang} = Request) -> ejabberd_http_poll:process_request(Request#request{path = RPath}); +process_get({_, true, _}, + #request{us = _US, + path = ["http-bind" | RPath], + q = _Query, + lang = _Lang} = Request) -> + ejabberd_http_bind:process_request(Request#request{path = RPath}); + process_get(_, _Request) -> {404, [], make_xhtml([?XC("h1", "Not found")])}. Index: src/web/ejabberd_http.erl =================================================================== --- src/web/ejabberd_http.erl (Revision 565) +++ src/web/ejabberd_http.erl (Arbeitskopie) @@ -30,6 +30,7 @@ request_keepalive, request_content_length, request_lang = "en", + use_http_bind = false, use_http_poll = false, use_web_admin = false, end_of_request = false, @@ -70,15 +71,17 @@ _ -> ok end, + UseHTTPBind = lists:member(http_bind, Opts), UseHTTPPoll = lists:member(http_poll, Opts), UseWebAdmin = lists:member(web_admin, Opts), - ?DEBUG("S: ~p~n", [{UseHTTPPoll, UseWebAdmin}]), + ?DEBUG("S: ~p~n", [{UseHTTPPoll, UseHTTPBind, UseWebAdmin}]), ?INFO_MSG("started: ~p", [{SockMod1, Socket1}]), {ok, proc_lib:spawn_link(ejabberd_http, receive_headers, [#state{sockmod = SockMod1, socket = Socket1, use_http_poll = UseHTTPPoll, + use_http_bind = UseHTTPBind, use_web_admin = UseWebAdmin}])}. @@ -185,6 +188,7 @@ #state{sockmod = SockMod, socket = Socket, use_http_poll = State#state.use_http_poll, + use_http_bind = State#state.use_http_bind, use_web_admin = State#state.use_web_admin}; _ -> #state{end_of_request = true} @@ -200,6 +204,7 @@ request_auth = Auth, request_lang = Lang, use_http_poll = UseHTTPPoll, + use_http_bind = UseHTTPBind, use_web_admin = UseWebAdmin} = State) -> case (catch url_decode_q_split(Path)) of {'EXIT', _} -> @@ -217,7 +222,7 @@ q = LQuery, auth = Auth, lang = Lang}, - case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, + case ejabberd_web:process_get({UseHTTPPoll, UseHTTPBind, UseWebAdmin}, Request) of El when element(1, El) == xmlelement -> make_xhtml_output(State, 200, [], El); @@ -240,6 +245,7 @@ sockmod = SockMod, socket = Socket, use_http_poll = UseHTTPPoll, + use_http_bind = UseHTTPBind, use_web_admin = UseWebAdmin} = State) when is_integer(Len) -> case SockMod of @@ -267,7 +273,7 @@ auth = Auth, data = Data, lang = Lang}, - case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, + case ejabberd_web:process_get({UseHTTPPoll, UseHTTPBind, UseWebAdmin}, Request) of El when element(1, El) == xmlelement -> make_xhtml_output(State, 200, [], El); Index: src/xml.erl =================================================================== --- src/xml.erl (Revision 565) +++ src/xml.erl (Arbeitskopie) @@ -18,6 +18,7 @@ get_tag_attr/2, get_tag_attr_s/2, get_subtag/2, get_path_s/2, + remove_tag_attr/2, replace_tag_attr/3]). %element_to_string(El) -> @@ -224,6 +225,14 @@ get_path_s(El, [cdata]) -> get_tag_cdata(El). +remove_tag_attr(Attr, El) -> + case El of + {xmlelement, Name, Attrs, Els} -> + Attrs1 = lists:keydelete(Attr, 1, Attrs), + {xmlelement, Name, Attrs1, Els}; + _ -> + El + end. replace_tag_attr(Attr, Value, {xmlelement, Name, Attrs, Els}) -> Attrs1 = lists:keydelete(Attr, 1, Attrs), Index: src/ejabberd_sup.erl =================================================================== --- src/ejabberd_sup.erl (Revision 565) +++ src/ejabberd_sup.erl (Arbeitskopie) @@ -123,6 +123,14 @@ infinity, supervisor, [ejabberd_tmp_sup]}, + HTTPBindSupervisor = + {ejabberd_http_bind_sup, + {ejabberd_tmp_sup, start_link, + [ejabberd_http_bind_sup, ejabberd_http_bind]}, + permanent, + infinity, + supervisor, + [ejabberd_tmp_sup]}, IQSupervisor = {ejabberd_iq_sup, {ejabberd_tmp_sup, start_link, @@ -145,6 +153,7 @@ ServiceSupervisor, HTTPSupervisor, HTTPPollSupervisor, + HTTPBindSupervisor, IQSupervisor, Listener]}}.