Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a497cdff5e | |||
| 47b17acbaf | |||
| 788049be7b | |||
| 62ea763089 | |||
| 3e7ee6af6d | |||
| b5a09f8b15 | |||
| 5dfd95e5a7 | |||
| 6f2e178de1 | |||
| 137a4ee087 | |||
| b2eb9f197a | |||
| 2a115a83d2 | |||
| 1472bd398a | |||
| c6f48022a4 | |||
| d048d1e619 | |||
| efdf5636be | |||
| 81a7ca7b1e | |||
| 92b9fb30e0 | |||
| c62bd0c911 | |||
| 8a2fa62cac | |||
| 2d22d6061e | |||
| 7c1e7e5b5f | |||
| 1f2b7e8f20 | |||
| 06701b126b | |||
| ff4a00b1f3 | |||
| 97a93263c1 | |||
| 28e28e926a | |||
| 5e467b96a1 | |||
| 53bd0ee818 | |||
| 70606667c6 | |||
| 7d99484859 | |||
| 35c33a8a29 | |||
| 98c172aed6 | |||
| 25f95d6954 | |||
| 4fce1a17d7 | |||
| 1578f17eff | |||
| 87273619a6 | |||
| b5ac0db895 | |||
| 6d25db6600 | |||
| 880114a909 | |||
| 653105a44f | |||
| f938c2a5e2 | |||
| 6a23cad454 | |||
| 6aeb9dcb38 | |||
| 83dd79a6a7 | |||
| 6378c673df | |||
| c7750689e8 | |||
| 41d65f8fe2 | |||
| 24fa95fd60 | |||
| 67d3b50598 |
+3
-1
@@ -34,7 +34,9 @@ before_script:
|
||||
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure --enable-all --disable-odbc --disable-elixir
|
||||
- ./configure --prefix=/tmp/ejabberd --enable-all --disable-odbc --disable-elixir
|
||||
- make
|
||||
- make install
|
||||
- make xref
|
||||
- ERL_LIBS=$PWD make test
|
||||
- grep -q 'TEST COMPLETE, \([[:digit:]]*\) ok, .* of \1 ' logs/raw.log
|
||||
|
||||
+28
-27
@@ -17,6 +17,9 @@ BINDIR = $(DESTDIR)@bindir@
|
||||
# /sbin/
|
||||
SBINDIR = $(DESTDIR)@sbindir@
|
||||
|
||||
# /lib/
|
||||
LIBDIR = $(DESTDIR)@libdir@
|
||||
|
||||
# /lib/ejabberd/
|
||||
EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd
|
||||
|
||||
@@ -109,9 +112,32 @@ spec:
|
||||
$(ERL) -noinput +B -pa ebin -pa deps/*/ebin -eval \
|
||||
'case xml_gen:compile("tools/xmpp_codec.spec") of ok -> halt(0); _ -> halt(1) end.'
|
||||
|
||||
DLLs := $(wildcard deps/*/priv/*.so) $(wildcard deps/*/priv/lib/*.so)
|
||||
TO_DEST=$(foreach path,$(1),$(if $(filter deps/%,$(path)),$(patsubst deps/%,$(LIBDIR)/%,$(path)),$(patsubst %,$(LIBDIR)/ejabberd/%,$(path))))
|
||||
FILTER_DIRS=$(foreach path,$(1),$(if $(wildcard $(path)/*),,$(path)))
|
||||
FILES_WILDCARD=$(call FILTER_DIRS,$(foreach w,$(1),$(wildcard $(w))))
|
||||
|
||||
install: all
|
||||
DEPS_FILES:=$(call FILES_WILDCARD,deps/*/ebin/*.beam deps/*/ebin/*.app deps/*/priv/* deps/*/priv/lib/* deps/*/priv/bin/* deps/*/include/*.hrl)
|
||||
DEPS_FILES_FILTERED:=$(filter-out %/epam,$(DEPS_FILES))
|
||||
DEPS_DIRS:=$(sort deps/ $(wildcard deps/*) $(dir $(DEPS_FILES)))
|
||||
|
||||
MAIN_FILES:=$(filter-out %/configure.beam,$(call FILES_WILDCARD,ebin/*.beam ebin/*.app priv/msgs/*.msg priv/lib/* include/*.hrl))
|
||||
MAIN_DIRS:=$(sort $(dir $(MAIN_FILES)))
|
||||
|
||||
define COPY_template
|
||||
$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; $$(INSTALL) -m 644 $(1) $(call TO_DEST,$(1))
|
||||
endef
|
||||
|
||||
$(foreach file,$(DEPS_FILES_FILTERED) $(MAIN_FILES),$(eval $(call COPY_template,$(file))))
|
||||
|
||||
$(call TO_DEST,$(MAIN_DIRS) $(DEPS_DIRS)):
|
||||
$(INSTALL) -d $@
|
||||
|
||||
$(call TO_DEST,deps/p1_pam/priv/bin/epam): $(LIBDIR)/%: deps/% $(call TO_DEST,deps/p1_pam/priv/bin/)
|
||||
$(INSTALL) -m 750 $(O_USER) $< $@
|
||||
|
||||
copy-files: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES))
|
||||
|
||||
install: all copy-files
|
||||
#
|
||||
# Configuration files
|
||||
$(INSTALL) -d -m 750 $(G_USER) $(ETCDIR)
|
||||
@@ -146,41 +172,16 @@ install: all
|
||||
-e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \
|
||||
> ejabberd.init
|
||||
chmod 755 ejabberd.init
|
||||
#
|
||||
# Binary Erlang files
|
||||
$(INSTALL) -d $(BEAMDIR)
|
||||
$(INSTALL) -m 644 ebin/*.app $(BEAMDIR)
|
||||
$(INSTALL) -m 644 ebin/*.beam $(BEAMDIR)
|
||||
$(INSTALL) -m 644 deps/*/ebin/*.app $(BEAMDIR)
|
||||
$(INSTALL) -m 644 deps/*/ebin/*.beam $(BEAMDIR)
|
||||
# Install Elixir and Elixir dependancies
|
||||
-$(INSTALL) -m 644 deps/*/lib/*/ebin/*.app $(BEAMDIR)
|
||||
-$(INSTALL) -m 644 deps/*/lib/*/ebin/*.beam $(BEAMDIR)
|
||||
rm -f $(BEAMDIR)/configure.beam
|
||||
#
|
||||
# ejabberd header files
|
||||
$(INSTALL) -d $(INCLUDEDIR)
|
||||
$(INSTALL) -m 644 include/*.hrl $(INCLUDEDIR)
|
||||
$(INSTALL) -m 644 deps/*/include/*.hrl $(INCLUDEDIR)
|
||||
#
|
||||
# Binary C programs
|
||||
$(INSTALL) -d $(PBINDIR)
|
||||
$(INSTALL) -m 750 $(O_USER) tools/captcha.sh $(PBINDIR)
|
||||
$(INSTALL) -m 750 $(O_USER) tools/joincluster $(PBINDIR)
|
||||
$(INSTALL) -m 750 $(O_USER) tools/leavecluster $(PBINDIR)
|
||||
[ -f deps/p1_pam/priv/bin/epam ] \
|
||||
&& $(INSTALL) -m 750 $(O_USER) deps/p1_pam/priv/bin/epam $(PBINDIR) \
|
||||
|| true
|
||||
#
|
||||
# Binary system libraries
|
||||
$(INSTALL) -d $(SODIR)
|
||||
$(INSTALL) -m 644 $(DLLs) $(SODIR)
|
||||
[ -f $(SODIR)/jiffy.so ] && (cd $(PRIVDIR); ln -s lib/jiffy.so; true) || true
|
||||
[ -f $(SODIR)/sqlite3_drv.so ] && (cd $(PRIVDIR); ln -s lib/sqlite3_drv.so; true) || true
|
||||
#
|
||||
# Translated strings
|
||||
$(INSTALL) -d $(MSGSDIR)
|
||||
$(INSTALL) -m 644 priv/msgs/*.msg $(MSGSDIR)
|
||||
#
|
||||
# Copy lite.sql
|
||||
[ -d deps/sqlite3 ] && $(INSTALL) -d $(SQLDIR) || true
|
||||
|
||||
@@ -273,5 +273,8 @@ AC_SUBST(iconv)
|
||||
AC_SUBST(debug)
|
||||
AC_SUBST(lager)
|
||||
AC_SUBST(tools)
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CPPFLAGS)
|
||||
AC_SUBST(LDFLAGS)
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -169,6 +169,18 @@
|
||||
#
|
||||
#CONTRIB_MODULES_PATH=/opt/ejabberd-modules
|
||||
|
||||
#.
|
||||
#' CONTRIB_MODULES_CONF_DIR: configuration directory for contributed modules
|
||||
#
|
||||
# Specify the full path to the configuration directory for contributed ejabberd
|
||||
# modules. In order to configure a module named mod_foo, a mod_foo.yml file can
|
||||
# be created in this directory. This file will then be used instead of the
|
||||
# default configuration file provided with the module.
|
||||
#
|
||||
# Default: $CONTRIB_MODULES_PATH/conf
|
||||
#
|
||||
#CONTRIB_MODULES_CONF_DIR=/etc/ejabberd/modules
|
||||
|
||||
#.
|
||||
#'
|
||||
# vim: foldmarker=#',#. foldmethod=marker:
|
||||
|
||||
+3
-13
@@ -15,6 +15,7 @@ ERL={{erl}}
|
||||
IEX={{bindir}}/iex
|
||||
EPMD={{bindir}}/epmd
|
||||
INSTALLUSER={{installuser}}
|
||||
ERL_LIBS={{libdir}}
|
||||
|
||||
# check the proper system user is used if defined
|
||||
if [ "$INSTALLUSER" != "" ] ; then
|
||||
@@ -87,24 +88,14 @@ if [ "{{release}}" != "true" ] ; then
|
||||
if [ "$EJABBERDDIR" = "" ] ; then
|
||||
EJABBERDDIR={{libdir}}/ejabberd
|
||||
fi
|
||||
if [ "$EJABBERD_EBIN_PATH" = "" ] ; then
|
||||
EJABBERD_EBIN_PATH=$EJABBERDDIR/ebin
|
||||
fi
|
||||
if [ "$EJABBERD_PRIV_PATH" = "" ] ; then
|
||||
EJABBERD_PRIV_PATH=$EJABBERDDIR/priv
|
||||
fi
|
||||
if [ "$EJABBERD_BIN_PATH" = "" ] ; then
|
||||
EJABBERD_BIN_PATH=$EJABBERD_PRIV_PATH/bin
|
||||
fi
|
||||
if [ "$EJABBERD_SO_PATH" = "" ] ; then
|
||||
EJABBERD_SO_PATH=$EJABBERD_PRIV_PATH/lib
|
||||
fi
|
||||
if [ "$EJABBERD_MSGS_PATH" = "" ] ; then
|
||||
EJABBERD_MSGS_PATH=$EJABBERD_PRIV_PATH/msgs
|
||||
fi
|
||||
fi
|
||||
EJABBERD_LOG_PATH=$LOGS_DIR/ejabberd.log
|
||||
SASL_LOG_PATH=$LOGS_DIR/erlang.log
|
||||
DATETIME=`date "+%Y%m%d-%H%M%S"`
|
||||
ERL_CRASH_DUMP=$LOGS_DIR/erl_crash_$DATETIME.dump
|
||||
ERL_INETRC=$ETC_DIR/inetrc
|
||||
@@ -155,9 +146,7 @@ cd $SPOOL_DIR
|
||||
|
||||
# export global variables
|
||||
export EJABBERD_CONFIG_PATH
|
||||
export EJABBERD_MSGS_PATH
|
||||
export EJABBERD_LOG_PATH
|
||||
export EJABBERD_SO_PATH
|
||||
export EJABBERD_BIN_PATH
|
||||
export EJABBERD_DOC_PATH
|
||||
export EJABBERD_PID_PATH
|
||||
@@ -167,6 +156,8 @@ export ERL_INETRC
|
||||
export ERL_MAX_PORTS
|
||||
export ERL_MAX_ETS_TABLES
|
||||
export CONTRIB_MODULES_PATH
|
||||
export CONTRIB_MODULES_CONF_DIR
|
||||
export ERL_LIBS
|
||||
|
||||
# start server
|
||||
start()
|
||||
@@ -180,7 +171,6 @@ start()
|
||||
$KERNEL_OPTS \
|
||||
$EJABBERD_OPTS \
|
||||
-s ejabberd \
|
||||
-sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \
|
||||
$ERLANG_OPTS $ARGS \"$@\""
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(request,
|
||||
{method, % :: method(),
|
||||
{method :: method(),
|
||||
path = [] :: [binary()],
|
||||
q = [] :: [{binary() | nokey, binary()}],
|
||||
us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
@@ -30,11 +30,10 @@
|
||||
ip :: {inet:ip_address(), inet:port_number()},
|
||||
host = <<"">> :: binary(),
|
||||
port = 5280 :: inet:port_number(),
|
||||
tp = http, % :: protocol(),
|
||||
opts = [] :: list(),
|
||||
tp = http :: protocol(),
|
||||
headers = [] :: [{atom() | binary(), binary()}]}).
|
||||
|
||||
|
||||
-record(ws,
|
||||
{socket :: inet:socket() | p1_tls:tls_socket(),
|
||||
sockmod = gen_tcp :: gen_tcp | p1_tls,
|
||||
@@ -47,3 +46,7 @@
|
||||
q = [] :: [{binary() | nokey, binary()}],
|
||||
buf :: binary(),
|
||||
http_opts = [] :: list()}).
|
||||
|
||||
-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE'.
|
||||
-type protocol() :: http | https.
|
||||
-type http_request() :: #request{}.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
-record(offline_msg,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
timestamp = now() :: erlang:timestamp() | '_',
|
||||
expire = now() :: erlang:timestamp() | never | '_',
|
||||
from = #jid{} :: jid() | '_',
|
||||
to = #jid{} :: jid() | '_',
|
||||
packet = #xmlel{} :: xmlel() | '_'}).
|
||||
|
||||
-record(state,
|
||||
{host = <<"">> :: binary(),
|
||||
access_max_offline_messages}).
|
||||
@@ -152,4 +152,7 @@
|
||||
-define(NS_CLIENT_STATE, <<"urn:xmpp:csi:0">>).
|
||||
-define(NS_STREAM_MGMT_2, <<"urn:xmpp:sm:2">>).
|
||||
-define(NS_STREAM_MGMT_3, <<"urn:xmpp:sm:3">>).
|
||||
-define(NS_HTTP_UPLOAD, <<"urn:xmpp:http:upload">>).
|
||||
-define(NS_HTTP_UPLOAD_OLD, <<"eu:siacs:conversations:http:upload">>).
|
||||
-define(NS_THUMBS_1, <<"urn:xmpp:thumbs:1">>).
|
||||
-define(NS_NICK, <<"http://jabber.org/protocol/nick">>).
|
||||
|
||||
@@ -17,8 +17,10 @@ defmodule Ejabberd.Mixfile do
|
||||
def application do
|
||||
[mod: {:ejabberd_app, []},
|
||||
applications: [:ssl],
|
||||
included_applications: [:p1_logger,:p1_yaml,:p1_tls,:p1_xml,:p1_stringprep,:p1_zlib,:p1_cache_tab,:mnesia,:p1_utils,
|
||||
:p1_iconv,:esip,:p1_stun,:ehyperloglog,:p1_mysql,:p1_pgsql,:eredis]]
|
||||
included_applications: [:p1_logger, :p1_yaml, :p1_tls, :p1_xml,
|
||||
:p1_stringprep, :p1_zlib, :p1_cache_tab,
|
||||
:mnesia, :p1_utils, :p1_iconv, :esip, :p1_stun,
|
||||
:p1_mysql, :p1_pgsql, :eredis, :oauth2, :xmlrpc]]
|
||||
end
|
||||
|
||||
defp erlc_options do
|
||||
@@ -39,10 +41,11 @@ defmodule Ejabberd.Mixfile do
|
||||
{:p1_iconv, git: "https://github.com/processone/eiconv"},
|
||||
{:esip, git: "https://github.com/processone/p1_sip"},
|
||||
{:p1_stun, git: "https://github.com/processone/stun"},
|
||||
{:ehyperloglog, git: "https://github.com/vaxelfel/eHyperLogLog"},
|
||||
{:p1_mysql, git: "https://github.com/processone/mysql"},
|
||||
{:p1_pgsql, git: "https://github.com/processone/pgsql"},
|
||||
{:eredis, git: "https://github.com/wooga/eredis"},
|
||||
{:oauth2, git: "https://github.com/prefiks/oauth2.git"},
|
||||
{:xmlrpc, git: "https://github.com/rds13/xmlrpc.git"},
|
||||
{:exrm, "~> 0.19.2"}]
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
%{"bbmustache": {:hex, :bbmustache, "1.0.3"},
|
||||
"conform": {:hex, :conform, "0.17.0"},
|
||||
"eredis": {:git, "https://github.com/wooga/eredis", "bf12ecb30253c84a2331f4f0d93fd68856fcb9f4", []},
|
||||
"erlware_commons": {:hex, :erlware_commons, "0.15.0"},
|
||||
"esip": {:git, "https://github.com/processone/p1_sip", "d662d3fe7f6288b444ea321d854de0bd6d40e022", []},
|
||||
"exrm": {:hex, :exrm, "0.19.9"},
|
||||
"getopt": {:hex, :getopt, "0.8.2"},
|
||||
"neotoma": {:hex, :neotoma, "1.7.3"},
|
||||
"oauth2": {:git, "https://github.com/prefiks/oauth2.git", "e6da9912e5d8f658e7e868f41a102d085bdbef59", []},
|
||||
"p1_cache_tab": {:git, "https://github.com/processone/cache_tab", "f7ea12b0ba962a3d2f9a406d2954cf7de4e27230", []},
|
||||
"p1_iconv": {:git, "https://github.com/processone/eiconv", "8b7542b1aaf0a851f335e464956956985af6d9a2", []},
|
||||
"p1_logger": {:git, "https://github.com/processone/p1_logger", "3e19507fd5606a73694917158767ecb3f5704e3f", []},
|
||||
"p1_mysql": {:git, "https://github.com/processone/mysql", "dfa87da95f8fdb92e270741c2a53f796b682f918", []},
|
||||
"p1_pgsql": {:git, "https://github.com/processone/pgsql", "e72c03c60bfcb56bbb5d259342021d9cb3581dac", []},
|
||||
"p1_stringprep": {:git, "https://github.com/processone/stringprep", "3c640237a3a7831dc39de6a6d329d3a9af25c579", []},
|
||||
"p1_stun": {:git, "https://github.com/processone/stun", "061bdae484268cbf0457ad4797e74b8516df3ad1", []},
|
||||
"p1_tls": {:git, "https://github.com/processone/tls", "e56321afd974e9da33da913cd31beebc8e73e75f", []},
|
||||
"p1_utils": {:git, "https://github.com/processone/p1_utils", "7aa022f995e3be006224a25405cd431483e7d026", []},
|
||||
"p1_xml": {:git, "https://github.com/processone/xml", "403066ea5e0b2cd89e65c3ed1a329789c02c3588", []},
|
||||
"p1_yaml": {:git, "https://github.com/processone/p1_yaml", "79f756ba73a235c4d3836ec07b5f7f2b55f49638", []},
|
||||
"p1_zlib": {:git, "https://github.com/processone/zlib", "e3d4222b7aae616d7ef2e7e2fa0bbf451516c602", []},
|
||||
"providers": {:hex, :providers, "1.4.1"},
|
||||
"relx": {:hex, :relx, "3.5.0"},
|
||||
"xmlrpc": {:git, "https://github.com/rds13/xmlrpc.git", "42e6e96a0fe7106830274feed915125feb1056f3", []}}
|
||||
+29
-24
@@ -54,22 +54,27 @@ SrcDirs = lists:foldl(
|
||||
Acc
|
||||
end, [], Cfg),
|
||||
|
||||
Deps = [{p1_cache_tab, ".*", {git, "https://github.com/processone/cache_tab"}},
|
||||
{p1_tls, ".*", {git, "https://github.com/processone/tls"}},
|
||||
{p1_stringprep, ".*", {git, "https://github.com/processone/stringprep"}},
|
||||
{p1_xml, ".*", {git, "https://github.com/processone/xml"}},
|
||||
{esip, ".*", {git, "https://github.com/processone/p1_sip"}},
|
||||
{p1_stun, ".*", {git, "https://github.com/processone/stun"}},
|
||||
{p1_yaml, ".*", {git, "https://github.com/processone/p1_yaml"}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils"}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy"}},
|
||||
{oauth2, ".*", {git, "https://github.com/prefiks/oauth2.git"}},
|
||||
{xmlrpc, ".*", {git, "https://github.com/rds13/xmlrpc.git"}}],
|
||||
Deps = [{p1_cache_tab, ".*", {git, "https://github.com/processone/cache_tab", "f7ea12b0ba962a3d2f9a406d2954cf7de4e27230"}},
|
||||
{p1_tls, ".*", {git, "https://github.com/processone/tls", "e56321afd974e9da33da913cd31beebc8e73e75f"}},
|
||||
{p1_stringprep, ".*", {git, "https://github.com/processone/stringprep", "3c640237a3a7831dc39de6a6d329d3a9af25c579"}},
|
||||
{p1_xml, ".*", {git, "https://github.com/processone/xml", "1c8b016b0ac7986efb823baf1682a43565449e65"}},
|
||||
{esip, ".*", {git, "https://github.com/processone/p1_sip", "d662d3fe7f6288b444ea321d854de0bd6d40e022"}},
|
||||
{p1_stun, ".*", {git, "https://github.com/processone/stun", "061bdae484268cbf0457ad4797e74b8516df3ad1"}},
|
||||
{p1_yaml, ".*", {git, "https://github.com/processone/p1_yaml", "79f756ba73a235c4d3836ec07b5f7f2b55f49638"}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", "d7800881e6702723ce58b7646b60c9e4cd25d563"}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", "cfc61a2e952dc3182e0f9b1473467563699992e2"}},
|
||||
{oauth2, ".*", {git, "https://github.com/prefiks/oauth2.git", "e6da9912e5d8f658e7e868f41a102d085bdbef59"}},
|
||||
{xmlrpc, ".*", {git, "https://github.com/rds13/xmlrpc.git", "42e6e96a0fe7106830274feed915125feb1056f3"}}],
|
||||
|
||||
CFLags = proplists:get_value(cflags, Cfg, ""),
|
||||
CPPFLags = proplists:get_value(cppflags, Cfg, ""),
|
||||
LDFLags = proplists:get_value(ldflags, Cfg, ""),
|
||||
|
||||
ConfigureCmd = fun(Pkg, Flags) ->
|
||||
{'get-deps',
|
||||
"sh -c 'cd deps/" ++ Pkg ++
|
||||
" && ./configure" ++ Flags ++ "'"}
|
||||
" && CFLAGS=\""++ CFLags ++"\" CPPFLAGS=\""++ CPPFLags ++"\" LDFLAGS=\""++ LDFLags ++"\"" ++
|
||||
" ./configure" ++ Flags ++ "'"}
|
||||
end,
|
||||
|
||||
XMLFlags = lists:foldl(
|
||||
@@ -89,30 +94,30 @@ PostHooks = [ConfigureCmd("p1_tls", ""),
|
||||
|
||||
CfgDeps = lists:flatmap(
|
||||
fun({mysql, true}) ->
|
||||
[{p1_mysql, ".*", {git, "https://github.com/processone/mysql"}}];
|
||||
[{p1_mysql, ".*", {git, "https://github.com/processone/mysql", "dfa87da95f8fdb92e270741c2a53f796b682f918"}}];
|
||||
({pgsql, true}) ->
|
||||
[{p1_pgsql, ".*", {git, "https://github.com/processone/pgsql"}}];
|
||||
[{p1_pgsql, ".*", {git, "https://github.com/processone/pgsql", "e72c03c60bfcb56bbb5d259342021d9cb3581dac"}}];
|
||||
({sqlite, true}) ->
|
||||
[{sqlite3, ".*", {git, "https://github.com/alexeyr/erlang-sqlite3"}}];
|
||||
[{sqlite3, ".*", {git, "https://github.com/alexeyr/erlang-sqlite3", "8350dc603804c503f99c92bfd2eab1dd6885758e"}}];
|
||||
({pam, true}) ->
|
||||
[{p1_pam, ".*", {git, "https://github.com/processone/epam"}}];
|
||||
[{p1_pam, ".*", {git, "https://github.com/processone/epam", "d3ce290b7da75d780a03e86e7a8198a80e9826a6"}}];
|
||||
({zlib, true}) ->
|
||||
[{p1_zlib, ".*", {git, "https://github.com/processone/zlib"}}];
|
||||
[{p1_zlib, ".*", {git, "https://github.com/processone/zlib", "e3d4222b7aae616d7ef2e7e2fa0bbf451516c602"}}];
|
||||
({riak, true}) ->
|
||||
[{riakc, ".*", {git, "https://github.com/basho/riak-erlang-client", {tag, "1.4.2"}}}];
|
||||
({elixir, true}) ->
|
||||
[{rebar_elixir_plugin, ".*", {git, "https://github.com/yrashk/rebar_elixir_plugin"}},
|
||||
{elixir, ".*", {git, "https://github.com/elixir-lang/elixir", {branch, "v1.0"}}}];
|
||||
[{rebar_elixir_plugin, ".*", {git, "https://github.com/yrashk/rebar_elixir_plugin", "7058379b7c7e017555647f6b9cecfd87cd50f884"}},
|
||||
{elixir, ".*", {git, "https://github.com/elixir-lang/elixir", "1d9548fd285d243721b7eba71912bde2ffd1f6c3"}}];
|
||||
({iconv, true}) ->
|
||||
[{p1_iconv, ".*", {git, "https://github.com/processone/eiconv"}}];
|
||||
[{p1_iconv, ".*", {git, "https://github.com/processone/eiconv", "8b7542b1aaf0a851f335e464956956985af6d9a2"}}];
|
||||
({lager, true}) ->
|
||||
[{lager, ".*", {git, "https://github.com/basho/lager"}}];
|
||||
[{lager, ".*", {git, "https://github.com/basho/lager", "4d2ec8c701e1fa2d386f92f2b83b23faf8608ac3"}}];
|
||||
({lager, false}) ->
|
||||
[{p1_logger, ".*", {git, "https://github.com/processone/p1_logger"}}];
|
||||
[{p1_logger, ".*", {git, "https://github.com/processone/p1_logger", "3e19507fd5606a73694917158767ecb3f5704e3f"}}];
|
||||
({tools, true}) ->
|
||||
[{meck, "0.*", {git, "https://github.com/eproxus/meck"}}];
|
||||
[{meck, "0.*", {git, "https://github.com/eproxus/meck", "0845277398b8326f9dddddd9fc3cf73467ba6877"}}];
|
||||
({redis, true}) ->
|
||||
[{eredis, ".*", {git, "https://github.com/wooga/eredis"}}];
|
||||
[{eredis, ".*", {git, "https://github.com/wooga/eredis", "bf12ecb30253c84a2331f4f0d93fd68856fcb9f4"}}];
|
||||
(_) ->
|
||||
[]
|
||||
end, Cfg),
|
||||
|
||||
+14
-19
@@ -98,7 +98,7 @@ to_record(Host, ACLName, ACLSpec) ->
|
||||
-spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}.
|
||||
|
||||
add(Host, ACLName, ACLSpec) ->
|
||||
{ResL, BadNodes} = rpc:multicall(mnesia:system_info(running_db_nodes),
|
||||
{ResL, BadNodes} = ejabberd_cluster:multicall(
|
||||
?MODULE, add_local,
|
||||
[Host, ACLName, ACLSpec]),
|
||||
case lists:keyfind(aborted, 1, ResL) of
|
||||
@@ -125,7 +125,7 @@ add_local(Host, ACLName, ACLSpec) ->
|
||||
-spec add_list(binary(), [acl()], boolean()) -> ok | {error, any()}.
|
||||
|
||||
add_list(Host, ACLs, Clear) ->
|
||||
{ResL, BadNodes} = rpc:multicall(mnesia:system_info(running_db_nodes),
|
||||
{ResL, BadNodes} = ejabberd_cluster:multicall(
|
||||
?MODULE, add_list_local,
|
||||
[Host, ACLs, Clear]),
|
||||
case lists:keyfind(aborted, 1, ResL) of
|
||||
@@ -167,16 +167,12 @@ add_list_local(Host, ACLs, Clear) ->
|
||||
access_name(), [access_rule()]) -> ok | {error, any()}.
|
||||
|
||||
add_access(Host, Access, Rules) ->
|
||||
case mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:write(
|
||||
#access{name = {Access, Host},
|
||||
rules = Rules})
|
||||
end) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
Err ->
|
||||
{error, Err}
|
||||
Obj = #access{name = {Access, Host}, rules = Rules},
|
||||
case mnesia:transaction(fun() -> mnesia:write(Obj) end) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
Err ->
|
||||
{error, Err}
|
||||
end.
|
||||
|
||||
-spec load_from_config() -> ok.
|
||||
@@ -239,8 +235,7 @@ normalize_spec(Spec) ->
|
||||
{server_regexp, SR} -> {server_regexp, b(SR)};
|
||||
{server_glob, S} -> {server_glob, b(S)};
|
||||
{resource_glob, R} -> {resource_glob, b(R)};
|
||||
{ip, {Net, Mask}} ->
|
||||
{ip, {Net, Mask}};
|
||||
{ip, {Net, Mask}} -> {ip, {Net, Mask}};
|
||||
{ip, S} ->
|
||||
case parse_ip_netmask(b(S)) of
|
||||
{ok, Net, Mask} ->
|
||||
@@ -298,9 +293,7 @@ match_acl(ACL, IP, Host) when tuple_size(IP) == 4;
|
||||
is_ip_match(IP, Net, Mask);
|
||||
(_) ->
|
||||
false
|
||||
end,
|
||||
ets:lookup(acl, {ACL, Host}) ++
|
||||
ets:lookup(acl, {ACL, global}));
|
||||
end, get_aclspecs(ACL, Host));
|
||||
match_acl(ACL, JID, Host) ->
|
||||
{User, Server, Resource} = jlib:jid_tolower(JID),
|
||||
lists:any(
|
||||
@@ -350,8 +343,10 @@ match_acl(ACL, JID, Host) ->
|
||||
false
|
||||
end
|
||||
end,
|
||||
ets:lookup(acl, {ACL, Host}) ++
|
||||
ets:lookup(acl, {ACL, global})).
|
||||
get_aclspecs(ACL, Host)).
|
||||
|
||||
get_aclspecs(ACL, Host) ->
|
||||
ets:lookup(acl, {ACL, Host}) ++ ets:lookup(acl, {ACL, global}).
|
||||
|
||||
is_regexp_match(String, RegExp) ->
|
||||
case ejabberd_regexp:run(String, RegExp) of
|
||||
|
||||
+11
-16
@@ -27,11 +27,11 @@
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/6, mech_step/2, parse/1]).
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {host, is_user_exists}).
|
||||
-record(state, {host}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"X-OAUTH2">>, ?MODULE, plain),
|
||||
@@ -39,25 +39,20 @@ start(_Opts) ->
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest,
|
||||
IsUserExists, _ClientCertFile) ->
|
||||
{ok, #state{host = Host, is_user_exists = IsUserExists}}.
|
||||
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{host = Host}}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case prepare(ClientIn) of
|
||||
[AuthzId, User, Token] ->
|
||||
case (State#state.is_user_exists)(User) of
|
||||
case ejabberd_oauth:check_token(
|
||||
User, State#state.host, <<"sasl_auth">>, Token) of
|
||||
true ->
|
||||
case ejabberd_oauth:check_token(
|
||||
User, State#state.host, <<"sasl_auth">>, Token) of
|
||||
true ->
|
||||
{ok,
|
||||
[{username, User}, {authzid, AuthzId},
|
||||
{auth_module, ejabberd_oauth}]};
|
||||
false ->
|
||||
{error, <<"not-authorized">>, User}
|
||||
end;
|
||||
_ -> {error, <<"not-authorized">>, User}
|
||||
{ok,
|
||||
[{username, User}, {authzid, AuthzId},
|
||||
{auth_module, ejabberd_oauth}]};
|
||||
false ->
|
||||
{error, <<"not-authorized">>, User}
|
||||
end;
|
||||
_ -> {error, <<"bad-protocol">>}
|
||||
end.
|
||||
|
||||
@@ -462,7 +462,7 @@ restore_mnesia(Path) ->
|
||||
|
||||
%% Mnesia database restore
|
||||
%% This function is called from ejabberd_ctl, ejabberd_web_admin and
|
||||
%% mod_configure/adhoc
|
||||
%% mod_configure/adhoc
|
||||
restore(Path) ->
|
||||
mnesia:restore(Path, [{keep_tables,keep_tables()},
|
||||
{default_op, skip_tables}]).
|
||||
@@ -477,7 +477,7 @@ keep_tables() ->
|
||||
|
||||
%% Returns the list of modules tables in use, according to the list of actually
|
||||
%% loaded modules
|
||||
keep_modules_tables() ->
|
||||
keep_modules_tables() ->
|
||||
lists:map(fun(Module) -> module_tables(Module) end,
|
||||
gen_mod:loaded_modules(?MYNAME)).
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
-callback get_password_s(binary(), binary()) -> binary() | {binary(), binary(), binary(), integer()}.
|
||||
|
||||
start() ->
|
||||
%% This is only executed by ejabberd_c2s for non-SASL auth client
|
||||
%% This is only executed by ejabberd_c2s for non-SASL auth client
|
||||
lists:foreach(fun (Host) ->
|
||||
lists:foreach(fun (M) -> M:start(Host) end,
|
||||
auth_modules(Host))
|
||||
@@ -187,7 +187,8 @@ try_register(User, Server, Password) ->
|
||||
case is_user_exists(User, Server) of
|
||||
true -> {atomic, exists};
|
||||
false ->
|
||||
case lists:member(jlib:nameprep(Server), ?MYHOSTS) of
|
||||
LServer = jlib:nameprep(Server),
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
Res = lists:foldl(fun (_M, {atomic, ok} = Res) -> Res;
|
||||
(M, _) ->
|
||||
|
||||
@@ -281,7 +281,6 @@ is_fresh_enough(TimeStampLast, CacheTime) ->
|
||||
get_last_access(User, Server) ->
|
||||
case ejabberd_sm:get_user_resources(User, Server) of
|
||||
[] ->
|
||||
%% _US = {User, Server},
|
||||
case get_last_info(User, Server) of
|
||||
mod_last_required -> mod_last_required;
|
||||
not_found -> never;
|
||||
|
||||
@@ -83,7 +83,7 @@ check_password(User, Server, Password) ->
|
||||
#scram{storedkey = StoredKey,
|
||||
serverkey = ServerKey,
|
||||
salt = Salt,
|
||||
iterationcount = jlib:binary_to_integer(
|
||||
iterationcount = binary_to_integer(
|
||||
IterationCount)},
|
||||
is_password_scram_valid(Password, Scram);
|
||||
{selected, [<<"password">>, <<"serverkey">>,
|
||||
@@ -168,7 +168,7 @@ set_password(User, Server, Password) ->
|
||||
ejabberd_odbc:escape(Scram#scram.storedkey),
|
||||
ejabberd_odbc:escape(Scram#scram.serverkey),
|
||||
ejabberd_odbc:escape(Scram#scram.salt),
|
||||
jlib:integer_to_binary(Scram#scram.iterationcount)
|
||||
integer_to_binary(Scram#scram.iterationcount)
|
||||
)
|
||||
of
|
||||
{atomic, ok} -> ok;
|
||||
@@ -204,7 +204,7 @@ try_register(User, Server, Password) ->
|
||||
ejabberd_odbc:escape(Scram#scram.storedkey),
|
||||
ejabberd_odbc:escape(Scram#scram.serverkey),
|
||||
ejabberd_odbc:escape(Scram#scram.salt),
|
||||
jlib:integer_to_binary(Scram#scram.iterationcount)
|
||||
integer_to_binary(Scram#scram.iterationcount)
|
||||
) of
|
||||
{updated, 1} -> {atomic, ok};
|
||||
_ -> {atomic, exists}
|
||||
@@ -278,7 +278,7 @@ get_password(User, Server) ->
|
||||
{jlib:decode_base64(StoredKey),
|
||||
jlib:decode_base64(ServerKey),
|
||||
jlib:decode_base64(Salt),
|
||||
jlib:binary_to_integer(IterationCount)};
|
||||
binary_to_integer(IterationCount)};
|
||||
_ -> false
|
||||
end;
|
||||
false ->
|
||||
@@ -439,7 +439,7 @@ convert_to_scram(Server) ->
|
||||
case ejabberd_odbc:sql_query_t(
|
||||
[<<"select username, password from users where "
|
||||
"iterationcount=0 limit ">>,
|
||||
jlib:integer_to_binary(?BATCH_SIZE),
|
||||
integer_to_binary(?BATCH_SIZE),
|
||||
<<";">>]) of
|
||||
{selected, [<<"username">>, <<"password">>], []} ->
|
||||
ok;
|
||||
@@ -453,7 +453,7 @@ convert_to_scram(Server) ->
|
||||
ejabberd_odbc:escape(Scram#scram.storedkey),
|
||||
ejabberd_odbc:escape(Scram#scram.serverkey),
|
||||
ejabberd_odbc:escape(Scram#scram.salt),
|
||||
jlib:integer_to_binary(Scram#scram.iterationcount)
|
||||
integer_to_binary(Scram#scram.iterationcount)
|
||||
)
|
||||
end, Rs),
|
||||
continue;
|
||||
|
||||
+363
-415
File diff suppressed because it is too large
Load Diff
@@ -75,13 +75,6 @@
|
||||
tref :: reference(),
|
||||
args :: any()}).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [],
|
||||
[]).
|
||||
@@ -276,12 +269,6 @@ create_captcha_x(SID, To, Lang, Limiter, HeadEls,
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
%% @spec (Id::string(), Lang::string()) -> {FormEl, {ImgEl, TextEl, IdEl, KeyEl}} | captcha_not_found
|
||||
%% where FormEl = xmlelement()
|
||||
%% ImgEl = xmlelement()
|
||||
%% TextEl = xmlelement()
|
||||
%% IdEl = xmlelement()
|
||||
%% KeyEl = xmlelement()
|
||||
-spec build_captcha_html(binary(), binary()) -> captcha_not_found |
|
||||
{xmlel(),
|
||||
{xmlel(), xmlel(),
|
||||
@@ -330,12 +317,6 @@ build_captcha_html(Id, Lang) ->
|
||||
_ -> captcha_not_found
|
||||
end.
|
||||
|
||||
%% @spec (Id::string(), ProvidedKey::string()) -> captcha_valid | captcha_non_valid | captcha_not_found
|
||||
-spec check_captcha(binary(), binary()) -> captcha_not_found |
|
||||
captcha_valid |
|
||||
captcha_non_valid.
|
||||
|
||||
|
||||
-spec process_reply(xmlel()) -> ok | {error, bad_match | not_found | malformed}.
|
||||
|
||||
process_reply(#xmlel{} = El) ->
|
||||
@@ -405,9 +386,6 @@ process(_Handlers,
|
||||
process(_Handlers, _Request) ->
|
||||
ejabberd_web:error(not_found).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
init([]) ->
|
||||
mnesia:delete_table(captcha),
|
||||
ets:new(captcha,
|
||||
@@ -454,16 +432,6 @@ terminate(_Reason, _State) -> ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: create_image() -> {ok, Type, Key, Image} | {error, Reason}
|
||||
%% Type = "image/png" | "image/jpeg" | "image/gif"
|
||||
%% Key = string()
|
||||
%% Image = binary()
|
||||
%% Reason = atom()
|
||||
%%--------------------------------------------------------------------
|
||||
create_image() -> create_image(undefined).
|
||||
|
||||
create_image(Limiter) ->
|
||||
@@ -596,12 +564,6 @@ is_limited(Limiter) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: cmd(Cmd) -> Data | {error, Reason}
|
||||
%% Cmd = string()
|
||||
%% Data = binary()
|
||||
%% Description: os:cmd/1 replacement
|
||||
%%--------------------------------------------------------------------
|
||||
-define(CMD_TIMEOUT, 5000).
|
||||
|
||||
-define(MAX_FILE_SIZE, 64 * 1024).
|
||||
@@ -663,6 +625,10 @@ lookup_captcha(Id) ->
|
||||
_ -> {error, enoent}
|
||||
end.
|
||||
|
||||
-spec check_captcha(binary(), binary()) -> captcha_not_found |
|
||||
captcha_valid |
|
||||
captcha_non_valid.
|
||||
|
||||
check_captcha(Id, ProvidedKey) ->
|
||||
case ets:lookup(captcha, Id) of
|
||||
[#captcha{pid = Pid, args = Args, key = ValidKey,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_cluster.erl
|
||||
%%% Author : Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% Purpose : Ejabberd clustering management
|
||||
%%% Created : 7 Oct 2015 by Christophe Romain <christophe.romain@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2015 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.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_cluster).
|
||||
|
||||
%% API
|
||||
-export([get_nodes/0, call/4, multicall/3, multicall/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-spec get_nodes() -> [node()].
|
||||
|
||||
get_nodes() ->
|
||||
mnesia:system_info(running_db_nodes).
|
||||
|
||||
-spec call(node(), module(), atom(), [any()]) -> any().
|
||||
|
||||
call(Node, Module, Function, Args) ->
|
||||
rpc:call(Node, Module, Function, Args, 5000).
|
||||
|
||||
-spec multicall(module(), atom(), [any()]) -> {list(), [node()]}.
|
||||
|
||||
multicall(Module, Function, Args) ->
|
||||
multicall(get_nodes(), Module, Function, Args).
|
||||
|
||||
-spec multicall([node()], module(), atom(), list()) -> {list(), [node()]}.
|
||||
|
||||
multicall(Nodes, Module, Function, Args) ->
|
||||
rpc:multicall(Nodes, Module, Function, Args, 5000).
|
||||
|
||||
+21
-11
@@ -226,6 +226,7 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-define(POLICY_ACCESS, '$policy').
|
||||
|
||||
init() ->
|
||||
ets:new(ejabberd_commands, [named_table, set, public,
|
||||
@@ -298,7 +299,8 @@ get_command_format(Name, Auth) ->
|
||||
case Matched of
|
||||
[] ->
|
||||
{error, command_unknown};
|
||||
[[Args, Result, user]] when Admin ->
|
||||
[[Args, Result, user]] when Admin;
|
||||
Auth == noauth ->
|
||||
{[{user, binary}, {server, binary} | Args], Result};
|
||||
[[Args, Result, _]] ->
|
||||
{Args, Result}
|
||||
@@ -326,7 +328,7 @@ execute_command(Name, Arguments) ->
|
||||
) -> any().
|
||||
|
||||
%% @spec (AccessCommands, Auth, Name::atom(), Arguments) -> ResultTerm | {error, Error}
|
||||
%% where
|
||||
%% where
|
||||
%% AccessCommands = [{Access, CommandNames, Arguments}]
|
||||
%% Auth = {User::string(), Server::string(), Password::string(), Admin::boolean()}
|
||||
%% | noauth
|
||||
@@ -362,6 +364,9 @@ execute_command2(
|
||||
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]).
|
||||
@@ -414,7 +419,7 @@ get_tags_commands() ->
|
||||
%% -----------------------------
|
||||
|
||||
%% @spec (AccessCommands, Auth, Method, Command, Arguments) -> ok
|
||||
%% where
|
||||
%% where
|
||||
%% AccessCommands = [ {Access, CommandNames, Arguments} ]
|
||||
%% Auth = {User::string(), Server::string(), Password::string()} | noauth
|
||||
%% Method = atom()
|
||||
@@ -428,7 +433,9 @@ check_access_commands([], _Auth, _Method, _Command, _Arguments) ->
|
||||
check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) ->
|
||||
Command =
|
||||
case {Command1#ejabberd_commands.policy, Auth} of
|
||||
{user, admin} ->
|
||||
{user, {_, _, _}} ->
|
||||
Command1;
|
||||
{user, _} ->
|
||||
Command1#ejabberd_commands{
|
||||
args = [{user, binary}, {server, binary} |
|
||||
Command1#ejabberd_commands.args]};
|
||||
@@ -479,11 +486,11 @@ check_auth(Command, {User, Server, {oauth, Token}, _}) ->
|
||||
check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
|
||||
%% Check the account exists and password is valid
|
||||
case ejabberd_auth:check_password(User, Server, Password) of
|
||||
true -> {ok, User, Server};
|
||||
_ -> throw({error, invalid_account_data})
|
||||
true -> {ok, User, Server};
|
||||
_ -> throw({error, invalid_account_data})
|
||||
end.
|
||||
|
||||
check_access(Command, all, _)
|
||||
check_access(Command, ?POLICY_ACCESS, _)
|
||||
when Command#ejabberd_commands.policy == open ->
|
||||
true;
|
||||
check_access(_Command, _Access, admin) ->
|
||||
@@ -491,7 +498,8 @@ check_access(_Command, _Access, admin) ->
|
||||
check_access(_Command, _Access, {_User, _Server, _, true}) ->
|
||||
false;
|
||||
check_access(Command, Access, Auth)
|
||||
when Command#ejabberd_commands.policy == open;
|
||||
when Access =/= ?POLICY_ACCESS;
|
||||
Command#ejabberd_commands.policy == open;
|
||||
Command#ejabberd_commands.policy == user ->
|
||||
case check_auth(Command, Auth) of
|
||||
{ok, User, Server} ->
|
||||
@@ -502,6 +510,8 @@ check_access(Command, Access, Auth)
|
||||
check_access(_Command, _Access, _Auth) ->
|
||||
false.
|
||||
|
||||
check_access2(?POLICY_ACCESS, _User, _Server) ->
|
||||
true;
|
||||
check_access2(Access, User, Server) ->
|
||||
%% Check this user has access permission
|
||||
case acl:match_rule(Server, Access, jlib:make_jid(User, Server, <<"">>)) of
|
||||
@@ -528,16 +538,16 @@ check_access_arguments(Command, ArgumentRestrictions, Arguments) ->
|
||||
|
||||
tag_arguments(ArgsDefs, Args) ->
|
||||
lists:zipwith(
|
||||
fun({ArgName, _ArgType}, ArgValue) ->
|
||||
fun({ArgName, _ArgType}, ArgValue) ->
|
||||
{ArgName, ArgValue}
|
||||
end,
|
||||
end,
|
||||
ArgsDefs,
|
||||
Args).
|
||||
|
||||
|
||||
get_access_commands(undefined) ->
|
||||
Cmds = get_commands(),
|
||||
[{all, Cmds, []}];
|
||||
[{?POLICY_ACCESS, Cmds, []}];
|
||||
get_access_commands(AccessCommands) ->
|
||||
AccessCommands.
|
||||
|
||||
|
||||
+30
-12
@@ -120,7 +120,12 @@ read_file(File) ->
|
||||
{include_modules_configs, true}]).
|
||||
|
||||
read_file(File, Opts) ->
|
||||
Terms = get_plain_terms_file(File, Opts),
|
||||
Terms1 = get_plain_terms_file(File, Opts),
|
||||
Terms_macros = case proplists:get_bool(replace_macros, Opts) of
|
||||
true -> replace_macros(Terms1);
|
||||
false -> Terms1
|
||||
end,
|
||||
Terms = transform_terms(Terms_macros),
|
||||
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
|
||||
{Head, Tail} = lists:partition(
|
||||
fun({host_config, _}) -> false;
|
||||
@@ -187,7 +192,6 @@ env_binary_to_list(Application, Parameter) ->
|
||||
%% Returns a list of plain terms,
|
||||
%% in which the options 'include_config_file' were parsed
|
||||
%% and the terms in those files were included.
|
||||
%% @spec(string()) -> [term()]
|
||||
%% @spec(iolist()) -> [term()]
|
||||
get_plain_terms_file(File) ->
|
||||
get_plain_terms_file(File, [{include_files, true}]).
|
||||
@@ -209,16 +213,11 @@ get_plain_terms_file(File1, Opts) ->
|
||||
[]
|
||||
end,
|
||||
BinTerms = BinTerms1 ++ [{include_config_file, list_to_binary(V)} || V <- ModInc],
|
||||
BinTerms2 = case proplists:get_bool(replace_macros, Opts) of
|
||||
true -> replace_macros(BinTerms);
|
||||
false -> BinTerms
|
||||
end,
|
||||
BinTerms3 = transform_terms(BinTerms2),
|
||||
case proplists:get_bool(include_files, Opts) of
|
||||
true ->
|
||||
include_config_files(BinTerms3);
|
||||
include_config_files(BinTerms);
|
||||
false ->
|
||||
BinTerms3
|
||||
BinTerms
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG(Reason, []),
|
||||
@@ -373,8 +372,27 @@ exit_or_halt(ExitText) ->
|
||||
|
||||
get_config_option_key(Name, Val) ->
|
||||
if Name == listen ->
|
||||
[{Key, _, _}] = ejabberd_listener:validate_cfg([Val]),
|
||||
Key;
|
||||
case Val of
|
||||
{{Port, IP, Trans}, _Mod, _Opts} ->
|
||||
{Port, IP, Trans};
|
||||
{{Port, Trans}, _Mod, _Opts} when Trans == tcp; Trans == udp ->
|
||||
{Port, {0,0,0,0}, Trans};
|
||||
{{Port, IP}, _Mod, _Opts} ->
|
||||
{Port, IP, tcp};
|
||||
{Port, _Mod, _Opts} ->
|
||||
{Port, {0,0,0,0}, tcp};
|
||||
V when is_list(V) ->
|
||||
lists:foldl(
|
||||
fun({port, Port}, {_, IP, T}) ->
|
||||
{Port, IP, T};
|
||||
({ip, IP}, {Port, _, T}) ->
|
||||
{Port, IP, T};
|
||||
({transport, T}, {Port, IP, _}) ->
|
||||
{Port, IP, T};
|
||||
(_, Res) ->
|
||||
Res
|
||||
end, {5222, {0,0,0,0}, tcp}, Val)
|
||||
end;
|
||||
is_tuple(Val) ->
|
||||
element(1, Val);
|
||||
true ->
|
||||
@@ -390,7 +408,6 @@ maps_to_lists(IMap) ->
|
||||
[{Name, Val} | Res]
|
||||
end, [], IMap).
|
||||
|
||||
|
||||
merge_configs(Terms, ResMap) ->
|
||||
lists:foldl(fun({Name, Val}, Map) when is_list(Val) ->
|
||||
Old = maps:get(Name, Map, #{}),
|
||||
@@ -834,6 +851,7 @@ replace_module(mod_roster_odbc) -> {mod_roster, odbc};
|
||||
replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, odbc};
|
||||
replace_module(mod_vcard_odbc) -> {mod_vcard, odbc};
|
||||
replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, odbc};
|
||||
replace_module(mod_pubsub_odbc) -> {mod_pubsub, odbc};
|
||||
replace_module(Module) ->
|
||||
case is_elixir_module(Module) of
|
||||
true -> expand_elixir_module(Module);
|
||||
|
||||
@@ -80,7 +80,7 @@ start() ->
|
||||
end
|
||||
end,
|
||||
Node = list_to_atom(SNode1),
|
||||
Status = case rpc:call(Node, ?MODULE, process, [Args]) of
|
||||
Status = case ejabberd_cluster:call(Node, ?MODULE, process, [Args]) of
|
||||
{badrpc, Reason} ->
|
||||
print("Failed RPC connection to the node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
@@ -209,7 +209,7 @@ process(Args) ->
|
||||
|
||||
%% @spec (Args::[string()], AccessCommands) -> {String::string(), Code::integer()}
|
||||
process2(["--auth", User, Server, Pass | Args], AccessCommands) ->
|
||||
process2(Args, {list_to_binary(User), list_to_binary(Server), list_to_binary(Pass)}, AccessCommands);
|
||||
process2(Args, {list_to_binary(User), list_to_binary(Server), list_to_binary(Pass), true}, AccessCommands);
|
||||
process2(Args, AccessCommands) ->
|
||||
process2(Args, admin, AccessCommands).
|
||||
|
||||
@@ -392,7 +392,10 @@ format_result(ElementsTuple, {_Name, {tuple, ElementsDef}}) ->
|
||||
fun({Element, ElementDef}) ->
|
||||
["\t" | format_result(Element, ElementDef)]
|
||||
end,
|
||||
ElementsAndDef)].
|
||||
ElementsAndDef)];
|
||||
|
||||
format_result(404, {_Name, _}) ->
|
||||
make_status(not_found).
|
||||
|
||||
make_status(ok) -> ?STATUS_SUCCESS;
|
||||
make_status(true) -> ?STATUS_SUCCESS;
|
||||
|
||||
@@ -58,10 +58,6 @@
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link(Module, SockMod, Socket, Opts, Receiver) ->
|
||||
gen_server:start_link(?MODULE,
|
||||
[Module, SockMod, Socket, Opts, Receiver], []).
|
||||
@@ -137,18 +133,10 @@ peername(_FsmRef) ->
|
||||
%%gen_server:call(FsmRef, peername).
|
||||
{ok, {{0, 0, 0, 0}, 0}}.
|
||||
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([Module, SockMod, Socket, Opts, Receiver]) ->
|
||||
Node = ejabberd_node_groups:get_closest_node(backend),
|
||||
{SockMod2, Socket2} = check_starttls(SockMod, Socket, Receiver, Opts),
|
||||
@@ -159,15 +147,6 @@ init([Module, SockMod, Socket, Opts, Receiver]) ->
|
||||
socket = Socket2,
|
||||
receiver = Receiver}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call({starttls, TLSOpts}, _From, State) ->
|
||||
{ok, TLSSocket} = p1_tls:tcp_to_tls(State#state.socket, TLSOpts),
|
||||
ejabberd_receiver:starttls(State#state.receiver, TLSSocket),
|
||||
@@ -184,7 +163,6 @@ handle_call({starttls, TLSOpts, Data}, _From, State) ->
|
||||
{reply, Reply,
|
||||
State#state{socket = TLSSocket, sockmod = p1_tls},
|
||||
?HIBERNATE_TIMEOUT};
|
||||
|
||||
handle_call({compress, Data}, _From, State) ->
|
||||
{ok, ZlibSocket} =
|
||||
ejabberd_receiver:compress(State#state.receiver, Data),
|
||||
@@ -238,21 +216,9 @@ handle_call(peername, _From, State) ->
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info(timeout, State) ->
|
||||
proc_lib:hibernate(gen_server, enter_loop,
|
||||
[?MODULE, [], State]),
|
||||
@@ -260,31 +226,16 @@ handle_info(timeout, State) ->
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
check_starttls(SockMod, Socket, Receiver, Opts) ->
|
||||
TLSEnabled = proplists:get_bool(tls, Opts),
|
||||
TLSOpts = lists:filter(fun({certfile, _}) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
if
|
||||
TLSEnabled ->
|
||||
if TLSEnabled ->
|
||||
{ok, TLSSocket} = p1_tls:tcp_to_tls(Socket, TLSOpts),
|
||||
ejabberd_receiver:starttls(Receiver, TLSSocket),
|
||||
{p1_tls, TLSSocket};
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
%% Timeout of 5 seconds in calls to distributed hooks
|
||||
-define(TIMEOUT_DISTRIBUTED_HOOK, 5000).
|
||||
|
||||
-record(state, {}).
|
||||
-type local_hook() :: { Seq :: integer(), Module :: atom(), Function :: atom()}.
|
||||
-type distributed_hook() :: { Seq :: integer(), Node :: atom(), Module :: atom(), Function :: atom()}.
|
||||
@@ -310,7 +307,7 @@ run1([], _Hook, _Args) ->
|
||||
%% It is not attempted again in case of failure. Next hook will be executed
|
||||
run1([{_Seq, Node, Module, Function} | Ls], Hook, Args) ->
|
||||
%% MR: Should we have a safe rpc, like we have a safe apply or is bad_rpc enough ?
|
||||
case rpc:call(Node, Module, Function, Args, ?TIMEOUT_DISTRIBUTED_HOOK) of
|
||||
case ejabberd_cluster:call(Node, Module, Function, Args) of
|
||||
timeout ->
|
||||
?ERROR_MSG("Timeout on RPC to ~p~nrunning hook: ~p",
|
||||
[Node, {Hook, Args}]),
|
||||
@@ -344,7 +341,7 @@ run1([{_Seq, Module, Function} | Ls], Hook, Args) ->
|
||||
run_fold1([], _Hook, Val, _Args) ->
|
||||
Val;
|
||||
run_fold1([{_Seq, Node, Module, Function} | Ls], Hook, Val, Args) ->
|
||||
case rpc:call(Node, Module, Function, [Val | Args], ?TIMEOUT_DISTRIBUTED_HOOK) of
|
||||
case ejabberd_cluster:call(Node, Module, Function, [Val | Args]) of
|
||||
{badrpc, Reason} ->
|
||||
?ERROR_MSG("Bad RPC error to ~p: ~p~nrunning hook: ~p",
|
||||
[Node, Reason, {Hook, Args}]),
|
||||
|
||||
+33
-45
@@ -166,7 +166,8 @@ init({SockMod, Socket}, Opts) ->
|
||||
{error, _} -> State
|
||||
end.
|
||||
|
||||
become_controller(_Pid) -> ok.
|
||||
become_controller(_Pid) ->
|
||||
ok.
|
||||
|
||||
socket_type() ->
|
||||
raw.
|
||||
@@ -201,22 +202,20 @@ parse_headers(#state{request_method = Method,
|
||||
trail = Data} =
|
||||
State) ->
|
||||
PktType = case Method of
|
||||
undefined -> http_bin;
|
||||
_ -> httph_bin
|
||||
end,
|
||||
undefined -> http_bin;
|
||||
_ -> httph_bin
|
||||
end,
|
||||
case erlang:decode_packet(PktType, Data, []) of
|
||||
{ok, Pkt, Rest} ->
|
||||
NewState = process_header(State#state{trail = Rest}, {ok, Pkt}),
|
||||
{ok, Pkt, Rest} ->
|
||||
NewState = process_header(State#state{trail = Rest}, {ok, Pkt}),
|
||||
case NewState#state.end_of_request of
|
||||
true ->
|
||||
ok;
|
||||
_ ->
|
||||
parse_headers(NewState)
|
||||
true -> ok;
|
||||
_ -> parse_headers(NewState)
|
||||
end;
|
||||
{more, _} ->
|
||||
receive_headers(State#state{trail = Data});
|
||||
_ ->
|
||||
ok
|
||||
{more, _} ->
|
||||
receive_headers(State#state{trail = Data});
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
process_header(State, Data) ->
|
||||
@@ -266,10 +265,8 @@ process_header(State, Data) ->
|
||||
State#state{request_host = Host,
|
||||
request_headers = add_header(Name, Host, State)};
|
||||
{ok, {http_header, _, Name, _, Value}} when is_binary(Name) ->
|
||||
State#state{request_headers =
|
||||
add_header(normalize_header_name(Name),
|
||||
Value,
|
||||
State)};
|
||||
State#state{request_headers =
|
||||
add_header(normalize_header_name(Name), Value, State)};
|
||||
{ok, {http_header, _, Name, _, Value}} ->
|
||||
State#state{request_headers =
|
||||
add_header(Name, Value, State)};
|
||||
@@ -322,14 +319,6 @@ get_host_really_served(Default, Provided) ->
|
||||
false -> Default
|
||||
end.
|
||||
|
||||
%% @spec (SockMod, HostPort) -> {Host::string(), Port::integer(), TP}
|
||||
%% where
|
||||
%% SockMod = gen_tcp | tls
|
||||
%% HostPort = string()
|
||||
%% TP = http | https
|
||||
%% @doc Given a socket and hostport header, return data of transfer protocol.
|
||||
%% Note that HostPort can be a string of a host like "example.org",
|
||||
%% or a string of a host and port like "example.org:5280".
|
||||
get_transfer_protocol(SockMod, HostPort) ->
|
||||
[Host | PortList] = str:tokens(HostPort, <<":">>),
|
||||
case {SockMod, PortList} of
|
||||
@@ -416,17 +405,17 @@ extract_path_query(_State) ->
|
||||
false.
|
||||
|
||||
process_request(#state{request_method = Method,
|
||||
request_auth = Auth,
|
||||
request_lang = Lang,
|
||||
sockmod = SockMod,
|
||||
socket = Socket,
|
||||
options = Options,
|
||||
request_host = Host,
|
||||
request_port = Port,
|
||||
request_tp = TP,
|
||||
request_headers = RequestHeaders,
|
||||
request_handlers = RequestHandlers,
|
||||
trail = Trail} = State) ->
|
||||
request_auth = Auth,
|
||||
request_lang = Lang,
|
||||
sockmod = SockMod,
|
||||
socket = Socket,
|
||||
options = Options,
|
||||
request_host = Host,
|
||||
request_port = Port,
|
||||
request_tp = TP,
|
||||
request_headers = RequestHeaders,
|
||||
request_handlers = RequestHandlers,
|
||||
trail = Trail} = State) ->
|
||||
case extract_path_query(State) of
|
||||
false ->
|
||||
make_bad_request(State);
|
||||
@@ -476,7 +465,6 @@ process_request(#state{request_method = Method,
|
||||
end.
|
||||
|
||||
make_bad_request(State) ->
|
||||
%% Support for X-Forwarded-From
|
||||
make_xhtml_output(State, 400, [],
|
||||
ejabberd_web:make_xhtml([#xmlel{name = <<"h1">>,
|
||||
attrs = [],
|
||||
@@ -532,13 +520,13 @@ recv_data(State, Len, Acc) ->
|
||||
|
||||
make_xhtml_output(State, Status, Headers, XHTML) ->
|
||||
Data = case lists:member(html, Headers) of
|
||||
true ->
|
||||
iolist_to_binary([?HTML_DOCTYPE,
|
||||
xml:element_to_binary(XHTML)]);
|
||||
_ ->
|
||||
iolist_to_binary([?XHTML_DOCTYPE,
|
||||
xml:element_to_binary(XHTML)])
|
||||
end,
|
||||
true ->
|
||||
iolist_to_binary([?HTML_DOCTYPE,
|
||||
xml:element_to_binary(XHTML)]);
|
||||
_ ->
|
||||
iolist_to_binary([?XHTML_DOCTYPE,
|
||||
xml:element_to_binary(XHTML)])
|
||||
end,
|
||||
Headers1 = case lists:keysearch(<<"Content-Type">>, 1,
|
||||
Headers)
|
||||
of
|
||||
|
||||
+64
-91
@@ -1,11 +1,27 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_http_bind.erl
|
||||
%%% Author : Stefan Strigler <steve@zeank.in-berlin.de>
|
||||
%%% Purpose : Implements XMPP over BOSH (XEP-0206) (formerly known as
|
||||
%%% HTTP Binding)
|
||||
%%% Purpose : Implements XMPP over BOSH (XEP-0206)
|
||||
%%% Created : 21 Sep 2005 by Stefan Strigler <steve@zeank.in-berlin.de>
|
||||
%%% Modified: may 2009 by Mickael Remond, Alexey Schepin
|
||||
%%% Id : $Id: ejabberd_http_bind.erl 953 2009-05-07 10:40:40Z alexey $
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2015 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.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_http_bind).
|
||||
@@ -78,7 +94,7 @@
|
||||
wait_timer,
|
||||
ctime = 0,
|
||||
timer,
|
||||
pause=0,
|
||||
pause = 0,
|
||||
unprocessed_req_list = [], % list of request that have been delayed for proper reordering: {Request, PID}
|
||||
req_list = [], % list of requests (cache)
|
||||
max_inactivity,
|
||||
@@ -132,19 +148,15 @@
|
||||
|
||||
-define(PROCESS_DELAY_MAX, 1000).
|
||||
|
||||
%% Line copied from mod_http_bind.erl
|
||||
-define(PROCNAME_MHB, ejabberd_mod_http_bind).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
%% TODO: If compile with no supervisor option, start the session without
|
||||
%% supervisor
|
||||
start(XMPPDomain, Sid, Key, IP, HOpts) ->
|
||||
?DEBUG("Starting session", []),
|
||||
SupervisorProc = gen_mod:get_module_proc(XMPPDomain, ?PROCNAME_MHB),
|
||||
case catch supervisor:start_child(SupervisorProc, [Sid, Key, IP, HOpts]) of
|
||||
{ok, Pid} -> {ok, Pid};
|
||||
case catch supervisor:start_child(SupervisorProc,
|
||||
[Sid, Key, IP, HOpts])
|
||||
of
|
||||
{ok, Pid} -> {ok, Pid};
|
||||
_ -> check_bind_module(XMPPDomain),
|
||||
{error, "Cannot start HTTP bind session"}
|
||||
end.
|
||||
@@ -222,7 +234,7 @@ process_request(Data, IP, HOpts) ->
|
||||
XmppDomain ->
|
||||
NXmppDomain = jlib:nameprep(XmppDomain),
|
||||
Sid = p1_sha:sha(term_to_binary({now(), make_ref()})),
|
||||
case start(NXmppDomain, Sid, <<"">>, IP, HOpts) of
|
||||
case start(NXmppDomain, Sid, <<"">>, IP, HOpts) of
|
||||
{error, _} ->
|
||||
{500, ?HEADER,
|
||||
<<"<body type='terminate' condition='internal-se"
|
||||
@@ -323,13 +335,6 @@ handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs,
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([Sid, Key, IP, HOpts]) ->
|
||||
?DEBUG("started: ~p", [{Sid, Key, IP}]),
|
||||
Opts1 = ejabberd_c2s_config:get_c2s_limits(),
|
||||
@@ -355,12 +360,6 @@ init([Sid, Key, IP, HOpts]) ->
|
||||
max_pause = ?MAX_PAUSE,
|
||||
timer = Timer}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event({become_controller, C2SPid}, StateName, StateData) ->
|
||||
case StateData#state.input of
|
||||
cancel ->
|
||||
@@ -381,15 +380,6 @@ handle_event({change_shaper, Shaper}, StateName,
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event({send_xml, Packet}, _From, StateName,
|
||||
#state{http_receiver = undefined} = StateData) ->
|
||||
Output = [Packet | StateData#state.output],
|
||||
@@ -458,10 +448,8 @@ handle_sync_event(#http_put{payload_size =
|
||||
shaper_timer = NewShaperTimer});
|
||||
%% HTTP GET: send packets to the client
|
||||
handle_sync_event({http_get, Rid, Wait, Hold}, From, StateName, StateData) ->
|
||||
%% setup timer
|
||||
TNow = tnow(),
|
||||
if
|
||||
(Hold > 0) and
|
||||
if (Hold > 0) and
|
||||
((StateData#state.output == []) or (StateData#state.rid < Rid)) and
|
||||
((TNow - StateData#state.ctime) < (Wait*1000*1000)) and
|
||||
(StateData#state.rid =< Rid) and
|
||||
@@ -469,7 +457,6 @@ handle_sync_event({http_get, Rid, Wait, Hold}, From, StateName, StateData) ->
|
||||
send_receiver_reply(StateData#state.http_receiver, {ok, empty}),
|
||||
cancel_timer(StateData#state.wait_timer),
|
||||
WaitTimer = erlang:start_timer(Wait * 1000, self(), []),
|
||||
%% MR: Not sure we should cancel the state timer here.
|
||||
cancel_timer(StateData#state.timer),
|
||||
{next_state, StateName, StateData#state{
|
||||
http_receiver = From,
|
||||
@@ -479,34 +466,30 @@ handle_sync_event({http_get, Rid, Wait, Hold}, From, StateName, StateData) ->
|
||||
true ->
|
||||
cancel_timer(StateData#state.timer),
|
||||
Reply = {ok, StateData#state.output},
|
||||
%% save request
|
||||
ReqList = [#hbr{rid = Rid,
|
||||
key = StateData#state.key,
|
||||
out = StateData#state.output
|
||||
} |
|
||||
[El || El <- StateData#state.req_list,
|
||||
El#hbr.rid /= Rid ]
|
||||
],
|
||||
if
|
||||
(StateData#state.http_receiver /= undefined) and
|
||||
StateData#state.out_of_order_receiver ->
|
||||
{reply, Reply, StateName, StateData#state{
|
||||
output = [],
|
||||
timer = undefined,
|
||||
req_list = ReqList,
|
||||
out_of_order_receiver = false}};
|
||||
true ->
|
||||
send_receiver_reply(StateData#state.http_receiver, {ok, empty}),
|
||||
cancel_timer(StateData#state.wait_timer),
|
||||
Timer = set_inactivity_timer(StateData#state.pause,
|
||||
StateData#state.max_inactivity),
|
||||
{reply, Reply, StateName,
|
||||
StateData#state{output = [],
|
||||
http_receiver = undefined,
|
||||
wait_timer = undefined,
|
||||
timer = Timer,
|
||||
req_list = ReqList}}
|
||||
end
|
||||
out = StateData#state.output}
|
||||
| [El
|
||||
|| El <- StateData#state.req_list,
|
||||
El#hbr.rid /= Rid]],
|
||||
if (StateData#state.http_receiver /= undefined) and
|
||||
StateData#state.out_of_order_receiver ->
|
||||
{reply, Reply, StateName,
|
||||
StateData#state{output = [], timer = undefined,
|
||||
req_list = ReqList,
|
||||
out_of_order_receiver = false}};
|
||||
true ->
|
||||
send_receiver_reply(StateData#state.http_receiver, {ok, empty}),
|
||||
cancel_timer(StateData#state.wait_timer),
|
||||
Timer = set_inactivity_timer(StateData#state.pause,
|
||||
StateData#state.max_inactivity),
|
||||
{reply, Reply, StateName,
|
||||
StateData#state{output = [],
|
||||
http_receiver = undefined,
|
||||
wait_timer = undefined,
|
||||
timer = Timer,
|
||||
req_list = ReqList}}
|
||||
end
|
||||
end;
|
||||
handle_sync_event(peername, _From, StateName,
|
||||
StateData) ->
|
||||
@@ -519,13 +502,6 @@ handle_sync_event(_Event, _From, StateName,
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
%% We reached the max_inactivity timeout:
|
||||
handle_info({timeout, Timer, _}, _StateName,
|
||||
#state{id = SID, timer = Timer} = StateData) ->
|
||||
?INFO_MSG("Session timeout. Closing the HTTP bind "
|
||||
@@ -558,11 +534,6 @@ handle_info({timeout, ShaperTimer, _}, StateName,
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(_Reason, _StateName, StateData) ->
|
||||
?DEBUG("terminate: Deleting session ~s",
|
||||
[StateData#state.id]),
|
||||
@@ -804,19 +775,22 @@ http_put(Sid, Rid, Attrs, Payload, PayloadSize,
|
||||
?DEBUG("Looking for session: ~p", [Sid]),
|
||||
case mnesia:dirty_read({http_bind, Sid}) of
|
||||
[] ->
|
||||
{error, not_exists};
|
||||
[#http_bind{pid = FsmRef, hold=Hold, to={To, StreamVersion}}=Sess] ->
|
||||
NewStream =
|
||||
case StreamStart of
|
||||
true ->
|
||||
{To, StreamVersion};
|
||||
_ ->
|
||||
<<"">>
|
||||
end,
|
||||
{gen_fsm:sync_send_all_state_event(
|
||||
FsmRef, #http_put{rid = Rid, attrs = Attrs, payload = Payload,
|
||||
payload_size = PayloadSize, hold = Hold,
|
||||
stream = NewStream, ip = IP}, 30000), Sess}
|
||||
{error, not_exists};
|
||||
[#http_bind{pid = FsmRef, hold=Hold,
|
||||
to= {To, StreamVersion}} = Sess] ->
|
||||
NewStream = case StreamStart of
|
||||
true -> {To, StreamVersion};
|
||||
_ -> <<"">>
|
||||
end,
|
||||
{gen_fsm:sync_send_all_state_event(
|
||||
FsmRef, #http_put{rid = Rid,
|
||||
attrs = Attrs,
|
||||
payload = Payload,
|
||||
payload_size = PayloadSize,
|
||||
hold = Hold,
|
||||
stream = NewStream,
|
||||
ip = IP},
|
||||
30000), Sess}
|
||||
end.
|
||||
|
||||
handle_http_put_error(Reason,
|
||||
@@ -1189,7 +1163,6 @@ set_inactivity_timer(Pause, _MaxInactivity)
|
||||
set_inactivity_timer(_Pause, MaxInactivity) ->
|
||||
erlang:start_timer(MaxInactivity, self(), []).
|
||||
|
||||
%% TODO: Use tail recursion and list reverse ?
|
||||
elements_to_string([]) -> [];
|
||||
elements_to_string([El | Els]) ->
|
||||
[xml:element_to_binary(El) | elements_to_string(Els)].
|
||||
|
||||
@@ -186,8 +186,8 @@ init_tcp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
|
||||
listen_tcp(PortIP, Module, SockOpts, Port, IPS) ->
|
||||
case ets:lookup(listen_sockets, PortIP) of
|
||||
[{PortIP, ListenSocket}] ->
|
||||
?INFO_MSG("Reusing listening port for ~p", [Port]),
|
||||
ets:delete(listen_sockets, Port),
|
||||
?INFO_MSG("Reusing listening port for ~p", [PortIP]),
|
||||
ets:delete(listen_sockets, PortIP),
|
||||
ListenSocket;
|
||||
_ ->
|
||||
Res = gen_tcp:listen(Port, [binary,
|
||||
|
||||
+9
-45
@@ -175,13 +175,6 @@ bounce_resource_packet(From, To, Packet) ->
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([]) ->
|
||||
lists:foreach(fun (Host) ->
|
||||
ejabberd_router:register_route(Host,
|
||||
@@ -200,28 +193,7 @@ init([]) ->
|
||||
mnesia:add_table_copy(iq_response, node(), ram_copies),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
Reply = ok, {reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
@@ -272,26 +244,18 @@ handle_info(refresh_iq_handlers, State) ->
|
||||
handle_info({timeout, _TRef, ID}, State) ->
|
||||
process_iq_timeout(ID),
|
||||
{noreply, State};
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
do_route(From, To, Packet) ->
|
||||
?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket "
|
||||
"~P~n",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2013, Evgeniy Khramtsov
|
||||
%%% @doc
|
||||
%%%
|
||||
%%% @end
|
||||
|
||||
+16
-19
@@ -44,8 +44,8 @@
|
||||
to_bool/1,
|
||||
sqlite_db/1,
|
||||
sqlite_file/1,
|
||||
encode_term/1,
|
||||
decode_term/1,
|
||||
encode_term/1,
|
||||
decode_term/1,
|
||||
odbc_config/0,
|
||||
freetds_config/0,
|
||||
odbcinst_config/0,
|
||||
@@ -66,11 +66,11 @@
|
||||
|
||||
-record(state,
|
||||
{db_ref = self() :: pid(),
|
||||
db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql,
|
||||
start_interval = 0 :: non_neg_integer(),
|
||||
host = <<"">> :: binary(),
|
||||
db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql,
|
||||
start_interval = 0 :: non_neg_integer(),
|
||||
host = <<"">> :: binary(),
|
||||
max_pending_requests_len :: non_neg_integer(),
|
||||
pending_requests = {0, queue:new()} :: {non_neg_integer(), ?TQUEUE}}).
|
||||
pending_requests = {0, queue:new()} :: {non_neg_integer(), ?TQUEUE}}).
|
||||
|
||||
-define(STATE_KEY, ejabberd_odbc_state).
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
-define(KEEPALIVE_TIMEOUT, 60000).
|
||||
|
||||
-define(KEEPALIVE_QUERY, <<"SELECT 1;">>).
|
||||
-define(KEEPALIVE_QUERY, [<<"SELECT 1;">>]).
|
||||
|
||||
%%-define(DBGFSM, true).
|
||||
|
||||
@@ -477,10 +477,6 @@ sql_query_internal(Query) ->
|
||||
pgsql ->
|
||||
pgsql_to_odbc(pgsql:squery(State#state.db_ref, Query));
|
||||
mysql ->
|
||||
?DEBUG("MySQL, Send query~n~p~n", [Query]),
|
||||
%%squery to be able to specify result_type = binary
|
||||
%%[Query] because p1_mysql_conn expect query to be a list (elements can be binaries, or iolist)
|
||||
%% but doesn't accept just a binary
|
||||
R = mysql_to_odbc(p1_mysql_conn:squery(State#state.db_ref,
|
||||
[Query], self(),
|
||||
[{timeout, (?TRANSACTION_TIMEOUT) - 1000},
|
||||
@@ -614,14 +610,15 @@ pgsql_item_to_odbc(_) -> {updated, undefined}.
|
||||
%% Open a database connection to MySQL
|
||||
mysql_connect(Server, Port, DB, Username, Password) ->
|
||||
case p1_mysql_conn:start(binary_to_list(Server), Port,
|
||||
binary_to_list(Username), binary_to_list(Password),
|
||||
binary_to_list(DB), fun log/3)
|
||||
binary_to_list(Username),
|
||||
binary_to_list(Password),
|
||||
binary_to_list(DB), fun log/3)
|
||||
of
|
||||
{ok, Ref} ->
|
||||
p1_mysql_conn:fetch(Ref, [<<"set names 'utf8';">>],
|
||||
self()),
|
||||
{ok, Ref};
|
||||
Err -> Err
|
||||
{ok, Ref} ->
|
||||
p1_mysql_conn:fetch(
|
||||
Ref, [<<"set names 'utf8';">>], self()),
|
||||
{ok, Ref};
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
%% Convert MySQL query result to Erlang ODBC result formalism
|
||||
@@ -706,7 +703,7 @@ db_opts(Host) ->
|
||||
[odbc, <<"DSN=", Host/binary, ";UID=", Username/binary,
|
||||
";PWD=", Pass/binary>>];
|
||||
_ ->
|
||||
[Type, Server, Port, DB, User, Pass]
|
||||
[Type, Server, Port, DB, User, Pass]
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
%%% Created : 17 Jul 2008 by Pablo Polvorin <pablo.polvorin@process-one.net>
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2012, Evgeniy Khramtsov
|
||||
%%% @doc
|
||||
%%%
|
||||
%%%
|
||||
@@ -714,31 +713,9 @@ make_xinclude(Fn) ->
|
||||
Base = filename:basename(Fn),
|
||||
io_lib:format("<xi:include href='~s'/>", [Base]).
|
||||
|
||||
%%%==================================
|
||||
%%%% Export user
|
||||
%% @spec (Fd, Username::string(), Host::string()) -> ok
|
||||
%% @doc Extract user information and print it.
|
||||
%% @spec (Username::string(), Host::string()) -> string()
|
||||
%% @spec (InfoName::atom(), Username::string(), Host::string()) -> string()
|
||||
%%%==================================
|
||||
%%%% Interface with ejabberd offline storage
|
||||
%% Copied from mod_offline.erl and customized
|
||||
%%%==================================
|
||||
%%%% Interface with ejabberd private storage
|
||||
%%%==================================
|
||||
%%%% Disk file access
|
||||
%% @spec () -> string()
|
||||
%% @spec (Dir::string(), FnT::string()) -> string()
|
||||
%% @spec (FnT::string(), Host::string()) -> FnH::string()
|
||||
%% @doc Make the filename for the host.
|
||||
%% Example: ``("20080804-231550", "jabber.example.org") -> "20080804-231550_jabber_example_org.xml"''
|
||||
%% @spec (Fn::string()) -> {ok, Fd}
|
||||
%% @spec (Fd) -> ok
|
||||
%% @spec (Fd, String::string()) -> ok
|
||||
print(Fd, String) ->
|
||||
%%%==================================
|
||||
%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%%,%%%= foldmethod=marker:
|
||||
file:write(Fd, String).
|
||||
|
||||
opt_type(auth_password_format) -> fun (X) -> X end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
|
||||
|
||||
@@ -58,13 +58,6 @@
|
||||
|
||||
-define(HIBERNATE_TIMEOUT, 90000).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start_link(inet:socket(), atom(), shaper:shaper(),
|
||||
non_neg_integer() | infinity) -> ignore |
|
||||
{error, any()} |
|
||||
@@ -76,10 +69,6 @@ start_link(Socket, SockMod, Shaper, MaxStanzaSize) ->
|
||||
|
||||
-spec start(inet:socket(), atom(), shaper:shaper()) -> undefined | pid().
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start(Socket, SockMod, Shaper) ->
|
||||
start(Socket, SockMod, Shaper, infinity).
|
||||
|
||||
@@ -127,13 +116,6 @@ close(Pid) ->
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([Socket, SockMod, Shaper, MaxStanzaSize]) ->
|
||||
ShaperState = shaper:new(Shaper),
|
||||
Timeout = case SockMod of
|
||||
@@ -145,15 +127,6 @@ init([Socket, SockMod, Shaper, MaxStanzaSize]) ->
|
||||
shaper_state = ShaperState,
|
||||
max_stanza_size = MaxStanzaSize, timeout = Timeout}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call({starttls, TLSSocket}, _From,
|
||||
#state{xml_stream_state = XMLStreamState,
|
||||
c2s_pid = C2SPid,
|
||||
@@ -163,15 +136,15 @@ handle_call({starttls, TLSSocket}, _From,
|
||||
undefined ->
|
||||
XMLStreamState;
|
||||
_ ->
|
||||
xml_stream:new(C2SPid,
|
||||
MaxStanzaSize)
|
||||
xml_stream:new(C2SPid, MaxStanzaSize)
|
||||
end,
|
||||
NewState = State#state{socket = TLSSocket,
|
||||
sock_mod = p1_tls,
|
||||
xml_stream_state = NewXMLStreamState},
|
||||
case p1_tls:recv_data(TLSSocket, <<"">>) of
|
||||
{ok, TLSData} ->
|
||||
{reply, ok, process_data(TLSData, NewState), ?HIBERNATE_TIMEOUT};
|
||||
{reply, ok,
|
||||
process_data(TLSData, NewState), ?HIBERNATE_TIMEOUT};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, ok, NewState}
|
||||
end;
|
||||
@@ -186,24 +159,23 @@ handle_call({compress, Data}, _From,
|
||||
true -> ok
|
||||
end,
|
||||
close_stream(XMLStreamState),
|
||||
NewXMLStreamState = xml_stream:new(C2SPid,
|
||||
MaxStanzaSize),
|
||||
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
|
||||
NewState = State#state{socket = ZlibSocket,
|
||||
sock_mod = ezlib,
|
||||
xml_stream_state = NewXMLStreamState},
|
||||
case ezlib:recv_data(ZlibSocket, <<"">>) of
|
||||
{ok, ZlibData} ->
|
||||
{reply, {ok, ZlibSocket},
|
||||
process_data(ZlibData, NewState), ?HIBERNATE_TIMEOUT};
|
||||
{error, _Reason} -> {stop, normal, ok, NewState}
|
||||
{reply, {ok, ZlibSocket},
|
||||
process_data(ZlibData, NewState), ?HIBERNATE_TIMEOUT};
|
||||
{error, _Reason} ->
|
||||
{stop, normal, ok, NewState}
|
||||
end;
|
||||
handle_call(reset_stream, _From,
|
||||
#state{xml_stream_state = XMLStreamState,
|
||||
c2s_pid = C2SPid, max_stanza_size = MaxStanzaSize} =
|
||||
State) ->
|
||||
close_stream(XMLStreamState),
|
||||
NewXMLStreamState = xml_stream:new(C2SPid,
|
||||
MaxStanzaSize),
|
||||
NewXMLStreamState = xml_stream:new(C2SPid, MaxStanzaSize),
|
||||
Reply = ok,
|
||||
{reply, Reply,
|
||||
State#state{xml_stream_state = NewXMLStreamState},
|
||||
@@ -218,12 +190,6 @@ handle_call({become_controller, C2SPid}, _From, State) ->
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast({change_shaper, Shaper}, State) ->
|
||||
NewShaperState = shaper:new(Shaper),
|
||||
{noreply, State#state{shaper_state = NewShaperState},
|
||||
@@ -232,12 +198,6 @@ handle_cast(close, State) -> {stop, normal, State};
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({Tag, _TCPSocket, Data},
|
||||
#state{socket = Socket, sock_mod = SockMod} = State)
|
||||
when (Tag == tcp) or (Tag == ssl) or
|
||||
@@ -285,13 +245,6 @@ handle_info(timeout, State) ->
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason,
|
||||
#state{xml_stream_state = XMLStreamState,
|
||||
c2s_pid = C2SPid} =
|
||||
@@ -304,10 +257,6 @@ terminate(_Reason,
|
||||
catch (State#state.sock_mod):close(State#state.socket),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
%%% Created : 29 Dec 2011 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%% @copyright (C) 2002-2015 ProcessOne
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2015 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
|
||||
|
||||
@@ -226,7 +226,8 @@ dirty_get_all_domains() ->
|
||||
init([]) ->
|
||||
update_tables(),
|
||||
mnesia:create_table(route,
|
||||
[{ram_copies, [node()]}, {type, bag},
|
||||
[{ram_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, route)}]),
|
||||
mnesia:add_table_copy(route, node(), ram_copies),
|
||||
mnesia:subscribe({table, route, simple}),
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
%%% Author : Badlop <badlop@process-one.net>
|
||||
%%% Purpose : Multicast router
|
||||
%%% Created : 11 Aug 2007 by Badlop <badlop@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2015 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.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_router_multicast).
|
||||
|
||||
+24
-62
@@ -85,13 +85,6 @@
|
||||
|
||||
-type temporarily_blocked() :: #temporarily_blocked{}.
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [],
|
||||
[]).
|
||||
@@ -162,7 +155,7 @@ remove_connection(FromTo, Pid, Key) ->
|
||||
|
||||
have_connection(FromTo) ->
|
||||
case catch mnesia:dirty_read(s2s, FromTo) of
|
||||
[_] ->
|
||||
[_] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
@@ -251,51 +244,26 @@ check_peer_certificate(SockMod, Sock, Peer) ->
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([]) ->
|
||||
update_tables(),
|
||||
mnesia:create_table(s2s, [{ram_copies, [node()]}, {type, bag},
|
||||
{attributes, record_info(fields, s2s)}]),
|
||||
mnesia:create_table(s2s,
|
||||
[{ram_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, s2s)}]),
|
||||
mnesia:add_table_copy(s2s, node(), ram_copies),
|
||||
mnesia:subscribe(system),
|
||||
ejabberd_commands:register_commands(commands()),
|
||||
mnesia:create_table(temporarily_blocked, [{ram_copies, [node()]}, {attributes, record_info(fields, temporarily_blocked)}]),
|
||||
mnesia:create_table(temporarily_blocked,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, temporarily_blocked)}]),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
{reply, ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
||||
clean_table_from_bad_node(Node),
|
||||
{noreply, State};
|
||||
@@ -309,27 +277,17 @@ handle_info({route, From, To, Packet}, State) ->
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ejabberd_commands:unregister_commands(commands()),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
clean_table_from_bad_node(Node) ->
|
||||
F = fun() ->
|
||||
Es = mnesia:select(
|
||||
@@ -520,7 +478,8 @@ parent_domains(Domain) ->
|
||||
end,
|
||||
[], lists:reverse(str:tokens(Domain, <<".">>))).
|
||||
|
||||
send_element(Pid, El) -> Pid ! {send_element, El}.
|
||||
send_element(Pid, El) ->
|
||||
Pid ! {send_element, El}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% ejabberd commands
|
||||
@@ -566,10 +525,8 @@ update_tables() ->
|
||||
{'EXIT', _} -> ok
|
||||
end,
|
||||
case lists:member(local_s2s, mnesia:system_info(tables)) of
|
||||
true ->
|
||||
mnesia:delete_table(local_s2s);
|
||||
false ->
|
||||
ok
|
||||
true -> mnesia:delete_table(local_s2s);
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
%% Check if host is in blacklist or white list
|
||||
@@ -663,10 +620,15 @@ get_s2s_state(S2sPid) ->
|
||||
[{s2s_pid, S2sPid} | Infos].
|
||||
|
||||
get_cert_domains(Cert) ->
|
||||
{rdnSequence, Subject} =
|
||||
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject,
|
||||
Extensions =
|
||||
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.extensions,
|
||||
TBSCert = Cert#'Certificate'.tbsCertificate,
|
||||
Subject = case TBSCert#'TBSCertificate'.subject of
|
||||
{rdnSequence, Subj} -> lists:flatten(Subj);
|
||||
_ -> []
|
||||
end,
|
||||
Extensions = case TBSCert#'TBSCertificate'.extensions of
|
||||
Exts when is_list(Exts) -> Exts;
|
||||
_ -> []
|
||||
end,
|
||||
lists:flatmap(fun (#'AttributeTypeAndValue'{type =
|
||||
?'id-at-commonName',
|
||||
value = Val}) ->
|
||||
@@ -689,7 +651,7 @@ get_cert_domains(Cert) ->
|
||||
end;
|
||||
(_) -> []
|
||||
end,
|
||||
lists:flatten(Subject))
|
||||
Subject)
|
||||
++
|
||||
lists:flatmap(fun (#'Extension'{extnID =
|
||||
?'id-ce-subjectAltName',
|
||||
|
||||
@@ -111,9 +111,6 @@
|
||||
-define(INVALID_XML_ERR,
|
||||
xml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(SockData, Opts) -> ?SUPERVISOR_START.
|
||||
|
||||
start_link(SockData, Opts) ->
|
||||
@@ -126,13 +123,6 @@ socket_type() -> xml_stream.
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([{SockMod, Socket}, Opts]) ->
|
||||
?DEBUG("started: ~p", [{SockMod, Socket}]),
|
||||
Shaper = case lists:keysearch(shaper, 1, Opts) of
|
||||
@@ -567,20 +557,8 @@ stream_established(closed, StateData) ->
|
||||
% Reply = ok,
|
||||
% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: The associated StateData for this connection
|
||||
%% {reply, Reply, NextStateName, NextStateData}
|
||||
%% Reply = {state_infos, [{InfoName::atom(), InfoValue::any()]
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
handle_sync_event(get_state_infos, _From, StateName,
|
||||
StateData) ->
|
||||
@@ -621,12 +599,6 @@ handle_sync_event(_Event, _From, StateName,
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({send_text, Text}, StateName, StateData) ->
|
||||
send_text(StateData, Text),
|
||||
{next_state, StateName, StateData};
|
||||
@@ -636,11 +608,6 @@ handle_info({timeout, Timer, _}, _StateName,
|
||||
handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, _StateName, StateData) ->
|
||||
?DEBUG("terminated: ~p", [Reason]),
|
||||
case Reason of
|
||||
@@ -661,11 +628,6 @@ get_external_hosts(StateData) ->
|
||||
|| {{D, _}, established} <- dict:to_list(Connections)]
|
||||
end.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: print_state/1
|
||||
%% Purpose: Prepare the state to be printed on error log
|
||||
%% Returns: State to print
|
||||
%%----------------------------------------------------------------------
|
||||
print_state(State) -> State.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
start_connection/1,
|
||||
terminate_if_waiting_delay/2,
|
||||
stop_connection/1,
|
||||
transform_options/1]).
|
||||
transform_options/1]).
|
||||
|
||||
-export([init/1, open_socket/2, wait_for_stream/2,
|
||||
wait_for_validation/2, wait_for_features/2,
|
||||
@@ -141,13 +141,6 @@ stop_connection(Pid) -> p1_fsm:send_event(Pid, closed).
|
||||
%%% Callback functions from p1_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([From, Server, Type]) ->
|
||||
process_flag(trap_exit, true),
|
||||
?DEBUG("started: ~p", [{From, Server, Type}]),
|
||||
@@ -222,12 +215,6 @@ init([From, Server, Type]) ->
|
||||
tls_options = TLSOpts, queue = queue:new(), myname = From,
|
||||
server = Server, new = New, verify = Verify, timer = Timer}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
open_socket(init, StateData) ->
|
||||
log_s2s_out(StateData#state.new, StateData#state.myname,
|
||||
StateData#state.server, StateData#state.tls),
|
||||
@@ -292,8 +279,6 @@ open_socket(timeout, StateData) ->
|
||||
open_socket(_, StateData) ->
|
||||
{next_state, open_socket, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% IPv4
|
||||
open_socket1({_, _, _, _} = Addr, Port) ->
|
||||
open_socket2(inet, Addr, Port);
|
||||
%% IPv6
|
||||
@@ -872,19 +857,7 @@ stream_established(closed, StateData) ->
|
||||
%% Reply = ok,
|
||||
%% {reply, Reply, state_name, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: The associated StateData for this connection
|
||||
%% {reply, Reply, NextStateName, NextStateData}
|
||||
%% Reply = {state_infos, [{InfoName::atom(), InfoValue::any()]
|
||||
%%----------------------------------------------------------------------
|
||||
{next_state, StateName, StateData,
|
||||
get_timeout_interval(StateName)}.
|
||||
|
||||
@@ -933,12 +906,6 @@ handle_sync_event(_Event, _From, StateName,
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({send_text, Text}, StateName, StateData) ->
|
||||
send_text(StateData, Text),
|
||||
cancel_timer(StateData#state.timer),
|
||||
@@ -995,11 +962,6 @@ handle_info(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData,
|
||||
get_timeout_interval(StateName)}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
?DEBUG("terminated: ~p", [{Reason, StateName}]),
|
||||
case StateData#state.new of
|
||||
@@ -1018,11 +980,6 @@ terminate(Reason, StateName, StateData) ->
|
||||
end,
|
||||
ok.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: print_state/1
|
||||
%% Purpose: Prepare the state to be printed on error log
|
||||
%% Returns: State to print
|
||||
%%----------------------------------------------------------------------
|
||||
print_state(State) -> State.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -1328,10 +1285,6 @@ wait_before_reconnect(StateData) ->
|
||||
D1 -> lists:min([D1 * 2, get_max_retry_delay()])
|
||||
end,
|
||||
Timer = erlang:start_timer(Delay, self(), []),
|
||||
%% @doc Get the maximum allowed delay for retry to reconnect (in miliseconds).
|
||||
%% The default value is 5 minutes.
|
||||
%% The option {s2s_max_retry_delay, Seconds} can be used (in seconds).
|
||||
%% @spec () -> integer()
|
||||
{next_state, wait_before_retry,
|
||||
StateData#state{timer = Timer, delay_to_retry = Delay,
|
||||
queue = queue:new()}}.
|
||||
|
||||
+14
-50
@@ -95,10 +95,6 @@
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
-export_type([sid/0]).
|
||||
|
||||
start() ->
|
||||
@@ -107,8 +103,7 @@ start() ->
|
||||
supervisor:start_child(ejabberd_sup, ChildSpec).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [],
|
||||
[]).
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
-spec route(jid(), jid(), xmlel() | broadcast()) -> ok.
|
||||
|
||||
@@ -149,6 +144,9 @@ close_session(SID, User, Server, Resource) ->
|
||||
ejabberd_hooks:run(sm_remove_connection_hook,
|
||||
JID#jid.lserver, [SID, JID, Info]).
|
||||
|
||||
-spec check_in_subscription(any(), binary(), binary(),
|
||||
any(), any(), any()) -> any().
|
||||
|
||||
check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) ->
|
||||
case ejabberd_auth:is_user_exists(User, Server) of
|
||||
true -> Acc;
|
||||
@@ -268,6 +266,8 @@ dirty_get_sessions_list() ->
|
||||
Mod = get_sm_backend(),
|
||||
[S#session.usr || S <- Mod:get_sessions()].
|
||||
|
||||
-spec dirty_get_my_sessions_list() -> [#session{}].
|
||||
|
||||
dirty_get_my_sessions_list() ->
|
||||
Mod = get_sm_backend(),
|
||||
[S || S <- Mod:get_sessions(), node(element(2, S#session.sid)) == node()].
|
||||
@@ -285,20 +285,20 @@ get_all_pids() ->
|
||||
Mod = get_sm_backend(),
|
||||
[element(2, S#session.sid) || S <- Mod:get_sessions()].
|
||||
|
||||
-spec get_vh_session_number(binary()) -> non_neg_integer().
|
||||
|
||||
get_vh_session_number(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
Mod = get_sm_backend(),
|
||||
length(Mod:get_sessions(LServer)).
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
ejabberd_sm !
|
||||
{register_iq_handler, Host, XMLNS, Module, Fun}.
|
||||
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}.
|
||||
|
||||
-spec register_iq_handler(binary(), binary(), atom(), atom(), list()) -> any().
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
|
||||
ejabberd_sm !
|
||||
{register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
|
||||
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
|
||||
|
||||
-spec unregister_iq_handler(binary(), binary()) -> any().
|
||||
|
||||
@@ -310,13 +310,6 @@ unregister_iq_handler(Host, XMLNS) ->
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([]) ->
|
||||
Mod = get_sm_backend(),
|
||||
Mod:init(),
|
||||
@@ -333,32 +326,11 @@ init([]) ->
|
||||
ejabberd_commands:register_commands(commands()),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({route, From, To, Packet}, State) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
@@ -388,27 +360,19 @@ handle_info({unregister_iq_handler, Host, XMLNS},
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ejabberd_commands:unregister_commands(commands()),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec set_session(sid(), binary(), binary(), binary(),
|
||||
prio(), info()) -> ok.
|
||||
|
||||
set_session(SID, User, Server, Resource, Priority, Info) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
@@ -741,7 +705,7 @@ force_update_presence({LUser, LServer}) ->
|
||||
Mod = get_sm_backend(),
|
||||
Ss = Mod:get_sessions(LUser, LServer),
|
||||
lists:foreach(fun (#session{sid = {_, Pid}}) ->
|
||||
Pid ! {force_update_presence, LUser}
|
||||
Pid ! {force_update_presence, LUser, LServer}
|
||||
end,
|
||||
Ss).
|
||||
|
||||
|
||||
@@ -131,7 +131,8 @@ get_sessions(LUser, LServer) ->
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec get_sessions(binary(), binary(), binary()) -> [#session{}].
|
||||
-spec get_sessions(binary(), binary(), binary()) ->
|
||||
[#session{}].
|
||||
get_sessions(LUser, LServer, LResource) ->
|
||||
USKey = us_to_key({LUser, LServer}),
|
||||
case eredis:q(?PROCNAME, ["HGETALL", USKey]) of
|
||||
|
||||
@@ -67,15 +67,12 @@
|
||||
|
||||
-export_type([socket_state/0, sockmod/0]).
|
||||
|
||||
-spec start(atom(), sockmod(), socket(), [{atom(), any()}]) -> any().
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function:
|
||||
%% Description:
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start(atom(), sockmod(), socket(), [{atom(), any()}]) -> any().
|
||||
|
||||
start(Module, SockMod, Socket, Opts) ->
|
||||
case Module:socket_type() of
|
||||
xml_stream ->
|
||||
@@ -241,7 +238,3 @@ peername(#socket_state{sockmod = SockMod,
|
||||
_ -> SockMod:peername(Socket)
|
||||
end.
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
%====================================================================
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0, process_command/3,
|
||||
-export([start_link/0, process_command/3, register_hook/1,
|
||||
process_remote_command/1]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
@@ -85,6 +85,10 @@ process_command(From, To, Packet) ->
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
register_hook(Host) ->
|
||||
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||
?MODULE, process_command, 50).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
@@ -100,11 +104,7 @@ init(Opts) ->
|
||||
LH = proplists:get_value(large_heap, Opts),
|
||||
process_flag(priority, high),
|
||||
erlang:system_monitor(self(), [{large_heap, LH}]),
|
||||
lists:foreach(fun (Host) ->
|
||||
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||
?MODULE, process_command, 50)
|
||||
end,
|
||||
?MYHOSTS),
|
||||
lists:foreach(fun register_hook/1, ?MYHOSTS),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
@@ -245,8 +245,9 @@ s2s_out_info(Pid) ->
|
||||
[<<"Process type: s2s_out">>,
|
||||
case FromTo of
|
||||
[{From, To}] ->
|
||||
list_to_binary(io_lib:format("\nS2S connection: from ~s to ~s",
|
||||
[From, To]));
|
||||
<<"\n",
|
||||
(io_lib:format("S2S connection: from ~s to ~s",
|
||||
[From, To]))/binary>>;
|
||||
_ -> <<"">>
|
||||
end,
|
||||
check_send_queue(Pid), <<"\n">>,
|
||||
@@ -310,7 +311,7 @@ help() ->
|
||||
"<node>\n setlh <node> <integer>">>.
|
||||
|
||||
remote_command(Node, Args, From, To) ->
|
||||
Message = case rpc:call(Node, ?MODULE,
|
||||
Message = case ejabberd_cluster:call(Node, ?MODULE,
|
||||
process_remote_command, [Args])
|
||||
of
|
||||
{badrpc, Reason} ->
|
||||
|
||||
+11
-14
@@ -67,9 +67,6 @@ update(ModulesToUpdate) ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
%% OTP R14B03 and older provided release_handler_1:eval_script/3
|
||||
%% But OTP R14B04 and newer provide release_handler_1:eval_script/5
|
||||
%% Dialyzer reports a call to missing function; don't worry.
|
||||
eval_script(Script, Apps, LibDirs) ->
|
||||
release_handler_1:eval_script(Script, Apps, LibDirs, [], []).
|
||||
|
||||
@@ -138,17 +135,17 @@ build_script(Dir, UpdatedBeams) ->
|
||||
LowLevelScript,
|
||||
[{ejabberd, "", filename:join(Dir, "..")}]),
|
||||
Check1 = case Check of
|
||||
{ok, []} ->
|
||||
?DEBUG("script: ~p~n", [Script]),
|
||||
?DEBUG("low level script: ~p~n", [LowLevelScript]),
|
||||
?DEBUG("check: ~p~n", [Check]),
|
||||
ok;
|
||||
_ ->
|
||||
?ERROR_MSG("script: ~p~n", [Script]),
|
||||
?ERROR_MSG("low level script: ~p~n", [LowLevelScript]),
|
||||
?ERROR_MSG("check: ~p~n", [Check]),
|
||||
error
|
||||
end,
|
||||
{ok, []} ->
|
||||
?DEBUG("script: ~p~n", [Script]),
|
||||
?DEBUG("low level script: ~p~n", [LowLevelScript]),
|
||||
?DEBUG("check: ~p~n", [Check]),
|
||||
ok;
|
||||
_ ->
|
||||
?ERROR_MSG("script: ~p~n", [Script]),
|
||||
?ERROR_MSG("low level script: ~p~n", [LowLevelScript]),
|
||||
?ERROR_MSG("check: ~p~n", [Check]),
|
||||
error
|
||||
end,
|
||||
{Script, LowLevelScript, Check1}.
|
||||
|
||||
%% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl
|
||||
|
||||
+47
-47
@@ -1062,8 +1062,9 @@ term_to_string(T) ->
|
||||
|
||||
%% @spec (T::any(), Cols::integer()) -> {NumLines::integer(), Paragraph::string()}
|
||||
term_to_paragraph(T, Cols) ->
|
||||
Paragraph = list_to_binary(erl_prettypr:format(erl_syntax:abstract(T),
|
||||
[{paper, Cols}])),
|
||||
P1 = erl_syntax:abstract(T),
|
||||
P2 = erl_prettypr:format(P1, [{paper, Cols}]),
|
||||
Paragraph = list_to_binary(P2),
|
||||
FieldList = ejabberd_regexp:split(Paragraph, <<"\n">>),
|
||||
NumLines = length(FieldList),
|
||||
{NumLines, Paragraph}.
|
||||
@@ -1477,10 +1478,8 @@ get_stats(global, Lang) ->
|
||||
+ Total
|
||||
end,
|
||||
0, ?MYHOSTS),
|
||||
S2SConns = ejabberd_s2s:dirty_get_connections(),
|
||||
S2SConnections = length(S2SConns),
|
||||
S2SServers = length(lists:usort([element(2, C)
|
||||
|| C <- S2SConns])),
|
||||
OutS2SNumber = ejabberd_s2s:outgoing_s2s_number(),
|
||||
InS2SNumber = ejabberd_s2s:incoming_s2s_number(),
|
||||
[?XAE(<<"table">>, [],
|
||||
[?XE(<<"tbody">>,
|
||||
[?XE(<<"tr">>,
|
||||
@@ -1491,10 +1490,10 @@ get_stats(global, Lang) ->
|
||||
?XC(<<"td">>, (pretty_string_int(OnlineUsers)))]),
|
||||
?XE(<<"tr">>,
|
||||
[?XCT(<<"td">>, <<"Outgoing s2s Connections:">>),
|
||||
?XC(<<"td">>, (pretty_string_int(S2SConnections)))]),
|
||||
?XC(<<"td">>, (pretty_string_int(OutS2SNumber)))]),
|
||||
?XE(<<"tr">>,
|
||||
[?XCT(<<"td">>, <<"Outgoing s2s Servers:">>),
|
||||
?XC(<<"td">>, (pretty_string_int(S2SServers)))])])])];
|
||||
[?XCT(<<"td">>, <<"Incoming s2s Connections:">>),
|
||||
?XC(<<"td">>, (pretty_string_int(InS2SNumber)))])])])];
|
||||
get_stats(Host, Lang) ->
|
||||
OnlineUsers =
|
||||
length(ejabberd_sm:get_vh_session_list(Host)),
|
||||
@@ -1799,7 +1798,7 @@ get_node(Host, Node, [], _Query, Lang) ->
|
||||
<<"Modules">>)])]
|
||||
++ MenuItems2))];
|
||||
get_node(global, Node, [<<"db">>], Query, Lang) ->
|
||||
case rpc:call(Node, mnesia, system_info, [tables]) of
|
||||
case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of
|
||||
{badrpc, _Reason} ->
|
||||
[?XCT(<<"h1">>, <<"RPC Call Error">>)];
|
||||
Tables ->
|
||||
@@ -1811,7 +1810,7 @@ get_node(global, Node, [<<"db">>], Query, Lang) ->
|
||||
Rows = lists:map(fun (Table) ->
|
||||
STable =
|
||||
iolist_to_binary(atom_to_list(Table)),
|
||||
TInfo = case rpc:call(Node, mnesia,
|
||||
TInfo = case ejabberd_cluster:call(Node, mnesia,
|
||||
table_info,
|
||||
[Table, all])
|
||||
of
|
||||
@@ -2031,7 +2030,7 @@ get_node(global, Node, [<<"backup">>], Query, Lang) ->
|
||||
[?INPUTT(<<"submit">>, <<"import_dir">>,
|
||||
<<"OK">>)])])])])])];
|
||||
get_node(global, Node, [<<"ports">>], Query, Lang) ->
|
||||
Ports = rpc:call(Node, ejabberd_config,
|
||||
Ports = ejabberd_cluster:call(Node, ejabberd_config,
|
||||
get_local_option, [listen,
|
||||
{ejabberd_listener, validate_cfg},
|
||||
[]]),
|
||||
@@ -2045,7 +2044,7 @@ get_node(global, Node, [<<"ports">>], Query, Lang) ->
|
||||
{error, iolist_to_binary(io_lib:format("~p", [Reason]))};
|
||||
_ -> nothing
|
||||
end,
|
||||
NewPorts = lists:sort(rpc:call(Node, ejabberd_config,
|
||||
NewPorts = lists:sort(ejabberd_cluster:call(Node, ejabberd_config,
|
||||
get_local_option,
|
||||
[listen,
|
||||
{ejabberd_listener, validate_cfg},
|
||||
@@ -2068,7 +2067,7 @@ get_node(global, Node, [<<"ports">>], Query, Lang) ->
|
||||
[node_ports_to_xhtml(NewPorts, Lang)])];
|
||||
get_node(Host, Node, [<<"modules">>], Query, Lang)
|
||||
when is_binary(Host) ->
|
||||
Modules = rpc:call(Node, gen_mod,
|
||||
Modules = ejabberd_cluster:call(Node, gen_mod,
|
||||
loaded_modules_with_opts, [Host]),
|
||||
Res = case catch node_modules_parse_query(Host, Node,
|
||||
Modules, Query)
|
||||
@@ -2077,7 +2076,7 @@ get_node(Host, Node, [<<"modules">>], Query, Lang)
|
||||
{'EXIT', Reason} -> ?INFO_MSG("~p~n", [Reason]), error;
|
||||
_ -> nothing
|
||||
end,
|
||||
NewModules = lists:sort(rpc:call(Node, gen_mod,
|
||||
NewModules = lists:sort(ejabberd_cluster:call(Node, gen_mod,
|
||||
loaded_modules_with_opts, [Host])),
|
||||
H1String = list_to_binary(io_lib:format(?T(<<"Modules at ~p">>), [Node])),
|
||||
(?H1GL(H1String, <<"modules-overview">>,
|
||||
@@ -2093,21 +2092,21 @@ get_node(Host, Node, [<<"modules">>], Query, Lang)
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[node_modules_to_xhtml(NewModules, Lang)])];
|
||||
get_node(global, Node, [<<"stats">>], _Query, Lang) ->
|
||||
UpTime = rpc:call(Node, erlang, statistics,
|
||||
UpTime = ejabberd_cluster:call(Node, erlang, statistics,
|
||||
[wall_clock]),
|
||||
UpTimeS = list_to_binary(io_lib:format("~.3f",
|
||||
[element(1, UpTime) / 1000])),
|
||||
CPUTime = rpc:call(Node, erlang, statistics, [runtime]),
|
||||
CPUTime = ejabberd_cluster:call(Node, erlang, statistics, [runtime]),
|
||||
CPUTimeS = list_to_binary(io_lib:format("~.3f",
|
||||
[element(1, CPUTime) / 1000])),
|
||||
OnlineUsers = mnesia:table_info(session, size),
|
||||
TransactionsCommitted = rpc:call(Node, mnesia,
|
||||
TransactionsCommitted = ejabberd_cluster:call(Node, mnesia,
|
||||
system_info, [transaction_commits]),
|
||||
TransactionsAborted = rpc:call(Node, mnesia,
|
||||
TransactionsAborted = ejabberd_cluster:call(Node, mnesia,
|
||||
system_info, [transaction_failures]),
|
||||
TransactionsRestarted = rpc:call(Node, mnesia,
|
||||
TransactionsRestarted = ejabberd_cluster:call(Node, mnesia,
|
||||
system_info, [transaction_restarts]),
|
||||
TransactionsLogged = rpc:call(Node, mnesia, system_info,
|
||||
TransactionsLogged = ejabberd_cluster:call(Node, mnesia, system_info,
|
||||
[transaction_log_writes]),
|
||||
[?XC(<<"h1">>,
|
||||
list_to_binary(io_lib:format(?T(<<"Statistics of ~p">>), [Node]))),
|
||||
@@ -2142,12 +2141,12 @@ get_node(global, Node, [<<"stats">>], _Query, Lang) ->
|
||||
?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}],
|
||||
(pretty_string_int(TransactionsLogged)))])])])];
|
||||
get_node(global, Node, [<<"update">>], Query, Lang) ->
|
||||
rpc:call(Node, code, purge, [ejabberd_update]),
|
||||
ejabberd_cluster:call(Node, code, purge, [ejabberd_update]),
|
||||
Res = node_update_parse_query(Node, Query),
|
||||
rpc:call(Node, code, load_file, [ejabberd_update]),
|
||||
ejabberd_cluster:call(Node, code, load_file, [ejabberd_update]),
|
||||
{ok, _Dir, UpdatedBeams, Script, LowLevelScript,
|
||||
Check} =
|
||||
rpc:call(Node, ejabberd_update, update_info, []),
|
||||
ejabberd_cluster:call(Node, ejabberd_update, update_info, []),
|
||||
Mods = case UpdatedBeams of
|
||||
[] -> ?CT(<<"None">>);
|
||||
_ ->
|
||||
@@ -2216,14 +2215,14 @@ get_node(Host, Node, NPath, Query, Lang) ->
|
||||
node_parse_query(Node, Query) ->
|
||||
case lists:keysearch(<<"restart">>, 1, Query) of
|
||||
{value, _} ->
|
||||
case rpc:call(Node, init, restart, []) of
|
||||
case ejabberd_cluster:call(Node, init, restart, []) of
|
||||
{badrpc, _Reason} -> error;
|
||||
_ -> ok
|
||||
end;
|
||||
_ ->
|
||||
case lists:keysearch(<<"stop">>, 1, Query) of
|
||||
{value, _} ->
|
||||
case rpc:call(Node, init, stop, []) of
|
||||
case ejabberd_cluster:call(Node, init, stop, []) of
|
||||
{badrpc, _Reason} -> error;
|
||||
_ -> ok
|
||||
end;
|
||||
@@ -2307,35 +2306,35 @@ node_backup_parse_query(Node, Query) ->
|
||||
{value, {_, Path}} ->
|
||||
Res = case Action of
|
||||
<<"store">> ->
|
||||
rpc:call(Node, mnesia, backup,
|
||||
ejabberd_cluster:call(Node, mnesia, backup,
|
||||
[binary_to_list(Path)]);
|
||||
<<"restore">> ->
|
||||
rpc:call(Node, ejabberd_admin,
|
||||
ejabberd_cluster:call(Node, ejabberd_admin,
|
||||
restore, [Path]);
|
||||
<<"fallback">> ->
|
||||
rpc:call(Node, mnesia,
|
||||
ejabberd_cluster:call(Node, mnesia,
|
||||
install_fallback,
|
||||
[binary_to_list(Path)]);
|
||||
<<"dump">> ->
|
||||
rpc:call(Node, ejabberd_admin,
|
||||
ejabberd_cluster:call(Node, ejabberd_admin,
|
||||
dump_to_textfile,
|
||||
[Path]);
|
||||
<<"load">> ->
|
||||
rpc:call(Node, mnesia,
|
||||
ejabberd_cluster:call(Node, mnesia,
|
||||
load_textfile,
|
||||
[binary_to_list(Path)]);
|
||||
<<"import_piefxis_file">> ->
|
||||
rpc:call(Node, ejabberd_piefxis,
|
||||
ejabberd_cluster:call(Node, ejabberd_piefxis,
|
||||
import_file, [Path]);
|
||||
<<"export_piefxis_dir">> ->
|
||||
rpc:call(Node, ejabberd_piefxis,
|
||||
ejabberd_cluster:call(Node, ejabberd_piefxis,
|
||||
export_server, [Path]);
|
||||
<<"export_piefxis_host_dir">> ->
|
||||
{value, {_, Host}} =
|
||||
lists:keysearch(<<Action/binary,
|
||||
"host">>,
|
||||
1, Query),
|
||||
rpc:call(Node, ejabberd_piefxis,
|
||||
ejabberd_cluster:call(Node, ejabberd_piefxis,
|
||||
export_host,
|
||||
[Path, Host]);
|
||||
<<"export_sql_file">> ->
|
||||
@@ -2343,13 +2342,13 @@ node_backup_parse_query(Node, Query) ->
|
||||
lists:keysearch(<<Action/binary,
|
||||
"host">>,
|
||||
1, Query),
|
||||
rpc:call(Node, ejd2odbc,
|
||||
ejabberd_cluster:call(Node, ejd2odbc,
|
||||
export, [Host, Path]);
|
||||
<<"import_file">> ->
|
||||
rpc:call(Node, ejabberd_admin,
|
||||
ejabberd_cluster:call(Node, ejabberd_admin,
|
||||
import_file, [Path]);
|
||||
<<"import_dir">> ->
|
||||
rpc:call(Node, ejabberd_admin,
|
||||
ejabberd_cluster:call(Node, ejabberd_admin,
|
||||
import_dir, [Path])
|
||||
end,
|
||||
case Res of
|
||||
@@ -2474,10 +2473,10 @@ node_ports_parse_query(Node, Ports, Query) ->
|
||||
{ok, Tokens, _} =
|
||||
erl_scan:string(binary_to_list(SOpts) ++ "."),
|
||||
{ok, Opts} = erl_parse:parse_term(Tokens),
|
||||
rpc:call(Node, ejabberd_listener,
|
||||
ejabberd_cluster:call(Node, ejabberd_listener,
|
||||
delete_listener,
|
||||
[PortIpNetp2, Module1]),
|
||||
R = rpc:call(Node, ejabberd_listener,
|
||||
R = ejabberd_cluster:call(Node, ejabberd_listener,
|
||||
add_listener,
|
||||
[PortIpNetp2, Module, Opts]),
|
||||
throw({is_added, R});
|
||||
@@ -2487,7 +2486,7 @@ node_ports_parse_query(Node, Ports, Query) ->
|
||||
1, Query)
|
||||
of
|
||||
{value, _} ->
|
||||
rpc:call(Node, ejabberd_listener,
|
||||
ejabberd_cluster:call(Node, ejabberd_listener,
|
||||
delete_listener,
|
||||
[PortIpNetp, Module1]),
|
||||
throw(submitted);
|
||||
@@ -2520,7 +2519,7 @@ node_ports_parse_query(Node, Ports, Query) ->
|
||||
{Port2, _SPort, IP2, _SIP, _SSPort, NetProt2,
|
||||
OptsClean} =
|
||||
get_port_data({Port2, STIP2, NetProt2}, Opts),
|
||||
R = rpc:call(Node, ejabberd_listener, add_listener,
|
||||
R = ejabberd_cluster:call(Node, ejabberd_listener, add_listener,
|
||||
[{Port2, IP2, NetProt2}, Module, OptsClean]),
|
||||
throw({is_added, R});
|
||||
_ -> ok
|
||||
@@ -2579,9 +2578,9 @@ node_modules_parse_query(Host, Node, Modules, Query) ->
|
||||
{ok, Tokens, _} =
|
||||
erl_scan:string(binary_to_list(<<SOpts/binary, ".">>)),
|
||||
{ok, Opts} = erl_parse:parse_term(Tokens),
|
||||
rpc:call(Node, gen_mod, stop_module,
|
||||
ejabberd_cluster:call(Node, gen_mod, stop_module,
|
||||
[Host, Module]),
|
||||
rpc:call(Node, gen_mod, start_module,
|
||||
ejabberd_cluster:call(Node, gen_mod, start_module,
|
||||
[Host, Module, Opts]),
|
||||
throw(submitted);
|
||||
_ ->
|
||||
@@ -2589,7 +2588,7 @@ node_modules_parse_query(Host, Node, Modules, Query) ->
|
||||
1, Query)
|
||||
of
|
||||
{value, _} ->
|
||||
rpc:call(Node, gen_mod, stop_module,
|
||||
ejabberd_cluster:call(Node, gen_mod, stop_module,
|
||||
[Host, Module]),
|
||||
throw(submitted);
|
||||
_ -> ok
|
||||
@@ -2605,7 +2604,7 @@ node_modules_parse_query(Host, Node, Modules, Query) ->
|
||||
Module = jlib:binary_to_atom(SModule),
|
||||
{ok, Tokens, _} = erl_scan:string(binary_to_list(<<SOpts/binary, ".">>)),
|
||||
{ok, Opts} = erl_parse:parse_term(Tokens),
|
||||
rpc:call(Node, gen_mod, start_module,
|
||||
ejabberd_cluster:call(Node, gen_mod, start_module,
|
||||
[Host, Module, Opts]),
|
||||
throw(submitted);
|
||||
_ -> ok
|
||||
@@ -2618,7 +2617,7 @@ node_update_parse_query(Node, Query) ->
|
||||
proplists:get_all_values(<<"selected">>, Query),
|
||||
ModulesToUpdate = [jlib:binary_to_atom(M)
|
||||
|| M <- ModulesToUpdateStrings],
|
||||
case rpc:call(Node, ejabberd_update, update,
|
||||
case ejabberd_cluster:call(Node, ejabberd_update, update,
|
||||
[ModulesToUpdate])
|
||||
of
|
||||
{ok, _} -> ok;
|
||||
@@ -2893,7 +2892,8 @@ make_menu_item(item, 3, URI, Name, Lang) ->
|
||||
|
||||
%%%==================================
|
||||
|
||||
%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=:
|
||||
|
||||
opt_type(access) -> fun (V) -> V end;
|
||||
opt_type(_) -> [access].
|
||||
|
||||
%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=:
|
||||
|
||||
+10
-69
@@ -629,27 +629,10 @@ init([Hosts, Port, Rootdn, Passwd, Opts]) ->
|
||||
id = 0, dict = dict:new(), req_q = queue:new()},
|
||||
0}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Called when gen_fsm:send_event/2,3 is invoked (async)
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
connecting(timeout, S) ->
|
||||
{ok, NextState, NewS} = connect_bind(S),
|
||||
{next_state, NextState, NewS}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Called when gen_fsm:sync_send_event/2,3 is invoked.
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
connecting(Event, From, S) ->
|
||||
Q = queue:in({Event, From}, S#eldap.req_q),
|
||||
{next_state, connecting, S#eldap{req_q = Q}}.
|
||||
@@ -678,34 +661,15 @@ handle_event(close, _StateName, S) ->
|
||||
handle_event(_Event, StateName, S) ->
|
||||
{next_state, StateName, S}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Called when gen_fsm:sync_send_all_state_event/2,3 is invoked
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event(_Event, _From, StateName, S) ->
|
||||
{reply, {StateName, S}, StateName, S}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%%
|
||||
%% Packets arriving in various states
|
||||
%%
|
||||
handle_info({Tag, _Socket, Data}, connecting, S)
|
||||
when Tag == tcp; Tag == ssl ->
|
||||
?DEBUG("tcp packet received when disconnected!~n~p",
|
||||
[Data]),
|
||||
?DEBUG("tcp packet received when disconnected!~n~p", [Data]),
|
||||
{next_state, connecting, S};
|
||||
handle_info({Tag, _Socket, Data}, wait_bind_response, S)
|
||||
when Tag == tcp; Tag == ssl ->
|
||||
@@ -724,8 +688,7 @@ handle_info({Tag, _Socket, Data}, wait_bind_response, S)
|
||||
{next_state, connecting, close_and_retry(S)}
|
||||
end;
|
||||
handle_info({Tag, _Socket, Data}, StateName, S)
|
||||
when (StateName == active orelse
|
||||
StateName == active_bind)
|
||||
when (StateName == active orelse StateName == active_bind)
|
||||
andalso (Tag == tcp orelse Tag == ssl) ->
|
||||
case catch recvd_packet(Data, S) of
|
||||
{response, Response, RequestType} ->
|
||||
@@ -766,8 +729,7 @@ handle_info({timeout, Timer, {cmd_timeout, Id}},
|
||||
handle_info({timeout, retry_connect}, connecting, S) ->
|
||||
{ok, NextState, NewS} = connect_bind(S),
|
||||
{next_state, NextState, NewS};
|
||||
handle_info({timeout, _Timer, bind_timeout},
|
||||
wait_bind_response, S) ->
|
||||
handle_info({timeout, _Timer, bind_timeout}, wait_bind_response, S) ->
|
||||
{next_state, connecting, close_and_retry(S)};
|
||||
%%
|
||||
%% Make sure we don't fill the message queue with rubbish
|
||||
@@ -825,16 +787,14 @@ send_command(Command, From, S) ->
|
||||
Message = #'LDAPMessage'{messageID = Id,
|
||||
protocolOp = {Name, Request}},
|
||||
?DEBUG("~p~n", [{Name, ejabberd_config:may_hide_data(Request)}]),
|
||||
{ok, Bytes} = 'ELDAPv3':encode('LDAPMessage',
|
||||
Message),
|
||||
{ok, Bytes} = 'ELDAPv3':encode('LDAPMessage', Message),
|
||||
case (S#eldap.sockmod):send(S#eldap.fd, Bytes) of
|
||||
ok ->
|
||||
Timer = erlang:start_timer(?CMD_TIMEOUT, self(),
|
||||
{cmd_timeout, Id}),
|
||||
New_dict = dict:store(Id,
|
||||
[{Timer, Command, From, Name}], S#eldap.dict),
|
||||
{ok, S#eldap{id = Id, dict = New_dict}};
|
||||
Error -> Error
|
||||
Timer = erlang:start_timer(?CMD_TIMEOUT, self(), {cmd_timeout, Id}),
|
||||
New_dict = dict:store(Id, [{Timer, Command, From, Name}], S#eldap.dict),
|
||||
{ok, S#eldap{id = Id, dict = New_dict}};
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
gen_req({search, A}) ->
|
||||
@@ -1148,8 +1108,7 @@ bind_request(Socket, S) ->
|
||||
Message = #'LDAPMessage'{messageID = Id,
|
||||
protocolOp = {bindRequest, Req}},
|
||||
?DEBUG("Bind Request Message:~p~n", [ejabberd_config:may_hide_data(Message)]),
|
||||
{ok, Bytes} = 'ELDAPv3':encode('LDAPMessage',
|
||||
Message),
|
||||
{ok, Bytes} = 'ELDAPv3':encode('LDAPMessage', Message),
|
||||
case (S#eldap.sockmod):send(Socket, Bytes) of
|
||||
ok -> {ok, S#eldap{id = Id}};
|
||||
Error -> Error
|
||||
@@ -1162,24 +1121,6 @@ next_host(Host,
|
||||
Hosts) -> % Find next in turn
|
||||
next_host(Host, Hosts, Hosts).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Verify the input data
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Get and Validate the initial configuration
|
||||
%%% --------------------------------------------------------------------
|
||||
%% get_atom(Key, List) ->
|
||||
%% case lists:keysearch(Key, 1, List) of
|
||||
%% {value, {Key, Value}} when is_atom(Value) ->
|
||||
%% Value;
|
||||
%% {value, {Key, _Value}} ->
|
||||
%% throw({error, "Bad Value in Config for " ++ atom_to_list(Key)});
|
||||
%% false ->
|
||||
%% throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
|
||||
%% end.
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Other Stuff
|
||||
%%% --------------------------------------------------------------------
|
||||
next_host(Host, [Host], Hosts) ->
|
||||
hd(Hosts); % Wrap back to first
|
||||
next_host(Host, [Host | Tail], _Hosts) ->
|
||||
|
||||
+13
-3
@@ -36,6 +36,7 @@
|
||||
config_dir/0, opt_type/1]).
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-define(REPOS, "https://github.com/processone/ejabberd-contrib").
|
||||
|
||||
@@ -111,10 +112,19 @@ commands() ->
|
||||
|
||||
update() ->
|
||||
add_sources(?REPOS),
|
||||
lists:foreach(fun({Package, Spec}) ->
|
||||
Res = lists:foldl(fun({Package, Spec}, Acc) ->
|
||||
Path = proplists:get_value(url, Spec, ""),
|
||||
add_sources(Package, Path)
|
||||
end, modules_spec(sources_dir(), "*")).
|
||||
Update = add_sources(Package, Path),
|
||||
?INFO_MSG("Update package ~s: ~p", [Package, Update]),
|
||||
case Update of
|
||||
ok -> Acc;
|
||||
Error -> [Error|Acc]
|
||||
end
|
||||
end, [], modules_spec(sources_dir(), "*")),
|
||||
case Res of
|
||||
[] -> ok;
|
||||
[Error|_] -> Error
|
||||
end.
|
||||
|
||||
available() ->
|
||||
Jungle = modules_spec(sources_dir(), "*/*"),
|
||||
|
||||
+42
-73
@@ -44,9 +44,9 @@
|
||||
|
||||
-record(state, {host, module, function}).
|
||||
|
||||
%%-type component() :: ejabberd_sm | ejabberd_local.
|
||||
-type component() :: ejabberd_sm | ejabberd_local.
|
||||
-type type() :: no_queue | one_queue | pos_integer() | parallel.
|
||||
%%-type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel.
|
||||
-type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel.
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
@@ -62,33 +62,37 @@ start_link(Host, Module, Function) ->
|
||||
add_iq_handler(Component, Host, NS, Module, Function,
|
||||
Type) ->
|
||||
case Type of
|
||||
no_queue ->
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, no_queue);
|
||||
one_queue ->
|
||||
{ok, Pid} = supervisor:start_child(ejabberd_iq_sup,
|
||||
[Host, Module, Function]),
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, {one_queue, Pid});
|
||||
N when is_integer(N) ->
|
||||
Pids = lists:map(fun (_) ->
|
||||
{ok, Pid} =
|
||||
supervisor:start_child(ejabberd_iq_sup,
|
||||
[Host, Module,
|
||||
Function]),
|
||||
Pid
|
||||
end,
|
||||
lists:seq(1, N)),
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, {queues, Pids});
|
||||
parallel ->
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, parallel)
|
||||
no_queue ->
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, no_queue);
|
||||
one_queue ->
|
||||
{ok, Pid} = supervisor:start_child(ejabberd_iq_sup,
|
||||
[Host, Module, Function]),
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, {one_queue, Pid});
|
||||
N when is_integer(N) ->
|
||||
Pids = lists:map(fun (_) ->
|
||||
{ok, Pid} =
|
||||
supervisor:start_child(ejabberd_iq_sup,
|
||||
[Host, Module,
|
||||
Function]),
|
||||
Pid
|
||||
end,
|
||||
lists:seq(1, N)),
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, {queues, Pids});
|
||||
parallel ->
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, parallel)
|
||||
end.
|
||||
|
||||
-spec remove_iq_handler(component(), binary(), binary()) -> any().
|
||||
|
||||
remove_iq_handler(Component, Host, NS) ->
|
||||
Component:unregister_iq_handler(Host, NS).
|
||||
|
||||
-spec stop_iq_handler(atom(), atom(), [pid()]) -> any().
|
||||
|
||||
stop_iq_handler(_Module, _Function, Opts) ->
|
||||
case Opts of
|
||||
{one_queue, Pid} -> gen_server:call(Pid, stop);
|
||||
@@ -100,21 +104,25 @@ stop_iq_handler(_Module, _Function, Opts) ->
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
-spec handle(binary(), atom(), atom(), opts(), jid(), jid(), iq()) -> any().
|
||||
|
||||
handle(Host, Module, Function, Opts, From, To, IQ) ->
|
||||
case Opts of
|
||||
no_queue ->
|
||||
process_iq(Host, Module, Function, From, To, IQ);
|
||||
{one_queue, Pid} -> Pid ! {process_iq, From, To, IQ};
|
||||
{queues, Pids} ->
|
||||
Pid = lists:nth(erlang:phash(now(), length(Pids)),
|
||||
Pids),
|
||||
Pid ! {process_iq, From, To, IQ};
|
||||
parallel ->
|
||||
spawn(?MODULE, process_iq,
|
||||
no_queue ->
|
||||
process_iq(Host, Module, Function, From, To, IQ);
|
||||
{one_queue, Pid} ->
|
||||
Pid ! {process_iq, From, To, IQ};
|
||||
{queues, Pids} ->
|
||||
Pid = lists:nth(erlang:phash(now(), length(Pids)), Pids),
|
||||
Pid ! {process_iq, From, To, IQ};
|
||||
parallel ->
|
||||
spawn(?MODULE, process_iq,
|
||||
[Host, Module, Function, From, To, IQ]);
|
||||
_ -> todo
|
||||
_ -> todo
|
||||
end.
|
||||
|
||||
-spec process_iq(binary(), atom(), atom(), jid(), jid(), iq()) -> any().
|
||||
|
||||
process_iq(_Host, Module, Function, From, To, IQ) ->
|
||||
case catch Module:Function(From, To, IQ) of
|
||||
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
|
||||
@@ -146,44 +154,16 @@ transform_module_options(Opts) ->
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([Host, Module, Function]) ->
|
||||
{ok,
|
||||
#state{host = Host, module = Module,
|
||||
function = Function}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(stop, _From, State) ->
|
||||
Reply = ok, {stop, normal, Reply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({process_iq, From, To, IQ},
|
||||
#state{host = Host, module = Module,
|
||||
function = Function} =
|
||||
@@ -192,19 +172,8 @@ handle_info({process_iq, From, To, IQ},
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
+16
-16
@@ -25,6 +25,7 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
-module (mod_carboncopy).
|
||||
|
||||
-author ('ecestari@process-one.net').
|
||||
-protocol({xep, 280, '0.8'}).
|
||||
|
||||
@@ -112,7 +113,7 @@ iq_handler(From, _To, #iq{type=set, sub_el = #xmlel{name = Operation, children
|
||||
?INFO_MSG("carbons disabled for user ~s@~s/~s", [U,S,R]),
|
||||
disable(S, U, R)
|
||||
end,
|
||||
case Result of
|
||||
case Result of
|
||||
ok ->
|
||||
?DEBUG("carbons IQ result: ok", []),
|
||||
IQ#iq{type=result, sub_el=[]};
|
||||
@@ -129,9 +130,8 @@ user_send_packet(Packet, _C2SState, From, To) ->
|
||||
|
||||
user_receive_packet(Packet, _C2SState, JID, _From, To) ->
|
||||
check_and_forward(JID, To, Packet, received).
|
||||
|
||||
% verifier si le trafic est local
|
||||
% Modified from original version:
|
||||
|
||||
% Modified from original version:
|
||||
% - registered to the user_send_packet hook, to be called only once even for multicast
|
||||
% - do not support "private" message mode, and do not modify the original packet in any way
|
||||
% - we also replicate "read" notifications
|
||||
@@ -145,8 +145,8 @@ check_and_forward(JID, To, Packet, Direction)->
|
||||
send_copies(JID, To, Packet, Direction),
|
||||
Packet;
|
||||
true ->
|
||||
%% stop the hook chain, we don't want mod_logdb to register
|
||||
%% this message (duplicate)
|
||||
%% stop the hook chain, we don't want logging modules to duplicates
|
||||
%% this message
|
||||
{stop, Packet}
|
||||
end;
|
||||
_ ->
|
||||
@@ -156,7 +156,7 @@ check_and_forward(JID, To, Packet, Direction)->
|
||||
remove_connection(User, Server, Resource, _Status)->
|
||||
disable(Server, User, Resource),
|
||||
ok.
|
||||
|
||||
|
||||
|
||||
%%% Internal
|
||||
%% Direction = received | sent <received xmlns='urn:xmpp:carbons:1'/>
|
||||
@@ -208,31 +208,31 @@ send_copies(JID, To, Packet, Direction)->
|
||||
ok.
|
||||
|
||||
build_forward_packet(JID, Packet, Sender, Dest, Direction, ?NS_CARBONS_2) ->
|
||||
#xmlel{name = <<"message">>,
|
||||
#xmlel{name = <<"message">>,
|
||||
attrs = [{<<"xmlns">>, <<"jabber:client">>},
|
||||
{<<"type">>, message_type(Packet)},
|
||||
{<<"from">>, jlib:jid_to_string(Sender)},
|
||||
{<<"to">>, jlib:jid_to_string(Dest)}],
|
||||
children = [
|
||||
#xmlel{name = list_to_binary(atom_to_list(Direction)),
|
||||
children = [
|
||||
#xmlel{name = list_to_binary(atom_to_list(Direction)),
|
||||
attrs = [{<<"xmlns">>, ?NS_CARBONS_2}],
|
||||
children = [
|
||||
#xmlel{name = <<"forwarded">>,
|
||||
#xmlel{name = <<"forwarded">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_FORWARD}],
|
||||
children = [
|
||||
complete_packet(JID, Packet, Direction)]}
|
||||
]}
|
||||
]};
|
||||
build_forward_packet(JID, Packet, Sender, Dest, Direction, ?NS_CARBONS_1) ->
|
||||
#xmlel{name = <<"message">>,
|
||||
#xmlel{name = <<"message">>,
|
||||
attrs = [{<<"xmlns">>, <<"jabber:client">>},
|
||||
{<<"type">>, message_type(Packet)},
|
||||
{<<"from">>, jlib:jid_to_string(Sender)},
|
||||
{<<"to">>, jlib:jid_to_string(Dest)}],
|
||||
children = [
|
||||
#xmlel{name = list_to_binary(atom_to_list(Direction)),
|
||||
children = [
|
||||
#xmlel{name = list_to_binary(atom_to_list(Direction)),
|
||||
attrs = [{<<"xmlns">>, ?NS_CARBONS_1}]},
|
||||
#xmlel{name = <<"forwarded">>,
|
||||
#xmlel{name = <<"forwarded">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_FORWARD}],
|
||||
children = [complete_packet(JID, Packet, Direction)]}
|
||||
]}.
|
||||
@@ -285,7 +285,7 @@ has_non_empty_body(Packet) ->
|
||||
xml:get_subtag_cdata(Packet, <<"body">>) =/= <<"">>.
|
||||
|
||||
%% list {resource, cc_version} with carbons enabled for given user and host
|
||||
list(User, Server)->
|
||||
list(User, Server) ->
|
||||
mnesia:dirty_select(?TABLE, [{#carboncopy{us = {User, Server}, resource = '$2', version = '$3'}, [], [{{'$2','$3'}}]}]).
|
||||
|
||||
|
||||
|
||||
+10
-11
@@ -985,7 +985,7 @@ get_form(_Host, [<<"running nodes">>, ENode, <<"DB">>],
|
||||
case search_running_node(ENode) of
|
||||
false -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||
Node ->
|
||||
case rpc:call(Node, mnesia, system_info, [tables]) of
|
||||
case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of
|
||||
{badrpc, _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
Tables ->
|
||||
@@ -1007,7 +1007,7 @@ get_form(_Host, [<<"running nodes">>, ENode, <<"DB">>],
|
||||
?T(Lang,
|
||||
<<"Choose storage type of tables">>)}]}
|
||||
| lists:map(fun (Table) ->
|
||||
case rpc:call(Node, mnesia,
|
||||
case ejabberd_cluster:call(Node, mnesia,
|
||||
table_info,
|
||||
[Table,
|
||||
storage_type])
|
||||
@@ -1028,7 +1028,7 @@ get_form(Host,
|
||||
case search_running_node(ENode) of
|
||||
false -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||
Node ->
|
||||
case rpc:call(Node, gen_mod, loaded_modules, [Host]) of
|
||||
case ejabberd_cluster:call(Node, gen_mod, loaded_modules, [Host]) of
|
||||
{badrpc, _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
Modules ->
|
||||
@@ -1607,7 +1607,7 @@ set_form(_From, Host,
|
||||
case Vals of
|
||||
[<<"1">>] ->
|
||||
Module = jlib:binary_to_atom(Var),
|
||||
rpc:call(Node, gen_mod, stop_module,
|
||||
ejabberd_cluster:call(Node, gen_mod, stop_module,
|
||||
[Host, Module]);
|
||||
_ -> ok
|
||||
end
|
||||
@@ -1634,7 +1634,7 @@ set_form(_From, Host,
|
||||
case erl_parse:parse_term(Tokens) of
|
||||
{ok, Modules} ->
|
||||
lists:foreach(fun ({Module, Args}) ->
|
||||
rpc:call(Node, gen_mod,
|
||||
ejabberd_cluster:call(Node, gen_mod,
|
||||
start_module,
|
||||
[Host, Module, Args])
|
||||
end,
|
||||
@@ -1656,7 +1656,7 @@ set_form(_From, _Host,
|
||||
case lists:keysearch(<<"path">>, 1, XData) of
|
||||
false -> {error, ?ERR_BAD_REQUEST};
|
||||
{value, {_, [String]}} ->
|
||||
case rpc:call(Node, mnesia, backup, [String]) of
|
||||
case ejabberd_cluster:call(Node, mnesia, backup, [String]) of
|
||||
{badrpc, _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
{error, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
@@ -1675,7 +1675,7 @@ set_form(_From, _Host,
|
||||
case lists:keysearch(<<"path">>, 1, XData) of
|
||||
false -> {error, ?ERR_BAD_REQUEST};
|
||||
{value, {_, [String]}} ->
|
||||
case rpc:call(Node, ejabberd_admin, restore, [String])
|
||||
case ejabberd_cluster:call(Node, ejabberd_admin, restore, [String])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
@@ -1695,7 +1695,7 @@ set_form(_From, _Host,
|
||||
case lists:keysearch(<<"path">>, 1, XData) of
|
||||
false -> {error, ?ERR_BAD_REQUEST};
|
||||
{value, {_, [String]}} ->
|
||||
case rpc:call(Node, ejabberd_admin, dump_to_textfile,
|
||||
case ejabberd_cluster:call(Node, ejabberd_admin, dump_to_textfile,
|
||||
[String])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
@@ -1715,7 +1715,7 @@ set_form(_From, _Host,
|
||||
case lists:keysearch(<<"path">>, 1, XData) of
|
||||
false -> {error, ?ERR_BAD_REQUEST};
|
||||
{value, {_, [String]}} ->
|
||||
rpc:call(Node, jd2ejd, import_file, [String]),
|
||||
ejabberd_cluster:call(Node, jd2ejd, import_file, [String]),
|
||||
{result, []};
|
||||
_ -> {error, ?ERR_BAD_REQUEST}
|
||||
end
|
||||
@@ -1729,7 +1729,7 @@ set_form(_From, _Host,
|
||||
case lists:keysearch(<<"path">>, 1, XData) of
|
||||
false -> {error, ?ERR_BAD_REQUEST};
|
||||
{value, {_, [String]}} ->
|
||||
rpc:call(Node, jd2ejd, import_dir, [String]),
|
||||
ejabberd_cluster:call(Node, jd2ejd, import_dir, [String]),
|
||||
{result, []};
|
||||
_ -> {error, ?ERR_BAD_REQUEST}
|
||||
end
|
||||
@@ -1912,7 +1912,6 @@ set_form(From, Host,
|
||||
Server)
|
||||
of
|
||||
[] ->
|
||||
%% _US = {User, Server},
|
||||
case get_last_info(User, Server) of
|
||||
not_found -> ?T(Lang, <<"Never">>);
|
||||
{ok, Timestamp, _Status} ->
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2014, Evgeny Khramtsov
|
||||
%%% @doc
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 15 Aug 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2014-2015 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
|
||||
@@ -0,0 +1,957 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : mod_http_upload.erl
|
||||
%%% Author : Holger Weiss <holger@zedat.fu-berlin.de>
|
||||
%%% Purpose : HTTP File Upload (XEP-0363)
|
||||
%%% Created : 20 Aug 2015 by Holger Weiss <holger@zedat.fu-berlin.de>
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(mod_http_upload).
|
||||
-author('holger@zedat.fu-berlin.de').
|
||||
|
||||
-define(GEN_SERVER, gen_server).
|
||||
-define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds.
|
||||
-define(SLOT_TIMEOUT, 18000000). % 5 hours.
|
||||
-define(PROCNAME, ?MODULE).
|
||||
-define(FORMAT(Error), file:format_error(Error)).
|
||||
-define(URL_ENC(URL), binary_to_list(ejabberd_http:url_encode(URL))).
|
||||
-define(ADDR_TO_STR(IP), ejabberd_config:may_hide_data(jlib:ip_to_list(IP))).
|
||||
-define(STR_TO_INT(Str, B), jlib:binary_to_integer(iolist_to_binary(Str), B)).
|
||||
-define(DEFAULT_CONTENT_TYPE, <<"application/octet-stream">>).
|
||||
-define(CONTENT_TYPES,
|
||||
[{<<".avi">>, <<"video/avi">>},
|
||||
{<<".bmp">>, <<"image/bmp">>},
|
||||
{<<".bz2">>, <<"application/x-bzip2">>},
|
||||
{<<".gif">>, <<"image/gif">>},
|
||||
{<<".gz">>, <<"application/x-gzip">>},
|
||||
{<<".html">>, <<"text/html">>},
|
||||
{<<".jpeg">>, <<"image/jpeg">>},
|
||||
{<<".jpg">>, <<"image/jpeg">>},
|
||||
{<<".mp3">>, <<"audio/mpeg">>},
|
||||
{<<".mp4">>, <<"video/mp4">>},
|
||||
{<<".mpeg">>, <<"video/mpeg">>},
|
||||
{<<".mpg">>, <<"video/mpeg">>},
|
||||
{<<".ogg">>, <<"application/ogg">>},
|
||||
{<<".pdf">>, <<"application/pdf">>},
|
||||
{<<".png">>, <<"image/png">>},
|
||||
{<<".rtf">>, <<"application/rtf">>},
|
||||
{<<".svg">>, <<"image/svg+xml">>},
|
||||
{<<".tiff">>, <<"image/tiff">>},
|
||||
{<<".txt">>, <<"text/plain">>},
|
||||
{<<".wav">>, <<"audio/wav">>},
|
||||
{<<".webp">>, <<"image/webp">>},
|
||||
{<<".xz">>, <<"application/x-xz">>},
|
||||
{<<".zip">>, <<"application/zip">>}]).
|
||||
|
||||
-behaviour(?GEN_SERVER).
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% gen_mod/supervisor callbacks.
|
||||
-export([start_link/3,
|
||||
start/2,
|
||||
stop/1,
|
||||
mod_opt_type/1]).
|
||||
|
||||
%% gen_server callbacks.
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3]).
|
||||
|
||||
%% ejabberd_http callback.
|
||||
-export([process/2]).
|
||||
|
||||
%% ejabberd_hooks callback.
|
||||
-export([remove_user/2]).
|
||||
|
||||
%% Utility functions.
|
||||
-export([get_proc_name/2,
|
||||
expand_home/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-record(state,
|
||||
{server_host :: binary(),
|
||||
host :: binary(),
|
||||
name :: binary(),
|
||||
access :: atom(),
|
||||
max_size :: pos_integer() | infinity,
|
||||
secret_length :: pos_integer(),
|
||||
jid_in_url :: sha1 | node,
|
||||
file_mode :: integer() | undefined,
|
||||
dir_mode :: integer() | undefined,
|
||||
docroot :: binary(),
|
||||
put_url :: binary(),
|
||||
get_url :: binary(),
|
||||
service_url :: binary() | undefined,
|
||||
thumbnail :: boolean(),
|
||||
slots = dict:new() :: term()}). % dict:dict() requires Erlang 17.
|
||||
|
||||
-record(media_info,
|
||||
{type :: string(),
|
||||
height :: integer(),
|
||||
width :: integer()}).
|
||||
|
||||
-type state() :: #state{}.
|
||||
-type slot() :: [binary()].
|
||||
-type media_info() :: #media_info{}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_mod/supervisor callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec start_link(binary(), atom(), gen_mod:opts())
|
||||
-> {ok, pid()} | ignore | {error, _}.
|
||||
|
||||
start_link(ServerHost, Proc, Opts) ->
|
||||
?GEN_SERVER:start_link({local, Proc}, ?MODULE, {ServerHost, Opts}, []).
|
||||
|
||||
-spec start(binary(), gen_mod:opts()) -> {ok, _} | {ok, _, _} | {error, _}.
|
||||
|
||||
start(ServerHost, Opts) ->
|
||||
case gen_mod:get_opt(rm_on_unregister, Opts,
|
||||
fun(B) when is_boolean(B) -> B end,
|
||||
true) of
|
||||
true ->
|
||||
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50),
|
||||
ejabberd_hooks:add(anonymous_purge_hook, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
false -> ok
|
||||
end,
|
||||
Proc = get_proc_name(ServerHost, ?PROCNAME),
|
||||
Spec = {Proc,
|
||||
{?MODULE, start_link, [ServerHost, Proc, Opts]},
|
||||
permanent,
|
||||
3000,
|
||||
worker,
|
||||
[?MODULE]},
|
||||
supervisor:start_child(ejabberd_sup, Spec).
|
||||
|
||||
-spec stop(binary()) -> ok.
|
||||
|
||||
stop(ServerHost) ->
|
||||
case gen_mod:get_module_opt(ServerHost, ?MODULE, rm_on_unregister,
|
||||
fun(B) when is_boolean(B) -> B end,
|
||||
true) of
|
||||
true ->
|
||||
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50),
|
||||
ejabberd_hooks:delete(anonymous_purge_hook, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
false -> ok
|
||||
end,
|
||||
Proc = get_proc_name(ServerHost, ?PROCNAME),
|
||||
ok = supervisor:terminate_child(ejabberd_sup, Proc),
|
||||
ok = supervisor:delete_child(ejabberd_sup, Proc).
|
||||
|
||||
-spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
|
||||
|
||||
mod_opt_type(host) ->
|
||||
fun iolist_to_binary/1;
|
||||
mod_opt_type(name) ->
|
||||
fun iolist_to_binary/1;
|
||||
mod_opt_type(access) ->
|
||||
fun(A) when is_atom(A) -> A end;
|
||||
mod_opt_type(max_size) ->
|
||||
fun(I) when is_integer(I), I > 0 -> I;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
mod_opt_type(secret_length) ->
|
||||
fun(I) when is_integer(I), I >= 8 -> I end;
|
||||
mod_opt_type(jid_in_url) ->
|
||||
fun(sha1) -> sha1;
|
||||
(node) -> node
|
||||
end;
|
||||
mod_opt_type(file_mode) ->
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end;
|
||||
mod_opt_type(dir_mode) ->
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end;
|
||||
mod_opt_type(docroot) ->
|
||||
fun iolist_to_binary/1;
|
||||
mod_opt_type(put_url) ->
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
end;
|
||||
mod_opt_type(get_url) ->
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
end;
|
||||
mod_opt_type(service_url) ->
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
end;
|
||||
mod_opt_type(custom_headers) ->
|
||||
fun(Headers) ->
|
||||
lists:map(fun({K, V}) ->
|
||||
{iolist_to_binary(K), iolist_to_binary(V)}
|
||||
end, Headers)
|
||||
end;
|
||||
mod_opt_type(rm_on_unregister) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(thumbnail) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) ->
|
||||
[host, name, access, max_size, secret_length, jid_in_url, file_mode,
|
||||
dir_mode, docroot, put_url, get_url, service_url, custom_headers,
|
||||
rm_on_unregister, thumbnail].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec init({binary(), gen_mod:opts()}) -> {ok, state()}.
|
||||
|
||||
init({ServerHost, Opts}) ->
|
||||
process_flag(trap_exit, true),
|
||||
Host = gen_mod:get_opt_host(ServerHost, Opts, <<"upload.@HOST@">>),
|
||||
Name = gen_mod:get_opt(name, Opts,
|
||||
fun iolist_to_binary/1,
|
||||
<<"HTTP File Upload">>),
|
||||
Access = gen_mod:get_opt(access, Opts,
|
||||
fun(A) when is_atom(A) -> A end,
|
||||
local),
|
||||
MaxSize = gen_mod:get_opt(max_size, Opts,
|
||||
fun(I) when is_integer(I), I > 0 -> I;
|
||||
(infinity) -> infinity
|
||||
end,
|
||||
104857600),
|
||||
SecretLength = gen_mod:get_opt(secret_length, Opts,
|
||||
fun(I) when is_integer(I), I >= 8 -> I end,
|
||||
40),
|
||||
JIDinURL = gen_mod:get_opt(jid_in_url, Opts,
|
||||
fun(sha1) -> sha1;
|
||||
(node) -> node
|
||||
end,
|
||||
sha1),
|
||||
DocRoot = gen_mod:get_opt(docroot, Opts,
|
||||
fun iolist_to_binary/1,
|
||||
<<"@HOME@/upload">>),
|
||||
FileMode = gen_mod:get_opt(file_mode, Opts,
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end),
|
||||
DirMode = gen_mod:get_opt(dir_mode, Opts,
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end),
|
||||
PutURL = gen_mod:get_opt(put_url, Opts,
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
end,
|
||||
<<"http://@HOST@:5444">>),
|
||||
GetURL = gen_mod:get_opt(get_url, Opts,
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
end,
|
||||
PutURL),
|
||||
ServiceURL = gen_mod:get_opt(service_url, Opts,
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
end),
|
||||
Thumbnail = gen_mod:get_opt(thumbnail, Opts,
|
||||
fun(B) when is_boolean(B) -> B end,
|
||||
true),
|
||||
case ServiceURL of
|
||||
undefined ->
|
||||
ok;
|
||||
<<"http://", _/binary>> ->
|
||||
application:start(inets);
|
||||
<<"https://", _/binary>> ->
|
||||
application:start(inets),
|
||||
application:start(crypto),
|
||||
application:start(asn1),
|
||||
application:start(public_key),
|
||||
application:start(ssl)
|
||||
end,
|
||||
case DirMode of
|
||||
undefined ->
|
||||
ok;
|
||||
Mode ->
|
||||
file:change_mode(DocRoot, Mode)
|
||||
end,
|
||||
ejabberd_router:register_route(Host),
|
||||
{ok, #state{server_host = ServerHost, host = Host, name = Name,
|
||||
access = Access, max_size = MaxSize,
|
||||
secret_length = SecretLength, jid_in_url = JIDinURL,
|
||||
file_mode = FileMode, dir_mode = DirMode,
|
||||
thumbnail = Thumbnail,
|
||||
docroot = expand_home(str:strip(DocRoot, right, $/)),
|
||||
put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
|
||||
get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
|
||||
service_url = ServiceURL}}.
|
||||
|
||||
-spec handle_call(_, {pid(), _}, state())
|
||||
-> {reply, {ok, pos_integer(), binary(),
|
||||
pos_integer() | undefined,
|
||||
pos_integer() | undefined}, state()} |
|
||||
{reply, {error, binary()}, state()} | {noreply, state()}.
|
||||
|
||||
handle_call({use_slot, Slot}, _From, #state{file_mode = FileMode,
|
||||
dir_mode = DirMode,
|
||||
get_url = GetPrefix,
|
||||
thumbnail = Thumbnail,
|
||||
docroot = DocRoot} = State) ->
|
||||
case get_slot(Slot, State) of
|
||||
{ok, {Size, Timer}} ->
|
||||
timer:cancel(Timer),
|
||||
NewState = del_slot(Slot, State),
|
||||
Path = str:join([DocRoot | Slot], <<$/>>),
|
||||
{reply, {ok, Size, Path, FileMode, DirMode, GetPrefix, Thumbnail},
|
||||
NewState};
|
||||
error ->
|
||||
{reply, {error, <<"Invalid slot">>}, State}
|
||||
end;
|
||||
handle_call(get_docroot, _From, #state{docroot = DocRoot} = State) ->
|
||||
{reply, {ok, DocRoot}, State};
|
||||
handle_call(Request, From, State) ->
|
||||
?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_cast(_, state()) -> {noreply, state()}.
|
||||
|
||||
handle_cast(Request, State) ->
|
||||
?ERROR_MSG("Got unexpected request: ~p", [Request]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_info(timeout | _, state()) -> {noreply, state()}.
|
||||
|
||||
handle_info({route, From, To, #xmlel{name = <<"iq">>} = Stanza}, State) ->
|
||||
Request = jlib:iq_query_info(Stanza),
|
||||
{Reply, NewState} = case process_iq(From, Request, State) of
|
||||
R when is_record(R, iq) ->
|
||||
{R, State};
|
||||
{R, S} ->
|
||||
{R, S};
|
||||
not_request ->
|
||||
{none, State}
|
||||
end,
|
||||
if Reply /= none ->
|
||||
ejabberd_router:route(To, From, jlib:iq_to_xml(Reply));
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
{noreply, NewState};
|
||||
handle_info({slot_timed_out, Slot}, State) ->
|
||||
NewState = del_slot(Slot, State),
|
||||
{noreply, NewState};
|
||||
handle_info(Info, State) ->
|
||||
?ERROR_MSG("Got unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec terminate(normal | shutdown | {shutdown, _} | _, _) -> ok.
|
||||
|
||||
terminate(Reason, #state{server_host = ServerHost, host = Host}) ->
|
||||
?DEBUG("Stopping HTTP upload process for ~s: ~p", [ServerHost, Reason]),
|
||||
ejabberd_router:unregister_route(Host),
|
||||
ok.
|
||||
|
||||
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
|
||||
|
||||
code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
|
||||
?DEBUG("Updating HTTP upload process for ~s", [ServerHost]),
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% ejabberd_http callback.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec process([binary()], #request{})
|
||||
-> {pos_integer(), [{binary(), binary()}], binary()}.
|
||||
|
||||
process(LocalPath, #request{method = 'PUT', host = Host, ip = IP,
|
||||
data = Data}) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
case catch gen_server:call(Proc, {use_slot, LocalPath}) of
|
||||
{ok, Size, Path, FileMode, DirMode, GetPrefix, Thumbnail}
|
||||
when byte_size(Data) == Size ->
|
||||
?DEBUG("Storing file from ~s for ~s: ~s",
|
||||
[?ADDR_TO_STR(IP), Host, Path]),
|
||||
case store_file(Path, Data, FileMode, DirMode,
|
||||
GetPrefix, LocalPath, Thumbnail) of
|
||||
ok ->
|
||||
http_response(Host, 201);
|
||||
{ok, Headers, OutData} ->
|
||||
http_response(Host, 201, Headers, OutData);
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot store file ~s from ~s for ~s: ~p",
|
||||
[Path, ?ADDR_TO_STR(IP), Host, Error]),
|
||||
http_response(Host, 500)
|
||||
end;
|
||||
{ok, Size, Path} ->
|
||||
?INFO_MSG("Rejecting file ~s from ~s for ~s: Size is ~B, not ~B",
|
||||
[Path, ?ADDR_TO_STR(IP), Host, byte_size(Data), Size]),
|
||||
http_response(Host, 413);
|
||||
{error, Error} ->
|
||||
?INFO_MSG("Rejecting file from ~s for ~s: ~p",
|
||||
[?ADDR_TO_STR(IP), Host, Error]),
|
||||
http_response(Host, 403);
|
||||
Error ->
|
||||
?ERROR_MSG("Cannot handle PUT request from ~s for ~s: ~p",
|
||||
[?ADDR_TO_STR(IP), Host, Error]),
|
||||
http_response(Host, 500)
|
||||
end;
|
||||
process(LocalPath, #request{method = Method, host = Host, ip = IP})
|
||||
when Method == 'GET';
|
||||
Method == 'HEAD' ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
case catch gen_server:call(Proc, get_docroot) of
|
||||
{ok, DocRoot} ->
|
||||
Path = str:join([DocRoot | LocalPath], <<$/>>),
|
||||
case file:read_file(Path) of
|
||||
{ok, Data} ->
|
||||
?INFO_MSG("Serving ~s to ~s", [Path, ?ADDR_TO_STR(IP)]),
|
||||
FileName = lists:last(LocalPath),
|
||||
ContentType = guess_content_type(FileName),
|
||||
Headers1 = case ContentType of
|
||||
<<"image/", _SubType/binary>> -> [];
|
||||
<<"text/", _SubType/binary>> -> [];
|
||||
_ ->
|
||||
[{<<"Content-Disposition">>,
|
||||
<<"attachment; filename=",
|
||||
$", FileName/binary, $">>}]
|
||||
end,
|
||||
Headers2 = [{<<"Content-Type">>, ContentType} | Headers1],
|
||||
http_response(Host, 200, Headers2, Data);
|
||||
{error, eacces} ->
|
||||
?INFO_MSG("Cannot serve ~s to ~s: Permission denied",
|
||||
[Path, ?ADDR_TO_STR(IP)]),
|
||||
http_response(Host, 403);
|
||||
{error, enoent} ->
|
||||
?INFO_MSG("Cannot serve ~s to ~s: No such file or directory",
|
||||
[Path, ?ADDR_TO_STR(IP)]),
|
||||
http_response(Host, 404);
|
||||
{error, eisdir} ->
|
||||
?INFO_MSG("Cannot serve ~s to ~s: Is a directory",
|
||||
[Path, ?ADDR_TO_STR(IP)]),
|
||||
http_response(Host, 404);
|
||||
{error, Error} ->
|
||||
?INFO_MSG("Cannot serve ~s to ~s: ~s",
|
||||
[Path, ?ADDR_TO_STR(IP), ?FORMAT(Error)]),
|
||||
http_response(Host, 500)
|
||||
end;
|
||||
Error ->
|
||||
?ERROR_MSG("Cannot handle ~s request from ~s for ~s: ~p",
|
||||
[Method, ?ADDR_TO_STR(IP), Host, Error]),
|
||||
http_response(Host, 500)
|
||||
end;
|
||||
process(_LocalPath, #request{method = 'OPTIONS', host = Host, ip = IP}) ->
|
||||
?DEBUG("Responding to OPTIONS request from ~s for ~s",
|
||||
[?ADDR_TO_STR(IP), Host]),
|
||||
http_response(Host, 200);
|
||||
process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
|
||||
?DEBUG("Rejecting ~s request from ~s for ~s",
|
||||
[Method, ?ADDR_TO_STR(IP), Host]),
|
||||
http_response(Host, 405, [{<<"Allow">>, <<"OPTIONS, HEAD, GET, PUT">>}]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Exported utility functions.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec get_proc_name(binary(), atom()) -> atom().
|
||||
|
||||
get_proc_name(ServerHost, ModuleName) ->
|
||||
PutURL = gen_mod:get_module_opt(ServerHost, ?MODULE, put_url,
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL;
|
||||
(_) -> <<"http://@HOST@">>
|
||||
end,
|
||||
<<"http://@HOST@">>),
|
||||
[_, ProcHost | _] = binary:split(expand_host(PutURL, ServerHost),
|
||||
[<<"http://">>, <<"https://">>,
|
||||
<<":">>, <<"/">>], [global]),
|
||||
gen_mod:get_module_proc(ProcHost, ModuleName).
|
||||
|
||||
-spec expand_home(binary()) -> binary().
|
||||
|
||||
expand_home(Subject) ->
|
||||
{ok, [[Home]]} = init:get_argument(home),
|
||||
Parts = binary:split(Subject, <<"@HOME@">>, [global]),
|
||||
str:join(Parts, list_to_binary(Home)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% XMPP request handling.
|
||||
|
||||
-spec process_iq(jid(), iq_request() | reply | invalid, state())
|
||||
-> {iq_reply(), state()} | iq_reply() | not_request.
|
||||
|
||||
process_iq(_From,
|
||||
#iq{type = get, xmlns = ?NS_DISCO_INFO, lang = Lang} = IQ,
|
||||
#state{server_host = ServerHost, name = Name}) ->
|
||||
AddInfo = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
|
||||
[ServerHost, ?MODULE, <<"">>, <<"">>]),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [#xmlel{name = <<"query">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}],
|
||||
children = iq_disco_info(Lang, Name) ++ AddInfo}]};
|
||||
process_iq(From,
|
||||
#iq{type = get, xmlns = XMLNS, lang = Lang, sub_el = SubEl} = IQ,
|
||||
#state{server_host = ServerHost, access = Access} = State)
|
||||
when XMLNS == ?NS_HTTP_UPLOAD;
|
||||
XMLNS == ?NS_HTTP_UPLOAD_OLD ->
|
||||
case acl:match_rule(ServerHost, Access, From) of
|
||||
allow ->
|
||||
case parse_request(SubEl, Lang) of
|
||||
{ok, File, Size, ContentType} ->
|
||||
case create_slot(State, From, File, Size, ContentType, Lang) of
|
||||
{ok, Slot} ->
|
||||
{ok, Timer} = timer:send_after(?SLOT_TIMEOUT,
|
||||
{slot_timed_out, Slot}),
|
||||
NewState = add_slot(Slot, Size, Timer, State),
|
||||
SlotEl = slot_el(Slot, State, XMLNS),
|
||||
{IQ#iq{type = result, sub_el = [SlotEl]}, NewState};
|
||||
{ok, PutURL, GetURL} ->
|
||||
SlotEl = slot_el(PutURL, GetURL, XMLNS),
|
||||
IQ#iq{type = result, sub_el = [SlotEl]};
|
||||
{error, Error} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end;
|
||||
{error, Error} ->
|
||||
?DEBUG("Cannot parse request from ~s",
|
||||
[jlib:jid_to_string(From)]),
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end;
|
||||
deny ->
|
||||
?DEBUG("Denying HTTP upload slot request from ~s",
|
||||
[jlib:jid_to_string(From)]),
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}
|
||||
end;
|
||||
process_iq(_From, #iq{sub_el = SubEl} = IQ, _State) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
process_iq(_From, reply, _State) ->
|
||||
not_request;
|
||||
process_iq(_From, invalid, _State) ->
|
||||
not_request.
|
||||
|
||||
-spec parse_request(xmlel(), binary())
|
||||
-> {ok, binary(), pos_integer(), binary()} | {error, xmlel()}.
|
||||
|
||||
parse_request(#xmlel{name = <<"request">>, attrs = Attrs} = Request, Lang) ->
|
||||
case xml:get_attr(<<"xmlns">>, Attrs) of
|
||||
{value, XMLNS} when XMLNS == ?NS_HTTP_UPLOAD;
|
||||
XMLNS == ?NS_HTTP_UPLOAD_OLD ->
|
||||
case {xml:get_subtag_cdata(Request, <<"filename">>),
|
||||
xml:get_subtag_cdata(Request, <<"size">>),
|
||||
xml:get_subtag_cdata(Request, <<"content-type">>)} of
|
||||
{File, SizeStr, ContentType} when byte_size(File) > 0 ->
|
||||
case catch jlib:binary_to_integer(SizeStr) of
|
||||
Size when is_integer(Size), Size > 0 ->
|
||||
{ok, File, Size, yield_content_type(ContentType)};
|
||||
_ ->
|
||||
Text = <<"Please specify file size.">>,
|
||||
{error, ?ERRT_BAD_REQUEST(Lang, Text)}
|
||||
end;
|
||||
_ ->
|
||||
Text = <<"Please specify file name.">>,
|
||||
{error, ?ERRT_BAD_REQUEST(Lang, Text)}
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
parse_request(_El, _Lang) -> {error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
-spec create_slot(state(), jid(), binary(), pos_integer(), binary(), binary())
|
||||
-> {ok, slot()} | {ok, binary(), binary()} | {error, xmlel()}.
|
||||
|
||||
create_slot(#state{service_url = undefined, max_size = MaxSize},
|
||||
JID, File, Size, _ContentType, Lang) when MaxSize /= infinity,
|
||||
Size > MaxSize ->
|
||||
Text = <<"File larger than ", (jlib:integer_to_binary(MaxSize))/binary,
|
||||
" Bytes.">>,
|
||||
?INFO_MSG("Rejecting file ~s from ~s (too large: ~B bytes)",
|
||||
[File, jlib:jid_to_string(JID), Size]),
|
||||
{error, ?ERRT_NOT_ACCEPTABLE(Lang, Text)};
|
||||
create_slot(#state{service_url = undefined,
|
||||
jid_in_url = JIDinURL,
|
||||
secret_length = SecretLength,
|
||||
server_host = ServerHost,
|
||||
docroot = DocRoot},
|
||||
JID, File, Size, _ContentType, Lang) ->
|
||||
UserStr = make_user_string(JID, JIDinURL),
|
||||
UserDir = <<DocRoot/binary, $/, UserStr/binary>>,
|
||||
case ejabberd_hooks:run_fold(http_upload_slot_request, ServerHost, allow,
|
||||
[JID, UserDir, Size, Lang]) of
|
||||
allow ->
|
||||
RandStr = make_rand_string(SecretLength),
|
||||
FileStr = make_file_string(File),
|
||||
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)",
|
||||
[jlib:jid_to_string(JID), File]),
|
||||
{ok, [UserStr, RandStr, FileStr]};
|
||||
deny ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE};
|
||||
#xmlel{} = Error ->
|
||||
{error, Error}
|
||||
end;
|
||||
create_slot(#state{service_url = ServiceURL},
|
||||
#jid{luser = U, lserver = S} = JID, File, Size, ContentType,
|
||||
_Lang) ->
|
||||
Options = [{body_format, binary}, {full_result, false}],
|
||||
HttpOptions = [{timeout, ?SERVICE_REQUEST_TIMEOUT}],
|
||||
SizeStr = jlib:integer_to_binary(Size),
|
||||
GetRequest = binary_to_list(ServiceURL) ++
|
||||
"?jid=" ++ ?URL_ENC(jlib:jid_to_string({U, S, <<"">>})) ++
|
||||
"&name=" ++ ?URL_ENC(File) ++
|
||||
"&size=" ++ ?URL_ENC(SizeStr) ++
|
||||
"&content_type=" ++ ?URL_ENC(ContentType),
|
||||
case httpc:request(get, {GetRequest, []}, HttpOptions, Options) of
|
||||
{ok, {Code, Body}} when Code >= 200, Code =< 299 ->
|
||||
case binary:split(Body, <<$\n>>, [global, trim]) of
|
||||
[<<"http", _/binary>> = PutURL, <<"http", _/binary>> = GetURL] ->
|
||||
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)",
|
||||
[jlib:jid_to_string(JID), File]),
|
||||
{ok, PutURL, GetURL};
|
||||
Lines ->
|
||||
?ERROR_MSG("Cannot parse data received for ~s from <~s>: ~p",
|
||||
[jlib:jid_to_string(JID), ServiceURL, Lines]),
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}
|
||||
end;
|
||||
{ok, {402, _Body}} ->
|
||||
?INFO_MSG("Got status code 402 for ~s from <~s>",
|
||||
[jlib:jid_to_string(JID), ServiceURL]),
|
||||
{error, ?ERR_RESOURCE_CONSTRAINT};
|
||||
{ok, {403, _Body}} ->
|
||||
?INFO_MSG("Got status code 403 for ~s from <~s>",
|
||||
[jlib:jid_to_string(JID), ServiceURL]),
|
||||
{error, ?ERR_NOT_ALLOWED};
|
||||
{ok, {413, _Body}} ->
|
||||
?INFO_MSG("Got status code 413 for ~s from <~s>",
|
||||
[jlib:jid_to_string(JID), ServiceURL]),
|
||||
{error, ?ERR_NOT_ACCEPTABLE};
|
||||
{ok, {Code, _Body}} ->
|
||||
?ERROR_MSG("Got unexpected status code for ~s from <~s>: ~B",
|
||||
[jlib:jid_to_string(JID), ServiceURL, Code]),
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE};
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Error requesting upload slot for ~s from <~s>: ~p",
|
||||
[jlib:jid_to_string(JID), ServiceURL, Reason]),
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}
|
||||
end.
|
||||
|
||||
-spec add_slot(slot(), pos_integer(), timer:tref(), state()) -> state().
|
||||
|
||||
add_slot(Slot, Size, Timer, #state{slots = Slots} = State) ->
|
||||
NewSlots = dict:store(Slot, {Size, Timer}, Slots),
|
||||
State#state{slots = NewSlots}.
|
||||
|
||||
-spec get_slot(slot(), state()) -> {ok, {pos_integer(), timer:tref()}} | error.
|
||||
|
||||
get_slot(Slot, #state{slots = Slots}) ->
|
||||
dict:find(Slot, Slots).
|
||||
|
||||
-spec del_slot(slot(), state()) -> state().
|
||||
|
||||
del_slot(Slot, #state{slots = Slots} = State) ->
|
||||
NewSlots = dict:erase(Slot, Slots),
|
||||
State#state{slots = NewSlots}.
|
||||
|
||||
-spec slot_el(slot() | binary(), state() | binary(), binary()) -> xmlel().
|
||||
|
||||
slot_el(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS) ->
|
||||
PutURL = str:join([PutPrefix | Slot], <<$/>>),
|
||||
GetURL = str:join([GetPrefix | Slot], <<$/>>),
|
||||
slot_el(PutURL, GetURL, XMLNS);
|
||||
slot_el(PutURL, GetURL, XMLNS) ->
|
||||
#xmlel{name = <<"slot">>,
|
||||
attrs = [{<<"xmlns">>, XMLNS}],
|
||||
children = [#xmlel{name = <<"put">>,
|
||||
children = [{xmlcdata, PutURL}]},
|
||||
#xmlel{name = <<"get">>,
|
||||
children = [{xmlcdata, GetURL}]}]}.
|
||||
|
||||
-spec make_user_string(jid(), sha1 | node) -> binary().
|
||||
|
||||
make_user_string(#jid{luser = U, lserver = S}, sha1) ->
|
||||
p1_sha:sha(<<U/binary, $@, S/binary>>);
|
||||
make_user_string(#jid{luser = U}, node) ->
|
||||
re:replace(U, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
|
||||
|
||||
-spec make_file_string(binary()) -> binary().
|
||||
|
||||
make_file_string(File) ->
|
||||
re:replace(File, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
|
||||
|
||||
-spec make_rand_string(non_neg_integer()) -> binary().
|
||||
|
||||
make_rand_string(Length) ->
|
||||
list_to_binary(make_rand_string([], Length)).
|
||||
|
||||
-spec make_rand_string(string(), non_neg_integer()) -> string().
|
||||
|
||||
make_rand_string(S, 0) -> S;
|
||||
make_rand_string(S, N) -> make_rand_string([make_rand_char() | S], N - 1).
|
||||
|
||||
-spec make_rand_char() -> char().
|
||||
|
||||
make_rand_char() ->
|
||||
map_int_to_char(crypto:rand_uniform(0, 62)).
|
||||
|
||||
-spec map_int_to_char(0..61) -> char().
|
||||
|
||||
map_int_to_char(N) when N =< 9 -> N + 48; % Digit.
|
||||
map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character.
|
||||
map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
|
||||
|
||||
-spec expand_host(binary(), binary()) -> binary().
|
||||
|
||||
expand_host(Subject, Host) ->
|
||||
Parts = binary:split(Subject, <<"@HOST@">>, [global]),
|
||||
str:join(Parts, Host).
|
||||
|
||||
-spec yield_content_type(binary()) -> binary().
|
||||
|
||||
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
|
||||
yield_content_type(Type) -> Type.
|
||||
|
||||
-spec iq_disco_info(binary(), binary()) -> [xmlel()].
|
||||
|
||||
iq_disco_info(Lang, Name) ->
|
||||
[#xmlel{name = <<"identity">>,
|
||||
attrs = [{<<"category">>, <<"store">>},
|
||||
{<<"type">>, <<"file">>},
|
||||
{<<"name">>, translate:translate(Lang, Name)}]},
|
||||
#xmlel{name = <<"feature">>,
|
||||
attrs = [{<<"var">>, ?NS_HTTP_UPLOAD}]},
|
||||
#xmlel{name = <<"feature">>,
|
||||
attrs = [{<<"var">>, ?NS_HTTP_UPLOAD_OLD}]}].
|
||||
|
||||
%% HTTP request handling.
|
||||
|
||||
-spec store_file(file:filename_all(), binary(),
|
||||
integer() | undefined,
|
||||
integer() | undefined,
|
||||
binary(), binary(), boolean())
|
||||
-> ok | {ok, [{binary(), binary()}], binary()} | {error, term()}.
|
||||
|
||||
store_file(Path, Data, FileMode, DirMode, GetPrefix, LocalPath, Thumbnail) ->
|
||||
case do_store_file(Path, Data, FileMode, DirMode) of
|
||||
ok when Thumbnail ->
|
||||
case identify(Path) of
|
||||
{ok, MediaInfo} ->
|
||||
case convert(Path, MediaInfo) of
|
||||
pass ->
|
||||
ok;
|
||||
{ok, OutPath} ->
|
||||
[UserDir, RandDir|_] = LocalPath,
|
||||
FileName = filename:basename(OutPath),
|
||||
URL = str:join([GetPrefix, UserDir,
|
||||
RandDir, FileName], <<$/>>),
|
||||
ThumbEl = thumb_el(OutPath, URL),
|
||||
{ok,
|
||||
[{<<"Content-Type">>,
|
||||
<<"text/xml; charset=utf-8">>}],
|
||||
xml:element_to_binary(ThumbEl)}
|
||||
end;
|
||||
{error, _} ->
|
||||
ok
|
||||
end;
|
||||
ok ->
|
||||
ok;
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
-spec do_store_file(file:filename_all(), binary(),
|
||||
integer() | undefined,
|
||||
integer() | undefined)
|
||||
-> ok | {error, term()}.
|
||||
|
||||
do_store_file(Path, Data, FileMode, DirMode) ->
|
||||
try
|
||||
ok = filelib:ensure_dir(Path),
|
||||
{ok, Io} = file:open(Path, [write, exclusive, raw]),
|
||||
Ok = file:write(Io, Data),
|
||||
ok = file:close(Io),
|
||||
if is_integer(FileMode) ->
|
||||
ok = file:change_mode(Path, FileMode);
|
||||
FileMode == undefined ->
|
||||
ok
|
||||
end,
|
||||
if is_integer(DirMode) ->
|
||||
RandDir = filename:dirname(Path),
|
||||
UserDir = filename:dirname(RandDir),
|
||||
ok = file:change_mode(RandDir, DirMode),
|
||||
ok = file:change_mode(UserDir, DirMode);
|
||||
DirMode == undefined ->
|
||||
ok
|
||||
end,
|
||||
ok = Ok % Raise an exception if file:write/2 failed.
|
||||
catch
|
||||
_:{badmatch, {error, Error}} ->
|
||||
{error, ?FORMAT(Error)};
|
||||
_:Error ->
|
||||
{error, Error}
|
||||
end.
|
||||
|
||||
-spec guess_content_type(binary()) -> binary().
|
||||
|
||||
guess_content_type(FileName) ->
|
||||
mod_http_fileserver:content_type(FileName,
|
||||
?DEFAULT_CONTENT_TYPE,
|
||||
?CONTENT_TYPES).
|
||||
|
||||
-spec http_response(binary(), 100..599)
|
||||
-> {pos_integer(), [{binary(), binary()}], binary()}.
|
||||
|
||||
http_response(Host, Code) ->
|
||||
http_response(Host, Code, []).
|
||||
|
||||
-spec http_response(binary(), 100..599, [{binary(), binary()}])
|
||||
-> {pos_integer(), [{binary(), binary()}], binary()}.
|
||||
|
||||
http_response(Host, Code, ExtraHeaders) ->
|
||||
Message = <<(code_to_message(Code))/binary, $\n>>,
|
||||
http_response(Host, Code, ExtraHeaders, Message).
|
||||
|
||||
-spec http_response(binary(), 100..599, [{binary(), binary()}], binary())
|
||||
-> {pos_integer(), [{binary(), binary()}], binary()}.
|
||||
|
||||
http_response(Host, Code, ExtraHeaders, Body) ->
|
||||
ServerHeader = {<<"Server">>, <<"ejabberd ", (?VERSION)/binary>>},
|
||||
CustomHeaders =
|
||||
gen_mod:get_module_opt(Host, ?MODULE, custom_headers,
|
||||
fun(Headers) ->
|
||||
lists:map(fun({K, V}) ->
|
||||
{iolist_to_binary(K),
|
||||
iolist_to_binary(V)}
|
||||
end, Headers)
|
||||
end,
|
||||
[]),
|
||||
Headers = case proplists:is_defined(<<"Content-Type">>, ExtraHeaders) of
|
||||
true ->
|
||||
[ServerHeader | ExtraHeaders];
|
||||
false ->
|
||||
[ServerHeader, {<<"Content-Type">>, <<"text/plain">>} |
|
||||
ExtraHeaders]
|
||||
end ++ CustomHeaders,
|
||||
{Code, Headers, Body}.
|
||||
|
||||
-spec code_to_message(100..599) -> binary().
|
||||
|
||||
code_to_message(201) -> <<"Upload successful.">>;
|
||||
code_to_message(403) -> <<"Forbidden.">>;
|
||||
code_to_message(404) -> <<"Not found.">>;
|
||||
code_to_message(405) -> <<"Method not allowed.">>;
|
||||
code_to_message(413) -> <<"File size doesn't match requested size.">>;
|
||||
code_to_message(500) -> <<"Internal server error.">>;
|
||||
code_to_message(_Code) -> <<"">>.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Image manipulation stuff
|
||||
%%--------------------------------------------------------------------
|
||||
-spec identify(string()) -> {ok, media_info()} | {error, string()}.
|
||||
|
||||
identify(Path) ->
|
||||
Cmd = lists:flatten(io_lib:fwrite("identify -format \"ok %m %h %w\" ~s",
|
||||
[Path])),
|
||||
Res = string:strip(os:cmd(Cmd), right, $\n),
|
||||
case string:tokens(Res, " ") of
|
||||
["ok", T, H, W] ->
|
||||
{ok, #media_info{
|
||||
type = string:to_lower(T),
|
||||
height = list_to_integer(H),
|
||||
width = list_to_integer(W)}};
|
||||
_ ->
|
||||
?ERROR_MSG("failed to identify type of ~s: ~s", [Path, Res]),
|
||||
{error, Res}
|
||||
end.
|
||||
|
||||
-spec convert(string(), media_info()) -> {ok, string()} | pass.
|
||||
|
||||
convert(Path, #media_info{type = T, width = W, height = H}) ->
|
||||
if W*H >= 25000000 ->
|
||||
?DEBUG("the image ~s is more than 25 Mbpx", [Path]),
|
||||
pass;
|
||||
(W =< 300) and (H =< 300) ->
|
||||
{ok, Path};
|
||||
T == "gif"; T == "jpeg"; T == "png"; T == "webp" ->
|
||||
Dir = filename:dirname(Path),
|
||||
FileName = binary_to_list(randoms:get_string()) ++ "." ++ T,
|
||||
OutPath = filename:join(Dir, FileName),
|
||||
Cmd = lists:flatten(io_lib:fwrite("convert -resize 300 ~s ~s",
|
||||
[Path, OutPath])),
|
||||
case os:cmd(Cmd) of
|
||||
"" ->
|
||||
{ok, OutPath};
|
||||
Err ->
|
||||
?ERROR_MSG("failed to convert ~s to ~s: ~s",
|
||||
[Path, OutPath, string:strip(Err, right, $\n)]),
|
||||
pass
|
||||
end;
|
||||
true ->
|
||||
?DEBUG("do not call 'convert' for unknown type ~s", [T]),
|
||||
pass
|
||||
end.
|
||||
|
||||
-spec thumb_el(string(), binary()) -> xmlel().
|
||||
|
||||
thumb_el(Path, URI) ->
|
||||
ContentType = guess_content_type(list_to_binary(Path)),
|
||||
case identify(Path) of
|
||||
{ok, #media_info{height = H, width = W}} ->
|
||||
#xmlel{name = <<"thumbnail">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_THUMBS_1},
|
||||
{<<"media-type">>, ContentType},
|
||||
{<<"uri">>, URI},
|
||||
{<<"height">>, jlib:integer_to_binary(H)},
|
||||
{<<"width">>, jlib:integer_to_binary(W)}]};
|
||||
{error, _} ->
|
||||
#xmlel{name = <<"thumbnail">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_THUMBS_1},
|
||||
{<<"uri">>, URI},
|
||||
{<<"media-type">>, ContentType}]}
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Remove user.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec remove_user(binary(), binary()) -> ok.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
ServerHost = jlib:nameprep(Server),
|
||||
DocRoot = gen_mod:get_module_opt(ServerHost, ?MODULE, docroot,
|
||||
fun iolist_to_binary/1,
|
||||
<<"@HOME@/upload">>),
|
||||
JIDinURL = gen_mod:get_module_opt(ServerHost, ?MODULE, jid_in_url,
|
||||
fun(sha1) -> sha1;
|
||||
(node) -> node
|
||||
end,
|
||||
sha1),
|
||||
UserStr = make_user_string(jlib:make_jid(User, Server, <<"">>), JIDinURL),
|
||||
UserDir = str:join([expand_home(DocRoot), UserStr], <<$/>>),
|
||||
case del_tree(UserDir) of
|
||||
ok ->
|
||||
?INFO_MSG("Removed HTTP upload directory of ~s@~s", [User, Server]);
|
||||
{error, enoent} ->
|
||||
?DEBUG("Found no HTTP upload directory of ~s@~s", [User, Server]);
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot remove HTTP upload directory of ~s@~s: ~p",
|
||||
[User, Server, Error])
|
||||
end,
|
||||
ok.
|
||||
|
||||
-spec del_tree(file:filename_all()) -> ok | {error, term()}.
|
||||
|
||||
del_tree(Dir) when is_binary(Dir) ->
|
||||
del_tree(binary_to_list(Dir));
|
||||
del_tree(Dir) ->
|
||||
try
|
||||
{ok, Entries} = file:list_dir(Dir),
|
||||
lists:foreach(fun(Path) ->
|
||||
case filelib:is_dir(Path) of
|
||||
true ->
|
||||
ok = del_tree(Path);
|
||||
false ->
|
||||
ok = file:delete(Path)
|
||||
end
|
||||
end, [Dir ++ "/" ++ Entry || Entry <- Entries]),
|
||||
ok = file:del_dir(Dir)
|
||||
catch
|
||||
_:{badmatch, {error, Error}} ->
|
||||
{error, ?FORMAT(Error)};
|
||||
_:Error ->
|
||||
{error, Error}
|
||||
end.
|
||||
@@ -0,0 +1,345 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : mod_http_upload_quota.erl
|
||||
%%% Author : Holger Weiss <holger@zedat.fu-berlin.de>
|
||||
%%% Purpose : Quota management for HTTP File Upload (XEP-0363)
|
||||
%%% Created : 15 Oct 2015 by Holger Weiss <holger@zedat.fu-berlin.de>
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(mod_http_upload_quota).
|
||||
-author('holger@zedat.fu-berlin.de').
|
||||
|
||||
-define(GEN_SERVER, gen_server).
|
||||
-define(PROCNAME, ?MODULE).
|
||||
-define(TIMEOUT, timer:hours(24)).
|
||||
-define(INITIAL_TIMEOUT, timer:minutes(10)).
|
||||
-define(FORMAT(Error), file:format_error(Error)).
|
||||
|
||||
-behaviour(?GEN_SERVER).
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% gen_mod/supervisor callbacks.
|
||||
-export([start_link/3,
|
||||
start/2,
|
||||
stop/1,
|
||||
mod_opt_type/1]).
|
||||
|
||||
%% gen_server callbacks.
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3]).
|
||||
|
||||
%% ejabberd_hooks callback.
|
||||
-export([handle_slot_request/5]).
|
||||
|
||||
-include("jlib.hrl").
|
||||
-include("logger.hrl").
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
-record(state,
|
||||
{server_host :: binary(),
|
||||
access_soft_quota :: atom(),
|
||||
access_hard_quota :: atom(),
|
||||
max_days :: pos_integer() | infinity,
|
||||
docroot :: binary(),
|
||||
disk_usage = dict:new() :: term(),
|
||||
timers :: [timer:tref()]}).
|
||||
|
||||
-type state() :: #state{}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_mod/supervisor callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec start_link(binary(), atom(), gen_mod:opts())
|
||||
-> {ok, pid()} | ignore | {error, _}.
|
||||
|
||||
start_link(ServerHost, Proc, Opts) ->
|
||||
?GEN_SERVER:start_link({local, Proc}, ?MODULE, {ServerHost, Opts}, []).
|
||||
|
||||
-spec start(binary(), gen_mod:opts()) -> {ok, _} | {ok, _, _} | {error, _}.
|
||||
|
||||
start(ServerHost, Opts) ->
|
||||
Proc = mod_http_upload:get_proc_name(ServerHost, ?PROCNAME),
|
||||
Spec = {Proc,
|
||||
{?MODULE, start_link, [ServerHost, Proc, Opts]},
|
||||
permanent,
|
||||
3000,
|
||||
worker,
|
||||
[?MODULE]},
|
||||
supervisor:start_child(ejabberd_sup, Spec).
|
||||
|
||||
-spec stop(binary()) -> ok.
|
||||
|
||||
stop(ServerHost) ->
|
||||
Proc = mod_http_upload:get_proc_name(ServerHost, ?PROCNAME),
|
||||
ok = supervisor:terminate_child(ejabberd_sup, Proc),
|
||||
ok = supervisor:delete_child(ejabberd_sup, Proc).
|
||||
|
||||
-spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
|
||||
|
||||
mod_opt_type(access_soft_quota) ->
|
||||
fun(A) when is_atom(A) -> A end;
|
||||
mod_opt_type(access_hard_quota) ->
|
||||
fun(A) when is_atom(A) -> A end;
|
||||
mod_opt_type(max_days) ->
|
||||
fun(I) when is_integer(I), I > 0 -> I;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[access_soft_quota, access_hard_quota, max_days].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec init({binary(), gen_mod:opts()}) -> {ok, state()}.
|
||||
|
||||
init({ServerHost, Opts}) ->
|
||||
process_flag(trap_exit, true),
|
||||
AccessSoftQuota = gen_mod:get_opt(access_soft_quota, Opts,
|
||||
fun(A) when is_atom(A) -> A end,
|
||||
soft_upload_quota),
|
||||
AccessHardQuota = gen_mod:get_opt(access_hard_quota, Opts,
|
||||
fun(A) when is_atom(A) -> A end,
|
||||
hard_upload_quota),
|
||||
MaxDays = gen_mod:get_opt(max_days, Opts,
|
||||
fun(I) when is_integer(I), I > 0 -> I;
|
||||
(infinity) -> infinity
|
||||
end,
|
||||
infinity),
|
||||
DocRoot1 = gen_mod:get_module_opt(ServerHost, mod_http_upload, docroot,
|
||||
fun iolist_to_binary/1,
|
||||
<<"@HOME@/upload">>),
|
||||
DocRoot2 = mod_http_upload:expand_home(str:strip(DocRoot1, right, $/)),
|
||||
Timers = if MaxDays == infinity -> [];
|
||||
true ->
|
||||
{ok, T1} = timer:send_after(?INITIAL_TIMEOUT, sweep),
|
||||
{ok, T2} = timer:send_interval(?TIMEOUT, sweep),
|
||||
[T1, T2]
|
||||
end,
|
||||
ejabberd_hooks:add(http_upload_slot_request, ServerHost, ?MODULE,
|
||||
handle_slot_request, 50),
|
||||
{ok, #state{server_host = ServerHost,
|
||||
access_soft_quota = AccessSoftQuota,
|
||||
access_hard_quota = AccessHardQuota,
|
||||
max_days = MaxDays,
|
||||
docroot = DocRoot2,
|
||||
timers = Timers}}.
|
||||
|
||||
-spec handle_call(_, {pid(), _}, state()) -> {noreply, state()}.
|
||||
|
||||
handle_call(Request, From, State) ->
|
||||
?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_cast(_, state()) -> {noreply, state()}.
|
||||
|
||||
handle_cast({handle_slot_request, #jid{user = U, server = S} = JID, Path, Size},
|
||||
#state{server_host = ServerHost,
|
||||
access_soft_quota = AccessSoftQuota,
|
||||
access_hard_quota = AccessHardQuota,
|
||||
disk_usage = DiskUsage} = State) ->
|
||||
HardQuota = case acl:match_rule(ServerHost, AccessHardQuota, JID) of
|
||||
Hard when is_integer(Hard), Hard > 0 ->
|
||||
Hard * 1024 * 1024;
|
||||
_ ->
|
||||
0
|
||||
end,
|
||||
SoftQuota = case acl:match_rule(ServerHost, AccessSoftQuota, JID) of
|
||||
Soft when is_integer(Soft), Soft > 0 ->
|
||||
Soft * 1024 * 1024;
|
||||
_ ->
|
||||
0
|
||||
end,
|
||||
OldSize = case dict:find({U, S}, DiskUsage) of
|
||||
{ok, Value} ->
|
||||
Value;
|
||||
error ->
|
||||
undefined
|
||||
end,
|
||||
NewSize = case {HardQuota, SoftQuota} of
|
||||
{0, 0} ->
|
||||
?DEBUG("No quota specified for ~s",
|
||||
[jlib:jid_to_string(JID)]),
|
||||
Size;
|
||||
{0, _} ->
|
||||
?WARNING_MSG("No hard quota specified for ~s",
|
||||
[jlib:jid_to_string(JID)]),
|
||||
enforce_quota(Path, Size, OldSize, SoftQuota, SoftQuota);
|
||||
{_, 0} ->
|
||||
?WARNING_MSG("No soft quota specified for ~s",
|
||||
[jlib:jid_to_string(JID)]),
|
||||
enforce_quota(Path, Size, OldSize, HardQuota, HardQuota);
|
||||
_ when SoftQuota > HardQuota ->
|
||||
?WARNING_MSG("Bad quota for ~s (soft: ~p, hard: ~p)",
|
||||
[jlib:jid_to_string(JID),
|
||||
SoftQuota, HardQuota]),
|
||||
enforce_quota(Path, Size, OldSize, SoftQuota, SoftQuota);
|
||||
_ ->
|
||||
?DEBUG("Enforcing quota for ~s",
|
||||
[jlib:jid_to_string(JID)]),
|
||||
enforce_quota(Path, Size, OldSize, SoftQuota, HardQuota)
|
||||
end,
|
||||
{noreply, State#state{disk_usage = dict:store({U, S}, NewSize, DiskUsage)}};
|
||||
handle_cast(Request, State) ->
|
||||
?ERROR_MSG("Got unexpected request: ~p", [Request]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_info(_, state()) -> {noreply, state()}.
|
||||
|
||||
handle_info(sweep, #state{server_host = ServerHost,
|
||||
docroot = DocRoot,
|
||||
max_days = MaxDays} = State)
|
||||
when is_integer(MaxDays), MaxDays > 0 ->
|
||||
?DEBUG("Got 'sweep' message for ~s", [ServerHost]),
|
||||
case file:list_dir(DocRoot) of
|
||||
{ok, Entries} ->
|
||||
BackThen = secs_since_epoch() - (MaxDays * 86400),
|
||||
DocRootS = binary_to_list(DocRoot),
|
||||
PathNames = lists:map(fun(Entry) -> DocRootS ++ "/" ++ Entry end,
|
||||
Entries),
|
||||
UserDirs = lists:filter(fun filelib:is_dir/1, PathNames),
|
||||
lists:foreach(fun(UserDir) -> delete_old_files(UserDir, BackThen) end,
|
||||
UserDirs);
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot open document root ~s: ~s",
|
||||
[DocRoot, ?FORMAT(Error)])
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info(Info, State) ->
|
||||
?ERROR_MSG("Got unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec terminate(normal | shutdown | {shutdown, _} | _, _) -> ok.
|
||||
|
||||
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).
|
||||
|
||||
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
|
||||
|
||||
code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
|
||||
?DEBUG("Updating upload quota process for ~s", [ServerHost]),
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% ejabberd_hooks callback.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec handle_slot_request(term(), jid(), binary(), non_neg_integer(), binary())
|
||||
-> term().
|
||||
|
||||
handle_slot_request(allow, #jid{lserver = ServerHost} = JID, Path, Size,
|
||||
_Lang) ->
|
||||
Proc = mod_http_upload:get_proc_name(ServerHost, ?PROCNAME),
|
||||
?GEN_SERVER:cast(Proc, {handle_slot_request, JID, Path, Size}),
|
||||
allow;
|
||||
handle_slot_request(Acc, _JID, _Path, _Size, _Lang) -> Acc.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec enforce_quota(file:filename_all(), non_neg_integer(),
|
||||
non_neg_integer() | undefined, non_neg_integer(),
|
||||
non_neg_integer())
|
||||
-> non_neg_integer().
|
||||
|
||||
enforce_quota(_UserDir, SlotSize, OldSize, _MinSize, MaxSize)
|
||||
when is_integer(OldSize), OldSize + SlotSize =< MaxSize ->
|
||||
OldSize + SlotSize;
|
||||
enforce_quota(UserDir, SlotSize, _OldSize, MinSize, MaxSize) ->
|
||||
Files = lists:sort(fun({_PathA, _SizeA, TimeA}, {_PathB, _SizeB, TimeB}) ->
|
||||
TimeA > TimeB
|
||||
end, gather_file_info(UserDir)),
|
||||
{DelFiles, OldSize, NewSize} =
|
||||
lists:foldl(fun({_Path, Size, _Time}, {[], AccSize, AccSize})
|
||||
when AccSize + Size + SlotSize =< MinSize ->
|
||||
{[], AccSize + Size, AccSize + Size};
|
||||
({Path, Size, _Time}, {[], AccSize, AccSize}) ->
|
||||
{[Path], AccSize + Size, AccSize};
|
||||
({Path, Size, _Time}, {AccFiles, AccSize, NewSize}) ->
|
||||
{[Path | AccFiles], AccSize + Size, NewSize}
|
||||
end, {[], 0, 0}, Files),
|
||||
if OldSize + SlotSize > MaxSize ->
|
||||
lists:foreach(fun(File) -> del_file_and_dir(File) end, DelFiles),
|
||||
file:del_dir(UserDir), % In case it's empty, now.
|
||||
NewSize + SlotSize;
|
||||
true ->
|
||||
OldSize + SlotSize
|
||||
end.
|
||||
|
||||
-spec delete_old_files(file:filename_all(), integer()) -> ok.
|
||||
|
||||
delete_old_files(UserDir, Timestamp) ->
|
||||
FileInfo = gather_file_info(UserDir),
|
||||
case [Path || {Path, _Size, Time} <- FileInfo, Time < Timestamp] of
|
||||
[] ->
|
||||
ok;
|
||||
OldFiles ->
|
||||
lists:foreach(fun(File) -> del_file_and_dir(File) end, OldFiles),
|
||||
file:del_dir(UserDir) % In case it's empty, now.
|
||||
end.
|
||||
|
||||
-spec gather_file_info(file:filename_all())
|
||||
-> [{binary(), non_neg_integer(), non_neg_integer()}].
|
||||
|
||||
gather_file_info(Dir) when is_binary(Dir) ->
|
||||
gather_file_info(binary_to_list(Dir));
|
||||
gather_file_info(Dir) ->
|
||||
case file:list_dir(Dir) of
|
||||
{ok, Entries} ->
|
||||
lists:foldl(fun(Entry, Acc) ->
|
||||
Path = Dir ++ "/" ++ Entry,
|
||||
case file:read_file_info(Path, [{time, posix}]) of
|
||||
{ok, #file_info{type = directory}} ->
|
||||
gather_file_info(Path) ++ Acc;
|
||||
{ok, #file_info{type = regular,
|
||||
mtime = Time,
|
||||
size = Size}} ->
|
||||
[{Path, Size, Time} | Acc];
|
||||
{ok, _Info} ->
|
||||
?DEBUG("Won't stat(2) non-regular file ~s",
|
||||
[Path]),
|
||||
Acc;
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot stat(2) ~s: ~s",
|
||||
[Path, ?FORMAT(Error)]),
|
||||
Acc
|
||||
end
|
||||
end, [], Entries);
|
||||
{error, enoent} ->
|
||||
?DEBUG("Directory ~s doesn't exist", [Dir]),
|
||||
[];
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot open directory ~s: ~s", [Dir, ?FORMAT(Error)]),
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec del_file_and_dir(file:name_all()) -> ok.
|
||||
|
||||
del_file_and_dir(File) ->
|
||||
case file:delete(File) of
|
||||
ok ->
|
||||
?INFO_MSG("Removed ~s", [File]),
|
||||
Dir = filename:dirname(File),
|
||||
case file:del_dir(Dir) of
|
||||
ok ->
|
||||
?DEBUG("Removed ~s", [Dir]);
|
||||
{error, Error} ->
|
||||
?INFO_MSG("Cannot remove ~s: ~s", [Dir, ?FORMAT(Error)])
|
||||
end;
|
||||
{error, Error} ->
|
||||
?WARNING_MSG("Cannot remove ~s: ~s", [File, ?FORMAT(Error)])
|
||||
end.
|
||||
|
||||
-spec secs_since_epoch() -> non_neg_integer().
|
||||
|
||||
secs_since_epoch() ->
|
||||
{MegaSecs, Secs, _MicroSecs} = os:timestamp(),
|
||||
MegaSecs * 1000000 + Secs.
|
||||
@@ -784,8 +784,6 @@ set_form(ServerHost, Host, From, [], _Lang, XData) ->
|
||||
set_form(_ServerHost, _Host, _, _, _Lang, _XData) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
%% Host = "irc.example.com"
|
||||
%% ServerHost = "example.com"
|
||||
get_connection_params(Host, From, IRCServer) ->
|
||||
[_ | HostTail] = str:tokens(Host, <<".">>),
|
||||
ServerHost = str:join(HostTail, <<".">>),
|
||||
|
||||
+300
-267
@@ -43,27 +43,27 @@
|
||||
-include("mod_muc_room.hrl").
|
||||
|
||||
-record(archive_msg,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2',
|
||||
id = <<>> :: binary() | '_',
|
||||
timestamp = now() :: erlang:timestamp() | '_' | '$1',
|
||||
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
||||
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2',
|
||||
id = <<>> :: binary() | '_',
|
||||
timestamp = now() :: erlang:timestamp() | '_' | '$1',
|
||||
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
||||
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
||||
packet = #xmlel{} :: xmlel() | '_',
|
||||
nick = <<"">> :: binary(),
|
||||
type = chat :: chat | groupchat}).
|
||||
|
||||
-record(archive_prefs,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
default = never :: never | always | roster,
|
||||
always = [] :: [ljid()],
|
||||
never = [] :: [ljid()]}).
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
default = never :: never | always | roster,
|
||||
always = [] :: [ljid()],
|
||||
never = [] :: [ljid()]}).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||
one_queue),
|
||||
one_queue),
|
||||
DBType = gen_mod:db_type(Host, Opts),
|
||||
init_db(DBType, Host),
|
||||
init_cache(DBType, Opts),
|
||||
@@ -80,9 +80,9 @@ start(Host, Opts) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_MAM_1, ?MODULE, process_iq_v0_3, IQDisc),
|
||||
ejabberd_hooks:add(user_receive_packet, Host, ?MODULE,
|
||||
user_receive_packet, 500),
|
||||
user_receive_packet, 500),
|
||||
ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
|
||||
user_send_packet, 500),
|
||||
user_send_packet, 500),
|
||||
ejabberd_hooks:add(muc_filter_message, Host, ?MODULE,
|
||||
muc_filter_message, 50),
|
||||
ejabberd_hooks:add(muc_process_iq, Host, ?MODULE,
|
||||
@@ -95,21 +95,21 @@ start(Host, Opts) ->
|
||||
|
||||
init_db(mnesia, _Host) ->
|
||||
mnesia:create_table(archive_msg,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, archive_msg)}]),
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, archive_msg)}]),
|
||||
mnesia:create_table(archive_prefs,
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes, record_info(fields, archive_prefs)}]);
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes, record_info(fields, archive_prefs)}]);
|
||||
init_db(_, _) ->
|
||||
ok.
|
||||
|
||||
init_cache(_DBType, Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts,
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
1000),
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
1000),
|
||||
LifeTime = gen_mod:get_opt(cache_life_time, Opts,
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
timer:hours(1) div 1000),
|
||||
cache_tab:new(archive_prefs, [{max_size, MaxSize},
|
||||
{life_time, LifeTime}]).
|
||||
@@ -144,9 +144,9 @@ remove_user(User, Server) ->
|
||||
remove_user(LUser, LServer, mnesia) ->
|
||||
US = {LUser, LServer},
|
||||
F = fun () ->
|
||||
mnesia:delete({archive_msg, US}),
|
||||
mnesia:delete({archive_prefs, US})
|
||||
end,
|
||||
mnesia:delete({archive_msg, US}),
|
||||
mnesia:delete({archive_prefs, US})
|
||||
end,
|
||||
mnesia:transaction(F);
|
||||
remove_user(LUser, LServer, odbc) ->
|
||||
SUser = ejabberd_odbc:escape(LUser),
|
||||
@@ -157,29 +157,30 @@ remove_user(LUser, LServer, odbc) ->
|
||||
LServer,
|
||||
[<<"delete from archive_prefs where username='">>, SUser, <<"';">>]).
|
||||
|
||||
user_receive_packet(Pkt, C2SState, JID, Peer, _To) ->
|
||||
user_receive_packet(Pkt, C2SState, JID, Peer, To) ->
|
||||
LUser = JID#jid.luser,
|
||||
LServer = JID#jid.lserver,
|
||||
IsBareCopy = is_bare_copy(JID, To),
|
||||
case should_archive(Pkt) of
|
||||
true ->
|
||||
NewPkt = strip_my_archived_tag(Pkt, LServer),
|
||||
true when not IsBareCopy ->
|
||||
NewPkt = strip_my_archived_tag(Pkt, LServer),
|
||||
case store_msg(C2SState, NewPkt, LUser, LServer, Peer, recv) of
|
||||
{ok, ID} ->
|
||||
Archived = #xmlel{name = <<"archived">>,
|
||||
attrs = [{<<"by">>, LServer},
|
||||
{<<"xmlns">>, ?NS_MAM_TMP},
|
||||
{<<"id">>, ID}]},
|
||||
{ok, ID} ->
|
||||
Archived = #xmlel{name = <<"archived">>,
|
||||
attrs = [{<<"by">>, LServer},
|
||||
{<<"xmlns">>, ?NS_MAM_TMP},
|
||||
{<<"id">>, ID}]},
|
||||
StanzaID = #xmlel{name = <<"stanza-id">>,
|
||||
attrs = [{<<"by">>, LServer},
|
||||
{<<"xmlns">>, ?NS_SID_0},
|
||||
{<<"id">>, ID}]},
|
||||
{<<"xmlns">>, ?NS_SID_0},
|
||||
{<<"id">>, ID}]},
|
||||
NewEls = [Archived, StanzaID|NewPkt#xmlel.children],
|
||||
NewPkt#xmlel{children = NewEls};
|
||||
_ ->
|
||||
NewPkt
|
||||
end;
|
||||
false ->
|
||||
Pkt
|
||||
NewPkt#xmlel{children = NewEls};
|
||||
_ ->
|
||||
NewPkt
|
||||
end;
|
||||
_ ->
|
||||
Pkt
|
||||
end.
|
||||
|
||||
user_send_packet(Pkt, C2SState, JID, Peer) ->
|
||||
@@ -187,12 +188,12 @@ user_send_packet(Pkt, C2SState, JID, Peer) ->
|
||||
LServer = JID#jid.lserver,
|
||||
case should_archive(Pkt) of
|
||||
true ->
|
||||
NewPkt = strip_my_archived_tag(Pkt, LServer),
|
||||
NewPkt = strip_my_archived_tag(Pkt, LServer),
|
||||
store_msg(C2SState, jlib:replace_from_to(JID, Peer, NewPkt),
|
||||
LUser, LServer, Peer, send),
|
||||
NewPkt;
|
||||
false ->
|
||||
Pkt
|
||||
NewPkt;
|
||||
false ->
|
||||
Pkt
|
||||
end.
|
||||
|
||||
muc_filter_message(Pkt, #state{config = Config} = MUCState,
|
||||
@@ -217,23 +218,23 @@ muc_filter_message(Pkt, #state{config = Config} = MUCState,
|
||||
|
||||
% Query archive v0.2
|
||||
process_iq_v0_2(#jid{lserver = LServer} = From,
|
||||
#jid{lserver = LServer} = To,
|
||||
#iq{type = get, sub_el = #xmlel{name = <<"query">>} = SubEl} = IQ) ->
|
||||
#jid{lserver = LServer} = To,
|
||||
#iq{type = get, sub_el = #xmlel{name = <<"query">>} = SubEl} = IQ) ->
|
||||
Fs = lists:flatmap(
|
||||
fun(#xmlel{name = <<"start">>} = El) ->
|
||||
[{<<"start">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"end">>} = El) ->
|
||||
[{<<"end">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"with">>} = El) ->
|
||||
[{<<"with">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"withroom">>} = El) ->
|
||||
[{<<"withroom">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"withtext">>} = El) ->
|
||||
[{<<"withtext">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"set">>}) ->
|
||||
[{<<"set">>, SubEl}];
|
||||
(_) ->
|
||||
[]
|
||||
fun (#xmlel{name = <<"start">>} = El) ->
|
||||
[{<<"start">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"end">>} = El) ->
|
||||
[{<<"end">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"with">>} = El) ->
|
||||
[{<<"with">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"withroom">>} = El) ->
|
||||
[{<<"withroom">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"withtext">>} = El) ->
|
||||
[{<<"withtext">>, [xml:get_tag_cdata(El)]}];
|
||||
(#xmlel{name = <<"set">>}) ->
|
||||
[{<<"set">>, SubEl}];
|
||||
(_) ->
|
||||
[]
|
||||
end, SubEl#xmlel.children),
|
||||
process_iq(LServer, From, To, IQ, SubEl, Fs, chat);
|
||||
process_iq_v0_2(From, To, IQ) ->
|
||||
@@ -241,8 +242,8 @@ process_iq_v0_2(From, To, IQ) ->
|
||||
|
||||
% Query archive v0.3
|
||||
process_iq_v0_3(#jid{lserver = LServer} = From,
|
||||
#jid{lserver = LServer} = To,
|
||||
#iq{type = set, sub_el = #xmlel{name = <<"query">>} = SubEl} = IQ) ->
|
||||
#jid{lserver = LServer} = To,
|
||||
#iq{type = set, sub_el = #xmlel{name = <<"query">>} = SubEl} = IQ) ->
|
||||
process_iq(LServer, From, To, IQ, SubEl, get_xdata_fields(SubEl), chat);
|
||||
process_iq_v0_3(From, To, IQ) ->
|
||||
process_iq(From, To, IQ).
|
||||
@@ -265,15 +266,15 @@ muc_process_iq(IQ, _MUCState, _From, _To) ->
|
||||
|
||||
get_xdata_fields(SubEl) ->
|
||||
case {xml:get_subtag_with_xmlns(SubEl, <<"x">>, ?NS_XDATA),
|
||||
xml:get_subtag_with_xmlns(SubEl, <<"set">>, ?NS_RSM)} of
|
||||
{#xmlel{} = XData, false} ->
|
||||
jlib:parse_xdata_submit(XData);
|
||||
{#xmlel{} = XData, #xmlel{}} ->
|
||||
[{<<"set">>, SubEl} | jlib:parse_xdata_submit(XData)];
|
||||
{false, #xmlel{}} ->
|
||||
[{<<"set">>, SubEl}];
|
||||
{false, false} ->
|
||||
[]
|
||||
xml:get_subtag_with_xmlns(SubEl, <<"set">>, ?NS_RSM)} of
|
||||
{#xmlel{} = XData, false} ->
|
||||
jlib:parse_xdata_submit(XData);
|
||||
{#xmlel{} = XData, #xmlel{}} ->
|
||||
[{<<"set">>, SubEl} | jlib:parse_xdata_submit(XData)];
|
||||
{false, #xmlel{}} ->
|
||||
[{<<"set">>, SubEl}];
|
||||
{false, false} ->
|
||||
[]
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
@@ -282,32 +283,32 @@ get_xdata_fields(SubEl) ->
|
||||
|
||||
% Preference setting (both v0.2 & v0.3)
|
||||
process_iq(#jid{luser = LUser, lserver = LServer},
|
||||
#jid{lserver = LServer},
|
||||
#iq{type = set, sub_el = #xmlel{name = <<"prefs">>} = SubEl} = IQ) ->
|
||||
#jid{lserver = LServer},
|
||||
#iq{type = set, sub_el = #xmlel{name = <<"prefs">>} = SubEl} = IQ) ->
|
||||
try {case xml:get_tag_attr_s(<<"default">>, SubEl) of
|
||||
<<"always">> -> always;
|
||||
<<"never">> -> never;
|
||||
<<"roster">> -> roster
|
||||
end,
|
||||
lists:foldl(
|
||||
fun(#xmlel{name = <<"always">>, children = Els}, {A, N}) ->
|
||||
{get_jids(Els) ++ A, N};
|
||||
(#xmlel{name = <<"never">>, children = Els}, {A, N}) ->
|
||||
{A, get_jids(Els) ++ N};
|
||||
(_, {A, N}) ->
|
||||
{A, N}
|
||||
end, {[], []}, SubEl#xmlel.children)} of
|
||||
{Default, {Always, Never}} ->
|
||||
case write_prefs(LUser, LServer, LServer, Default,
|
||||
lists:usort(Always), lists:usort(Never)) of
|
||||
ok ->
|
||||
IQ#iq{type = result, sub_el = []};
|
||||
_Err ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||
end
|
||||
<<"always">> -> always;
|
||||
<<"never">> -> never;
|
||||
<<"roster">> -> roster
|
||||
end,
|
||||
lists:foldl(
|
||||
fun(#xmlel{name = <<"always">>, children = Els}, {A, N}) ->
|
||||
{get_jids(Els) ++ A, N};
|
||||
(#xmlel{name = <<"never">>, children = Els}, {A, N}) ->
|
||||
{A, get_jids(Els) ++ N};
|
||||
(_, {A, N}) ->
|
||||
{A, N}
|
||||
end, {[], []}, SubEl#xmlel.children)} of
|
||||
{Default, {Always, Never}} ->
|
||||
case write_prefs(LUser, LServer, LServer, Default,
|
||||
lists:usort(Always), lists:usort(Never)) of
|
||||
ok ->
|
||||
IQ#iq{type = result, sub_el = []};
|
||||
_Err ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||
end
|
||||
catch _:_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||
end;
|
||||
process_iq(_, _, #iq{sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
|
||||
@@ -343,64 +344,64 @@ process_iq(LServer, From, To, IQ, SubEl, Fs, MsgType) ->
|
||||
|
||||
should_archive(#xmlel{name = <<"message">>} = Pkt) ->
|
||||
case {xml:get_attr_s(<<"type">>, Pkt#xmlel.attrs),
|
||||
xml:get_subtag_cdata(Pkt, <<"body">>)} of
|
||||
{<<"error">>, _} ->
|
||||
false;
|
||||
{<<"groupchat">>, _} ->
|
||||
xml:get_subtag_cdata(Pkt, <<"body">>)} of
|
||||
{<<"error">>, _} ->
|
||||
false;
|
||||
{_, <<>>} ->
|
||||
%% Empty body
|
||||
false;
|
||||
_ ->
|
||||
true
|
||||
{<<"groupchat">>, _} ->
|
||||
false;
|
||||
{_, <<>>} ->
|
||||
%% Empty body
|
||||
false;
|
||||
_ ->
|
||||
true
|
||||
end;
|
||||
should_archive(#xmlel{}) ->
|
||||
false.
|
||||
|
||||
strip_my_archived_tag(Pkt, LServer) ->
|
||||
NewEls = lists:filter(
|
||||
fun(#xmlel{name = Tag, attrs = Attrs})
|
||||
when Tag == <<"archived">>; Tag == <<"stanza-id">> ->
|
||||
case catch jlib:nameprep(
|
||||
xml:get_attr_s(
|
||||
<<"by">>, Attrs)) of
|
||||
LServer ->
|
||||
false;
|
||||
_ ->
|
||||
true
|
||||
end;
|
||||
(_) ->
|
||||
true
|
||||
end, Pkt#xmlel.children),
|
||||
fun(#xmlel{name = Tag, attrs = Attrs})
|
||||
when Tag == <<"archived">>; Tag == <<"stanza-id">> ->
|
||||
case catch jlib:nameprep(
|
||||
xml:get_attr_s(
|
||||
<<"by">>, Attrs)) of
|
||||
LServer ->
|
||||
false;
|
||||
_ ->
|
||||
true
|
||||
end;
|
||||
(_) ->
|
||||
true
|
||||
end, Pkt#xmlel.children),
|
||||
Pkt#xmlel{children = NewEls}.
|
||||
|
||||
should_archive_peer(C2SState,
|
||||
#archive_prefs{default = Default,
|
||||
always = Always,
|
||||
never = Never},
|
||||
Peer) ->
|
||||
#archive_prefs{default = Default,
|
||||
always = Always,
|
||||
never = Never},
|
||||
Peer) ->
|
||||
LPeer = jlib:jid_tolower(Peer),
|
||||
case lists:member(LPeer, Always) of
|
||||
true ->
|
||||
true;
|
||||
false ->
|
||||
case lists:member(LPeer, Never) of
|
||||
true ->
|
||||
false;
|
||||
false ->
|
||||
case Default of
|
||||
always -> true;
|
||||
never -> false;
|
||||
roster ->
|
||||
case ejabberd_c2s:get_subscription(
|
||||
LPeer, C2SState) of
|
||||
both -> true;
|
||||
from -> true;
|
||||
to -> true;
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
end
|
||||
true ->
|
||||
true;
|
||||
false ->
|
||||
case lists:member(LPeer, Never) of
|
||||
true ->
|
||||
false;
|
||||
false ->
|
||||
case Default of
|
||||
always -> true;
|
||||
never -> false;
|
||||
roster ->
|
||||
case ejabberd_c2s:get_subscription(
|
||||
LPeer, C2SState) of
|
||||
both -> true;
|
||||
from -> true;
|
||||
to -> true;
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
should_archive_muc(_MUCState, _Peer) ->
|
||||
@@ -410,12 +411,12 @@ should_archive_muc(_MUCState, _Peer) ->
|
||||
store_msg(C2SState, Pkt, LUser, LServer, Peer, Dir) ->
|
||||
Prefs = get_prefs(LUser, LServer),
|
||||
case should_archive_peer(C2SState, Prefs, Peer) of
|
||||
true ->
|
||||
true ->
|
||||
US = {LUser, LServer},
|
||||
store(Pkt, LServer, US, chat, Peer, <<"">>, Dir,
|
||||
gen_mod:db_type(LServer, ?MODULE));
|
||||
false ->
|
||||
pass
|
||||
gen_mod:db_type(LServer, ?MODULE));
|
||||
false ->
|
||||
pass
|
||||
end.
|
||||
|
||||
store_muc(MUCState, Pkt, RoomJID, Peer, Nick) ->
|
||||
@@ -435,17 +436,17 @@ store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, mnesia) ->
|
||||
ID = jlib:integer_to_binary(now_to_usec(TS)),
|
||||
case mnesia:dirty_write(
|
||||
#archive_msg{us = {LUser, LServer},
|
||||
id = ID,
|
||||
timestamp = TS,
|
||||
peer = LPeer,
|
||||
bare_peer = {PUser, PServer, <<>>},
|
||||
id = ID,
|
||||
timestamp = TS,
|
||||
peer = LPeer,
|
||||
bare_peer = {PUser, PServer, <<>>},
|
||||
type = Type,
|
||||
nick = Nick,
|
||||
packet = Pkt}) of
|
||||
ok ->
|
||||
{ok, ID};
|
||||
Err ->
|
||||
Err
|
||||
packet = Pkt}) of
|
||||
ok ->
|
||||
{ok, ID};
|
||||
Err ->
|
||||
Err
|
||||
end;
|
||||
store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, odbc) ->
|
||||
TSinteger = now_to_usec(now()),
|
||||
@@ -455,28 +456,28 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, odbc) ->
|
||||
groupchat -> jlib:jid_to_string({LUser, LHost, <<>>})
|
||||
end,
|
||||
BarePeer = jlib:jid_to_string(
|
||||
jlib:jid_tolower(
|
||||
jlib:jid_remove_resource(Peer))),
|
||||
jlib:jid_tolower(
|
||||
jlib:jid_remove_resource(Peer))),
|
||||
LPeer = jlib:jid_to_string(
|
||||
jlib:jid_tolower(Peer)),
|
||||
jlib:jid_tolower(Peer)),
|
||||
XML = xml:element_to_binary(Pkt),
|
||||
Body = xml:get_subtag_cdata(Pkt, <<"body">>),
|
||||
case ejabberd_odbc:sql_query(
|
||||
LServer,
|
||||
[<<"insert into archive (username, timestamp, "
|
||||
"peer, bare_peer, xml, txt, kind, nick) values (">>,
|
||||
<<"'">>, ejabberd_odbc:escape(SUser), <<"', ">>,
|
||||
<<"'">>, TS, <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(LPeer), <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(BarePeer), <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(XML), <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(Body), <<"', ">>,
|
||||
<<"'">>, jlib:atom_to_binary(Type), <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(Nick), <<"');">>]) of
|
||||
{updated, _} ->
|
||||
{ok, ID};
|
||||
Err ->
|
||||
Err
|
||||
LServer,
|
||||
[<<"insert into archive (username, timestamp, "
|
||||
"peer, bare_peer, xml, txt, kind, nick) values (">>,
|
||||
<<"'">>, ejabberd_odbc:escape(SUser), <<"', ">>,
|
||||
<<"'">>, TS, <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(LPeer), <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(BarePeer), <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(XML), <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(Body), <<"', ">>,
|
||||
<<"'">>, jlib:atom_to_binary(Type), <<"', ">>,
|
||||
<<"'">>, ejabberd_odbc:escape(Nick), <<"');">>]) of
|
||||
{updated, _} ->
|
||||
{ok, ID};
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
write_prefs(LUser, LServer, Host, Default, Always, Never) ->
|
||||
@@ -485,9 +486,9 @@ write_prefs(LUser, LServer, Host, Default, Always, Never) ->
|
||||
DB -> DB
|
||||
end,
|
||||
Prefs = #archive_prefs{us = {LUser, LServer},
|
||||
default = Default,
|
||||
always = Always,
|
||||
never = Never},
|
||||
default = Default,
|
||||
always = Always,
|
||||
never = Never},
|
||||
cache_tab:dirty_insert(
|
||||
archive_prefs, {LUser, LServer}, Prefs,
|
||||
fun() -> write_prefs(LUser, LServer, Prefs, DBType) end).
|
||||
@@ -495,21 +496,21 @@ write_prefs(LUser, LServer, Host, Default, Always, Never) ->
|
||||
write_prefs(_LUser, _LServer, Prefs, mnesia) ->
|
||||
mnesia:dirty_write(Prefs);
|
||||
write_prefs(LUser, _LServer, #archive_prefs{default = Default,
|
||||
never = Never,
|
||||
always = Always},
|
||||
{odbc, Host}) ->
|
||||
never = Never,
|
||||
always = Always},
|
||||
{odbc, Host}) ->
|
||||
SUser = ejabberd_odbc:escape(LUser),
|
||||
SDefault = erlang:atom_to_binary(Default, utf8),
|
||||
SAlways = ejabberd_odbc:encode_term(Always),
|
||||
SNever = ejabberd_odbc:encode_term(Never),
|
||||
case update(Host, <<"archive_prefs">>,
|
||||
[<<"username">>, <<"def">>, <<"always">>, <<"never">>],
|
||||
[SUser, SDefault, SAlways, SNever],
|
||||
[<<"username='">>, SUser, <<"'">>]) of
|
||||
{updated, _} ->
|
||||
ok;
|
||||
Err ->
|
||||
Err
|
||||
[<<"username">>, <<"def">>, <<"always">>, <<"never">>],
|
||||
[SUser, SDefault, SAlways, SNever],
|
||||
[<<"username='">>, SUser, <<"'">>]) of
|
||||
{updated, _} ->
|
||||
ok;
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
get_prefs(LUser, LServer) ->
|
||||
@@ -519,24 +520,24 @@ get_prefs(LUser, LServer) ->
|
||||
DBType)
|
||||
end),
|
||||
case Res of
|
||||
{ok, Prefs} ->
|
||||
Prefs;
|
||||
error ->
|
||||
Default = gen_mod:get_module_opt(
|
||||
LServer, ?MODULE, default,
|
||||
fun(always) -> always;
|
||||
(never) -> never;
|
||||
(roster) -> roster
|
||||
end, never),
|
||||
#archive_prefs{us = {LUser, LServer}, default = Default}
|
||||
{ok, Prefs} ->
|
||||
Prefs;
|
||||
error ->
|
||||
Default = gen_mod:get_module_opt(
|
||||
LServer, ?MODULE, default,
|
||||
fun(always) -> always;
|
||||
(never) -> never;
|
||||
(roster) -> roster
|
||||
end, never),
|
||||
#archive_prefs{us = {LUser, LServer}, default = Default}
|
||||
end.
|
||||
|
||||
get_prefs(LUser, LServer, mnesia) ->
|
||||
case mnesia:dirty_read(archive_prefs, {LUser, LServer}) of
|
||||
[Prefs] ->
|
||||
{ok, Prefs};
|
||||
_ ->
|
||||
error
|
||||
[Prefs] ->
|
||||
{ok, Prefs};
|
||||
_ ->
|
||||
error
|
||||
end;
|
||||
get_prefs(LUser, LServer, odbc) ->
|
||||
case ejabberd_odbc:sql_query(
|
||||
@@ -544,16 +545,16 @@ get_prefs(LUser, LServer, odbc) ->
|
||||
[<<"select def, always, never from archive_prefs ">>,
|
||||
<<"where username='">>,
|
||||
ejabberd_odbc:escape(LUser), <<"';">>]) of
|
||||
{selected, _, [[SDefault, SAlways, SNever]]} ->
|
||||
Default = erlang:binary_to_existing_atom(SDefault, utf8),
|
||||
Always = ejabberd_odbc:decode_term(SAlways),
|
||||
Never = ejabberd_odbc:decode_term(SNever),
|
||||
{ok, #archive_prefs{us = {LUser, LServer},
|
||||
default = Default,
|
||||
always = Always,
|
||||
never = Never}};
|
||||
_ ->
|
||||
error
|
||||
{selected, _, [[SDefault, SAlways, SNever]]} ->
|
||||
Default = erlang:binary_to_existing_atom(SDefault, utf8),
|
||||
Always = ejabberd_odbc:decode_term(SAlways),
|
||||
Never = ejabberd_odbc:decode_term(SNever),
|
||||
{ok, #archive_prefs{us = {LUser, LServer},
|
||||
default = Default,
|
||||
always = Always,
|
||||
never = Never}};
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
select_and_send(LServer, From, To, Start, End, With, RSM, IQ, MsgType) ->
|
||||
@@ -577,7 +578,7 @@ select_and_start(LServer, From, To, Start, End, With, RSM, MsgType, DBType) ->
|
||||
{room, {_, _, <<"">>} = WithJID} ->
|
||||
select(LServer, jlib:make_jid(WithJID), Start, End,
|
||||
WithJID, RSM, MsgType, DBType);
|
||||
_ ->
|
||||
_ ->
|
||||
select(LServer, From, Start, End,
|
||||
With, RSM, MsgType, DBType)
|
||||
end;
|
||||
@@ -630,8 +631,8 @@ select(_LServer, #jid{luser = LUser, lserver = LServer} = JidRequestor,
|
||||
Count = length(Msgs),
|
||||
{lists:map(
|
||||
fun(Msg) ->
|
||||
{Msg#archive_msg.id,
|
||||
jlib:binary_to_integer(Msg#archive_msg.id),
|
||||
{Msg#archive_msg.id,
|
||||
jlib:binary_to_integer(Msg#archive_msg.id),
|
||||
msg_to_el(Msg, MsgType, JidRequestor)}
|
||||
end, FilteredMsgs), IsComplete, Count};
|
||||
select(LServer, #jid{luser = LUser} = JidRequestor,
|
||||
@@ -649,8 +650,8 @@ select(LServer, #jid{luser = LUser} = JidRequestor,
|
||||
% the client did not specify a limit using RSM then the server
|
||||
% should return a policy-violation error to the client.
|
||||
case {ejabberd_odbc:sql_query(Host, Query),
|
||||
ejabberd_odbc:sql_query(Host, CountQuery)} of
|
||||
{{selected, _, Res}, {selected, _, [[Count]]}} ->
|
||||
ejabberd_odbc:sql_query(Host, CountQuery)} of
|
||||
{{selected, _, Res}, {selected, _, [[Count]]}} ->
|
||||
{Max, Direction} = case RSM of
|
||||
#rsm_in{max = M, direction = D} -> {M, D};
|
||||
_ -> {undefined, undefined}
|
||||
@@ -665,24 +666,24 @@ select(LServer, #jid{luser = LUser} = JidRequestor,
|
||||
true ->
|
||||
{Res, true}
|
||||
end,
|
||||
{lists:map(
|
||||
{lists:map(
|
||||
fun([TS, XML, PeerBin, Kind, Nick]) ->
|
||||
#xmlel{} = El = xml_stream:parse_element(XML),
|
||||
Now = usec_to_now(jlib:binary_to_integer(TS)),
|
||||
PeerJid = jlib:jid_tolower(jlib:string_to_jid(PeerBin)),
|
||||
#xmlel{} = El = xml_stream:parse_element(XML),
|
||||
Now = usec_to_now(jlib:binary_to_integer(TS)),
|
||||
PeerJid = jlib:jid_tolower(jlib:string_to_jid(PeerBin)),
|
||||
T = if Kind /= <<"">> ->
|
||||
jlib:binary_to_atom(Kind);
|
||||
true -> chat
|
||||
end,
|
||||
{TS, jlib:binary_to_integer(TS),
|
||||
msg_to_el(#archive_msg{timestamp = Now,
|
||||
{TS, jlib:binary_to_integer(TS),
|
||||
msg_to_el(#archive_msg{timestamp = Now,
|
||||
packet = El,
|
||||
type = T,
|
||||
nick = Nick,
|
||||
peer = PeerJid},
|
||||
MsgType,
|
||||
JidRequestor)}
|
||||
end, Res1), IsComplete, jlib:binary_to_integer(Count)};
|
||||
end, Res1), IsComplete, jlib:binary_to_integer(Count)};
|
||||
_ ->
|
||||
{[], false, 0}
|
||||
end.
|
||||
@@ -692,12 +693,12 @@ msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
|
||||
Delay = jlib:now_to_utc_string(TS),
|
||||
Pkt = maybe_update_from_to(Pkt1, JidRequestor, Peer, MsgType, Nick),
|
||||
#xmlel{name = <<"forwarded">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_FORWARD}],
|
||||
children = [#xmlel{name = <<"delay">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_DELAY},
|
||||
{<<"stamp">>, Delay}]},
|
||||
xml:replace_tag_attr(
|
||||
<<"xmlns">>, <<"jabber:client">>, Pkt)]}.
|
||||
attrs = [{<<"xmlns">>, ?NS_FORWARD}],
|
||||
children = [#xmlel{name = <<"delay">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_DELAY},
|
||||
{<<"stamp">>, Delay}]},
|
||||
xml:replace_tag_attr(
|
||||
<<"xmlns">>, <<"jabber:client">>, Pkt)]}.
|
||||
|
||||
maybe_update_from_to(Pkt, JidRequestor, Peer, chat, _Nick) ->
|
||||
case xml:get_attr_s(<<"type">>, Pkt#xmlel.attrs) of
|
||||
@@ -726,6 +727,38 @@ maybe_update_from_to(#xmlel{children = Els} = Pkt, JidRequestor,
|
||||
Pkt2 = jlib:replace_from(jlib:jid_replace_resource(JidRequestor, Nick), Pkt1),
|
||||
jlib:remove_attr(<<"to">>, Pkt2).
|
||||
|
||||
is_bare_copy(#jid{luser = U, lserver = S, lresource = R}, To) ->
|
||||
PrioRes = ejabberd_sm:get_user_present_resources(U, S),
|
||||
MaxRes = case catch lists:max(PrioRes) of
|
||||
{_Prio, Res} when is_binary(Res) ->
|
||||
Res;
|
||||
_ ->
|
||||
undefined
|
||||
end,
|
||||
IsBareTo = case To of
|
||||
#jid{lresource = <<"">>} ->
|
||||
true;
|
||||
#jid{lresource = LRes} ->
|
||||
%% Unavailable resources are handled like bare JIDs.
|
||||
lists:keyfind(LRes, 2, PrioRes) =:= false
|
||||
end,
|
||||
case {IsBareTo, R} of
|
||||
{true, MaxRes} ->
|
||||
?DEBUG("Recipient of message to bare JID has top priority: ~s@~s/~s",
|
||||
[U, S, R]),
|
||||
false;
|
||||
{true, _R} ->
|
||||
%% The message was sent to our bare JID, and we currently have
|
||||
%% multiple resources with the same highest priority, so the session
|
||||
%% manager routes the message to each of them. We store the message
|
||||
%% only from the resource where R equals MaxRes.
|
||||
?DEBUG("Additional recipient of message to bare JID: ~s@~s/~s",
|
||||
[U, S, R]),
|
||||
true;
|
||||
{false, _R} ->
|
||||
false
|
||||
end.
|
||||
|
||||
send(From, To, Msgs, RSM, Count, IsComplete, #iq{sub_el = SubEl} = IQ) ->
|
||||
QID = xml:get_tag_attr_s(<<"queryid">>, SubEl),
|
||||
NS = xml:get_tag_attr_s(<<"xmlns">>, SubEl),
|
||||
@@ -831,42 +864,42 @@ match_rsm(_Now, _) ->
|
||||
make_matchspec(LUser, LServer, Start, End, {_, _, <<>>} = With) ->
|
||||
ets:fun2ms(
|
||||
fun(#archive_msg{timestamp = TS,
|
||||
us = US,
|
||||
bare_peer = BPeer} = Msg)
|
||||
when Start =< TS, End >= TS,
|
||||
US == {LUser, LServer},
|
||||
BPeer == With ->
|
||||
Msg
|
||||
us = US,
|
||||
bare_peer = BPeer} = Msg)
|
||||
when Start =< TS, End >= TS,
|
||||
US == {LUser, LServer},
|
||||
BPeer == With ->
|
||||
Msg
|
||||
end);
|
||||
make_matchspec(LUser, LServer, Start, End, {_, _, _} = With) ->
|
||||
ets:fun2ms(
|
||||
fun(#archive_msg{timestamp = TS,
|
||||
us = US,
|
||||
peer = Peer} = Msg)
|
||||
when Start =< TS, End >= TS,
|
||||
US == {LUser, LServer},
|
||||
Peer == With ->
|
||||
Msg
|
||||
us = US,
|
||||
peer = Peer} = Msg)
|
||||
when Start =< TS, End >= TS,
|
||||
US == {LUser, LServer},
|
||||
Peer == With ->
|
||||
Msg
|
||||
end);
|
||||
make_matchspec(LUser, LServer, Start, End, none) ->
|
||||
ets:fun2ms(
|
||||
fun(#archive_msg{timestamp = TS,
|
||||
us = US,
|
||||
peer = Peer} = Msg)
|
||||
when Start =< TS, End >= TS,
|
||||
US == {LUser, LServer} ->
|
||||
Msg
|
||||
us = US,
|
||||
peer = Peer} = Msg)
|
||||
when Start =< TS, End >= TS,
|
||||
US == {LUser, LServer} ->
|
||||
Msg
|
||||
end).
|
||||
|
||||
make_sql_query(User, _LServer, Start, End, With, RSM) ->
|
||||
{Max, Direction, ID} = case RSM of
|
||||
#rsm_in{} ->
|
||||
{RSM#rsm_in.max,
|
||||
RSM#rsm_in.direction,
|
||||
RSM#rsm_in.id};
|
||||
none ->
|
||||
{none, none, <<>>}
|
||||
end,
|
||||
#rsm_in{} ->
|
||||
{RSM#rsm_in.max,
|
||||
RSM#rsm_in.direction,
|
||||
RSM#rsm_in.id};
|
||||
none ->
|
||||
{none, none, <<>>}
|
||||
end,
|
||||
LimitClause = if is_integer(Max), Max >= 0 ->
|
||||
[<<" limit ">>, jlib:integer_to_binary(Max+1)];
|
||||
true ->
|
||||
@@ -903,19 +936,19 @@ make_sql_query(User, _LServer, Start, End, With, RSM) ->
|
||||
[]
|
||||
end,
|
||||
StartClause = case Start of
|
||||
{_, _, _} ->
|
||||
[<<" and timestamp >= ">>,
|
||||
jlib:integer_to_binary(now_to_usec(Start))];
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
{_, _, _} ->
|
||||
[<<" and timestamp >= ">>,
|
||||
jlib:integer_to_binary(now_to_usec(Start))];
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
EndClause = case End of
|
||||
{_, _, _} ->
|
||||
[<<" and timestamp <= ">>,
|
||||
jlib:integer_to_binary(now_to_usec(End))];
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
{_, _, _} ->
|
||||
[<<" and timestamp <= ">>,
|
||||
jlib:integer_to_binary(now_to_usec(End))];
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
SUser = ejabberd_odbc:escape(User),
|
||||
|
||||
Query = [<<"SELECT timestamp, xml, peer, kind, nick"
|
||||
@@ -958,11 +991,11 @@ datetime_to_now(DateTime, USecs) ->
|
||||
get_jids(Els) ->
|
||||
lists:flatmap(
|
||||
fun(#xmlel{name = <<"jid">>} = El) ->
|
||||
J = jlib:string_to_jid(xml:get_tag_cdata(El)),
|
||||
[jlib:jid_tolower(jlib:jid_remove_resource(J)),
|
||||
jlib:jid_tolower(J)];
|
||||
(_) ->
|
||||
[]
|
||||
J = jlib:string_to_jid(xml:get_tag_cdata(El)),
|
||||
[jlib:jid_tolower(jlib:jid_remove_resource(J)),
|
||||
jlib:jid_tolower(J)];
|
||||
(_) ->
|
||||
[]
|
||||
end, Els).
|
||||
|
||||
update(LServer, Table, Fields, Vals, Where) ->
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : mod_metrics.erl
|
||||
%%% Author : Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% Purpose : Simple metrics handler for runtime statistics
|
||||
%%% Created : 22 Oct 2015 by Christophe Romain <christophe.romain@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2015 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.
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(mod_metrics).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('christophe.romain@process-one.net').
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(HOOKS, [offline_message_hook,
|
||||
sm_register_connection_hook, sm_remove_connection_hook,
|
||||
user_send_packet, user_receive_packet,
|
||||
s2s_send_packet, s2s_receive_packet,
|
||||
remove_user, register_user]).
|
||||
|
||||
-export([start/2, stop/1, send_metrics/4]).
|
||||
|
||||
-export([offline_message_hook/3,
|
||||
sm_register_connection_hook/3, sm_remove_connection_hook/3,
|
||||
user_send_packet/4, user_receive_packet/5,
|
||||
s2s_send_packet/3, s2s_receive_packet/3,
|
||||
remove_user/2, register_user/2]).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
|
||||
start(Host, _Opts) ->
|
||||
[ejabberd_hooks:add(Hook, Host, ?MODULE, Hook, 20)
|
||||
|| Hook <- ?HOOKS].
|
||||
|
||||
stop(Host) ->
|
||||
[ejabberd_hooks:delete(Hook, Host, ?MODULE, Hook, 20)
|
||||
|| Hook <- ?HOOKS].
|
||||
|
||||
%%====================================================================
|
||||
%% Hooks handlers
|
||||
%%====================================================================
|
||||
|
||||
offline_message_hook(_From, #jid{lserver=LServer}, _Packet) ->
|
||||
push(LServer, offline_message).
|
||||
|
||||
sm_register_connection_hook(_SID, #jid{lserver=LServer}, _Info) ->
|
||||
push(LServer, sm_register_connection).
|
||||
sm_remove_connection_hook(_SID, #jid{lserver=LServer}, _Info) ->
|
||||
push(LServer, sm_remove_connection).
|
||||
|
||||
user_send_packet(Packet, _C2SState, #jid{lserver=LServer}, _To) ->
|
||||
push(LServer, user_send_packet),
|
||||
Packet.
|
||||
user_receive_packet(Packet, _C2SState, _JID, _From, #jid{lserver=LServer}) ->
|
||||
push(LServer, user_receive_packet),
|
||||
Packet.
|
||||
|
||||
s2s_send_packet(#jid{lserver=LServer}, _To, _Packet) ->
|
||||
push(LServer, s2s_send_packet).
|
||||
s2s_receive_packet(_From, #jid{lserver=LServer}, _Packet) ->
|
||||
push(LServer, s2s_receive_packet).
|
||||
|
||||
remove_user(_User, Server) ->
|
||||
push(jlib:nameprep(Server), remove_user).
|
||||
register_user(_User, Server) ->
|
||||
push(jlib:nameprep(Server), register_user).
|
||||
|
||||
%%====================================================================
|
||||
%% metrics push handler
|
||||
%%====================================================================
|
||||
|
||||
push(Host, Probe) ->
|
||||
spawn(?MODULE, send_metrics, [Host, Probe, {127,0,0,1}, 11111]).
|
||||
|
||||
send_metrics(Host, Probe, Peer, Port) ->
|
||||
% our default metrics handler is https://github.com/processone/grapherl
|
||||
% grapherl metrics are named first with service domain, then nodename
|
||||
% and name of the data itself, followed by type timestamp and value
|
||||
% example => process-one.net/xmpp-1.user_receive_packet:c/1441784958:1
|
||||
[_, NodeId] = str:tokens(jlib:atom_to_binary(node()), <<"@">>),
|
||||
[Node | _] = str:tokens(NodeId, <<".">>),
|
||||
BaseId = <<Host/binary, "/", Node/binary, ".">>,
|
||||
DateTime = erlang:universaltime(),
|
||||
UnixTime = calendar:datetime_to_gregorian_seconds(DateTime) - 62167219200,
|
||||
TS = integer_to_binary(UnixTime),
|
||||
case gen_udp:open(0) of
|
||||
{ok, Socket} ->
|
||||
case Probe of
|
||||
{Key, Val} ->
|
||||
BVal = integer_to_binary(Val),
|
||||
Data = <<BaseId/binary, (jlib:atom_to_binary(Key))/binary,
|
||||
":g/", TS/binary, ":", BVal/binary>>,
|
||||
gen_udp:send(Socket, Peer, Port, Data);
|
||||
Key ->
|
||||
Data = <<BaseId/binary, (jlib:atom_to_binary(Key))/binary,
|
||||
":c/", TS/binary, ":1">>,
|
||||
gen_udp:send(Socket, Peer, Port, Data)
|
||||
end,
|
||||
gen_udp:close(Socket);
|
||||
Error ->
|
||||
?WARNING_MSG("can not open udp socket to grapherl: ~p", [Error])
|
||||
end.
|
||||
+63
-124
@@ -42,12 +42,12 @@
|
||||
restore_room/3,
|
||||
forget_room/3,
|
||||
create_room/5,
|
||||
shutdown_rooms/1,
|
||||
shutdown_rooms/1,
|
||||
process_iq_disco_items/4,
|
||||
broadcast_service_message/2,
|
||||
export/1,
|
||||
import/1,
|
||||
import/3,
|
||||
export/1,
|
||||
import/1,
|
||||
import/3,
|
||||
can_use_nick/4]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
@@ -85,10 +85,6 @@
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link(Host, Opts) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:start_link({local, Proc}, ?MODULE,
|
||||
@@ -295,13 +291,6 @@ can_use_nick(LServer, Host, JID, Nick, odbc) ->
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([Host, Opts]) ->
|
||||
MyHost = gen_mod:get_opt_host(Host, Opts,
|
||||
<<"conference.@HOST@">>),
|
||||
@@ -390,8 +379,7 @@ init([Host, Opts]) ->
|
||||
ejabberd_router:register_route(MyHost),
|
||||
load_permanent_rooms(MyHost, Host,
|
||||
{Access, AccessCreate, AccessAdmin, AccessPersistent},
|
||||
HistorySize,
|
||||
RoomShaper),
|
||||
HistorySize, RoomShaper),
|
||||
{ok, #state{host = MyHost,
|
||||
server_host = Host,
|
||||
access = {Access, AccessCreate, AccessAdmin, AccessPersistent},
|
||||
@@ -399,15 +387,6 @@ init([Host, Opts]) ->
|
||||
history_size = HistorySize,
|
||||
room_shaper = RoomShaper}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, normal, ok, State};
|
||||
handle_call({create, Room, From, Nick, Opts}, _From,
|
||||
@@ -428,20 +407,8 @@ handle_call({create, Room, From, Nick, Opts}, _From,
|
||||
register_room(Host, Room, Pid),
|
||||
{reply, ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({route, From, To, Packet},
|
||||
#state{host = Host, server_host = ServerHost,
|
||||
access = Access, default_room_opts = DefRoomOpts,
|
||||
@@ -468,21 +435,10 @@ handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, State) ->
|
||||
ejabberd_router:unregister_route(State#state.host),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
@@ -508,13 +464,13 @@ do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||
case acl:match_rule(ServerHost, AccessRoute, From) of
|
||||
allow ->
|
||||
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||
From, To, Packet, DefRoomOpts);
|
||||
From, To, Packet, DefRoomOpts);
|
||||
_ ->
|
||||
#xmlel{attrs = Attrs} = Packet,
|
||||
Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
|
||||
ErrText = <<"Access denied by service policy">>,
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route_error(To, From, Err, Packet)
|
||||
end.
|
||||
|
||||
@@ -645,26 +601,23 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||
<<"error">> -> ok;
|
||||
<<"result">> -> ok;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
case mnesia:dirty_read(muc_online_room, {Room, Host}) of
|
||||
[] ->
|
||||
Type = xml:get_attr_s(<<"type">>, Attrs),
|
||||
case {Name, Type} of
|
||||
{<<"presence">>, <<"">>} ->
|
||||
case check_user_can_create_room(ServerHost,
|
||||
AccessCreate, From,
|
||||
Room) of
|
||||
AccessCreate, From, Room) of
|
||||
true ->
|
||||
{ok, Pid} = start_new_room(
|
||||
Host, ServerHost, Access,
|
||||
Room, HistorySize,
|
||||
RoomShaper, From,
|
||||
Nick, DefRoomOpts),
|
||||
{ok, Pid} = start_new_room(Host, ServerHost, Access,
|
||||
Room, HistorySize,
|
||||
RoomShaper, From, Nick, DefRoomOpts),
|
||||
register_room(Host, Room, Pid),
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
ok;
|
||||
@@ -676,19 +629,18 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
_ ->
|
||||
Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
|
||||
ErrText = <<"Conference room does not exist">>,
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
?ERRT_ITEM_NOT_FOUND(Lang,
|
||||
ErrText)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
[R] ->
|
||||
Pid = R#muc_online_room.pid,
|
||||
?DEBUG("MUC: send to process ~p~n", [Pid]),
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
ok
|
||||
end
|
||||
Lang = xml:get_attr_s(<<"xml:lang">>, Attrs),
|
||||
ErrText = <<"Conference room does not exist">>,
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
[R] ->
|
||||
Pid = R#muc_online_room.pid,
|
||||
?DEBUG("MUC: send to process ~p~n", [Pid]),
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
check_user_can_create_room(ServerHost, AccessCreate,
|
||||
@@ -743,48 +695,43 @@ get_rooms(LServer, Host, odbc) ->
|
||||
Err -> ?ERROR_MSG("failed to get rooms: ~p", [Err]), []
|
||||
end.
|
||||
|
||||
load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
|
||||
load_permanent_rooms(Host, ServerHost, Access,
|
||||
HistorySize, RoomShaper) ->
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
{Room, Host} = R#muc_room.name_host,
|
||||
case mnesia:dirty_read(muc_online_room, {Room, Host}) of
|
||||
[] ->
|
||||
{ok, Pid} = mod_muc_room:start(
|
||||
Host,
|
||||
ServerHost,
|
||||
Access,
|
||||
Room,
|
||||
HistorySize,
|
||||
RoomShaper,
|
||||
R#muc_room.opts),
|
||||
register_room(Host, Room, Pid);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, get_rooms(ServerHost, Host)).
|
||||
{Room, Host} = R#muc_room.name_host,
|
||||
case mnesia:dirty_read(muc_online_room, {Room, Host}) of
|
||||
[] ->
|
||||
{ok, Pid} = mod_muc_room:start(Host,
|
||||
ServerHost, Access, Room,
|
||||
HistorySize, RoomShaper,
|
||||
R#muc_room.opts),
|
||||
register_room(Host, Room, Pid);
|
||||
_ -> ok
|
||||
end
|
||||
end,
|
||||
get_rooms(ServerHost, Host)).
|
||||
|
||||
start_new_room(Host, ServerHost, Access, Room,
|
||||
HistorySize, RoomShaper, From,
|
||||
Nick, DefRoomOpts) ->
|
||||
HistorySize, RoomShaper, From,
|
||||
Nick, DefRoomOpts) ->
|
||||
case restore_room(ServerHost, Host, Room) of
|
||||
error ->
|
||||
error ->
|
||||
?DEBUG("MUC: open new room '~s'~n", [Room]),
|
||||
mod_muc_room:start(Host, ServerHost, Access,
|
||||
Room, HistorySize,
|
||||
RoomShaper, From,
|
||||
Nick, DefRoomOpts);
|
||||
Opts ->
|
||||
mod_muc_room:start(Host, ServerHost, Access, Room,
|
||||
HistorySize, RoomShaper,
|
||||
From, Nick, DefRoomOpts);
|
||||
Opts ->
|
||||
?DEBUG("MUC: restore room '~s'~n", [Room]),
|
||||
mod_muc_room:start(Host, ServerHost, Access,
|
||||
Room, HistorySize,
|
||||
RoomShaper, Opts)
|
||||
mod_muc_room:start(Host, ServerHost, Access, Room,
|
||||
HistorySize, RoomShaper, Opts)
|
||||
end.
|
||||
|
||||
register_room(Host, Room, Pid) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#muc_online_room{name_host = {Room, Host},
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:write(#muc_online_room{name_host = {Room, Host},
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
@@ -813,7 +760,9 @@ iq_disco_info(ServerHost, Lang) ->
|
||||
case gen_mod:is_loaded(ServerHost, mod_mam) of
|
||||
true ->
|
||||
[#xmlel{name = <<"feature">>,
|
||||
attrs = [{<<"var">>, ?NS_MAM_0}]}];
|
||||
attrs = [{<<"var">>, ?NS_MAM_0}]},
|
||||
#xmlel{name = <<"feature">>,
|
||||
attrs = [{<<"var">>, ?NS_MAM_1}]}];
|
||||
false ->
|
||||
[]
|
||||
end.
|
||||
@@ -840,7 +789,6 @@ iq_disco_items(Host, From, Lang, none) ->
|
||||
_ -> false
|
||||
end
|
||||
end, get_vh_rooms(Host));
|
||||
|
||||
iq_disco_items(Host, From, Lang, Rsm) ->
|
||||
{Rooms, RsmO} = get_vh_rooms(Host, Rsm),
|
||||
RsmOut = jlib:rsm_encode(RsmO),
|
||||
@@ -920,13 +868,6 @@ get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
|
||||
flush() -> receive _ -> flush() after 0 -> ok end.
|
||||
|
||||
-define(XFIELD(Type, Label, Var, Val),
|
||||
%% @doc Get a pseudo unique Room Name. The Room Name is generated as a hash of
|
||||
%% the requester JID, the local time and a random salt.
|
||||
%%
|
||||
%% "pseudo" because we don't verify that there is not a room
|
||||
%% with the returned Name already created, nor mark the generated Name
|
||||
%% as "already used". But in practice, it is unique enough. See
|
||||
%% http://xmpp.org/extensions/xep-0045.html#createroom-unique
|
||||
#xmlel{name = <<"field">>,
|
||||
attrs =
|
||||
[{<<"type">>, Type},
|
||||
@@ -1177,13 +1118,12 @@ iq_get_vcard(Lang) ->
|
||||
<<"ejabberd MUC module">>))/binary,
|
||||
"\nCopyright (c) 2003-2015 ProcessOne">>}]}].
|
||||
|
||||
|
||||
broadcast_service_message(Host, Msg) ->
|
||||
lists:foreach(
|
||||
fun(#muc_online_room{pid = Pid}) ->
|
||||
gen_fsm:send_all_state_event(
|
||||
Pid, {service_message, Msg})
|
||||
end, get_vh_rooms(Host)).
|
||||
fun(#muc_online_room{pid = Pid}) ->
|
||||
gen_fsm:send_all_state_event(
|
||||
Pid, {service_message, Msg})
|
||||
end, get_vh_rooms(Host)).
|
||||
|
||||
|
||||
get_vh_rooms(Host) ->
|
||||
@@ -1349,8 +1289,7 @@ import(_LServer) ->
|
||||
[{<<"select name, host, opts from muc_room;">>,
|
||||
fun([Name, RoomHost, SOpts]) ->
|
||||
Opts = opts_to_binary(ejabberd_odbc:decode_term(SOpts)),
|
||||
#muc_room{name_host = {Name, RoomHost},
|
||||
opts = Opts}
|
||||
#muc_room{name_host = {Name, RoomHost}, opts = Opts}
|
||||
end},
|
||||
{<<"select jid, host, nick from muc_registered;">>,
|
||||
fun([J, RoomHost, Nick]) ->
|
||||
|
||||
@@ -411,7 +411,6 @@ create_room(Name, Host, ServerHost) ->
|
||||
AcCreate = gen_mod:get_module_opt(ServerHost, mod_muc, access_create, fun(X) -> X end, all),
|
||||
AcAdmin = gen_mod:get_module_opt(ServerHost, mod_muc, access_admin, fun(X) -> X end, none),
|
||||
AcPer = gen_mod:get_module_opt(ServerHost, mod_muc, access_persistent, fun(X) -> X end, all),
|
||||
_PersistHistory = gen_mod:get_module_opt(ServerHost, mod_muc, persist_history, fun(X) -> X end, false),
|
||||
HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, fun(X) -> X end, 20),
|
||||
RoomShaper = gen_mod:get_module_opt(ServerHost, mod_muc, room_shaper, fun(X) -> X end, none),
|
||||
|
||||
|
||||
+21
-72
@@ -76,23 +76,14 @@
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link(Host, Opts) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
|
||||
|
||||
start(Host, Opts) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
ChildSpec =
|
||||
{Proc,
|
||||
{?MODULE, start_link, [Host, Opts]},
|
||||
temporary,
|
||||
1000,
|
||||
worker,
|
||||
[?MODULE]},
|
||||
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
|
||||
temporary, 1000, worker, [?MODULE]},
|
||||
supervisor:start_child(ejabberd_sup, ChildSpec).
|
||||
|
||||
stop(Host) ->
|
||||
@@ -123,19 +114,11 @@ transform_module_options(Opts) ->
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
init([Host, Opts]) ->
|
||||
OutDir = gen_mod:get_opt(outdir, Opts,
|
||||
fun iolist_to_binary/1,
|
||||
<<"www/muc">>),
|
||||
DirType = gen_mod:get_opt(dirtype, Opts,
|
||||
DirType = gen_mod:get_opt(dirtype, Opts,
|
||||
fun(subdirs) -> subdirs;
|
||||
(plain) -> plain
|
||||
end, subdirs),
|
||||
@@ -181,31 +164,17 @@ init([Host, Opts]) ->
|
||||
{ok,
|
||||
#logstate{host = Host, out_dir = OutDir,
|
||||
dir_type = DirType, dir_name = DirName,
|
||||
file_format = FileFormat, file_permissions = FilePermissions, css_file = CSSFile,
|
||||
file_format = FileFormat, css_file = CSSFile,
|
||||
file_permissions = FilePermissions,
|
||||
access = AccessLog, lang = Lang, timezone = Timezone,
|
||||
spam_prevention = NoFollow, top_link = Top_link}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling call messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_call({check_access_log, ServerHost, FromJID}, _From, State) ->
|
||||
Reply = acl:match_rule(ServerHost, State#logstate.access, FromJID),
|
||||
{reply, Reply, State};
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, normal, ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast({add_to_log, Type, Data, Room, Opts}, State) ->
|
||||
case catch add_to_log2(Type, Data, Room, Opts, State) of
|
||||
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
|
||||
@@ -214,27 +183,10 @@ handle_cast({add_to_log, Type, Data, Room, Opts}, State) ->
|
||||
{noreply, State};
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: terminate(Reason, State) -> void()
|
||||
%% Description: This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any necessary
|
||||
%% cleaning up. When it returns, the gen_server terminates with Reason.
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
@@ -242,19 +194,19 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
%%--------------------------------------------------------------------
|
||||
add_to_log2(text, {Nick, Packet}, Room, Opts, State) ->
|
||||
case has_no_permanent_store_hint(Packet) of
|
||||
false ->
|
||||
case {xml:get_subtag(Packet, <<"subject">>),
|
||||
xml:get_subtag(Packet, <<"body">>)}
|
||||
of
|
||||
{false, false} -> ok;
|
||||
{false, SubEl} ->
|
||||
Message = {body, xml:get_tag_cdata(SubEl)},
|
||||
add_message_to_log(Nick, Message, Room, Opts, State);
|
||||
{SubEl, _} ->
|
||||
Message = {subject, xml:get_tag_cdata(SubEl)},
|
||||
add_message_to_log(Nick, Message, Room, Opts, State)
|
||||
end;
|
||||
true -> ok
|
||||
false ->
|
||||
case {xml:get_subtag(Packet, <<"subject">>),
|
||||
xml:get_subtag(Packet, <<"body">>)}
|
||||
of
|
||||
{false, false} -> ok;
|
||||
{false, SubEl} ->
|
||||
Message = {body, xml:get_tag_cdata(SubEl)},
|
||||
add_message_to_log(Nick, Message, Room, Opts, State);
|
||||
{SubEl, _} ->
|
||||
Message = {subject, xml:get_tag_cdata(SubEl)},
|
||||
add_message_to_log(Nick, Message, Room, Opts, State)
|
||||
end;
|
||||
true -> ok
|
||||
end;
|
||||
add_to_log2(roomconfig_change, _Occupants, Room, Opts,
|
||||
State) ->
|
||||
@@ -349,7 +301,6 @@ close_previous_log(Fn, Images_dir, FileFormat) ->
|
||||
|
||||
write_last_lines(_, _, plaintext) -> ok;
|
||||
write_last_lines(F, Images_dir, _FileFormat) ->
|
||||
%% list_to_integer/2 was introduced in OTP R14
|
||||
fw(F, <<"<div class=\"legend\">">>),
|
||||
fw(F,
|
||||
<<" <a href=\"http://www.ejabberd.im\"><img "
|
||||
@@ -378,7 +329,7 @@ write_last_lines(F, Images_dir, _FileFormat) ->
|
||||
fw(F, <<"</span></div></body></html>">>).
|
||||
|
||||
set_filemode(Fn, {FileMode, FileGroup}) ->
|
||||
ok = file:change_mode(Fn, list_to_integer(integer_to_list(FileMode), 8)),
|
||||
ok = file:change_mode(Fn, list_to_integer(integer_to_list(FileMode), 8)),
|
||||
ok = file:change_group(Fn, FileGroup).
|
||||
|
||||
htmlize_nick(Nick1, html) ->
|
||||
@@ -1163,10 +1114,7 @@ roomoccupants_to_string(Users, _FileFormat) ->
|
||||
Users1 /= []],
|
||||
iolist_to_binary([<<"<div class=\"rcot\">">>, Res, <<"</div>">>]).
|
||||
|
||||
%% Users = [{JID, Nick, Role}]
|
||||
group_by_role(Users) ->
|
||||
%% Role = atom()
|
||||
%% Users = [{JID, Nick}]
|
||||
{Ms, Ps, Vs, Ns} = lists:foldl(fun ({JID, Nick,
|
||||
moderator},
|
||||
{Mod, Par, Vis, Non}) ->
|
||||
@@ -1238,7 +1186,8 @@ get_room_state(RoomPid) ->
|
||||
get_state),
|
||||
R.
|
||||
|
||||
get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?PROCNAME).
|
||||
get_proc_name(Host) ->
|
||||
gen_mod:get_module_proc(Host, ?PROCNAME).
|
||||
|
||||
calc_hour_offset(TimeHere) ->
|
||||
TimeZero = calendar:now_to_universal_time(now()),
|
||||
|
||||
+19
-57
@@ -112,23 +112,17 @@ start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts) ->
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, DefRoomOpts]) ->
|
||||
init([Host, ServerHost, Access, Room, HistorySize,
|
||||
RoomShaper, Creator, _Nick, DefRoomOpts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
Shaper = shaper:new(RoomShaper),
|
||||
State = set_affiliation(Creator, owner,
|
||||
#state{host = Host, server_host = ServerHost,
|
||||
access = Access, room = Room,
|
||||
history = lqueue_new(HistorySize),
|
||||
jid = jlib:make_jid(Room, Host, <<"">>),
|
||||
just_created = true,
|
||||
room_shaper = Shaper}),
|
||||
#state{host = Host, server_host = ServerHost,
|
||||
access = Access, room = Room,
|
||||
history = lqueue_new(HistorySize),
|
||||
jid = jlib:make_jid(Room, Host, <<"">>),
|
||||
just_created = true,
|
||||
room_shaper = Shaper}),
|
||||
State1 = set_opts(DefRoomOpts, State),
|
||||
if (State1#state.config)#config.persistent ->
|
||||
mod_muc:store_room(State1#state.server_host,
|
||||
@@ -137,7 +131,7 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, D
|
||||
make_opts(State1));
|
||||
true -> ok
|
||||
end,
|
||||
?INFO_MSG("Created MUC room ~s@~s by ~s",
|
||||
?INFO_MSG("Created MUC room ~s@~s by ~s",
|
||||
[Room, Host, jlib:jid_to_string(Creator)]),
|
||||
add_to_log(room_existence, created, State1),
|
||||
add_to_log(room_existence, started, State1),
|
||||
@@ -155,12 +149,6 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts]) ->
|
||||
add_to_log(room_existence, started, State),
|
||||
{ok, normal_state, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
normal_state({route, From, <<"">>,
|
||||
#xmlel{name = <<"message">>, attrs = Attrs,
|
||||
children = Els} =
|
||||
@@ -688,12 +676,6 @@ normal_state({route, From, ToNick,
|
||||
normal_state(_Event, StateData) ->
|
||||
{next_state, normal_state, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event({service_message, Msg}, _StateName,
|
||||
StateData) ->
|
||||
MessagePkt = #xmlel{name = <<"message">>,
|
||||
@@ -742,15 +724,6 @@ handle_event({set_affiliations, Affiliations},
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event({get_disco_item, JID, Lang}, _From, StateName, StateData) ->
|
||||
Reply = get_roomdesc_reply(JID, StateData,
|
||||
get_roomdesc_tail(StateData, Lang)),
|
||||
@@ -779,12 +752,6 @@ handle_sync_event(_Event, _From, StateName,
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info({process_user_presence, From}, normal_state = _StateName, StateData) ->
|
||||
RoomQueueEmpty = queue:is_empty(StateData#state.room_queue),
|
||||
RoomQueue = queue:in({presence, From}, StateData#state.room_queue),
|
||||
@@ -864,11 +831,6 @@ handle_info(shutdown, _StateName, StateData) ->
|
||||
handle_info(_Info, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, _StateName, StateData) ->
|
||||
?INFO_MSG("Stopping MUC room ~s@~s",
|
||||
[StateData#state.room, StateData#state.host]),
|
||||
@@ -979,7 +941,7 @@ process_groupchat_message(From,
|
||||
StateData#state.server_host,
|
||||
StateData#state.users,
|
||||
NewPacket),
|
||||
NewStateData2 = case has_body_or_subject(Packet) of
|
||||
NewStateData2 = case has_body_or_subject(NewPacket) of
|
||||
true ->
|
||||
add_message_to_history(FromNick, From,
|
||||
NewPacket,
|
||||
@@ -1808,9 +1770,9 @@ is_nick_change(JID, Nick, StateData) ->
|
||||
|
||||
nick_collision(User, Nick, StateData) ->
|
||||
UserOfNick = find_jid_by_nick(Nick, StateData),
|
||||
UserOfNick /= false andalso
|
||||
(UserOfNick /= false andalso
|
||||
jlib:jid_remove_resource(jlib:jid_tolower(UserOfNick))
|
||||
/= jlib:jid_remove_resource(jlib:jid_tolower(User)).
|
||||
/= jlib:jid_remove_resource(jlib:jid_tolower(User))).
|
||||
|
||||
add_new_user(From, Nick,
|
||||
#xmlel{attrs = Attrs, children = Els} = Packet,
|
||||
@@ -4412,7 +4374,6 @@ check_invitation(From, Els, Lang, StateData) ->
|
||||
jlib:jid_to_string({StateData#state.room,
|
||||
StateData#state.host,
|
||||
<<"">>})]),
|
||||
|
||||
case
|
||||
(StateData#state.config)#config.password_protected
|
||||
of
|
||||
@@ -4561,6 +4522,13 @@ tab_count_user(JID) ->
|
||||
element_size(El) ->
|
||||
byte_size(xml:element_to_binary(El)).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Multicast
|
||||
|
||||
send_multiple(From, Server, Users, Packet) ->
|
||||
JIDs = [ User#user.jid || {_, User} <- ?DICT:to_list(Users)],
|
||||
ejabberd_router_multicast:route_multicast(From, Server, JIDs, Packet).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Detect messange stanzas that don't have meaninful content
|
||||
|
||||
@@ -4571,9 +4539,3 @@ has_body_or_subject(Packet) ->
|
||||
(_) -> true
|
||||
end, Packet#xmlel.children).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Multicast
|
||||
|
||||
send_multiple(From, Server, Users, Packet) ->
|
||||
JIDs = [ User#user.jid || {_, User} <- ?DICT:to_list(Users)],
|
||||
ejabberd_router_multicast:route_multicast(From, Server, JIDs, Packet).
|
||||
|
||||
+45
-30
@@ -3,6 +3,24 @@
|
||||
%%% Author : Badlop <badlop@process-one.net>
|
||||
%%% Purpose : Extended Stanza Addressing (XEP-0033) support
|
||||
%%% Created : 29 May 2007 by Badlop <badlop@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2015 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.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_multicast).
|
||||
@@ -713,8 +731,7 @@ process_iqreply_error(From, LServiceS, _Packet) ->
|
||||
%%% Check protocol support: Receive response: Disco
|
||||
%%%-------------------------
|
||||
|
||||
process_iqreply_result(From, LServiceS, Packet,
|
||||
State) ->
|
||||
process_iqreply_result(From, LServiceS, Packet, State) ->
|
||||
#xmlel{name = <<"query">>, attrs = Attrs2,
|
||||
children = Els2} =
|
||||
xml:get_subtag(Packet, <<"query">>),
|
||||
@@ -741,37 +758,35 @@ process_discoinfo_result(From, LServiceS, Els,
|
||||
|
||||
process_discoinfo_result2(From, FromS, LServiceS, Els,
|
||||
Waiter) ->
|
||||
Multicast_support = lists:any(fun (XML) ->
|
||||
case XML of
|
||||
#xmlel{name = <<"feature">>,
|
||||
attrs = Attrs} ->
|
||||
(?NS_ADDRESS) ==
|
||||
xml:get_attr_s(<<"var">>,
|
||||
Attrs);
|
||||
_ -> false
|
||||
end
|
||||
end,
|
||||
Els),
|
||||
Multicast_support =
|
||||
lists:any(
|
||||
fun(XML) ->
|
||||
case XML of
|
||||
#xmlel{name = <<"feature">>, attrs = Attrs} ->
|
||||
(?NS_ADDRESS) == xml:get_attr_s(<<"var">>, Attrs);
|
||||
_ -> false
|
||||
end
|
||||
end,
|
||||
Els),
|
||||
Group = Waiter#waiter.group,
|
||||
RServer = Group#group.server,
|
||||
case Multicast_support of
|
||||
true ->
|
||||
SenderT = sender_type(From),
|
||||
RLimits = get_limits_xml(Els, SenderT),
|
||||
add_response(RServer,
|
||||
{multicast_supported, FromS, RLimits}),
|
||||
FromM = Waiter#waiter.sender,
|
||||
DestsM = Group#group.dests,
|
||||
PacketM = Waiter#waiter.packet,
|
||||
AAttrsM = Waiter#waiter.aattrs,
|
||||
AddressesM = Waiter#waiter.addresses,
|
||||
RServiceM = FromS,
|
||||
route_packet_multicast(FromM, RServiceM, PacketM,
|
||||
AAttrsM, DestsM, AddressesM, RLimits),
|
||||
delo_waiter(Waiter);
|
||||
false ->
|
||||
case FromS of
|
||||
RServer ->
|
||||
true ->
|
||||
SenderT = sender_type(From),
|
||||
RLimits = get_limits_xml(Els, SenderT),
|
||||
add_response(RServer, {multicast_supported, FromS, RLimits}),
|
||||
FromM = Waiter#waiter.sender,
|
||||
DestsM = Group#group.dests,
|
||||
PacketM = Waiter#waiter.packet,
|
||||
AAttrsM = Waiter#waiter.aattrs,
|
||||
AddressesM = Waiter#waiter.addresses,
|
||||
RServiceM = FromS,
|
||||
route_packet_multicast(FromM, RServiceM, PacketM,
|
||||
AAttrsM, DestsM, AddressesM, RLimits),
|
||||
delo_waiter(Waiter);
|
||||
false ->
|
||||
case FromS of
|
||||
RServer ->
|
||||
send_query_items(FromS, LServiceS),
|
||||
delo_waiter(Waiter),
|
||||
add_waiter(Waiter#waiter{awaiting =
|
||||
|
||||
+29
-37
@@ -39,7 +39,7 @@
|
||||
-export([count_offline_messages/2]).
|
||||
|
||||
-export([start/2,
|
||||
start_link/2,
|
||||
start_link/2,
|
||||
stop/1,
|
||||
store_packet/3,
|
||||
resend_offline_messages/2,
|
||||
@@ -48,11 +48,11 @@
|
||||
remove_expired_messages/1,
|
||||
remove_old_messages/2,
|
||||
remove_user/2,
|
||||
import/1,
|
||||
import/3,
|
||||
export/1,
|
||||
import/1,
|
||||
import/3,
|
||||
export/1,
|
||||
get_queue_length/2,
|
||||
get_offline_els/2,
|
||||
get_offline_els/2,
|
||||
webadmin_page/3,
|
||||
webadmin_user/4,
|
||||
webadmin_user_parse_query/5]).
|
||||
@@ -70,17 +70,7 @@
|
||||
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
|
||||
-record(offline_msg,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
timestamp = now() :: erlang:timestamp() | '_',
|
||||
expire = now() :: erlang:timestamp() | never | '_',
|
||||
from = #jid{} :: jid() | '_',
|
||||
to = #jid{} :: jid() | '_',
|
||||
packet = #xmlel{} :: xmlel() | '_'}).
|
||||
|
||||
-record(state,
|
||||
{host = <<"">> :: binary(),
|
||||
access_max_offline_messages}).
|
||||
-include("mod_offline.hrl").
|
||||
|
||||
-define(PROCNAME, ejabberd_offline).
|
||||
|
||||
@@ -138,8 +128,11 @@ init([Host, Opts]) ->
|
||||
ejabberd_hooks:add(webadmin_user, Host,
|
||||
?MODULE, webadmin_user, 50),
|
||||
ejabberd_hooks:add(webadmin_user_parse_query, Host,
|
||||
?MODULE, webadmin_user_parse_query, 50),
|
||||
AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, fun(A) -> A end, max_user_offline_messages),
|
||||
?MODULE, webadmin_user_parse_query, 50),
|
||||
AccessMaxOfflineMsgs =
|
||||
gen_mod:get_opt(access_max_user_messages, Opts,
|
||||
fun(A) when is_atom(A) -> A end,
|
||||
max_user_offline_messages),
|
||||
{ok,
|
||||
#state{host = Host,
|
||||
access_max_offline_messages = AccessMaxOfflineMsgs}}.
|
||||
@@ -253,7 +246,6 @@ store_offline_msg(Host, {User, _}, Msgs, Len, MaxOfflineMsgs,
|
||||
end, Msgs)
|
||||
end.
|
||||
|
||||
%% Function copied from ejabberd_sm.erl:
|
||||
get_max_user_messages(AccessRule, {User, Server}, Host) ->
|
||||
case acl:match_rule(
|
||||
Host, AccessRule, jlib:make_jid(User, Server, <<"">>)) of
|
||||
@@ -308,24 +300,24 @@ need_to_store(LServer, Packet) ->
|
||||
store_packet(From, To, Packet) ->
|
||||
case need_to_store(To#jid.lserver, Packet) of
|
||||
true ->
|
||||
case has_no_store_hint(Packet) of
|
||||
false ->
|
||||
case check_event(From, To, Packet) of
|
||||
true ->
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
TimeStamp = now(),
|
||||
#xmlel{children = Els} = Packet,
|
||||
Expire = find_x_expire(TimeStamp, Els),
|
||||
gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
|
||||
#offline_msg{us = {LUser, LServer},
|
||||
timestamp = TimeStamp, expire = Expire,
|
||||
from = From, to = To, packet = Packet},
|
||||
stop;
|
||||
_ -> ok
|
||||
end;
|
||||
_ -> ok
|
||||
end;
|
||||
false -> ok
|
||||
case has_no_store_hint(Packet) of
|
||||
false ->
|
||||
case check_event(From, To, Packet) of
|
||||
true ->
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
TimeStamp = now(),
|
||||
#xmlel{children = Els} = Packet,
|
||||
Expire = find_x_expire(TimeStamp, Els),
|
||||
gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
|
||||
#offline_msg{us = {LUser, LServer},
|
||||
timestamp = TimeStamp, expire = Expire,
|
||||
from = From, to = To, packet = Packet},
|
||||
stop;
|
||||
_ -> ok
|
||||
end;
|
||||
_ -> ok
|
||||
end;
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
has_no_store_hint(Packet) ->
|
||||
|
||||
+7
-2
@@ -63,6 +63,7 @@
|
||||
{host = <<"">>,
|
||||
send_pings = ?DEFAULT_SEND_PINGS :: boolean(),
|
||||
ping_interval = ?DEFAULT_PING_INTERVAL :: non_neg_integer(),
|
||||
ping_ack_timeout = undefined :: non_neg_integer(),
|
||||
timeout_action = none :: none | kill,
|
||||
timers = (?DICT):new() :: ?TDICT}).
|
||||
|
||||
@@ -106,6 +107,9 @@ init([Host, Opts]) ->
|
||||
PingInterval = gen_mod:get_opt(ping_interval, Opts,
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
?DEFAULT_PING_INTERVAL),
|
||||
PingAckTimeout = gen_mod:get_opt(ping_ack_timeout, Opts,
|
||||
fun(I) when is_integer(I), I>0 -> I * 1000 end,
|
||||
undefined),
|
||||
TimeoutAction = gen_mod:get_opt(timeout_action, Opts,
|
||||
fun(none) -> none;
|
||||
(kill) -> kill
|
||||
@@ -131,6 +135,7 @@ init([Host, Opts]) ->
|
||||
#state{host = Host, send_pings = SendPings,
|
||||
ping_interval = PingInterval,
|
||||
timeout_action = TimeoutAction,
|
||||
ping_ack_timeout = PingAckTimeout,
|
||||
timers = (?DICT):new()}}.
|
||||
|
||||
terminate(_Reason, #state{host = Host}) ->
|
||||
@@ -169,7 +174,7 @@ handle_cast({iq_pong, JID, timeout}, State) ->
|
||||
JID,
|
||||
case ejabberd_sm:get_session_pid(User, Server, Resource)
|
||||
of
|
||||
Pid when is_pid(Pid) -> ejabberd_c2s:stop(Pid);
|
||||
Pid when is_pid(Pid) -> ejabberd_c2s:close(Pid);
|
||||
_ -> ok
|
||||
end;
|
||||
_ -> ok
|
||||
@@ -187,7 +192,7 @@ handle_info({timeout, _TRef, {ping, JID}}, State) ->
|
||||
gen_server:cast(Pid, {iq_pong, JID, Response})
|
||||
end,
|
||||
From = jlib:make_jid(<<"">>, State#state.host, <<"">>),
|
||||
ejabberd_local:route_iq(From, JID, IQ, F),
|
||||
ejabberd_local:route_iq(From, JID, IQ, F, State#state.ping_ack_timeout),
|
||||
Timers = add_timer(JID, State#state.ping_interval,
|
||||
State#state.timers),
|
||||
{noreply, State#state{timers = Timers}};
|
||||
|
||||
+10
-12
@@ -584,11 +584,10 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
|
||||
ejabberd_sm:route(jlib:make_jid(LUser, LServer,
|
||||
<<"">>),
|
||||
jlib:make_jid(LUser, LServer, <<"">>),
|
||||
{broadcast,
|
||||
{privacy_list,
|
||||
#userlist{name = Name,
|
||||
list = []},
|
||||
Name}}),
|
||||
{broadcast, {privacy_list,
|
||||
#userlist{name = Name,
|
||||
list = []},
|
||||
Name}}),
|
||||
{result, []};
|
||||
_ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end;
|
||||
@@ -601,12 +600,11 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
|
||||
ejabberd_sm:route(jlib:make_jid(LUser, LServer,
|
||||
<<"">>),
|
||||
jlib:make_jid(LUser, LServer, <<"">>),
|
||||
{broadcast,
|
||||
{privacy_list,
|
||||
#userlist{name = Name,
|
||||
list = List,
|
||||
needdb = NeedDb},
|
||||
Name}}),
|
||||
{broadcast, {privacy_list,
|
||||
#userlist{name = Name,
|
||||
list = List,
|
||||
needdb = NeedDb},
|
||||
Name}}),
|
||||
{result, []};
|
||||
_ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end
|
||||
@@ -1166,7 +1164,7 @@ update_table() ->
|
||||
end.
|
||||
|
||||
export(Server) ->
|
||||
case ejabberd_odbc:sql_query(jlib:nameprep(Server),
|
||||
case catch ejabberd_odbc:sql_query(jlib:nameprep(Server),
|
||||
[<<"select id from privacy_list order by "
|
||||
"id desc limit 1;">>]) of
|
||||
{selected, [<<"id">>], [[I]]} ->
|
||||
|
||||
@@ -64,8 +64,9 @@ start_link(Host, Opts) ->
|
||||
[]).
|
||||
|
||||
init([Opts]) ->
|
||||
mnesia:create_table(bytestream, [{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, bytestream)}]),
|
||||
mnesia:create_table(bytestream,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, bytestream)}]),
|
||||
mnesia:add_table_copy(bytestream, node(), ram_copies),
|
||||
MaxConnections = gen_mod:get_opt(max_connections, Opts,
|
||||
fun(I) when is_integer(I), I>0 ->
|
||||
|
||||
+13
-7
@@ -2450,7 +2450,7 @@ get_item(Host, Node, ItemId) ->
|
||||
|
||||
get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) ->
|
||||
case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, none) of
|
||||
{result, {I, none}} -> {result, I};
|
||||
{result, {Items, _RSM}} -> {result, Items};
|
||||
Error -> Error
|
||||
end.
|
||||
get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, RSM) ->
|
||||
@@ -3480,10 +3480,9 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
|
||||
NodeOptions = Node#pubsub_node.options,
|
||||
lists:foldl(fun({LJID, SubID, SubOptions}, {JIDs, Recipients}) ->
|
||||
case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of
|
||||
true ->
|
||||
%% If is to deliver :
|
||||
true ->
|
||||
case state_can_deliver(LJID, SubOptions) of
|
||||
[] -> {JIDs, Recipients};
|
||||
[] -> {JIDs, Recipients};
|
||||
JIDsToDeliver ->
|
||||
lists:foldl(
|
||||
fun(JIDToDeliver, {JIDsAcc, RecipientsAcc}) ->
|
||||
@@ -3494,11 +3493,14 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
|
||||
%% - add the Jid to JIDs list co-accumulator ;
|
||||
%% - create a tuple of the Jid, Nidx, and SubID (as list),
|
||||
%% and add the tuple to the Recipients list co-accumulator
|
||||
{[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]};
|
||||
{[JIDToDeliver | JIDsAcc],
|
||||
[{JIDToDeliver, NodeName, [SubID]}
|
||||
| RecipientsAcc]};
|
||||
true ->
|
||||
%% - if the JIDs co-accumulator contains the Jid
|
||||
%% get the tuple containing the Jid from the Recipient list co-accumulator
|
||||
{_, {JIDToDeliver, NodeName1, SubIDs}} = lists:keysearch(JIDToDeliver, 1, RecipientsAcc),
|
||||
{_, {JIDToDeliver, NodeName1, SubIDs}} =
|
||||
lists:keysearch(JIDToDeliver, 1, RecipientsAcc),
|
||||
%% delete the tuple from the Recipients list
|
||||
% v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients),
|
||||
% v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, Nidx1, [SubID | SubIDs]}),
|
||||
@@ -3507,7 +3509,11 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
|
||||
% v1.1 : {JIDs, lists:append(Recipients1, [{LJID, Nidx1, lists:append(SubIDs, [SubID])}])}
|
||||
% v1.2 : {JIDs, [{LJID, Nidx1, [SubID | SubIDs]} | Recipients1]}
|
||||
% v2: {JIDs, Recipients1}
|
||||
{JIDsAcc, lists:keyreplace(JIDToDeliver, 1, RecipientsAcc, {JIDToDeliver, NodeName1, [SubID | SubIDs]})}
|
||||
{JIDsAcc,
|
||||
lists:keyreplace(JIDToDeliver, 1,
|
||||
RecipientsAcc,
|
||||
{JIDToDeliver, NodeName1,
|
||||
[SubID | SubIDs]})}
|
||||
end
|
||||
end, {JIDs, Recipients}, JIDsToDeliver)
|
||||
end;
|
||||
|
||||
@@ -408,6 +408,8 @@ try_register(User, Server, Password, SourceRaw, Lang) ->
|
||||
{error, ?ERR_JID_MALFORMED};
|
||||
{error, not_allowed} ->
|
||||
{error, ?ERR_NOT_ALLOWED};
|
||||
{error, too_many_users} ->
|
||||
{error, ?ERR_NOT_ALLOWED};
|
||||
{error, _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end
|
||||
|
||||
@@ -1005,7 +1005,6 @@ send_unsubscription_to_rosteritems(LUser, LServer) ->
|
||||
end,
|
||||
RosterItems).
|
||||
|
||||
%% @spec (From::jid(), Item::roster()) -> ok
|
||||
send_unsubscribing_presence(From, Item) ->
|
||||
IsTo = case Item#roster.subscription of
|
||||
both -> true;
|
||||
|
||||
+12
-17
@@ -98,9 +98,6 @@ start(Host, Opts) ->
|
||||
ejabberd_hooks:add(remove_user, Host, ?MODULE,
|
||||
remove_user, 50).
|
||||
|
||||
%%ejabberd_hooks:add(remove_user, Host,
|
||||
%% ?MODULE, remove_user, 50),
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(webadmin_menu_host, Host, ?MODULE,
|
||||
webadmin_menu, 70),
|
||||
@@ -126,12 +123,11 @@ stop(Host) ->
|
||||
register_user, 50),
|
||||
ejabberd_hooks:delete(anonymous_purge_hook, Host,
|
||||
?MODULE, remove_user, 50),
|
||||
%%ejabberd_hooks:delete(remove_user, Host,
|
||||
%% ?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:delete(remove_user, Host, ?MODULE,
|
||||
remove_user,
|
||||
50).%%ejabberd_hooks:delete(remove_user, Host,
|
||||
%% ?MODULE, remove_user, 50),
|
||||
50).
|
||||
%%ejabberd_hooks:delete(remove_user, Host,
|
||||
%% ?MODULE, remove_user, 50),
|
||||
|
||||
get_user_roster(Items, US) ->
|
||||
{U, S} = US,
|
||||
@@ -622,7 +618,6 @@ get_group_users(Host1, Group1) ->
|
||||
|
||||
get_group_users(Host, Group, GroupOpts) ->
|
||||
case proplists:get_value(all_users, GroupOpts, false) of
|
||||
%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
|
||||
true -> ejabberd_auth:get_vh_registered_users(Host);
|
||||
false -> []
|
||||
end
|
||||
@@ -675,25 +670,22 @@ get_group_name(Host1, Group1) ->
|
||||
|
||||
%% Get list of names of groups that have @all@/@online@/etc in the memberlist
|
||||
get_special_users_groups(Host) ->
|
||||
%% Get list of names of groups that have @online@ in the memberlist
|
||||
lists:filter(fun (Group) ->
|
||||
get_group_opt(Host, Group, all_users, false) orelse
|
||||
get_group_opt(Host, Group, online_users, false)
|
||||
end,
|
||||
list_groups(Host)).
|
||||
|
||||
%% Get list of names of groups that have @online@ in the memberlist
|
||||
get_special_users_groups_online(Host) ->
|
||||
%% Given two lists of groupnames and their options,
|
||||
%% return the list of displayed groups to the second list
|
||||
lists:filter(fun (Group) ->
|
||||
get_group_opt(Host, Group, online_users, false)
|
||||
end,
|
||||
list_groups(Host)).
|
||||
|
||||
%% Given two lists of groupnames and their options,
|
||||
%% return the list of displayed groups to the second list
|
||||
displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
|
||||
%% Given a list of group names with options,
|
||||
%% for those that have @all@ in memberlist,
|
||||
%% get the list of groups displayed
|
||||
DisplayedGroups = lists:usort(lists:flatmap(fun
|
||||
({_Group, Opts}) ->
|
||||
[G
|
||||
@@ -712,6 +704,9 @@ displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
|
||||
lists:member(disabled,
|
||||
proplists:get_value(G, GroupsOpts, []))].
|
||||
|
||||
%% Given a list of group names with options,
|
||||
%% for those that have @all@ in memberlist,
|
||||
%% get the list of groups displayed
|
||||
get_special_displayed_groups(GroupsOpts) ->
|
||||
Groups = lists:filter(fun ({_Group, Opts}) ->
|
||||
proplists:get_value(all_users, Opts, false)
|
||||
@@ -825,7 +820,7 @@ is_user_in_group(US, Group, Host, odbc) ->
|
||||
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
|
||||
add_user_to_group(Host, US, Group) ->
|
||||
{LUser, LServer} = US,
|
||||
case ejabberd_regexp:run(LUser, <<"^@.+@$">>) of
|
||||
case ejabberd_regexp:run(LUser, <<"^@.+@\$">>) of
|
||||
match ->
|
||||
GroupOpts = (?MODULE):get_group_opts(Host, Group),
|
||||
MoreGroupOpts = case LUser of
|
||||
@@ -884,7 +879,7 @@ push_displayed_to_user(LUser, LServer, Host, Subscription, DisplayedGroups) ->
|
||||
|
||||
remove_user_from_group(Host, US, Group) ->
|
||||
{LUser, LServer} = US,
|
||||
case ejabberd_regexp:run(LUser, <<"^@.+@$">>) of
|
||||
case ejabberd_regexp:run(LUser, <<"^@.+@\$">>) of
|
||||
match ->
|
||||
GroupOpts = (?MODULE):get_group_opts(Host, Group),
|
||||
NewGroupOpts = case LUser of
|
||||
@@ -1030,7 +1025,7 @@ push_item(User, Server, Item) ->
|
||||
children = [item_to_xml(Item)]}]}),
|
||||
lists:foreach(fun (Resource) ->
|
||||
JID = jlib:make_jid(User, Server, Resource),
|
||||
ejabberd_router:route(JID, JID, Stanza)
|
||||
ejabberd_router:route(jlib:jid_remove_resource(JID), JID, Stanza)
|
||||
end,
|
||||
ejabberd_sm:get_user_resources(User, Server)).
|
||||
|
||||
|
||||
@@ -275,16 +275,9 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
%% For a given user, map all his shared roster contacts to groups they are
|
||||
%% members of. Skip the user himself iff SkipUS is true.
|
||||
|
||||
get_user_to_groups_map({_, Server} = US, SkipUS) ->
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
%% Pass given FilterParseArgs to eldap_filter:parse, and if successful, run and
|
||||
%% return the resulting filter, retrieving given AttributesList. Return the
|
||||
%% result entries. On any error silently return an empty list of results.
|
||||
%%
|
||||
%% Eldap server ID and base DN for the query are both retrieved from the State
|
||||
%% record.
|
||||
lists:foldl(fun (Group, Dict1) ->
|
||||
GroupName = get_group_name(Server, Group),
|
||||
lists:foldl(fun (Contact, Dict) ->
|
||||
|
||||
+1
-1
@@ -1,11 +1,11 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2014, Evgeny Khramtsov
|
||||
%%% @doc
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 21 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2014-2015 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2014, Evgeny Khramtsov
|
||||
%%% @doc
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 21 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2014-2015 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2014, Evgeny Khramtsov
|
||||
%%% @doc
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 23 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2014-2015 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
|
||||
+7
-7
@@ -175,7 +175,7 @@ get_local_stat(_Server, _, Name) ->
|
||||
|
||||
get_node_stat(Node, Name)
|
||||
when Name == <<"time/uptime">> ->
|
||||
case catch rpc:call(Node, erlang, statistics,
|
||||
case catch ejabberd_cluster:call(Node, erlang, statistics,
|
||||
[wall_clock])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
@@ -188,7 +188,7 @@ get_node_stat(Node, Name)
|
||||
end;
|
||||
get_node_stat(Node, Name)
|
||||
when Name == <<"time/cputime">> ->
|
||||
case catch rpc:call(Node, erlang, statistics, [runtime])
|
||||
case catch ejabberd_cluster:call(Node, erlang, statistics, [runtime])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
?STATERR(<<"500">>, <<"Internal Server Error">>);
|
||||
@@ -200,7 +200,7 @@ get_node_stat(Node, Name)
|
||||
end;
|
||||
get_node_stat(Node, Name)
|
||||
when Name == <<"users/online">> ->
|
||||
case catch rpc:call(Node, ejabberd_sm,
|
||||
case catch ejabberd_cluster:call(Node, ejabberd_sm,
|
||||
dirty_get_my_sessions_list, [])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
@@ -211,7 +211,7 @@ get_node_stat(Node, Name)
|
||||
end;
|
||||
get_node_stat(Node, Name)
|
||||
when Name == <<"transactions/committed">> ->
|
||||
case catch rpc:call(Node, mnesia, system_info,
|
||||
case catch ejabberd_cluster:call(Node, mnesia, system_info,
|
||||
[transaction_commits])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
@@ -222,7 +222,7 @@ get_node_stat(Node, Name)
|
||||
end;
|
||||
get_node_stat(Node, Name)
|
||||
when Name == <<"transactions/aborted">> ->
|
||||
case catch rpc:call(Node, mnesia, system_info,
|
||||
case catch ejabberd_cluster:call(Node, mnesia, system_info,
|
||||
[transaction_failures])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
@@ -233,7 +233,7 @@ get_node_stat(Node, Name)
|
||||
end;
|
||||
get_node_stat(Node, Name)
|
||||
when Name == <<"transactions/restarted">> ->
|
||||
case catch rpc:call(Node, mnesia, system_info,
|
||||
case catch ejabberd_cluster:call(Node, mnesia, system_info,
|
||||
[transaction_restarts])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
@@ -244,7 +244,7 @@ get_node_stat(Node, Name)
|
||||
end;
|
||||
get_node_stat(Node, Name)
|
||||
when Name == <<"transactions/logged">> ->
|
||||
case catch rpc:call(Node, mnesia, system_info,
|
||||
case catch ejabberd_cluster:call(Node, mnesia, system_info,
|
||||
[transaction_log_writes])
|
||||
of
|
||||
{badrpc, _Reason} ->
|
||||
|
||||
+2
-1
@@ -54,7 +54,8 @@ init(Host, ServerHost, Opts) ->
|
||||
ok.
|
||||
|
||||
terminate(Host, ServerHost) ->
|
||||
node_flat:terminate(Host, ServerHost), ok.
|
||||
node_flat:terminate(Host, ServerHost),
|
||||
ok.
|
||||
|
||||
options() ->
|
||||
[{deliver_payloads, true},
|
||||
|
||||
@@ -55,7 +55,8 @@ init(Host, ServerHost, Opts) ->
|
||||
ok.
|
||||
|
||||
terminate(Host, ServerHost) ->
|
||||
node_flat_odbc:terminate(Host, ServerHost), ok.
|
||||
node_flat_odbc:terminate(Host, ServerHost),
|
||||
ok.
|
||||
|
||||
options() ->
|
||||
[{odbc, true}, {rsm, true} | node_pep:options()].
|
||||
|
||||
+1
-3
@@ -86,6 +86,4 @@ hi_round(Password, UPrev, IterationCount) ->
|
||||
IterationCount - 1)))).
|
||||
|
||||
sha_mac(Key, Data) ->
|
||||
Context1 = crypto:hmac_init(sha, Key),
|
||||
Context2 = crypto:hmac_update(Context1, Data),
|
||||
crypto:hmac_final(Context2).
|
||||
crypto:hmac(sha, Key, Data).
|
||||
|
||||
@@ -47,6 +47,10 @@
|
||||
{libdir, "{{release_dir}}/lib"}.
|
||||
{docdir, "{{release_dir}}/doc"}.
|
||||
|
||||
{ldflags, "@LDFLAGS@"}.
|
||||
{cflags, "@CFLAGS@"}.
|
||||
{cppflags, "@CPPFLAGS@"}.
|
||||
|
||||
%% Local Variables:
|
||||
%% mode: erlang
|
||||
%% End:
|
||||
|
||||
Reference in New Issue
Block a user