25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-26 17:38:45 +01:00

Apply Apollo fixes. More fixes. Improve command descriptions.

This commit is contained in:
Badlop 2010-10-20 16:40:01 +02:00
parent c96a1805e8
commit ca62271a89

View File

@ -92,9 +92,12 @@
%% Roster %% Roster
add_rosteritem/6, add_rosteritem/6,
delete_rosteritem/3, delete_rosteritem/3,
link_contacts/4, add_rosteritem_groups/5,
del_rosteritem_groups/5,
modify_rosteritem_groups/6,
link_contacts/6,
unlink_contacts/2, unlink_contacts/2,
link_contacts/5, unlink_contacts/3, % Versions with Push parameter link_contacts/7, unlink_contacts/3, % Versions with Push parameter
get_roster/2, get_roster/2,
get_roster_with_presence/2, get_roster_with_presence/2,
add_contacts/3, add_contacts/3,
@ -159,7 +162,7 @@ commands() ->
#ejabberd_commands{name = rename_account, tags = [accounts], #ejabberd_commands{name = rename_account, tags = [accounts],
desc = "Change an acount name", desc = "Change an acount name",
longdesc = "Creates a new account " longdesc = "Creates a new account "
"and copies the roster from the old one. " "and copies the roster from the old one, and updates the rosters of his contacts. "
"Offline messages and private storage are lost.", "Offline messages and private storage are lost.",
module = ?MODULE, function = rename_account, module = ?MODULE, function = rename_account,
args = [{user, string}, {server, string}, args = [{user, string}, {server, string},
@ -195,6 +198,7 @@ commands() ->
" - both: presence packets are sent in both direction.\n" " - both: presence packets are sent in both direction.\n"
" - to: the user sees the presence of the given JID.\n" " - to: the user sees the presence of the given JID.\n"
" - from: the JID specified sees the user presence.\n\n" " - from: the JID specified sees the user presence.\n\n"
"ejabberd sends to the user's connected client both the roster item and the presence."
"Don't forget that roster items should keep symmetric: " "Don't forget that roster items should keep symmetric: "
"when adding a roster item for a user, " "when adding a roster item for a user, "
"you have to do the symmetric roster item addition.\n\n", "you have to do the symmetric roster item addition.\n\n",
@ -205,10 +209,11 @@ commands() ->
%% This command is also implemented in mod_admin_contrib %% This command is also implemented in mod_admin_contrib
#ejabberd_commands{name = delete_rosteritem, tags = [roster], #ejabberd_commands{name = delete_rosteritem, tags = [roster],
desc = "Remove an entry for a user roster", desc = "Remove a roster item from the user's roster",
longdesc = "Roster items should be kept symmetric: " longdesc = "Roster items should be kept symmetric: "
"when removing a roster item for a user you have to do " "when removing a roster item for a user you have to do "
"the symmetric roster item removal. \n\n" "the symmetric roster item removal. \n\n"
"ejabberd sends to the user's connected client both the roster item removel and the presence unsubscription."
"This mechanism bypass the standard roster approval " "This mechanism bypass the standard roster approval "
"addition mechanism and should only be used for server " "addition mechanism and should only be used for server "
"administration or server integration purpose.", "administration or server integration purpose.",
@ -216,20 +221,46 @@ commands() ->
args = [{user, string}, {server, string}, {jid, string}], args = [{user, string}, {server, string}, {jid, string}],
result = {res, integer}}, result = {res, integer}},
#ejabberd_commands{name = add_rosteritem_groups, tags = [roster],
desc = "Add new groups in an existing roster item",
longdesc = "The argument newgroups must be a string with group names separated by the character ;",
module = ?MODULE, function = add_rosteritem_groups,
args = [{user, string}, {server, string}, {jid, string},
{newgroups, string}, {push, string}],
result = {res, integer}},
#ejabberd_commands{name = del_rosteritem_groups, tags = [roster],
desc = "Delete groups in an existing roster item",
longdesc = "The argument groups must be a string with group names separated by the character ;",
module = ?MODULE, function = del_rosteritem_groups,
args = [{user, string}, {server, string}, {jid, string},
{groups, string}, {push, string}],
result = {res, integer}},
#ejabberd_commands{name = modify_rosteritem_groups, tags = [roster],
desc = "Modify the groups of an existing roster item",
longdesc = "The argument groups must be a string with group names separated by the character ;",
module = ?MODULE, function = modify_rosteritem_groups,
args = [{user, string}, {server, string}, {jid, string},
{groups, string}, {subs, string}, {push, string}],
result = {res, integer}},
#ejabberd_commands{name = link_contacts, tags = [roster], #ejabberd_commands{name = link_contacts, tags = [roster],
desc = "Add a symmetrical entry in two users roster", desc = "Add a symmetrical entry in two users roster",
longdesc = "jid1 is the JabberID of the user1 you would " longdesc = "jid1 is the JabberID of the user1 you would "
"like to add in user2 roster on the server.\n" "like to add in user2 roster on the server.\n"
"nick1 is the nick of user1.\n" "nick1 is the nick of user1.\n"
"group1 is the group name when adding user1 to user2 roster.\n"
"jid2 is the JabberID of the user2 you would like to " "jid2 is the JabberID of the user2 you would like to "
"add in user1 roster on the server.\n" "add in user1 roster on the server.\n"
"nick2 is the nick of user2.\n\n" "nick2 is the nick of user2.\n"
"This mechanism bypass the standard roster approval " "group2 is the group name when adding user2 to user1 roster.\n\n"
"This mechanism bypasses the standard roster approval "
"addition mechanism " "addition mechanism "
"and should only be userd for server administration or " "and should only be userd for server administration or "
"server integration purpose.", "server integration purpose.",
module = ?MODULE, function = link_contacts, module = ?MODULE, function = link_contacts,
args = [{jid1, string}, {nick1, string}, {jid2, string}, {nick2, string}], args = [{jid1, string}, {nick1, string}, {group1, string}, {jid2, string}, {nick2, string}, {group2, string}],
result = {res, integer}}, result = {res, integer}},
#ejabberd_commands{name = unlink_contacts, tags = [roster], #ejabberd_commands{name = unlink_contacts, tags = [roster],
@ -238,7 +269,7 @@ commands() ->
"jid2 is the JabberID of the user2.\n\n" "jid2 is the JabberID of the user2.\n\n"
"This mechanism bypass the standard roster approval " "This mechanism bypass the standard roster approval "
"addition mechanism " "addition mechanism "
"and should only be userd for server administration or " "and should only be used for server administration or "
"server integration purpose.", "server integration purpose.",
module = ?MODULE, function = unlink_contacts, module = ?MODULE, function = unlink_contacts,
args = [{jid1, string}, {jid2, string}], args = [{jid1, string}, {jid2, string}],
@ -334,7 +365,7 @@ commands() ->
"Pending can be \"in\", \"out\" or \"none\".", "Pending can be \"in\", \"out\" or \"none\".",
module = ?MODULE, function = get_roster, module = ?MODULE, function = get_roster,
args = [{user, string}, {server, string}], args = [{user, string}, {server, string}],
result = {contacts, {list, {contact, {tuple, [{jid, string}, {group, string}, result = {contacts, {list, {contact, {tuple, [{jid, string}, {groups, {list, {group, string}}},
{nick, string}, {subscription, string}, {pending, string}]}}}}}, {nick, string}, {subscription, string}, {pending, string}]}}}}},
#ejabberd_commands{name = get_roster_with_presence, tags = [roster], #ejabberd_commands{name = get_roster_with_presence, tags = [roster],
@ -582,15 +613,15 @@ add_rosteritem(U, S, JID, G, N, Subs, Push) ->
Fun = fun() -> add_rosteritem2(U, S, JID, N, G, Subs, Push) end, Fun = fun() -> add_rosteritem2(U, S, JID, N, G, Subs, Push) end,
user_action(U, S, Fun, {atomic, ok}). user_action(U, S, Fun, {atomic, ok}).
link_contacts(JID1, Nick1, JID2, Nick2) -> link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2) ->
link_contacts(JID1, Nick1, JID2, Nick2, true). link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2, true).
link_contacts(JID1, Nick1, JID2, Nick2, Push) -> link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2, Push) ->
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)), {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
{U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)), {U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(U2, S2)} of case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(U2, S2)} of
{true, true} -> {true, true} ->
case link_contacts2(JID1, Nick1, JID2, Nick2, Push) of case link_contacts2(JID1, Nick1, Group1, JID2, Nick2, Group2, Push) of
{atomic, ok} -> {atomic, ok} ->
0; 0;
_ -> _ ->
@ -687,6 +718,178 @@ check_users_registration(Users) ->
{U, S, Registered} {U, S, Registered}
end, Users). end, Users).
%%%
%%% Groups of Roster Item
%%%
add_rosteritem_groups(User, Server, JID, NewGroupsString, PushString) ->
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
NewGroups = string:tokens(NewGroupsString, ";"),
Push = list_to_atom(PushString),
case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(User, Server)} of
{true, true} ->
case add_rosteritem_groups2(User, Server, JID, NewGroups, Push) of
ok ->
{false, {response, [0]}};
Error ->
?INFO_MSG("Error found: ~n~p", [Error]),
{false, {response, [1]}}
end;
_ ->
{false, {response, [404]}}
end.
del_rosteritem_groups(User, Server, JID, NewGroupsString, PushString) ->
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
NewGroups = string:tokens(NewGroupsString, ";"),
Push = list_to_atom(PushString),
case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(User, Server)} of
{true, true} ->
case del_rosteritem_groups2(User, Server, JID, NewGroups, Push) of
ok ->
{false, {response, [0]}};
Error ->
?INFO_MSG("Error found: ~n~p", [Error]),
{false, {response, [1]}}
end;
_ ->
{false, {response, [404]}}
end.
modify_rosteritem_groups(User, Server, JID, NewGroupsString, SubsString, PushString) ->
Nick = "", %% That information will not be used, anyway
Subs = list_to_atom(SubsString),
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
NewGroups = string:tokens(NewGroupsString, ";"),
Push = list_to_atom(PushString),
case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(User, Server)} of
{true, true} ->
case modify_rosteritem_groups2(User, Server, JID, NewGroups, Push, Nick, Subs) of
ok ->
{false, {response, [0]}};
Error ->
?INFO_MSG("Error found: ~n~p", [Error]),
{false, {response, [1]}}
end;
_ ->
{false, {response, [404]}}
end.
add_rosteritem_groups2(User, Server, JID, NewGroups, Push) ->
GroupsFun =
fun(Groups) ->
lists:usort(NewGroups ++ Groups)
end,
change_rosteritem_group(User, Server, JID, GroupsFun, Push).
del_rosteritem_groups2(User, Server, JID, NewGroups, Push) ->
GroupsFun =
fun(Groups) ->
Groups -- NewGroups
end,
change_rosteritem_group(User, Server, JID, GroupsFun, Push).
modify_rosteritem_groups2(User, Server, JID2, NewGroups, Push, Nick, Subs) when NewGroups == [] ->
JID1 = jlib:jid_to_string(jlib:make_jid(User, Server, "")),
case unlink_contacts(JID1, JID2) of
{atomic, ok} ->
ok;
Error ->
Error
end;
modify_rosteritem_groups2(User, Server, JID, NewGroups, Push, Nick, Subs) ->
GroupsFun =
fun(_Groups) ->
NewGroups
end,
change_rosteritem_group(User, Server, JID, GroupsFun, Push, NewGroups, Nick, Subs).
change_rosteritem_group(User, Server, JID, GroupsFun, Push) ->
change_rosteritem_group(User, Server, JID, GroupsFun, Push, [], "", "both").
change_rosteritem_group(User, Server, JID, GroupsFun, Push, NewGroups, Nick, Subs) ->
{RU, RS, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
LJID = {RU,RS,[]},
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
Result =
case roster_backend(LServer) of
mnesia ->
mnesia:transaction(
fun() ->
case mnesia:read({roster, {LUser, LServer, LJID}}) of
[#roster{} = Roster] ->
NewGroups2 = GroupsFun(Roster#roster.groups),
NewRoster = Roster#roster{groups = NewGroups2},
mnesia:write(NewRoster),
{ok, NewRoster#roster.name,
NewRoster#roster.subscription,
NewGroups2};
_ ->
not_in_roster
end
end);
odbc ->
ejabberd_odbc:sql_transaction(
LServer,
fun() ->
Username = ejabberd_odbc:escape(User),
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
case ejabberd_odbc:sql_query_t(
["select nick, subscription from rosterusers "
" where username='", Username, "' "
" and jid='", SJID, "';"]) of
{selected, ["nick", "subscription"],
[{Name, SSubscription}]} ->
Subscription =
case SSubscription of
"B" -> both;
"T" -> to;
"F" -> from;
_ -> none
end,
Groups =
case odbc_queries:get_roster_groups(
LServer, Username, SJID) of
{selected, ["grp"], JGrps}
when is_list(JGrps) ->
[JGrp || {JGrp} <- JGrps];
_ ->
[]
end,
NewGroups2 = GroupsFun(Groups),
ejabberd_odbc:sql_query_t(
["delete from rostergroups "
" where username='", Username, "' "
" and jid='", SJID, "';"]),
lists:foreach(
fun(Group) ->
ejabberd_odbc:sql_query_t(
["insert into rostergroups("
" username, jid, grp) "
" values ('", Username, "',"
"'", SJID, "',"
"'", ejabberd_odbc:escape(Group), "');"])
end,
NewGroups2),
{ok, Name, Subscription, NewGroups2};
_ ->
not_in_roster
end
end);
none ->
%% Apollo change: force roster push anyway with success
{atomic, {ok, Nick, Subs, NewGroups}}
end,
case {Result, Push} of
{{atomic, {ok, Name, Subscription, NewGroups3}}, true} ->
roster_push(User, Server, JID,
Name, atom_to_list(Subscription), NewGroups3),
ok;
{{atomic, {ok, _Name, _Subscription, _NewGroups3}}, false} -> ok;
{{atomic, not_in_roster}, _} -> not_in_roster;
Error -> {error, Error}
end.
%%% %%%
%%% PubSub %%% PubSub
@ -835,9 +1038,9 @@ add_rosteritem2(User, Server, JID, Nick, Group, Subscription, Push) ->
{selected, ["username"],[]} -> {selected, ["username"],[]} ->
ItemVals = record_to_string(Roster), ItemVals = record_to_string(Roster),
ItemGroups = groups_to_string(Roster), ItemGroups = groups_to_string(Roster),
odbc_queries:update_roster(Server, Username, ejabberd_odbc:sql_query_t(
SJID, ItemVals, odbc_queries:update_roster_sql(
ItemGroups); Username, SJID, ItemVals, ItemGroups));
_ -> _ ->
already_added already_added
end end
@ -883,9 +1086,6 @@ del_rosteritem(User, Server, JID, Push) ->
end, end,
Result. Result.
link_contacts2(JID1, Nick1, JID2, Nick2, Push) ->
link_contacts2(JID1, Nick1, [], JID2, Nick2, [], Push).
link_contacts2(JID1, Nick1, Group1, JID2, Nick2, Group2, Push) -> link_contacts2(JID1, Nick1, Group1, JID2, Nick2, Group2, Push) ->
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)), {U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
{U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)), {U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
@ -906,52 +1106,58 @@ roster_push(User, Server, JID, Nick, Subscription, Groups) ->
LJID = jlib:make_jid(User, Server, ""), LJID = jlib:make_jid(User, Server, ""),
TJID = jlib:string_to_jid(JID), TJID = jlib:string_to_jid(JID),
{TU, TS, _} = jlib:jid_tolower(TJID), {TU, TS, _} = jlib:jid_tolower(TJID),
Presence =
%% TODO: Problem: We assume that both user are local. More test {xmlelement, "presence",
%% are needed to check if the JID is remote or not: [{"type",
case Subscription of
%% TODO: We need to probe user2 especially, if it is not local. "remove" -> "unsubscribed";
%% As a quick fix, I do not go for the probe solution however, because all users "none" -> "unsubscribe";
%% are local "both" -> "subscribed";
case Subscription of _ -> "subscribe"
"to" -> %% Probe second user to route his presence to modified user end}], []},
%% TODO: For now we assume both user are local so we do not, but we need to move to probe. ItemAttrs =
set_roster(User, Server, TJID, Nick, Subscription, Groups); case Nick of
"from" -> "" -> [{"jid", JID}, {"subscription", Subscription}];
%% Send roster updates _ -> [{"jid", JID}, {"name", Nick}, {"subscription", Subscription}]
set_roster(User, Server, TJID, Nick, Subscription, Groups); end,
"both" -> ItemGroups =
%% Update both presence lists:map(fun(G) ->
set_roster(User, Server, TJID, Nick, Subscription, Groups), {xmlelement, "group", [], [{xmlcdata, G}]}
UJID = jlib:make_jid(User, Server, ""), end, Groups),
set_roster(TU, TS, UJID, Nick, Subscription, Groups); Result =
_ -> jlib:iq_to_xml(
%% Remove subscription #iq{type = set, xmlns = ?NS_ROSTER, id = "push",
set_roster(User, Server, TJID, Nick, "none", Groups) lang = "langxmlrpc-en",
end. sub_el = [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}],
[{xmlelement, "item", ItemAttrs, ItemGroups}]}]}),
%% ejabberd_router:route(TJID, LJID, Presence),
set_roster(User, Server, TJID, Nick, Subscription, Groups) -> %% ejabberd_router:route(LJID, LJID, Result),
GroupsXML = [{xmlelement, "group", [], [{xmlcdata, GroupString}]} || GroupString <- Groups], lists:foreach(
Item = case Nick of fun(Resource) ->
"" -> [{"jid", jlib:jid_to_string(TJID)}, {"subscription", Subscription}]; UJID = jlib:make_jid(User, Server, Resource),
_ -> [{"jid", jlib:jid_to_string(TJID)}, {"name", Nick}, {"subscription", Subscription}] ejabberd_router:route(TJID, UJID, Presence),
end, ejabberd_router:route(UJID, UJID, Result),
Result = jlib:iq_to_xml(#iq{type = set, xmlns = ?NS_ROSTER, id = "push", case Subscription of
sub_el = [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}], "remove" -> none;
[{xmlelement, "item", Item, GroupsXML}]}]}), _ ->
lists:foreach(fun(Session) -> lists:foreach(
JID = jlib:make_jid(Session#session.usr), fun(TR) ->
ejabberd_router:route(JID, JID, Result), ejabberd_router:route(
PID = element(2, Session#session.sid), jlib:make_jid(TU, TS, TR), UJID,
ejabberd_c2s:add_rosteritem(PID, TJID, list_to_atom(Subscription)) %% TODO: Better error management {xmlelement, "presence", [], []})
end, get_sessions(User, Server)). end, get_resources(TU, TS))
end
end, [R || R <- get_resources(User, Server)]).
roster_backend(Server) -> roster_backend(Server) ->
case lists:member(mod_roster, gen_mod:loaded_modules(Server)) of Modules = gen_mod:loaded_modules(Server),
true -> mnesia; Mnesia = lists:member(mod_roster, Modules),
_ -> odbc % we assume that Odbc = lists:member(mod_roster_odbc, Modules),
if Mnesia -> mnesia;
true ->
if Odbc -> odbc;
true -> none
end
end. end.
record_to_string(#roster{us = {User, _Server}, record_to_string(#roster{us = {User, _Server},
@ -1013,7 +1219,7 @@ format_roster([#roster{jid=JID, name=Nick, groups=Group,
subscription=Subs, ask=Ask}|Items], Structs) -> subscription=Subs, ask=Ask}|Items], Structs) ->
{User,Server,_Resource} = JID, {User,Server,_Resource} = JID,
Struct = {lists:flatten([User,"@",Server]), Struct = {lists:flatten([User,"@",Server]),
extract_group(Group), Group,
Nick, Nick,
atom_to_list(Subs), atom_to_list(Subs),
atom_to_list(Ask) atom_to_list(Ask)
@ -1053,7 +1259,12 @@ format_roster_with_presence([#roster{jid=JID, name=Nick, groups=Group,
format_roster_with_presence(Items, [Struct|Structs]). format_roster_with_presence(Items, [Struct|Structs]).
extract_group([]) -> []; extract_group([]) -> [];
extract_group([Group|_Groups]) -> Group. %extract_group([Group|_Groups]) -> Group.
extract_group(Groups) -> string:join(Groups, ";").
extract_groups([]) -> [];
%extract_groups([Group|_Groups]) -> Group.
extract_groups(Groups) -> {list, Groups}.
%% ----------------------------- %% -----------------------------
%% Internal session handling %% Internal session handling