diff --git a/ChangeLog b/ChangeLog index a2d0e2489..72b293491 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2004-12-05 Alexey Shchepin + + * src/web/ejabberd_web_admin.erl: Changed type of password field + to "password" + + * src/jlib.hrl: More stream error defines (thanks to Sergei + Golovan) + + * src/ejabberd_c2s.erl: Support for starttls_required option + (thanks to Sergei Golovan) + + * src/mod_muc/mod_muc_room.erl: Fixed mistake in case condition + (thanks to Sergei Golovan) + + * src/xml_stream.erl: Added function parse_element/1 + + * src/expat_erl.c: Added PARSE_FINAL_COMMAND + 2004-12-03 Alexey Shchepin * src/ejabberd_listener.erl: Enable keepalive option diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 2084a05e7..92b3fb214 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -46,6 +46,7 @@ access, shaper, tls = false, + tls_required = false, tls_enabled = false, tls_options = [], authentificated = false, @@ -82,6 +83,8 @@ xml:element_to_string(?SERR_INVALID_NAMESPACE)). -define(INVALID_XML_ERR, xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)). +-define(POLICY_VIOLATION_ERR(Lang, Text), + xml:element_to_string(?SERRT_POLICY_VIOLATION(Lang, Text))). %%%---------------------------------------------------------------------- %%% API @@ -117,8 +120,9 @@ init([{SockMod, Socket}, Opts]) -> _ -> none end, StartTLS = lists:member(starttls, Opts), + StartTLSRequired = lists:member(starttls_required, Opts), TLSEnabled = lists:member(tls, Opts), - TLS = StartTLS orelse TLSEnabled, + TLS = StartTLS orelse StartTLSRequired orelse TLSEnabled, TLSOpts = lists:filter(fun({certfile, _}) -> true; (_) -> false end, Opts), @@ -132,15 +136,16 @@ init([{SockMod, Socket}, Opts]) -> RecPid = ejabberd_receiver:start(Socket, SockMod, none), {SockMod, Socket, RecPid} end, - {ok, wait_for_stream, #state{socket = Socket1, - sockmod = SockMod1, - receiver = ReceiverPid, - tls = TLS, - tls_enabled = TLSEnabled, - tls_options = TLSOpts, - streamid = new_id(), - access = Access, - shaper = Shaper}}. + {ok, wait_for_stream, #state{socket = Socket1, + sockmod = SockMod1, + receiver = ReceiverPid, + tls = TLS, + tls_required = StartTLSRequired, + tls_enabled = TLSEnabled, + tls_options = TLSOpts, + streamid = new_id(), + access = Access, + shaper = Shaper}}. %%---------------------------------------------------------------------- @@ -179,14 +184,23 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> end, cyrsasl:listmech()), TLS = StateData#state.tls, TLSEnabled = StateData#state.tls_enabled, + TLSRequired = StateData#state.tls_required, SockMod = StateData#state.sockmod, TLSFeature = case (TLS == true) andalso (TLSEnabled == false) andalso (SockMod == gen_tcp) of true -> - [{xmlelement, "starttls", - [{"xmlns", ?NS_TLS}], []}]; + case TLSRequired of + true -> + [{xmlelement, "starttls", + [{"xmlns", ?NS_TLS}], + [{xmlelement, "required", + [], []}]}]; + _ -> + [{xmlelement, "starttls", + [{"xmlns", ?NS_TLS}], []}] + end; false -> [] end, @@ -379,9 +393,10 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) -> {xmlelement, Name, Attrs, Els} = El, TLS = StateData#state.tls, TLSEnabled = StateData#state.tls_enabled, + TLSRequired = StateData#state.tls_required, SockMod = StateData#state.sockmod, case {xml:get_attr_s("xmlns", Attrs), Name} of - {?NS_SASL, "auth"} -> + {?NS_SASL, "auth"} when not ((SockMod == gen_tcp) and TLSRequired) -> Mech = xml:get_attr_s("mechanism", Attrs), ClientIn = jlib:decode_base64(xml:get_cdata(Els)), case cyrsasl:server_start(StateData#state.sasl_state, @@ -429,18 +444,28 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) -> tls_enabled = true }}; _ -> - case jlib:iq_query_info(El) of - #iq{xmlns = ?NS_REGISTER} = IQ -> - ResIQ = mod_register:process_iq( - {"", "", ""}, {"", ?MYNAME, ""}, IQ), - Res1 = jlib:replace_from_to({"", ?MYNAME, ""}, - {"", "", ""}, - jlib:iq_to_xml(ResIQ)), - Res = jlib:remove_attr("to", Res1), - send_element(StateData, Res), - {next_state, wait_for_feature_request, StateData}; - _ -> - {next_state, wait_for_feature_request, StateData} + if + (SockMod == gen_tcp) and TLSRequired -> + Lang = StateData#state.lang, + send_text(StateData, ?POLICY_VIOLATION_ERR( + Lang, + "Use of STARTTLS required") ++ + ?STREAM_TRAILER), + {stop, normal, StateData}; + true -> + case jlib:iq_query_info(El) of + #iq{xmlns = ?NS_REGISTER} = IQ -> + ResIQ = mod_register:process_iq( + {"", "", ""}, {"", ?MYNAME, ""}, IQ), + Res1 = jlib:replace_from_to({"", ?MYNAME, ""}, + {"", "", ""}, + jlib:iq_to_xml(ResIQ)), + Res = jlib:remove_attr("to", Res1), + send_element(StateData, Res), + {next_state, wait_for_feature_request, StateData}; + _ -> + {next_state, wait_for_feature_request, StateData} + end end end; diff --git a/src/expat_erl.c b/src/expat_erl.c index 8cc9f2903..1b197dace 100644 --- a/src/expat_erl.c +++ b/src/expat_erl.c @@ -107,6 +107,7 @@ int ei_x_encode_string_fixed(ei_x_buff* x, const char* s) #define XML_ERROR 3 #define PARSE_COMMAND 0 +#define PARSE_FINAL_COMMAND 1 ei_x_buff event_buf; @@ -208,8 +209,9 @@ static int expat_erl_control(ErlDrvData drv_data, switch (command) { case PARSE_COMMAND: + case PARSE_FINAL_COMMAND: ei_x_new_with_version(&event_buf); - res = XML_Parse(d->parser, buf, len, 0); + res = XML_Parse(d->parser, buf, len, command == PARSE_FINAL_COMMAND); if(!res) { diff --git a/src/jlib.hrl b/src/jlib.hrl index c2a9ba43a..5f9e17b50 100644 --- a/src/jlib.hrl +++ b/src/jlib.hrl @@ -214,6 +214,63 @@ %-define(SERR_, % ?STREAM_ERROR("")). +-define(STREAM_ERRORT(Condition, Lang, Text), + {xmlelement, "stream:error", + [], + [{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}, + {xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}], + [{xmlcdata, translate:translate(Lang, Text)}]}]}). + +-define(SERRT_BAD_FORMAT(Lang, Text), + ?STREAM_ERRORT("bad-format", Lang, Text)). +-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text), + ?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)). +-define(SERRT_CONFLICT(Lang, Text), + ?STREAM_ERRORT("conflict", Lang, Text)). +-define(SERRT_CONNECTION_TIMEOUT(Lang, Text), + ?STREAM_ERRORT("connection-timeout", Lang, Text)). +-define(SERRT_HOST_GONE(Lang, Text), + ?STREAM_ERRORT("host-gone", Lang, Text)). +-define(SERRT_HOST_UNKNOWN(Lang, Text), + ?STREAM_ERRORT("host-unknown", Lang, Text)). +-define(SERRT_IMPROPER_ADDRESSING(Lang, Text), + ?STREAM_ERRORT("improper-addressing", Lang, Text)). +-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text), + ?STREAM_ERRORT("internal-server-error", Lang, Text)). +-define(SERRT_INVALID_FROM(Lang, Text), + ?STREAM_ERRORT("invalid-from", Lang, Text)). +-define(SERRT_INVALID_ID(Lang, Text), + ?STREAM_ERRORT("invalid-id", Lang, Text)). +-define(SERRT_INVALID_NAMESPACE(Lang, Text), + ?STREAM_ERRORT("invalid-namespace", Lang, Text)). +-define(SERRT_INVALID_XML(Lang, Text), + ?STREAM_ERRORT("invalid-xml", Lang, Text)). +-define(SERRT_NOT_AUTHORIZED(Lang, Text), + ?STREAM_ERRORT("not-authorized", Lang, Text)). +-define(SERRT_POLICY_VIOLATION(Lang, Text), + ?STREAM_ERRORT("policy-violation", Lang, Text)). +-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text), + ?STREAM_ERRORT("remote-connection-failed", Lang, Text)). +-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text), + ?STREAM_ERRORT("resource-constraint", Lang, Text)). +-define(SERRT_RESTRICTED_XML(Lang, Text), + ?STREAM_ERRORT("restricted-xml", Lang, Text)). +% TODO: include hostname or IP +-define(SERRT_SEE_OTHER_HOST(Lang, Text), + ?STREAM_ERRORT("see-other-host", Lang, Text)). +-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text), + ?STREAM_ERRORT("system-shutdown", Lang, Text)). +-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text), + ?STREAM_ERRORT("unsupported-encoding", Lang, Text)). +-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text), + ?STREAM_ERRORT("unsupported-stanza-type", Lang, Text)). +-define(SERRT_UNSUPPORTED_VERSION(Lang, Text), + ?STREAM_ERRORT("unsupported-version", Lang, Text)). +-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text), + ?STREAM_ERRORT("xml-not-well-formed", Lang, Text)). +%-define(SERRT_(Lang, Text), +% ?STREAM_ERRORT("", Lang, Text)). + -record(jid, {user, server, resource, luser, lserver, lresource}). diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 810f1d580..f41c4532e 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -1023,7 +1023,6 @@ count_stanza_shift(Nick, Els, StateData) -> _ -> count_maxchars_shift(Nick, MaxChars, HL) end, - lists:max([Shift0, Shift1, Shift2, Shift3]). count_seconds_shift(Seconds, HistoryList) -> @@ -1087,7 +1086,7 @@ extract_history([{xmlelement, _Name, Attrs, _SubEls} = El | Els], Type) -> end; _ -> case catch list_to_integer(AttrVal) of - IntVal when is_integer(IntVal) and IntVal >= 0 -> + IntVal when is_integer(IntVal) and (IntVal >= 0) -> IntVal; _ -> false @@ -1486,7 +1485,7 @@ process_admin_items_set(UJID, Items, Lang, StateData) -> JID, outcast, set_role(JID, none, SD)); {JID, affiliation, A, Reason} when - (A == admin) or (A == owner)-> + (A == admin) or (A == owner) -> SD1 = set_affiliation(JID, A, SD), SD2 = set_role(JID, moderator, SD1), send_update_presence(JID, SD2), diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl index 13e15eca4..49531222a 100644 --- a/src/web/ejabberd_web_admin.erl +++ b/src/web/ejabberd_web_admin.erl @@ -1228,7 +1228,7 @@ user_info(User, Query, Lang) -> end, lists:sort(Resources)))] end, Password = ejabberd_auth:get_password_s(User), - FPassword = [?INPUT("text", "password", Password), ?C(" "), + FPassword = [?INPUT("password", "password", Password), ?C(" "), ?INPUTT("submit", "chpassword", "Change Password")], QueueLen = length(mnesia:dirty_read({offline_msg, User})), FQueueLen = [?AC("queue/", diff --git a/src/xml_stream.erl b/src/xml_stream.erl index d5fbd9372..d00fd4ca8 100644 --- a/src/xml_stream.erl +++ b/src/xml_stream.erl @@ -15,7 +15,8 @@ send_text/2, new/1, parse/2, - close/1]). + close/1, + parse_element/1]). -define(XML_START, 0). -define(XML_END, 1). @@ -23,6 +24,7 @@ -define(XML_ERROR, 3). -define(PARSE_COMMAND, 0). +-define(PARSE_FINAL_COMMAND, 1). -record(xml_stream_state, {callback_pid, port, stack}). @@ -123,3 +125,54 @@ parse(#xml_stream_state{callback_pid = CallbackPid, close(#xml_stream_state{port = Port}) -> port_close(Port). + + +parse_element(Str) -> + Port = open_port({spawn, expat_erl}, [binary]), + Res = port_control(Port, ?PARSE_FINAL_COMMAND, Str), + port_close(Port), + process_element_events(binary_to_term(Res)). + +process_element_events(Events) -> + process_element_events(Events, []). + +process_element_events([], _Stack) -> + {error, parse_error}; +process_element_events([Event | Events], Stack) -> + case Event of + {?XML_START, {Name, Attrs}} -> + process_element_events( + Events, [{xmlelement, Name, Attrs, []} | Stack]); + {?XML_END, _EndName} -> + case Stack of + [{xmlelement, Name, Attrs, Els} | Tail] -> + NewEl = {xmlelement, Name, Attrs, lists:reverse(Els)}, + case Tail of + [] -> + if + Events == [] -> + NewEl; + true -> + {error, parse_error} + end; + [{xmlelement, Name1, Attrs1, Els1} | Tail1] -> + process_element_events( + Events, + [{xmlelement, Name1, Attrs1, [NewEl | Els1]} | + Tail1]) + end + end; + {?XML_CDATA, CData} -> + case Stack of + [{xmlelement, Name, Attrs, Els} | Tail] -> + process_element_events( + Events, + [{xmlelement, Name, Attrs, [{xmlcdata, CData} | Els]} | + Tail]); + [] -> + process_element_events(Events, []) + end; + {?XML_ERROR, Err} -> + {error, Err} + end. +