aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Khramtsov <ekhramtsov@process-one.net>2019-08-02 13:55:48 +0300
committerEvgeny Khramtsov <ekhramtsov@process-one.net>2019-08-02 13:59:42 +0300
commit5a976719fb821801fd162cde4516085416ef71e6 (patch)
tree9811d2e4b7da2e4396259a2e1ce7e1c839c5194a
parentBump pkix version (diff)
Introduce 'vcard' option for the modules supporting vCards
The mapping between vCard's XML elements and YAML elements of 'vcard' option is straightforward. For example, if you want mod_muc to return the following vCard: ``` <vCard xmlns='vcard-temp'> <FN>Conferences</FN> <ADR> <WORK/> <STREET>Elm Street</STREET> </ADR> </vCard> ``` you need to set the configuration as: ``` modules: ... mod_muc: vcard: fn: Conferences adr: - work: true street: Elm Street ... ```
-rw-r--r--src/econf.erl169
-rw-r--r--src/mod_http_upload.erl18
-rw-r--r--src/mod_http_upload_opt.erl7
-rw-r--r--src/mod_muc.erl21
-rw-r--r--src/mod_muc_opt.erl7
-rw-r--r--src/mod_multicast.erl23
-rw-r--r--src/mod_multicast_opt.erl7
-rw-r--r--src/mod_proxy65.erl5
-rw-r--r--src/mod_proxy65_opt.erl7
-rw-r--r--src/mod_proxy65_service.erl17
-rw-r--r--src/mod_pubsub.erl27
-rw-r--r--src/mod_pubsub_opt.erl7
-rw-r--r--src/mod_vcard.erl23
-rw-r--r--src/mod_vcard_opt.erl7
-rwxr-xr-xtools/opt_types.sh3
15 files changed, 312 insertions, 36 deletions
diff --git a/src/econf.erl b/src/econf.erl
index ec127c4e1..4b7ccd587 100644
--- a/src/econf.erl
+++ b/src/econf.erl
@@ -51,6 +51,7 @@
-export([jid/0, user/0, domain/0, resource/0]).
-export([db_type/1, ldap_filter/0]).
-export([host/0, hosts/0]).
+-export([vcard_temp/0]).
-ifdef(SIP).
-export([sip_uri/0]).
-endif.
@@ -526,6 +527,157 @@ host() ->
hosts() ->
list(host(), [unique]).
+-spec vcard_temp() -> yconf:validator().
+vcard_temp() ->
+ vcard_validator(
+ vcard_temp, undefined,
+ [{version, undefined, binary()},
+ {fn, undefined, binary()},
+ {n, undefined, vcard_name()},
+ {nickname, undefined, binary()},
+ {photo, undefined, vcard_photo()},
+ {bday, undefined, binary()},
+ {adr, [], list(vcard_adr())},
+ {label, [], list(vcard_label())},
+ {tel, [], list(vcard_tel())},
+ {email, [], list(vcard_email())},
+ {jabberid, undefined, binary()},
+ {mailer, undefined, binary()},
+ {tz, undefined, binary()},
+ {geo, undefined, vcard_geo()},
+ {title, undefined, binary()},
+ {role, undefined, binary()},
+ {logo, undefined, vcard_logo()},
+ {org, undefined, vcard_org()},
+ {categories, [], list(binary())},
+ {note, undefined, binary()},
+ {prodid, undefined, binary()},
+ {rev, undefined, binary()},
+ {sort_string, undefined, binary()},
+ {sound, undefined, vcard_sound()},
+ {uid, undefined, binary()},
+ {url, undefined, binary()},
+ {class, undefined, enum([confidential, private, public])},
+ {key, undefined, vcard_key()},
+ {desc, undefined, binary()}]).
+
+-spec vcard_name() -> yconf:validator().
+vcard_name() ->
+ vcard_validator(
+ vcard_name, undefined,
+ [{family, undefined, binary()},
+ {given, undefined, binary()},
+ {middle, undefined, binary()},
+ {prefix, undefined, binary()},
+ {suffix, undefined, binary()}]).
+
+-spec vcard_photo() -> yconf:validator().
+vcard_photo() ->
+ vcard_validator(
+ vcard_photo, undefined,
+ [{type, undefined, binary()},
+ {binval, undefined, binary()},
+ {extval, undefined, binary()}]).
+
+-spec vcard_adr() -> yconf:validator().
+vcard_adr() ->
+ vcard_validator(
+ vcard_adr, [],
+ [{home, false, bool()},
+ {work, false, bool()},
+ {postal, false, bool()},
+ {parcel, false, bool()},
+ {dom, false, bool()},
+ {intl, false, bool()},
+ {pref, false, bool()},
+ {pobox, undefined, binary()},
+ {extadd, undefined, binary()},
+ {street, undefined, binary()},
+ {locality, undefined, binary()},
+ {region, undefined, binary()},
+ {pcode, undefined, binary()},
+ {ctry, undefined, binary()}]).
+
+-spec vcard_label() -> yconf:validator().
+vcard_label() ->
+ vcard_validator(
+ vcard_label, [],
+ [{home, false, bool()},
+ {work, false, bool()},
+ {postal, false, bool()},
+ {parcel, false, bool()},
+ {dom, false, bool()},
+ {intl, false, bool()},
+ {pref, false, bool()},
+ {line, [], list(binary())}]).
+
+-spec vcard_tel() -> yconf:validator().
+vcard_tel() ->
+ vcard_validator(
+ vcard_tel, [],
+ [{home, false, bool()},
+ {work, false, bool()},
+ {voice, false, bool()},
+ {fax, false, bool()},
+ {pager, false, bool()},
+ {msg, false, bool()},
+ {cell, false, bool()},
+ {video, false, bool()},
+ {bbs, false, bool()},
+ {modem, false, bool()},
+ {isdn, false, bool()},
+ {pcs, false, bool()},
+ {pref, false, bool()},
+ {number, undefined, binary()}]).
+
+-spec vcard_email() -> yconf:validator().
+vcard_email() ->
+ vcard_validator(
+ vcard_email, [],
+ [{home, false, bool()},
+ {work, false, bool()},
+ {internet, false, bool()},
+ {pref, false, bool()},
+ {x400, false, bool()},
+ {userid, undefined, binary()}]).
+
+-spec vcard_geo() -> yconf:validator().
+vcard_geo() ->
+ vcard_validator(
+ vcard_geo, undefined,
+ [{lat, undefined, binary()},
+ {lon, undefined, binary()}]).
+
+-spec vcard_logo() -> yconf:validator().
+vcard_logo() ->
+ vcard_validator(
+ vcard_logo, undefined,
+ [{type, undefined, binary()},
+ {binval, undefined, binary()},
+ {extval, undefined, binary()}]).
+
+-spec vcard_org() -> yconf:validator().
+vcard_org() ->
+ vcard_validator(
+ vcard_org, undefined,
+ [{name, undefined, binary()},
+ {units, [], list(binary())}]).
+
+-spec vcard_sound() -> yconf:validator().
+vcard_sound() ->
+ vcard_validator(
+ vcard_sound, undefined,
+ [{phonetic, undefined, binary()},
+ {binval, undefined, binary()},
+ {extval, undefined, binary()}]).
+
+-spec vcard_key() -> yconv:validator().
+vcard_key() ->
+ vcard_validator(
+ vcard_key, undefined,
+ [{type, undefined, binary()},
+ {cred, undefined, binary()}]).
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
@@ -546,3 +698,20 @@ format_addr_port({IP, Port}) ->
-spec format(iolist(), list()) -> string().
format(Fmt, Args) ->
lists:flatten(io_lib:format(Fmt, Args)).
+
+-spec vcard_validator(atom(), term(), [{atom(), term(), validator()}]) -> validator().
+vcard_validator(Name, Default, Schema) ->
+ Defaults = [{Key, Val} || {Key, Val, _} <- Schema],
+ and_then(
+ options(
+ maps:from_list([{Key, Fun} || {Key, _, Fun} <- Schema]),
+ [{return, map}, {unique, true}]),
+ fun(Options) ->
+ merge(Defaults, Options, Name, Default)
+ end).
+
+-spec merge([{atom(), term()}], #{atom() => term()}, atom(), T) -> tuple() | T.
+merge(_, Options, _, Default) when Options == #{} ->
+ Default;
+merge(Defaults, Options, Name, _) ->
+ list_to_tuple([Name|[maps:get(Key, Options, Val) || {Key, Val} <- Defaults]]).
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index a3414b3b1..2bf3e0fcc 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -196,7 +196,9 @@ mod_opt_type(external_secret) ->
mod_opt_type(host) ->
econf:host();
mod_opt_type(hosts) ->
- econf:hosts().
+ econf:hosts();
+mod_opt_type(vcard) ->
+ econf:vcard_temp().
-spec mod_options(binary()) -> [{thumbnail, boolean()} |
{atom(), any()}].
@@ -204,6 +206,7 @@ mod_options(Host) ->
[{host, <<"upload.", Host/binary>>},
{hosts, []},
{name, ?T("HTTP File Upload")},
+ {vcard, undefined},
{access, local},
{max_size, 104857600},
{secret_length, 40},
@@ -517,6 +520,18 @@ process_iq(#iq{type = get, lang = Lang, sub_els = [#disco_info{}]} = IQ,
xmpp:make_iq_result(IQ, iq_disco_info(ServerHost, Lang, Name, AddInfo));
process_iq(#iq{type = get, sub_els = [#disco_items{}]} = IQ, _State) ->
xmpp:make_iq_result(IQ, #disco_items{});
+process_iq(#iq{type = get, sub_els = [#vcard_temp{}], lang = Lang} = IQ,
+ #state{server_host = ServerHost}) ->
+ VCard = case mod_http_upload_opt:vcard(ServerHost) of
+ undefined ->
+ #vcard_temp{fn = <<"ejabberd/mod_http_upload">>,
+ url = ejabberd_config:get_uri(),
+ desc = misc:get_descr(
+ Lang, ?T("ejabberd HTTP Upload service"))};
+ V ->
+ V
+ end,
+ xmpp:make_iq_result(IQ, VCard);
process_iq(#iq{type = get, sub_els = [#upload_request{filename = File,
size = Size,
'content-type' = CType,
@@ -736,6 +751,7 @@ iq_disco_info(Host, Lang, Name, AddInfo) ->
features = [?NS_HTTP_UPLOAD,
?NS_HTTP_UPLOAD_0,
?NS_HTTP_UPLOAD_OLD,
+ ?NS_VCARD,
?NS_DISCO_INFO,
?NS_DISCO_ITEMS],
xdata = Form}.
diff --git a/src/mod_http_upload_opt.erl b/src/mod_http_upload_opt.erl
index 9c35b3c02..8590a38a1 100644
--- a/src/mod_http_upload_opt.erl
+++ b/src/mod_http_upload_opt.erl
@@ -20,6 +20,7 @@
-export([secret_length/1]).
-export([service_url/1]).
-export([thumbnail/1]).
+-export([vcard/1]).
-spec access(gen_mod:opts() | global | binary()) -> 'local' | acl:acl().
access(Opts) when is_map(Opts) ->
@@ -123,3 +124,9 @@ thumbnail(Opts) when is_map(Opts) ->
thumbnail(Host) ->
gen_mod:get_module_opt(Host, mod_http_upload, thumbnail).
+-spec vcard(gen_mod:opts() | global | binary()) -> 'undefined' | tuple().
+vcard(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(vcard, Opts);
+vcard(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, vcard).
+
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index f75981736..d50e6552e 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -556,11 +556,17 @@ route_to_room(Packet, ServerHost) ->
end.
-spec process_vcard(iq()) -> iq().
-process_vcard(#iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]} = IQ) ->
- xmpp:make_iq_result(
- IQ, #vcard_temp{fn = <<"ejabberd/mod_muc">>,
- url = ejabberd_config:get_uri(),
- desc = misc:get_descr(Lang, ?T("ejabberd MUC module"))});
+process_vcard(#iq{type = get, to = To, lang = Lang, sub_els = [#vcard_temp{}]} = IQ) ->
+ ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
+ VCard = case mod_muc_opt:vcard(ServerHost) of
+ undefined ->
+ #vcard_temp{fn = <<"ejabberd/mod_muc">>,
+ url = ejabberd_config:get_uri(),
+ desc = misc:get_descr(Lang, ?T("ejabberd MUC module"))};
+ V ->
+ V
+ end,
+ xmpp:make_iq_result(IQ, VCard);
process_vcard(#iq{type = set, lang = Lang} = IQ) ->
Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
@@ -1182,7 +1188,9 @@ mod_opt_type(hosts) ->
mod_opt_type(queue_type) ->
econf:queue_type();
mod_opt_type(hibernation_timeout) ->
- econf:timeout(second, infinity).
+ econf:timeout(second, infinity);
+mod_opt_type(vcard) ->
+ econf:vcard_temp().
mod_options(Host) ->
[{access, all},
@@ -1214,6 +1222,7 @@ mod_options(Host) ->
{user_presence_shaper, none},
{preload_rooms, true},
{hibernation_timeout, infinity},
+ {vcard, undefined},
{default_room_options,
[{allow_change_subj,true},
{allow_private_messages,true},
diff --git a/src/mod_muc_opt.erl b/src/mod_muc_opt.erl
index 2e2bbc945..25bd4dc5f 100644
--- a/src/mod_muc_opt.erl
+++ b/src/mod_muc_opt.erl
@@ -33,6 +33,7 @@
-export([room_shaper/1]).
-export([user_message_shaper/1]).
-export([user_presence_shaper/1]).
+-export([vcard/1]).
-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
access(Opts) when is_map(Opts) ->
@@ -214,3 +215,9 @@ user_presence_shaper(Opts) when is_map(Opts) ->
user_presence_shaper(Host) ->
gen_mod:get_module_opt(Host, mod_muc, user_presence_shaper).
+-spec vcard(gen_mod:opts() | global | binary()) -> 'undefined' | tuple().
+vcard(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(vcard, Opts);
+vcard(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, vcard).
+
diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl
index 280484763..8652188ad 100644
--- a/src/mod_multicast.erl
+++ b/src/mod_multicast.erl
@@ -273,8 +273,8 @@ process_iq(#iq{type = get, lang = Lang, from = From,
{result, iq_disco_info(From, Lang, State)};
process_iq(#iq{type = get, sub_els = [#disco_items{}]}, _) ->
{result, #disco_items{}};
-process_iq(#iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]}, _) ->
- {result, iq_vcard(Lang)};
+process_iq(#iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]}, State) ->
+ {result, iq_vcard(Lang, State)};
process_iq(#iq{type = T}, _) when T == set; T == get ->
{error, xmpp:err_service_unavailable()};
process_iq(_, _) ->
@@ -291,10 +291,16 @@ iq_disco_info(From, Lang, State) ->
features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_VCARD, ?NS_ADDRESS],
xdata = iq_disco_info_extras(From, State)}.
-iq_vcard(Lang) ->
- #vcard_temp{fn = <<"ejabberd/mod_multicast">>,
- url = ejabberd_config:get_uri(),
- desc = misc:get_descr(Lang, ?T("ejabberd Multicast service"))}.
+-spec iq_vcard(binary(), state()) -> #vcard_temp{}.
+iq_vcard(Lang, State) ->
+ case mod_multicast_opt:vcard(State#state.lserver) of
+ undefined ->
+ #vcard_temp{fn = <<"ejabberd/mod_multicast">>,
+ url = ejabberd_config:get_uri(),
+ desc = misc:get_descr(Lang, ?T("ejabberd Multicast service"))};
+ VCard ->
+ VCard
+ end.
%%%-------------------------
%%% Route
@@ -1142,11 +1148,14 @@ mod_opt_type(limits) ->
mod_opt_type(host) ->
econf:host();
mod_opt_type(hosts) ->
- econf:hosts().
+ econf:hosts();
+mod_opt_type(vcard) ->
+ econf:vcard_temp().
mod_options(Host) ->
[{access, all},
{host, <<"multicast.", Host/binary>>},
{hosts, []},
{limits, [{local, []}, {remote, []}]},
+ {vcard, undefined},
{name, ?T("Multicast")}].
diff --git a/src/mod_multicast_opt.erl b/src/mod_multicast_opt.erl
index f149d1ddc..bdf709803 100644
--- a/src/mod_multicast_opt.erl
+++ b/src/mod_multicast_opt.erl
@@ -8,6 +8,7 @@
-export([hosts/1]).
-export([limits/1]).
-export([name/1]).
+-export([vcard/1]).
-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
access(Opts) when is_map(Opts) ->
@@ -39,3 +40,9 @@ name(Opts) when is_map(Opts) ->
name(Host) ->
gen_mod:get_module_opt(Host, mod_multicast, name).
+-spec vcard(gen_mod:opts() | global | binary()) -> 'undefined' | tuple().
+vcard(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(vcard, Opts);
+vcard(Host) ->
+ gen_mod:get_module_opt(Host, mod_multicast, vcard).
+
diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl
index bd8cdde66..61e09439d 100644
--- a/src/mod_proxy65.erl
+++ b/src/mod_proxy65.erl
@@ -121,7 +121,9 @@ mod_opt_type(recbuf) ->
mod_opt_type(shaper) ->
econf:shaper();
mod_opt_type(sndbuf) ->
- econf:pos_int().
+ econf:pos_int();
+mod_opt_type(vcard) ->
+ econf:vcard_temp().
mod_options(Host) ->
[{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
@@ -132,6 +134,7 @@ mod_options(Host) ->
{ip, undefined},
{port, 7777},
{name, ?T("SOCKS5 Bytestreams")},
+ {vcard, undefined},
{max_connections, infinity},
{auth_type, anonymous},
{recbuf, 65536},
diff --git a/src/mod_proxy65_opt.erl b/src/mod_proxy65_opt.erl
index d65e74d16..95f039b16 100644
--- a/src/mod_proxy65_opt.erl
+++ b/src/mod_proxy65_opt.erl
@@ -17,6 +17,7 @@
-export([server_host/1]).
-export([shaper/1]).
-export([sndbuf/1]).
+-export([vcard/1]).
-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
access(Opts) when is_map(Opts) ->
@@ -102,3 +103,9 @@ sndbuf(Opts) when is_map(Opts) ->
sndbuf(Host) ->
gen_mod:get_module_opt(Host, mod_proxy65, sndbuf).
+-spec vcard(gen_mod:opts() | global | binary()) -> 'undefined' | tuple().
+vcard(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(vcard, Opts);
+vcard(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, vcard).
+
diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl
index bac3911fe..31344d27e 100644
--- a/src/mod_proxy65_service.erl
+++ b/src/mod_proxy65_service.erl
@@ -171,11 +171,18 @@ process_disco_items(#iq{type = get} = IQ) ->
process_vcard(#iq{type = set, lang = Lang} = IQ) ->
Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
-process_vcard(#iq{type = get, lang = Lang} = IQ) ->
- xmpp:make_iq_result(
- IQ, #vcard_temp{fn = <<"ejabberd/mod_proxy65">>,
- url = ejabberd_config:get_uri(),
- desc = misc:get_descr(Lang, ?T("ejabberd SOCKS5 Bytestreams module"))}).
+process_vcard(#iq{type = get, to = To, lang = Lang} = IQ) ->
+ ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
+ VCard = case mod_proxy65_opt:vcard(ServerHost) of
+ undefined ->
+ #vcard_temp{fn = <<"ejabberd/mod_proxy65">>,
+ url = ejabberd_config:get_uri(),
+ desc = misc:get_descr(
+ Lang, ?T("ejabberd SOCKS5 Bytestreams module"))};
+ V ->
+ V
+ end,
+ xmpp:make_iq_result(IQ, VCard).
-spec process_bytestreams(iq()) -> iq().
process_bytestreams(#iq{type = get, from = JID, to = To, lang = Lang} = IQ) ->
diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl
index f1f1dbd51..92379a81f 100644
--- a/src/mod_pubsub.erl
+++ b/src/mod_pubsub.erl
@@ -878,8 +878,9 @@ process_pubsub_owner(#iq{to = To} = IQ) ->
end.
-spec process_vcard(iq()) -> iq().
-process_vcard(#iq{type = get, lang = Lang} = IQ) ->
- xmpp:make_iq_result(IQ, iq_get_vcard(Lang));
+process_vcard(#iq{type = get, to = To, lang = Lang} = IQ) ->
+ ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
+ xmpp:make_iq_result(IQ, iq_get_vcard(ServerHost, Lang));
process_vcard(#iq{type = set, lang = Lang} = IQ) ->
Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)).
@@ -1100,12 +1101,17 @@ iq_sm(#iq{to = To, sub_els = [SubEl]} = IQ) ->
xmpp:make_error(IQ, Error)
end.
--spec iq_get_vcard(binary()) -> vcard_temp().
-iq_get_vcard(Lang) ->
- Desc = misc:get_descr(Lang, ?T("ejabberd Publish-Subscribe module")),
- #vcard_temp{fn = <<"ejabberd/mod_pubsub">>,
- url = ejabberd_config:get_uri(),
- desc = Desc}.
+-spec iq_get_vcard(binary(), binary()) -> vcard_temp().
+iq_get_vcard(ServerHost, Lang) ->
+ case mod_pubsub_opt:vcard(ServerHost) of
+ undefined ->
+ Desc = misc:get_descr(Lang, ?T("ejabberd Publish-Subscribe module")),
+ #vcard_temp{fn = <<"ejabberd/mod_pubsub">>,
+ url = ejabberd_config:get_uri(),
+ desc = Desc};
+ VCard ->
+ VCard
+ end.
-spec iq_pubsub(binary() | ljid(), atom(), iq()) ->
{result, pubsub()} | {error, stanza_error()}.
@@ -4152,7 +4158,9 @@ mod_opt_type(host) ->
mod_opt_type(hosts) ->
econf:hosts();
mod_opt_type(db_type) ->
- econf:db_type(?MODULE).
+ econf:db_type(?MODULE);
+mod_opt_type(vcard) ->
+ econf:vcard_temp().
mod_options(Host) ->
[{access_createnode, all},
@@ -4160,6 +4168,7 @@ mod_options(Host) ->
{host, <<"pubsub.", Host/binary>>},
{hosts, []},
{name, ?T("Publish-Subscribe")},
+ {vcard, undefined},
{ignore_pep_from_offline, true},
{last_item_cache, false},
{max_items_node, ?MAXITEMS},
diff --git a/src/mod_pubsub_opt.erl b/src/mod_pubsub_opt.erl
index a77130976..68ed7f960 100644
--- a/src/mod_pubsub_opt.erl
+++ b/src/mod_pubsub_opt.erl
@@ -17,6 +17,7 @@
-export([nodetree/1]).
-export([pep_mapping/1]).
-export([plugins/1]).
+-export([vcard/1]).
-spec access_createnode(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
access_createnode(Opts) when is_map(Opts) ->
@@ -102,3 +103,9 @@ plugins(Opts) when is_map(Opts) ->
plugins(Host) ->
gen_mod:get_module_opt(Host, mod_pubsub, plugins).
+-spec vcard(gen_mod:opts() | global | binary()) -> 'undefined' | tuple().
+vcard(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(vcard, Opts);
+vcard(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, vcard).
+
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index 41f04940a..16f2f9c7c 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -203,12 +203,18 @@ get_sm_features(Acc, _From, _To, Node, _Lang) ->
process_local_iq(#iq{type = set, lang = Lang} = IQ) ->
Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
-process_local_iq(#iq{type = get, lang = Lang} = IQ) ->
- xmpp:make_iq_result(
- IQ, #vcard_temp{fn = <<"ejabberd">>,
- url = ejabberd_config:get_uri(),
- desc = misc:get_descr(Lang, ?T("Erlang Jabber Server")),
- bday = <<"2002-11-16">>}).
+process_local_iq(#iq{type = get, to = To, lang = Lang} = IQ) ->
+ ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
+ VCard = case mod_vcard_opt:vcard(ServerHost) of
+ undefined ->
+ #vcard_temp{fn = <<"ejabberd">>,
+ url = ejabberd_config:get_uri(),
+ desc = misc:get_descr(Lang, ?T("Erlang Jabber Server")),
+ bday = <<"2002-11-16">>};
+ V ->
+ V
+ end,
+ xmpp:make_iq_result(IQ, VCard).
-spec process_sm_iq(iq()) -> iq().
process_sm_iq(#iq{type = set, lang = Lang, from = From} = IQ) ->
@@ -562,7 +568,9 @@ mod_opt_type(cache_size) ->
mod_opt_type(cache_missed) ->
econf:bool();
mod_opt_type(cache_life_time) ->
- econf:timeout(second, infinity).
+ econf:timeout(second, infinity);
+mod_opt_type(vcard) ->
+ econf:vcard_temp().
mod_options(Host) ->
[{allow_return_all, false},
@@ -571,6 +579,7 @@ mod_options(Host) ->
{matches, 30},
{search, false},
{name, ?T("vCard User Search")},
+ {vcard, undefined},
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{use_cache, ejabberd_option:use_cache(Host)},
{cache_size, ejabberd_option:cache_size(Host)},
diff --git a/src/mod_vcard_opt.erl b/src/mod_vcard_opt.erl
index 79be37a37..3a7cc7754 100644
--- a/src/mod_vcard_opt.erl
+++ b/src/mod_vcard_opt.erl
@@ -14,6 +14,7 @@
-export([name/1]).
-export([search/1]).
-export([use_cache/1]).
+-export([vcard/1]).
-spec allow_return_all(gen_mod:opts() | global | binary()) -> boolean().
allow_return_all(Opts) when is_map(Opts) ->
@@ -81,3 +82,9 @@ use_cache(Opts) when is_map(Opts) ->
use_cache(Host) ->
gen_mod:get_module_opt(Host, mod_vcard, use_cache).
+-spec vcard(gen_mod:opts() | global | binary()) -> 'undefined' | tuple().
+vcard(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(vcard, Opts);
+vcard(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, vcard).
+
diff --git a/tools/opt_types.sh b/tools/opt_types.sh
index dba1f6679..658357cb2 100755
--- a/tools/opt_types.sh
+++ b/tools/opt_types.sh
@@ -393,6 +393,9 @@ spec(host, 0, _, _) ->
erl_types:t_binary();
spec(hosts, 0, _, _) ->
erl_types:t_list(erl_types:t_binary());
+spec(vcard_temp, 0, _, _) ->
+ erl_types:t_sup([erl_types:t_atom(undefined),
+ erl_types:t_tuple()]);
spec(options, A, [Form|OForm], Mod) when A == 1; A == 2 ->
case erl_syntax:type(Form) of
map_expr ->