From 6db9c2630d89c2d045d3d565c3156ac52ec0e51f Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 17 Sep 2004 19:52:59 +0000 Subject: [PATCH] * src/mod_muc/mod_muc_room.erl: Send password in room invitation (thanks to Sergei Golovan) * src/mod_disco.erl: Added registration of sm features and nodes (thanks to Sergei Golovan) * src/mod_vcard.erl: Register vcard-temp feature (thanks to Sergei Golovan) * src/jlib.erl: Added functions now_to_utc_string/1, now_to_local_string/1, and datetime_string_to_timestamp/1 (thanks to Sergei Golovan) * src/mod_muc/mod_muc_room.erl: Use time parsing functions from jlib (thanks to Sergei Golovan) * ejabberd/src/mod_pubsub/mod_pubsub.erl: Bugfix (thanks to Mickael Remond) * src/mod_pubsub/mod_pubsub.erl: Bugfix SVN Revision: 265 --- ChangeLog | 25 +++++++ src/ejabberd_c2s.erl | 2 +- src/jlib.erl | 120 ++++++++++++++++++++++++++++++ src/mod_disco.erl | 136 ++++++++++++++++++++++++++-------- src/mod_muc/mod_muc_room.erl | 104 ++++---------------------- src/mod_pubsub/mod_pubsub.erl | 6 +- src/mod_vcard.erl | 2 + 7 files changed, 274 insertions(+), 121 deletions(-) diff --git a/ChangeLog b/ChangeLog index 490436747..d2f599d0b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2004-09-17 Alexey Shchepin + + * src/mod_muc/mod_muc_room.erl: Send password in room invitation + (thanks to Sergei Golovan) + + * src/mod_disco.erl: Added registration of sm features and nodes + (thanks to Sergei Golovan) + * src/mod_vcard.erl: Register vcard-temp feature (thanks to Sergei + Golovan) + + * src/jlib.erl: Added functions now_to_utc_string/1, + now_to_local_string/1, and datetime_string_to_timestamp/1 (thanks + to Sergei Golovan) + * src/mod_muc/mod_muc_room.erl: Use time parsing functions from + jlib (thanks to Sergei Golovan) + +2004-09-16 Alexey Shchepin + + * ejabberd/src/mod_pubsub/mod_pubsub.erl: Bugfix (thanks to + Mickael Remond) + +2004-09-15 Alexey Shchepin + + * src/mod_pubsub/mod_pubsub.erl: Bugfix + 2004-09-10 Alexey Shchepin * tools/ejabberdctl: Added call to "exec" (thanks to Sergei diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index d8021606f..7452eb9c2 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -1354,7 +1354,7 @@ update_priority(El, StateData) -> ejabberd_sm:set_presence(StateData#state.user, StateData#state.resource, Pri). - + process_privacy_iq(From, To, diff --git a/src/jlib.erl b/src/jlib.erl index 07ad93332..ffd4e51d9 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -37,6 +37,9 @@ parse_xdata_submit/1, timestamp_to_iso/1, timestamp_to_xml/1, + now_to_utc_string/1, + now_to_local_string/1, + datetime_string_to_timestamp/1, decode_base64/1, encode_base64/1]). @@ -438,6 +441,123 @@ timestamp_to_xml({{Year, Month, Day}, {Hour, Minute, Second}}) -> [Year, Month, Day, Hour, Minute, Second]))}], []}. +now_to_utc_string({MegaSecs, Secs, MicroSecs}) -> + {{Year, Month, Day}, {Hour, Minute, Second}} = + calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}), + lists:flatten( + io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6..0wZ", + [Year, Month, Day, Hour, Minute, Second, MicroSecs])). + +now_to_local_string({MegaSecs, Secs, MicroSecs}) -> + LocalTime = calendar:now_to_local_time({MegaSecs, Secs, MicroSecs}), + UTCTime = calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}), + Seconds = calendar:datetime_to_gregorian_seconds(LocalTime) - + calendar:datetime_to_gregorian_seconds(UTCTime), + {{H, M, _}, Sign} = if + Seconds < 0 -> + {calendar:seconds_to_time(-Seconds), "-"}; + true -> + {calendar:seconds_to_time(Seconds), "+"} + end, + {{Year, Month, Day}, {Hour, Minute, Second}} = LocalTime, + lists:flatten( + io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6..0w~s~2..0w:~2..0w", + [Year, Month, Day, Hour, Minute, Second, MicroSecs, Sign, H, M])). + + +% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {MegaSecs, Secs, MicroSecs} +datetime_string_to_timestamp(TimeStr) -> + case catch parse_datetime(TimeStr) of + {'EXIT', _Err} -> + undefined; + TimeStamp -> + TimeStamp + end. + +parse_datetime(TimeStr) -> + [Date, Time] = string:tokens(TimeStr, "T"), + D = parse_date(Date), + {T, MS, TZH, TZM} = parse_time(Time), + S = calendar:datetime_to_gregorian_seconds({D, T}), + S1 = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}), + Seconds = (S - S1) - TZH * 60 * 60 - TZM * 60, + {Seconds div 1000000, Seconds rem 1000000, MS}. + +% yyyy-mm-dd +parse_date(Date) -> + [Y, M, D] = string:tokens(Date, "-"), + Date1 = {list_to_integer(Y), list_to_integer(M), list_to_integer(D)}, + case calendar:valid_date(Date1) of + true -> + Date1; + _ -> + false + end. + +% hh:mm:ss[.sss]TZD +parse_time(Time) -> + case string:str(Time, "Z") of + 0 -> + parse_time_with_timezone(Time); + _ -> + [T | _] = string:tokens(Time, "Z"), + {TT, MS} = parse_time1(T), + {TT, MS, 0, 0} + end. + +parse_time_with_timezone(Time) -> + case string:str(Time, "+") of + 0 -> + case string:str(Time, "-") of + 0 -> + false; + _ -> + parse_time_with_timezone(Time, "-") + end; + _ -> + parse_time_with_timezone(Time, "+") + end. + +parse_time_with_timezone(Time, Delim) -> + [T, TZ] = string:tokens(Time, Delim), + {TZH, TZM} = parse_timezone(TZ), + {TT, MS} = parse_time1(T), + case Delim of + "-" -> + {TT, MS, -TZH, -TZM}; + "+" -> + {TT, MS, TZH, TZM} + end. + +parse_timezone(TZ) -> + [H, M] = string:tokens(TZ, ":"), + {[H1, M1], true} = check_list([{H, 12}, {M, 60}]), + {H1, M1}. + +parse_time1(Time) -> + [HMS | T] = string:tokens(Time, "."), + MS = case T of + [] -> + 0; + [Val] -> + list_to_integer(string:left(Val, 6, $0)) + end, + [H, M, S] = string:tokens(HMS, ":"), + {[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]), + {{H1, M1, S1}, MS}. + +check_list(List) -> + lists:mapfoldl( + fun({L, N}, B)-> + V = list_to_integer(L), + if + (V >= 0) and (V =< N) -> + {V, B}; + true -> + {false, false} + end + end, true, List). + % % Base64 stuff (based on httpd_util.erl) diff --git a/src/mod_disco.erl b/src/mod_disco.erl index af992df0b..3a7578ddf 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -21,7 +21,11 @@ register_feature/1, unregister_feature/1, register_extra_domain/1, - unregister_extra_domain/1]). + unregister_extra_domain/1, + register_sm_feature/1, + unregister_sm_feature/1, + register_sm_node/4, + unregister_sm_node/1]). -include("ejabberd.hrl"). -include("jlib.hrl"). @@ -53,6 +57,8 @@ start(Opts) -> catch ets:new(disco_extra_domains, [named_table, ordered_set, public]), ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []), lists:foreach(fun register_extra_domain/1, ExtraDomains), + catch ets:new(disco_sm_features, [named_table, ordered_set, public]), + catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]), ok. stop() -> @@ -463,43 +469,108 @@ get_stopped_nodes(Lang) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -process_sm_iq_items(From, To, #iq{type = Type, sub_el = SubEl} = IQ) -> - #jid{user = User} = To, - case {acl:match_rule(configure, From), Type} of - {deny, _} -> +register_sm_feature(Feature) -> + catch ets:new(disco_sm_features, [named_table, ordered_set, public]), + ets:insert(disco_sm_features, {Feature}). + +unregister_sm_feature(Feature) -> + catch ets:new(disco_sm_features, [named_table, ordered_set, public]), + ets:delete(disco_sm_features, Feature). + +register_sm_node(Node, Name, Module, Function) -> + catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]), + ets:insert(disco_sm_nodes, {Node, Name, Module, Function}). + +unregister_sm_node(Node) -> + catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]), + ets:delete(disco_sm_nodes, Node). + +process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) -> + #jid{user = User, luser = LTo} = To, + #jid{luser = LFrom, lserver = LServer} = From, + Self = (LTo == LFrom) andalso (LServer == ?MYNAME), + Node = xml:get_tag_attr_s("node", SubEl), + case {acl:match_rule(configure, From), Type, Self, Node} of + {_, set, _, _} -> IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - {allow, set} -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - {allow, get} -> - case xml:get_tag_attr_s("node", SubEl) of - "" -> - IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", ?NS_DISCO_ITEMS}], - get_user_resources(User) - }]}; - _ -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]} - end + {_, get, true, []} -> + Nodes = lists:map(fun({Nod, Name, _, _}) -> + node_to_xml(User, + Nod, + translate:translate(Lang, Name)) + end, ets:tab2list(disco_sm_nodes)), + IQ#iq{type = result, + sub_el = [{xmlelement, "query", + [{"xmlns", ?NS_DISCO_ITEMS}], + get_user_resources(User) ++ Nodes}]}; + {allow, get, _, []} -> + Nodes = lists:map(fun({Nod, Name, _, _}) -> + node_to_xml(User, + Nod, + translate:translate(Lang, Name)) + end, ets:tab2list(disco_sm_nodes)), + IQ#iq{type = result, + sub_el = [{xmlelement, "query", + [{"xmlns", ?NS_DISCO_ITEMS}], + get_user_resources(User) ++ Nodes}]}; + {A, get, S, _} when (A == allow) or (S == true) -> + case ets:lookup(disco_sm_nodes, Node) of + [] -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}; + [{Node, _Name, Module, Function}] -> + case Module:Function(From, To, IQ) of + {error, Err} -> + IQ#iq{type = error, sub_el = [SubEl, Err]}; + {result, Res} -> + IQ#iq{type = result, + sub_el = [{xmlelement, "query", + [{"xmlns", ?NS_DISCO_ITEMS}, + {"node", Node}], + Res}]} + end + end; + {_, get, _, _} -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}; + _ -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]} end. process_sm_iq_info(From, To, #iq{type = Type, xmlns = XMLNS, sub_el = SubEl} = IQ) -> - case {acl:match_rule(configure, From), Type} of - {deny, _} -> + #jid{luser = LTo} = To, + #jid{luser = LFrom, lserver = LServer} = From, + Self = (LTo == LFrom) andalso (LServer == ?MYNAME), + Node = xml:get_tag_attr_s("node", SubEl), + case {acl:match_rule(configure, From), Type, Self, Node} of + {_, set, _, _} -> IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - {allow, set} -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - {allow, get} -> - case xml:get_tag_attr_s("node", SubEl) of - "" -> - IQ#iq{type = result, - sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}], - [feature_to_xml({?NS_EJABBERD_CONFIG})]}]}; + {allow, get, _, []} -> + Features = lists:map(fun feature_to_xml/1, + ets:tab2list(disco_sm_features)), + IQ#iq{type = result, + sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}], + [feature_to_xml({?NS_EJABBERD_CONFIG})] ++ + Features}]}; + {_, get, _, []} -> + Features = lists:map(fun feature_to_xml/1, + ets:tab2list(disco_sm_features)), + IQ#iq{type = result, + sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}], + Features}]}; + {A, get, S, _} when (A == allow) or (S == true) -> + case ets:lookup(disco_sm_nodes, Node) of + [] -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}; _ -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]} - end + IQ#iq{type = result, sub_el = [{xmlelement, "query", + [{"xmlns", XMLNS}, + {"node", Node}], []}]} + end; + {_, get, _, _} -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}; + _ -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]} end. @@ -512,3 +583,8 @@ get_user_resources(User) -> {"name", User}], []} end, lists:sort(Rs)). +node_to_xml(User, Node, Name) -> + {xmlelement, "item", [{"jid", User ++ "@" ++ ?MYNAME}, + {"node", Node}, + {"name", Name}], []}. + diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl index ba6187d6b..17aaeacaa 100644 --- a/src/mod_muc/mod_muc_room.erl +++ b/src/mod_muc/mod_muc_room.erl @@ -1079,23 +1079,18 @@ extract_history([{xmlelement, _Name, Attrs, _SubEls} = El | Els], Type) -> [{elem, "history"}, {attr, Type}]), case Type of "since" -> - case catch parse_datetime(AttrVal) of - {'EXIT', _Err} -> + case jlib:datetime_string_to_timestamp(AttrVal) of + undefined -> false; - Res -> - Res + TS -> + calendar:now_to_universal_time(TS) end; _ -> case catch list_to_integer(AttrVal) of - {'EXIT', _} -> - false; - IntVal -> - if - IntVal >= 0 -> - IntVal; - true -> - false - end + IntVal when is_integer(IntVal) and IntVal >= 0 -> + IntVal; + _ -> + false end end; _ -> @@ -1104,80 +1099,6 @@ extract_history([{xmlelement, _Name, Attrs, _SubEls} = El | Els], Type) -> extract_history([_ | Els], Type) -> extract_history(Els, Type). -% JEP-0082 -% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {{yyyy, mm, dd}, {hh, mm, ss}} (UTC) -parse_datetime(TimeStr) -> - [Date, Time] = string:tokens(TimeStr, "T"), - D = parse_date(Date), - {T, TZH, TZM} = parse_time(Time), - S = calendar:datetime_to_gregorian_seconds({D, T}), - calendar:gregorian_seconds_to_datetime(S - TZH * 60 * 60 - TZM * 60). - -% yyyy-mm-dd -parse_date(Date) -> - YearMonthDay = string:tokens(Date, "-"), - [Y, M, D] = lists:map( - fun(L)-> - list_to_integer(L) - end, YearMonthDay), - {Y, M, D}. - -% hh:mm:ss[.sss]TZD -parse_time(Time) -> - case string:str(Time, "Z") of - 0 -> - parse_time_with_timezone(Time); - _ -> - [T | _] = string:tokens(Time, "Z"), - {parse_time1(T), 0, 0} - end. - -parse_time_with_timezone(Time) -> - case string:str(Time, "+") of - 0 -> - case string:str(Time, "-") of - 0 -> - false; - _ -> - parse_time_with_timezone(Time, "-") - end; - _ -> - parse_time_with_timezone(Time, "+") - end. - -parse_time_with_timezone(Time, Delim) -> - [T, TZ] = string:tokens(Time, Delim), - {TZH, TZM} = parse_timezone(TZ), - TT = parse_time1(T), - case Delim of - "-" -> - {TT, -TZH, -TZM}; - "+" -> - {TT, TZH, TZM} - end. - -parse_timezone(TZ) -> - [H, M] = string:tokens(TZ, ":"), - {[H1, M1], true} = check_list([{H, 12}, {M, 60}]), - {H1, M1}. - -parse_time1(Time) -> - [HMS | _] = string:tokens(Time, "."), - [H, M, S] = string:tokens(HMS, ":"), - {[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]), - {H1, M1, S1}. - -check_list(List) -> - lists:mapfoldl( - fun({L, N}, B)-> - V = list_to_integer(L), - if - (V >= 0) and (V =< N) -> - {V, B}; - true -> - {false, false} - end - end, true, List). send_update_presence(JID, StateData) -> LJID = jlib:jid_tolower(JID), @@ -2316,7 +2237,14 @@ check_invitation(From, Els, StateData) -> jlib:jid_to_string(From)}], [{xmlelement, "reason", [], [{xmlcdata, Reason}]}]}], - PasswdEl = [], + PasswdEl = + case (StateData#state.config)#config.password_protected of + true -> + [{xmlelement, "password", [], + [{xmlcdata, (StateData#state.config)#config.password}]}]; + _ -> + [] + end, Msg = {xmlelement, "message", [{"type", "normal"}], diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 3acd0a76d..3a9a93c6a 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -452,6 +452,8 @@ iq_pubsub(Host, From, Type, SubEl) -> [{xmlelement, "value", [], [{xmlcdata, Val}]}]}). +%% Create new pubsub nodes +%% This function is used during init to create the first bootstrap nodes create_new_node(Host, Node, Owner) -> case Node of [] -> @@ -867,7 +869,7 @@ set_entities(OJID, Node, EntitiesEls) -> (Subscription == false) -> error; true -> - [{JID, + [{jlib:jid_tolower(JID), #entity{ affiliation = Affiliation, subscription = Subscription}} | @@ -984,7 +986,7 @@ subscription_to_string(Subscription) -> check_create_permission(Host, Node, Owner) -> if - #jid{lserver = Host} == Owner -> + Owner#jid.lserver == Host -> true; true -> #jid{luser = User, lserver = Server} = Owner, diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 854df2c0a..49c0728dc 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -64,6 +64,7 @@ start(Opts) -> ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_VCARD, ?MODULE, process_sm_iq, IQDisc), + catch mod_disco:register_sm_feature(?NS_VCARD), Host = gen_mod:get_opt(host, Opts, "vjud." ++ ?MYNAME), Search = gen_mod:get_opt(search, Opts, true), register(ejabberd_mod_vcard, spawn(?MODULE, init, [Host, Search])). @@ -98,6 +99,7 @@ loop(Host) -> stop() -> gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_VCARD), gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_VCARD), + catch mod_disco:unregister_sm_feature(?NS_VCARD), ejabberd_mod_vcard ! stop, ok.