Compare commits

...

19 Commits

Author SHA1 Message Date
Evgeniy Khramtsov d03de1bb43 Fix some type specs and errors 2013-12-10 21:44:46 +10:00
Evgeniy Khramtsov 33764bb931 Add ejabberd_xmlrpc 2013-12-10 21:25:12 +10:00
Christophe Romain eadc899046 remove version.tex file which is auto-generated 2013-12-05 14:19:55 +01:00
Badlop 5a1300bc70 Add access rule to mod_roster (EJAB-72) 2013-12-04 14:57:44 +01:00
Christophe Romain 9c17163b55 bind values for get_parentnodes_tree 2013-12-03 10:51:01 +01:00
Christophe Romain e11c835bd3 fix use of virtual nodetree 2013-12-03 10:34:59 +01:00
badlop 5ecd832e81 Merge pull request #119 from Mikhail-D/patch-1
Create mssql2012.sql
2013-12-02 02:02:44 -08:00
Mikhail-D a95aa46fe5 Create mssql2012.sql
replace sp_dboption with new instructions
2013-12-02 00:29:52 -08:00
Alexey Shchepin 1dd94ac0d0 Support for OpenSSL ciphers list in ejabberd_c2s, ejabberd_s2s_in and ejabberd_s2s_out 2013-11-28 19:39:11 +02:00
Badlop a6b0e18bde add Pubsub data migration from mnesia to odbc (EJAB-1126)
By calling:
  ejd2odbc:export_pubsub("localhost","/tmp/aa.txt").
it will generate SQL files like these:
  /tmp/pubsub_item.txt
  /tmp/pubsub_node.txt
  /tmp/pubsub_state.txt

Conflicts:
	src/ejabberd_admin.erl
	src/ejd2odbc.erl
2013-11-14 19:29:16 +01:00
Evgeniy Khramtsov 89a17ba84a Correctly convert ACLs into YAML representation 2013-11-07 02:43:43 +10:00
Evgeniy Khramtsov a87b475361 Do not use functions from crypto module wherever possible 2013-11-05 20:07:38 +10:00
Evgeniy Khramtsov b7c7d2747b Fix some type errors 2013-11-05 19:49:30 +10:00
Evgeniy Khramtsov c0240e7249 Do not try to start STUN application during config checks 2013-11-02 10:30:19 +10:00
Evgeniy Khramtsov 6dd31299cf Avoid case clause crash when loading permanent rooms 2013-10-23 12:23:00 +10:00
Evgeniy Khramtsov 2846a2978b Get rid of deprecated MySQL variable 'table_type' 2013-10-23 11:58:26 +10:00
Evgeniy Khramtsov 6df09f5ad6 Check libyaml presence at configure time 2013-10-22 20:12:39 +10:00
Badlop d9da6b77de Enumerate convert_to_yaml command in list of commands 2013-10-09 16:05:45 +02:00
Badlop cd0381bab5 Fix display ACLs in WebAdmin 2013-10-01 23:23:01 +02:00
28 changed files with 2566 additions and 139 deletions
+34 -2
View File
@@ -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}
-2
View File
@@ -1,2 +0,0 @@
% ejabberd version (automatically generated).
\newcommand{\version}{13.10}
+1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+25 -29
View File
@@ -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
View File
@@ -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) ->
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
+7 -10
View File
@@ -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}.
+7 -1
View File
@@ -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.
+7 -2
View File
@@ -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,
+7 -2
View File
@@ -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
View File
@@ -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}.
+484
View File
@@ -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].
+1
View File
@@ -56,6 +56,7 @@ modules() ->
mod_offline,
mod_privacy,
mod_private,
mod_pubsub,
mod_roster,
mod_shared_roster,
mod_vcard,
+2 -2
View File
@@ -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);
+2 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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) ->
+95
View File
@@ -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],":").
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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",
+12 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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),