diff options
Diffstat (limited to 'src/mod_muc.erl')
-rw-r--r-- | src/mod_muc.erl | 725 |
1 files changed, 310 insertions, 415 deletions
diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 6b878b05b..ea8bff5e3 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -43,7 +43,12 @@ forget_room/3, create_room/5, shutdown_rooms/1, - process_iq_disco_items/5, + process_disco_info/1, + process_disco_items/1, + process_vcard/1, + process_register/1, + process_muc_unique/1, + process_mucsub/1, broadcast_service_message/2, export/1, import/1, @@ -58,7 +63,7 @@ -include("ejabberd.hrl"). -include("logger.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -include("mod_muc.hrl"). -record(state, @@ -153,17 +158,6 @@ forget_room(ServerHost, Host, Name) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:forget_room(LServer, Host, Name). -process_iq_disco_items(Host, From, To, MaxRoomsDiscoItems, - #iq{lang = Lang} = IQ) -> - Rsm = jlib:rsm_decode(IQ), - DiscoNode = fxml:get_tag_attr_s(<<"node">>, IQ#iq.sub_el), - Res = IQ#iq{type = result, - sub_el = - [#xmlel{name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS}], - children = iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, DiscoNode, Rsm)}]}, - ejabberd_router:route(To, From, jlib:iq_to_xml(Res)). - can_use_nick(_ServerHost, _Host, _JID, <<"">>) -> false; can_use_nick(ServerHost, Host, JID, Nick) -> LServer = jid:nameprep(ServerHost), @@ -175,6 +169,8 @@ can_use_nick(ServerHost, Host, JID, Nick) -> %%==================================================================== init([Host, Opts]) -> + IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, + one_queue), MyHost = gen_mod:get_opt_host(Host, Opts, <<"conference.@HOST@">>), Mod = gen_mod:db_mod(Host, Opts, ?MODULE), @@ -258,6 +254,18 @@ init([Host, Opts]) -> RoomShaper = gen_mod:get_opt(room_shaper, Opts, fun(A) when is_atom(A) -> A end, none), + gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER, + ?MODULE, process_register, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_VCARD, + ?MODULE, process_vcard, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_MUCSUB, + ?MODULE, process_mucsub, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_MUC_UNIQUE, + ?MODULE, process_muc_unique, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO, + ?MODULE, process_disco_info, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS, + ?MODULE, process_disco_items, IQDisc), ejabberd_router:register_route(MyHost, Host), load_permanent_rooms(MyHost, Host, {Access, AccessCreate, AccessAdmin, AccessPersistent}, @@ -319,8 +327,14 @@ handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> {noreply, State}; handle_info(_Info, State) -> {noreply, State}. -terminate(_Reason, State) -> - ejabberd_router:unregister_route(State#state.host), +terminate(_Reason, #state{host = MyHost}) -> + ejabberd_router:unregister_route(MyHost), + gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER), + gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD), + gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_MUCSUB), + gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_MUC_UNIQUE), + gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO), + gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. @@ -330,215 +344,195 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- do_route(Host, ServerHost, Access, HistorySize, RoomShaper, - From, To, Packet, DefRoomOpts, MaxRoomsDiscoItems) -> + From, To, Packet, DefRoomOpts, _MaxRoomsDiscoItems) -> {AccessRoute, _AccessCreate, _AccessAdmin, _AccessPersistent} = Access, case acl:match_rule(ServerHost, AccessRoute, From) of allow -> do_route1(Host, ServerHost, Access, HistorySize, RoomShaper, - From, To, Packet, DefRoomOpts, MaxRoomsDiscoItems); - _ -> - #xmlel{attrs = Attrs} = Packet, - Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs), + From, To, Packet, DefRoomOpts); + deny -> + Lang = xmpp:get_lang(Packet), ErrText = <<"Access denied by service policy">>, - Err = jlib:make_error_reply(Packet, - ?ERRT_FORBIDDEN(Lang, ErrText)), - ejabberd_router:route_error(To, From, Err, Packet) + Err = xmpp:err_forbidden(ErrText, Lang), + ejabberd_router:route_error(To, From, Packet, Err) end. - +do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper, + From, #jid{luser = <<"">>, lresource = <<"">>} = To, + #iq{} = IQ, _DefRoomOpts) -> + ejabberd_local:process_iq(From, To, IQ); +do_route1(Host, ServerHost, Access, _HistorySize, _RoomShaper, + From, #jid{luser = <<"">>, lresource = <<"">>} = To, + #message{lang = Lang, body = Body, type = Type} = Packet, _) -> + {_AccessRoute, _AccessCreate, AccessAdmin, _AccessPersistent} = Access, + if Type == error -> + ok; + true -> + case acl:match_rule(ServerHost, AccessAdmin, From) of + allow -> + Msg = xmpp:get_text(Body), + broadcast_service_message(Host, Msg); + deny -> + ErrText = <<"Only service administrators are allowed " + "to send service messages">>, + Err = xmpp:make_error( + Packet, xmpp:err_forbidden(ErrText, Lang)), + ejabberd_router:route(To, From, Err) + end + end; +do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper, + From, #jid{luser = <<"">>} = To, Packet, _DefRoomOpts) -> + Err = xmpp:err_service_unavailable(), + ejabberd_router:route_error(To, From, Packet, Err); do_route1(Host, ServerHost, Access, HistorySize, RoomShaper, - From, To, Packet, DefRoomOpts, MaxRoomsDiscoItems) -> - {_AccessRoute, AccessCreate, AccessAdmin, _AccessPersistent} = Access, + From, To, Packet, DefRoomOpts) -> + {_AccessRoute, AccessCreate, _AccessAdmin, _AccessPersistent} = Access, {Room, _, Nick} = jid:tolower(To), - #xmlel{name = Name, attrs = Attrs} = Packet, - case Room of - <<"">> -> - case Nick of - <<"">> -> - case Name of - <<"iq">> -> - case jlib:iq_query_info(Packet) of - #iq{type = get, xmlns = (?NS_DISCO_INFO) = XMLNS, - sub_el = _SubEl, lang = Lang} = - IQ -> - Info = ejabberd_hooks:run_fold(disco_info, - ServerHost, [], - [ServerHost, ?MODULE, - <<"">>, <<"">>]), - Res = IQ#iq{type = result, - sub_el = - [#xmlel{name = <<"query">>, - attrs = - [{<<"xmlns">>, XMLNS}], - children = - iq_disco_info( - ServerHost, Lang) ++ - Info}]}, - ejabberd_router:route(To, From, - jlib:iq_to_xml(Res)); - #iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ -> - spawn(?MODULE, process_iq_disco_items, - [Host, From, To, MaxRoomsDiscoItems, IQ]); - #iq{type = get, xmlns = (?NS_REGISTER) = XMLNS, - lang = Lang, sub_el = _SubEl} = - IQ -> - Res = IQ#iq{type = result, - sub_el = - [#xmlel{name = <<"query">>, - attrs = - [{<<"xmlns">>, XMLNS}], - children = - iq_get_register_info(ServerHost, - Host, - From, - Lang)}]}, - ejabberd_router:route(To, From, - jlib:iq_to_xml(Res)); - #iq{type = set, xmlns = (?NS_REGISTER) = XMLNS, - lang = Lang, sub_el = SubEl} = - IQ -> - case process_iq_register_set(ServerHost, Host, From, - SubEl, Lang) - of - {result, IQRes} -> - Res = IQ#iq{type = result, - sub_el = - [#xmlel{name = <<"query">>, - attrs = - [{<<"xmlns">>, - XMLNS}], - children = IQRes}]}, - ejabberd_router:route(To, From, - jlib:iq_to_xml(Res)); - {error, Error} -> - Err = jlib:make_error_reply(Packet, Error), - ejabberd_router:route(To, From, Err) - end; - #iq{type = get, xmlns = (?NS_VCARD) = XMLNS, - lang = Lang, sub_el = _SubEl} = - IQ -> - Res = IQ#iq{type = result, - sub_el = - [#xmlel{name = <<"vCard">>, - attrs = - [{<<"xmlns">>, XMLNS}], - children = - iq_get_vcard(Lang)}]}, - ejabberd_router:route(To, From, - jlib:iq_to_xml(Res)); - #iq{type = get, xmlns = ?NS_MUCSUB, - sub_el = #xmlel{name = <<"subscriptions">>} = SubEl} = IQ -> - RoomJIDs = get_subscribed_rooms(ServerHost, Host, From), - Subs = lists:map( - fun(J) -> - #xmlel{name = <<"subscription">>, - attrs = [{<<"jid">>, - jid:to_string(J)}]} - end, RoomJIDs), - Res = IQ#iq{type = result, - sub_el = [SubEl#xmlel{children = Subs}]}, - ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); - #iq{type = get, xmlns = ?NS_MUC_UNIQUE} = IQ -> - Res = IQ#iq{type = result, - sub_el = - [#xmlel{name = <<"unique">>, - attrs = - [{<<"xmlns">>, - ?NS_MUC_UNIQUE}], - children = - [iq_get_unique(From)]}]}, - ejabberd_router:route(To, From, - jlib:iq_to_xml(Res)); - #iq{} -> - Err = jlib:make_error_reply(Packet, - ?ERR_FEATURE_NOT_IMPLEMENTED), - ejabberd_router:route(To, From, Err); - _ -> ok - end; - <<"message">> -> - case fxml:get_attr_s(<<"type">>, Attrs) of - <<"error">> -> ok; - _ -> - case acl:match_rule(ServerHost, AccessAdmin, From) - of - allow -> - Msg = fxml:get_path_s(Packet, - [{elem, <<"body">>}, - cdata]), - broadcast_service_message(Host, Msg); - _ -> - Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs), - ErrText = - <<"Only service administrators are allowed " - "to send service messages">>, - Err = jlib:make_error_reply(Packet, - ?ERRT_FORBIDDEN(Lang, - ErrText)), - ejabberd_router:route(To, From, Err) - end - end; - <<"presence">> -> ok - end; - _ -> - case fxml:get_attr_s(<<"type">>, Attrs) of - <<"error">> -> ok; - <<"result">> -> ok; - _ -> - Err = jlib:make_error_reply(Packet, - ?ERR_ITEM_NOT_FOUND), - ejabberd_router:route(To, From, Err) - end - end; - _ -> - case mnesia:dirty_read(muc_online_room, {Room, Host}) of - [] -> - case is_create_request(Packet) of + case mnesia:dirty_read(muc_online_room, {Room, Host}) of + [] -> + case is_create_request(Packet) of + true -> + case check_user_can_create_room( + ServerHost, AccessCreate, From, Room) and + check_create_roomid(ServerHost, Room) of true -> - case check_user_can_create_room(ServerHost, - AccessCreate, From, Room) and - check_create_roomid(ServerHost, Room) of - true -> - {ok, Pid} = start_new_room(Host, ServerHost, Access, - Room, HistorySize, - RoomShaper, From, Nick, DefRoomOpts), - register_room(Host, Room, Pid), - mod_muc_room:route(Pid, From, Nick, Packet), - ok; - false -> - Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs), - ErrText = <<"Room creation is denied by service policy">>, - Err = jlib:make_error_reply( - Packet, ?ERRT_FORBIDDEN(Lang, ErrText)), - ejabberd_router:route(To, From, Err) - end; + {ok, Pid} = start_new_room( + Host, ServerHost, Access, + Room, HistorySize, + RoomShaper, From, Nick, DefRoomOpts), + register_room(Host, Room, Pid), + mod_muc_room:route(Pid, From, Nick, Packet), + ok; false -> - Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs), - ErrText = <<"Conference room does not exist">>, - Err = jlib:make_error_reply(Packet, - ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)), + Lang = xmpp:get_lang(Packet), + ErrText = <<"Room creation is denied by service policy">>, + Err = xmpp:make_error( + Packet, xmpp:err_forbidden(ErrText, Lang)), ejabberd_router:route(To, From, Err) end; - [R] -> - Pid = R#muc_online_room.pid, - ?DEBUG("MUC: send to process ~p~n", [Pid]), - mod_muc_room:route(Pid, From, Nick, Packet), - ok - end + false -> + Lang = xmpp:get_lang(Packet), + ErrText = <<"Conference room does not exist">>, + Err = xmpp:err_item_not_found(ErrText, Lang), + ejabberd_router:route_error(To, From, Packet, Err) + end; + [R] -> + Pid = R#muc_online_room.pid, + ?DEBUG("MUC: send to process ~p~n", [Pid]), + mod_muc_room:route(Pid, From, Nick, Packet), + ok end. --spec is_create_request(xmlel()) -> boolean(). -is_create_request(#xmlel{name = <<"presence">>} = Packet) -> - <<"">> == fxml:get_tag_attr_s(<<"type">>, Packet); -is_create_request(#xmlel{name = <<"iq">>} = Packet) -> - case jlib:iq_query_info(Packet) of - #iq{type = set, xmlns = ?NS_MUCSUB, - sub_el = #xmlel{name = <<"subscribe">>}} -> - true; - #iq{type = get, xmlns = ?NS_MUC_OWNER, sub_el = SubEl} -> - [] == fxml:remove_cdata(SubEl#xmlel.children); - _ -> - false - end; +-spec process_vcard(iq()) -> iq(). +process_vcard(#iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]} = IQ) -> + Desc = translate:translate(Lang, <<"ejabberd MUC module">>), + Copyright = <<"Copyright (c) 2003-2016 ProcessOne">>, + xmpp:make_iq_result( + IQ, #vcard_temp{fn = <<"ejabberd/mod_muc">>, + url = ?EJABBERD_URI, + desc = <<Desc/binary, $\n, Copyright/binary>>}); +process_vcard(#iq{type = set, lang = Lang} = IQ) -> + Txt = <<"Value 'set' of 'type' attribute is not allowed">>, + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); +process_vcard(#iq{lang = Lang} = IQ) -> + Txt = <<"No module is handling this query">>, + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). + +-spec process_register(iq()) -> iq(). +process_register(#iq{type = get, from = From, to = To, lang = Lang, + sub_els = [#register{}]} = IQ) -> + Host = To#jid.lserver, + ServerHost = ejabberd_router:host_of_route(Host), + xmpp:make_iq_result(IQ, iq_get_register_info(ServerHost, Host, From, Lang)); +process_register(#iq{type = set, from = From, to = To, + lang = Lang, sub_els = [El = #register{}]} = IQ) -> + Host = To#jid.lserver, + ServerHost = ejabberd_router:host_of_route(Host), + case process_iq_register_set(ServerHost, Host, From, El, Lang) of + {result, Result} -> + xmpp:make_iq_result(IQ, Result); + {error, Err} -> + xmpp:make_error(IQ, Err) + end. + +-spec process_disco_info(iq()) -> iq(). +process_disco_info(#iq{type = set, lang = Lang} = IQ) -> + Txt = <<"Value 'set' of 'type' attribute is not allowed">>, + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); +process_disco_info(#iq{type = get, to = To, lang = Lang, + sub_els = [#disco_info{node = <<"">>}]} = IQ) -> + ServerHost = ejabberd_router:host_of_route(To#jid.lserver), + X = ejabberd_hooks:run_fold(disco_info, ServerHost, [], + [ServerHost, ?MODULE, <<"">>, Lang]), + MAMFeatures = case gen_mod:is_loaded(ServerHost, mod_mam) of + true -> [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1]; + false -> [] + end, + Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, + ?NS_REGISTER, ?NS_MUC, ?NS_RSM, + ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE | MAMFeatures], + Identity = #identity{category = <<"conference">>, + type = <<"text">>, + name = translate:translate(Lang, <<"Chatrooms">>)}, + xmpp:make_iq_result( + IQ, #disco_info{features = Features, + identities = [Identity], + xdata = X}); +process_disco_info(#iq{type = get, lang = Lang, + sub_els = [#disco_info{}]} = IQ) -> + xmpp:make_error(IQ, xmpp:err_item_not_found(<<"Node not found">>, Lang)); +process_disco_info(#iq{lang = Lang} = IQ) -> + Txt = <<"No module is handling this query">>, + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). + +-spec process_disco_items(iq()) -> iq(). +process_disco_items(#iq{type = set, lang = Lang} = IQ) -> + Txt = <<"Value 'set' of 'type' attribute is not allowed">>, + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); +process_disco_items(#iq{type = get, from = From, to = To, lang = Lang, + sub_els = [#disco_items{node = Node, rsm = RSM}]} = IQ) -> + Host = To#jid.lserver, + ServerHost = ejabberd_router:host_of_route(Host), + MaxRoomsDiscoItems = gen_mod:get_module_opt( + ServerHost, ?MODULE, max_rooms_discoitems, + fun(I) when is_integer(I), I>=0 -> I end, + 100), + Items = iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, Node, RSM), + xmpp:make_iq_result(IQ, #disco_items{node = Node, items = Items}); +process_disco_items(#iq{lang = Lang} = IQ) -> + Txt = <<"No module is handling this query">>, + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). + +-spec process_muc_unique(iq()) -> iq(). +process_muc_unique(#iq{type = set, lang = Lang} = IQ) -> + Txt = <<"Value 'set' of 'type' attribute is not allowed">>, + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); +process_muc_unique(#iq{from = From, type = get, + sub_els = [#muc_unique{}]} = IQ) -> + Name = p1_sha:sha(term_to_binary([From, p1_time_compat:timestamp(), + randoms:get_string()])), + xmpp:make_iq_result(IQ, #muc_unique{name = Name}). + +-spec process_mucsub(iq()) -> iq(). +process_mucsub(#iq{type = set, lang = Lang} = IQ) -> + Txt = <<"Value 'set' of 'type' attribute is not allowed">>, + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); +process_mucsub(#iq{type = get, from = From, to = To, + sub_els = [#muc_subscriptions{}]} = IQ) -> + Host = To#jid.lserver, + ServerHost = ejabberd_router:host_of_route(Host), + RoomJIDs = get_subscribed_rooms(ServerHost, Host, From), + xmpp:make_iq_result(IQ, #muc_subscriptions{list = RoomJIDs}); +process_mucsub(#iq{lang = Lang} = IQ) -> + Txt = <<"No module is handling this query">>, + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). + +-spec is_create_request(stanza()) -> boolean(). +is_create_request(#presence{type = available}) -> + true; +is_create_request(#iq{type = T} = IQ) when T == get; T == set -> + xmpp:has_subtag(IQ, #muc_subscribe{}) orelse + xmpp:has_subtag(IQ, #muc_owner{}); is_create_request(_) -> false. @@ -603,61 +597,21 @@ register_room(Host, Room, Pid) -> end, mnesia:transaction(F). - -iq_disco_info(ServerHost, Lang) -> - [#xmlel{name = <<"identity">>, - attrs = - [{<<"category">>, <<"conference">>}, - {<<"type">>, <<"text">>}, - {<<"name">>, - translate:translate(Lang, <<"Chatrooms">>)}], - children = []}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_DISCO_INFO}], children = []}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_DISCO_ITEMS}], children = []}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_MUC}], children = []}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_MUC_UNIQUE}], children = []}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_REGISTER}], children = []}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_RSM}], children = []}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_MUCSUB}], children = []}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_VCARD}], children = []}] ++ - case gen_mod:is_loaded(ServerHost, mod_mam) of - true -> - [#xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_MAM_TMP}]}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_MAM_0}]}, - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, ?NS_MAM_1}]}]; - false -> - [] - end. - -iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, <<>>, none) -> +iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, <<"">>, undefined) -> Rooms = get_vh_rooms(Host), case erlang:length(Rooms) < MaxRoomsDiscoItems of true -> iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang}); false -> - iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, <<"nonemptyrooms">>, none) + iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, <<"nonemptyrooms">>, undefined) end; -iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, <<"nonemptyrooms">>, none) -> - XmlEmpty = #xmlel{name = <<"item">>, - attrs = - [{<<"jid">>, <<"conference.localhost">>}, - {<<"node">>, <<"emptyrooms">>}, - {<<"name">>, translate:translate(Lang, <<"Empty Rooms">>)}], - children = []}, +iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, <<"nonemptyrooms">>, undefined) -> + Empty = #disco_item{jid = jid:make(<<"conference.localhost">>), + node = <<"emptyrooms">>, + name = translate:translate(Lang, <<"Empty Rooms">>)}, Query = {get_disco_item, only_non_empty, From, Lang}, - [XmlEmpty | iq_disco_items_list(Host, get_vh_rooms(Host), Query)]; -iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, <<"emptyrooms">>, none) -> + [Empty | iq_disco_items_list(Host, get_vh_rooms(Host), Query)]; +iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, <<"emptyrooms">>, undefined) -> iq_disco_items_list(Host, get_vh_rooms(Host), {get_disco_item, 0, From, Lang}); iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, _DiscoNode, Rsm) -> {Rooms, RsmO} = get_vh_rooms(Host, Rsm), @@ -665,70 +619,62 @@ iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, _DiscoNode, Rsm) -> iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang}) ++ RsmOut. iq_disco_items_list(Host, Rooms, Query) -> - lists:zf(fun (#muc_online_room{name_host = - {Name, _Host}, - pid = Pid}) -> - case catch gen_fsm:sync_send_all_state_event(Pid, - Query, - 100) - of - {item, Desc} -> - flush(), - {true, - #xmlel{name = <<"item">>, - attrs = - [{<<"jid">>, - jid:to_string({Name, Host, - <<"">>})}, - {<<"name">>, Desc}], - children = []}}; - _ -> false - end - end, Rooms). - -get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})-> - AllRooms = lists:sort(get_vh_rooms(Host)), - Count = erlang:length(AllRooms), - Guard = case Direction of - _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}]; - aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}]; - before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}]; - _ -> [{'==', {element, 2, '$1'}, Host}] - end, - L = lists:sort( - mnesia:dirty_select(muc_online_room, - [{#muc_online_room{name_host = '$1', _ = '_'}, - Guard, - ['$_']}])), - L2 = if - Index == undefined andalso Direction == before -> - lists:reverse(lists:sublist(lists:reverse(L), 1, M)); - Index == undefined -> - lists:sublist(L, 1, M); - Index > Count orelse Index < 0 -> - []; - true -> - lists:sublist(L, Index+1, M) - end, - if L2 == [] -> {L2, #rsm_out{count = Count}}; - true -> - H = hd(L2), - NewIndex = get_room_pos(H, AllRooms), - T = lists:last(L2), - {F, _} = H#muc_online_room.name_host, - {Last, _} = T#muc_online_room.name_host, - {L2, - #rsm_out{first = F, last = Last, count = Count, - index = NewIndex}} - end. + lists:zf( + fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) -> + case catch gen_fsm:sync_send_all_state_event(Pid, Query, 100) of + {item, Desc} -> + flush(), + {true, #disco_item{jid = jid:make(Name, Host), + name = Desc}}; + _ -> + false + end + end, Rooms). -get_subscribed_rooms(_ServerHost, Host1, From) -> - Rooms = get_vh_rooms(Host1), +get_vh_rooms(_, _) -> + todo. + %% AllRooms = lists:sort(get_vh_rooms(Host)), + %% Count = erlang:length(AllRooms), + %% Guard = case Direction of + %% _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}]; + %% aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}]; + %% before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}]; + %% _ -> [{'==', {element, 2, '$1'}, Host}] + %% end, + %% L = lists:sort( + %% mnesia:dirty_select(muc_online_room, + %% [{#muc_online_room{name_host = '$1', _ = '_'}, + %% Guard, + %% ['$_']}])), + %% L2 = if + %% Index == undefined andalso Direction == before -> + %% lists:reverse(lists:sublist(lists:reverse(L), 1, M)); + %% Index == undefined -> + %% lists:sublist(L, 1, M); + %% Index > Count orelse Index < 0 -> + %% []; + %% true -> + %% lists:sublist(L, Index+1, M) + %% end, + %% if L2 == [] -> {L2, #rsm_out{count = Count}}; + %% true -> + %% H = hd(L2), + %% NewIndex = get_room_pos(H, AllRooms), + %% T = lists:last(L2), + %% {F, _} = H#muc_online_room.name_host, + %% {Last, _} = T#muc_online_room.name_host, + %% {L2, + %% #rsm_out{first = F, last = Last, count = Count, + %% index = NewIndex}} + %% end. + +get_subscribed_rooms(_ServerHost, Host, From) -> + Rooms = get_vh_rooms(Host), BareFrom = jid:remove_resource(From), lists:flatmap( - fun(#muc_online_room{name_host = {Name, Host}, pid = Pid}) -> + fun(#muc_online_room{name_host = {Name, _}, pid = Pid}) -> case gen_fsm:sync_send_all_state_event(Pid, {is_subscribed, BareFrom}) of - true -> [jid:make(Name, Host, <<>>)]; + true -> [jid:make(Name, Host)]; false -> [] end; (_) -> @@ -750,60 +696,31 @@ get_room_pos(Desired, [_ | Rooms], HeadPosition) -> flush() -> receive _ -> flush() after 0 -> ok end. --define(XFIELD(Type, Label, Var, Val), - #xmlel{name = <<"field">>, - attrs = - [{<<"type">>, Type}, - {<<"label">>, translate:translate(Lang, Label)}, - {<<"var">>, Var}], - children = - [#xmlel{name = <<"value">>, attrs = [], - children = [{xmlcdata, Val}]}]}). - -iq_get_unique(From) -> - {xmlcdata, - p1_sha:sha(term_to_binary([From, p1_time_compat:timestamp(), - randoms:get_string()]))}. - get_nick(ServerHost, Host, From) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:get_nick(LServer, Host, From). iq_get_register_info(ServerHost, Host, From, Lang) -> - {Nick, Registered} = case get_nick(ServerHost, Host, - From) - of - error -> {<<"">>, []}; - N -> - {N, - [#xmlel{name = <<"registered">>, attrs = [], - children = []}]} + {Nick, Registered} = case get_nick(ServerHost, Host, From) of + error -> {<<"">>, false}; + N -> {N, true} end, - Registered ++ - [#xmlel{name = <<"instructions">>, attrs = [], - children = - [{xmlcdata, - translate:translate(Lang, - <<"You need a client that supports x:data " - "to register the nickname">>)}]}, - #xmlel{name = <<"x">>, - attrs = [{<<"xmlns">>, ?NS_XDATA}, - {<<"type">>, <<"form">>}], - children = - [#xmlel{name = <<"title">>, attrs = [], - children = - [{xmlcdata, - <<(translate:translate(Lang, - <<"Nickname Registration at ">>))/binary, - Host/binary>>}]}, - #xmlel{name = <<"instructions">>, attrs = [], - children = - [{xmlcdata, - translate:translate(Lang, - <<"Enter nickname you want to register">>)}]}, - ?XFIELD(<<"text-single">>, <<"Nickname">>, <<"nick">>, - Nick)]}]. + Title = <<(translate:translate( + Lang, <<"Nickname Registration at ">>))/binary, Host/binary>>, + Inst = translate:translate(Lang, <<"Enter nickname you want to register">>), + Fields = muc_register:encode( + [{roomnick, Nick}], + fun(T) -> translate:translate(Lang, T) end), + X = #xdata{type = form, title = Title, + instructions = [Inst], fields = Fields}, + #register{nick = Nick, + registered = Registered, + instructions = + translate:translate( + Lang, <<"You need a client that supports x:data " + "to register the nickname">>), + xdata = X}. set_nick(ServerHost, Host, From, Nick) -> LServer = jid:nameprep(ServerHost), @@ -813,66 +730,44 @@ set_nick(ServerHost, Host, From, Nick) -> iq_set_register_info(ServerHost, Host, From, Nick, Lang) -> case set_nick(ServerHost, Host, From, Nick) of - {atomic, ok} -> {result, []}; + {atomic, ok} -> {result, undefined}; {atomic, false} -> ErrText = <<"That nickname is registered by another " "person">>, - {error, ?ERRT_CONFLICT(Lang, ErrText)}; + {error, xmpp:err_conflict(ErrText, Lang)}; _ -> Txt = <<"Database failure">>, - {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)} + {error, xmpp:err_internal_server_error(Txt, Lang)} end. -process_iq_register_set(ServerHost, Host, From, SubEl, - Lang) -> - #xmlel{children = Els} = SubEl, - case fxml:get_subtag(SubEl, <<"remove">>) of - false -> - case fxml:remove_cdata(Els) of - [#xmlel{name = <<"x">>} = XEl] -> - case {fxml:get_tag_attr_s(<<"xmlns">>, XEl), - fxml:get_tag_attr_s(<<"type">>, XEl)} - of - {?NS_XDATA, <<"cancel">>} -> {result, []}; - {?NS_XDATA, <<"submit">>} -> - XData = jlib:parse_xdata_submit(XEl), - case XData of - invalid -> - Txt = <<"Incorrect data form">>, - {error, ?ERRT_BAD_REQUEST(Lang, Txt)}; - _ -> - case lists:keysearch(<<"nick">>, 1, XData) of - {value, {_, [Nick]}} when Nick /= <<"">> -> - iq_set_register_info(ServerHost, Host, From, - Nick, Lang); - _ -> - ErrText = - <<"You must fill in field \"Nickname\" " - "in the form">>, - {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)} - end - end; - _ -> {error, ?ERR_BAD_REQUEST} - end; - _ -> {error, ?ERR_BAD_REQUEST} - end; - _ -> - iq_set_register_info(ServerHost, Host, From, <<"">>, - Lang) +process_iq_register_set(ServerHost, Host, From, + #register{remove = true}, Lang) -> + iq_set_register_info(ServerHost, Host, From, <<"">>, Lang); +process_iq_register_set(_ServerHost, _Host, _From, + #register{xdata = #xdata{type = cancel}}, _Lang) -> + {result, undefined}; +process_iq_register_set(ServerHost, Host, From, + #register{nick = Nick, xdata = XData}, Lang) -> + case XData of + #xdata{type = submit, fields = Fs} -> + try + Options = muc_register:decode(Fs), + N = proplists:get_value(roomnick, Options), + iq_set_register_info(ServerHost, Host, From, N, Lang) + catch _:{muc_register, Why} -> + ErrText = muc_register:format_error(Why), + {error, xmpp:err_bad_request(ErrText, Lang)} + end; + #xdata{} -> + Txt = <<"Incorrect data form">>, + {error, xmpp:err_bad_request(Txt, Lang)}; + _ when is_binary(Nick), Nick /= <<"">> -> + iq_set_register_info(ServerHost, Host, From, Nick, Lang); + _ -> + ErrText = <<"You must fill in field \"Nickname\" in the form">>, + {error, xmpp:err_not_acceptable(ErrText, Lang)} end. -iq_get_vcard(Lang) -> - [#xmlel{name = <<"FN">>, attrs = [], - children = [{xmlcdata, <<"ejabberd/mod_muc">>}]}, - #xmlel{name = <<"URL">>, attrs = [], - children = [{xmlcdata, ?EJABBERD_URI}]}, - #xmlel{name = <<"DESC">>, attrs = [], - children = - [{xmlcdata, - <<(translate:translate(Lang, - <<"ejabberd MUC module">>))/binary, - "\nCopyright (c) 2003-2016 ProcessOne">>}]}]. - broadcast_service_message(Host, Msg) -> lists:foreach( fun(#muc_online_room{pid = Pid}) -> |