* 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
This commit is contained in:
Alexey Shchepin 2004-09-17 19:52:59 +00:00
parent e0ede61e0f
commit 6db9c2630d
7 changed files with 274 additions and 121 deletions

View File

@ -1,3 +1,28 @@
2004-09-17 Alexey Shchepin <alexey@sevcom.net>
* 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 <alexey@sevcom.net>
* ejabberd/src/mod_pubsub/mod_pubsub.erl: Bugfix (thanks to
Mickael Remond)
2004-09-15 Alexey Shchepin <alexey@sevcom.net>
* src/mod_pubsub/mod_pubsub.erl: Bugfix
2004-09-10 Alexey Shchepin <alexey@sevcom.net>
* tools/ejabberdctl: Added call to "exec" (thanks to Sergei

View File

@ -1354,7 +1354,7 @@ update_priority(El, StateData) ->
ejabberd_sm:set_presence(StateData#state.user,
StateData#state.resource,
Pri).
process_privacy_iq(From, To,

View File

@ -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)

View File

@ -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}], []}.

View File

@ -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"}],

View File

@ -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,

View File

@ -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.