From d05c0eb41e8f08577caf599f5edb42e3fffc0b6a Mon Sep 17 00:00:00 2001 From: Piotr Nosek Date: Thu, 23 Mar 2017 13:12:56 +0100 Subject: [PATCH 1/2] Rock mod_muc*.erl --- apps/ejabberd/include/mod_muc_light.hrl | 4 + apps/ejabberd/include/mod_muc_room.hrl | 2 + apps/ejabberd/src/mod_muc.erl | 309 ++- apps/ejabberd/src/mod_muc_iq.erl | 19 +- apps/ejabberd/src/mod_muc_light_db.erl | 2 +- apps/ejabberd/src/mod_muc_light_db_mnesia.erl | 7 +- apps/ejabberd/src/mod_muc_light_room.erl | 8 +- apps/ejabberd/src/mod_muc_light_utils.erl | 2 +- apps/ejabberd/src/mod_muc_log.erl | 303 +-- apps/ejabberd/src/mod_muc_room.erl | 1651 ++++++++--------- 10 files changed, 1098 insertions(+), 1209 deletions(-) diff --git a/apps/ejabberd/include/mod_muc_light.hrl b/apps/ejabberd/include/mod_muc_light.hrl index 19abf9e7e8a..d5f4f1ff13e 100644 --- a/apps/ejabberd/include/mod_muc_light.hrl +++ b/apps/ejabberd/include/mod_muc_light.hrl @@ -75,6 +75,8 @@ raw_config = [] :: raw_config() }). +-type op_config() :: #config{}. + -record(affiliations, { id = <<>> :: binary(), prev_version = <<>> :: binary(), @@ -82,6 +84,8 @@ aff_users = [] :: aff_users() }). +-type op_affiliations() :: #affiliations{}. + -record(info, { id = <<>> :: binary(), prev_version = <<>> :: binary(), diff --git a/apps/ejabberd/include/mod_muc_room.hrl b/apps/ejabberd/include/mod_muc_room.hrl index e8a07e2c88a..91b14077eea 100644 --- a/apps/ejabberd/include/mod_muc_room.hrl +++ b/apps/ejabberd/include/mod_muc_room.hrl @@ -59,6 +59,8 @@ last_presence }). +-type mod_muc_room_user() :: #user{}. + -record(activity, {message_time = 0, presence_time = 0, message_shaper :: shaper:shaper(), diff --git a/apps/ejabberd/src/mod_muc.erl b/apps/ejabberd/src/mod_muc.erl index 17a21d742e6..9850ac00aa6 100644 --- a/apps/ejabberd/src/mod_muc.erl +++ b/apps/ejabberd/src/mod_muc.erl @@ -285,7 +285,6 @@ init([Host, Opts]) -> mnesia:add_table_copy(muc_registered, node(), disc_copies), catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]), MyHost = gen_mod:get_opt_subhost(Host, Opts, default_host()), - update_tables(MyHost), clean_table_from_bad_node(node(), MyHost), mnesia:add_table_index(muc_registered, nick), mnesia:subscribe(system), @@ -662,8 +661,8 @@ route_by_type(<<"message">>, {From, To, Packet}, broadcast_service_message(Host, Msg); _ -> Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), - ErrText = <<"Only service administrators are allowed to send service messages">>, - Err = ?ERRT_FORBIDDEN(Lang, ErrText), + ErrTxt = <<"Only service administrators are allowed to send service messages">>, + Err = ?ERRT_FORBIDDEN(Lang, ErrTxt), ErrorReply = jlib:make_error_reply(Packet, Err), ejabberd_router:route(To, From, ErrorReply) end @@ -688,35 +687,36 @@ check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) -> Access :: access(), HistorySize :: 'undefined' | integer(), RoomShaper :: shaper:shaper(), HttpAuthPool :: none | mongoose_http_client:pool()) -> 'ok'. load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper, HttpAuthPool) -> + RoomsToLoad = case catch mnesia:dirty_select( muc_room, [{#muc_room{name_host = {'_', Host}, _ = '_'}, [], ['$_']}]) of {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), - ok; + []; Rs -> - lists:foreach( - fun(R) -> - {Room, Host} = R#muc_room.name_host, - case mnesia:dirty_read(muc_online_room, {Room, Host}) of - [] -> - {ok, Pid} = mod_muc_room:start( - Host, - ServerHost, - Access, - Room, - HistorySize, - RoomShaper, - HttpAuthPool, - R#muc_room.opts), - register_room(Host, Room, Pid); - _ -> - ok - end - end, Rs) - end. - + Rs + end, + lists:foreach( + fun(R) -> + {Room, Host} = R#muc_room.name_host, + case mnesia:dirty_read(muc_online_room, {Room, Host}) of + [] -> + {ok, Pid} = mod_muc_room:start( + Host, + ServerHost, + Access, + Room, + HistorySize, + RoomShaper, + HttpAuthPool, + R#muc_room.opts), + register_room(Host, Room, Pid); + _ -> + ok + end + end, RoomsToLoad). -spec start_new_room(Host :: 'undefined' | ejabberd:server(), Srv :: ejabberd:server(), Access :: access(), room(), @@ -836,20 +836,20 @@ get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index}) -> [{#muc_online_room{name_host = '$1', _ = '_'}, Guard, ['$_']}])), - L2 = if - Index == undefined andalso Direction == before -> + L2 = case {Index, Direction} of + {undefined, before} -> lists:reverse(lists:sublist(lists:reverse(L), 1, M)); - Index == undefined -> + {undefined, _} -> lists:sublist(L, 1, M); - Index > Count orelse Index < 0 -> + {Index, _} when Index > Count orelse Index < 0 -> []; - true -> + _ -> lists:sublist(L, Index+1, M) end, - if - L2 == [] -> + case L2 of + [] -> {L2, #rsm_out{count=Count}}; - true -> + _ -> H = hd(L2), NewIndex = get_room_pos(H, AllRooms), T=lists:last(L2), @@ -911,26 +911,30 @@ iq_get_register_info(Host, From, Lang) -> {LUser, LServer, _} = jid:to_lower(From), LUS = {LUser, LServer}, {Nick, Registered} = - case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of - {'EXIT', _Reason} -> - {<<>>, []}; - [] -> - {<<>>, []}; - [#muc_registered{nick = N}] -> - {N, [#xmlel{name = <<"registered">>}]} - end, + case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of + {'EXIT', _Reason} -> + {<<>>, []}; + [] -> + {<<>>, []}; + [#muc_registered{nick = N}] -> + {N, [#xmlel{name = <<"registered">>}]} + end, + ClientReqText = translate:translate( + Lang, <<"You need a client that supports x:data to register the nickname">>), + ClientReqEl = #xmlel{name = <<"instructions">>, + children = [#xmlcdata{content = ClientReqText}]}, + EnterNicknameText = translate:translate(Lang, <<"Enter nickname you want to register">>), + EnterNicknameEl = #xmlel{name = <<"instructions">>, + children = [#xmlcdata{content = EnterNicknameText}]}, + TitleText = <<(translate:translate(Lang, <<"Nickname Registration at ">>))/binary, + Host/binary>>, + TitleEl = #xmlel{name = <<"title">>, children = [#xmlcdata{content = TitleText}]}, Registered ++ - [#xmlel{name = <<"instructions">>, - children = [#xmlcdata{content = translate:translate( - Lang, <<"You need a client that supports x:data to register the nickname">>)}]}, - #xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], - children = [#xmlel{name = <<"title">>, - children = [#xmlcdata{content = <<(translate:translate( - Lang, <<"Nickname Registration at ">>))/binary, Host/binary>>}]}, - #xmlel{name = <<"instructions">>, - children = [#xmlcdata{content = translate:translate( - Lang, <<"Enter nickname you want to register">>)}]}, - xfield(<<"text-single">>, <<"Nickname">>, <<"nick">>, Nick, Lang)]}]. + [ClientReqEl, + #xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], + children = [TitleEl, + EnterNicknameEl, + xfield(<<"text-single">>, <<"Nickname">>, <<"nick">>, Nick, Lang)]}]. -spec iq_set_register_info(ejabberd:server(), @@ -939,37 +943,7 @@ iq_get_register_info(Host, From, Lang) -> iq_set_register_info(Host, From, Nick, Lang) -> {LUser, LServer, _} = jid:to_lower(From), LUS = {LUser, LServer}, - F = fun() -> - case Nick of - <<>> -> - mnesia:delete({muc_registered, {LUS, Host}}), - ok; - _ -> - Allow = - case mnesia:select( - muc_registered, - [{#muc_registered{us_host = '$1', - nick = Nick, - _ = '_'}, - [{'==', {element, 2, '$1'}, Host}], - ['$_']}]) of - [] -> - true; - [#muc_registered{us_host = {U, _Host}}] -> - U == LUS - end, - if - Allow -> - mnesia:write( - #muc_registered{us_host = {LUS, Host}, - nick = Nick}), - ok; - true -> - false - end - end - end, - case mnesia:transaction(F) of + case mnesia:transaction(iq_set_register_info_t(Host, LUS, Nick)) of {atomic, ok} -> {result, []}; {atomic, false} -> @@ -979,37 +953,42 @@ iq_set_register_info(Host, From, Nick, Lang) -> {error, ?ERR_INTERNAL_SERVER_ERROR} end. +-spec iq_set_register_info_t(Host :: ejabberd:server(), LUS :: ejabberd:simple_bare_jid(), + Nick :: binary()) -> fun(() -> ok | false). +iq_set_register_info_t(Host, LUS, <<>>) -> + fun() -> + mnesia:delete({muc_registered, {LUS, Host}}), + ok + end; +iq_set_register_info_t(Host, LUS, Nick) -> + Allow = + case mnesia:select(muc_registered, + [{#muc_registered{us_host = '$1', nick = Nick, _ = '_'}, + [{'==', {element, 2, '$1'}, Host}], + ['$_']}]) of + [] -> + true; + [#muc_registered{us_host = {U, _Host}}] -> + U == LUS + end, + case Allow of + true -> + mnesia:write(#muc_registered{us_host = {LUS, Host}, nick = Nick}), + ok; + false -> + false + end. --spec process_iq_register_set(ejabberd:server(), ejabberd:jid(), - jlib:xmlel(), ejabberd:lang()) - -> {'error', jlib:xmlel()} | {'result', []}. -process_iq_register_set(Host, From, SubEl, Lang) -> - #xmlel{children = Els} = SubEl, +-spec process_iq_register_set(ejabberd:server(), jid(), exml:element(), ejabberd:lang()) -> + {error, exml:element()} | {result, []}. +process_iq_register_set(Host, From, #xmlel{ children = Els } = SubEl, Lang) -> case xml:get_subtag(SubEl, <<"remove">>) of false -> case xml:remove_cdata(Els) of [#xmlel{name = <<"x">>} = XEl] -> - case {xml:get_tag_attr_s(<<"xmlns">>, XEl), - xml:get_tag_attr_s(<<"type">>, XEl)} of - {?NS_XDATA, <<"cancel">>} -> - {result, []}; - {?NS_XDATA, <<"submit">>} -> - XData = jlib:parse_xdata_submit(XEl), - case XData of - invalid -> - {error, ?ERR_BAD_REQUEST}; - _ -> - case lists:keysearch(<<"nick">>, 1, XData) of - {value, {_, [Nick]}} when Nick /= <<>> -> - iq_set_register_info(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; + process_register(xml:get_tag_attr_s(<<"xmlns">>, XEl), + xml:get_tag_attr_s(<<"type">>, XEl), + Host, From, Lang, XEl); _ -> {error, ?ERR_BAD_REQUEST} end; @@ -1017,6 +996,27 @@ process_iq_register_set(Host, From, SubEl, Lang) -> iq_set_register_info(Host, From, <<>>, Lang) end. +-spec process_register(XMLNS :: binary(), Type :: binary(), Host :: ejabberd:server(), + From :: jid(), Lang :: ejabberd:lang(), XEl :: exml:element()) -> + {error, exml:element()} | {result, []}. +process_register(?NS_XDATA, <<"cancel">>, _Host, _From, _Lang, _XEl) -> + {result, []}; +process_register(?NS_XDATA, <<"submit">>, Host, From, Lang, XEl) -> + XData = jlib:parse_xdata_submit(XEl), + case XData of + invalid -> + {error, ?ERR_BAD_REQUEST}; + _ -> + case lists:keysearch(<<"nick">>, 1, XData) of + {value, {_, [Nick]}} when Nick /= <<>> -> + iq_set_register_info(Host, From, Nick, Lang); + _ -> + ErrText = <<"You must fill in field \"Nickname\" in the form">>, + {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)} + end + end; +process_register(_, _, _Host, _From, _Lang, _XEl) -> + {error, ?ERR_BAD_REQUEST}. -spec iq_get_vcard(ejabberd:lang()) -> [jlib:xmlel(), ...]. iq_get_vcard(Lang) -> @@ -1077,97 +1077,6 @@ clean_table_from_bad_node(Node, Host) -> end, mnesia:async_dirty(F). - --spec update_tables(ejabberd:server()) -> any(). -update_tables(Host) -> - update_muc_room_table(Host), - update_muc_registered_table(Host). - - --spec update_muc_room_table(ejabberd:server()) -> any(). -update_muc_room_table(Host) -> - Fields = record_info(fields, muc_room), - case mnesia:table_info(muc_room, attributes) of - Fields -> - ok; - [name, opts] -> - ?INFO_MSG("Converting muc_room table from {name, opts} format", []), - {atomic, ok} = mnesia:create_table( - mod_muc_tmp_table, - [{disc_only_copies, [node()]}, - {type, bag}, - {local_content, true}, - {record_name, muc_room}, - {attributes, record_info(fields, muc_room)}]), - mnesia:transform_table(muc_room, ignore, Fields), - F1 = fun() -> - mnesia:write_lock_table(mod_muc_tmp_table), - mnesia:foldl( - fun(#muc_room{name_host = Name} = R, _) -> - mnesia:dirty_write( - mod_muc_tmp_table, - R#muc_room{name_host = {Name, Host}}) - end, ok, muc_room) - end, - mnesia:transaction(F1), - mnesia:clear_table(muc_room), - F2 = fun() -> - mnesia:write_lock_table(muc_room), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, mod_muc_tmp_table) - end, - mnesia:transaction(F2), - mnesia:delete_table(mod_muc_tmp_table); - _ -> - ?INFO_MSG("Recreating muc_room table", []), - mnesia:transform_table(muc_room, ignore, Fields) - end. - - --spec update_muc_registered_table(ejabberd:server()) -> any(). -update_muc_registered_table(Host) -> - Fields = record_info(fields, muc_registered), - case mnesia:table_info(muc_registered, attributes) of - Fields -> - ok; - [user, nick] -> - ?INFO_MSG("Converting muc_registered table from {user, nick} format", []), - {atomic, ok} = mnesia:create_table( - mod_muc_tmp_table, - [{disc_only_copies, [node()]}, - {type, bag}, - {local_content, true}, - {record_name, muc_registered}, - {attributes, record_info(fields, muc_registered)}]), - mnesia:del_table_index(muc_registered, nick), - mnesia:transform_table(muc_registered, ignore, Fields), - F1 = fun() -> - mnesia:write_lock_table(mod_muc_tmp_table), - mnesia:foldl( - fun(#muc_registered{us_host = US} = R, _) -> - mnesia:dirty_write( - mod_muc_tmp_table, - R#muc_registered{us_host = {US, Host}}) - end, ok, muc_registered) - end, - mnesia:transaction(F1), - mnesia:clear_table(muc_registered), - F2 = fun() -> - mnesia:write_lock_table(muc_registered), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, mod_muc_tmp_table) - end, - mnesia:transaction(F2), - mnesia:delete_table(mod_muc_tmp_table); - _ -> - ?INFO_MSG("Recreating muc_registered table", []), - mnesia:transform_table(muc_registered, ignore, Fields) - end. - %%==================================================================== %% Hooks handlers %%==================================================================== diff --git a/apps/ejabberd/src/mod_muc_iq.erl b/apps/ejabberd/src/mod_muc_iq.erl index 112ab023937..2f3894c652e 100644 --- a/apps/ejabberd/src/mod_muc_iq.erl +++ b/apps/ejabberd/src/mod_muc_iq.erl @@ -1,8 +1,8 @@ %% @doc Stores a table of custom IQ-handlers for mod_muc_room. -module(mod_muc_iq). + -export([start_link/0, process_iq/4, - register_iq_handler/4, register_iq_handler/5, unregister_iq_handler/2]). @@ -39,9 +39,6 @@ start_link() -> ejabberd:iq()) -> error | ignore | any(). process_iq(Host, From, RoomJID, IQ = #iq{xmlns = XMLNS}) -> case ets:lookup(tbl_name(), {XMLNS, Host}) of - [{_, Module, Function}] -> - %% TODO: Introduce some stricter type checking here - Module:Function(From, RoomJID, IQ); [{_, Module, Function, Opts}] -> gen_iq_handler:handle(Host, Module, Function, Opts, From, RoomJID, IQ), @@ -50,20 +47,13 @@ process_iq(Host, From, RoomJID, IQ = #iq{xmlns = XMLNS}) -> end. --spec register_iq_handler(ejabberd:server(), binary(), module(), atom()) -> 'ok'. -register_iq_handler(Host, XMLNS, Module, Fun) -> - gen_server:cast(srv_name(), - {register_iq_handler, Host, XMLNS, Module, Fun}). - - --spec register_iq_handler(ejabberd:server(), binary(), module(), atom(), any()) - -> 'ok'. +-spec register_iq_handler(ejabberd:server(), binary(), module(), atom(), any()) -> ok. register_iq_handler(Host, XMLNS, Module, Fun, Opts) -> gen_server:cast(srv_name(), {register_iq_handler, Host, XMLNS, Module, Fun, Opts}). --spec unregister_iq_handler(ejabberd:server(), binary()) -> 'ok'. +-spec unregister_iq_handler(ejabberd:server(), binary()) -> ok. unregister_iq_handler(Host, XMLNS) -> gen_server:cast(srv_name(), {unregister_iq_handler, Host, XMLNS}). @@ -103,9 +93,6 @@ handle_call(_Request, _From, State) -> %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast({register_iq_handler, Host, XMLNS, Module, Function}, State) -> - ets:insert(tbl_name(), {{XMLNS, Host}, Module, Function}), - {noreply, State}; handle_cast({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) -> ets:insert(tbl_name(), {{XMLNS, Host}, Module, Function, Opts}), {noreply, State}; diff --git a/apps/ejabberd/src/mod_muc_light_db.erl b/apps/ejabberd/src/mod_muc_light_db.erl index f4c9fc58f6a..b59c739aa06 100644 --- a/apps/ejabberd/src/mod_muc_light_db.erl +++ b/apps/ejabberd/src/mod_muc_light_db.erl @@ -15,7 +15,7 @@ | {error, any()}. -type remove_user_return() :: [{RoomUS :: ejabberd:simple_bare_jid(), - modify_aff_users_return()}]. + modify_aff_users_return()}]. -export_type([modify_aff_users_return/0, remove_user_return/0]). diff --git a/apps/ejabberd/src/mod_muc_light_db_mnesia.erl b/apps/ejabberd/src/mod_muc_light_db_mnesia.erl index 81b9bdcfd93..a62508f411a 100644 --- a/apps/ejabberd/src/mod_muc_light_db_mnesia.erl +++ b/apps/ejabberd/src/mod_muc_light_db_mnesia.erl @@ -64,6 +64,9 @@ -define(USER_ROOM_TAB, muc_light_user_room). -define(BLOCKING_TAB, muc_light_blocking). +-type muc_light_room() :: ?ROOM_TAB. +-type muc_light_blocking() :: ?BLOCKING_TAB. + -record(?ROOM_TAB, { room :: ejabberd:simple_bare_jid(), config :: [{atom(), term()}], @@ -359,7 +362,7 @@ set_config_transaction(RoomUS, ConfigChanges, Version) -> %% ------------------------ Blocking manipulation ------------------------ --spec dirty_get_blocking_raw(UserUS :: ejabberd:simple_bare_jid()) -> [#?BLOCKING_TAB{}]. +-spec dirty_get_blocking_raw(UserUS :: ejabberd:simple_bare_jid()) -> [muc_light_blocking()]. dirty_get_blocking_raw(UserUS) -> mnesia:dirty_read(?BLOCKING_TAB, UserUS). @@ -385,7 +388,7 @@ modify_aff_users_transaction(RoomUS, AffUsersChanges, ExternalCheck, Version) -> end. -spec verify_externally_and_submit(RoomUS :: ejabberd:simple_bare_jid(), - RoomRec :: #?ROOM_TAB{}, + RoomRec :: muc_light_room(), ChangeResult :: mod_muc_light_utils:change_aff_success(), CheckResult :: ok | {error, any()}, Version :: binary()) -> diff --git a/apps/ejabberd/src/mod_muc_light_room.erl b/apps/ejabberd/src/mod_muc_light_room.erl index e18a4b19294..c36de848b39 100644 --- a/apps/ejabberd/src/mod_muc_light_room.erl +++ b/apps/ejabberd/src/mod_muc_light_room.erl @@ -146,10 +146,10 @@ process_request(_UnknownReq, _From, _UserUS, _RoomUS, _Auth, _AffUsers) -> %% --------- Config set --------- --spec process_config_set(ConfigReq :: #config{}, RoomUS :: ejabberd:simple_bare_jid(), +-spec process_config_set(ConfigReq :: op_config(), RoomUS :: ejabberd:simple_bare_jid(), UserAff :: member | owner, AffUsers :: aff_users(), UserAllowedToConfigure :: boolean()) -> - {set, #config{}} | {error, not_allowed} | validation_error(). + {set, op_config()} | {error, not_allowed} | validation_error(). process_config_set(#config{ raw_config = [{<<"subject">>, _}] } = ConfigReq, RoomUS, UserAff, AffUsers, false) -> % Everyone is allowed to change subject @@ -193,10 +193,10 @@ validate_aff_changes_by_member([{_, member} = AffUserChange | RAffUsersChanges], validate_aff_changes_by_member(_AffUsersChanges, _Acc, _UserUS, _OwnerUS, _RoomUS, _AllCanInvite) -> {error, not_allowed}. --spec process_aff_set(AffReq :: #affiliations{}, +-spec process_aff_set(AffReq :: op_affiliations(), RoomUS :: ejabberd:simple_bare_jid(), ValidateResult :: {ok, aff_users()} | {error, not_allowed}) -> - {set, #affiliations{}, OldAffUsers :: aff_users(), NewAffUsers :: aff_users()} + {set, op_affiliations(), OldAffUsers :: aff_users(), NewAffUsers :: aff_users()} | {error, not_allowed}. process_aff_set(AffReq, _RoomUS, {ok, []}) -> % It seems that all users blocked this request {set, AffReq, [], []}; % Just return result to the user, don't change or broadcast anything diff --git a/apps/ejabberd/src/mod_muc_light_utils.erl b/apps/ejabberd/src/mod_muc_light_utils.erl index 478d662ee66..3056b93f7fa 100644 --- a/apps/ejabberd/src/mod_muc_light_utils.erl +++ b/apps/ejabberd/src/mod_muc_light_utils.erl @@ -315,7 +315,7 @@ apply_aff_users_change([{User1, _} | _] = AU, NAU, [{User2, member} | RAUC], CD, apply_aff_users_change([{User1, _} | _] = _AU, _NAU, [{User2, none} | _RAUC], _CD, _NO, _JA, _LA) when User1 > User2 -> % Meaningless change - user not in the room - {error, bad_request}; + {error, bad_request}; apply_aff_users_change([{User1, _} | _] = AU, NAU, [{User2, _} = NewAffUser | RAUC], CD, NO, JA, LA) when User1 > User2 -> %% Adding new member to a room - owner or member diff --git a/apps/ejabberd/src/mod_muc_log.erl b/apps/ejabberd/src/mod_muc_log.erl index 15517da7b91..8690b7aa410 100644 --- a/apps/ejabberd/src/mod_muc_log.erl +++ b/apps/ejabberd/src/mod_muc_log.erl @@ -48,7 +48,9 @@ -define(T(Text), translate:translate(Lang, Text)). -define(PROCNAME, ejabberd_mod_muc_log). + -record(room, {jid, title, subject, subject_author, config}). +-type room() :: #room{}. -type command() :: 'join' | 'kickban' @@ -158,7 +160,7 @@ init([Host, Opts]) -> AccessLog = gen_mod:get_opt(access_log, Opts, muc_admin), Timezone = gen_mod:get_opt(timezone, Opts, local), {TL1, TL2} = gen_mod:get_opt(top_link, Opts, {"/", "Home"}), - Top_link = {list_to_binary(TL1), list_to_binary(TL2)}, + TopLink = {list_to_binary(TL1), list_to_binary(TL2)}, NoFollow = gen_mod:get_opt(spam_prevention, Opts, true), Lang = case ejabberd_config:get_local_option({language, Host}) of undefined -> @@ -178,7 +180,7 @@ init([Host, Opts]) -> lang = Lang, timezone = Timezone, spam_prevention = NoFollow, - top_link = Top_link}}. + top_link = TopLink}}. %%-------------------------------------------------------------------- %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | @@ -360,11 +362,11 @@ get_timestamp_daydiff(TimeStamp, Daydiff) -> %% @doc Try to close the previous day log, if it exists -spec close_previous_log(binary(), any(), file_format()) -> 'ok' | {'error', atom()}. -close_previous_log(Fn, Images_dir, FileFormat) -> +close_previous_log(Fn, ImagesDir, FileFormat) -> case file:read_file_info(Fn) of {ok, _} -> {ok, F} = file:open(Fn, [append]), - write_last_lines(F, Images_dir, FileFormat), + write_last_lines(F, ImagesDir, FileFormat), file:close(F); _ -> ok end. @@ -375,11 +377,17 @@ write_last_lines(_, _, plaintext) -> ok; write_last_lines(F, ImagesDir, _FileFormat) -> fw(F, <<"
">>), - fw(F, <<" \"Powered">>), - fw(F, <<" \"Powered">>), + fw(F, <<" \"Powered">>), + fw(F, <<" \"Powered">>), fw(F, <<"">>), - fw(F, <<" \"Valid">>), - fw(F, <<" \"Valid">>), + fw(F, <<" " + "\"Valid">>), + fw(F, <<" " + "\"Valid">>), fw(F, <<"
">>). @@ -404,7 +412,8 @@ add_message_to_log(Nick1, Message, RoomJID, Opts, State) -> local -> calendar:now_to_local_time(Now); universal -> calendar:now_to_universal_time(Now) end, - {Fd, Fn, _Dir} = build_filename_string(TimeStamp, OutDir, Room#room.jid, DirType, DirName, FileFormat), + {Fd, Fn, _Dir} = build_filename_string(TimeStamp, OutDir, Room#room.jid, + DirType, DirName, FileFormat), {Date, Time} = TimeStamp, %% Open file, create if it does not exist, create parent dirs if needed @@ -430,75 +439,86 @@ add_message_to_log(Nick1, Message, RoomJID, Opts, State) -> put_header(F, Room, Datestring, CSSFile, Lang, HourOffset, DatePrev, DateNext, TopLink, FileFormat, OccupantsMap), - Images_dir = <>, - file:make_dir(Images_dir), - create_image_files(Images_dir), - Images_url = case DirType of + ImagesDir = <>, + file:make_dir(ImagesDir), + create_image_files(ImagesDir), + ImagesUrl = case DirType of subdirs -> <<"../../../images">>; plain -> <<"../images">> end, - close_previous_log(FnYesterday, Images_url, FileFormat) + close_previous_log(FnYesterday, ImagesUrl, FileFormat) end, %% Build message - Text = case Message of - roomconfig_change -> - RoomConfig = roomconfig_to_binary(Room#room.config, Lang, FileFormat), - put_room_config(F, RoomConfig, Lang, FileFormat), - <<"", (?T(<<"Chatroom configuration modified">>))/binary, "
">>; - {roomconfig_change, Occupants} -> - RoomConfig = roomconfig_to_binary(Room#room.config, Lang, FileFormat), - put_room_config(F, RoomConfig, Lang, FileFormat), - RoomOccupants = roomoccupants_to_binary(Occupants, FileFormat), - put_room_occupants(F, RoomOccupants, Lang, FileFormat), - <<"", (?T(<<"Chatroom configuration modified">>))/binary, "
">>; - join -> - <<"", Nick/binary, " ", (?T(<<"joins the room">>))/binary, "
">>; - leave -> - <<"", Nick/binary, " ", (?T(<<"leaves the room">>))/binary, "
">>; - {leave, Reason} -> - <<"", Nick/binary, " ", (?T(<<"leaves the room">>))/binary, ": ", - (htmlize(Reason, NoFollow, FileFormat))/binary, ": ~s
">>; - {kickban, "301", ""} -> - <<"", Nick/binary, " ", (?T(<<"has been banned">>))/binary, "
">>; - {kickban, "301", Reason} -> - <<"", Nick/binary, " ", (?T(<<"has been banned">>))/binary, ": ", - (htmlize(Reason, FileFormat))/binary, "
">>; - {kickban, "307", ""} -> - <<"", Nick/binary, " ", (?T(<<"has been kicked">>))/binary, "
">>; - {kickban, "307", Reason} -> - <<"", Nick/binary, " ", (?T(<<"has been kicked">>))/binary, ": ", - (htmlize(Reason, FileFormat))/binary, "
">>; - {kickban, "321", ""} -> - <<"", Nick/binary, " ", - (?T(<<"has been kicked because of an affiliation change">>))/binary, "
">>; - {kickban, "322", ""} -> - <<"", Nick/binary, " ", - (?T(<<"has been kicked because the room has been changed to members-only">>))/binary, "
">>; - {kickban, "332", ""} -> - <<"", Nick/binary, " ", - (?T(<<"has been kicked because of a system shutdown">>))/binary, "
">>; - {nickchange, OldNick} -> - <<"", (htmlize(OldNick, FileFormat))/binary, " ", - (?T(<<"is now known as">>))/binary, " ", Nick/binary, "
">>; - {subject, T} -> - <<"", Nick/binary, (?T(<<" has set the subject to: ">>))/binary, - (htmlize(T, NoFollow, FileFormat))/binary, "
">>; - {body, T} -> - case {re:run(T, <<"^/me\s">>, [{capture, none}]), Nick} of - {_, ""} -> - <<"", (htmlize(T, NoFollow, FileFormat))/binary, "
">>; - {match, _} -> - %% Delete "/me " from the beginning. - <<_Pref:32, SubStr/binary>> = htmlize(T, FileFormat), - <<"", Nick/binary, " ", SubStr/binary, "
">>; - {nomatch, _} -> - <<"", Nick2/binary, " ", - (htmlize(T, NoFollow, FileFormat))/binary, "
">> - end; - {room_existence, RoomNewExistence} -> - <<"", (get_room_existence_string(RoomNewExistence, Lang))/binary, "
">> - end, + Text + = case Message of + roomconfig_change -> + RoomConfig = roomconfig_to_binary(Room#room.config, Lang, FileFormat), + put_room_config(F, RoomConfig, Lang, FileFormat), + <<"", (?T(<<"Chatroom configuration modified">>))/binary, + "
">>; + {roomconfig_change, Occupants} -> + RoomConfig = roomconfig_to_binary(Room#room.config, Lang, FileFormat), + put_room_config(F, RoomConfig, Lang, FileFormat), + RoomOccupants = roomoccupants_to_binary(Occupants, FileFormat), + put_room_occupants(F, RoomOccupants, Lang, FileFormat), + <<"", (?T(<<"Chatroom configuration modified">>))/binary, + "
">>; + join -> + <<"", Nick/binary, " ", (?T(<<"joins the room">>))/binary, + "
">>; + leave -> + <<"", Nick/binary, " ", (?T(<<"leaves the room">>))/binary, + "
">>; + {leave, Reason} -> + <<"", Nick/binary, " ", (?T(<<"leaves the room">>))/binary, ": ", + (htmlize(Reason, NoFollow, FileFormat))/binary, ": ~s
">>; + {kickban, "301", ""} -> + <<"", Nick/binary, " ", (?T(<<"has been banned">>))/binary, + "
">>; + {kickban, "301", Reason} -> + <<"", Nick/binary, " ", (?T(<<"has been banned">>))/binary, ": ", + (htmlize(Reason, FileFormat))/binary, "
">>; + {kickban, "307", ""} -> + <<"", Nick/binary, " ", (?T(<<"has been kicked">>))/binary, + "
">>; + {kickban, "307", Reason} -> + <<"", Nick/binary, " ", (?T(<<"has been kicked">>))/binary, ": ", + (htmlize(Reason, FileFormat))/binary, "
">>; + {kickban, "321", ""} -> + <<"", Nick/binary, " ", + (?T(<<"has been kicked because of an affiliation change">>))/binary, + "
">>; + {kickban, "322", ""} -> + <<"", Nick/binary, " ", + (?T(<<"has been kicked because the room has been changed to" + " members-only">>))/binary, "
">>; + {kickban, "332", ""} -> + <<"", Nick/binary, " ", + (?T(<<"has been kicked because of a system shutdown">>))/binary, "
">>; + {nickchange, OldNick} -> + <<"", (htmlize(OldNick, FileFormat))/binary, " ", + (?T(<<"is now known as">>))/binary, " ", Nick/binary, "
">>; + {subject, T} -> + <<"", Nick/binary, (?T(<<" has set the subject to: ">>))/binary, + (htmlize(T, NoFollow, FileFormat))/binary, "
">>; + {body, T} -> + case {re:run(T, <<"^/me\s">>, [{capture, none}]), Nick} of + {_, ""} -> + <<"", (htmlize(T, NoFollow, FileFormat))/binary, + "
">>; + {match, _} -> + %% Delete "/me " from the beginning. + <<_Pref:32, SubStr/binary>> = htmlize(T, FileFormat), + <<"", Nick/binary, " ", SubStr/binary, "
">>; + {nomatch, _} -> + <<"", Nick2/binary, " ", + (htmlize(T, NoFollow, FileFormat))/binary, "
">> + end; + {room_existence, RoomNewExistence} -> + <<"", (get_room_existence_string(RoomNewExistence, Lang))/binary, + "
">> + end, {Hour, Minute, Second} = Time, STime = lists:flatten( io_lib:format("~2..0w:~2..0w:~2..0w", [Hour, Minute, Second])), @@ -709,7 +729,7 @@ image_base64(<<"powered-by-ejabberd.png">>) -> -spec create_image_files(<<_:8, _:_*8>>) -> 'ok'. -create_image_files(Images_dir) -> +create_image_files(ImagesDir) -> Filenames = [<<"powered-by-ejabberd.png">>, <<"powered-by-erlang.png">>, <<"valid-xhtml10.png">>, @@ -717,8 +737,8 @@ create_image_files(Images_dir) -> ], lists:foreach( fun(Filename) -> - Filename_full = filename:join([Images_dir, Filename]), - {ok, F} = file:open(Filename_full, [write]), + FilenameFull = filename:join([ImagesDir, Filename]), + {ok, F} = file:open(FilenameFull, [write]), Image = jlib:decode_base64(image_base64(Filename)), io:format(F, "~s", [Image]), file:close(F) @@ -743,16 +763,18 @@ fw(F, S, FileFormat) -> io:format(F, S2, []). --spec put_header(file:io_device(), Room :: #room{}, Date :: binary(), - CSSFile :: boolean(), Lang :: ejabberd:lang(), Hour_offset :: integer(), - Date_prev :: binary(), Date_next :: binary(), Top_link :: tuple(), +-spec put_header(file:io_device(), Room :: room(), Date :: binary(), + CSSFile :: boolean(), Lang :: ejabberd:lang(), HourOffset :: integer(), + DatePrev :: binary(), DateNext :: binary(), TopLink :: tuple(), file_format(), OccupantsMap :: #{binary() => [jid_nick_role()]}) -> 'ok'. put_header(_, _, _, _, _, _, _, _, _, plaintext, _) -> ok; -put_header(F, Room, Date, CSSFile, Lang, Hour_offset, Date_prev, Date_next, Top_link, FileFormat, +put_header(F, Room, Date, CSSFile, Lang, HourOffset, DatePrev, DateNext, TopLink, FileFormat, OccupantsMap) -> - fw(F, <<"">>), - fw(F, <<"">>), + fw(F, <<"">>), + fw(F, <<"">>), fw(F, <<"">>), fw(F, <<"">>), fw(F, <<"", (htmlize(Room#room.title))/binary, " - ", Date/binary, "">>), @@ -760,25 +782,32 @@ put_header(F, Room, Date, CSSFile, Lang, Hour_offset, Date_prev, Date_next, Top_ put_header_script(F), fw(F, <<"">>), fw(F, <<"">>), - {Top_url, Top_text} = Top_link, - fw(F, <<"
", Top_text/binary, "
">>), + {TopUrl, TopText} = TopLink, + fw(F, <<"
", + TopText/binary, "
">>), fw(F, <<"
", (htmlize(Room#room.title))/binary, "
">>), - fw(F, <<"", (Room#room.jid)/binary, "">>), - fw(F, <<"
", Date/binary, "< ^ >
">>), + fw(F, <<"", + (Room#room.jid)/binary, "">>), + fw(F, <<"
", Date/binary, "< ^" + " >
">>), case {htmlize(Room#room.subject_author), htmlize(Room#room.subject)} of - {<<"">>, <<"">>} -> ok; - {SuA, Su} -> fw(F, <<"
", SuA/binary, (?T(<<" has set the subject to: ">>))/binary, Su/binary, "
">>) + {<<"">>, <<"">>} -> + ok; + {SuA, Su} -> fw(F, <<"
", SuA/binary, + (?T(<<" has set the subject to: ">>))/binary, Su/binary, "
">>) end, RoomConfig = roomconfig_to_binary(Room#room.config, Lang, FileFormat), put_room_config(F, RoomConfig, Lang, FileFormat), Occupants = maps:get(Room#room.jid, OccupantsMap, []), RoomOccupants = roomoccupants_to_binary(Occupants, FileFormat), put_room_occupants(F, RoomOccupants, Lang, FileFormat), - Time_offset_bin = case Hour_offset<0 of - true -> list_to_binary(lists:flatten(io_lib:format("~p", [Hour_offset]))); - false -> list_to_binary(lists:flatten(io_lib:format("+~p", [Hour_offset]))) + TimeOffsetBin = case HourOffset<0 of + true -> list_to_binary(lists:flatten(io_lib:format("~p", [HourOffset]))); + false -> list_to_binary(lists:flatten(io_lib:format("+~p", [HourOffset]))) end, - fw(F, <<"
GMT", Time_offset_bin/binary, "
">>). + fw(F, <<"
GMT", TimeOffsetBin/binary, "
">>). -spec put_header_css(file:io_device(), 'false' | binary()) -> 'ok'. @@ -796,13 +825,22 @@ put_header_css(F, false) -> fw(F, <<".mnc {color: #009900; font-style: italic;}">>), fw(F, <<".mn {color: #0000AA;}">>), fw(F, <<".mne {color: #AA0099;}">>), - fw(F, <<"a.nav {color: #AAAAAA; font-family: monospace; letter-spacing: 3px; text-decoration: none;}">>), + fw(F, <<"a.nav {color: #AAAAAA; font-family: monospace; letter-spacing: 3px;" + " text-decoration: none;}">>), fw(F, <<"div.roomtitle {border-bottom: #224466 solid 3pt; margin-left: 20pt;}">>), - fw(F, <<"div.roomtitle {color: #336699; font-size: 24px; font-weight: bold; font-family: sans-serif; letter-spacing: 3px; text-decoration: none;}">>), - fw(F, <<"a.roomjid {color: #336699; font-size: 24px; font-weight: bold; font-family: sans-serif; letter-spacing: 3px; margin-left: 20pt; text-decoration: none;}">>), - fw(F, <<"div.logdate {color: #663399; font-size: 20px; font-weight: bold; font-family: sans-serif; letter-spacing: 2px; border-bottom: #224466 solid 1pt; margin-left:80pt; margin-top:20px;}">>), - fw(F, <<"div.roomsubject {color: #336699; font-size: 18px; font-family: sans-serif; margin-left: 80pt; margin-bottom: 10px;}">>), - fw(F, <<"div.rc {color: #336699; font-size: 12px; font-family: sans-serif; margin-left: 50%; text-align: right; background: #f3f6f9; border-bottom: 1px solid #336699; border-right: 4px solid #336699;}">>), + fw(F, <<"div.roomtitle {color: #336699; font-size: 24px; font-weight: bold;" + " font-family: sans-serif; letter-spacing: 3px; text-decoration: none;}">>), + fw(F, <<"a.roomjid {color: #336699; font-size: 24px; font-weight: bold;" + " font-family: sans-serif; letter-spacing: 3px; margin-left: 20pt;" + " text-decoration: none;}">>), + fw(F, <<"div.logdate {color: #663399; font-size: 20px; font-weight: bold;" + " font-family: sans-serif; letter-spacing: 2px; border-bottom: #224466 solid 1pt;" + " margin-left:80pt; margin-top:20px;}">>), + fw(F, <<"div.roomsubject {color: #336699; font-size: 18px; font-family: sans-serif;" + " margin-left: 80pt; margin-bottom: 10px;}">>), + fw(F, <<"div.rc {color: #336699; font-size: 12px; font-family: sans-serif; margin-left: 50%;" + " text-align: right; background: #f3f6f9; border-bottom: 1px solid #336699;" + " border-right: 4px solid #336699;}">>), fw(F, <<"div.rct {font-weight: bold; background: #e3e6e9; padding-right: 10px;}">>), fw(F, <<"div.rcos {padding-right: 10px;}">>), fw(F, <<"div.rcoe {color: green;}">>), @@ -810,13 +848,17 @@ put_header_css(F, false) -> fw(F, <<"div.rcoe:after {content: \": v\";}">>), fw(F, <<"div.rcod:after {content: \": x\";}">>), fw(F, <<"div.rcot:after {}">>), - fw(F, <<".legend {width: 100%; margin-top: 30px; border-top: #224466 solid 1pt; padding: 10px 0px 10px 0px; text-align: left; font-family: monospace; letter-spacing: 2px;}">>), - fw(F, <<".w3c {position: absolute; right: 10px; width: 60%; text-align: right; font-family: monospace; letter-spacing: 1px;}">>), + fw(F, <<".legend {width: 100%; margin-top: 30px; border-top: #224466 solid 1pt;" + " padding: 10px 0px 10px 0px; text-align: left;" + " font-family: monospace; letter-spacing: 2px;}">>), + fw(F, <<".w3c {position: absolute; right: 10px; width: 60%; text-align: right;" + " font-family: monospace; letter-spacing: 1px;}">>), fw(F, <<"//-->">>), fw(F, <<"">>); put_header_css(F, CSSFileStr) -> CSSFile = list_to_binary(CSSFileStr), - fw(F, <<"">>). + fw(F, <<"">>). put_header_script(F) -> fw(F, <<"