./ct_report/coverage/mod_vcard_api.COVER.html

1 %% @doc Provide an interface for frontends (like graphql or ctl) to manage vcard.
2 -module(mod_vcard_api).
3
4 -include("mongoose.hrl").
5 -include("jlib.hrl").
6 -include("mod_vcard.hrl").
7
8 -type vcard_map() :: #{binary() => vcard_subelement_binary() | vcard_subelement_map()}.
9 -type vcard_subelement_binary() :: binary() | [{ok, binary()}].
10 -type vcard_subelement_map() :: #{binary() => binary() | [{ok, binary()}]}.
11
12 -export([set_vcard/2,
13 get_vcard/1]).
14
15 -spec set_vcard(jid:jid(), vcard_map()) ->
16 {ok, vcard_map()} | {user_not_found, string()} | {internal, string()} | {vcard_not_found, string()}.
17 set_vcard(#jid{luser = LUser, lserver = LServer} = UserJID, Vcard) ->
18 9 case check_user(UserJID) of
19 {ok, HostType} ->
20 5 case set_vcard(HostType, UserJID, Vcard) of
21 ok ->
22 5 get_vcard_from_db(HostType, LUser, LServer);
23 _ ->
24
:-(
{internal, "Internal server error"}
25 end;
26 Error ->
27 4 Error
28 end.
29
30 -spec get_vcard(jid:jid()) ->
31 {ok, vcard_map()} | {user_not_found, string()} | {internal, string()} | {vcard_not_found, string()}
32 | {vcard_not_configured_error, string()}.
33 get_vcard(#jid{luser = LUser, lserver = LServer} = UserJID) ->
34 % check if mod_vcard is loaded is needed in user's get_vcard command, when user variable is not passed
35 20 case check_user(UserJID) of
36 {ok, HostType} ->
37 14 case gen_mod:is_loaded(HostType, mod_vcard) of
38 true ->
39 13 get_vcard_from_db(HostType, LUser, LServer);
40 false ->
41 1 {vcard_not_configured_error, "Mod_vcard is not loaded for this host"}
42 end;
43 Error ->
44 6 Error
45 end.
46
47 set_vcard(HostType, UserJID, Vcard) ->
48 5 mod_vcard:unsafe_set_vcard(HostType, UserJID, transform_from_map(Vcard)).
49
50 get_vcard_from_db(HostType, LUser, LServer) ->
51 18 ItemNotFoundError = mongoose_xmpp_errors:item_not_found(),
52 18 case mod_vcard_backend:get_vcard(HostType, LUser, LServer) of
53 {ok, Result} ->
54 14 [#xmlel{children = VcardData}] = Result,
55 14 {ok, to_map_format(VcardData)};
56 {error, ItemNotFoundError} ->
57 4 {vcard_not_found, "Vcard for user not found"};
58 _ ->
59
:-(
{internal, "Internal server error"}
60 end.
61
62 -spec check_user(jid:jid()) -> {ok, mongooseim:host_type()} | {user_not_found, binary()}.
63 check_user(JID = #jid{lserver = LServer}) ->
64 29 case mongoose_domain_api:get_domain_host_type(LServer) of
65 {ok, HostType} ->
66 24 case ejabberd_auth:does_user_exist(HostType, JID, stored) of
67 19 true -> {ok, HostType};
68 5 false -> {user_not_found, <<"Given user does not exist">>}
69 end;
70 {error, not_found} ->
71 5 {user_not_found, <<"User's domain does not exist">>}
72 end.
73
74 transform_from_map(Vcard) ->
75 11 #xmlel{name = <<"vCard">>,
76 attrs = [{<<"xmlns">>, <<"vcard-temp">>}],
77 children = lists:foldl(fun({Name, Value}, Acc) ->
78 319 Acc ++ transform_field_and_value(Name, Value)
79 end, [], maps:to_list(Vcard))}.
80
81 construct_xmlel(Name, Children) when is_list(Children)->
82 496 [#xmlel{name = Name,
83 attrs = [],
84 children = Children}].
85
86 transform_field_and_value(_Name, null) ->
87 202 [];
88 transform_field_and_value(Name, Value) when is_list(Value) ->
89 95 lists:foldl(fun(Element, Acc) ->
90 194 Acc ++ transform_field_and_value(Name, Element)
91 end, [], Value);
92 transform_field_and_value(Name, Value) when is_map(Value) ->
93 109 construct_xmlel(from_map_to_xml(Name), process_child_map(Value));
94 transform_field_and_value(Name, Value) ->
95 107 construct_xmlel(from_map_to_xml(Name), [{xmlcdata, Value}]).
96
97 transform_subfield_and_value(_Name, null) ->
98 75 [];
99 transform_subfield_and_value(<<"vcard">>, Value) ->
100 6 [transform_from_map(Value)];
101 transform_subfield_and_value(<<"tags">>, TagsList) ->
102 32 lists:foldl(fun(Tag, Acc) ->
103 61 Acc ++ construct_xmlel(Tag, [])
104 end, [], TagsList);
105 transform_subfield_and_value(Name, Value) when is_list(Value) ->
106 18 lists:foldl(fun(Element, Acc) ->
107 30 Acc ++ construct_xmlel(from_map_to_xml(Name), [{xmlcdata, Element}])
108 end, [], Value);
109 transform_subfield_and_value(Name, Value) ->
110 189 construct_xmlel(from_map_to_xml(Name), [{xmlcdata, Value}]).
111
112 process_child_map(Value) ->
113 109 lists:foldl(fun({Name, SubfieldValue}, Acc) ->
114 320 Acc ++ transform_subfield_and_value(Name, SubfieldValue)
115 end, [], maps:to_list(Value)).
116
117 11 from_map_to_xml(<<"formattedName">>) -> <<"FN">>;
118 11 from_map_to_xml(<<"nameComponents">>) -> <<"N">>;
119 6 from_map_to_xml(<<"birthday">>) -> <<"BDAY">>;
120 8 from_map_to_xml(<<"address">>) -> <<"ADR">>;
121 6 from_map_to_xml(<<"telephone">>) -> <<"TEL">>;
122 6 from_map_to_xml(<<"timeZone">>) -> <<"TZ">>;
123 6 from_map_to_xml(<<"sortString">>) -> <<"SOR">>;
124 9 from_map_to_xml(<<"givenName">>) -> <<"GIVEN">>;
125 9 from_map_to_xml(<<"middleName">>) -> <<"MIDDLE">>;
126 6 from_map_to_xml(<<"credential">>) -> <<"CRED">>;
127 8 from_map_to_xml(<<"country">>) -> <<"CTRY">>;
128 15 from_map_to_xml(<<"binValue">>) -> <<"BINVAL">>;
129 18 from_map_to_xml(<<"extValue">>) -> <<"EXTVAL">>;
130 316 from_map_to_xml(Name) -> list_to_binary(string:to_upper(binary_to_list(Name))).
131
132 to_map_format(Vcard) ->
133 26 lists:foldl(fun(#xmlel{name = Name, children = Value}, Acc) ->
134 441 maps:merge(Acc, transform_from_xml(Name, Value, Acc))
135 end, #{}, Vcard).
136
137 transform_from_xml(<<"FN">>, [{_, Value}], _) ->
138 24 #{<<"formattedName">> => Value};
139 transform_from_xml(<<"N">>, Value, _) ->
140 22 #{<<"nameComponents">> => lists:foldl(fun name_components_process/2, #{}, Value)};
141 transform_from_xml(<<"NICKNAME">>, Value, Acc) ->
142 36 simple_process(<<"nickname">>, Value, Acc);
143 transform_from_xml(<<"PHOTO">>, Value, Acc) ->
144 36 complex_process(<<"photo">>, Value, Acc, fun image_components_process/2);
145 transform_from_xml(<<"BDAY">>, Value, Acc) ->
146 12 simple_process(<<"birthday">>, Value, Acc);
147 transform_from_xml(<<"ADR">>, Value, Acc) ->
148 18 complex_process(<<"address">>, Value, Acc, fun address_components_process/2);
149 transform_from_xml(<<"LABEL">>, Value, Acc) ->
150 12 complex_process(<<"label">>, Value, Acc, fun label_components_process/2);
151 transform_from_xml(<<"TEL">>, Value, Acc) ->
152 12 complex_process(<<"telephone">>, Value, Acc, fun telephone_components_process/2);
153 transform_from_xml(<<"EMAIL">>, Value, Acc) ->
154 16 complex_process(<<"email">>, Value, Acc, fun email_components_process/2);
155 transform_from_xml(<<"JABBERID">>, Value, Acc) ->
156 12 simple_process(<<"jabberId">>, Value, Acc);
157 transform_from_xml(<<"MAILER">>, Value, Acc) ->
158 12 simple_process(<<"mailer">>, Value, Acc);
159 transform_from_xml(<<"TZ">>, Value, Acc) ->
160 12 simple_process(<<"timeZone">>, Value, Acc);
161 transform_from_xml(<<"GEO">>, Value, Acc) ->
162 12 complex_process(<<"geo">>, Value, Acc, fun geo_components_process/2);
163 transform_from_xml(<<"TITLE">>, Value, Acc) ->
164 12 simple_process(<<"title">>, Value, Acc);
165 transform_from_xml(<<"ROLE">>, Value, Acc) ->
166 12 simple_process(<<"role">>, Value, Acc);
167 transform_from_xml(<<"LOGO">>, Value, Acc) ->
168 12 complex_process(<<"logo">>, Value, Acc, fun image_components_process/2);
169 transform_from_xml(<<"AGENT">>, Value, Acc) ->
170 18 complex_process(<<"agent">>, Value, Acc, fun agent_components_process/2);
171 transform_from_xml(<<"ORG">>, Value, Acc) ->
172 12 complex_process(<<"org">>, Value, Acc, fun org_components_process/2);
173 transform_from_xml(<<"CATEGORIES">>, Value, Acc) ->
174 12 complex_process(<<"categories">>, Value, Acc, fun categories_components_process/2);
175 transform_from_xml(<<"NOTE">>, Value, Acc) ->
176 12 simple_process(<<"note">>, Value, Acc);
177 transform_from_xml(<<"PRODID">>, Value, Acc) ->
178 12 simple_process(<<"prodId">>, Value, Acc);
179 transform_from_xml(<<"REV">>, Value, Acc) ->
180 12 simple_process(<<"rev">>, Value, Acc);
181 transform_from_xml(<<"SOR">>, Value, Acc) ->
182 12 simple_process(<<"sortString">>, Value, Acc);
183 transform_from_xml(<<"SOUND">>, Value, Acc) ->
184 18 complex_process(<<"sound">>, Value, Acc, fun sound_components_process/2);
185 transform_from_xml(<<"UID">>, Value, Acc) ->
186 12 simple_process(<<"uid">>, Value, Acc);
187 transform_from_xml(<<"URL">>, Value, Acc) ->
188 12 simple_process(<<"url">>, Value, Acc);
189 transform_from_xml(<<"DESC">>, Value, Acc) ->
190 12 simple_process(<<"desc">>, Value, Acc);
191 transform_from_xml(<<"CLASS">>, Value, Acc) ->
192 12 complex_process(<<"class">>, Value, Acc, fun class_components_process/2);
193 transform_from_xml(<<"KEY">>, Value, Acc) ->
194 12 complex_process(<<"key">>, Value, Acc, fun key_components_process/2);
195 transform_from_xml(_, _, _) ->
196 1 #{}.
197
198 process_value([{_, Value}]) ->
199 456 Value;
200 process_value(_) ->
201
:-(
null.
202
203 simple_process(Name, [{_, Value}], Acc) ->
204 192 List = maps:get(Name, Acc, []),
205 192 #{Name => List ++ [{ok, Value}]};
206 simple_process(_, _, _) ->
207
:-(
#{}.
208
209 complex_process(Name, Value, Acc, Fun) ->
210 202 List = maps:get(Name, Acc, []),
211 202 #{Name => List ++ [{ok, lists:foldl(fun(Element, Accumulator) ->
212 510 Fun(Element, Accumulator)
213 end, #{}, Value)}]}.
214
215 name_components_process(#xmlel{name = <<"FAMILY">>, children = Value}, Acc) ->
216 18 maps:put(<<"family">>, process_value(Value), Acc);
217 name_components_process(#xmlel{name = <<"GIVEN">>, children = Value}, Acc) ->
218 18 maps:put(<<"givenName">>, process_value(Value), Acc);
219 name_components_process(#xmlel{name = <<"MIDDLE">>, children = Value}, Acc) ->
220 18 maps:put(<<"middleName">>, process_value(Value), Acc);
221 name_components_process(#xmlel{name = <<"PREFIX">>, children = Value}, Acc) ->
222 18 maps:put(<<"prefix">>, process_value(Value), Acc);
223 name_components_process(#xmlel{name = <<"SUFFIX">>, children = Value}, Acc) ->
224 18 maps:put(<<"suffix">>, process_value(Value), Acc).
225
226 address_components_process(#xmlel{name = <<"POBOX">>, children = Value}, Acc) ->
227 18 maps:put(<<"pobox">>, process_value(Value), Acc);
228 address_components_process(#xmlel{name = <<"EXTADD">>, children = Value}, Acc) ->
229 18 maps:put(<<"extadd">>, process_value(Value), Acc);
230 address_components_process(#xmlel{name = <<"STREET">>, children = Value}, Acc) ->
231 18 maps:put(<<"street">>, process_value(Value), Acc);
232 address_components_process(#xmlel{name = <<"LOCALITY">>, children = Value}, Acc) ->
233 18 maps:put(<<"locality">>, process_value(Value), Acc);
234 address_components_process(#xmlel{name = <<"REGION">>, children = Value}, Acc) ->
235 18 maps:put(<<"region">>, process_value(Value), Acc);
236 address_components_process(#xmlel{name = <<"PCODE">>, children = Value}, Acc) ->
237 14 maps:put(<<"pcode">>, process_value(Value), Acc);
238 address_components_process(#xmlel{name = <<"CTRY">>, children = Value}, Acc) ->
239 18 maps:put(<<"country">>, process_value(Value), Acc);
240 address_components_process(#xmlel{name = Name, children = []}, Acc) ->
241 42 List = maps:get(<<"tags">>, Acc, []),
242 42 maps:merge(Acc, #{<<"tags">> => List ++ [{ok, Name}]}).
243
244 label_components_process(#xmlel{name = <<"LINE">>, children = Value}, Acc) ->
245 24 List = maps:get(<<"line">>, Acc, []),
246 24 maps:merge(Acc, #{<<"line">> => List ++ [{ok, process_value(Value)}]});
247 label_components_process(#xmlel{name = Name, children = []}, Acc) ->
248 24 List = maps:get(<<"tags">>, Acc, []),
249 24 maps:merge(Acc, #{<<"tags">> => List ++ [{ok, Name}]}).
250
251 telephone_components_process(#xmlel{name = <<"NUMBER">>, children = Value}, Acc) ->
252 12 maps:put(<<"number">>, process_value(Value), Acc);
253 telephone_components_process(#xmlel{name = Name, children = []}, Acc) ->
254 24 List = maps:get(<<"tags">>, Acc, []),
255 24 maps:merge(Acc, #{<<"tags">> => List ++ [{ok, Name}]}).
256
257 email_components_process(#xmlel{name = <<"USERID">>, children = Value}, Acc) ->
258 16 maps:put(<<"userId">>, process_value(Value), Acc);
259 email_components_process(#xmlel{name = Name, children = []}, Acc) ->
260 24 List = maps:get(<<"tags">>, Acc, []),
261 24 maps:merge(Acc, #{<<"tags">> => List ++ [{ok, Name}]}).
262
263 geo_components_process(#xmlel{name = <<"LAT">>, children = Value}, Acc) ->
264 12 maps:put(<<"lat">>, process_value(Value), Acc);
265 geo_components_process(#xmlel{name = <<"LON">>, children = Value}, Acc) ->
266 12 maps:put(<<"lon">>, process_value(Value), Acc).
267
268 org_components_process(#xmlel{name = <<"ORGNAME">>, children = Value}, Acc) ->
269 12 maps:put(<<"orgname">>, process_value(Value), Acc);
270 org_components_process(#xmlel{name = <<"ORGUNIT">>, children = Value}, Acc) ->
271 18 List = maps:get(<<"orgunit">>, Acc, []),
272 18 maps:merge(Acc, #{<<"orgunit">> => List ++ [{ok, process_value(Value)}]}).
273
274 categories_components_process(#xmlel{name = <<"KEYWORD">>, children = Value}, Acc) ->
275 18 List = maps:get(<<"keyword">>, Acc, []),
276 18 maps:merge(Acc, #{<<"keyword">> => List ++ [{ok, process_value(Value)}]}).
277
278 key_components_process(#xmlel{name = <<"CRED">>, children = Value}, Acc) ->
279 12 maps:put(<<"credential">>, process_value(Value), Acc);
280 key_components_process(#xmlel{name = <<"TYPE">>, children = Value}, Acc) ->
281 12 maps:put(<<"type">>, process_value(Value), Acc).
282
283 class_components_process(#xmlel{name = Name, children = []}, Acc) ->
284 18 List = maps:get(<<"tags">>, Acc, []),
285 18 maps:merge(Acc, #{<<"tags">> => List ++ [{ok, Name}]}).
286
287 image_components_process(#xmlel{name = <<"TYPE">>, children = Value}, Acc) ->
288 24 maps:put(<<"type">>, process_value(Value), Acc);
289 image_components_process(#xmlel{name = <<"BINVAL">>, children = Value}, Acc) ->
290 24 maps:put(<<"binValue">>, process_value(Value), Acc);
291 image_components_process(#xmlel{name = <<"EXTVAL">>, children = Value}, Acc) ->
292 24 maps:put(<<"extValue">>, process_value(Value), Acc).
293
294 sound_components_process(#xmlel{name = <<"PHONETIC">>, children = Value}, Acc) ->
295 6 maps:put(<<"phonetic">>, process_value(Value), Acc);
296 sound_components_process(#xmlel{name = <<"BINVAL">>, children = Value}, Acc) ->
297 6 maps:put(<<"binValue">>, process_value(Value), Acc);
298 sound_components_process(#xmlel{name = <<"EXTVAL">>, children = Value}, Acc) ->
299 6 maps:put(<<"extValue">>, process_value(Value), Acc).
300
301 agent_components_process(#xmlel{name = <<"vCard">>, children = Value}, Acc) ->
302 12 maps:put(<<"vcard">>, to_map_format(Value), Acc);
303 agent_components_process(#xmlel{name = <<"EXTVAL">>, children = Value}, Acc) ->
304 6 maps:put(<<"extValue">>, process_value(Value), Acc).
Line Hits Source