aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2004-09-10 20:57:00 +0000
committerAlexey Shchepin <alexey@process-one.net>2004-09-10 20:57:00 +0000
commite0ede61e0ff46d72542ffd001f7b3e35f1184431 (patch)
tree2c4d64ec7d2b0565657e9250fab1c644f8794bc8
parent* doc/guide.tex: Fix (thanks to Sander Devrieze) (diff)
* tools/ejabberdctl: Added call to "exec" (thanks to Sergei
Golovan) * src/msgs/ru.msg: Updated (thanks to Sergei Golovan) * src/mod_vcard.erl: Support for searching of prefix substring and limiting of result items (thanks to Sergei Golovan) * src/mod_offline.erl: Support for message expiration (JEP-0023) (thanks to Sergei Golovan) * src/jlib.hrl: Added NS_EXPIRE macros (thanks to Sergei Golovan) * src/ejabberd_logger_h.erl: Added reopen_log/0 (thanks to Sergei Golovan) * src/ejabberd_ctl.erl: Added return codes, updated "reopen-log" command, added "delete-expired-messages" and "status" commands (thanks to Sergei Golovan) * doc/guide.tex: Updated (thanks to Sergei Golovan) SVN Revision: 264
-rw-r--r--ChangeLog27
-rw-r--r--doc/guide.tex4
-rw-r--r--src/ejabberd_ctl.erl130
-rw-r--r--src/ejabberd_logger_h.erl5
-rw-r--r--src/jlib.hrl1
-rw-r--r--src/mod_offline.erl119
-rw-r--r--src/mod_roster.erl16
-rw-r--r--src/mod_vcard.erl64
-rw-r--r--src/msgs/ru.msg4
-rwxr-xr-xtools/ejabberdctl2
10 files changed, 290 insertions, 82 deletions
diff --git a/ChangeLog b/ChangeLog
index fe4695784..490436747 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2004-09-10 Alexey Shchepin <alexey@sevcom.net>
+
+ * tools/ejabberdctl: Added call to "exec" (thanks to Sergei
+ Golovan)
+
+ * src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
+
+ * src/mod_vcard.erl: Support for searching of prefix substring and
+ limiting of result items (thanks to Sergei Golovan)
+
+ * src/mod_offline.erl: Support for message expiration (JEP-0023)
+ (thanks to Sergei Golovan)
+ * src/jlib.hrl: Added NS_EXPIRE macros (thanks to Sergei Golovan)
+
+ * src/ejabberd_logger_h.erl: Added reopen_log/0 (thanks to Sergei
+ Golovan)
+
+ * src/ejabberd_ctl.erl: Added return codes, updated "reopen-log"
+ command, added "delete-expired-messages" and "status" commands
+ (thanks to Sergei Golovan)
+
+ * doc/guide.tex: Updated (thanks to Sergei Golovan)
+
+2004-09-04 Alexey Shchepin <alexey@sevcom.net>
+
+ * src/mod_roster.erl: Removed useless transactions
+
2004-08-28 Alexey Shchepin <alexey@sevcom.net>
* doc/guide.tex: Fix (thanks to Sander Devrieze)
diff --git a/doc/guide.tex b/doc/guide.tex
index d8c758572..f656d9323 100644
--- a/doc/guide.tex
+++ b/doc/guide.tex
@@ -1131,6 +1131,8 @@ Options:
\titem{search} Specifies wheather search is enabled (value is \term{true}, default) or
disabled (value is \term{false}) by the service. If \term{search} is set to \term{false},
option \term{host} is ignored and service does not appear in Jabber Discovery items.
+\titem{matches} Limits the number of reported search results. If value is set to
+\term{infinity} then all search results are reported. Default value is \term{30}.
\end{description}
Example:
@@ -1138,7 +1140,7 @@ Example:
{modules,
[
...
- {mod_vcard, [{search, false}]}
+ {mod_vcard, [{search, false}, {matches, 20}]}
...
]}.
\end{verbatim}
diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl
index 36be47a04..1c1d33877 100644
--- a/src/ejabberd_ctl.erl
+++ b/src/ejabberd_ctl.erl
@@ -11,124 +11,166 @@
-export([start/0]).
+-define(STATUS_SUCCESS, 0).
+-define(STATUS_ERROR, 1).
+-define(STATUS_USAGE, 2).
+-define(STATUS_BADRPC, 3).
+
start() ->
case init:get_plain_arguments() of
[SNode | Args] ->
Node = list_to_atom(SNode),
- process(Node, Args);
+ Status = process(Node, Args),
+ halt(Status);
_ ->
- print_usage()
- end,
- halt().
+ print_usage(),
+ halt(?STATUS_USAGE)
+ end.
+
+process(Node, ["status"]) ->
+ case rpc:call(Node, init, get_status, []) of
+ {badrpc, Reason} ->
+ io:format("Can't get node ~p status: ~p~n",
+ [Node, Reason]),
+ ?STATUS_BADRPC;
+ {InternalStatus, ProvidedStatus} ->
+ io:format("Node ~p is ~p. Status: ~p~n",
+ [Node, InternalStatus, ProvidedStatus]),
+ ?STATUS_SUCCESS
+ end;
process(Node, ["stop"]) ->
case rpc:call(Node, init, stop, []) of
{badrpc, Reason} ->
io:format("Can't stop node ~p: ~p~n",
- [Node, Reason]);
+ [Node, Reason]),
+ ?STATUS_BADRPC;
_ ->
- ok
+ ?STATUS_SUCCESS
end;
process(Node, ["restart"]) ->
case rpc:call(Node, init, restart, []) of
{badrpc, Reason} ->
io:format("Can't restart node ~p: ~p~n",
- [Node, Reason]);
+ [Node, Reason]),
+ ?STATUS_BADRPC;
_ ->
- ok
+ ?STATUS_SUCCESS
end;
process(Node, ["reopen-log"]) ->
- {error_logger, Node} ! {emulator, noproc, reopen};
+ case rpc:call(Node, ejabberd_logger_h, reopen_log, []) of
+ {badrpc, Reason} ->
+ io:format("Can't reopen node ~p log: ~p~n",
+ [Node, Reason]),
+ ?STATUS_BADRPC;
+ _ ->
+ ?STATUS_SUCCESS
+ end;
process(Node, ["register", User, Password]) ->
case rpc:call(Node, ejabberd_auth, try_register, [User, Password]) of
{atomic, ok} ->
- ok;
+ ?STATUS_SUCCESS;
{atomic, exists} ->
io:format("User ~p already registered on node ~p~n",
- [User, Node]);
+ [User, Node]),
+ ?STATUS_ERROR;
{error, Reason} ->
io:format("Can't register user ~p on node ~p: ~p~n",
- [User, Node, Reason]);
+ [User, Node, Reason]),
+ ?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't register user ~p on node ~p: ~p~n",
- [User, Node, Reason])
+ [User, Node, Reason]),
+ ?STATUS_BADRPC
end;
process(Node, ["unregister", User]) ->
case rpc:call(Node, ejabberd_auth, remove_user, [User]) of
{atomic, ok} ->
- ok;
+ ?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't unregister user ~p on node ~p: ~p~n",
- [User, Node, Reason]);
+ [User, Node, Reason]),
+ ?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't unregister user ~p on node ~p: ~p~n",
- [User, Node, Reason])
+ [User, Node, Reason]),
+ ?STATUS_BADRPC
end;
process(Node, ["backup", Path]) ->
case rpc:call(Node, mnesia, backup, [Path]) of
ok ->
- ok;
+ ?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't store backup in ~p on node ~p: ~p~n",
- [Path, Node, Reason]);
+ [Path, Node, Reason]),
+ ?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't store backup in ~p on node ~p: ~p~n",
- [Path, Node, Reason])
+ [Path, Node, Reason]),
+ ?STATUS_BADRPC
end;
process(Node, ["dump", Path]) ->
case rpc:call(Node, mnesia, dump_to_textfile, [Path]) of
ok ->
- ok;
+ ?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't store dump in ~p on node ~p: ~p~n",
- [Path, Node, Reason]);
+ [Path, Node, Reason]),
+ ?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't store dump in ~p on node ~p: ~p~n",
- [Path, Node, Reason])
+ [Path, Node, Reason]),
+ ?STATUS_BADRPC
end;
process(Node, ["load", Path]) ->
case rpc:call(Node, mnesia, load_textfile, [Path]) of
ok ->
- ok;
+ ?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't load dump in ~p on node ~p: ~p~n",
- [Path, Node, Reason]);
+ [Path, Node, Reason]),
+ ?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't load dump in ~p on node ~p: ~p~n",
- [Path, Node, Reason])
+ [Path, Node, Reason]),
+ ?STATUS_BADRPC
end;
process(Node, ["restore", Path]) ->
case rpc:call(Node,
mnesia, restore, [Path, [{default_op, keep_tables}]]) of
{atomic, ok} ->
- ok;
+ ?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't restore backup from ~p on node ~p: ~p~n",
- [Path, Node, Reason]);
+ [Path, Node, Reason]),
+ ?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't restore backup from ~p on node ~p: ~p~n",
- [Path, Node, Reason])
+ [Path, Node, Reason]),
+ ?STATUS_BADRPC
end;
process(Node, ["install-fallback", Path]) ->
case rpc:call(Node, mnesia, install_fallback, [Path]) of
{atomic, ok} ->
- ok;
+ ?STATUS_SUCCESS;
{error, Reason} ->
io:format("Can't install fallback from ~p on node ~p: ~p~n",
- [Path, Node, Reason]);
+ [Path, Node, Reason]),
+ ?STATUS_ERROR;
{badrpc, Reason} ->
io:format("Can't install fallback from ~p on node ~p: ~p~n",
- [Path, Node, Reason])
+ [Path, Node, Reason]),
+ ?STATUS_BADRPC
end;
process(Node, ["registered-users"]) ->
@@ -138,14 +180,30 @@ process(Node, ["registered-users"]) ->
SUsers = lists:sort(Users),
FUsers = lists:map(fun(U) -> [U, NewLine] end, SUsers),
io:format("~s", [FUsers]),
- ok;
- {ErrorTag, Reason} when (ErrorTag == error) or (ErrorTag == badrpc) ->
+ ?STATUS_SUCCESS;
+ {error, Reason} ->
+ io:format("Can't get list of registered users on node ~p: ~p~n",
+ [Node, Reason]),
+ ?STATUS_ERROR;
+ {badrpc, Reason} ->
io:format("Can't get list of registered users on node ~p: ~p~n",
- [Node, Reason])
+ [Node, Reason]),
+ ?STATUS_BADRPC
+ end;
+
+process(Node, ["delete-expired-messages"]) ->
+ case rpc:call(Node, mod_offline, remove_expired_messages, []) of
+ {badrpc, Reason} ->
+ io:format("Can't delete expired messages at node ~p: ~p~n",
+ [Node, Reason]),
+ ?STATUS_BADRPC;
+ _ ->
+ ?STATUS_SUCCESS
end;
process(_Node, _Args) ->
- print_usage().
+ print_usage(),
+ ?STATUS_USAGE.
@@ -154,6 +212,7 @@ print_usage() ->
"Usage: ejabberdctl node command~n"
"~n"
"Available commands:~n"
+ " status\t\t\tget ejabberd status~n"
" stop\t\t\t\tstop ejabberd~n"
" restart\t\t\trestart ejabberd~n"
" reopen-log\t\t\treopen log file~n"
@@ -165,6 +224,7 @@ print_usage() ->
" dump file\t\t\tdump a database in a text file~n"
" load file\t\t\trestore a database from a text file~n"
" registered-users\t\tlist all registered users~n"
+ " delete-expired-messages\tdelete expired offline messages from database~n"
"~n"
"Example:~n"
" ejabberdctl ejabberd@host restart~n"
diff --git a/src/ejabberd_logger_h.erl b/src/ejabberd_logger_h.erl
index 8c18f7220..dff4553a2 100644
--- a/src/ejabberd_logger_h.erl
+++ b/src/ejabberd_logger_h.erl
@@ -15,7 +15,7 @@
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
- code_change/3]).
+ code_change/3, reopen_log/0]).
-record(state, {fd, file}).
@@ -89,6 +89,9 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
+reopen_log() ->
+ error_logger ! {emulator, noproc, reopen}.
+
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
diff --git a/src/jlib.hrl b/src/jlib.hrl
index c684a7f3b..1e0b31992 100644
--- a/src/jlib.hrl
+++ b/src/jlib.hrl
@@ -21,6 +21,7 @@
-define(NS_XDATA, "jabber:x:data").
-define(NS_IQDATA, "jabber:iq:data").
-define(NS_DELAY, "jabber:x:delay").
+-define(NS_EXPIRE, "jabber:x:expire").
-define(NS_EVENT, "jabber:x:event").
-define(NS_XCONFERENCE, "jabber:x:conference").
-define(NS_STATS, "http://jabber.org/protocol/stats").
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index 89ee0765c..6959dbcef 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -17,12 +17,14 @@
store_packet/3,
resend_offline_messages/1,
pop_offline_messages/2,
+ remove_expired_messages/0,
remove_old_messages/1,
remove_user/1]).
+-include("ejabberd.hrl").
-include("jlib.hrl").
--record(offline_msg, {user, timestamp, from, to, packet}).
+-record(offline_msg, {user, timestamp, expire, from, to, packet}).
-define(PROCNAME, ejabberd_offline).
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
@@ -32,6 +34,7 @@ start(_) ->
[{disc_only_copies, [node()]},
{type, bag},
{attributes, record_info(fields, offline_msg)}]),
+ update_table(),
ejabberd_hooks:add(offline_message_hook,
?MODULE, store_packet, 50),
ejabberd_hooks:add(offline_subscription_hook,
@@ -92,8 +95,11 @@ store_packet(From, To, Packet) ->
true ->
#jid{luser = LUser} = To,
TimeStamp = now(),
+ {xmlelement, _Name, _Attrs, Els} = Packet,
+ Expire = find_x_expire(TimeStamp, Els),
?PROCNAME ! #offline_msg{user = LUser,
timestamp = TimeStamp,
+ expire = Expire,
from = From,
to = To,
packet = Packet},
@@ -150,6 +156,34 @@ find_x_event([El | Els]) ->
find_x_event(Els)
end.
+find_x_expire(_, []) ->
+ never;
+find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
+ find_x_expire(TimeStamp, Els);
+find_x_expire(TimeStamp, [El | Els]) ->
+ case xml:get_tag_attr_s("xmlns", El) of
+ ?NS_EXPIRE ->
+ case xml:get_tag_attr_s("seconds", El) of
+ Val ->
+ case catch list_to_integer(Val) of
+ {'EXIT', _} ->
+ never;
+ Int when Int > 0 ->
+ {MegaSecs, Secs, MicroSecs} = TimeStamp,
+ S = MegaSecs * 1000000 + Secs + Int,
+ MegaSecs1 = S div 1000000,
+ Secs1 = S rem 1000000,
+ {MegaSecs1, Secs1, MicroSecs};
+ _ ->
+ never
+ end;
+ _ ->
+ never
+ end;
+ _ ->
+ find_x_expire(TimeStamp, Els)
+ end.
+
resend_offline_messages(User) ->
LUser = jlib:nodeprep(User),
@@ -187,23 +221,54 @@ pop_offline_messages(Ls, User) ->
end,
case mnesia:transaction(F) of
{atomic, Rs} ->
- lists:map(
- fun(R) ->
- {xmlelement, Name, Attrs, Els} = R#offline_msg.packet,
- {route,
- R#offline_msg.from,
- R#offline_msg.to,
- {xmlelement, Name, Attrs,
- Els ++
- [jlib:timestamp_to_xml(
- calendar:now_to_universal_time(
- R#offline_msg.timestamp))]}}
- end,
- Ls ++ lists:keysort(#offline_msg.timestamp, Rs));
+ TS = now(),
+ Ls ++ lists:map(
+ fun(R) ->
+ {xmlelement, Name, Attrs, Els} = R#offline_msg.packet,
+ {route,
+ R#offline_msg.from,
+ R#offline_msg.to,
+ {xmlelement, Name, Attrs,
+ Els ++
+ [jlib:timestamp_to_xml(
+ calendar:now_to_universal_time(
+ R#offline_msg.timestamp))]}}
+ end,
+ lists:filter(
+ fun(R) ->
+ case R#offline_msg.expire of
+ never ->
+ true;
+ TimeStamp ->
+ TS < TimeStamp
+ end
+ end,
+ lists:keysort(#offline_msg.timestamp, Rs)));
_ ->
Ls
end.
+remove_expired_messages() ->
+ TimeStamp = now(),
+ F = fun() ->
+ mnesia:write_lock_table(offline_msg),
+ mnesia:foldl(
+ fun(Rec, _Acc) ->
+ case Rec#offline_msg.expire of
+ never ->
+ ok;
+ TS ->
+ if
+ TS < TimeStamp ->
+ mnesia:delete_object(Rec);
+ true ->
+ ok
+ end
+ end
+ end, ok, offline_msg)
+ end,
+ mnesia:transaction(F).
+
remove_old_messages(Days) ->
{MegaSecs, Secs, _MicroSecs} = now(),
S = MegaSecs * 1000000 + Secs - 60 * 60 * 24 * Days,
@@ -227,3 +292,29 @@ remove_user(User) ->
mnesia:delete({offline_msg, LUser})
end,
mnesia:transaction(F).
+
+update_table() ->
+ Fields = record_info(fields, offline_msg),
+ case mnesia:table_info(offline_msg, attributes) of
+ Fields ->
+ ok;
+ [user, timestamp, from, to, packet] ->
+ ?INFO_MSG("Converting offline_msg table from "
+ "{user, timestamp, from, to, packet} format", []),
+ mnesia:transform_table(
+ offline_msg,
+ fun({_, U, TS, F, T, P}) ->
+ {xmlelement, _Name, _Attrs, Els} = P,
+ Expire = find_x_expire(TS, Els),
+ #offline_msg{user = U,
+ timestamp = TS,
+ expire = Expire,
+ from = F,
+ to = T,
+ packet = P}
+ end, Fields);
+ _ ->
+ ?INFO_MSG("Recreating offline_msg table", []),
+ mnesia:transform_table(last_activity, ignore, Fields)
+ end.
+
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index 384bf13f3..b356b412e 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : mod_roster.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
-%%% Purpose :
+%%% Purpose : Roster management
%%% Created : 11 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
@@ -90,11 +90,8 @@ process_local_iq(From, To, #iq{type = Type} = IQ) ->
process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
#jid{luser = LUser} = From,
- F = fun() ->
- mnesia:index_read(roster, LUser, #roster.user)
- end,
- case mnesia:transaction(F) of
- {atomic, Items} ->
+ case catch mnesia:dirty_index_read(roster, LUser, #roster.user) of
+ Items when is_list(Items) ->
XItems = lists:map(fun item_to_xml/1, Items),
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@@ -316,11 +313,8 @@ push_item(User, Resource, From, Item) ->
get_subscription_lists(User) ->
LUser = jlib:nodeprep(User),
- F = fun() ->
- mnesia:index_read(roster, LUser, #roster.user)
- end,
- case mnesia:transaction(F) of
- {atomic, Items} ->
+ case mnesia:dirty_index_read(roster, LUser, #roster.user) of
+ Items when is_list(Items) ->
fill_subscription_lists(Items, [], []);
_ ->
{[], []}
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index e2257772a..854df2c0a 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -22,7 +22,8 @@
-include("jlib.hrl").
-%-define(JUD_ALLOW_RETURN_ALL, true)
+%-define(JUD_ALLOW_RETURN_ALL, true).
+-define(JUD_MATCHES, 30).
-record(vcard_search, {user, luser,
fn, lfn,
@@ -234,8 +235,10 @@ set_vcard(User, VCARD) ->
[{xmlcdata, translate:translate(Lang, "Search users in ") ++
jlib:jid_to_string(JID)}]},
{xmlelement, "instructions", [],
- [{xmlcdata, translate:translate(Lang, "Fill in fields to search "
- "for any matching Jabber User")}]},
+ [{xmlcdata, translate:translate(Lang, "Fill in the form to search "
+ "for any matching Jabber User "
+ "(Add * to the end of field to "
+ "match substring)")}]},
?TLFIELD("text-single", "User", "user"),
?TLFIELD("text-single", "Full Name", "fn"),
?TLFIELD("text-single", "Name", "given"),
@@ -452,7 +455,17 @@ search(Data) ->
?ERROR_MSG("~p", [Reason]),
[];
Rs ->
- Rs
+ case gen_mod:get_module_opt(?MODULE, matches, 30) of
+ infinity ->
+ Rs;
+ Val when is_integer(Val) and Val > 0 ->
+ lists:sublist(Rs, Val);
+ Val ->
+ ?ERROR_MSG("Illegal option value ~p. "
+ "Default value ~p substituted.",
+ [{matches, Val}, ?JUD_MATCHES]),
+ lists:sublist(Rs, ?JUD_MATCHES)
+ end
end.
-else.
@@ -469,7 +482,17 @@ search(Data) ->
?ERROR_MSG("~p", [Reason]),
[];
Rs ->
- Rs
+ case gen_mod:get_module_opt(?MODULE, matches, ?JUD_MATCHES) of
+ infinity ->
+ Rs;
+ Val when is_integer(Val) and (Val > 0) ->
+ lists:sublist(Rs, Val);
+ Val ->
+ ?ERROR_MSG("Illegal option value ~p. "
+ "Default value ~p substituted.",
+ [{matches, Val}, ?JUD_MATCHES]),
+ lists:sublist(Rs, ?JUD_MATCHES)
+ end
end
end.
@@ -499,24 +522,31 @@ filter_fields([{SVar, [Val]} | Ds], Match)
when is_list(Val) and (Val /= "") ->
LVal = stringprep:tolower(Val),
NewMatch = case SVar of
- "user" -> Match#vcard_search{luser = LVal};
- "fn" -> Match#vcard_search{lfn = LVal};
- "family" -> Match#vcard_search{lfamily = LVal};
- "given" -> Match#vcard_search{lgiven = LVal};
- "middle" -> Match#vcard_search{lmiddle = LVal};
- "nickname" -> Match#vcard_search{lnickname = LVal};
- "bday" -> Match#vcard_search{lbday = LVal};
- "ctry" -> Match#vcard_search{lctry = LVal};
- "locality" -> Match#vcard_search{llocality = LVal};
- "email" -> Match#vcard_search{lemail = LVal};
- "orgname" -> Match#vcard_search{lorgname = LVal};
- "orgunit" -> Match#vcard_search{lorgunit = LVal};
+ "user" -> Match#vcard_search{luser = make_val(LVal)};
+ "fn" -> Match#vcard_search{lfn = make_val(LVal)};
+ "family" -> Match#vcard_search{lfamily = make_val(LVal)};
+ "given" -> Match#vcard_search{lgiven = make_val(LVal)};
+ "middle" -> Match#vcard_search{lmiddle = make_val(LVal)};
+ "nickname" -> Match#vcard_search{lnickname = make_val(LVal)};
+ "bday" -> Match#vcard_search{lbday = make_val(LVal)};
+ "ctry" -> Match#vcard_search{lctry = make_val(LVal)};
+ "locality" -> Match#vcard_search{llocality = make_val(LVal)};
+ "email" -> Match#vcard_search{lemail = make_val(LVal)};
+ "orgname" -> Match#vcard_search{lorgname = make_val(LVal)};
+ "orgunit" -> Match#vcard_search{lorgunit = make_val(LVal)};
_ -> Match
end,
filter_fields(Ds, NewMatch);
filter_fields([_ | Ds], Match) ->
filter_fields(Ds, Match).
+make_val(Val) ->
+ case lists:suffix("*", Val) of
+ true ->
+ lists:sublist(Val, length(Val) - 1) ++ '_';
+ _ ->
+ Val
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/src/msgs/ru.msg b/src/msgs/ru.msg
index ede822d2c..551048060 100644
--- a/src/msgs/ru.msg
+++ b/src/msgs/ru.msg
@@ -74,8 +74,8 @@
{"You need an x:data capable client to search",
"Чтобы воспользоваться поиском, требуется x:data-совместимый клиент"}.
{"Search users in ", "Поиск пользователей в "}.
-{"Fill in fields to search for any matching Jabber User",
- "Заполните поля для поиска пользователя Jabber"}.
+{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)",
+"Заполните форму для поиска пользователя Jabber (Если добавить * в конец поля, то происходит поиск подстроки)"}.
{"Results of search in ", "Результаты поиска в "}.
{"User", "Пользователь"}.
diff --git a/tools/ejabberdctl b/tools/ejabberdctl
index 4fa0edf8f..48294cae9 100755
--- a/tools/ejabberdctl
+++ b/tools/ejabberdctl
@@ -1,4 +1,4 @@
#!/bin/sh
-erl -noinput -sname ejabberdctl -s ejabberd_ctl -extra $@
+exec erl -noinput -sname ejabberdctl -s ejabberd_ctl -extra $@