]> granicus.if.org Git - ejabberd/commitdiff
Introduce 'vcard' option for the modules supporting vCards
authorEvgeny Khramtsov <ekhramtsov@process-one.net>
Fri, 2 Aug 2019 10:55:48 +0000 (13:55 +0300)
committerEvgeny Khramtsov <ekhramtsov@process-one.net>
Fri, 2 Aug 2019 10:59:42 +0000 (13:59 +0300)
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
  ...
```

15 files changed:
src/econf.erl
src/mod_http_upload.erl
src/mod_http_upload_opt.erl
src/mod_muc.erl
src/mod_muc_opt.erl
src/mod_multicast.erl
src/mod_multicast_opt.erl
src/mod_proxy65.erl
src/mod_proxy65_opt.erl
src/mod_proxy65_service.erl
src/mod_pubsub.erl
src/mod_pubsub_opt.erl
src/mod_vcard.erl
src/mod_vcard_opt.erl
tools/opt_types.sh

index ec127c4e18f7210202ae45aa1456f5249b9f5d2a..4b7ccd587313c9c06e8f855c5dd77e3fa01c2cb0 100644 (file)
@@ -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]]).
index a3414b3b1df1338aa8377b833bfabd192601883d..2bf3e0fccf07802c6f712961cbd2340f6ef44957 100644 (file)
@@ -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}.
index 9c35b3c02ab4968ab38aa818e95f228781fdb17c..8590a38a1341a2101d6fed124e0028473abaebeb 100644 (file)
@@ -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).
+
index f75981736151909cf6f5add190aee7aa4ce5d3bb..d50e6552e31db3655a24daf13a106601d204ca50 100644 (file)
@@ -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},
index 2e2bbc94520601bac6283eb453d7f1e47c0d116a..25bd4dc5f1a915d8b4a749b4901808a1a6fdcdb1 100644 (file)
@@ -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).
+
index 280484763a66ba07b14d313c7ebced494ca49b12..8652188adca08ac56b59cccd0462ffcb972a10c3 100644 (file)
@@ -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")}].
index f149d1ddcf927891a1e6defe3d397c19e4467ec4..bdf709803df82920e78c06bcadbe12f06fbf4c24 100644 (file)
@@ -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).
+
index bd8cdde660dd917d42f88b059f39f4a60ce575e2..61e09439df2eddab1e902bd99b6bea1614424933 100644 (file)
@@ -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},
index d65e74d16934b6269bfbab7a4581b231ee4cded7..95f039b1673089dc8bf4e53b6f726f0fc1aa6caa 100644 (file)
@@ -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).
+
index bac3911fe64c111f00566b5d161d091ba8bdec5c..31344d27e6f3861f29bcec0caa71dd2d74654462 100644 (file)
@@ -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) ->
index f1f1dbd514964429e4a447e986d2c08104ab192a..92379a81f59693fc350e79f6a94033238d0ab103 100644 (file)
@@ -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},
index a77130976bc1a4d2ba5d76d31be8ae4c11d833e7..68ed7f96097cac0fad41970652229a80e1931d32 100644 (file)
@@ -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).
+
index 41f04940a4842272571b180e1405a7161d9b557f..16f2f9c7cbe3fa2134667753d0a9220e2b7a58f9 100644 (file)
@@ -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)},
index 79be37a377999170f3b40653b84612f79958bbaa..3a7cc7754f3bd5ee74f0c33f2c7eaf18a56ad606 100644 (file)
@@ -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).
+
index dba1f667994effa9c5cbb70599f62c8784c8f22b..658357cb2812f5096c2eaf6b76f456244d915aaa 100755 (executable)
@@ -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 ->