Compare commits

...

105 Commits

Author SHA1 Message Date
Christophe Romain 72b0fb49e8 Fix type convertion bug injected by 4ccc40b (#1229) 2016-08-04 09:49:23 +02:00
Alexey Shchepin 111aa83f5e Add tokens cache to ejabberd_oauth 2016-08-04 01:59:28 +03:00
Holger Weiss 78fa9e08a5 XEP-0198: Handle timeouts during stream resumption
If session resumption failed because requesting the #state from the old
c2s process took too long, the new c2s process will usually receive the
response.  Let the new process handle that case gracefully.
2016-08-03 02:28:46 +02:00
Holger Weiss 3c1e4f0dfd XEP-0198: Increase timeout for stream resumption
During stream resumption, the #state is transferred from the old c2s
process to the new one.  This is usually very fast, but under certain
conditions, it can take longer than five seconds.
2016-08-03 02:15:15 +02:00
Alexey Shchepin 4add262090 Add OAUTH SQL backend 2016-08-01 16:55:43 +03:00
Mickael Remond 76eba3647a Implement gen_mod callback in ModPresenceDemo module 2016-08-01 15:46:14 +02:00
Mickaël Rémond 2ef58a33a9 Merge pull request #1223 from processone/expand_api
More API fixes and improvements
2016-08-01 15:36:47 +02:00
Mickael Remond d02d7b2b6a Remove compile warning 2016-08-01 15:35:54 +02:00
Mickael Remond 90ea3ca361 Improve error message when try to call api on api root 2016-08-01 15:29:47 +02:00
Christophe Romain bf45c9eeee Switch mix worker to transient 2016-08-01 14:09:16 +02:00
Christophe Romain a9c6748ec7 Add missing comas in sql statement (#1219) 2016-08-01 10:55:02 +02:00
Mickael Remond 4982639d05 Fix error return expectation in command test 2016-08-01 09:28:54 +02:00
Mickael Remond c5c394e929 Fix HTTP process return formatting 2016-08-01 08:58:49 +02:00
Mickael Remond 6ea7153e31 Improve error handling 2016-07-31 22:48:24 +02:00
Mickael Remond 2a49f8cae7 Change name of result key for offline count to value
This is more user friendly and should be more consistent with other commands.
2016-07-30 20:12:04 +02:00
Mickael Remond 674a8039ef Add support for sending back missing scope error to API ReST command calls 2016-07-30 18:51:54 +02:00
Mickael Remond 4bf8ce7681 Make s2s stats commands more robust 2016-07-30 18:50:58 +02:00
Mickael Remond 19ad6e6145 Ensure ejabberdctl status result is in valid shell supported range 2016-07-30 13:18:39 +02:00
Mickael Remond 39640b67c7 Add support for rich error reporting for API 2016-07-30 13:08:30 +02:00
Mickael Remond fb2603d3cd Return 409 conflict error code on register if user already exists 2016-07-30 11:50:04 +02:00
Mickaël Rémond 4a49dfecf3 Merge pull request #1221 from processone/expand_api
Do not crash on check when we do not have JID
2016-07-30 10:55:39 +02:00
Mickael Remond 42e6f72ee9 Do not crash on check when we do not have JID 2016-07-29 20:38:05 +02:00
Christophe Romain 3c58a93eb8 Merge pull request #1178 from candrews/patch-1
Harden the systemd unit
2016-07-29 11:33:32 +02:00
Christophe Romain a080322055 Switch workers from temporary to transient 2016-07-29 11:18:42 +02:00
Paweł Chmielowski fd365b2893 Display data that is send to websocket connection in debug log level
This should help with detecting problems like in #1097
2016-07-28 16:20:28 +02:00
Christophe Romain fad088a3c4 Merge pull request #1193 from gabrielgatu/support-elixir-module-installer
Fix issue #625: Writing Elixir modules
2016-07-28 16:06:12 +02:00
gabrielgatu 91865c66c0 Start elixir application after ejabberd_app:start_apps() 2016-07-28 15:57:35 +02:00
Mickaël Rémond 7a74a4836a Merge pull request #1211 from processone/expand_api
There is still work to do, be we reached a stable state and can merge up to this point.
2016-07-28 14:57:48 +02:00
Holger Weiss 72445bb374 mod_http_upload_quota: Apply cosmetic changes
Use "fun f/1" syntax in place of "fun(X) -> f(X) end".
2016-07-27 00:28:47 +02:00
Evgeniy Khramtsov 984c4cf6bd Add 'allow_subscription' MUC configuration option 2016-07-26 14:37:28 +03:00
Mickael Remond 2a8005e47f Add ability to run test with Elixir mix 2016-07-26 12:17:37 +02:00
Mickael Remond 7781f39b74 Clarify command module API 2016-07-26 12:15:03 +02:00
Mickael Remond e5fd1ee4f6 Avoid starting several time the owner process 2016-07-26 12:12:48 +02:00
Mickael Remond 9ff7257287 Make jlib ETS table more resilient 2016-07-26 11:58:14 +02:00
Mickael Remond 12f74b4aa7 Fix list appending bug 2016-07-26 11:57:38 +02:00
Mickael Remond fede85c9bd Remove unused import 2016-07-26 11:53:34 +02:00
Alexey Shchepin 839490b0d9 Add DB backend support for ejabberd_oauth 2016-07-25 20:08:30 +03:00
Mickael Remond dbc0498279 Fix tests, command need to be properly added to list of exposed commands 2016-07-25 18:28:40 +02:00
Mickael Remond c183092aa4 Simplify code for command policy group expansion 2016-07-25 18:28:05 +02:00
Badlop 5d4f8bcf0d Export acl:parse_ip_netmask/1 for mod_rest (ejabberd-contrib#175) 2016-07-25 16:57:05 +02:00
Mickael Remond d7ad99f147 Initial attempt on access on commands
May change and will require more work / test / refactor
2016-07-25 11:43:49 +02:00
Holger Weiss 4b0d71d402 Don't return error for blocked MUC PMs
If a message stanza is blocked as per XEP-0016 or XEP-0191 and the
stanza is marked as a private MUC message, don't return an error.  This
makes sure users won't be kicked from MUC rooms when blocking other
participants.
2016-07-24 20:55:11 +02:00
Mickael Remond b4a430541d Return more user friendly, human readable error description 2016-07-24 14:10:12 +02:00
Mickael Remond bfa61eaa46 Make default OAuth token TTL values more user friendly 2016-07-23 18:57:57 +02:00
Mickael Remond 68555ff466 Add support for checking access rules conformance for commands 2016-07-23 18:21:45 +02:00
Mickael Remond caf2c20210 Error when not authorized should be 403 2016-07-23 18:21:45 +02:00
Mickael Remond 1485b56211 Move any access rules check to ACL module 2016-07-23 18:21:45 +02:00
Mickael Remond 2c70c572c8 Clean-up of error codes and format json structure 2016-07-23 18:21:45 +02:00
Holger Weiss d4d1941133 XEP-0198: Log debug message when dropping stanza
Log a debug message when an unacknowledged message is neither resent nor
bounced because it's archived.
2016-07-23 01:23:24 +02:00
Holger Weiss 814b80c644 Preserve PID for offline sessions
Don't set the PID to 'undefined' when a session goes offline, as this
looses the information which node created the session table entry.

Fixes #1196.
2016-07-23 01:08:05 +02:00
Pablo Polvorin 4332dddbc4 Support oauth password grant type
As in https://tools.ietf.org/html/rfc6749#section-4.3
2016-07-22 19:17:12 -03:00
Pablo Polvorin 57aeef74d5 stringprep might already be started
Depending on the way the test us ran
(full test suite or the elixir quicktest one)
the stringprep might already be loaded.
2016-07-22 19:15:56 -03:00
Pablo Polvorin 12b58b9870 Fix elixir test case: stringprep was required 2016-07-22 16:25:54 -03:00
Pablo Polvorin caf7b54305 oauth: single jid field instead of username/password fields 2016-07-22 15:37:48 -03:00
Badlop c5d9d35e7b Convert password provided by web form to UTF8 before passing it (#375) 2016-07-22 16:52:13 +02:00
Jerome Sautret ffbe97d988 Quote postgresql database name (#1136) 2016-07-22 16:33:40 +02:00
Paweł Chmielowski bdfef09c0f Fix handling of complex values as arguments in http_api 2016-07-22 15:26:27 +02:00
Evgeny Khramtsov dd38bef8b1 Merge pull request #1201 from xmppjingle/master
External Component Connection Hooks
2016-07-22 00:35:43 +04:00
xmppjingle 6983dfa21f External Component Hook
Changed Hook Trigger Event and included a Reason upon
component_disconnected/2 Hook
2016-07-21 14:03:01 -03:00
Pablo Polvorin cbfab687e8 Oauth callback must pass expires_in as ttl instead of epoch 2016-07-20 14:47:11 -03:00
Paweł Chmielowski c2753cd51c Use different version of elixir depending on erlang version 2016-07-20 10:12:00 +02:00
Paweł Chmielowski 5458d8bfcb Add else branch to if_version_{above,below} 2016-07-20 10:11:34 +02:00
Paweł Chmielowski 7748dd4e5d Make processing of if_* clauses in rebar.config recursive 2016-07-20 10:11:08 +02:00
Pablo Polvorin 0c0c6465ba Fix test for changes in oauth expiry 2016-07-19 20:36:02 -03:00
Pablo Polvorin b5a90be3cb Merge branch 'master' of github.com:processone/ejabberd 2016-07-19 20:19:17 -03:00
Pablo Polvorin 1d317e8068 Let user choose the desired oauth token TTL 2016-07-19 20:18:07 -03:00
Holger Weiss 8f8c499cfa mod_mam: Fix handling of result set page limit
Restore function clause for handling a client-specified result set page
limit that doesn't exceed mod_mam's upper threshold.
2016-07-19 21:23:30 +02:00
Holger Weiss 9fcb81dea9 mod_mam: Always limit result set page size
Limit the number of messages returned for a given MAM request even if
the client didn't specify an RSM set (not just if the client specified
an RSM set without a limit).

This is still not done for MAM v0.2 requests though, as that version of
the XEP doesn't require clients to support RSM.
2016-07-19 21:08:13 +02:00
Mickael Remond 490a758050 Upgrade Elixir version to 1.2 in rebar config
This matches the version used in mix.exs
2016-07-19 13:05:01 +02:00
Mickael Remond f79ac6874e Lock relx version as newer version does not compile fine 2016-07-19 12:35:45 +02:00
Paweł Chmielowski 655cbf6055 Make access rules in ejabberd_web_admin configurable 2016-07-19 11:27:45 +02:00
Pablo Polvorin 483ef09263 Fix command argument formatting 2016-07-19 00:51:04 -03:00
Pablo Polvorin 33e0283f0d Add 'ejabberd:user' and 'ejabberd:admin' oauth scopes
'ejabberd:user' includes all commands defined with policy "user".
'ejabberd:admin' includes commands defined with policy "admin".
2016-07-19 00:24:06 -03:00
Pablo Polvorin 673a654c47 Fix ce0d1704c6
Original request was to allow ejabberd sysadmin to generate
tokens for specific users.  JIDs must not be passed as argument
when requesting the tokens.
2016-07-18 20:25:23 -03:00
xmppjingle 48c88b61b6 Merge remote-tracking branch 'processone/master' 2016-07-18 17:55:31 -03:00
xmppjingle fca2f24231 External Component Connection Hooks 2016-07-18 17:55:10 -03:00
Holger Weiss 8bc3dc9c49 jlib: Don't try to keep just one <delay/> tag
It seems unclear whether XEP-0203 really mandates that stanzas may not
have multiple <delay/> tags.  Editing/removing existing tags doesn't
seem worth the effort, especially as we'd have to take more care which
tag to keep if the stanza already has more than one.
2016-07-18 22:31:08 +02:00
Holger Weiss 749033598d Omit [info] message with number of queued stanzas
Just log a debug message if a stream management session times out and
some stanzas weren't acknowledged.
2016-07-10 22:21:57 +02:00
Evgeniy Khramtsov f6e960d326 Fix compilation error 2016-07-10 08:45:24 +03:00
Evgeniy Khramtsov 786bd4f26c Use hooks instead of direct calls to mod_mam 2016-07-09 12:43:01 +03:00
Holger Weiss 5f48d2641b mod_http_upload_quota: Depend on mod_http_upload
mod_http_upload_quota uses mod_http_upload's "docroot" option, so the
mod_http_upload configuration must be parsed, first.  Fixes #1025.
2016-07-08 20:47:02 +02:00
Mickael Remond 1a62d4e04b Update stringprep and iconv 2016-07-08 18:28:51 +02:00
Christophe Romain 6b38d19085 Do send last items only for subscription on current plugin type 2016-07-08 15:18:39 +02:00
Evgeniy Khramtsov 661b041302 Rename MUC/Sub's namespace 2016-07-08 15:07:26 +03:00
Evgeniy Khramtsov 368b202144 Handle MUC/Sub subscriptions list request 2016-07-08 15:07:10 +03:00
Evgeniy Khramtsov caaf02eaa0 Advertise MUC/Sub support in MUC service disco#info 2016-07-08 15:06:08 +03:00
Evgeniy Khramtsov 32de9a56a5 Experimental MUC/Sub support 2016-07-08 15:05:50 +03:00
Mickael Remond febbc2bb5a Update dependencies 2016-07-08 11:40:28 +02:00
Evgeniy Khramtsov 71f27ee7d4 Get rid of warnings 2016-07-07 12:17:38 +03:00
Evgeniy Khramtsov c718cbbd9f Warn on cyclic modules dependencies 2016-07-07 11:34:17 +03:00
Badlop 12c0d888b1 Revert "Recover fix of 907e239 lost in 9deb294 (thanks to Alexey Shchepin)" (#1183)
This reverts commit 53f3a45803.
2016-07-06 17:54:37 +02:00
Evgeniy Khramtsov 4220a2b98c Make modules loading in a dependent order (#1191) 2016-07-06 14:58:48 +03:00
Christophe Romain de9f80f2ce Add missing '/' for jid matching from commit e300f80 2016-07-06 10:06:17 +02:00
Alexey Shchepin be3a4acb55 Fix missed escaping in node_flat_sql.erl 2016-07-05 17:45:37 +03:00
Christophe Romain 3820aaa421 Quote reserver 'type' keyword for pgsql to fix e300f80 2016-07-05 16:16:40 +02:00
Christophe Romain e300f8095d Fix use of like parameter in sql pubsub's requests 2016-07-05 15:43:59 +02:00
gabrielgatu b31c0d9e2e Support elixir module installer 2016-07-05 12:36:49 +02:00
Holger Weiss 8e04a7ef4d mod_configure: Fix editing of access rules 2016-07-03 22:58:54 +02:00
Paweł Chmielowski 16b1d8541a Grab new p1_utils that has fix for R19 2016-07-01 21:41:12 +02:00
Paweł Chmielowski 0737958b45 Fix compilation issues on R19 2016-07-01 21:20:10 +02:00
Mickael Remond 024124decb Fix dependencies when using Elixir Mix 2016-06-30 11:35:42 +02:00
Mickael Remond 88ac1dc56b Update dependencies 2016-06-30 11:33:38 +02:00
Holger Weiss 8be1d49961 mod_mam_mnesia: Force garbage collection
The VM fails to collect the garbage generated during MAM lookups
automatically, so mod_mam_mnesia's memory usage easily goes up to
several gigabytes if we don't force garbage collection.
2016-06-29 22:32:59 +02:00
Holger Weiss 10d4c16a97 mod_client_state: Throttle PEP stanzas by default 2016-06-29 22:22:49 +02:00
Craig Andrews 2e28d06744 Harden the systemd unit
Restrict capabilities, have a private tmp directory, private /dev, and don't accessing file system locations that really shouldn't be accessed.
2016-06-28 17:02:41 -04:00
108 changed files with 2686 additions and 1692 deletions
+7
View File
@@ -12,6 +12,13 @@ ExecStop=@ctlscriptpath@/ejabberdctl stop
ExecReload=@ctlscriptpath@/ejabberdctl reload_config
Type=oneshot
RemainAfterExit=yes
# The CAP_DAC_OVERRIDE capability is required for pam authentication to work
CapabilityBoundingSet=CAP_DAC_OVERRIDE
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=full
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
+25
View File
@@ -26,6 +26,25 @@
{tuple, [rterm()]} | {list, rterm()} |
rescode | restuple.
-type oauth_scope() :: atom().
%% ejabberd_commands OAuth ReST ACL definition:
%% Two fields exist that are used to control access on a command from ReST API:
%% 1. Policy
%% If policy is:
%% - restricted: command is not exposed as OAuth Rest API.
%% - admin: Command is allowed for user that have Admin Rest command enabled by access rule: commands_admin_access
%% - user: Command might be called by any server user.
%% - open: Command can be called by anyone.
%%
%% Policy is just used to control who can call the command. A specific additional access rules can be performed, as
%% defined by access option.
%% Access option can be a list of:
%% - {Module, accessName, DefaultValue}: Reference and existing module access to limit who can use the command.
%% - AccessRule name: direct name of the access rule to check in config file.
%% TODO: Access option could be atom command (not a list). In the case, User performing the command, will be added as first parameter
%% to command, so that the command can perform additional check.
-record(ejabberd_commands,
{name :: atom(),
tags = [] :: [atom()] | '_' | '$2',
@@ -36,19 +55,25 @@
function :: atom() | '_',
args = [] :: [aterm()] | '_' | '$1' | '$2',
policy = restricted :: open | restricted | admin | user,
%% access is: [accessRuleName] or [{Module, AccessOption, DefaultAccessRuleName}]
access = [] :: [{atom(),atom(),atom()}|atom()],
result = {res, rescode} :: rterm() | '_' | '$2',
args_desc = none :: none | [string()] | '_',
result_desc = none :: none | string() | '_',
args_example = none :: none | [any()] | '_',
result_example = none :: any()}).
%% TODO Fix me: Type is not up to date
-type ejabberd_commands() :: #ejabberd_commands{name :: atom(),
tags :: [atom()],
desc :: string(),
longdesc :: string(),
version :: integer(),
module :: atom(),
function :: atom(),
args :: [aterm()],
policy :: open | restricted | admin | user,
access :: [{atom(),atom(),atom()}|atom()],
result :: rterm()}.
%% @type ejabberd_commands() = #ejabberd_commands{
+26
View File
@@ -0,0 +1,26 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-record(oauth_token, {
token = <<"">> :: binary() | '_',
us = {<<"">>, <<"">>} :: {binary(), binary()} | '_',
scope = [] :: [binary()] | '_',
expire :: integer() | '$1'
}).
+2 -2
View File
@@ -1,9 +1,9 @@
-ifndef(EJABBERD_SM_HRL).
-define(EJABBERD_SM_HRL, true).
-record(session, {sid, usr, us, priority, info}).
-record(session, {sid, usr, us, priority, info = []}).
-record(session_counter, {vhost, count}).
-type sid() :: {erlang:timestamp(), pid()} | {erlang:timestamp(), undefined}.
-type sid() :: {erlang:timestamp(), pid()}.
-type ip() :: {inet:ip_address(), inet:port_number()} | undefined.
-type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()}
| {oor, boolean()} | {auth_module, atom()}
+3
View File
@@ -53,6 +53,7 @@
members_by_default = true :: boolean(),
members_only = false :: boolean(),
allow_user_invites = false :: boolean(),
allow_subscription = false :: boolean(),
password_protected = false :: boolean(),
password = <<"">> :: binary(),
anonymous = true :: boolean(),
@@ -76,6 +77,8 @@
jid :: jid(),
nick :: binary(),
role :: role(),
is_subscriber = false :: boolean(),
subscriptions = [] :: [binary()],
last_presence :: xmlel()
}).
+8
View File
@@ -164,3 +164,11 @@
-define(NS_MIX_NODES_PARTICIPANTS, <<"urn:xmpp:mix:nodes:participants">>).
-define(NS_MIX_NODES_SUBJECT, <<"urn:xmpp:mix:nodes:subject">>).
-define(NS_MIX_NODES_CONFIG, <<"urn:xmpp:mix:nodes:config">>).
-define(NS_MUCSUB, <<"urn:xmpp:mucsub:0">>).
-define(NS_MUCSUB_NODES_PRESENCE, <<"urn:xmpp:mucsub:nodes:presence">>).
-define(NS_MUCSUB_NODES_MESSAGES, <<"urn:xmpp:mucsub:nodes:messages">>).
-define(NS_MUCSUB_NODES_PARTICIPANTS, <<"urn:xmpp:mucsub:nodes:participants">>).
-define(NS_MUCSUB_NODES_AFFILIATIONS, <<"urn:xmpp:mucsub:nodes:affiliations">>).
-define(NS_MUCSUB_NODES_SUBJECT, <<"urn:xmpp:mucsub:nodes:subject">>).
-define(NS_MUCSUB_NODES_CONFIG, <<"urn:xmpp:mucsub:nodes:config">>).
-define(NS_MUCSUB_NODES_SYSTEM, <<"urn:xmpp:mucsub:nodes:system">>).
+1 -1
View File
@@ -3,7 +3,7 @@ defmodule ExUnit.CTFormatter do
use GenEvent
import ExUnit.Formatter, only: [format_time: 2, format_filters: 2, format_test_failure: 5,
import ExUnit.Formatter, only: [format_time: 2, format_test_failure: 5,
format_test_case_failure: 5]
def init(opts) do
+8 -3
View File
@@ -7,15 +7,20 @@ defmodule ModPresenceDemo do
Ejabberd.Hooks.add(:set_presence_hook, host, __ENV__.module, :on_presence, 50)
:ok
end
def stop(host) do
info('Stopping ejabberd module Presence Demo')
Ejabberd.Hooks.delete(:set_presence_hook, host, __ENV__.module, :on_presence, 50)
:ok
end
def on_presence(user, _server, _resource, _packet) do
info('Receive presence for #{user}')
:none
end
end
# gen_mod callbacks
def depends(_host, _opts), do: []
def mod_opt_type(_), do: []
end
+8 -1
View File
@@ -11,6 +11,8 @@ defmodule Ejabberd.Mixfile do
compilers: [:asn1] ++ Mix.compilers,
erlc_options: erlc_options,
erlc_paths: ["asn1", "src"],
# Elixir tests are starting the part of ejabberd they need
aliases: [test: "test --no-start"],
package: package,
deps: deps]
end
@@ -56,7 +58,12 @@ defmodule Ejabberd.Mixfile do
{:ezlib, "~> 1.0"},
{:iconv, "~> 1.0"},
{:eredis, "~> 1.0"},
{:exrm, "~> 1.0.0-rc7", only: :dev}]
{:exrm, "~> 1.0.0", only: :dev},
# relx is used by exrm. Lock version as for now, ejabberd doesn not compile fine with
# version 3.20:
{:relx, "~> 3.19.0", only: :dev},
{:meck, "~> 0.8.4", only: :test},
{:moka, github: "processone/moka", tag: "1.0.5b", only: :test}]
end
defp package do
+14 -11
View File
@@ -1,26 +1,29 @@
%{"bbmustache": {:hex, :bbmustache, "1.0.4", "7ba94f971c5afd7b6617918a4bb74705e36cab36eb84b19b6a1b7ee06427aa38", [:rebar], []},
"cache_tab": {:hex, :cache_tab, "1.0.2", "c12099fff8b0f7011e7656844f5f67b958b7033a521c69595dbf2a5d7c8bb34f", [:rebar3], [{:p1_utils, "1.0.3", [hex: :p1_utils, optional: false]}]},
"cache_tab": {:hex, :cache_tab, "1.0.3", "0e3c40dde2fe2a6a4db241d7583cea0cc1bcf29e546a0a22f15b75366b2f336e", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]},
"cf": {:hex, :cf, "0.2.1", "69d0b1349fd4d7d4dc55b7f407d29d7a840bf9a1ef5af529f1ebe0ce153fc2ab", [:rebar3], []},
"eredis": {:hex, :eredis, "1.0.8", "ab4fda1c4ba7fbe6c19c26c249dc13da916d762502c4b4fa2df401a8d51c5364", [:rebar], []},
"erlware_commons": {:hex, :erlware_commons, "0.19.0", "7b43caf2c91950c5f60dc20451e3c3afba44d3d4f7f27bcdc52469285a5a3e70", [:rebar3], [{:cf, "0.2.1", [hex: :cf, optional: false]}]},
"esip": {:hex, :esip, "1.0.4", "47426c264f6ea5a2a74b53ecf825593b689b47ed3eab873ff8a595ea35aa8507", [:rebar3], [{:stun, "1.0.3", [hex: :stun, optional: false]}, {:p1_utils, "1.0.3", [hex: :p1_utils, optional: false]}, {:fast_tls, "1.0.3", [hex: :fast_tls, optional: false]}]},
"exrm": {:hex, :exrm, "1.0.5", "53ecb20da2f4e5b4c82ea6776824fbc677c8d287bf20efc9fc29cacc2cca124f", [:mix], [{:relx, "~> 3.5", [hex: :relx, optional: false]}]},
"esip": {:hex, :esip, "1.0.7", "f75f6a5cac6814e506f0ff96141fbe276dee3261fca1471c8edfdde25b74f877", [:rebar3], [{:fast_tls, "1.0.6", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}, {:stun, "1.0.6", [hex: :stun, optional: false]}]},
"exrm": {:hex, :exrm, "1.0.8", "5aa8990cdfe300282828b02cefdc339e235f7916388ce99f9a1f926a9271a45d", [:mix], [{:relx, "~> 3.5", [hex: :relx, optional: false]}]},
"ezlib": {:hex, :ezlib, "1.0.1", "add8b2770a1a70c174aaea082b4a8668c0c7fdb03ee6cc81c6c68d3a6c3d767d", [:rebar3], []},
"fast_tls": {:hex, :fast_tls, "1.0.3", "7ccd02ffcba24f8792dd88bd71fa543ea438c58bd0f132576031533083ede578", [:rebar3], [{:p1_utils, "1.0.3", [hex: :p1_utils, optional: false]}]},
"fast_xml": {:hex, :fast_xml, "1.1.11", "1d9aedbe367a3d42ba2c6d9d4ee5808c0846e06a28e366e262e41d3ce462cbdf", [:rebar3], [{:p1_utils, "1.0.3", [hex: :p1_utils, optional: false]}]},
"fast_yaml": {:hex, :fast_yaml, "1.0.3", "862e3f89d52aa6a72eef3121edf303aac2f3c559cbaba2d2a1fd0c09d5f15f9a", [:rebar3], [{:p1_utils, "1.0.3", [hex: :p1_utils, optional: false]}]},
"fast_tls": {:hex, :fast_tls, "1.0.6", "750a74aabb05056f0f222910f0955883649e6c5d67df6ca504ff676160d22b89", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]},
"fast_xml": {:hex, :fast_xml, "1.1.14", "23d4de66e645bca1d8a557444e83062cc42f235d7a7e2d4072d525bac3986f04", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]},
"fast_yaml": {:hex, :fast_yaml, "1.0.5", "a67772c75abb84181c6c9899e1f988b08ac214ea0d764ff1f9889bb7e27f74d4", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]},
"getopt": {:hex, :getopt, "0.8.2", "b17556db683000ba50370b16c0619df1337e7af7ecbf7d64fbf8d1d6bce3109b", [:rebar], []},
"goldrush": {:hex, :goldrush, "0.1.7", "349a351d17c71c2fdaa18a6c2697562abe136fec945f147b381f0cf313160228", [:rebar3], []},
"iconv": {:hex, :iconv, "1.0.0", "5ff1c54e5b3b9a8235de872632e9612c7952acdf89bc21db2f2efae0e72647be", [:rebar3], []},
"iconv": {:hex, :iconv, "1.0.1", "dbb8700070577e7a021a095cc5ead221069a0c4034bfadca2516c1f1109ee7fd", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]},
"jiffy": {:hex, :jiffy, "0.14.7", "9f33b893edd6041ceae03bc1e50b412e858cc80b46f3d7535a7a9940a79a1c37", [:rebar, :make], []},
"lager": {:hex, :lager, "3.0.2", "25dc81bc3659b62f5ab9bd073e97ddd894fc4c242019fccef96f3889d7366c97", [:rebar3], [{:goldrush, "0.1.7", [hex: :goldrush, optional: false]}]},
"meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:rebar, :make], []},
"moka": {:git, "https://github.com/processone/moka.git", "768efea96443c57125e6247dbebee687f17be149", [tag: "1.0.5b"]},
"p1_mysql": {:hex, :p1_mysql, "1.0.1", "d2be1cfc71bb4f1391090b62b74c3f5cb8e7a45b0076b8cb290cd6b2856c581b", [:rebar3], []},
"p1_oauth2": {:hex, :p1_oauth2, "0.6.1", "4e021250cc198c538b097393671a41e7cebf463c248980320e038fe0316eb56b", [:rebar3], []},
"p1_pgsql": {:hex, :p1_pgsql, "1.1.0", "ca525c42878eac095e5feb19563acc9915c845648f48fdec7ba6266c625d4ac7", [:rebar3], []},
"p1_utils": {:hex, :p1_utils, "1.0.3", "8d469a34e8fe3898dda9dfda545fdb69cabfee144ebe31732d2c7905420603ec", [:rebar3], []},
"p1_utils": {:hex, :p1_utils, "1.0.4", "7face65db102b5d1ebe7ad3c7517c5ee8cfbe174c6658e3affbb00eb66e06787", [:rebar3], []},
"p1_xmlrpc": {:hex, :p1_xmlrpc, "1.15.1", "a382b62dc21bb372281c2488f99294d84f2b4020ed0908a1c4ad710ace3cf35a", [:rebar3], []},
"providers": {:hex, :providers, "1.6.0", "db0e2f9043ae60c0155205fcd238d68516331d0e5146155e33d1e79dc452964a", [:rebar3], [{:getopt, "0.8.2", [hex: :getopt, optional: false]}]},
"relx": {:hex, :relx, "3.19.0", "286dd5244b4786f56aac75d5c8e2d1fb4cfd306810d4ec8548f3ae1b3aadb8f7", [:rebar3], [{:providers, "1.6.0", [hex: :providers, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:erlware_commons, "0.19.0", [hex: :erlware_commons, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}]},
"relx": {:hex, :relx, "3.19.0", "286dd5244b4786f56aac75d5c8e2d1fb4cfd306810d4ec8548f3ae1b3aadb8f7", [:rebar3], [{:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:erlware_commons, "0.19.0", [hex: :erlware_commons, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:providers, "1.6.0", [hex: :providers, optional: false]}]},
"samerlib": {:git, "https://github.com/processone/samerlib", "9158f65d18ec63f8b409543b6fb46dd5fce46160", [tag: "0.8.0b"]},
"sqlite3": {:hex, :sqlite3, "1.1.5", "794738b6d07b6d36ec6d42492cb9d629bad9cf3761617b8b8d728e765db19840", [:rebar3], []},
"stringprep": {:hex, :stringprep, "1.0.3", "0fc697484a83c868817c5c6d74c310a1f821b3cf8b6c0eb105335421d0b94d99", [:rebar3], [{:p1_utils, "1.0.3", [hex: :p1_utils, optional: false]}]},
"stun": {:hex, :stun, "1.0.3", "d8dcf8beb38939f3fcded73e89e5753cb5a2fed2e74fa5b658124849a9e97fe9", [:rebar3], [{:p1_utils, "1.0.3", [hex: :p1_utils, optional: false]}, {:fast_tls, "1.0.3", [hex: :fast_tls, optional: false]}]}}
"stringprep": {:hex, :stringprep, "1.0.5", "f29395275c35af5051b29bf875b44ac632dc4d0287880f0e143b536c61fd0ed5", [:rebar3], [{:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]},
"stun": {:hex, :stun, "1.0.6", "1ca9dea574e09f60971bd8de9cb7e34f327cbf435462cf56aa30f05c1ee2f231", [:rebar3], [{:fast_tls, "1.0.6", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.4", [hex: :p1_utils, optional: false]}]}}
+12 -12
View File
@@ -7,15 +7,15 @@
%%% Created : 1 May 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
{deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.0.2"}}},
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.4"}}},
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.2"}}},
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.4"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.3"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.12"}}},
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.4"}}},
{esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.5"}}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.3"}}},
{deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.2.1"}}},
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.5"}}},
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.3"}}},
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.6"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.5"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.14"}}},
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.6"}}},
{esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.7"}}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.5"}}},
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.7"}}},
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.1"}}},
{p1_xmlrpc, ".*", {git, "https://github.com/processone/p1_xmlrpc", {tag, "1.15.1"}}},
@@ -34,17 +34,17 @@
"527722d12d0433b837cdb92a60900c2cb5df8942"}}},
%% Forces correct dependency for riakc and allow using newer meck version)
{if_var_true, riak, {hamcrest, ".*", {git, "https://github.com/hyperthunk/hamcrest-erlang",
"908a24fda4a46776a5135db60ca071e3d783f9f6"}}}, % for riak_pb-2.1.0.7
"13f9bfb9b27d216e8e033b0e0a9a29097ed923dd"}}}, % for riak_pb-2.1.0.7
{if_var_true, riak, {protobuffs, ".*", {git, "https://github.com/basho/erlang_protobuffs",
"6e7fc924506e2dc166a6170e580ce1d95ebbd5bd"}}}, % for riak_pb-2.1.0.7 with correct meck dependency
%% Elixir support, needed to run tests
{if_var_true, elixir, {elixir, ".*", {git, "https://github.com/elixir-lang/elixir",
{tag, "v1.1.1"}}}},
{tag, {if_version_above, "17", "v1.2.6", "v1.1.1"}}}}},
%% TODO: When modules are fully migrated to new structure and mix, we will not need anymore rebar_elixir_plugin
{if_var_true, elixir, {rebar_elixir_plugin, ".*",
{git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}},
{if_var_true, iconv, {iconv, ".*", {git, "https://github.com/processone/iconv",
{tag, "1.0.0"}}}},
{tag, "1.0.1"}}}},
{if_var_true, tools, {meck, "0.8.*", {git, "https://github.com/eproxus/meck",
{tag, "0.8.4"}}}},
{if_var_true, tools, {moka, ".*", {git, "https://github.com/processone/moka.git",
+26 -5
View File
@@ -19,7 +19,7 @@ ModCfg0 = fun(F, Cfg, [Key|Tail], Op, Default) ->
[{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg]
end
end,
ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, Default) end.
ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, Default) end,
Cfg = case file:consult(filename:join(filename:dirname(SCRIPT), "vars.config")) of
{ok, Terms} ->
@@ -28,6 +28,13 @@ Cfg = case file:consult(filename:join(filename:dirname(SCRIPT), "vars.config"))
[]
end,
ProcessSingleVar = fun(F, Var, Tail) ->
case F(F, [Var], []) of
[] -> Tail;
[Val] -> [Val | Tail]
end
end,
ProcessVars = fun(_F, [], Acc) ->
lists:reverse(Acc);
(F, [{Type, Ver, Value} | Tail], Acc) when
@@ -40,17 +47,31 @@ ProcessVars = fun(_F, [], Acc) ->
SysVer < Ver
end,
if Include ->
F(F, Tail, [Value | Acc]);
F(F, Tail, ProcessSingleVar(F, Value, Acc));
true ->
F(F, Tail, Acc)
end;
(F, [{Type, Ver, Value, ElseValue} | Tail], Acc) when
Type == if_version_above orelse
Type == if_version_below ->
SysVer = erlang:system_info(otp_release),
Include = if Type == if_version_above ->
SysVer > Ver;
true ->
SysVer < Ver
end,
if Include ->
F(F, Tail, ProcessSingleVar(F, Value, Acc));
true ->
F(F, Tail, ProcessSingleVar(F, ElseValue, Acc))
end;
(F, [{Type, Var, Value} | Tail], Acc) when
Type == if_var_true orelse
Type == if_var_false ->
Flag = Type == if_var_true,
case proplists:get_bool(Var, Cfg) of
V when V == Flag ->
F(F, Tail, [Value | Acc]);
F(F, Tail, ProcessSingleVar(F, Value, Acc));
_ ->
F(F, Tail, Acc)
end;
@@ -59,7 +80,7 @@ ProcessVars = fun(_F, [], Acc) ->
Type == if_var_no_match ->
case proplists:get_value(Var, Cfg) of
V when V == Match ->
F(F, Tail, [Value | Acc]);
F(F, Tail, ProcessSingleVar(F, Value, Acc));
_ ->
F(F, Tail, Acc)
end;
@@ -146,7 +167,7 @@ Conf6 = case {lists:keyfind(cover_enabled, 1, Conf5), os:getenv("TRAVIS")} of
Conf5
end,
%io:format("ejabberd configuration:~n ~p~n", [Conf5]),
%io:format("ejabberd configuration:~n ~p~n", [Conf6]),
Conf6.
+7
View File
@@ -313,3 +313,10 @@ CREATE TABLE sm (
CREATE UNIQUE INDEX i_sm_sid ON sm(usec, pid);
CREATE INDEX i_sm_node ON sm(node);
CREATE INDEX i_sm_username ON sm(username);
CREATE TABLE oauth_token (
token text NOT NULL PRIMARY KEY,
jid text NOT NULL,
scope text NOT NULL,
expire bigint NOT NULL
);
+10
View File
@@ -480,3 +480,13 @@ ON DELETE CASCADE;
ALTER TABLE [dbo].[pubsub_state] CHECK CONSTRAINT [pubsub_state_ibfk_1];
CREATE TABLE [dbo].[oauth_token] (
[token] [varchar] (250) NOT NULL,
[jid] [text] NOT NULL,
[scope] [text] NOT NULL,
[expire] [bigint] NOT NULL,
CONSTRAINT [oauth_token_PRIMARY] PRIMARY KEY CLUSTERED
(
[token] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
) TEXTIMAGE_ON [PRIMARY];
+7
View File
@@ -328,3 +328,10 @@ CREATE TABLE sm (
CREATE UNIQUE INDEX i_sid ON sm(usec, pid(75));
CREATE INDEX i_node ON sm(node(75));
CREATE INDEX i_username ON sm(username);
CREATE TABLE oauth_token (
token varchar(191) NOT NULL PRIMARY KEY,
jid text NOT NULL,
scope text NOT NULL,
expire bigint NOT NULL
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+9
View File
@@ -330,3 +330,12 @@ CREATE TABLE sm (
CREATE UNIQUE INDEX i_sm_sid ON sm USING btree (usec, pid);
CREATE INDEX i_sm_node ON sm USING btree (node);
CREATE INDEX i_sm_username ON sm USING btree (username);
CREATE TABLE oauth_token (
token text NOT NULL,
jid text NOT NULL,
scope text NOT NULL,
expire bigint NOT NULL
);
CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token);
+11 -1
View File
@@ -31,10 +31,11 @@
-export([add_access/3, clear/0]).
-export([start/0, add/3, add_list/3, add_local/3, add_list_local/3,
load_from_config/0, match_rule/3,
load_from_config/0, match_rule/3, any_rules_allowed/3,
transform_options/1, opt_type/1, acl_rule_matches/3,
acl_rule_verify/1, access_matches/3,
transform_access_rules_config/1,
parse_ip_netmask/1,
access_rules_validator/1, shaper_rules_validator/1]).
-include("ejabberd.hrl").
@@ -274,6 +275,15 @@ normalize_spec(Spec) ->
end
end.
-spec any_rules_allowed(global | binary(), access_name(),
jid() | ljid() | inet:ip_address()) -> boolean().
any_rules_allowed(Host, Access, Entity) ->
lists:any(fun (Rule) ->
allow == acl:match_rule(Host, Rule, Entity)
end,
Access).
-spec match_rule(global | binary(), access_name(),
jid() | ljid() | inet:ip_address()) -> any().
+5 -18
View File
@@ -41,13 +41,7 @@
%% Parse an ad-hoc request. Return either an adhoc_request record or
%% an {error, ErrorType} tuple.
%%
-spec(parse_request/1 ::
(
IQ :: iq_request())
-> adhoc_response()
%%
| {error, _}
).
-spec parse_request(IQ :: iq_request()) -> adhoc_response() | {error, _}.
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
?DEBUG("entering parse_request...", []),
@@ -88,12 +82,9 @@ find_xdata_el1([_ | Els]) -> find_xdata_el1(Els).
%% record, filling in values for language, node and session id from
%% the request.
%%
-spec(produce_response/2 ::
(
Adhoc_Request :: adhoc_request(),
Adhoc_Response :: adhoc_response())
-> Xmlel::xmlel()
).
-spec produce_response(Adhoc_Request :: adhoc_request(),
Adhoc_Response :: adhoc_response()) ->
Xmlel::xmlel().
%% Produce a <command/> node to use as response from an adhoc_response
%% record.
@@ -104,11 +95,7 @@ produce_response(#adhoc_request{lang = Lang, node = Node, sessionid = SessionID}
}).
%%
-spec(produce_response/1 ::
(
Adhoc_Response::adhoc_response())
-> Xmlel::xmlel()
).
-spec produce_response(Adhoc_Response::adhoc_response()) -> Xmlel::xmlel().
produce_response(
#adhoc_response{
+5 -23
View File
@@ -88,13 +88,8 @@ start() ->
ok.
%%
-spec(register_mechanism/3 ::
(
Mechanim :: mechanism(),
Module :: module(),
PasswordType :: password_type())
-> any()
).
-spec register_mechanism(Mechanim :: mechanism(), Module :: module(),
PasswordType :: password_type()) -> any().
register_mechanism(Mechanism, Module, PasswordType) ->
case is_disabled(Mechanism) of
@@ -139,11 +134,7 @@ check_credentials(_State, Props) ->
_LUser -> ok
end.
-spec(listmech/1 ::
(
Host ::binary())
-> Mechanisms::mechanisms()
).
-spec listmech(Host ::binary()) -> Mechanisms::mechanisms().
listmech(Host) ->
Mechs = ets:select(sasl_mechanism,
@@ -213,12 +204,7 @@ server_step(State, ClientIn) ->
%% Remove the anonymous mechanism from the list if not enabled for the given
%% host
%%
-spec(filter_anonymous/2 ::
(
Host :: binary(),
Mechs :: mechanisms())
-> mechanisms()
).
-spec filter_anonymous(Host :: binary(), Mechs :: mechanisms()) -> mechanisms().
filter_anonymous(Host, Mechs) ->
case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of
@@ -226,11 +212,7 @@ filter_anonymous(Host, Mechs) ->
false -> Mechs -- [<<"ANONYMOUS">>]
end.
-spec(is_disabled/1 ::
(
Mechanism :: mechanism())
-> boolean()
).
-spec is_disabled(Mechanism :: mechanism()) -> boolean().
is_disabled(Mechanism) ->
Disabled = ejabberd_config:get_option(
+1 -1
View File
@@ -51,7 +51,7 @@ mech_step(State, ClientIn) ->
{ok,
[{username, User}, {authzid, AuthzId},
{auth_module, ejabberd_oauth}]};
false ->
_ ->
{error, <<"not-authorized">>, User}
end;
_ -> {error, <<"bad-protocol">>}
+8 -6
View File
@@ -87,6 +87,7 @@ get_commands_spec() ->
args = [], result = {res, rescode}},
#ejabberd_commands{name = reopen_log, tags = [logs, server],
desc = "Reopen the log files",
policy = admin,
module = ?MODULE, function = reopen_log,
args = [], result = {res, rescode}},
#ejabberd_commands{name = rotate_log, tags = [logs, server],
@@ -129,6 +130,8 @@ get_commands_spec() ->
#ejabberd_commands{name = register, tags = [accounts],
desc = "Register a user",
policy = admin,
access = [{mod_register, access, configure}],
module = ?MODULE, function = register,
args = [{user, binary}, {host, binary}, {password, binary}],
result = {res, restuple}},
@@ -166,7 +169,7 @@ get_commands_spec() ->
#ejabberd_commands{name = list_cluster, tags = [cluster],
desc = "List nodes that are part of the cluster handled by Node",
module = ?MODULE, function = list_cluster,
args = [],
args = [],
result = {nodes, {list, {node, atom}}}},
#ejabberd_commands{name = import_file, tags = [mnesia],
@@ -220,7 +223,7 @@ get_commands_spec() ->
desc = "Delete offline messages older than DAYS",
module = ?MODULE, function = delete_old_messages,
args = [{days, integer}], result = {res, rescode}},
#ejabberd_commands{name = export2sql, tags = [mnesia],
desc = "Export virtual host information from Mnesia tables to SQL files",
module = ejd2sql, function = export,
@@ -378,13 +381,12 @@ register(User, Host, Password) ->
{atomic, ok} ->
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
{atomic, exists} ->
String = io_lib:format("User ~s@~s already registered at node ~p",
[User, Host, node()]),
{exists, String};
Msg = io_lib:format("User ~s@~s already registered", [User, Host]),
{error, conflict, 10090, Msg};
{error, Reason} ->
String = io_lib:format("Can't register user ~s@~s at node ~p: ~p",
[User, Host, node(), Reason]),
{cannot_register, String}
{error, cannot_register, 10001, String}
end.
unregister(User, Host) ->
+7
View File
@@ -45,6 +45,7 @@ start(normal, _Args) ->
write_pid_file(),
jid:start(),
start_apps(),
start_elixir_application(),
ejabberd:check_app(ejabberd),
randoms:start(),
db_init(),
@@ -237,3 +238,9 @@ opt_type(modules) ->
Mods)
end;
opt_type(_) -> [cluster_nodes, loglevel, modules, net_ticktime].
start_elixir_application() ->
case application:ensure_started(elixir) of
ok -> ok;
{error, _Msg} -> ?ERROR_MSG("Elixir application not started.", [])
end.
+28 -10
View File
@@ -1318,7 +1318,7 @@ handle_sync_event({resume_session, Time}, _From, _StateName,
StateData#state.user,
StateData#state.server,
StateData#state.resource),
{stop, normal, {ok, StateData}, StateData#state{mgmt_state = resumed}};
{stop, normal, {resume, StateData}, StateData#state{mgmt_state = resumed}};
handle_sync_event({resume_session, _Time}, _From, StateName,
StateData) ->
{reply, {error, <<"Previous session not found">>}, StateName, StateData};
@@ -1632,11 +1632,18 @@ handle_info({route, From, To,
<<"groupchat">> -> ok;
<<"headline">> -> ok;
_ ->
Err =
jlib:make_error_reply(Packet,
?ERR_SERVICE_UNAVAILABLE),
ejabberd_router:route(To, From,
Err)
case fxml:get_subtag_with_xmlns(Packet,
<<"x">>,
?NS_MUC_USER)
of
false ->
Err =
jlib:make_error_reply(Packet,
?ERR_SERVICE_UNAVAILABLE),
ejabberd_router:route(To, From,
Err);
_ -> ok
end
end,
{false, Attrs, StateData}
end;
@@ -1738,6 +1745,13 @@ handle_info({broadcast, Type, From, Packet}, StateName, StateData) ->
fsm_next_state(StateName, StateData);
handle_info(dont_ask_offline, StateName, StateData) ->
fsm_next_state(StateName, StateData#state{ask_offline = false});
handle_info({_Ref, {resume, OldStateData}}, StateName, StateData) ->
%% This happens if the resume_session/1 request timed out; the new session
%% now receives the late response.
?DEBUG("Received old session state for ~s after failed resumption",
[jid:to_string(OldStateData#state.jid)]),
handle_unacked_stanzas(OldStateData#state{mgmt_resend = false}),
fsm_next_state(StateName, StateData);
handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
fsm_next_state(StateName, StateData).
@@ -1855,6 +1869,7 @@ send_text(StateData, Text) ->
send_element(StateData, El) when StateData#state.mgmt_state == pending ->
?DEBUG("Cannot send element while waiting for resumption: ~p", [El]);
send_element(StateData, El) when StateData#state.xml_socket ->
?DEBUG("Send XML on stream = ~p", [fxml:element_to_binary(El)]),
(StateData#state.sockmod):send_xml(StateData#state.socket,
{xmlstreamelement, El});
send_element(StateData, El) ->
@@ -2873,8 +2888,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData, F)
0 ->
ok;
N ->
?INFO_MSG("~B stanzas were not acknowledged by ~s",
[N, jid:to_string(StateData#state.jid)]),
?DEBUG("~B stanza(s) were not acknowledged by ~s",
[N, jid:to_string(StateData#state.jid)]),
lists:foreach(
fun({_, Time, #xmlel{attrs = Attrs} = El}) ->
From_s = fxml:get_attr_s(<<"from">>, Attrs),
@@ -2951,6 +2966,9 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData)
[StateData, From,
StateData#state.jid, El]) of
true ->
?DEBUG("Dropping archived message stanza from ~s",
[fxml:get_attr_s(<<"from">>,
El#xmlel.attrs)]),
ok;
false ->
ReRoute(From, To, El, Time)
@@ -3006,7 +3024,7 @@ inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) ->
OldPID ->
OldSID = {Time, OldPID},
case catch resume_session(OldSID) of
{ok, OldStateData} ->
{resume, OldStateData} ->
NewSID = {Time, self()}, % Old time, new PID
Priority = case OldStateData#state.pres_last of
undefined ->
@@ -3048,7 +3066,7 @@ inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) ->
end.
resume_session({Time, PID}) ->
(?GEN_FSM):sync_send_all_state_event(PID, {resume_session, Time}, 5000).
(?GEN_FSM):sync_send_all_state_event(PID, {resume_session, Time}, 15000).
make_resume_id(StateData) ->
{Time, _} = StateData#state.sid,
+183 -123
View File
@@ -218,14 +218,15 @@
get_command_format/1,
get_command_format/2,
get_command_format/3,
get_command_policy/1,
get_command_policy_and_scope/1,
get_command_definition/1,
get_command_definition/2,
get_tags_commands/0,
get_tags_commands/1,
get_commands/0,
get_exposed_commands/0,
register_commands/1,
unregister_commands/1,
unregister_commands/1,
expose_commands/1,
execute_command/2,
execute_command/3,
execute_command/4,
@@ -275,10 +276,10 @@ get_commands_spec() ->
init() ->
mnesia:delete_table(ejabberd_commands),
mnesia:create_table(ejabberd_commands,
[{ram_copies, [node()]},
[{ram_copies, [node()]},
{local_content, true},
{attributes, record_info(fields, ejabberd_commands)},
{type, bag}]),
{attributes, record_info(fields, ejabberd_commands)},
{type, bag}]),
mnesia:add_table_copy(ejabberd_commands, node(), ram_copies),
register_commands(get_commands_spec()).
@@ -287,12 +288,14 @@ init() ->
%% @doc Register ejabberd commands.
%% If a command is already registered, a warning is printed and the
%% old command is preserved.
%% A registered command is not directly available to be called through
%% ejabberd ReST API. It need to be exposed to be available through API.
register_commands(Commands) ->
lists:foreach(
fun(Command) ->
% XXX check if command exists
mnesia:dirty_write(Command)
% ?DEBUG("This command is already defined:~n~p", [Command])
%% XXX check if command exists
mnesia:dirty_write(Command)
%% ?DEBUG("This command is already defined:~n~p", [Command])
end,
Commands).
@@ -306,6 +309,25 @@ unregister_commands(Commands) ->
end,
Commands).
%% @doc Expose command through ejabberd ReST API.
%% Pass a list of command names or policy to expose.
-spec expose_commands([ejabberd_commands()|atom()|open|user|admin|restricted]) -> ok | {error, atom()}.
expose_commands(Commands) ->
Names = lists:map(fun(#ejabberd_commands{name = Name}) ->
Name;
(Name) when is_atom(Name) ->
Name
end,
Commands),
case ejabberd_config:add_local_option(commands, [{add_commands, Names}]) of
{aborted, Reason} ->
{error, Reason};
{atomic, Result} ->
Result
end.
-spec list_commands() -> [{atom(), [aterm()], string()}].
%% @doc Get a list of all the available commands, arguments and description.
@@ -319,8 +341,8 @@ list_commands() ->
list_commands(Version) ->
Commands = get_commands_definition(Version),
[{Name, Args, Desc} || #ejabberd_commands{name = Name,
args = Args,
desc = Desc} <- Commands].
args = Args,
desc = Desc} <- Commands].
-spec list_commands_policy(integer()) ->
@@ -331,10 +353,10 @@ list_commands(Version) ->
list_commands_policy(Version) ->
Commands = get_commands_definition(Version),
[{Name, Args, Desc, Policy} ||
#ejabberd_commands{name = Name,
args = Args,
desc = Desc,
policy = Policy} <- Commands].
#ejabberd_commands{name = Name,
args = Args,
desc = Desc,
policy = Policy} <- Commands].
-spec get_command_format(atom()) -> {[aterm()], rterm()}.
@@ -356,27 +378,33 @@ get_command_format(Name, Auth, Version) ->
Admin = is_admin(Name, Auth, #{}),
#ejabberd_commands{args = Args,
result = Result,
policy = Policy} =
get_command_definition(Name, Version),
policy = Policy} =
get_command_definition(Name, Version),
case Policy of
user when Admin;
Auth == noauth ->
{[{user, binary}, {server, binary} | Args], Result};
_ ->
{Args, Result}
user when Admin;
Auth == noauth ->
{[{user, binary}, {server, binary} | Args], Result};
_ ->
{Args, Result}
end.
-spec get_command_policy(atom()) -> {ok, open|user|admin|restricted} | {error, command_not_found}.
-spec get_command_policy_and_scope(atom()) -> {ok, open|user|admin|restricted, [oauth_scope()]} | {error, command_not_found}.
%% @doc return command policy.
get_command_policy(Name) ->
get_command_policy_and_scope(Name) ->
case get_command_definition(Name) of
#ejabberd_commands{policy = Policy} ->
{ok, Policy};
#ejabberd_commands{policy = Policy} = Cmd ->
{ok, Policy, cmd_scope(Cmd)};
command_not_found ->
{error, command_not_found}
end.
%% The oauth scopes for a command are the command name itself,
%% also might include either 'ejabberd:user' or 'ejabberd:admin'
cmd_scope(#ejabberd_commands{policy = Policy, name = Name}) ->
[erlang:atom_to_binary(Name,utf8)] ++ [<<"ejabberd:user">> || Policy == user] ++ [<<"ejabberd:admin">> || Policy == admin].
-spec get_command_definition(atom()) -> ejabberd_commands().
%% @doc Get the definition record of a command.
@@ -388,16 +416,16 @@ get_command_definition(Name) ->
%% @doc Get the definition record of a command in a given API version.
get_command_definition(Name, Version) ->
case lists:reverse(
lists:sort(
mnesia:dirty_select(
ejabberd_commands,
ets:fun2ms(
fun(#ejabberd_commands{name = N, version = V} = C)
when N == Name, V =< Version ->
{V, C}
end)))) of
[{_, Command} | _ ] -> Command;
_E -> throw(unknown_command)
lists:sort(
mnesia:dirty_select(
ejabberd_commands,
ets:fun2ms(
fun(#ejabberd_commands{name = N, version = V} = C)
when N == Name, V =< Version ->
{V, C}
end)))) of
[{_, Command} | _ ] -> Command;
_E -> throw({error, unknown_command})
end.
-spec get_commands_definition(integer()) -> [ejabberd_commands()].
@@ -405,20 +433,20 @@ get_command_definition(Name, Version) ->
% @doc Returns all commands for a given API version
get_commands_definition(Version) ->
L = lists:reverse(
lists:sort(
mnesia:dirty_select(
ejabberd_commands,
ets:fun2ms(
fun(#ejabberd_commands{name = Name, version = V} = C)
when V =< Version ->
{Name, V, C}
end)))),
lists:sort(
mnesia:dirty_select(
ejabberd_commands,
ets:fun2ms(
fun(#ejabberd_commands{name = Name, version = V} = C)
when V =< Version ->
{Name, V, C}
end)))),
F = fun({_Name, _V, Command}, []) ->
[Command];
({Name, _V, _Command}, [#ejabberd_commands{name=Name}|_T] = Acc) ->
Acc;
({_Name, _V, Command}, Acc) -> [Command | Acc]
end,
[Command];
({Name, _V, _Command}, [#ejabberd_commands{name=Name}|_T] = Acc) ->
Acc;
({_Name, _V, Command}, Acc) -> [Command | Acc]
end,
lists:foldl(F, [], L).
%% @spec (Name::atom(), Arguments) -> ResultTerm
@@ -427,7 +455,7 @@ get_commands_definition(Version) ->
%% @doc Execute a command.
%% Can return the following exceptions:
%% command_unknown | account_unprivileged | invalid_account_data |
%% no_auth_provided
%% no_auth_provided | access_rules_unauthorized
execute_command(Name, Arguments) ->
execute_command(Name, Arguments, ?DEFAULT_VERSION).
@@ -488,41 +516,64 @@ execute_command(AccessCommands, Auth, Name, Arguments) ->
%%
%% @doc Execute a command in a given API version
%% Can return the following exceptions:
%% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided
%% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided | access_rules_unauthorized
execute_command(AccessCommands1, Auth1, Name, Arguments, Version) ->
execute_command(AccessCommands1, Auth1, Name, Arguments, Version, #{}).
execute_command(AccessCommands1, Auth1, Name, Arguments, Version, #{}).
execute_command(AccessCommands1, Auth1, Name, Arguments, Version, CallerInfo) ->
Auth = case is_admin(Name, Auth1, CallerInfo) of
true -> admin;
false -> Auth1
end,
TokenJID = oauth_token_user(Auth1),
Command = get_command_definition(Name, Version),
AccessCommands = get_access_commands(AccessCommands1, Version),
AccessCommands = get_all_access_commands(AccessCommands1),
case check_access_commands(AccessCommands, Auth, Name, Command, Arguments, CallerInfo) of
ok -> execute_command2(Auth, Command, Arguments)
ok -> execute_check_policy(Auth, TokenJID, Command, Arguments)
end.
execute_command2(
_Auth, #ejabberd_commands{policy = open} = Command, Arguments) ->
execute_command2(Command, Arguments);
execute_command2(
_Auth, #ejabberd_commands{policy = restricted} = Command, Arguments) ->
execute_command2(Command, Arguments);
execute_command2(
_Auth, #ejabberd_commands{policy = admin} = Command, Arguments) ->
execute_command2(Command, Arguments);
execute_command2(
admin, #ejabberd_commands{policy = user} = Command, Arguments) ->
execute_command2(Command, Arguments);
execute_command2(
noauth, #ejabberd_commands{policy = user} = Command, Arguments) ->
execute_command2(Command, Arguments);
execute_command2(
{User, Server, _, _}, #ejabberd_commands{policy = user} = Command, Arguments) ->
execute_command2(Command, [User, Server | Arguments]).
execute_command2(Command, Arguments) ->
execute_check_policy(
_Auth, _JID, #ejabberd_commands{policy = open} = Command, Arguments) ->
do_execute_command(Command, Arguments);
execute_check_policy(
_Auth, _JID, #ejabberd_commands{policy = restricted} = Command, Arguments) ->
do_execute_command(Command, Arguments);
execute_check_policy(
_Auth, JID, #ejabberd_commands{policy = admin} = Command, Arguments) ->
execute_check_access(JID, Command, Arguments);
execute_check_policy(
admin, JID, #ejabberd_commands{policy = user} = Command, Arguments) ->
execute_check_access(JID, Command, Arguments);
execute_check_policy(
noauth, _JID, #ejabberd_commands{policy = user} = Command, Arguments) ->
do_execute_command(Command, Arguments);
execute_check_policy(
{User, Server, _, _}, JID, #ejabberd_commands{policy = user} = Command, Arguments) ->
execute_check_access(JID, Command, [User, Server | Arguments]).
execute_check_access(_FromJID, #ejabberd_commands{access = []} = Command, Arguments) ->
do_execute_command(Command, Arguments);
execute_check_access(undefined, _Command, _Arguments) ->
throw({error, access_rules_unauthorized});
execute_check_access(FromJID, #ejabberd_commands{access = AccessRefs} = Command, Arguments) ->
%% TODO Review: Do we have smarter / better way to check rule on other Host than global ?
Host = global,
Rules = lists:map(fun({Mod, AccessName, Default}) ->
gen_mod:get_module_opt(Host, Mod,
AccessName, fun(A) -> A end, Default);
(Default) ->
Default
end, AccessRefs),
case acl:any_rules_allowed(Host, Rules, FromJID) of
true ->
do_execute_command(Command, Arguments);
false ->
throw({error, access_rules_unauthorized})
end.
do_execute_command(Command, Arguments) ->
Module = Command#ejabberd_commands.module,
Function = Command#ejabberd_commands.function,
?DEBUG("Executing command ~p:~p with Args=~p", [Module, Function, Arguments]),
@@ -592,31 +643,31 @@ check_access_commands(AccessCommands, Auth, Method, Command1, Arguments, CallerI
Command1
end,
AccessCommandsAllowed =
lists:filter(
fun({Access, Commands, ArgumentRestrictions}) ->
case check_access(Command, Access, Auth, CallerInfo) of
true ->
check_access_command(Commands, Command,
ArgumentRestrictions,
Method, Arguments);
false ->
false
end;
({Access, Commands}) ->
ArgumentRestrictions = [],
case check_access(Command, Access, Auth, CallerInfo) of
true ->
check_access_command(Commands, Command,
ArgumentRestrictions,
Method, Arguments);
false ->
false
end
end,
AccessCommands),
lists:filter(
fun({Access, Commands, ArgumentRestrictions}) ->
case check_access(Command, Access, Auth, CallerInfo) of
true ->
check_access_command(Commands, Command,
ArgumentRestrictions,
Method, Arguments);
false ->
false
end;
({Access, Commands}) ->
ArgumentRestrictions = [],
case check_access(Command, Access, Auth, CallerInfo) of
true ->
check_access_command(Commands, Command,
ArgumentRestrictions,
Method, Arguments);
false ->
false
end
end,
AccessCommands),
case AccessCommandsAllowed of
[] -> throw({error, account_unprivileged});
L when is_list(L) -> ok
[] -> throw({error, account_unprivileged});
L when is_list(L) -> ok
end.
-spec check_auth(ejabberd_commands(), noauth) -> noauth_provided;
@@ -627,11 +678,11 @@ check_access_commands(AccessCommands, Auth, Method, Command1, Arguments, CallerI
check_auth(_Command, noauth) ->
no_auth_provided;
check_auth(Command, {User, Server, {oauth, Token}, _}) ->
Scope = erlang:atom_to_binary(Command#ejabberd_commands.name, utf8),
case ejabberd_oauth:check_token(User, Server, Scope, Token) of
ScopeList = cmd_scope(Command),
case ejabberd_oauth:check_token(User, Server, ScopeList, Token) of
true ->
{ok, User, Server};
false ->
_ ->
throw({error, invalid_account_data})
end;
check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
@@ -680,9 +731,9 @@ check_access2(Access, AccessInfo, Server) ->
check_access_command(Commands, Command, ArgumentRestrictions,
Method, Arguments) ->
case Commands==all orelse lists:member(Method, Commands) of
true -> check_access_arguments(Command, ArgumentRestrictions,
Arguments);
false -> false
true -> check_access_arguments(Command, ArgumentRestrictions,
Arguments);
false -> false
end.
check_access_arguments(Command, ArgumentRestrictions, Arguments) ->
@@ -705,19 +756,23 @@ tag_arguments(ArgsDefs, Args) ->
Args).
%% Get commands for all version
get_all_access_commands(AccessCommands) ->
get_access_commands(AccessCommands, ?DEFAULT_VERSION).
get_access_commands(undefined, Version) ->
Cmds = get_commands(Version),
Cmds = get_exposed_commands(Version),
[{?POLICY_ACCESS, Cmds, []}];
get_access_commands(AccessCommands, _Version) ->
AccessCommands.
get_commands() ->
get_commands(?DEFAULT_VERSION).
get_commands(Version) ->
get_exposed_commands() ->
get_exposed_commands(?DEFAULT_VERSION).
get_exposed_commands(Version) ->
Opts0 = ejabberd_config:get_option(
commands,
fun(V) when is_list(V) -> V end,
[]),
[]),
Opts = lists:map(fun(V) when is_tuple(V) -> [V]; (V) -> V end, Opts0),
CommandsList = list_commands_policy(Version),
OpenCmds = [N || {N, _, _, open} <- CommandsList],
@@ -727,27 +782,32 @@ get_commands(Version) ->
Cmds =
lists:foldl(
fun([{add_commands, L}], Acc) ->
Cmds = case L of
open -> OpenCmds;
restricted -> RestrictedCmds;
admin -> AdminCmds;
user -> UserCmds;
_ when is_list(L) -> L
end,
Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds),
lists:usort(Cmds ++ Acc);
([{remove_commands, L}], Acc) ->
Cmds = case L of
open -> OpenCmds;
restricted -> RestrictedCmds;
admin -> AdminCmds;
user -> UserCmds;
_ when is_list(L) -> L
end,
Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds),
Acc -- Cmds;
(_, Acc) -> Acc
end, AdminCmds ++ UserCmds, Opts),
end, [], Opts),
Cmds.
%% This is used to allow mixing command policy (like open, user, admin, restricted), with command entry
expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds) when is_list(L) ->
lists:foldl(fun(open, Acc) -> OpenCmds ++ Acc;
(user, Acc) -> UserCmds ++ Acc;
(admin, Acc) -> AdminCmds ++ Acc;
(restricted, Acc) -> RestrictedCmds ++ Acc;
(Command, Acc) when is_atom(Command) ->
[Command|Acc]
end, [], L).
oauth_token_user(noauth) ->
undefined;
oauth_token_user(admin) ->
undefined;
oauth_token_user({User, Server, _, _}) ->
jid:make(User, Server, <<>>).
is_admin(_Name, admin, _Extra) ->
true;
is_admin(_Name, {_User, _Server, _, false}, _Extra) ->
+1 -4
View File
@@ -90,7 +90,7 @@ hosts_to_start(State) ->
%% @private
%% At the moment, these functions are mainly used to setup unit tests.
-spec(start/2 :: (Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok).
-spec start(Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok.
start(Hosts, Opts) ->
mnesia_init(),
set_opts(set_hosts_in_options(Hosts, #state{opts = Opts})).
@@ -220,9 +220,6 @@ env_binary_to_list(Application, Parameter) ->
%% in which the options 'include_config_file' were parsed
%% and the terms in those files were included.
%% @spec(iolist()) -> [term()]
get_plain_terms_file(File) ->
get_plain_terms_file(File, [{include_files, true}]).
get_plain_terms_file(File, Opts) when is_binary(File) ->
get_plain_terms_file(binary_to_list(File), Opts);
get_plain_terms_file(File1, Opts) ->
+10 -2
View File
@@ -212,7 +212,7 @@ process(["help" | Mode], Version) ->
end;
process(["--version", Arg | Args], _) ->
Version =
Version =
try
list_to_integer(Arg)
catch _:_ ->
@@ -321,7 +321,7 @@ call_command([CmdString | Args], Auth, AccessCommands, Version) ->
{ArgsFormat, ResultFormat} ->
case (catch format_args(Args, ArgsFormat)) of
ArgsFormatted when is_list(ArgsFormatted) ->
Result = ejabberd_commands:execute_command(AccessCommands,
Result = ejabberd_commands:execute_command(AccessCommands,
Auth, Command,
ArgsFormatted,
Version),
@@ -374,6 +374,12 @@ format_arg2(Arg, Parse)->
format_result({error, ErrorAtom}, _) ->
{io_lib:format("Error: ~p", [ErrorAtom]), make_status(error)};
%% An error should always be allowed to return extended error to help with API.
%% Extended error is of the form:
%% {error, type :: atom(), code :: int(), Desc :: string()}
format_result({error, ErrorAtom, Code, _Msg}, _) ->
{io_lib:format("Error: ~p", [ErrorAtom]), make_status(Code)};
format_result(Atom, {_Name, atom}) ->
io_lib:format("~p", [Atom]);
@@ -433,6 +439,8 @@ format_result(404, {_Name, _}) ->
make_status(ok) -> ?STATUS_SUCCESS;
make_status(true) -> ?STATUS_SUCCESS;
make_status(Code) when is_integer(Code), Code > 255 -> ?STATUS_ERROR;
make_status(Code) when is_integer(Code), Code > 0 -> Code;
make_status(_Error) -> ?STATUS_ERROR.
get_list_commands(Version) ->
+2 -1
View File
@@ -763,7 +763,8 @@ parse_auth(<<"Basic ", Auth64/binary>>) ->
undefined;
Pos ->
{User, <<$:, Pass/binary>>} = erlang:split_binary(Auth, Pos-1),
{User, Pass}
PassUtf8 = unicode:characters_to_binary(binary_to_list(Pass), utf8),
{User, PassUtf8}
end;
parse_auth(<<"Bearer ", SToken/binary>>) ->
Token = str:strip(SToken),
+258 -124
View File
@@ -39,7 +39,6 @@
authenticate_user/2,
authenticate_client/2,
verify_resowner_scope/3,
verify_client_scope/3,
associate_access_code/3,
associate_access_token/3,
associate_refresh_token/3,
@@ -48,7 +47,7 @@
process/2,
opt_type/1]).
-export([oauth_issue_token/1, oauth_list_tokens/0, oauth_revoke_token/1, oauth_list_scopes/0]).
-export([oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1, oauth_list_scopes/0]).
-include("jlib.hrl").
@@ -57,6 +56,7 @@
-include("ejabberd_http.hrl").
-include("ejabberd_web_admin.hrl").
-include("ejabberd_oauth.hrl").
-include("ejabberd_commands.hrl").
@@ -65,23 +65,30 @@
%% * Using the web form/api results in the token being generated in behalf of the user providing the user/pass
%% * Using the command line and oauth_issue_token command, the token is generated in behalf of ejabberd' sysadmin
%% (as it has access to ejabberd command line).
-record(oauth_token, {
token = {<<"">>, <<"">>} :: {binary(), binary()},
us = {<<"">>, <<"">>} :: {binary(), binary()} | server_admin,
scope = [] :: [binary()],
expire :: integer()
}).
-define(EXPIRE, 3600).
-define(EXPIRE, 31536000).
start() ->
init_db(mnesia, ?MYNAME),
DBMod = get_db_backend(),
DBMod:init(),
MaxSize =
ejabberd_config:get_option(
oauth_cache_size,
fun(I) when is_integer(I), I>0 -> I end,
1000),
LifeTime =
ejabberd_config:get_option(
oauth_cache_life_time,
fun(I) when is_integer(I), I>0 -> I end,
timer:hours(1) div 1000),
cache_tab:new(oauth_token,
[{max_size, MaxSize}, {life_time, LifeTime}]),
Expire = expire(),
application:set_env(oauth2, backend, ejabberd_oauth),
application:set_env(oauth2, expiry_time, Expire),
application:start(oauth2),
ChildSpec = {?MODULE, {?MODULE, start_link, []},
temporary, 1000, worker, [?MODULE]},
transient, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec),
ejabberd_commands:register_commands(get_commands_spec()),
ok.
@@ -90,57 +97,61 @@ start() ->
get_commands_spec() ->
[
#ejabberd_commands{name = oauth_issue_token, tags = [oauth],
desc = "Issue an oauth token. Available scopes are the ones usable by ejabberd admins",
desc = "Issue an oauth token for the given jid",
module = ?MODULE, function = oauth_issue_token,
args = [{scopes, string}],
args = [{jid, string},{ttl, integer}, {scopes, string}],
policy = restricted,
args_example = ["connected_users_number;muc_online_rooms"],
args_example = ["user@server.com", "connected_users_number;muc_online_rooms"],
args_desc = ["List of scopes to allow, separated by ';'"],
result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}}
},
#ejabberd_commands{name = oauth_list_tokens, tags = [oauth],
desc = "List oauth tokens, their scope, and how many seconds remain until expirity",
desc = "List oauth tokens, their user and scope, and how many seconds remain until expirity",
module = ?MODULE, function = oauth_list_tokens,
args = [],
policy = restricted,
result = {tokens, {list, {token, {tuple, [{token, string}, {scope, string}, {expires_in, string}]}}}}
result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}}
},
#ejabberd_commands{name = oauth_list_scopes, tags = [oauth],
desc = "List scopes that can be granted to tokens generated through the command line",
desc = "List scopes that can be granted to tokens generated through the command line, together with the commands they allow",
module = ?MODULE, function = oauth_list_scopes,
args = [],
policy = restricted,
result = {scopes, {list, {scope, string}}}
result = {scopes, {list, {scope, {tuple, [{scope, string}, {commands, string}]}}}}
},
#ejabberd_commands{name = oauth_revoke_token, tags = [oauth],
desc = "Revoke authorization for a token",
module = ?MODULE, function = oauth_revoke_token,
args = [{token, string}],
policy = restricted,
result = {tokens, {list, {token, {tuple, [{token, string}, {scope, string}, {expires_in, string}]}}}},
result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}},
result_desc = "List of remaining tokens"
}
].
oauth_issue_token(ScopesString) ->
oauth_issue_token(Jid, TTLSeconds, ScopesString) ->
Scopes = [list_to_binary(Scope) || Scope <- string:tokens(ScopesString, ";")],
case oauth2:authorize_client_credentials(ejabberd_ctl, Scopes, none) of
{ok, {_AppCtx, Authorization}} ->
{ok, {_AppCtx2, Response}} = oauth2:issue_token(Authorization, none),
{ok, AccessToken} = oauth2_response:access_token(Response),
{ok, Expires} = oauth2_response:expires_in(Response),
{ok, VerifiedScope} = oauth2_response:scope(Response),
{AccessToken, VerifiedScope, integer_to_list(Expires) ++ " seconds"};
{error, Error} ->
{error, Error}
case jid:from_string(list_to_binary(Jid)) of
#jid{luser =Username, lserver = Server} ->
case oauth2:authorize_password({Username, Server}, Scopes, admin_generated) of
{ok, {_Ctx,Authorization}} ->
{ok, {_AppCtx2, Response}} = oauth2:issue_token(Authorization, [{expiry_time, TTLSeconds}]),
{ok, AccessToken} = oauth2_response:access_token(Response),
{ok, VerifiedScope} = oauth2_response:scope(Response),
{AccessToken, VerifiedScope, integer_to_list(TTLSeconds) ++ " seconds"};
{error, Error} ->
{error, Error}
end;
error ->
{error, "Invalid JID: " ++ Jid}
end.
oauth_list_tokens() ->
Tokens = mnesia:dirty_match_object(#oauth_token{us = server_admin, _ = '_'}),
Tokens = mnesia:dirty_match_object(#oauth_token{_ = '_'}),
{MegaSecs, Secs, _MiniSecs} = os:timestamp(),
TS = 1000000 * MegaSecs + Secs,
[{Token, Scope, integer_to_list(Expires - TS) ++ " seconds"} ||
#oauth_token{token=Token, scope=Scope, expire=Expires} <- Tokens].
[{Token, jid:to_string(jid:make(U,S,<<>>)), Scope, integer_to_list(Expires - TS) ++ " seconds"} ||
#oauth_token{token=Token, scope=Scope, us= {U,S},expire=Expires} <- Tokens].
oauth_revoke_token(Token) ->
@@ -148,8 +159,7 @@ oauth_revoke_token(Token) ->
oauth_list_tokens().
oauth_list_scopes() ->
get_cmd_scopes().
[ {Scope, string:join([atom_to_list(Cmd) || Cmd <- Cmds], ",")} || {Scope, Cmds} <- dict:to_list(get_cmd_scopes())].
@@ -170,15 +180,8 @@ handle_cast(_Msg, State) -> {noreply, State}.
handle_info(clean, State) ->
{MegaSecs, Secs, MiniSecs} = os:timestamp(),
TS = 1000000 * MegaSecs + Secs,
F = fun() ->
Ts = mnesia:select(
oauth_token,
[{#oauth_token{expire = '$1', _ = '_'},
[{'<', '$1', TS}],
['$_']}]),
lists:foreach(fun mnesia:delete_object/1, Ts)
end,
mnesia:async_dirty(F),
DBMod = get_db_backend(),
DBMod:clean(TS),
erlang:send_after(trunc(expire() * 1000 * (1 + MiniSecs / 1000000)),
self(), clean),
{noreply, State};
@@ -189,21 +192,11 @@ terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
init_db(mnesia, _Host) ->
mnesia:create_table(oauth_token,
[{disc_copies, [node()]},
{attributes,
record_info(fields, oauth_token)}]),
mnesia:add_table_copy(oauth_token, node(), disc_copies);
init_db(_, _) ->
ok.
get_client_identity(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
verify_redirection_uri(_, _, Ctx) -> {ok, Ctx}.
authenticate_user({User, Server}, {password, Password} = Ctx) ->
authenticate_user({User, Server}, Ctx) ->
case jid:make(User, Server, <<"">>) of
#jid{} = JID ->
Access =
@@ -213,11 +206,16 @@ authenticate_user({User, Server}, {password, Password} = Ctx) ->
none),
case acl:match_rule(JID#jid.lserver, Access, JID) of
allow ->
case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
true ->
{ok, {Ctx, {user, User, Server}}};
false ->
{error, badpass}
case Ctx of
{password, Password} ->
case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
true ->
{ok, {Ctx, {user, User, Server}}};
false ->
{error, badpass}
end;
admin_generated ->
{ok, {Ctx, {user, User, Server}}}
end;
deny ->
{error, badpass}
@@ -229,8 +227,8 @@ authenticate_user({User, Server}, {password, Password} = Ctx) ->
authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
verify_resowner_scope({user, _User, _Server}, Scope, Ctx) ->
Cmds = ejabberd_commands:get_commands(),
Cmds1 = [sasl_auth | Cmds],
Cmds = ejabberd_commands:get_exposed_commands(),
Cmds1 = ['ejabberd:user', 'ejabberd:admin', sasl_auth | Cmds],
RegisteredScope = [atom_to_binary(C, utf8) || C <- Cmds1],
case oauth2_priv_set:is_subset(oauth2_priv_set:new(Scope),
oauth2_priv_set:new(RegisteredScope)) of
@@ -244,92 +242,132 @@ verify_resowner_scope(_, _, _) ->
get_cmd_scopes() ->
Cmds = lists:filter(fun(Cmd) -> case ejabberd_commands:get_command_policy(Cmd) of
{ok, Policy} when Policy =/= restricted -> true;
_ -> false
end end,
ejabberd_commands:get_commands()),
[atom_to_binary(C, utf8) || C <- Cmds].
ScopeMap = lists:foldl(fun(Cmd, Accum) ->
case ejabberd_commands:get_command_policy_and_scope(Cmd) of
{ok, Policy, Scopes} when Policy =/= restricted ->
lists:foldl(fun(Scope, Accum2) ->
dict:append(Scope, Cmd, Accum2)
end, Accum, Scopes);
_ -> Accum
end end, dict:new(), ejabberd_commands:get_exposed_commands()),
ScopeMap.
%% This is callback for oauth tokens generated through the command line. Only open and admin commands are
%% made available.
verify_client_scope({client, ejabberd_ctl}, Scope, Ctx) ->
RegisteredScope = get_cmd_scopes(),
case oauth2_priv_set:is_subset(oauth2_priv_set:new(Scope),
oauth2_priv_set:new(RegisteredScope)) of
true ->
{ok, {Ctx, Scope}};
false ->
{error, badscope}
end.
%verify_client_scope({client, ejabberd_ctl}, Scope, Ctx) ->
% RegisteredScope = dict:fetch_keys(get_cmd_scopes()),
% case oauth2_priv_set:is_subset(oauth2_priv_set:new(Scope),
% oauth2_priv_set:new(RegisteredScope)) of
% true ->
% {ok, {Ctx, Scope}};
% false ->
% {error, badscope}
% end.
-spec seconds_since_epoch(integer()) -> non_neg_integer().
seconds_since_epoch(Diff) ->
{Mega, Secs, _} = os:timestamp(),
Mega * 1000000 + Secs + Diff.
associate_access_code(_AccessCode, _Context, AppContext) ->
%put(?ACCESS_CODE_TABLE, AccessCode, Context),
{ok, AppContext}.
associate_access_token(AccessToken, Context, AppContext) ->
%% Tokens generated using the API/WEB belongs to users and always include the user, server pair.
%% Tokens generated form command line aren't tied to an user, and instead belongs to the ejabberd sysadmin
US = case proplists:get_value(<<"resource_owner">>, Context, <<"">>) of
{user, User, Server} -> {jid:nodeprep(User), jid:nodeprep(Server)};
undefined -> server_admin
end,
{user, User, Server} = proplists:get_value(<<"resource_owner">>, Context, <<"">>),
Expire = case proplists:get_value(expiry_time, AppContext, undefined) of
undefined ->
proplists:get_value(<<"expiry_time">>, Context, 0);
ExpiresIn ->
%% There is no clean way in oauth2 lib to actually override the TTL of the generated token.
%% It always pass the global configured value. Here we use the app context to pass the per-case
%% ttl if we want to override it.
seconds_since_epoch(ExpiresIn)
end,
{user, User, Server} = proplists:get_value(<<"resource_owner">>, Context, <<"">>),
Scope = proplists:get_value(<<"scope">>, Context, []),
Expire = proplists:get_value(<<"expiry_time">>, Context, 0),
R = #oauth_token{
token = AccessToken,
us = US,
us = {jid:nodeprep(User), jid:nodeprep(Server)},
scope = Scope,
expire = Expire
},
mnesia:dirty_write(R),
store(R),
{ok, AppContext}.
associate_refresh_token(_RefreshToken, _Context, AppContext) ->
%put(?REFRESH_TOKEN_TABLE, RefreshToken, Context),
{ok, AppContext}.
check_token(User, Server, Scope, Token) ->
check_token(User, Server, ScopeList, Token) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
case catch mnesia:dirty_read(oauth_token, Token) of
[#oauth_token{us = {LUser, LServer},
scope = TokenScope,
expire = Expire}] ->
case lookup(Token) of
{ok, #oauth_token{us = {LUser, LServer},
scope = TokenScope,
expire = Expire}} ->
{MegaSecs, Secs, _} = os:timestamp(),
TS = 1000000 * MegaSecs + Secs,
oauth2_priv_set:is_member(
Scope, oauth2_priv_set:new(TokenScope)) andalso
Expire > TS;
_ ->
false
end.
check_token(Scope, Token) ->
case catch mnesia:dirty_read(oauth_token, Token) of
[#oauth_token{us = US,
scope = TokenScope,
expire = Expire}] ->
{MegaSecs, Secs, _} = os:timestamp(),
TS = 1000000 * MegaSecs + Secs,
case oauth2_priv_set:is_member(
Scope, oauth2_priv_set:new(TokenScope)) andalso
Expire > TS of
true -> case US of
{LUser, LServer} -> {ok, user, {LUser, LServer}};
server_admin -> {ok, server_admin}
end;
false -> false
if
Expire > TS ->
TokenScopeSet = oauth2_priv_set:new(TokenScope),
lists:any(fun(Scope) ->
oauth2_priv_set:is_member(Scope, TokenScopeSet) end,
ScopeList);
true ->
{false, expired}
end;
_ ->
false
{false, not_found}
end.
check_token(ScopeList, Token) ->
case lookup(Token) of
{ok, #oauth_token{us = US,
scope = TokenScope,
expire = Expire}} ->
{MegaSecs, Secs, _} = os:timestamp(),
TS = 1000000 * MegaSecs + Secs,
if
Expire > TS ->
TokenScopeSet = oauth2_priv_set:new(TokenScope),
case lists:any(fun(Scope) ->
oauth2_priv_set:is_member(Scope, TokenScopeSet) end,
ScopeList) of
true -> {ok, user, US};
false -> {false, no_matching_scope}
end;
true ->
{false, expired}
end;
_ ->
{false, not_found}
end.
store(R) ->
cache_tab:insert(
oauth_token, R#oauth_token.token, R,
fun() ->
DBMod = get_db_backend(),
DBMod:store(R)
end).
lookup(Token) ->
cache_tab:lookup(
oauth_token, Token,
fun() ->
DBMod = get_db_backend(),
case DBMod:lookup(Token) of
#oauth_token{} = R -> {ok, R};
_ -> error
end
end).
expire() ->
ejabberd_config:get_option(
@@ -358,12 +396,9 @@ process(_Handlers,
?XAE(<<"form">>,
[{<<"action">>, <<"authorization_token">>},
{<<"method">>, <<"post">>}],
[?LABEL(<<"username">>, [?CT(<<"User">>), ?C(<<": ">>)]),
[?LABEL(<<"username">>, [?CT(<<"User (jid)">>), ?C(<<": ">>)]),
?INPUTID(<<"text">>, <<"username">>, <<"">>),
?BR,
?LABEL(<<"server">>, [?CT(<<"Server">>), ?C(<<": ">>)]),
?INPUTID(<<"text">>, <<"server">>, <<"">>),
?BR,
?LABEL(<<"password">>, [?CT(<<"Password">>), ?C(<<": ">>)]),
?INPUTID(<<"password">>, <<"password">>, <<"">>),
?INPUT(<<"hidden">>, <<"response_type">>, ResponseType),
@@ -372,6 +407,15 @@ process(_Handlers,
?INPUT(<<"hidden">>, <<"scope">>, Scope),
?INPUT(<<"hidden">>, <<"state">>, State),
?BR,
?LABEL(<<"ttl">>, [?CT(<<"Token TTL">>), ?CT(<<": ">>)]),
?XAE(<<"select">>, [{<<"name">>, <<"ttl">>}],
[
?XAC(<<"option">>, [{<<"value">>, <<"3600">>}],<<"1 Hour">>),
?XAC(<<"option">>, [{<<"value">>, <<"86400">>}],<<"1 Day">>),
?XAC(<<"option">>, [{<<"value">>, <<"2592000">>}],<<"1 Month">>),
?XAC(<<"option">>, [{<<"selected">>, <<"selected">>},{<<"value">>, <<"31536000">>}],<<"1 Year">>),
?XAC(<<"option">>, [{<<"value">>, <<"315360000">>}],<<"10 Years">>)]),
?BR,
?INPUTT(<<"submit">>, <<"">>, <<"Accept">>)
]),
Top =
@@ -415,11 +459,16 @@ process(_Handlers,
ClientId = proplists:get_value(<<"client_id">>, Q, <<"">>),
RedirectURI = proplists:get_value(<<"redirect_uri">>, Q, <<"">>),
SScope = proplists:get_value(<<"scope">>, Q, <<"">>),
Username = proplists:get_value(<<"username">>, Q, <<"">>),
Server = proplists:get_value(<<"server">>, Q, <<"">>),
StringJID = proplists:get_value(<<"username">>, Q, <<"">>),
#jid{user = Username, server = Server} = jid:from_string(StringJID),
Password = proplists:get_value(<<"password">>, Q, <<"">>),
State = proplists:get_value(<<"state">>, Q, <<"">>),
Scope = str:tokens(SScope, <<" ">>),
TTL = proplists:get_value(<<"ttl">>, Q, <<"">>),
ExpiresIn = case TTL of
<<>> -> undefined;
_ -> jlib:binary_to_integer(TTL)
end,
case oauth2:authorize_password({Username, Server},
ClientId,
RedirectURI,
@@ -427,10 +476,18 @@ process(_Handlers,
{password, Password}) of
{ok, {_AppContext, Authorization}} ->
{ok, {_AppContext2, Response}} =
oauth2:issue_token(Authorization, none),
oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined ]),
{ok, AccessToken} = oauth2_response:access_token(Response),
{ok, Type} = oauth2_response:token_type(Response),
{ok, Expires} = oauth2_response:expires_in(Response),
%%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have
%%per-case expirity time.
Expires = case ExpiresIn of
undefined ->
{ok, Ex} = oauth2_response:expires_in(Response),
Ex;
_ ->
ExpiresIn
end,
{ok, VerifiedScope} = oauth2_response:scope(Response),
%oauth2_wrq:redirected_access_token_response(ReqData,
% RedirectURI,
@@ -459,11 +516,82 @@ process(_Handlers,
}],
ejabberd_web:make_xhtml([?XC(<<"h1">>, <<"302 Found">>)])}
end;
process(_Handlers,
#request{method = 'POST', q = Q, lang = _Lang,
path = [_, <<"token">>]}) ->
case proplists:get_value(<<"grant_type">>, Q, <<"">>) of
<<"password">> ->
SScope = proplists:get_value(<<"scope">>, Q, <<"">>),
StringJID = proplists:get_value(<<"username">>, Q, <<"">>),
#jid{user = Username, server = Server} = jid:from_string(StringJID),
Password = proplists:get_value(<<"password">>, Q, <<"">>),
Scope = str:tokens(SScope, <<" ">>),
TTL = proplists:get_value(<<"ttl">>, Q, <<"">>),
ExpiresIn = case TTL of
<<>> -> undefined;
_ -> jlib:binary_to_integer(TTL)
end,
case oauth2:authorize_password({Username, Server},
Scope,
{password, Password}) of
{ok, {_AppContext, Authorization}} ->
{ok, {_AppContext2, Response}} =
oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined ]),
{ok, AccessToken} = oauth2_response:access_token(Response),
{ok, Type} = oauth2_response:token_type(Response),
%%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have
%%per-case expirity time.
Expires = case ExpiresIn of
undefined ->
{ok, Ex} = oauth2_response:expires_in(Response),
Ex;
_ ->
ExpiresIn
end,
{ok, VerifiedScope} = oauth2_response:scope(Response),
json_response(200, {[
{<<"access_token">>, AccessToken},
{<<"token_type">>, Type},
{<<"scope">>, str:join(VerifiedScope, <<" ">>)},
{<<"expires_in">>, Expires}]});
{error, Error} when is_atom(Error) ->
json_error(400, <<"invalid_grant">>, Error)
end;
_OtherGrantType ->
json_error(400, <<"unsupported_grant_type">>, unsupported_grant_type)
end;
process(_Handlers, _Request) ->
ejabberd_web:error(not_found).
-spec get_db_backend() -> module().
get_db_backend() ->
DBType = ejabberd_config:get_option(
oauth_db_type,
fun(T) -> ejabberd_config:v_db(?MODULE, T) end,
mnesia),
list_to_atom("ejabberd_oauth_" ++ atom_to_list(DBType)).
%% Headers as per RFC 6749
json_response(Code, Body) ->
{Code, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>},
{<<"Cache-Control">>, <<"no-store">>},
{<<"Pragma">>, <<"no-cache">>}],
jiffy:encode(Body)}.
%% OAauth error are defined in:
%% https://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-5.2
json_error(Code, Error, Reason) ->
Desc = json_error_desc(Reason),
Body = {[{<<"error">>, Error},
{<<"error_description">>, Desc}]},
json_response(Code, Body).
json_error_desc(access_denied) -> <<"Access denied">>;
json_error_desc(unsupported_grant_type) -> <<"Unsupported grant type">>;
json_error_desc(invalid_scope) -> <<"Invalid scope">>.
web_head() ->
[?XA(<<"meta">>, [{<<"http-equiv">>, <<"X-UA-Compatible">>},
@@ -577,7 +705,7 @@ css() ->
text-decoration: underline;
}
.container > .section {
.container > .section {
background: #424A55;
}
@@ -595,4 +723,10 @@ opt_type(oauth_expire) ->
fun(I) when is_integer(I), I >= 0 -> I end;
opt_type(oauth_access) ->
fun acl:access_rules_validator/1;
opt_type(_) -> [oauth_expire, oauth_access].
opt_type(oauth_db_type) ->
fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
opt_type(oauth_cache_life_time) ->
fun (I) when is_integer(I), I > 0 -> I end;
opt_type(oauth_cache_size) ->
fun (I) when is_integer(I), I > 0 -> I end;
opt_type(_) -> [oauth_expire, oauth_access, oauth_db_type].
+65
View File
@@ -0,0 +1,65 @@
%%%-------------------------------------------------------------------
%%% File : ejabberd_oauth_mnesia.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : OAUTH2 mnesia backend
%%% Created : 20 Jul 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%-------------------------------------------------------------------
-module(ejabberd_oauth_mnesia).
-export([init/0,
store/1,
lookup/1,
clean/1]).
-include("ejabberd_oauth.hrl").
init() ->
mnesia:create_table(oauth_token,
[{disc_copies, [node()]},
{attributes,
record_info(fields, oauth_token)}]),
mnesia:add_table_copy(oauth_token, node(), disc_copies),
ok.
store(R) ->
mnesia:dirty_write(R).
lookup(Token) ->
case catch mnesia:dirty_read(oauth_token, Token) of
[R] ->
R;
_ ->
false
end.
clean(TS) ->
F = fun() ->
Ts = mnesia:select(
oauth_token,
[{#oauth_token{expire = '$1', _ = '_'},
[{'<', '$1', TS}],
['$_']}]),
lists:foreach(fun mnesia:delete_object/1, Ts)
end,
mnesia:async_dirty(F).
+78
View File
@@ -0,0 +1,78 @@
%%%-------------------------------------------------------------------
%%% File : ejabberd_oauth_sql.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : OAUTH2 SQL backend
%%% Created : 27 Jul 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%-------------------------------------------------------------------
-module(ejabberd_oauth_sql).
-compile([{parse_transform, ejabberd_sql_pt}]).
-export([init/0,
store/1,
lookup/1,
clean/1]).
-include("ejabberd_oauth.hrl").
-include("ejabberd.hrl").
-include("ejabberd_sql_pt.hrl").
-include("jlib.hrl").
init() ->
ok.
store(R) ->
Token = R#oauth_token.token,
{User, Server} = R#oauth_token.us,
SJID = jid:to_string({User, Server, <<"">>}),
Scope = str:join(R#oauth_token.scope, <<" ">>),
Expire = R#oauth_token.expire,
?SQL_UPSERT(
?MYNAME,
"oauth_token",
["!token=%(Token)s",
"jid=%(SJID)s",
"scope=%(Scope)s",
"expire=%(Expire)d"]).
lookup(Token) ->
case ejabberd_sql:sql_query(
?MYNAME,
?SQL("select @(jid)s, @(scope)s, @(expire)d"
" from oauth_token where token=%(Token)s")) of
{selected, [{SJID, Scope, Expire}]} ->
JID = jid:from_string(SJID),
US = {JID#jid.luser, JID#jid.lserver},
#oauth_token{token = Token,
us = US,
scope = str:tokens(Scope, <<" ">>),
expire = Expire};
_ ->
false
end.
clean(TS) ->
ejabberd_sql:sql_query(
?MYNAME,
?SQL("delete from oauth_token where expire < %(TS)d")).
+24 -18
View File
@@ -473,28 +473,34 @@ send_element(Pid, El) ->
%%% ejabberd commands
get_commands_spec() ->
[#ejabberd_commands{name = incoming_s2s_number,
tags = [stats, s2s],
desc =
"Number of incoming s2s connections on "
"the node",
policy = admin,
module = ?MODULE, function = incoming_s2s_number,
args = [], result = {s2s_incoming, integer}},
#ejabberd_commands{name = outgoing_s2s_number,
tags = [stats, s2s],
desc =
"Number of outgoing s2s connections on "
"the node",
policy = admin,
module = ?MODULE, function = outgoing_s2s_number,
args = [], result = {s2s_outgoing, integer}}].
[#ejabberd_commands{
name = incoming_s2s_number,
tags = [stats, s2s],
desc = "Number of incoming s2s connections on the node",
policy = admin,
module = ?MODULE, function = incoming_s2s_number,
args = [], result = {s2s_incoming, integer}},
#ejabberd_commands{
name = outgoing_s2s_number,
tags = [stats, s2s],
desc = "Number of outgoing s2s connections on the node",
policy = admin,
module = ?MODULE, function = outgoing_s2s_number,
args = [], result = {s2s_outgoing, integer}}].
%% TODO Move those stats commands to ejabberd stats command ?
incoming_s2s_number() ->
length(supervisor:which_children(ejabberd_s2s_in_sup)).
supervisor_count(ejabberd_s2s_in_sup).
outgoing_s2s_number() ->
length(supervisor:which_children(ejabberd_s2s_out_sup)).
supervisor_count(ejabberd_s2s_out_sup).
supervisor_count(Supervisor) ->
case catch supervisor:which_children(Supervisor) of
{'EXIT', _} -> 0;
Result ->
length(Result)
end.
%%%----------------------------------------------------------------------
%%% Update Mnesia tables
+7 -3
View File
@@ -224,8 +224,10 @@ wait_for_handshake({xmlstreamelement, El}, StateData) ->
fun (H) ->
ejabberd_router:register_route(H, ?MYNAME),
?INFO_MSG("Route registered for service ~p~n",
[H])
end, dict:fetch_keys(StateData#state.host_opts)),
[H]),
ejabberd_hooks:run(component_connected,
[H])
end, dict:fetch_keys(StateData#state.host_opts)),
{next_state, stream_established, StateData};
_ ->
send_text(StateData, ?INVALID_HANDSHAKE_ERR),
@@ -382,7 +384,9 @@ terminate(Reason, StateName, StateData) ->
case StateName of
stream_established ->
lists:foreach(fun (H) ->
ejabberd_router:unregister_route(H)
ejabberd_router:unregister_route(H),
ejabberd_hooks:run(component_disconnected,
[StateData#state.host, Reason])
end,
dict:fetch_keys(StateData#state.host_opts));
_ -> ok
+24 -18
View File
@@ -270,25 +270,28 @@ get_session_pid(User, Server, Resource) ->
-spec set_offline_info(sid(), binary(), binary(), binary(), info()) -> ok.
set_offline_info({Time, _Pid}, User, Server, Resource, Info) ->
SID = {Time, undefined},
set_offline_info(SID, User, Server, Resource, Info) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
set_session(SID, LUser, LServer, LResource, undefined, Info).
set_session(SID, LUser, LServer, LResource, undefined, [offline | Info]).
-spec get_offline_info(erlang:timestamp(), binary(), binary(),
binary()) -> none | info().
get_offline_info(Time, User, Server, Resource) ->
SID = {Time, undefined},
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
Mod = get_sm_backend(LServer),
case Mod:get_sessions(LUser, LServer, LResource) of
[#session{sid = SID, info = Info}] ->
Info;
[#session{sid = {Time, _}, info = Info}] ->
case proplists:get_bool(offline, Info) of
true ->
Info;
false ->
none
end;
_ ->
none
end.
@@ -425,11 +428,12 @@ set_session(SID, User, Server, Resource, Priority, Info) ->
-spec online([#session{}]) -> [#session{}].
online(Sessions) ->
lists:filter(fun(#session{sid = {_, undefined}}) ->
false;
(_) ->
true
end, Sessions).
lists:filter(fun is_online/1, Sessions).
-spec is_online(#session{}) -> boolean().
is_online(#session{info = Info}) ->
not proplists:get_bool(offline, Info).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -678,15 +682,17 @@ check_for_sessions_to_replace(User, Server, Resource) ->
check_max_sessions(LUser, LServer).
check_existing_resources(LUser, LServer, LResource) ->
SIDs = get_resource_sessions(LUser, LServer, LResource),
if SIDs == [] -> ok;
Mod = get_sm_backend(LServer),
Ss = Mod:get_sessions(LUser, LServer, LResource),
{OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss),
lists:foreach(fun(#session{sid = S}) ->
Mod:delete_session(LUser, LServer, LResource, S)
end, OfflineSs),
if OnlineSs == [] -> ok;
true ->
SIDs = [SID || #session{sid = SID} <- OnlineSs],
MaxSID = lists:max(SIDs),
lists:foreach(fun ({_, undefined} = S) ->
Mod = get_sm_backend(LServer),
Mod:delete_session(LUser, LServer, LResource,
S);
({_, Pid} = S) when S /= MaxSID ->
lists:foreach(fun ({_, Pid} = S) when S /= MaxSID ->
Pid ! replaced;
(_) -> ok
end,
+1 -1
View File
@@ -790,7 +790,7 @@ pgsql_connect(Server, Port, DB, Username, Password) ->
{port, Port},
{as_binary, true}]) of
{ok, Ref} ->
pgsql:squery(Ref, [<<"alter database ">>, DB, <<" set ">>,
pgsql:squery(Ref, [<<"alter database \"">>, DB, <<"\" set ">>,
<<"standard_conforming_strings='off';">>]),
pgsql:squery(Ref, [<<"set standard_conforming_strings to 'off';">>]),
{ok, Ref};
+24 -16
View File
@@ -74,20 +74,27 @@ get_acl_rule([<<"vhosts">>], _) ->
%% The pages of a vhost are only accesible if the user is admin of that vhost:
get_acl_rule([<<"server">>, VHost | _RPath], Method)
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
{VHost, [configure, webadmin_view]};
AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access, fun(A) -> A end, configure),
ACR = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access_readonly, fun(A) -> A end, webadmin_view),
{VHost, [AC, ACR]};
get_acl_rule([<<"server">>, VHost | _RPath], 'POST') ->
{VHost, [configure]};
AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access, fun(A) -> A end, configure),
{VHost, [AC]};
%% Default rule: only global admins can access any other random page
get_acl_rule(_RPath, Method)
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
{global, [configure, webadmin_view]};
get_acl_rule(_RPath, 'POST') -> {global, [configure]}.
is_acl_match(Host, Rules, Jid) ->
lists:any(fun (Rule) ->
allow == acl:match_rule(Host, Rule, Jid)
end,
Rules).
AC = gen_mod:get_module_opt(global, ejabberd_web_admin,
access, fun(A) -> A end, configure),
ACR = gen_mod:get_module_opt(global, ejabberd_web_admin,
access_readonly, fun(A) -> A end, webadmin_view),
{global, [AC, ACR]};
get_acl_rule(_RPath, 'POST') ->
AC = gen_mod:get_module_opt(global, ejabberd_web_admin,
access, fun(A) -> A end, configure),
{global, [AC]}.
%%%==================================
%%%% Menu Items Access
@@ -138,7 +145,7 @@ is_allowed_path([<<"admin">> | Path], JID) ->
is_allowed_path(Path, JID);
is_allowed_path(Path, JID) ->
{HostOfRule, AccessRule} = get_acl_rule(Path, 'GET'),
is_acl_match(HostOfRule, AccessRule, JID).
acl:any_rules_allowed(HostOfRule, AccessRule, JID).
%% @spec(Path) -> URL
%% where Path = [string()]
@@ -266,8 +273,8 @@ get_auth_account(HostOfRule, AccessRule, User, Server,
Pass) ->
case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
true ->
case is_acl_match(HostOfRule, AccessRule,
jid:make(User, Server, <<"">>))
case acl:any_rules_allowed(HostOfRule, AccessRule,
jid:make(User, Server, <<"">>))
of
false -> {unauthorized, <<"unprivileged-account">>};
true -> {ok, {User, Server}}
@@ -1333,7 +1340,7 @@ parse_access_rule(Text) ->
list_vhosts(Lang, JID) ->
Hosts = (?MYHOSTS),
HostsAllowed = lists:filter(fun (Host) ->
is_acl_match(Host,
acl:any_rules_allowed(Host,
[configure, webadmin_view],
JID)
end,
@@ -2965,7 +2972,8 @@ make_menu_item(item, 3, URI, Name, Lang) ->
%%%==================================
opt_type(access) -> fun (V) -> V end;
opt_type(_) -> [access].
opt_type(access) -> fun acl:access_rules_validator/1;
opt_type(access_readonly) -> fun acl:access_rules_validator/1;
opt_type(_) -> [access, access_readonly].
%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=:
+1 -1
View File
@@ -186,7 +186,7 @@ delete(LServer, Table, ConvertFun) ->
mnesia:write_lock_table(Table),
{_N, SQLs} =
mnesia:foldl(
fun(R, {N, SQLs} = Acc) ->
fun(R, Acc) ->
case ConvertFun(LServer, R) of
[] ->
Acc;
+34 -3
View File
@@ -484,17 +484,28 @@ compile_deps(_Module, _Spec, DestDir) ->
filelib:ensure_dir(filename:join(Ebin, ".")),
Result = lists:foldl(fun(Dep, Acc) ->
Inc = filename:join(Dep, "include"),
Lib = filename:join(Dep, "lib"),
Src = filename:join(Dep, "src"),
Options = [{outdir, Ebin}, {i, Inc}],
[file:copy(App, Ebin) || App <- filelib:wildcard(Src++"/*.app")],
Acc++[case compile:file(File, Options) of
%% Compile erlang files
Acc1 = Acc ++ [case compile:file(File, Options) of
{ok, _} -> ok;
{ok, _, _} -> ok;
{ok, _, _, _} -> ok;
error -> {error, {compilation_failed, File}};
Error -> Error
end
|| File <- filelib:wildcard(Src++"/*.erl")]
|| File <- filelib:wildcard(Src++"/*.erl")],
%% Compile elixir files
Acc1 ++ [case compile_elixir_file(Ebin, File) of
{ok, _} -> ok;
{error, File} -> {error, {compilation_failed, File}}
end
|| File <- filelib:wildcard(Lib ++ "/*.ex")]
end, [], filelib:wildcard("deps/*")),
case lists:dropwhile(
fun(ok) -> true;
@@ -515,6 +526,8 @@ compile(_Module, _Spec, DestDir) ->
verbose, report_errors, report_warnings]
++ ExtLib,
[file:copy(App, Ebin) || App <- filelib:wildcard("src/*.app")],
%% Compile erlang files
Result = [case compile:file(File, Options) of
{ok, _} -> ok;
{ok, _, _} -> ok;
@@ -523,14 +536,32 @@ compile(_Module, _Spec, DestDir) ->
Error -> Error
end
|| File <- filelib:wildcard("src/*.erl")],
%% Compile elixir files
Result1 = Result ++ [case compile_elixir_file(Ebin, File) of
{ok, _} -> ok;
{error, File} -> {error, {compilation_failed, File}}
end
|| File <- filelib:wildcard("lib/*.ex")],
case lists:dropwhile(
fun(ok) -> true;
(_) -> false
end, Result) of
end, Result1) of
[] -> ok;
[Error|_] -> Error
end.
compile_elixir_file(Dest, File) when is_list(Dest) and is_list(File) ->
compile_elixir_file(list_to_binary(Dest), list_to_binary(File));
compile_elixir_file(Dest, File) ->
try 'Elixir.Kernel.ParallelCompiler':files_to_path([File], Dest, []) of
[Module] -> {ok, Module}
catch
_ -> {error, File}
end.
install(Module, Spec, DestDir) ->
Errors = lists:dropwhile(fun({_, {ok, _}}) -> true;
(_) -> false
+59 -16
View File
@@ -53,6 +53,7 @@
-callback start(binary(), opts()) -> any().
-callback stop(binary()) -> any().
-callback mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
-callback depends(binary(), opts()) -> [{module(), hard | soft}].
-export_type([opts/0]).
-export_type([db_type/0]).
@@ -77,18 +78,56 @@ start_modules() ->
get_modules_options(Host) ->
ejabberd_config:get_option(
{modules, Host},
fun(Mods) ->
lists:map(
{modules, Host},
fun(Mods) ->
lists:map(
fun({M, A}) when is_atom(M), is_list(A) ->
{M, A}
{M, A}
end, Mods)
end, []).
end, []).
sort_modules(Host, ModOpts) ->
G = digraph:new([acyclic]),
lists:foreach(
fun({Mod, Opts}) ->
digraph:add_vertex(G, Mod, Opts),
Deps = try Mod:depends(Host, Opts) catch _:undef -> [] end,
lists:foreach(
fun({DepMod, Type}) ->
case lists:keyfind(DepMod, 1, ModOpts) of
false when Type == hard ->
ErrTxt = io_lib:format(
"failed to load module '~s' "
"because it depends on module '~s' "
"which is not found in the config",
[Mod, DepMod]),
?ERROR_MSG(ErrTxt, []),
digraph:del_vertex(G, Mod),
maybe_halt_ejabberd(ErrTxt);
false when Type == soft ->
?WARNING_MSG("module '~s' is recommended for "
"module '~s' but is not found in "
"the config",
[DepMod, Mod]);
{DepMod, DepOpts} ->
digraph:add_vertex(G, DepMod, DepOpts),
case digraph:add_edge(G, DepMod, Mod) of
{error, {bad_edge, Path}} ->
?WARNING_MSG("cyclic dependency detected "
"between modules: ~p",
[Path]);
_ ->
ok
end
end
end, Deps)
end, ModOpts),
[digraph:vertex(G, V) || V <- digraph_utils:topsort(G)].
-spec start_modules(binary()) -> any().
start_modules(Host) ->
Modules = get_modules_options(Host),
Modules = sort_modules(Host, get_modules_options(Host)),
lists:foreach(
fun({Module, Opts}) ->
start_module(Host, Module, Opts)
@@ -121,16 +160,20 @@ start_module(Host, Module, Opts0) ->
[Module, Host, Opts, Class, Reason,
erlang:get_stacktrace()]),
?CRITICAL_MSG(ErrorText, []),
case is_app_running(ejabberd) of
true ->
erlang:raise(Class, Reason, erlang:get_stacktrace());
false ->
?CRITICAL_MSG("ejabberd initialization was aborted "
"because a module start failed.",
[]),
timer:sleep(3000),
erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199))
end
maybe_halt_ejabberd(ErrorText),
erlang:raise(Class, Reason, erlang:get_stacktrace())
end.
maybe_halt_ejabberd(ErrorText) ->
case is_app_running(ejabberd) of
false ->
?CRITICAL_MSG("ejabberd initialization was aborted "
"because a module start failed.",
[]),
timer:sleep(3000),
erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199));
true ->
ok
end.
is_app_running(AppName) ->
+25 -1
View File
@@ -50,11 +50,35 @@
-spec start() -> ok.
start() ->
{ok, Owner} = ets_owner(),
SplitPattern = binary:compile_pattern([<<"@">>, <<"/">>]),
catch ets:new(jlib, [named_table, protected, set, {keypos, 1}]),
%% Table is public to allow ETS insert to fix / update the table even if table already exist
%% with another owner.
catch ets:new(jlib, [named_table, public, set, {keypos, 1}, {heir, Owner, undefined}]),
ets:insert(jlib, {string_to_jid_pattern, SplitPattern}),
ok.
ets_owner() ->
case whereis(jlib_ets) of
undefined ->
Pid = spawn(fun() -> ets_keepalive() end),
case catch register(jlib_ets, Pid) of
true ->
{ok, Pid};
Error -> Error
end;
Pid ->
{ok,Pid}
end.
%% Process used to keep jlib ETS table alive in case the original owner dies.
%% The table need to be public, otherwise subsequent inserts would fail.
ets_keepalive() ->
receive
_ ->
ets_keepalive()
end.
-spec make(binary(), binary(), binary()) -> jid() | error.
make(User, Server, Resource) ->
+15 -61
View File
@@ -307,21 +307,16 @@ get_iq_namespace(#xmlel{name = <<"iq">>, children = Els}) ->
get_iq_namespace(_) -> <<"">>.
%%
-spec(iq_query_info/1 ::
(
Xmlel :: xmlel())
-> iq_request() | 'reply' | 'invalid' | 'not_iq'
).
-spec iq_query_info(Xmlel :: xmlel()) ->
iq_request() | 'reply' | 'invalid' | 'not_iq'.
%% @spec (xmlelement()) -> iq() | reply | invalid | not_iq
iq_query_info(El) -> iq_info_internal(El, request).
%%
-spec(iq_query_or_response_info/1 ::
(
Xmlel :: xmlel())
-> iq_request() | iq_reply() | 'reply' | 'invalid' | 'not_iq'
).
-spec iq_query_or_response_info(Xmlel :: xmlel()) ->
iq_request() | iq_reply() |
'reply' | 'invalid' | 'not_iq'.
iq_query_or_response_info(El) ->
iq_info_internal(El, any).
@@ -373,11 +368,7 @@ iq_type_to_string(get) -> <<"get">>;
iq_type_to_string(result) -> <<"result">>;
iq_type_to_string(error) -> <<"error">>.
-spec(iq_to_xml/1 ::
(
IQ :: iq())
-> xmlel()
).
-spec iq_to_xml(IQ :: iq()) -> xmlel().
iq_to_xml(#iq{id = ID, type = Type, sub_el = SubEl}) ->
if ID /= <<"">> ->
@@ -391,13 +382,8 @@ iq_to_xml(#iq{id = ID, type = Type, sub_el = SubEl}) ->
children = SubEl}
end.
-spec(parse_xdata_submit/1 ::
(
El :: xmlel())
-> [{Var::binary(), Values::[binary()]}]
%%
| 'invalid'
).
-spec parse_xdata_submit(El :: xmlel()) ->
[{Var::binary(), Values::[binary()]}] | 'invalid'.
parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) ->
case fxml:get_attr_s(<<"type">>, Attrs) of
@@ -409,12 +395,9 @@ parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) ->
invalid
end.
-spec(parse_xdata_fields/2 ::
(
Xmlels :: [xmlel() | cdata()],
Res :: [{Var::binary(), Values :: [binary()]}])
-> [{Var::binary(), Values::[binary()]}]
).
-spec parse_xdata_fields(Xmlels :: [xmlel() | cdata()],
Res :: [{Var::binary(), Values :: [binary()]}]) ->
[{Var::binary(), Values::[binary()]}].
parse_xdata_fields([], Res) -> Res;
parse_xdata_fields([#xmlel{name = <<"field">>, attrs = Attrs, children = SubEls}
@@ -429,12 +412,8 @@ parse_xdata_fields([#xmlel{name = <<"field">>, attrs = Attrs, children = SubEls}
parse_xdata_fields([_ | Els], Res) ->
parse_xdata_fields(Els, Res).
-spec(parse_xdata_values/2 ::
(
Xmlels :: [xmlel() | cdata()],
Res :: [binary()])
-> [binary()]
).
-spec parse_xdata_values(Xmlels :: [xmlel() | cdata()],
Res :: [binary()]) -> [binary()].
parse_xdata_values([], Res) -> Res;
parse_xdata_values([#xmlel{name = <<"value">>, children = SubEls} | Els], Res) ->
@@ -598,33 +577,8 @@ add_delay_info(El, From, Time) ->
binary()) -> xmlel().
add_delay_info(El, From, Time, Desc) ->
case fxml:get_subtag_with_xmlns(El, <<"delay">>, ?NS_DELAY) of
false ->
%% Add new tag
DelayTag = create_delay_tag(Time, From, Desc),
fxml:append_subtags(El, [DelayTag]);
DelayTag ->
%% Update existing tag
NewDelayTag =
case {fxml:get_tag_cdata(DelayTag), Desc} of
{<<"">>, <<"">>} ->
DelayTag;
{OldDesc, <<"">>} ->
DelayTag#xmlel{children = [{xmlcdata, OldDesc}]};
{<<"">>, NewDesc} ->
DelayTag#xmlel{children = [{xmlcdata, NewDesc}]};
{OldDesc, NewDesc} ->
case binary:match(OldDesc, NewDesc) of
nomatch ->
FinalDesc = <<OldDesc/binary, ", ", NewDesc/binary>>,
DelayTag#xmlel{children = [{xmlcdata, FinalDesc}]};
_ ->
DelayTag#xmlel{children = [{xmlcdata, OldDesc}]}
end
end,
NewEl = fxml:remove_subtags(El, <<"delay">>, {<<"xmlns">>, ?NS_DELAY}),
fxml:append_subtags(NewEl, [NewDelayTag])
end.
DelayTag = create_delay_tag(Time, From, Desc),
fxml:append_subtags(El, [DelayTag]).
-spec create_delay_tag(erlang:timestamp(), jid() | ljid() | binary(), binary())
-> xmlel() | error.
+4 -1
View File
@@ -35,7 +35,7 @@
process_sm_iq/3, get_local_commands/5,
get_local_identity/5, get_local_features/5,
get_sm_commands/5, get_sm_identity/5, get_sm_features/5,
ping_item/4, ping_command/4, mod_opt_type/1]).
ping_item/4, ping_command/4, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -284,6 +284,9 @@ ping_command(_Acc, _From, _To,
end;
ping_command(Acc, _From, _To, _Request) -> Acc.
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(report_commands_node) ->
fun (B) when is_boolean(B) -> B end;
+17 -12
View File
@@ -47,7 +47,7 @@
srg_delete/2, srg_list/1, srg_get_info/2,
srg_get_members/2, srg_user_add/4, srg_user_del/4,
send_message/5, send_stanza/3, send_stanza_c2s/4, privacy_set/3,
stats/1, stats/2, mod_opt_type/1, get_commands_spec/0]).
stats/1, stats/2, mod_opt_type/1, get_commands_spec/0, depends/2]).
-include("ejabberd.hrl").
@@ -66,6 +66,8 @@ start(_Host, _Opts) ->
stop(_Host) ->
ejabberd_commands:unregister_commands(get_commands_spec()).
depends(_Host, _Opts) ->
[].
%%%
%%% Register commands
@@ -533,7 +535,7 @@ get_commands_spec() ->
policy = user,
module = mod_offline, function = count_offline_messages,
args = [],
result = {res, integer}},
result = {value, integer}},
#ejabberd_commands{name = send_message, tags = [stanza],
desc = "Send a message to a local or remote bare of full JID",
module = ?MODULE, function = send_message,
@@ -861,12 +863,15 @@ connected_users_vhost(Host) ->
%% Code copied from ejabberd_sm.erl and customized
dirty_get_sessions_list2() ->
mnesia:dirty_select(
session,
[{#session{usr = '$1', sid = {'$2', '$3'}, priority = '$4', info = '$5',
_ = '_'},
[{is_pid, '$3'}],
[['$1', {{'$2', '$3'}}, '$4', '$5']]}]).
Ss = mnesia:dirty_select(
session,
[{#session{usr = '$1', sid = '$2', priority = '$3', info = '$4',
_ = '_'},
[],
[['$1', '$2', '$3', '$4']]}]),
lists:filter(fun([_USR, _SID, _Priority, Info]) ->
not proplists:get_bool(offline, Info)
end, Ss).
%% Make string more print-friendly
stringize(String) ->
@@ -901,8 +906,8 @@ user_sessions_info(User, Host) ->
{'EXIT', _Reason} ->
[];
Ss ->
lists:filter(fun(#session{sid = {_, Pid}}) ->
is_pid(Pid)
lists:filter(fun(#session{info = Info}) ->
not proplists:get_bool(offline, Info)
end, Ss)
end,
lists:map(
@@ -1166,8 +1171,8 @@ subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) -
subscribe_roster({Name, Server, Group, Nick}, Roster);
%% Subscribe Name2 to Name1
subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
subscribe(Name1, Server1, list_to_binary(Name2), list_to_binary(Server2),
list_to_binary(Nick2), list_to_binary(Group2), <<"both">>, []),
subscribe(Name1, Server1, iolist_to_binary(Name2), iolist_to_binary(Server2),
iolist_to_binary(Nick2), iolist_to_binary(Group2), <<"both">>, []),
subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
push_alltoall(S, G) ->
+4 -1
View File
@@ -33,7 +33,7 @@
-export([start/2, init/0, stop/1, export/1, import/1,
import/3, announce/3, send_motd/1, disco_identity/5,
disco_features/5, disco_items/5,
disco_features/5, disco_items/5, depends/2,
send_announcement_to_all/3, announce_commands/4,
announce_items/4, mod_opt_type/1]).
@@ -74,6 +74,9 @@ start(Host, Opts) ->
register(gen_mod:get_module_proc(Host, ?PROCNAME),
proc_lib:spawn(?MODULE, init, [])).
depends(_Host, _Opts) ->
[{mod_adhoc, hard}].
init() ->
loop().
+4 -1
View File
@@ -30,7 +30,7 @@
-protocol({xep, 191, '1.2'}).
-export([start/2, stop/1, process_iq/3,
process_iq_set/4, process_iq_get/5, mod_opt_type/1]).
process_iq_set/4, process_iq_get/5, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -63,6 +63,9 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
?NS_BLOCKING).
depends(_Host, _Opts) ->
[{mod_privacy, hard}].
process_iq(_From, _To, IQ) ->
SubEl = IQ#iq.sub_el,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
+4 -1
View File
@@ -41,7 +41,7 @@
import_start/2, import_stop/2]).
%% gen_mod callbacks
-export([start/2, start_link/2, stop/1]).
-export([start/2, start_link/2, stop/1, depends/2]).
%% gen_server callbacks
-export([init/1, handle_info/2, handle_call/3,
@@ -306,6 +306,9 @@ c2s_broadcast_recipients(InAcc, Host, C2SState,
end;
c2s_broadcast_recipients(Acc, _, _, _, _, _) -> Acc.
depends(_Host, _Opts) ->
[].
init([Host, Opts]) ->
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
+4 -1
View File
@@ -37,7 +37,7 @@
-export([user_send_packet/4, user_receive_packet/5,
iq_handler2/3, iq_handler1/3, remove_connection/4,
is_carbon_copy/1, mod_opt_type/1]).
is_carbon_copy/1, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -278,6 +278,9 @@ list(User, Server) ->
Mod = gen_mod:db_mod(Server, ?MODULE),
Mod:list(User, Server).
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(_) -> [db_type, iqdisc].
+8 -3
View File
@@ -31,7 +31,7 @@
-behavior(gen_mod).
%% gen_mod callbacks.
-export([start/2, stop/1, mod_opt_type/1]).
-export([start/2, stop/1, mod_opt_type/1, depends/2]).
%% ejabberd_hooks callbacks.
-export([filter_presence/3, filter_chat_states/3, filter_pep/3, filter_other/3,
@@ -66,7 +66,7 @@ start(Host, Opts) ->
QueuePEP =
gen_mod:get_opt(queue_pep, Opts,
fun(B) when is_boolean(B) -> B end,
false),
true),
if QueuePresence; QueueChatStates; QueuePEP ->
ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE,
add_stream_feature, 50),
@@ -106,7 +106,7 @@ stop(Host) ->
QueuePEP =
gen_mod:get_module_opt(Host, ?MODULE, queue_pep,
fun(B) when is_boolean(B) -> B end,
false),
true),
if QueuePresence; QueueChatStates; QueuePEP ->
ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE,
add_stream_feature, 50),
@@ -142,6 +142,11 @@ mod_opt_type(queue_pep) ->
fun(B) when is_boolean(B) -> B end;
mod_opt_type(_) -> [queue_presence, queue_chat_states, queue_pep].
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[].
%%--------------------------------------------------------------------
%% ejabberd_hooks callbacks.
%%--------------------------------------------------------------------
+35 -26
View File
@@ -35,7 +35,8 @@
get_local_features/5, get_local_items/5,
adhoc_local_items/4, adhoc_local_commands/4,
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
adhoc_sm_items/4, adhoc_sm_commands/4, mod_opt_type/1]).
adhoc_sm_items/4, adhoc_sm_commands/4, mod_opt_type/1,
depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -95,6 +96,9 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
?NS_COMMANDS).
depends(_Host, _Opts) ->
[{mod_adhoc, hard}, {mod_last, soft}].
%%%-----------------------------------------------------------------------
-define(INFO_IDENTITY(Category, Type, Name, Lang),
@@ -1368,10 +1372,9 @@ get_form(Host, [<<"config">>, <<"access">>], Lang) ->
[{xmlcdata, S}]}
end,
str:tokens(iolist_to_binary(io_lib:format("~p.",
[ets:select(local_config,
[{{local_config,
{access,
'$1',
[ets:select(access,
[{{access,
{'$1',
'$2'},
'$3'},
[{'==',
@@ -1826,10 +1829,9 @@ set_form(_From, Host, [<<"config">>, <<"access">>],
Lang, XData) ->
SetAccess = fun (Rs) ->
mnesia:transaction(fun () ->
Os = mnesia:select(local_config,
[{{local_config,
{access,
'$1',
Os = mnesia:select(access,
[{{access,
{'$1',
'$2'},
'$3'},
[{'==',
@@ -1843,9 +1845,8 @@ set_form(_From, Host, [<<"config">>, <<"access">>],
lists:foreach(fun ({access,
Name,
Rules}) ->
mnesia:write({local_config,
{access,
Name,
mnesia:write({access,
{Name,
Host},
Rules})
end,
@@ -1916,21 +1917,29 @@ set_form(From, Host, ?NS_ADMINL(<<"end-user-session">>),
Xmlelement = ?SERRT_POLICY_VIOLATION(Lang, <<"has been kicked">>),
case JID#jid.lresource of
<<>> ->
SIDs = mnesia:dirty_select(session,
[{#session{sid = {'$1', '$2'},
usr = {LUser, LServer, '_'},
_ = '_'},
[{is_pid, '$2'}],
[{{'$1', '$2'}}]}]),
[Pid ! {kick, kicked_by_admin, Xmlelement} || {_, Pid} <- SIDs];
SIs = mnesia:dirty_select(session,
[{#session{usr = {LUser, LServer, '_'},
sid = '$1',
info = '$2',
_ = '_'},
[], [{{'$1', '$2'}}]}]),
Pids = [P || {{_, P}, Info} <- SIs,
not proplists:get_bool(offline, Info)],
lists:foreach(fun(Pid) ->
Pid ! {kick, kicked_by_admin, Xmlelement}
end, Pids);
R ->
[{_, Pid}] = mnesia:dirty_select(session,
[{#session{sid = {'$1', '$2'},
usr = {LUser, LServer, R},
_ = '_'},
[{is_pid, '$2'}],
[{{'$1', '$2'}}]}]),
Pid ! {kick, kicked_by_admin, Xmlelement}
[{{_, Pid}, Info}] = mnesia:dirty_select(
session,
[{#session{usr = {LUser, LServer, R},
sid = '$1',
info = '$2',
_ = '_'},
[], [{{'$1', '$2'}}]}]),
case proplists:get_bool(offline, Info) of
true -> ok;
false -> Pid ! {kick, kicked_by_admin, Xmlelement}
end
end,
{result, []};
set_form(From, Host,
+4 -1
View File
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_local_iq/3,
mod_opt_type/1, opt_type/1]).
mod_opt_type/1, opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -201,6 +201,9 @@ process_get(#xmlel{name = <<"last">>, attrs = Attrs}, Lang) ->
%% {result, };
process_get(_, _) -> {error, ?ERR_BAD_REQUEST}.
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [iqdisc].
+4 -1
View File
@@ -39,7 +39,7 @@
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
get_info/5, register_feature/2, unregister_feature/2,
register_extra_domain/2, unregister_extra_domain/2,
transform_module_options/1, mod_opt_type/1]).
transform_module_options/1, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -534,6 +534,9 @@ values_to_xml(Values) ->
end,
Values).
depends(_Host, _Opts) ->
[].
mod_opt_type(extra_domains) ->
fun (Hs) -> [iolist_to_binary(H) || H <- Hs] end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
+5 -2
View File
@@ -37,7 +37,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -63,7 +63,7 @@ start_link(Host, Opts) ->
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
temporary, 1000, worker, [?MODULE]},
transient, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
@@ -200,5 +200,8 @@ do_client_version(enabled, From, To) ->
?INFO_MSG("Information of the client: ~s~s",
[ToS, Values_string2]).
depends(_Host, _Opts) ->
[].
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(_) -> [host].
+4 -1
View File
@@ -33,7 +33,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-include_lib("stdlib/include/ms_transform.hrl").
-include("ejabberd.hrl").
@@ -120,6 +120,9 @@ stop(Host) ->
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
depends(_Host, _Opts) ->
[].
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
+125 -79
View File
@@ -74,7 +74,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process/2, mod_opt_type/1]).
-export([start/2, stop/1, process/2, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@@ -123,6 +123,9 @@ start(_Host, _Opts) ->
stop(_Host) ->
ok.
depends(_Host, _Opts) ->
[].
%% ----------
%% basic auth
%% ----------
@@ -130,13 +133,13 @@ stop(_Host) ->
check_permissions(Request, Command) ->
case catch binary_to_existing_atom(Command, utf8) of
Call when is_atom(Call) ->
{ok, CommandPolicy} = ejabberd_commands:get_command_policy(Call),
check_permissions2(Request, Call, CommandPolicy);
{ok, CommandPolicy, Scope} = ejabberd_commands:get_command_policy_and_scope(Call),
check_permissions2(Request, Call, CommandPolicy, Scope);
_ ->
unauthorized_response()
json_error(404, 40, <<"Endpoint not found.">>)
end.
check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _)
check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _, ScopeList)
when HTTPAuth /= undefined ->
Admin =
case lists:keysearch(<<"X-Admin">>, 1, Headers) of
@@ -156,24 +159,23 @@ check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _)
false
end;
{oauth, Token, _} ->
case oauth_check_token(Call, Token) of
case oauth_check_token(ScopeList, Token) of
{ok, user, {User, Server}} ->
{ok, {User, Server, {oauth, Token}, Admin}};
{ok, server_admin} -> %% token whas generated using issue_token command line
{ok, admin};
false ->
false
{false, Reason} ->
{false, Reason}
end;
_ ->
false
end,
case Auth of
{ok, A} -> {allowed, Call, A};
{false, no_matching_scope} -> outofscope_response();
_ -> unauthorized_response()
end;
check_permissions2(_Request, Call, open) ->
check_permissions2(_Request, Call, open, _Scope) ->
{allowed, Call, noauth};
check_permissions2(#request{ip={IP, _Port}}, Call, _Policy) ->
check_permissions2(#request{ip={IP, _Port}}, Call, _Policy, _Scope) ->
Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access,
fun(V) -> V end,
none),
@@ -188,18 +190,16 @@ check_permissions2(#request{ip={IP, _Port}}, Call, _Policy) ->
Commands when is_list(Commands) ->
case lists:member(Call, Commands) of
true -> {allowed, Call, admin};
_ -> unauthorized_response()
_ -> outofscope_response()
end;
_E ->
{allowed, Call, noauth}
end;
check_permissions2(_Request, _Call, _Policy) ->
check_permissions2(_Request, _Call, _Policy, _Scope) ->
unauthorized_response().
oauth_check_token(Scope, Token) when is_atom(Scope) ->
oauth_check_token(atom_to_binary(Scope, utf8), Token);
oauth_check_token(Scope, Token) ->
ejabberd_oauth:check_token(Scope, Token).
oauth_check_token(ScopeList, Token) when is_list(ScopeList) ->
ejabberd_oauth:check_token(ScopeList, Token).
%% ------------------
%% command processing
@@ -213,24 +213,24 @@ process(_, #request{method = 'POST', data = <<>>}) ->
process([Call], #request{method = 'POST', data = Data, ip = {IP, _} = IPPort} = Req) ->
Version = get_api_version(Req),
try
Args = case jiffy:decode(Data) of
List when is_list(List) -> List;
{List} when is_list(List) -> List;
Other -> [Other]
end,
Args = extract_args(Data),
log(Call, Args, IPPort),
case check_permissions(Req, Call) of
{allowed, Cmd, Auth} ->
{Code, Result} = handle(Cmd, Auth, Args, Version, IP),
json_response(Code, jiffy:encode(Result));
Result = handle(Cmd, Auth, Args, Version, IP),
json_format(Result);
%% Warning: check_permission direcly formats 401 reply if not authorized
ErrorResponse ->
ErrorResponse
end
catch _:{error,{_,invalid_json}} = _Err ->
?DEBUG("Bad Request: ~p", [_Err]),
badrequest_response(<<"Invalid JSON input">>);
_:_Error ->
catch
%% TODO We need to refactor to remove redundant error return formatting
throw:{error, unknown_command} ->
{404, 40, <<"Command not found.">>};
_:{error,{_,invalid_json}} = _Err ->
?DEBUG("Bad Request: ~p", [_Err]),
badrequest_response(<<"Invalid JSON input">>);
_:_Error ->
?DEBUG("Bad Request: ~p ~p", [_Error, erlang:get_stacktrace()]),
badrequest_response()
end;
@@ -244,13 +244,18 @@ process([Call], #request{method = 'GET', q = Data, ip = IP} = Req) ->
log(Call, Args, IP),
case check_permissions(Req, Call) of
{allowed, Cmd, Auth} ->
{Code, Result} = handle(Cmd, Auth, Args, Version, IP),
json_response(Code, jiffy:encode(Result));
Result = handle(Cmd, Auth, Args, Version, IP),
json_format(Result);
%% Warning: check_permission direcly formats 401 reply if not authorized
ErrorResponse ->
ErrorResponse
end
catch _:_Error ->
catch
%% TODO We need to refactor to remove redundant error return formatting
throw:{error, unknown_command} ->
json_format({404, 44, <<"Command not found.">>});
_:_Error ->
?DEBUG("Bad Request: ~p ~p", [_Error, erlang:get_stacktrace()]),
badrequest_response()
end;
@@ -258,17 +263,26 @@ process([], #request{method = 'OPTIONS', data = <<>>}) ->
{200, ?OPTIONS_HEADER, []};
process(_Path, Request) ->
?DEBUG("Bad Request: no handler ~p", [Request]),
badrequest_response().
json_error(400, 40, <<"Missing command name.">>).
%% Be tolerant to make API more easily usable from command-line pipe.
extract_args(<<"\n">>) -> [];
extract_args(Data) ->
case jiffy:decode(Data) of
List when is_list(List) -> List;
{List} when is_list(List) -> List;
Other -> [Other]
end.
% get API version N from last "vN" element in URL path
get_api_version(#request{path = Path}) ->
get_api_version(lists:reverse(Path));
get_api_version([<<"v", String/binary>> | Tail]) ->
case catch jlib:binary_to_integer(String) of
N when is_integer(N) ->
N;
_ ->
get_api_version(Tail)
N when is_integer(N) ->
N;
_ ->
get_api_version(Tail)
end;
get_api_version([_Head | Tail]) ->
get_api_version(Tail);
@@ -279,6 +293,8 @@ get_api_version([]) ->
%% command handlers
%% ----------------
%% TODO Check accept types of request before decided format of reply.
% generic ejabberd command handler
handle(Call, Auth, Args, Version, IP) when is_atom(Call), is_list(Args) ->
case ejabberd_commands:get_command_format(Call, Auth, Version) of
@@ -297,7 +313,7 @@ handle(Call, Auth, Args, Version, IP) when is_atom(Call), is_list(Args) ->
[{Key, undefined}|Acc]
end, [], ArgsSpec),
try
handle2(Call, Auth, match(Args2, Spec), Version, IP)
handle2(Call, Auth, match(Args2, Spec), Version, IP)
catch throw:not_found ->
{404, <<"not_found">>};
throw:{not_found, Why} when is_atom(Why) ->
@@ -310,8 +326,10 @@ handle(Call, Auth, Args, Version, IP) when is_atom(Call), is_list(Args) ->
{401, jlib:atom_to_binary(Why)};
throw:{not_allowed, Msg} ->
{401, iolist_to_binary(Msg)};
throw:{error, account_unprivileged} ->
{401, iolist_to_binary(<<"Unauthorized: Account Unpriviledged">>)};
throw:{error, account_unprivileged} ->
{403, 31, <<"Command need to be run with admin priviledge.">>};
throw:{error, access_rules_unauthorized} ->
{403, 32, <<"AccessRules: Account associated to token does not have the right to perform the operation.">>};
throw:{invalid_parameter, Msg} ->
{400, iolist_to_binary(Msg)};
throw:{error, Why} when is_atom(Why) ->
@@ -367,28 +385,33 @@ format_args(Args, ArgsFormat) ->
L when is_list(L) -> exit({additional_unused_args, L})
end.
format_arg({array, Elements},
{list, {ElementDefName, ElementDefFormat}})
format_arg(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}]},
[format_arg(Element, ElementDefFormat)
|| Element <- Elements];
format_arg({[{Name, Value}]},
{tuple, [{_Tuple1N, Tuple1S}, {_Tuple2N, Tuple2S}]})
when Tuple1S == binary;
Tuple1S == string ->
{format_arg(Name, Tuple1S), format_arg(Value, Tuple2S)};
format_arg({Elements},
{tuple, ElementsDef})
when is_list(Elements) ->
FormattedList = format_args(Elements, ElementsDef),
list_to_tuple(FormattedList);
format_arg({array, Elements}, {list, ElementsDef})
F = lists:map(fun({TElName, TElDef}) ->
case lists:keyfind(atom_to_binary(TElName, latin1), 1, Elements) of
{_, Value} ->
format_arg(Value, TElDef);
_ when TElDef == binary; TElDef == string ->
<<"">>;
_ ->
?ERROR_MSG("missing field ~p in tuple ~p", [TElName, Elements]),
throw({invalid_parameter,
io_lib:format("Missing field ~w in tuple ~w", [TElName, Elements])})
end
end, ElementsDef),
list_to_tuple(F);
format_arg(Elements, {list, ElementsDef})
when is_list(Elements) and is_atom(ElementsDef) ->
[format_arg(Element, ElementsDef)
|| Element <- Elements];
@@ -402,7 +425,7 @@ format_arg(undefined, string) -> <<>>;
format_arg(Arg, Format) ->
?ERROR_MSG("don't know how to format Arg ~p for format ~p", [Arg, Format]),
throw({invalid_parameter,
io_lib:format("Arg ~p is not in format ~p",
io_lib:format("Arg ~w is not in format ~w",
[Arg, Format])}).
process_unicode_codepoints(Str) ->
@@ -432,22 +455,24 @@ ejabberd_command(Auth, Cmd, Args, Version, IP) ->
format_command_result(Cmd, Auth, Result, Version) ->
{_, ResultFormat} = ejabberd_commands:get_command_format(Cmd, Auth, Version),
case {ResultFormat, Result} of
{{_, rescode}, V} when V == true; V == ok ->
{200, 0};
{{_, rescode}, _} ->
{200, 1};
{{_, restuple}, {V1, Text1}} when V1 == true; V1 == ok ->
{200, iolist_to_binary(Text1)};
{{_, restuple}, {_, Text2}} ->
{500, iolist_to_binary(Text2)};
{{_, {list, _}}, _V} ->
{_, L} = format_result(Result, ResultFormat),
{200, L};
{{_, {tuple, _}}, _V} ->
{_, T} = format_result(Result, ResultFormat),
{200, T};
_ ->
{200, {[format_result(Result, ResultFormat)]}}
{{_, rescode}, V} when V == true; V == ok ->
{200, 0};
{{_, rescode}, _} ->
{200, 1};
{_, {error, ErrorAtom, Code, Msg}} ->
format_error_result(ErrorAtom, Code, Msg);
{{_, restuple}, {V, Text}} when V == true; V == ok ->
{200, iolist_to_binary(Text)};
{{_, restuple}, {ErrorAtom, Msg}} ->
format_error_result(ErrorAtom, 0, Msg);
{{_, {list, _}}, _V} ->
{_, L} = format_result(Result, ResultFormat),
{200, L};
{{_, {tuple, _}}, _V} ->
{_, T} = format_result(Result, ResultFormat),
{200, T};
_ ->
{200, {[format_result(Result, ResultFormat)]}}
end.
format_result(Atom, {Name, atom}) ->
@@ -485,19 +510,40 @@ format_result(Tuple, {Name, {tuple, Def}}) ->
format_result(404, {_Name, _}) ->
"not_found".
format_error_result(conflict, Code, Msg) ->
{409, Code, iolist_to_binary(Msg)};
format_error_result(_ErrorAtom, Code, Msg) ->
{500, Code, iolist_to_binary(Msg)}.
unauthorized_response() ->
unauthorized_response(<<"401 Unauthorized">>).
unauthorized_response(Body) ->
json_response(401, jiffy:encode(Body)).
json_error(401, 10, <<"Oauth Token is invalid or expired.">>).
outofscope_response() ->
json_error(401, 11, <<"Token does not grant usage to command required scope.">>).
badrequest_response() ->
badrequest_response(<<"400 Bad Request">>).
badrequest_response(Body) ->
json_response(400, jiffy:encode(Body)).
json_format({Code, Result}) ->
json_response(Code, jiffy:encode(Result));
json_format({HTMLCode, JSONErrorCode, Message}) ->
json_error(HTMLCode, JSONErrorCode, Message).
json_response(Code, Body) when is_integer(Code) ->
{Code, ?HEADER(?CT_JSON), Body}.
%% HTTPCode, JSONCode = integers
%% message is binary
json_error(HTTPCode, JSONCode, Message) ->
{HTTPCode, ?HEADER(?CT_JSON),
jiffy:encode({[{<<"status">>, <<"error">>},
{<<"code">>, JSONCode},
{<<"message">>, Message}]})
}.
log(Call, Args, {Addr, Port}) ->
AddrS = jlib:ip_to_list({Addr, Port}),
?INFO_MSG("API call ~s ~p from ~s:~p", [Call, Args, AddrS, Port]);
+3 -1
View File
@@ -37,7 +37,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process/2, mod_opt_type/1]).
-export([start/2, stop/1, process/2, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -109,6 +109,8 @@ mod_opt_type(max_pause) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(_) -> [max_inactivity, max_pause].
depends(_Host, _Opts) ->
[].
%%%----------------------------------------------------------------------
%%% Help Web Page
+4 -1
View File
@@ -46,7 +46,7 @@
%% utility for other http modules
-export([content_type/3]).
-export([reopen_log/1, mod_opt_type/1]).
-export([reopen_log/1, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -109,6 +109,9 @@ stop(Host) ->
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
depends(_Host, _Opts) ->
[].
%%====================================================================
%% API
%%====================================================================
+6
View File
@@ -68,6 +68,7 @@
-export([start_link/3,
start/2,
stop/1,
depends/2,
mod_opt_type/1]).
%% gen_server callbacks.
@@ -222,6 +223,11 @@ mod_opt_type(_) ->
dir_mode, docroot, put_url, get_url, service_url, custom_headers,
rm_on_unregister, thumbnail].
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[].
%%--------------------------------------------------------------------
%% gen_server callbacks.
%%--------------------------------------------------------------------
+9 -3
View File
@@ -39,6 +39,7 @@
-export([start_link/3,
start/2,
stop/1,
depends/2,
mod_opt_type/1]).
%% gen_server callbacks.
@@ -109,6 +110,11 @@ mod_opt_type(max_days) ->
mod_opt_type(_) ->
[access_soft_quota, access_hard_quota, max_days].
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[{mod_http_upload, hard}].
%%--------------------------------------------------------------------
%% gen_server callbacks.
%%--------------------------------------------------------------------
@@ -245,7 +251,7 @@ terminate(Reason, #state{server_host = ServerHost, timers = Timers}) ->
?DEBUG("Stopping upload quota process for ~s: ~p", [ServerHost, Reason]),
ejabberd_hooks:delete(http_upload_slot_request, ServerHost, ?MODULE,
handle_slot_request, 50),
lists:foreach(fun(Timer) -> timer:cancel(Timer) end, Timers).
lists:foreach(fun timer:cancel/1, Timers).
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
@@ -293,7 +299,7 @@ enforce_quota(UserDir, SlotSize, _OldSize, MinSize, MaxSize) ->
{[Path | AccFiles], AccSize + Size, NewSize}
end, {[], 0, 0}, Files),
if OldSize + SlotSize > MaxSize ->
lists:foreach(fun(File) -> del_file_and_dir(File) end, DelFiles),
lists:foreach(fun del_file_and_dir/1, DelFiles),
file:del_dir(UserDir), % In case it's empty, now.
NewSize + SlotSize;
true ->
@@ -308,7 +314,7 @@ delete_old_files(UserDir, CutOff) ->
[] ->
ok;
OldFiles ->
lists:foreach(fun(File) -> del_file_and_dir(File) end, OldFiles),
lists:foreach(fun del_file_and_dir/1, OldFiles),
file:del_dir(UserDir) % In case it's empty, now.
end.
+4 -1
View File
@@ -36,7 +36,7 @@
-export([update_bl_c2s/0]).
-export([is_ip_in_c2s_blacklist/3, mod_opt_type/1]).
-export([is_ip_in_c2s_blacklist/3, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -65,6 +65,9 @@ preinit(Parent, State) ->
error:_ -> Parent ! {ok, Pid, true}
end.
depends(_Host, _Opts) ->
[].
%% TODO:
stop(_Host) -> ok.
+5 -2
View File
@@ -38,7 +38,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -90,7 +90,7 @@ start(Host, Opts) ->
start_supervisor(Host),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
temporary, 1000, worker, [?MODULE]},
transient, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
@@ -99,6 +99,9 @@ stop(Host) ->
gen_server:call(Proc, stop),
supervisor:delete_child(ejabberd_sup, Proc).
depends(_Host, _Opts) ->
[].
%%====================================================================
%% gen_server callbacks
%%====================================================================
+4 -1
View File
@@ -37,7 +37,7 @@
process_sm_iq/3, on_presence_update/4, import/1,
import/3, store_last_info/4, get_last_info/2,
remove_user/2, transform_options/1, mod_opt_type/1,
opt_type/1, register_user/2]).
opt_type/1, register_user/2, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -255,6 +255,9 @@ transform_options({node_start, {_, _, _} = Now}, Opts) ->
transform_options(Opt, Opts) ->
[Opt|Opts].
depends(_Host, _Opts) ->
[].
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [db_type, iqdisc].
+54 -2
View File
@@ -31,13 +31,13 @@
-behaviour(gen_mod).
%% API
-export([start/2, stop/1]).
-export([start/2, stop/1, depends/2]).
-export([user_send_packet/4, user_send_packet_strip_tag/4, user_receive_packet/5,
process_iq_v0_2/3, process_iq_v0_3/3, disco_sm_features/5,
remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/4,
muc_filter_message/5, message_is_archived/5, delete_old_messages/2,
get_commands_spec/0, msg_to_el/4]).
get_commands_spec/0, msg_to_el/4, get_room_config/4, set_room_option/4]).
-include("jlib.hrl").
-include("logger.hrl").
@@ -102,6 +102,12 @@ start(Host, Opts) ->
disco_sm_features, 50),
ejabberd_hooks:add(remove_user, Host, ?MODULE,
remove_user, 50),
ejabberd_hooks:add(remove_room, Host, ?MODULE,
remove_room, 50),
ejabberd_hooks:add(get_room_config, Host, ?MODULE,
get_room_config, 50),
ejabberd_hooks:add(set_room_option, Host, ?MODULE,
set_room_option, 50),
ejabberd_hooks:add(anonymous_purge_hook, Host, ?MODULE,
remove_user, 50),
case gen_mod:get_opt(assume_mam_usage, Opts,
@@ -149,6 +155,12 @@ stop(Host) ->
disco_sm_features, 50),
ejabberd_hooks:delete(remove_user, Host, ?MODULE,
remove_user, 50),
ejabberd_hooks:delete(remove_room, Host, ?MODULE,
remove_room, 50),
ejabberd_hooks:delete(get_room_config, Host, ?MODULE,
get_room_config, 50),
ejabberd_hooks:delete(set_room_option, Host, ?MODULE,
set_room_option, 50),
ejabberd_hooks:delete(anonymous_purge_hook, Host,
?MODULE, remove_user, 50),
case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage,
@@ -165,6 +177,9 @@ stop(Host) ->
ejabberd_commands:unregister_commands(get_commands_spec()),
ok.
depends(_Host, _Opts) ->
[].
remove_user(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
@@ -177,6 +192,41 @@ remove_room(LServer, Name, Host) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:remove_room(LServer, LName, LHost).
get_room_config(X, RoomState, _From, Lang) ->
Config = RoomState#state.config,
Label = <<"Enable message archiving">>,
Var = <<"muc#roomconfig_mam">>,
Val = case Config#config.mam of
true -> <<"1">>;
_ -> <<"0">>
end,
XField = #xmlel{name = <<"field">>,
attrs =
[{<<"type">>, <<"boolean">>},
{<<"label">>, translate:translate(Lang, Label)},
{<<"var">>, Var}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Val}]}]},
X ++ [XField].
set_room_option(_Acc, <<"muc#roomconfig_mam">> = Opt, Vals, Lang) ->
try
Val = case Vals of
[<<"0">>|_] -> false;
[<<"false">>|_] -> false;
[<<"1">>|_] -> true;
[<<"true">>|_] -> true
end,
{#config.mam, Val}
catch _:{case_clause, _} ->
Txt = <<"Value of '~s' should be boolean">>,
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])),
{error, ?ERRT_BAD_REQUEST(Lang, ErrTxt)}
end;
set_room_option(Acc, _Opt, _Vals, _Lang) ->
Acc.
user_receive_packet(Pkt, C2SState, JID, Peer, To) ->
LUser = JID#jid.luser,
LServer = JID#jid.lserver,
@@ -982,6 +1032,8 @@ filter_by_max(_Msgs, _Junk) ->
limit_max(RSM, ?NS_MAM_TMP) ->
RSM; % XEP-0313 v0.2 doesn't require clients to support RSM.
limit_max(none, _NS) ->
#rsm_in{max = ?DEF_PAGE_SIZE};
limit_max(#rsm_in{max = Max} = RSM, _NS) when not is_integer(Max) ->
RSM#rsm_in{max = ?DEF_PAGE_SIZE};
limit_max(#rsm_in{max = Max} = RSM, _NS) when Max > ?MAX_PAGE_SIZE ->
+9 -6
View File
@@ -138,12 +138,15 @@ select(_LServer, JidRequestor,
SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs),
{FilteredMsgs, IsComplete} = filter_by_rsm(SortedMsgs, RSM),
Count = length(Msgs),
{lists:map(
fun(Msg) ->
{Msg#archive_msg.id,
jlib:binary_to_integer(Msg#archive_msg.id),
mod_mam:msg_to_el(Msg, MsgType, JidRequestor, JidArchive)}
end, FilteredMsgs), IsComplete, Count}.
Result = {lists:map(
fun(Msg) ->
{Msg#archive_msg.id,
jlib:binary_to_integer(Msg#archive_msg.id),
mod_mam:msg_to_el(Msg, MsgType, JidRequestor,
JidArchive)}
end, FilteredMsgs), IsComplete, Count},
erlang:garbage_collect(),
Result.
%%%===================================================================
%%% Internal functions
-22
View File
@@ -295,25 +295,3 @@ make_sql_query(User, LServer, Start, End, With, RSM) ->
{QueryPage,
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
SUser, <<"'">>, WithClause, StartClause, EndClause, <<";">>]}.
update(LServer, Table, Fields, Vals, Where) ->
UPairs = lists:zipwith(fun (A, B) ->
<<A/binary, "='", B/binary, "'">>
end,
Fields, Vals),
case ejabberd_sql:sql_query(LServer,
[<<"update ">>, Table, <<" set ">>,
join(UPairs, <<", ">>), <<" where ">>, Where,
<<";">>])
of
{updated, 1} -> {updated, 1};
_ ->
ejabberd_sql:sql_query(LServer,
[<<"insert into ">>, Table, <<"(">>,
join(Fields, <<", ">>), <<") values ('">>,
join(Vals, <<"', '">>), <<"');">>])
end.
%% Almost a copy of string:join/2.
join([], _Sep) -> [];
join([H | T], Sep) -> [H, [[Sep, X] || X <- T]].
+5 -1
View File
@@ -39,7 +39,8 @@
s2s_send_packet, s2s_receive_packet,
remove_user, register_user]).
-export([start/2, stop/1, send_metrics/4, opt_type/1, mod_opt_type/1]).
-export([start/2, stop/1, send_metrics/4, opt_type/1, mod_opt_type/1,
depends/2]).
-export([offline_message_hook/3,
sm_register_connection_hook/3, sm_remove_connection_hook/3,
@@ -59,6 +60,9 @@ stop(Host) ->
[ejabberd_hooks:delete(Hook, Host, ?MODULE, Hook, 20)
|| Hook <- ?HOOKS].
depends(_Host, _Opts) ->
[].
%%====================================================================
%% Hooks handlers
%%====================================================================
+5 -2
View File
@@ -14,7 +14,7 @@
%% API
-export([start_link/2, start/2, stop/1, process_iq/3,
disco_items/5, disco_identity/5, disco_info/5,
disco_features/5, mod_opt_type/1]).
disco_features/5, mod_opt_type/1, depends/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -44,7 +44,7 @@ start_link(Host, Opts) ->
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
temporary, 5000, worker, [?MODULE]},
transient, 5000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
@@ -343,6 +343,9 @@ is_not_subscribed({error, ErrEl}) ->
_ -> false
end.
depends(_Host, _Opts) ->
[{mod_pubsub, hard}].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(_) -> [host, iqdisc].
+34 -11
View File
@@ -53,7 +53,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -95,7 +95,7 @@ start_link(Host, Opts) ->
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
temporary, 1000, worker, [?MODULE]},
transient, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
@@ -105,6 +105,9 @@ stop(Host) ->
supervisor:delete_child(ejabberd_sup, Proc),
{wait, Rooms}.
depends(_Host, _Opts) ->
[{mod_mam, soft}].
shutdown_rooms(Host) ->
MyHost = gen_mod:get_module_opt_host(Host, mod_muc,
<<"conference.@HOST@">>),
@@ -147,18 +150,10 @@ restore_room(ServerHost, Host, Name) ->
forget_room(ServerHost, Host, Name) ->
LServer = jid:nameprep(ServerHost),
remove_room_mam(LServer, Host, Name),
ejabberd_hooks:run(remove_room, LServer, [LServer, Name, Host]),
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:forget_room(LServer, Host, Name).
remove_room_mam(LServer, Host, Name) ->
case gen_mod:is_loaded(LServer, mod_mam) of
true ->
mod_mam:remove_room(LServer, Name, Host);
false ->
ok
end.
process_iq_disco_items(Host, From, To,
#iq{lang = Lang} = IQ) ->
Rsm = jlib:rsm_decode(IQ),
@@ -230,6 +225,7 @@ init([Host, Opts]) ->
public -> Bool;
public_list -> Bool;
mam -> Bool;
allow_subscription -> Bool;
password -> fun iolist_to_binary/1;
title -> fun iolist_to_binary/1;
allow_private_messages_from_visitors ->
@@ -426,6 +422,18 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
iq_get_vcard(Lang)}]},
ejabberd_router:route(To, From,
jlib:iq_to_xml(Res));
#iq{type = get, xmlns = ?NS_MUCSUB,
sub_el = #xmlel{name = <<"subscriptions">>} = SubEl} = IQ ->
RoomJIDs = get_subscribed_rooms(ServerHost, Host, From),
Subs = lists:map(
fun(J) ->
#xmlel{name = <<"subscription">>,
attrs = [{<<"jid">>,
jid:to_string(J)}]}
end, RoomJIDs),
Res = IQ#iq{type = result,
sub_el = [SubEl#xmlel{children = Subs}]},
ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
#iq{type = get, xmlns = ?NS_MUC_UNIQUE} = IQ ->
Res = IQ#iq{type = result,
sub_el =
@@ -597,6 +605,8 @@ iq_disco_info(ServerHost, Lang) ->
attrs = [{<<"var">>, ?NS_REGISTER}], children = []},
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, ?NS_RSM}], children = []},
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, ?NS_MUCSUB}], children = []},
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, ?NS_VCARD}], children = []}] ++
case gen_mod:is_loaded(ServerHost, mod_mam) of
@@ -693,6 +703,19 @@ get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
index = NewIndex}}
end.
get_subscribed_rooms(ServerHost, Host, From) ->
Rooms = get_rooms(ServerHost, Host),
lists:flatmap(
fun(#muc_room{name_host = {Name, _}, opts = Opts}) ->
Subscribers = proplists:get_value(subscribers, Opts, []),
case lists:keymember(From, 1, Subscribers) of
true -> [jid:make(Name, Host, <<>>)];
false -> []
end;
(_) ->
[]
end, Rooms).
%% @doc Return the position of desired room in the list of rooms.
%% The room must exist in the list. The count starts in 0.
%% @spec (Desired::muc_online_room(), Rooms::[muc_online_room()]) -> integer()
+4 -1
View File
@@ -11,7 +11,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, muc_online_rooms/1,
-export([start/2, stop/1, depends/2, muc_online_rooms/1,
muc_unregister_nick/1, create_room/3, destroy_room/2,
create_rooms_file/1, destroy_rooms_file/1,
rooms_unused_list/2, rooms_unused_destroy/2,
@@ -49,6 +49,9 @@ stop(Host) ->
ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50),
ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, web_page_host, 50).
depends(_Host, _Opts) ->
[{mod_muc, hard}].
%%%
%%% Register commands
%%%
+5 -2
View File
@@ -41,7 +41,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1, opt_type/1]).
mod_opt_type/1, opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -81,7 +81,7 @@ start_link(Host, Opts) ->
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
temporary, 1000, worker, [?MODULE]},
transient, 1000, worker, [?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
@@ -109,6 +109,9 @@ transform_module_options(Opts) ->
Opt
end, Opts).
depends(_Host, _Opts) ->
[{mod_muc, hard}].
%%====================================================================
%% gen_server callbacks
%%====================================================================
+758 -403
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -40,7 +40,7 @@
-export([init/1, handle_info/2, handle_call/3,
handle_cast/2, terminate/2, code_change/3]).
-export([purge_loop/1, mod_opt_type/1]).
-export([purge_loop/1, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -1219,6 +1219,9 @@ stj(String) -> jid:from_string(String).
jts(String) -> jid:to_string(String).
depends(_Host, _Opts) ->
[].
mod_opt_type(access) ->
fun acl:access_rules_validator/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
+4 -2
View File
@@ -66,7 +66,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-deprecated({get_queue_length,2}).
@@ -125,6 +125,8 @@ stop(Host) ->
supervisor:delete_child(ejabberd_sup, Proc),
ok.
depends(_Host, _Opts) ->
[].
%%====================================================================
%% gen_server callbacks
@@ -791,7 +793,7 @@ get_messages_subset(User, Host, MsgsAll) ->
fun(A) when is_atom(A) -> A end,
max_user_offline_messages),
MaxOfflineMsgs = case get_max_user_messages(Access,
{User, Host}, Host)
User, Host)
of
Number when is_integer(Number) -> Number;
_ -> 100
+4 -1
View File
@@ -55,7 +55,7 @@
handle_cast/2, handle_info/2, code_change/3]).
-export([iq_ping/3, user_online/3, user_offline/3,
user_send/4, mod_opt_type/1]).
user_send/4, mod_opt_type/1, depends/2]).
-record(state,
{host = <<"">>,
@@ -253,6 +253,9 @@ cancel_timer(TRef) ->
_ -> ok
end.
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(ping_interval) ->
fun (I) when is_integer(I), I > 0 -> I end;
+4 -1
View File
@@ -28,7 +28,7 @@
-behavior(gen_mod).
-export([start/2, stop/1, check_packet/6,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -48,6 +48,9 @@ stop(Host) ->
?MODULE, check_packet, 25),
ok.
depends(_Host, _Opts) ->
[].
check_packet(_, _User, Server, _PrivacyList,
{From, To, #xmlel{name = Name, attrs = Attrs}}, Dir) ->
case Name of
+4 -1
View File
@@ -36,7 +36,7 @@
check_packet/6, remove_user/2,
is_list_needdb/1, updated_list/3,
item_to_xml/1, get_user_lists/2, import/3,
set_privacy_list/1, mod_opt_type/1]).
set_privacy_list/1, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -593,6 +593,9 @@ import(LServer, DBType, Data) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Data).
depends(_Host, _Opts) ->
[].
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [db_type, iqdisc].
+4 -1
View File
@@ -33,7 +33,7 @@
-export([start/2, stop/1, process_sm_iq/3, import/3,
remove_user/2, get_data/2, export/1, import/1,
mod_opt_type/1, set_data/3]).
mod_opt_type/1, set_data/3, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -173,6 +173,9 @@ import(LServer, DBType, PD) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, PD).
depends(_Host, _Opts) ->
[].
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [db_type, iqdisc].
+4 -1
View File
@@ -39,7 +39,7 @@
%% supervisor callbacks.
-export([init/1]).
-export([start_link/2, mod_opt_type/1]).
-export([start_link/2, mod_opt_type/1, depends/2]).
-define(PROCNAME, ejabberd_mod_proxy65).
@@ -84,6 +84,9 @@ init([Host, Opts]) ->
{{one_for_one, 10, 1},
[StreamManager, StreamSupervisor, Service]}}.
depends(_Host, _Opts) ->
[].
mod_opt_type(auth_type) ->
fun (plain) -> plain;
(anonymous) -> anonymous
+132 -311
View File
@@ -77,7 +77,7 @@
%% API and gen_server callbacks
-export([start_link/2, start/2, stop/1, init/1,
handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
terminate/2, code_change/3, depends/2]).
-export([send_loop/1, mod_opt_type/1]).
@@ -234,11 +234,7 @@ stop(Host) ->
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
-spec(init/1 ::
(
[binary() | [{_,_}],...])
-> {'ok',state()}
).
-spec init([binary() | [{_,_}],...]) -> {'ok',state()}.
init([ServerHost, Opts]) ->
?DEBUG("pubsub init ~p ~p", [ServerHost, Opts]),
@@ -351,6 +347,18 @@ init_send_loop(ServerHost) ->
end,
{Pid, State}.
depends(ServerHost, Opts) ->
Host = gen_mod:get_opt_host(ServerHost, Opts, <<"pubsub.@HOST@">>),
Plugins = gen_mod:get_opt(plugins, Opts,
fun(A) when is_list(A) -> A end, [?STDNODE]),
lists:flatmap(
fun(Name) ->
Plugin = plugin(ServerHost, Name),
try apply(Plugin, depends, [Host, ServerHost, Opts])
catch _:undef -> []
end
end, Plugins).
%% @doc Call the init/1 function for each plugin declared in the config file.
%% The default plugin module is implicit.
%% <p>The Erlang code for the plugin is located in a module called
@@ -410,7 +418,8 @@ send_loop(State) ->
{_, Node} = NodeRec#pubsub_node.nodeid,
Nidx = NodeRec#pubsub_node.id,
Options = NodeRec#pubsub_node.options,
send_items(Host, Node, Nidx, PType, Options, SubJID, last)
[send_items(Host, Node, Nidx, PType, Options, SubJID, last)
|| NodeRec#pubsub_node.type == PType]
end,
lists:usort(Subs))
end,
@@ -480,15 +489,10 @@ send_loop(State) ->
%% disco hooks handling functions
%%
-spec(disco_local_identity/5 ::
(
Acc :: [xmlel()],
_From :: jid(),
To :: jid(),
Node :: <<>> | mod_pubsub:nodeId(),
Lang :: binary())
-> [xmlel()]
).
-spec disco_local_identity(Acc :: [xmlel()], _From :: jid(),
To :: jid(), Node :: <<>> | mod_pubsub:nodeId(),
Lang :: binary()) -> [xmlel()].
disco_local_identity(Acc, _From, To, <<>>, _Lang) ->
case lists:member(?PEPNODE, plugins(host(To#jid.lserver))) of
true ->
@@ -502,15 +506,10 @@ disco_local_identity(Acc, _From, To, <<>>, _Lang) ->
disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
Acc.
-spec(disco_local_features/5 ::
(
Acc :: [xmlel()],
_From :: jid(),
To :: jid(),
Node :: <<>> | mod_pubsub:nodeId(),
Lang :: binary())
-> [binary(),...]
).
-spec disco_local_features(Acc :: [xmlel()], _From :: jid(),
To :: jid(), Node :: <<>> | mod_pubsub:nodeId(),
Lang :: binary()) -> [binary(),...].
disco_local_features(Acc, _From, To, <<>>, _Lang) ->
Host = host(To#jid.lserver),
Feats = case Acc of
@@ -528,15 +527,10 @@ disco_local_items(Acc, _From, _To, _Node, _Lang) -> Acc.
% when is_binary(Node) ->
% disco_sm_identity(Acc, From, To, iolist_to_binary(Node),
% Lang);
-spec(disco_sm_identity/5 ::
(
Acc :: empty | [xmlel()],
From :: jid(),
To :: jid(),
Node :: mod_pubsub:nodeId(),
Lang :: binary())
-> [xmlel()]
).
-spec disco_sm_identity(Acc :: empty | [xmlel()], From :: jid(),
To :: jid(), Node :: mod_pubsub:nodeId(),
Lang :: binary()) -> [xmlel()].
disco_sm_identity(empty, From, To, Node, Lang) ->
disco_sm_identity([], From, To, Node, Lang);
disco_sm_identity(Acc, From, To, Node, _Lang) ->
@@ -571,15 +565,9 @@ disco_identity(Host, Node, From) ->
_ -> []
end.
-spec(disco_sm_features/5 ::
(
Acc :: empty | {result, Features::[Feature::binary()]},
From :: jid(),
To :: jid(),
Node :: mod_pubsub:nodeId(),
Lang :: binary())
-> {result, Features::[Feature::binary()]}
).
-spec disco_sm_features(Acc :: empty | {result, Features::[Feature::binary()]},
From :: jid(), To :: jid(), Node :: mod_pubsub:nodeId(),
Lang :: binary()) -> {result, Features::[Feature::binary()]}.
%disco_sm_features(Acc, From, To, Node, Lang)
% when is_binary(Node) ->
% disco_sm_features(Acc, From, To, iolist_to_binary(Node),
@@ -607,15 +595,9 @@ disco_features(Host, Node, From) ->
_ -> []
end.
-spec(disco_sm_items/5 ::
(
Acc :: empty | {result, [xmlel()]},
From :: jid(),
To :: jid(),
Node :: mod_pubsub:nodeId(),
Lang :: binary())
-> {result, [xmlel()]}
).
-spec disco_sm_items(Acc :: empty | {result, [xmlel()]}, From :: jid(),
To :: jid(), Node :: mod_pubsub:nodeId(),
Lang :: binary()) -> {result, [xmlel()]}.
%disco_sm_items(Acc, From, To, Node, Lang)
% when is_binary(Node) ->
% disco_sm_items(Acc, From, To, iolist_to_binary(Node),
@@ -627,13 +609,8 @@ disco_sm_items({result, OtherItems}, From, To, Node, _Lang) ->
disco_items(jid:tolower(jid:remove_resource(To)), Node, From))};
disco_sm_items(Acc, _From, _To, _Node, _Lang) -> Acc.
-spec(disco_items/3 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
From :: jid())
-> [xmlel()]
).
-spec disco_items(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
From :: jid()) -> [xmlel()].
disco_items(Host, <<>>, From) ->
Action = fun (#pubsub_node{nodeid = {_, Node},
options = Options, type = Type, id = Nidx, owners = O},
@@ -847,12 +824,8 @@ handle_call(stop, _From, State) ->
%% @private
handle_cast(_Msg, State) -> {noreply, State}.
-spec(handle_info/2 ::
(
_ :: {route, From::jid(), To::jid(), Packet::xmlel()},
State :: state())
-> {noreply, state()}
).
-spec handle_info(_ :: {route, From::jid(), To::jid(), Packet::xmlel()},
State :: state()) -> {noreply, state()}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
@@ -935,17 +908,9 @@ terminate(_Reason,
%% @private
code_change(_OldVsn, State, _Extra) -> {ok, State}.
-spec(do_route/7 ::
(
ServerHost :: binary(),
Access :: atom(),
Plugins :: [binary(),...],
Host :: mod_pubsub:hostPubsub(),
From :: jid(),
To :: jid(),
Packet :: xmlel())
-> ok
).
-spec do_route(ServerHost :: binary(), Access :: atom(),
Plugins :: [binary(),...], Host :: mod_pubsub:hostPubsub(),
From :: jid(), To :: jid(), Packet :: xmlel()) -> ok.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -1132,14 +1097,8 @@ iq_disco_info(Host, SNode, From, Lang) ->
node_disco_info(Host, Node, From)
end.
-spec(iq_disco_items/4 ::
(
Host :: mod_pubsub:host(),
Node :: <<>> | mod_pubsub:nodeId(),
From :: jid(),
Rsm :: none | rsm_in())
-> {result, [xmlel()]}
).
-spec iq_disco_items(Host :: mod_pubsub:host(), Node :: <<>> | mod_pubsub:nodeId(),
From :: jid(), Rsm :: none | rsm_in()) -> {result, [xmlel()]}.
iq_disco_items(Host, <<>>, From, _RSM) ->
{result,
lists:map(fun (#pubsub_node{nodeid = {_, SubNode}, options = Options}) ->
@@ -1202,13 +1161,7 @@ iq_disco_items(Host, Item, From, RSM) ->
end
end.
-spec(iq_sm/3 ::
(
From :: jid(),
To :: jid(),
IQ :: iq_request())
-> iq_result() | iq_error()
).
-spec iq_sm(From :: jid(), To :: jid(), IQ :: iq_request()) -> iq_result() | iq_error().
iq_sm(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
ServerHost = To#jid.lserver,
LOwner = jid:tolower(jid:remove_resource(To)),
@@ -1233,36 +1186,17 @@ iq_get_vcard(Lang) ->
<<(translate:translate(Lang, <<"ejabberd Publish-Subscribe module">>))/binary,
"\nCopyright (c) 2004-2016 ProcessOne">>}]}].
-spec(iq_pubsub/6 ::
(
Host :: mod_pubsub:host(),
ServerHost :: binary(),
From :: jid(),
IQType :: 'get' | 'set',
SubEl :: xmlel(),
Lang :: binary())
-> {result, [xmlel()]}
%%%
| {error, xmlel()}
).
-spec iq_pubsub(Host :: mod_pubsub:host(), ServerHost :: binary(), From :: jid(),
IQType :: 'get' | 'set', SubEl :: xmlel(), Lang :: binary()) ->
{result, [xmlel()]} | {error, xmlel()}.
iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang) ->
iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, all, plugins(Host)).
-spec(iq_pubsub/8 ::
(
Host :: mod_pubsub:host(),
ServerHost :: binary(),
From :: jid(),
IQType :: 'get' | 'set',
SubEl :: xmlel(),
Lang :: binary(),
Access :: atom(),
Plugins :: [binary(),...])
-> {result, [xmlel()]}
%%%
| {error, xmlel()}
).
-spec iq_pubsub(Host :: mod_pubsub:host(), ServerHost :: binary(), From :: jid(),
IQType :: 'get' | 'set', SubEl :: xmlel(), Lang :: binary(),
Access :: atom(), Plugins :: [binary(),...]) ->
{result, [xmlel()]} | {error, xmlel()}.
iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
#xmlel{children = SubEls} = SubEl,
@@ -1368,18 +1302,10 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
end.
-spec(iq_pubsub_owner/6 ::
(
Host :: mod_pubsub:host(),
ServerHost :: binary(),
From :: jid(),
IQType :: 'get' | 'set',
SubEl :: xmlel(),
Lang :: binary())
-> {result, [xmlel()]}
%%%
| {error, xmlel()}
).
-spec iq_pubsub_owner(Host :: mod_pubsub:host(), ServerHost :: binary(), From :: jid(),
IQType :: 'get' | 'set', SubEl :: xmlel(), Lang :: binary()) ->
{result, [xmlel()]} | {error, xmlel()}.
iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
#xmlel{children = SubEls} = SubEl,
Action = fxml:remove_cdata(SubEls),
@@ -1654,10 +1580,10 @@ send_authorization_approval(Host, JID, SNode, Subscription) ->
%{S, SID} ->
% [{<<"subscription">>, subscription_to_string(S)},
% {<<"subid">>, SID}];
S ->
S ->
[{<<"subscription">>, subscription_to_string(S)}]
end,
Stanza = event_stanza(<<"subscription">>,
Stanza = event_stanza(<<"subscription">>,
[{<<"jid">>, jid:to_string(JID)}
| nodeAttr(SNode)]
++ SubAttrs),
@@ -1802,33 +1728,17 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
%%<li>nodetree create_node checks if nodeid already exists</li>
%%<li>node plugin create_node just sets default affiliation/subscription</li>
%%</ul>
-spec(create_node/5 ::
(
Host :: mod_pubsub:host(),
ServerHost :: binary(),
Node :: <<>> | mod_pubsub:nodeId(),
Owner :: jid(),
Type :: binary())
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
-spec create_node(Host :: mod_pubsub:host(), ServerHost :: binary(),
Node :: <<>> | mod_pubsub:nodeId(), Owner :: jid(),
Type :: binary()) -> {result, [xmlel(),...]} | {error, xmlel()}.
create_node(Host, ServerHost, Node, Owner, Type) ->
create_node(Host, ServerHost, Node, Owner, Type, all, []).
-spec(create_node/7 ::
(
Host :: mod_pubsub:host(),
ServerHost :: binary(),
Node :: <<>> | mod_pubsub:nodeId(),
Owner :: jid(),
Type :: binary(),
Access :: atom(),
Configuration :: [xmlel()])
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
-spec create_node(Host :: mod_pubsub:host(), ServerHost :: binary(),
Node :: <<>> | mod_pubsub:nodeId(), Owner :: jid(),
Type :: binary(), Access :: atom(), Configuration :: [xmlel()]) ->
{result, [xmlel(),...]} | {error, xmlel()}.
create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
case lists:member(<<"instant-nodes">>, plugin_features(Host, Type)) of
true ->
@@ -1942,15 +1852,8 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
%%<li>The node is the root collection node, which cannot be deleted.</li>
%%<li>The specified node does not exist.</li>
%%</ul>
-spec(delete_node/3 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
Owner :: jid())
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
-spec delete_node(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
Owner :: jid()) -> {result, [xmlel(),...]} | {error, xmlel()}.
delete_node(_Host, <<>>, _Owner) ->
{error, ?ERRT_NOT_ALLOWED(?MYLANG, <<"No node specified">>)};
delete_node(Host, Node, Owner) ->
@@ -2026,17 +1929,9 @@ delete_node(Host, Node, Owner) ->
%%<li>The node does not support subscriptions.</li>
%%<li>The node does not exist.</li>
%%</ul>
-spec(subscribe_node/5 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
From :: jid(),
JID :: binary(),
Configuration :: [xmlel()])
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
-spec subscribe_node(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
From :: jid(), JID :: binary(), Configuration :: [xmlel()]) ->
{result, [xmlel(),...]} | {error, xmlel()}.
subscribe_node(Host, Node, From, JID, Configuration) ->
SubModule = subscription_plugin(Host),
SubOpts = case SubModule:parse_options_xform(Configuration) of
@@ -2145,17 +2040,11 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
%%<li>The node does not exist.</li>
%%<li>The request specifies a subscription ID that is not valid or current.</li>
%%</ul>
-spec(unsubscribe_node/5 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
From :: jid(),
JID :: binary() | ljid(),
SubId :: mod_pubsub:subId())
-> {result, []}
%%%
| {error, xmlel()}
).
-spec unsubscribe_node(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
From :: jid(), JID :: binary() | ljid(),
SubId :: mod_pubsub:subId()) ->
{result, []} | {error, xmlel()}.
unsubscribe_node(Host, Node, From, JID, SubId) when is_binary(JID) ->
unsubscribe_node(Host, Node, From, string_to_ljid(JID), SubId);
unsubscribe_node(Host, Node, From, Subscriber, SubId) ->
@@ -2183,18 +2072,12 @@ unsubscribe_node(Host, Node, From, Subscriber, SubId) ->
%%<li>The item contains more than one payload element or the namespace of the root payload element does not match the configured namespace for the node.</li>
%%<li>The request does not match the node configuration.</li>
%%</ul>
-spec(publish_item/6 ::
(
Host :: mod_pubsub:host(),
ServerHost :: binary(),
Node :: mod_pubsub:nodeId(),
Publisher :: jid(),
ItemId :: <<>> | mod_pubsub:itemId(),
Payload :: mod_pubsub:payload())
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
-spec publish_item(Host :: mod_pubsub:host(), ServerHost :: binary(),
Node :: mod_pubsub:nodeId(), Publisher :: jid(),
ItemId :: <<>> | mod_pubsub:itemId(),
Payload :: mod_pubsub:payload()) ->
{result, [xmlel(),...]} | {error, xmlel()}.
publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, [], all).
publish_item(Host, ServerHost, Node, Publisher, <<>>, Payload, PubOpts, Access) ->
@@ -2319,16 +2202,10 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, PubOpts, Access
%%<li>The node does not support persistent items.</li>
%%<li>The service does not support the deletion of items.</li>
%%</ul>
-spec(delete_item/4 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
Publisher :: jid(),
ItemId :: mod_pubsub:itemId())
-> {result, []}
%%%
| {error, xmlel()}
).
-spec delete_item(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
Publisher :: jid(), ItemId :: mod_pubsub:itemId()) ->
{result, []} | {error, xmlel()}.
delete_item(Host, Node, Publisher, ItemId) ->
delete_item(Host, Node, Publisher, ItemId, false).
delete_item(_, <<>>, _, _, _) ->
@@ -2383,15 +2260,10 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
%%<li>The node is not configured to persist items.</li>
%%<li>The specified node does not exist.</li>
%%</ul>
-spec(purge_node/3 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
Owner :: jid())
-> {result, []}
%%%
| {error, xmlel()}
).
-spec purge_node(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
Owner :: jid()) ->
{result, []} | {error, xmlel()}.
purge_node(Host, Node, Owner) ->
Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) ->
Features = plugin_features(Host, Type),
@@ -2435,19 +2307,12 @@ purge_node(Host, Node, Owner) ->
%% <p>The permission are not checked in this function.</p>
%% @todo We probably need to check that the user doing the query has the right
%% to read the items.
-spec(get_items/7 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
From :: jid(),
SubId :: mod_pubsub:subId(),
SMaxItems :: binary(),
ItemIds :: [mod_pubsub:itemId()],
Rsm :: none | rsm_in())
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
-spec get_items(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
From :: jid(), SubId :: mod_pubsub:subId(),
SMaxItems :: binary(), ItemIds :: [mod_pubsub:itemId()],
Rsm :: none | rsm_in()) ->
{result, [xmlel(),...]} | {error, xmlel()}.
get_items(Host, Node, From, SubId, SMaxItems, ItemIds, RSM) ->
MaxItems = if SMaxItems == <<>> ->
case get_max_items_node(Host) of
@@ -2511,7 +2376,7 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIds, RSM) ->
get_items(Host, Node) ->
Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
node_call(Host, Type, get_items, [Nidx, service_jid(Host), none])
node_call(Host, Type, get_items, [Nidx, service_jid(Host), none])
end,
case transaction(Host, Node, Action, sync_dirty) of
{result, {_, {Items, _}}} -> Items;
@@ -2610,16 +2475,10 @@ dispatch_items(From, To, _Node, Stanza) ->
ejabberd_router:route(service_jid(From), jid:make(To), Stanza).
%% @doc <p>Return the list of affiliations as an XMPP response.</p>
-spec(get_affiliations/4 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
JID :: jid(),
Plugins :: [binary()])
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
-spec get_affiliations(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
JID :: jid(), Plugins :: [binary()]) ->
{result, [xmlel(),...]} | {error, xmlel()}.
get_affiliations(Host, Node, JID, Plugins) when is_list(Plugins) ->
Result = lists:foldl( fun (Type, {Status, Acc}) ->
Features = plugin_features(Host, Type),
@@ -2663,15 +2522,10 @@ get_affiliations(Host, Node, JID, Plugins) when is_list(Plugins) ->
Error
end.
-spec(get_affiliations/3 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
JID :: jid())
-> {result, [xmlel(),...]}
%%%
| {error, xmlel()}
).
-spec get_affiliations(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
JID :: jid()) ->
{result, [xmlel(),...]} | {error, xmlel()}.
get_affiliations(Host, Node, JID) ->
Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
Features = plugin_features(Host, Type),
@@ -2708,16 +2562,10 @@ get_affiliations(Host, Node, JID) ->
Error
end.
-spec(set_affiliations/4 ::
(
Host :: mod_pubsub:host(),
Node :: mod_pubsub:nodeId(),
From :: jid(),
EntitiesEls :: [xmlel()])
-> {result, []}
%%%
| {error, xmlel()}
).
-spec set_affiliations(Host :: mod_pubsub:host(), Node :: mod_pubsub:nodeId(),
From :: jid(), EntitiesEls :: [xmlel()]) ->
{result, []} | {error, xmlel()}.
set_affiliations(Host, Node, From, EntitiesEls) ->
Owner = jid:tolower(jid:remove_resource(From)),
Entities = lists:foldl(fun
@@ -3113,15 +2961,13 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->
end
end.
-spec(get_presence_and_roster_permissions/5 ::
(
Host :: mod_pubsub:host(),
From :: ljid(),
Owners :: [ljid(),...],
AccessModel :: mod_pubsub:accessModel(),
AllowedGroups :: [binary()])
-> {PresenceSubscription::boolean(), RosterGroup::boolean()}
).
-spec get_presence_and_roster_permissions(Host :: mod_pubsub:host(),
From :: ljid(), Owners :: [ljid(),...],
AccessModel :: mod_pubsub:accessModel(),
AllowedGroups :: [binary()]) ->
{PresenceSubscription::boolean(),
RosterGroup::boolean()}.
get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) ->
if (AccessModel == presence) or (AccessModel == roster) ->
case Host of
@@ -3179,11 +3025,7 @@ subscription_to_string(pending) -> <<"pending">>;
subscription_to_string(unconfigured) -> <<"unconfigured">>;
subscription_to_string(_) -> <<"none">>.
-spec(service_jid/1 ::
(
Host :: mod_pubsub:host())
-> jid()
).
-spec service_jid(Host :: mod_pubsub:host()) -> jid().
service_jid(#jid{} = Jid) -> Jid;
service_jid({U, S, R}) -> jid:make(U, S, R);
service_jid(Host) -> jid:make(<<>>, Host, <<>>).
@@ -3217,12 +3059,7 @@ sub_option_can_deliver(_, _, {deliver, false}) -> false;
sub_option_can_deliver(_, _, {expire, When}) -> p1_time_compat:timestamp() < When;
sub_option_can_deliver(_, _, _) -> true.
-spec(presence_can_deliver/2 ::
(
Entity :: ljid(),
_ :: boolean())
-> boolean()
).
-spec presence_can_deliver(Entity :: ljid(), _ :: boolean()) -> boolean().
presence_can_deliver(_, false) ->
true;
presence_can_deliver({User, Server, Resource}, true) ->
@@ -3243,12 +3080,10 @@ presence_can_deliver({User, Server, Resource}, true) ->
false, Ss)
end.
-spec(state_can_deliver/2 ::
(
Entity::ljid(),
SubOptions :: mod_pubsub:subOptions() | [])
-> [ljid()]
).
-spec state_can_deliver(Entity::ljid(),
SubOptions :: mod_pubsub:subOptions() | []) ->
[ljid()].
state_can_deliver({U, S, R}, []) -> [{U, S, R}];
state_can_deliver({U, S, R}, SubOptions) ->
case lists:keysearch(show_values, 1, SubOptions) of
@@ -3268,13 +3103,10 @@ state_can_deliver({U, S, R}, SubOptions) ->
[], Resources)
end.
-spec(get_resource_state/3 ::
(
Entity :: ljid(),
ShowValues :: [binary()],
JIDs :: [ljid()])
-> [ljid()]
).
-spec get_resource_state(Entity :: ljid(), ShowValues :: [binary()],
JIDs :: [ljid()]) ->
[ljid()].
get_resource_state({U, S, R}, ShowValues, JIDs) ->
case ejabberd_sm:get_session_pid(U, S, R) of
none ->
@@ -3293,11 +3125,8 @@ get_resource_state({U, S, R}, ShowValues, JIDs) ->
end
end.
-spec(payload_xmlelements/1 ::
(
Payload :: mod_pubsub:payload())
-> Count :: non_neg_integer()
).
-spec payload_xmlelements(Payload :: mod_pubsub:payload()) ->
Count :: non_neg_integer().
payload_xmlelements(Payload) ->
payload_xmlelements(Payload, 0).
@@ -4040,12 +3869,9 @@ unset_cached_item(Host, Nidx) ->
_ -> ok
end.
-spec(get_cached_item/2 ::
(
Host :: mod_pubsub:host(),
Nidx :: mod_pubsub:nodeIdx())
-> undefined | mod_pubsub:pubsubItem()
).
-spec get_cached_item(Host :: mod_pubsub:host(), Nidx :: mod_pubsub:nodeIdx()) ->
undefined | mod_pubsub:pubsubItem().
get_cached_item({_, ServerHost, _}, Nidx) ->
get_cached_item(ServerHost, Nidx);
get_cached_item(Host, Nidx) ->
@@ -4340,7 +4166,7 @@ string_to_ljid(JID) ->
end
end.
-spec(uniqid/0 :: () -> mod_pubsub:itemId()).
-spec uniqid() -> mod_pubsub:itemId().
uniqid() ->
{T1, T2, T3} = p1_time_compat:timestamp(),
iolist_to_binary(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])).
@@ -4355,12 +4181,7 @@ itemsEls(Items) ->
[#xmlel{name = <<"item">>, attrs = itemAttr(ItemId), children = Payload}
|| #pubsub_item{itemid = {ItemId, _}, payload = Payload} <- Items].
-spec(add_message_type/2 ::
(
Message :: xmlel(),
Type :: atom())
-> xmlel()
).
-spec add_message_type(Message :: xmlel(), Type :: atom()) -> xmlel().
add_message_type(Message, normal) -> Message;
add_message_type(#xmlel{name = <<"message">>, attrs = Attrs, children = Els}, Type) ->
+4 -1
View File
@@ -37,7 +37,7 @@
unauthenticated_iq_register/4, try_register/5,
process_iq/3, send_registration_notifications/3,
transform_options/1, transform_module_options/1,
mod_opt_type/1, opt_type/1]).
mod_opt_type/1, opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -72,6 +72,9 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
?NS_REGISTER).
depends(_Host, _Opts) ->
[].
stream_feature_register(Acc, Host) ->
AF = gen_mod:get_module_opt(Host, ?MODULE, access_from,
fun(A) -> A end,
+4 -1
View File
@@ -55,7 +55,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process/2, mod_opt_type/1]).
-export([start/2, stop/1, process/2, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -76,6 +76,9 @@ start(_Host, _Opts) ->
stop(_Host) -> ok.
depends(_Host, _Opts) ->
[{mod_register, hard}].
%%%----------------------------------------------------------------------
%%% HTTP handlers
%%%----------------------------------------------------------------------
+4 -1
View File
@@ -49,7 +49,7 @@
get_jid_info/4, item_to_xml/1, webadmin_page/3,
webadmin_user/4, get_versioning_feature/2,
roster_versioning_enabled/1, roster_version/2,
mod_opt_type/1, set_roster/1]).
mod_opt_type/1, set_roster/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -136,6 +136,9 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
?NS_ROSTER).
depends(_Host, _Opts) ->
[].
process_iq(From, To, IQ) when ((From#jid.luser == <<"">>) andalso (From#jid.resource == <<"">>)) ->
process_iq_manager(From, To, IQ);
-25
View File
@@ -244,31 +244,6 @@ raw_to_record(LServer,
askmessage = SAskMessage}
end.
record_to_string(#roster{us = {User, _Server},
jid = JID, name = Name, subscription = Subscription,
ask = Ask, askmessage = AskMessage}) ->
Username = ejabberd_sql:escape(User),
SJID =
ejabberd_sql:escape(jid:to_string(jid:tolower(JID))),
Nick = ejabberd_sql:escape(Name),
SSubscription = case Subscription of
both -> <<"B">>;
to -> <<"T">>;
from -> <<"F">>;
none -> <<"N">>
end,
SAsk = case Ask of
subscribe -> <<"S">>;
unsubscribe -> <<"U">>;
both -> <<"B">>;
out -> <<"O">>;
in -> <<"I">>;
none -> <<"N">>
end,
SAskMessage = ejabberd_sql:escape(AskMessage),
[Username, SJID, Nick, SSubscription, SAsk, SAskMessage,
<<"N">>, <<"">>, <<"item">>].
record_to_row(
#roster{us = {LUser, _LServer},
jid = JID, name = Name, subscription = Subscription,
+4 -1
View File
@@ -30,7 +30,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, log_user_send/4,
log_user_receive/5, mod_opt_type/1]).
log_user_receive/5, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -51,6 +51,9 @@ stop(Host) ->
?MODULE, log_user_receive, 50),
ok.
depends(_Host, _Opts) ->
[].
log_user_send(Packet, _C2SState, From, To) ->
log_packet(From, To, Packet, From#jid.lserver),
Packet.
+4 -1
View File
@@ -39,7 +39,7 @@
delete_group/2, get_group_opts/2, set_group_opts/3,
get_group_users/2, get_group_explicit_users/2,
is_user_in_group/3, add_user_to_group/3, opts_to_binary/1,
remove_user_from_group/3, mod_opt_type/1]).
remove_user_from_group/3, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -132,6 +132,9 @@ stop(Host) ->
%%ejabberd_hooks:delete(remove_user, Host,
%% ?MODULE, remove_user, 50),
depends(_Host, _Opts) ->
[].
get_user_roster(Items, US) ->
{U, S} = US,
DisplayedGroups = get_user_displayed_groups(US),
+4 -1
View File
@@ -41,7 +41,7 @@
-export([get_user_roster/2, get_subscription_lists/3,
get_jid_info/4, process_item/2, in_subscription/6,
out_subscription/4, mod_opt_type/1, opt_type/1]).
out_subscription/4, mod_opt_type/1, opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -105,6 +105,9 @@ stop(Host) ->
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
depends(_Host, _Opts) ->
[{mod_roster, hard}].
%%--------------------------------------------------------------------
%% Hooks
%%--------------------------------------------------------------------
+4 -1
View File
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_local_iq/3,
process_sm_iq/3, mod_opt_type/1]).
process_sm_iq/3, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -55,6 +55,9 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
?NS_SIC).
depends(_Host, _Opts) ->
[].
process_local_iq(#jid{user = User, server = Server,
resource = Resource},
_To, #iq{type = get, sub_el = _SubEl} = IQ) ->
+4 -1
View File
@@ -34,7 +34,7 @@
-export([data_in/2, data_out/2, message_in/2,
message_out/2, request/2, request/3, response/2,
locate/1, mod_opt_type/1]).
locate/1, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -62,6 +62,9 @@ start(_Host, _Opts) ->
stop(_Host) ->
ok.
depends(_Host, _Opts) ->
[].
data_in(Data, #sip_socket{type = Transport,
addr = {MyIP, MyPort},
peer = {PeerIP, PeerPort}}) ->
+4 -1
View File
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_local_iq/3,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -48,6 +48,9 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_local, Host,
?NS_STATS).
depends(_Host, _Opts) ->
[].
process_local_iq(_From, To,
#iq{id = _ID, type = Type, xmlns = XMLNS,
sub_el = SubEl, lang = Lang} =
+4 -1
View File
@@ -33,7 +33,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_local_iq/3,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -86,5 +86,8 @@ process_local_iq(_From, _To,
sign(N) when N < 0 -> <<"-">>;
sign(_) -> <<"+">>.
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [iqdisc].
+4 -1
View File
@@ -34,7 +34,7 @@
-export([start/2, init/3, stop/1, get_sm_features/5,
process_local_iq/3, process_sm_iq/3, string2lower/1,
remove_user/2, export/1, import/1, import/3,
remove_user/2, export/1, import/1, import/3, depends/2,
mod_opt_type/1, set_vcard/3, make_vcard_search/4]).
-include("ejabberd.hrl").
@@ -594,6 +594,9 @@ import(LServer, DBType, VCard) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, VCard).
depends(_Host, _Opts) ->
[].
mod_opt_type(allow_return_all) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+4 -1
View File
@@ -40,7 +40,7 @@
-export([start/2, start_link/2, stop/1,
get_sm_features/5, process_local_iq/3, process_sm_iq/3,
remove_user/1, route/4, transform_module_options/1,
mod_opt_type/1, opt_type/1]).
mod_opt_type/1, opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -138,6 +138,9 @@ stop(Host) ->
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
depends(_Host, _Opts) ->
[].
terminate(_Reason, State) ->
Host = State#state.serverhost,
gen_iq_handler:remove_iq_handler(ejabberd_local, Host,
+4 -1
View File
@@ -13,7 +13,7 @@
-export([start/2, stop/1]).
-export([update_presence/3, vcard_set/3, export/1,
import/1, import/3, mod_opt_type/1]).
import/1, import/3, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -46,6 +46,9 @@ stop(Host) ->
vcard_set, 100),
ok.
depends(_Host, _Opts) ->
[].
%%====================================================================
%% Hooks
%%====================================================================
+4 -1
View File
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_local_iq/3,
mod_opt_type/1]).
mod_opt_type/1, depends/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -93,6 +93,9 @@ get_os() ->
#xmlel{name = <<"os">>, attrs = [],
children = [{xmlcdata, OS}]}.
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(show_os) ->
fun (B) when is_boolean(B) -> B end;
+35 -74
View File
@@ -365,21 +365,22 @@ get_entity_subscriptions(Host, Owner) ->
H = encode_host(Host),
SJ = encode_jid(SubKey),
GJ = encode_jid(GenKey),
GJLike = <<(encode_jid_like(GenKey))/binary, "%">>,
GJLike = <<(encode_jid_like(GenKey))/binary, "/%">>,
Query =
case SubKey of
GenKey ->
?SQL("select @(node)s, @(type)s, @(i.nodeid)d,"
" @(jid)s, @(subscriptions)s "
"from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and jid like %(GJLike)s"
" escape '^' and host=%(H)s");
"where i.nodeid = n.nodeid and "
"(jid=%(GJ)s or jid like %(GJLike)s escape '^')"
" and host=%(H)s");
_ ->
?SQL("select @(node)s, @(type)s, @(i.nodeid)d,"
" @(jid)s, @(subscriptions)s "
"from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and jid in"
" (%(SJ)s, %(GJ)s) and host=%(H)s")
"where i.nodeid = n.nodeid and"
" jid in (%(SJ)s, %(GJ)s) and host=%(H)s")
end,
Reply = case catch ejabberd_sql:sql_query_t(Query) of
{selected, RItems} ->
@@ -402,24 +403,20 @@ get_entity_subscriptions(Host, Owner) ->
end,
{result, Reply}.
-spec(get_entity_subscriptions_for_send_last/2 ::
(
Host :: mod_pubsub:hostPubsub(),
Owner :: jid())
-> {result,
[{mod_pubsub:pubsubNode(),
mod_pubsub:subscription(),
mod_pubsub:subId(),
ljid()}]
}
).
-spec get_entity_subscriptions_for_send_last(Host :: mod_pubsub:hostPubsub(),
Owner :: jid()) ->
{result, [{mod_pubsub:pubsubNode(),
mod_pubsub:subscription(),
mod_pubsub:subId(),
ljid()}]}.
get_entity_subscriptions_for_send_last(Host, Owner) ->
SubKey = jid:tolower(Owner),
GenKey = jid:remove_resource(SubKey),
H = encode_host(Host),
SJ = encode_jid(SubKey),
GJ = encode_jid(GenKey),
GJLike = <<(encode_jid_like(GenKey))/binary, "%">>,
GJLike = <<(encode_jid_like(GenKey))/binary, "/%">>,
Query =
case SubKey of
GenKey ->
@@ -427,8 +424,9 @@ get_entity_subscriptions_for_send_last(Host, Owner) ->
" @(jid)s, @(subscriptions)s "
"from pubsub_state i, pubsub_node n, pubsub_node_option o "
"where i.nodeid = n.nodeid and n.nodeid = o.nodeid and name='send_last_published_item' "
"and val='on_sub_and_presence' and jid like %(GJLike)s"
" escape '^' and host=%(H)s");
"and val='on_sub_and_presence' and "
"(jid=%(GJ)s or jid like %(GJLike)s escape '^')"
" and host=%(H)s");
_ ->
?SQL("select @(node)s, @(type)s, @(i.nodeid)d,"
" @(jid)s, @(subscriptions)s "
@@ -611,11 +609,9 @@ get_state(Nidx, JID) ->
{SJID, _} = State#pubsub_state.stateid,
State#pubsub_state{items = itemids(Nidx, SJID)}.
-spec(get_state_without_itemids/2 ::
(Nidx :: mod_pubsub:nodeIdx(),
Key :: ljid()) ->
mod_pubsub:pubsubState()
).
-spec get_state_without_itemids(Nidx :: mod_pubsub:nodeIdx(), Key :: ljid()) ->
mod_pubsub:pubsubState().
get_state_without_itemids(Nidx, JID) ->
J = encode_jid(JID),
case catch
@@ -918,11 +914,13 @@ first_in_list(Pred, [H | T]) ->
end.
itemids(Nidx, {_U, _S, _R} = JID) ->
SJID = <<(ejabberd_sql:escape(encode_jid_like(JID)))/binary, "%">>,
SJID = encode_jid(JID),
SJIDLike = <<(ejabberd_sql:escape(encode_jid_like(JID)))/binary, "/%">>,
case catch
ejabberd_sql:sql_query_t(
?SQL("select @(itemid)s from pubsub_item where "
"nodeid=%(Nidx)d and publisher like %(SJID)s escape '^' "
"nodeid=%(Nidx)d and (publisher=%(SJID)s"
" or publisher like %(SJIDLike)s escape '^') "
"order by modification desc"))
of
{selected, RItems} ->
@@ -973,17 +971,11 @@ update_subscription(Nidx, JID, Subscription) ->
"-affiliation='n'"
]).
-spec(decode_jid/1 ::
( SJID :: binary())
-> ljid()
).
-spec decode_jid(SJID :: binary()) -> ljid().
decode_jid(SJID) ->
jid:tolower(jid:from_string(SJID)).
-spec(decode_affiliation/1 ::
( Arg :: binary())
-> atom()
).
-spec decode_affiliation(Arg :: binary()) -> atom().
decode_affiliation(<<"o">>) -> owner;
decode_affiliation(<<"p">>) -> publisher;
decode_affiliation(<<"u">>) -> publish_only;
@@ -991,19 +983,13 @@ decode_affiliation(<<"m">>) -> member;
decode_affiliation(<<"c">>) -> outcast;
decode_affiliation(_) -> none.
-spec(decode_subscription/1 ::
( Arg :: binary())
-> atom()
).
-spec decode_subscription(Arg :: binary()) -> atom().
decode_subscription(<<"s">>) -> subscribed;
decode_subscription(<<"p">>) -> pending;
decode_subscription(<<"u">>) -> unconfigured;
decode_subscription(_) -> none.
-spec(decode_subscriptions/1 ::
( Subscriptions :: binary())
-> [] | [{atom(), binary()},...]
).
-spec decode_subscriptions(Subscriptions :: binary()) -> [] | [{atom(), binary()},...].
decode_subscriptions(Subscriptions) ->
lists:foldl(fun (Subscription, Acc) ->
case str:tokens(Subscription, <<":">>) of
@@ -1013,36 +999,24 @@ decode_subscriptions(Subscriptions) ->
end,
[], str:tokens(Subscriptions, <<",">>)).
-spec(encode_jid/1 ::
( JID :: ljid())
-> binary()
).
-spec encode_jid(JID :: ljid()) -> binary().
encode_jid(JID) ->
jid:to_string(JID).
-spec(encode_jid_like/1 :: (JID :: ljid()) -> binary()).
-spec encode_jid_like(JID :: ljid()) -> binary().
encode_jid_like(JID) ->
ejabberd_sql:escape_like_arg_circumflex(jid:to_string(JID)).
-spec(encode_host/1 ::
( Host :: host())
-> binary()
).
-spec encode_host(Host :: host()) -> binary().
encode_host({_U, _S, _R} = LJID) -> encode_jid(LJID);
encode_host(Host) -> Host.
-spec(encode_host_like/1 ::
( Host :: host())
-> binary()
).
-spec encode_host_like(Host :: host()) -> binary().
encode_host_like({_U, _S, _R} = LJID) -> ejabberd_sql:escape(encode_jid_like(LJID));
encode_host_like(Host) ->
ejabberd_sql:escape(ejabberd_sql:escape_like_arg_circumflex(Host)).
-spec(encode_affiliation/1 ::
( Arg :: atom())
-> binary()
).
-spec encode_affiliation(Arg :: atom()) -> binary().
encode_affiliation(owner) -> <<"o">>;
encode_affiliation(publisher) -> <<"p">>;
encode_affiliation(publish_only) -> <<"u">>;
@@ -1050,32 +1024,19 @@ encode_affiliation(member) -> <<"m">>;
encode_affiliation(outcast) -> <<"c">>;
encode_affiliation(_) -> <<"n">>.
-spec(encode_subscription/1 ::
( Arg :: atom())
-> binary()
).
-spec encode_subscription(Arg :: atom()) -> binary().
encode_subscription(subscribed) -> <<"s">>;
encode_subscription(pending) -> <<"p">>;
encode_subscription(unconfigured) -> <<"u">>;
encode_subscription(_) -> <<"n">>.
-spec(encode_subscriptions/1 ::
( Subscriptions :: [] | [{atom(), binary()},...])
-> binary()
).
-spec encode_subscriptions(Subscriptions :: [] | [{atom(), binary()},...]) -> binary().
encode_subscriptions(Subscriptions) ->
str:join([<<(encode_subscription(S))/binary, ":", SubId/binary>>
|| {S, SubId} <- Subscriptions], <<",">>).
%%% record getter/setter
state_to_raw(Nidx, State) ->
{JID, _} = State#pubsub_state.stateid,
J = ejabberd_sql:escape(encode_jid(JID)),
A = encode_affiliation(State#pubsub_state.affiliation),
S = encode_subscriptions(State#pubsub_state.subscriptions),
[<<"'">>, Nidx, <<"', '">>, J, <<"', '">>, A, <<"', '">>, S, <<"'">>].
raw_to_item(Nidx, [ItemId, SJID, Creation, Modification, XML]) ->
raw_to_item(Nidx, {ItemId, SJID, Creation, Modification, XML});
raw_to_item(Nidx, {ItemId, SJID, Creation, Modification, XML}) ->
+4 -20
View File
@@ -45,11 +45,13 @@
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]).
path_to_node/1, depends/3]).
depends(_Host, _ServerHost, _Opts) ->
[{mod_caps, hard}].
init(Host, ServerHost, Opts) ->
node_flat:init(Host, ServerHost, Opts),
complain_if_modcaps_disabled(ServerHost),
ok.
terminate(Host, ServerHost) ->
@@ -245,21 +247,3 @@ node_to_path(Node) ->
path_to_node(Path) ->
node_flat:path_to_node(Path).
%%%
%%% Internal
%%%
%% @doc Check mod_caps is enabled, otherwise show warning.
%% The PEP plugin for mod_pubsub requires mod_caps to be enabled in the host.
%% Check that the mod_caps module is enabled in that Jabber Host
%% If not, show a warning message in the ejabberd log file.
complain_if_modcaps_disabled(ServerHost) ->
case gen_mod:is_loaded(ServerHost, mod_caps) of
false ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
"but it does not seems enabled, please check config.",
[ServerHost]);
true -> ok
end.
+4 -20
View File
@@ -45,12 +45,14 @@
get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1,
path_to_node/1, depends/3,
get_entity_subscriptions_for_send_last/2, get_last_items/3]).
depends(_Host, _ServerHost, _Opts) ->
[{mod_caps, hard}].
init(Host, ServerHost, Opts) ->
node_flat_sql:init(Host, ServerHost, Opts),
complain_if_modcaps_disabled(ServerHost),
ok.
terminate(Host, ServerHost) ->
@@ -237,21 +239,3 @@ node_to_path(Node) ->
path_to_node(Path) ->
node_flat_sql:path_to_node(Path).
%%%
%%% Internal
%%%
%% @doc Check mod_caps is enabled, otherwise show warning.
%% The PEP plugin for mod_pubsub requires mod_caps to be enabled in the host.
%% Check that the mod_caps module is enabled in that Jabber Host
%% If not, show a warning message in the ejabberd log file.
complain_if_modcaps_disabled(ServerHost) ->
case gen_mod:is_loaded(ServerHost, mod_caps) of
false ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
"to be enabled, but it isn't.",
[ServerHost]);
true -> ok
end.
+11 -23
View File
@@ -167,12 +167,8 @@ oid(Key, Name) -> {Key, Name}.
%% Key = jlib:jid() | host()
%% Node = string()
-spec(find_node/2 ::
(
Key :: mod_pubsub:hostPubsub(),
Node :: mod_pubsub:nodeId())
-> mod_pubsub:pubsubNode() | false
).
-spec find_node(Key :: mod_pubsub:hostPubsub(), Node :: mod_pubsub:nodeId()) ->
mod_pubsub:pubsubNode() | false.
find_node(Key, Node) ->
case mnesia:read(pubsub_node, oid(Key, Node), read) of
[] -> false;
@@ -188,14 +184,11 @@ find_opt(Key, Default, Options) ->
_ -> Default
end.
-spec(traversal_helper/4 ::
(
Pred :: fun(),
Tr :: fun(),
Host :: mod_pubsub:hostPubsub(),
Nodes :: [mod_pubsub:nodeId(),...])
-> [{Depth::non_neg_integer(), Nodes::[mod_pubsub:pubsubNode(),...]}]
).
-spec traversal_helper(Pred :: fun(), Tr :: fun(), Host :: mod_pubsub:hostPubsub(),
Nodes :: [mod_pubsub:nodeId(),...]) ->
[{Depth::non_neg_integer(),
Nodes::[mod_pubsub:pubsubNode(),...]}].
traversal_helper(Pred, Tr, Host, Nodes) ->
traversal_helper(Pred, Tr, 0, Host, Nodes, []).
@@ -220,15 +213,10 @@ remove_config_parent(Node, [{collection, Parents} | T], Acc) ->
remove_config_parent(Node, [H | T], Acc) ->
remove_config_parent(Node, T, [H | Acc]).
-spec(validate_parentage/3 ::
(
Key :: mod_pubsub:hostPubsub(),
Owners :: [ljid(),...],
Parent_Nodes :: [mod_pubsub:nodeId()])
-> true
%%%
| {error, xmlel()}
).
-spec validate_parentage(Key :: mod_pubsub:hostPubsub(), Owners :: [ljid(),...],
Parent_Nodes :: [mod_pubsub:nodeId()]) ->
true | {error, xmlel()}.
validate_parentage(_Key, _Owners, []) ->
true;
validate_parentage(Key, Owners, [[] | T]) ->
+25 -17
View File
@@ -77,9 +77,9 @@ set_node(Record) when is_record(Record, pubsub_node) ->
catch
ejabberd_sql:sql_query_t(
?SQL("update pubsub_node set"
" host=%(H)s"
" node=%(Node)s"
" parent=%(Parent)s"
" host=%(H)s,"
" node=%(Node)s,"
" parent=%(Parent)s,"
" type=%(Type)s "
"where nodeid=%(OldNidx)d")),
OldNidx;
@@ -191,18 +191,25 @@ get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node).
get_subnodes_tree(Host, Node) ->
H = node_flat_sql:encode_host(Host),
N = <<(ejabberd_sql:escape_like_arg_circumflex(Node))/binary, "%">>,
case catch
ejabberd_sql:sql_query_t(
?SQL("select @(node)s, @(parent)s, @(type)s, @(nodeid)d from "
"pubsub_node where host=%(H)s"
" and node like %(N)s escape '^'"))
of
{selected, RItems} ->
[raw_to_node(Host, Item) || Item <- RItems];
_ ->
[]
case get_node(Host, Node) of
{error, _} ->
[];
Rec ->
H = node_flat_sql:encode_host(Host),
N = <<(ejabberd_sql:escape_like_arg_circumflex(Node))/binary, "/%">>,
Sub = case catch
ejabberd_sql:sql_query_t(
?SQL("select @(node)s, @(parent)s, @(type)s, @(nodeid)d from "
"pubsub_node where host=%(H)s"
" and node like %(N)s escape '^'"
" and \"type\"='hometree'"))
of
{selected, RItems} ->
[raw_to_node(Host, Item) || Item <- RItems];
_ ->
[]
end,
[Rec|Sub]
end.
create_node(Host, Node, Type, Owner, Options, Parents) ->
@@ -252,11 +259,12 @@ create_node(Host, Node, Type, Owner, Options, Parents) ->
delete_node(Host, Node) ->
H = node_flat_sql:encode_host(Host),
N = <<(ejabberd_sql:escape_like_arg_circumflex(Node))/binary, "%">>,
N = <<(ejabberd_sql:escape_like_arg_circumflex(Node))/binary, "/%">>,
Removed = get_subnodes_tree(Host, Node),
catch ejabberd_sql:sql_query_t(
?SQL("delete from pubsub_node where host=%(H)s"
" and node like %(N)s escape '^'")),
" and (node=%(Node)s"
" or (\"type\"='hometree' and node like %(N)s escape '^'))")),
Removed.
%% helpers
+11 -38
View File
@@ -152,13 +152,9 @@ create_table() ->
Other -> Other
end.
-spec(add_subscription/3 ::
(
_JID :: ljid(),
_NodeId :: mod_pubsub:nodeIdx(),
Options :: [] | mod_pubsub:subOptions())
-> SubId :: mod_pubsub:subId()
).
-spec add_subscription(_JID :: ljid(), _NodeId :: mod_pubsub:nodeIdx(),
Options :: [] | mod_pubsub:subOptions()) ->
SubId :: mod_pubsub:subId().
add_subscription(_JID, _NodeId, []) -> make_subid();
add_subscription(_JID, _NodeId, Options) ->
@@ -166,25 +162,13 @@ add_subscription(_JID, _NodeId, Options) ->
mnesia:write(#pubsub_subscription{subid = SubID, options = Options}),
SubID.
-spec(delete_subscription/3 ::
(
_JID :: _,
_NodeId :: _,
SubId :: mod_pubsub:subId())
-> ok
).
-spec delete_subscription(_JID :: _, _NodeId :: _, SubId :: mod_pubsub:subId()) -> ok.
delete_subscription(_JID, _NodeId, SubID) ->
mnesia:delete({pubsub_subscription, SubID}).
-spec(read_subscription/3 ::
(
_JID :: ljid(),
_NodeId :: _,
SubID :: mod_pubsub:subId())
-> mod_pubsub:pubsubSubscription()
| {error, notfound}
).
-spec read_subscription(_JID :: ljid(), _NodeId :: _, SubID :: mod_pubsub:subId()) ->
mod_pubsub:pubsubSubscription() | {error, notfound}.
read_subscription(_JID, _NodeId, SubID) ->
case mnesia:read({pubsub_subscription, SubID}) of
@@ -192,19 +176,13 @@ read_subscription(_JID, _NodeId, SubID) ->
_ -> {error, notfound}
end.
-spec(write_subscription/4 ::
(
_JID :: ljid(),
_NodeId :: _,
SubID :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions())
-> ok
).
-spec write_subscription(_JID :: ljid(), _NodeId :: _, SubID :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions()) -> ok.
write_subscription(_JID, _NodeId, SubID, Options) ->
mnesia:write(#pubsub_subscription{subid = SubID, options = Options}).
-spec(make_subid/0 :: () -> SubId::mod_pubsub:subId()).
-spec make_subid() -> SubId::mod_pubsub:subId().
make_subid() ->
{T1, T2, T3} = p1_time_compat:timestamp(),
iolist_to_binary(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])).
@@ -272,13 +250,8 @@ xopt_to_bool(Option, _) ->
ErrTxt = iolist_to_binary(io_lib:format(Txt, [Option])),
{error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}.
-spec(get_option_xfield/3 ::
(
Lang :: binary(),
Key :: atom(),
Options :: mod_pubsub:subOptions())
-> xmlel()
).
-spec get_option_xfield(Lang :: binary(), Key :: atom(),
Options :: mod_pubsub:subOptions()) -> xmlel().
%% Return a field for an XForm for Key, with data filled in, if
%% applicable, from Options.
+12 -32
View File
@@ -73,54 +73,34 @@
init() -> ok = create_table().
-spec(subscribe_node/3 ::
(
_JID :: _,
_NodeId :: _,
Options :: [] | mod_pubsub:subOptions())
-> {result, mod_pubsub:subId()}
).
-spec subscribe_node(_JID :: _, _NodeId :: _, Options :: [] | mod_pubsub:subOptions()) ->
{result, mod_pubsub:subId()}.
subscribe_node(_JID, _NodeId, Options) ->
SubID = make_subid(),
(?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID, options = Options}),
{result, SubID}.
-spec(unsubscribe_node/3 ::
(
_JID :: _,
_NodeId :: _,
SubID :: mod_pubsub:subId())
-> {result, mod_pubsub:subscription()}
| {error, notfound}
).
-spec unsubscribe_node(_JID :: _, _NodeId :: _, SubID :: mod_pubsub:subId()) ->
{result, mod_pubsub:subscription()} | {error, notfound}.
unsubscribe_node(_JID, _NodeId, SubID) ->
case (?DB_MOD):read_subscription(SubID) of
{ok, Sub} -> (?DB_MOD):delete_subscription(SubID), {result, Sub};
notfound -> {error, notfound}
end.
-spec(get_subscription/3 ::
(
_JID :: _,
_NodeId :: _,
SubId :: mod_pubsub:subId())
-> {result, mod_pubsub:subscription()}
| {error, notfound}
).
-spec get_subscription(_JID :: _, _NodeId :: _, SubId :: mod_pubsub:subId()) ->
{result, mod_pubsub:subscription()} | {error, notfound}.
get_subscription(_JID, _NodeId, SubID) ->
case (?DB_MOD):read_subscription(SubID) of
{ok, Sub} -> {result, Sub};
notfound -> {error, notfound}
end.
-spec(set_subscription/4 ::
(
_JID :: _,
_NodeId :: _,
SubId :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions())
-> {result, ok}
).
-spec set_subscription(_JID :: _, _NodeId :: _, SubId :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions()) -> {result, ok}.
set_subscription(_JID, _NodeId, SubID, Options) ->
case (?DB_MOD):read_subscription(SubID) of
{ok, _} ->
@@ -167,7 +147,7 @@ parse_options_xform(XFields) ->
%%====================================================================
create_table() -> ok.
-spec(make_subid/0 :: () -> mod_pubsub:subId()).
-spec make_subid() -> mod_pubsub:subId().
make_subid() ->
{T1, T2, T3} = p1_time_compat:timestamp(),
iolist_to_binary(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])).
+1
View File
@@ -26,6 +26,7 @@ defmodule ACLTest do
setup_all do
:ok = :mnesia.start
:ok = :jid.start
:stringprep.start
:ok = :ejabberd_config.start(["domain1", "domain2"], [])
:ok = :acl.start
end

Some files were not shown because too many files have changed in this diff Show More