./ct_report/coverage/mod_roster_api.COVER.html

1 %% @doc Provide an interface for frontends (like graphql or ctl) to manage roster.
2 -module(mod_roster_api).
3
4 -export([list_contacts/1,
5 get_contact/2,
6 add_contact/4,
7 delete_contact/2,
8 subscription/3,
9 subscribe_both/2,
10 set_mutual_subscription/3]).
11
12 -include("mongoose.hrl").
13 -include("jlib.hrl").
14 -include("mod_roster.hrl").
15
16 -type sub_mutual_action() :: connect | disconnect.
17
18 -define(UNKNOWN_DOMAIN_RESULT, {unknown_domain, "Domain not found"}).
19 -define(INTERNAL_ERROR_RESULT(Error, Operation),
20 {internal, io_lib:format("Cannot ~p a contact because: ~p", [Operation, Error])}).
21
22 -spec add_contact(jid:jid(), jid:jid(), binary(), [binary()]) ->
23 {ok | internal | user_not_exist | unknown_domain, iolist()}.
24 add_contact(#jid{lserver = LServer} = CallerJID, ContactJID, Name, Groups) ->
25
:-(
case mongoose_domain_api:get_domain_host_type(LServer) of
26 {ok, HostType} ->
27
:-(
case does_users_exist(CallerJID, ContactJID) of
28 ok ->
29
:-(
case mod_roster:set_roster_entry(HostType, CallerJID, ContactJID,
30 Name, Groups) of
31 ok ->
32
:-(
{ok, "Contact added successfully"};
33 {error, Error} ->
34
:-(
?INTERNAL_ERROR_RESULT(Error, create)
35 end;
36 Error ->
37
:-(
Error
38 end;
39 {error, not_found} ->
40
:-(
?UNKNOWN_DOMAIN_RESULT
41 end.
42
43 -spec list_contacts(jid:jid()) -> {ok, [mod_roster:roster()]} | {unknown_domain, iolist()}.
44 list_contacts(#jid{lserver = LServer} = CallerJID) ->
45
:-(
case mongoose_domain_api:get_domain_host_type(LServer) of
46 {ok, HostType} ->
47
:-(
case ejabberd_auth:does_user_exist(CallerJID) of
48 true ->
49
:-(
mongoose_instrument:execute(mod_roster_get, #{host_type => HostType},
50 #{count => 1, jid => CallerJID}),
51
:-(
Acc0 = mongoose_acc:new(#{ location => ?LOCATION,
52 host_type => HostType,
53 lserver => LServer,
54 element => undefined }),
55
:-(
Roster = mongoose_hooks:roster_get(Acc0, CallerJID, true),
56
:-(
{ok, Roster};
57 false ->
58
:-(
{user_not_exist, io_lib:format("The user ~s does not exist",
59 [jid:to_binary(CallerJID)])}
60 end;
61 {error, not_found} ->
62
:-(
?UNKNOWN_DOMAIN_RESULT
63 end.
64
65 -spec get_contact(jid:jid(), jid:jid()) ->
66 {ok, mod_roster:roster()} |
67 {contact_not_found | internal | unknown_domain | user_not_exist, iolist()}.
68 get_contact(#jid{lserver = LServer} = UserJID, ContactJID) ->
69
:-(
case mongoose_domain_api:get_domain_host_type(LServer) of
70 {ok, HostType} ->
71
:-(
ContactL = jid:to_lower(ContactJID),
72
:-(
case mod_roster:get_roster_entry(HostType, UserJID, ContactL, full) of
73 #roster{} = R ->
74
:-(
{ok, R};
75 does_not_exist ->
76
:-(
{contact_not_found, "Given contact does not exist"};
77 error ->
78
:-(
?INTERNAL_ERROR_RESULT(error, get)
79 end;
80 {error, not_found} ->
81
:-(
?UNKNOWN_DOMAIN_RESULT
82 end.
83
84 -spec delete_contact(jid:jid(), jid:jid()) ->
85 {ok | contact_not_found | internal | unknown_domain, iolist()}.
86 delete_contact(#jid{lserver = LServer} = CallerJID, ContactJID) ->
87
:-(
case mongoose_domain_api:get_domain_host_type(LServer) of
88 {ok, HostType} ->
89
:-(
case mod_roster:remove_from_roster(HostType, CallerJID, ContactJID) of
90 ok ->
91
:-(
{ok, io_lib:format("Contact ~s deleted successfully",
92 [jid:to_binary(ContactJID)])};
93 {error, does_not_exist} ->
94
:-(
ErrMsg = io_lib:format("Cannot remove ~s contact that does not exist",
95 [jid:to_binary(ContactJID)]),
96
:-(
{contact_not_found, ErrMsg};
97 {error, Error} ->
98
:-(
?INTERNAL_ERROR_RESULT(Error, delete)
99 end;
100 {error, not_found} ->
101
:-(
?UNKNOWN_DOMAIN_RESULT
102 end.
103
104 -spec subscription(jid:jid(), jid:jid(), mod_roster:sub_presence()) ->
105 {ok | unknown_domain, iolist()}.
106 subscription(#jid{lserver = LServer} = CallerJID, ContactJID, Type) ->
107
:-(
StanzaType = atom_to_binary(Type, latin1),
108
:-(
El = #xmlel{name = <<"presence">>, attrs = [{<<"type">>, StanzaType}]},
109
:-(
case mongoose_domain_api:get_domain_host_type(LServer) of
110 {ok, HostType} ->
111
:-(
Acc1 = mongoose_acc:new(#{ location => ?LOCATION,
112 from_jid => CallerJID,
113 to_jid => ContactJID,
114 host_type => HostType,
115 lserver => LServer,
116 element => El }),
117
:-(
Acc2 = mongoose_hooks:roster_out_subscription(Acc1, CallerJID, ContactJID, Type),
118
:-(
ejabberd_router:route(CallerJID, jid:to_bare(ContactJID), Acc2),
119
:-(
{ok, io_lib:format("Subscription stanza with type ~s sent successfully", [StanzaType])};
120 {error, not_found} ->
121
:-(
?UNKNOWN_DOMAIN_RESULT
122 end.
123
124 -spec set_mutual_subscription(jid:jid(), jid:jid(), sub_mutual_action()) ->
125 {ok | contact_not_found | internal | unknown_domain | user_not_exist, iolist()}.
126 set_mutual_subscription(UserA, UserB, connect) ->
127
:-(
subscribe_both({UserA, <<>>, []}, {UserB, <<>>, []});
128 set_mutual_subscription(UserA, UserB, disconnect) ->
129
:-(
Seq = [fun() -> delete_contact(UserA, UserB) end,
130
:-(
fun() -> delete_contact(UserB, UserA) end],
131
:-(
case run_seq(Seq, ok) of
132 ok ->
133
:-(
{ok, "Mutual subscription removed successfully"};
134 Error ->
135
:-(
Error
136 end.
137
138 -spec subscribe_both({jid:jid(), binary(), [binary()]}, {jid:jid(), binary(), [binary()]}) ->
139 {ok | internal | unknown_domain | user_not_exist, iolist()}.
140 subscribe_both({UserA, NameA, GroupsA}, {UserB, NameB, GroupsB}) ->
141
:-(
Seq = [fun() -> add_contact(UserA, UserB, NameB, GroupsB) end,
142
:-(
fun() -> add_contact(UserB, UserA, NameA, GroupsA) end,
143
:-(
fun() -> subscription(UserA, UserB, subscribe) end,
144
:-(
fun() -> subscription(UserB, UserA, subscribe) end,
145
:-(
fun() -> subscription(UserA, UserB, subscribed) end,
146
:-(
fun() -> subscription(UserB, UserA, subscribed) end],
147
:-(
case run_seq(Seq, ok) of
148 ok ->
149
:-(
{ok, io_lib:format("Subscription between users ~s and ~s created successfully",
150 [jid:to_binary(UserA), jid:to_binary(UserB)])};
151 Error ->
152
:-(
Error
153 end.
154
155 %% Internal
156
157 -spec does_users_exist(jid:jid(), jid:jid()) -> ok | {user_not_exist, iolist()}.
158 does_users_exist(User, Contact) ->
159
:-(
case lists:filter(fun(U) -> not ejabberd_auth:does_user_exist(U) end, [User, Contact]) of
160 [] ->
161
:-(
ok;
162 [NotExist | _]->
163
:-(
{user_not_exist, io_lib:format("The user ~s does not exist",
164 [jid:to_binary(NotExist)])}
165 end.
166
167 -spec run_seq([fun(() -> any())], term()) -> ok | {atom(), iolist()}.
168 run_seq([Cmd | Seq], ok) ->
169
:-(
run_seq(Seq, Cmd());
170 run_seq([Cmd | Seq], {ok, _}) ->
171
:-(
run_seq(Seq, Cmd());
172
:-(
run_seq([], _) -> ok;
173 run_seq(_, {_, _} = Error) ->
174
:-(
Error.
Line Hits Source