From 900b013658d2d2a7ca99651b096c94b75a4ce07d Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Thu, 25 Aug 2011 17:22:48 +0200 Subject: [PATCH 01/77] typo fix (thanks to Artem Sh)(EJAB-1424) --- src/mod_pubsub/node_pep_odbc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_pubsub/node_pep_odbc.erl b/src/mod_pubsub/node_pep_odbc.erl index d6cda1cda..44c11cb5a 100644 --- a/src/mod_pubsub/node_pep_odbc.erl +++ b/src/mod_pubsub/node_pep_odbc.erl @@ -216,7 +216,7 @@ get_entity_subscriptions(_Host, Owner) -> {selected, ["host", "node", "type", "nodeid", "jid", "subscriptions"], RItems} -> lists:map(fun({H, N, T, I, J, S}) -> O = node_hometree_odbc:decode_jid(H), - Node = nodetree_odbc:raw_to_node(O, {N, "", T, I}), + Node = nodetree_tree_odbc:raw_to_node(O, {N, "", T, I}), {Node, node_hometree_odbc:decode_subscriptions(S), node_hometree_odbc:decode_jid(J)} end, RItems); _ -> @@ -250,7 +250,7 @@ get_entity_subscriptions_for_send_last(_Host, Owner) -> {selected, ["host", "node", "type", "nodeid", "jid", "subscriptions"], RItems} -> lists:map(fun({H, N, T, I, J, S}) -> O = node_hometree_odbc:decode_jid(H), - Node = nodetree_odbc:raw_to_node(O, {N, "", T, I}), + Node = nodetree_tree_odbc:raw_to_node(O, {N, "", T, I}), {Node, node_hometree_odbc:decode_subscriptions(S), node_hometree_odbc:decode_jid(J)} end, RItems); _ -> From 917d1be994f6af33b6d078d29f9c6e1415cf8c75 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 29 Aug 2011 12:36:49 +0200 Subject: [PATCH 02/77] Explicitly skip useless ejabberd 2.0* mnesia tables when restoring backup Reference: http://www.ejabberd.im/node/4867 --- src/ejabberd_admin.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 148bdccb9..9ceaa1836 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -427,8 +427,12 @@ restore_mnesia(Path) -> %% mod_configure/adhoc restore(Path) -> mnesia:restore(Path, [{keep_tables,keep_tables()}, + {skip_tables, skip_tables()}, {default_op, skip_tables}]). +skip_tables() -> + [disco_publish,user_caps,user_caps_resources]. + %% This function return a list of tables that should be kept from a previous %% version backup. %% Obsolete tables or tables created by module who are no longer used are not From 75fc431fb85af9054f37f5241c368dcbdb8785de Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Wed, 31 Aug 2011 16:11:01 +0200 Subject: [PATCH 03/77] enable pubsub#deliver_notification checking (thanks to Karim Gemayel)(EJAB-1453) --- src/mod_pubsub/mod_pubsub.erl | 21 ++++-- src/mod_pubsub/mod_pubsub_odbc.erl | 19 +++-- src/mod_pubsub/pubsub_odbc.patch | 112 ++++++++++++++++++++++------- 3 files changed, 114 insertions(+), 38 deletions(-) diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 38d56d17c..705c6e67a 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -2064,13 +2064,20 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> NodeId = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, - BroadcastPayload = case Broadcast of - default -> Payload; - broadcast -> Payload; - PluginPayload -> PluginPayload - end, - broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, jlib:jid_tolower(Publisher), BroadcastPayload), - set_cached_item(Host, NodeId, ItemId, Publisher, Payload), + case get_option(Options, deliver_notifications) of + true -> + BroadcastPayload = case Broadcast of + default -> Payload; + broadcast -> Payload; + PluginPayload -> PluginPayload + end, + broadcast_publish_item(Host, Node, NodeId, Type, Options, + Removed, ItemId, jlib:jid_tolower(Publisher), + BroadcastPayload); + false -> + ok + end, + set_cached_item(Host, NodeId, ItemId, Publisher, Payload), case Result of default -> {result, Reply}; _ -> {result, Result} diff --git a/src/mod_pubsub/mod_pubsub_odbc.erl b/src/mod_pubsub/mod_pubsub_odbc.erl index 2337c26d3..9c24456cc 100644 --- a/src/mod_pubsub/mod_pubsub_odbc.erl +++ b/src/mod_pubsub/mod_pubsub_odbc.erl @@ -1877,12 +1877,19 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> NodeId = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, - BroadcastPayload = case Broadcast of - default -> Payload; - broadcast -> Payload; - PluginPayload -> PluginPayload - end, - broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, jlib:jid_tolower(Publisher), BroadcastPayload), + case get_option(Options, deliver_notifications) of + true -> + BroadcastPayload = case Broadcast of + default -> Payload; + broadcast -> Payload; + PluginPayload -> PluginPayload + end, + broadcast_publish_item(Host, Node, NodeId, Type, Options, + Removed, ItemId, jlib:jid_tolower(Publisher), + BroadcastPayload); + false -> + ok + end, set_cached_item(Host, NodeId, ItemId, Publisher, Payload), case Result of default -> {result, Reply}; diff --git a/src/mod_pubsub/pubsub_odbc.patch b/src/mod_pubsub/pubsub_odbc.patch index 998132f74..ef2ba0eee 100644 --- a/src/mod_pubsub/pubsub_odbc.patch +++ b/src/mod_pubsub/pubsub_odbc.patch @@ -1,5 +1,5 @@ ---- mod_pubsub.erl 2010-12-07 13:54:26.000000000 +0100 -+++ mod_pubsub_odbc.erl 2010-12-07 13:59:56.000000000 +0100 +--- mod_pubsub.erl 2011-08-31 16:08:42.000000000 +0200 ++++ mod_pubsub_odbc.erl 2011-08-31 16:08:42.000000000 +0200 @@ -42,7 +42,7 @@ %%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see %%% XEP-0060 section 12.18. @@ -531,7 +531,30 @@ {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups), if not SubscribeFeature -> -@@ -2231,7 +2047,7 @@ +@@ -2021,12 +1837,9 @@ + Features = features(Type), + PublishFeature = lists:member("publish", Features), + PublishModel = get_option(Options, publish_model), ++ MaxItems = max_items(Host, Options), + DeliverPayloads = get_option(Options, deliver_payloads), + PersistItems = get_option(Options, persist_items), +- MaxItems = case PersistItems of +- false -> 0; +- true -> max_items(Host, Options) +- end, + PayloadCount = payload_xmlelements(Payload), + PayloadSize = size(term_to_binary(Payload))-2, % size(term_to_binary([])) == 2 + PayloadMaxSize = get_option(Options, max_payload_size), +@@ -2077,7 +1890,7 @@ + false -> + ok + end, +- set_cached_item(Host, NodeId, ItemId, Publisher, Payload), ++ set_cached_item(Host, NodeId, ItemId, Publisher, Payload), + case Result of + default -> {result, Reply}; + _ -> {result, Result} +@@ -2241,7 +2054,7 @@ %%

The permission are not checked in this function.

%% @todo We probably need to check that the user doing the query has the right %% to read the items. @@ -540,7 +563,7 @@ MaxItems = if SMaxItems == "" -> get_max_items_node(Host); -@@ -2245,12 +2061,13 @@ +@@ -2255,12 +2068,13 @@ {error, Error} -> {error, Error}; _ -> @@ -555,7 +578,7 @@ {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), if not RetreiveFeature -> -@@ -2263,11 +2080,11 @@ +@@ -2273,11 +2087,11 @@ node_call(Type, get_items, [NodeId, From, AccessModel, PresenceSubscription, RosterGroup, @@ -569,7 +592,7 @@ SendItems = case ItemIDs of [] -> Items; -@@ -2280,7 +2097,8 @@ +@@ -2290,7 +2104,8 @@ %% number of items sent to MaxItems: {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], [{xmlelement, "items", nodeAttr(Node), @@ -579,7 +602,7 @@ Error -> Error end -@@ -2302,10 +2120,15 @@ +@@ -2312,10 +2127,15 @@ Error -> Error end. get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) -> @@ -596,11 +619,13 @@ %% @spec (Host, Node, NodeId, Type, LJID, Number) -> any() -@@ -2318,16 +2141,27 @@ +@@ -2327,31 +2147,29 @@ + %% Number = last | integer() %% @doc

Resend the items of a node to the user.

%% @todo use cache-last-item feature - send_items(Host, Node, NodeId, Type, LJID, last) -> +-send_items(Host, Node, NodeId, Type, {U,S,R} = LJID, last) -> - case get_cached_item(Host, NodeId) of ++send_items(Host, Node, NodeId, Type, LJID, last) -> + Stanza = case get_cached_item(Host, NodeId) of undefined -> - send_items(Host, Node, NodeId, Type, LJID, 1); @@ -622,15 +647,52 @@ + event_stanza_with_delay( [{xmlelement, "items", nodeAttr(Node), - itemsEls([LastItem])}], ModifNow, ModifUSR), -- ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza) +- case is_tuple(Host) of +- false -> +- ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza); +- true -> +- case ejabberd_sm:get_session_pid(U,S,R) of +- C2SPid when is_pid(C2SPid) -> +- ejabberd_c2s:broadcast(C2SPid, +- {pep_message, binary_to_list(Node)++"+notify"}, +- _Sender = service_jid(Host), +- Stanza); +- _ -> +- ok +- end +- end - end; +-send_items(Host, Node, NodeId, Type, {U,S,R} = LJID, Number) -> + itemsEls([LastItem])}], ModifNow, ModifUSR) + end, + ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza); - send_items(Host, Node, NodeId, Type, LJID, Number) -> ++send_items(Host, Node, NodeId, Type, LJID, Number) -> ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of {result, []} -> -@@ -2453,7 +2287,8 @@ + []; +@@ -2374,20 +2192,7 @@ + [{xmlelement, "items", nodeAttr(Node), + itemsEls(ToSend)}]) + end, +- case is_tuple(Host) of +- false -> +- ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza); +- true -> +- case ejabberd_sm:get_session_pid(U,S,R) of +- C2SPid when is_pid(C2SPid) -> +- ejabberd_c2s:broadcast(C2SPid, +- {pep_message, binary_to_list(Node)++"+notify"}, +- _Sender = service_jid(Host), +- Stanza); +- _ -> +- ok +- end +- end. ++ ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza). + + %% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response} + %% Host = host() +@@ -2489,7 +2294,8 @@ error -> {error, ?ERR_BAD_REQUEST}; _ -> @@ -640,7 +702,7 @@ case lists:member(Owner, Owners) of true -> OwnerJID = jlib:make_jid(Owner), -@@ -2463,24 +2298,7 @@ +@@ -2499,24 +2305,7 @@ end, lists:foreach( fun({JID, Affiliation}) -> @@ -666,7 +728,7 @@ end, FilteredEntities), {result, []}; _ -> -@@ -2533,11 +2351,11 @@ +@@ -2569,11 +2358,11 @@ end. read_sub(Subscriber, Node, NodeID, SubID, Lang) -> @@ -680,7 +742,7 @@ OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)}, {"subid", SubID}|nodeAttr(Node)], [XdataEl]}, -@@ -2563,7 +2381,7 @@ +@@ -2599,7 +2388,7 @@ end. set_options_helper(Configuration, JID, NodeID, SubID, Type) -> @@ -689,7 +751,7 @@ {result, GoodSubOpts} -> GoodSubOpts; _ -> invalid end, -@@ -2592,7 +2410,7 @@ +@@ -2628,7 +2417,7 @@ write_sub(_Subscriber, _NodeID, _SubID, invalid) -> {error, extended_error(?ERR_BAD_REQUEST, "invalid-options")}; write_sub(Subscriber, NodeID, SubID, Options) -> @@ -698,7 +760,7 @@ {error, notfound} -> {error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; {result, _} -> -@@ -2760,8 +2578,8 @@ +@@ -2796,8 +2585,8 @@ {"subscription", subscription_to_string(Sub)} | nodeAttr(Node)], []}]}]}, ejabberd_router:route(service_jid(Host), jlib:make_jid(JID), Stanza) end, @@ -709,7 +771,7 @@ true -> Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) -> -@@ -3116,7 +2934,7 @@ +@@ -3152,7 +2941,7 @@ {Depth, [{N, get_node_subs(N)} || N <- Nodes]} end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))} end, @@ -718,7 +780,7 @@ {result, CollSubs} -> CollSubs; _ -> [] end. -@@ -3130,9 +2948,9 @@ +@@ -3166,9 +2955,9 @@ get_options_for_subs(NodeID, Subs) -> lists:foldl(fun({JID, subscribed, SubID}, Acc) -> @@ -730,7 +792,7 @@ _ -> Acc end; (_, Acc) -> -@@ -3321,6 +3139,30 @@ +@@ -3357,6 +3146,30 @@ Result end. @@ -761,7 +823,7 @@ %% @spec (Host, Options) -> MaxItems %% Host = host() %% Options = [Option] -@@ -3717,7 +3559,13 @@ +@@ -3753,7 +3566,13 @@ tree_action(Host, Function, Args) -> ?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]), Fun = fun() -> tree_call(Host, Function, Args) end, @@ -776,7 +838,7 @@ %% @doc

node plugin call.

node_call(Type, Function, Args) -> -@@ -3737,13 +3585,13 @@ +@@ -3773,13 +3592,13 @@ node_action(Host, Type, Function, Args) -> ?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]), @@ -792,7 +854,7 @@ case tree_call(Host, get_node, [Host, Node]) of N when is_record(N, pubsub_node) -> case Action(N) of -@@ -3755,13 +3603,19 @@ +@@ -3791,13 +3610,19 @@ Error end end, Trans). @@ -816,7 +878,7 @@ {result, Result} -> {result, Result}; {error, Error} -> {error, Error}; {atomic, {result, Result}} -> {result, Result}; -@@ -3769,6 +3623,15 @@ +@@ -3805,6 +3630,15 @@ {aborted, Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), {error, ?ERR_INTERNAL_SERVER_ERROR}; @@ -832,7 +894,7 @@ {'EXIT', Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), {error, ?ERR_INTERNAL_SERVER_ERROR}; -@@ -3777,6 +3640,17 @@ +@@ -3813,6 +3647,17 @@ {error, ?ERR_INTERNAL_SERVER_ERROR} end. From 41d028d101cd5bd134315a652942c7256032e7ed Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 4 Sep 2011 13:28:32 +0200 Subject: [PATCH 04/77] First check occupant existence, later check the message --- src/mod_muc/mod_muc_room.erl | 55 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index e31f565aa..6ee609f15 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -426,39 +426,38 @@ normal_state({route, From, ToNick, ToNick), From, Err); _ -> - ToJIDs = find_jids_by_nick(ToNick, StateData), - SrcIsVisitor = is_visitor(From, StateData), - DstIsModerator = is_moderator(hd(ToJIDs), StateData), - PmFromVisitors = (StateData#state.config)#config.allow_private_messages_from_visitors, - if SrcIsVisitor == false; - PmFromVisitors == anyone; - (PmFromVisitors == moderators) and (DstIsModerator) -> - case ToJIDs of - false -> - ErrText = "Recipient is not in the conference room", - Err = jlib:make_error_reply( - Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)), - ejabberd_router:route( - jlib:jid_replace_resource( - StateData#state.jid, - ToNick), - From, Err); - _ -> - {ok, #user{nick = FromNick}} = - ?DICT:find(jlib:jid_tolower(From), - StateData#state.users), - FromNickJID = jlib:jid_replace_resource(StateData#state.jid, FromNick), - [ejabberd_router:route(FromNickJID, ToJID, Packet) || ToJID <- ToJIDs] - end; - true -> - ErrText = "It is not allowed to send private messages", + case find_jids_by_nick(ToNick, StateData) of + false -> + ErrText = "Recipient is not in the conference room", Err = jlib:make_error_reply( - Packet, ?ERRT_FORBIDDEN(Lang, ErrText)), + Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)), ejabberd_router:route( jlib:jid_replace_resource( StateData#state.jid, ToNick), - From, Err) + From, Err); + ToJIDs -> + SrcIsVisitor = is_visitor(From, StateData), + DstIsModerator = is_moderator(hd(ToJIDs), StateData), + PmFromVisitors = (StateData#state.config)#config.allow_private_messages_from_visitors, + if SrcIsVisitor == false; + PmFromVisitors == anyone; + (PmFromVisitors == moderators) and (DstIsModerator) -> + {ok, #user{nick = FromNick}} = + ?DICT:find(jlib:jid_tolower(From), + StateData#state.users), + FromNickJID = jlib:jid_replace_resource(StateData#state.jid, FromNick), + [ejabberd_router:route(FromNickJID, ToJID, Packet) || ToJID <- ToJIDs]; + true -> + ErrText = "It is not allowed to send private messages", + Err = jlib:make_error_reply( + Packet, ?ERRT_FORBIDDEN(Lang, ErrText)), + ejabberd_router:route( + jlib:jid_replace_resource( + StateData#state.jid, + ToNick), + From, Err) + end end end; {true, false} -> From d6fcee4faadfe5e54486d96f6aefc303ca6e79ab Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 4 Sep 2011 14:56:56 +0200 Subject: [PATCH 05/77] Replace calls of OTP's Binary, since they would require R14 --- src/scram.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/scram.erl b/src/scram.erl index 30bd6bb27..dc1490189 100644 --- a/src/scram.erl +++ b/src/scram.erl @@ -53,29 +53,29 @@ client_signature(StoredKey, AuthMessage) -> crypto:sha_mac(StoredKey, AuthMessage). client_key(ClientProof, ClientSignature) -> - binary:list_to_bin(lists:zipwith(fun(X, Y) -> + list_to_binary(lists:zipwith(fun(X, Y) -> X bxor Y end, - binary:bin_to_list(ClientProof), - binary:bin_to_list(ClientSignature))). + binary_to_list(ClientProof), + binary_to_list(ClientSignature))). server_signature(ServerKey, AuthMessage) -> crypto:sha_mac(ServerKey, AuthMessage). hi(Password, Salt, IterationCount) -> - U1 = crypto:sha_mac(Password, string:concat(binary:bin_to_list(Salt), [0,0,0,1])), - binary:list_to_bin(lists:zipwith(fun(X, Y) -> + U1 = crypto:sha_mac(Password, string:concat(binary_to_list(Salt), [0,0,0,1])), + list_to_binary(lists:zipwith(fun(X, Y) -> X bxor Y end, - binary:bin_to_list(U1), - binary:bin_to_list(hi_round(Password, U1, IterationCount-1)))). + binary_to_list(U1), + binary_to_list(hi_round(Password, U1, IterationCount-1)))). hi_round(Password, UPrev, 1) -> crypto:sha_mac(Password, UPrev); hi_round(Password, UPrev, IterationCount) -> U = crypto:sha_mac(Password, UPrev), - binary:list_to_bin(lists:zipwith(fun(X, Y) -> + list_to_binary(lists:zipwith(fun(X, Y) -> X bxor Y end, - binary:bin_to_list(U), - binary:bin_to_list(hi_round(Password, U, IterationCount-1)))). + binary_to_list(U), + binary_to_list(hi_round(Password, U, IterationCount-1)))). From 2434be48038be745acc05186fe3b06753877589c Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 15:13:54 +1000 Subject: [PATCH 06/77] Get rid of useless function clause --- src/treap.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/treap.erl b/src/treap.erl index 4c2fd35a6..2bc740303 100644 --- a/src/treap.erl +++ b/src/treap.erl @@ -63,8 +63,6 @@ insert1({HashKey1, Priority1, Value1, Left, Right} = Tree, insert1(delete_root(Tree), HashKey, Priority, Value) end. -heapify(nil) -> - nil; heapify({_HashKey, _Priority, _Value, nil, nil} = Tree) -> Tree; heapify({HashKey, Priority, Value, From 1994c8a17463863737847ca04e4b17a358394e23 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 15:28:01 +1000 Subject: [PATCH 07/77] Change argument of open_port/2 to string in order to shut up the dialyzer --- src/ejabberd_app.erl | 2 +- src/ejabberd_zlib/ejabberd_zlib.erl | 4 ++-- src/mod_irc/iconv.erl | 2 +- src/sha.erl | 2 +- src/stringprep/stringprep.erl | 2 +- src/tls/tls.erl | 6 +++--- src/xml_stream.erl | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index fc60c8b99..32b4b9073 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -110,7 +110,7 @@ init() -> ok -> ok; {error, already_loaded} -> ok end, - Port = open_port({spawn, expat_erl}, [binary]), + Port = open_port({spawn, "expat_erl"}, [binary]), loop(Port). diff --git a/src/ejabberd_zlib/ejabberd_zlib.erl b/src/ejabberd_zlib/ejabberd_zlib.erl index 9d90a0dc5..dbfd57a07 100644 --- a/src/ejabberd_zlib/ejabberd_zlib.erl +++ b/src/ejabberd_zlib/ejabberd_zlib.erl @@ -63,7 +63,7 @@ init([]) -> ok -> ok; {error, already_loaded} -> ok end, - Port = open_port({spawn, ejabberd_zlib_drv}, [binary]), + Port = open_port({spawn, "ejabberd_zlib_drv"}, [binary]), {ok, Port}. @@ -99,7 +99,7 @@ enable_zlib(SockMod, Socket) -> ok -> ok; {error, already_loaded} -> ok end, - Port = open_port({spawn, ejabberd_zlib_drv}, [binary]), + Port = open_port({spawn, "ejabberd_zlib_drv"}, [binary]), {ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}}. disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) -> diff --git a/src/mod_irc/iconv.erl b/src/mod_irc/iconv.erl index d80db78ac..dde4d7e8d 100644 --- a/src/mod_irc/iconv.erl +++ b/src/mod_irc/iconv.erl @@ -52,7 +52,7 @@ init([]) -> ok -> ok; {error, already_loaded} -> ok end, - Port = open_port({spawn, iconv_erl}, []), + Port = open_port({spawn, "iconv_erl"}, []), ets:new(iconv_table, [set, public, named_table]), ets:insert(iconv_table, {port, Port}), {ok, Port}. diff --git a/src/sha.erl b/src/sha.erl index 5a2ed3f1d..33a82b889 100644 --- a/src/sha.erl +++ b/src/sha.erl @@ -47,7 +47,7 @@ start() -> end, case Res of ok -> - Port = open_port({spawn, ?DRIVER}, [binary]), + Port = open_port({spawn, atom_to_list(?DRIVER)}, [binary]), register(?DRIVER, Port); {error, Reason} -> ?CRITICAL_MSG("unable to load driver '~s': ~s", diff --git a/src/stringprep/stringprep.erl b/src/stringprep/stringprep.erl index 9db6d2847..e14bfca17 100644 --- a/src/stringprep/stringprep.erl +++ b/src/stringprep/stringprep.erl @@ -60,7 +60,7 @@ init([]) -> ok -> ok; {error, already_loaded} -> ok end, - Port = open_port({spawn, stringprep_drv}, []), + Port = open_port({spawn, "stringprep_drv"}, []), register(?STRINGPREP_PORT, Port), {ok, Port}. diff --git a/src/tls/tls.erl b/src/tls/tls.erl index 967cf9249..dfb9d2306 100644 --- a/src/tls/tls.erl +++ b/src/tls/tls.erl @@ -84,7 +84,7 @@ init([]) -> ok -> ok; {error, already_loaded} -> ok end, - Port = open_port({spawn, tls_drv}, [binary]), + Port = open_port({spawn, "tls_drv"}, [binary]), Res = port_control(Port, ?SET_CERTIFICATE_FILE_ACCEPT, "./ssl.pem" ++ [0]), case Res of <<0>> -> @@ -130,7 +130,7 @@ tcp_to_tls(TCPSocket, Options) -> ok -> ok; {error, already_loaded} -> ok end, - Port = open_port({spawn, tls_drv}, [binary]), + Port = open_port({spawn, "tls_drv"}, [binary]), Flags = case lists:member(verify_none, Options) of true -> @@ -267,7 +267,7 @@ test() -> ok -> ok; {error, already_loaded} -> ok end, - Port = open_port({spawn, tls_drv}, [binary]), + Port = open_port({spawn, "tls_drv"}, [binary]), ?PRINT("open_port: ~p~n", [Port]), PCRes = port_control(Port, ?SET_CERTIFICATE_FILE_ACCEPT, "./ssl.pem" ++ [0]), diff --git a/src/xml_stream.erl b/src/xml_stream.erl index 1deb8936e..205945fce 100644 --- a/src/xml_stream.erl +++ b/src/xml_stream.erl @@ -99,7 +99,7 @@ new(CallbackPid) -> new(CallbackPid, infinity). new(CallbackPid, MaxSize) -> - Port = open_port({spawn, expat_erl}, [binary]), + Port = open_port({spawn, "expat_erl"}, [binary]), #xml_stream_state{callback_pid = CallbackPid, port = Port, stack = [], @@ -140,7 +140,7 @@ close(#xml_stream_state{port = Port}) -> parse_element(Str) -> - Port = open_port({spawn, expat_erl}, [binary]), + 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)). From 0da2107974c151be57e6e0533909d1cd6c273d1e Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 15:35:46 +1000 Subject: [PATCH 08/77] Fix some case clauses --- src/cyrsasl_scram.erl | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/cyrsasl_scram.erl b/src/cyrsasl_scram.erl index e3d63e612..2cd146353 100644 --- a/src/cyrsasl_scram.erl +++ b/src/cyrsasl_scram.erl @@ -55,6 +55,8 @@ mech_step(#state{step = 2} = State, ClientIn) -> case string:tokens(ClientIn, ",") of [CBind, UserNameAttribute, ClientNonceAttribute] when (CBind == "y") or (CBind == "n") -> case parse_attribute(UserNameAttribute) of + {error, Reason} -> + {error, Reason}; {_, EscapedUserName} -> case unescape_username(EscapedUserName) of error -> @@ -89,11 +91,7 @@ mech_step(#state{step = 2} = State, ClientIn) -> _Else -> {error, "not-supported"} end - end; - {error, Reason} -> - {error, Reason}; - _Else -> - {error, "bad-protocol"} + end end; _Else -> {error, "bad-protocol"} @@ -145,12 +143,8 @@ parse_attribute(Attribute) -> true -> if SecondChar == $= -> - case string:substr(Attribute, 3) of - String when is_list(String) -> - {lists:nth(1, Attribute), String}; - _Else -> - {error, "bad-format failed"} - end; + String = string:substr(Attribute, 3), + {lists:nth(1, Attribute), String}; true -> {error, "bad-format second char not equal sign"} end; From d74f02e3675af6c98771bf788d64159dbc4d7ef4 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 15:39:55 +1000 Subject: [PATCH 09/77] ejabberd_auth:remove_user/2 always returns ok --- src/ejabberd_auth.erl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 1eab9af83..2baa71adc 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -306,19 +306,16 @@ is_user_exists_in_other_modules_loop([AuthModule|AuthModules], User, Server) -> end. -%% @spec (User, Server) -> ok | error | {error, not_allowed} +%% @spec (User, Server) -> ok %% @doc Remove user. %% Note: it may return ok even if there was some problem removing the user. remove_user(User, Server) -> - R = lists:foreach( + lists:foreach( fun(M) -> M:remove_user(User, Server) end, auth_modules(Server)), - case R of - ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]); - _ -> none - end, - R. + ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]), + ok. %% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error %% @doc Try to remove user if the provided password is correct. From ffe063a0e99ded18a41ee13cb48f83e9a3ff222a Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 15:59:30 +1000 Subject: [PATCH 10/77] Get rid of useless code --- src/eldap/eldap.erl | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/eldap/eldap.erl b/src/eldap/eldap.erl index c438626fa..a134a4c95 100644 --- a/src/eldap/eldap.erl +++ b/src/eldap/eldap.erl @@ -112,7 +112,7 @@ host = null, % Connected Host LDAP server port = 389, % The LDAP server port sockmod, % SockMod (gen_tcp|tls) - tls = none, % LDAP/LDAPS (none|starttls|tls) + tls = none, % LDAP/LDAPS (none|tls) tls_options = [], fd = null, % Socket filedescriptor. rootdn = "", % Name of the entry to bind as @@ -424,8 +424,8 @@ get_handle(Name) when is_list(Name) -> list_to_atom("eldap_" ++ Name). %%---------------------------------------------------------------------- init([]) -> case get_config() of - {ok, Hosts, Rootdn, Passwd, Opts} -> - init({Hosts, Rootdn, Passwd, Opts}); + {ok, Hosts, Port, Rootdn, Passwd, Opts} -> + init({Hosts, Port, Rootdn, Passwd, Opts}); {error, Reason} -> {stop, Reason} end; @@ -441,8 +441,6 @@ init({Hosts, Port, Rootdn, Passwd, Opts}) -> case Encrypt of tls -> ?LDAPS_PORT; - starttls -> - ?LDAP_PORT; _ -> ?LDAP_PORT end; @@ -902,14 +900,9 @@ cancel_timer(Timer) -> %%% Sanity check of received packet check_tag(Data) -> - case asn1rt_ber_bin:decode_tag(Data) of - {_Tag, Data1, _Rb} -> - case asn1rt_ber_bin:decode_length(Data1) of - {{_Len,_Data2}, _Rb2} -> ok; - _ -> throw({error,decoded_tag_length}) - end; - _ -> throw({error,decoded_tag}) - end. + {_Tag, Data1, _Rb} = asn1rt_ber_bin:decode_tag(Data), + {{_Len,_Data2}, _Rb2} = asn1rt_ber_bin:decode_length(Data1), + ok. close_and_retry(S, Timeout) -> catch (S#eldap.sockmod):close(S#eldap.fd), From f1a91108ef13fdb4faf0d8679da3f2040289eea8 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 16:31:58 +1000 Subject: [PATCH 11/77] Correct some function clauses and value types --- src/web/ejabberd_http.erl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl index ea6851902..44d3e161f 100644 --- a/src/web/ejabberd_http.erl +++ b/src/web/ejabberd_http.erl @@ -353,7 +353,7 @@ process_request(#state{request_method = Method, when Method=:='GET' orelse Method=:='HEAD' orelse Method=:='DELETE' orelse Method=:='OPTIONS' -> case (catch url_decode_q_split(Path)) of {'EXIT', _} -> - process_request(false); + make_bad_request(State); {NPath, Query} -> LPath = [path_decode(NPE) || NPE <- string:tokens(NPath, "/")], LQuery = case (catch parse_urlencoded(Query)) of @@ -430,7 +430,7 @@ process_request(#state{request_method = Method, ?DEBUG("client data: ~p~n", [Data]), case (catch url_decode_q_split(Path)) of {'EXIT', _} -> - process_request(false); + make_bad_request(State); {NPath, _Query} -> LPath = [path_decode(NPE) || NPE <- string:tokens(NPath, "/")], LQuery = case (catch parse_urlencoded(Data)) of @@ -464,6 +464,9 @@ process_request(#state{request_method = Method, end; process_request(State) -> + make_bad_request(State). + +make_bad_request(State) -> make_xhtml_output(State, 400, [], @@ -506,7 +509,7 @@ recv_data(State, Len, Acc) -> [] -> case (State#state.sockmod):recv(State#state.socket, Len, 300000) of {ok, Data} -> - recv_data(State, Len - size(Data), [Acc | Data]); + recv_data(State, Len - size(Data), [Acc | [Data]]); _ -> "" end; @@ -1089,8 +1092,8 @@ is_space(_) -> strip_spaces(String) -> strip_spaces(String, both). -strip_spaces(String, left) -> - drop_spaces(String); +%% strip_spaces(String, left) -> +%% drop_spaces(String); strip_spaces(String, right) -> lists:reverse(drop_spaces(lists:reverse(String))); strip_spaces(String, both) -> From ffdf5cb2068f03493cd03c50cf874e783b764acb Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 16:47:18 +1000 Subject: [PATCH 12/77] Remove useless case clause --- src/mod_stats.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 5c110801d..da7751cd6 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -167,12 +167,7 @@ get_local_stat(_Server, [], Name) when Name == "users/all-hosts/total" -> ejabberd_auth:get_vh_registered_users_number(Host) + Total end, 0, ejabberd_config:get_global_option(hosts)), - case NumUsers of - {'EXIT', _Reason} -> - ?STATERR("500", "Internal Server Error"); - Users -> - ?STATVAL(integer_to_list(Users), "users") - end; + ?STATVAL(integer_to_list(NumUsers), "users"); get_local_stat(_Server, _, Name) -> ?STATERR("404", "Not Found"). From de712e5ccb663c792e8513f40d21910e6154de58 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 16:48:04 +1000 Subject: [PATCH 13/77] Remove useless case clauses --- src/mod_irc/mod_irc_connection.erl | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/mod_irc/mod_irc_connection.erl b/src/mod_irc/mod_irc_connection.erl index 62abd0556..da9d26ca8 100644 --- a/src/mod_irc/mod_irc_connection.erl +++ b/src/mod_irc/mod_irc_connection.erl @@ -813,13 +813,9 @@ process_channel_topic_who(StateData, Chan, String) -> Words = string:tokens(String, " "), Msg1 = case Words of [_, "333", _, _Chan, Whoset , Timeset] -> - case string:to_integer(Timeset) of - {Unixtimeset, _Rest} -> - "Topic for #" ++ Chan ++ " set by " ++ Whoset ++ - " at " ++ unixtime2string(Unixtimeset); - _-> - "Topic for #" ++ Chan ++ " set by " ++ Whoset - end; + {Unixtimeset, _Rest} = string:to_integer(Timeset), + "Topic for #" ++ Chan ++ " set by " ++ Whoset ++ + " at " ++ unixtime2string(Unixtimeset); [_, "333", _, _Chan, Whoset | _] -> "Topic for #" ++ Chan ++ " set by " ++ Whoset; _ -> @@ -1327,15 +1323,12 @@ filter_mirc_colors(Msg) -> unixtime2string(Unixtime) -> Secs = Unixtime + calendar:datetime_to_gregorian_seconds( {{1970, 1, 1}, {0,0,0}}), - case calendar:universal_time_to_local_time( - calendar:gregorian_seconds_to_datetime(Secs)) of - {{Year, Month, Day}, {Hour, Minute, Second}} -> - lists:flatten( - io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, Second])); - _-> - "0000-00-00 00:00:00" - end. + {{Year, Month, Day}, {Hour, Minute, Second}} = + calendar:universal_time_to_local_time( + calendar:gregorian_seconds_to_datetime(Secs)), + lists:flatten( + io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", + [Year, Month, Day, Hour, Minute, Second])). toupper([C | Cs]) -> if From 8b5fa88b6f326ef5334ee5ae53839143786e8374 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 16:52:36 +1000 Subject: [PATCH 14/77] Export do_client_version/3 in order to shut up the dialyzer --- src/mod_echo.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_echo.erl b/src/mod_echo.erl index f8d9ce622..284944541 100644 --- a/src/mod_echo.erl +++ b/src/mod_echo.erl @@ -31,7 +31,7 @@ -behaviour(gen_mod). %% API --export([start_link/2, start/2, stop/1]). +-export([start_link/2, start/2, stop/1, do_client_version/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, From 3b95108a3af11df4b731730da416dd9fa751af8f Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 17:09:36 +1000 Subject: [PATCH 15/77] Replace VVALUEL macro with function in order to shut up the dialyzer --- src/mod_announce.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/mod_announce.erl b/src/mod_announce.erl index 6f61ae301..ce271461c 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -473,17 +473,18 @@ announce_commands(From, To, -define(VVALUE(Val), {xmlelement, "value", [], [{xmlcdata, Val}]}). --define(VVALUEL(Val), - case Val of - "" -> []; - _ -> [?VVALUE(Val)] - end). -define(TVFIELD(Type, Var, Val), {xmlelement, "field", [{"type", Type}, {"var", Var}], - ?VVALUEL(Val)}). + vvaluel(Val)}). -define(HFIELD(), ?TVFIELD("hidden", "FORM_TYPE", ?NS_ADMIN)). +vvaluel(Val) -> + case Val of + "" -> []; + _ -> [?VVALUE(Val)] + end. + generate_adhoc_form(Lang, Node, ServerHost) -> LNode = tokenize(Node), {OldSubject, OldBody} = if (LNode == ?NS_ADMINL("edit-motd")) @@ -512,12 +513,12 @@ generate_adhoc_form(Lang, Node, ServerHost) -> [{"var", "subject"}, {"type", "text-single"}, {"label", translate:translate(Lang, "Subject")}], - ?VVALUEL(OldSubject)}, + vvaluel(OldSubject)}, {xmlelement, "field", [{"var", "body"}, {"type", "text-multi"}, {"label", translate:translate(Lang, "Message body")}], - ?VVALUEL(OldBody)}] + vvaluel(OldBody)}] end}. join_lines([]) -> From 826fab99990c34aab272888afaf6e2f28cea74eb Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 17:12:45 +1000 Subject: [PATCH 16/77] Remove useless case clause --- src/mod_configure.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mod_configure.erl b/src/mod_configure.erl index 33657e9de..2d58d58e7 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -1403,9 +1403,7 @@ set_form(_From, Host, ["running nodes", ENode, "modules", "start"], _Lang, XData end; _ -> {error, ?ERR_BAD_REQUEST} - end; - _ -> - {error, ?ERR_BAD_REQUEST} + end end end; From 0c1796ea408c96555b0cffc26da91e70c34bbc51 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 17:27:15 +1000 Subject: [PATCH 17/77] Fix potential case-clause crash --- src/mod_muc/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 6ee609f15..9b7255dd5 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -1488,7 +1488,7 @@ remove_online_user(JID, StateData, Reason) -> ?DICT:erase(Nick, StateData#state.nicks); {ok, U} -> ?DICT:store(Nick, U -- [LJID], StateData#state.nicks); - false -> + error -> StateData#state.nicks end, StateData#state{users = Users, nicks = Nicks}. From eccb499346db9d41271b7898cc8cfde7ee5c33dc Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 17:33:51 +1000 Subject: [PATCH 18/77] Add some magic in order to shut up the dialyzer (because I do not want to replace the macro with a function) --- src/mod_muc/mod_muc_room.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 9b7255dd5..d53ff3d73 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3340,9 +3340,9 @@ set_xoption([{"anonymous", [Val]} | Opts], Config) -> set_xoption([{"muc#roomconfig_whois", [Val]} | Opts], Config) -> case Val of "moderators" -> - ?SET_BOOL_XOPT(anonymous, "1"); + ?SET_BOOL_XOPT(anonymous, integer_to_list(1)); "anyone" -> - ?SET_BOOL_XOPT(anonymous, "0"); + ?SET_BOOL_XOPT(anonymous, integer_to_list(0)); _ -> {error, ?ERR_BAD_REQUEST} end; From 7ae7d6315fd5589c2738452a5276830996147c8e Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 18:07:50 +1000 Subject: [PATCH 19/77] Remove useless clauses --- src/web/ejabberd_web_admin.erl | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl index b595f494e..d08ad6371 100644 --- a/src/web/ejabberd_web_admin.erl +++ b/src/web/ejabberd_web_admin.erl @@ -106,16 +106,6 @@ get_menu_items(global, cluster, Lang, JID) -> ); get_menu_items(Host, cluster, Lang, JID) -> {Base, _, Items} = make_host_menu(Host, [], Lang, JID), - lists:map( - fun({URI, Name}) -> - {Base++URI++"/", Name}; - ({URI, Name, _SubMenu}) -> - {Base++URI++"/", Name} - end, - Items - ); -get_menu_items(Host, Node, Lang, JID) -> - {Base, _, Items} = make_host_node_menu(Host, Node, Lang, JID), lists:map( fun({URI, Name}) -> {Base++URI++"/", Name}; @@ -124,6 +114,16 @@ get_menu_items(Host, Node, Lang, JID) -> end, Items ). +%% get_menu_items(Host, Node, Lang, JID) -> +%% {Base, _, Items} = make_host_node_menu(Host, Node, Lang, JID), +%% lists:map( +%% fun({URI, Name}) -> +%% {Base++URI++"/", Name}; +%% ({URI, Name, _SubMenu}) -> +%% {Base++URI++"/", Name} +%% end, +%% Items +%% ). is_allowed_path(BasePath, {Path, _}, JID) -> is_allowed_path(BasePath ++ [Path], JID); @@ -2023,7 +2023,6 @@ get_node(global, Node, ["db"], Query, Lang) -> get_node(global, Node, ["backup"], Query, Lang) -> HomeDirRaw = case {os:getenv("HOME"), os:type()} of {EnvHome, _} when is_list(EnvHome) -> EnvHome; - {false, win32} -> "C:/"; {false, {win32, _Osname}} -> "C:/"; {false, _} -> "/tmp/" end, From da555fb90840c2d562c3f41b90a059da8e179605 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 5 Sep 2011 12:48:26 +0200 Subject: [PATCH 20/77] Fix SCRAM auth of uppercase passwords (EJAB-1196) --- src/scram.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scram.erl b/src/scram.erl index dc1490189..860854ce1 100644 --- a/src/scram.erl +++ b/src/scram.erl @@ -37,8 +37,9 @@ client_key/2 ]). +%% ejabberd doesn't implement SASLPREP, so we use the similar RESOURCEPREP instead salted_password(Password, Salt, IterationCount) -> - hi(jlib:nameprep(Password), Salt, IterationCount). + hi(jlib:resourceprep(Password), Salt, IterationCount). client_key(SaltedPassword) -> crypto:sha_mac(SaltedPassword, "Client Key"). From 2482c9c5edaa176587663e6bcd0deea12be1c401 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 20:53:01 +1000 Subject: [PATCH 21/77] Merge set_affiliation/3 and set_affiliation_and_reason/4 into set_affiliation/4 --- src/mod_muc/mod_muc_room.erl | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index d53ff3d73..171279f17 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -1156,19 +1156,9 @@ expulse_participant(Packet, From, StateData, Reason1) -> set_affiliation(JID, Affiliation, StateData) -> - LJID = jlib:jid_remove_resource(jlib:jid_tolower(JID)), - Affiliations = case Affiliation of - none -> - ?DICT:erase(LJID, - StateData#state.affiliations); - _ -> - ?DICT:store(LJID, - Affiliation, - StateData#state.affiliations) - end, - StateData#state{affiliations = Affiliations}. + set_affiliation(JID, Affiliation, StateData, ""). -set_affiliation_and_reason(JID, Affiliation, Reason, StateData) -> +set_affiliation(JID, Affiliation, StateData, Reason) -> LJID = jlib:jid_remove_resource(jlib:jid_tolower(JID)), Affiliations = case Affiliation of none -> @@ -2443,18 +2433,18 @@ process_admin_items_set(UJID, Items, Lang, StateData) -> {JID, affiliation, outcast, Reason} -> catch send_kickban_presence( JID, Reason, "301", outcast, SD), - set_affiliation_and_reason( - JID, outcast, Reason, - set_role(JID, none, SD)); + set_affiliation( + JID, outcast, + set_role(JID, none, SD), Reason); {JID, affiliation, A, Reason} when (A == admin) or (A == owner) -> - SD1 = set_affiliation_and_reason(JID, A, Reason, SD), + SD1 = set_affiliation(JID, A, SD, Reason), SD2 = set_role(JID, moderator, SD1), send_update_presence(JID, Reason, SD2), SD2; {JID, affiliation, member, Reason} -> - SD1 = set_affiliation_and_reason( - JID, member, Reason, SD), + SD1 = set_affiliation( + JID, member, SD, Reason), SD2 = set_role(JID, participant, SD1), send_update_presence(JID, Reason, SD2), SD2; From f3c90ee2661154d77afe045a7f2045f33d8beeda Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 21:06:18 +1000 Subject: [PATCH 22/77] Get rid of useless check --- src/mod_muc/mod_muc_room.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 171279f17..abb53e032 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -1622,7 +1622,6 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) -> mod_muc, max_user_conferences, 10), Collision = nick_collision(From, Nick, StateData), case {(ServiceAffiliation == owner orelse - MaxUsers == none orelse ((Affiliation == admin orelse Affiliation == owner) andalso NUsers < MaxAdminUsers) orelse NUsers < MaxUsers) andalso From 6b0eb1f09d310f95f673f8b41d7fa3db675f6ec3 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 21:27:31 +1000 Subject: [PATCH 23/77] Reorganize the code to shut up the dialyzer --- src/ejabberd_s2s_out.erl | 50 ++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 55f48afbf..aa2df3f37 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -1073,33 +1073,29 @@ get_addr_port(Server) -> {ok, HEnt} -> ?DEBUG("srv lookup of '~s': ~p~n", [Server, HEnt#hostent.h_addr_list]), - case HEnt#hostent.h_addr_list of - [] -> - [{Server, outgoing_s2s_port()}]; - AddrList -> - %% Probabilities are not exactly proportional to weights - %% for simplicity (higher weigths are overvalued) - {A1, A2, A3} = now(), - random:seed(A1, A2, A3), - case (catch lists:map( - fun({Priority, Weight, Port, Host}) -> - N = case Weight of - 0 -> 0; - _ -> (Weight + 1) * random:uniform() - end, - {Priority * 65536 - N, Host, Port} - end, AddrList)) of - {'EXIT', _Reason} -> - [{Server, outgoing_s2s_port()}]; - SortedList -> - List = lists:map( - fun({_, Host, Port}) -> - {Host, Port} - end, lists:keysort(1, SortedList)), - ?DEBUG("srv lookup of '~s': ~p~n", [Server, List]), - List - end - end + AddrList = HEnt#hostent.h_addr_list, + %% Probabilities are not exactly proportional to weights + %% for simplicity (higher weigths are overvalued) + {A1, A2, A3} = now(), + random:seed(A1, A2, A3), + case (catch lists:map( + fun({Priority, Weight, Port, Host}) -> + N = case Weight of + 0 -> 0; + _ -> (Weight + 1) * random:uniform() + end, + {Priority * 65536 - N, Host, Port} + end, AddrList)) of + SortedList = [_|_] -> + List = lists:map( + fun({_, Host, Port}) -> + {Host, Port} + end, lists:keysort(1, SortedList)), + ?DEBUG("srv lookup of '~s': ~p~n", [Server, List]), + List; + _ -> + [{Server, outgoing_s2s_port()}] + end end. srv_lookup(Server) -> From 0fde148a9fcdd151859b667d4f8a9c48de2c0620 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 5 Sep 2011 22:00:50 +1000 Subject: [PATCH 24/77] Fix dialyzer warnings --- src/mod_shared_roster.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 286cac972..4b2bd75e2 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -30,6 +30,7 @@ -behaviour(gen_mod). -export([start/2, stop/1, + item_to_xml/1, webadmin_menu/3, webadmin_page/3, get_user_roster/2, get_subscription_lists/3, @@ -726,8 +727,6 @@ displayed_to_groups(GroupName, LServer) -> lists:member(GroupName, proplists:get_value(displayed_groups, Opts, [])) end, GroupsOpts). -push_item(_User, _Server, _From, none) -> - ok; push_item(User, Server, From, Item) -> %% It was %% ejabberd_sm:route(jlib:make_jid("", "", ""), From 76a174a007c7f2e97d4f5f557afddc2fcc88b297 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 5 Sep 2011 19:24:05 +0200 Subject: [PATCH 25/77] Fix typo in the Guide about user_regexp --- doc/guide.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/guide.tex b/doc/guide.tex index 008363a28..76546c84e 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -1486,10 +1486,10 @@ declarations of ACLs in the configuration file have the following syntax: {acl, tests, {user_regexp, "^test[0-9]*$"}}. \end{verbatim} %$ -\titem{\{user\_regexp, UserRegexp, Server\}} Matches any user with a name +\titem{\{user\_regexp, Regexp, Server\}} Matches any user with a name that matches \term{Regexp} at server \term{Server}. Example: \begin{verbatim} -{acl, tests, {user_Userregexp, "^test", "example.org"}}. +{acl, tests, {user_regexp, "^test", "example.org"}}. \end{verbatim} \titem{\{server\_regexp, Regexp\}} Matches any JID from the server that matches \term{Regexp}. Example: From 20f3fc96c3435b272d87c1c92bda5ac58ef57696 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 5 Sep 2011 23:21:38 +0200 Subject: [PATCH 26/77] Fix SCRAM to support attributes which value has only one character --- src/cyrsasl_scram.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cyrsasl_scram.erl b/src/cyrsasl_scram.erl index 2cd146353..002d6e446 100644 --- a/src/cyrsasl_scram.erl +++ b/src/cyrsasl_scram.erl @@ -137,7 +137,7 @@ mech_step(#state{step = 4} = State, ClientIn) -> parse_attribute(Attribute) -> AttributeLen = string:len(Attribute), if - AttributeLen > 3 -> + AttributeLen >= 3 -> SecondChar = lists:nth(2, Attribute), case is_alpha(lists:nth(1, Attribute)) of true -> From 3eca0259f728dd28e16ca979fa58f2d0ef813490 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 6 Sep 2011 13:39:26 +0200 Subject: [PATCH 27/77] Clarification about system user of ejabberd.init --- doc/guide.tex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/guide.tex b/doc/guide.tex index 76546c84e..5e3ce7d25 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -250,8 +250,9 @@ and for example it doesn't read the file ejabberdctl.cfg. On a *nix system, if you want ejabberd to be started as daemon at boot time, copy \term{ejabberd.init} from the 'bin' directory to something like \term{/etc/init.d/ejabberd} (depending on your distribution). -Create a system user called \term{ejabberd}; -it will be used by the script to start the server. +Create a system user called \term{ejabberd}, +give it write access to the directories \term{database/} and \term{logs/}, and set that as home; +the script will start the server with that user. Then you can call \term{/etc/inid.d/ejabberd start} as root to start the server. If \term{ejabberd} doesn't start correctly in Windows, From 609b7ed5c5fb054b1f9b4d988304c7907ecdee69 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Sep 2011 11:41:45 +0200 Subject: [PATCH 28/77] Support to import XML files without password-format attribute --- src/ejabberd_piefxis.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index cf5178bbe..a5d3ee921 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -195,7 +195,7 @@ process_element(El,State) -> add_user(El, Domain) -> User = exmpp_xml:get_attribute(El, <<"name">>, none), - PasswordFormat = exmpp_xml:get_attribute(El, <<"password-format">>, none), + PasswordFormat = exmpp_xml:get_attribute(El, <<"password-format">>, <<"plaintext">>), Password = exmpp_xml:get_attribute(El, <<"password">>, none), add_user(El, Domain, User, PasswordFormat, Password). From dbb2d1c22915f275796fbb3bbd58b75b933fb98b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Sep 2011 12:34:09 +0200 Subject: [PATCH 29/77] Fix room config change logging, due to allow_private_messages_from_visitors --- src/mod_muc/mod_muc_log.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_log.erl b/src/mod_muc/mod_muc_log.erl index a17f6e0ac..40f59c3e7 100644 --- a/src/mod_muc/mod_muc_log.erl +++ b/src/mod_muc/mod_muc_log.erl @@ -863,6 +863,7 @@ roomconfig_to_string(Options, Lang, FileFormat) -> max_users -> "
" ++ OptText ++ ": \"" ++ htmlize(integer_to_list(T), FileFormat) ++ "\"
"; title -> "
" ++ OptText ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"
"; description -> "
" ++ OptText ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"
"; + allow_private_messages_from_visitors -> "
" ++ OptText ++ ": \"" ++ htmlize(?T(atom_to_list(T)), FileFormat) ++ "\"
"; _ -> "\"" ++ T ++ "\"" end end, @@ -884,7 +885,7 @@ get_roomconfig_text(moderated) -> "Make room moderated"; get_roomconfig_text(members_by_default) -> "Default users as participants"; get_roomconfig_text(allow_change_subj) -> "Allow users to change the subject"; get_roomconfig_text(allow_private_messages) -> "Allow users to send private messages"; -get_roomconfig_text(allow_private_messages_from_visitors) -> "Allow visitors to send private messages"; +get_roomconfig_text(allow_private_messages_from_visitors) -> "Allow visitors to send private messages to"; get_roomconfig_text(allow_query_users) -> "Allow users to query other users"; get_roomconfig_text(allow_user_invites) -> "Allow users to send invites"; get_roomconfig_text(logging) -> "Enable logging"; From b8969f68e08e0a5685fd2aee1d1313095e20a31e Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Sep 2011 16:10:38 +0200 Subject: [PATCH 30/77] Fixed support for multi-resource occupants (EJAB-305) --- src/mod_muc/mod_muc_room.erl | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index abb53e032..6483bfb45 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -2464,7 +2464,7 @@ process_admin_items_set(UJID, Items, Lang, StateData) -> NSD -> NSD end - end, StateData, Res), + end, StateData, lists:flatten(Res)), case (NSD#state.config)#config.persistent of true -> mod_muc:store_room(NSD#state.host, NSD#state.room, @@ -2501,7 +2501,7 @@ find_changed_items(UJID, UAffiliation, URole, _ -> case xml:get_attr("nick", Attrs) of {value, N} -> - case find_jid_by_nick(N, StateData) of + case find_jids_by_nick(N, StateData) of false -> ErrText = io_lib:format( @@ -2518,7 +2518,7 @@ find_changed_items(UJID, UAffiliation, URole, end end, case TJID of - {value, JID} -> + {value, [JID|_]=JIDs} -> TAffiliation = get_affiliation(JID, StateData), TRole = get_role(JID, StateData), case xml:get_attr("role", Attrs) of @@ -2568,16 +2568,13 @@ find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, Res); true -> + Reason = xml:get_path_s(Item, [{elem, "reason"}, cdata]), + MoreRes = [{jlib:jid_remove_resource(Jidx), affiliation, SAffiliation, Reason} || Jidx <- JIDs], find_changed_items( UJID, UAffiliation, URole, Items, Lang, StateData, - [{jlib:jid_remove_resource(JID), - affiliation, - SAffiliation, - xml:get_path_s( - Item, [{elem, "reason"}, - cdata])} | Res]); + [MoreRes | Res]); false -> {error, ?ERR_NOT_ALLOWED} end @@ -2625,14 +2622,13 @@ find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, Res); true -> + Reason = xml:get_path_s(Item, [{elem, "reason"}, cdata]), + MoreRes = [{Jidx, role, SRole, Reason} || Jidx <- JIDs], find_changed_items( UJID, UAffiliation, URole, Items, Lang, StateData, - [{JID, role, SRole, - xml:get_path_s( - Item, [{elem, "reason"}, - cdata])} | Res]); + [MoreRes | Res]); _ -> {error, ?ERR_NOT_ALLOWED} end From e432ff58f96ece65ee28cb820cb9e6398787913e Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Fri, 9 Sep 2011 12:34:06 +1000 Subject: [PATCH 31/77] Avoid crash when performing admin action --- src/mod_muc/mod_muc_room.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 6483bfb45..a0a62298b 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -2496,7 +2496,7 @@ find_changed_items(UJID, UAffiliation, URole, "Jabber ID ~s is invalid"), [S]), {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}; J -> - {value, J} + {value, [J]} end; _ -> case xml:get_attr("nick", Attrs) of @@ -2511,7 +2511,7 @@ find_changed_items(UJID, UAffiliation, URole, [N]), {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}; J -> - {value, J} + {value, [J]} end; _ -> {error, ?ERR_BAD_REQUEST} From 228a587fc5fa2c91a4929accd5d21a81f36c5417 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Fri, 9 Sep 2011 12:40:07 +1000 Subject: [PATCH 32/77] Fix previous commit --- src/mod_muc/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index a0a62298b..c7d43e689 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -2511,7 +2511,7 @@ find_changed_items(UJID, UAffiliation, URole, [N]), {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}; J -> - {value, [J]} + {value, J} end; _ -> {error, ?ERR_BAD_REQUEST} From 07fdd089a081201ae5fc4673d72797e8fc9e66e7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 14 Sep 2011 12:15:53 +0200 Subject: [PATCH 33/77] No need to explicitly skip old useless tables, simply fix keep_tables --- src/ejabberd_admin.erl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 9ceaa1836..a11b4bf4a 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -427,18 +427,14 @@ restore_mnesia(Path) -> %% mod_configure/adhoc restore(Path) -> mnesia:restore(Path, [{keep_tables,keep_tables()}, - {skip_tables, skip_tables()}, {default_op, skip_tables}]). -skip_tables() -> - [disco_publish,user_caps,user_caps_resources]. - %% This function return a list of tables that should be kept from a previous %% version backup. %% Obsolete tables or tables created by module who are no longer used are not %% restored and are ignored. keep_tables() -> - lists:flatten([acl, passwd, config, local_config, disco_publish, + lists:flatten([acl, passwd, config, local_config, keep_modules_tables()]). %% Returns the list of modules tables in use, according to the list of actually From 2494e39c9e0877051b2bae59356980e05eed6211 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Thu, 15 Sep 2011 16:59:45 +1000 Subject: [PATCH 34/77] Fix dialyzer warnings --- src/p1_fsm.erl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/p1_fsm.erl b/src/p1_fsm.erl index e21086001..36e80d389 100644 --- a/src/p1_fsm.erl +++ b/src/p1_fsm.erl @@ -456,7 +456,7 @@ decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, Limits, Queue, QueueLen); _Msg -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, StateName}, {in, Msg}), handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, Debug1, Limits, Queue, QueueLen) @@ -470,6 +470,8 @@ system_continue(Parent, Debug, [Name, StateName, StateData, loop(Parent, Name, StateName, StateData, Mod, Time, Debug, Limits, Queue, QueueLen). +-spec system_terminate(term(), _, _, [term(),...]) -> no_return(). + system_terminate(Reason, _Parent, Debug, [Name, StateName, StateData, Mod, _Time, _Limits]) -> terminate(Reason, Name, [], Mod, StateName, StateData, Debug). @@ -590,12 +592,12 @@ handle_msg(Msg, Parent, Name, StateName, StateData, From = from(Msg), case catch dispatch(Msg, Mod, StateName, StateData) of {next_state, NStateName, NStateData} -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, NStateName}, return), loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1, Limits, Queue, QueueLen); {next_state, NStateName, NStateData, Time1} -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, NStateName}, return), loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1, Limits, Queue, QueueLen); @@ -664,13 +666,15 @@ reply({To, Tag}, Reply) -> reply(Name, {To, Tag}, Reply, Debug, StateName) -> reply({To, Tag}, Reply), - sys:handle_debug(Debug, {?MODULE, print_event}, Name, + sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, StateName}). %%% --------------------------------------------------- %%% Terminate the server. %%% --------------------------------------------------- +-spec terminate(term(), _, _, atom(), _, _, _) -> no_return(). + terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) -> case catch Mod:terminate(Reason, StateName, StateData) of {'EXIT', R} -> From ede8d57373807ec81f5a7c71dc173f12789cdd66 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sat, 17 Sep 2011 10:48:52 +1000 Subject: [PATCH 35/77] Fix dialyzer warnings --- src/ejabberd_ctl.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 6c1d25beb..189aa2c6b 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -269,8 +269,8 @@ try_call_command(Args, Auth, AccessCommands) -> try call_command(Args, Auth, AccessCommands) of {error, command_unknown} -> {io_lib:format("Error: command ~p not known.", [hd(Args)]), ?STATUS_ERROR}; - {error, wrong_number_parameters} -> - {"Error: wrong number of parameters", ?STATUS_ERROR}; + {error, wrong_command_arguments} -> + {"Error: wrong arguments", ?STATUS_ERROR}; Res -> Res catch @@ -637,7 +637,7 @@ print_usage_help(MaxC, ShCode) -> ArgsDef = [], C = #ejabberd_commands{ desc = "Show help of ejabberd commands", - longdesc = LongDesc, + longdesc = lists:flatten(LongDesc), args = ArgsDef, result = {help, string}}, print_usage_command("help", C, MaxC, ShCode). From 33766a124dd569222c9dd1d2a3b39bc38855b783 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 19 Sep 2011 16:58:55 +1000 Subject: [PATCH 36/77] New option support: ldap_deref_aliases (EJAB-639) --- doc/guide.tex | 6 ++++-- src/ejabberd_auth_ldap.erl | 27 +++++++++++++++++++-------- src/eldap/eldap.erl | 10 +++++++++- src/eldap/eldap.hrl | 1 + src/mod_shared_roster_ldap.erl | 12 ++++++++++++ src/mod_vcard_ldap.erl | 29 ++++++++++++++++++++++------- 6 files changed, 67 insertions(+), 18 deletions(-) diff --git a/doc/guide.tex b/doc/guide.tex index 5e3ce7d25..afbbbd997 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -2303,6 +2303,7 @@ the value previously stored in the database will be used instead of the default is~\term{""} which means `anonymous connection'. \titem{\{ldap\_password, Password\}} \ind{options!ldap\_password}Bind password. The default value is \term{""}. +\titem{\{ldap\_deref\_aliases, never|always|finding|searching\}} \ind{options!ldap\_deref\_aliases} Whether or not to dereference aliases. The default is \term{never}. \end{description} Example: @@ -4689,8 +4690,9 @@ The \modvcardldap{} module has its own optional parameters. The first group of parameters has the same meaning as the top-level LDAP parameters to set the authentication method: \option{ldap\_servers}, \option{ldap\_port}, \option{ldap\_rootdn}, -\option{ldap\_password}, \option{ldap\_base}, \option{ldap\_uids}, and -\option{ldap\_filter}. See section~\ref{ldapauth} for detailed information +\option{ldap\_password}, \option{ldap\_base}, \option{ldap\_uids}, +\option{ldap\_deref\_aliases} and \option{ldap\_filter}. +See section~\ref{ldapauth} for detailed information about these options. If one of these options is not set, \ejabberd{} will look for the top-level option with the same name. diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl index 77216a1cd..54a346c07 100644 --- a/src/ejabberd_auth_ldap.erl +++ b/src/ejabberd_auth_ldap.erl @@ -75,6 +75,7 @@ ufilter, sfilter, lfilter, %% Local filter (performed by ejabberd, not LDAP) + deref_aliases, dn_filter, dn_filter_attrs }). @@ -230,10 +231,12 @@ get_vh_registered_users_ldap(Server) -> ResAttrs = result_attrs(State), case eldap_filter:parse(State#state.sfilter) of {ok, EldapFilter} -> - case eldap_pool:search(Eldap_ID, [{base, State#state.base}, - {filter, EldapFilter}, - {timeout, ?LDAP_SEARCH_TIMEOUT}, - {attributes, ResAttrs}]) of + case eldap_pool:search(Eldap_ID, + [{base, State#state.base}, + {filter, EldapFilter}, + {timeout, ?LDAP_SEARCH_TIMEOUT}, + {deref_aliases, State#state.deref_aliases}, + {attributes, ResAttrs}]) of #eldap_search_result{entries = Entries} -> lists:flatmap( fun(#eldap_entry{attributes = Attrs, @@ -285,6 +288,7 @@ find_user_dn(User, State) -> case eldap_pool:search(State#state.eldap_id, [{base, State#state.base}, {filter, Filter}, + {deref_aliases, State#state.deref_aliases}, {attributes, ResAttrs}]) of #eldap_search_result{entries = [#eldap_entry{attributes = Attrs, object_name = DN} | _]} -> @@ -322,10 +326,11 @@ is_valid_dn(DN, Attrs, State) -> end ++ [{"%d", State#state.host}, {"%D", DN}], case eldap_filter:parse(State#state.dn_filter, SubstValues) of {ok, EldapFilter} -> - case eldap_pool:search(State#state.eldap_id, [ - {base, State#state.base}, - {filter, EldapFilter}, - {attributes, ["dn"]}]) of + case eldap_pool:search(State#state.eldap_id, + [{base, State#state.base}, + {filter, EldapFilter}, + {deref_aliases, State#state.deref_aliases}, + {attributes, ["dn"]}]) of #eldap_search_result{entries = [_|_]} -> DN; _ -> @@ -421,6 +426,11 @@ parse_options(Host) -> end, eldap_utils:check_filter(DNFilter), LocalFilter = ejabberd_config:get_local_option({ldap_local_filter, Host}), + DerefAliases = case ejabberd_config:get_local_option( + {ldap_deref_aliases, Host}) of + undefined -> never; + Val -> Val + end, #state{host = Host, eldap_id = Eldap_ID, bind_eldap_id = Bind_Eldap_ID, @@ -438,6 +448,7 @@ parse_options(Host) -> ufilter = UserFilter, sfilter = SearchFilter, lfilter = LocalFilter, + deref_aliases = DerefAliases, dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs }. diff --git a/src/eldap/eldap.erl b/src/eldap/eldap.erl index a134a4c95..aa22e1849 100644 --- a/src/eldap/eldap.erl +++ b/src/eldap/eldap.erl @@ -323,6 +323,14 @@ parse_search_args([{timeout, Timeout}|T],A) when is_integer(Timeout) -> parse_search_args(T,A#eldap_search{timeout = Timeout}); parse_search_args([{limit, Limit}|T],A) when is_integer(Limit) -> parse_search_args(T,A#eldap_search{limit = Limit}); +parse_search_args([{deref_aliases, never}|T],A) -> + parse_search_args(T,A#eldap_search{deref_aliases = neverDerefAliases}); +parse_search_args([{deref_aliases, searching}|T],A) -> + parse_search_args(T,A#eldap_search{deref_aliases = derefInSearching}); +parse_search_args([{deref_aliases, finding}|T],A) -> + parse_search_args(T,A#eldap_search{deref_aliases = derefFindingBaseObj}); +parse_search_args([{deref_aliases, always}|T],A) -> + parse_search_args(T,A#eldap_search{deref_aliases = derefAlways}); parse_search_args([H|_],_) -> throw({error,{unknown_arg, H}}); parse_search_args([],A) -> @@ -700,7 +708,7 @@ gen_req({search, A}) -> {searchRequest, #'SearchRequest'{baseObject = A#eldap_search.base, scope = v_scope(A#eldap_search.scope), - derefAliases = neverDerefAliases, + derefAliases = A#eldap_search.deref_aliases, sizeLimit = A#eldap_search.limit, timeLimit = v_timeout(A#eldap_search.timeout), typesOnly = v_bool(A#eldap_search.types_only), diff --git a/src/eldap/eldap.hrl b/src/eldap/eldap.hrl index 5436fa794..23b498435 100644 --- a/src/eldap/eldap.hrl +++ b/src/eldap/eldap.hrl @@ -28,6 +28,7 @@ limit = 0, attributes = [], types_only = false, + deref_aliases = neverDerefAliases, timeout = 0}). diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 0e1143d4a..04e90dd4d 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -63,6 +63,7 @@ base, password, uid, + deref_aliases, group_attr, group_desc, user_desc, @@ -314,6 +315,7 @@ eldap_search(State, FilterParseArgs, AttributesList) -> [{base, State#state.base}, {filter, EldapFilter}, {timeout, ?LDAP_SEARCH_TIMEOUT}, + {deref_aliases, State#state.deref_aliases}, {attributes, AttributesList}]) of #eldap_search_result{entries = Es} -> %% A result with entries. Return their list. @@ -659,6 +661,15 @@ parse_options(Host, Opts) -> "" -> GroupSubFilter; _ -> "(&" ++ GroupSubFilter ++ ConfigFilter ++ ")" end, + DerefAliases = case gen_mod:get_opt(deref_aliases, Opts, undefined) of + undefined -> + case ejabberd_config:get_local_option( + {deref_aliases, Host}) of + undefined -> never; + D -> D + end; + D -> D + end, #state{host = Host, eldap_id = Eldap_ID, servers = LDAPServers, @@ -672,6 +683,7 @@ parse_options(Host, Opts) -> base = LDAPBase, password = Password, uid = UIDAttr, + deref_aliases = DerefAliases, group_attr = GroupAttr, group_desc = GroupDesc, user_desc = UserDesc, diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index 56ba9eb4d..03c00f628 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -74,6 +74,7 @@ search_fields, search_reported, search_reported_attrs, + deref_aliases, matches }). @@ -287,9 +288,11 @@ find_ldap_user(User, State) -> VCardAttrs = State#state.vcard_map_attrs, case eldap_filter:parse(RFC2254_Filter, [{"%u", User}]) of {ok, EldapFilter} -> - case eldap_pool:search(Eldap_ID, [{base, Base}, - {filter, EldapFilter}, - {attributes, VCardAttrs}]) of + case eldap_pool:search(Eldap_ID, + [{base, Base}, + {filter, EldapFilter}, + {deref_aliases, State#state.deref_aliases}, + {attributes, VCardAttrs}]) of #eldap_search_result{entries = [E | _]} -> E; _ -> @@ -572,10 +575,12 @@ search(State, Data) -> Limit = State#state.matches, ReportedAttrs = State#state.search_reported_attrs, Filter = eldap:'and'([SearchFilter, eldap_utils:make_filter(Data, UIDs)]), - case eldap_pool:search(Eldap_ID, [{base, Base}, - {filter, Filter}, - {limit, Limit}, - {attributes, ReportedAttrs}]) of + case eldap_pool:search(Eldap_ID, + [{base, Base}, + {filter, Filter}, + {limit, Limit}, + {deref_aliases, State#state.deref_aliases}, + {attributes, ReportedAttrs}]) of #eldap_search_result{entries = E} -> search_items(E, State); _ -> @@ -779,6 +784,15 @@ parse_options(Host, Opts) -> _ -> [] end end, SearchReported) ++ UIDAttrs), + DerefAliases = case gen_mod:get_opt(deref_aliases, Opts, undefined) of + undefined -> + case ejabberd_config:get_local_option( + {deref_aliases, Host}) of + undefined -> never; + D -> D + end; + D -> D + end, #state{serverhost = Host, myhost = MyHost, eldap_id = Eldap_ID, @@ -801,5 +815,6 @@ parse_options(Host, Opts) -> search_fields = SearchFields, search_reported = SearchReported, search_reported_attrs = SearchReportedAttrs, + deref_aliases = DerefAliases, matches = Matches }. From 32fc36b17d5f1834b13a24c2256cf0803edd1a25 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Sep 2011 16:44:51 +0200 Subject: [PATCH 37/77] Add support for @online@ to add_user_to_group --- src/mod_shared_roster.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 4b2bd75e2..1043e86e3 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -617,14 +617,15 @@ add_user_to_group(Host, US, Group) -> case regexp:match(LUser, "^@.+@$") of {match,_,_} -> GroupOpts = mod_shared_roster:get_group_opts(Host, Group), - AllUsersOpt = - case LUser == "@all@" of - true -> [{all_users, true}]; - false -> [] + MoreGroupOpts = + case LUser of + "@all@" -> [{all_users, true}]; + "@online@" -> [{online_users, true}]; + _ -> [] end, mod_shared_roster:set_group_opts( Host, Group, - GroupOpts ++ AllUsersOpt); + GroupOpts ++ MoreGroupOpts); nomatch -> %% Push this new user to members of groups where this group is displayed push_user_to_displayed(LUser, LServer, Group, both), @@ -652,7 +653,9 @@ remove_user_from_group(Host, US, Group) -> NewGroupOpts = case LUser of "@all@" -> - lists:filter(fun(X) -> X/={all_users,true} end, GroupOpts) + lists:filter(fun(X) -> X/={all_users,true} end, GroupOpts); + "@online@" -> + lists:filter(fun(X) -> X/={online_users,true} end, GroupOpts) end, mod_shared_roster:set_group_opts(Host, Group, NewGroupOpts); nomatch -> From 9f4ae0710073c640cbaeaa89b1a02eea3c81e73a Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Sep 2011 16:50:22 +0200 Subject: [PATCH 38/77] added svg mimetype to default content types (thanks to Markus Kohlhase) --- src/web/mod_http_fileserver.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/web/mod_http_fileserver.erl b/src/web/mod_http_fileserver.erl index 95c080bc3..bd730b293 100644 --- a/src/web/mod_http_fileserver.erl +++ b/src/web/mod_http_fileserver.erl @@ -91,9 +91,10 @@ {".html", "text/html"}, {".jar", "application/java-archive"}, {".jpeg", "image/jpeg"}, - {".jpg", "image/jpeg"}, + {".jpg", "image/jpeg"}, {".js", "text/javascript"}, {".png", "image/png"}, + {".svg", "image/svg+xml"}, {".txt", "text/plain"}, {".xml", "application/xml"}, {".xpi", "application/x-xpinstall"}, From d5b4d6785879f0a5192c26f5b5e218aec8104798 Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Wed, 21 Sep 2011 14:39:50 +0200 Subject: [PATCH 39/77] Fix Denial of Service when user sends malformed publish stanza (thanks to Oleg Smirnov) (EJAB-1498) --- src/mod_pubsub/mod_pubsub.erl | 6 ++-- src/mod_pubsub/mod_pubsub_odbc.erl | 6 ++-- src/mod_pubsub/pubsub_odbc.patch | 46 +++++++++++++++--------------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 705c6e67a..d7643c21c 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -2108,8 +2108,10 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> case lists:member("auto-create", features(Type)) of true -> case create_node(Host, ServerHost, Node, Publisher, Type) of - {result, _} -> - publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload); + {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], + [{xmlelement, "create", [{"node", NewNode}], []}]}]} -> + publish_item(Host, ServerHost, list_to_binary(NewNode), + Publisher, ItemId, Payload); _ -> {error, ?ERR_ITEM_NOT_FOUND} end; diff --git a/src/mod_pubsub/mod_pubsub_odbc.erl b/src/mod_pubsub/mod_pubsub_odbc.erl index 9c24456cc..b8cf4899b 100644 --- a/src/mod_pubsub/mod_pubsub_odbc.erl +++ b/src/mod_pubsub/mod_pubsub_odbc.erl @@ -1921,8 +1921,10 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> case lists:member("auto-create", features(Type)) of true -> case create_node(Host, ServerHost, Node, Publisher, Type) of - {result, _} -> - publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload); + {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], + [{xmlelement, "create", [{"node", NewNode}], []}]}]} -> + publish_item(Host, ServerHost, list_to_binary(NewNode), + Publisher, ItemId, Payload); _ -> {error, ?ERR_ITEM_NOT_FOUND} end; diff --git a/src/mod_pubsub/pubsub_odbc.patch b/src/mod_pubsub/pubsub_odbc.patch index ef2ba0eee..cfa58640f 100644 --- a/src/mod_pubsub/pubsub_odbc.patch +++ b/src/mod_pubsub/pubsub_odbc.patch @@ -1,5 +1,5 @@ ---- mod_pubsub.erl 2011-08-31 16:08:42.000000000 +0200 -+++ mod_pubsub_odbc.erl 2011-08-31 16:08:42.000000000 +0200 +--- mod_pubsub.erl 2011-09-21 14:37:16.000000000 +0200 ++++ mod_pubsub_odbc.erl 2011-09-21 14:37:36.000000000 +0200 @@ -42,7 +42,7 @@ %%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see %%% XEP-0060 section 12.18. @@ -554,7 +554,7 @@ case Result of default -> {result, Reply}; _ -> {result, Result} -@@ -2241,7 +2054,7 @@ +@@ -2243,7 +2056,7 @@ %%

The permission are not checked in this function.

%% @todo We probably need to check that the user doing the query has the right %% to read the items. @@ -563,7 +563,7 @@ MaxItems = if SMaxItems == "" -> get_max_items_node(Host); -@@ -2255,12 +2068,13 @@ +@@ -2257,12 +2070,13 @@ {error, Error} -> {error, Error}; _ -> @@ -578,7 +578,7 @@ {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), if not RetreiveFeature -> -@@ -2273,11 +2087,11 @@ +@@ -2275,11 +2089,11 @@ node_call(Type, get_items, [NodeId, From, AccessModel, PresenceSubscription, RosterGroup, @@ -592,7 +592,7 @@ SendItems = case ItemIDs of [] -> Items; -@@ -2290,7 +2104,8 @@ +@@ -2292,7 +2106,8 @@ %% number of items sent to MaxItems: {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], [{xmlelement, "items", nodeAttr(Node), @@ -602,7 +602,7 @@ Error -> Error end -@@ -2312,10 +2127,15 @@ +@@ -2314,10 +2129,15 @@ Error -> Error end. get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) -> @@ -619,7 +619,7 @@ %% @spec (Host, Node, NodeId, Type, LJID, Number) -> any() -@@ -2327,31 +2147,29 @@ +@@ -2329,31 +2149,29 @@ %% Number = last | integer() %% @doc

Resend the items of a node to the user.

%% @todo use cache-last-item feature @@ -670,7 +670,7 @@ ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of {result, []} -> []; -@@ -2374,20 +2192,7 @@ +@@ -2376,20 +2194,7 @@ [{xmlelement, "items", nodeAttr(Node), itemsEls(ToSend)}]) end, @@ -692,7 +692,7 @@ %% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response} %% Host = host() -@@ -2489,7 +2294,8 @@ +@@ -2491,7 +2296,8 @@ error -> {error, ?ERR_BAD_REQUEST}; _ -> @@ -702,7 +702,7 @@ case lists:member(Owner, Owners) of true -> OwnerJID = jlib:make_jid(Owner), -@@ -2499,24 +2305,7 @@ +@@ -2501,24 +2307,7 @@ end, lists:foreach( fun({JID, Affiliation}) -> @@ -728,7 +728,7 @@ end, FilteredEntities), {result, []}; _ -> -@@ -2569,11 +2358,11 @@ +@@ -2571,11 +2360,11 @@ end. read_sub(Subscriber, Node, NodeID, SubID, Lang) -> @@ -742,7 +742,7 @@ OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)}, {"subid", SubID}|nodeAttr(Node)], [XdataEl]}, -@@ -2599,7 +2388,7 @@ +@@ -2601,7 +2390,7 @@ end. set_options_helper(Configuration, JID, NodeID, SubID, Type) -> @@ -751,7 +751,7 @@ {result, GoodSubOpts} -> GoodSubOpts; _ -> invalid end, -@@ -2628,7 +2417,7 @@ +@@ -2630,7 +2419,7 @@ write_sub(_Subscriber, _NodeID, _SubID, invalid) -> {error, extended_error(?ERR_BAD_REQUEST, "invalid-options")}; write_sub(Subscriber, NodeID, SubID, Options) -> @@ -760,7 +760,7 @@ {error, notfound} -> {error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; {result, _} -> -@@ -2796,8 +2585,8 @@ +@@ -2798,8 +2587,8 @@ {"subscription", subscription_to_string(Sub)} | nodeAttr(Node)], []}]}]}, ejabberd_router:route(service_jid(Host), jlib:make_jid(JID), Stanza) end, @@ -771,7 +771,7 @@ true -> Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) -> -@@ -3152,7 +2941,7 @@ +@@ -3154,7 +2943,7 @@ {Depth, [{N, get_node_subs(N)} || N <- Nodes]} end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))} end, @@ -780,7 +780,7 @@ {result, CollSubs} -> CollSubs; _ -> [] end. -@@ -3166,9 +2955,9 @@ +@@ -3168,9 +2957,9 @@ get_options_for_subs(NodeID, Subs) -> lists:foldl(fun({JID, subscribed, SubID}, Acc) -> @@ -792,7 +792,7 @@ _ -> Acc end; (_, Acc) -> -@@ -3357,6 +3146,30 @@ +@@ -3359,6 +3148,30 @@ Result end. @@ -823,7 +823,7 @@ %% @spec (Host, Options) -> MaxItems %% Host = host() %% Options = [Option] -@@ -3753,7 +3566,13 @@ +@@ -3755,7 +3568,13 @@ tree_action(Host, Function, Args) -> ?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]), Fun = fun() -> tree_call(Host, Function, Args) end, @@ -838,7 +838,7 @@ %% @doc

node plugin call.

node_call(Type, Function, Args) -> -@@ -3773,13 +3592,13 @@ +@@ -3775,13 +3594,13 @@ node_action(Host, Type, Function, Args) -> ?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]), @@ -854,7 +854,7 @@ case tree_call(Host, get_node, [Host, Node]) of N when is_record(N, pubsub_node) -> case Action(N) of -@@ -3791,13 +3610,19 @@ +@@ -3793,13 +3612,19 @@ Error end end, Trans). @@ -878,7 +878,7 @@ {result, Result} -> {result, Result}; {error, Error} -> {error, Error}; {atomic, {result, Result}} -> {result, Result}; -@@ -3805,6 +3630,15 @@ +@@ -3807,6 +3632,15 @@ {aborted, Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), {error, ?ERR_INTERNAL_SERVER_ERROR}; @@ -894,7 +894,7 @@ {'EXIT', Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), {error, ?ERR_INTERNAL_SERVER_ERROR}; -@@ -3813,6 +3647,17 @@ +@@ -3815,6 +3649,17 @@ {error, ?ERR_INTERNAL_SERVER_ERROR} end. From cb9aecf35e1753009381b3b01e52d602f6960370 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 9 Feb 2011 13:30:01 +0100 Subject: [PATCH 40/77] ejabberdctl doesn't support parameters with blankspaces (EJAB-1458) Revert "Escape output from ctlexec() to erl script (thanks to Dan Scott)(EJAB-1399)" This reverts commit 6dea2d230714b556118fab6710141a88cc8aad23. --- src/ejabberdctl.template | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template index d2f20fa79..0960f9aff 100644 --- a/src/ejabberdctl.template +++ b/src/ejabberdctl.template @@ -315,21 +315,13 @@ ctlexec () { CONN_NAME=$1; shift COMMAND=$@ - - CTLEXEC="$ERL \ + $EXEC_CMD "$ERL \ $NAME ${CONN_NAME} \ -noinput \ -hidden \ -pa $EJABBERD_EBIN_PATH \ $KERNEL_OPTS \ - -s ejabberd_ctl -extra $ERLANG_NODE" - - # quote input from the command line - for i in $COMMAND; do - CTLEXEC="$CTLEXEC '$i'"; - done - - $EXEC_CMD "$CTLEXEC" + -s ejabberd_ctl -extra $ERLANG_NODE $COMMAND" } # display ctl usage From 33c0ebb10087d5103c8045b261f2971765a2a17c Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Thu, 22 Sep 2011 20:51:59 +0200 Subject: [PATCH 41/77] fix broken PEP after EJAB-1498 (thanks to Karim Gemayel) --- src/mod_pubsub/node_pep.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mod_pubsub/node_pep.erl b/src/mod_pubsub/node_pep.erl index d8a28c728..5f24d5145 100644 --- a/src/mod_pubsub/node_pep.erl +++ b/src/mod_pubsub/node_pep.erl @@ -136,10 +136,7 @@ create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> {result, Allowed}. create_node(NodeId, Owner) -> - case node_hometree:create_node(NodeId, Owner) of - {result, _} -> {result, []}; - Error -> Error - end. + node_hometree:create_node(NodeId, Owner). delete_node(Removed) -> case node_hometree:delete_node(Removed) of From 32ff6b56ebbb937d38f238c64ca2b6311d254273 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Sep 2011 17:00:50 +0200 Subject: [PATCH 42/77] Fix mod_muc_log crash when first log entry is room being destroyed (EJAB-1499) --- src/mod_muc/mod_muc_log.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_log.erl b/src/mod_muc/mod_muc_log.erl index 40f59c3e7..7560016a0 100644 --- a/src/mod_muc/mod_muc_log.erl +++ b/src/mod_muc/mod_muc_log.erl @@ -945,7 +945,7 @@ get_room_state(RoomName, MucService) -> RoomPid = R#muc_online_room.pid, get_room_state(RoomPid); [] -> - room_not_found + #state{} end. get_room_state(RoomPid) -> From c4f9a050c9e58f21d0b95d9a3e4b6bfd2d91cbda Mon Sep 17 00:00:00 2001 From: Janusz Dziemidowicz Date: Tue, 20 Sep 2011 21:20:51 +0200 Subject: [PATCH 43/77] Decrease CPU usage caused by tls:send with large data. Sending one large chunk of data with tls:send eats lots of CPU power and blocks whole Erlang emulator. This is caused by the fact that encrypted output is read from memory BIO in 1k chunks. Memory BIO, after reading data, shifts the remaining part. If large chunks of data (few MB) is sent and then read in 1k chunks, then a _lot_ of shifting is performed eating CPU. The solution is to simply allocate binary of the needed size (amount of data in memory BIO can be retrieved with BIO_ctrl_pending) and then issue only one read that reads the whole data. --- src/tls/tls_drv.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/tls/tls_drv.c b/src/tls/tls_drv.c index 36cd8d9b9..bdb5446f2 100644 --- a/src/tls/tls_drv.c +++ b/src/tls/tls_drv.c @@ -407,22 +407,12 @@ static int tls_drv_control(ErlDrvData handle, break; case GET_ENCRYPTED_OUTPUT: die_unless(d->ssl, "SSL not initialized"); - size = BUF_SIZE + 1; - rlen = 1; + size = BIO_ctrl_pending(d->bio_write) + 1; b = driver_alloc_binary(size); b->orig_bytes[0] = 0; - while ((res = BIO_read(d->bio_write, - b->orig_bytes + rlen, BUF_SIZE)) > 0) - { - //printf("%d bytes of encrypted data read from state machine\r\n", res); - - rlen += res; - size += BUF_SIZE; - b = driver_realloc_binary(b, size); - } - b = driver_realloc_binary(b, rlen); + BIO_read(d->bio_write, b->orig_bytes + 1, size - 1); *rbuf = (char *)b; - return rlen; + return size; case GET_DECRYPTED_INPUT: if (!SSL_is_init_finished(d->ssl)) { From 2a41ca062847bdc4ac196ac209705b2202110ab6 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Sat, 23 Jul 2011 12:16:17 +0300 Subject: [PATCH 44/77] More correct dispatching of normal messages to conference room is_invitation/1 function returns true if element of message is an invitation --- src/mod_muc/mod_muc_room.erl | 94 ++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 35 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index c7d43e689..a0ff8df59 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -256,42 +256,48 @@ normal_state({route, From, "", From, Err), {next_state, normal_state, StateData}; Type when (Type == "") or (Type == "normal") -> - case catch check_invitation(From, Els, Lang, StateData) of - {error, Error} -> - Err = jlib:make_error_reply( - Packet, Error), - ejabberd_router:route( - StateData#state.jid, - From, Err), - {next_state, normal_state, StateData}; - IJID -> - Config = StateData#state.config, - case Config#config.members_only of - true -> - case get_affiliation(IJID, StateData) of - none -> - NSD = set_affiliation( - IJID, - member, - StateData), - case (NSD#state.config)#config.persistent of - true -> - mod_muc:store_room( - NSD#state.host, - NSD#state.room, - make_opts(NSD)); + IsInvitation = is_invitation(Els), + if + IsInvitation -> + case catch check_invitation(From, Els, Lang, StateData) of + {error, Error} -> + Err = jlib:make_error_reply( + Packet, Error), + ejabberd_router:route( + StateData#state.jid, + From, Err), + {next_state, normal_state, StateData}; + IJID -> + Config = StateData#state.config, + case Config#config.members_only of + true -> + case get_affiliation(IJID, StateData) of + none -> + NSD = set_affiliation( + IJID, + member, + StateData), + case (NSD#state.config)#config.persistent of + true -> + mod_muc:store_room( + NSD#state.host, + NSD#state.room, + make_opts(NSD)); + _ -> + ok + end, + {next_state, normal_state, NSD}; _ -> - ok - end, - {next_state, normal_state, NSD}; - _ -> - {next_state, normal_state, - StateData} - end; - false -> - {next_state, normal_state, StateData} - end - end; + {next_state, normal_state, + StateData} + end; + false -> + {next_state, normal_state, StateData} + end + end; + true -> + {next_state, normal_state, StateData} + end; _ -> ErrText = "Improper message type", Err = jlib:make_error_reply( @@ -3623,6 +3629,24 @@ get_mucroom_disco_items(StateData) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support +is_invitation(Els) -> + case xml:remove_cdata(Els) of + [{xmlelement, "x", _Attrs1, Els1} = XEl] -> + case xml:get_tag_attr_s("xmlns", XEl) of + ?NS_MUC_USER -> + case xml:remove_cdata(Els1) of + [{xmlelement, "invite", _, _}] -> + true; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end. + check_invitation(From, Els, Lang, StateData) -> FAffiliation = get_affiliation(From, StateData), CanInvite = (StateData#state.config)#config.allow_user_invites From f175be6b785702fab558f333fd2933ab38ff7411 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Sat, 23 Jul 2011 17:13:27 +0300 Subject: [PATCH 45/77] Add function for detecting voice requests --- src/mod_muc/mod_muc_room.erl | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index a0ff8df59..c8623c10a 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -257,6 +257,7 @@ normal_state({route, From, "", {next_state, normal_state, StateData}; Type when (Type == "") or (Type == "normal") -> IsInvitation = is_invitation(Els), + IsVoiceRequest = is_voice_request(Els), if IsInvitation -> case catch check_invitation(From, Els, Lang, StateData) of @@ -295,6 +296,8 @@ normal_state({route, From, "", {next_state, normal_state, StateData} end end; + IsVoiceRequest -> + {next_state, normal_state, StateData}; true -> {next_state, normal_state, StateData} end; @@ -3626,6 +3629,48 @@ get_mucroom_disco_items(StateData) -> end, ?DICT:to_list(StateData#state.users)). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Voice request support + +is_voice_request(Els) -> + try + case xml:remove_cdata(Els) of + [{xmlelement, "x", _, Els1} = XEl] -> + case xml:get_attr_s("xmlns", XEl) of + ?NS_XDATA -> + lists:foldl(check_voice_requests_fields, true, Els1) + end + end + catch + _ -> + false + end. + +check_voice_request_fields({xmlelement, "field", _, Els} = Elem, Acc) -> + try + case Acc of + true -> + case xml:get_attr_s("var", Elem) of + "FORM_TYPE" -> + [{xmlelement, "value", _, Value}] = Els, + case xml:get_cdata(Value) of + "http://jabber.org/protocol/muc#request" -> + true + end; + "muc#role" -> + [{xmlelement, "value", _, Value}] = Els, + case xml:get_cdata(Value) of + "participant" -> + true + end + end + end + catch + _ -> + false + end. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support From 30d89abf665acc6113390cc27434391d41bc1a4d Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Sun, 24 Jul 2011 12:46:47 +0300 Subject: [PATCH 46/77] Implemented sending voice request and fixed some bugs --- src/mod_muc/mod_muc_room.erl | 50 ++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index c8623c10a..2a6f7a13e 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -297,6 +297,7 @@ normal_state({route, From, "", end end; IsVoiceRequest -> + send_voice_request(From, StateData), {next_state, normal_state, StateData}; true -> {next_state, normal_state, StateData} @@ -3636,9 +3637,13 @@ is_voice_request(Els) -> try case xml:remove_cdata(Els) of [{xmlelement, "x", _, Els1} = XEl] -> - case xml:get_attr_s("xmlns", XEl) of + case xml:get_tag_attr_s("xmlns", XEl) of ?NS_XDATA -> - lists:foldl(check_voice_requests_fields, true, Els1) + lists:foldl( + fun(X,Y) -> + check_voice_request_fields(X,Y) + end, + true, xml:remove_cdata(Els1)) end end catch @@ -3650,15 +3655,15 @@ check_voice_request_fields({xmlelement, "field", _, Els} = Elem, Acc) -> try case Acc of true -> - case xml:get_attr_s("var", Elem) of + case xml:get_tag_attr_s("var", Elem) of "FORM_TYPE" -> - [{xmlelement, "value", _, Value}] = Els, + [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), case xml:get_cdata(Value) of "http://jabber.org/protocol/muc#request" -> true end; "muc#role" -> - [{xmlelement, "value", _, Value}] = Els, + [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), case xml:get_cdata(Value) of "participant" -> true @@ -3670,6 +3675,41 @@ check_voice_request_fields({xmlelement, "field", _, Els} = Elem, Acc) -> false end. +prepare_request_form(Requester, Nick, Lang) -> + {xmlelement, "message", [{"type", "normal"}], [ + {xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}], + [ + {xmlelement, "title", [], + [{xmlcdata, translate:translate(Lang, "Voice request")}]}, + {xmlelement, "instructions", [], + [{xmlcdata, translate:translate(Lang, "To approve this request for voice, select the "Grant voice to this person?" checkbox and click OK. To skip this request, click the cancel button.")}]}, + {xmlelement, "field", [{"var", "FORM_TYPE"}, {"type", "hidden"}], + [{xmlelement, "value", [], + [{xmlcdata, "http://jabber.org/protocol/muc#request"}]}]}, + ?STRINGXFIELD("Requested role", "muc#role", "participant"), + ?STRINGXFIELD("User JID", "muc#jid", jlib:jid_to_string(Requester)), + ?STRINGXFIELD("Nickname", "muc#roomnick", Nick), + ?BOOLXFIELD("Grant voice to this person?", "muc#request_allow", false) + ] + }]}. + +send_voice_request(From, StateData) -> + ?ERROR_MSG("MUC ROOM: send_voice_request: From = ~p~n", [From]), + Moderators = search_role(moderator, StateData), + ?ERROR_MSG("MUC ROOM: send_voice_request: Moderators = ~p~n", [Moderators]), + [{_, #user{nick = FromNick}}] = lists:filter( + fun({_, #user{jid = Jid}}) -> Jid == From end, + ?DICT:to_list(StateData#state.users)), + ?ERROR_MSG("MUC ROOM: send_voice_request: FromNick = ~p~n", [FromNick]), + lists:map( + fun({_, User}) -> send_packet_to( + prepare_request_form(From, FromNick, ""), + User#user.jid, StateData) + end, Moderators), + ok. + +send_packet_to(Packet, To, StateData) -> + ejabberd_router:route(StateData#state.jid, To, Packet). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support From 44987479a521e6ad56d5c7492451af72ebc5b961 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Sun, 24 Jul 2011 14:57:13 +0300 Subject: [PATCH 47/77] Fix exception handling in is_voice_request/1 --- src/mod_muc/mod_muc_room.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 2a6f7a13e..5f9edfd2f 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3647,7 +3647,7 @@ is_voice_request(Els) -> end end catch - _ -> + error: _ -> false end. @@ -3671,7 +3671,7 @@ check_voice_request_fields({xmlelement, "field", _, Els} = Elem, Acc) -> end end catch - _ -> + error: _ -> false end. From c77358bdc1eebecd8fa51c1d850444e1c55d77c1 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Sun, 24 Jul 2011 15:01:04 +0300 Subject: [PATCH 48/77] Use exception handling in is_invitation/1 --- src/mod_muc/mod_muc_room.erl | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 5f9edfd2f..16b8de841 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3715,20 +3715,19 @@ send_packet_to(Packet, To, StateData) -> % Invitation support is_invitation(Els) -> - case xml:remove_cdata(Els) of - [{xmlelement, "x", _Attrs1, Els1} = XEl] -> - case xml:get_tag_attr_s("xmlns", XEl) of - ?NS_MUC_USER -> - case xml:remove_cdata(Els1) of - [{xmlelement, "invite", _, _}] -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> + try + case xml:remove_cdata(Els) of + [{xmlelement, "x", _Attrs1, Els1} = XEl] -> + case xml:get_tag_attr_s("xmlns", XEl) of + ?NS_MUC_USER -> + case xml:remove_cdata(Els1) of + [{xmlelement, "invite", _, _}] -> + true + end + end + end + catch + error: _ -> false end. From d83716decef7383b9858ba44baf1a513df26bd26 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Sun, 24 Jul 2011 15:06:12 +0300 Subject: [PATCH 49/77] Use ejabberd_router:route/3 directly, instead of send_packet_to/3 --- src/mod_muc/mod_muc_room.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 16b8de841..b1ecc1af4 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3702,15 +3702,14 @@ send_voice_request(From, StateData) -> ?DICT:to_list(StateData#state.users)), ?ERROR_MSG("MUC ROOM: send_voice_request: FromNick = ~p~n", [FromNick]), lists:map( - fun({_, User}) -> send_packet_to( - prepare_request_form(From, FromNick, ""), - User#user.jid, StateData) + fun({_, User}) -> + ejabberd_router:route( + StateData#state.jid, + User#user.jid, + prepare_request_form(From, FromNick, "")) end, Moderators), ok. -send_packet_to(Packet, To, StateData) -> - ejabberd_router:route(StateData#state.jid, To, Packet). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support From 04267930ded9fe3b934d866a405eeaf5c38a95fa Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Sun, 24 Jul 2011 15:25:53 +0300 Subject: [PATCH 50/77] Fix form instructions --- src/mod_muc/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index b1ecc1af4..b46370ade 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3682,7 +3682,7 @@ prepare_request_form(Requester, Nick, Lang) -> {xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "Voice request")}]}, {xmlelement, "instructions", [], - [{xmlcdata, translate:translate(Lang, "To approve this request for voice, select the "Grant voice to this person?" checkbox and click OK. To skip this request, click the cancel button.")}]}, + [{xmlcdata, translate:translate(Lang, "To approve this request for voice, select the \"Grant voice to this person?\" checkbox and click OK. To skip this request, click the cancel button.")}]}, {xmlelement, "field", [{"var", "FORM_TYPE"}, {"type", "hidden"}], [{xmlelement, "value", [], [{xmlcdata, "http://jabber.org/protocol/muc#request"}]}]}, From 8dd6f128bf6343045412562254ddba2fc24b84e6 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Mon, 25 Jul 2011 19:09:11 +0300 Subject: [PATCH 51/77] Implement voice approvement detection and remove some debug code --- src/mod_muc/mod_muc_room.erl | 58 ++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index b46370ade..d569cdb15 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -258,6 +258,7 @@ normal_state({route, From, "", Type when (Type == "") or (Type == "normal") -> IsInvitation = is_invitation(Els), IsVoiceRequest = is_voice_request(Els), + IsVoiceApprovement = is_voice_approvement(Els), if IsInvitation -> case catch check_invitation(From, Els, Lang, StateData) of @@ -299,6 +300,8 @@ normal_state({route, From, "", IsVoiceRequest -> send_voice_request(From, StateData), {next_state, normal_state, StateData}; + IsVoiceApprovement -> + {next_state, normal_state, StateData}; true -> {next_state, normal_state, StateData} end; @@ -3694,13 +3697,10 @@ prepare_request_form(Requester, Nick, Lang) -> }]}. send_voice_request(From, StateData) -> - ?ERROR_MSG("MUC ROOM: send_voice_request: From = ~p~n", [From]), Moderators = search_role(moderator, StateData), - ?ERROR_MSG("MUC ROOM: send_voice_request: Moderators = ~p~n", [Moderators]), [{_, #user{nick = FromNick}}] = lists:filter( fun({_, #user{jid = Jid}}) -> Jid == From end, ?DICT:to_list(StateData#state.users)), - ?ERROR_MSG("MUC ROOM: send_voice_request: FromNick = ~p~n", [FromNick]), lists:map( fun({_, User}) -> ejabberd_router:route( @@ -3710,6 +3710,58 @@ send_voice_request(From, StateData) -> end, Moderators), ok. +is_voice_approvement(Els) -> + try + case xml:remove_cdata(Els) of + [{xmlelement, "x", Attrs, Els1}] -> + case xml:get_attr_s("xmlns", Attrs) of + ?NS_XDATA -> + case xml:get_attr_s("type", Attrs) of + "submit" -> + lists:foldl( + fun(X,Y) -> + check_voice_approvement_fields(X,Y) + end, + true, xml:remove_cdata(Els1)) + end + end + end + catch + error: _ -> + false + end. + +check_voice_approvement_fields({xmlelement, "field", Attrs, Els}, Acc) -> + if Acc -> + case xml:get_attr_s("var", Attrs) of + "FORM_TYPE" -> + [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), + case xml:get_cdata(Value) of + "http://jabber.org/protocol/muc#request" -> + true + end; + "muc#role" -> + [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), + case xml:get_cdata(Value) of + "participant" -> + true + end; + "muc#jid" -> + true; % TODO: make some validation here + "muc#roomnick" -> + true; + "muc#request_allow" -> + % XXX: submitted forms with request_allow unchecked ignored here + [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), + case xml:get_cdata(Value) of + "true" -> + true; + "1" -> + true + end + end + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support From 18dd0d08cbc9245405a93de859f037f40190a2f3 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Mon, 25 Jul 2011 19:54:45 +0300 Subject: [PATCH 52/77] Implement basic voice request processing logic. No rate control or configuration for now --- src/mod_muc/mod_muc_room.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index d569cdb15..aec54b684 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -298,7 +298,16 @@ normal_state({route, From, "", end end; IsVoiceRequest -> - send_voice_request(From, StateData), + case is_visitor(From, StateData) of + true -> + send_voice_request(From, StateData); + _ -> + ErrText = "Only visitors allowed to request voice", + Err = jlib:make_error_reply( + Packet, ?ERRT_NOT_ALLOWED(Lang, ErrText)), + ejabberd_router:route( + StateData#state.jid, From, Err) + end, {next_state, normal_state, StateData}; IsVoiceApprovement -> {next_state, normal_state, StateData}; From 6193ed63b1e6f2a8f9bccba1675e25aa677a7c82 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Mon, 25 Jul 2011 22:46:59 +0300 Subject: [PATCH 53/77] Voice approvement support --- src/mod_muc/mod_muc_room.erl | 57 +++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index aec54b684..3e61f80d8 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -310,7 +310,37 @@ normal_state({route, From, "", end, {next_state, normal_state, StateData}; IsVoiceApprovement -> - {next_state, normal_state, StateData}; + NewStateData = case is_moderator(From, StateData) of + true -> + case extract_jid_from_voice_approvement(Els) of + {error, X} -> + ?ERROR_MSG("Failed to extract JID from voice approvement: ~n~p", [X]), + ErrText = "Failed to extract JID from your voice request approvement", + Err = jlib:make_error_reply( + Packet, ?ERRT_BAD_REQUEST(Lang, ErrText)), + ejabberd_router:route( + StateData#state.jid, From, Err), + StateData; + TargetJid -> + case is_visitor(TargetJid, StateData) of + true -> + Reason = [], + NSD = set_role(TargetJid, participant, StateData), + catch send_new_presence(TargetJid, Reason, NSD), + NSD; + _ -> + StateData + end + end; + _ -> + ErrText = "Only moderators can approve voice requests", + Err = jlib:make_error_reply( + Packet, ?ERRT_NOT_ALLOWED(Lang, ErrText)), + ejabberd_router:route( + StateData#state.jid, From, Err), + StateData + end, + {next_state, normal_state, NewStateData}; true -> {next_state, normal_state, StateData} end; @@ -3771,6 +3801,31 @@ check_voice_approvement_fields({xmlelement, "field", Attrs, Els}, Acc) -> end end. +extract_jid_from_voice_approvement(Els) -> + try + [{xmlelement, "x", _, Els1}] = xml:remove_cdata(Els), + Jid = lists:foldl( + fun(El, Acc) -> + case xml:get_tag_attr_s("var", El) of + "muc#jid" -> + {xmlelement, "field", _, Els2} = El, + [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els2), + xml:get_cdata(Value); + _ -> + Acc + end + end, {error, jid_not_found}, xml:remove_cdata(Els1)), + case Jid of + {error, _} = Err -> + Err; + _ -> + jlib:string_to_jid(Jid) + end + catch + error: X -> + {error, X} + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support From 5921f9c506f2a2c11160a7465a70a24d3cb2be5f Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Thu, 28 Jul 2011 20:40:46 +0300 Subject: [PATCH 54/77] Simple rate control Visitor allowed to send new voice request only after specified amount of time (or after rejoining). --- src/mod_muc/mod_muc_room.erl | 67 ++++++++++++++++++++++++++++++------ src/mod_muc/mod_muc_room.hrl | 1 + 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 3e61f80d8..c54af83d2 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -298,23 +298,39 @@ normal_state({route, From, "", end end; IsVoiceRequest -> - case is_visitor(From, StateData) of + NewStateData = case is_visitor(From, StateData) of true -> - send_voice_request(From, StateData); + MinInterval = 1800, + FromNick = find_nick_by_jid(From, StateData), + LastTime = last_voice_request_time(FromNick, StateData), + {MegaSecs, Secs, _} = erlang:now(), + Now = MegaSecs * 1000000 + Secs, + if + timer:now_diff(LastTime, erlang:now()) > MinInterval*1000000 -> + send_voice_request(From, StateData), + update_voice_request_time(FromNick, StateData); + true -> + ErrText = "Please, wait for a while before sending new voice request", + Err = jlib:make_error_reply( + Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)), + ejabberd_router:route( + StateData#state.jid, From, Err), + StateData + end; _ -> ErrText = "Only visitors allowed to request voice", Err = jlib:make_error_reply( Packet, ?ERRT_NOT_ALLOWED(Lang, ErrText)), ejabberd_router:route( - StateData#state.jid, From, Err) + StateData#state.jid, From, Err), + StateData end, - {next_state, normal_state, StateData}; + {next_state, normal_state, NewStateData}; IsVoiceApprovement -> NewStateData = case is_moderator(From, StateData) of true -> case extract_jid_from_voice_approvement(Els) of {error, X} -> - ?ERROR_MSG("Failed to extract JID from voice approvement: ~n~p", [X]), ErrText = "Failed to extract JID from your voice request approvement", Err = jlib:make_error_reply( Packet, ?ERRT_BAD_REQUEST(Lang, ErrText)), @@ -1533,7 +1549,9 @@ remove_online_user(JID, StateData, Reason) -> error -> StateData#state.nicks end, - StateData#state{users = Users, nicks = Nicks}. + LastTimes = ?DICT:erase(Nick, StateData#state.last_voice_request_time), + StateData#state{users = Users, nicks = Nicks, + last_voice_request_time = LastTimes}. filter_presence({xmlelement, "presence", Attrs, Els}) -> @@ -1641,6 +1659,12 @@ get_priority_from_presence(PresencePacket) -> end end. +find_nick_by_jid(Jid, StateData) -> + [{_, #user{nick = Nick}}] = lists:filter( + fun({_, #user{jid = FJid}}) -> FJid == Jid end, + ?DICT:to_list(StateData#state.users)), + Nick. + is_nick_change(JID, Nick, StateData) -> LJID = jlib:jid_tolower(JID), case Nick of @@ -2169,7 +2193,18 @@ change_nick(JID, Nick, StateData) -> ?DICT:store(OldNick, OldNickUsers -- [LJID], StateData#state.nicks)) end, - NewStateData = StateData#state{users = Users, nicks = Nicks}, + LastTimes = + case ?DICT:find(OldNick, StateData#state.last_voice_request_time) of + {ok, Time} -> + ?DICT:store( + Nick, Time, + ?DICT:erase(OldNick, StateData#state.last_voice_request_time) + ); + error -> + StateData#state.last_voice_request_time + end, + NewStateData = StateData#state{users = Users, nicks = Nicks, + last_voice_request_time = LastTimes}, send_nick_changing(JID, OldNick, NewStateData, SendOldUnavailable, SendNewAvailable), add_to_log(nickchange, {OldNick, Nick}, StateData), NewStateData. @@ -3737,9 +3772,7 @@ prepare_request_form(Requester, Nick, Lang) -> send_voice_request(From, StateData) -> Moderators = search_role(moderator, StateData), - [{_, #user{nick = FromNick}}] = lists:filter( - fun({_, #user{jid = Jid}}) -> Jid == From end, - ?DICT:to_list(StateData#state.users)), + FromNick = find_nick_by_jid(From, StateData), lists:map( fun({_, User}) -> ejabberd_router:route( @@ -3826,6 +3859,20 @@ extract_jid_from_voice_approvement(Els) -> {error, X} end. +last_voice_request_time(Nick, StateData) -> + case ?DICT:find(Nick, StateData#state.last_voice_request_time) of + {ok, Value} -> + Value; + error -> + 0 + end. + +update_voice_request_time(Nick, StateData) -> + {MegaSecs, Secs, _} = erlang:now(), + Time = MegaSecs * 1000000 + Secs, + NewDict = ?DICT:store(Nick, Time, StateData#state.last_voice_request_time), + StateData#state{last_voice_request_time = NewDict}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support diff --git a/src/mod_muc/mod_muc_room.hrl b/src/mod_muc/mod_muc_room.hrl index 10120cba6..abbc1870e 100644 --- a/src/mod_muc/mod_muc_room.hrl +++ b/src/mod_muc/mod_muc_room.hrl @@ -69,6 +69,7 @@ jid, config = #config{}, users = ?DICT:new(), + last_voice_request_time = ?DICT:new(), robots = ?DICT:new(), nicks = ?DICT:new(), affiliations = ?DICT:new(), From 6708914e993c26de71dfff7537befdd28791eb5f Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Fri, 29 Jul 2011 21:22:06 +0300 Subject: [PATCH 55/77] Add 2 room config options - Allow voice requests? - Minimum interval between voice requests --- src/mod_muc/mod_muc_room.erl | 30 ++++++++++++++++++++++++++---- src/mod_muc/mod_muc_room.hrl | 2 ++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index c54af83d2..56839d280 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -298,9 +298,10 @@ normal_state({route, From, "", end end; IsVoiceRequest -> - NewStateData = case is_visitor(From, StateData) of - true -> - MinInterval = 1800, + NewStateData = case {is_visitor(From, StateData), + (StateData#state.config)#config.allow_voice_requests} of + {true, true} -> + MinInterval = (StateData#state.config)#config.voice_request_min_interval, FromNick = find_nick_by_jid(From, StateData), LastTime = last_voice_request_time(FromNick, StateData), {MegaSecs, Secs, _} = erlang:now(), @@ -317,6 +318,13 @@ normal_state({route, From, "", StateData#state.jid, From, Err), StateData end; + {_, false} -> + ErrText = "Voice requests are disabled in this room", + Err = jlib:make_error_reply( + Packet, ?ERRT_FORBIDDEN(Lang, ErrText)), + ejabberd_router:route( + StateData#state.jid, From, Err), + StateData; _ -> ErrText = "Only visitors allowed to request voice", Err = jlib:make_error_reply( @@ -3266,7 +3274,13 @@ get_config(Lang, StateData, From) -> Config#config.allow_visitor_status), ?BOOLXFIELD("Allow visitors to change nickname", "muc#roomconfig_allowvisitornickchange", - Config#config.allow_visitor_nickchange) + Config#config.allow_visitor_nickchange), + ?BOOLXFIELD("Allow visitors to send voice requests", + "muc#roomconfig_allowvoicerequests", + Config#config.allow_voice_requests), + ?STRINGXFIELD("Minimum interval between voice requests (in seconds)", + "muc#roomconfig_voicerequestmininterval", + erlang:integer_to_list(Config#config.voice_request_min_interval)) ] ++ case ejabberd_captcha:is_feature_available() of true -> @@ -3409,6 +3423,10 @@ set_xoption([{"muc#roomconfig_roomsecret", [Val]} | Opts], Config) -> ?SET_STRING_XOPT(password, Val); set_xoption([{"anonymous", [Val]} | Opts], Config) -> ?SET_BOOL_XOPT(anonymous, Val); +set_xoption([{"muc#roomconfig_allowvoicerequests", [Val]} | Opts], Config) -> + ?SET_BOOL_XOPT(allow_voice_requests, Val); +set_xoption([{"muc#roomconfig_voicerequestmininterval", [Val]} | Opts], Config) -> + ?SET_NAT_XOPT(voice_request_min_interval, Val); set_xoption([{"muc#roomconfig_whois", [Val]} | Opts], Config) -> case Val of "moderators" -> @@ -3501,6 +3519,8 @@ set_opts([{Opt, Val} | Opts], StateData) -> anonymous -> StateData#state{config = (StateData#state.config)#config{anonymous = Val}}; logging -> StateData#state{config = (StateData#state.config)#config{logging = Val}}; captcha_whitelist -> StateData#state{config = (StateData#state.config)#config{captcha_whitelist = ?SETS:from_list(Val)}}; + allow_voice_requests -> StateData#state{config = (StateData#state.config)#config{allow_voice_requests = Val}}; + voice_request_min_interval -> StateData#state{config = (StateData#state.config)#config{voice_request_min_interval = Val}}; max_users -> ServiceMaxUsers = get_service_max_users(StateData), MaxUsers = if @@ -3546,6 +3566,8 @@ make_opts(StateData) -> ?MAKE_CONFIG_OPT(anonymous), ?MAKE_CONFIG_OPT(logging), ?MAKE_CONFIG_OPT(max_users), + ?MAKE_CONFIG_OPT(allow_voice_requests), + ?MAKE_CONFIG_OPT(voice_request_min_interval), {captcha_whitelist, ?SETS:to_list((StateData#state.config)#config.captcha_whitelist)}, {affiliations, ?DICT:to_list(StateData#state.affiliations)}, diff --git a/src/mod_muc/mod_muc_room.hrl b/src/mod_muc/mod_muc_room.hrl index abbc1870e..edfa3f410 100644 --- a/src/mod_muc/mod_muc_room.hrl +++ b/src/mod_muc/mod_muc_room.hrl @@ -45,6 +45,8 @@ password_protected = false, password = "", anonymous = true, + allow_voice_requests = true, + voice_request_min_interval = 1800, max_users = ?MAX_USERS_DEFAULT, logging = false, captcha_whitelist = ?SETS:empty() From 6848d3affebe6b6cc3002295b3a70c051e711d89 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Sun, 31 Jul 2011 22:48:56 +0300 Subject: [PATCH 56/77] Ignore any extra elements in voice approvement form submission --- src/mod_muc/mod_muc_room.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 56839d280..d46817737 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3852,9 +3852,13 @@ check_voice_approvement_fields({xmlelement, "field", Attrs, Els}, Acc) -> true; "1" -> true - end + end; + _ -> + true % ignore unknown fields end - end. + end; +check_voice_approvement_fields({xmlelement, _, _, _}, _) -> + true. extract_jid_from_voice_approvement(Els) -> try From 604563af761145406339909bca1048b4bac79dd0 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 10:08:36 +0300 Subject: [PATCH 57/77] Remove needless time conversions --- src/mod_muc/mod_muc_room.erl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index d46817737..e0a753424 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -304,8 +304,6 @@ normal_state({route, From, "", MinInterval = (StateData#state.config)#config.voice_request_min_interval, FromNick = find_nick_by_jid(From, StateData), LastTime = last_voice_request_time(FromNick, StateData), - {MegaSecs, Secs, _} = erlang:now(), - Now = MegaSecs * 1000000 + Secs, if timer:now_diff(LastTime, erlang:now()) > MinInterval*1000000 -> send_voice_request(From, StateData), @@ -3894,9 +3892,7 @@ last_voice_request_time(Nick, StateData) -> end. update_voice_request_time(Nick, StateData) -> - {MegaSecs, Secs, _} = erlang:now(), - Time = MegaSecs * 1000000 + Secs, - NewDict = ?DICT:store(Nick, Time, StateData#state.last_voice_request_time), + NewDict = ?DICT:store(Nick, erlang:now(), StateData#state.last_voice_request_time), StateData#state{last_voice_request_time = NewDict}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From c196c1f5e5cdb35dd8895f83257cec593be4028d Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 10:55:43 +0300 Subject: [PATCH 58/77] Use lists:foreach instead of lists:map in send_voice_request/2 --- src/mod_muc/mod_muc_room.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index e0a753424..18856fcd3 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3793,14 +3793,13 @@ prepare_request_form(Requester, Nick, Lang) -> send_voice_request(From, StateData) -> Moderators = search_role(moderator, StateData), FromNick = find_nick_by_jid(From, StateData), - lists:map( + lists:foreach( fun({_, User}) -> ejabberd_router:route( StateData#state.jid, User#user.jid, prepare_request_form(From, FromNick, "")) - end, Moderators), - ok. + end, Moderators). is_voice_approvement(Els) -> try From b1d8168dd38e25a71333d9dcab95af17300e36dd Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 13:30:00 +0300 Subject: [PATCH 59/77] Use bare JID as key in state.last_voice_request_time --- src/mod_muc/mod_muc_room.erl | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 18856fcd3..5ae18a86f 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -302,12 +302,13 @@ normal_state({route, From, "", (StateData#state.config)#config.allow_voice_requests} of {true, true} -> MinInterval = (StateData#state.config)#config.voice_request_min_interval, - FromNick = find_nick_by_jid(From, StateData), - LastTime = last_voice_request_time(FromNick, StateData), + BareFrom = jlib:jid_remove_resource(jlib:jid_to_lower(From)), + LastTime = last_voice_request_time(BareFrom, StateData), + TimeFromLastRequest = timer:now_diff(LastTime, erlang:now()), if - timer:now_diff(LastTime, erlang:now()) > MinInterval*1000000 -> + TimeFromLastRequest > MinInterval*1000000 -> send_voice_request(From, StateData), - update_voice_request_time(FromNick, StateData); + update_voice_request_time(BareFrom, StateData); true -> ErrText = "Please, wait for a while before sending new voice request", Err = jlib:make_error_reply( @@ -1555,7 +1556,8 @@ remove_online_user(JID, StateData, Reason) -> error -> StateData#state.nicks end, - LastTimes = ?DICT:erase(Nick, StateData#state.last_voice_request_time), + LastTimes = ?DICT:erase(jlib:jid_remove_resource(LJID), + StateData#state.last_voice_request_time), StateData#state{users = Users, nicks = Nicks, last_voice_request_time = LastTimes}. @@ -2199,18 +2201,7 @@ change_nick(JID, Nick, StateData) -> ?DICT:store(OldNick, OldNickUsers -- [LJID], StateData#state.nicks)) end, - LastTimes = - case ?DICT:find(OldNick, StateData#state.last_voice_request_time) of - {ok, Time} -> - ?DICT:store( - Nick, Time, - ?DICT:erase(OldNick, StateData#state.last_voice_request_time) - ); - error -> - StateData#state.last_voice_request_time - end, - NewStateData = StateData#state{users = Users, nicks = Nicks, - last_voice_request_time = LastTimes}, + NewStateData = StateData#state{users = Users, nicks = Nicks}, send_nick_changing(JID, OldNick, NewStateData, SendOldUnavailable, SendNewAvailable), add_to_log(nickchange, {OldNick, Nick}, StateData), NewStateData. @@ -3882,16 +3873,16 @@ extract_jid_from_voice_approvement(Els) -> {error, X} end. -last_voice_request_time(Nick, StateData) -> - case ?DICT:find(Nick, StateData#state.last_voice_request_time) of +last_voice_request_time(BareJID, StateData) -> + case ?DICT:find(BareJID, StateData#state.last_voice_request_time) of {ok, Value} -> Value; error -> 0 end. -update_voice_request_time(Nick, StateData) -> - NewDict = ?DICT:store(Nick, erlang:now(), StateData#state.last_voice_request_time), +update_voice_request_time(BareJID, StateData) -> + NewDict = ?DICT:store(BareJID, erlang:now(), StateData#state.last_voice_request_time), StateData#state{last_voice_request_time = NewDict}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 7c8eab4f43b1976740d605c9406c25d52af5507a Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 16:04:29 +0300 Subject: [PATCH 60/77] Use jlib:parse_xdata_submit/1 in is_voice_request/1 and is_voice_approvement/1 --- src/mod_muc/mod_muc_room.erl | 186 +++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 84 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 5ae18a86f..31be2b819 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -337,7 +337,7 @@ normal_state({route, From, "", NewStateData = case is_moderator(From, StateData) of true -> case extract_jid_from_voice_approvement(Els) of - {error, X} -> + {error, _} -> ErrText = "Failed to extract JID from your voice request approvement", Err = jlib:make_error_reply( Packet, ?ERRT_BAD_REQUEST(Lang, ErrText)), @@ -3721,46 +3721,53 @@ get_mucroom_disco_items(StateData) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Voice request support +is_voice_request({xmlelement, "x", _, _} = Elem) -> + try + case xml:get_tag_attr_s("xmlns", Elem) of + ?NS_XDATA -> + Fields = jlib:parse_xdata_submit(Elem), + lists:foldl( + fun(X,Y) -> + check_voice_request_fields(X,Y) + end, + true, Fields) + end + catch + error: _ -> + false + end; is_voice_request(Els) -> - try - case xml:remove_cdata(Els) of - [{xmlelement, "x", _, Els1} = XEl] -> - case xml:get_tag_attr_s("xmlns", XEl) of - ?NS_XDATA -> - lists:foldl( - fun(X,Y) -> - check_voice_request_fields(X,Y) - end, - true, xml:remove_cdata(Els1)) - end - end - catch - error: _ -> - false - end. - -check_voice_request_fields({xmlelement, "field", _, Els} = Elem, Acc) -> - try - case Acc of - true -> - case xml:get_tag_attr_s("var", Elem) of - "FORM_TYPE" -> - [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), - case xml:get_cdata(Value) of - "http://jabber.org/protocol/muc#request" -> - true + lists:foldl( + fun(X, Acc) -> + case Acc of + false -> + case X of + {xmlelement, "x", _, _} -> + is_voice_request(X); + _ -> + false end; - "muc#role" -> - [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), - case xml:get_cdata(Value) of - "participant" -> - true - end + true -> + true end + end, false, Els). + +check_voice_request_fields({Field, Value}, Acc) -> + if Acc -> + case Field of + "FORM_TYPE" -> + case Value of + "http://jabber.org/protocol/muc#request" -> + true + end; + "muc#role" -> + case Value of + "participant" -> + true + end; + _ -> + true % silently ignore any extra fields end - catch - error: _ -> - false end. prepare_request_form(Requester, Nick, Lang) -> @@ -3792,39 +3799,47 @@ send_voice_request(From, StateData) -> prepare_request_form(From, FromNick, "")) end, Moderators). -is_voice_approvement(Els) -> +is_voice_approvement({xmlelement, "x", _, _} = Elem) -> try - case xml:remove_cdata(Els) of - [{xmlelement, "x", Attrs, Els1}] -> - case xml:get_attr_s("xmlns", Attrs) of - ?NS_XDATA -> - case xml:get_attr_s("type", Attrs) of - "submit" -> - lists:foldl( - fun(X,Y) -> - check_voice_approvement_fields(X,Y) - end, - true, xml:remove_cdata(Els1)) - end - end + case xml:get_tag_attr_s("xmlns", Elem) of + ?NS_XDATA -> + Fields = jlib:parse_xdata_submit(Elem), + lists:foldl( + fun(X,Y) -> + check_voice_approvement_fields(X,Y) + end, + true, Fields) end catch error: _ -> false - end. + end; +is_voice_approvement(Els) -> + lists:foldl( + fun(X, Acc) -> + case Acc of + false -> + case X of + {xmlelement, "x", _, _} -> + is_voice_approvement(X); + _ -> + false + end; + true -> + true + end + end, false, Els). -check_voice_approvement_fields({xmlelement, "field", Attrs, Els}, Acc) -> +check_voice_approvement_fields({Field, Value}, Acc) -> if Acc -> - case xml:get_attr_s("var", Attrs) of + case Field of "FORM_TYPE" -> - [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), - case xml:get_cdata(Value) of + case Value of "http://jabber.org/protocol/muc#request" -> true end; "muc#role" -> - [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), - case xml:get_cdata(Value) of + case Value of "participant" -> true end; @@ -3834,8 +3849,7 @@ check_voice_approvement_fields({xmlelement, "field", Attrs, Els}, Acc) -> true; "muc#request_allow" -> % XXX: submitted forms with request_allow unchecked ignored here - [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els), - case xml:get_cdata(Value) of + case Value of "true" -> true; "1" -> @@ -3844,34 +3858,38 @@ check_voice_approvement_fields({xmlelement, "field", Attrs, Els}, Acc) -> _ -> true % ignore unknown fields end - end; -check_voice_approvement_fields({xmlelement, _, _, _}, _) -> - true. + end. extract_jid_from_voice_approvement(Els) -> - try - [{xmlelement, "x", _, Els1}] = xml:remove_cdata(Els), - Jid = lists:foldl( - fun(El, Acc) -> - case xml:get_tag_attr_s("var", El) of - "muc#jid" -> - {xmlelement, "field", _, Els2} = El, - [{xmlelement, "value", _, Value}] = xml:remove_cdata(Els2), - xml:get_cdata(Value); + lists:foldl( + fun(X, Acc) -> + case Acc of + {error, _} -> + case X of + {xmlelement, "x", _, _} -> + Fields = jlib:parse_xdata_submit(X), + Jid = lists:foldl( + fun(T, Acc2) -> + case Acc2 of + {error, _} -> + case T of + {"muc#jid", Jid} -> + Jid; + _ -> + Acc2 + end; + _ -> + Acc2 + end + end, {error, jid_not_found}, Fields), + jlib:string_to_jid(Jid); _ -> Acc - end - end, {error, jid_not_found}, xml:remove_cdata(Els1)), - case Jid of - {error, _} = Err -> - Err; - _ -> - jlib:string_to_jid(Jid) - end - catch - error: X -> - {error, X} - end. + end; + _ -> + Acc + end + end, {error, jid_not_found}, Els). last_voice_request_time(BareJID, StateData) -> case ?DICT:find(BareJID, StateData#state.last_voice_request_time) of From c53ffe6dc5e3e3156a242eb18b10d862e7e39904 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 16:25:44 +0300 Subject: [PATCH 61/77] Fix return value inconsistency in last_voice_request_time/2 --- src/mod_muc/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 31be2b819..618b1c685 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3896,7 +3896,7 @@ last_voice_request_time(BareJID, StateData) -> {ok, Value} -> Value; error -> - 0 + {0, 0, 0} end. update_voice_request_time(BareJID, StateData) -> From eb8347f92b5f6772c36855c1a7a345f3109bb583 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 16:28:16 +0300 Subject: [PATCH 62/77] Reverse arguments order for timer:now_diff/2 --- src/mod_muc/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 618b1c685..ae6bbac0a 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -304,7 +304,7 @@ normal_state({route, From, "", MinInterval = (StateData#state.config)#config.voice_request_min_interval, BareFrom = jlib:jid_remove_resource(jlib:jid_to_lower(From)), LastTime = last_voice_request_time(BareFrom, StateData), - TimeFromLastRequest = timer:now_diff(LastTime, erlang:now()), + TimeFromLastRequest = timer:now_diff(erlang:now(), LastTime), if TimeFromLastRequest > MinInterval*1000000 -> send_voice_request(From, StateData), From faed7698c2170ec792201f286aef2922870d7405 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 16:52:53 +0300 Subject: [PATCH 63/77] Use pattern matching in check_voice_request_fields/2 and check_voice_approvement_fields/2 --- src/mod_muc/mod_muc_room.erl | 76 ++++++++++++++---------------------- 1 file changed, 30 insertions(+), 46 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index ae6bbac0a..f7375f5a4 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3752,23 +3752,18 @@ is_voice_request(Els) -> end end, false, Els). -check_voice_request_fields({Field, Value}, Acc) -> - if Acc -> - case Field of - "FORM_TYPE" -> - case Value of - "http://jabber.org/protocol/muc#request" -> - true - end; - "muc#role" -> - case Value of - "participant" -> - true - end; - _ -> - true % silently ignore any extra fields - end - end. +check_voice_request_fields(_, false) -> + false; +check_voice_request_fields({"FORM_TYPE", "http://jabber.org/protocol/muc#request"}, true) -> + true; +check_voice_request_fields({"FORM_TYPE", _}, _) -> + false; +check_voice_request_fields({"muc#role", "participant"}, true) -> + true; +check_voice_request_fields({"muc#role", _}, _) -> + false; +check_voice_request_fields(_, true) -> + true. % silently ignore any extra fields prepare_request_form(Requester, Nick, Lang) -> {xmlelement, "message", [{"type", "normal"}], [ @@ -3830,35 +3825,24 @@ is_voice_approvement(Els) -> end end, false, Els). -check_voice_approvement_fields({Field, Value}, Acc) -> - if Acc -> - case Field of - "FORM_TYPE" -> - case Value of - "http://jabber.org/protocol/muc#request" -> - true - end; - "muc#role" -> - case Value of - "participant" -> - true - end; - "muc#jid" -> - true; % TODO: make some validation here - "muc#roomnick" -> - true; - "muc#request_allow" -> - % XXX: submitted forms with request_allow unchecked ignored here - case Value of - "true" -> - true; - "1" -> - true - end; - _ -> - true % ignore unknown fields - end - end. +check_voice_approvement_fields(_, false) -> + false; +check_voice_approvement_fields({"FORM_TYPE", "http://jabber.org/protocol/muc#request"}, true) -> + true; +check_voice_approvement_fields({"FORM_TYPE", _}, _) -> + false; +check_voice_approvement_fields({"muc#role", "participant"}, true) -> + true; +check_voice_approvement_fields({"muc#role", _}, _) -> + false; +check_voice_approvement_fields({"muc#request_allow", "true"}, true) -> + true; +check_voice_approvement_fields({"muc#request_allow", "1"}, true) -> + true; +check_voice_approvement_fields({"muc#request_allow", _}, _) -> + false; +check_voice_approvement_fields(_, true) -> + true; % do not check any other fields extract_jid_from_voice_approvement(Els) -> lists:foldl( From a91ae03dc639d6c9f20971da8129b14a6c8e1333 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 20:06:29 +0300 Subject: [PATCH 64/77] Remove try/catch --- src/mod_muc/mod_muc_room.erl | 42 ++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index f7375f5a4..bd26b74d0 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3722,18 +3722,15 @@ get_mucroom_disco_items(StateData) -> % Voice request support is_voice_request({xmlelement, "x", _, _} = Elem) -> - try - case xml:get_tag_attr_s("xmlns", Elem) of - ?NS_XDATA -> - Fields = jlib:parse_xdata_submit(Elem), - lists:foldl( - fun(X,Y) -> - check_voice_request_fields(X,Y) - end, - true, Fields) - end - catch - error: _ -> + case xml:get_tag_attr_s("xmlns", Elem) of + ?NS_XDATA -> + Fields = jlib:parse_xdata_submit(Elem), + lists:foldl( + fun(X,Y) -> + check_voice_request_fields(X,Y) + end, + true, Fields); + _ -> false end; is_voice_request(Els) -> @@ -3795,18 +3792,15 @@ send_voice_request(From, StateData) -> end, Moderators). is_voice_approvement({xmlelement, "x", _, _} = Elem) -> - try - case xml:get_tag_attr_s("xmlns", Elem) of - ?NS_XDATA -> - Fields = jlib:parse_xdata_submit(Elem), - lists:foldl( - fun(X,Y) -> - check_voice_approvement_fields(X,Y) - end, - true, Fields) - end - catch - error: _ -> + case xml:get_tag_attr_s("xmlns", Elem) of + ?NS_XDATA -> + Fields = jlib:parse_xdata_submit(Elem), + lists:foldl( + fun(X,Y) -> + check_voice_approvement_fields(X,Y) + end, + true, Fields); + _ -> false end; is_voice_approvement(Els) -> From f919349173d8dae2d895b3419057ea453663be5c Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 20:27:08 +0300 Subject: [PATCH 65/77] Use more pattern-matching --- src/mod_muc/mod_muc_room.erl | 38 ++++++++++++------------------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index bd26b74d0..c191c4122 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3735,18 +3735,12 @@ is_voice_request({xmlelement, "x", _, _} = Elem) -> end; is_voice_request(Els) -> lists:foldl( - fun(X, Acc) -> - case Acc of - false -> - case X of - {xmlelement, "x", _, _} -> - is_voice_request(X); - _ -> - false - end; - true -> - true - end + fun(_, true) -> + true; + ({xmlelement, "x", _, _} = X, false) -> + is_voice_request(X); + (_, _) -> + false end, false, Els). check_voice_request_fields(_, false) -> @@ -3805,18 +3799,12 @@ is_voice_approvement({xmlelement, "x", _, _} = Elem) -> end; is_voice_approvement(Els) -> lists:foldl( - fun(X, Acc) -> - case Acc of - false -> - case X of - {xmlelement, "x", _, _} -> - is_voice_approvement(X); - _ -> - false - end; - true -> - true - end + fun(_, true) -> + true; + ({xmlelement, "x", _, _} = X, false) -> + is_voice_approvement(X); + (_, _) -> + false end, false, Els). check_voice_approvement_fields(_, false) -> @@ -3836,7 +3824,7 @@ check_voice_approvement_fields({"muc#request_allow", "1"}, true) -> check_voice_approvement_fields({"muc#request_allow", _}, _) -> false; check_voice_approvement_fields(_, true) -> - true; % do not check any other fields + true. % do not check any other fields extract_jid_from_voice_approvement(Els) -> lists:foldl( From 845f5cabece7e505db185bcb51a60b6e9fe1ec8e Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 20:57:50 +0300 Subject: [PATCH 66/77] Fix patterns in check_voice_request_fields/2 and check_voice_approvement_fields/2 --- src/mod_muc/mod_muc_room.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index c191c4122..5986e2fb4 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3745,11 +3745,11 @@ is_voice_request(Els) -> check_voice_request_fields(_, false) -> false; -check_voice_request_fields({"FORM_TYPE", "http://jabber.org/protocol/muc#request"}, true) -> +check_voice_request_fields({"FORM_TYPE", ["http://jabber.org/protocol/muc#request"]}, true) -> true; check_voice_request_fields({"FORM_TYPE", _}, _) -> false; -check_voice_request_fields({"muc#role", "participant"}, true) -> +check_voice_request_fields({"muc#role", ["participant"]}, true) -> true; check_voice_request_fields({"muc#role", _}, _) -> false; @@ -3809,17 +3809,17 @@ is_voice_approvement(Els) -> check_voice_approvement_fields(_, false) -> false; -check_voice_approvement_fields({"FORM_TYPE", "http://jabber.org/protocol/muc#request"}, true) -> +check_voice_approvement_fields({"FORM_TYPE", ["http://jabber.org/protocol/muc#request"]}, true) -> true; check_voice_approvement_fields({"FORM_TYPE", _}, _) -> false; -check_voice_approvement_fields({"muc#role", "participant"}, true) -> +check_voice_approvement_fields({"muc#role", ["participant"]}, true) -> true; check_voice_approvement_fields({"muc#role", _}, _) -> false; -check_voice_approvement_fields({"muc#request_allow", "true"}, true) -> +check_voice_approvement_fields({"muc#request_allow", ["true"]}, true) -> true; -check_voice_approvement_fields({"muc#request_allow", "1"}, true) -> +check_voice_approvement_fields({"muc#request_allow", ["1"]}, true) -> true; check_voice_approvement_fields({"muc#request_allow", _}, _) -> false; From 322263fd5c377fcae5ef73754aa31b54d3949e71 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Tue, 20 Sep 2011 21:12:07 +0300 Subject: [PATCH 67/77] Fix typo --- src/mod_muc/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 5986e2fb4..34e0e1f40 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -302,7 +302,7 @@ normal_state({route, From, "", (StateData#state.config)#config.allow_voice_requests} of {true, true} -> MinInterval = (StateData#state.config)#config.voice_request_min_interval, - BareFrom = jlib:jid_remove_resource(jlib:jid_to_lower(From)), + BareFrom = jlib:jid_remove_resource(jlib:jid_tolower(From)), LastTime = last_voice_request_time(BareFrom, StateData), TimeFromLastRequest = timer:now_diff(erlang:now(), LastTime), if From 0c261fb9038c4ba0abcaac0f73bf804205f81ba0 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Wed, 21 Sep 2011 07:38:32 +0300 Subject: [PATCH 68/77] Fix voice request/approvement detection and extract_jid_from_voice_approvement/1 --- src/mod_muc/mod_muc_room.erl | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 34e0e1f40..1d3601468 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -257,8 +257,8 @@ normal_state({route, From, "", {next_state, normal_state, StateData}; Type when (Type == "") or (Type == "normal") -> IsInvitation = is_invitation(Els), - IsVoiceRequest = is_voice_request(Els), - IsVoiceApprovement = is_voice_approvement(Els), + IsVoiceRequest = is_voice_request(Els) and is_visitor(From, StateData), + IsVoiceApprovement = is_voice_approvement(Els) and not is_visitor(From, StateData), if IsInvitation -> case catch check_invitation(From, Els, Lang, StateData) of @@ -298,9 +298,8 @@ normal_state({route, From, "", end end; IsVoiceRequest -> - NewStateData = case {is_visitor(From, StateData), - (StateData#state.config)#config.allow_voice_requests} of - {true, true} -> + NewStateData = case (StateData#state.config)#config.allow_voice_requests of + true -> MinInterval = (StateData#state.config)#config.voice_request_min_interval, BareFrom = jlib:jid_remove_resource(jlib:jid_tolower(From)), LastTime = last_voice_request_time(BareFrom, StateData), @@ -317,19 +316,12 @@ normal_state({route, From, "", StateData#state.jid, From, Err), StateData end; - {_, false} -> + false -> ErrText = "Voice requests are disabled in this room", Err = jlib:make_error_reply( Packet, ?ERRT_FORBIDDEN(Lang, ErrText)), ejabberd_router:route( StateData#state.jid, From, Err), - StateData; - _ -> - ErrText = "Only visitors allowed to request voice", - Err = jlib:make_error_reply( - Packet, ?ERRT_NOT_ALLOWED(Lang, ErrText)), - ejabberd_router:route( - StateData#state.jid, From, Err), StateData end, {next_state, normal_state, NewStateData}; @@ -3839,7 +3831,7 @@ extract_jid_from_voice_approvement(Els) -> case Acc2 of {error, _} -> case T of - {"muc#jid", Jid} -> + {"muc#jid", [Jid]} -> Jid; _ -> Acc2 From 499b884c67feb937a90fcb625c4273d4ea8ffb21 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Wed, 21 Sep 2011 10:25:32 +0300 Subject: [PATCH 69/77] Use treap for keeping last voice request timestamps --- src/mod_muc/mod_muc_room.erl | 8 ++++---- src/mod_muc/mod_muc_room.hrl | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 1d3601468..75cff1637 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -1548,7 +1548,7 @@ remove_online_user(JID, StateData, Reason) -> error -> StateData#state.nicks end, - LastTimes = ?DICT:erase(jlib:jid_remove_resource(LJID), + LastTimes = treap:delete(jlib:jid_remove_resource(LJID), StateData#state.last_voice_request_time), StateData#state{users = Users, nicks = Nicks, last_voice_request_time = LastTimes}. @@ -3850,15 +3850,15 @@ extract_jid_from_voice_approvement(Els) -> end, {error, jid_not_found}, Els). last_voice_request_time(BareJID, StateData) -> - case ?DICT:find(BareJID, StateData#state.last_voice_request_time) of - {ok, Value} -> + case treap:lookup(BareJID, StateData#state.last_voice_request_time) of + {ok, _, Value} -> Value; error -> {0, 0, 0} end. update_voice_request_time(BareJID, StateData) -> - NewDict = ?DICT:store(BareJID, erlang:now(), StateData#state.last_voice_request_time), + NewDict = treap:insert(BareJID, {0, 0}, erlang:now(), StateData#state.last_voice_request_time), StateData#state{last_voice_request_time = NewDict}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/mod_muc/mod_muc_room.hrl b/src/mod_muc/mod_muc_room.hrl index edfa3f410..b0cbc43c2 100644 --- a/src/mod_muc/mod_muc_room.hrl +++ b/src/mod_muc/mod_muc_room.hrl @@ -71,7 +71,7 @@ jid, config = #config{}, users = ?DICT:new(), - last_voice_request_time = ?DICT:new(), + last_voice_request_time = treap:empty(), robots = ?DICT:new(), nicks = ?DICT:new(), affiliations = ?DICT:new(), From ff57c8a58cc115e6799cc24973cdd2282cfb81fb Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Mon, 26 Sep 2011 09:55:07 +0300 Subject: [PATCH 70/77] Apply patch from Evgeniy Khramtsov --- src/mod_muc/mod_muc_room.erl | 411 +++++++++++++++++------------------ 1 file changed, 205 insertions(+), 206 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 75cff1637..5c75ba571 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -256,109 +256,140 @@ normal_state({route, From, "", From, Err), {next_state, normal_state, StateData}; Type when (Type == "") or (Type == "normal") -> - IsInvitation = is_invitation(Els), - IsVoiceRequest = is_voice_request(Els) and is_visitor(From, StateData), - IsVoiceApprovement = is_voice_approvement(Els) and not is_visitor(From, StateData), - if - IsInvitation -> - case catch check_invitation(From, Els, Lang, StateData) of + IsInvitation = is_invitation(Els), + IsVoiceRequest = is_voice_request(Els) + and is_visitor(From, StateData), + IsVoiceApprovement = is_voice_approvement(Els) + and not is_visitor(From, StateData), + if IsInvitation -> + case catch check_invitation(From, Els, Lang, StateData) of {error, Error} -> - Err = jlib:make_error_reply( - Packet, Error), - ejabberd_router:route( - StateData#state.jid, - From, Err), - {next_state, normal_state, StateData}; + Err = jlib:make_error_reply( + Packet, Error), + ejabberd_router:route( + StateData#state.jid, + From, Err), + {next_state, normal_state, StateData}; IJID -> - Config = StateData#state.config, - case Config#config.members_only of + Config = StateData#state.config, + case Config#config.members_only of true -> - case get_affiliation(IJID, StateData) of + case get_affiliation(IJID, StateData) of none -> - NSD = set_affiliation( - IJID, - member, - StateData), - case (NSD#state.config)#config.persistent of + NSD = set_affiliation( + IJID, + member, + StateData), + case (NSD#state.config)#config.persistent of true -> - mod_muc:store_room( - NSD#state.host, - NSD#state.room, - make_opts(NSD)); + mod_muc:store_room( + NSD#state.host, + NSD#state.room, + make_opts(NSD)); _ -> - ok - end, - {next_state, normal_state, NSD}; + ok + end, + {next_state, normal_state, NSD}; _ -> - {next_state, normal_state, - StateData} - end; + {next_state, normal_state, + StateData} + end; false -> - {next_state, normal_state, StateData} - end - end; + {next_state, normal_state, StateData} + end + end; IsVoiceRequest -> - NewStateData = case (StateData#state.config)#config.allow_voice_requests of - true -> - MinInterval = (StateData#state.config)#config.voice_request_min_interval, - BareFrom = jlib:jid_remove_resource(jlib:jid_tolower(From)), - LastTime = last_voice_request_time(BareFrom, StateData), - TimeFromLastRequest = timer:now_diff(erlang:now(), LastTime), - if - TimeFromLastRequest > MinInterval*1000000 -> - send_voice_request(From, StateData), - update_voice_request_time(BareFrom, StateData); - true -> - ErrText = "Please, wait for a while before sending new voice request", - Err = jlib:make_error_reply( - Packet, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)), + NewStateData = + case (StateData#state.config)#config.allow_voice_requests of + true -> + MinInterval = (StateData#state.config) + #config.voice_request_min_interval, + BareFrom = jlib:jid_remove_resource( + jlib:jid_tolower(From)), + LastTime = last_voice_request_time( + BareFrom, StateData), + TimeFromLastRequest = + timer:now_diff( + now(), LastTime) div 1000000, + if TimeFromLastRequest > MinInterval -> + send_voice_request( + From, StateData), + update_voice_request_time( + BareFrom, StateData); + true -> + ErrText = "Please, wait for " + "a while before sending " + "new voice request", + Err = jlib:make_error_reply( + Packet, + ?ERRT_NOT_ACCEPTABLE( + Lang, ErrText)), ejabberd_router:route( - StateData#state.jid, From, Err), + StateData#state.jid, + From, Err), StateData end; - false -> - ErrText = "Voice requests are disabled in this room", + false -> + ErrText = "Voice requests are " + "disabled in this room", Err = jlib:make_error_reply( - Packet, ?ERRT_FORBIDDEN(Lang, ErrText)), + Packet, + ?ERRT_FORBIDDEN( + Lang, ErrText)), ejabberd_router:route( - StateData#state.jid, From, Err), + StateData#state.jid, From, Err), StateData end, - {next_state, normal_state, NewStateData}; - IsVoiceApprovement -> - NewStateData = case is_moderator(From, StateData) of - true -> + {next_state, normal_state, NewStateData}; + IsVoiceApprovement -> + NewStateData = + case is_moderator(From, StateData) of + true -> case extract_jid_from_voice_approvement(Els) of - {error, _} -> - ErrText = "Failed to extract JID from your voice request approvement", + error -> + ErrText = "Failed to extract " + "JID from your voice " + "request approvement", Err = jlib:make_error_reply( - Packet, ?ERRT_BAD_REQUEST(Lang, ErrText)), + Packet, + ?ERRT_BAD_REQUEST( + Lang, ErrText)), ejabberd_router:route( - StateData#state.jid, From, Err), + StateData#state.jid, + From, Err), StateData; - TargetJid -> - case is_visitor(TargetJid, StateData) of - true -> + {ok, TargetJid} -> + case is_visitor( + TargetJid, StateData) of + true -> Reason = [], - NSD = set_role(TargetJid, participant, StateData), - catch send_new_presence(TargetJid, Reason, NSD), + NSD = set_role( + TargetJid, + participant, + StateData), + catch send_new_presence( + TargetJid, + Reason, NSD), NSD; - _ -> + _ -> StateData end end; - _ -> - ErrText = "Only moderators can approve voice requests", + _ -> + ErrText = "Only moderators can " + "approve voice requests", Err = jlib:make_error_reply( - Packet, ?ERRT_NOT_ALLOWED(Lang, ErrText)), + Packet, + ?ERRT_NOT_ALLOWED( + Lang, ErrText)), ejabberd_router:route( - StateData#state.jid, From, Err), + StateData#state.jid, From, Err), StateData end, - {next_state, normal_state, NewStateData}; - true -> - {next_state, normal_state, StateData} - end; + {next_state, normal_state, NewStateData}; + true -> + {next_state, normal_state, StateData} + end; _ -> ErrText = "Improper message type", Err = jlib:make_error_reply( @@ -3713,153 +3744,121 @@ get_mucroom_disco_items(StateData) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Voice request support -is_voice_request({xmlelement, "x", _, _} = Elem) -> - case xml:get_tag_attr_s("xmlns", Elem) of - ?NS_XDATA -> - Fields = jlib:parse_xdata_submit(Elem), - lists:foldl( - fun(X,Y) -> - check_voice_request_fields(X,Y) - end, - true, Fields); - _ -> - false - end; is_voice_request(Els) -> - lists:foldl( - fun(_, true) -> - true; - ({xmlelement, "x", _, _} = X, false) -> - is_voice_request(X); - (_, _) -> - false - end, false, Els). - -check_voice_request_fields(_, false) -> - false; -check_voice_request_fields({"FORM_TYPE", ["http://jabber.org/protocol/muc#request"]}, true) -> - true; -check_voice_request_fields({"FORM_TYPE", _}, _) -> - false; -check_voice_request_fields({"muc#role", ["participant"]}, true) -> - true; -check_voice_request_fields({"muc#role", _}, _) -> - false; -check_voice_request_fields(_, true) -> - true. % silently ignore any extra fields + lists:foldl( + fun({xmlelement, "x", Attrs, _} = El, false) -> + case xml:get_attr_s("xmlns", Attrs) of + ?NS_XDATA -> + case jlib:parse_xdata_submit(El) of + [_|_] = Fields -> + case {lists:keysearch("FORM_TYPE", 1, Fields), + lists:keysearch("muc#role", 1, Fields)} of + {["http://jabber.org/protocol/muc#request"], + ["participant"]} -> + true; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end; + (_, Acc) -> + Acc + end, false, Els). prepare_request_form(Requester, Nick, Lang) -> - {xmlelement, "message", [{"type", "normal"}], [ - {xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}], - [ - {xmlelement, "title", [], - [{xmlcdata, translate:translate(Lang, "Voice request")}]}, - {xmlelement, "instructions", [], - [{xmlcdata, translate:translate(Lang, "To approve this request for voice, select the \"Grant voice to this person?\" checkbox and click OK. To skip this request, click the cancel button.")}]}, - {xmlelement, "field", [{"var", "FORM_TYPE"}, {"type", "hidden"}], - [{xmlelement, "value", [], - [{xmlcdata, "http://jabber.org/protocol/muc#request"}]}]}, - ?STRINGXFIELD("Requested role", "muc#role", "participant"), - ?STRINGXFIELD("User JID", "muc#jid", jlib:jid_to_string(Requester)), - ?STRINGXFIELD("Nickname", "muc#roomnick", Nick), - ?BOOLXFIELD("Grant voice to this person?", "muc#request_allow", false) - ] - }]}. + {xmlelement, "message", [{"type", "normal"}], + [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}], + [{xmlelement, "title", [], + [{xmlcdata, translate:translate(Lang, "Voice request")}]}, + {xmlelement, "instructions", [], + [{xmlcdata, + translate:translate( + Lang, "To approve this request for voice, select the " + "\"Grant voice to this person?\" checkbox and click OK. " + "To skip this request, click the cancel button.")}]}, + {xmlelement, "field", [{"var", "FORM_TYPE"}, {"type", "hidden"}], + [{xmlelement, "value", [], + [{xmlcdata, "http://jabber.org/protocol/muc#request"}]}]}, + ?STRINGXFIELD("Requested role", "muc#role", "participant"), + ?STRINGXFIELD("User JID", "muc#jid", jlib:jid_to_string(Requester)), + ?STRINGXFIELD("Nickname", "muc#roomnick", Nick), + ?BOOLXFIELD("Grant voice to this person?", "muc#request_allow", false) + ]}]}. send_voice_request(From, StateData) -> - Moderators = search_role(moderator, StateData), - FromNick = find_nick_by_jid(From, StateData), - lists:foreach( - fun({_, User}) -> - ejabberd_router:route( - StateData#state.jid, - User#user.jid, - prepare_request_form(From, FromNick, "")) - end, Moderators). + Moderators = search_role(moderator, StateData), + FromNick = find_nick_by_jid(From, StateData), + lists:foreach( + fun({_, User}) -> + ejabberd_router:route( + StateData#state.jid, + User#user.jid, + prepare_request_form(From, FromNick, "")) + end, Moderators). -is_voice_approvement({xmlelement, "x", _, _} = Elem) -> - case xml:get_tag_attr_s("xmlns", Elem) of - ?NS_XDATA -> - Fields = jlib:parse_xdata_submit(Elem), - lists:foldl( - fun(X,Y) -> - check_voice_approvement_fields(X,Y) - end, - true, Fields); - _ -> - false - end; is_voice_approvement(Els) -> - lists:foldl( - fun(_, true) -> - true; - ({xmlelement, "x", _, _} = X, false) -> - is_voice_approvement(X); - (_, _) -> - false - end, false, Els). - -check_voice_approvement_fields(_, false) -> - false; -check_voice_approvement_fields({"FORM_TYPE", ["http://jabber.org/protocol/muc#request"]}, true) -> - true; -check_voice_approvement_fields({"FORM_TYPE", _}, _) -> - false; -check_voice_approvement_fields({"muc#role", ["participant"]}, true) -> - true; -check_voice_approvement_fields({"muc#role", _}, _) -> - false; -check_voice_approvement_fields({"muc#request_allow", ["true"]}, true) -> - true; -check_voice_approvement_fields({"muc#request_allow", ["1"]}, true) -> - true; -check_voice_approvement_fields({"muc#request_allow", _}, _) -> - false; -check_voice_approvement_fields(_, true) -> - true. % do not check any other fields + lists:foldl( + fun({xmlelement, "x", Attrs, _} = El, false) -> + case xml:get_attr_s("xmlns", Attrs) of + ?NS_XDATA -> + case jlib:parse_xdata_submit(El) of + [_|_] = Fs -> + case {lists:keysearch("FORM_TYPE", 1, Fs), + lists:keysearch("muc#role", 1, Fs), + lists:keysearch("muc#request_allow", 1, Fs)} of + {["http://jabber.org/protocol/muc#request"], + ["participant"], [Flag]} + when Flag == "true"; Flag == "1" -> + true; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end; + (_, Acc) -> + Acc + end, false, Els). extract_jid_from_voice_approvement(Els) -> - lists:foldl( - fun(X, Acc) -> - case Acc of - {error, _} -> - case X of - {xmlelement, "x", _, _} -> - Fields = jlib:parse_xdata_submit(X), - Jid = lists:foldl( - fun(T, Acc2) -> - case Acc2 of - {error, _} -> - case T of - {"muc#jid", [Jid]} -> - Jid; - _ -> - Acc2 - end; - _ -> - Acc2 - end - end, {error, jid_not_found}, Fields), - jlib:string_to_jid(Jid); - _ -> - Acc - end; - _ -> - Acc - end - end, {error, jid_not_found}, Els). + lists:foldl( + fun({xmlelement, "x", _, _} = El, error) -> + Fields = case jlib:parse_xdata_submit(El) of + invalid -> []; + Res -> Res + end, + lists:foldl( + fun({"muc#jid", [JIDStr]}, error) -> + case jlib:string_to_jid(JIDStr) of + error -> error; + J -> {ok, J} + end; + (_, Acc) -> + Acc + end, error, Fields); + (_, Acc) -> + Acc + end, error, Els). last_voice_request_time(BareJID, StateData) -> - case treap:lookup(BareJID, StateData#state.last_voice_request_time) of + case treap:lookup(BareJID, StateData#state.last_voice_request_time) of {ok, _, Value} -> - Value; + Value; error -> - {0, 0, 0} - end. + {0, 0, 0} + end. update_voice_request_time(BareJID, StateData) -> - NewDict = treap:insert(BareJID, {0, 0}, erlang:now(), StateData#state.last_voice_request_time), - StateData#state{last_voice_request_time = NewDict}. + NewDict = treap:insert(BareJID, {0, 0}, erlang:now(), + StateData#state.last_voice_request_time), + StateData#state{last_voice_request_time = NewDict}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support From f17a531e15ecc44c3656c90811b4bacc2c2b38c4 Mon Sep 17 00:00:00 2001 From: Maxim Ignatenko Date: Mon, 26 Sep 2011 11:13:40 +0300 Subject: [PATCH 71/77] Use treap correctly --- src/mod_muc/mod_muc_room.erl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index 5c75ba571..c4220ff53 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -306,16 +306,15 @@ normal_state({route, From, "", #config.voice_request_min_interval, BareFrom = jlib:jid_remove_resource( jlib:jid_tolower(From)), - LastTime = last_voice_request_time( + {LastTime, NewStateData1} = last_voice_request_time( BareFrom, StateData), TimeFromLastRequest = - timer:now_diff( - now(), LastTime) div 1000000, + (now_to_usec(now()) - LastTime) div 1000000, if TimeFromLastRequest > MinInterval -> send_voice_request( - From, StateData), + From, NewStateData1), update_voice_request_time( - BareFrom, StateData); + BareFrom, NewStateData1); true -> ErrText = "Please, wait for " "a while before sending " @@ -327,7 +326,7 @@ normal_state({route, From, "", ejabberd_router:route( StateData#state.jid, From, Err), - StateData + NewStateData1 end; false -> ErrText = "Voice requests are " @@ -1579,10 +1578,7 @@ remove_online_user(JID, StateData, Reason) -> error -> StateData#state.nicks end, - LastTimes = treap:delete(jlib:jid_remove_resource(LJID), - StateData#state.last_voice_request_time), - StateData#state{users = Users, nicks = Nicks, - last_voice_request_time = LastTimes}. + StateData#state{users = Users, nicks = Nicks}. filter_presence({xmlelement, "presence", Attrs, Els}) -> @@ -3848,17 +3844,21 @@ extract_jid_from_voice_approvement(Els) -> end, error, Els). last_voice_request_time(BareJID, StateData) -> + Timeout = (StateData#state.config)#config.voice_request_min_interval, + Times = clean_treap(StateData#state.last_voice_request_time, + -now_to_usec(now()) + Timeout*1000000), + NewStateData = StateData#state{last_voice_request_time = Times}, case treap:lookup(BareJID, StateData#state.last_voice_request_time) of - {ok, _, Value} -> - Value; + {ok, Value, _} -> + {-Value, NewStateData}; error -> - {0, 0, 0} + {0, NewStateData} end. update_voice_request_time(BareJID, StateData) -> - NewDict = treap:insert(BareJID, {0, 0}, erlang:now(), + Times = treap:insert(BareJID, -now_to_usec(now()), true, StateData#state.last_voice_request_time), - StateData#state{last_voice_request_time = NewDict}. + StateData#state{last_voice_request_time = Times}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support From e7d12f4b9e59e657a15ae58705fa9a79c67e3b36 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 26 Sep 2011 18:44:41 +1000 Subject: [PATCH 72/77] Code cleanup --- src/mod_muc/mod_muc_room.erl | 80 +++++++++++++++++------------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index c4220ff53..d4f64bcca 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -306,16 +306,24 @@ normal_state({route, From, "", #config.voice_request_min_interval, BareFrom = jlib:jid_remove_resource( jlib:jid_tolower(From)), - {LastTime, NewStateData1} = last_voice_request_time( - BareFrom, StateData), - TimeFromLastRequest = - (now_to_usec(now()) - LastTime) div 1000000, - if TimeFromLastRequest > MinInterval -> - send_voice_request( - From, NewStateData1), - update_voice_request_time( - BareFrom, NewStateData1); - true -> + NowPriority = -now_to_usec(now()), + CleanPriority = + NowPriority + MinInterval*1000000, + Times = clean_treap( + StateData#state.last_voice_request_time, + CleanPriority), + case treap:lookup(BareFrom, Times) of + error -> + Times1 = treap:insert( + BareFrom, + NowPriority, + true, Times), + NSD = StateData#state{ + last_voice_request_time = + Times1}, + send_voice_request(From, NSD), + NSD; + {ok, _, _} -> ErrText = "Please, wait for " "a while before sending " "new voice request", @@ -326,7 +334,9 @@ normal_state({route, From, "", ejabberd_router:route( StateData#state.jid, From, Err), - NewStateData1 + StateData#state{ + last_voice_request_time = + Times} end; false -> ErrText = "Voice requests are " @@ -3843,42 +3853,26 @@ extract_jid_from_voice_approvement(Els) -> Acc end, error, Els). -last_voice_request_time(BareJID, StateData) -> - Timeout = (StateData#state.config)#config.voice_request_min_interval, - Times = clean_treap(StateData#state.last_voice_request_time, - -now_to_usec(now()) + Timeout*1000000), - NewStateData = StateData#state{last_voice_request_time = Times}, - case treap:lookup(BareJID, StateData#state.last_voice_request_time) of - {ok, Value, _} -> - {-Value, NewStateData}; - error -> - {0, NewStateData} - end. - -update_voice_request_time(BareJID, StateData) -> - Times = treap:insert(BareJID, -now_to_usec(now()), true, - StateData#state.last_voice_request_time), - StateData#state{last_voice_request_time = Times}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invitation support is_invitation(Els) -> - try - case xml:remove_cdata(Els) of - [{xmlelement, "x", _Attrs1, Els1} = XEl] -> - case xml:get_tag_attr_s("xmlns", XEl) of - ?NS_MUC_USER -> - case xml:remove_cdata(Els1) of - [{xmlelement, "invite", _, _}] -> - true - end - end - end - catch - error: _ -> - false - end. + lists:foldl( + fun({xmlelement, "x", Attrs, _} = El, false) -> + case xml:get_attr_s("xmlns", Attrs) of + ?NS_MUC_USER -> + case xml:get_subtag(El, "invite") of + false -> + false; + _ -> + true + end; + _ -> + false + end; + (_, Acc) -> + Acc + end, false, Els). check_invitation(From, Els, Lang, StateData) -> FAffiliation = get_affiliation(From, StateData), From 8631dd14e97cdc7670a97dcfee24725d9473c81b Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 26 Sep 2011 19:06:34 +1000 Subject: [PATCH 73/77] Correct lists:keysearch output processing and fix dialyzer warning --- src/mod_muc/mod_muc_room.erl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index d4f64bcca..a901e61b8 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3759,8 +3759,9 @@ is_voice_request(Els) -> [_|_] = Fields -> case {lists:keysearch("FORM_TYPE", 1, Fields), lists:keysearch("muc#role", 1, Fields)} of - {["http://jabber.org/protocol/muc#request"], - ["participant"]} -> + {{value, + {_, ["http://jabber.org/protocol/muc#request"]}}, + {value, {_, ["participant"]}}} -> true; _ -> false @@ -3792,7 +3793,8 @@ prepare_request_form(Requester, Nick, Lang) -> ?STRINGXFIELD("Requested role", "muc#role", "participant"), ?STRINGXFIELD("User JID", "muc#jid", jlib:jid_to_string(Requester)), ?STRINGXFIELD("Nickname", "muc#roomnick", Nick), - ?BOOLXFIELD("Grant voice to this person?", "muc#request_allow", false) + ?BOOLXFIELD("Grant voice to this person?", "muc#request_allow", + list_to_atom("false")) ]}]}. send_voice_request(From, StateData) -> @@ -3816,8 +3818,10 @@ is_voice_approvement(Els) -> case {lists:keysearch("FORM_TYPE", 1, Fs), lists:keysearch("muc#role", 1, Fs), lists:keysearch("muc#request_allow", 1, Fs)} of - {["http://jabber.org/protocol/muc#request"], - ["participant"], [Flag]} + {{value, + {_, ["http://jabber.org/protocol/muc#request"]}}, + {value, {_, ["participant"]}}, + {value, {_, [Flag]}}} when Flag == "true"; Flag == "1" -> true; _ -> From de4d4a4bbbe44f0ef0b1b784fd3d0b83746f73ac Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 26 Sep 2011 19:54:55 +1000 Subject: [PATCH 74/77] Change "muc#role" field type to "hidden" --- src/mod_muc/mod_muc_room.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index a901e61b8..e4daddb44 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -3790,7 +3790,8 @@ prepare_request_form(Requester, Nick, Lang) -> {xmlelement, "field", [{"var", "FORM_TYPE"}, {"type", "hidden"}], [{xmlelement, "value", [], [{xmlcdata, "http://jabber.org/protocol/muc#request"}]}]}, - ?STRINGXFIELD("Requested role", "muc#role", "participant"), + {xmlelement, "field", [{"var", "muc#role"}, {"type", "hidden"}], + [{xmlelement, "value", [], [{xmlcdata, "participant"}]}]}, ?STRINGXFIELD("User JID", "muc#jid", jlib:jid_to_string(Requester)), ?STRINGXFIELD("Nickname", "muc#roomnick", Nick), ?BOOLXFIELD("Grant voice to this person?", "muc#request_allow", From 39bc499b10fa231c01e6edd2d7190327ea39ba13 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 26 Sep 2011 20:00:43 +1000 Subject: [PATCH 75/77] Fix instruction and error text --- src/mod_muc/mod_muc_room.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index e4daddb44..164805d9c 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -340,7 +340,7 @@ normal_state({route, From, "", end; false -> ErrText = "Voice requests are " - "disabled in this room", + "disabled in this conference", Err = jlib:make_error_reply( Packet, ?ERRT_FORBIDDEN( @@ -3784,9 +3784,7 @@ prepare_request_form(Requester, Nick, Lang) -> {xmlelement, "instructions", [], [{xmlcdata, translate:translate( - Lang, "To approve this request for voice, select the " - "\"Grant voice to this person?\" checkbox and click OK. " - "To skip this request, click the cancel button.")}]}, + Lang, "Either approve or decline the voice request.")}]}, {xmlelement, "field", [{"var", "FORM_TYPE"}, {"type", "hidden"}], [{xmlelement, "value", [], [{xmlcdata, "http://jabber.org/protocol/muc#request"}]}]}, From 3b0b318730bab5e4f5aadb00fa0adf2db1f79354 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Sep 2011 18:09:01 +0200 Subject: [PATCH 76/77] New release notes for 2.1.9 --- doc/release_notes_2.1.9.txt | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 doc/release_notes_2.1.9.txt diff --git a/doc/release_notes_2.1.9.txt b/doc/release_notes_2.1.9.txt new file mode 100644 index 000000000..77cc9ac1d --- /dev/null +++ b/doc/release_notes_2.1.9.txt @@ -0,0 +1,56 @@ + + Release Notes + ejabberd 2.1.9 + + ejabberd 2.1.9 is the eighth release in ejabberd 2.1.x branch, + and includes a lot of bugfixes and improvements. + + Read more details about the changes in: + http://redir.process-one.net/ejabberd-2.1.9 + + Download the source code and installers from: + http://www.process-one.net/en/ejabberd/ + + + The changes are: + +* Core ejabberd +- Decrease CPU usage caused by tls:send with large data +- Escape iolist correctly when NIFs are disabled (EJAB-1462) +- Fix code to satisfy Dialyzer warnings +- Fix compilation in Windows +- Replace calls of OTP's Binary, since they would require R14 + +* LDAP +- Document ldap_tls_cacertfile and ldap_tls_depth options (EJAB-1299) +- Log an error when an LDAP filter is incorrect (EJAB-1395) +- New options: ldap_tls_cacertfile and ldap_tls_depth (EJAB-1299) +- New option: ldap_deref_aliases (EJAB-639) +- Match ldap_uidattr_format case-insensitively (EJAB-1449) + +* MUC +- Support for multiple entry with same nick to MUC rooms (EJAB-305) +- Support voice request and approvement +- New room option: allow_private_messages_from_visitors +- New room options: allow_voice_requests and voice_request_min_interval +- Include status 110 in presence to new occupant (EJAB-740) +- Fix mod_muc_log crash when first log entry is room destroy (EJAB-1499) +- Many fixes and improvements in mod_muc + +* Pubsub +- Enable pubsub#deliver_notification checking (EJAB-1453) +- Fix Denial of Service when user sends malformed publish stanza (EJAB-1498) + +* ODBC +- Fix ODBC account counting (EJAB-1491) +- Optimized mod_roster_odbc:get_roster + +* Miscellanea: +- New SASL SCRAM-SHA-1 authentication mechanism (EJAB-1196) +- New option: resource_conflict (EJAB-650) + + + Bug reports + + You can officially report bugs on ProcessOne support site: + http://support.process-one.net/ From 4be7984a0e77eddffd0be7309d788c7d9f065013 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Sep 2011 18:11:18 +0200 Subject: [PATCH 77/77] Update ejabberd version number to 2.1.9 --- doc/dev.html | 4 +- doc/features.html | 4 +- doc/guide.html | 100 +++++++++++++++++++++++++++++++++------------- doc/version.tex | 2 +- src/ejabberd.app | 2 +- 5 files changed, 78 insertions(+), 34 deletions(-) diff --git a/doc/dev.html b/doc/dev.html index bad9c21d3..adc826cc7 100644 --- a/doc/dev.html +++ b/doc/dev.html @@ -2,7 +2,7 @@ "http://www.w3.org/TR/REC-html40/loose.dtd"> -Ejabberd 2.1.7 Developers Guide +<TITLE>Ejabberd 2.1.9 Developers Guide @@ -49,7 +49,7 @@ TD P{margin:0px;}

-

Ejabberd 2.1.7 Developers Guide

Alexey Shchepin
+

Ejabberd 2.1.9 Developers Guide

Alexey Shchepin
mailto:alexey@sevcom.net
xmpp:aleksey@jabber.ru

diff --git a/doc/features.html b/doc/features.html index f9b6bec58..520514375 100644 --- a/doc/features.html +++ b/doc/features.html @@ -2,7 +2,7 @@ "http://www.w3.org/TR/REC-html40/loose.dtd"> -Ejabberd 2.1.7 Feature Sheet +<TITLE>Ejabberd 2.1.9 Feature Sheet @@ -50,7 +50,7 @@ SPAN{width:20%; float:right; text-align:left; margin-left:auto;}

-

- + @@ -2077,7 +2112,7 @@ able to send such messages).

Examples:

  • Only administrators can send announcements: -
    {access, announce, [{allow, admins}]}.
    +
    {access, announce, [{allow, admin}]}.
     
     {modules,
      [
    @@ -2089,9 +2124,9 @@ Only administrators can send announcements:
     
  • Administrators as well as the direction can send announcements:
    {acl, direction, {user, "big_boss", "example.org"}}.
     {acl, direction, {user, "assistant", "example.org"}}.
    -{acl, admins, {user, "admin", "example.org"}}.
    +{acl, admin, {user, "admin", "example.org"}}.
     
    -{access, announce, [{allow, admins},
    +{access, announce, [{allow, admin},
                         {allow, direction}]}.
     
     {modules,
    @@ -2497,6 +2532,7 @@ The available room options and the default values are:
     
    {allow_change_subj, true|false}
    Allow occupants to change the subject.
    {allow_private_messages, true|false}
    Occupants can send private messages to other occupants. +
    {allow_private_messages_from_visitors, anyone|moderators|nobody}
    Visitors can send private messages to other occupants.
    {allow_query_users, true|false}
    Occupants can send IQ queries to other occupants.
    {allow_user_invites, false|true}
    Allow occupants to send invitations.
    {allow_visitor_nickchange, true|false}
    Allow visitors to @@ -2715,10 +2751,10 @@ used. The names of the log files will only contain the day (number), and there will be subdirectories for each year and month. The log files will be stored in /var/www/muclogs, and the local time will be used. Finally, the top link will be the default <a href="/">Home</a>. -
    {acl, admins, {user, "admin1", "example.org"}}.
    -{acl, admins, {user, "admin2", "example.net"}}.
    +
    {acl, admin, {user, "admin1", "example.org"}}.
    +{acl, admin, {user, "admin2", "example.net"}}.
     
    -{access, muc_log, [{allow, admins},
    +{access, muc_log, [{allow, admin},
                        {deny, all}]}.
     
     {modules,
    @@ -3115,7 +3151,9 @@ To enable this feature, configure the options captcha_cmd and captcha_host.

    < {registration_watchers, [ JID, ...]}
    This option defines a list of JIDs which will be notified each time a new account is registered.

    This example configuration shows how to enable the module and the web handler: -

    {listen, [
    +

    {hosts, ["localhost", "example.org", "example.com"]}.
    +
    +{listen, [
       ...
       {5281, ejabberd_http, [
         tls,
    @@ -3131,7 +3169,8 @@ list of JIDs which will be notified each time a new account is registered.
       {mod_register_web, []},
       ...
      ]}.
    -

    The users can visit this page: https://localhost:5281/register/ +

    For example, the users of the host example.org can visit the page: +https://example.org:5281/register/ It is important to include the last / character in the URL, otherwise the subpages URL will be incorrect.

    3.3.21  mod_roster

    @@ -3598,8 +3637,9 @@ and LDAP server supports its own optional parameters. The first group of parameters has the same meaning as the top-level LDAP parameters to set the authentication method: ldap_servers, ldap_port, ldap_rootdn, -ldap_password, ldap_base, ldap_uids, and -ldap_filter. See section 3.2.5 for detailed information +ldap_password, ldap_base, ldap_uids, +ldap_deref_aliases and ldap_filter. +See section 3.2.5 for detailed information about these options. If one of these options is not set, ejabberd will look for the top-level option with the same name.

    The second group of parameters consists of the following mod_vcard_ldap-specific options:

    @@ -3865,6 +3905,8 @@ all the environment variables and command line parameters.

    The environment This path is used to read the file .erlang.cookie.

    ERL_CRASH_DUMP
    Path to the file where crash reports will be dumped. +
    ERL_EPMD_ADDRESS
    + IP address where epmd listens for connections (see section 5.2).
    ERL_INETRC
    Indicates which IP name resolution to use. If using -sname, specify either this option or -kernel inetrc filepath. @@ -3891,10 +3933,10 @@ This is only useful if you plan to setup an ejabberd cluster with nodes connections (see section 5.2).
    -detached
    Starts the Erlang system detached from the system console. - Useful for running daemons and backgrounds processes. + Useful for running daemons and background processes.
    -noinput
    Ensures that the Erlang system never tries to read any input. - Useful for running daemons and backgrounds processes. + Useful for running daemons and background processes.
    -pa /var/lib/ejabberd/ebin
    Specify the directory where Erlang binary files (*.beam) are located.
    -s ejabberd
    @@ -4059,11 +4101,11 @@ URL). If you log in with ‘admin@example.com’ on
    http://example.org:5280/admin/server/example.com/ you can only administer the virtual host example.com. The account ‘reviewer@example.com’ can browse that vhost in read-only mode. -
    {acl, admins, {user, "admin", "example.net"}}.
    -{host_config, "example.com", [{acl, admins, {user, "admin", "example.com"}}]}.
    +
    {acl, admin, {user, "admin", "example.net"}}.
    +{host_config, "example.com", [{acl, admin, {user, "admin", "example.com"}}]}.
     {host_config, "example.com", [{acl, viewers, {user, "reviewer", "example.com"}}]}.
     
    -{access, configure, [{allow, admins}]}.
    +{access, configure, [{allow, admin}]}.
     {access, webadmin_view, [{allow, viewers}]}.
     
     {hosts, ["example.org"]}.
    @@ -4178,7 +4220,9 @@ and connects to the Erlang node that holds ejabberd.
     In order for this communication to work,
     epmd must be running and listening for name requests in the port 4369.
     You should block the port 4369 in the firewall in such a way that
    -only the programs in your machine can access it.

    If you build a cluster of several ejabberd instances, +only the programs in your machine can access it. +or configure the option ERL_EPMD_ADDRESS in the file ejabberdctl.cfg +(this option works only in Erlang/OTP R14B03 or higher).

    If you build a cluster of several ejabberd instances, each ejabberd instance is called an ejabberd node. Those ejabberd nodes use a special Erlang communication method to build the cluster, and EPMD is again needed listening in the port 4369. diff --git a/doc/version.tex b/doc/version.tex index 255412336..ebaed03ec 100644 --- a/doc/version.tex +++ b/doc/version.tex @@ -1,2 +1,2 @@ % ejabberd version (automatically generated). -\newcommand{\version}{2.1.7} +\newcommand{\version}{2.1.9} diff --git a/src/ejabberd.app b/src/ejabberd.app index 031fdaf46..ea755a7b9 100644 --- a/src/ejabberd.app +++ b/src/ejabberd.app @@ -2,7 +2,7 @@ {application, ejabberd, [{description, "ejabberd"}, - {vsn, "2.1.7"}, + {vsn, "2.1.9"}, {modules, [acl, adhoc, configure,

Ejabberd 2.1.7 Feature Sheet

Sander Devrieze
+

Ejabberd 2.1.9 Feature Sheet

Sander Devrieze
mailto:s.devrieze@pandora.be
xmpp:sander@devrieze.dyndns.org

diff --git a/doc/guide.html b/doc/guide.html index 24bb2d306..c2eb4bf6d 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -6,7 +6,7 @@ - ejabberd 2.1.7 + ejabberd 2.1.9 Installation and Operation Guide @@ -76,7 +76,7 @@ BLOCKQUOTE.figure DIV.center DIV.center HR{display:none;}


- +
ejabberd 2.1.7
ejabberd 2.1.9
 
Installation and Operation Guide

@@ -306,8 +306,9 @@ Note that the Windows service is a feature still in development, and for example it doesn’t read the file ejabberdctl.cfg.

On a *nix system, if you want ejabberd to be started as daemon at boot time, copy ejabberd.init from the ’bin’ directory to something like /etc/init.d/ejabberd (depending on your distribution). -Create a system user called ejabberd; -it will be used by the script to start the server. +Create a system user called ejabberd, +give it write access to the directories database/ and logs/, and set that as home; +the script will start the server with that user. Then you can call /etc/inid.d/ejabberd start as root to start the server.

If ejabberd doesn’t start correctly in Windows, try to start it using the shortcut in desktop or start menu. If the window shows error 14001, the solution is to install: @@ -525,8 +526,8 @@ Using ejabberdctl (see section 4.1):

  • Using a XMPP client and In-Band Registration (see section 3.3.19).
  • Edit the ejabberd configuration file to give administration rights to the XMPP account you created: -
    {acl, admins, {user, "admin1", "example.org"}}.
    -{access, configure, [{allow, admins}]}.
    +
    {acl, admin, {user, "admin1", "example.org"}}.
    +{access, configure, [{allow, admin}]}.
     
    You can grant administrative privileges to many XMPP accounts, and also to accounts in other XMPP servers.
  • Restart ejabberd to load the new configuration. @@ -1051,17 +1052,40 @@ internal (default) — See section 3.1.4. 3.2.2 and 3.2.4.
  • anonymous — See section 3.1.4.
  • pam — See section 3.1.4. -
  • Account creation is only supported by internal, external and odbc methods.

    +

    Account creation is only supported by internal, external and odbc methods.

    The option resource_conflict defines the action when a client attempts to +login to an account with a resource that is already connected. +The option syntax is: +

    {resource_conflict, setresource|closenew|closeold}.

    +The possible values match exactly the three possibilities described in +XMPP Core: section 7.7.2.2. +The default value is closeold. +If the client uses old Jabber Non-SASL authentication (XEP-0078), +then this option is not respected, and the action performed is closeold.

    Internal

    ejabberd uses its internal Mnesia database as the default authentication method. -The value internal will enable the internal authentication method.

    Examples: +The value internal will enable the internal authentication method.

    The option {auth_password_format, plain|scram} +defines in what format the users passwords are stored: +

    +plain
    +The password is stored as plain text in the database. +This is risky because the passwords can be read if your database gets compromised. +This is the default value. +This format allows clients to authenticate using: +the old Jabber Non-SASL (XEP-0078), SASL PLAIN, +SASL DIGEST-MD5, and SASL SCRAM-SHA-1.
    scram
    +The password is not stored, only some information that allows to verify the hash provided by the client. +It is impossible to obtain the original plain password from the stored information; +for this reason, when this value is configured it cannot be changed to plain anymore. +This format allows clients to authenticate using: SASL PLAIN and SASL SCRAM-SHA-1. +

    Examples:

    • To use internal authentication on example.org and LDAP authentication on example.net:
      {host_config, "example.org", [{auth_method, [internal]}]}.
       {host_config, "example.net", [{auth_method, [ldap]}]}.
      -
    • To use internal authentication on all virtual hosts: +
    • To use internal authentication with hashed passwords on all virtual hosts:
      {auth_method, internal}.
      +{auth_password_format, scram}.
       

    External Script

    In this authentication method, when ejabberd starts, @@ -1208,9 +1232,9 @@ declarations of ACLs in the configuration file have the following syntax:

    {user_regexp, Regexp}
    Matches any local user with a name that matches Regexp on local virtual hosts. Example:
    {acl, tests, {user_regexp, "^test[0-9]*$"}}.
    -
    {user_regexp, UserRegexp, Server}
    Matches any user with a name +
    {user_regexp, Regexp, Server}
    Matches any user with a name that matches Regexp at server Server. Example: -
    {acl, tests, {user_Userregexp, "^test", "example.org"}}.
    +
    {acl, tests, {user_regexp, "^test", "example.org"}}.
     
    {server_regexp, Regexp}
    Matches any JID from the server that matches Regexp. Example:
    {acl, icq, {server_regexp, "^icq\\."}}.
    @@ -1701,6 +1725,16 @@ This option specifies whether to verify LDAP server certificate or not when TLS
     When hard is enabled ejabberd doesn’t proceed if a certificate is invalid.
     When soft is enabled ejabberd proceeds even if check fails.
     The default is false which means no checks are performed.
    +
    {ldap_tls_cacertfile, Path}
    +Path to file containing PEM encoded CA certificates. This option is needed +(and required) when TLS verification is enabled. +
    {ldap_tls_depth, Number}
    +Specifies the maximum verification depth when TLS verification is enabled, +i.e. how far in a chain of certificates the verification process can proceed +before the verification is considered to fail. +Peer certificate = 0, CA certificate = 1, higher level CA certificate = 2, etc. +The value 2 thus means that a chain can at most contain peer cert, +CA cert, next CA cert, and an additional CA cert. The default value is 1.
    {ldap_port, Number}
    Port to connect to your LDAP server. The default port is 389 if encryption is disabled; and 636 if encryption is enabled. If you configure a value, it is stored in ejabberd’s database. @@ -1710,6 +1744,7 @@ the value previously stored in the database will be used instead of the default is "" which means ‘anonymous connection’.
    {ldap_password, Password}
    Bind password. The default value is "". +
    {ldap_deref_aliases, never|always|finding|searching}
    Whether or not to dereference aliases. The default is never.

    Example:

    {auth_method, ldap}.
     {ldap_servers, ["ldap.example.org"]}.
    @@ -1931,7 +1966,7 @@ all entries end with a comma:
     

    mod_offlineOffline message storage (XEP-0160) 
    mod_offline_odbcOffline message storage (XEP-0160)supported DB (*)
    mod_pingXMPP Ping and periodic keepalives (XEP-0199) 
    mod_privacyDetect presence subscription flood 
    mod_pres_counterDetect presence subscription flood 
    mod_privacyBlocking Communication (XEP-0016) 
    mod_privacy_odbcBlocking Communication (XEP-0016)supported DB (*)
    mod_privatePrivate XML Storage (XEP-0049)