Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7248ee1dbc | |||
| 75b6162b44 | |||
| 75510a4b1b | |||
| 4a4f3eb8c5 | |||
| 8168526f01 | |||
| 09b4e36457 | |||
| dfd7936ea6 | |||
| 970f74288b | |||
| b8e9c6a7a2 | |||
| 6c08c53d74 | |||
| 82296c277a | |||
| 806d5497c4 | |||
| b0bf89e319 | |||
| 9d9a4d1bc7 | |||
| c55aa9be7b | |||
| 4d2408c9c9 | |||
| bd25bd465c | |||
| 6898553afd | |||
| a8dd91512c | |||
| 05d95f2dd7 | |||
| f839b1c6ff | |||
| b8026d8df7 | |||
| c611361bbe | |||
| 7fe05e67a7 | |||
| 0c7ce8b7fe | |||
| 34fc5d82b3 | |||
| 7436ef1fb1 | |||
| 099f7d6521 | |||
| 545162a283 | |||
| a2d60a1977 | |||
| 33745954a9 | |||
| 028d158e13 | |||
| b684e90292 | |||
| 191187a279 | |||
| 80483d8fd1 | |||
| 4689b0d861 | |||
| c73aeca393 | |||
| f893669d91 | |||
| 3c549bca4e | |||
| 198298df2c | |||
| 5a876dfd33 | |||
| 9cf43ffc9e | |||
| 8d2def619d | |||
| 25a6493e1b | |||
| 71bfa17354 | |||
| dedd2a7f59 | |||
| 415a50921f |
+1
-1
@@ -137,7 +137,7 @@ session manager, else it is processed depending on it's content.
|
||||
\subsection{Session Manager}
|
||||
|
||||
This module routes packets to local users. It searches for what user resource
|
||||
packet must be sended via presence table. If this resource is connected to
|
||||
packet must be sent via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.
|
||||
|
||||
|
||||
+29
-22
@@ -66,6 +66,7 @@
|
||||
\newcommand{\module}[1]{\texttt{#1}}
|
||||
\newcommand{\modadhoc}{\module{mod\_adhoc}}
|
||||
\newcommand{\modannounce}{\module{mod\_announce}}
|
||||
\newcommand{\modblocking}{\module{mod\_blocking}}
|
||||
\newcommand{\modcaps}{\module{mod\_caps}}
|
||||
\newcommand{\modconfigure}{\module{mod\_configure}}
|
||||
\newcommand{\moddisco}{\module{mod\_disco}}
|
||||
@@ -1017,7 +1018,7 @@ However, the c2s and s2s connections to the domain \term{example.com} use the fi
|
||||
\item Port 5269 listens for s2s connections with STARTTLS. The socket is set for IPv6 instead of IPv4.
|
||||
\item Port 3478 listens for STUN requests over UDP.
|
||||
\item Port 5280 listens for HTTP requests, and serves the HTTP Poll service.
|
||||
\item Port 5281 listens for HTTP requests, and serves the Web Admin using HTTPS as explained in
|
||||
\item Port 5281 listens for HTTP requests, using HTTPS to serve HTTP-Bind (BOSH) and the Web Admin as explained in
|
||||
section~\ref{webadmin}. The socket only listens connections to the IP address 127.0.0.1.
|
||||
\end{itemize}
|
||||
\begin{verbatim}
|
||||
@@ -1046,6 +1047,7 @@ However, the c2s and s2s connections to the domain \term{example.com} use the fi
|
||||
]},
|
||||
{{5281, "127.0.0.1"}, ejabberd_http, [
|
||||
web_admin,
|
||||
http_bind,
|
||||
tls, {certfile, "/etc/ejabberd/server.pem"},
|
||||
]}
|
||||
]
|
||||
@@ -1550,7 +1552,7 @@ declarations of ACLs in the configuration file have the following syntax:
|
||||
with a name that matches \term{UserRegexp} at any server that matches
|
||||
\term{ServerRegexp}. Example:
|
||||
\begin{verbatim}
|
||||
{acl, yohzik, {node_regexp, "^yohzik$", "^example.(com|org)$"}}.
|
||||
{acl, yozhik, {node_regexp, "^yozhik$", "^example.(com|org)$"}}.
|
||||
\end{verbatim}
|
||||
\titem{\{user\_glob, Glob\}}
|
||||
\titem{\{user\_glob, Glob, Server\}}
|
||||
@@ -1710,13 +1712,13 @@ The configurable options are:
|
||||
Full path to a script that generates the image.
|
||||
The default value is an empty string: \term{""}
|
||||
\titem{\{captcha\_host, ProtocolHostPort\}}
|
||||
Host part of the URL sent to the user,
|
||||
and the port number where ejabberd listens for CAPTCHA requests.
|
||||
The URL sent to the user is formed by: \term{http://Host:Port/captcha/}
|
||||
The default value is: the first hostname configured, and port 5280.
|
||||
If the port number you specify does not match exactly an ejabberd listener
|
||||
ProtocolHostPort is a string with the host, and optionally the Protocol and Port number.
|
||||
It must identify where ejabberd listens for CAPTCHA requests.
|
||||
The URL sent to the user is formed by: \term{Protocol://Host:Port/captcha/}
|
||||
The default value is: protocol \term{http}, the first hostname configured, and port \term{80}.
|
||||
If you specify a port number that does not match exactly an ejabberd listener
|
||||
(because you are using a reverse proxy or other port-forwarding tool),
|
||||
then specify also the transfer protocol, as seen in the example below.
|
||||
then you must specify the transfer protocol, as seen in the example below.
|
||||
\end{description}
|
||||
|
||||
Additionally, an \term{ejabberd\_http} listener must be enabled with the \term{captcha} option.
|
||||
@@ -1729,6 +1731,7 @@ Example configuration:
|
||||
{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}.
|
||||
{captcha_host, "example.org:5280"}.
|
||||
%% {captcha_host, "https://example.org:443"}.
|
||||
%% {captcha_host, "http://example.com"}.
|
||||
|
||||
{listen,
|
||||
[
|
||||
@@ -1943,6 +1946,7 @@ The following LDAP servers are tested with \ejabberd{}:
|
||||
\item \footahref{http://www.microsoft.com/activedirectory/}{Active Directory}
|
||||
(see section~\ref{ad})
|
||||
\item \footahref{http://www.openldap.org/}{OpenLDAP}
|
||||
\item \footahref{http://www.communigate.com/}{CommuniGate Pro}
|
||||
\item Normally any LDAP compatible server should work; inform us about your
|
||||
success with a not-listed server so that we can list it here.
|
||||
\end{itemize}
|
||||
@@ -2579,6 +2583,7 @@ The following table lists all modules included in \ejabberd{}.
|
||||
\hline
|
||||
\hline \modadhoc{} & Ad-Hoc Commands (\xepref{0050}) & \\
|
||||
\hline \ahrefloc{modannounce}{\modannounce{}} & Manage announcements & recommends \modadhoc{} \\
|
||||
\hline \modblocking{} & Simple Communications Blocking (\xepref{0191}) & \modprivacy{} \\
|
||||
\hline \modcaps{} & Entity Capabilities (\xepref{0115}) & \\
|
||||
\hline \modconfigure{} & Server configuration using Ad-Hoc & \modadhoc{} \\
|
||||
\hline \ahrefloc{moddisco}{\moddisco{}} & Service Discovery (\xepref{0030}) & \\
|
||||
@@ -2666,15 +2671,16 @@ The syntax is:
|
||||
Possible \term{Value} are:
|
||||
\begin{description}
|
||||
\titem{no\_queue} All queries of a namespace with this processing discipline are
|
||||
processed immediately. This also means that no other packets can be processed
|
||||
processed directly. This means that the XMPP connection that sends this IQ query gets blocked:
|
||||
no other packets can be processed
|
||||
until this one has been completely processed. Hence this discipline is not
|
||||
recommended if the processing of a query can take a relatively long time.
|
||||
\titem{one\_queue} In this case a separate queue is created for the processing
|
||||
of IQ queries of a namespace with this discipline. In addition, the processing
|
||||
of this queue is done in parallel with that of other packets. This discipline
|
||||
is most recommended.
|
||||
\titem{\{queues, N\}} N separate queues are created to process the
|
||||
queries. The queries are thus process in parallel, but in a
|
||||
\titem{\{queues, N\}} N separate queues are created to process the
|
||||
queries. The queries are thus processed in parallel, but in a
|
||||
controlled way.
|
||||
\titem{parallel} For every packet with this discipline a separate Erlang process
|
||||
is spawned. Consequently, all these packets are processed in parallel.
|
||||
@@ -3836,10 +3842,11 @@ enables end users to use a \XMPP{} client to:
|
||||
|
||||
Options:
|
||||
\begin{description}
|
||||
\titem{\{access, AccessName\}} \ind{options!access}This option can be configured to specify
|
||||
rules to restrict registration. If a rule returns `deny' on the requested
|
||||
user name, registration for that user name is denied. (there are no
|
||||
restrictions by default).
|
||||
\titem{\{access, AccessName\}} \ind{options!access}
|
||||
Specify rules to restrict what usernames can be registered and unregistered.
|
||||
If a rule returns `deny' on the requested username,
|
||||
registration and unregistration of that user name is denied.
|
||||
There are no restrictions by default.
|
||||
\titem{\{access\_from, AccessName\}} \ind{options!access\_from}By default, \ejabberd{}
|
||||
doesn't allow to register new accounts from s2s or existing c2s sessions. You can
|
||||
change it by defining access rule in this option. Use with care: allowing registration
|
||||
@@ -4664,7 +4671,7 @@ Examples:
|
||||
\makesubsection{modvcardldap}{\modvcardldap{}}
|
||||
\ind{modules!\modvcardldap{}}\ind{JUD}\ind{Jabber User Directory}\ind{vCard}\ind{protocols!XEP-0054: vcard-temp}
|
||||
|
||||
%TODO: verify if the referers to the LDAP section are still correct
|
||||
%TODO: verify if the referrers to the LDAP section are still correct
|
||||
|
||||
\ejabberd{} can map LDAP attributes to vCard fields. This behaviour is
|
||||
implemented in the \modvcardldap{} module. This module does not depend on the
|
||||
@@ -5016,6 +5023,8 @@ The environment variables:
|
||||
This path is used to read the file \term{.erlang.cookie}.
|
||||
\titem{ERL\_CRASH\_DUMP}
|
||||
Path to the file where crash reports will be dumped.
|
||||
\titem{ERL\_EPMD\_ADDRESS}
|
||||
IP address where epmd listens for connections (see section \ref{epmd}).
|
||||
\titem{ERL\_INETRC}
|
||||
Indicates which IP name resolution to use.
|
||||
If using \term{-sname}, specify either this option or \term{-kernel inetrc filepath}.
|
||||
@@ -5044,10 +5053,10 @@ The command line parameters:
|
||||
connections (see section \ref{epmd}).
|
||||
\titem{-detached}
|
||||
Starts the Erlang system detached from the system console.
|
||||
Useful for running daemons and backgrounds processes.
|
||||
Useful for running daemons and background processes.
|
||||
\titem{-noinput}
|
||||
Ensures that the Erlang system never tries to read any input.
|
||||
Useful for running daemons and backgrounds processes.
|
||||
Useful for running daemons and background processes.
|
||||
\titem{-pa /var/lib/ejabberd/ebin}
|
||||
Specify the directory where Erlang binary files (*.beam) are located.
|
||||
\titem{-s ejabberd}
|
||||
@@ -5437,6 +5446,8 @@ In order for this communication to work,
|
||||
\term{epmd} must be running and listening for name requests in the port 4369.
|
||||
You should block the port 4369 in the firewall in such a way that
|
||||
only the programs in your machine can access it.
|
||||
or configure the option \term{ERL\_EPMD\_ADDRESS} in the file \term{ejabberdctl.cfg}
|
||||
(this option works only in Erlang/OTP R14B03 or higher).
|
||||
|
||||
If you build a cluster of several \ejabberd{} instances,
|
||||
each \ejabberd{} instance is called an \ejabberd{} node.
|
||||
@@ -5844,10 +5855,6 @@ The Web Admin also supports the \verb|Accept-Language| HTTP header.
|
||||
\end{figure}
|
||||
|
||||
|
||||
%\section{Ultra Complex Example}
|
||||
%\label{ultracomplexexample}
|
||||
%TODO: a very big example covering the whole guide, with a good explanation before the example: different authenticaton mechanisms, transports, ACLs, multple virtual hosts, virtual host specific settings and general settings, modules,...
|
||||
|
||||
\makechapter{releasenotes}{Release Notes}
|
||||
\ind{release notes}
|
||||
|
||||
|
||||
@@ -196,6 +196,7 @@ install: all
|
||||
sed -e "s*@ctlscriptpath@*$(SBINDIR)*" \
|
||||
-e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \
|
||||
> ejabberd.init
|
||||
chmod 755 ejabberd.init
|
||||
#
|
||||
# Binary Erlang files
|
||||
install -d $(BEAMDIR)
|
||||
|
||||
+5
-4
@@ -204,7 +204,7 @@ server_new(Service, ServerFQDN, UserRealm, _SecFlags,
|
||||
%% Continue = {continue, ServerOut, New_State}
|
||||
%% ServerOut = string()
|
||||
%% New_State = saslstate()
|
||||
%% Error = {error, Reason} | {error, Username, Reason}
|
||||
%% Error = {error, Reason} | {error, Reason, Username}
|
||||
%% Reason = term()
|
||||
%% Username = string()
|
||||
|
||||
@@ -236,8 +236,9 @@ server_start(State, Mech, ClientIn) ->
|
||||
%% Continue = {continue, ServerOut, New_State}
|
||||
%% ServerOut = string()
|
||||
%% New_State = saslstate()
|
||||
%% Error = {error, Reason} | {error, Username, Reason}
|
||||
%% Error = {error, Reason} | {error, Reason, Text, Username}
|
||||
%% Reason = term()
|
||||
%% Text = string()
|
||||
%% Username = string()
|
||||
|
||||
server_step(State, ClientIn) ->
|
||||
@@ -254,8 +255,8 @@ server_step(State, ClientIn) ->
|
||||
{continue, ServerOut, NewMechState} ->
|
||||
{continue, ServerOut,
|
||||
State#sasl_state{mech_state = NewMechState}};
|
||||
{error, Error, Username} ->
|
||||
{error, Error, Username};
|
||||
{error, Error, Text, Username} ->
|
||||
{error, Error, Text, Username};
|
||||
{error, Error} ->
|
||||
{error, Error}
|
||||
end.
|
||||
|
||||
@@ -80,7 +80,7 @@ mech_new(#sasl_params{host=Host, get_password=GetPassword,
|
||||
%% Continue = {continue, ServerOut, New_State}
|
||||
%% ServerOut = string()
|
||||
%% New_State = mechstate()
|
||||
%% Error = {error, Reason} | {error, Reason, Username}
|
||||
%% Error = {error, Reason} | {error, Reason, Text, Username}
|
||||
%% Reason = term()
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
@@ -99,12 +99,12 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
false ->
|
||||
?DEBUG("User login not authorized because digest-uri "
|
||||
"seems invalid: ~p", [DigestURI]),
|
||||
{error, 'not-authorized', UserName};
|
||||
{error, 'not-authorized', "", UserName};
|
||||
true ->
|
||||
AuthzId = proplists:get_value("authzid", KeyVals, ""),
|
||||
case (State#state.get_password)(UserName) of
|
||||
{false, _} ->
|
||||
{error, 'not-authorized', UserName};
|
||||
{error, 'not-authorized', "", UserName};
|
||||
{Passwd, AuthModule} ->
|
||||
case (State#state.check_password)(UserName, "",
|
||||
proplists:get_value("response", KeyVals, ""),
|
||||
@@ -121,9 +121,9 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
false ->
|
||||
{error, 'not-authorized', UserName};
|
||||
{error, 'not-authorized', "", UserName};
|
||||
{false, _} ->
|
||||
{error, 'not-authorized', UserName}
|
||||
{error, 'not-authorized', "", UserName}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -128,13 +128,13 @@ do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
|
||||
{needsmore, RspAuth} ->
|
||||
?DEBUG("needsmore~n", []),
|
||||
if (Step > 0) and (ClientIn =:= []) and (RspAuth =:= <<>>) ->
|
||||
{error, "not-authorized"};
|
||||
{error, 'not-authorized'};
|
||||
true ->
|
||||
{continue, binary_to_list(RspAuth),
|
||||
State#state{step=Step+1}}
|
||||
end;
|
||||
{error, _} ->
|
||||
{error, "not-authorized"}
|
||||
{error, 'not-authorized'}
|
||||
end.
|
||||
|
||||
handle_step_ok(State, []) ->
|
||||
@@ -147,7 +147,7 @@ check_user(#state{authid=Authid,authzid=Authzid,
|
||||
authrealm=Auth_realm,host=Host,realm=Realm}) ->
|
||||
if Realm =/= Auth_realm ->
|
||||
?DEBUG("bad realm ~p (expected ~p)~n",[Auth_realm, Realm]),
|
||||
throw({error, "not-authorized"});
|
||||
throw({error, 'not-authorized'});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
@@ -155,7 +155,7 @@ check_user(#state{authid=Authid,authzid=Authzid,
|
||||
case ejabberd_auth:is_user_exists(Authid, Host) of
|
||||
false ->
|
||||
?DEBUG("bad user ~p~n",[Authid]),
|
||||
throw({error, "not-authorized"});
|
||||
throw({error, 'not-authorized'});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
|
||||
@@ -62,8 +62,7 @@ mech_new(#sasl_params{check_password = CheckPassword}) ->
|
||||
%% Username = string()
|
||||
%% AuthzId = string()
|
||||
%% AuthModule = atom()
|
||||
%% Error = {error, Reason} | {error, Reason, Username}
|
||||
%% Reason = term()
|
||||
%% Error = {error, Reason} | {error, Reason, Text, Username}
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case prepare(ClientIn) of
|
||||
@@ -73,9 +72,9 @@ mech_step(State, ClientIn) ->
|
||||
{ok, [{username, User}, {authzid, AuthzId},
|
||||
{auth_module, AuthModule}]};
|
||||
{false, ReasonAuthFail} when is_list(ReasonAuthFail) ->
|
||||
{error, ReasonAuthFail, User};
|
||||
{error, 'not-authorized', ReasonAuthFail, User};
|
||||
_ ->
|
||||
{error, 'not-authorized', User}
|
||||
{error, 'not-authorized', "", User}
|
||||
end;
|
||||
_ ->
|
||||
{error, 'bad-protocol'}
|
||||
|
||||
@@ -513,6 +513,7 @@
|
||||
[
|
||||
{mod_adhoc, []},
|
||||
{mod_announce, [{access, announce}]}, % recommends mod_adhoc
|
||||
{mod_blocking,[]}, % requires mod_privacy
|
||||
{mod_caps, []}, % 1 proc/host
|
||||
{mod_configure,[]}, % requires mod_adhoc
|
||||
{mod_disco, []},
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
#! /bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: ejabberd
|
||||
# Required-Start: $remote_fs $network $named $time
|
||||
# Required-Stop: $remote_fs $network $named $time
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Starts ejabberd jabber server
|
||||
# Description: Starts ejabberd jabber server, an XMPP
|
||||
# compliant server written in Erlang.
|
||||
### END INIT INFO
|
||||
|
||||
# chkconfig: 2345 90 10
|
||||
# description: ejabberd XMPP server
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
DIR=@ctlscriptpath@
|
||||
CTL="$DIR"/ejabberdctl
|
||||
@@ -32,14 +46,17 @@ case "$1" in
|
||||
su - $USER -c "$CTL stopped"
|
||||
echo "done."
|
||||
;;
|
||||
|
||||
status)
|
||||
test -x "$CTL" || exit 0
|
||||
echo "Getting ejabberd status..."
|
||||
su - $USER -c "$CTL status"
|
||||
;;
|
||||
force-reload|restart)
|
||||
"$0" stop
|
||||
"$0" start
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|force-reload}"
|
||||
echo "Usage: $0 {start|stop|restart|force-reload|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
|
||||
@@ -141,7 +141,8 @@ check_password(User, Server, Password, Digest, DigestGen)
|
||||
%% {true, AuthModule} | {false, Reason::string()}
|
||||
%% where
|
||||
%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external
|
||||
%% | ejabberd_auth_ldap | ejabberd_auth_pam | ejabberd_auth_storage
|
||||
%% | ejabberd_auth_internal | ejabberd_auth_ldap
|
||||
%% | ejabberd_auth_odbc | ejabberd_auth_pam
|
||||
%% @doc Check if the user and password can login in server.
|
||||
%% The user can login if at least an authentication method accepts the user
|
||||
%% and the password.
|
||||
|
||||
+49
-10
@@ -716,13 +716,13 @@ wait_for_feature_request({xmlstreamelement, #xmlel{ns = NS, name = Name} = El},
|
||||
fsm_next_state(wait_for_sasl_response,
|
||||
StateData#state{
|
||||
sasl_state = NewSASLState});
|
||||
{error, Error, Username} when is_list(Error) ->
|
||||
{error, Error, Text, Username} ->
|
||||
?INFO_MSG(
|
||||
"(~w) Failed authentication for ~s@~s due to ~s",
|
||||
"(~w) Failed authentication for ~s@~s due to ~p ~s",
|
||||
[StateData#state.socket,
|
||||
Username, StateData#state.server, Error]),
|
||||
Username, StateData#state.server, Error, Text]),
|
||||
send_element(StateData,
|
||||
exmpp_server_sasl:failure(Error)),
|
||||
exmpp_server_sasl:failure(Error, Text)),
|
||||
{next_state, wait_for_feature_request, StateData,
|
||||
?C2S_OPEN_TIMEOUT};
|
||||
{error, Error} ->
|
||||
@@ -834,13 +834,13 @@ wait_for_sasl_response({xmlstreamelement, #xmlel{ns = NS, name = Name} = El},
|
||||
exmpp_server_sasl:challenge(ServerOut)),
|
||||
fsm_next_state(wait_for_sasl_response,
|
||||
StateData#state{sasl_state = NewSASLState});
|
||||
{error, Error, Username} ->
|
||||
{error, Error, Text, Username} ->
|
||||
?INFO_MSG(
|
||||
"(~w) Failed authentication for ~s@~s",
|
||||
"(~w) Failed authentication for ~s@~s due to ~p ~s",
|
||||
[StateData#state.socket,
|
||||
Username, StateData#state.server]),
|
||||
Username, StateData#state.server, Error, Text]),
|
||||
send_element(StateData,
|
||||
exmpp_server_sasl:failure(Error)),
|
||||
exmpp_server_sasl:failure(Error, Text)),
|
||||
fsm_next_state(wait_for_feature_request, StateData);
|
||||
{error, Error} ->
|
||||
send_element(StateData,
|
||||
@@ -894,7 +894,8 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
|
||||
Err = exmpp_server_binding:error(El, 'bad-request'),
|
||||
send_element(StateData, Err),
|
||||
fsm_next_state(wait_for_bind, StateData);
|
||||
throw:_Exception ->
|
||||
throw:Exception ->
|
||||
?DEBUG("When processing:~n ~p~nThis exception was catched:~n ~p", [El, Exception]),
|
||||
fsm_next_state(wait_for_bind, StateData)
|
||||
end;
|
||||
|
||||
@@ -1076,7 +1077,9 @@ session_established2(El, StateData) ->
|
||||
end;
|
||||
#xmlel{ns = ?NS_JABBER_CLIENT, name = 'iq'} ->
|
||||
case exmpp_iq:xmlel_to_iq(El) of
|
||||
#iq{kind = request, ns = ?NS_PRIVACY} = IQ_Rec ->
|
||||
#iq{kind = request, ns = Xmlns} = IQ_Rec
|
||||
when Xmlns == ?NS_PRIVACY;
|
||||
Xmlns == ?NS_BLOCKING ->
|
||||
process_privacy_iq(
|
||||
FromJID, ToJID, IQ_Rec, StateData);
|
||||
_ ->
|
||||
@@ -1341,6 +1344,13 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
send_element(StateData, PrivPushEl),
|
||||
{false, Attrs, StateData#state{privacy_list = NewPL}}
|
||||
end;
|
||||
blocking ->
|
||||
CDataString = exmpp_xml:get_cdata_as_list(Packet),
|
||||
{ok, A2, _} = erl_scan:string(CDataString),
|
||||
{_, W} = erl_parse:parse_exprs(A2),
|
||||
{value, What, []} = erl_eval:exprs(W, []),
|
||||
route_blocking(What, StateData),
|
||||
{false, Attrs, StateData};
|
||||
_ ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
@@ -2273,6 +2283,35 @@ bounce_messages() ->
|
||||
end.
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% XEP-0191
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
route_blocking(What, StateData) ->
|
||||
SubEl =
|
||||
case What of
|
||||
{Action, JIDs} when (Action == block) or (Action == unblock) ->
|
||||
UnblockJids =
|
||||
lists:map(
|
||||
fun(JidString) ->
|
||||
exmpp_xml:set_attribute(#xmlel{ns = ?NS_BLOCKING,
|
||||
name = item},
|
||||
<<"jid">>,
|
||||
JidString)
|
||||
end, JIDs),
|
||||
#xmlel{ns = ?NS_BLOCKING, name = Action,
|
||||
children = UnblockJids};
|
||||
unblock_all ->
|
||||
#xmlel{ns = ?NS_BLOCKING, name = 'unblock'}
|
||||
end,
|
||||
El1 = exmpp_iq:set(?NS_BLOCKING, SubEl, random),
|
||||
El2 = exmpp_stanza:set_sender(El1, exmpp_jid:bare(StateData#state.jid)),
|
||||
El3 = exmpp_stanza:set_recipient(El2, StateData#state.jid),
|
||||
send_element(StateData, El3),
|
||||
%% No need to replace active privacy list here,
|
||||
%% blocking pushes are always accompanied by
|
||||
%% Privacy List pushes
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% JID Set memory footprint reduction code
|
||||
|
||||
@@ -627,13 +627,17 @@ get_prog_name() ->
|
||||
get_url(Str) ->
|
||||
CaptchaHost = ejabberd_config:get_local_option(captcha_host),
|
||||
case string:tokens(CaptchaHost, ":") of
|
||||
[TransferProt, Host, PortString] ->
|
||||
TransferProt ++ ":" ++ Host ++ ":" ++ PortString ++ "/captcha/" ++ Str;
|
||||
[Host] ->
|
||||
"http://" ++ Host ++ "/captcha/" ++ Str;
|
||||
["http"++_ = TransferProt, Host] ->
|
||||
TransferProt ++ ":" ++ Host ++ "/captcha/" ++ Str;
|
||||
[Host, PortString] ->
|
||||
TransferProt = atom_to_list(get_transfer_protocol(PortString)),
|
||||
TransferProt ++ "://" ++ Host ++ ":" ++ PortString ++ "/captcha/" ++ Str;
|
||||
[TransferProt, Host, PortString] ->
|
||||
TransferProt ++ ":" ++ Host ++ ":" ++ PortString ++ "/captcha/" ++ Str;
|
||||
_ ->
|
||||
"http://" ++ ?MYNAME ++ ":5280/captcha/" ++ Str
|
||||
"http://" ++ ?MYNAME ++ "/captcha/" ++ Str
|
||||
end.
|
||||
|
||||
get_transfer_protocol(PortString) ->
|
||||
|
||||
@@ -263,7 +263,7 @@ exit_or_halt(ExitText) ->
|
||||
case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of
|
||||
[] ->
|
||||
timer:sleep(1000),
|
||||
halt(ExitText);
|
||||
halt(string:substr(ExitText, 1, 199));
|
||||
[_] ->
|
||||
exit(ExitText)
|
||||
end.
|
||||
|
||||
@@ -61,12 +61,28 @@
|
||||
#
|
||||
INET_DIST_INTERFACE={127,0,0,1}
|
||||
|
||||
#.
|
||||
#' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections
|
||||
#
|
||||
# IMPORTANT: This option works only in Erlang/OTP R14B03 and newer.
|
||||
#
|
||||
# This environment variable may be set to a comma-separated
|
||||
# list of IP addresses, in which case the epmd daemon
|
||||
# will listen only on the specified address(es) and on the
|
||||
# loopback address (which is implicitly added to the list if it
|
||||
# has not been specified). The default behaviour is to listen on
|
||||
# all available IP addresses.
|
||||
#
|
||||
# Default: 0.0.0.0
|
||||
#
|
||||
#ERL_EPMD_ADDRESS=127.0.0.1
|
||||
|
||||
#.
|
||||
#' ERL_PROCESSES: Maximum number of Erlang processes
|
||||
#
|
||||
# Erlang consumes a lot of lightweight processes. If there is a lot of activity
|
||||
# on ejabberd so that the maximum number of processes is reached, people will
|
||||
# experiment greater latency times. As these processes are implemented in
|
||||
# experience greater latency times. As these processes are implemented in
|
||||
# Erlang, and therefore not related to the operating system processes, you do
|
||||
# not have to worry about allowing a huge number of them.
|
||||
#
|
||||
|
||||
@@ -133,6 +133,7 @@ export EJABBERD_BIN_PATH
|
||||
export EJABBERD_DOC_PATH
|
||||
export EJABBERD_PID_PATH
|
||||
export ERL_CRASH_DUMP
|
||||
export ERL_EPMD_ADDRESS
|
||||
export ERL_INETRC
|
||||
export ERL_MAX_PORTS
|
||||
export ERL_MAX_ETS_TABLES
|
||||
|
||||
+12
-4
@@ -45,11 +45,18 @@
|
||||
start(Host, ExtPrg) ->
|
||||
lists:foreach(
|
||||
fun(This) ->
|
||||
spawn(?MODULE, init, [get_process_name(Host, This), ExtPrg])
|
||||
start_instance(get_process_name(Host, This), ExtPrg)
|
||||
end,
|
||||
lists:seq(0, get_instances(Host)-1)
|
||||
).
|
||||
|
||||
start_instance(ProcessName, ExtPrg) ->
|
||||
spawn(?MODULE, init, [ProcessName, ExtPrg]).
|
||||
|
||||
restart_instance(ProcessName, ExtPrg) ->
|
||||
unregister(ProcessName),
|
||||
start_instance(ProcessName, ExtPrg).
|
||||
|
||||
init(ProcessName, ExtPrg) ->
|
||||
register(ProcessName, self()),
|
||||
process_flag(trap_exit,true),
|
||||
@@ -125,8 +132,7 @@ loop(Port, Timeout, ProcessName, ExtPrg) ->
|
||||
Timeout ->
|
||||
?ERROR_MSG("extauth call '~p' didn't receive response", [Msg]),
|
||||
Caller ! {eauth, false},
|
||||
unregister(ProcessName),
|
||||
Pid = spawn(?MODULE, init, [ProcessName, ExtPrg]),
|
||||
Pid = restart_instance(ProcessName, ExtPrg),
|
||||
flush_buffer_and_forward_messages(Pid),
|
||||
exit(port_terminated)
|
||||
end;
|
||||
@@ -137,7 +143,9 @@ loop(Port, Timeout, ProcessName, ExtPrg) ->
|
||||
exit(normal)
|
||||
end;
|
||||
{'EXIT', Port, Reason} ->
|
||||
?CRITICAL_MSG("~p ~n", [Reason]),
|
||||
?CRITICAL_MSG("extauth script has exitted abruptly with reason '~p'", [Reason]),
|
||||
Pid = restart_instance(ProcessName, ExtPrg),
|
||||
flush_buffer_and_forward_messages(Pid),
|
||||
exit(port_terminated)
|
||||
end.
|
||||
|
||||
|
||||
+15
-2
@@ -85,11 +85,24 @@ start_module2(Host, Module, Opts) ->
|
||||
catch Class:Reason ->
|
||||
del_module_mnesia(Host, Module),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
?ERROR_MSG("Problem starting the module ~p for host ~p with options:~n ~p~n ~p: ~p",
|
||||
ErrorText = io_lib:format("Problem starting the module ~p for host ~p ~n options: ~p~n ~p: ~p",
|
||||
[Module, Host, Opts, Class, Reason]),
|
||||
erlang:raise(Class, Reason, erlang:get_stacktrace())
|
||||
?CRITICAL_MSG(ErrorText, []),
|
||||
case is_app_running(ejabberd) of
|
||||
true ->
|
||||
erlang:raise(Class, Reason, erlang:get_stacktrace());
|
||||
false ->
|
||||
?CRITICAL_MSG("ejabberd initialization was aborted because a module start failed.", []),
|
||||
timer:sleep(3000),
|
||||
erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199))
|
||||
end
|
||||
end.
|
||||
|
||||
is_app_running(AppName) ->
|
||||
%% Use a high timeout to prevent a false positive in a high load system
|
||||
Timeout = 15000,
|
||||
lists:keymember(AppName, 1, application:which_applications(Timeout)).
|
||||
|
||||
%% @doc Stop the module in a host, and forget its configuration.
|
||||
stop_module(Host, Module) ->
|
||||
case stop_module_keep_config(Host, Module) of
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_blocking.erl
|
||||
%%% Author : Stephan Maka
|
||||
%%% Purpose : XEP-0191: Simple Communications Blocking
|
||||
%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_blocking).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1,
|
||||
process_iq/3,
|
||||
process_iq_set/4,
|
||||
process_iq_get/5]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include_lib("exmpp/include/exmpp.hrl").
|
||||
-include("mod_privacy.hrl").
|
||||
|
||||
start(Host, Opts) ->
|
||||
HostB = list_to_binary(Host),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
ejabberd_hooks:add(privacy_iq_get, HostB,
|
||||
?MODULE, process_iq_get, 40),
|
||||
ejabberd_hooks:add(privacy_iq_set, HostB,
|
||||
?MODULE, process_iq_set, 40),
|
||||
mod_disco:register_feature(HostB, ?NS_BLOCKING),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_BLOCKING,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
stop(Host) ->
|
||||
HostB = list_to_binary(Host),
|
||||
ejabberd_hooks:delete(privacy_iq_get, HostB,
|
||||
?MODULE, process_iq_get, 40),
|
||||
ejabberd_hooks:delete(privacy_iq_set, HostB,
|
||||
?MODULE, process_iq_set, 40),
|
||||
mod_disco:unregister_feature(HostB, ?NS_BLOCKING),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_BLOCKING).
|
||||
|
||||
process_iq(_From, _To, IQ_Rec) ->
|
||||
exmpp_iq:error(IQ_Rec, 'not-allowed').
|
||||
|
||||
process_iq_get(_, From, _To, #iq{ns = ?NS_BLOCKING, payload = SubEl}, _) ->
|
||||
case SubEl#xmlel.name == blocklist of
|
||||
true ->
|
||||
LUser = exmpp_jid:prep_node(From),
|
||||
LServer = exmpp_jid:prep_domain(From),
|
||||
process_blocklist_get(LUser, LServer);
|
||||
false ->
|
||||
{error, 'bad-request'}
|
||||
end;
|
||||
|
||||
process_iq_get(Acc, _, _, _, _) ->
|
||||
Acc.
|
||||
|
||||
process_iq_set(_, From, _To, #iq{ns = ?NS_BLOCKING,
|
||||
payload = #xmlel{name = SubElName,
|
||||
children = SubEls}}) ->
|
||||
LUser = exmpp_jid:prep_node(From),
|
||||
LServer = exmpp_jid:prep_domain(From),
|
||||
case {SubElName, exmpp_xml:remove_cdata_from_list(SubEls)} of
|
||||
{block, []} ->
|
||||
{error, 'bad-request'};
|
||||
{block, Els} ->
|
||||
JIDs = parse_blocklist_items(Els, []),
|
||||
process_blocklist_block(LUser, LServer, JIDs);
|
||||
{unblock, []} ->
|
||||
process_blocklist_unblock_all(LUser, LServer);
|
||||
{unblock, Els} ->
|
||||
JIDs = parse_blocklist_items(Els, []),
|
||||
process_blocklist_unblock(LUser, LServer, JIDs);
|
||||
_ ->
|
||||
{error, 'bad-request'}
|
||||
end;
|
||||
|
||||
process_iq_set(Acc, _, _, _) ->
|
||||
Acc.
|
||||
|
||||
is_list_needdb(Items) ->
|
||||
lists:any(
|
||||
fun(X) ->
|
||||
case X#listitem.type of
|
||||
subscription -> true;
|
||||
group -> true;
|
||||
_ -> false
|
||||
end
|
||||
end, Items).
|
||||
|
||||
get_list_blocklist_jids(LUser, LServer, Name) ->
|
||||
Tuples = gen_storage:dirty_select(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name},
|
||||
{'=', type, jid}]),
|
||||
[Tuple#privacy_list_data.value ||
|
||||
Tuple <- Tuples, Tuple#privacy_list_data.match_all == true].
|
||||
|
||||
parse_blocklist_items([], JIDs) ->
|
||||
JIDs;
|
||||
|
||||
parse_blocklist_items([#xmlel{name = item} = El | Els], JIDs) ->
|
||||
case exmpp_xml:get_attribute(El, <<"jid">>, false) of
|
||||
false ->
|
||||
%% Tolerate missing jid attribute
|
||||
parse_blocklist_items(Els, JIDs);
|
||||
JID1 ->
|
||||
JID = exmpp_jid:to_binary(exmpp_jid:parse(JID1)),
|
||||
parse_blocklist_items(Els, [JID | JIDs])
|
||||
end;
|
||||
|
||||
parse_blocklist_items([_ | Els], JIDs) ->
|
||||
%% Tolerate unknown elements
|
||||
parse_blocklist_items(Els, JIDs).
|
||||
|
||||
process_blocklist_block(LUser, LServer, JIDs) ->
|
||||
F =
|
||||
fun() ->
|
||||
case gen_storage:read(LServer, {privacy_list, {LUser, LServer}}) of
|
||||
[] ->
|
||||
%% No lists yet
|
||||
%% TODO: i18n here:
|
||||
Default = <<"Blocked contacts">>,
|
||||
gen_storage:write(LServer,
|
||||
#privacy_list{
|
||||
user_host = {LUser, LServer},
|
||||
name = Default}),
|
||||
gen_storage:write(LServer,
|
||||
#privacy_default_list{
|
||||
user_host = {LUser, LServer},
|
||||
name = Default}),
|
||||
ok;
|
||||
_Lists ->
|
||||
case gen_storage:read(LServer,
|
||||
{privacy_default_list, {LUser, LServer}}) of
|
||||
[#privacy_default_list{name = Default}] ->
|
||||
%% Default list exists
|
||||
Default;
|
||||
[] ->
|
||||
%% No default list yet, create one
|
||||
%% TODO: i18n here:
|
||||
Default = <<"Blocked contacts">>,
|
||||
gen_storage:write(LServer,
|
||||
#privacy_list{
|
||||
user_host = {LUser, LServer},
|
||||
name = Default}),
|
||||
gen_storage:write(LServer,
|
||||
#privacy_default_list{
|
||||
user_host = {LUser, LServer},
|
||||
name = Default})
|
||||
end
|
||||
end,
|
||||
AlreadyBlocked = get_list_blocklist_jids(LUser, LServer, Default),
|
||||
NewItems = lists:foldr(fun(JID, Res) ->
|
||||
case lists:member(JID, AlreadyBlocked) of
|
||||
true ->
|
||||
Res;
|
||||
false ->
|
||||
Data = #privacy_list_data{
|
||||
user_host = {LUser, LServer},
|
||||
name = Default,
|
||||
type = jid,
|
||||
value = JID,
|
||||
action = deny,
|
||||
order = 0,
|
||||
match_all = true
|
||||
},
|
||||
gen_storage:write(LServer, Data),
|
||||
[Data | Res]
|
||||
end
|
||||
end, [], JIDs),
|
||||
{ok, Default, NewItems}
|
||||
end,
|
||||
case gen_storage:transaction(LServer, privacy_list_data, F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
{atomic, {ok, Default, Data}} ->
|
||||
%% Data = gen_storage:select(LServer, privacy_list_data,
|
||||
%% [{'=', user_host, {LUser, LServer}},
|
||||
%% {'=', name, Default}]),
|
||||
List = list_data_to_items(Data),
|
||||
broadcast_list_update(LUser, LServer, Default, List),
|
||||
broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
|
||||
{result, []};
|
||||
Error ->
|
||||
?DEBUG("Error ~n~p", [Error]),
|
||||
{error, 'internal-server-error'}
|
||||
end.
|
||||
|
||||
%%Copied from mod_privacy
|
||||
%% storage representation to ejabberd representation
|
||||
list_data_to_items(Data) ->
|
||||
List =
|
||||
lists:map(
|
||||
fun(Data1) ->
|
||||
#listitem{type = Data1#privacy_list_data.type,
|
||||
value = Data1#privacy_list_data.value,
|
||||
action = Data1#privacy_list_data.action,
|
||||
order = Data1#privacy_list_data.order,
|
||||
match_all = Data1#privacy_list_data.match_all,
|
||||
match_iq = Data1#privacy_list_data.match_iq,
|
||||
match_message = Data1#privacy_list_data.match_message,
|
||||
match_presence_in = Data1#privacy_list_data.match_presence_in,
|
||||
match_presence_out = Data1#privacy_list_data.match_presence_out}
|
||||
end, Data),
|
||||
SortedList = lists:keysort(#listitem.order, List),
|
||||
SortedList.
|
||||
|
||||
process_blocklist_unblock_all(LUser, LServer) ->
|
||||
F =
|
||||
fun() ->
|
||||
case gen_storage:read(LServer, {privacy_list, {LUser, LServer}}) of
|
||||
[] ->
|
||||
%% No lists, nothing to unblock
|
||||
ok;
|
||||
_ ->
|
||||
case gen_storage:read(LServer, {privacy_default_list, {LUser, LServer}}) of
|
||||
[#privacy_default_list{name = Default}] ->
|
||||
%% Default list, remove all deny items
|
||||
gen_storage:delete_where(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Default},
|
||||
{'=', action, deny},
|
||||
{'=', match_all, true},
|
||||
{'=', type, jid}]),
|
||||
Data = gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Default}]),
|
||||
{ok, Default, Data};
|
||||
[] ->
|
||||
%% No default list, nothing to unblock
|
||||
ok
|
||||
end
|
||||
end
|
||||
end,
|
||||
case gen_storage:transaction(LServer, privacy_list_data, F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
{atomic, ok} ->
|
||||
{result, []};
|
||||
{atomic, {ok, Default, Data}} ->
|
||||
List = list_data_to_items(Data),
|
||||
broadcast_list_update(LUser, LServer, Default, List),
|
||||
broadcast_blocklist_event(LUser, LServer, unblock_all),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, 'internal-server-error'}
|
||||
end.
|
||||
|
||||
process_blocklist_unblock(LUser, LServer, JIDs) ->
|
||||
F =
|
||||
fun() ->
|
||||
case gen_storage:read(LServer, {privacy_list, {LUser, LServer}}) of
|
||||
[] ->
|
||||
%% No lists, nothing to unblock
|
||||
ok;
|
||||
_ ->
|
||||
case gen_storage:read(LServer, {privacy_default_list, {LUser, LServer}}) of
|
||||
[#privacy_default_list{name = Default}] ->
|
||||
%% Default list, remove matching deny items
|
||||
lists:foreach(
|
||||
fun(JID) ->
|
||||
gen_storage:delete_where(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Default},
|
||||
{'=', action, deny},
|
||||
{'=', match_all, true},
|
||||
{'=', value, JID},
|
||||
{'=', type, jid}])
|
||||
end, JIDs),
|
||||
Data = gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Default}]),
|
||||
{ok, Default, Data};
|
||||
[] ->
|
||||
%% No default list, nothing to unblock
|
||||
ok
|
||||
end
|
||||
end
|
||||
end,
|
||||
case gen_storage:transaction(LServer, privacy_list_data, F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
{atomic, ok} ->
|
||||
{result, []};
|
||||
{atomic, {ok, Default, Data}} ->
|
||||
List = list_data_to_items(Data),
|
||||
broadcast_list_update(LUser, LServer, Default, List),
|
||||
broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, 'internal-server-error'}
|
||||
end.
|
||||
|
||||
broadcast_list_update(LUser, LServer, Name, List) ->
|
||||
NeedDb = is_list_needdb(List),
|
||||
JID = exmpp_jid:make(LUser, LServer),
|
||||
ListString = lists:flatten(io_lib:format("~p.", [#userlist{name = Name, list = List, needdb = NeedDb}])),
|
||||
ejabberd_router:route(
|
||||
JID,
|
||||
JID,
|
||||
#xmlel{name = 'broadcast', ns = privacy_list,
|
||||
attrs = [?XMLATTR(<<"list_name">>, Name)],
|
||||
children = [exmpp_xml:cdata(ListString)]}).
|
||||
|
||||
broadcast_blocklist_event(LUser, LServer, Event) ->
|
||||
JID = exmpp_jid:make(LUser, LServer),
|
||||
EventString = lists:flatten(io_lib:format("~p.", [Event])),
|
||||
ejabberd_router:route(
|
||||
JID, JID,
|
||||
#xmlel{name = 'broadcast', ns = blocking,
|
||||
children = [exmpp_xml:cdata(EventString)]}).
|
||||
|
||||
process_blocklist_get(LUser, LServer) ->
|
||||
case catch gen_storage:dirty_read(LServer, privacy_default_list, {LUser, LServer}) of
|
||||
{'EXIT', _Reason} ->
|
||||
{error, 'internal-server-error'};
|
||||
[] ->
|
||||
{result, #xmlel{name = 'blocklist', ns = ?NS_BLOCKING}};
|
||||
[#privacy_default_list{name = Default}] ->
|
||||
JIDs = get_list_blocklist_jids(LUser, LServer, Default),
|
||||
Items = lists:map(
|
||||
fun(JID) ->
|
||||
?DEBUG("JID: ~p",[JID]),
|
||||
#xmlel{name = item, ns = privacy_list,
|
||||
attrs = [?XMLATTR(<<"jid">>, JID)]}
|
||||
end, JIDs),
|
||||
{result,
|
||||
#xmlel{name = 'blocklist', ns = ?NS_BLOCKING, children = Items}}
|
||||
end.
|
||||
+4
-4
@@ -186,7 +186,7 @@ user_receive_packet(_JID, _From, _To, _Packet) ->
|
||||
|
||||
caps_stream_features(Acc, MyHost) ->
|
||||
case make_my_disco_hash(MyHost) of
|
||||
"" ->
|
||||
<<"">> ->
|
||||
Acc;
|
||||
Hash ->
|
||||
[#xmlel{name = c,
|
||||
@@ -470,7 +470,7 @@ make_my_disco_hash(Host) ->
|
||||
end, Features),
|
||||
make_disco_hash(Identities ++ Info ++ Feats, sha1);
|
||||
_Err ->
|
||||
""
|
||||
<<"">>
|
||||
end.
|
||||
|
||||
-ifdef(HAVE_MD2).
|
||||
@@ -478,7 +478,7 @@ make_disco_hash(DiscoEls, Algo) ->
|
||||
Concat = [concat_identities(DiscoEls),
|
||||
concat_features(DiscoEls),
|
||||
concat_info(DiscoEls)],
|
||||
base64:encode_to_string(
|
||||
base64:encode(
|
||||
if Algo == md2 ->
|
||||
sha:md2(Concat);
|
||||
Algo == md5 ->
|
||||
@@ -519,7 +519,7 @@ make_disco_hash(DiscoEls, Algo) ->
|
||||
Concat = [concat_identities(DiscoEls),
|
||||
concat_features(DiscoEls),
|
||||
concat_info(DiscoEls)],
|
||||
base64:encode_to_string(
|
||||
base64:encode(
|
||||
if Algo == md5 ->
|
||||
crypto:md5(Concat);
|
||||
Algo == sha1 ->
|
||||
|
||||
+2
-1
@@ -142,8 +142,9 @@ process_sm_iq(From, To, #iq{type = get} = IQ_Rec) ->
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, exmpp_jid:prep_domain(To),
|
||||
{none, []}, [exmpp_jid:prep_node(To), exmpp_jid:prep_domain(To), From]),
|
||||
SameUser = exmpp_jid:bare_compare(From, To),
|
||||
if
|
||||
(Subscription == both) or (Subscription == from) ->
|
||||
(Subscription == both) or (Subscription == from) or SameUser ->
|
||||
UserListRecord = ejabberd_hooks:run_fold(
|
||||
privacy_get_user_list, exmpp_jid:prep_domain(To),
|
||||
#userlist{},
|
||||
|
||||
@@ -26,6 +26,16 @@
|
||||
|
||||
%%% Database schema (version / storage / table)
|
||||
%%%
|
||||
%%% 2.1.x / mnesia / muc_online_room
|
||||
%%% name_host = {Name::string(), Host::string()}
|
||||
%%% pid = pid()
|
||||
%%% 2.1.x / mnesia / muc_registered
|
||||
%%% us_host = {{Username::string, Hostname::string()}, Host::string()}}
|
||||
%%% nick = string()
|
||||
%%% 2.1.x / mnesia / muc_room
|
||||
%%% name_host = {Name::string(), Host::string()}
|
||||
%%% opts = [{Option::atom(), Value::any()}
|
||||
%%%
|
||||
%%% 3.0.0-alpha / mnesia / muc_online_room
|
||||
%%% name_host = {Name::binary(), Host::binary()}
|
||||
%%% pid = pid()
|
||||
@@ -333,6 +343,7 @@ init([Host, Opts]) ->
|
||||
gen_storage:add_table_copy(MyHost, muc_online_room, node(), ram_copies),
|
||||
catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
|
||||
gen_storage:add_table_index(MyHost, muc_registered, nick),
|
||||
update_tables(MyHost_L, Backend),
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
AccessCreate = gen_mod:get_opt(access_create, Opts, all),
|
||||
AccessAdmin = gen_mod:get_opt(access_admin, Opts, none),
|
||||
@@ -1013,3 +1024,46 @@ get_vh_rooms_all_nodes(Host) ->
|
||||
get_vh_rooms(Host) when is_binary(Host) ->
|
||||
gen_storage:dirty_select(Host, muc_online_room,
|
||||
[{'=', name_host, {'_', Host}}]).
|
||||
|
||||
%%%%%
|
||||
%%%%% Database migration from ejabberd 2.1.x
|
||||
%%%%%
|
||||
|
||||
update_tables(Host, mnesia) ->
|
||||
HostB = list_to_binary(Host),
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, muc_registered,
|
||||
[{muc_registered, [us_host, nick],
|
||||
fun({muc_registered, {{UserS, ServerS}, HostS}, NickS}) ->
|
||||
#muc_registered{user_host = {exmpp_jid:make(UserS, ServerS),
|
||||
list_to_binary(HostS)},
|
||||
nick = list_to_binary(NickS)}
|
||||
end}]),
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, muc_room_opt,
|
||||
[{muc_room, [name_host, opts],
|
||||
fun({muc_room, {NameString, HostString}, Options}) ->
|
||||
NameHost = {list_to_binary(NameString),
|
||||
list_to_binary(HostString)},
|
||||
lists:foreach(
|
||||
fun({affiliations, Affiliations}) ->
|
||||
lists:foreach(
|
||||
fun({{Username, Hostname, Resource}, AffiliationIni}) ->
|
||||
UserJid = exmpp_jid:make(Username, Hostname, Resource),
|
||||
{Affiliation, Reason} = case AffiliationIni of
|
||||
Aff when is_atom(Aff) -> {Aff, ""};
|
||||
{Aff, Reas} -> {Aff, Reas}
|
||||
end,
|
||||
gen_storage:write(HostB,
|
||||
#muc_room_affiliation{name_host = NameHost,
|
||||
jid = UserJid,
|
||||
affiliation = Affiliation,
|
||||
reason = Reason})
|
||||
end, Affiliations);
|
||||
({Opt, Val}) ->
|
||||
gen_storage:write(HostB,
|
||||
#muc_room_opt{name_host = NameHost,
|
||||
opt = Opt,
|
||||
val = Val})
|
||||
end, Options)
|
||||
end}]).
|
||||
|
||||
@@ -2756,13 +2756,20 @@ send_kickban_presence(JID, Reason, Code, NewAffiliation, StateData) ->
|
||||
end, LJIDs).
|
||||
|
||||
send_kickban_presence1(UJID, Reason, Code, Affiliation, StateData) ->
|
||||
{ok, #user{jid = _RealJID,
|
||||
{ok, #user{jid = RealJID,
|
||||
nick = Nick}} = ?DICT:find(UJID, StateData#state.users),
|
||||
SAffiliation = affiliation_to_binary(Affiliation),
|
||||
BannedJID = exmpp_jid:to_binary(RealJID),
|
||||
lists:foreach(
|
||||
fun({_LJID, Info}) ->
|
||||
JidAttrList = case (Info#user.role == moderator) orelse
|
||||
((StateData#state.config)#config.anonymous
|
||||
== false) of
|
||||
true -> [?XMLATTR(<<"jid">>, BannedJID)];
|
||||
false -> []
|
||||
end,
|
||||
ItemAttrs = [?XMLATTR(<<"affiliation">>, SAffiliation),
|
||||
?XMLATTR(<<"role">>, <<"none">>)],
|
||||
?XMLATTR(<<"role">>, <<"none">>)] ++ JidAttrList,
|
||||
ItemEls = case Reason of
|
||||
"" ->
|
||||
[];
|
||||
|
||||
+46
-39
@@ -56,12 +56,12 @@
|
||||
%%% Same as 2.1.x
|
||||
%%%
|
||||
%%% 3.0.0-alpha / mnesia / offline_msg
|
||||
%%% user_host = {Username::string(), Host::string()}
|
||||
%%% user_host = {Username::binary(), Host::binary()}
|
||||
%%% timestamp = integer()
|
||||
%%% expire = 0 | integer()
|
||||
%%% from = jid()
|
||||
%%% to = jid()
|
||||
%%% packet = string()
|
||||
%%% packet = binary()
|
||||
%%%
|
||||
%%% 3.0.0-alpha / odbc / offline_msg
|
||||
%%% user = varchar150
|
||||
@@ -98,8 +98,7 @@
|
||||
-include("web/ejabberd_http.hrl").
|
||||
-include("web/ejabberd_web_admin.hrl").
|
||||
|
||||
%% The packet is stored serialized as a string
|
||||
%% TODO: packet always handled serialized?
|
||||
%% The packet is stored serialized as a binary
|
||||
-record(offline_msg, {user_host, timestamp, expire, from, to, packet}).
|
||||
|
||||
-define(PROCNAME, ejabberd_offline).
|
||||
@@ -149,6 +148,12 @@ start(Host, Opts) ->
|
||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
spawn(?MODULE, loop, [AccessMaxOfflineMsgs])).
|
||||
|
||||
stanza_to_store(Stanza) ->
|
||||
exmpp_xml:document_to_binary(Stanza).
|
||||
store_to_stanza(StoreBinary) ->
|
||||
[Xml] = exmpp_xml:parse_document(StoreBinary),
|
||||
Xml.
|
||||
|
||||
loop(AccessMaxOfflineMsgs) ->
|
||||
receive
|
||||
#offline_msg{user_host=US} = Msg ->
|
||||
@@ -156,8 +161,6 @@ loop(AccessMaxOfflineMsgs) ->
|
||||
Len = length(Msgs),
|
||||
%% TODO: is lower?
|
||||
{User, Host} = US,
|
||||
MsgsSerialized = [M#offline_msg{packet = exmpp_xml:document_to_list(P)}
|
||||
|| #offline_msg{packet = P} = M <- Msgs],
|
||||
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
|
||||
User, Host),
|
||||
F = fun() ->
|
||||
@@ -180,7 +183,7 @@ loop(AccessMaxOfflineMsgs) ->
|
||||
end,
|
||||
lists:foreach(fun(M) ->
|
||||
gen_storage:write(Host, M)
|
||||
end, MsgsSerialized)
|
||||
end, Msgs)
|
||||
end
|
||||
end,
|
||||
gen_storage:transaction(Host, offline_msg, F),
|
||||
@@ -251,8 +254,8 @@ store_packet(From, To, Packet) ->
|
||||
(Type /= <<"headline">>) ->
|
||||
case check_event_chatstates(From, To, Packet) of
|
||||
true ->
|
||||
LUser = exmpp_jid:prep_node_as_list(To),
|
||||
LServer = exmpp_jid:prep_domain_as_list(To),
|
||||
LUser = exmpp_jid:prep_node(To),
|
||||
LServer = exmpp_jid:prep_domain(To),
|
||||
TimeStamp = make_timestamp(),
|
||||
Expire = find_x_expire(TimeStamp, Packet#xmlel.children),
|
||||
gen_mod:get_module_proc(LServer, ?PROCNAME) !
|
||||
@@ -261,7 +264,7 @@ store_packet(From, To, Packet) ->
|
||||
expire = Expire,
|
||||
from = From,
|
||||
to = To,
|
||||
packet = Packet},
|
||||
packet = stanza_to_store(Packet)},
|
||||
stop;
|
||||
_ ->
|
||||
ok
|
||||
@@ -338,10 +341,11 @@ find_x_expire(TimeStamp, [_ | Els]) ->
|
||||
find_x_expire(TimeStamp, Els).
|
||||
|
||||
|
||||
%% @spec(User::string(), Server::string()) -> ok
|
||||
resend_offline_messages(User, Server) ->
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
LUser = exmpp_stringprep:nodeprep(list_to_binary(User)),
|
||||
LServer = exmpp_stringprep:nameprep(list_to_binary(Server)),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
Rs = gen_storage:read(LServer, offline_msg, US, write),
|
||||
@@ -352,7 +356,7 @@ resend_offline_messages(User, Server) ->
|
||||
{atomic, Rs} ->
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
Packet = R#offline_msg.packet,
|
||||
Packet = store_to_stanza(R#offline_msg.packet),
|
||||
TimeXml = timestamp_to_xml(R#offline_msg.timestamp, Server),
|
||||
TimeXml91 = timestamp_to_xml(R#offline_msg.timestamp),
|
||||
ejabberd_sm !
|
||||
@@ -373,12 +377,11 @@ resend_offline_messages(User, Server) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% @spec(Ls::list(), User::binary(), Server::binary()) -> list()
|
||||
pop_offline_messages(Ls, User, Server)
|
||||
when is_binary(User), is_binary(Server) ->
|
||||
try
|
||||
LUser = binary_to_list(User),
|
||||
LServer = binary_to_list(Server),
|
||||
US = {LUser, LServer},
|
||||
US = {User, Server},
|
||||
F = fun() ->
|
||||
Rs = gen_storage:read(Server, offline_msg, US, write),
|
||||
gen_storage:delete(Server, {offline_msg, US}),
|
||||
@@ -389,7 +392,7 @@ pop_offline_messages(Ls, User, Server)
|
||||
TS = make_timestamp(),
|
||||
Ls ++ lists:map(
|
||||
fun(R) ->
|
||||
[Packet] = exmpp_xml:parse_document(R#offline_msg.packet),
|
||||
Packet = store_to_stanza(R#offline_msg.packet),
|
||||
TimeXml = timestamp_to_xml(R#offline_msg.timestamp, Server),
|
||||
TimeXml91 = timestamp_to_xml(R#offline_msg.timestamp),
|
||||
{route,
|
||||
@@ -444,6 +447,7 @@ remove_old_messages(Days) ->
|
||||
{atomic, _} = gen_storage:transaction(HostB, offline_msg, F)
|
||||
end, gen_storage:all_table_hosts(offline_msg)).
|
||||
|
||||
%% @spec(User::binary(), Server::binary()) -> ok
|
||||
remove_user(User, Server) when is_binary(User), is_binary(Server) ->
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
@@ -486,7 +490,7 @@ update_table(Host, mnesia) ->
|
||||
end,
|
||||
PacketXmlel = exmpp_xml:xmlelement_to_xmlel(
|
||||
Packet, [?DEFAULT_NS], ?PREFIXED_NS),
|
||||
Packet1 = exmpp_xml:document_to_list(PacketXmlel),
|
||||
Packet1 = stanza_to_store(PacketXmlel),
|
||||
OM#offline_msg{user_host = {US_U1, US_S1},
|
||||
timestamp = TsMegaSecs * 1000000000 + TsSecs * 1000 + TsMicroSecs div 1000,
|
||||
expire = Expire1,
|
||||
@@ -515,7 +519,7 @@ update_table(Host, odbc) ->
|
||||
end}]).
|
||||
|
||||
convert_jid_to_exmpp("") -> undefined;
|
||||
convert_jid_to_exmpp(V) -> V.
|
||||
convert_jid_to_exmpp(V) -> list_to_binary(V).
|
||||
|
||||
%% Return the current timestamp in milliseconds
|
||||
make_timestamp() ->
|
||||
@@ -570,7 +574,8 @@ find_x_timestamp([_ | Els]) ->
|
||||
%% Warn senders that their messages have been discarded:
|
||||
discard_warn_sender(Msgs) ->
|
||||
lists:foreach(
|
||||
fun(#offline_msg{from=From, to=To, packet=Packet}) ->
|
||||
fun(#offline_msg{from=From, to=To, packet=PacketStored}) ->
|
||||
Packet = store_to_stanza(PacketStored),
|
||||
ErrText = "Your contact offline message queue is full. The message has been discarded.",
|
||||
Error = exmpp_stanza:error(Packet#xmlel.ns, 'resource-constraint',
|
||||
{"en", ErrText}),
|
||||
@@ -592,31 +597,32 @@ webadmin_page(_, Host,
|
||||
webadmin_page(Acc, _, _) -> Acc.
|
||||
|
||||
user_queue(User, Server, Query, Lang) ->
|
||||
ServerB = list_to_binary(Server),
|
||||
US0 = {
|
||||
exmpp_stringprep:nodeprep(User),
|
||||
exmpp_stringprep:nameprep(Server)
|
||||
exmpp_stringprep:nodeprep(list_to_binary(User)),
|
||||
exmpp_stringprep:nameprep(list_to_binary(Server))
|
||||
},
|
||||
{US, MsgsAll, Res} = try
|
||||
{US, Res, MsgsAll} = try
|
||||
{
|
||||
US0,
|
||||
lists:keysort(#offline_msg.timestamp,
|
||||
gen_storage:dirty_read(Server, {offline_msg, US0})),
|
||||
user_queue_parse_query(US0, Query)
|
||||
user_queue_parse_query(US0, Query),
|
||||
lists:keysort(#offline_msg.timestamp,
|
||||
gen_storage:dirty_read(ServerB, {offline_msg, US0}))
|
||||
}
|
||||
catch
|
||||
_ ->
|
||||
{{"invalid", "invalid"}, [], nothing}
|
||||
{{"invalid", "invalid"}, [], []}
|
||||
end,
|
||||
Msgs = get_messages_subset(User, Server, MsgsAll),
|
||||
FMsgs =
|
||||
lists:map(
|
||||
fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
|
||||
packet = PacketInitialString} = Msg) ->
|
||||
packet = PacketStored} = Msg) ->
|
||||
ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
|
||||
Time = timestamp_to_isostring(TimeStamp),
|
||||
SFrom = exmpp_jid:to_list(From),
|
||||
STo = exmpp_jid:to_list(To),
|
||||
[Packet] = exmpp_xmlstream:parse_element(PacketInitialString),
|
||||
Packet = store_to_stanza(PacketStored),
|
||||
Packet1 = exmpp_stanza:set_jids(Packet, SFrom, STo),
|
||||
FPacket = exmpp_xml:node_to_list(
|
||||
exmpp_xml:indent_document(Packet1, <<" ">>),
|
||||
@@ -632,7 +638,7 @@ user_queue(User, Server, Query, Lang) ->
|
||||
[?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
|
||||
[us_to_list(US)]))] ++
|
||||
case Res of
|
||||
ok -> [?CT("Submitted"), ?P];
|
||||
ok -> [?XREST("Submitted")];
|
||||
nothing -> []
|
||||
end ++
|
||||
[?XAE("form", [?XMLATTR(<<"action">>, <<"">>), ?XMLATTR(<<"method">>, <<"post">>)],
|
||||
@@ -687,10 +693,9 @@ user_queue_parse_query(US, Query) ->
|
||||
us_to_list({User, Server}) ->
|
||||
exmpp_jid:to_list(User, Server).
|
||||
|
||||
%% @spec (User::string(), Host::string()) -> integer()
|
||||
%% @spec (User::binary(), Host::binary()) -> integer()
|
||||
get_queue_length(User, Host) ->
|
||||
HostB = list_to_binary(Host),
|
||||
gen_storage:dirty_count_records(HostB, offline_msg, [{'=', user_host, {User, Host}}]).
|
||||
gen_storage:dirty_count_records(Host, offline_msg, [{'=', user_host, {User, Host}}]).
|
||||
|
||||
get_messages_subset(User, Host, MsgsAll) ->
|
||||
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
|
||||
@@ -711,7 +716,7 @@ get_messages_subset2(Max, Length, MsgsAll) ->
|
||||
NoJID = exmpp_jid:make("...", "...", ""),
|
||||
TimeStamp = make_timestamp(),
|
||||
IntermediateMsg = #offline_msg{timestamp = TimeStamp, from = NoJID, to = NoJID,
|
||||
packet = exmpp_xml:element("...")},
|
||||
packet = <<"...">>},
|
||||
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
|
||||
|
||||
webadmin_user(Acc, User, Server, Lang) ->
|
||||
@@ -722,15 +727,17 @@ webadmin_user(Acc, User, Server, Lang) ->
|
||||
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
|
||||
|
||||
webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
|
||||
US = {User, Server},
|
||||
UserB = list_to_binary(User),
|
||||
ServerB = list_to_binary(Server),
|
||||
US = {UserB, ServerB},
|
||||
F = fun() ->
|
||||
gen_storage:write_lock_table(Server, offline_msg),
|
||||
gen_storage:write_lock_table(ServerB, offline_msg),
|
||||
lists:foreach(
|
||||
fun(Msg) ->
|
||||
gen_storage:delete_object(Server, Msg)
|
||||
end, gen_storage:read(Server, {offline_msg, US}))
|
||||
gen_storage:delete_object(ServerB, Msg)
|
||||
end, gen_storage:read(ServerB, {offline_msg, US}))
|
||||
end,
|
||||
case gen_storage:transaction(Server, offline_msg, F) of
|
||||
case gen_storage:transaction(ServerB, offline_msg, F) of
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
|
||||
{stop, error};
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ init([Host, Opts]) ->
|
||||
SendPings = gen_mod:get_opt(send_pings, Opts, ?DEFAULT_SEND_PINGS),
|
||||
PingInterval = gen_mod:get_opt(ping_interval, Opts, ?DEFAULT_PING_INTERVAL),
|
||||
TimeoutAction = gen_mod:get_opt(timeout_action, Opts, none),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue),
|
||||
mod_disco:register_feature(HostB, ?NS_PING),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PING,
|
||||
?MODULE, iq_ping, IQDisc),
|
||||
|
||||
+62
-70
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_privacy.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : jabber:iq:privacy support
|
||||
%%% Purpose : XEP-0016: Privacy Lists
|
||||
%%% Created : 21 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
@@ -59,16 +59,16 @@
|
||||
%%% Same as 2.1.x
|
||||
%%%
|
||||
%%% 3.0.0-alpha / mnesia / privacy_default_list
|
||||
%%% user_host = {Username::string(), Server::string()}
|
||||
%%% name = string()
|
||||
%%% user_host = {Username::binary(), Server::binary()}
|
||||
%%% name = binary()
|
||||
%%% 3.0.0-alpha / mnesia / privacy_list
|
||||
%%% user_host = {Username::string(), Server::string()}
|
||||
%%% name = string()
|
||||
%%% user_host = {Username::binary(), Server::binary()}
|
||||
%%% name = binary()
|
||||
%%% 3.0.0-alpha / mnesia / privacy_list_data
|
||||
%%% user_host = {Username::string(), Server::string()}
|
||||
%%% name = string()
|
||||
%%% user_host = {Username::binary(), Server::binary()}
|
||||
%%% name = binary()
|
||||
%%% type = jid | group | subscription | none
|
||||
%%% value = JID::binary() | Group::binary() | <<"none">> | <<"both">> | <<"from">> | <<"to">>
|
||||
%%% value = JID::binary() | Group::binary() | <<"none">> | <<"both">> | <<"from">> | <<"to">> | none
|
||||
%%% action = allow | deny
|
||||
%%% order = integer()
|
||||
%%% match_all = boolean()
|
||||
@@ -118,13 +118,6 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("mod_privacy.hrl").
|
||||
|
||||
-record(privacy_list, {user_host, name}).
|
||||
-record(privacy_default_list, {user_host, name}).
|
||||
-record(privacy_list_data, {user_host, name,
|
||||
type, value, action, order,
|
||||
match_all, match_iq, match_message,
|
||||
match_presence_in, match_presence_out}).
|
||||
|
||||
start(Host, Opts) ->
|
||||
HostB = list_to_binary(Host),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
@@ -194,41 +187,42 @@ process_iq(_From, _To, IQ_Rec) ->
|
||||
exmpp_iq:error(IQ_Rec, 'not-allowed').
|
||||
|
||||
|
||||
process_iq_get(_, From, _To, #iq{payload = SubEl},
|
||||
process_iq_get(_, From, _To, #iq{ns = ?NS_PRIVACY, payload = SubEl},
|
||||
#userlist{name = Active}) ->
|
||||
LUser = exmpp_jid:prep_node_as_list(From),
|
||||
LServer = exmpp_jid:prep_domain_as_list(From),
|
||||
LUser = exmpp_jid:prep_node(From),
|
||||
LServer = exmpp_jid:prep_domain(From),
|
||||
case exmpp_xml:get_child_elements(SubEl) of
|
||||
[] ->
|
||||
process_lists_get(LUser, LServer, Active);
|
||||
[#xmlel{name = Name} = Child] ->
|
||||
case Name of
|
||||
list ->
|
||||
ListName = exmpp_xml:get_attribute_as_list(Child, <<"name">>, false),
|
||||
ListName = exmpp_xml:get_attribute_as_binary(Child, <<"name">>, false),
|
||||
process_list_get(LUser, LServer, ListName);
|
||||
_ ->
|
||||
{error, 'bad-request'}
|
||||
end;
|
||||
_ ->
|
||||
{error, 'bad-request'}
|
||||
end.
|
||||
end;
|
||||
|
||||
process_iq_get(Acc, _, _, _, _) ->
|
||||
Acc.
|
||||
|
||||
process_lists_get(LUser, LServer, Active) ->
|
||||
LServerB = list_to_binary(LServer),
|
||||
F = fun() ->
|
||||
Default =
|
||||
case gen_storage:read(LServerB, {privacy_default_list, {LUser, LServer}}) of
|
||||
case gen_storage:read(LServer, {privacy_default_list, {LUser, LServer}}) of
|
||||
[#privacy_default_list{name = Name}] ->
|
||||
Name;
|
||||
_ ->
|
||||
none
|
||||
end,
|
||||
Lists = [List#privacy_list.name
|
||||
|| List <- gen_storage:read(LServerB, {privacy_list, {LUser, LServer}})],
|
||||
|| List <- gen_storage:read(LServer, {privacy_list, {LUser, LServer}})],
|
||||
{Default, Lists}
|
||||
end,
|
||||
case gen_storage:transaction(LServerB, privacy_list, F) of
|
||||
case gen_storage:transaction(LServer, privacy_list, F) of
|
||||
{aborted, _Reason} ->
|
||||
{error, 'internal-server-error'};
|
||||
{atomic, {Default, Lists}} ->
|
||||
@@ -259,20 +253,19 @@ process_list_get(_LUser, _LServer, false) ->
|
||||
{error, 'bad-request'};
|
||||
|
||||
process_list_get(LUser, LServer, Name) ->
|
||||
LServerB = list_to_binary(LServer),
|
||||
F = fun() ->
|
||||
case gen_storage:select(LServerB, privacy_list,
|
||||
case gen_storage:select(LServer, privacy_list,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]) of
|
||||
[] ->
|
||||
none;
|
||||
[#privacy_list{}] ->
|
||||
gen_storage:select(LServerB, privacy_list_data,
|
||||
gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}])
|
||||
end
|
||||
end,
|
||||
case gen_storage:transaction(LServerB, privacy_list, F) of
|
||||
case gen_storage:transaction(LServer, privacy_list, F) of
|
||||
{aborted, _Reason} ->
|
||||
{error, 'internal-server-error'};
|
||||
{atomic, none} ->
|
||||
@@ -354,12 +347,12 @@ list_to_action(S) ->
|
||||
|
||||
|
||||
|
||||
process_iq_set(_, From, _To, #iq{payload = SubEl}) ->
|
||||
LUser = exmpp_jid:prep_node_as_list(From),
|
||||
LServer = exmpp_jid:prep_domain_as_list(From),
|
||||
process_iq_set(_, From, _To, #iq{ns = ?NS_PRIVACY, payload = SubEl}) ->
|
||||
LUser = exmpp_jid:prep_node(From),
|
||||
LServer = exmpp_jid:prep_domain(From),
|
||||
case exmpp_xml:get_child_elements(SubEl) of
|
||||
[#xmlel{name = Name} = Child] ->
|
||||
ListName = exmpp_xml:get_attribute_as_list(Child, <<"name">>, false),
|
||||
ListName = exmpp_xml:get_attribute_as_binary(Child, <<"name">>, false),
|
||||
case Name of
|
||||
list ->
|
||||
process_list_set(LUser, LServer, ListName,
|
||||
@@ -373,15 +366,17 @@ process_iq_set(_, From, _To, #iq{payload = SubEl}) ->
|
||||
end;
|
||||
_ ->
|
||||
{error, 'bad-request'}
|
||||
end.
|
||||
end;
|
||||
|
||||
process_iq_set(Acc, _, _, _) ->
|
||||
Acc.
|
||||
|
||||
|
||||
process_default_set(LUser, LServer, false) ->
|
||||
LServerB = list_to_binary(LServer),
|
||||
F = fun() ->
|
||||
gen_storage:delete(LServerB, {privacy_default_list, {LUser, LServer}})
|
||||
gen_storage:delete(LServer, {privacy_default_list, {LUser, LServer}})
|
||||
end,
|
||||
case gen_storage:transaction(LServerB, privacy_default_list, F) of
|
||||
case gen_storage:transaction(LServer, privacy_default_list, F) of
|
||||
{atomic, _} ->
|
||||
{result, []};
|
||||
_ ->
|
||||
@@ -389,21 +384,20 @@ process_default_set(LUser, LServer, false) ->
|
||||
end;
|
||||
|
||||
process_default_set(LUser, LServer, Name) ->
|
||||
LServerB = list_to_binary(LServer),
|
||||
F = fun() ->
|
||||
case gen_storage:select(LServerB, privacy_list,
|
||||
case gen_storage:select(LServer, privacy_list,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]) of
|
||||
[] ->
|
||||
{error, 'item-not-found'};
|
||||
[#privacy_list{}] ->
|
||||
gen_storage:write(LServerB,
|
||||
gen_storage:write(LServer,
|
||||
#privacy_default_list{user_host = {LUser, LServer},
|
||||
name = Name}),
|
||||
{result, []}
|
||||
end
|
||||
end,
|
||||
case gen_storage:transaction(LServerB, privacy_list, F) of
|
||||
case gen_storage:transaction(LServer, privacy_list, F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
{atomic, {result, _} = Res} ->
|
||||
@@ -417,13 +411,12 @@ process_active_set(_LUser, _LServer, false) ->
|
||||
{result, [], #userlist{}};
|
||||
|
||||
process_active_set(LUser, LServer, Name) ->
|
||||
LServerB = list_to_binary(LServer),
|
||||
F = fun() ->
|
||||
case gen_storage:select(LServerB, privacy_list,
|
||||
case gen_storage:select(LServer, privacy_list,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]) of
|
||||
[#privacy_list{}] ->
|
||||
Data = gen_storage:select(LServerB, privacy_list_data,
|
||||
Data = gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]),
|
||||
List = list_data_to_items(Data),
|
||||
@@ -435,7 +428,7 @@ process_active_set(LUser, LServer, Name) ->
|
||||
{error, 'item-not-found'}
|
||||
end
|
||||
end,
|
||||
case gen_storage:transaction(LServerB, privacy_list, F) of
|
||||
case gen_storage:transaction(LServer, privacy_list, F) of
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
@@ -448,27 +441,26 @@ process_list_set(_LUser, _LServer, false, _Els) ->
|
||||
{error, 'bad-request'};
|
||||
|
||||
process_list_set(LUser, LServer, Name, Els) ->
|
||||
LServerB = list_to_binary(LServer),
|
||||
case parse_items(Els) of
|
||||
false ->
|
||||
{error, 'bad-request'};
|
||||
remove ->
|
||||
F = fun() ->
|
||||
case gen_storage:read(LServerB,
|
||||
case gen_storage:read(LServer,
|
||||
{privacy_default_list, {LUser, LServer}}) of
|
||||
[#privacy_default_list{name = Default}] when Name == Default ->
|
||||
{error, 'conflict'};
|
||||
_ ->
|
||||
gen_storage:delete_where(LServerB, privacy_list,
|
||||
gen_storage:delete_where(LServer, privacy_list,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]),
|
||||
gen_storage:delete_where(LServerB, privacy_list_data,
|
||||
gen_storage:delete_where(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]),
|
||||
{result, []}
|
||||
end
|
||||
end,
|
||||
case gen_storage:transaction(LServerB, privacy_list, F) of
|
||||
case gen_storage:transaction(LServer, privacy_list, F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
{atomic, {result, _} = Res} ->
|
||||
@@ -486,24 +478,24 @@ process_list_set(LUser, LServer, Name, Els) ->
|
||||
List ->
|
||||
F = fun() ->
|
||||
OldData =
|
||||
gen_storage:select(LServerB, privacy_list_data,
|
||||
gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]),
|
||||
lists:foreach(
|
||||
fun(Data1) ->
|
||||
gen_storage:delete_object(LServerB, Data1)
|
||||
gen_storage:delete_object(LServer, Data1)
|
||||
end, OldData),
|
||||
|
||||
gen_storage:write(LServerB, #privacy_list{user_host = {LUser, LServer},
|
||||
gen_storage:write(LServer, #privacy_list{user_host = {LUser, LServer},
|
||||
name = Name}),
|
||||
NewData = list_items_to_data(LUser, LServer, Name, List),
|
||||
lists:foreach(
|
||||
fun(Data1) ->
|
||||
gen_storage:write(LServerB, Data1)
|
||||
gen_storage:write(LServer, Data1)
|
||||
end, NewData),
|
||||
{result, []}
|
||||
end,
|
||||
case gen_storage:transaction(LServerB, privacy_list, F) of
|
||||
case gen_storage:transaction(LServer, privacy_list, F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
{atomic, {result, _} = Res} ->
|
||||
@@ -656,17 +648,15 @@ is_list_needdb(Items) ->
|
||||
end
|
||||
end, Items).
|
||||
|
||||
get_user_list(_, User, LServerB)
|
||||
when is_binary(User), is_binary(LServerB) ->
|
||||
LUser = binary_to_list(User),
|
||||
LServer = binary_to_list(LServerB),
|
||||
get_user_list(_, LUser, LServer)
|
||||
when is_binary(LUser), is_binary(LServer) ->
|
||||
F = fun() ->
|
||||
case gen_storage:read(LServerB,
|
||||
case gen_storage:read(LServer,
|
||||
{privacy_default_list, {LUser, LServer}}) of
|
||||
[] ->
|
||||
#userlist{};
|
||||
[#privacy_default_list{name = Default}] ->
|
||||
Data = gen_storage:select(LServerB, privacy_list_data,
|
||||
Data = gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Default}]),
|
||||
List = list_data_to_items(Data),
|
||||
@@ -676,7 +666,7 @@ get_user_list(_, User, LServerB)
|
||||
list = List}
|
||||
end
|
||||
end,
|
||||
{atomic, Res} = gen_storage:transaction(LServerB, privacy_default_list, F),
|
||||
{atomic, Res} = gen_storage:transaction(LServer, privacy_default_list, F),
|
||||
Res.
|
||||
|
||||
|
||||
@@ -789,16 +779,15 @@ is_type_match(group, Value, _JID, _Subscription, Groups) ->
|
||||
%% The ejabberd hook provides the arguments as binaries,
|
||||
%% but the mod_privacy internal functions provide them as strings.
|
||||
%% Once this module stores information as binaries, this incoherence will be solved.
|
||||
remove_user(User, Server) when is_binary(User) and is_binary(Server) ->
|
||||
remove_user(binary_to_list(User), binary_to_list(Server));
|
||||
remove_user(User, Server) when is_list(User) and is_list(Server) ->
|
||||
remove_user(list_to_binary(User), list_to_binary(Server));
|
||||
remove_user(User, Server) when is_binary(User) and is_binary(Server) ->
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
LServerB = list_to_binary(LServer),
|
||||
F = fun() ->
|
||||
gen_storage:delete(LServerB, {privacy_list, {LUser, LServer}})
|
||||
gen_storage:delete(LServer, {privacy_list, {LUser, LServer}})
|
||||
end,
|
||||
case gen_storage:transaction(LServerB, privacy_list, F) of
|
||||
case gen_storage:transaction(LServer, privacy_list, F) of
|
||||
{atomic, _} ->
|
||||
{result, []};
|
||||
_ ->
|
||||
@@ -823,9 +812,11 @@ update_tables(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, privacy_default_list,
|
||||
[{privacy, [us, default, lists],
|
||||
fun({privacy, US, Default, Lists}) ->
|
||||
fun({privacy, {U, S}, Default, Lists}) ->
|
||||
US = {list_to_binary(U), list_to_binary(S)},
|
||||
lists:foreach(
|
||||
fun({Name, List}) ->
|
||||
NameBin = list_to_binary(Name),
|
||||
lists:foreach(
|
||||
fun(#listitem{type = Type,
|
||||
value = Value,
|
||||
@@ -839,7 +830,7 @@ update_tables(Host, mnesia) ->
|
||||
ValueBin = convert_value_to_binary(Value),
|
||||
gen_storage:write(HostB,
|
||||
#privacy_list_data{user_host = US,
|
||||
name = Name,
|
||||
name = NameBin,
|
||||
type = Type,
|
||||
value = ValueBin,
|
||||
action = Action,
|
||||
@@ -852,12 +843,13 @@ update_tables(Host, mnesia) ->
|
||||
end, List),
|
||||
gen_storage:write(HostB,
|
||||
#privacy_list{user_host = US,
|
||||
name = Name})
|
||||
name = NameBin})
|
||||
end, Lists),
|
||||
if
|
||||
is_list(Default) ->
|
||||
DefaultBin = list_to_binary(Default),
|
||||
#privacy_default_list{user_host = US,
|
||||
name = Default};
|
||||
name = DefaultBin};
|
||||
true -> null
|
||||
end
|
||||
end}]);
|
||||
|
||||
@@ -19,10 +19,19 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(privacy_list, {user_host, name}).
|
||||
-record(privacy_default_list, {user_host, name}).
|
||||
-record(privacy_list_data, {user_host, name,
|
||||
type, value, action, order,
|
||||
match_all, match_iq, match_message,
|
||||
match_presence_in, match_presence_out}).
|
||||
|
||||
%% ejabberd 2 format:
|
||||
-record(privacy, {user_host,
|
||||
default = none,
|
||||
lists = []}).
|
||||
|
||||
%% ejabberd 2 format:
|
||||
-record(listitem, {type = none,
|
||||
value = none,
|
||||
action,
|
||||
@@ -35,3 +44,4 @@
|
||||
}).
|
||||
|
||||
-record(userlist, {name = none, list = [], needdb = false }).
|
||||
|
||||
|
||||
@@ -594,10 +594,10 @@ send_loop(State) ->
|
||||
lists:foreach(
|
||||
fun({Node, subscribed, _, SubJID}) ->
|
||||
if (SubJID == LJID) or (SubJID == BJID) ->
|
||||
#pubsub_node{id = {H, NodeId}, type = Type, idx = NodeIdx, options = Options} = Node,
|
||||
#pubsub_node{id = {H, NodeId}, type = Type, idx = Nidx, options = Options} = Node,
|
||||
case get_option(Options, 'send_last_published_item') of
|
||||
'on_sub_and_presence' ->
|
||||
send_items(H, NodeId, NodeIdx, Type, LJID, 'last');
|
||||
send_items(H, NodeId, Nidx, Type, LJID, 'last');
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
@@ -646,7 +646,7 @@ send_loop(State) ->
|
||||
spawn(fun() ->
|
||||
Host = State#state.host,
|
||||
Owner = {U,S,undefined},
|
||||
lists:foreach(fun(#pubsub_node{id = {_, NodeId}, type = Type, idx = NodeIdx, options = Options}) ->
|
||||
lists:foreach(fun(#pubsub_node{id = {_, NodeId}, type = Type, idx = Nidx, options = Options}) ->
|
||||
case get_option(Options, 'send_last_published_item') of
|
||||
'on_sub_and_presence' ->
|
||||
lists:foreach(fun(Resource) ->
|
||||
@@ -660,7 +660,7 @@ send_loop(State) ->
|
||||
RosterGroups = get_option(Options, 'roster_groups_allowed', []),
|
||||
element(2, get_roster_info(U, S, LJID, RosterGroups))
|
||||
end,
|
||||
if Subscribed -> send_items(Owner, NodeId, NodeIdx, Type, LJID, 'last');
|
||||
if Subscribed -> send_items(Owner, NodeId, Nidx, Type, LJID, 'last');
|
||||
true -> ok
|
||||
end
|
||||
end, Resources);
|
||||
@@ -763,8 +763,8 @@ disco_identity(_Host, <<>> = _NodeId, _From) ->
|
||||
[#xmlel{name = 'identity', ns = ?NS_DISCO_INFO,
|
||||
attrs = [?XMLATTR(<<"category">>, <<"pubsub">>), ?XMLATTR(<<"type">>, <<"pep">>)]}];
|
||||
disco_identity(#jid{node = U, domain = S, resource = R} = Host, NodeId, From) ->
|
||||
Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
|
||||
Action = fun(#pubsub_node{idx = Nidx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
{result,
|
||||
[#xmlel{name = 'identity', ns = ?NS_DISCO_INFO,
|
||||
@@ -813,8 +813,8 @@ disco_features(_Host, <<>> = _NodeId, _From) ->
|
||||
[?NS_PUBSUB_s
|
||||
| [?NS_PUBSUB_s++"#"++Feature || Feature <- features("pep")]];
|
||||
disco_features(#jid{node = U, domain = S, resource = R} = Host, NodeId, From) ->
|
||||
Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
|
||||
Action = fun(#pubsub_node{idx = Nidx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
{result, [?NS_PUBSUB_s
|
||||
| [?NS_PUBSUB_s ++ "#" ++ Feature || Feature <- features("pep")]]};
|
||||
@@ -853,8 +853,8 @@ disco_sm_items({result, OtherItems} = _Acc, From, To, NodeId, _Lang) ->
|
||||
).
|
||||
|
||||
disco_items(#jid{raw = JID, node = U, domain = S, resource = R} = Host, <<>>, From) ->
|
||||
Action = fun(#pubsub_node{id ={_, NodeId}, options = Options, type = Type, idx = NodeIdx, owners = Owners}, Acc) ->
|
||||
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
|
||||
Action = fun(#pubsub_node{id ={_, NodeId}, options = Options, type = Type, idx = Nidx, owners = Owners}, Acc) ->
|
||||
case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
[#xmlel{name = 'item', ns = ?NS_DISCO_INFO,
|
||||
attrs = [?XMLATTR(<<"jid">>, JID),
|
||||
@@ -873,8 +873,8 @@ disco_items(#jid{raw = JID, node = U, domain = S, resource = R} = Host, <<>>, Fr
|
||||
end;
|
||||
|
||||
disco_items(#jid{raw = JID, node = U, domain = S, resource = R} = Host, NodeId, From) ->
|
||||
Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
|
||||
Action = fun(#pubsub_node{idx = Nidx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
|
||||
{result, Items} ->
|
||||
{result,
|
||||
[#xmlel{name = 'item', ns = ?NS_DISCO_INFO,
|
||||
@@ -1331,11 +1331,11 @@ command_disco_info(_Host, ?NS_PUBSUB_GET_PENDING_b = _NodeId, _From) ->
|
||||
).
|
||||
|
||||
node_disco_info(Host, NodeId, From) ->
|
||||
Action = fun(#pubsub_node{type = Plugin, idx = NodeIdx}) ->
|
||||
Action = fun(#pubsub_node{type = Plugin, idx = Nidx}) ->
|
||||
Types = case tree_call(Host, get_subnodes, [Host, NodeId, From]) of
|
||||
[] -> ["leaf"];
|
||||
_ ->
|
||||
case node_call(Plugin, get_items, [NodeIdx, From]) of
|
||||
case node_call(Plugin, get_items, [Nidx, From]) of
|
||||
{result, []} -> ["collection"];
|
||||
{result, _} -> ["leaf", "collection"];
|
||||
_ -> []
|
||||
@@ -1449,8 +1449,8 @@ iq_disco_items(_Host, ?NS_PUBSUB_GET_PENDING_b = _NodeId, _From) ->
|
||||
%% TODO
|
||||
{result, []};
|
||||
iq_disco_items(Host, NodeId, From) ->
|
||||
Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options, owners = Owners}) ->
|
||||
NodeItems = case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
|
||||
Action = fun(#pubsub_node{idx = Nidx, type = Type, options = Options, owners = Owners}) ->
|
||||
NodeItems = case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
|
||||
{result, R} -> R;
|
||||
_ -> []
|
||||
end,
|
||||
@@ -2417,9 +2417,12 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
|
||||
Features = features(Type),
|
||||
PublishFeature = lists:member("publish", Features),
|
||||
PublishModel = get_option(Options, publish_model),
|
||||
MaxItems = max_items(Host, Options),
|
||||
DeliverPayloads = get_option(Options, deliver_payloads),
|
||||
PersistItems = get_option(Options, persist_items),
|
||||
MaxItems = case PersistItems of
|
||||
0 -> 0;
|
||||
1 -> max_items(Host, Options)
|
||||
end,
|
||||
{PayloadCount, PayloadNS} = payload_els_ns(Payload),
|
||||
PayloadSize = size(term_to_binary(Payload))-2, % size(term_to_binary([])) == 2
|
||||
PayloadMaxSize = get_option(Options, max_payload_size),
|
||||
@@ -2698,8 +2701,8 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIds) ->
|
||||
).
|
||||
|
||||
get_items(Host, NodeId) ->
|
||||
Action = fun(#pubsub_node{type = Type, idx = NodeIdx}) ->
|
||||
node_call(Type, get_items, [NodeIdx, service_jid(Host)])
|
||||
Action = fun(#pubsub_node{type = Type, idx = Nidx}) ->
|
||||
node_call(Type, get_items, [Nidx, service_jid(Host)])
|
||||
end,
|
||||
case transaction(Host, NodeId, Action, sync_dirty) of
|
||||
{result, {_, Items}} -> Items
|
||||
@@ -2716,8 +2719,8 @@ get_items(Host, NodeId) ->
|
||||
).
|
||||
|
||||
get_item(Host, NodeId, ItemId) ->
|
||||
Action = fun(#pubsub_node{type = Type, idx = NodeIdx}) ->
|
||||
node_call(Type, get_item, [NodeIdx, ItemId])
|
||||
Action = fun(#pubsub_node{type = Type, idx = Nidx}) ->
|
||||
node_call(Type, get_item, [Nidx, ItemId])
|
||||
end,
|
||||
case transaction(Host, NodeId, Action, sync_dirty) of
|
||||
{result, {_, Items}} -> Items;
|
||||
@@ -2733,19 +2736,32 @@ get_item(Host, NodeId, ItemId) ->
|
||||
%% Number = last | integer()
|
||||
%% @doc <p>Resend the items of a node to the user.</p>
|
||||
%% @todo use cache-last-item feature
|
||||
send_items(Host, Node, NodeId, Type, LJID, 'last') ->
|
||||
case get_cached_item(Host, NodeId) of
|
||||
send_items(Host, Node, Nidx, Type, {U,S,R} = LJID, 'last') ->
|
||||
case get_cached_item(Host, Nidx) of
|
||||
undefined ->
|
||||
send_items(Host, Node, NodeId, Type, LJID, 1);
|
||||
send_items(Host, Node, Nidx, Type, LJID, 1);
|
||||
LastItem ->
|
||||
{ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
|
||||
Stanza = event_stanza_with_delay(
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = nodeAttr(Node),
|
||||
children = itemsEls([LastItem])}], ModifNow, ModifUSR),
|
||||
ejabberd_router:route(service_jid(Host), exmpp_jid:make(LJID), Stanza)
|
||||
case is_tuple(Host) of
|
||||
false ->
|
||||
ejabberd_router:route(service_jid(Host), exmpp_jid:make(U, S, R), Stanza);
|
||||
true ->
|
||||
case ejabberd_sm:get_session_pid(U,S,R) of
|
||||
C2SPid when is_pid(C2SPid) ->
|
||||
ejabberd_c2s:broadcast(C2SPid,
|
||||
{pep_message, << Node/binary, <<"+notify">>/binary >>},
|
||||
_Sender = service_jid(Host),
|
||||
Stanza);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end
|
||||
end;
|
||||
send_items(Host, Node, NodeId, Type, {LU, LS, LR} = LJID, Number) ->
|
||||
ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of
|
||||
send_items(Host, Node, Nidx, Type, {U,S,R} = LJID, Number) ->
|
||||
ToSend = case node_action(Host, Type, get_items, [Nidx, LJID]) of
|
||||
{result, []} ->
|
||||
[];
|
||||
{result, Items} ->
|
||||
@@ -2767,7 +2783,20 @@ send_items(Host, Node, NodeId, Type, {LU, LS, LR} = LJID, Number) ->
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = nodeAttr(Node), children =
|
||||
itemsEls(ToSend)}])
|
||||
end,
|
||||
ejabberd_router:route(service_jid(Host), exmpp_jid:make(LU, LS, LR), Stanza).
|
||||
case is_tuple(Host) of
|
||||
false ->
|
||||
ejabberd_router:route(service_jid(Host), exmpp_jid:make(U, S, R), Stanza);
|
||||
true ->
|
||||
case ejabberd_sm:get_session_pid(U,S,R) of
|
||||
C2SPid when is_pid(C2SPid) ->
|
||||
ejabberd_c2s:broadcast(C2SPid,
|
||||
{pep_message, << Node/binary, <<"+notify">>/binary >>},
|
||||
_Sender = service_jid(Host),
|
||||
Stanza);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
|
||||
%% Host = host()
|
||||
@@ -2926,7 +2955,7 @@ get_options(Host, Node, JID, SubId, Lang) ->
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
get_options_helper(JID, Lang, Node, NodeId, SubId, Type) ->
|
||||
get_options_helper(JID, Lang, Node, Nidx, SubId, Type) ->
|
||||
Subscriber = try exmpp_jid:parse(JID) of
|
||||
J -> jlib:short_jid(J)
|
||||
catch
|
||||
@@ -2934,7 +2963,7 @@ get_options_helper(JID, Lang, Node, NodeId, SubId, Type) ->
|
||||
exmpp_jid:make("", "", "") %% TODO, check if use <<>> instead of ""
|
||||
end,
|
||||
{result, Subs} = node_call(Type, get_subscriptions,
|
||||
[NodeId, Subscriber]),
|
||||
[Nidx, Subscriber]),
|
||||
SubIds = lists:foldl(fun({subscribed, SID}, Acc) ->
|
||||
[SID | Acc];
|
||||
(_, Acc) ->
|
||||
@@ -2944,17 +2973,15 @@ get_options_helper(JID, Lang, Node, NodeId, SubId, Type) ->
|
||||
{_, []} ->
|
||||
{error, extended_error('not-acceptable', "not-subscribed")};
|
||||
{[], [SID]} ->
|
||||
read_sub(Subscriber, Node, NodeId, SID, Lang);
|
||||
read_sub(Subscriber, Node, Nidx, SID, Lang);
|
||||
{[], _} ->
|
||||
{error, extended_error('not-acceptable', "subid-required")};
|
||||
{_, _} ->
|
||||
read_sub(Subscriber, Node, NodeId, SubId, Lang)
|
||||
read_sub(Subscriber, Node, Nidx, SubId, Lang)
|
||||
end.
|
||||
|
||||
read_sub(Subscriber, Node, NodeId, SubId, Lang) ->
|
||||
case pubsub_subscription:get_subscription(Subscriber, NodeId, SubId) of
|
||||
{error, notfound} ->
|
||||
{error, extended_error('not-acceptable', "invalid-subid")};
|
||||
read_sub(Subscriber, Node, Nidx, SubId, Lang) ->
|
||||
case pubsub_subscription:get_subscription(Subscriber, Nidx, SubId) of
|
||||
{result, #pubsub_subscription{options = Options}} ->
|
||||
{result, XdataEl} = pubsub_subscription:get_options_xform(Lang, Options),
|
||||
OptionsEl = #xmlel{ns = ?NS_PUBSUB, name = 'options',
|
||||
@@ -2962,6 +2989,12 @@ read_sub(Subscriber, Node, NodeId, SubId, Lang) ->
|
||||
?XMLATTR(<<"subid">>, SubId) | nodeAttr(Node)],
|
||||
children = [XdataEl]},
|
||||
PubsubEl = #xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [OptionsEl]},
|
||||
{result, PubsubEl};
|
||||
_ ->
|
||||
OptionsEl = #xmlel{ns = ?NS_PUBSUB, name = 'options',
|
||||
attrs = [?XMLATTR(<<"jid">>, exmpp_jid:to_binary(Subscriber)),
|
||||
?XMLATTR(<<"subid">>, SubId) | nodeAttr(Node)]},
|
||||
PubsubEl = #xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [OptionsEl]},
|
||||
{result, PubsubEl}
|
||||
end.
|
||||
|
||||
@@ -2982,7 +3015,7 @@ set_options(Host, Node, JID, SubId, Configuration) ->
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
set_options_helper(Configuration, JID, NodeId, SubId, Type) ->
|
||||
set_options_helper(Configuration, JID, Nidx, SubId, Type) ->
|
||||
SubOpts = case pubsub_subscription:parse_options_xform(Configuration) of
|
||||
{result, GoodSubOpts} -> GoodSubOpts;
|
||||
_ -> invalid
|
||||
@@ -2993,7 +3026,7 @@ set_options_helper(Configuration, JID, NodeId, SubId, Type) ->
|
||||
_ -> exmpp_jid:make("", "", "") %% TODO, check if use <<>> instead of ""
|
||||
end,
|
||||
{result, Subs} = node_call(Type, get_subscriptions,
|
||||
[NodeId, Subscriber]),
|
||||
[Nidx, Subscriber]),
|
||||
SubIds = lists:foldl(fun({subscribed, SID}, Acc) ->
|
||||
[SID | Acc];
|
||||
(_, Acc) ->
|
||||
@@ -3003,21 +3036,23 @@ set_options_helper(Configuration, JID, NodeId, SubId, Type) ->
|
||||
{_, []} ->
|
||||
{error, extended_error('not-acceptable', "not-subscribed")};
|
||||
{[], [SID]} ->
|
||||
write_sub(Subscriber, NodeId, SID, SubOpts);
|
||||
write_sub(Subscriber, Nidx, SID, SubOpts);
|
||||
{[], _} ->
|
||||
{error, extended_error('not-acceptable', "subid-required")};
|
||||
{_, _} ->
|
||||
write_sub(Subscriber, NodeId, SubId, SubOpts)
|
||||
write_sub(Subscriber, Nidx, SubId, SubOpts)
|
||||
end.
|
||||
|
||||
write_sub(_Subscriber, _NodeId, _SubId, invalid) ->
|
||||
write_sub(_Subscriber, _Nidx, _SubId, invalid) ->
|
||||
{error, extended_error('bad-request', "invalid-options")};
|
||||
write_sub(Subscriber, NodeId, SubId, Options) ->
|
||||
case pubsub_subscription:set_subscription(Subscriber, NodeId, SubId, Options) of
|
||||
{error, notfound} ->
|
||||
{error, extended_error('not-acceptable', "invalid-subid")};
|
||||
write_sub(_Subscriber, _Nidx, _SubId, []) ->
|
||||
{result, []};
|
||||
write_sub(Subscriber, Nidx, SubId, Options) ->
|
||||
case pubsub_subscription:set_subscription(Subscriber, Nidx, SubId, Options) of
|
||||
{result, _} ->
|
||||
{result, []}
|
||||
{result, []};
|
||||
{error, _} ->
|
||||
{error, extended_error('not-acceptable', "invalid-subid")}
|
||||
end.
|
||||
|
||||
%% @spec (Host, Node, JID, Plugins) -> {error, Reason} | {result, Response}
|
||||
@@ -3600,18 +3635,18 @@ node_subscriptions_full(Host, Node, NotifyType) ->
|
||||
{_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth),
|
||||
JIDSubs.
|
||||
|
||||
sub_with_options(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
case node_call(Type, get_node_subscriptions, [NodeId]) of
|
||||
sub_with_options(#pubsub_node{type = Type, idx = Nidx}) ->
|
||||
case node_call(Type, get_node_subscriptions, [Nidx]) of
|
||||
{result, Subs} ->
|
||||
lists:foldl(
|
||||
fun({JID, subscribed, SubId}, Acc) -> [sub_with_options(JID, NodeId, SubId) | Acc];
|
||||
fun({JID, subscribed, SubId}, Acc) -> [sub_with_options(JID, Nidx, SubId) | Acc];
|
||||
(_, Acc) -> Acc
|
||||
end, [], Subs);
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
sub_with_options(JID, NodeId, SubId) ->
|
||||
case pubsub_subscription:read_subscription(JID, NodeId, SubId) of
|
||||
sub_with_options(JID, Nidx, SubId) ->
|
||||
case pubsub_subscription:read_subscription(JID, Nidx, SubId) of
|
||||
#pubsub_subscription{options = Options} -> {JID, SubId, Options};
|
||||
_ -> {JID, SubId, []}
|
||||
end.
|
||||
|
||||
@@ -2758,8 +2758,6 @@ get_options_helper(JID, Lang, Node, NodeId, SubId, Type) ->
|
||||
|
||||
read_sub(Subscriber, Node, NodeId, SubId, Lang) ->
|
||||
case pubsub_subscription_odbc:get_subscription(Subscriber, NodeId, SubId) of
|
||||
{error, notfound} ->
|
||||
{error, extended_error('not-acceptable', "invalid-subid")};
|
||||
{result, #pubsub_subscription{options = Options}} ->
|
||||
{result, XdataEl} = pubsub_subscription_odbc:get_options_xform(Lang, Options),
|
||||
OptionsEl = #xmlel{ns = ?NS_PUBSUB, name = 'options',
|
||||
@@ -2767,6 +2765,12 @@ read_sub(Subscriber, Node, NodeId, SubId, Lang) ->
|
||||
?XMLATTR(<<"subid">>, SubId) | nodeAttr(Node)],
|
||||
children = [XdataEl]},
|
||||
PubsubEl = #xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [OptionsEl]},
|
||||
{result, PubsubEl};
|
||||
_ ->
|
||||
OptionsEl = #xmlel{ns = ?NS_PUBSUB, name = 'options',
|
||||
attrs = [?XMLATTR(<<"jid">>, exmpp_jid:to_binary(Subscriber)),
|
||||
?XMLATTR(<<"subid">>, SubId) | nodeAttr(Node)]},
|
||||
PubsubEl = #xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children = [OptionsEl]},
|
||||
{result, PubsubEl}
|
||||
end.
|
||||
|
||||
@@ -2817,12 +2821,14 @@ set_options_helper(Configuration, JID, NodeId, SubId, Type) ->
|
||||
|
||||
write_sub(_Subscriber, _NodeId, _SubId, invalid) ->
|
||||
{error, extended_error('bad-request', "invalid-options")};
|
||||
write_sub(_Subscriber, _NodeID, _SubID, []) ->
|
||||
{result, []};
|
||||
write_sub(Subscriber, NodeId, SubId, Options) ->
|
||||
case pubsub_subscription_odbc:set_subscription(Subscriber, NodeId, SubId, Options) of
|
||||
{error, notfound} ->
|
||||
{error, extended_error('not-acceptable', "invalid-subid")};
|
||||
{result, _} ->
|
||||
{result, []}
|
||||
{result, []};
|
||||
{error, _} ->
|
||||
{error, extended_error('not-acceptable', "invalid-subid")}
|
||||
end.
|
||||
|
||||
%% @spec (Host, Node, JID, Plugins) -> {error, Reason} | {result, Response}
|
||||
@@ -3288,7 +3294,7 @@ broadcast({U, S, R}, Node, NodeId, Type, NodeOptions, Subscriptions, Stanza, SHI
|
||||
%% set the from address on the notification to the bare JID of the account owner
|
||||
%% Also, add "replyto" if entity has presence subscription to the account owner
|
||||
%% See XEP-0163 1.1 section 4.3.1
|
||||
Event = {pep_message, binary_to_list(Node)++"+notify"},
|
||||
Event = {pep_message, << Node/binary, <<"+notify">>/binary >>},
|
||||
Message = case get_option(NodeOptions, notification_type, headline) of
|
||||
normal -> Stanza;
|
||||
MsgType -> add_message_type(Stanza, atom_to_list(MsgType))
|
||||
|
||||
@@ -107,9 +107,11 @@ init(_Host, _ServerHost, _Opts) ->
|
||||
pubsub_subscription:init(),
|
||||
mnesia:create_table(pubsub_state,
|
||||
[{disc_copies, [node()]},
|
||||
{index, [nodeidx]},
|
||||
{attributes, record_info(fields, pubsub_state)}]),
|
||||
mnesia:create_table(pubsub_item,
|
||||
[{disc_only_copies, [node()]},
|
||||
{index, [nodeidx]},
|
||||
{attributes, record_info(fields, pubsub_item)}]),
|
||||
ItemsFields = record_info(fields, pubsub_item),
|
||||
case mnesia:table_info(pubsub_item, attributes) of
|
||||
@@ -234,7 +236,7 @@ create_node_permission(Host, ServerHost, _NodeId, _ParentNodeId, #jid{node = U,
|
||||
).
|
||||
|
||||
create_node(NodeIdx, #jid{node = U, domain = S} = _JID) ->
|
||||
set_state(#pubsub_state{id = {{U,S,undefined}, NodeIdx}, affiliation = 'owner'}),
|
||||
set_state(#pubsub_state{id = {{U,S,undefined}, NodeIdx}, nodeidx = NodeIdx, affiliation = 'owner'}),
|
||||
{'result', {'default', 'broadcast'}}.
|
||||
|
||||
%% @spec (Removed) -> ok
|
||||
@@ -557,6 +559,7 @@ publish_item(NodeIdx, #jid{node = U, domain = S, resource = R} = _JID, PublishMo
|
||||
_ ->
|
||||
#pubsub_item{
|
||||
id = {ItemId, NodeIdx},
|
||||
nodeidx = NodeIdx,
|
||||
creation = {Now, GenKey}, % TODO, better use {Now, SubKey} ?
|
||||
modification = Modification,
|
||||
payload = Payload}
|
||||
@@ -648,10 +651,10 @@ delete_item(NodeIdx, #jid{node = U, domain = S} = _JID, PublishModel, ItemId) ->
|
||||
false ->
|
||||
case Affiliation of
|
||||
'owner' ->
|
||||
%% Owner can delete other publishers items as well
|
||||
%% Owner can delete any items from its own node
|
||||
{'result', States} = get_states(NodeIdx),
|
||||
lists:foldl(fun
|
||||
(#pubsub_state{items = ItemIds, affiliation = publisher} = State, Result) ->
|
||||
(#pubsub_state{items = ItemIds} = State, Result) ->
|
||||
case lists:member(ItemId, ItemIds) of
|
||||
true ->
|
||||
del_item(NodeIdx, ItemId),
|
||||
@@ -1024,7 +1027,7 @@ end.
|
||||
|
||||
get_states(NodeIdx) ->
|
||||
States = case
|
||||
catch mnesia:match_object(#pubsub_state{id = {'_', NodeIdx}, _ = '_'})
|
||||
catch mnesia:index_read(pubsub_state, NodeIdx, #pubsub_state.nodeidx)
|
||||
of
|
||||
PubsubStates when is_list(PubsubStates) -> PubsubStates; %% is_list(PubsubStates) useful ?
|
||||
_ -> []
|
||||
@@ -1046,7 +1049,7 @@ get_states(NodeIdx) ->
|
||||
get_state(NodeIdx, Entity) ->
|
||||
case catch mnesia:read({pubsub_state, {Entity, NodeIdx}}) of
|
||||
[#pubsub_state{} = State] -> State;
|
||||
_ -> #pubsub_state{id = {Entity, NodeIdx}}
|
||||
_ -> #pubsub_state{id = {Entity, NodeIdx}, nodeidx=NodeIdx}
|
||||
end.
|
||||
|
||||
%% @spec (State) -> ok | {error, Reason::stanzaError()}
|
||||
@@ -1094,7 +1097,7 @@ del_state(NodeIdx, Entity) ->
|
||||
).
|
||||
|
||||
get_items(NodeIdx, _Entity) ->
|
||||
Items = mnesia:match_object(#pubsub_item{id = {'_', NodeIdx}, _ = '_'}),
|
||||
Items = mnesia:index_read(pubsub_item, NodeIdx, #pubsub_item.nodeidx),
|
||||
{result, lists:reverse(lists:keysort(#pubsub_item.modification, Items))}.
|
||||
|
||||
|
||||
|
||||
@@ -307,8 +307,9 @@
|
||||
-type(pubsubNode() :: #pubsub_node{}).
|
||||
|
||||
|
||||
%%% @type pubsubState() = {pubsub_state, Id, Items, Affiliation, Subscriptions}
|
||||
%%% @type pubsubState() = {pubsub_state, Id, NodeIdx, Items, Affiliation, Subscriptions}
|
||||
%%% Id = {fullUsr(), nodeIdx()}
|
||||
%%% NodeIdx = nodeIdx(),
|
||||
%%% Items = [itemId()]
|
||||
%%% Affiliation = affiliation()
|
||||
%%% Subscriptions = [{subscription(), subId()}].
|
||||
@@ -319,6 +320,7 @@
|
||||
-record(pubsub_state,
|
||||
{
|
||||
id :: {fullUsr(), nodeIdx()},
|
||||
nodeidx :: nodeIdx(),
|
||||
items = [] :: [itemId()],
|
||||
affiliation = 'none' :: affiliation(),
|
||||
subscriptions = [] :: [{subscription(), subId()}]
|
||||
@@ -327,8 +329,9 @@
|
||||
-type(pubsubState() :: #pubsub_state{}).
|
||||
|
||||
|
||||
%%% @type pubsubItem() = {pubsub_item, Id, Creation, Modification, Payload}
|
||||
%%% @type pubsubItem() = {pubsub_item, Id, NodeIdx, Creation, Modification, Payload}
|
||||
%%% Id = {itemId(), nodeIdx()}
|
||||
%%% NodeIdx = nodeIdx()
|
||||
%%% Creation = {now(), bareUsr()}
|
||||
%%% Modification = {now(), fullUsr()}
|
||||
%%% Payload = payload().
|
||||
@@ -339,6 +342,7 @@
|
||||
-record(pubsub_item,
|
||||
{
|
||||
id :: {itemId(), nodeIdx()},
|
||||
nodeidx :: nodeIdx(),
|
||||
creation = {unknown,unknown} :: {now(), bareUsr()},
|
||||
modification = {unknown,unknown} :: {now(), fullUsr()},
|
||||
payload = [] :: payload()
|
||||
|
||||
@@ -38,9 +38,6 @@ read_subscription(SubId) ->
|
||||
["select opt_name, opt_value "
|
||||
"from pubsub_subscription_opt "
|
||||
"where subid = '", ejabberd_odbc:escape(SubId), "'"]) of
|
||||
{selected, ["opt_name", "opt_value"], []} ->
|
||||
notfound;
|
||||
|
||||
{selected, ["opt_name", "opt_value"], Options} ->
|
||||
|
||||
{ok, #pubsub_subscription{subid = SubId,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--- mod_pubsub.erl 2011-02-21 15:11:24.000000000 +0100
|
||||
+++ mod_pubsub_odbc.erl 2011-02-21 15:12:03.000000000 +0100
|
||||
--- mod_pubsub.erl 2011-03-23 09:31:16.000000000 +0100
|
||||
+++ mod_pubsub_odbc.erl 2011-03-23 09:51:43.000000000 +0100
|
||||
@@ -42,7 +42,7 @@
|
||||
%%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see
|
||||
%%% XEP-0060 section 12.18.
|
||||
@@ -705,21 +705,19 @@
|
||||
end, FilteredEntities),
|
||||
{result, []};
|
||||
_ ->
|
||||
@@ -2952,11 +2757,11 @@
|
||||
@@ -2952,9 +2757,9 @@
|
||||
end.
|
||||
|
||||
read_sub(Subscriber, Node, NodeId, SubId, Lang) ->
|
||||
- case pubsub_subscription:get_subscription(Subscriber, NodeId, SubId) of
|
||||
+ case pubsub_subscription_odbc:get_subscription(Subscriber, NodeId, SubId) of
|
||||
{error, notfound} ->
|
||||
{error, extended_error('not-acceptable', "invalid-subid")};
|
||||
{result, #pubsub_subscription{options = Options}} ->
|
||||
- {result, XdataEl} = pubsub_subscription:get_options_xform(Lang, Options),
|
||||
+ {result, XdataEl} = pubsub_subscription_odbc:get_options_xform(Lang, Options),
|
||||
OptionsEl = #xmlel{ns = ?NS_PUBSUB, name = 'options',
|
||||
attrs = [ ?XMLATTR(<<"jid">>, exmpp_jid:to_binary(Subscriber)),
|
||||
?XMLATTR(<<"subid">>, SubId) | nodeAttr(Node)],
|
||||
@@ -2983,7 +2788,7 @@
|
||||
@@ -2987,7 +2792,7 @@
|
||||
end.
|
||||
|
||||
set_options_helper(Configuration, JID, NodeId, SubId, Type) ->
|
||||
@@ -728,16 +726,16 @@
|
||||
{result, GoodSubOpts} -> GoodSubOpts;
|
||||
_ -> invalid
|
||||
end,
|
||||
@@ -3013,7 +2818,7 @@
|
||||
write_sub(_Subscriber, _NodeId, _SubId, invalid) ->
|
||||
{error, extended_error('bad-request', "invalid-options")};
|
||||
@@ -3019,7 +2824,7 @@
|
||||
write_sub(_Subscriber, _NodeID, _SubID, []) ->
|
||||
{result, []};
|
||||
write_sub(Subscriber, NodeId, SubId, Options) ->
|
||||
- case pubsub_subscription:set_subscription(Subscriber, NodeId, SubId, Options) of
|
||||
+ case pubsub_subscription_odbc:set_subscription(Subscriber, NodeId, SubId, Options) of
|
||||
{error, notfound} ->
|
||||
{error, extended_error('not-acceptable', "invalid-subid")};
|
||||
{result, _} ->
|
||||
@@ -3187,8 +2992,8 @@
|
||||
{result, []};
|
||||
{error, _} ->
|
||||
@@ -3193,8 +2998,8 @@
|
||||
?XMLATTR(<<"subsription">>, subscription_to_string(Sub)) | nodeAttr(Node)]}]}]},
|
||||
ejabberd_router:route(service_jid(Host), JID, Stanza)
|
||||
end,
|
||||
@@ -748,7 +746,7 @@
|
||||
true ->
|
||||
Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) ->
|
||||
|
||||
@@ -3542,7 +3347,7 @@
|
||||
@@ -3548,7 +3353,7 @@
|
||||
Collection = tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]),
|
||||
{result, [{Depth, [{N, sub_with_options(N)} || N <- Nodes]} || {Depth, Nodes} <- Collection]}
|
||||
end,
|
||||
@@ -757,7 +755,7 @@
|
||||
{result, CollSubs} -> subscribed_nodes_by_jid(NotifyType, CollSubs);
|
||||
_ -> []
|
||||
end.
|
||||
@@ -3611,8 +3416,8 @@
|
||||
@@ -3617,8 +3422,8 @@
|
||||
[]
|
||||
end.
|
||||
sub_with_options(JID, NodeId, SubId) ->
|
||||
@@ -768,7 +766,7 @@
|
||||
_ -> {JID, SubId, []}
|
||||
end.
|
||||
|
||||
@@ -3724,6 +3529,30 @@
|
||||
@@ -3730,6 +3535,30 @@
|
||||
Result
|
||||
end.
|
||||
|
||||
@@ -799,7 +797,7 @@
|
||||
%% @spec (Host, Options) -> MaxItems
|
||||
%% Host = host()
|
||||
%% Options = [Option]
|
||||
@@ -4247,9 +4076,14 @@
|
||||
@@ -4253,9 +4082,14 @@
|
||||
|
||||
tree_action(Host, Function, Args) ->
|
||||
?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]),
|
||||
@@ -817,7 +815,7 @@
|
||||
|
||||
%% @doc <p>node plugin call.</p>
|
||||
-spec(node_call/3 ::
|
||||
@@ -4287,7 +4121,7 @@
|
||||
@@ -4293,7 +4127,7 @@
|
||||
|
||||
node_action(Host, Type, Function, Args) ->
|
||||
?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]),
|
||||
@@ -826,7 +824,7 @@
|
||||
node_call(Type, Function, Args)
|
||||
end, sync_dirty).
|
||||
|
||||
@@ -4302,7 +4136,7 @@
|
||||
@@ -4308,7 +4142,7 @@
|
||||
).
|
||||
|
||||
transaction(Host, NodeId, Action, Trans) ->
|
||||
@@ -835,7 +833,7 @@
|
||||
case tree_call(Host, get_node, [Host, NodeId]) of
|
||||
#pubsub_node{} = Node ->
|
||||
case Action(Node) of
|
||||
@@ -4316,7 +4150,7 @@
|
||||
@@ -4322,7 +4156,7 @@
|
||||
end, Trans).
|
||||
|
||||
|
||||
@@ -844,7 +842,7 @@
|
||||
(
|
||||
Host :: string() | host(),
|
||||
Action :: fun(),
|
||||
@@ -4324,21 +4158,28 @@
|
||||
@@ -4330,21 +4164,28 @@
|
||||
-> {'result', Nodes :: [] | [Node::pubsubNode()]}
|
||||
).
|
||||
|
||||
@@ -878,7 +876,7 @@
|
||||
{result, Result} -> {result, Result};
|
||||
{error, Error} -> {error, Error};
|
||||
{atomic, {result, Result}} -> {result, Result};
|
||||
@@ -4346,6 +4187,15 @@
|
||||
@@ -4352,6 +4193,15 @@
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
|
||||
{error, 'internal-server-error'};
|
||||
@@ -894,7 +892,7 @@
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]),
|
||||
{error, 'internal-server-error'};
|
||||
@@ -4354,6 +4204,16 @@
|
||||
@@ -4360,6 +4210,16 @@
|
||||
{error, 'internal-server-error'}
|
||||
end.
|
||||
|
||||
|
||||
@@ -223,6 +223,8 @@ create_table() ->
|
||||
-> SubId::subId()
|
||||
).
|
||||
|
||||
add_subscription(_JID, _NodeID, []) ->
|
||||
make_subid();
|
||||
add_subscription(_Entity, _NodeIdx, Options) ->
|
||||
SubId = make_subid(),
|
||||
mnesia:write(#pubsub_subscription{subid = SubId, options = Options}),
|
||||
@@ -262,14 +264,11 @@ read_subscription(_Entity, _NodeIdx, SubId) ->
|
||||
NodeIdx :: nodeIdx(),
|
||||
SubId :: subId(),
|
||||
Options :: [nodeOption()])
|
||||
-> 'ok' | {'error', 'notfound'}
|
||||
).
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
write_subscription(Entity, NodeIdx, SubId, Options) ->
|
||||
case read_subscription(Entity, NodeIdx, SubId) of
|
||||
{error, 'notfound'} -> {error, 'notfound'};
|
||||
Subscription -> mnesia:write(Subscription#pubsub_subscription{options = Options})
|
||||
end.
|
||||
write_subscription(_Entity, _NodeIdx, SubId, Options) ->
|
||||
mnesia:write(#pubsub_subscription{subid = SubId, options = Options}).
|
||||
|
||||
|
||||
-spec(make_subid/0 :: () -> SubId::subId()).
|
||||
|
||||
@@ -108,8 +108,10 @@ process_iq(From, To,
|
||||
PTag = exmpp_xml:get_element(SubEl, 'password'),
|
||||
RTag = exmpp_xml:get_element(SubEl, 'remove'),
|
||||
Server = exmpp_jid:prep_domain_as_list(To),
|
||||
Access = gen_mod:get_module_opt(Server, ?MODULE, access, all),
|
||||
AllowRemove = (allow == acl:match_rule(Server, Access, From)),
|
||||
if
|
||||
(UTag /= undefined) and (RTag /= undefined) ->
|
||||
(UTag /= undefined) and (RTag /= undefined) and AllowRemove ->
|
||||
User = exmpp_xml:get_cdata_as_list(UTag),
|
||||
case {exmpp_jid:node_as_list(From), exmpp_jid:prep_domain_as_list(From)} of
|
||||
{User, Server} ->
|
||||
@@ -144,7 +146,7 @@ process_iq(From, To,
|
||||
exmpp_iq:error(IQ_Rec, 'bad-request')
|
||||
end
|
||||
end;
|
||||
(UTag == undefined) and (RTag /= undefined) ->
|
||||
(UTag == undefined) and (RTag /= undefined) and AllowRemove ->
|
||||
case {exmpp_jid:node_as_list(From),
|
||||
exmpp_jid:prep_domain_as_list(From),
|
||||
exmpp_jid:resource_as_list(From)}of
|
||||
|
||||
+11
-14
@@ -258,8 +258,7 @@ roster_version_on_db(Host) ->
|
||||
get_versioning_feature(Acc, Host) ->
|
||||
case roster_versioning_enabled(Host) of
|
||||
true ->
|
||||
Feature = exmpp_xml:element(?NS_ROSTER_VER_s, 'ver', [],
|
||||
[exmpp_xml:element(?NS_ROSTER_VER_s, 'optional')]),
|
||||
Feature = exmpp_xml:element(?NS_ROSTER_VER_s, 'ver'),
|
||||
[Feature | Acc];
|
||||
false -> []
|
||||
end.
|
||||
@@ -658,10 +657,18 @@ push_item(User, Server, From, Item)
|
||||
%% Item = rosteritem()
|
||||
|
||||
% TODO: don't push to those who didn't load roster
|
||||
push_item(User, Server, Resource, From, Item)
|
||||
push_item(User, Server, Resource, From, Item) ->
|
||||
push_item(User, Server, Resource, From, Item, not_found).
|
||||
|
||||
push_item(User, Server, Resource, From, Item, RosterVersion)
|
||||
when is_binary(User), is_binary(Server), is_binary(Resource),
|
||||
?IS_JID(From) ->
|
||||
ExtraAttrs = case RosterVersion of
|
||||
not_found -> [];
|
||||
_ -> [?XMLATTR(<<"ver">>, RosterVersion)]
|
||||
end,
|
||||
Request = #xmlel{ns = ?NS_ROSTER, name = 'query',
|
||||
attrs = ExtraAttrs,
|
||||
children = [item_to_xml(Item)]},
|
||||
ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request,
|
||||
"push" ++ randoms:get_string()),
|
||||
@@ -674,19 +681,9 @@ push_item(User, Server, Resource, From, Item)
|
||||
%% TODO: don't push to those who didn't load roster
|
||||
push_item_version(Server, User, From, Item, RosterVersion) ->
|
||||
lists:foreach(fun(Resource) ->
|
||||
push_item_version(User, Server, Resource, From, Item, RosterVersion)
|
||||
push_item(User, Server, Resource, From, Item, RosterVersion)
|
||||
end, ejabberd_sm:get_user_resources(User, Server)).
|
||||
|
||||
push_item_version(User, Server, Resource, From, Item, RosterVersion) ->
|
||||
Request = #xmlel{ns = ?NS_ROSTER, name = 'query', attrs = [?XMLATTR(<<"ver">>, RosterVersion)],
|
||||
children = [mod_roster:item_to_xml(Item)]},
|
||||
ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request,
|
||||
"push" ++ randoms:get_string()),
|
||||
ejabberd_router:route(
|
||||
From,
|
||||
exmpp_jid:make(User, Server, Resource),
|
||||
ResIQ).
|
||||
|
||||
%% @spec (Ignored, User, Server) -> Subscription_Lists
|
||||
%% Ignored = term()
|
||||
%% User = binary()
|
||||
|
||||
@@ -300,7 +300,7 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
%% For a given user, map all his shared roster contacts to groups they are
|
||||
%% members of. Skip the user himself iff SkipUS is true.
|
||||
%% members of. Skip the user himself if SkipUS is true.
|
||||
get_user_to_groups_map({_, Server} = US, SkipUS) ->
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
lists:foldl(
|
||||
|
||||
+37
-25
@@ -29,6 +29,12 @@
|
||||
%%% 2.1.x / mnesia / vcard
|
||||
%%% us = {Username::string(), Host::string()}
|
||||
%%% vcard = xmlelement()
|
||||
%%% 2.1.x / mnesia / vcard_search
|
||||
%%% us = {Username::string(), Host::string()}
|
||||
%%% user = {Username::string(), Host::string()}
|
||||
%%% luser = Username::string()
|
||||
%%% fn = string()
|
||||
%%% ... = string()
|
||||
%%%
|
||||
%%% 2.1.x / odbc / vcard
|
||||
%%% username = varchar250
|
||||
@@ -42,8 +48,14 @@
|
||||
%%% Same as 2.1.x
|
||||
%%%
|
||||
%%% 3.0.0-alpha / mnesia / vcard
|
||||
%%% user_host = {Username::string(), Host::string()}
|
||||
%%% user_host = {Username::binary(), Host::binary()}
|
||||
%%% vcard = xmlel()
|
||||
%%% 3.0.0-alpha / mnesia / vcard_search
|
||||
%%% user_host = {Username::binary(), Host::binary()}
|
||||
%%% username = string()
|
||||
%%% lusername = string()
|
||||
%%% fn = string()
|
||||
%%% ... = string()
|
||||
%%%
|
||||
%%% 3.0.0-alpha / odbc / vcard
|
||||
%%% user = varchar150
|
||||
@@ -231,12 +243,12 @@ process_sm_iq(_From, To, #iq{type = get} = IQ_Rec) ->
|
||||
exmpp_iq:result(IQ_Rec)
|
||||
end;
|
||||
process_sm_iq(From, _To, #iq{type = set, payload = Request} = IQ_Rec) ->
|
||||
User = exmpp_jid:node_as_list(From),
|
||||
LServer = exmpp_jid:prep_domain_as_list(From),
|
||||
LServerB = exmpp_jid:prep_domain(From),
|
||||
case ?IS_MY_HOST(LServer) of
|
||||
User = exmpp_jid:node(From),
|
||||
Server = exmpp_jid:prep_domain(From),
|
||||
ServerS = exmpp_jid:prep_domain_as_list(From),
|
||||
case ?IS_MY_HOST(ServerS) of
|
||||
true ->
|
||||
set_vcard(User, LServer, LServerB, Request),
|
||||
set_vcard(User, Server, Request),
|
||||
exmpp_iq:result(IQ_Rec);
|
||||
false ->
|
||||
exmpp_iq:error(IQ_Rec, 'not-allowed')
|
||||
@@ -258,7 +270,7 @@ get_vcard(User, Host) ->
|
||||
end.
|
||||
|
||||
|
||||
set_vcard(User, LServer, LServerB, VCARD) ->
|
||||
set_vcard(User, Server, VCARD) ->
|
||||
FN = exmpp_xml:get_path(VCARD,
|
||||
[{element, 'FN'}, cdata_as_list]),
|
||||
Family = exmpp_xml:get_path(VCARD,
|
||||
@@ -291,7 +303,8 @@ set_vcard(User, LServer, LServerB, VCARD) ->
|
||||
end,
|
||||
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
UserStr = binary_to_list(User),
|
||||
LUser = binary_to_list(exmpp_stringprep:nodeprep(User)),
|
||||
LFN = exmpp_stringprep:to_lower(FN),
|
||||
LFamily = exmpp_stringprep:to_lower(Family),
|
||||
LGiven = exmpp_stringprep:to_lower(Given),
|
||||
@@ -304,18 +317,18 @@ set_vcard(User, LServer, LServerB, VCARD) ->
|
||||
LOrgName = exmpp_stringprep:to_lower(OrgName),
|
||||
LOrgUnit = exmpp_stringprep:to_lower(OrgUnit),
|
||||
|
||||
US = {LUser, LServer},
|
||||
US = {User, Server},
|
||||
|
||||
VcardToStore = case gen_storage:table_info(LServerB, vcard, backend) of
|
||||
VcardToStore = case gen_storage:table_info(Server, vcard, backend) of
|
||||
mnesia -> VCARD;
|
||||
odbc -> lists:flatten(exmpp_xml:document_to_list(VCARD))
|
||||
end,
|
||||
|
||||
F = fun() ->
|
||||
gen_storage:write(LServerB, #vcard{user_host = US, vcard = VcardToStore}),
|
||||
gen_storage:write(LServerB,
|
||||
gen_storage:write(Server, #vcard{user_host = US, vcard = VcardToStore}),
|
||||
gen_storage:write(Server,
|
||||
#vcard_search{user_host=US,
|
||||
username = User, lusername = LUser,
|
||||
username = UserStr, lusername = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
@@ -329,9 +342,8 @@ set_vcard(User, LServer, LServerB, VCARD) ->
|
||||
orgunit = OrgUnit, lorgunit = LOrgUnit
|
||||
})
|
||||
end,
|
||||
LServerB = list_to_binary(LServer),
|
||||
gen_storage:transaction(LServerB, vcard, F),
|
||||
ejabberd_hooks:run(vcard_set, LServerB, [list_to_binary(LUser), LServerB, VCARD])
|
||||
gen_storage:transaction(Server, vcard, F),
|
||||
ejabberd_hooks:run(vcard_set, Server, [LUser, Server, VCARD])
|
||||
catch
|
||||
_ ->
|
||||
{error, badarg}
|
||||
@@ -536,7 +548,7 @@ record_to_item(R) ->
|
||||
{User, Server} = R#vcard_search.user_host,
|
||||
#xmlel{ns = ?NS_DATA_FORMS, name = 'item', children =
|
||||
[
|
||||
?FIELD(<<"jid">>, list_to_binary(User ++ "@" ++ Server)),
|
||||
?FIELD(<<"jid">>, exmpp_jid:to_binary(User, Server)),
|
||||
?FIELD(<<"fn">>, list_to_binary(R#vcard_search.fn)),
|
||||
?FIELD(<<"last">>, list_to_binary(R#vcard_search.family)),
|
||||
?FIELD(<<"first">>, list_to_binary(R#vcard_search.given)),
|
||||
@@ -729,8 +741,8 @@ reindex_vcards() ->
|
||||
|
||||
|
||||
remove_user(User, Server) when is_binary(User), is_binary(Server) ->
|
||||
LUser = binary_to_list(exmpp_stringprep:nodeprep(User)),
|
||||
LServer = binary_to_list(exmpp_stringprep:nameprep(Server)),
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
gen_storage:delete(Server, {vcard, US}),
|
||||
@@ -747,14 +759,14 @@ update_tables(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, vcard,
|
||||
[{vcard, [us, vcard],
|
||||
fun({vcard, US, Vcard}) ->
|
||||
#vcard{user_host = US,
|
||||
fun({vcard, {UserS, ServerS}, Vcard}) ->
|
||||
#vcard{user_host = {list_to_binary(UserS), list_to_binary(ServerS)},
|
||||
vcard = convert_vcard_element(Vcard)}
|
||||
end}]),
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, vcard_search,
|
||||
[{vcard_search, [us,
|
||||
username, lusername,
|
||||
user, luser,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
@@ -766,8 +778,9 @@ update_tables(Host, mnesia) ->
|
||||
email, lemail,
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit],
|
||||
fun(Record) ->
|
||||
Record
|
||||
fun(#vcard_search{user_host = {UserS, ServerS}} = Record) ->
|
||||
UserHost = {list_to_binary(UserS), list_to_binary(ServerS)},
|
||||
Record#vcard_search{user_host = UserHost, username = UserS}
|
||||
end}]);
|
||||
|
||||
update_tables(Host, odbc) ->
|
||||
@@ -896,7 +909,6 @@ get_user_photo(User, Host) ->
|
||||
|
||||
user_queue_parse_query(US, Query) ->
|
||||
{User, Server} = US,
|
||||
?INFO_MSG("Query vcard: ~p", [Query]), %+++
|
||||
case lists:keysearch("removevcard", 1, Query) of
|
||||
{value, _} ->
|
||||
case remove_user(list_to_binary(User), list_to_binary(Server)) of
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Mòdul ejannerd Publicar-Subscriure"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","mòdul ejabberd SOCKS5 Bytestreams"}.
|
||||
{"ejabberd vCard module","Mòdul ejabberd vCard"}.
|
||||
{"ejabberd virtual hosts","Hosts virtuals del ejabberd"}.
|
||||
{"ejabberd Web Admin","Web d'administració del ejabberd"}.
|
||||
{"Elements","Elements"}.
|
||||
{"Email","Email"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Usuari"}.
|
||||
{"Validate","Validar"}.
|
||||
{"vCard User Search","Recerca de vCard d'usuari"}.
|
||||
{"Virtual Hosts","Hosts virtuals"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Els visitants no tenen permés canviar el seus Nicknames en esta sala"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Els visitants no poden enviar missatges a tots els ocupants"}.
|
||||
{"Wednesday","Dimecres"}.
|
||||
|
||||
+3
-3
@@ -1237,9 +1237,9 @@ msgstr "en format text"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Configuració de les Regles d'Accés ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Hosts virtuals del ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Hosts virtuals"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}.
|
||||
{"ejabberd vCard module","ejabberd vCard modul"}.
|
||||
{"ejabberd virtual hosts","Virtuální hostitelé ejabberd"}.
|
||||
{"ejabberd Web Admin","Webová administrace ejabberd"}.
|
||||
{"Elements","Položek"}.
|
||||
{"Email","E-mail"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Uživatel"}.
|
||||
{"Validate","Ověřit"}.
|
||||
{"vCard User Search","Hledání uživatelů podle vizitek"}.
|
||||
{"Virtual Hosts","Virtuální hostitelé"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Návštěvníkům této místnosti je zakázáno měnit přezdívku"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Návštevníci nemají povoleno zasílat zprávy všem účastníkům konference"}.
|
||||
{"Wednesday","Středa"}.
|
||||
|
||||
+3
-3
@@ -1227,9 +1227,9 @@ msgstr "Zdroj"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s konfigurace pravidla přístupu"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Virtuální hostitelé ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Virtuální hostitelé"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe-Modul"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5-Bytestreams-Modul"}.
|
||||
{"ejabberd vCard module","ejabberd vCard-Modul"}.
|
||||
{"ejabberd virtual hosts","ejabberd virtuelle Hosts"}.
|
||||
{"ejabberd Web Admin","ejabberd Web-Admin"}.
|
||||
{"Elements","Elemente"}.
|
||||
{"Email","E-Mail"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"Users Last Activity","Letzte Benutzeraktivität"}.
|
||||
{"Validate","Validieren"}.
|
||||
{"vCard User Search","vCard-Benutzer-Suche"}.
|
||||
{"Virtual Hosts","Virtuelle Hosts"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Besucher dürfen in diesem Raum ihren Spitznamen nicht ändern"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Besucher dürfen nicht an alle Teilnehmer Nachrichten verschicken"}.
|
||||
{"Wednesday","Mittwoch"}.
|
||||
|
||||
+3
-3
@@ -1249,9 +1249,9 @@ msgstr "Unformatiert"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s Zugangsregel-Konfiguration"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "ejabberd virtuelle Hosts"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Virtuelle Hosts"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
@@ -1208,8 +1208,8 @@ msgstr ""
|
||||
msgid "~s access rule configuration"
|
||||
msgstr ""
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr ""
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd module Δημοσίευσης-Εγγραφής"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}.
|
||||
{"ejabberd vCard module","ejabberd vCard module"}.
|
||||
{"ejabberd virtual hosts","εικονικοί κεντρικοί υπολογιστές ejabberd"}.
|
||||
{"ejabberd Web Admin","ejabberd Web Admin"}.
|
||||
{"Elements","Στοιχεία"}.
|
||||
{"Email","Email"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Χρήστης"}.
|
||||
{"Validate","Επαληθεύστε"}.
|
||||
{"vCard User Search","vCard Αναζήτηση χρηστών"}.
|
||||
{"Virtual Hosts","εικονικοί κεντρικοί υπολογιστές"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Οι επισκέπτες δεν επιτρέπεται να αλλάξουν τα ψευδώνυμα τους σε αυτή την αίθουσα"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Οι επισκέπτες δεν επιτρέπεται να στείλουν μηνύματα σε όλους τους συμμετέχωντες"}.
|
||||
{"Wednesday","Τετάρτη"}.
|
||||
|
||||
+3
-3
@@ -1256,9 +1256,9 @@ msgstr "Ακατέργαστο"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s διαμόρφωση κανόνα πρόσβασης"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "εικονικοί κεντρικοί υπολογιστές ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "εικονικοί κεντρικοί υπολογιστές"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Public-Abonada modulo"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bajtfluo modulo"}.
|
||||
{"ejabberd vCard module","ejabberd vCard-modulo"}.
|
||||
{"ejabberd virtual hosts","ejabberd virtual-gastigoj"}.
|
||||
{"ejabberd Web Admin","ejabberd Teksaĵa Administro"}.
|
||||
{"Elements","Eroj"}.
|
||||
{"Email","Retpoŝto"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Uzanto"}.
|
||||
{"Validate","Validigu"}.
|
||||
{"vCard User Search","Serĉado de vizitkartoj"}.
|
||||
{"Virtual Hosts","Virtual-gastigoj"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Ne estas permesata al vizitantoj ŝanĝi siajn kaŝnomojn en ĉi tiu ĉambro"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Vizitantoj ne rajtas sendi mesaĝojn al ĉiuj partoprenantoj"}.
|
||||
{"Wednesday","Merkredo"}.
|
||||
|
||||
+3
-6
@@ -1234,9 +1234,9 @@ msgstr "Kruda"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Agordo de atingo-reguloj de ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "ejabberd virtual-gastigoj"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Virtual-gastigoj"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1683,9 +1683,6 @@ msgstr "Startu"
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(Kruda)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "Virtual-gastigoj"
|
||||
|
||||
#~ msgid "Specified nickname is already registered"
|
||||
#~ msgstr "Donita kaŝnomo jam estas registrita"
|
||||
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Módulo de Publicar-Subscribir de ejabberd"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","Módulo SOCKS5 Bytestreams para ejabberd"}.
|
||||
{"ejabberd vCard module","Módulo vCard para ejabberd"}.
|
||||
{"ejabberd virtual hosts","Dominios de ejabberd"}.
|
||||
{"ejabberd Web Admin","ejabberd Web Admin"}.
|
||||
{"Elements","Elementos"}.
|
||||
{"Email","correo"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Usuario"}.
|
||||
{"Validate","Validar"}.
|
||||
{"vCard User Search","Buscar vCard de usuario"}.
|
||||
{"Virtual Hosts","Dominios Virtuales"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Los visitantes no tienen permitido cambiar sus apodos en esta sala"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Los visitantes no pueden enviar mensajes a todos los ocupantes"}.
|
||||
{"Wednesday","miércoles"}.
|
||||
|
||||
+3
-3
@@ -1242,9 +1242,9 @@ msgstr "Crudo"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Configuración de las Regla de Acceso ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Dominios de ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Dominios Virtuales"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Module Publish-Subscribe d'ejabberd"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}.
|
||||
{"ejabberd vCard module","Module vCard ejabberd"}.
|
||||
{"ejabberd virtual hosts","Serveurs virtuels d'ejabberd"}.
|
||||
{"ejabberd Web Admin","Console Web d'administration de ejabberd"}.
|
||||
{"Elements","Éléments"}.
|
||||
{"Email","Email"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Utilisateur"}.
|
||||
{"Validate","Valider"}.
|
||||
{"vCard User Search","Recherche dans l'annnuaire"}.
|
||||
{"Virtual Hosts","Serveurs virtuels"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Les visiteurs ne sont pas autorisés à changer de pseudo dans ce salon"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Les visiteurs ne sont pas autorisés à envoyer des messages à tout les occupants"}.
|
||||
{"Wednesday","Mercredi"}.
|
||||
|
||||
+3
-6
@@ -1252,9 +1252,9 @@ msgstr "Brut"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Configuration des règles d'accès ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Serveurs virtuels d'ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Serveurs virtuels"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1711,6 +1711,3 @@ msgstr "Démarrer"
|
||||
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(Brut)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "Serveurs virtuels"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Módulo de Publicar-Subscribir de ejabberd"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}.
|
||||
{"ejabberd vCard module","Módulo vCard para ejabberd"}.
|
||||
{"ejabberd virtual hosts","Hosts virtuais de ejabberd"}.
|
||||
{"ejabberd Web Admin","Ejabberd Administrador Web"}.
|
||||
{"Elements","Elementos"}.
|
||||
{"Email","Email"}.
|
||||
@@ -319,6 +318,7 @@
|
||||
{"User","Usuario"}.
|
||||
{"Validate","Validar"}.
|
||||
{"vCard User Search","Procura de usuario en vCard"}.
|
||||
{"Virtual Hosts","Hosts Virtuais"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Os visitantes non están autorizados a cambiar os seus That alcumes nesta sala"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Os visitantes non poden enviar mensaxes a todos os ocupantes"}.
|
||||
{"Wednesday","Mércores"}.
|
||||
|
||||
+3
-6
@@ -1236,9 +1236,9 @@ msgstr "Cru"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Configuración das Regra de Acceso ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Hosts virtuais de ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Hosts Virtuais"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1694,9 +1694,6 @@ msgstr "Iniciar"
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(Cru)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "Hosts Virtuais"
|
||||
|
||||
#~ msgid "Specified nickname is already registered"
|
||||
#~ msgstr "O alcume especificado xa está rexistrado, terás que buscar outro"
|
||||
|
||||
|
||||
+1
-1
@@ -80,7 +80,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Modul ejabberd Setujui-Pertemanan"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","modul ejabberd SOCKS5 Bytestreams"}.
|
||||
{"ejabberd vCard module","Modul ejabberd vCard"}.
|
||||
{"ejabberd virtual hosts","ejabberd virtual hosts"}.
|
||||
{"ejabberd Web Admin","Admin Web ejabberd"}.
|
||||
{"Elements","Elemen-elemen"}.
|
||||
{"Email","Email"}.
|
||||
@@ -388,6 +387,7 @@
|
||||
{"Users","Pengguna"}.
|
||||
{"Validate","Mengesahkan"}.
|
||||
{"vCard User Search","vCard Pencarian Pengguna"}.
|
||||
{"Virtual Hosts","Virtual Hosts"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Visitor tidak diperbolehkan untuk mengubah nama julukan di ruangan ini"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Visitor tidak diperbolehkan untuk mengirim pesan ke semua penghuni"}.
|
||||
{"Wednesday","Rabu"}.
|
||||
|
||||
+2
-2
@@ -1362,8 +1362,8 @@ msgid "~s access rule configuration"
|
||||
msgstr "~s aturan akses konfigurasi"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "ejabberd virtual hosts"
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Virtual Hosts"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1039 web/ejabberd_web_admin.erl:1046
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Modulo Pubblicazione/Iscrizione (PubSub) per ejabberd"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","Modulo SOCKS5 Bytestreams per ejabberd"}.
|
||||
{"ejabberd vCard module","Modulo vCard per ejabberd"}.
|
||||
{"ejabberd virtual hosts","Host virtuali di ejabberd"}.
|
||||
{"ejabberd Web Admin","Amministrazione web ejabberd"}.
|
||||
{"Elements","Elementi"}.
|
||||
{"Email","E-mail"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Utente"}.
|
||||
{"Validate","Validare"}.
|
||||
{"vCard User Search","Ricerca di utenti per vCard"}.
|
||||
{"Virtual Hosts","Host Virtuali"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Non è consentito ai visitatori cambiare il nickname in questa stanza"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Non è consentito ai visitatori l'invio di messaggi a tutti i presenti"}.
|
||||
{"Wednesday","Mercoledì"}.
|
||||
|
||||
+3
-3
@@ -1243,9 +1243,9 @@ msgstr "Grezzo"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Configurazione delle regole di accesso per ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Host virtuali di ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Host Virtuali"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe module"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}.
|
||||
{"ejabberd vCard module","ejabberd vCard module"}.
|
||||
{"ejabberd virtual hosts","ejabberd ヴァーチャルホスト"}.
|
||||
{"ejabberd Web Admin","ejabberd Web 管理"}.
|
||||
{"Elements","要素"}.
|
||||
{"Email","メールアドレス"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"Users Last Activity","ユーザーの活動履歴"}.
|
||||
{"Validate","検証"}.
|
||||
{"vCard User Search","vCard ユーザー検索"}.
|
||||
{"Virtual Hosts","ヴァーチャルホスト"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","ビジターはこのチャットルームでニックネームを変更することは許可されていません"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","ビジターが移住者ににメッセージを送ることは許可されていません"}.
|
||||
{"Wednesday","水曜日"}.
|
||||
|
||||
+3
-6
@@ -1232,9 +1232,9 @@ msgstr "Raw"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s アクセスルール設定"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "ejabberd ヴァーチャルホスト"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "ヴァーチャルホスト"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1680,9 +1680,6 @@ msgstr "開始"
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(Raw)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "ヴァーチャルホスト"
|
||||
|
||||
#~ msgid "Specified nickname is already registered"
|
||||
#~ msgstr "指定されたニックネームは既に登録されています"
|
||||
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe module"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}.
|
||||
{"ejabberd vCard module","ejabberd's vCard-module"}.
|
||||
{"ejabberd virtual hosts","Virtuele hosts"}.
|
||||
{"ejabberd Web Admin","ejabberd Webbeheer"}.
|
||||
{"Elements","Elementen"}.
|
||||
{"Email","E-mail"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"Users Last Activity","Laatste activiteit van gebruikers"}.
|
||||
{"Validate","Bevestigen"}.
|
||||
{"vCard User Search","Gebruikers zoeken"}.
|
||||
{"Virtual Hosts","Virtuele hosts"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Het is bezoekers niet toegestaan hun naam te veranderen in dit kanaal"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Bezoekers mogen geen berichten verzenden naar alle aanwezigen"}.
|
||||
{"Wednesday","Woensdag"}.
|
||||
|
||||
+2
-2
@@ -1246,8 +1246,8 @@ msgstr "Ruw"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Access rules op ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Virtuele hosts"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}.
|
||||
{"ejabberd vCard module","ejabberd vCard modul"}.
|
||||
{"ejabberd virtual hosts","virtuella ejabberd maskiner"}.
|
||||
{"ejabberd Web Admin","ejabberd Web Admin"}.
|
||||
{"Elements","Elementer"}.
|
||||
{"Email","Epost"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"Users Last Activity","Brukers Siste Aktivitet"}.
|
||||
{"Validate","Bekrefte gyldighet"}.
|
||||
{"vCard User Search","vCard Bruker Søk"}.
|
||||
{"Virtual Hosts","Virtuella Maskiner"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Besøkende får ikke lov å endre kallenavn i dette "}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Besøkende får ikke sende meldinger til alle deltakere"}.
|
||||
{"Wednesday","onsdag"}.
|
||||
|
||||
+3
-3
@@ -1229,9 +1229,9 @@ msgstr "Rå"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "tilgangsregel konfigurasjon for ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "virtuella ejabberd maskiner"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Virtuella Maskiner"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Moduł Publish-Subscribe"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","Moduł SOCKS5 Bytestreams"}.
|
||||
{"ejabberd vCard module","Moduł vCard ejabberd"}.
|
||||
{"ejabberd virtual hosts","wirtualne hosty ejabberd"}.
|
||||
{"ejabberd Web Admin","ejabberd: Panel Administracyjny"}.
|
||||
{"Elements","Elementy"}.
|
||||
{"Email","Email"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Użytkownik"}.
|
||||
{"Validate","Potwierdź"}.
|
||||
{"vCard User Search","Wyszukiwanie vCard użytkowników"}.
|
||||
{"Virtual Hosts","Wirtualne Hosty"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Uczestnicy tego pokoju nie mogą zmieniać swoich nicków"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Odwiedzający nie mogą wysyłać wiadomości do wszystkich obecnych"}.
|
||||
{"Wednesday","Środa"}.
|
||||
|
||||
+3
-3
@@ -1240,9 +1240,9 @@ msgstr "Żródło"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s konfiguracja zasad dostępu"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "wirtualne hosty ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Wirtualne Hosty"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Módulo para Publicar Tópicos do ejabberd"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","Modulo ejabberd SOCKS5 Bytestreams"}.
|
||||
{"ejabberd vCard module","Módulo vCard para ejabberd"}.
|
||||
{"ejabberd virtual hosts","Maquinas virtuais de ejabberd"}.
|
||||
{"ejabberd Web Admin","ejabberd Web Admin"}.
|
||||
{"Elements","Elementos"}.
|
||||
{"Email","e-mail"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Usuário"}.
|
||||
{"Validate","Validar"}.
|
||||
{"vCard User Search","Busca de Usuário vCard"}.
|
||||
{"Virtual Hosts","Hosts virtuais"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Nesta sala, os visitantes não pode mudar seus apelidos"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Os visitantes não podem enviar mensagens a todos os ocupantes"}.
|
||||
{"Wednesday","Quarta"}.
|
||||
|
||||
+3
-3
@@ -1239,9 +1239,9 @@ msgstr "Intocado"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Configuração da Regra de Acesso ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Maquinas virtuais de ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Hosts virtuais"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+2
-2
@@ -1292,8 +1292,8 @@ msgstr "Configuração das Regra de Acesso ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
#, fuzzy
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Estatísticas do ejabberd"
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Servidores virtuales"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Модуль ejabberd Публикации-Подписки"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams модуль"}.
|
||||
{"ejabberd vCard module","ejabberd vCard модуль"}.
|
||||
{"ejabberd virtual hosts","Виртуальные хосты ejabberd"}.
|
||||
{"ejabberd Web Admin","Web-интерфейс администрирования ejabberd"}.
|
||||
{"Elements","Элементы"}.
|
||||
{"Email","Электронная почта"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Пользователь"}.
|
||||
{"Validate","Утвердить"}.
|
||||
{"vCard User Search","Поиск пользователей по vCard"}.
|
||||
{"Virtual Hosts","Виртуальные хосты"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Посетителям запрещено изменять свои псевдонимы в этой комнате"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Посетителям не разрешается посылать сообщения всем присутствующим"}.
|
||||
{"Wednesday","Среда"}.
|
||||
|
||||
+3
-3
@@ -1235,9 +1235,9 @@ msgstr "Необработанный формат"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Конфигурация правила доступа ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Виртуальные хосты ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Виртуальные хосты"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modul"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}.
|
||||
{"ejabberd vCard module","ejabberd vCard modul"}.
|
||||
{"ejabberd virtual hosts","ejabberd virtuálne servery"}.
|
||||
{"ejabberd Web Admin","ejabberd Web Admin"}.
|
||||
{"Elements","Prvky"}.
|
||||
{"Email","E-mail"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Užívateľ"}.
|
||||
{"Validate","Overiť"}.
|
||||
{"vCard User Search","Hľadať užívateľov vo vCard"}.
|
||||
{"Virtual Hosts","Virtuálne servery"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","V tejto miestnosti nieje povolené meniť prezývky"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Návštevníci nemajú povolené zasielať správy všetkým prihláseným do konferencie"}.
|
||||
{"Wednesday","Streda"}.
|
||||
|
||||
+3
-6
@@ -1232,9 +1232,9 @@ msgstr "Raw"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s konfigurácia prístupového pravidla"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "ejabberd virtuálne servery"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Virtuálne servery"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1681,9 +1681,6 @@ msgstr "Štart"
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(Raw)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "Virtuálne servery"
|
||||
|
||||
#~ msgid "Specified nickname is already registered"
|
||||
#~ msgstr "Zadaná prezývka je už registrovaná"
|
||||
|
||||
|
||||
+1
-1
@@ -65,7 +65,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd publikprenumerations modul"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestrem modul"}.
|
||||
{"ejabberd vCard module","ejabberd vCard-modul"}.
|
||||
{"ejabberd virtual hosts","Virtuella ejabberd-servrar"}.
|
||||
{"ejabberd Web Admin","ejabberd Web Admin"}.
|
||||
{"Elements","Elements"}.
|
||||
{"Email","Email"}.
|
||||
@@ -312,6 +311,7 @@
|
||||
{"Users Last Activity","Användarens senaste aktivitet"}.
|
||||
{"Validate","Validera"}.
|
||||
{"vCard User Search","vCard användare sök"}.
|
||||
{"Virtual Hosts","Virtuella servrar"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Det är inte tillåtet for gäster att ändra sina smeknamn i detta rummet"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Besökare får inte skicka medelande till alla"}.
|
||||
{"Wednesday","Onsdag"}.
|
||||
|
||||
+3
-3
@@ -1245,9 +1245,9 @@ msgstr "Ra"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Åtkomstregelkonfiguration för ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Virtuella ejabberd-servrar"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Virtuella servrar"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -60,7 +60,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe module"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}.
|
||||
{"ejabberd vCard module","ejabberd vCard module"}.
|
||||
{"ejabberd virtual hosts","โฮสต์เสมือน ejabberd"}.
|
||||
{"Email","อีเมล"}.
|
||||
{"Enable logging","เปิดใช้งานการบันทึก"}.
|
||||
{"End User Session","สิ้นสุดเซสชันของผู้ใช้"}.
|
||||
@@ -277,6 +276,7 @@
|
||||
{"Users Last Activity","กิจกรรมล่าสุดของผู้ใช้"}.
|
||||
{"Validate","ตรวจสอบ"}.
|
||||
{"vCard User Search","ค้นหาผู้ใช้ vCard "}.
|
||||
{"Virtual Hosts","โฮสต์เสมือน"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","ผู้เยี่ยมเยือนไม่ได้รับอนุญาตให้ส่งข้อความถึงผู้ครอบครองห้องทั้งหมด"}.
|
||||
{"Wednesday","วันพุธ"}.
|
||||
{"When to send the last published item","เวลาที่ส่งรายการที่เผยแพร่ครั้งล่าสุด"}.
|
||||
|
||||
+3
-6
@@ -1243,9 +1243,9 @@ msgstr "ข้อมูลดิบ"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s การกำหนดค่ากฎการเข้าถึง"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "โฮสต์เสมือน ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "โฮสต์เสมือน"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1673,9 +1673,6 @@ msgstr "เริ่ม"
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(ข้อมูลดิบ)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "โฮสต์เสมือน"
|
||||
|
||||
#~ msgid "Specified nickname is already registered"
|
||||
#~ msgstr "ชื่อเล่นที่ระบุได้รับการลงได้ทะเบียนแล้ว"
|
||||
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe modülü"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modülü"}.
|
||||
{"ejabberd vCard module","ejabberd vCard modülü"}.
|
||||
{"ejabberd virtual hosts","ejabberd sanal sunucuları"}.
|
||||
{"ejabberd Web Admin","ejabberd Web Yöneticisi"}.
|
||||
{"Elements","Elementler"}.
|
||||
{"Email","E-posta"}.
|
||||
@@ -319,6 +318,7 @@
|
||||
{"Users Last Activity","Kullanıcıların Son Aktiviteleri"}.
|
||||
{"Validate","Geçerli"}.
|
||||
{"vCard User Search","vCard Kullanıcı Araması"}.
|
||||
{"Virtual Hosts","Sanal Sunucuları"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Bu odada ziyaretçilerin takma isimlerini değiştirmesine izin verilmiyor"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Ziyaretçilerin odadaki tüm sakinlere mesaj göndermesine izin verilmiyor"}.
|
||||
{"Wednesday","Çarşamba"}.
|
||||
|
||||
+3
-3
@@ -1244,9 +1244,9 @@ msgstr "Ham"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s erişim kuralları ayarları"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "ejabberd sanal sunucuları"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Sanal Sunucuları"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Модуль ejabberd Публікації-Абонування"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams модуль"}.
|
||||
{"ejabberd vCard module","ejabberd vCard модуль"}.
|
||||
{"ejabberd virtual hosts","віртуальні хости ejabberd"}.
|
||||
{"ejabberd Web Admin","Веб-інтерфейс Адміністрування ejabberd"}.
|
||||
{"Elements","Елементи"}.
|
||||
{"Email","Електронна пошта"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","Користувач"}.
|
||||
{"Validate","Затвердити"}.
|
||||
{"vCard User Search","Пошук користувачів по vCard"}.
|
||||
{"Virtual Hosts","віртуальні хости"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Відвідувачам не дозволяється змінювати псевдонім в цій кімнаті"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Відвідувачам не дозволяється надсилати повідомлення всім присутнім"}.
|
||||
{"Wednesday","Середа"}.
|
||||
|
||||
+3
-3
@@ -1244,9 +1244,9 @@ msgstr "необроблений формат"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Конфігурація правила доступу ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "віртуальні хости ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "віртуальні хости"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
|
||||
+1
-1
@@ -60,7 +60,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Môdun ejabberd Xuất Bản-Đăng Ký Bản quyền"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","Môdun SOCKS5 Bytestreams Bản quyền"}.
|
||||
{"ejabberd vCard module","Môdun ejabberd vCard Bản quyền"}.
|
||||
{"ejabberd virtual hosts","Máy chủ ảo ejabberd"}.
|
||||
{"Email","Email"}.
|
||||
{"Enable logging","Cho phép ghi nhật ký"}.
|
||||
{"End User Session","Kết Thúc Phiên Giao Dịch Người Sử Dụng"}.
|
||||
@@ -277,6 +276,7 @@
|
||||
{"Users","Người sử dụng"}.
|
||||
{"Validate","Xác nhận hợp lệ"}.
|
||||
{"vCard User Search","Tìm Kiếm Người Sử Dụng vCard"}.
|
||||
{"Virtual Hosts","Máy Chủ Ảo"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Người ghé thăm không được phép gửi thư đến tất cả các người tham dự"}.
|
||||
{"Wednesday","Thứ Tư"}.
|
||||
{"When to send the last published item","Khi cần gửi mục được xuất bản cuối cùng"}.
|
||||
|
||||
+3
-6
@@ -1257,9 +1257,9 @@ msgstr "Thô"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s cấu hình quy tắc truy cập"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Máy chủ ảo ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Máy Chủ Ảo"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1697,9 +1697,6 @@ msgstr "Khởi động"
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(Thô)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "Máy Chủ Ảo"
|
||||
|
||||
#~ msgid "Specified nickname is already registered"
|
||||
#~ msgstr "Bí danh xác định đã đăng ký rồi"
|
||||
|
||||
|
||||
+1
-1
@@ -63,7 +63,6 @@
|
||||
{"ejabberd Publish-Subscribe module","Module d' eplaidaedje-abounmint po ejabberd"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","Module SOCKS5 Bytestreams po ejabberd"}.
|
||||
{"ejabberd vCard module","Module vCard ejabberd"}.
|
||||
{"ejabberd virtual hosts","Forveyous sierveus da ejabberd"}.
|
||||
{"ejabberd Web Admin","Manaedjeu waibe ejabberd"}.
|
||||
{"Email","Emile"}.
|
||||
{"Enable logging","Mete en alaedje li djournå"}.
|
||||
@@ -290,6 +289,7 @@
|
||||
{"User","Uzeu"}.
|
||||
{"Validate","Valider"}.
|
||||
{"vCard User Search","Calpin des uzeus"}.
|
||||
{"Virtual Hosts","Forveyous sierveus"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Les viziteus èn polèt nén candjî leus metous no po ç' såle ci"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Les viziteus n' polèt nén evoyî des messaedjes a tos les prezints"}.
|
||||
{"Wednesday","mierkidi"}.
|
||||
|
||||
+3
-6
@@ -1254,9 +1254,9 @@ msgstr "Dinêyes brutes"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "Apontiaedje des rîles d' accès a ~s"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "Forveyous sierveus da ejabberd"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "Forveyous sierveus"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1697,9 +1697,6 @@ msgstr "Enonder"
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(Dinêyes brutes)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "Forveyous sierveus"
|
||||
|
||||
#~ msgid "Specified nickname is already registered"
|
||||
#~ msgstr "Li no metou dné est ddja edjîstré"
|
||||
|
||||
|
||||
+1
-1
@@ -66,7 +66,6 @@
|
||||
{"ejabberd Publish-Subscribe module","ejabberd 发行-订阅模块"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 字节流模块"}.
|
||||
{"ejabberd vCard module","ejabberd vCard 模块"}.
|
||||
{"ejabberd virtual hosts","ejabberd 虚拟主机"}.
|
||||
{"ejabberd Web Admin","ejabberd 网页管理"}.
|
||||
{"Elements","元素"}.
|
||||
{"Email","电子邮件"}.
|
||||
@@ -321,6 +320,7 @@
|
||||
{"User","用户"}.
|
||||
{"Validate","确认"}.
|
||||
{"vCard User Search","vCard 用户搜索"}.
|
||||
{"Virtual Hosts","虚拟主机"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","此房间不允许用户更改昵称"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","不允许访客给所有占有者发送消息"}.
|
||||
{"Wednesday","星期三"}.
|
||||
|
||||
+3
-6
@@ -1223,9 +1223,9 @@ msgstr "原始格式"
|
||||
msgid "~s access rule configuration"
|
||||
msgstr "~s 访问规则配置"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1038
|
||||
msgid "ejabberd virtual hosts"
|
||||
msgstr "ejabberd 虚拟主机"
|
||||
#: web/ejabberd_web_admin.erl:1031
|
||||
msgid "Virtual Hosts"
|
||||
msgstr "虚拟主机"
|
||||
|
||||
#: web/ejabberd_web_admin.erl:1046 web/ejabberd_web_admin.erl:1053
|
||||
msgid "Users"
|
||||
@@ -1663,9 +1663,6 @@ msgstr "开始"
|
||||
#~ msgid "(Raw)"
|
||||
#~ msgstr "(原始格式)"
|
||||
|
||||
#~ msgid "Virtual Hosts"
|
||||
#~ msgstr "虚拟主机"
|
||||
|
||||
#~ msgid "Specified nickname is already registered"
|
||||
#~ msgstr "指定的昵称已被注册"
|
||||
|
||||
|
||||
@@ -600,6 +600,40 @@ BEGIN
|
||||
END
|
||||
GO
|
||||
|
||||
/******************************************************************/
|
||||
/****** Object: StoredProcedure [dbo].[set_roster_version] **/
|
||||
/** Update users roster_version **/
|
||||
/******************************************************************/
|
||||
CREATE PROCEDURE [dbo].[set_roster_version]
|
||||
@Username varchar(200),
|
||||
@Version varchar(50)
|
||||
AS
|
||||
BEGIN
|
||||
IF EXISTS (SELECT username FROM roster_version WITH (NOLOCK) WHERE username=@Username)
|
||||
BEGIN
|
||||
UPDATE roster_version SET username=@Username, version=@Version WHERE username=@Username;
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
INSERT INTO roster_version (username, version) VALUES (@Username, @Version);
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
/******************************************************************/
|
||||
/****** Object: StoredProcedure [dbo].[get_roster_version] **/
|
||||
/** Retrive the user roster_version **/
|
||||
/******************************************************************/
|
||||
CREATE PROCEDURE [dbo].[get_roster_version]
|
||||
@Username varchar(200)
|
||||
AS
|
||||
BEGIN
|
||||
SELECT roster_version.version as version
|
||||
FROM roster_version WITH (NOLOCK)
|
||||
WHERE username=@Username;
|
||||
END
|
||||
GO
|
||||
|
||||
/***************************************************************/
|
||||
/****** Object: StoredProcedure [dbo].[clean_spool_msg] ******/
|
||||
/** Delete messages older that 3 days from spool **/
|
||||
|
||||
@@ -923,6 +923,40 @@ BEGIN
|
||||
END
|
||||
GO
|
||||
|
||||
/******************************************************************/
|
||||
/****** Object: StoredProcedure [dbo].[set_roster_version] **/
|
||||
/** Update users roster_version **/
|
||||
/******************************************************************/
|
||||
CREATE PROCEDURE [dbo].[set_roster_version]
|
||||
@Username varchar(200),
|
||||
@Version varchar(8000)
|
||||
AS
|
||||
BEGIN
|
||||
IF EXISTS (SELECT username FROM roster_version WITH (NOLOCK) WHERE username=@Username)
|
||||
BEGIN
|
||||
UPDATE roster_version SET username=@Username, version=@Version WHERE username=@Username;
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
INSERT INTO roster_version (username, version) VALUES (@Username, @Version);
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
/******************************************************************/
|
||||
/****** Object: StoredProcedure [dbo].[get_roster_version] **/
|
||||
/** Retrive the user roster_version **/
|
||||
/******************************************************************/
|
||||
CREATE PROCEDURE [dbo].[get_roster_version]
|
||||
@Username varchar(200)
|
||||
AS
|
||||
BEGIN
|
||||
SELECT roster_version.version as version
|
||||
FROM roster_version WITH (NOLOCK)
|
||||
WHERE username=@Username;
|
||||
END
|
||||
GO
|
||||
|
||||
/***************************************************************/
|
||||
/****** Object: StoredProcedure [dbo].[clean_spool_msg] ******/
|
||||
/** Delete messages older that 3 days from spool **/
|
||||
|
||||
@@ -92,6 +92,8 @@
|
||||
-define(generic, true).
|
||||
-endif.
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
%% -----------------
|
||||
%% Generic queries
|
||||
-ifdef(generic).
|
||||
@@ -909,8 +911,14 @@ count_records_where(LServer, Table, WhereClause) ->
|
||||
["select count(*) from ", Table, " with (nolock) ", WhereClause]).
|
||||
|
||||
get_roster_version(LServer, LUser) ->
|
||||
ejabberd_odbc:sql_query(LServer,
|
||||
["select version from dbo.roster_version with (nolock) where username = '", LUser, "'"]).
|
||||
set_roster_version(LUser, Version) ->
|
||||
update_t("dbo.roster_version", ["username", "version"], [LUser, Version], ["username = '", LUser, "'"]).
|
||||
ejabberd_odbc:sql_query(
|
||||
LServer,
|
||||
["EXECUTE dbo.get_roster_version '", LUser, "'"]).
|
||||
|
||||
set_roster_version(Username, Version) ->
|
||||
%% This function doesn't know the vhost, so we hope it's the first one defined:
|
||||
LServer = ?MYNAME,
|
||||
ejabberd_odbc:sql_query(
|
||||
LServer,
|
||||
["EXECUTE dbo.set_roster_version '", Username, "', '", Version, "'"]).
|
||||
-endif.
|
||||
|
||||
@@ -562,7 +562,13 @@ make_xhtml_output(State, Status, Headers, XHTML) ->
|
||||
end, HeadersOut),
|
||||
SL = [Version, integer_to_list(Status), " ",
|
||||
code_to_phrase(Status), "\r\n"],
|
||||
[SL, H, "\r\n", Data].
|
||||
|
||||
Data2 = case State#state.request_method of
|
||||
'HEAD' -> "";
|
||||
_ -> Data
|
||||
end,
|
||||
|
||||
[SL, H, "\r\n", Data2].
|
||||
|
||||
make_text_output(State, Status, Headers, Text) when is_list(Text) ->
|
||||
make_text_output(State, Status, Headers, list_to_binary(Text));
|
||||
@@ -599,7 +605,13 @@ make_text_output(State, Status, Headers, Data) when is_binary(Data) ->
|
||||
end, HeadersOut),
|
||||
SL = [Version, integer_to_list(Status), " ",
|
||||
code_to_phrase(Status), "\r\n"],
|
||||
[SL, H, "\r\n", Data].
|
||||
|
||||
Data2 = case State#state.request_method of
|
||||
'HEAD' -> "";
|
||||
_ -> Data
|
||||
end,
|
||||
|
||||
[SL, H, "\r\n", Data2].
|
||||
|
||||
|
||||
parse_lang(Langs) ->
|
||||
|
||||
@@ -1264,6 +1264,10 @@ parse_request(Data, PayloadSize, MaxStanzaSize) ->
|
||||
{'EXIT', _} ->
|
||||
{error, bad_request};
|
||||
Rid ->
|
||||
Els2 = case [E || E <- Els, E#xmlel.ns == ?NS_HTTP_BIND] of
|
||||
[] -> Els;
|
||||
_ -> force_ns(Data)
|
||||
end,
|
||||
%% I guess this is to remove XMLCDATA: Is it really needed ?
|
||||
FixedEls =
|
||||
lists:filter(
|
||||
@@ -1275,7 +1279,7 @@ parse_request(Data, PayloadSize, MaxStanzaSize) ->
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, Els),
|
||||
end, Els2),
|
||||
Sid = exmpp_xml:get_attribute_as_list(Xml, <<"sid">>, ""),
|
||||
if
|
||||
PayloadSize =< MaxStanzaSize ->
|
||||
@@ -1290,6 +1294,21 @@ parse_request(Data, PayloadSize, MaxStanzaSize) ->
|
||||
{error, bad_request}
|
||||
end.
|
||||
|
||||
%% This BOSH stanza contains at least one XMPP stanza without NS,
|
||||
%% and exmpp sets the NS of BOSH to it and its children, instead of assuming jabber:client.
|
||||
%% To avoid that, we set the jabber:client NS to the BOSH stanza, parse it,
|
||||
%% and the NS will be inherited to the NS-less XMPP stanza.
|
||||
force_ns(Data) ->
|
||||
Data2 = change_ns(Data, ""),
|
||||
[#xmlel{children = Els}] = exmpp_xmlstream:parse_element(Data2),
|
||||
Els.
|
||||
change_ns([], Res) ->
|
||||
lists:reverse(Res);
|
||||
change_ns(?NS_HTTP_BIND_s ++ String, Res) ->
|
||||
change_ns(String, lists:reverse(?NS_JABBER_CLIENT_s) ++ Res);
|
||||
change_ns([Char | String], Res) ->
|
||||
change_ns(String, [Char | Res]).
|
||||
|
||||
send_receiver_reply(undefined, _Reply) ->
|
||||
ok;
|
||||
send_receiver_reply(Receiver, Reply) ->
|
||||
|
||||
@@ -64,11 +64,15 @@ get_acl_rule(["additions.js"],_) -> {"localhost", [all]};
|
||||
get_acl_rule(["vhosts"],_) -> {"localhost", [all]};
|
||||
|
||||
%% The pages of a vhost are only accesible if the user is admin of that vhost:
|
||||
get_acl_rule(["server", VHost | _RPath], 'GET') -> {VHost, [configure, webadmin_view]};
|
||||
get_acl_rule(["server", VHost | _RPath], Method)
|
||||
when Method=:='GET' orelse Method=:='HEAD' ->
|
||||
{VHost, [configure, webadmin_view]};
|
||||
get_acl_rule(["server", VHost | _RPath], 'POST') -> {VHost, [configure]};
|
||||
|
||||
%% Default rule: only global admins can access any other random page
|
||||
get_acl_rule(_RPath, 'GET') -> {global, [configure, webadmin_view]};
|
||||
get_acl_rule(_RPath, Method)
|
||||
when Method=:='GET' orelse Method=:='HEAD' ->
|
||||
{global, [configure, webadmin_view]};
|
||||
get_acl_rule(_RPath, 'POST') -> {global, [configure]}.
|
||||
|
||||
is_acl_match(Host, Rules, Jid) ->
|
||||
@@ -1036,7 +1040,7 @@ process_admin(global,
|
||||
auth = {_, _Auth, AJID},
|
||||
lang = Lang}) ->
|
||||
Res = list_vhosts(Query, Lang, AJID),
|
||||
make_xhtml(?H1GL(?T("ejabberd virtual hosts"), "hostnames", "Host Names") ++ Res, global, Lang, AJID);
|
||||
make_xhtml(?H1GL(?T("Virtual Hosts"), "hostnames", "Host Names") ++ Res, global, Lang, AJID);
|
||||
|
||||
process_admin(Host,
|
||||
#request{
|
||||
@@ -1706,7 +1710,7 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
|
||||
get_offlinemsg_length(ModOffline, User, Server) ->
|
||||
case ModOffline of
|
||||
none -> "disabled";
|
||||
_ -> pretty_string_int(ModOffline:get_queue_length(User, Server))
|
||||
_ -> pretty_string_int(ModOffline:get_queue_length(list_to_binary(User), list_to_binary(Server)))
|
||||
end.
|
||||
|
||||
get_offlinemsg_module(Server) ->
|
||||
@@ -2356,7 +2360,7 @@ get_node(global, Node, ["backup"], Query, Lang) ->
|
||||
ok -> [?XREST("Submitted")];
|
||||
{error, Error} -> [?XRES(?T("Error") ++": " ++ io_lib:format("~p", [Error]))]
|
||||
end,
|
||||
[?XC('h1', ?T("Backup of ") ++ atom_to_list(Node))] ++
|
||||
?H1GL(?T("Backup of ") ++ atom_to_list(Node), "list-eja-commands", "List of ejabberd Commands") ++
|
||||
ResS ++
|
||||
[?XCT('p', "Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately."),
|
||||
?XAE('form', [?XMLATTR(<<"action">>, <<>>), ?XMLATTR(<<"method">>, <<"post">>)],
|
||||
|
||||
Reference in New Issue
Block a user