aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-10-22 13:01:45 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-10-22 13:01:45 +0300
commitf6236d456d599544baed11d51c3e6d7ef7e459eb (patch)
treed579a9d63588b8042646b2001c14b2ad82028f80 /src
parentAdd more MUC tests (diff)
Add more tests for privacy lists and blocking command
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd_c2s.erl174
-rw-r--r--src/ejabberd_piefxis.erl2
-rw-r--r--src/ejabberd_socket.erl9
-rw-r--r--src/mod_admin_extra.erl6
-rw-r--r--src/mod_blocking.erl13
-rw-r--r--src/mod_privacy.erl67
-rw-r--r--src/mod_roster.erl9
7 files changed, 144 insertions, 136 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index f7d8e9dbb..986310546 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -881,18 +881,20 @@ decode_element(#xmlel{} = El, StateName, StateData) ->
end
catch error:{xmpp_codec, Why} ->
NS = xmpp:get_ns(El),
- case xmpp:is_stanza(El) of
- true ->
- Lang = xmpp:get_lang(El),
- Txt = xmpp:format_error(Why),
- send_error(StateData, El, xmpp:err_bad_request(Txt, Lang));
- false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 ->
- Err = #sm_failed{reason = 'bad-request', xmlns = NS},
- send_element(StateData, Err);
- false ->
- ok
- end,
- fsm_next_state(StateName, StateData)
+ fsm_next_state(
+ StateName,
+ case xmpp:is_stanza(El) of
+ true ->
+ Lang = xmpp:get_lang(El),
+ Txt = xmpp:format_error(Why),
+ send_error(StateData, El, xmpp:err_bad_request(Txt, Lang));
+ false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 ->
+ Err = #sm_failed{reason = 'bad-request', xmlns = NS},
+ send_element(StateData, Err),
+ StateData;
+ false ->
+ StateData
+ end)
end.
wait_for_bind({xmlstreamelement, El}, StateData) ->
@@ -957,13 +959,14 @@ wait_for_bind(closed, StateData) ->
wait_for_bind(stop, StateData) ->
{stop, normal, StateData};
wait_for_bind(Pkt, StateData) ->
- case xmpp:is_stanza(Pkt) of
- true ->
- send_error(StateData, Pkt, xmpp:err_not_acceptable());
- false ->
- ok
- end,
- fsm_next_state(wait_for_bind, StateData).
+ fsm_next_state(
+ wait_for_bind,
+ case xmpp:is_stanza(Pkt) of
+ true ->
+ send_error(StateData, Pkt, xmpp:err_not_acceptable());
+ false ->
+ StateData
+ end).
-spec open_session(state()) -> {ok, state()} | {error, stanza_error()}.
open_session(StateData) ->
@@ -1315,27 +1318,23 @@ handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Pac
allow ->
{true, StateData};
deny ->
- Err = xmpp:make_error(
- Packet,
- xmpp:err_service_unavailable()),
- ejabberd_router:route(To, From, Err),
+ ejabberd_router:route_error(
+ To, From, Packet,
+ xmpp:err_service_unavailable()),
{false, StateData}
end;
_ ->
- Err = xmpp:make_error(Packet, xmpp:err_forbidden()),
- ejabberd_router:route(To, From, Err),
+ ejabberd_router:route_error(
+ To, From, Packet, xmpp:err_forbidden()),
{false, StateData}
end;
_ ->
case privacy_check_packet(StateData, From, To, Packet, in) of
allow ->
{true, StateData};
- deny when T == get; T == set ->
- Err = xmpp:make_error(
- Packet, xmpp:err_service_unavailable()),
- ejabberd_router:route(To, From, Err),
- {false, StateData};
deny ->
+ ejabberd_router:route_error(
+ To, From, Packet, xmpp:err_service_unavailable()),
{false, StateData}
end
end;
@@ -1345,13 +1344,11 @@ handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Pac
{true, StateData};
deny ->
case T of
- error -> ok;
groupchat -> ok;
headline -> ok;
_ ->
- Err = xmpp:make_error(
- Packet, xmpp:err_service_unavailable()),
- ejabberd_router:route(To, From, Err)
+ ejabberd_router:route_error(
+ To, From, Packet, xmpp:err_service_unavailable())
end,
{false, StateData}
end
@@ -1572,14 +1569,14 @@ send_element(StateData, #xmlel{} = El) ->
send_element(StateData, Pkt) ->
send_element(StateData, xmpp:encode(Pkt, ?NS_CLIENT)).
--spec send_error(state(), xmlel() | stanza(), stanza_error()) -> ok.
+-spec send_error(state(), xmlel() | stanza(), stanza_error()) -> state().
send_error(StateData, Stanza, Error) ->
Type = xmpp:get_type(Stanza),
if Type == error; Type == result;
Type == <<"error">>; Type == <<"result">> ->
- ok;
+ StateData;
true ->
- send_element(StateData, xmpp:make_error(Stanza, Error))
+ send_stanza(StateData, xmpp:make_error(Stanza, Error))
end.
-spec send_stanza(state(), xmpp_element()) -> state().
@@ -1754,47 +1751,56 @@ presence_track(From, To, Packet, StateData) ->
LTo = jid:tolower(To),
User = StateData#state.user,
Server = StateData#state.server,
- case Type of
- unavailable ->
- A = ?SETS:del_element(LTo, StateData#state.pres_a),
- check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet);
- subscribe ->
- try_roster_subscribe(subscribe, User, Server, From, To, Packet, StateData);
- subscribed ->
- ejabberd_hooks:run(roster_out_subscription, Server,
- [User, Server, To, subscribed]),
- check_privacy_route(From, StateData,
- jid:remove_resource(From), To, Packet);
- unsubscribe ->
- try_roster_subscribe(unsubscribe, User, Server, From, To, Packet, StateData);
- unsubscribed ->
- ejabberd_hooks:run(roster_out_subscription, Server,
- [User, Server, To, unsubscribed]),
- check_privacy_route(From, StateData,
- jid:remove_resource(From), To, Packet);
- error ->
- check_privacy_route(From, StateData, From, To, Packet);
- probe ->
- check_privacy_route(From, StateData, From, To, Packet);
- _ ->
- A = (?SETS):add_element(LTo, StateData#state.pres_a),
- check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet)
+ Lang = StateData#state.lang,
+ case privacy_check_packet(StateData, From, To, Packet, out) of
+ deny ->
+ ErrText = <<"Your active privacy list has denied "
+ "the routing of this stanza.">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ send_error(StateData, xmpp:set_from_to(Packet, From, To), Err);
+ allow when Type == subscribe; Type == subscribed;
+ Type == unsubscribe; Type == unsubscribed ->
+ Access = gen_mod:get_module_opt(Server, mod_roster, access,
+ fun(A) when is_atom(A) -> A end,
+ all),
+ MyBareJID = jid:make(User, Server, <<"">>),
+ case acl:match_rule(Server, Access, MyBareJID) of
+ deny ->
+ ErrText = <<"Denied by ACL">>,
+ Err = xmpp:err_forbidden(ErrText, Lang),
+ send_error(StateData, xmpp:set_from_to(Packet, From, To), Err);
+ allow ->
+ ejabberd_hooks:run(roster_out_subscription,
+ Server,
+ [User, Server, To, Type]),
+ ejabberd_router:route(jid:remove_resource(From), To, Packet),
+ StateData
+ end;
+ allow when Type == error; Type == probe ->
+ ejabberd_router:route(From, To, Packet),
+ StateData;
+ allow ->
+ ejabberd_router:route(From, To, Packet),
+ A = case Type of
+ available ->
+ ?SETS:add_element(LTo, StateData#state.pres_a);
+ unavailable ->
+ ?SETS:del_element(LTo, StateData#state.pres_a)
+ end,
+ StateData#state{pres_a = A}
end.
-spec check_privacy_route(jid(), state(), jid(), jid(), stanza()) -> state().
check_privacy_route(From, StateData, FromRoute, To,
Packet) ->
case privacy_check_packet(StateData, From, To, Packet,
- out)
- of
+ out) of
deny ->
Lang = StateData#state.lang,
ErrText = <<"Your active privacy list has denied "
- "the routing of this stanza.">>,
- Err = xmpp:make_error(
- xmpp:set_from_to(Packet, From, To),
- xmpp:err_not_acceptable(ErrText, Lang)),
- send_stanza(StateData, Err);
+ "the routing of this stanza.">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ send_error(StateData, xmpp:set_from_to(Packet, From, To), Err);
allow ->
ejabberd_router:route(FromRoute, To, Packet),
StateData
@@ -1815,24 +1821,6 @@ is_privacy_allow(StateData, From, To, Packet, Dir) ->
allow ==
privacy_check_packet(StateData, From, To, Packet, Dir).
-%%% Check ACL before allowing to send a subscription stanza
--spec try_roster_subscribe(subscribe | unsubscribe, binary(), binary(),
- jid(), jid(), presence(), state()) -> state().
-try_roster_subscribe(Type, User, Server, From, To, Packet, StateData) ->
- JID1 = jid:make(User, Server, <<"">>),
- Access = gen_mod:get_module_opt(Server, mod_roster, access, fun(A) when is_atom(A) -> A end, all),
- case acl:match_rule(Server, Access, JID1) of
- deny ->
- %% Silently drop this (un)subscription request
- StateData;
- allow ->
- ejabberd_hooks:run(roster_out_subscription,
- Server,
- [User, Server, To, Type]),
- check_privacy_route(From, StateData, jid:remove_resource(From),
- To, Packet)
- end.
-
%% Send presence when disconnecting
-spec presence_broadcast(state(), jid(), ?SETS:set(), presence()) -> ok.
presence_broadcast(StateData, From, JIDSet, Packet) ->
@@ -1980,7 +1968,7 @@ process_privacy_iq(#iq{from = From, to = To,
privacy_iq_set,
StateData#state.server,
{error, xmpp:err_feature_not_implemented(Txt, Lang)},
- [IQ])
+ [IQ, StateData#state.privacy_list])
of
{result, R, NewPrivList} ->
{{result, R},
@@ -2522,9 +2510,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData)
false ->
fun(From, To, El, _Time) ->
Txt = <<"User session terminated">>,
- Err = xmpp:make_error(
- El, xmpp:err_service_unavailable(Txt, Lang)),
- ejabberd_router:route(To, From, Err)
+ ejabberd_router:route_error(
+ To, From, El, xmpp:err_service_unavailable(Txt, Lang))
end
end,
F = fun(From, _To, #presence{}, _Time) ->
@@ -2532,9 +2519,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData)
[jid:to_string(From)]);
(From, To, #iq{} = El, _Time) ->
Txt = <<"User session terminated">>,
- Err = xmpp:make_error(
- El, xmpp:err_service_unavailable(Txt, Lang)),
- ejabberd_router:route(To, From, Err);
+ ejabberd_router:route_error(
+ To, From, El, xmpp:err_service_unavailable(Txt, Lang));
(From, To, El, Time) ->
%% We'll drop the stanza if it was <forwarded/> by some
%% encapsulating protocol as per XEP-0297. One such protocol is
diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl
index 5e6e1bf58..0e79c9913 100644
--- a/src/ejabberd_piefxis.erl
+++ b/src/ejabberd_piefxis.erl
@@ -486,7 +486,7 @@ process_privacy(#privacy_query{lists = Lists,
from = JID, to = JID, sub_els = [PrivacyQuery]},
Txt = <<"No module is handling this query">>,
Error = {error, xmpp:err_feature_not_implemented(Txt, ?MYLANG)},
- case mod_privacy:process_iq_set(Error, IQ) of
+ case mod_privacy:process_iq_set(Error, IQ, #userlist{}) of
{error, #stanza_error{reason = Reason}} = Err ->
if Reason == 'item-not-found', Lists == [],
Active == undefined, Default /= undefined ->
diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl
index f8dc84630..b5fa52ded 100644
--- a/src/ejabberd_socket.erl
+++ b/src/ejabberd_socket.erl
@@ -31,6 +31,7 @@
-export([start/4,
connect/3,
connect/4,
+ connect/5,
starttls/2,
starttls/3,
compress/1,
@@ -125,19 +126,21 @@ start(Module, SockMod, Socket, Opts) ->
end.
connect(Addr, Port, Opts) ->
- connect(Addr, Port, Opts, infinity).
+ connect(Addr, Port, Opts, infinity, self()).
connect(Addr, Port, Opts, Timeout) ->
+ connect(Addr, Port, Opts, Timeout, self()).
+
+connect(Addr, Port, Opts, Timeout, Owner) ->
case gen_tcp:connect(Addr, Port, Opts, Timeout) of
{ok, Socket} ->
Receiver = ejabberd_receiver:start(Socket, gen_tcp,
none),
SocketData = #socket_state{sockmod = gen_tcp,
socket = Socket, receiver = Receiver},
- Pid = self(),
case gen_tcp:controlling_process(Socket, Receiver) of
ok ->
- ejabberd_receiver:become_controller(Receiver, Pid),
+ ejabberd_receiver:become_controller(Receiver, Owner),
{ok, SocketData};
{error, _Reason} = Error -> gen_tcp:close(Socket), Error
end;
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 627b5b58f..4598805c2 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -53,6 +53,7 @@
-include("ejabberd.hrl").
-include("ejabberd_commands.hrl").
-include("mod_roster.hrl").
+-include("mod_privacy.hrl").
-include("ejabberd_sm.hrl").
-include("xmpp.hrl").
@@ -1380,11 +1381,12 @@ privacy_set(Username, Host, QueryS) ->
To = jid:make(Host),
QueryEl = fxml_stream:parse_element(QueryS),
SubEl = xmpp:decode(QueryEl),
- IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl]},
+ IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
+ from = From, to = To},
ejabberd_hooks:run_fold(privacy_iq_set,
Host,
{error, xmpp:err_feature_not_implemented()},
- [From, To, IQ]),
+ [IQ, #userlist{}]),
ok.
%%%
diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl
index b3bbff96e..d2b187d26 100644
--- a/src/mod_blocking.erl
+++ b/src/mod_blocking.erl
@@ -30,7 +30,7 @@
-protocol({xep, 191, '1.2'}).
-export([start/2, stop/1, process_iq/1,
- process_iq_set/2, process_iq_get/3, mod_opt_type/1, depends/2]).
+ process_iq_set/3, process_iq_get/3, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -85,10 +85,11 @@ process_iq_get(Acc, _, _) -> Acc.
-spec process_iq_set({error, stanza_error()} |
{result, xmpp_element() | undefined} |
{result, xmpp_element() | undefined, userlist()},
- iq()) -> {error, stanza_error()} |
- {result, xmpp_element() | undefined} |
- {result, xmpp_element() | undefined, userlist()}.
-process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}) ->
+ iq(), userlist()) ->
+ {error, stanza_error()} |
+ {result, xmpp_element() | undefined} |
+ {result, xmpp_element() | undefined, userlist()}.
+process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}, _) ->
#jid{luser = LUser, lserver = LServer} = From,
case SubEl of
#block{items = []} ->
@@ -105,7 +106,7 @@ process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}) ->
_ ->
Acc
end;
-process_iq_set(Acc, _) -> Acc.
+process_iq_set(Acc, _, _) -> Acc.
-spec list_to_blocklist_jids([listitem()], [ljid()]) -> [ljid()].
list_to_blocklist_jids([], JIDs) -> JIDs;
diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl
index 2f318deec..d4c8464f1 100644
--- a/src/mod_privacy.erl
+++ b/src/mod_privacy.erl
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_iq/1, export/1, import/1,
- process_iq_set/2, process_iq_get/3, get_user_list/3,
+ process_iq_set/3, process_iq_get/3, get_user_list/3,
check_packet/6, remove_user/2, encode_list_item/1,
is_list_needdb/1, updated_list/3,
item_to_xml/1, get_user_lists/2, import/3,
@@ -103,6 +103,12 @@ process_iq(IQ) ->
-spec process_iq_get({error, stanza_error()} | {result, xmpp_element() | undefined},
iq(), userlist()) -> {error, stanza_error()} |
{result, xmpp_element() | undefined}.
+process_iq_get(_, #iq{lang = Lang,
+ sub_els = [#privacy_query{default = Default,
+ active = Active}]},
+ _) when Default /= undefined; Active /= undefined ->
+ Txt = <<"Only <list/> element is allowed in this query">>,
+ {error, xmpp:err_bad_request(Txt, Lang)};
process_iq_get(_, #iq{from = From, lang = Lang,
sub_els = [#privacy_query{lists = Lists}]},
#userlist{name = Active}) ->
@@ -205,7 +211,7 @@ encode_value(Type, Val) ->
listitem_value().
decode_value(Type, Value) ->
case Type of
- jid -> jid:from_string(Value);
+ jid -> jid:tolower(jid:from_string(Value));
subscription ->
case Value of
<<"from">> -> from;
@@ -213,35 +219,37 @@ decode_value(Type, Value) ->
<<"both">> -> both;
<<"none">> -> none
end;
- group -> Value;
+ group when Value /= <<"">> -> Value;
undefined -> none
end.
-spec process_iq_set({error, stanza_error()} |
{result, xmpp_element() | undefined} |
{result, xmpp_element() | undefined, userlist()},
- iq()) -> {error, stanza_error()} |
- {result, xmpp_element() | undefined} |
- {result, xmpp_element() | undefined, userlist()}.
+ iq(), #userlist{}) ->
+ {error, stanza_error()} |
+ {result, xmpp_element() | undefined} |
+ {result, xmpp_element() | undefined, userlist()}.
process_iq_set(_, #iq{from = From, lang = Lang,
sub_els = [#privacy_query{default = Default,
active = Active,
- lists = Lists}]}) ->
+ lists = Lists}]},
+ #userlist{} = UserList) ->
#jid{luser = LUser, lserver = LServer} = From,
case Lists of
[#privacy_list{items = Items, name = ListName}]
when Default == undefined, Active == undefined ->
- process_lists_set(LUser, LServer, ListName, Items, Lang);
+ process_lists_set(LUser, LServer, ListName, Items, UserList, Lang);
[] when Default == undefined, Active /= undefined ->
process_active_set(LUser, LServer, Active, Lang);
[] when Active == undefined, Default /= undefined ->
process_default_set(LUser, LServer, Default, Lang);
_ ->
- Txt = <<"There should be exactly one element in this query: "
- "<list/>, <active/> or <default/>">>,
+ Txt = <<"The stanza MUST contain only one <active/> element, "
+ "one <default/> element, or one <list/> element">>,
{error, xmpp:err_bad_request(Txt, Lang)}
end;
-process_iq_set(Acc, _) ->
+process_iq_set(Acc, _, _) ->
Acc.
-spec process_default_set(binary(), binary(), none | binary(),
@@ -286,13 +294,20 @@ set_privacy_list(#privacy{us = {_, LServer}} = Privacy) ->
Mod:set_privacy_list(Privacy).
-spec process_lists_set(binary(), binary(), binary(), [privacy_item()],
- binary()) -> {error, stanza_error()} | {result, undefined}.
-process_lists_set(LUser, LServer, Name, [], Lang) ->
+ #userlist{}, binary()) -> {error, stanza_error()} |
+ {result, undefined}.
+process_lists_set(_LUser, _LServer, Name, [], #userlist{name = Name}, Lang) ->
+ Txt = <<"Cannot remove active list">>,
+ {error, xmpp:err_conflict(Txt, Lang)};
+process_lists_set(LUser, LServer, Name, [], _UserList, Lang) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:remove_privacy_list(LUser, LServer, Name) of
{atomic, conflict} ->
Txt = <<"Cannot remove default list">>,
{error, xmpp:err_conflict(Txt, Lang)};
+ {atomic, not_found} ->
+ Txt = <<"No privacy list with this name found">>,
+ {error, xmpp:err_item_not_found(Txt, Lang)};
{atomic, ok} ->
ejabberd_sm:route(jid:make(LUser, LServer,
<<"">>),
@@ -308,7 +323,7 @@ process_lists_set(LUser, LServer, Name, [], Lang) ->
Txt = <<"Database failure">>,
{error, xmpp:err_internal_server_error(Txt, Lang)}
end;
-process_lists_set(LUser, LServer, Name, Items, Lang) ->
+process_lists_set(LUser, LServer, Name, Items, _UserList, Lang) ->
case catch lists:map(fun decode_item/1, Items) of
{error, Why} ->
Txt = xmpp:format_error(Why),
@@ -358,9 +373,7 @@ decode_item(#privacy_item{order = Order,
action = Action,
type = Type,
value = Value},
- if MatchMessage and MatchIQ and MatchPresenceIn and MatchPresenceOut ->
- ListItem#listitem{match_all = true};
- not (MatchMessage or MatchIQ or MatchPresenceIn or MatchPresenceOut) ->
+ if not (MatchMessage or MatchIQ or MatchPresenceIn or MatchPresenceOut) ->
ListItem#listitem{match_all = true};
true ->
ListItem#listitem{match_iq = MatchIQ,
@@ -468,17 +481,11 @@ check_packet_aux([Item | List], PType, JID,
Item,
case is_ptype_match(Item, PType) of
true ->
- case Type of
- none -> Action;
- _ ->
- case is_type_match(Type, Value, JID, Subscription,
- Groups)
- of
- true -> Action;
- false ->
- check_packet_aux(List, PType, JID, Subscription, Groups)
- end
- end;
+ case is_type_match(Type, Value, JID, Subscription, Groups) of
+ true -> Action;
+ false ->
+ check_packet_aux(List, PType, JID, Subscription, Groups)
+ end;
false ->
check_packet_aux(List, PType, JID, Subscription, Groups)
end.
@@ -499,8 +506,10 @@ is_ptype_match(Item, PType) ->
end
end.
--spec is_type_match(jid | subscription | group, listitem_value(),
+-spec is_type_match(none | jid | subscription | group, listitem_value(),
ljid(), none | both | from | to, [binary()]) -> boolean().
+is_type_match(none, _Value, _JID, _Subscription, _Groups) ->
+ true;
is_type_match(Type, Value, JID, Subscription, Groups) ->
case Type of
jid ->
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index feebd3945..423fe9e0e 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -49,7 +49,7 @@
get_jid_info/4, encode_item/1, webadmin_page/3,
webadmin_user/4, get_versioning_feature/2,
roster_versioning_enabled/1, roster_version/2,
- mod_opt_type/1, set_roster/1, depends/2]).
+ mod_opt_type/1, set_roster/1, del_roster/3, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -297,6 +297,13 @@ set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
roster_subscribe_t(LUser, LServer, LJID, Item)
end).
+del_roster(LUser, LServer, LJID) ->
+ transaction(
+ LServer,
+ fun() ->
+ del_roster_t(LUser, LServer, LJID)
+ end).
+
encode_item(Item) ->
#roster_item{jid = jid:make(Item#roster.jid),
name = Item#roster.name,