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 ->