aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 ->