Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d03de1bb43 | |||
| 33764bb931 | |||
| eadc899046 | |||
| 5a1300bc70 | |||
| 9c17163b55 | |||
| e11c835bd3 | |||
| 5ecd832e81 | |||
| a95aa46fe5 | |||
| 1dd94ac0d0 | |||
| a6b0e18bde | |||
| 89a17ba84a | |||
| a87b475361 | |||
| b7c7d2747b | |||
| c0240e7249 | |||
| 6dd31299cf | |||
| 2846a2978b | |||
| 6df09f5ad6 | |||
| d9da6b77de | |||
| cd0381bab5 |
+34
-2
@@ -691,7 +691,7 @@ Note that both styles are used in this document.
|
||||
In previous \ejabberd{} version the configuration file should be written
|
||||
in Erlang terms. The format is still supported, but it is highly recommended
|
||||
to convert it to the new YAML format using \term{convert\_to\_yaml} command
|
||||
from \term{ejabberdctl} (see~\ref{ejabberdctl} for details).
|
||||
from \term{ejabberdctl} (see~\ref{ejabberdctl} and \ref{list-eja-commands} for details).
|
||||
|
||||
\makesubsection{hostnames}{Host Names}
|
||||
\ind{options!hosts}\ind{host names}
|
||||
@@ -869,7 +869,8 @@ The available modules, their purpose and the options allowed by each one are:
|
||||
\begin{description}
|
||||
\titem{\texttt{ejabberd\_c2s}}
|
||||
Handles c2s connections.\\
|
||||
Options: \texttt{access}, \texttt{certfile}, \texttt{max\_fsm\_queue},
|
||||
Options: \texttt{access}, \texttt{certfile}, \texttt{ciphers},
|
||||
\texttt{max\_fsm\_queue},
|
||||
\texttt{max\_stanza\_size}, \texttt{shaper},
|
||||
\texttt{starttls}, \texttt{starttls\_required}, \texttt{tls},
|
||||
\texttt{zlib}, \texttt{tls\_compression}
|
||||
@@ -908,6 +909,8 @@ This is a detailed description of each option allowed by the listening modules:
|
||||
Simple web page that allows a user to fill a CAPTCHA challenge (see section \ref{captcha}).
|
||||
\titem{certfile: Path} Full path to a file containing the default SSL certificate.
|
||||
To define a certificate file specific for a given domain, use the global option \term{domain\_certfile}.
|
||||
\titem{ciphers: Ciphers} OpenSSL ciphers list in the same format accepted by
|
||||
`\verb|openssl ciphers|' command.
|
||||
\titem{default\_host: undefined|HostName\}}
|
||||
If the HTTP request received by ejabberd contains the HTTP header \term{Host}
|
||||
with an ambiguous virtual host that doesn't match any one defined in ejabberd (see \ref{hostnames}),
|
||||
@@ -1054,6 +1057,8 @@ There are some additional global options that can be specified in the ejabberd c
|
||||
file containing a SSL certificate.
|
||||
\titem{domain\_certfile: Path} \ind{options!domain\_certfile}
|
||||
Full path to the file containing the SSL certificate for a specific domain.
|
||||
\titem{s2s\_ciphers: Ciphers} \ind{options!s2s\_ciphers} OpenSSL ciphers list
|
||||
in the same format accepted by `\verb|openssl ciphers|' command.
|
||||
\titem{outgoing\_s2s\_families: [Family, ...]} \ind{options!outgoing\_s2s\_families}
|
||||
Specify which address families to try, in what order.
|
||||
By default it first tries connecting with IPv4, if that fails it tries using IPv6.
|
||||
@@ -4034,6 +4039,13 @@ Options:
|
||||
This option is disabled by default.
|
||||
Important: if you use \modsharedroster{} or \modsharedrosterldap{},
|
||||
you must disable this option.
|
||||
\titem{access} \ind{options!access}
|
||||
This option can be configured to specify rules to restrict roster management.
|
||||
If a rule returns `deny' on the requested user name,
|
||||
that user cannot modify his personal roster:
|
||||
not add/remove/modify contacts,
|
||||
or subscribe/unsubscribe presence.
|
||||
By default there aren't restrictions.
|
||||
\end{description}
|
||||
|
||||
This example configuration enables Roster Versioning with storage of current id:
|
||||
@@ -4046,6 +4058,24 @@ modules:
|
||||
...
|
||||
\end{verbatim}
|
||||
|
||||
With this example configuration, only admins can manage their rosters;
|
||||
everybody else cannot modify the roster:
|
||||
\begin{verbatim}
|
||||
acl:
|
||||
admin:
|
||||
user:
|
||||
- "sarah": "example.org"
|
||||
access:
|
||||
roster:
|
||||
admin: allow
|
||||
|
||||
modules:
|
||||
...
|
||||
mod_roster:
|
||||
access: roster
|
||||
...
|
||||
\end{verbatim}
|
||||
|
||||
\makesubsection{modservicelog}{\modservicelog{}}
|
||||
\ind{modules!\modservicelog{}}\ind{message auditing}\ind{Bandersnatch}
|
||||
|
||||
@@ -5129,6 +5159,8 @@ The most interesting ones are:
|
||||
\titem{reopen\_log} Reopen the log files after they were renamed.
|
||||
If the old files were not renamed before calling this command,
|
||||
they are automatically renamed to \term{"*-old.log"}. See section \ref{logfiles}.
|
||||
\titem {convert\_to\_yaml /etc/ejabberd/ejabberd.cfg /etc/ejabberd/ejabberd-converted.yml}
|
||||
Convert an old ejabberd.cfg file to the YAML syntax in a new file.
|
||||
\titem {backup ejabberd.backup}
|
||||
Store internal Mnesia database to a binary backup file.
|
||||
\titem {restore ejabberd.backup}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
% ejabberd version (automatically generated).
|
||||
\newcommand{\version}{13.10}
|
||||
@@ -76,6 +76,7 @@ XMLFlags = lists:foldl(
|
||||
|
||||
PostHooks = [ConfigureCmd("p1_tls", ""),
|
||||
ConfigureCmd("p1_stringprep", ""),
|
||||
ConfigureCmd("p1_yaml", ""),
|
||||
ConfigureCmd("p1_xml", XMLFlags)],
|
||||
|
||||
CfgDeps = lists:flatmap(
|
||||
|
||||
+1783
File diff suppressed because it is too large
Load Diff
+25
-29
@@ -17,21 +17,18 @@
|
||||
-- 02111-1307 USA
|
||||
--
|
||||
|
||||
-- Needs MySQL (at least 4.0.x) with innodb back-end
|
||||
SET table_type=InnoDB;
|
||||
|
||||
CREATE TABLE users (
|
||||
username varchar(250) PRIMARY KEY,
|
||||
password text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
|
||||
CREATE TABLE last (
|
||||
username varchar(250) PRIMARY KEY,
|
||||
seconds text NOT NULL,
|
||||
state text NOT NULl
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
|
||||
CREATE TABLE rosterusers (
|
||||
@@ -45,7 +42,7 @@ CREATE TABLE rosterusers (
|
||||
subscribe text NOT NULL,
|
||||
type text,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers(username(75), jid(75));
|
||||
CREATE INDEX i_rosteru_username ON rosterusers(username);
|
||||
@@ -55,7 +52,7 @@ CREATE TABLE rostergroups (
|
||||
username varchar(250) NOT NULL,
|
||||
jid varchar(250) NOT NULL,
|
||||
grp text NOT NULL
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE INDEX pk_rosterg_user_jid ON rostergroups(username(75), jid(75));
|
||||
|
||||
@@ -63,13 +60,13 @@ CREATE TABLE sr_group (
|
||||
name varchar(250) NOT NULL,
|
||||
opts text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE TABLE sr_user (
|
||||
jid varchar(250) NOT NULL,
|
||||
grp varchar(250) NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE UNIQUE INDEX i_sr_user_jid_group ON sr_user(jid(75), grp(75));
|
||||
CREATE INDEX i_sr_user_jid ON sr_user(jid);
|
||||
@@ -80,22 +77,21 @@ CREATE TABLE spool (
|
||||
xml text NOT NULL,
|
||||
seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE INDEX i_despool USING BTREE ON spool(username);
|
||||
|
||||
|
||||
CREATE TABLE vcard (
|
||||
username varchar(250) PRIMARY KEY,
|
||||
vcard mediumtext NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE TABLE vcard_xupdate (
|
||||
username varchar(250) PRIMARY KEY,
|
||||
hash text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE TABLE vcard_search (
|
||||
username varchar(250) NOT NULL,
|
||||
@@ -122,7 +118,7 @@ CREATE TABLE vcard_search (
|
||||
lorgname varchar(250) NOT NULL,
|
||||
orgunit text NOT NULL,
|
||||
lorgunit varchar(250) NOT NULL
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn);
|
||||
CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily);
|
||||
@@ -139,14 +135,14 @@ CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
|
||||
CREATE TABLE privacy_default_list (
|
||||
username varchar(250) PRIMARY KEY,
|
||||
name varchar(250) NOT NULL
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE TABLE privacy_list (
|
||||
username varchar(250) NOT NULL,
|
||||
name varchar(250) NOT NULL,
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE INDEX i_privacy_list_username USING BTREE ON privacy_list(username);
|
||||
CREATE UNIQUE INDEX i_privacy_list_username_name USING BTREE ON privacy_list (username(75), name(75));
|
||||
@@ -162,14 +158,14 @@ CREATE TABLE privacy_list_data (
|
||||
match_message boolean NOT NULL,
|
||||
match_presence_in boolean NOT NULL,
|
||||
match_presence_out boolean NOT NULL
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE TABLE private_storage (
|
||||
username varchar(250) NOT NULL,
|
||||
namespace varchar(250) NOT NULL,
|
||||
data text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE INDEX i_private_storage_username USING BTREE ON private_storage(username);
|
||||
CREATE UNIQUE INDEX i_private_storage_username_namespace USING BTREE ON private_storage(username(75), namespace(75));
|
||||
@@ -178,7 +174,7 @@ CREATE UNIQUE INDEX i_private_storage_username_namespace USING BTREE ON private_
|
||||
CREATE TABLE roster_version (
|
||||
username varchar(250) PRIMARY KEY,
|
||||
version text NOT NULL
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
-- To update from 1.x:
|
||||
-- ALTER TABLE rosterusers ADD COLUMN askmessage text AFTER ask;
|
||||
@@ -191,7 +187,7 @@ CREATE TABLE pubsub_node (
|
||||
parent text,
|
||||
type text,
|
||||
nodeid bigint auto_increment primary key
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
CREATE INDEX i_pubsub_node_parent ON pubsub_node(parent(120));
|
||||
CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(20), node(120));
|
||||
|
||||
@@ -199,14 +195,14 @@ CREATE TABLE pubsub_node_option (
|
||||
nodeid bigint,
|
||||
name text,
|
||||
val text
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option(nodeid);
|
||||
ALTER TABLE `pubsub_node_option` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE pubsub_node_owner (
|
||||
nodeid bigint,
|
||||
owner text
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner(nodeid);
|
||||
ALTER TABLE `pubsub_node_owner` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE;
|
||||
|
||||
@@ -216,7 +212,7 @@ CREATE TABLE pubsub_state (
|
||||
affiliation character(1),
|
||||
subscriptions text,
|
||||
stateid bigint auto_increment primary key
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
CREATE INDEX i_pubsub_state_jid ON pubsub_state(jid(60));
|
||||
CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state(nodeid, jid(60));
|
||||
ALTER TABLE `pubsub_state` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE;
|
||||
@@ -228,7 +224,7 @@ CREATE TABLE pubsub_item (
|
||||
creation text,
|
||||
modification text,
|
||||
payload text
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36));
|
||||
CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36));
|
||||
ALTER TABLE `pubsub_item` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE;
|
||||
@@ -245,7 +241,7 @@ CREATE TABLE muc_room (
|
||||
host text NOT NULL,
|
||||
opts text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE UNIQUE INDEX i_muc_room_name_host USING BTREE ON muc_room(name(75), host(75));
|
||||
|
||||
@@ -254,7 +250,7 @@ CREATE TABLE muc_registered (
|
||||
host text NOT NULL,
|
||||
nick text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE INDEX i_muc_registered_nick USING BTREE ON muc_registered(nick(75));
|
||||
CREATE UNIQUE INDEX i_muc_registered_jid_host USING BTREE ON muc_registered(jid(75), host(75));
|
||||
@@ -264,7 +260,7 @@ CREATE TABLE irc_custom (
|
||||
host text NOT NULL,
|
||||
data text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE UNIQUE INDEX i_irc_custom_jid_host USING BTREE ON irc_custom(jid(75), host(75));
|
||||
|
||||
@@ -272,13 +268,13 @@ CREATE TABLE motd (
|
||||
username varchar(250) PRIMARY KEY,
|
||||
xml text,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE TABLE caps_features (
|
||||
node varchar(250) NOT NULL,
|
||||
subnode varchar(250) NOT NULL,
|
||||
feature text,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) CHARACTER SET utf8;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8;
|
||||
|
||||
CREATE INDEX i_caps_features_node_subnode ON caps_features(node(75), subnode(75));
|
||||
|
||||
+17
-17
@@ -37,7 +37,7 @@
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(acl, {aclname, aclspec}).
|
||||
-record(access, {name :: access_name(),
|
||||
-record(access, {name :: aclname(),
|
||||
rules = [] :: [access_rule()]}).
|
||||
|
||||
-type regexp() :: binary().
|
||||
@@ -455,22 +455,22 @@ transform_options({acl, Name, Type}, Opts) ->
|
||||
T = case Type of
|
||||
all -> all;
|
||||
none -> none;
|
||||
{user, U} -> {user, [U]};
|
||||
{user, U, S} -> {user, [[{U, S}]]};
|
||||
{shared_group, G} -> {shared_group, [G]};
|
||||
{shared_group, G, H} -> {shared_group, [[{G, H}]]};
|
||||
{user_regexp, UR} -> {user_regexp, [UR]};
|
||||
{user_regexp, UR, S} -> {user_regexp, [[{UR, S}]]};
|
||||
{node_regexp, UR, SR} -> {node_regexp, [[{UR, SR}]]};
|
||||
{user_glob, UR} -> {user_glob, [UR]};
|
||||
{user_glob, UR, S} -> {user_glob, [[{UR, S}]]};
|
||||
{node_glob, UR, SR} -> {node_glob, [[{UR, SR}]]};
|
||||
{server, S} -> {server, [S]};
|
||||
{resource, R} -> {resource, [R]};
|
||||
{server_regexp, SR} -> {server_regexp, [SR]};
|
||||
{server_glob, S} -> {server_glob, [S]};
|
||||
{ip, S} -> {ip, [S]};
|
||||
{resource_glob, R} -> {resource_glob, [R]}
|
||||
{user, U} -> {user, [b(U)]};
|
||||
{user, U, S} -> {user, [[{b(U), b(S)}]]};
|
||||
{shared_group, G} -> {shared_group, [b(G)]};
|
||||
{shared_group, G, H} -> {shared_group, [[{b(G), b(H)}]]};
|
||||
{user_regexp, UR} -> {user_regexp, [b(UR)]};
|
||||
{user_regexp, UR, S} -> {user_regexp, [[{b(UR), b(S)}]]};
|
||||
{node_regexp, UR, SR} -> {node_regexp, [[{b(UR), b(SR)}]]};
|
||||
{user_glob, UR} -> {user_glob, [b(UR)]};
|
||||
{user_glob, UR, S} -> {user_glob, [[{b(UR), b(S)}]]};
|
||||
{node_glob, UR, SR} -> {node_glob, [[{b(UR), b(SR)}]]};
|
||||
{server, S} -> {server, [b(S)]};
|
||||
{resource, R} -> {resource, [b(R)]};
|
||||
{server_regexp, SR} -> {server_regexp, [b(SR)]};
|
||||
{server_glob, S} -> {server_glob, [b(S)]};
|
||||
{ip, S} -> {ip, [b(S)]};
|
||||
{resource_glob, R} -> {resource_glob, [b(R)]}
|
||||
end,
|
||||
[{acl, [{Name, [T]}]}|Opts];
|
||||
transform_options({access, Name, Rules}, Opts) ->
|
||||
|
||||
@@ -237,7 +237,7 @@ response(KeyVals, User, Passwd, Nonce, AuthzId,
|
||||
DigestURI = proplists_get_bin_value(<<"digest-uri">>, KeyVals, <<>>),
|
||||
NC = proplists_get_bin_value(<<"nc">>, KeyVals, <<>>),
|
||||
QOP = proplists_get_bin_value(<<"qop">>, KeyVals, <<>>),
|
||||
MD5Hash = crypto:md5(<<User/binary, ":", Realm/binary, ":",
|
||||
MD5Hash = erlang:md5(<<User/binary, ":", Realm/binary, ":",
|
||||
Passwd/binary>>),
|
||||
A1 = case AuthzId of
|
||||
<<"">> ->
|
||||
@@ -253,7 +253,7 @@ response(KeyVals, User, Passwd, Nonce, AuthzId,
|
||||
<<A2Prefix/binary, ":", DigestURI/binary,
|
||||
":00000000000000000000000000000000">>
|
||||
end,
|
||||
T = <<(hex((crypto:md5(A1))))/binary, ":", Nonce/binary,
|
||||
T = <<(hex((erlang:md5(A1))))/binary, ":", Nonce/binary,
|
||||
":", NC/binary, ":", CNonce/binary, ":", QOP/binary,
|
||||
":", (hex((crypto:md5(A2))))/binary>>,
|
||||
hex((crypto:md5(T))).
|
||||
":", (hex((erlang:md5(A2))))/binary>>,
|
||||
hex((erlang:md5(T))).
|
||||
|
||||
+19
-8
@@ -241,6 +241,7 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
TLS = StartTLS orelse
|
||||
StartTLSRequired orelse TLSEnabled,
|
||||
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
|
||||
({ciphers, _}) -> true;
|
||||
(_) -> false
|
||||
end,
|
||||
Opts),
|
||||
@@ -1878,10 +1879,7 @@ presence_track(From, To, Packet, StateData) ->
|
||||
A = remove_element(LTo, StateData#state.pres_a),
|
||||
StateData#state{pres_a = A};
|
||||
<<"subscribe">> ->
|
||||
ejabberd_hooks:run(roster_out_subscription, Server,
|
||||
[User, Server, To, subscribe]),
|
||||
check_privacy_route(From, StateData,
|
||||
jlib:jid_remove_resource(From), To, Packet),
|
||||
try_roster_subscribe(subscribe, User, Server, From, To, Packet, StateData),
|
||||
StateData;
|
||||
<<"subscribed">> ->
|
||||
ejabberd_hooks:run(roster_out_subscription, Server,
|
||||
@@ -1890,10 +1888,7 @@ presence_track(From, To, Packet, StateData) ->
|
||||
jlib:jid_remove_resource(From), To, Packet),
|
||||
StateData;
|
||||
<<"unsubscribe">> ->
|
||||
ejabberd_hooks:run(roster_out_subscription, Server,
|
||||
[User, Server, To, unsubscribe]),
|
||||
check_privacy_route(From, StateData,
|
||||
jlib:jid_remove_resource(From), To, Packet),
|
||||
try_roster_subscribe(unsubscribe, User, Server, From, To, Packet, StateData),
|
||||
StateData;
|
||||
<<"unsubscribed">> ->
|
||||
ejabberd_hooks:run(roster_out_subscription, Server,
|
||||
@@ -1942,6 +1937,22 @@ is_privacy_allow(StateData, From, To, Packet, Dir) ->
|
||||
allow ==
|
||||
privacy_check_packet(StateData, From, To, Packet, Dir).
|
||||
|
||||
%%% Check ACL before allowing to send a subscription stanza
|
||||
try_roster_subscribe(Type, User, Server, From, To, Packet, StateData) ->
|
||||
JID1 = jlib:make_jid(User, Server, <<"">>),
|
||||
Access = gen_mod:get_module_opt(Server, mod_roster, access, fun(A) when is_atom(A) -> A end, all),
|
||||
case acl:match_rule(Server, Access, JID1) of
|
||||
deny ->
|
||||
%% Silently drop this (un)subscription request
|
||||
ok;
|
||||
allow ->
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
Server,
|
||||
[User, Server, To, Type]),
|
||||
check_privacy_route(From, StateData, jlib:jid_remove_resource(From),
|
||||
To, Packet)
|
||||
end.
|
||||
|
||||
%% Send presence when disconnecting
|
||||
presence_broadcast(StateData, From, JIDSet, Packet) ->
|
||||
JIDs = ?SETS:to_list(JIDSet),
|
||||
|
||||
+21
-19
@@ -84,7 +84,7 @@ start() ->
|
||||
Node = list_to_atom(SNode1),
|
||||
Status = case rpc:call(Node, ?MODULE, process, [Args]) of
|
||||
{badrpc, Reason} ->
|
||||
?PRINT("Failed RPC connection to the node ~p: ~p~n",
|
||||
print("Failed RPC connection to the node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
%% TODO: show minimal start help
|
||||
?STATUS_BADRPC;
|
||||
@@ -131,17 +131,17 @@ unregister_commands(CmdDescs, Module, Function) ->
|
||||
%% they are usable even if ejabberd is completely stopped.
|
||||
process(["status"]) ->
|
||||
{InternalStatus, ProvidedStatus} = init:get_status(),
|
||||
?PRINT("The node ~p is ~p with status: ~p~n",
|
||||
print("The node ~p is ~p with status: ~p~n",
|
||||
[node(), InternalStatus, ProvidedStatus]),
|
||||
case lists:keysearch(ejabberd, 1, application:which_applications()) of
|
||||
false ->
|
||||
EjabberdLogPath = ejabberd_logger:get_log_path(),
|
||||
?PRINT("ejabberd is not running in that node~n"
|
||||
print("ejabberd is not running in that node~n"
|
||||
"Check for error messages: ~s~n"
|
||||
"or other files in that directory.~n", [EjabberdLogPath]),
|
||||
?STATUS_ERROR;
|
||||
{value, {_, _, Version}} ->
|
||||
?PRINT("ejabberd ~s is running in that node~n", [Version]),
|
||||
print("ejabberd ~s is running in that node~n", [Version]),
|
||||
?STATUS_SUCCESS
|
||||
end;
|
||||
|
||||
@@ -155,7 +155,7 @@ process(["restart"]) ->
|
||||
?STATUS_SUCCESS;
|
||||
|
||||
process(["mnesia"]) ->
|
||||
?PRINT("~p~n", [mnesia:system_info(all)]),
|
||||
print("~p~n", [mnesia:system_info(all)]),
|
||||
?STATUS_SUCCESS;
|
||||
|
||||
process(["mnesia", "info"]) ->
|
||||
@@ -164,8 +164,8 @@ process(["mnesia", "info"]) ->
|
||||
|
||||
process(["mnesia", Arg]) ->
|
||||
case catch mnesia:system_info(list_to_atom(Arg)) of
|
||||
{'EXIT', Error} -> ?PRINT("Error: ~p~n", [Error]);
|
||||
Return -> ?PRINT("~p~n", [Return])
|
||||
{'EXIT', Error} -> print("Error: ~p~n", [Error]);
|
||||
Return -> print("~p~n", [Return])
|
||||
end,
|
||||
?STATUS_SUCCESS;
|
||||
|
||||
@@ -261,7 +261,7 @@ try_run_ctp(Args, Auth, AccessCommands) ->
|
||||
Error:Why ->
|
||||
%% In this case probably ejabberd is not started, so let's show Status
|
||||
process(["status"]),
|
||||
?PRINT("~n", []),
|
||||
print("~n", []),
|
||||
{io_lib:format("Error in ejabberd ctl process: '~p' ~p", [Error, Why]), ?STATUS_USAGE}
|
||||
end.
|
||||
|
||||
@@ -457,14 +457,14 @@ print_usage(HelpMode, MaxC, ShCode) ->
|
||||
get_list_commands() ++
|
||||
get_list_ctls(),
|
||||
|
||||
?PRINT(
|
||||
print(
|
||||
["Usage: ", ?B("ejabberdctl"), " [--node ", ?U("nodename"), "] [--auth ",
|
||||
?U("user"), " ", ?U("host"), " ", ?U("password"), "] ",
|
||||
?U("command"), " [", ?U("options"), "]\n"
|
||||
"\n"
|
||||
"Available commands in this ejabberd node:\n"], []),
|
||||
print_usage_commands(HelpMode, MaxC, ShCode, AllCommands),
|
||||
?PRINT(
|
||||
print(
|
||||
["\n"
|
||||
"Examples:\n"
|
||||
" ejabberdctl restart\n"
|
||||
@@ -498,7 +498,7 @@ print_usage_commands(HelpMode, MaxC, ShCode, Commands) ->
|
||||
%% Convert its definition to a line
|
||||
FmtCmdDescs = format_command_lines(CmdArgsLenDescsSorted, MaxCmdLen, MaxC, ShCode, HelpMode),
|
||||
|
||||
?PRINT([FmtCmdDescs], []).
|
||||
print([FmtCmdDescs], []).
|
||||
|
||||
|
||||
%% Get some info about the shell:
|
||||
@@ -584,20 +584,20 @@ format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) ->
|
||||
%%-----------------------------
|
||||
|
||||
print_usage_tags(MaxC, ShCode) ->
|
||||
?PRINT("Available tags and commands:", []),
|
||||
print("Available tags and commands:", []),
|
||||
TagsCommands = ejabberd_commands:get_tags_commands(),
|
||||
lists:foreach(
|
||||
fun({Tag, Commands} = _TagCommands) ->
|
||||
?PRINT(["\n\n ", ?B(Tag), "\n "], []),
|
||||
print(["\n\n ", ?B(Tag), "\n "], []),
|
||||
Words = lists:sort(Commands),
|
||||
Desc = prepare_long_line(5, MaxC, Words),
|
||||
?PRINT(Desc, [])
|
||||
print(Desc, [])
|
||||
end,
|
||||
TagsCommands),
|
||||
?PRINT("\n\n", []).
|
||||
print("\n\n", []).
|
||||
|
||||
print_usage_tags(Tag, MaxC, ShCode) ->
|
||||
?PRINT(["Available commands with tag ", ?B(Tag), ":", "\n"], []),
|
||||
print(["Available commands with tag ", ?B(Tag), ":", "\n"], []),
|
||||
HelpMode = long,
|
||||
TagsCommands = ejabberd_commands:get_tags_commands(),
|
||||
CommandsNames = case lists:keysearch(Tag, 1, TagsCommands) of
|
||||
@@ -615,7 +615,7 @@ print_usage_tags(Tag, MaxC, ShCode) ->
|
||||
end,
|
||||
CommandsNames),
|
||||
print_usage_commands(HelpMode, MaxC, ShCode, CommandsList),
|
||||
?PRINT("\n", []).
|
||||
print("\n", []).
|
||||
|
||||
|
||||
%%-----------------------------
|
||||
@@ -673,7 +673,7 @@ print_usage_commands2(Cmds, MaxC, ShCode) ->
|
||||
fun(Cmd, Remaining) ->
|
||||
print_usage_command(Cmd, MaxC, ShCode),
|
||||
case Remaining > 1 of
|
||||
true -> ?PRINT([" ", lists:duplicate(MaxC, 126), " \n"], []);
|
||||
true -> print([" ", lists:duplicate(MaxC, 126), " \n"], []);
|
||||
false -> ok
|
||||
end,
|
||||
{ok, Remaining-1}
|
||||
@@ -749,7 +749,7 @@ print_usage_command(Cmd, C, MaxC, ShCode) ->
|
||||
false -> [" ", ?B("Note:"), " This command cannot be executed using ejabberdctl. Try ejabberd_xmlrpc.\n\n"]
|
||||
end,
|
||||
|
||||
?PRINT(["\n", NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, "\n\n", XmlrpcFmt, TagsFmt, "\n\n", DescFmt, "\n\n", LongDescFmt, NoteEjabberdctl], []).
|
||||
print(["\n", NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, "\n\n", XmlrpcFmt, TagsFmt, "\n\n", DescFmt, "\n\n", LongDescFmt, NoteEjabberdctl], []).
|
||||
|
||||
format_usage_ctype(Type, _Indentation)
|
||||
when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)->
|
||||
@@ -780,6 +780,8 @@ format_usage_tuple([ElementDef | ElementsDef], Indentation) ->
|
||||
MarginString = lists:duplicate(Indentation, $\s), % Put spaces
|
||||
[ElementFmt, ",\n", MarginString, format_usage_tuple(ElementsDef, Indentation)].
|
||||
|
||||
print(Format, Args) ->
|
||||
io:format(lists:flatten(Format), Args).
|
||||
|
||||
%%-----------------------------
|
||||
%% Command managment
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
-export_type([poll_socket/0]).
|
||||
|
||||
-record(state,
|
||||
{id, key, socket, output = <<"">>, input = <<"">>,
|
||||
{id, key, socket, output = [], input = <<"">>,
|
||||
waiting_input = false, last_receiver, http_poll_timeout,
|
||||
timer}).
|
||||
|
||||
@@ -253,7 +253,7 @@ handle_event({activate, From}, StateName, StateData) ->
|
||||
Input ->
|
||||
Receiver = From,
|
||||
Receiver !
|
||||
{tcp, StateData#state.socket, iolist_to_binary(Input)},
|
||||
{tcp, StateData#state.socket, Input},
|
||||
{next_state, StateName,
|
||||
StateData#state{input = <<"">>, waiting_input = false,
|
||||
last_receiver = Receiver}}
|
||||
@@ -272,11 +272,8 @@ handle_event(_Event, StateName, StateData) ->
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event({send, Packet}, _From, StateName,
|
||||
StateData) ->
|
||||
Packet2 = if is_binary(Packet) -> (Packet);
|
||||
true -> Packet
|
||||
end,
|
||||
Output = StateData#state.output ++
|
||||
[lists:flatten(Packet2)],
|
||||
Packet2 = iolist_to_binary(Packet),
|
||||
Output = StateData#state.output ++ [Packet2],
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName,
|
||||
StateData#state{output = Output}};
|
||||
@@ -287,7 +284,7 @@ handle_sync_event({http_put, Key, NewKey, Packet},
|
||||
Allow = case StateData#state.key of
|
||||
<<"">> -> true;
|
||||
OldKey ->
|
||||
NextKey = jlib:encode_base64((crypto:sha(Key))),
|
||||
NextKey = jlib:encode_base64((p1_sha:sha1(Key))),
|
||||
if OldKey == NextKey -> true;
|
||||
true -> false
|
||||
end
|
||||
@@ -295,7 +292,7 @@ handle_sync_event({http_put, Key, NewKey, Packet},
|
||||
if Allow ->
|
||||
case StateData#state.waiting_input of
|
||||
false ->
|
||||
Input = [StateData#state.input | Packet],
|
||||
Input = <<(StateData#state.input)/binary, Packet/binary>>,
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName,
|
||||
StateData#state{input = Input, key = NewKey}};
|
||||
@@ -320,7 +317,7 @@ handle_sync_event(http_get, _From, StateName,
|
||||
StateData) ->
|
||||
Reply = {ok, StateData#state.output},
|
||||
{reply, Reply, StateName,
|
||||
StateData#state{output = <<"">>}};
|
||||
StateData#state{output = []}};
|
||||
handle_sync_event(_Event, _From, StateName,
|
||||
StateData) ->
|
||||
Reply = ok, {reply, Reply, StateName, StateData}.
|
||||
|
||||
@@ -341,6 +341,7 @@ start_listener2(Port, Module, Opts) ->
|
||||
%% It is only required to start the supervisor in some cases.
|
||||
%% But it doesn't hurt to attempt to start it for any listener.
|
||||
%% So, it's normal (and harmless) that in most cases this call returns: {error, {already_started, pid()}}
|
||||
maybe_start_stun(Module),
|
||||
start_module_sup(Port, Module),
|
||||
start_listener_sup(Port, Module, Opts).
|
||||
|
||||
@@ -455,6 +456,12 @@ is_frontend(_) -> false.
|
||||
strip_frontend({frontend, Module}) -> Module;
|
||||
strip_frontend(Module) when is_atom(Module) -> Module.
|
||||
|
||||
-spec maybe_start_stun(module()) -> any().
|
||||
|
||||
maybe_start_stun(ejabberd_stun) ->
|
||||
ejabberd:start_app(p1_stun);
|
||||
maybe_start_stun(_) ->
|
||||
ok.
|
||||
|
||||
%%%
|
||||
%%% Check options
|
||||
@@ -636,7 +643,6 @@ prepare_ip(IP) when is_binary(IP) ->
|
||||
prepare_mod(ejabberd_stun) ->
|
||||
prepare_mod(stun);
|
||||
prepare_mod(stun) ->
|
||||
ejabberd:start_app(p1_stun),
|
||||
stun;
|
||||
prepare_mod(Mod) when is_atom(Mod) ->
|
||||
Mod.
|
||||
|
||||
@@ -177,9 +177,14 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
undefined -> [];
|
||||
CertFile -> [{certfile, CertFile}]
|
||||
end,
|
||||
TLSOpts2 = case ejabberd_config:get_option(
|
||||
s2s_ciphers, fun iolist_to_binary/1) of
|
||||
undefined -> TLSOpts1;
|
||||
Ciphers -> [{ciphers, Ciphers} | TLSOpts1]
|
||||
end,
|
||||
TLSOpts = case proplists:get_bool(tls_compression, Opts) of
|
||||
false -> [compression_none | TLSOpts1];
|
||||
true -> TLSOpts1
|
||||
false -> [compression_none | TLSOpts2];
|
||||
true -> TLSOpts2
|
||||
end,
|
||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||
{ok, wait_for_stream,
|
||||
|
||||
@@ -191,13 +191,18 @@ init([From, Server, Type]) ->
|
||||
undefined -> [connect];
|
||||
CertFile -> [{certfile, CertFile}, connect]
|
||||
end,
|
||||
TLSOpts2 = case ejabberd_config:get_option(
|
||||
s2s_ciphers, fun iolist_to_binary/1) of
|
||||
undefined -> TLSOpts1;
|
||||
Ciphers -> [{ciphers, Ciphers} | TLSOpts1]
|
||||
end,
|
||||
TLSOpts = case ejabberd_config:get_option(
|
||||
{s2s_tls_compression, From},
|
||||
fun(true) -> true;
|
||||
(false) -> false
|
||||
end, true) of
|
||||
false -> [compression_none | TLSOpts1];
|
||||
true -> TLSOpts1
|
||||
false -> [compression_none | TLSOpts2];
|
||||
true -> TLSOpts2
|
||||
end,
|
||||
{New, Verify} = case Type of
|
||||
{new, Key} -> {Key, false};
|
||||
|
||||
+11
-17
@@ -646,10 +646,7 @@ process_admin(Host,
|
||||
{ok, Tokens, _} ->
|
||||
case erl_parse:parse_term(Tokens) of
|
||||
{ok, NewACLs} ->
|
||||
case acl:add_list(Host, NewACLs, true) of
|
||||
ok -> ok;
|
||||
_ -> error
|
||||
end;
|
||||
acl:add_list(Host, NewACLs, true);
|
||||
_ -> error
|
||||
end;
|
||||
_ -> error
|
||||
@@ -689,10 +686,7 @@ process_admin(Host,
|
||||
{'EXIT', _} -> error;
|
||||
NewACLs ->
|
||||
?INFO_MSG("NewACLs at ~s: ~p", [Host, NewACLs]),
|
||||
case acl:add_list(Host, NewACLs, true) of
|
||||
ok -> ?INFO_MSG("NewACLs: ok", []), ok;
|
||||
_ -> error
|
||||
end
|
||||
acl:add_list(Host, NewACLs, true)
|
||||
end;
|
||||
_ -> nothing
|
||||
end,
|
||||
@@ -1004,24 +998,24 @@ acls_to_xhtml(ACLs) ->
|
||||
[?INPUT(<<"text">>, <<"namenew">>, <<"">>)])]
|
||||
++ acl_spec_to_xhtml(<<"new">>, {user, <<"">>})))]))]).
|
||||
|
||||
acl_spec_to_text({user, {U, S}}) ->
|
||||
{user, <<U/binary, "@", S/binary>>};
|
||||
acl_spec_to_text({user, U}) -> {user, U};
|
||||
acl_spec_to_text({server, S}) -> {server, S};
|
||||
acl_spec_to_text({user, U, S}) ->
|
||||
{user, <<U/binary, "@", S/binary>>};
|
||||
acl_spec_to_text({user_regexp, {RU, S}}) ->
|
||||
{user_regexp, <<RU/binary, "@", S/binary>>};
|
||||
acl_spec_to_text({user_regexp, RU}) ->
|
||||
{user_regexp, RU};
|
||||
acl_spec_to_text({user_regexp, RU, S}) ->
|
||||
{user_regexp, <<RU/binary, "@", S/binary>>};
|
||||
acl_spec_to_text({server_regexp, RS}) ->
|
||||
{server_regexp, RS};
|
||||
acl_spec_to_text({node_regexp, RU, RS}) ->
|
||||
acl_spec_to_text({node_regexp, {RU, RS}}) ->
|
||||
{node_regexp, <<RU/binary, "@", RS/binary>>};
|
||||
acl_spec_to_text({user_glob, RU}) -> {user_glob, RU};
|
||||
acl_spec_to_text({user_glob, RU, S}) ->
|
||||
acl_spec_to_text({user_glob, {RU, S}}) ->
|
||||
{user_glob, <<RU/binary, "@", S/binary>>};
|
||||
acl_spec_to_text({user_glob, RU}) -> {user_glob, RU};
|
||||
acl_spec_to_text({server_glob, RS}) ->
|
||||
{server_glob, RS};
|
||||
acl_spec_to_text({node_glob, RU, RS}) ->
|
||||
acl_spec_to_text({node_glob, {RU, RS}}) ->
|
||||
{node_glob, <<RU/binary, "@", RS/binary>>};
|
||||
acl_spec_to_text(all) -> {all, <<"">>};
|
||||
acl_spec_to_text(Spec) -> {raw, term_to_string(Spec)}.
|
||||
@@ -2447,7 +2441,7 @@ get_port_data(PortIP, Opts) ->
|
||||
io_lib:format("~.16b", [N])
|
||||
end,
|
||||
binary_to_list(
|
||||
crypto:md5(
|
||||
erlang:md5(
|
||||
[SPort, IPS, atom_to_list(NetProt)])))),
|
||||
{Port, SPort, IPT, IPS, SSPort, NetProt, OptsClean}.
|
||||
|
||||
|
||||
@@ -0,0 +1,484 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_xmlrpc.erl
|
||||
%%% Author : Badlop <badlop@process-one.net>
|
||||
%%% Purpose : XML-RPC server that frontends ejabberd commands
|
||||
%%% Created : 21 Aug 2007 by Badlop <badlop@ono.com>
|
||||
%%% Id : $Id: ejabberd_xmlrpc.erl 595 2008-05-20 11:39:31Z badlop $
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%% TODO: Implement a command in ejabberdctl 'help COMMAND LANGUAGE' that shows
|
||||
%%% a coding example to call that command in a specific language (python, php).
|
||||
|
||||
%%% TODO: Remove support for plaintext password
|
||||
|
||||
%%% TODO: commands strings should be strings without ~n
|
||||
|
||||
-module(ejabberd_xmlrpc).
|
||||
|
||||
-author('badlop@process-one.net').
|
||||
|
||||
-export([start/2, handler/2, socket_type/0, transform_listen_option/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("mod_roster.hrl").
|
||||
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(state,
|
||||
{access_commands = [] :: list(),
|
||||
auth = noauth :: noauth | {binary(), binary(), binary()},
|
||||
get_auth = true :: boolean()}).
|
||||
|
||||
%% Test:
|
||||
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_integer, [{struct, [{thisinteger, 5}]}]}).
|
||||
%% {ok,{response,[{struct,[{zero,0}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_string, [{struct, [{thisstring, "abcd"}]}]}).
|
||||
%% {ok,{response,[{struct,[{thatstring,"abcd"}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, tell_tuple_3integer, [{struct, [{thisstring, "abcd"}]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{thattuple,
|
||||
%% {array,
|
||||
%% [{struct,[{first,123}]},
|
||||
%% {struct,[{second,456}]},
|
||||
%% {struct,[{third,789}]}]}}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, pow, [{struct, [{base, 5}, {exponent, 7}]}]}).
|
||||
%% {ok,{response,[{struct,[{pow,78125}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, seq, [{struct, [{from, 3}, {to, 7}]}]}).
|
||||
%% {ok,{response,[{array,[{struct,[{intermediate,3}]},
|
||||
%% {struct,[{intermediate,4}]},
|
||||
%% {struct,[{intermediate,5}]},
|
||||
%% {struct,[{intermediate,6}]},
|
||||
%% {struct,[{intermediate,7}]}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, substrs, [{struct, [{word, "abcd"}]}]}).
|
||||
%% NO:
|
||||
%% {ok,{response,[{array,[{struct,[{miniword,"a"}]},
|
||||
%% {struct,[{miniword,"ab"}]},
|
||||
%% {struct,[{miniword,"abc"}]},
|
||||
%% {struct,[{miniword,"abcd"}]}]}]}}
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{substrings,
|
||||
%% {array,
|
||||
%% [{struct,[{miniword,"a"}]},
|
||||
%% {struct,[{miniword,"ab"}]},
|
||||
%% {struct,[{miniword,"abc"}]},
|
||||
%% {struct,[{miniword,"abcd"}]}]}}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, splitjid, [{struct, [{jid, "abcd@localhost/work"}]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{jidparts,
|
||||
%% {array,
|
||||
%% [{struct,[{user,"abcd"}]},
|
||||
%% {struct,[{server,"localhost"}]},
|
||||
%% {struct,[{resource,"work"}]}]}}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}, {thisinteger, 55}]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{thistuple,
|
||||
%% {array,
|
||||
%% [{struct,[{thisinteger,55}]},
|
||||
%% {struct,[{thisstring,"abc"}]}]}}]}]}}
|
||||
%%
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_list_integer, [{struct, [{thislist, {array, [{struct, [{thisinteger, 55}, {thisinteger, 4567}]}]}}]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{thatlist,
|
||||
%% {array,
|
||||
%% [{struct,[{thatinteger,55}]},
|
||||
%% {struct,[{thatinteger,4567}]}]}}]}]}}
|
||||
%%
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_list_string, [{struct, [{thisinteger, 123456}, {thislist, {array, [{struct, [{thisstring, "abc"}, {thisstring, "bobo baba"}]}]}}]}]}).
|
||||
%% {ok,
|
||||
%% {response,
|
||||
%% [{struct,
|
||||
%% [{thistuple,
|
||||
%% {array,
|
||||
%% [{struct,[{thatinteger,123456}]},
|
||||
%% {struct,
|
||||
%% [{thatlist,
|
||||
%% {array,
|
||||
%% [{struct,[{thatstring,"abc"}]},
|
||||
%% {struct,[{thatstring,"bobo baba"}]}]}}]}]}}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisinteger2, 4567}]}]}}]}]}).
|
||||
%% {ok,{response,[{struct,[{zero,0}]}]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_isatils, [{struct,
|
||||
%% [{thisinteger, 123456990},
|
||||
%% {thisstring, "This is ISATILS"},
|
||||
%% {thisatom, "test_isatils"},
|
||||
%% {thistuple, {array, [{struct, [
|
||||
%% {listlen, 2},
|
||||
%% {thislist, {array, [{struct, [
|
||||
%% {contentstring, "word1"},
|
||||
%% {contentstring, "word 2"}
|
||||
%% ]}]}}
|
||||
%% ]}]}}
|
||||
%% ]}]}).
|
||||
%% {ok,{response,
|
||||
%% [{struct,
|
||||
%% [{results,
|
||||
%% {array,
|
||||
%% [{struct,[{thatinteger,123456990}]},
|
||||
%% {struct,[{thatstring,"This is ISATILS"}]},
|
||||
%% {struct,[{thatatom,"test_isatils"}]},
|
||||
%% {struct,
|
||||
%% [{thattuple,
|
||||
%% {array,
|
||||
%% [{struct,[{listlen,123456990}]},
|
||||
%% {struct,[{thatlist,...}]}]}}]}]}}]}]}}
|
||||
|
||||
%% ecommand doesn't exist:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string2, [{struct, [{thisstring, "abc"}]}]}).
|
||||
%% {ok,{response,{fault,-1, "Unknown call: {call,echo_integer_string2,[{struct,[{thisstring,\"abc\"}]}]}"}}}
|
||||
%%
|
||||
%% Duplicated argument:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}, {thisinteger, 44}, {thisinteger, 55}]}]}).
|
||||
%% {ok,{response,{fault,-104, "Error -104\nAttribute 'thisinteger' duplicated:\n[{thisstring,\"abc\"},{thisinteger,44},{thisinteger,55}]"}}}
|
||||
%%
|
||||
%% Missing argument:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}]}]}).
|
||||
%% {ok,{response,{fault,-106, "Error -106\nRequired attribute 'thisinteger' not found:\n[{thisstring,\"abc\"}]"}}}
|
||||
%%
|
||||
%% Duplicated tuple element:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisinteger1, 66}, {thisinteger2, 4567}]}]}}]}]}).
|
||||
%% {ok,{response,{fault,-104, "Error -104\nAttribute 'thisinteger1' defined multiple times:\n[{thisinteger1,55},{thisinteger1,66},{thisinteger2,4567}]"}}}
|
||||
%%
|
||||
%% Missing element in tuple:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisintegerc, 66}, {thisinteger, 4567}]}]}}]}]}).
|
||||
%% {ok,{response,{fault,-106, "Error -106\nRequired attribute 'thisinteger2' not found:\n[{thisintegerc,66},{thisinteger,4567}]"}}}
|
||||
%%
|
||||
%% The ecommand crashed:
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, this_crashes, [{struct, []}]}).
|
||||
%% {ok,{response,{fault,-100, "Error -100\nA problem 'error' occurred executing the command this_crashes with arguments []: badarith"}}}
|
||||
|
||||
%% -----------------------------
|
||||
%% Listener interface
|
||||
%% -----------------------------
|
||||
|
||||
start({gen_tcp = _SockMod, Socket}, Opts) ->
|
||||
%MaxSessions = gen_mod:get_opt(maxsessions, Opts,
|
||||
% fun(I) when is_integer(I), I>0 -> I end,
|
||||
% 10),
|
||||
Timeout = gen_mod:get_opt(timeout, Opts,
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
5000),
|
||||
AccessCommandsOpts = gen_mod:get_opt(access_commands, Opts,
|
||||
fun(L) when is_list(L) -> L end,
|
||||
[]),
|
||||
AccessCommands = lists:flatmap(
|
||||
fun({Ac, AcOpts}) ->
|
||||
Commands = gen_mod:get_opt(
|
||||
commands, AcOpts,
|
||||
fun(A) when is_atom(A) ->
|
||||
A;
|
||||
(L) when is_list(L) ->
|
||||
true = lists:all(
|
||||
fun is_atom/1,
|
||||
L),
|
||||
L
|
||||
end, all),
|
||||
CommOpts = gen_mod:get_opt(
|
||||
options, AcOpts,
|
||||
fun(L) when is_list(L) -> L end,
|
||||
[]),
|
||||
[{Ac, Commands, CommOpts}];
|
||||
(Wrong) ->
|
||||
?WARNING_MSG("wrong options format for ~p: ~p",
|
||||
[?MODULE, Wrong]),
|
||||
[]
|
||||
end, AccessCommandsOpts),
|
||||
GetAuth = case [ACom
|
||||
|| {Ac, _, _} = ACom <- AccessCommands, Ac /= all]
|
||||
of
|
||||
[] -> false;
|
||||
_ -> true
|
||||
end,
|
||||
Handler = {?MODULE, handler},
|
||||
State = #state{access_commands = AccessCommands,
|
||||
get_auth = GetAuth},
|
||||
Pid = proc_lib:spawn(xmlrpc_http, handler, [Socket, Timeout, Handler, State]),
|
||||
{ok, Pid}.
|
||||
|
||||
socket_type() -> raw.
|
||||
|
||||
%% -----------------------------
|
||||
%% Access verification
|
||||
%% -----------------------------
|
||||
|
||||
get_auth(AuthList) ->
|
||||
[User, Server, Password] = try get_attrs([user, server,
|
||||
password],
|
||||
AuthList)
|
||||
of
|
||||
[U, S, P] -> [U, S, P]
|
||||
catch
|
||||
exit:{attribute_not_found, Attr, _} ->
|
||||
throw({error, missing_auth_arguments,
|
||||
Attr})
|
||||
end,
|
||||
{User, Server, Password}.
|
||||
|
||||
%% -----------------------------
|
||||
%% Handlers
|
||||
%% -----------------------------
|
||||
|
||||
%% Call: Arguments: Returns:
|
||||
|
||||
%% .............................
|
||||
%% Access verification
|
||||
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [152]}).
|
||||
%% {ok,{response,{fault,-103, "Error -103\nRequired authentication: {call,echothis,[152]}"}}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "ada"}]}, 152]}).
|
||||
%% {ok,{response,{fault,-103,
|
||||
%% "Error -103\nAuthentication non valid: [{user,\"badlop\"},\n
|
||||
%% {server,\"localhost\"},\n
|
||||
%% {password,\"ada\"}]"}}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "ada90ada"}]}, 152]}).
|
||||
%% {ok,{response,[152]}}
|
||||
%%
|
||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "79C1574A43BC995F2B145A299EF97277"}]}, 152]}).
|
||||
%% {ok,{response,[152]}}
|
||||
|
||||
handler(#state{get_auth = true, auth = noauth} = State,
|
||||
{call, Method,
|
||||
[{struct, AuthList} | Arguments] = AllArgs}) ->
|
||||
try get_auth(AuthList) of
|
||||
Auth ->
|
||||
handler(State#state{get_auth = false, auth = Auth},
|
||||
{call, Method, Arguments})
|
||||
catch
|
||||
{error, missing_auth_arguments, _Attr} ->
|
||||
handler(State#state{get_auth = false, auth = noauth},
|
||||
{call, Method, AllArgs})
|
||||
end;
|
||||
%% .............................
|
||||
%% Debug
|
||||
%% echothis String String
|
||||
handler(_State, {call, echothis, [A]}) ->
|
||||
{false, {response, [A]}};
|
||||
%% echothisnew struct[{sentence, String}] struct[{repeated, String}]
|
||||
handler(_State,
|
||||
{call, echothisnew, [{struct, [{sentence, A}]}]}) ->
|
||||
{false, {response, [{struct, [{repeated, A}]}]}};
|
||||
%% multhis struct[{a, Integer}, {b, Integer}] Integer
|
||||
handler(_State,
|
||||
{call, multhis, [{struct, [{a, A}, {b, B}]}]}) ->
|
||||
{false, {response, [A * B]}};
|
||||
%% multhisnew struct[{a, Integer}, {b, Integer}] struct[{mu, Integer}]
|
||||
handler(_State,
|
||||
{call, multhisnew, [{struct, [{a, A}, {b, B}]}]}) ->
|
||||
{false, {response, [{struct, [{mu, A * B}]}]}};
|
||||
%% .............................
|
||||
%% ejabberd commands
|
||||
handler(State, {call, Command, []}) ->
|
||||
handler(State, {call, Command, [{struct, []}]});
|
||||
handler(State,
|
||||
{call, Command, [{struct, AttrL}]} = Payload) ->
|
||||
case ejabberd_commands:get_command_format(Command) of
|
||||
{error, command_unknown} ->
|
||||
build_fault_response(-112, "Unknown call: ~p",
|
||||
[Payload]);
|
||||
{ArgsF, ResultF} ->
|
||||
try_do_command(State#state.access_commands,
|
||||
State#state.auth, Command, AttrL, ArgsF, ResultF)
|
||||
end;
|
||||
%% If no other guard matches
|
||||
handler(_State, Payload) ->
|
||||
build_fault_response(-112, "Unknown call: ~p",
|
||||
[Payload]).
|
||||
|
||||
%% -----------------------------
|
||||
%% Command
|
||||
%% -----------------------------
|
||||
|
||||
try_do_command(AccessCommands, Auth, Command, AttrL,
|
||||
ArgsF, ResultF) ->
|
||||
try do_command(AccessCommands, Auth, Command, AttrL,
|
||||
ArgsF, ResultF)
|
||||
of
|
||||
{command_result, ResultFormatted} ->
|
||||
{false, {response, [ResultFormatted]}}
|
||||
catch
|
||||
exit:{duplicated_attribute, ExitAt, ExitAtL} ->
|
||||
build_fault_response(-114,
|
||||
"Attribute '~p' duplicated:~n~p",
|
||||
[ExitAt, ExitAtL]);
|
||||
exit:{attribute_not_found, ExitAt, ExitAtL} ->
|
||||
build_fault_response(-116,
|
||||
"Required attribute '~p' not found:~n~p",
|
||||
[ExitAt, ExitAtL]);
|
||||
exit:{additional_unused_args, ExitAtL} ->
|
||||
build_fault_response(-120,
|
||||
"The call provided additional unused "
|
||||
"arguments:~n~p",
|
||||
[ExitAtL]);
|
||||
Why ->
|
||||
build_fault_response(-118,
|
||||
"A problem '~p' occurred executing the "
|
||||
"command ~p with arguments~n~p",
|
||||
[Why, Command, AttrL])
|
||||
end.
|
||||
|
||||
build_fault_response(Code, ParseString, ParseArgs) ->
|
||||
FaultString = "Error " ++ integer_to_list(Code) ++ "\n"
|
||||
++ lists:flatten(io_lib:format(ParseString, ParseArgs)),
|
||||
?WARNING_MSG(FaultString, []),
|
||||
{false, {response, {fault, Code, FaultString}}}.
|
||||
|
||||
do_command(AccessCommands, Auth, Command, AttrL, ArgsF,
|
||||
ResultF) ->
|
||||
ArgsFormatted = format_args(AttrL, ArgsF),
|
||||
Result =
|
||||
ejabberd_commands:execute_command(AccessCommands, Auth,
|
||||
Command, ArgsFormatted),
|
||||
ResultFormatted = format_result(Result, ResultF),
|
||||
{command_result, ResultFormatted}.
|
||||
|
||||
%%-----------------------------
|
||||
%% Format arguments
|
||||
%%-----------------------------
|
||||
|
||||
get_attrs(Attribute_names, L) ->
|
||||
[get_attr(A, L) || A <- Attribute_names].
|
||||
|
||||
get_attr(A, L) ->
|
||||
case lists:keysearch(A, 1, L) of
|
||||
{value, {A, Value}} -> Value;
|
||||
false ->
|
||||
%% Report the error and then force a crash
|
||||
exit({attribute_not_found, A, L})
|
||||
end.
|
||||
|
||||
get_elem_delete(A, L) ->
|
||||
case proplists:get_all_values(A, L) of
|
||||
[Value] -> {Value, proplists:delete(A, L)};
|
||||
[_, _ | _] ->
|
||||
%% Crash reporting the error
|
||||
exit({duplicated_attribute, A, L});
|
||||
[] ->
|
||||
%% Report the error and then force a crash
|
||||
exit({attribute_not_found, A, L})
|
||||
end.
|
||||
|
||||
format_args(Args, ArgsFormat) ->
|
||||
{ArgsRemaining, R} = lists:foldl(fun ({ArgName,
|
||||
ArgFormat},
|
||||
{Args1, Res}) ->
|
||||
{ArgValue, Args2} =
|
||||
get_elem_delete(ArgName,
|
||||
Args1),
|
||||
Formatted = format_arg(ArgValue,
|
||||
ArgFormat),
|
||||
{Args2, Res ++ [Formatted]}
|
||||
end,
|
||||
{Args, []}, ArgsFormat),
|
||||
case ArgsRemaining of
|
||||
[] -> R;
|
||||
L when is_list(L) -> exit({additional_unused_args, L})
|
||||
end.
|
||||
|
||||
format_arg({array, Elements},
|
||||
{list, {ElementDefName, ElementDefFormat}})
|
||||
when is_list(Elements) ->
|
||||
lists:map(fun ({struct, [{ElementName, ElementValue}]}) when
|
||||
ElementDefName == ElementName ->
|
||||
format_arg(ElementValue, ElementDefFormat)
|
||||
end,
|
||||
Elements);
|
||||
format_arg({array, [{struct, Elements}]},
|
||||
{list, {ElementDefName, ElementDefFormat}})
|
||||
when is_list(Elements) ->
|
||||
lists:map(fun ({ElementName, ElementValue}) ->
|
||||
true = ElementDefName == ElementName,
|
||||
format_arg(ElementValue, ElementDefFormat)
|
||||
end,
|
||||
Elements);
|
||||
format_arg({array, [{struct, Elements}]},
|
||||
{tuple, ElementsDef})
|
||||
when is_list(Elements) ->
|
||||
FormattedList = format_args(Elements, ElementsDef),
|
||||
list_to_tuple(FormattedList);
|
||||
format_arg({array, Elements}, {list, ElementsDef})
|
||||
when is_list(Elements) and is_atom(ElementsDef) ->
|
||||
[format_arg(Element, ElementsDef)
|
||||
|| Element <- Elements];
|
||||
format_arg(Arg, integer) when is_integer(Arg) -> Arg;
|
||||
format_arg(Arg, binary) when is_list(Arg) -> list_to_binary(Arg);
|
||||
format_arg(Arg, binary) when is_binary(Arg) -> Arg;
|
||||
format_arg(Arg, string) when is_binary(Arg) -> Arg.
|
||||
|
||||
%% -----------------------------
|
||||
%% Result
|
||||
%% -----------------------------
|
||||
|
||||
format_result({error, Error}, _) ->
|
||||
throw({error, Error});
|
||||
format_result(String, string) -> lists:flatten(String);
|
||||
format_result(Atom, {Name, atom}) ->
|
||||
{struct,
|
||||
[{Name, iolist_to_binary(atom_to_list(Atom))}]};
|
||||
format_result(Int, {Name, integer}) ->
|
||||
{struct, [{Name, Int}]};
|
||||
format_result(String, {Name, string}) when is_list(String) ->
|
||||
{struct, [{Name, lists:flatten(String)}]};
|
||||
format_result(Binary, {Name, string}) when is_binary(Binary) ->
|
||||
{struct, [{Name, binary_to_list(Binary)}]};
|
||||
format_result(Code, {Name, rescode}) ->
|
||||
{struct, [{Name, make_status(Code)}]};
|
||||
format_result({Code, Text}, {Name, restuple}) ->
|
||||
{struct,
|
||||
[{Name, make_status(Code)},
|
||||
{text, lists:flatten(Text)}]};
|
||||
%% Result is a list of something: [something()]
|
||||
format_result(Elements, {Name, {list, ElementsDef}}) ->
|
||||
FormattedList = lists:map(fun (Element) ->
|
||||
format_result(Element, ElementsDef)
|
||||
end,
|
||||
Elements),
|
||||
{struct, [{Name, {array, FormattedList}}]};
|
||||
%% Result is a tuple with several elements: {something1(), something2(), ...}
|
||||
format_result(ElementsTuple,
|
||||
{Name, {tuple, ElementsDef}}) ->
|
||||
ElementsList = tuple_to_list(ElementsTuple),
|
||||
ElementsAndDef = lists:zip(ElementsList, ElementsDef),
|
||||
FormattedList = lists:map(fun ({Element, ElementDef}) ->
|
||||
format_result(Element, ElementDef)
|
||||
end,
|
||||
ElementsAndDef),
|
||||
{struct, [{Name, {array, FormattedList}}]};
|
||||
format_result(404, {Name, _}) ->
|
||||
{struct, [{Name, make_status(not_found)}]}.
|
||||
|
||||
make_status(ok) -> 0;
|
||||
make_status(true) -> 0;
|
||||
make_status(false) -> 1;
|
||||
make_status(error) -> 1;
|
||||
make_status(_) -> 1.
|
||||
|
||||
transform_listen_option({access_commands, ACOpts}, Opts) ->
|
||||
NewACOpts = lists:map(
|
||||
fun({AName, ACmds, AOpts}) ->
|
||||
{AName, [{commands, ACmds}, {options, AOpts}]};
|
||||
(Opt) ->
|
||||
Opt
|
||||
end, ACOpts),
|
||||
[{access_commands, NewACOpts}|Opts];
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
@@ -56,6 +56,7 @@ modules() ->
|
||||
mod_offline,
|
||||
mod_privacy,
|
||||
mod_private,
|
||||
mod_pubsub,
|
||||
mod_roster,
|
||||
mod_shared_roster,
|
||||
mod_vcard,
|
||||
|
||||
+2
-2
@@ -467,8 +467,8 @@ make_disco_hash(DiscoEls, Algo) ->
|
||||
Concat = list_to_binary([concat_identities(DiscoEls),
|
||||
concat_features(DiscoEls), concat_info(DiscoEls)]),
|
||||
jlib:encode_base64(case Algo of
|
||||
md5 -> crypto:md5(Concat);
|
||||
sha1 -> crypto:sha(Concat);
|
||||
md5 -> erlang:md5(Concat);
|
||||
sha1 -> p1_sha:sha1(Concat);
|
||||
sha224 -> p1_sha:sha224(Concat);
|
||||
sha256 -> p1_sha:sha256(Concat);
|
||||
sha384 -> p1_sha:sha384(Concat);
|
||||
|
||||
@@ -1756,10 +1756,8 @@ set_form(_From, Host, [<<"config">>, <<"acls">>], _Lang,
|
||||
{ok, Tokens, _} ->
|
||||
case erl_parse:parse_term(Tokens) of
|
||||
{ok, ACLs} ->
|
||||
case acl:add_list(Host, ACLs, true) of
|
||||
ok -> {result, []};
|
||||
_ -> {error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
acl:add_list(Host, ACLs, true),
|
||||
{result, []};
|
||||
_ -> {error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
_ -> {error, ?ERR_BAD_REQUEST}
|
||||
|
||||
+1
-1
@@ -491,7 +491,7 @@ get_fields_xml(Host, Module) ->
|
||||
Name = proplists:get_value(names, Opts, <<>>),
|
||||
URLs = proplists:get_value(urls, Opts, []),
|
||||
{Mods, Name, URLs}
|
||||
end, lists:flatmap(L))
|
||||
end, lists:flatten(L))
|
||||
end, []),
|
||||
Fields_good = lists:filter(fun ({Modules, _, _}) ->
|
||||
case Modules of
|
||||
|
||||
+2
-2
@@ -624,14 +624,14 @@ get_rooms(LServer, Host, odbc) ->
|
||||
[<<"select name, opts from muc_room ">>,
|
||||
<<"where host='">>, SHost, <<"';">>])
|
||||
of
|
||||
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), [];
|
||||
{selected, [<<"name">>, <<"opts">>], RoomOpts} ->
|
||||
lists:map(fun ([Room, Opts]) ->
|
||||
#muc_room{name_host = {Room, Host},
|
||||
opts = opts_to_binary(
|
||||
ejabberd_odbc:decode_term(Opts))}
|
||||
end,
|
||||
RoomOpts)
|
||||
RoomOpts);
|
||||
Err -> ?ERROR_MSG("failed to get rooms: ~p", [Err]), []
|
||||
end.
|
||||
|
||||
load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
|
||||
|
||||
@@ -111,6 +111,8 @@
|
||||
%% calls for parallel sending of last items
|
||||
-export([send_loop/1]).
|
||||
|
||||
-export([export/1]).
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_pubsub).
|
||||
|
||||
-define(LOOPNAME, ejabberd_mod_pubsub_loop).
|
||||
@@ -5376,3 +5378,96 @@ purge_offline({User, Server, _} = LJID) ->
|
||||
lists:usort(lists:flatten(Affiliations)));
|
||||
{Error, _} -> ?DEBUG("on_user_offline ~p", [Error])
|
||||
end.
|
||||
|
||||
|
||||
%% REVIEW:
|
||||
%% * this code takes NODEID from Itemid2, and forgets about Nodeidx
|
||||
%% * this code assumes Payload only contains one xmlelement()
|
||||
%% * PUBLISHER is taken from Creation
|
||||
export(_Server) ->
|
||||
[{pubsub_item,
|
||||
fun(_Host, #pubsub_item{itemid = {Itemid1, Itemid2},
|
||||
%nodeidx = _Nodeidx,
|
||||
creation = {{C1, C2, C3}, Cusr},
|
||||
modification = {{M1, M2, M3}, _Musr},
|
||||
payload = Payload}) ->
|
||||
ITEMID = ejabberd_odbc:escape(Itemid1),
|
||||
NODEID = integer_to_list(Itemid2),
|
||||
CREATION = ejabberd_odbc:escape(
|
||||
string:join([string:right(integer_to_list(I),6,$0)||I<-[C1,C2,C3]],":")),
|
||||
MODIFICATION = ejabberd_odbc:escape(
|
||||
string:join([string:right(integer_to_list(I),6,$0)||I<-[M1,M2,M3]],":")),
|
||||
PUBLISHER = ejabberd_odbc:escape(jlib:jid_to_string(Cusr)),
|
||||
[PayloadEl] = [El || {xmlelement,_,_,_} = El <- Payload],
|
||||
PAYLOAD = ejabberd_odbc:escape(xml:element_to_binary(PayloadEl)),
|
||||
["delete from pubsub_item where itemid='", ITEMID, "';\n"
|
||||
"insert into pubsub_item(itemid,nodeid,creation,modification,publisher,payload) \n"
|
||||
" values ('", ITEMID, "', ", NODEID, ", '", CREATION, "', '",
|
||||
MODIFICATION, "', '", PUBLISHER, "', '", PAYLOAD, "');\n"];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end},
|
||||
%% REVIEW:
|
||||
%% * From the mnesia table, the #pubsub_state.items is not used in ODBC
|
||||
%% * Right now AFFILIATION is the first letter of Affiliation
|
||||
%% * Right now SUBSCRIPTIONS expects only one Subscription
|
||||
%% * Right now SUBSCRIPTIONS letter is the first letter of Subscription
|
||||
{pubsub_state,
|
||||
fun(_Host, #pubsub_state{stateid = {Jid, Stateid},
|
||||
%nodeidx = Nodeidx,
|
||||
items = _Items,
|
||||
affiliation = Affiliation,
|
||||
subscriptions = Subscriptions}) ->
|
||||
STATEID = integer_to_list(Stateid),
|
||||
JID = ejabberd_odbc:escape(jlib:jid_to_string(Jid)),
|
||||
NODEID = "unknown", %% TODO: integer_to_list(Nodeidx),
|
||||
AFFILIATION = string:substr(atom_to_list(Affiliation),1,1),
|
||||
SUBSCRIPTIONS = parse_subscriptions(Subscriptions),
|
||||
["delete from pubsub_state where stateid='", STATEID, "';\n"
|
||||
"insert into pubsub_state(stateid,jid,nodeid,affiliation,subscriptions) \n"
|
||||
" values (", STATEID, ", '", JID, "', ", NODEID, ", '",
|
||||
AFFILIATION, "', '", SUBSCRIPTIONS, "');\n"];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end},
|
||||
|
||||
%% REVIEW:
|
||||
%% * Parents is not migrated to PARENTs
|
||||
%% * Probably some option VALs are not correctly represented in mysql
|
||||
{pubsub_node,
|
||||
fun(_Host, #pubsub_node{nodeid = {Hostid, Nodeid},
|
||||
id = Id,
|
||||
parents = _Parents,
|
||||
type = Type,
|
||||
owners = Owners,
|
||||
options = Options}) ->
|
||||
HOST = case Hostid of
|
||||
{U,S,R} -> ejabberd_odbc:escape(jlib:jid_to_string({U,S,R}));
|
||||
_ -> ejabberd_odbc:escape(Hostid)
|
||||
end,
|
||||
NODE = ejabberd_odbc:escape(Nodeid),
|
||||
NODEID = integer_to_list(Id),
|
||||
PARENT = "",
|
||||
TYPE = ejabberd_odbc:escape(Type++"_odbc"),
|
||||
["delete from pubsub_node where nodeid='", NODEID, "';\n"
|
||||
"insert into pubsub_node(host,node,nodeid,parent,type) \n"
|
||||
" values ('", HOST, "', '", NODE, "', ", NODEID, ", '", PARENT, "', '", TYPE, "');\n"
|
||||
"delete from pubsub_node_option where nodeid='", NODEID, "';\n",
|
||||
[["insert into pubsub_node_option(nodeid,name,val)\n"
|
||||
" values (", NODEID, ", '", atom_to_list(Name), "', '",
|
||||
io_lib:format("~p", [Val]), "');\n"] || {Name,Val} <- Options],
|
||||
"delete from pubsub_node_owner where nodeid='", NODEID, "';\n",
|
||||
[["insert into pubsub_node_owner(nodeid,owner)\n"
|
||||
" values (", NODEID, ", '", jlib:jid_to_string(Usr), "');\n"] || Usr <- Owners],"\n"];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end}].
|
||||
|
||||
parse_subscriptions([]) ->
|
||||
"";
|
||||
parse_subscriptions([{State, Item}]) ->
|
||||
STATE = case State of
|
||||
subscribed -> "s"
|
||||
end,
|
||||
string:join([STATE, Item],":").
|
||||
|
||||
|
||||
@@ -677,5 +677,7 @@ check_ip_access({User, Server, Resource}, IPAccess) ->
|
||||
_ ->
|
||||
deny
|
||||
end;
|
||||
check_ip_access(undefined, _IPAccess) ->
|
||||
deny;
|
||||
check_ip_access(IPAddress, IPAccess) ->
|
||||
acl:match_rule(global, IPAccess, IPAddress).
|
||||
|
||||
+12
-2
@@ -142,7 +142,7 @@ process_iq(From, To, IQ) ->
|
||||
|
||||
process_local_iq(From, To, #iq{type = Type} = IQ) ->
|
||||
case Type of
|
||||
set -> process_iq_set(From, To, IQ);
|
||||
set -> try_process_iq_set(From, To, IQ);
|
||||
get -> process_iq_get(From, To, IQ)
|
||||
end.
|
||||
|
||||
@@ -455,6 +455,16 @@ get_roster_by_jid_t(LUser, LServer, LJID, odbc) ->
|
||||
end
|
||||
end.
|
||||
|
||||
try_process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
|
||||
#jid{server = Server} = From,
|
||||
Access = gen_mod:get_module_opt(Server, ?MODULE, access, fun(A) when is_atom(A) -> A end, all),
|
||||
case acl:match_rule(Server, Access, From) of
|
||||
deny ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
allow ->
|
||||
process_iq_set(From, To, IQ)
|
||||
end.
|
||||
|
||||
process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
|
||||
#xmlel{children = Els} = SubEl,
|
||||
lists:foreach(fun (El) -> process_item_set(From, To, El)
|
||||
@@ -1508,7 +1518,7 @@ user_roster_item_parse_query(User, Server, Items,
|
||||
{value, _} ->
|
||||
UJID = jlib:make_jid(User, Server,
|
||||
<<"">>),
|
||||
process_iq(UJID, UJID,
|
||||
process_iq_set(UJID, UJID,
|
||||
#iq{type = set,
|
||||
sub_el =
|
||||
#xmlel{name =
|
||||
|
||||
+2
-6
@@ -78,12 +78,8 @@ process_local_iq(_From, To,
|
||||
end.
|
||||
|
||||
get_os() ->
|
||||
OSType = case os:type() of
|
||||
{Osfamily, Osname} ->
|
||||
<<(iolist_to_binary(atom_to_list(Osfamily)))/binary,
|
||||
"/", (iolist_to_binary(atom_to_list(Osname)))/binary>>;
|
||||
Osfamily -> iolist_to_binary(atom_to_list(Osfamily))
|
||||
end,
|
||||
{Osfamily, Osname} = os:type(),
|
||||
OSType = list_to_binary([atom_to_list(Osfamily), $/, atom_to_list(Osname)]),
|
||||
OSVersion = case os:version() of
|
||||
{Major, Minor, Release} ->
|
||||
iolist_to_binary(io_lib:format("~w.~w.~w",
|
||||
|
||||
@@ -119,7 +119,18 @@ get_nodes(_Host) -> [].
|
||||
|
||||
get_parentnodes(_Host, _Node, _From) -> [].
|
||||
|
||||
get_parentnodes_tree(_Host, _Node, _From) -> [].
|
||||
-spec(get_parentnodes_tree/3 ::
|
||||
(
|
||||
Host :: mod_pubsub:host(),
|
||||
NodeId :: mod_pubsub:nodeId(),
|
||||
From :: jid())
|
||||
-> [{0, [mod_pubsub:pubsubNode(),...]}]
|
||||
).
|
||||
get_parentnodes_tree(Host, NodeId, From) ->
|
||||
case get_node(Host, NodeId, From) of
|
||||
Node when is_record(Node, pubsub_node) -> [{0, [Node]}];
|
||||
_Error -> []
|
||||
end.
|
||||
|
||||
get_subnodes(Host, Node, _From) ->
|
||||
get_subnodes(Host, Node).
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ client_key(SaltedPassword) ->
|
||||
|
||||
-spec stored_key(binary()) -> binary().
|
||||
|
||||
stored_key(ClientKey) -> crypto:sha(ClientKey).
|
||||
stored_key(ClientKey) -> p1_sha:sha1(ClientKey).
|
||||
|
||||
-spec server_key(binary()) -> binary().
|
||||
|
||||
|
||||
+2
-2
@@ -122,9 +122,9 @@ update(#maxrate{} = State, Size) ->
|
||||
transform_options(Opts) ->
|
||||
lists:foldl(fun transform_options/2, [], Opts).
|
||||
|
||||
transform_options({shaper, Name, {maxrate, N}}, Opts) ->
|
||||
transform_options({OptName, Name, {maxrate, N}}, Opts) when OptName == shaper ->
|
||||
[{shaper, [{Name, N}]}|Opts];
|
||||
transform_options({shaper, Name, none}, Opts) ->
|
||||
transform_options({OptName, Name, none}, Opts) when OptName == shaper ->
|
||||
[{shaper, [{Name, none}]}|Opts];
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
+5
-5
@@ -254,11 +254,11 @@ response(User, Passwd, Nonce, AuthzId, Realm, CNonce,
|
||||
DigestURI, NC, QOP, A2Prefix) ->
|
||||
A1 = case AuthzId of
|
||||
<<"">> ->
|
||||
<<((crypto:md5(<<User/binary, ":", Realm/binary, ":",
|
||||
<<((erlang:md5(<<User/binary, ":", Realm/binary, ":",
|
||||
Passwd/binary>>)))/binary,
|
||||
":", Nonce/binary, ":", CNonce/binary>>;
|
||||
_ ->
|
||||
<<((crypto:md5(<<User/binary, ":", Realm/binary, ":",
|
||||
<<((erlang:md5(<<User/binary, ":", Realm/binary, ":",
|
||||
Passwd/binary>>)))/binary,
|
||||
":", Nonce/binary, ":", CNonce/binary, ":",
|
||||
AuthzId/binary>>
|
||||
@@ -270,10 +270,10 @@ response(User, Passwd, Nonce, AuthzId, Realm, CNonce,
|
||||
<<A2Prefix/binary, ":", DigestURI/binary,
|
||||
":00000000000000000000000000000000">>
|
||||
end,
|
||||
T = <<(hex((crypto:md5(A1))))/binary, ":", Nonce/binary,
|
||||
T = <<(hex((erlang:md5(A1))))/binary, ":", Nonce/binary,
|
||||
":", NC/binary, ":", CNonce/binary, ":", QOP/binary,
|
||||
":", (hex((crypto:md5(A2))))/binary>>,
|
||||
hex((crypto:md5(T))).
|
||||
":", (hex((erlang:md5(A2))))/binary>>,
|
||||
hex((erlang:md5(T))).
|
||||
|
||||
my_jid(Config) ->
|
||||
jlib:make_jid(?config(user, Config),
|
||||
|
||||
Reference in New Issue
Block a user