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