Compare commits
461 Commits
master
...
v3.0.0-P004
| Author | SHA1 | Date | |
|---|---|---|---|
| e4ec23919d | |||
| 1c095b609a | |||
| a819514e43 | |||
| 16cd93d587 | |||
| a8e5984f5f | |||
| 1a45637860 | |||
| 8cb3f61f88 | |||
| 4bc4f54e43 | |||
| 08774b1b5a | |||
| 300e765e20 | |||
| 951599be21 | |||
| 8bea14837e | |||
| 09250d6121 | |||
| 5968b35320 | |||
| f364fcda56 | |||
| 170874e115 | |||
| bec499a4a1 | |||
| d6709bd53a | |||
| 1cd1dd9f23 | |||
| af198dbe92 | |||
| 80ff4ad3d3 | |||
| 6e1691e2fa | |||
| e52c324a5e | |||
| e7b1f21e1c | |||
| a2d2581199 | |||
| eedd9496e4 | |||
| 3d4775d555 | |||
| 7f11f4a1fb | |||
| 612f15ab17 | |||
| af9ff6f1a1 | |||
| 9664de5242 | |||
| b13c51c8cf | |||
| 8229ee8dd8 | |||
| 476acbfa44 | |||
| 23dd81d18c | |||
| f08595b0e5 | |||
| 50b8500bd5 | |||
| 1cacd48261 | |||
| cf11acc450 | |||
| 0c18dd7068 | |||
| 4e807d69e8 | |||
| a61b7b1d98 | |||
| a55c85252f | |||
| 0168e39bb3 | |||
| 8f8b6870a0 | |||
| dc7208ed1c | |||
| 60b0b918a6 | |||
| d9e2d4e6a9 | |||
| 8fc80178b6 | |||
| c801bd9411 | |||
| b9d5a3350d | |||
| 2bb0095a32 | |||
| eda360914e | |||
| 042172eae1 | |||
| 0eaab78f67 | |||
| 5ec65571f8 | |||
| 7bbc98b04c | |||
| eda6b80ece | |||
| 2ddb7be19a | |||
| 04f20a21fb | |||
| 40ae9b33ef | |||
| e2357ecd96 | |||
| 0116287405 | |||
| 6ee534b262 | |||
| 42f72a49b1 | |||
| 771acc3ac7 | |||
| 5706aaa89c | |||
| c17bbc8859 | |||
| 8f317c0059 | |||
| d1c654a7e1 | |||
| ae5c31b06d | |||
| b09cda5c17 | |||
| 7887392c1d | |||
| cc2b7d2832 | |||
| 0a69935b6b | |||
| 4b7c74415f | |||
| 418baf4fa6 | |||
| fcf647738d | |||
| 3c9ab9e53d | |||
| c339c9b2ad | |||
| 4ef32bb18f | |||
| 0f87034539 | |||
| acee58f6bb | |||
| ceeef24faa | |||
| 201b0b3725 | |||
| 5f8b41a357 | |||
| a003ef556b | |||
| cb91e10803 | |||
| 5190866a24 | |||
| 473a58e3c2 | |||
| 3e6b88cbc3 | |||
| 362d7f617d | |||
| d11df52abf | |||
| 906161b27f | |||
| 347e8b28ef | |||
| c85c2796f4 | |||
| df72de96ae | |||
| e1f8233d08 | |||
| 38d2a27c56 | |||
| 6a42119292 | |||
| 89787875d4 | |||
| d76ae701cd | |||
| c4db156ad2 | |||
| d95778ea16 | |||
| a9b452fba9 | |||
| afed9e919e | |||
| 5252dcbd2c | |||
| a5cfac76dd | |||
| 45aa32f6fe | |||
| 2372e30150 | |||
| f3b55596b2 | |||
| 84eee8d5a7 | |||
| a800a5d4df | |||
| dfa47556d1 | |||
| 77111aeec1 | |||
| c9ac75474f | |||
| 0fcaef0566 | |||
| 031c7412a8 | |||
| 594ff79514 | |||
| 8f3f74a6d7 | |||
| ff2050b301 | |||
| 89ea1dd1c4 | |||
| 66902b788b | |||
| ec8cb81c0d | |||
| 1a24ac62e5 | |||
| 78fb913a00 | |||
| e521c8368a | |||
| ac8c536b50 | |||
| 84d9ee07b4 | |||
| ab9ac62138 | |||
| beaf351ba4 | |||
| 37d3a4e1f5 | |||
| 566c046cd5 | |||
| df921fef40 | |||
| 56e7affdfd | |||
| bef66dba24 | |||
| e62af41fa8 | |||
| 472089a328 | |||
| c4d582ace4 | |||
| a4e320c263 | |||
| 7d3d008940 | |||
| 3be9c27509 | |||
| 3ef6e4c834 | |||
| a15e689386 | |||
| 7c8a452b00 | |||
| e5d202a0bf | |||
| 083dfe01ea | |||
| f0c745b916 | |||
| 9ff1a80db4 | |||
| 5480ee29fd | |||
| 0d4b0b4218 | |||
| cfe396e155 | |||
| 2163cbb22e | |||
| 6b3f228327 | |||
| e58e6a09dd | |||
| 2d05ddd466 | |||
| 975f4c56d4 | |||
| 8eef2f02bf | |||
| 2f7c69fd14 | |||
| 02eeebd41a | |||
| 99610c3357 | |||
| aad740f34c | |||
| a70c72e50e | |||
| a2fdc75730 | |||
| b82789f11d | |||
| 6b7d70adf6 | |||
| 10f10a2fd3 | |||
| 35c4e740ab | |||
| e3085a4d48 | |||
| e8d008c392 | |||
| 674ab27700 | |||
| 9442a583bc | |||
| 9787416e88 | |||
| fbe2995696 | |||
| 33251cd5a8 | |||
| f387934886 | |||
| fb39b163df | |||
| 011535f0de | |||
| 75d3152a0f | |||
| db8bd0126b | |||
| 545f9ce525 | |||
| 7f1e9d3972 | |||
| 431c34709c | |||
| cf6d67ceed | |||
| ef0cf5d3d7 | |||
| e99ecf6d06 | |||
| 83c7da3831 | |||
| 8dc4dd7f3b | |||
| 66b7caafe0 | |||
| 7c0b2f5425 | |||
| 4645885180 | |||
| 472d046426 | |||
| 613d175f37 | |||
| a63bbe8a23 | |||
| bc118986b7 | |||
| 8d8dee5acf | |||
| 92feebd0fa | |||
| 0b423eb287 | |||
| 7d7c739cb3 | |||
| 60d422eb8e | |||
| 52df4fa024 | |||
| 6ebebdd02d | |||
| e30e7686e3 | |||
| 290432c0ee | |||
| 6563267055 | |||
| ecf7b0282e | |||
| ea6e85d926 | |||
| b4d107301d | |||
| fead37d1c5 | |||
| eaecb9b65c | |||
| 2948cddebf | |||
| 438dc57def | |||
| 4d64fbb0ac | |||
| e0781d9217 | |||
| f04fe5f743 | |||
| 66a5aff323 | |||
| 5746c08f72 | |||
| 39acf823ef | |||
| 2ea9e6ed59 | |||
| 21c75ebce5 | |||
| 32e0a88edc | |||
| bd91e2da16 | |||
| f3d24b6a07 | |||
| d1377da151 | |||
| d471be26cf | |||
| 31f6a9e66e | |||
| d736c47649 | |||
| 0e4806820e | |||
| 3850b91571 | |||
| 7b0174a626 | |||
| 2270df86d9 | |||
| a04131c6d7 | |||
| e5830253b9 | |||
| b7a07087d1 | |||
| adf56dedf3 | |||
| 6bfd8b8e9a | |||
| 1babae067d | |||
| 3ae4797848 | |||
| 86f0a9790d | |||
| 54acf9bde4 | |||
| 8f27a697c0 | |||
| c9a712a16a | |||
| d6e81ac06b | |||
| b0b371d23a | |||
| dd772404c5 | |||
| bed5e80ef9 | |||
| 9ccdb5d78b | |||
| 5bef1a8f77 | |||
| 4f1637fa40 | |||
| 796cb6634b | |||
| bb5480756a | |||
| edb030f49a | |||
| 0ed4ceebea | |||
| 31f7eadfca | |||
| 1c72c45404 | |||
| f8fd9969e1 | |||
| 8d09655a89 | |||
| a06b627631 | |||
| cb41c8ef80 | |||
| d4cea0f78f | |||
| 4d20abd7b6 | |||
| 351ad528f9 | |||
| 006da5589e | |||
| 5f32dd3959 | |||
| a9269df2aa | |||
| a7d82d6ecb | |||
| 5b10b58c9f | |||
| ae24f7d787 | |||
| 1dfd9fd568 | |||
| f655ab2ffc | |||
| 302294faec | |||
| b6a637c121 | |||
| 60009ece44 | |||
| 41fad8956b | |||
| f0c32433dc | |||
| 78f50c58bf | |||
| 133b8d42a3 | |||
| 3785b3e951 | |||
| 70e1545d3a | |||
| 8aaf9bffa0 | |||
| d65b785f5d | |||
| 9aabd59a1f | |||
| 8806fdc1c2 | |||
| 1922bf21f0 | |||
| c98ddeb59f | |||
| 613214da18 | |||
| 38693a670b | |||
| a97a60a888 | |||
| 49365da481 | |||
| 70e84021f2 | |||
| 24e033ac79 | |||
| 658ab235ba | |||
| 2f16a160c0 | |||
| 4a2f62062e | |||
| 33d4126290 | |||
| 87315e92a8 | |||
| 494add4fd0 | |||
| 3b7458bbc2 | |||
| f05a4d1638 | |||
| 75d2cbcb14 | |||
| f9fa168d84 | |||
| 5ad1d08b89 | |||
| dcb068c7ad | |||
| 2d32d2f25e | |||
| 307e57a105 | |||
| 8e68e89816 | |||
| 1564060c6c | |||
| f39ccd73c5 | |||
| 01689bc6b9 | |||
| 024a80d41f | |||
| f485109c39 | |||
| bde46896d6 | |||
| 0c30b012f7 | |||
| 14b39a0ee4 | |||
| e380eee223 | |||
| 1959546ff9 | |||
| 92f5509b35 | |||
| 1a2e6b02ab | |||
| 56bf156b6f | |||
| 5632901820 | |||
| 92b6c12420 | |||
| 062d58026a | |||
| 7ef85dddea | |||
| 4a9e7f0a3a | |||
| cffe224d4a | |||
| 15c27c9ddd | |||
| 149f8e2b45 | |||
| 2ab31cb613 | |||
| 03870f962c | |||
| 405e9b24b0 | |||
| 3c51ca06d5 | |||
| 02cfb11a6d | |||
| 4e875c7fb7 | |||
| 21f2817f40 | |||
| 31e4ccf78b | |||
| bfedd21c98 | |||
| 9f3cdad3f7 | |||
| 931866ee33 | |||
| b3facf092a | |||
| 70c1e1d0b1 | |||
| aea394861d | |||
| 2e33904bb8 | |||
| 2d3bbd43d7 | |||
| 6c0e9ef575 | |||
| ca62271a89 | |||
| c96a1805e8 | |||
| babff870a8 | |||
| 5cd3de9cd7 | |||
| 437d8c6b7c | |||
| 440eef74e9 | |||
| c849552177 | |||
| 6134c67df4 | |||
| aa60140ba8 | |||
| 59135cac6f | |||
| 8d69d4aaba | |||
| 426b7ca769 | |||
| b61d16dd33 | |||
| 807af3c08a | |||
| d07424365d | |||
| 70fe2948b9 | |||
| a5166f3946 | |||
| 11b00b92e9 | |||
| c10e43f95f | |||
| eeffc77a1a | |||
| 254686ab46 | |||
| 4a6fc46713 | |||
| bde3bce1e7 | |||
| f76dcd0d48 | |||
| 7da8d9e4e3 | |||
| 3a7d02dbd3 | |||
| 350af319bf | |||
| f81473fc65 | |||
| b6dcd41225 | |||
| db2baa8f84 | |||
| a894d25b1f | |||
| a93991bef2 | |||
| 7127d067c8 | |||
| ba326eb976 | |||
| fd50b2169b | |||
| 00d8b2ac30 | |||
| cac23c39c9 | |||
| a5813b798f | |||
| 191cd2af3c | |||
| f2cfee11de | |||
| b0c79c57b0 | |||
| 8ea523889b | |||
| b44c462b0e | |||
| 0987700a27 | |||
| ff4f052bb1 | |||
| 100b821c1a | |||
| 893c47a2e0 | |||
| 694af69982 | |||
| c576f340f9 | |||
| 179a0cf255 | |||
| a45ecb70ff | |||
| df1ab9149f | |||
| cb54444f00 | |||
| c77e7fbb7d | |||
| 40625b29f2 | |||
| 2624f3ba51 | |||
| 44832e12b3 | |||
| caa8d0c411 | |||
| 7e72d18292 | |||
| b0a81778af | |||
| 652774a83c | |||
| 2aea503a2a | |||
| 0d8aacb3e7 | |||
| e6be70943f | |||
| c86e4faba3 | |||
| c3c06ccd1c | |||
| 18b569a356 | |||
| 261acfce54 | |||
| c8567f1de2 | |||
| 35a0e27d04 | |||
| 73f7b2ba38 | |||
| 8a693df6e6 | |||
| c41bdea1f1 | |||
| cccbf7de12 | |||
| 660a2735f0 | |||
| 77136bccdf | |||
| b8b6fc0da5 | |||
| 8ecf8d7e27 | |||
| 4134edf8de | |||
| a77d53d738 | |||
| 49a3424a26 | |||
| 92a60ff7fd | |||
| 33c7d36a95 | |||
| 6c7316cbdd | |||
| 09da9eeb95 | |||
| 76d4ba66b2 | |||
| f284fc3284 | |||
| 86a59fb469 | |||
| 31da259a75 | |||
| 363711a370 | |||
| bf98fa0c01 | |||
| cd923838c3 | |||
| a22ebd3c49 | |||
| 353d16b8ef | |||
| 6bb0dc12f1 | |||
| ad00ec1518 | |||
| c03140d4be | |||
| ea8aa1f25b | |||
| 8fe6ed011d | |||
| 35cde6787d | |||
| 23b28ec60f | |||
| 59ae9bea76 | |||
| 7be707f7bc | |||
| 200815dcdb | |||
| 2d1c416daf | |||
| 3aaebe98f4 | |||
| 2ee7642816 | |||
| bf63d09d80 | |||
| 28c4c87956 | |||
| d0b7cd599b | |||
| cc1839a250 | |||
| 7d37715f8b | |||
| 091b4568d5 | |||
| cc0503fd5e | |||
| 4862251f34 | |||
| 987d796439 | |||
| 628571f8cf |
-20
@@ -16,23 +16,3 @@
|
||||
/doc/*.toc
|
||||
/doc/contributed_modules.tex
|
||||
/doc/version.tex
|
||||
/src/*.beam
|
||||
/src/*.so
|
||||
/src/*.so.dSYM
|
||||
/src/*/*.beam
|
||||
/src/*/Makefile
|
||||
/src/Makefile
|
||||
/src/XmppAddr.asn1db
|
||||
/src/XmppAddr.erl
|
||||
/src/XmppAddr.hrl
|
||||
/src/aclocal.m4
|
||||
/src/autom4te.cache
|
||||
/src/config.log
|
||||
/src/config.status
|
||||
/src/ejabberd.init
|
||||
/src/ejabberdctl.example
|
||||
/src/eldap/ELDAPv3.asn1db
|
||||
/src/eldap/ELDAPv3.erl
|
||||
/src/eldap/ELDAPv3.hrl
|
||||
/src/eldap/eldap_filter_yecc.erl
|
||||
/src/epam
|
||||
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
REBAR = @REBAR@
|
||||
INSTALL = @INSTALL@
|
||||
SED = @SED@
|
||||
ERL = @ERL@
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
|
||||
DESTDIR =
|
||||
|
||||
# /etc/ejabberd/
|
||||
ETCDIR = $(DESTDIR)@sysconfdir@/ejabberd
|
||||
|
||||
# /sbin/
|
||||
SBINDIR = $(DESTDIR)@sbindir@
|
||||
|
||||
# /lib/ejabberd/
|
||||
EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd
|
||||
|
||||
# /share/doc/ejabberd
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
datarootdir = @datarootdir@
|
||||
DOCDIR = $(DESTDIR)@docdir@
|
||||
|
||||
# /usr/lib/ejabberd/ebin/
|
||||
BEAMDIR = $(EJABBERDDIR)/ebin
|
||||
|
||||
# /usr/lib/ejabberd/include/
|
||||
INCLUDEDIR = $(EJABBERDDIR)/include
|
||||
|
||||
# /usr/lib/ejabberd/priv/
|
||||
PRIVDIR = $(EJABBERDDIR)/priv
|
||||
|
||||
# /usr/lib/ejabberd/priv/bin
|
||||
PBINDIR = $(PRIVDIR)/bin
|
||||
|
||||
# /usr/lib/ejabberd/priv/lib
|
||||
SODIR = $(PRIVDIR)/lib
|
||||
|
||||
# /usr/lib/ejabberd/priv/msgs
|
||||
MSGSDIR = $(PRIVDIR)/msgs
|
||||
|
||||
# /var/lib/ejabberd/
|
||||
SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd
|
||||
|
||||
# /var/lock/ejabberdctl
|
||||
CTLLOCKDIR = $(DESTDIR)@localstatedir@/lock/ejabberdctl
|
||||
|
||||
# /var/lib/ejabberd/.erlang.cookie
|
||||
COOKIEFILE = $(SPOOLDIR)/.erlang.cookie
|
||||
|
||||
# /var/log/ejabberd/
|
||||
LOGDIR = $(DESTDIR)@localstatedir@/log/ejabberd
|
||||
|
||||
INSTALLUSER=@INSTALLUSER@
|
||||
# if no user was enabled, don't set privileges or ownership
|
||||
ifeq ($(INSTALLUSER),)
|
||||
O_USER=
|
||||
G_USER=
|
||||
CHOWN_COMMAND=echo
|
||||
CHOWN_OUTPUT=/dev/null
|
||||
INIT_USER=root
|
||||
else
|
||||
O_USER=-o $(INSTALLUSER)
|
||||
G_USER=-g $(INSTALLUSER)
|
||||
CHOWN_COMMAND=chown
|
||||
CHOWN_OUTPUT=&1
|
||||
INIT_USER=$(INSTALLUSER)
|
||||
endif
|
||||
|
||||
all: deps src
|
||||
|
||||
deps:
|
||||
$(REBAR) get-deps
|
||||
|
||||
src:
|
||||
$(REBAR) compile
|
||||
|
||||
translations:
|
||||
contrib/extract_translations/prepare-translation.sh -updateall
|
||||
|
||||
doc:
|
||||
echo making $$target in doc; \
|
||||
(cd doc && $(MAKE) $$target) || exit 1
|
||||
|
||||
edoc:
|
||||
$(ERL) -noinput +B -eval \
|
||||
'case edoc:application(ejabberd, ".", []) of ok -> halt(0); error -> halt(1) end.'
|
||||
|
||||
install: all
|
||||
#
|
||||
# Configuration files
|
||||
$(INSTALL) -d -m 750 $(G_USER) $(ETCDIR)
|
||||
[ -f $(ETCDIR)/ejabberd.cfg ] \
|
||||
&& $(INSTALL) -b -m 640 $(G_USER) ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg-new \
|
||||
|| $(INSTALL) -b -m 640 $(G_USER) ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
|
||||
$(SED) -e "s*{{rootdir}}*@prefix@*" \
|
||||
-e "s*{{installuser}}*@INSTALLUSER@*" \
|
||||
-e "s*{{libdir}}*@libdir@*" \
|
||||
-e "s*{{sysconfdir}}*@sysconfdir@*" \
|
||||
-e "s*{{localstatedir}}*@localstatedir@*" \
|
||||
-e "s*{{docdir}}*@docdir@*" \
|
||||
-e "s*{{erl}}*@ERL@*" ejabberdctl.template \
|
||||
> ejabberdctl.example
|
||||
[ -f $(ETCDIR)/ejabberdctl.cfg ] \
|
||||
&& $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg-new \
|
||||
|| $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg
|
||||
$(INSTALL) -b -m 644 $(G_USER) inetrc $(ETCDIR)/inetrc
|
||||
#
|
||||
# Administration script
|
||||
[ -d $(SBINDIR) ] || $(INSTALL) -d -m 755 $(SBINDIR)
|
||||
$(INSTALL) -m 550 $(G_USER) ejabberdctl.example $(SBINDIR)/ejabberdctl
|
||||
#
|
||||
# Init script
|
||||
$(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*" \
|
||||
-e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \
|
||||
> ejabberd.init
|
||||
chmod 755 ejabberd.init
|
||||
#
|
||||
# Binary Erlang files
|
||||
$(INSTALL) -d $(BEAMDIR)
|
||||
$(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)
|
||||
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)
|
||||
#
|
||||
# Binary system libraries
|
||||
$(INSTALL) -d $(SODIR)
|
||||
#$(INSTALL) -m 644 priv/lib/*.so $(SODIR)
|
||||
$(INSTALL) -m 644 deps/*/priv/lib/*.so $(SODIR)
|
||||
#
|
||||
# Translated strings
|
||||
$(INSTALL) -d $(MSGSDIR)
|
||||
$(INSTALL) -m 644 priv/msgs/*.msg $(MSGSDIR)
|
||||
#
|
||||
# Spool directory
|
||||
$(INSTALL) -d -m 750 $(O_USER) $(SPOOLDIR)
|
||||
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT)
|
||||
chmod -R 750 $(SPOOLDIR)
|
||||
[ ! -f $(COOKIEFILE) ] || { $(CHOWN_COMMAND) @INSTALLUSER@ $(COOKIEFILE) >$(CHOWN_OUTPUT) ; chmod 400 $(COOKIEFILE) ; }
|
||||
#
|
||||
# ejabberdctl lock directory
|
||||
$(INSTALL) -d -m 750 $(O_USER) $(CTLLOCKDIR)
|
||||
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(CTLLOCKDIR) >$(CHOWN_OUTPUT)
|
||||
chmod -R 750 $(CTLLOCKDIR)
|
||||
#
|
||||
# Log directory
|
||||
$(INSTALL) -d -m 750 $(O_USER) $(LOGDIR)
|
||||
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(LOGDIR) >$(CHOWN_OUTPUT)
|
||||
chmod -R 750 $(LOGDIR)
|
||||
#
|
||||
# Documentation
|
||||
$(INSTALL) -d $(DOCDIR)
|
||||
$(INSTALL) -m 644 doc/dev.html $(DOCDIR)
|
||||
$(INSTALL) -m 644 doc/guide.html $(DOCDIR)
|
||||
$(INSTALL) -m 644 doc/*.png $(DOCDIR)
|
||||
$(INSTALL) -m 644 doc/*.txt $(DOCDIR)
|
||||
[ -f doc/guide.pdf ] \
|
||||
&& $(INSTALL) -m 644 doc/guide.pdf $(DOCDIR) \
|
||||
|| echo "No doc/guide.pdf was built"
|
||||
$(INSTALL) -m 644 COPYING $(DOCDIR)
|
||||
|
||||
uninstall: uninstall-binary
|
||||
|
||||
uninstall-binary:
|
||||
rm -f $(SBINDIR)/ejabberdctl
|
||||
rm -fr $(DOCDIR)
|
||||
rm -f $(BEAMDIR)/*.beam
|
||||
rm -f $(BEAMDIR)/*.app
|
||||
rm -fr $(BEAMDIR)
|
||||
rm -f $(INCLUDEDIR)/*.hrl
|
||||
rm -fr $(INCLUDEDIR)
|
||||
rm -fr $(PBINDIR)
|
||||
rm -f $(SODIR)/*.so
|
||||
rm -fr $(SODIR)
|
||||
rm -f $(MSGSDIR)/*.msgs
|
||||
rm -fr $(MSGSDIR)
|
||||
rm -fr $(PRIVDIR)
|
||||
rm -fr $(EJABBERDDIR)
|
||||
|
||||
uninstall-all: uninstall-binary
|
||||
rm -rf $(ETCDIR)
|
||||
rm -rf $(EJABBERDDIR)
|
||||
rm -rf $(SPOOLDIR)
|
||||
rm -rf $(CTLLOCKDIR)
|
||||
rm -rf $(LOGDIR)
|
||||
|
||||
clean:
|
||||
$(REBAR) clean
|
||||
|
||||
clean-rel:
|
||||
rm -rf rel/ejabberd
|
||||
|
||||
distclean: clean clean-rel
|
||||
rm -f config.status
|
||||
rm -f config.log
|
||||
rm -rf autom4te.cache
|
||||
rm -rf deps
|
||||
rm -rf ebin
|
||||
rm -f Makefile
|
||||
rm -f vars.config
|
||||
rm -f src/ejabberd.app.src
|
||||
[ ! -f ../ChangeLog ] || rm -f ../ChangeLog
|
||||
|
||||
rel: all
|
||||
$(REBAR) generate
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
||||
Makefile: Makefile.in
|
||||
|
||||
deps := $(filter-out deps/riak_pb/ebin, $(wildcard deps/*/ebin))
|
||||
|
||||
erlang.plt:
|
||||
-dialyzer --build_plt --output_plt erlang.plt \
|
||||
--apps kernel stdlib sasl crypto public_key ssl mnesia \
|
||||
inets odbc tools compiler erts webtool runtime_tools asn1 \
|
||||
observer xmerl et gs wx syntax_tools $(deps)
|
||||
|
||||
plt: erlang.plt
|
||||
|
||||
dialyzer: plt
|
||||
-dialyzer --plt erlang.plt --add_to_plt --output_plt ejabberd.plt \
|
||||
--get_warnings -o dialyzer.log ebin
|
||||
|
||||
.PHONY: src doc edoc dialyzer Makefile TAGS clean clean-rel distclean rel plt \
|
||||
install uninstall uninstall-binary uninstall-all translations
|
||||
@@ -59,6 +59,8 @@ release : build release_clean
|
||||
copy mod_muc\*.erl $(SRC_DIR)\mod_muc
|
||||
mkdir $(SRC_DIR)\mod_pubsub
|
||||
copy mod_pubsub\*.erl $(SRC_DIR)\mod_pubsub
|
||||
mkdir $(SRC_DIR)\mod_pubsub_ng
|
||||
copy mod_pubsub_ng\*.erl $(SRC_DIR)\mod_pubsub_ng
|
||||
mkdir $(SRC_DIR)\mod_proxy65
|
||||
copy mod_proxy65\*.erl $(SRC_DIR)\mod_proxy65
|
||||
copy mod_proxy65\*.hrl $(SRC_DIR)\mod_proxy65
|
||||
@@ -100,6 +102,8 @@ all-recursive :
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_pubsub
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_pubsub_ng
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\mod_proxy65
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\stringprep
|
||||
@@ -143,6 +147,8 @@ clean-recursive :
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_pubsub
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_pubsub_ng
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\mod_proxy65
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\stringprep
|
||||
@@ -14,8 +14,6 @@ To compile ejabberd you need:
|
||||
- OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
|
||||
- Zlib 1.2.3 or higher, for Stream Compression support
|
||||
(XEP-0138). Optional.
|
||||
- Erlang mysql library. Optional. MySQL authentication/storage.
|
||||
- Erlang pgsql library. Optional. PostgreSQL authentication/storage.
|
||||
- PAM library. Optional. For Pluggable Authentication Modules (PAM).
|
||||
- GNU Iconv 1.8 or higher, for the IRC Transport
|
||||
(mod_irc). Optional. Not needed on systems with GNU Libc.
|
||||
@@ -25,7 +23,7 @@ To compile ejabberd you need:
|
||||
|
||||
1. Compile and install on *nix systems
|
||||
|
||||
To compile ejabberd, go to the directory src/ and execute the commands:
|
||||
To compile ejabberd execute the commands:
|
||||
./configure
|
||||
make
|
||||
|
||||
|
||||
Vendored
+1538
-3225
File diff suppressed because it is too large
Load Diff
+266
@@ -0,0 +1,266 @@
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.53)
|
||||
AC_PACKAGE_VERSION(3.0.0)
|
||||
AC_INIT(ejabberd, 3.0.0, [ejabberd@process-one.net], [ejabberd])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_SED
|
||||
|
||||
if test "x$GCC" = "xyes"; then
|
||||
CFLAGS="$CFLAGS -Wall"
|
||||
fi
|
||||
|
||||
# Checks Erlang runtime and compiler
|
||||
AC_ERLANG_NEED_ERL
|
||||
AC_ERLANG_NEED_ERLC
|
||||
|
||||
# Checks and sets ERLANG_ROOT_DIR and ERLANG_LIB_DIR variable
|
||||
# AC_ERLANG_SUBST_ROOT_DIR
|
||||
# AC_ERLANG_SUBST_LIB_DIR
|
||||
|
||||
#locating escript
|
||||
AC_PATH_PROG([ESCRIPT], [escript], [])
|
||||
|
||||
#locating rebar
|
||||
AC_PATH_PROG([REBAR], [rebar], [./rebar])
|
||||
|
||||
#locating make
|
||||
AC_CHECK_PROG([MAKE], [make], [make], [])
|
||||
|
||||
if test "x$ESCRIPT" = "x"; then
|
||||
AC_MSG_ERROR(['escript' was not found])
|
||||
fi
|
||||
|
||||
if test "x$MAKE" = "x"; then
|
||||
AC_MSG_ERROR(['make' was not found])
|
||||
fi
|
||||
|
||||
# Change default prefix
|
||||
AC_PREFIX_DEFAULT(/)
|
||||
|
||||
AC_ARG_ENABLE(hipe,
|
||||
[AC_HELP_STRING([--enable-hipe], [compile natively with HiPE, not recommended (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) hipe=true ;;
|
||||
no) hipe=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-hipe) ;;
|
||||
esac],[hipe=false])
|
||||
|
||||
AC_ARG_ENABLE(roster_gateway_workaround,
|
||||
[AC_HELP_STRING([--enable-roster-gateway-workaround], [turn on workaround for processing gateway subscriptions (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) roster_gateway_workaround=true ;;
|
||||
no) roster_gateway_workaround=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-roster-gateway-workaround) ;;
|
||||
esac],[roster_gateway_workaround=false])
|
||||
|
||||
AC_ARG_ENABLE(flash_hack,
|
||||
[AC_HELP_STRING([--enable-flash-hack], [support Adobe Flash client XML (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) flash_hack=true ;;
|
||||
no) flash_hack=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-flash-hack) ;;
|
||||
esac],[flash_hack=false])
|
||||
|
||||
AC_ARG_ENABLE(transient_supervisors,
|
||||
[AC_HELP_STRING([--enable-transient_supervisors], [use Erlang supervision for transient process (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) transient_supervisors=true ;;
|
||||
no) transient_supervisors=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-transient_supervisors) ;;
|
||||
esac],[transient_supervisors=false])
|
||||
|
||||
AC_ARG_ENABLE(full_xml,
|
||||
[AC_HELP_STRING([--enable-full-xml], [use XML features in XMPP stream (ex: CDATA) (default: no, requires XML compliant clients)])],
|
||||
[case "${enableval}" in
|
||||
yes) full_xml=true ;;
|
||||
no) full_xml=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-full-xml) ;;
|
||||
esac],[full_xml=false])
|
||||
|
||||
AC_ARG_ENABLE(mssql,
|
||||
[AC_HELP_STRING([--enable-mssql], [use Microsoft SQL Server database (default: no, requires --enable-odbc)])],
|
||||
[case "${enableval}" in
|
||||
yes) db_type=mssql ;;
|
||||
no) db_type=generic ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-mssql) ;;
|
||||
esac],[db_type=generic])
|
||||
|
||||
AC_ARG_ENABLE(tools,
|
||||
[AC_HELP_STRING([--enable-tools], [build development tools (currently the ejabberd profiler only, default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) tools=true ;;
|
||||
no) tools=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;;
|
||||
esac],[tools=false])
|
||||
|
||||
AC_ARG_ENABLE(all,
|
||||
[AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-stun --enable-riak --enable-json --enable-iconv --enable-debug --enable-pubsub-ng --enable-http (useful for Dialyzer checks, default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) nif=true mysql=true pgsql=true pam=true zlib=true stun=true riak=true json=true iconv=true debug=true pubsub_ng=true http=true ;;
|
||||
no) nif=false mysql=false pgsql=false pam=false zlib=false stun=false riak=false json=false iconv=false debug=false pubsub_ng=false http=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;;
|
||||
esac],[])
|
||||
|
||||
AC_ARG_ENABLE(nif,
|
||||
[AC_HELP_STRING([--enable-nif], [replace some functions with C equivalents. Requires Erlang R13B04 or higher (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) nif=true ;;
|
||||
no) nif=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-nif) ;;
|
||||
esac],[if test "x$nif" = "x"; then nif=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(mysql,
|
||||
[AC_HELP_STRING([--enable-mysql], [enable MySQL support (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) mysql=true ;;
|
||||
no) mysql=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-mysql) ;;
|
||||
esac],[if test "x$mysql" = "x"; then mysql=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(pgsql,
|
||||
[AC_HELP_STRING([--enable-pgsql], [enable PostgreSQL support (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) pgsql=true ;;
|
||||
no) pgsql=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-pgsql) ;;
|
||||
esac],[if test "x$pgsql" = "x"; then pgsql=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(pam,
|
||||
[AC_HELP_STRING([--enable-pam], [enable PAM support (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) pam=true ;;
|
||||
no) pam=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-pam) ;;
|
||||
esac],[if test "x$pam" = "x"; then pam=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(zlib,
|
||||
[AC_HELP_STRING([--enable-zlib], [enable Stream Compression (XEP-0138) using zlib (default: yes)])],
|
||||
[case "${enableval}" in
|
||||
yes) zlib=true ;;
|
||||
no) zlib=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-zlib) ;;
|
||||
esac],[if test "x$zlib" = "x"; then zlib=true; fi])
|
||||
|
||||
AC_ARG_ENABLE(stun,
|
||||
[AC_HELP_STRING([--enable-stun], [enable STUN support (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) stun=true ;;
|
||||
no) stun=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-stun) ;;
|
||||
esac],[if test "x$stun" = "x"; then stun=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(riak,
|
||||
[AC_HELP_STRING([--enable-riak], [enable Riak support (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) riak=true ;;
|
||||
no) riak=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-riak) ;;
|
||||
esac],[if test "x$riak" = "x"; then riak=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(json,
|
||||
[AC_HELP_STRING([--enable-json], [enable JSON support for mod_bosh (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) json=true ;;
|
||||
no) json=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-json) ;;
|
||||
esac],[if test "x$json" = "x"; then json=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(iconv,
|
||||
[AC_HELP_STRING([--enable-iconv], [enable iconv support (default: yes)])],
|
||||
[case "${enableval}" in
|
||||
yes) iconv=true ;;
|
||||
no) iconv=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-iconv) ;;
|
||||
esac],[if test "x$iconv" = "x"; then iconv=true; fi])
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
[AC_HELP_STRING([--enable-debug], [enable debug information (default: yes)])],
|
||||
[case "${enableval}" in
|
||||
yes) debug=true ;;
|
||||
no) debug=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;;
|
||||
esac],[if test "x$debug" = "x"; then debug=true; fi])
|
||||
|
||||
AC_ARG_ENABLE(pubsub_ng,
|
||||
[AC_HELP_STRING([--enable-pubsub-ng], [enable PubSub NG (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) pubsub_ng=true ;;
|
||||
no) pubsub_ng=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-pubsub-ng) ;;
|
||||
esac],[if test "x$pubsub_ng" = "x"; then pubsub_ng=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(http,
|
||||
[AC_HELP_STRING([--enable-http], [build external HTTP libraries ('ibrowse' and 'lhttpc', default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) http=true ;;
|
||||
no) http=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-http) ;;
|
||||
esac],[if test "x$http" = "x"; then http=false; fi])
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
vars.config
|
||||
src/ejabberd.app.src])
|
||||
|
||||
ENABLEUSER=""
|
||||
AC_ARG_ENABLE(user,
|
||||
[AS_HELP_STRING([--enable-user[[[[=USER]]]]], [allow this system user to start ejabberd (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) ENABLEUSER=`whoami` ;;
|
||||
no) ENABLEUSER="" ;;
|
||||
*) ENABLEUSER=$enableval
|
||||
esac],
|
||||
[])
|
||||
if test "$ENABLEUSER" != ""; then
|
||||
echo "allow this system user to start ejabberd: $ENABLEUSER"
|
||||
AC_SUBST([INSTALLUSER], [$ENABLEUSER])
|
||||
fi
|
||||
|
||||
AC_ERLANG_CHECK_LIB([sasl], [],
|
||||
[AC_MSG_ERROR([Erlang application 'sasl' was not found])])
|
||||
AC_ERLANG_CHECK_LIB([crypto], [],
|
||||
[AC_MSG_ERROR([Erlang application 'crypto' was not found])])
|
||||
AC_ERLANG_CHECK_LIB([public_key], [],
|
||||
[AC_MSG_ERROR([Erlang application 'public_key' was not found])])
|
||||
AC_ERLANG_CHECK_LIB([ssl], [],
|
||||
[AC_MSG_ERROR([Erlang application 'ssl' was not found])])
|
||||
AC_ERLANG_CHECK_LIB([mnesia], [],
|
||||
[AC_MSG_ERROR([Erlang application 'mnesia' was not found])])
|
||||
AC_ERLANG_CHECK_LIB([inets], [],
|
||||
[AC_MSG_ERROR([Erlang application 'inets' was not found])])
|
||||
AC_ERLANG_CHECK_LIB([odbc], [],
|
||||
[AC_MSG_ERROR([Erlang application 'odbc' was not found])])
|
||||
AC_ERLANG_CHECK_LIB([compiler], [],
|
||||
[AC_MSG_ERROR([Erlang application 'compiler' was not found])])
|
||||
if test "x$tools" = "xtrue"; then
|
||||
AC_ERLANG_CHECK_LIB([tools], [],
|
||||
[AC_MSG_ERROR([Erlang application 'tools' was not found])])
|
||||
AC_ERLANG_CHECK_LIB([runtime_tools], [],
|
||||
[AC_MSG_ERROR([Erlang application 'runtime_tools' was not found])])
|
||||
fi
|
||||
|
||||
AC_SUBST(hipe)
|
||||
AC_SUBST(roster_gateway_workaround)
|
||||
AC_SUBST(flash_hack)
|
||||
AC_SUBST(transient_supervisors)
|
||||
AC_SUBST(full_xml)
|
||||
AC_SUBST(nif)
|
||||
AC_SUBST(db_type)
|
||||
AC_SUBST(mysql)
|
||||
AC_SUBST(pgsql)
|
||||
AC_SUBST(pam)
|
||||
AC_SUBST(zlib)
|
||||
AC_SUBST(stun)
|
||||
AC_SUBST(riak)
|
||||
AC_SUBST(json)
|
||||
AC_SUBST(iconv)
|
||||
AC_SUBST(debug)
|
||||
AC_SUBST(pubsub_ng)
|
||||
AC_SUBST(http)
|
||||
AC_SUBST(tools)
|
||||
|
||||
AC_OUTPUT
|
||||
@@ -1,5 +0,0 @@
|
||||
% List of ejabberd-modules to add for ejabberd packaging (source archive and installer)
|
||||
%
|
||||
% HTTP-binding:
|
||||
%https://svn.process-one.net/ejabberd-modules/http_bind/trunk
|
||||
%https://svn.process-one.net/ejabberd-modules/mod_http_fileserver/trunk
|
||||
@@ -15,13 +15,13 @@ prepare_dirs ()
|
||||
ERL=`which erl`
|
||||
|
||||
EJA_SRC_DIR=$EJA_DIR/src/
|
||||
EJA_MSGS_DIR=$EJA_SRC_DIR/msgs/
|
||||
EJA_MSGS_DIR=$EJA_DIR/priv/msgs/
|
||||
EXTRACT_DIR=$EJA_DIR/contrib/extract_translations/
|
||||
EXTRACT_ERL=$EXTRACT_DIR/extract_translations.erl
|
||||
EXTRACT_BEAM=$EXTRACT_DIR/extract_translations.beam
|
||||
|
||||
SRC_DIR=$RUN_DIR/src
|
||||
MSGS_DIR=$SRC_DIR/msgs
|
||||
MSGS_DIR=$EJA_DIR/priv/msgs
|
||||
|
||||
if !([[ -n $EJA_DIR ]])
|
||||
then
|
||||
@@ -288,8 +288,8 @@ translation_instructions ()
|
||||
echo " $MSGS_PATH"
|
||||
}
|
||||
|
||||
EJA_DIR=`pwd`/..
|
||||
RUN_DIR=`pwd`/..
|
||||
EJA_DIR=`pwd`
|
||||
RUN_DIR=`pwd`
|
||||
PROJECT=ejabberd
|
||||
|
||||
while [ $# -ne 0 ] ; do
|
||||
|
||||
+15
-2
@@ -21,10 +21,11 @@ release:
|
||||
@echo "\newcommand{\version}{"`sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../src/ejabberd.app`"}" >> version.tex
|
||||
@echo -n "% Contributed modules (automatically generated)." > contributed_modules.tex
|
||||
@echo -e "$(CONTRIBUTED_MODULES)" >> contributed_modules.tex
|
||||
@echo "% mod_admin_p1 commands list."
|
||||
|
||||
html: guide.html dev.html features.html
|
||||
html: guide.html dev.html features.html commercial.html
|
||||
|
||||
pdf: guide.pdf features.pdf
|
||||
pdf: guide.pdf features.pdf commercial.pdf
|
||||
|
||||
clean:
|
||||
rm -f *.aux
|
||||
@@ -60,3 +61,15 @@ guide.pdf: guide.tex
|
||||
|
||||
features.pdf: features.tex
|
||||
pdflatex features.tex
|
||||
|
||||
commercial.html: commercial.tex
|
||||
./mod_admin_p1_commands.sh
|
||||
hevea -fix -pedantic commercial.tex
|
||||
|
||||
commercial.pdf: commercial.tex
|
||||
./mod_admin_p1_commands.sh
|
||||
pdflatex commercial.tex
|
||||
pdflatex commercial.tex
|
||||
pdflatex commercial.tex
|
||||
makeindex commercial.idx
|
||||
pdflatex commercial.tex
|
||||
|
||||
+6184
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1210,7 +1210,7 @@ attacks.
|
||||
</LI><LI CLASS="li-itemize">You may want to allow login access only for certain users. <TT>pam_listfile.so</TT>
|
||||
module provides such functionality.
|
||||
</LI><LI CLASS="li-itemize">If you use <TT>pam_winbind</TT> to authorise against a Windows Active Directory,
|
||||
then <TT>/etc/nssswitch.conf</TT> must be configured to use <TT>winbind</TT> as well.
|
||||
then <TT>/etc/nsswitch.conf</TT> must be configured to use <TT>winbind</TT> as well.
|
||||
</LI></UL><P> <A NAME="accessrules"></A> </P><!--TOC subsection Access Rules-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc25">3.1.5</A>  <A HREF="#accessrules">Access Rules</A></H3><!--SEC END --><P> <A NAME="accessrules"></A>
|
||||
</P><P> <A NAME="ACLDefinition"></A> </P><!--TOC subsubsection ACL Definition-->
|
||||
@@ -4499,7 +4499,7 @@ Alexey Shchepin (<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT
|
||||
</LI><LI CLASS="li-itemize">Vsevolod Pelipas (<A HREF="xmpp:vsevoload@jabber.ru"><TT>xmpp:vsevoload@jabber.ru</TT></A>)
|
||||
</LI></UL><P> <A NAME="copyright"></A> </P><!--TOC chapter Copyright Information-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc103">Appendix D</A>  <A HREF="#copyright">Copyright Information</A></H1><!--SEC END --><P> <A NAME="copyright"></A> </P><P>Ejabberd Installation and Operation Guide.<BR>
|
||||
Copyright © 2003 — 2012 ProcessOne</P><P>This document is free software; you can redistribute it and/or
|
||||
Copyright © 2003 — 2013 ProcessOne</P><P>This document 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.</P><P>This document is distributed in the hope that it will be useful,
|
||||
|
||||
+149
-86
@@ -82,6 +82,7 @@
|
||||
\newcommand{\modmuc}{\module{mod\_muc}}
|
||||
\newcommand{\modmucodbc}{\module{mod\_muc\_odbc}}
|
||||
\newcommand{\modmuclog}{\module{mod\_muc\_log}}
|
||||
\newcommand{\modmulticast}{\module{mod\_multicast}}
|
||||
\newcommand{\modoffline}{\module{mod\_offline}}
|
||||
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
|
||||
\newcommand{\modping}{\module{mod\_ping}}
|
||||
@@ -253,6 +254,8 @@ go to the Windows service settings and set ejabberd to be automatically started.
|
||||
Note that the Windows service is a feature still in development,
|
||||
and for example it doesn't read the file ejabberdctl.cfg.
|
||||
|
||||
The OSX binary installer works on OSX 10.6 and newer.
|
||||
|
||||
On a *nix system, if you want ejabberd to be started as daemon at boot time,
|
||||
copy \term{ejabberd.init} from the 'bin' directory to something like \term{/etc/init.d/ejabberd}
|
||||
(depending on your distribution).
|
||||
@@ -383,10 +386,34 @@ Some options that you may be interested in modifying:
|
||||
\titem{--enable-pam}
|
||||
Enable the PAM authentication method (see section \ref{pam}).
|
||||
|
||||
\titem{--enable-odbc or --enable-mssql}
|
||||
\titem{--enable-mssql}
|
||||
Required if you want to use an external database.
|
||||
See section~\ref{database} for more information.
|
||||
|
||||
\titem{--enable-tools}
|
||||
Enable the use of development tools.
|
||||
|
||||
\titem{--enable-mysql}
|
||||
Enable MySQL support (see section \ref{mysql}).
|
||||
|
||||
\titem{--enable-pgsql}
|
||||
Enable PostgreSQL support (see section \ref{pgsql}).
|
||||
|
||||
\titem{--enable-zlib}
|
||||
Enable Stream Compression (XEP-0138) using zlib.
|
||||
|
||||
\titem{--enable-stun}
|
||||
Enable STUN support (see section \ref{stun}).
|
||||
|
||||
\titem{--enable-riak}
|
||||
Enable Riak support.
|
||||
|
||||
\titem{--enable-iconv}
|
||||
Enable iconv support. This is needed for \term{mod\_irc} (see seciont \ref{modirc}).
|
||||
|
||||
\titem{--enable-debug}
|
||||
Compile with \term{+debug\_info} enabled.
|
||||
|
||||
\titem{--enable-full-xml}
|
||||
Enable the use of XML based optimisations.
|
||||
It will for example use CDATA to escape characters in the XMPP stream.
|
||||
@@ -398,6 +425,9 @@ Some options that you may be interested in modifying:
|
||||
\titem{--enable-nif}
|
||||
Replaces some critical Erlang functions with equivalents written in C to improve performance.
|
||||
This feature requires Erlang/OTP R13B04 or higher.
|
||||
|
||||
\titem{--enable-flash-hack}
|
||||
Enable support for non-standard XML socket clients of Adobe Flash 8 and lower.
|
||||
\end{description}
|
||||
|
||||
\makesubsection{install}{Install}
|
||||
@@ -834,6 +864,9 @@ The available modules, their purpose and the options allowed by each one are:
|
||||
Handles incoming HTTP connections.\\
|
||||
Options: \texttt{captcha}, \texttt{certfile}, \texttt{default\_host}, \texttt{http\_bind}, \texttt{http\_poll},
|
||||
\texttt{request\_handlers}, \texttt{tls}, \texttt{trusted\_proxies}, \texttt{web\_admin}\\
|
||||
\titem{\texttt{ejabberd\_xmlrpc}}
|
||||
Handles incoming XML-RPC requests to execute ejabberd commands (see \ref{eja-commands}).\\
|
||||
Options: \texttt{access\_commands}, \texttt{timeout}\\
|
||||
\end{description}
|
||||
|
||||
|
||||
@@ -843,6 +876,19 @@ This is a detailed description of each option allowed by the listening modules:
|
||||
\begin{description}
|
||||
\titem{\{access, AccessName\}} \ind{options!access}This option defines
|
||||
access to the port. The default value is \term{all}.
|
||||
\titem{\{access\_commands, AccessCommands\}} \ind{options!accesscommands}
|
||||
This option allows to define a list of access restrictions (see \ref{accesscommands}).
|
||||
If this option is present, then XML-RPC calls must include as
|
||||
first argument a struct with a user, server and password of an
|
||||
account in ejabberd that has privileges in Access.
|
||||
If the option is not present, such struct must not be provided.
|
||||
The default value is to not define any restriction: \term{\[\]}
|
||||
When one or several access restrictions are defined and the
|
||||
XML-RPC call provides authentication for an account, each
|
||||
restriction is verified until one matches completely:
|
||||
the account matches the Access rule,
|
||||
the command name is listed in CommandNames,
|
||||
and the provided arguments do not contradict Arguments.
|
||||
\titem{\{backlog, Value\}} \ind{options!backlog}The backlog value
|
||||
defines the maximum length that the queue of pending connections may
|
||||
grow to. This should be increased if the server is going to handle
|
||||
@@ -950,6 +996,9 @@ This is a detailed description of each option allowed by the listening modules:
|
||||
No unencrypted connections will be allowed.
|
||||
You should also set the \option{certfile} option.
|
||||
You can define a certificate file for a specific domain using the global option \option{domain\_certfile}.
|
||||
\titem{\{timeout, Integer\}} \ind{options!timeout}
|
||||
Timeout of the connections, expressed in milliseconds.
|
||||
Default: 5000
|
||||
\titem{tls} \ind{options!tls}\ind{TLS}This option specifies that traffic on
|
||||
the port will be encrypted using SSL immediately after connecting.
|
||||
This was the traditional encryption method in the early Jabber software,
|
||||
@@ -1096,6 +1145,8 @@ In this example, the following configuration defines that:
|
||||
example in section~\ref{webadmin} shows how exactly this can be done.
|
||||
\item All users except for the administrators have a traffic of limit
|
||||
1,000\,Bytes/second
|
||||
\item The XML-RPC service listens in port 4560 and allows only
|
||||
a specific account, to request registrations and unregistrations in a specific host.
|
||||
\item \ind{transports!AIM}The
|
||||
\footahref{http://www.ejabberd.im/pyaimt}{AIM transport}
|
||||
\jid{aim.example.org} is connected to port 5233 on localhost IP addresses
|
||||
@@ -1126,6 +1177,8 @@ In this example, the following configuration defines that:
|
||||
{shaper, normal, {maxrate, 1000}}.
|
||||
{access, c2s_shaper, [{none, admin},
|
||||
{normal, all}]}.
|
||||
{acl, xmlrpc_bot, {user, "xmlrpc-robot", "example.org"}}.
|
||||
{access, xmlrpc_access, [{allow, xmlrpc_bot}]}.
|
||||
{listen,
|
||||
[{5222, ejabberd_c2s, [
|
||||
{access, c2s},
|
||||
@@ -1145,6 +1198,11 @@ In this example, the following configuration defines that:
|
||||
http_poll,
|
||||
web_admin
|
||||
]},
|
||||
{4560, ejabberd_xmlrpc, [
|
||||
{access_commands, [
|
||||
{xmlrpc_access, [register, unregister], [{host, "example.org"}]}
|
||||
]}
|
||||
]},
|
||||
{{5233, {127, 0, 0, 1}}, ejabberd_service, [
|
||||
{hosts, ["aim.example.org"],
|
||||
[{password, "aimsecret"}]}
|
||||
@@ -1223,6 +1281,7 @@ The following authentication methods are supported by \ejabberd{}:
|
||||
\ref{mssql} and \ref{odbc}.
|
||||
\item anonymous --- See section~\ref{saslanonymous}.
|
||||
\item pam --- See section~\ref{pam}.
|
||||
\item riak --- See section~\ref{riak}.
|
||||
\end{itemize}
|
||||
|
||||
Account creation is only supported by internal, external and odbc methods.
|
||||
@@ -1241,7 +1300,7 @@ The option \option{fqdn} allows you to define the Fully Qualified Domain Name
|
||||
of the machine, in case it isn't detected automatically.
|
||||
The FQDN is used to authenticate some clients that use the DIGEST-MD5 SASL mechanism.
|
||||
The option syntax is:
|
||||
\esyntax{\{fqdn, undefined|FqdnString\}.}
|
||||
\esyntax{\{fqdn, undefined|FqdnString|[FqdnString]\}.}
|
||||
|
||||
\makesubsubsection{internalauth}{Internal}
|
||||
\ind{internal authentication}\ind{Mnesia}
|
||||
@@ -1451,7 +1510,7 @@ attacks.
|
||||
\item You may want to allow login access only for certain users. \term{pam\_listfile.so}
|
||||
module provides such functionality.
|
||||
\item If you use \term{pam\_winbind} to authorise against a Windows Active Directory,
|
||||
then \term{/etc/nssswitch.conf} must be configured to use \term{winbind} as well.
|
||||
then \term{/etc/nsswitch.conf} must be configured to use \term{winbind} as well.
|
||||
\end{itemize}
|
||||
|
||||
\makesubsection{accessrules}{Access Rules}
|
||||
@@ -1938,14 +1997,8 @@ For example:
|
||||
\makesubsection{mysql}{MySQL}
|
||||
\ind{MySQL}\ind{MySQL!schema}
|
||||
|
||||
Although this section will describe \ejabberd{}'s configuration when you want to
|
||||
use the native MySQL driver, it does not describe MySQL's installation and
|
||||
database creation. Check the MySQL documentation and the tutorial \footahref{http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver}{Using ejabberd with MySQL native driver} for information regarding these topics.
|
||||
Note that the tutorial contains information about \ejabberd{}'s configuration
|
||||
which is duplicate to this section.
|
||||
|
||||
Moreover, the file mysql.sql in the directory src/odbc might be interesting for
|
||||
you. This file contains the \ejabberd{} schema for MySQL. At the end of the file
|
||||
There is a file \term{mysql.sql} in the directory \term{odbc}.
|
||||
This file contains the \ejabberd{} schema for MySQL. At the end of the file
|
||||
you can find information to update your database schema.
|
||||
|
||||
|
||||
@@ -1953,19 +2006,13 @@ you can find information to update your database schema.
|
||||
\ind{MySQL!Driver Compilation}
|
||||
|
||||
You can skip this step if you installed \ejabberd{} using a binary installer or
|
||||
if the binary packages of \ejabberd{} you are using include support for MySQL.
|
||||
if the binary packages of \ejabberd{} you are using include support for ODBC.
|
||||
|
||||
\begin{enumerate}
|
||||
\item First, install the \footahref{http://support.process-one.net/doc/display/CONTRIBS/Yxa}{Erlang
|
||||
MySQL library}. Make sure the compiled files are in your Erlang path; you can
|
||||
put them for example in the same directory as your \ejabberd{} .beam files.
|
||||
\item Then, configure and install \ejabberd{} with ODBC support enabled (this is
|
||||
also needed for native MySQL support!). This can be done, by using next
|
||||
commands:
|
||||
Use \term{--enable-mysql} configure option in order to build \ejabberd{} with
|
||||
MySQL support:
|
||||
\begin{verbatim}
|
||||
./configure --enable-odbc && make install
|
||||
./configure --enable-mysql && make install
|
||||
\end{verbatim}
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\makesubsubsection{configuremysql}{Database Connection}
|
||||
@@ -2038,16 +2085,9 @@ that you cannot have several variants of the same module loaded!
|
||||
\makesubsection{mssql}{Microsoft SQL Server}
|
||||
\ind{Microsoft SQL Server}\ind{Microsoft SQL Server!schema}
|
||||
|
||||
Although this section will describe \ejabberd{}'s configuration when you want to
|
||||
use Microsoft SQL Server, it does not describe Microsoft SQL Server's
|
||||
installation and database creation. Check the MySQL documentation and the
|
||||
tutorial \footahref{http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver}{Using ejabberd with MySQL native driver} for information regarding these topics.
|
||||
Note that the tutorial contains information about \ejabberd{}'s configuration
|
||||
which is duplicate to this section.
|
||||
|
||||
Moreover, the file mssql.sql in the directory src/odbc might be interesting for
|
||||
you. This file contains the \ejabberd{} schema for Microsoft SQL Server. At the end
|
||||
of the file you can find information to update your database schema.
|
||||
There is a file \term{mssql.sql} in the directory \term{odbc}.
|
||||
This file contains the \ejabberd{} schema for Microsoft SQL Server. At the end of the file
|
||||
you can find information to update your database schema.
|
||||
|
||||
|
||||
\makesubsubsection{compilemssql}{Driver Compilation}
|
||||
@@ -2056,20 +2096,29 @@ of the file you can find information to update your database schema.
|
||||
You can skip this step if you installed \ejabberd{} using a binary installer or
|
||||
if the binary packages of \ejabberd{} you are using include support for ODBC.
|
||||
|
||||
If you want to use Microsoft SQL Server with ODBC, you need to configure,
|
||||
compile and install \ejabberd{} with support for ODBC and Microsoft SQL Server
|
||||
enabled. This can be done, by using next commands:
|
||||
Use \term{--enable-mssql} configure option in order to build \ejabberd{} with
|
||||
Microsoft SQL Server support:
|
||||
\begin{verbatim}
|
||||
./configure --enable-odbc --enable-mssql && make install
|
||||
./configure --enable-mssql && make install
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
\makesubsubsection{configuremssql}{Database Connection}
|
||||
\ind{Microsoft SQL Server!Database Connection}
|
||||
|
||||
The configuration of Database Connection for a Microsoft SQL Server
|
||||
is the same as the configuration for
|
||||
ODBC compatible servers (see section~\ref{configureodbc}).
|
||||
By default \ejabberd{} opens 10 connections to the database for each virtual host.
|
||||
Use this option to modify the value:
|
||||
\begin{verbatim}
|
||||
{odbc_pool_size, 10}.
|
||||
\end{verbatim}
|
||||
|
||||
You can configure an interval to make a dummy SQL request
|
||||
to keep alive the connections to the database.
|
||||
The default value is 'undefined', so no keepalive requests are made.
|
||||
Specify in seconds: for example 28800 means 8 hours.
|
||||
\begin{verbatim}
|
||||
{odbc_keepalive_interval, undefined}.
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
\makesubsubsection{mssqlauth}{Authentication}
|
||||
@@ -2077,8 +2126,7 @@ ODBC compatible servers (see section~\ref{configureodbc}).
|
||||
|
||||
%TODO: not sure if this section is right!!!!!!
|
||||
|
||||
The configuration of Authentication for a Microsoft SQL Server
|
||||
is the same as the configuration for
|
||||
The configuration of Microsoft SQL Server is the same as the configuration of
|
||||
ODBC compatible servers (see section~\ref{odbcauth}).
|
||||
|
||||
\makesubsubsection{mssqlstorage}{Storage}
|
||||
@@ -2096,13 +2144,7 @@ module loaded!
|
||||
\makesubsection{pgsql}{PostgreSQL}
|
||||
\ind{PostgreSQL}\ind{PostgreSQL!schema}
|
||||
|
||||
Although this section will describe \ejabberd{}'s configuration when you want to
|
||||
use the native PostgreSQL driver, it does not describe PostgreSQL's installation
|
||||
and database creation. Check the PostgreSQL documentation and the tutorial \footahref{http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver}{Using ejabberd with MySQL native driver} for information regarding these topics.
|
||||
Note that the tutorial contains information about \ejabberd{}'s configuration
|
||||
which is duplicate to this section.
|
||||
|
||||
Also the file pg.sql in the directory src/odbc might be interesting for you.
|
||||
There is a file \term{pg.sql} in the directory \term{odbc}.
|
||||
This file contains the \ejabberd{} schema for PostgreSQL. At the end of the file
|
||||
you can find information to update your database schema.
|
||||
|
||||
@@ -2114,19 +2156,11 @@ You can skip this step if you installed \ejabberd{} using a binary installer or
|
||||
if the binary packages of \ejabberd{} you are using include support for
|
||||
PostgreSQL.
|
||||
|
||||
\begin{enumerate}
|
||||
\item First, install the Erlang pgsql library from
|
||||
\footahref{http://www.ejabberd.im/ejabberd-modules/}{ejabberd-modules SVN repository}.
|
||||
Make sure the compiled
|
||||
files are in your Erlang path; you can put them for example in the same
|
||||
directory as your \ejabberd{} .beam files.
|
||||
\item Then, configure, compile and install \ejabberd{} with ODBC support enabled
|
||||
(this is also needed for native PostgreSQL support!). This can be done, by
|
||||
using next commands:
|
||||
Use \term{--enable-pgsql} configure option in order to build \ejabberd{} with
|
||||
PostgreSQL support:
|
||||
\begin{verbatim}
|
||||
./configure --enable-odbc && make install
|
||||
./configure --enable-pgsql && make install
|
||||
\end{verbatim}
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\makesubsubsection{configurepgsql}{Database Connection}
|
||||
@@ -2193,31 +2227,6 @@ Keep in mind that you cannot have several variants of the same module loaded!
|
||||
\makesubsection{odbc}{ODBC Compatible}
|
||||
\ind{databases!ODBC}
|
||||
|
||||
Although this section will describe \ejabberd{}'s configuration when you want to
|
||||
use the ODBC driver, it does not describe the installation and database creation
|
||||
of your database. Check the documentation of your database. The tutorial \footahref{http://support.process-one.net/doc/display/MESSENGER/Using+ejabberd+with+MySQL+native+driver}{Using ejabberd with MySQL native driver} also can help you. Note that the tutorial
|
||||
contains information about \ejabberd{}'s configuration which is duplicate to
|
||||
this section.
|
||||
|
||||
|
||||
\makesubsubsection{compileodbc}{Driver Compilation}
|
||||
|
||||
You can skip this step if you installed \ejabberd{} using a binary installer or
|
||||
if the binary packages of \ejabberd{} you are using include support for
|
||||
ODBC.
|
||||
|
||||
\begin{enumerate}
|
||||
\item First, install the \footahref{http://support.process-one.net/doc/display/CONTRIBS/Yxa}{Erlang
|
||||
MySQL library}. Make sure the compiled files are in your Erlang path; you can
|
||||
put them for example in the same directory as your \ejabberd{} .beam files.
|
||||
\item Then, configure, compile and install \ejabberd{} with ODBC support
|
||||
enabled. This can be done, by using next commands:
|
||||
\begin{verbatim}
|
||||
./configure --enable-odbc && make install
|
||||
\end{verbatim}
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\makesubsubsection{configureodbc}{Database Connection}
|
||||
\ind{ODBC!Database Connection}
|
||||
|
||||
@@ -2601,6 +2610,7 @@ The following table lists all modules included in \ejabberd{}.
|
||||
\hline \ahrefloc{modmuc}{\modmuc{}} & Multi-User Chat (\xepref{0045}) & \\
|
||||
\hline \ahrefloc{modmuc}{\modmucodbc{}} & Multi-User Chat (\xepref{0045}) & supported DB (*) \\
|
||||
\hline \ahrefloc{modmuclog}{\modmuclog{}} & Multi-User Chat room logging & \modmuc{} or \modmucodbc{} \\
|
||||
\hline \ahrefloc{modmulticast}{\modmulticast{}} & Multicast service (\xepref{0033}) & \\
|
||||
\hline \ahrefloc{modoffline}{\modoffline{}} & Offline message storage (\xepref{0160}) & \\
|
||||
\hline \ahrefloc{modoffline}{\modofflineodbc{}} & Offline message storage (\xepref{0160}) & supported DB (*) \\
|
||||
\hline \ahrefloc{modping}{\modping{}} & XMPP Ping and periodic keepalives (\xepref{0199}) & \\
|
||||
@@ -3562,6 +3572,56 @@ Examples:
|
||||
\end{verbatim}
|
||||
\end{itemize}
|
||||
|
||||
\makesubsection{modmulticast}{\modmulticast{}}
|
||||
\ind{modules!\modmulticast{}}
|
||||
|
||||
This module implements Extended Stanza Addressing (\xepref{0033}).
|
||||
|
||||
\begin{description}
|
||||
\hostitem{multicast}
|
||||
\titem{\{access, AccessName\}}\ind{options!access}
|
||||
This option specifies the access rule that defines who can send packets to the multicast service.
|
||||
The default value is \term{all}.
|
||||
\titem{\{limits, [\{SenderType, StanzaType, Number\}]\}}\ind{options!limits}
|
||||
Specify a list of custom limits which override the default ones defined
|
||||
in (\xepref{0033}).
|
||||
Where:
|
||||
\begin{itemize}
|
||||
\item SenderType can have values: local or remote.
|
||||
\item StanzaType can have values: message or presence.
|
||||
\item Number can be a positive integer or the key word infinite.
|
||||
\end{itemize}
|
||||
The default value is \term{[]}.
|
||||
\end{description}
|
||||
|
||||
Example configuration:
|
||||
\begin{verbatim}
|
||||
%% Only admins can send packets to multicast service
|
||||
{access, multicast, [{allow, admin}, {deny, all}]}.
|
||||
|
||||
%% If you want to allow all your users:
|
||||
%%{access, multicast, [{allow, all}]}.
|
||||
|
||||
%% This allows both admins and remote users to send packets,
|
||||
%% but does not allow local users
|
||||
%%{acl, allservers, {server_glob, "*"}}.
|
||||
%%{access, multicast, [{allow, admin}, {deny, local}, {allow, allservers}]}.
|
||||
|
||||
{modules, [
|
||||
...
|
||||
{mod_multicast, [
|
||||
%%{host, "multicast.@HOST@"},
|
||||
{access, multicast},
|
||||
{limits, [
|
||||
{local, message, 40},
|
||||
{local, presence, infinite},
|
||||
{remote, message, 150}
|
||||
]}
|
||||
]},
|
||||
...
|
||||
]}.
|
||||
\end{verbatim}
|
||||
|
||||
\makesubsection{modoffline}{\modoffline{}}
|
||||
\ind{modules!\modoffline{}}
|
||||
|
||||
@@ -5125,9 +5185,10 @@ with a defined number and type of calling arguments and type of result
|
||||
that is registered in the \term{ejabberd\_commands} service.
|
||||
Those commands can be defined in any Erlang module and executed using any valid frontend.
|
||||
|
||||
\ejabberd{} includes a frontend to execute \term{ejabberd commands}: the script \term{ejabberdctl}.
|
||||
\ejabberd{} includes two frontends to execute \term{ejabberd commands}:
|
||||
the \term{ejabberdctl} shell script (see \ref{ejabberdctl})
|
||||
and the \term{ejabberd\_xmlrpc} XML-RPC listener (see \ref{listened-module}).
|
||||
Other known frontends that can be installed to execute ejabberd commands in different ways are:
|
||||
\term{ejabberd\_xmlrpc} (XML-RPC service),
|
||||
\term{mod\_rest} (HTTP POST service),
|
||||
\term{mod\_shcommands} (ejabberd WebAdmin page).
|
||||
|
||||
@@ -5185,6 +5246,8 @@ The most interesting ones are:
|
||||
from other Jabber/XMPP servers
|
||||
There exist tutorials to
|
||||
\footahref{http://www.ejabberd.im/migrate-to-ejabberd}{migrate from other software to ejabberd}.
|
||||
\titem{export\_odbc virtualhost filename} \ind{export mnesia data to a SQL file}
|
||||
Export virtual host information from Mnesia tables to a SQL file.
|
||||
\titem{delete\_expired\_messages} This option can be used to delete old messages
|
||||
in offline storage. This might be useful when the number of offline messages
|
||||
is very high.
|
||||
@@ -5917,7 +5980,7 @@ Thanks to all people who contributed to this guide:
|
||||
\makechapter{copyright}{Copyright Information}
|
||||
|
||||
Ejabberd Installation and Operation Guide.\\
|
||||
Copyright \copyright{} 2003 --- 2012 ProcessOne
|
||||
Copyright \copyright{} 2003 --- 2013 ProcessOne
|
||||
|
||||
This document is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
# Managing pubsub nodes through HTTP Atompub #
|
||||
|
||||
|
||||
## Configuration ##
|
||||
|
||||
These options will be used by the service to know how to build URLs. Using the previous configuration items the service should be accessed through `http://notify.push.bbc.co.uk:5280/pshb/<host>/<node>/`.
|
||||
|
||||
Also, in the ejabberd_http handler configuration, add the identified line.
|
||||
|
||||
{5280, ejabberd_http, [
|
||||
http_poll,
|
||||
web_admin,
|
||||
{request_handlers, [{["pshb"], pshb_http}]} % this should be added
|
||||
]}
|
||||
|
||||
It will automatically detect the version of mod_pubsub (odbc or mnesia) and call the appropriate module.
|
||||
|
||||
## Important notice ##
|
||||
|
||||
In the current version of the code, some security checks are not done :
|
||||
|
||||
* node creation uses the default `all` access_createnode acl, not checking for the actual configuration.
|
||||
|
||||
* most read operations are successfully executed without authentication. HOWEVER listing items can only be done when the node access_model is "open". In all other cases, the service returns 403. A finer grained authentication will be implemented.
|
||||
|
||||
|
||||
## Usage example with cURL ##
|
||||
|
||||
### Errors ###
|
||||
|
||||
HTTP status codes are used as intended. Additionally, the XMPP error stanza can also be set in the body :
|
||||
|
||||
$ curl -i -X POST -u cstar@localhost:encore -d @createnode.xml http://localhost:5280/pshb/localhost
|
||||
HTTP/1.1 409 Conflict
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 95
|
||||
Content-type: application/xml
|
||||
|
||||
<error code='409' type='cancel'><conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>
|
||||
|
||||
or
|
||||
|
||||
$ curl -i -X DELETE -u cstar@localhost:encore http://localhost:5280/pshb/localhost/princely_musings
|
||||
HTTP/1.1 404 Not Found
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 101
|
||||
Content-type: application/xml
|
||||
|
||||
<error code='404' type='cancel'><item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>
|
||||
|
||||
### Getting the service document ###
|
||||
|
||||
No authentication necessary. All nodes are listed.
|
||||
|
||||
$ curl -i http://host:port/pshb/domain/
|
||||
|
||||
### Getting items from a node ###
|
||||
|
||||
No authentication done, and all nodes are accessible.
|
||||
|
||||
$ curl -i http://host:port/pshb/domain/node/
|
||||
|
||||
|
||||
### Posting a new item ###
|
||||
|
||||
$ curl -u jid:password -i -X POST -d @entry.atom http://post:port/pshb/domain/node
|
||||
|
||||
User ability to post is based on node configuration.
|
||||
|
||||
### Editing a new item ###
|
||||
|
||||
$ curl -u jid:password -i -X POST -d @entry.atom http://post:port/pshb/domain/node/itemid
|
||||
|
||||
User ability to post is based on node configuration.
|
||||
|
||||
### Deleting an item ###
|
||||
|
||||
$ curl -u jid:password -i -X DELETE http://post:port/pshb/domain/node/itemid
|
||||
|
||||
User ability to post is based on node configuration.
|
||||
|
||||
|
||||
### Creating a new node ###
|
||||
|
||||
An instant node can be created if server configuration allows:
|
||||
|
||||
$ curl -X POST -u cstar@localhost:encore -d "" http://localhost:5280/pshb/localhost
|
||||
|
||||
or
|
||||
|
||||
$ curl -X POST -u cstar@localhost:encore -d "<pubsub><create node='princely_musings'/></pubsub>" http://localhost:5280/pshb/localhost
|
||||
|
||||
configure element (as per XEP-60) can be passed in the pubsub body.
|
||||
|
||||
$ cat createnode.xml
|
||||
<pubsub><create node='princely_musings' type='flat'/>
|
||||
<x xmlns='jabber:x:data' type='submit'>
|
||||
<field var='FORM_TYPE' type='hidden'>
|
||||
<value>http://jabber.org/protocol/pubsub#node_config</value>
|
||||
</field>
|
||||
<field var='pubsub#title'><value>Princely Musings (Atom)</value></field>
|
||||
<field var='pubsub#max_payload_size'><value>1028</value></field>
|
||||
<field var='pubsub#type'><value>Atom</value></field>
|
||||
</x>
|
||||
</pubsub>
|
||||
|
||||
$ curl -i -X POST -u cstar@localhost:encore -d @createnode.xml http://localhost:5280/pshb/localhost
|
||||
HTTP/1.1 200 OK
|
||||
Content-Length: 130
|
||||
Content-Type: application/xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?><pubsub xmlns='http://jabber.org/protocol/pubsub'><create node='princely_musings'/></pubsub>
|
||||
|
||||
### Editing a node configuration ###
|
||||
|
||||
$ cat editnode.xml
|
||||
<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
|
||||
<configure node='princely_musings'>
|
||||
<x xmlns='jabber:x:data' type='submit'>
|
||||
<field var='FORM_TYPE' type='hidden'>
|
||||
<value>http://jabber.org/protocol/pubsub#node_config</value>
|
||||
</field>
|
||||
<field var='pubsub#title'><value>Princely Musings (Atom)</value></field>
|
||||
<field var='pubsub#deliver_notifications'><value>1</value></field>
|
||||
<field var='pubsub#deliver_payloads'><value>1</value></field>
|
||||
<field var='pubsub#persist_items'><value>1</value></field>
|
||||
<field var='pubsub#max_items'><value>10</value></field>
|
||||
<field var='pubsub#item_expire'><value>604800</value></field>
|
||||
<field var='pubsub#access_model'><value>roster</value></field>
|
||||
</x>
|
||||
</configure>
|
||||
</pubsub>
|
||||
|
||||
|
||||
$ curl -i -X PUT -u cstar@localhost:encore -d @createnode.xml http://localhost:5280/pshb/localhost/princely_musings
|
||||
|
||||
|
||||
|
||||
### Deleting a node ###
|
||||
|
||||
A node is deleted by:
|
||||
|
||||
$ curl -X DELETE -u cstar@localhost:encore http://localhost:5280/pshb/localhost/princely_musings
|
||||
|
||||
|
||||
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env escript
|
||||
%% -*- erlang -*-
|
||||
|
||||
-record(cmd, {name, desc, longdesc, args, result}).
|
||||
|
||||
main(_) ->
|
||||
Dir = filename:absname(filename:join(["..", "src"])),
|
||||
FileIn = filename:join([Dir, "mod_admin_p1.erl"]),
|
||||
{ok, Forms1} = epp_dodger:parse_file(FileIn, [no_fail]),
|
||||
Comments = erl_comment_scan:file(FileIn),
|
||||
Forms = erl_recomment:recomment_forms(Forms1, Comments),
|
||||
Tree = erl_syntax:flatten_form_list(Forms),
|
||||
AuxFile = "mod_admin.tex",
|
||||
case file:open(AuxFile, [write]) of
|
||||
{ok, Fd} ->
|
||||
io:format(Fd, "\\newcommand{\\modadminsection}{\\begin{description}~n", []),
|
||||
process(Fd, Tree),
|
||||
io:format(Fd, "\\end{description}}~n", []),
|
||||
file:close(Fd),
|
||||
halt(0);
|
||||
{error, Why} ->
|
||||
io:format("failed to open file ~s: ~s",
|
||||
[AuxFile, file:format_error(Why)]),
|
||||
halt(1)
|
||||
end.
|
||||
|
||||
process(Fd, Tree) ->
|
||||
case erl_syntax:type(Tree) of
|
||||
record_expr ->
|
||||
case erl_syntax_lib:analyze_record_expr(Tree) of
|
||||
{record_expr, {ejabberd_commands, _}} ->
|
||||
Fs = erl_syntax:record_expr_fields(Tree),
|
||||
Cmd = lists:foldl(
|
||||
fun(F, C) ->
|
||||
Name = erl_syntax:record_field_name(F),
|
||||
Value = erl_syntax:record_field_value(F),
|
||||
case {erl_syntax:concrete(Name),
|
||||
catch erl_syntax:concrete(Value)} of
|
||||
{_, {'EXIT', _}} ->
|
||||
C;
|
||||
{name, V} ->
|
||||
C#cmd{name = V};
|
||||
{desc, V} ->
|
||||
C#cmd{desc = V};
|
||||
{longdesc, V} ->
|
||||
C#cmd{longdesc = V};
|
||||
{args, V} ->
|
||||
C#cmd{args = V};
|
||||
{result, V} ->
|
||||
C#cmd{result = V};
|
||||
_ ->
|
||||
C
|
||||
end
|
||||
end, #cmd{}, Fs),
|
||||
format_command(Fd, Cmd);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
case erl_syntax:subtrees(Tree) of
|
||||
[] ->
|
||||
ok;
|
||||
List ->
|
||||
lists:foreach(
|
||||
fun(Group) ->
|
||||
lists:foreach(
|
||||
fun(Subtree) ->
|
||||
process(Fd, Subtree)
|
||||
end, Group)
|
||||
end, List)
|
||||
end
|
||||
end.
|
||||
|
||||
-define(B(S), S).
|
||||
|
||||
format_command(Fd, #cmd{name = Cmd,
|
||||
desc = Desc,
|
||||
longdesc = _LongDesc,
|
||||
args = ArgsDef,
|
||||
result = _ResultDef}) ->
|
||||
io:format(Fd, "\\titem{~s ~s} ~s~n",
|
||||
[escape_underscores(atom_to_list(Cmd)),
|
||||
flatten_arguments(ArgsDef),
|
||||
escape_underscores(Desc)]).
|
||||
|
||||
flatten_arguments(Args) ->
|
||||
string:join(
|
||||
lists:map(
|
||||
fun({Name, _Type}) ->
|
||||
escape_underscores(io_lib:format("~s", [Name]))
|
||||
end, Args),
|
||||
" ").
|
||||
|
||||
escape_underscores(S) ->
|
||||
lists:flatten(
|
||||
[case C of
|
||||
$_ -> "\\_";
|
||||
_ -> C
|
||||
end || C <- S]).
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
% ejabberd version (automatically generated).
|
||||
\newcommand{\version}{2.1.11}
|
||||
\newcommand{\version}{3.0.0}
|
||||
|
||||
@@ -154,6 +154,13 @@
|
||||
%%
|
||||
%%{{3478, udp}, ejabberd_stun, []},
|
||||
|
||||
%%
|
||||
%% To handle XML-RPC requests that provide admin credentials:
|
||||
%%
|
||||
%%{4560, ejabberd_xmlrpc, [
|
||||
%% {access_commands, [{admin, all, []}]}
|
||||
%% ]},
|
||||
|
||||
{5280, ejabberd_http, [
|
||||
%%{request_handlers,
|
||||
%% [
|
||||
Regular → Executable
+120
-81
@@ -7,12 +7,14 @@ ERL_MAX_PORTS=32000
|
||||
ERL_PROCESSES=250000
|
||||
ERL_MAX_ETS_TABLES=1400
|
||||
|
||||
SCRIPT_DIR=$(cd ${0%/*} && pwd)
|
||||
|
||||
# define default environment variables
|
||||
NODE=ejabberd
|
||||
HOST=localhost
|
||||
ERLANG_NODE=$NODE@$HOST
|
||||
ERL=@erl@
|
||||
INSTALLUSER=@installuser@
|
||||
ERL={{erl}}
|
||||
INSTALLUSER={{installuser}}
|
||||
|
||||
# parse command line parameters
|
||||
ARGS=
|
||||
@@ -33,7 +35,7 @@ done
|
||||
|
||||
# Define ejabberd variable if they have not been defined from the command line
|
||||
if [ "$ETCDIR" = "" ] ; then
|
||||
ETCDIR=@SYSCONFDIR@/ejabberd
|
||||
ETCDIR={{sysconfdir}}/ejabberd
|
||||
fi
|
||||
if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then
|
||||
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.cfg
|
||||
@@ -41,18 +43,21 @@ fi
|
||||
if [ "$EJABBERDCTL_CONFIG_PATH" = "" ] ; then
|
||||
EJABBERDCTL_CONFIG_PATH=$ETCDIR/ejabberdctl.cfg
|
||||
fi
|
||||
[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH"
|
||||
if [ -f "$EJABBERDCTL_CONFIG_PATH" ] ; then
|
||||
. "$EJABBERDCTL_CONFIG_PATH"
|
||||
fi
|
||||
if [ "$LOGS_DIR" = "" ] ; then
|
||||
LOGS_DIR=@LOCALSTATEDIR@/log/ejabberd
|
||||
LOGS_DIR={{localstatedir}}/log/ejabberd
|
||||
fi
|
||||
if [ "$SPOOLDIR" = "" ] ; then
|
||||
SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd
|
||||
SPOOLDIR={{localstatedir}}/lib/ejabberd
|
||||
fi
|
||||
if [ "$EJABBERD_DOC_PATH" = "" ] ; then
|
||||
EJABBERD_DOC_PATH=@DOCDIR@
|
||||
EJABBERD_DOC_PATH={{docdir}}
|
||||
fi
|
||||
if [ "$ERLANG_NODE_ARG" != "" ] ; then
|
||||
ERLANG_NODE=$ERLANG_NODE_ARG
|
||||
NODE=${ERLANG_NODE%@*}
|
||||
fi
|
||||
|
||||
# check the proper system user is used
|
||||
@@ -62,19 +67,21 @@ EJID=`id -g $INSTALLUSER`
|
||||
EXEC_CMD="false"
|
||||
for GID in $GIDS; do
|
||||
if [ $GID -eq 0 ] ; then
|
||||
EXEC_CMD="su ${INSTALLUSER} -p -c"
|
||||
EXEC_CMD="su ${INSTALLUSER} -p -c"
|
||||
fi
|
||||
done
|
||||
if [ "$ID" -eq "$EJID" ] ; then
|
||||
EXEC_CMD="sh -c"
|
||||
EXEC_CMD="sh -c"
|
||||
fi
|
||||
if [ "$EXEC_CMD" = "false" ] ; then
|
||||
echo "This command can only be run by root or the user $INSTALLUSER" >&2
|
||||
exit 4
|
||||
echo "This command can only be run by root or the user $INSTALLUSER" >&2
|
||||
exit 4
|
||||
fi
|
||||
|
||||
NAME=-name
|
||||
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
|
||||
if [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] ; then
|
||||
NAME=-sname
|
||||
fi
|
||||
|
||||
KERNEL_OPTS=""
|
||||
if [ "$FIREWALL_WINDOW" != "" ] ; then
|
||||
@@ -87,23 +94,25 @@ fi
|
||||
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
|
||||
|
||||
# define additional environment variables
|
||||
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
|
||||
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
|
||||
@@ -143,6 +152,7 @@ export EXEC_CMD
|
||||
# start server
|
||||
start ()
|
||||
{
|
||||
check_start
|
||||
$EXEC_CMD "$ERL \
|
||||
$NAME $ERLANG_NODE \
|
||||
-noinput -detached \
|
||||
@@ -174,7 +184,7 @@ debug ()
|
||||
echo " EJABBERD_BYPASS_WARNINGS=true"
|
||||
echo "Press any key to continue"
|
||||
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
|
||||
read foo
|
||||
read foo
|
||||
fi
|
||||
echo ""
|
||||
TTY=`tty | sed -e 's/.*\///g'`
|
||||
@@ -189,6 +199,7 @@ debug ()
|
||||
# start interactive server
|
||||
live ()
|
||||
{
|
||||
check_start
|
||||
echo "--------------------------------------------------------------------"
|
||||
echo ""
|
||||
echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode."
|
||||
@@ -205,7 +216,7 @@ live ()
|
||||
echo " EJABBERD_BYPASS_WARNINGS=true"
|
||||
echo "Press any key to continue"
|
||||
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
|
||||
read foo
|
||||
read foo
|
||||
fi
|
||||
echo ""
|
||||
$EXEC_CMD "$ERL \
|
||||
@@ -217,6 +228,13 @@ live ()
|
||||
$ERLANG_OPTS $ARGS \"$@\""
|
||||
}
|
||||
|
||||
etop()
|
||||
{
|
||||
$EXEC_CMD "$ERL \
|
||||
$NAME debug-${TTY}-${ERLANG_NODE} \
|
||||
-hidden -s etop -s erlang halt -output text -node $ERLANG_NODE"
|
||||
}
|
||||
|
||||
help ()
|
||||
{
|
||||
echo ""
|
||||
@@ -244,69 +262,69 @@ ctl ()
|
||||
# using flock if available. Expects a linux-style
|
||||
# flock that can lock a file descriptor.
|
||||
MAXCONNID=100
|
||||
CONNLOCKDIR=@LOCALSTATEDIR@/lock/ejabberdctl
|
||||
CONNLOCKDIR={{localstatedir}}/lock/ejabberdctl
|
||||
FLOCK='/usr/bin/flock'
|
||||
if [ ! -x "$FLOCK" ] || [ ! -d "$CONNLOCKDIR" ] ; then
|
||||
JOT='/usr/bin/jot'
|
||||
if [ ! -x "$JOT" ] ; then
|
||||
# no flock or jot, simply invoke ctlexec()
|
||||
CTL_CONN="ctl-${ERLANG_NODE}"
|
||||
ctlexec $CTL_CONN $COMMAND
|
||||
result=$?
|
||||
else
|
||||
# no flock, but at least there is jot
|
||||
RAND=`jot -r 1 0 $MAXCONNID`
|
||||
CTL_CONN="ctl-${RAND}-${ERLANG_NODE}"
|
||||
ctlexec $CTL_CONN $COMMAND
|
||||
result=$?
|
||||
fi
|
||||
JOT='/usr/bin/jot'
|
||||
if [ ! -x "$JOT" ] ; then
|
||||
# no flock or jot, simply invoke ctlexec()
|
||||
CTL_CONN="ctl-${ERLANG_NODE}"
|
||||
ctlexec $CTL_CONN $COMMAND
|
||||
result=$?
|
||||
else
|
||||
# no flock, but at least there is jot
|
||||
RAND=`jot -r 1 0 $MAXCONNID`
|
||||
CTL_CONN="ctl-${RAND}-${ERLANG_NODE}"
|
||||
ctlexec $CTL_CONN $COMMAND
|
||||
result=$?
|
||||
fi
|
||||
else
|
||||
# we have flock so we get a lock
|
||||
# on one of a limited number of
|
||||
# conn names -- this allows
|
||||
# concurrent invocations using a bound
|
||||
# number of atoms
|
||||
for N in $(seq 1 $MAXCONNID); do
|
||||
CTL_CONN="ejabberdctl-$N"
|
||||
CTL_LOCKFILE="$CONNLOCKDIR/$CTL_CONN"
|
||||
(
|
||||
exec 8>"$CTL_LOCKFILE"
|
||||
if flock --nb 8; then
|
||||
ctlexec $CTL_CONN $COMMAND
|
||||
# we have flock so we get a lock
|
||||
# on one of a limited number of
|
||||
# conn names -- this allows
|
||||
# concurrent invocations using a bound
|
||||
# number of atoms
|
||||
for N in $(seq 1 $MAXCONNID); do
|
||||
CTL_CONN="ejabberdctl-$N"
|
||||
CTL_LOCKFILE="$CONNLOCKDIR/$CTL_CONN"
|
||||
(
|
||||
exec 8>"$CTL_LOCKFILE"
|
||||
if flock --nb 8; then
|
||||
ctlexec $CTL_CONN $COMMAND
|
||||
ssresult=$?
|
||||
# segregate from possible flock exit(1)
|
||||
ssresult=$(expr $ssresult \* 10)
|
||||
exit $ssresult
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
ssresult=$(expr $ssresult \* 10)
|
||||
exit $ssresult
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
)
|
||||
result=$?
|
||||
if [ $result -eq 1 ]; then
|
||||
result=$?
|
||||
if [ $result -eq 1 ] ; then
|
||||
# means we errored out in flock
|
||||
# rather than in the exec - stay in the loop
|
||||
# trying other conn names...
|
||||
badlock=1
|
||||
else
|
||||
badlock=""
|
||||
break;
|
||||
fi
|
||||
done
|
||||
result=$(expr $result / 10)
|
||||
badlock=1
|
||||
else
|
||||
badlock=""
|
||||
break;
|
||||
fi
|
||||
done
|
||||
result=$(expr $result / 10)
|
||||
fi
|
||||
|
||||
if [ "$badlock" ];then
|
||||
echo "Ran out of connections to try. Your ejabberd processes" >&2
|
||||
echo "may be stuck or this is a very busy server. For very" >&2
|
||||
echo "busy servers, consider raising MAXCONNID in ejabberdctl">&2
|
||||
exit 1;
|
||||
if [ "$badlock" ] ;then
|
||||
echo "Ran out of connections to try. Your ejabberd processes" >&2
|
||||
echo "may be stuck or this is a very busy server. For very" >&2
|
||||
echo "busy servers, consider raising MAXCONNID in ejabberdctl">&2
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
case $result in
|
||||
0) :;;
|
||||
1) :;;
|
||||
2) help;;
|
||||
3) help;;
|
||||
0) :;;
|
||||
1) :;;
|
||||
2) help;;
|
||||
3) help;;
|
||||
esac
|
||||
return $result
|
||||
}
|
||||
@@ -337,6 +355,26 @@ stop_epmd()
|
||||
epmd -names | grep -q name || epmd -kill
|
||||
}
|
||||
|
||||
# make sure node not already running and node name unregistered
|
||||
check_start()
|
||||
{
|
||||
epmd -names | grep -q $NODE && {
|
||||
ps ux | grep -v grep | grep -q $ERLANG_NODE && {
|
||||
echo "ERROR: The ejabberd node '$ERLANG_NODE' is already running."
|
||||
exit 4
|
||||
} || {
|
||||
ps ux | grep beam | grep -v "grep beam" && {
|
||||
echo "ERROR: The ejabberd node '$ERLANG_NODE' is registered,"
|
||||
echo " but no ejabberd process has been found."
|
||||
echo "Shutdown other erlang nodes, and call 'epmd -kill'."
|
||||
exit 5
|
||||
} || {
|
||||
epmd -kill
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# allow sync calls
|
||||
wait_for_status()
|
||||
{
|
||||
@@ -344,7 +382,7 @@ wait_for_status()
|
||||
# return: 0 OK, 1 KO
|
||||
timeout=$2
|
||||
status=4
|
||||
while [ $status -ne $1 ]; do
|
||||
while [ $status -ne $1 ] ; do
|
||||
sleep $3
|
||||
timeout=$(($timeout - 1))
|
||||
[ $timeout -eq 0 ] && {
|
||||
@@ -366,6 +404,7 @@ case $ARGS in
|
||||
' start') start;;
|
||||
' debug') debug;;
|
||||
' live') live;;
|
||||
' etop') etop;;
|
||||
' started') wait_for_status 0 30 2;; # wait 30x2s before timeout
|
||||
' stopped') wait_for_status 3 15 2; stop_epmd;; # wait 15x2s before timeout
|
||||
*) ctl $ARGS;;
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -19,18 +19,27 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(adhoc_request, {lang,
|
||||
node,
|
||||
sessionid,
|
||||
action,
|
||||
xdata,
|
||||
others}).
|
||||
-record(adhoc_request,
|
||||
{
|
||||
lang = <<"">> :: binary(),
|
||||
node = <<"">> :: binary(),
|
||||
sessionid = <<"">> :: binary(),
|
||||
action = <<"">> :: binary(),
|
||||
xdata = false :: false | xmlel(),
|
||||
others = [] :: [xmlel()]
|
||||
}).
|
||||
|
||||
-record(adhoc_response, {lang,
|
||||
node,
|
||||
sessionid,
|
||||
status,
|
||||
defaultaction = "",
|
||||
actions = [],
|
||||
notes = [],
|
||||
elements = []}).
|
||||
-record(adhoc_response,
|
||||
{
|
||||
lang = <<"">> :: binary(),
|
||||
node = <<"">> :: binary(),
|
||||
sessionid = <<"">> :: binary(),
|
||||
status :: atom(),
|
||||
defaultaction = <<"">> :: binary(),
|
||||
actions = [] :: [binary()],
|
||||
notes = [] :: [{binary(), binary()}],
|
||||
elements = [] :: [xmlel()]
|
||||
}).
|
||||
|
||||
-type adhoc_request() :: #adhoc_request{}.
|
||||
-type adhoc_response() :: #adhoc_response{}.
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -19,15 +19,34 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(roster, {usj,
|
||||
us,
|
||||
jid,
|
||||
name = "",
|
||||
subscription = none,
|
||||
ask = none,
|
||||
groups = [],
|
||||
askmessage = [],
|
||||
xs = []}).
|
||||
-define(CT_XML,
|
||||
{<<"Content-Type">>, <<"text/xml; charset=utf-8">>}).
|
||||
|
||||
-record(roster_version, {us,
|
||||
version}).
|
||||
-define(CT_PLAIN,
|
||||
{<<"Content-Type">>, <<"text/plain">>}).
|
||||
|
||||
-define(CT_JSON,
|
||||
{<<"Content-Type">>, <<"application/json">>}).
|
||||
|
||||
-define(AC_ALLOW_ORIGIN,
|
||||
{<<"Access-Control-Allow-Origin">>, <<"*">>}).
|
||||
|
||||
-define(AC_ALLOW_METHODS,
|
||||
{<<"Access-Control-Allow-Methods">>,
|
||||
<<"GET, POST, OPTIONS">>}).
|
||||
|
||||
-define(AC_ALLOW_HEADERS,
|
||||
{<<"Access-Control-Allow-Headers">>,
|
||||
<<"Content-Type">>}).
|
||||
|
||||
-define(AC_MAX_AGE,
|
||||
{<<"Access-Control-Max-Age">>, <<"86400">>}).
|
||||
|
||||
-define(OPTIONS_HEADER,
|
||||
[?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS,
|
||||
?AC_ALLOW_HEADERS, ?AC_MAX_AGE]).
|
||||
|
||||
-define(HEADER(CType),
|
||||
[CType, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]).
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_bosh).
|
||||
@@ -0,0 +1,61 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(VERSION, ejabberd_config:get_version()).
|
||||
|
||||
-define(MYHOSTS, ejabberd_config:get_myhosts()).
|
||||
|
||||
-define(MYNAME, hd(ejabberd_config:get_myhosts())).
|
||||
|
||||
-define(MYLANG, ejabberd_config:get_mylang()).
|
||||
|
||||
-define(MSGS_DIR, filename:join(["priv", "msgs"])).
|
||||
|
||||
-define(CONFIG_PATH, <<"ejabberd.cfg">>).
|
||||
|
||||
-define(LOG_PATH, <<"ejabberd.log">>).
|
||||
|
||||
-ifdef(ENABLE_FLASH_HACK).
|
||||
|
||||
-define(FLASH_HACK, true).
|
||||
|
||||
-else.
|
||||
|
||||
-define(FLASH_HACK, false).
|
||||
|
||||
-endif.
|
||||
|
||||
-define(EJABBERD_URI,
|
||||
<<"http://www.process-one.net/en/ejabberd/">>).
|
||||
|
||||
-define(S2STIMEOUT, 600000).
|
||||
|
||||
%%-define(DBGFSM, true).
|
||||
|
||||
-record(scram,
|
||||
{storedkey = <<"">>,
|
||||
serverkey = <<"">>,
|
||||
salt = <<"">>,
|
||||
iterationcount = 0 :: integer()}).
|
||||
|
||||
-type scram() :: #scram{}.
|
||||
|
||||
-define(SCRAM_DEFAULT_ITERATION_COUNT, 4096).
|
||||
@@ -0,0 +1,93 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-ifndef(mod_privacy_hrl).
|
||||
|
||||
-include("mod_privacy.hrl").
|
||||
|
||||
-endif.
|
||||
|
||||
%-define(SETS, gb_sets).
|
||||
-define(SETS, ejabberd_sets).
|
||||
|
||||
-define(DICT, dict).
|
||||
|
||||
-record(state,
|
||||
{socket,
|
||||
sockmod = ejabberd_socket :: ejabberd_socket | ejabberd_frontend_socket,
|
||||
socket_monitor = make_ref() :: reference(),
|
||||
xml_socket = false :: boolean(),
|
||||
streamid = <<"">> :: binary(),
|
||||
sasl_state :: any(),
|
||||
access :: atom(),
|
||||
shaper = none :: shaper:shaper(),
|
||||
zlib = false :: boolean(),
|
||||
tls = false :: boolean(),
|
||||
tls_required = false :: boolean(),
|
||||
tls_enabled = false :: boolean(),
|
||||
tls_options = [] :: list(),
|
||||
authenticated = false :: boolean() | replaced | rebinded,
|
||||
jid = #jid{} :: jid(),
|
||||
user = <<"">> :: binary(),
|
||||
server = ?MYNAME :: binary(),
|
||||
resource = <<"">> :: binary(),
|
||||
sid = {now(), self()} :: ejabberd_sm:sid(),
|
||||
pres_t = (?SETS):new() :: ?SETS:ej_set() | {pres_t, non_neg_integer()},
|
||||
pres_f = (?SETS):new() :: ?SETS:ej_set() | {pres_f, non_neg_integer()},
|
||||
pres_a = (?SETS):new() :: ?SETS:ej_set() | {pres_a, non_neg_integer()},
|
||||
pres_last :: xmlel(),
|
||||
pres_timestamp :: calendar:datetime(),
|
||||
privacy_list = #userlist{} :: userlist(),
|
||||
conn = unknown :: atom(),
|
||||
auth_module = unknown :: atom(),
|
||||
ip :: {inet:ip_address(), inet:port_number()},
|
||||
redirect = false :: boolean(),
|
||||
aux_fields = [] :: [{atom(), any()}],
|
||||
fsm_limit_opts = [] :: [{atom(), any()}],
|
||||
lang = ?MYLANG :: binary(),
|
||||
debug = false :: boolean(),
|
||||
flash_hack = false :: boolean(),
|
||||
flash_connection = false :: boolean(),
|
||||
reception = true :: boolean(),
|
||||
standby = false :: boolean(),
|
||||
queue = queue:new() :: queue(),
|
||||
queue_len = 0 :: integer(),
|
||||
pres_queue = gb_trees:empty() :: gb_tree(),
|
||||
keepalive_timer :: reference(),
|
||||
keepalive_timeout :: timeout(),
|
||||
oor_timeout :: timeout(),
|
||||
oor_status = <<"">> :: binary(),
|
||||
oor_show = <<"">> :: binary(),
|
||||
oor_notification :: xmlel(),
|
||||
oor_send_body = all :: first_per_user | first | all | none,
|
||||
oor_send_groupchat = false :: boolean(),
|
||||
oor_send_from = jid :: jid | username | name | none,
|
||||
oor_appid = <<"">> :: binary(),
|
||||
oor_unread = 0 :: integer(),
|
||||
oor_unread_users = (?SETS):new() :: ?SETS:ej_set(),
|
||||
oor_unread_client = 0 :: integer(),
|
||||
oor_offline = false :: boolean(),
|
||||
ack_enabled = false :: boolean(),
|
||||
ack_counter = 0 :: integer(),
|
||||
ack_queue = queue:new() :: queue(),
|
||||
ack_timer :: reference()}).
|
||||
|
||||
-type c2s_state() :: #state{}.
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -19,10 +19,40 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(ejabberd_commands, {name, tags = [],
|
||||
desc = "", longdesc = "",
|
||||
module, function,
|
||||
args = [], result = rescode}).
|
||||
-type aterm() :: {atom(), atype()}.
|
||||
-type atype() :: integer | string | binary |
|
||||
{tuple, [aterm()]} | {list, aterm()}.
|
||||
-type rterm() :: {atom(), rtype()}.
|
||||
-type rtype() :: integer | string | atom |
|
||||
{tuple, [rterm()]} | {list, rterm()} |
|
||||
rescode | restuple.
|
||||
|
||||
-record(ejabberd_commands,
|
||||
{name :: atom(),
|
||||
tags = [] :: [atom()] | '_' | '$2',
|
||||
desc = "" :: string() | '_' | '$3',
|
||||
longdesc = "" :: string() | '_',
|
||||
module :: atom(),
|
||||
function :: atom(),
|
||||
args = [] :: [aterm()] | '_' | '$1' | '$2',
|
||||
result = {res, rescode} :: rterm() | '_' | '$2',
|
||||
args_desc = none :: none | [string()],
|
||||
result_desc = none :: none | string(),
|
||||
args_example = none :: [any()],
|
||||
result_example = none :: any()}).
|
||||
|
||||
-type ejabberd_commands() :: #ejabberd_commands{name :: atom(),
|
||||
tags :: [atom()],
|
||||
desc :: string(),
|
||||
longdesc :: string(),
|
||||
module :: atom(),
|
||||
function :: atom(),
|
||||
args :: [aterm()],
|
||||
result :: rterm(),
|
||||
args_desc :: none | [string()],
|
||||
result_desc :: none | string(),
|
||||
args_example :: [any()],
|
||||
result_example :: any()}.
|
||||
|
||||
%% @type ejabberd_commands() = #ejabberd_commands{
|
||||
%% name = atom(),
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -19,21 +19,16 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(LDAP_PORT, 389).
|
||||
-define(LDAPS_PORT, 636).
|
||||
-record(config, {key :: any(), value :: any()}).
|
||||
|
||||
-record(eldap_search, {scope = wholeSubtree,
|
||||
base = [],
|
||||
filter,
|
||||
limit = 0,
|
||||
attributes = [],
|
||||
types_only = false,
|
||||
deref_aliases = neverDerefAliases,
|
||||
timeout = 0}).
|
||||
-record(local_config, {key :: any(), value :: any()}).
|
||||
|
||||
-type config() :: #config{}.
|
||||
-type local_config() :: #local_config{}.
|
||||
|
||||
-record(eldap_search_result, {entries,
|
||||
referrals}).
|
||||
|
||||
-record(eldap_entry, {object_name,
|
||||
attributes}).
|
||||
-record(state,
|
||||
{opts = [] :: [acl:acl() | config() | local_config()],
|
||||
hosts = [] :: [binary()],
|
||||
override_local = false :: boolean(),
|
||||
override_global = false :: boolean(),
|
||||
override_acls = false :: boolean()}).
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -20,6 +20,9 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(STATUS_SUCCESS, 0).
|
||||
-define(STATUS_ERROR, 1).
|
||||
-define(STATUS_USAGE, 2).
|
||||
-define(STATUS_BADRPC, 3).
|
||||
|
||||
-define(STATUS_ERROR, 1).
|
||||
|
||||
-define(STATUS_USAGE, 2).
|
||||
|
||||
-define(STATUS_BADRPC, 3).
|
||||
@@ -0,0 +1,55 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(request,
|
||||
{method :: method(),
|
||||
path = [] :: [binary()],
|
||||
q = [] :: [{binary() | nokey, binary()}],
|
||||
us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
auth :: {binary(), binary()} |
|
||||
{auth_jid, {binary(), binary()}, jlib:jid()},
|
||||
lang = <<"">> :: binary(),
|
||||
data = <<"">> :: binary(),
|
||||
ip :: {inet:ip_address(), inet:port_number()},
|
||||
host = <<"">> :: binary(),
|
||||
port = 5280 :: inet:port_number(),
|
||||
tp = http :: protocol(),
|
||||
headers = [] :: [{atom() | binary(), binary()}]}).
|
||||
|
||||
-record(ws,
|
||||
{socket :: inet:socket() | tls:tls_socket(),
|
||||
sockmod = gen_tcp :: gen_tcp | tls,
|
||||
ws_autoexit = false :: boolean(),
|
||||
ip :: {inet:ip_address(), inet:port_number()},
|
||||
vsn :: vsn(),
|
||||
origin = <<"">> :: binary(),
|
||||
host = <<"">> :: binary(),
|
||||
port = 5280 :: inet:port_number(),
|
||||
path = [] :: [binary()],
|
||||
headers = [] :: [{atom() | binary(), binary()}],
|
||||
local_path = [] :: [binary()],
|
||||
q = [] :: [{binary() | nokey, binary()}],
|
||||
buf :: binary()}).
|
||||
|
||||
-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE'.
|
||||
-type protocol() :: http | https.
|
||||
-type http_request() :: #request{}.
|
||||
-type vsn() :: {'draft-hybi' | 'draft-hixie', non_neg_integer()}.
|
||||
@@ -0,0 +1,102 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(X(Name),
|
||||
#xmlel{name = Name, attrs = [], children = []}).
|
||||
|
||||
-define(XA(Name, Attrs),
|
||||
#xmlel{name = Name, attrs = Attrs, children = []}).
|
||||
|
||||
-define(XE(Name, Els),
|
||||
#xmlel{name = Name, attrs = [], children = Els}).
|
||||
|
||||
-define(XAE(Name, Attrs, Els),
|
||||
#xmlel{name = Name, attrs = Attrs, children = Els}).
|
||||
|
||||
-define(C(Text), {xmlcdata, Text}).
|
||||
|
||||
-define(XC(Name, Text), ?XE(Name, [?C(Text)])).
|
||||
|
||||
-define(XAC(Name, Attrs, Text),
|
||||
?XAE(Name, Attrs, [?C(Text)])).
|
||||
|
||||
-define(T(Text), translate:translate(Lang, Text)).
|
||||
|
||||
-define(CT(Text), ?C((?T(Text)))).
|
||||
|
||||
-define(XCT(Name, Text), ?XC(Name, (?T(Text)))).
|
||||
|
||||
-define(XACT(Name, Attrs, Text),
|
||||
?XAC(Name, Attrs, (?T(Text)))).
|
||||
|
||||
-define(LI(Els), ?XE(<<"li">>, Els)).
|
||||
|
||||
-define(A(URL, Els),
|
||||
?XAE(<<"a">>, [{<<"href">>, URL}], Els)).
|
||||
|
||||
-define(AC(URL, Text), ?A(URL, [?C(Text)])).
|
||||
|
||||
-define(ACT(URL, Text), ?AC(URL, (?T(Text)))).
|
||||
|
||||
-define(P, ?X(<<"p">>)).
|
||||
|
||||
-define(BR, ?X(<<"br">>)).
|
||||
|
||||
-define(INPUT(Type, Name, Value),
|
||||
?XA(<<"input">>,
|
||||
[{<<"type">>, Type}, {<<"name">>, Name},
|
||||
{<<"value">>, Value}])).
|
||||
|
||||
-define(INPUTT(Type, Name, Value),
|
||||
?INPUT(Type, Name, (?T(Value)))).
|
||||
|
||||
-define(INPUTS(Type, Name, Value, Size),
|
||||
?XA(<<"input">>,
|
||||
[{<<"type">>, Type}, {<<"name">>, Name},
|
||||
{<<"value">>, Value}, {<<"size">>, Size}])).
|
||||
|
||||
-define(INPUTST(Type, Name, Value, Size),
|
||||
?INPUT(Type, Name, (?T(Value)), Size)).
|
||||
|
||||
-define(ACLINPUT(Text),
|
||||
?XE(<<"td">>,
|
||||
[?INPUT(<<"text">>, <<"value", ID/binary>>, Text)])).
|
||||
|
||||
-define(TEXTAREA(Name, Rows, Cols, Value),
|
||||
?XAC(<<"textarea">>,
|
||||
[{<<"name">>, Name}, {<<"rows">>, Rows},
|
||||
{<<"cols">>, Cols}],
|
||||
Value)).
|
||||
|
||||
-define(XRES(Text),
|
||||
?XAC(<<"p">>, [{<<"class">>, <<"result">>}], Text)).
|
||||
|
||||
-define(XREST(Text), ?XRES((?T(Text)))).
|
||||
|
||||
-define(GL(Ref, Title),
|
||||
?XAE(<<"div">>, [{<<"class">>, <<"guidelink">>}],
|
||||
[?XAE(<<"a">>,
|
||||
[{<<"href">>, <<"/admin/doc/guide.html#", Ref/binary>>},
|
||||
{<<"target">>, <<"_blank">>}],
|
||||
[?C(<<"[Guide: ", Title/binary, "]">>)])])).
|
||||
|
||||
-define(H1GL(Name, Ref, Title),
|
||||
[?XC(<<"h1">>, Name), ?GL(Ref, Title)]).
|
||||
@@ -0,0 +1,65 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(LDAP_PORT, 389).
|
||||
|
||||
-define(LDAPS_PORT, 636).
|
||||
|
||||
-type scope() :: baseObject | singleLevel | wholeSubtree.
|
||||
|
||||
-record(eldap_search,
|
||||
{scope = wholeSubtree :: scope(),
|
||||
base = <<"">> :: binary(),
|
||||
filter :: eldap:filter(),
|
||||
limit = 0 :: non_neg_integer(),
|
||||
attributes = [] :: [binary()],
|
||||
types_only = false :: boolean(),
|
||||
deref_aliases = neverDerefAliases :: neverDerefAliases |
|
||||
derefInSearching |
|
||||
derefFindingBaseObj |
|
||||
derefAlways,
|
||||
timeout = 0 :: non_neg_integer()}).
|
||||
|
||||
-record(eldap_search_result, {entries = [] :: [eldap_entry()],
|
||||
referrals = [] :: list()}).
|
||||
|
||||
-record(eldap_entry, {object_name = <<>> :: binary(),
|
||||
attributes = [] :: [{binary(), [binary()]}]}).
|
||||
|
||||
-type tlsopts() :: [{encrypt, tls | starttls | none} |
|
||||
{tls_cacertfile, binary() | undefined} |
|
||||
{tls_certfile, binary() | undefined} |
|
||||
{tls_depth, non_neg_integer() | undefined} |
|
||||
{tls_verify, hard | soft | false}].
|
||||
|
||||
-record(eldap_config, {servers = [] :: [binary()],
|
||||
backups = [] :: [binary()],
|
||||
tls_options = [] :: tlsopts(),
|
||||
port = ?LDAP_PORT :: inet:port_number(),
|
||||
dn = <<"">> :: binary(),
|
||||
password = <<"">> :: binary(),
|
||||
base = <<"">> :: binary(),
|
||||
deref_aliases = never :: never | searching |
|
||||
finding | always}).
|
||||
|
||||
-type eldap_config() :: #eldap_config{}.
|
||||
-type eldap_search() :: #eldap_search{}.
|
||||
-type eldap_entry() :: #eldap_entry{}.
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -19,14 +19,29 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(CT_XML, {"Content-Type", "text/xml; charset=utf-8"}).
|
||||
-define(CT_PLAIN, {"Content-Type", "text/plain"}).
|
||||
-define(CT_XML,
|
||||
{<<"Content-Type">>, <<"text/xml; charset=utf-8">>}).
|
||||
|
||||
-define(AC_ALLOW_ORIGIN, {"Access-Control-Allow-Origin", "*"}).
|
||||
-define(AC_ALLOW_METHODS, {"Access-Control-Allow-Methods", "GET, POST, OPTIONS"}).
|
||||
-define(AC_ALLOW_HEADERS, {"Access-Control-Allow-Headers", "Content-Type"}).
|
||||
-define(AC_MAX_AGE, {"Access-Control-Max-Age", "86400"}).
|
||||
-define(CT_PLAIN,
|
||||
{<<"Content-Type">>, <<"text/plain">>}).
|
||||
|
||||
-define(OPTIONS_HEADER, [?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS,
|
||||
?AC_ALLOW_HEADERS, ?AC_MAX_AGE]).
|
||||
-define(HEADER, [?CT_XML, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]).
|
||||
-define(AC_ALLOW_ORIGIN,
|
||||
{<<"Access-Control-Allow-Origin">>, <<"*">>}).
|
||||
|
||||
-define(AC_ALLOW_METHODS,
|
||||
{<<"Access-Control-Allow-Methods">>,
|
||||
<<"GET, POST, OPTIONS">>}).
|
||||
|
||||
-define(AC_ALLOW_HEADERS,
|
||||
{<<"Access-Control-Allow-Headers">>,
|
||||
<<"Content-Type">>}).
|
||||
|
||||
-define(AC_MAX_AGE,
|
||||
{<<"Access-Control-Max-Age">>, <<"86400">>}).
|
||||
|
||||
-define(OPTIONS_HEADER,
|
||||
[?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS,
|
||||
?AC_ALLOW_HEADERS, ?AC_MAX_AGE]).
|
||||
|
||||
-define(HEADER,
|
||||
[?CT_XML, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]).
|
||||
@@ -0,0 +1,701 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
|
||||
-define(NS_DISCO_ITEMS,
|
||||
<<"http://jabber.org/protocol/disco#items">>).
|
||||
|
||||
-define(NS_DISCO_INFO,
|
||||
<<"http://jabber.org/protocol/disco#info">>).
|
||||
|
||||
-define(NS_VCARD, <<"vcard-temp">>).
|
||||
|
||||
-define(NS_VCARD_UPDATE, <<"vcard-temp:x:update">>).
|
||||
|
||||
-define(NS_AUTH, <<"jabber:iq:auth">>).
|
||||
|
||||
-define(NS_AUTH_ERROR, <<"jabber:iq:auth:error">>).
|
||||
|
||||
-define(NS_REGISTER, <<"jabber:iq:register">>).
|
||||
|
||||
-define(NS_SEARCH, <<"jabber:iq:search">>).
|
||||
|
||||
-define(NS_ROSTER, <<"jabber:iq:roster">>).
|
||||
|
||||
-define(NS_ROSTER_VER,
|
||||
<<"urn:xmpp:features:rosterver">>).
|
||||
|
||||
-define(NS_PRIVACY, <<"jabber:iq:privacy">>).
|
||||
|
||||
-define(NS_BLOCKING, <<"urn:xmpp:blocking">>).
|
||||
|
||||
-define(NS_PRIVATE, <<"jabber:iq:private">>).
|
||||
|
||||
-define(NS_VERSION, <<"jabber:iq:version">>).
|
||||
|
||||
-define(NS_TIME90, <<"jabber:iq:time">>).
|
||||
|
||||
-define(NS_TIME, <<"urn:xmpp:time">>).
|
||||
|
||||
-define(NS_LAST, <<"jabber:iq:last">>).
|
||||
|
||||
-define(NS_XDATA, <<"jabber:x:data">>).
|
||||
|
||||
-define(NS_IQDATA, <<"jabber:iq:data">>).
|
||||
|
||||
-define(NS_DELAY91, <<"jabber:x:delay">>).
|
||||
|
||||
-define(NS_DELAY, <<"urn:xmpp:delay">>).
|
||||
|
||||
-define(NS_EXPIRE, <<"jabber:x:expire">>).
|
||||
|
||||
-define(NS_EVENT, <<"jabber:x:event">>).
|
||||
|
||||
-define(NS_CHATSTATES,
|
||||
<<"http://jabber.org/protocol/chatstates">>).
|
||||
|
||||
-define(NS_XCONFERENCE, <<"jabber:x:conference">>).
|
||||
|
||||
-define(NS_STATS,
|
||||
<<"http://jabber.org/protocol/stats">>).
|
||||
|
||||
-define(NS_MUC, <<"http://jabber.org/protocol/muc">>).
|
||||
|
||||
-define(NS_MUC_USER,
|
||||
<<"http://jabber.org/protocol/muc#user">>).
|
||||
|
||||
-define(NS_MUC_ADMIN,
|
||||
<<"http://jabber.org/protocol/muc#admin">>).
|
||||
|
||||
-define(NS_MUC_OWNER,
|
||||
<<"http://jabber.org/protocol/muc#owner">>).
|
||||
|
||||
-define(NS_MUC_UNIQUE,
|
||||
<<"http://jabber.org/protocol/muc#unique">>).
|
||||
|
||||
-define(NS_PUBSUB,
|
||||
<<"http://jabber.org/protocol/pubsub">>).
|
||||
|
||||
-define(NS_PUBSUB_EVENT,
|
||||
<<"http://jabber.org/protocol/pubsub#event">>).
|
||||
|
||||
-define(NS_PUBSUB_META_DATA,
|
||||
<<"http://jabber.org/protocol/pubsub#meta-data">>).
|
||||
|
||||
-define(NS_PUBSUB_OWNER,
|
||||
<<"http://jabber.org/protocol/pubsub#owner">>).
|
||||
|
||||
-define(NS_PUBSUB_NMI,
|
||||
<<"http://jabber.org/protocol/pubsub#node-meta-info">>).
|
||||
|
||||
-define(NS_PUBSUB_ERRORS,
|
||||
<<"http://jabber.org/protocol/pubsub#errors">>).
|
||||
|
||||
-define(NS_PUBSUB_NODE_CONFIG,
|
||||
<<"http://jabber.org/protocol/pubsub#node_config">>).
|
||||
|
||||
-define(NS_PUBSUB_SUB_OPTIONS,
|
||||
<<"http://jabber.org/protocol/pubsub#subscribe_options">>).
|
||||
|
||||
-define(NS_PUBSUB_SUBSCRIBE_OPTIONS,
|
||||
<<"http://jabber.org/protocol/pubsub#subscribe_options">>).
|
||||
|
||||
-define(NS_PUBSUB_PUBLISH_OPTIONS,
|
||||
<<"http://jabber.org/protocol/pubsub#publish_options">>).
|
||||
|
||||
-define(NS_PUBSUB_SUB_AUTH,
|
||||
<<"http://jabber.org/protocol/pubsub#subscribe_authorization">>).
|
||||
|
||||
-define(NS_PUBSUB_GET_PENDING,
|
||||
<<"http://jabber.org/protocol/pubsub#get-pending">>).
|
||||
|
||||
-define(NS_COMMANDS,
|
||||
<<"http://jabber.org/protocol/commands">>).
|
||||
|
||||
-define(NS_BYTESTREAMS,
|
||||
<<"http://jabber.org/protocol/bytestreams">>).
|
||||
|
||||
-define(NS_ADMIN,
|
||||
<<"http://jabber.org/protocol/admin">>).
|
||||
-define(NS_ADMIN_ANNOUNCE,
|
||||
<<"http://jabber.org/protocol/admin#announce">>).
|
||||
-define(NS_ADMIN_ANNOUNCE_ALL,
|
||||
<<"http://jabber.org/protocol/admin#announce-all">>).
|
||||
-define(NS_ADMIN_SET_MOTD,
|
||||
<<"http://jabber.org/protocol/admin#set-motd">>).
|
||||
-define(NS_ADMIN_EDIT_MOTD,
|
||||
<<"http://jabber.org/protocol/admin#edit-motd">>).
|
||||
-define(NS_ADMIN_DELETE_MOTD,
|
||||
<<"http://jabber.org/protocol/admin#delete-motd">>).
|
||||
-define(NS_ADMIN_ANNOUNCE_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#announce-allhosts">>).
|
||||
-define(NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#announce-all-allhosts">>).
|
||||
-define(NS_ADMIN_SET_MOTD_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#set-motd-allhosts">>).
|
||||
-define(NS_ADMIN_EDIT_MOTD_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#edit-motd-allhosts">>).
|
||||
-define(NS_ADMIN_DELETE_MOTD_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#delete-motd-allhosts">>).
|
||||
|
||||
-define(NS_SERVERINFO,
|
||||
<<"http://jabber.org/network/serverinfo">>).
|
||||
|
||||
-define(NS_RSM, <<"http://jabber.org/protocol/rsm">>).
|
||||
|
||||
-define(NS_EJABBERD_CONFIG, <<"ejabberd:config">>).
|
||||
|
||||
-define(NS_STREAM,
|
||||
<<"http://etherx.jabber.org/streams">>).
|
||||
|
||||
-define(NS_FLASH_STREAM,
|
||||
<<"http://www.jabber.com/streams/flash">>).
|
||||
|
||||
-define(NS_STANZAS,
|
||||
<<"urn:ietf:params:xml:ns:xmpp-stanzas">>).
|
||||
|
||||
-define(NS_STREAMS,
|
||||
<<"urn:ietf:params:xml:ns:xmpp-streams">>).
|
||||
|
||||
-define(NS_TLS, <<"urn:ietf:params:xml:ns:xmpp-tls">>).
|
||||
|
||||
-define(NS_SASL,
|
||||
<<"urn:ietf:params:xml:ns:xmpp-sasl">>).
|
||||
|
||||
-define(NS_SESSION,
|
||||
<<"urn:ietf:params:xml:ns:xmpp-session">>).
|
||||
|
||||
-define(NS_BIND,
|
||||
<<"urn:ietf:params:xml:ns:xmpp-bind">>).
|
||||
|
||||
-define(NS_FEATURE_IQAUTH,
|
||||
<<"http://jabber.org/features/iq-auth">>).
|
||||
|
||||
-define(NS_FEATURE_IQREGISTER,
|
||||
<<"http://jabber.org/features/iq-register">>).
|
||||
|
||||
-define(NS_FEATURE_COMPRESS,
|
||||
<<"http://jabber.org/features/compress">>).
|
||||
|
||||
-define(NS_FEATURE_MSGOFFLINE, <<"msgoffline">>).
|
||||
|
||||
-define(NS_COMPRESS,
|
||||
<<"http://jabber.org/protocol/compress">>).
|
||||
|
||||
-define(NS_CAPS, <<"http://jabber.org/protocol/caps">>).
|
||||
|
||||
-define(NS_SHIM, <<"http://jabber.org/protocol/shim">>).
|
||||
|
||||
-define(NS_ADDRESS,
|
||||
<<"http://jabber.org/protocol/address">>).
|
||||
|
||||
-define(NS_OOB, <<"jabber:x:oob">>).
|
||||
|
||||
-define(NS_CAPTCHA, <<"urn:xmpp:captcha">>).
|
||||
|
||||
-define(NS_MEDIA, <<"urn:xmpp:media-element">>).
|
||||
|
||||
-define(NS_BOB, <<"urn:xmpp:bob">>).
|
||||
|
||||
-include("xml.hrl").
|
||||
|
||||
-define(STANZA_ERROR(Code, Type, Condition),
|
||||
#xmlel{name = <<"error">>,
|
||||
attrs = [{<<"code">>, Code}, {<<"type">>, Type}],
|
||||
children =
|
||||
[#xmlel{name = Condition,
|
||||
attrs = [{<<"xmlns">>, ?NS_STANZAS}],
|
||||
children = []}]}).
|
||||
|
||||
-define(ERR_BAD_FORMAT,
|
||||
?STANZA_ERROR(<<"406">>, <<"modify">>,
|
||||
<<"bad-format">>)).
|
||||
|
||||
-define(ERR_BAD_REQUEST,
|
||||
?STANZA_ERROR(<<"400">>, <<"modify">>,
|
||||
<<"bad-request">>)).
|
||||
|
||||
-define(ERR_CONFLICT,
|
||||
?STANZA_ERROR(<<"409">>, <<"cancel">>, <<"conflict">>)).
|
||||
|
||||
-define(ERR_FEATURE_NOT_IMPLEMENTED,
|
||||
?STANZA_ERROR(<<"501">>, <<"cancel">>,
|
||||
<<"feature-not-implemented">>)).
|
||||
|
||||
-define(ERR_FORBIDDEN,
|
||||
?STANZA_ERROR(<<"403">>, <<"auth">>, <<"forbidden">>)).
|
||||
|
||||
-define(ERR_GONE,
|
||||
?STANZA_ERROR(<<"302">>, <<"modify">>, <<"gone">>)).
|
||||
|
||||
-define(ERR_INTERNAL_SERVER_ERROR,
|
||||
?STANZA_ERROR(<<"500">>, <<"wait">>,
|
||||
<<"internal-server-error">>)).
|
||||
|
||||
-define(ERR_ITEM_NOT_FOUND,
|
||||
?STANZA_ERROR(<<"404">>, <<"cancel">>,
|
||||
<<"item-not-found">>)).
|
||||
|
||||
-define(ERR_JID_MALFORMED,
|
||||
?STANZA_ERROR(<<"400">>, <<"modify">>,
|
||||
<<"jid-malformed">>)).
|
||||
|
||||
-define(ERR_NOT_ACCEPTABLE,
|
||||
?STANZA_ERROR(<<"406">>, <<"modify">>,
|
||||
<<"not-acceptable">>)).
|
||||
|
||||
-define(ERR_NOT_ALLOWED,
|
||||
?STANZA_ERROR(<<"405">>, <<"cancel">>,
|
||||
<<"not-allowed">>)).
|
||||
|
||||
-define(ERR_NOT_AUTHORIZED,
|
||||
?STANZA_ERROR(<<"401">>, <<"auth">>,
|
||||
<<"not-authorized">>)).
|
||||
|
||||
-define(ERR_PAYMENT_REQUIRED,
|
||||
?STANZA_ERROR(<<"402">>, <<"auth">>,
|
||||
<<"payment-required">>)).
|
||||
|
||||
-define(ERR_POLICY_VIOLATION,
|
||||
?STANZA_ERROR(<<"405">>, <<"cancel">>,
|
||||
<<"policy-violation">>)).
|
||||
|
||||
-define(ERR_RECIPIENT_UNAVAILABLE,
|
||||
?STANZA_ERROR(<<"404">>, <<"wait">>,
|
||||
<<"recipient-unavailable">>)).
|
||||
|
||||
-define(ERR_REDIRECT,
|
||||
?STANZA_ERROR(<<"302">>, <<"modify">>, <<"redirect">>)).
|
||||
|
||||
-define(ERR_REGISTRATION_REQUIRED,
|
||||
?STANZA_ERROR(<<"407">>, <<"auth">>,
|
||||
<<"registration-required">>)).
|
||||
|
||||
-define(ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
?STANZA_ERROR(<<"404">>, <<"cancel">>,
|
||||
<<"remote-server-not-found">>)).
|
||||
|
||||
-define(ERR_REMOTE_SERVER_TIMEOUT,
|
||||
?STANZA_ERROR(<<"504">>, <<"wait">>,
|
||||
<<"remote-server-timeout">>)).
|
||||
|
||||
-define(ERR_RESOURCE_CONSTRAINT,
|
||||
?STANZA_ERROR(<<"500">>, <<"wait">>,
|
||||
<<"resource-constraint">>)).
|
||||
|
||||
-define(ERR_SERVICE_UNAVAILABLE,
|
||||
?STANZA_ERROR(<<"503">>, <<"cancel">>,
|
||||
<<"service-unavailable">>)).
|
||||
|
||||
-define(ERR_SUBSCRIPTION_REQUIRED,
|
||||
?STANZA_ERROR(<<"407">>, <<"auth">>,
|
||||
<<"subscription-required">>)).
|
||||
|
||||
-define(ERR_UNEXPECTED_REQUEST,
|
||||
?STANZA_ERROR(<<"400">>, <<"wait">>,
|
||||
<<"unexpected-request">>)).
|
||||
|
||||
-define(ERR_UNEXPECTED_REQUEST_CANCEL,
|
||||
?STANZA_ERROR(<<"401">>, <<"cancel">>,
|
||||
<<"unexpected-request">>)).
|
||||
|
||||
%-define(ERR_,
|
||||
% ?STANZA_ERROR("", "", "")).
|
||||
|
||||
-define(STANZA_ERRORT(Code, Type, Condition, Lang,
|
||||
Text),
|
||||
#xmlel{name = <<"error">>,
|
||||
attrs = [{<<"code">>, Code}, {<<"type">>, Type}],
|
||||
children =
|
||||
[#xmlel{name = Condition,
|
||||
attrs = [{<<"xmlns">>, ?NS_STANZAS}], children = []},
|
||||
#xmlel{name = <<"text">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_STANZAS}],
|
||||
children =
|
||||
[{xmlcdata,
|
||||
translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(ERRT_BAD_FORMAT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"406">>, <<"modify">>,
|
||||
<<"bad-format">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_BAD_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT(<<"400">>, <<"modify">>,
|
||||
<<"bad-request">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_CONFLICT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"409">>, <<"cancel">>, <<"conflict">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"501">>, <<"cancel">>,
|
||||
<<"feature-not-implemented">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_FORBIDDEN(Lang, Text),
|
||||
?STANZA_ERRORT(<<"403">>, <<"auth">>, <<"forbidden">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(ERRT_GONE(Lang, Text),
|
||||
?STANZA_ERRORT(<<"302">>, <<"modify">>, <<"gone">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(ERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STANZA_ERRORT(<<"500">>, <<"wait">>,
|
||||
<<"internal-server-error">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_ITEM_NOT_FOUND(Lang, Text),
|
||||
?STANZA_ERRORT(<<"404">>, <<"cancel">>,
|
||||
<<"item-not-found">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_JID_MALFORMED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"400">>, <<"modify">>,
|
||||
<<"jid-malformed">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_NOT_ACCEPTABLE(Lang, Text),
|
||||
?STANZA_ERRORT(<<"406">>, <<"modify">>,
|
||||
<<"not-acceptable">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_NOT_ALLOWED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"405">>, <<"cancel">>,
|
||||
<<"not-allowed">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"401">>, <<"auth">>,
|
||||
<<"not-authorized">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_PAYMENT_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"402">>, <<"auth">>,
|
||||
<<"payment-required">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_RECIPIENT_UNAVAILABLE(Lang, Text),
|
||||
?STANZA_ERRORT(<<"404">>, <<"wait">>,
|
||||
<<"recipient-unavailable">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_REDIRECT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"302">>, <<"modify">>, <<"redirect">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(ERRT_REGISTRATION_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"407">>, <<"auth">>,
|
||||
<<"registration-required">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_REMOTE_SERVER_NOT_FOUND(Lang, Text),
|
||||
?STANZA_ERRORT(<<"404">>, <<"cancel">>,
|
||||
<<"remote-server-not-found">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_REMOTE_SERVER_TIMEOUT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"504">>, <<"wait">>,
|
||||
<<"remote-server-timeout">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_RESOURCE_CONSTRAINT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"500">>, <<"wait">>,
|
||||
<<"resource-constraint">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_SERVICE_UNAVAILABLE(Lang, Text),
|
||||
?STANZA_ERRORT(<<"503">>, <<"cancel">>,
|
||||
<<"service-unavailable">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_SUBSCRIPTION_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"407">>, <<"auth">>,
|
||||
<<"subscription-required">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_UNEXPECTED_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT(<<"400">>, <<"wait">>,
|
||||
<<"unexpected-request">>, Lang, Text)).
|
||||
|
||||
-define(ERR_AUTH_NO_RESOURCE_PROVIDED(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang, <<"No resource provided">>)).
|
||||
|
||||
-define(ERR_AUTH_BAD_RESOURCE_FORMAT(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang,
|
||||
<<"Illegal resource format">>)).
|
||||
|
||||
-define(ERR_AUTH_RESOURCE_CONFLICT(Lang),
|
||||
?ERRT_CONFLICT(Lang, <<"Resource conflict">>)).
|
||||
|
||||
-define(STREAM_ERROR(Condition, Cdata),
|
||||
#xmlel{name = <<"stream:error">>, attrs = [],
|
||||
children =
|
||||
[#xmlel{name = Condition,
|
||||
attrs = [{<<"xmlns">>, ?NS_STREAMS}],
|
||||
children = [{xmlcdata, Cdata}]}]}).
|
||||
|
||||
-define(SERR_BAD_FORMAT,
|
||||
?STREAM_ERROR(<<"bad-format">>, <<"">>)).
|
||||
|
||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
||||
?STREAM_ERROR(<<"bad-namespace-prefix">>, <<"">>)).
|
||||
|
||||
-define(SERR_CONFLICT,
|
||||
?STREAM_ERROR(<<"conflict">>, <<"">>)).
|
||||
|
||||
-define(SERR_CONNECTION_TIMEOUT,
|
||||
?STREAM_ERROR(<<"connection-timeout">>, <<"">>)).
|
||||
|
||||
-define(SERR_HOST_GONE,
|
||||
?STREAM_ERROR(<<"host-gone">>, <<"">>)).
|
||||
|
||||
-define(SERR_HOST_UNKNOWN,
|
||||
?STREAM_ERROR(<<"host-unknown">>, <<"">>)).
|
||||
|
||||
-define(SERR_IMPROPER_ADDRESSING,
|
||||
?STREAM_ERROR(<<"improper-addressing">>, <<"">>)).
|
||||
|
||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
||||
?STREAM_ERROR(<<"internal-server-error">>, <<"">>)).
|
||||
|
||||
-define(SERR_INVALID_FROM,
|
||||
?STREAM_ERROR(<<"invalid-from">>, <<"">>)).
|
||||
|
||||
-define(SERR_INVALID_ID,
|
||||
?STREAM_ERROR(<<"invalid-id">>, <<"">>)).
|
||||
|
||||
-define(SERR_INVALID_NAMESPACE,
|
||||
?STREAM_ERROR(<<"invalid-namespace">>, <<"">>)).
|
||||
|
||||
-define(SERR_INVALID_XML,
|
||||
?STREAM_ERROR(<<"invalid-xml">>, <<"">>)).
|
||||
|
||||
-define(SERR_NOT_AUTHORIZED,
|
||||
?STREAM_ERROR(<<"not-authorized">>, <<"">>)).
|
||||
|
||||
-define(SERR_POLICY_VIOLATION,
|
||||
?STREAM_ERROR(<<"policy-violation">>, <<"">>)).
|
||||
|
||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
||||
?STREAM_ERROR(<<"remote-connection-failed">>, <<"">>)).
|
||||
|
||||
-define(SERR_RESOURSE_CONSTRAINT,
|
||||
?STREAM_ERROR(<<"resource-constraint">>, <<"">>)).
|
||||
|
||||
-define(SERR_RESTRICTED_XML,
|
||||
?STREAM_ERROR(<<"restricted-xml">>, <<"">>)).
|
||||
|
||||
-define(SERR_SEE_OTHER_HOST(Host),
|
||||
?STREAM_ERROR(<<"see-other-host">>, Host)).
|
||||
|
||||
-define(SERR_SYSTEM_SHUTDOWN,
|
||||
?STREAM_ERROR(<<"system-shutdown">>, <<"">>)).
|
||||
|
||||
-define(SERR_UNSUPPORTED_ENCODING,
|
||||
?STREAM_ERROR(<<"unsupported-encoding">>, <<"">>)).
|
||||
|
||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
||||
?STREAM_ERROR(<<"unsupported-stanza-type">>, <<"">>)).
|
||||
|
||||
-define(SERR_UNSUPPORTED_VERSION,
|
||||
?STREAM_ERROR(<<"unsupported-version">>, <<"">>)).
|
||||
|
||||
-define(SERR_XML_NOT_WELL_FORMED,
|
||||
?STREAM_ERROR(<<"xml-not-well-formed">>, <<"">>)).
|
||||
|
||||
%-define(SERR_,
|
||||
% ?STREAM_ERROR("", "")).
|
||||
|
||||
-define(STREAM_ERRORT(Condition, Cdata, Lang, Text),
|
||||
#xmlel{name = <<"stream:error">>, attrs = [],
|
||||
children =
|
||||
[#xmlel{name = Condition,
|
||||
attrs = [{<<"xmlns">>, ?NS_STREAMS}],
|
||||
children = [{xmlcdata, Cdata}]},
|
||||
#xmlel{name = <<"text">>,
|
||||
attrs =
|
||||
[{<<"xml:lang">>, Lang},
|
||||
{<<"xmlns">>, ?NS_STREAMS}],
|
||||
children =
|
||||
[{xmlcdata,
|
||||
translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(SERRT_BAD_FORMAT(Lang, Text),
|
||||
?STREAM_ERRORT(<<"bad-format">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
|
||||
?STREAM_ERRORT(<<"bad-namespace-prefix">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_CONFLICT(Lang, Text),
|
||||
?STREAM_ERRORT(<<"conflict">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
|
||||
?STREAM_ERRORT(<<"connection-timeout">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_HOST_GONE(Lang, Text),
|
||||
?STREAM_ERRORT(<<"host-gone">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_HOST_UNKNOWN(Lang, Text),
|
||||
?STREAM_ERRORT(<<"host-unknown">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
|
||||
?STREAM_ERRORT(<<"improper-addressing">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STREAM_ERRORT(<<"internal-server-error">>, <<"">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(SERRT_INVALID_FROM(Lang, Text),
|
||||
?STREAM_ERRORT(<<"invalid-from">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_INVALID_ID(Lang, Text),
|
||||
?STREAM_ERRORT(<<"invalid-id">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_INVALID_NAMESPACE(Lang, Text),
|
||||
?STREAM_ERRORT(<<"invalid-namespace">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_INVALID_XML(Lang, Text),
|
||||
?STREAM_ERRORT(<<"invalid-xml">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STREAM_ERRORT(<<"not-authorized">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_POLICY_VIOLATION(Lang, Text),
|
||||
?STREAM_ERRORT(<<"policy-violation">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
|
||||
?STREAM_ERRORT(<<"remote-connection-failed">>, <<"">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
|
||||
?STREAM_ERRORT(<<"resource-constraint">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_RESTRICTED_XML(Lang, Text),
|
||||
?STREAM_ERRORT(<<"restricted-xml">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_SEE_OTHER_HOST(Host, Lang, Text),
|
||||
?STREAM_ERRORT(<<"see-other-host">>, Host, Lang, Text)).
|
||||
|
||||
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
|
||||
?STREAM_ERRORT(<<"system-shutdown">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
|
||||
?STREAM_ERRORT(<<"unsupported-encoding">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
|
||||
?STREAM_ERRORT(<<"unsupported-stanza-type">>, <<"">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
|
||||
?STREAM_ERRORT(<<"unsupported-version">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
|
||||
?STREAM_ERRORT(<<"xml-not-well-formed">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-record(jid, {user = <<"">> :: binary(),
|
||||
server = <<"">> :: binary(),
|
||||
resource = <<"">> :: binary(),
|
||||
luser = <<"">> :: binary(),
|
||||
lserver = <<"">> :: binary(),
|
||||
lresource = <<"">> :: binary()}).
|
||||
|
||||
-type(jid() :: #jid{}).
|
||||
|
||||
-type(ljid() :: {binary(), binary(), binary()}).
|
||||
|
||||
-record(iq, {id = <<"">> :: binary(),
|
||||
type = get :: get | set | result | error,
|
||||
xmlns = <<"">> :: binary(),
|
||||
lang = <<"">> :: binary(),
|
||||
sub_el = #xmlel{} :: xmlel() | [xmlel()]}).
|
||||
|
||||
-type(iq_get()
|
||||
:: #iq{
|
||||
id :: binary(),
|
||||
type :: get,
|
||||
xmlns :: binary(),
|
||||
lang :: binary(),
|
||||
sub_el :: xmlel()
|
||||
}
|
||||
).
|
||||
|
||||
-type(iq_set()
|
||||
:: #iq{
|
||||
id :: binary(),
|
||||
type :: set,
|
||||
xmlns :: binary(),
|
||||
lang :: binary(),
|
||||
sub_el :: xmlel()
|
||||
}
|
||||
).
|
||||
|
||||
-type iq_request() :: iq_get() | iq_set().
|
||||
|
||||
-type(iq_result()
|
||||
:: #iq{
|
||||
id :: binary(),
|
||||
type :: result,
|
||||
xmlns :: binary(),
|
||||
lang :: binary(),
|
||||
sub_el :: [xmlel()]
|
||||
}
|
||||
).
|
||||
|
||||
-type(iq_error()
|
||||
:: #iq{
|
||||
id :: binary(),
|
||||
type :: error,
|
||||
xmlns :: binary(),
|
||||
lang :: binary(),
|
||||
sub_el :: [xmlel()]
|
||||
}
|
||||
).
|
||||
|
||||
-type iq_reply() :: iq_result() | iq_error() .
|
||||
|
||||
-type(iq() :: iq_request() | iq_reply()).
|
||||
|
||||
-record(rsm_in, {max :: integer(),
|
||||
direction :: before | aft,
|
||||
id :: binary(),
|
||||
index :: integer()}).
|
||||
|
||||
-record(rsm_out, {count :: integer(),
|
||||
index :: integer(),
|
||||
first :: binary(),
|
||||
last :: binary()}).
|
||||
|
||||
-type(rsm_in() :: #rsm_in{}).
|
||||
|
||||
-type(rsm_out() :: #rsm_out{}).
|
||||
|
||||
-type broadcast() :: {broadcast, broadcast_data()}.
|
||||
|
||||
-type broadcast_data() ::
|
||||
{rebind, pid(), binary()} | %% ejabberd_c2s
|
||||
{item, ljid(), mod_roster:subscription()} | %% mod_roster/mod_shared_roster
|
||||
{exit, binary()} | %% mod_roster/mod_shared_roster
|
||||
{privacy_list, mod_privacy:userlist(), binary()} | %% mod_privacy
|
||||
{blocking, unblock_all | {block | unblock, [ljid()]}}. %% mod_blocking
|
||||
|
||||
-record(xmlelement, {name = "" :: string(),
|
||||
attrs = [] :: [{string(), string()}],
|
||||
children = [] :: [{xmlcdata, iodata()} | xmlelement()]}).
|
||||
|
||||
-type xmlelement() :: #xmlelement{}.
|
||||
@@ -0,0 +1 @@
|
||||
-define(IS_VALID, true).
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -18,20 +18,19 @@
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
-define(PRINT(Format, Args), io:format(Format, Args)).
|
||||
|
||||
-record(privacy, {us,
|
||||
default = none,
|
||||
lists = []}).
|
||||
-define(DEBUG(Format, Args),
|
||||
ejabberd_logger:debug_msg(?MODULE, ?LINE, Format, Args)).
|
||||
|
||||
-record(listitem, {type = none,
|
||||
value = none,
|
||||
action,
|
||||
order,
|
||||
match_all = false,
|
||||
match_iq = false,
|
||||
match_message = false,
|
||||
match_presence_in = false,
|
||||
match_presence_out = false
|
||||
}).
|
||||
-define(INFO_MSG(Format, Args),
|
||||
ejabberd_logger:info_msg(?MODULE, ?LINE, Format, Args)).
|
||||
|
||||
-record(userlist, {name = none, list = [], needdb = false }).
|
||||
-define(WARNING_MSG(Format, Args),
|
||||
ejabberd_logger:warning_msg(?MODULE, ?LINE, Format, Args)).
|
||||
|
||||
-define(ERROR_MSG(Format, Args),
|
||||
ejabberd_logger:error_msg(?MODULE, ?LINE, Format, Args)).
|
||||
|
||||
-define(CRITICAL_MSG(Format, Args),
|
||||
ejabberd_logger:critical_msg(?MODULE, ?LINE, Format, Args)).
|
||||
@@ -0,0 +1,117 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(MAX_USERS_DEFAULT, 200).
|
||||
|
||||
-define(SETS, gb_sets).
|
||||
|
||||
-define(DICT, dict).
|
||||
|
||||
-record(lqueue,
|
||||
{
|
||||
queue :: queue(),
|
||||
len :: integer(),
|
||||
max :: integer()
|
||||
}).
|
||||
|
||||
-type lqueue() :: #lqueue{}.
|
||||
|
||||
-record(config,
|
||||
{
|
||||
title = <<"">> :: binary(),
|
||||
description = <<"">> :: binary(),
|
||||
allow_change_subj = true :: boolean(),
|
||||
allow_query_users = true :: boolean(),
|
||||
allow_private_messages = true :: boolean(),
|
||||
allow_private_messages_from_visitors = anyone :: anyone | moderators | nobody ,
|
||||
allow_visitor_status = true :: boolean(),
|
||||
allow_visitor_nickchange = true :: boolean(),
|
||||
public = true :: boolean(),
|
||||
public_list = true :: boolean(),
|
||||
persistent = false :: boolean(),
|
||||
moderated = true :: boolean(),
|
||||
captcha_protected = false :: boolean(),
|
||||
members_by_default = true :: boolean(),
|
||||
members_only = false :: boolean(),
|
||||
allow_user_invites = false :: boolean(),
|
||||
password_protected = false :: boolean(),
|
||||
password = <<"">> :: binary(),
|
||||
anonymous = true :: boolean(),
|
||||
allow_voice_requests = true :: boolean(),
|
||||
voice_request_min_interval = 1800 :: non_neg_integer(),
|
||||
max_users = ?MAX_USERS_DEFAULT :: non_neg_integer() | none,
|
||||
logging = false :: boolean(),
|
||||
captcha_whitelist = (?SETS):empty() :: gb_set()
|
||||
}).
|
||||
|
||||
-type config() :: #config{}.
|
||||
|
||||
-type role() :: moderator | participant | visitor | none.
|
||||
|
||||
-record(user,
|
||||
{
|
||||
jid :: jid(),
|
||||
nick :: binary(),
|
||||
role :: role(),
|
||||
last_presence :: xmlel()
|
||||
}).
|
||||
|
||||
-record(activity,
|
||||
{
|
||||
message_time = 0 :: integer(),
|
||||
presence_time = 0 :: integer(),
|
||||
message_shaper :: shaper:shaper(),
|
||||
presence_shaper :: shaper:shaper(),
|
||||
message :: xmlel(),
|
||||
presence :: {binary(), xmlel()}
|
||||
}).
|
||||
|
||||
-record(state,
|
||||
{
|
||||
room = <<"">> :: binary(),
|
||||
host = <<"">> :: binary(),
|
||||
server_host = <<"">> :: binary(),
|
||||
access = {none,none,none,none} :: {atom(), atom(), atom(), atom()},
|
||||
jid = #jid{} :: jid(),
|
||||
config = #config{} :: config(),
|
||||
users = (?DICT):new() :: dict(),
|
||||
last_voice_request_time = treap:empty() :: treap:treap(),
|
||||
robots = (?DICT):new() :: dict(),
|
||||
nicks = (?DICT):new() :: dict(),
|
||||
affiliations = (?DICT):new() :: dict(),
|
||||
history :: lqueue(),
|
||||
persist_history = false :: boolean(),
|
||||
subject = <<"">> :: binary(),
|
||||
subject_author = <<"">> :: binary(),
|
||||
just_created = false :: boolean(),
|
||||
activity = treap:empty() :: treap:treap(),
|
||||
room_shaper = none :: shaper:shaper(),
|
||||
room_queue = queue:new() :: queue()
|
||||
}).
|
||||
|
||||
-record(muc_online_users, {us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
resource = <<>> :: binary() | '_',
|
||||
room = <<>> :: binary() | '_',
|
||||
host = <<>> :: binary() | '_'}).
|
||||
|
||||
-type muc_online_users() :: #muc_online_users{}.
|
||||
|
||||
-type muc_room_state() :: #state{}.
|
||||
@@ -0,0 +1,46 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(mod_privacy_hrl, true).
|
||||
|
||||
-record(privacy, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
default = none :: none | binary(),
|
||||
lists = [] :: [{binary(), [listitem()]}]}).
|
||||
|
||||
-record(listitem, {type = none :: none | jid | group | subscription,
|
||||
value = none :: none | both | from | to | ljid() | binary(),
|
||||
action = allow :: allow | deny,
|
||||
order = 0 :: integer(),
|
||||
match_all = false :: boolean(),
|
||||
match_iq = false :: boolean(),
|
||||
match_message = false :: boolean(),
|
||||
match_presence_in = false :: boolean(),
|
||||
match_presence_out = false :: boolean()}).
|
||||
|
||||
-type listitem() :: #listitem{}.
|
||||
|
||||
-record(userlist, {name = none :: none | binary(),
|
||||
list = [] :: [listitem()],
|
||||
needdb = false :: boolean()}).
|
||||
|
||||
-type userlist() :: #userlist{}.
|
||||
|
||||
-export_type([userlist/0]).
|
||||
@@ -2,7 +2,7 @@
|
||||
%%% RFC 1928 constants.
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -21,41 +21,48 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% Version
|
||||
-define(VERSION_5, 5).
|
||||
|
||||
%% Authentication methods
|
||||
-define(AUTH_ANONYMOUS, 0).
|
||||
-define(AUTH_GSSAPI, 1).
|
||||
-define(AUTH_PLAIN, 2).
|
||||
-define(AUTH_NO_METHODS, 16#FF).
|
||||
|
||||
%% Address Type
|
||||
-define(AUTH_GSSAPI, 1).
|
||||
|
||||
-define(AUTH_PLAIN, 2).
|
||||
|
||||
-define(AUTH_NO_METHODS, 255).
|
||||
|
||||
-define(ATYP_IPV4, 1).
|
||||
|
||||
-define(ATYP_DOMAINNAME, 3).
|
||||
|
||||
-define(ATYP_IPV6, 4).
|
||||
|
||||
%% Commands
|
||||
-define(CMD_CONNECT, 1).
|
||||
|
||||
-define(CMD_BIND, 2).
|
||||
|
||||
-define(CMD_UDP, 3).
|
||||
|
||||
%% RFC 1928 replies
|
||||
-define(SUCCESS, 0).
|
||||
|
||||
-define(ERR_GENERAL_FAILURE, 1).
|
||||
|
||||
-define(ERR_NOT_ALLOWED, 2).
|
||||
|
||||
-define(ERR_NETWORK_UNREACHABLE, 3).
|
||||
|
||||
-define(ERR_HOST_UNREACHABLE, 4).
|
||||
|
||||
-define(ERR_CONNECTION_REFUSED, 5).
|
||||
|
||||
-define(ERR_TTL_EXPIRED, 6).
|
||||
|
||||
-define(ERR_COMMAND_NOT_SUPPORTED, 7).
|
||||
|
||||
-define(ERR_ADDRESS_TYPE_NOT_SUPPORTED, 8).
|
||||
|
||||
%% RFC 1928 defined timeout.
|
||||
-define(SOCKS5_REPLY_TIMEOUT, 10000).
|
||||
|
||||
-record(s5_request, {
|
||||
rsv = 0,
|
||||
cmd,
|
||||
sha1
|
||||
}).
|
||||
-record(s5_request, {rsv = 0 :: integer(),
|
||||
cmd = connect :: connect | udp,
|
||||
sha1 = <<"">> :: binary()}).
|
||||
@@ -0,0 +1,42 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(roster,
|
||||
{
|
||||
usj = {<<>>, <<>>, {<<>>, <<>>, <<>>}} :: {binary(), binary(), ljid()} | '_',
|
||||
us = {<<>>, <<>>} :: {binary(), binary()} | '_',
|
||||
jid = {<<>>, <<>>, <<>>} :: ljid(),
|
||||
name = <<>> :: binary() | '_',
|
||||
subscription = none :: subscription() | '_',
|
||||
ask = none :: ask() | '_',
|
||||
groups = [] :: [binary()] | '_',
|
||||
askmessage = <<"">> :: binary() | '_',
|
||||
xs = [] :: [xmlel()] | '_'
|
||||
}).
|
||||
|
||||
-record(roster_version,
|
||||
{
|
||||
us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
version = <<>> :: binary()
|
||||
}).
|
||||
|
||||
-type ask() :: none | in | out | both | subscribe | unsubscribe.
|
||||
-type subscription() :: none | both | from | to | remove.
|
||||
@@ -0,0 +1,207 @@
|
||||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%%
|
||||
%%% copyright 2006-2013 ProcessOne
|
||||
%%%
|
||||
%%% This file contains pubsub types definition.
|
||||
%%% ====================================================================
|
||||
|
||||
-define(ERR_EXTENDED(E, C),
|
||||
mod_pubsub:extended_error(E, C)).
|
||||
|
||||
-define(MAXITEMS, 10).
|
||||
|
||||
-define(MAX_PAYLOAD_SIZE, 60000).
|
||||
|
||||
%% -------------------------------
|
||||
%% Pubsub types
|
||||
|
||||
-type(hostPubsub() :: binary()).
|
||||
%% <p><tt>hostPubsub</tt> is the name of the PubSub service. For example, it can be
|
||||
%% <tt>"pubsub.localhost"</tt>.</p>
|
||||
|
||||
-type(hostPEP() :: {binary(), binary(), <<>>}).
|
||||
%% @type hostPEP() = {User, Server, Resource}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Resource = [].
|
||||
%% <p>For example, it can be :
|
||||
%% ```{"bob", "example.org", []}'''.</p>
|
||||
|
||||
-type(host() :: hostPubsub() | hostPEP()).
|
||||
%% @type host() = hostPubsub() | hostPEP().
|
||||
|
||||
-type(nodeId() :: binary()).
|
||||
%% @type nodeId() = binary().
|
||||
%% <p>A node is defined by a list of its ancestors. The last element is the name
|
||||
%% of the current node. For example:
|
||||
%% ```<<"/home/localhost/user">>'''</p>
|
||||
|
||||
-type(nodeIdx() :: pos_integer()).
|
||||
%% @type nodeIdx() = integer().
|
||||
|
||||
-type(itemId() :: binary()).
|
||||
%% @type itemId() = string().
|
||||
|
||||
-type(subId() :: binary()).
|
||||
%% @type subId() = string().
|
||||
|
||||
|
||||
%% @type payload() = [#xmlelement{} | #xmlcdata{}].
|
||||
|
||||
%% @type stanzaError() = #xmlelement{}.
|
||||
%% Example:
|
||||
%% ```{xmlelement, "error",
|
||||
%% [{"code", Code}, {"type", Type}],
|
||||
%% [{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}'''
|
||||
%% @type pubsubIQResponse() = #xmlelement{}.
|
||||
%% Example:
|
||||
%% ```{xmlelement, "pubsub",
|
||||
%% [{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
%% [{xmlelement, "affiliations", [],
|
||||
%% []}]}'''
|
||||
|
||||
-type(nodeOption() ::
|
||||
{Option::atom(),
|
||||
Value::binary() | [binary()] | boolean() | non_neg_integer()
|
||||
}).
|
||||
|
||||
-type(nodeOptions() :: [NodeOption::mod_pubsub:nodeOption(),...]).
|
||||
|
||||
%% @type nodeOption() = {Option, Value}
|
||||
%% Option = atom()
|
||||
%% Value = term().
|
||||
%% Example:
|
||||
%% ```{deliver_payloads, true}'''
|
||||
|
||||
-type(subOption() ::
|
||||
{Option::atom(),
|
||||
Value::binary() | [binary()] | boolean()
|
||||
}).
|
||||
|
||||
-type(subOptions() :: [SubOption::mod_pubsub:subOption(),...]).
|
||||
|
||||
%% @type nodeType() = string().
|
||||
%% <p>The <tt>nodeType</tt> is a string containing the name of the PubSub
|
||||
%% plugin to use to manage a given node. For example, it can be
|
||||
%% <tt>"flat"</tt>, <tt>"hometree"</tt> or <tt>"blog"</tt>.</p>
|
||||
|
||||
%% @type jid() = {jid, User, Server, Resource, LUser, LServer, LResource}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Resource = string()
|
||||
%% LUser = string()
|
||||
%% LServer = string()
|
||||
%% LResource = string().
|
||||
|
||||
%-type(ljid() :: {binary(), binary(), binary()}).
|
||||
%% @type ljid() = {User, Server, Resource}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Resource = string().
|
||||
|
||||
-type(affiliation() :: 'none'
|
||||
| 'owner'
|
||||
| 'publisher'
|
||||
%| 'publish-only'
|
||||
| 'member'
|
||||
| 'outcast'
|
||||
).
|
||||
%% @type affiliation() = 'none' | 'owner' | 'publisher' | 'publish-only' | 'member' | 'outcast'.
|
||||
|
||||
-type(subscription() :: 'none'
|
||||
| 'pending'
|
||||
| 'unconfigured'
|
||||
| 'subscribed'
|
||||
).
|
||||
%% @type subscription() = 'none' | 'pending' | 'unconfigured' | 'subscribed'.
|
||||
|
||||
-type(accessModel() :: 'open'
|
||||
| 'presence'
|
||||
| 'roster'
|
||||
| 'authorize'
|
||||
| 'whitelist'
|
||||
).
|
||||
%% @type accessModel() = 'open' | 'presence' | 'roster' | 'authorize' | 'whitelist'.
|
||||
|
||||
-type(publishModel() :: 'publishers'
|
||||
| 'subscribers'
|
||||
| 'open'
|
||||
).
|
||||
|
||||
|
||||
-record(pubsub_index,
|
||||
{
|
||||
index :: atom(),
|
||||
last :: mod_pubsub:nodeIdx(),
|
||||
free :: [mod_pubsub:nodeIdx()]
|
||||
}).
|
||||
|
||||
-record(pubsub_node,
|
||||
{
|
||||
nodeid ,%:: {Host::mod_pubsub:host(), NodeId::mod_pubsub:nodeId()},
|
||||
id ,%:: mod_pubsub:nodeIdx(),
|
||||
parents = [] ,%:: [Parent_NodeId::mod_pubsub:nodeId()],
|
||||
type = <<"flat">> ,%:: binary(),
|
||||
owners = [] ,%:: [Owner::ljid(),...],
|
||||
options = [] %:: mod_pubsub:nodeOptions()
|
||||
}).
|
||||
|
||||
|
||||
%-record(pubsub_state,
|
||||
% {stateid, nodeidx, items = [], affiliation = none,
|
||||
% subscriptions = []}).
|
||||
-record(pubsub_state,
|
||||
{
|
||||
stateid ,%:: {Entity::ljid(), NodeIdx::mod_pubsub:nodeIdx()},
|
||||
nodeidx ,%:: mod_pubsub:nodeIdx(),
|
||||
items = [] ,%:: [ItemId::mod_pubsub:itemId()],
|
||||
affiliation = 'none' ,%:: mod_pubsub:affiliation(),
|
||||
subscriptions = [] %:: [{mod_pubsub:subscription(), mod_pubsub:subId()}]
|
||||
}).
|
||||
|
||||
%-record(pubsub_item,
|
||||
% {itemid, nodeidx, creation = {unknown, unknown},
|
||||
% modification = {unknown, unknown}, payload = []}).
|
||||
|
||||
-record(pubsub_item,
|
||||
{
|
||||
itemid ,%:: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
|
||||
nodeidx ,%:: mod_pubsub_nodeIdx(),
|
||||
creation = {unknown, unknown} ,%:: {erlang:timestamp(), ljid()},
|
||||
modification = {unknown, unknown} ,%:: {erlang:timestamp(), ljid()},
|
||||
payload = [] %:: mod_pubsub:payload()
|
||||
}).
|
||||
|
||||
%-record(pubsub_subscription, {subid, options}).
|
||||
-record(pubsub_subscription,
|
||||
{
|
||||
subid ,%:: mod_pubsub:subId(),
|
||||
options %:: [] | mod_pubsub:subOptions()
|
||||
}).
|
||||
|
||||
%-record(pubsub_last_item,
|
||||
% {nodeid, itemid, creation, payload}).
|
||||
|
||||
-record(pubsub_last_item,
|
||||
{
|
||||
nodeid ,%:: mod_pubsub:nodeIdx(),
|
||||
itemid ,%:: mod_pubsub:itemId(),
|
||||
creation ,%:: {erlang:timestamp(), ljid()},
|
||||
payload %:: mod_pubsub:payload()
|
||||
}).
|
||||
@@ -0,0 +1,685 @@
|
||||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%% @copyright 2006-2013 ProcessOne
|
||||
%%% @author Karim Gemayel <karim.gemayel@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%%% @headerfile "pubsub_dev.hrl"
|
||||
|
||||
-module(exmpp_pubsub).
|
||||
-author('karim.gemayel@process-one.net').
|
||||
|
||||
-compile(export_all).
|
||||
|
||||
-include("pubsub_dev.hrl").
|
||||
|
||||
|
||||
%% --------------------------------------------------------------------
|
||||
%% Documentation / type definitions.
|
||||
%% --------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
%-- Affiliations --%
|
||||
-export_type([
|
||||
affiliation/0,
|
||||
%%
|
||||
affiliation_owner/0,
|
||||
affiliation_publisher/0,
|
||||
affiliation_publish_only/0,
|
||||
affiliation_member/0,
|
||||
affiliation_none/0,
|
||||
affiliation_outcast/0
|
||||
]).
|
||||
|
||||
%% @type affiliation_owner() = 'owner'.
|
||||
-type(affiliation_owner() :: 'owner').
|
||||
|
||||
%% @type affiliation_publisher() = 'publisher'.
|
||||
-type(affiliation_publisher() :: 'publisher').
|
||||
|
||||
%% @type affiliation_publish_only() = 'publish-only'.
|
||||
-type(affiliation_publish_only() :: 'publish-only').
|
||||
|
||||
%% @type affiliation_member() = 'member'.
|
||||
-type(affiliation_member() :: 'member').
|
||||
|
||||
%% @type affiliation_none() = 'none'.
|
||||
-type(affiliation_none() :: 'none').
|
||||
|
||||
%% @type affiliation_outcast() = 'outcast'.
|
||||
-type(affiliation_outcast() :: 'outcast').
|
||||
|
||||
%% @type affiliation() = Owner | Publisher | Publish_Only | Member | None | Outcast
|
||||
%% Owner = exmpp_pubsub:affiliation_owner()
|
||||
%% Publisher = exmpp_pubsub:affiliation_publisher()
|
||||
%% Publish_Only = exmpp_pubsub:affiliation_publish_only()
|
||||
%% Member = exmpp_pubsub:affiliation_member()
|
||||
%% None = exmpp_pubsub:affiliation_none()
|
||||
%% Outcast = exmpp_pubsub:affiliation_outcast().
|
||||
-type(affiliation() :: exmpp_pubsub:affiliation_owner()
|
||||
| exmpp_pubsub:affiliation_publisher()
|
||||
| exmpp_pubsub:affiliation_publish_only()
|
||||
| exmpp_pubsub:affiliation_member()
|
||||
| exmpp_pubsub:affiliation_none()
|
||||
| exmpp_pubsub:affiliation_outcast()
|
||||
).
|
||||
|
||||
|
||||
%%
|
||||
|
||||
%-- Things identifiers --%
|
||||
-export_type([
|
||||
index/0,
|
||||
itemId/0,
|
||||
itemIds/0,
|
||||
level/0,
|
||||
nodeId/0,
|
||||
nodeIdx/0,
|
||||
subId/0,
|
||||
plugin/0
|
||||
]).
|
||||
|
||||
%% @type index() = 'node'.
|
||||
-type(index() :: 'node' ).
|
||||
|
||||
%% @type itemId() = binary().
|
||||
-type(itemId() :: binary()).
|
||||
|
||||
%% @type itemIds() = [ItemId::exmpp_pubsub:itemId(),...].
|
||||
-type(itemIds() :: [ItemId::exmpp_pubsub:itemId(),...]).
|
||||
|
||||
%% @type level() = non_neg_integer().
|
||||
-type(level() :: non_neg_integer()).
|
||||
|
||||
%% @type nodeId() = binary().
|
||||
-type(nodeId() :: binary()).
|
||||
|
||||
%% @type nodeIdx() = non_neg_integer().
|
||||
-type(nodeIdx() :: non_neg_integer()).
|
||||
|
||||
%% @type plugin() = module().
|
||||
-type(plugin() :: module()).
|
||||
|
||||
%% @type subId() = binary().
|
||||
-type(subId() :: binary()).
|
||||
|
||||
|
||||
-export_type([
|
||||
t_now/0
|
||||
]).
|
||||
|
||||
%% Temporary -type, until calendar.erl -type is exported
|
||||
-type(t_now()
|
||||
:: {MegaSec::non_neg_integer(),
|
||||
Sec::non_neg_integer(),
|
||||
MilliSec::non_neg_integer()}
|
||||
).
|
||||
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
feature/0,
|
||||
features/0,
|
||||
pubsub_feature/0,
|
||||
pubsub_features/0
|
||||
]).
|
||||
|
||||
-type(feature() :: binary()).
|
||||
|
||||
-type(features() :: Features::[Feature::exmpp_pubsub:feature(),...]).
|
||||
|
||||
-type(pubsub_feature() :: exmpp_pubsub:feature()).
|
||||
|
||||
-type(pubsub_features() :: Pubsub_Features::[Pubsub_Feature::exmpp_pubsub:feature(),...]).
|
||||
|
||||
%%
|
||||
|
||||
%-- Payload types --%
|
||||
-export_type([
|
||||
payload/0,
|
||||
%%
|
||||
payload_empty/0,
|
||||
payload_full/0
|
||||
]).
|
||||
|
||||
%% @type payload_empty() = [].
|
||||
-type(payload_empty() :: []).
|
||||
|
||||
%% @type payload_full() = #xmlel{}.
|
||||
-type(payload_full() :: xmlel()).
|
||||
|
||||
%% @type payload() = Empty_Payload | Full_Payload
|
||||
%% Empty_Payload = exmpp_pubsub:payload_empty()
|
||||
%% Full_Payload = exmpp_pubsub:payload_full().
|
||||
-type(payload() :: exmpp_pubsub:payload_empty()
|
||||
| exmpp_pubsub:payload_full()
|
||||
).
|
||||
|
||||
|
||||
%%
|
||||
|
||||
%-- Pusbub Host --%
|
||||
-export_type([
|
||||
host/0,
|
||||
%%
|
||||
host_pubsub/0,
|
||||
host_pep/0
|
||||
]).
|
||||
|
||||
%% @type host_pubsub() = xmpp_jid:domain_jid().
|
||||
%% ```<<"pubsub.shakespeare.lit">>'''
|
||||
%% Identifier type of the Pubsub service
|
||||
-type(host_pubsub() :: binary()).
|
||||
|
||||
%% @type host_pep() = xmpp_jid:usr_contact_bare().
|
||||
%% ```{<<"juliet">>, <<"capulet.lit">>, undefined}'''
|
||||
%% Identifier type of the Pubsub-On-Jid service
|
||||
-type(host_pep() :: xmpp_jid:usr_contact_bare()).
|
||||
|
||||
%% @type host() = Pubsub_Host | Pubsub_On_Jid_Host
|
||||
%% Pubsub_Host = exmpp_pubsub:host_pubsub()
|
||||
%% Pubsub_On_Jid_Host = exmpp_pubsub:host_pep().
|
||||
%% Identifier types of the Pubsub and PEP (Pubsub-On-Jid) services
|
||||
%% ```* Pubsub_Host : <<"pubsub.shakespeare.lit">>
|
||||
%% * Pubsub_PEP : {<<"juliet">>, <<"capulet.lit">>, undefined}'''
|
||||
-type(host() :: exmpp_pubsub:host_pubsub()
|
||||
| exmpp_pubsub:host_pep()
|
||||
).
|
||||
|
||||
|
||||
%%
|
||||
|
||||
%-- Subscription States --%
|
||||
-export_type([
|
||||
subscription_state/0,
|
||||
%%
|
||||
subscription_state_none/0,
|
||||
subscription_state_pending/0,
|
||||
subscription_state_unconfigured/0,
|
||||
subscription_state_subscribed/0
|
||||
]).
|
||||
|
||||
%% @type subscription_state_none() = 'none'.
|
||||
-type(subscription_state_none() :: 'none').
|
||||
|
||||
%% @type subscription_state_pending() = 'pending'.
|
||||
-type(subscription_state_pending() :: 'pending').
|
||||
|
||||
%% @type subscription_state_unconfigured() = 'unconfigured'.
|
||||
-type(subscription_state_unconfigured() :: 'unconfigured').
|
||||
|
||||
%% @type subscription_state_subscribed() = 'subscribed'.
|
||||
-type(subscription_state_subscribed() :: 'subscribed').
|
||||
|
||||
%% @type subscription_state() = None | Pending | Unconfigured | Subscribed
|
||||
%% None = exmpp_pubsub:subscription_state_none()
|
||||
%% Pending = exmpp_pubsub:subscription_state_pending()
|
||||
%% Unconfigured = exmpp_pubsub:subscription_state_unconfigured()
|
||||
%% Subscribed = exmpp_pubsub:subscription_state_subscribed()
|
||||
-type(subscription_state() :: exmpp_pubsub:subscription_state_pending()
|
||||
| exmpp_pubsub:subscription_state_unconfigured()
|
||||
| exmpp_pubsub:subscription_state_subscribed()
|
||||
).
|
||||
|
||||
%%
|
||||
|
||||
%-- Subscription -- %%
|
||||
-export_type([
|
||||
subscriptions/0,
|
||||
subscription/0,
|
||||
%%
|
||||
subscription_subscribed/0,
|
||||
subscription_unconfigured/0,
|
||||
subscription_pending/0
|
||||
]).
|
||||
|
||||
-type(subscription_subscribed()
|
||||
:: {Subscription_State :: exmpp_pubsub:subscription_state_subscribed(),
|
||||
SubId :: exmpp_pubsub:subId(),
|
||||
Resource :: undefined | xmpp_jid:resource_jid() | {'caps', xmpp_jid:resource_jid()},
|
||||
Subscription_Options :: [] | pubsub_options:options_subscription()}
|
||||
).
|
||||
|
||||
-type(subscription_unconfigured()
|
||||
:: {Subscription_State :: exmpp_pubsub:subscription_state_unconfigured(),
|
||||
SubId :: exmpp_pubsub:subId(),
|
||||
Resource :: undefined | xmpp_jid:resource_jid() | {'caps', xmpp_jid:resource_jid()},
|
||||
Subscription_Options :: []}
|
||||
).
|
||||
|
||||
-type(subscription_pending()
|
||||
:: {Subscription_State :: exmpp_pubsub:subscription_state_pending(),
|
||||
SubId :: exmpp_pubsub:subId(),
|
||||
Resource :: undefined | xmpp_jid:resource_jid() | {'caps', xmpp_jid:resource_jid()},
|
||||
Subscription_Options :: []}
|
||||
).
|
||||
|
||||
%% -type(subscription()
|
||||
%% :: {Subscription_State :: exmpp_pubsub:subscription_state(),
|
||||
%% SubId :: exmpp_pubsub:subId(),
|
||||
%% Resource :: undefined | xmpp_jid:resource_jid() | {'caps', xmpp_jid:resource_jid()},
|
||||
%% Subscription_Options :: [] | pubsub_options:options_subscription()}
|
||||
%% %% :: exmpp_pubsub:subscription_subscribed()
|
||||
%% %% | exmpp_pubsub:subscription_pending()
|
||||
%% %% | exmpp_pubsub:subscription_unconfigured()
|
||||
%% ).
|
||||
|
||||
-type(subscriptions()
|
||||
:: [exmpp_pubsub:subscription(),...]
|
||||
%% :: [exmpp_pubsub:subscription_subscribed(),...]
|
||||
%% | [exmpp_pubsub:subscription_unconfigured(),...]
|
||||
%% | [exmpp_pubsub:subscription_pending(),...]
|
||||
).
|
||||
|
||||
%% TODO : commented -type unions return the following dialyzer error (bug ?) :
|
||||
%% ...
|
||||
%% Adding information from /home/meta/otp-exmpp.plt to ejabberd.plt...
|
||||
%%=ERROR REPORT==== 29-Jul-2011::15:14:42 ===
|
||||
%Error in process <0.2604.0> with exit value: {function_clause,[{erl_types,inf_tuples_in_sets,4},{erl_types,inf_tuple_sets,4},{erl_types,inf_tuple_sets,3},{erl_types,t_inf,3},{erl_types,t_inf_lists_strict,4},{erl_types,t_inf,3},{dialyzer_typesig,solve_one_c...
|
||||
%%
|
||||
%%
|
||||
%%dialyzer: Analysis failed with error: {function_clause,[{erl_types,inf_tuples_in_sets,4},
|
||||
%% {erl_types,inf_tuple_sets,4},
|
||||
%% {erl_types,inf_tuple_sets,3},
|
||||
%% {erl_types,t_inf,3},
|
||||
%% {erl_types,t_inf_lists_strict,4},
|
||||
%% {erl_types,t_inf,3},
|
||||
%% {dialyzer_typesig,solve_one_c,...},
|
||||
%% {dialyzer_typesig,...}]}
|
||||
%%Last messages in the log cache:
|
||||
%% Typesig analysis for SCC: [{ejabberd_web_admin,make_xhtml,5}]
|
||||
%% Typesig analysis for SCC: [{ejabberd_web_admin,make_xhtml,4}]
|
||||
%% Typesig analysis for SCC: [{ejabberd_web_admin,process_admin,2}]
|
||||
%% Typesig analysis for SCC: [{ejabberd_web_admin,process,2}]
|
||||
%% Typesig analysis for SCC: [{mod_stats,get_local_stat,3}]
|
||||
%% Typesig analysis for SCC: [{mod_stats,get_local_stats,3}]
|
||||
%% Typesig analysis for SCC: [{mod_stats,process_local_iq,3}]
|
||||
%% Typesig analysis for SCC: [{mod_register_web,send_registration_notifications,2}]
|
||||
%% Typesig analysis for SCC: [{mod_register_web,process,2}]
|
||||
%% Typesig analysis for SCC: [{pubsub_db,create_node,5}]
|
||||
|
||||
%%
|
||||
|
||||
|
||||
%% Pubsub #xmlel{} templates
|
||||
|
||||
|
||||
%% --------------------------------------------------------------------
|
||||
%% Functions.
|
||||
%% --------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
%% @spec nodeId() -> NodeId::exmpp_pubsub:nodeId()
|
||||
-spec(nodeId/0 :: () -> NodeId::exmpp_pubsub:nodeId()).
|
||||
|
||||
nodeId() ->
|
||||
randoms:get_string().
|
||||
|
||||
%% @spec subId() -> SubId::exmpp_pubsub:subId()
|
||||
-spec(subId/0 :: () -> SubId::exmpp_pubsub:subId()).
|
||||
|
||||
subId() ->
|
||||
{T1, T2, T3} = now(),
|
||||
list_to_binary(lists:flatten(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3]))).
|
||||
|
||||
%% @spec itemId() -> ItemId::exmpp_pubsub:itemId()
|
||||
-spec(itemId/0 :: () -> ItemId::exmpp_pubsub:itemId()).
|
||||
|
||||
itemId() ->
|
||||
{T1, T2, T3} = now(),
|
||||
list_to_binary(lists:flatten(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3]))).
|
||||
|
||||
id() ->
|
||||
{T1, T2, T3} = now(),
|
||||
list_to_binary(lists:flatten(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3]))).
|
||||
|
||||
|
||||
|
||||
%%
|
||||
-spec(subscription_subscribed/2 ::
|
||||
(
|
||||
Resource :: undefined
|
||||
| xmpp_jid:resource_jid()
|
||||
| {'caps', xmpp_jid:resource_jid()},
|
||||
Subscription_Options :: [] | pubsub_options:options_subscription())
|
||||
-> Subscription_Subscribed::exmpp_pubsub:subscription_subscribed()
|
||||
).
|
||||
|
||||
subscription_subscribed(Resource, Subscription_Options) ->
|
||||
{'subscribed', subId(), Resource, Subscription_Options}.
|
||||
|
||||
%%
|
||||
-spec(subscription_pending/2 ::
|
||||
(
|
||||
Resource :: undefined
|
||||
| xmpp_jid:resource_jid()
|
||||
| {'caps', xmpp_jid:resource_jid()},
|
||||
Subscription_Options :: [] | pubsub_options:options_subscription())
|
||||
-> Subscription_Pending::exmpp_pubsub:subscription_pending()
|
||||
).
|
||||
|
||||
subscription_pending(Resource, Subscription_Options) ->
|
||||
{'pending', subId(), Resource, Subscription_Options}.
|
||||
|
||||
|
||||
|
||||
%% Pubsub Xmlel templates
|
||||
|
||||
xmlcdata(CData) ->
|
||||
{xmlcdata, CData}.
|
||||
|
||||
xmlattr(Name, Value) ->
|
||||
{Name, Value}.
|
||||
|
||||
|
||||
xmlel('disco#info', Name, Attrs, Children) ->
|
||||
xmlel(?NS_DISCO_INFO, Name, Attrs, Children);
|
||||
xmlel('disco#items', Name, Attrs, Children) ->
|
||||
xmlel(?NS_DISCO_ITEMS, Name, Attrs, Children);
|
||||
xmlel('jabber:client', Name, Attrs, Children) ->
|
||||
xmlel(?NS_JABBER_CLIENT, Name, Attrs, Children);
|
||||
xmlel('pubsub', Name, Attrs, Children) ->
|
||||
xmlel(?NS_PUBSUB, Name, Attrs, Children);
|
||||
xmlel('pubsub#event', Name, Attrs, Children) ->
|
||||
xmlel(?NS_PUBSUB_EVENT, Name, Attrs, Children);
|
||||
xmlel('pubsub#owner', Name, Attrs, Children) ->
|
||||
xmlel(?NS_PUBSUB_OWNER, Name, Attrs, Children);
|
||||
xmlel('shim', Name, Attrs, Children) ->
|
||||
xmlel(?NS_SHIM, Name, Attrs, Children);
|
||||
xmlel(NS, Name, Attrs, Children) ->
|
||||
#xmlel{name = Name, attrs = [{<<"xmlns">>, NS} | Attrs], children = Children}.
|
||||
%%
|
||||
xmlattr_affiliation(Affiliation) ->
|
||||
xmlattr(<<"affiliation">>, list_to_binary(atom_to_list(Affiliation))).
|
||||
%%
|
||||
xmlattr_category(Category) ->
|
||||
xmlattr(<<"category">>, Category).
|
||||
%%
|
||||
xmlattr_id(ItemId) ->
|
||||
xmlattr(<<"id">>, ItemId).
|
||||
%%
|
||||
xmlattr_jid(Jid) ->
|
||||
xmlattr(<<"jid">>, Jid).
|
||||
%%
|
||||
xmlattr_name(Name) ->
|
||||
xmlattr(<<"name">>, Name).
|
||||
%%
|
||||
xmlattr_node(NodeId) ->
|
||||
xmlattr(<<"node">>, NodeId).
|
||||
%%
|
||||
xmlattr_publisher(Publisher) ->
|
||||
xmlattr(<<"publisher">>, Publisher).
|
||||
%%
|
||||
xmlattr_subid(SubId) ->
|
||||
xmlattr(<<"subid">>, SubId).
|
||||
%%
|
||||
xmlattr_subscription(Subscription_State) ->
|
||||
xmlattr(<<"subscription">>, list_to_binary(atom_to_list(Subscription_State))).
|
||||
%%
|
||||
xmlattr_type(Type) when is_atom(Type)->
|
||||
xmlattr(<<"type">>, list_to_binary(atom_to_list(Type)));
|
||||
%%
|
||||
xmlattr_type(Type) ->
|
||||
xmlattr(<<"type">>, Type).
|
||||
%%
|
||||
xmlattr_uri(URI) ->
|
||||
xmlattr(<<"uri">>, URI).
|
||||
%%
|
||||
xmlattr_var(Var) ->
|
||||
xmlattr(<<"var">>, Var).
|
||||
|
||||
%%
|
||||
xmlel_affiliation('pubsub', NodeId, Affiliation) ->
|
||||
xmlel('pubsub', <<"affiliation">>,
|
||||
[xmlattr_node(NodeId),
|
||||
xmlattr_affiliation(Affiliation)],
|
||||
[]);
|
||||
xmlel_affiliation('pubsub#owner', Jid, Affiliation) ->
|
||||
xmlel('pubsub#owner', <<"affiliation">>,
|
||||
[xmlattr_jid(Jid),
|
||||
xmlattr_affiliation(Affiliation)],
|
||||
[]).
|
||||
%%
|
||||
xmlel_affiliations('pubsub', Xmlels) ->
|
||||
xmlel('pubsub', <<"affiliations">>, [], Xmlels).
|
||||
|
||||
xmlel_affiliations('pubsub#owner', NodeId, Xmlels) ->
|
||||
xmlel('pubsub#owner', <<"affiliations">>, [xmlattr_node(NodeId)], Xmlels).
|
||||
%%
|
||||
xmlel_create(NodeId) ->
|
||||
xmlel('pubsub', <<"create">>, [xmlattr_node(NodeId)], []).
|
||||
|
||||
%%
|
||||
xmlel_configure('pubsub#owner', NodeId, Xmlels) ->
|
||||
xmlel('pubsub#owner', <<"configure">>, [xmlattr_node(NodeId)], Xmlels).
|
||||
|
||||
%%
|
||||
xmlel_default('pubsub', Xmlels) ->
|
||||
xmlel('pubsub', <<"default">>, [], Xmlels);
|
||||
%%
|
||||
xmlel_default('pubsub#owner', Xmlels) ->
|
||||
xmlel('pubsub#owner', <<"default">>, [], Xmlels).
|
||||
|
||||
%%
|
||||
xmlel_default('pubsub', NodeId, Xmlels) ->
|
||||
xmlel('pubsub', <<"default">>, [xmlattr_node(NodeId)], Xmlels).
|
||||
|
||||
%%
|
||||
xmlel_delete(NodeId, RedirectURI) ->
|
||||
xmlel('pubsub#event', <<"delete">>,
|
||||
[xmlattr_node(NodeId)],
|
||||
case RedirectURI of
|
||||
undefined -> [];
|
||||
_RedirectURI -> [xmlel_redirect(RedirectURI)]
|
||||
end).
|
||||
%%
|
||||
xmlel_event(Xmlels) ->
|
||||
xmlel('pubsub#event', <<"event">>, [], Xmlels).
|
||||
%%
|
||||
xmlel_feature('disco#info', Feature) ->
|
||||
xmlel('disco#info', <<"feature">>, [xmlattr_var(Feature)], []).
|
||||
%%
|
||||
xmlel_header(SubId) ->
|
||||
xmlel('shim', <<"header">>, [xmlattr_name(<<"SubID">>)], [xmlcdata(SubId)]).
|
||||
%%
|
||||
xmlel_headers(Xmlels_Header) ->
|
||||
xmlel('shim', <<"headers">>, [], Xmlels_Header).
|
||||
|
||||
|
||||
xmlel_identity('disco#info', Category, Type, Name) ->
|
||||
xmlel('disco#info', <<"identity">>,
|
||||
case Name of
|
||||
undefined ->
|
||||
[xmlattr_category(Category),
|
||||
xmlattr_type(Type)];
|
||||
_ ->
|
||||
[xmlattr_category(Category),
|
||||
xmlattr_name(Name),
|
||||
xmlattr_type(Type)]
|
||||
end,
|
||||
[]).
|
||||
|
||||
%%
|
||||
xmlel_item('pubsub', ItemId) ->
|
||||
xmlel('pubsub', <<"item">>, [xmlattr_id(ItemId)], []).
|
||||
|
||||
%%
|
||||
xmlel_item('disco#items', ItemId, Jid) ->
|
||||
xmlel('disco#items', <<"item">>,
|
||||
[xmlattr_name(ItemId),
|
||||
xmlattr_jid(Jid)],
|
||||
[]);
|
||||
xmlel_item('pubsub', ItemId, Payload) ->
|
||||
xmlel('pubsub', <<"item">>, [xmlattr_id(ItemId)],
|
||||
case Payload of
|
||||
[] -> [];
|
||||
_Payload -> [Payload]
|
||||
end).
|
||||
|
||||
xmlel_item('disco#items', NodeId, Jid, Name) ->
|
||||
xmlel('disco#items', <<"item">>,
|
||||
case Name of
|
||||
undefined ->
|
||||
[xmlattr_jid(Jid),
|
||||
xmlattr_node(NodeId)];
|
||||
_ ->
|
||||
[xmlattr_jid(Jid),
|
||||
xmlattr_node(NodeId),
|
||||
xmlattr_name(Name)]
|
||||
end,
|
||||
[]);
|
||||
%%
|
||||
xmlel_item('pubsub#event', ItemId, Publisher, Payload) ->
|
||||
xmlel('pubsub#event', <<"item">>,
|
||||
case Publisher of
|
||||
undefined ->
|
||||
[xmlattr_id(ItemId)];
|
||||
_Publisher ->
|
||||
[xmlattr_id(ItemId),
|
||||
xmlattr_publisher(Publisher)]
|
||||
end,
|
||||
case Payload of
|
||||
[] -> [];
|
||||
_Payload -> [Payload]
|
||||
end).
|
||||
%%
|
||||
xmlel_items('pubsub', NodeId, Xmlels) ->
|
||||
xmlel('pubsub', <<"items">>, [xmlattr_node(NodeId)], Xmlels);
|
||||
%%
|
||||
xmlel_items('pubsub#event', undefined = _NodeId, Xmlels) ->
|
||||
xmlel('pubsub#event', <<"items">>, [], Xmlels);
|
||||
%%
|
||||
xmlel_items('pubsub#event', NodeId, Xmlels) ->
|
||||
xmlel('pubsub#event', <<"items">>, [xmlattr_node(NodeId)], Xmlels).
|
||||
%%
|
||||
xmlel_items('pubsub#event', NodeId, undefined = _ItemId, _Publisher, _Payload) ->
|
||||
xmlel('pubsub#event', <<"items">>, [xmlattr_node(NodeId)], []);
|
||||
xmlel_items('pubsub#event', NodeId, ItemId, Publisher, Payload) ->
|
||||
xmlel('pubsub#event', <<"items">>,
|
||||
[xmlattr_node(NodeId)],
|
||||
[xmlel_item('pubsub#event', ItemId, Publisher, Payload)]).
|
||||
%%
|
||||
xmlel_message(undefined = _Type, Xmlels) ->
|
||||
xmlel_message('normal', Xmlels);
|
||||
xmlel_message(Type, Xmlels) ->
|
||||
xmlel('jabber:client', <<"message">>,
|
||||
[xmlattr_type(Type),
|
||||
xmlattr_id(_Id = id())],
|
||||
Xmlels).
|
||||
%%
|
||||
xmlel_options('pubsub', NodeId, Jid, SubId, Xmlels) ->
|
||||
xmlel('pubsub', <<"options">>,
|
||||
case SubId of
|
||||
undefined ->
|
||||
[xmlattr_node(NodeId),
|
||||
xmlattr_jid(Jid)];
|
||||
_ ->
|
||||
[xmlattr_node(NodeId),
|
||||
xmlattr_jid(Jid),
|
||||
xmlattr_subid(SubId)]
|
||||
end,
|
||||
Xmlels).
|
||||
%%
|
||||
xmlel_publish('pubsub', NodeId, Xmlels) ->
|
||||
xmlel('pubsub', <<"publish">>, [xmlattr_node(NodeId)], Xmlels).
|
||||
%%
|
||||
xmlel_purge(NodeId) ->
|
||||
xmlel('pubsub#event', <<"purge">>, [xmlattr_node(NodeId)], []).
|
||||
|
||||
%%
|
||||
xmlel_query('disco#info', Xmlels) ->
|
||||
xmlel('disco#info', <<"query">>, [], Xmlels);
|
||||
%%
|
||||
xmlel_query('disco#items', Xmlels) ->
|
||||
xmlel('disco#items', <<"query">>, [], Xmlels).
|
||||
|
||||
%%
|
||||
xmlel_query('disco#info', NodeId, Xmlels) ->
|
||||
xmlel('disco#info', <<"query">>, [xmlattr_node(NodeId)], Xmlels);
|
||||
xmlel_query('disco#items', NodeId, Xmlels) ->
|
||||
xmlel('disco#items', 'query', [xmlattr_node(NodeId)], Xmlels).
|
||||
|
||||
%%
|
||||
xmlel_redirect(RedirectURI) ->
|
||||
xmlel('pubsub#event', <<"redirect">>, [xmlattr_uri(RedirectURI)], []).
|
||||
%%
|
||||
xmlel_retract(undefined = _ItemId) ->
|
||||
xmlel('pubsub#event', <<"retract">>, [], []);
|
||||
xmlel_retract(ItemId) ->
|
||||
xmlel('pubsub#event', <<"retract">>, [xmlattr_id(ItemId)], []).
|
||||
%%
|
||||
xmlel_subscription('pubsub', NodeId, Jid, SubId, Subscription_State) ->
|
||||
xmlel('pubsub', <<"subscription">>,
|
||||
[xmlattr_node(NodeId),
|
||||
xmlattr_jid(Jid),
|
||||
xmlattr_subid(SubId),
|
||||
xmlattr_subscription(Subscription_State)],
|
||||
[]);
|
||||
xmlel_subscription('pubsub#event', NodeId, Jid, SubId, Subscription_State) ->
|
||||
xmlel('pubsub#event', <<"subscription">>,
|
||||
case SubId of
|
||||
undefined ->
|
||||
[xmlattr_node(NodeId),
|
||||
xmlattr_jid(Jid),
|
||||
xmlattr_subscription(Subscription_State)];
|
||||
_SubId ->
|
||||
[xmlattr_node(NodeId),
|
||||
xmlattr_jid(Jid),
|
||||
xmlattr_subid(SubId),
|
||||
xmlattr_subscription(Subscription_State)]
|
||||
end,
|
||||
[]).
|
||||
%%
|
||||
xmlel_subscription('pubsub#owner', Jid, SubId, Subscription_State) ->
|
||||
xmlel('pubsub#owner', <<"subscription">>,
|
||||
case SubId of
|
||||
undefined ->
|
||||
[xmlattr_jid(Jid),
|
||||
xmlattr_subscription(Subscription_State)];
|
||||
_SubId ->
|
||||
[xmlattr_jid(Jid),
|
||||
xmlattr_subid(SubId),
|
||||
xmlattr_subscription(Subscription_State)]
|
||||
end,
|
||||
[]).
|
||||
%%
|
||||
xmlel_subscriptions('pubsub', Xmlels) ->
|
||||
xmlel('pubsub', <<"subscriptions">>, [], Xmlels).
|
||||
|
||||
xmlel_subscriptions('pubsub#owner', NodeId, Xmlels) ->
|
||||
xmlel('pubsub#owner', <<"subscriptions">>, [xmlattr_node(NodeId)], Xmlels).
|
||||
%%
|
||||
xmlel_publish(NodeId, undefined = _ItemId) ->
|
||||
xmlel('pubsub', <<"publish">>, [xmlattr_node(NodeId)], []);
|
||||
xmlel_publish(NodeId, ItemId) ->
|
||||
xmlel('pubsub', <<"publish">>,
|
||||
[xmlattr_node(NodeId)],
|
||||
[xmlel_item('pubsub', ItemId)]).
|
||||
%%
|
||||
xmlel_pubsub('pubsub', Children) ->
|
||||
xmlel('pubsub', <<"pubsub">>, [], Children);
|
||||
%%
|
||||
xmlel_pubsub('pubsub#owner', Children) ->
|
||||
xmlel('pubsub#owner', <<"pubsub">>, [], Children).
|
||||
|
||||
@@ -0,0 +1,863 @@
|
||||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%% @copyright 2006-2013 ProcessOne
|
||||
%%% @author Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @author Karim Gemayel <karim.gemayel@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%%% @headerfile "pubsub_dev.hrl"
|
||||
|
||||
-module(mod_pubsub_dev).
|
||||
-author('christophe.romain@process-one.net').
|
||||
-author('karim.gemayel@process-one.net').
|
||||
-version('1.13-0').
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-include("pubsub_dev.hrl").
|
||||
-include("pubsub_api.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
%%% Export Functions
|
||||
|
||||
%% API and gen_server callbacks
|
||||
-export([
|
||||
start_link/2,
|
||||
start/2,
|
||||
stop/1,
|
||||
init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3
|
||||
]).
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
resource_show/0,
|
||||
presence_cache/0
|
||||
]).
|
||||
|
||||
-type(resource_show()
|
||||
:: {Resource :: xmpp_jid:resource_jid(),
|
||||
Show :: 'away' | 'chat' | 'dnd' | 'online' | 'xa' }
|
||||
).
|
||||
|
||||
-type(presence_cache() :: [Resource_Show::resource_show()]).
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
resource_subids/0,
|
||||
resources_subids/0
|
||||
]).
|
||||
|
||||
-type(resource_subids()
|
||||
:: {Resource :: xmpp_jid:resource_jid(),
|
||||
SubIds :: [SubId::exmpp_pubsub:subId(),...]}
|
||||
).
|
||||
|
||||
-type(resources_subids() :: [Resource_SubIds::resource_subids(),...]).
|
||||
|
||||
%%%
|
||||
-export_type([
|
||||
entity/0,
|
||||
n0de/0,
|
||||
cache/0,
|
||||
subids/0,
|
||||
event/0
|
||||
]).
|
||||
|
||||
-type(entity() :: #entity{}).
|
||||
% id :: xmpp_jid:usr_bare(),
|
||||
% affiliation :: 'member' | 'owner' | 'publisher',
|
||||
% subscriptions :: []%[exmpp_pubsub:subscription(),...]
|
||||
%}).
|
||||
|
||||
%%%
|
||||
-type(n0de() :: #node{}).
|
||||
% id :: exmpp_pubsub:nodeId(),
|
||||
% owners :: [Node_Owner::xmpp_jid:usr_bare(),...],
|
||||
% access_model :: pubsub_options:access_model(),
|
||||
% itemreply :: 'owner' | 'publisher',
|
||||
% notification_type :: 'headline' | 'normal',
|
||||
% presence_based_delivery :: boolean(),
|
||||
% rosters_groups_allowed :: [] | pubsub_options:rosters_groups_allowed()
|
||||
%}).
|
||||
|
||||
%%%
|
||||
-type(cache() :: #cache{}).
|
||||
% presence :: undefined | mod_pubsub_dev:presence_cache(),
|
||||
% presence_subscriptions :: undefined | boolean(),
|
||||
% rosters_groups :: undefined | boolean()
|
||||
%}).
|
||||
|
||||
%%%
|
||||
-type(subids() :: #subids{}).
|
||||
% presence :: undefined | [] | mod_pubsub_dev:resources_subids(),
|
||||
% no_presence :: undefined | [] | mod_pubsub_dev:resources_subids()
|
||||
%}).
|
||||
|
||||
%%
|
||||
-type(event() :: #event{
|
||||
% host :: xmpp_jid:raw_jid_component_bare(),
|
||||
% component :: xmpp_jid:component_bare(),
|
||||
% entity :: mod_pubsub_dev:entity(),
|
||||
% node :: mod_pubsub_dev:n0de(),
|
||||
% cache :: mod_pubsub_dev:cache(),
|
||||
% subids :: mod_pubsub_dev:subids()
|
||||
}).
|
||||
|
||||
%%%
|
||||
-export_type([
|
||||
item/0,
|
||||
published_item/0,
|
||||
retracted_item/0
|
||||
]).
|
||||
|
||||
-type(published_item() :: #item{
|
||||
access_model :: undefined | pubsub_options:access_model(),
|
||||
presence_based_delivery :: undefined | boolean(),
|
||||
rosters_groups_allowed :: [] | pubsub_options:rosters_groups_allowed(),
|
||||
stanza :: xmlel()
|
||||
}).
|
||||
|
||||
-type(retracted_item() :: #item{
|
||||
access_model :: undefined | pubsub_options:access_model(),
|
||||
presence_based_delivery :: undefined | boolean(),
|
||||
rosters_groups_allowed :: [] | pubsub_options:rosters_groups_allowed(),
|
||||
stanza :: xmlel()
|
||||
}).
|
||||
|
||||
-type(item() :: published_item() | retracted_item()).
|
||||
|
||||
|
||||
%%
|
||||
|
||||
-export_type([
|
||||
pubsub_itemId/0,
|
||||
pubsub_nodeId/0,
|
||||
pubsub_stateId/0,
|
||||
item_owners/0,
|
||||
node_owners/0,
|
||||
item_creation/0,
|
||||
item_modification/0,
|
||||
node_creation/0
|
||||
]).
|
||||
|
||||
-type(pubsub_itemId()
|
||||
:: {ItemId::exmpp_pubsub:itemId(), NodeIdx::exmpp_pubsub:nodeIdx()}
|
||||
).
|
||||
|
||||
-type(pubsub_nodeId()
|
||||
:: {Pubsub_Host :: exmpp_pubsub:host(), NodeId :: exmpp_pubsub:nodeId()}
|
||||
).
|
||||
|
||||
-type(pubsub_stateId()
|
||||
:: {Entity::xmpp_jid:usr_bare(), NodeIdx::exmpp_pubsub:nodeIdx()}
|
||||
).
|
||||
|
||||
-type(item_owners() :: [Entity::xmpp_jid:usr_bare(),...]).
|
||||
-type(node_owners() :: [Entity::xmpp_jid:usr_bare(),...]).
|
||||
|
||||
-type(item_creation()
|
||||
:: {DateTime::erlang:timestamp(), Entity::xmpp_jid:usr_entity()}
|
||||
).
|
||||
|
||||
-type(item_modification()
|
||||
:: {DateTime::erlang:timestamp(), Entity::xmpp_jid:usr_entity()}
|
||||
).
|
||||
|
||||
-type(node_creation()
|
||||
:: {DateTime::erlang:timestamp(), Entity::xmpp_jid:usr_entity()}
|
||||
).
|
||||
|
||||
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
pubsub_state/0,
|
||||
pubsub_states/0,
|
||||
%
|
||||
pubsub_state_owner/0,
|
||||
pubsub_state_publisher/0,
|
||||
pubsub_state_publish_only/0,
|
||||
pubsub_state_member/0,
|
||||
pubsub_state_none/0,
|
||||
pubsub_state_outcast/0
|
||||
]).
|
||||
|
||||
-type(pubsub_state_owner()
|
||||
:: #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'owner',
|
||||
access :: undefined | 'presence' | 'roster',
|
||||
%subscriptions :: [exmpp_pubsub:subscription_subscribed() |
|
||||
% exmpp_pubsub:subscription_unconfigured()],
|
||||
subscriptions :: [exmpp_pubsub:subscription()],
|
||||
itemids :: [] | exmpp_pubsub:itemIds()
|
||||
}
|
||||
).
|
||||
|
||||
%%
|
||||
-type(pubsub_state_publisher()
|
||||
:: #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'publisher',
|
||||
access :: undefined | 'presence' | 'roster',
|
||||
%subscriptions :: [exmpp_pubsub:subscription_subscribed() |
|
||||
% exmpp_pubsub:subscription_unconfigured()],
|
||||
subscriptions :: [exmpp_pubsub:subscription()],
|
||||
itemids :: [] | exmpp_pubsub:itemIds()
|
||||
}
|
||||
).
|
||||
|
||||
%%
|
||||
-type(pubsub_state_publish_only()
|
||||
:: #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'publish-only',
|
||||
access :: undefined | 'presence' | 'roster',
|
||||
subscriptions :: [],
|
||||
itemids :: [] | exmpp_pubsub:itemIds()
|
||||
}
|
||||
).
|
||||
|
||||
%%
|
||||
-type(pubsub_state_member()
|
||||
:: #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'member',
|
||||
access :: undefined | 'pending' | 'presence' | 'roster',
|
||||
subscriptions :: [],
|
||||
itemids :: [] | exmpp_pubsub:itemIds()
|
||||
}
|
||||
%%
|
||||
| #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'member',
|
||||
access :: undefined | 'pending' | 'presence' | 'roster',
|
||||
%subscriptions :: [exmpp_pubsub:subscription_pending(),...],
|
||||
subscriptions :: [exmpp_pubsub:subscription()],
|
||||
itemids :: [] | exmpp_pubsub:itemIds()
|
||||
}
|
||||
%%
|
||||
| #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'member',
|
||||
subscriptions :: [exmpp_pubsub:subscription()],
|
||||
%subscriptions :: [exmpp_pubsub:subscription_pending() |
|
||||
% exmpp_pubsub:subscription_subscribed() |
|
||||
% exmpp_pubsub:subscription_unconfigured()],
|
||||
itemids :: [] | exmpp_pubsub:itemIds()
|
||||
}
|
||||
).
|
||||
|
||||
%%
|
||||
-type(pubsub_state_none()
|
||||
:: #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'none',
|
||||
access :: undefined | 'presence' | 'roster',
|
||||
subscriptions :: [],
|
||||
itemids :: []
|
||||
}
|
||||
%%
|
||||
| #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'none',
|
||||
access :: undefined | 'presence' | 'roster',
|
||||
subscriptions :: [],
|
||||
itemids :: exmpp_pubsub:itemIds()
|
||||
}
|
||||
%%
|
||||
| #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'none',
|
||||
access :: undefined | 'presence' | 'roster',
|
||||
subscriptions :: [],
|
||||
itemids :: exmpp_pubsub:itemIds()
|
||||
}
|
||||
).
|
||||
|
||||
%%
|
||||
-type(pubsub_state_outcast()
|
||||
:: #pubsub_state_dev{
|
||||
id :: mod_pubsub_dev:pubsub_stateId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
affiliation :: 'outcast',
|
||||
access :: undefined | 'presence' | 'roster',
|
||||
subscriptions :: [exmpp_pubsub:subscription()],
|
||||
%subscriptions :: [exmpp_pubsub:subscription_pending() |
|
||||
% exmpp_pubsub:subscription_subscribed() |
|
||||
% exmpp_pubsub:subscription_unconfigured()],
|
||||
itemids :: [] | exmpp_pubsub:itemIds()
|
||||
}
|
||||
).
|
||||
|
||||
|
||||
%%
|
||||
-type(pubsub_state() :: mod_pubsub_dev:pubsub_state_owner()
|
||||
| mod_pubsub_dev:pubsub_state_publisher()
|
||||
| mod_pubsub_dev:pubsub_state_publish_only()
|
||||
| mod_pubsub_dev:pubsub_state_member()
|
||||
| mod_pubsub_dev:pubsub_state_none()
|
||||
| mod_pubsub_dev:pubsub_state_outcast()
|
||||
).
|
||||
|
||||
-type(pubsub_states() :: [Pubsub_State::mod_pubsub_dev:pubsub_state(),...]).
|
||||
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
pubsub_node/0,
|
||||
pubsub_nodes/0
|
||||
]).
|
||||
|
||||
-type(pubsub_node()
|
||||
:: #pubsub_node_dev{
|
||||
id :: mod_pubsub_dev:pubsub_nodeId(),
|
||||
idx :: exmpp_pubsub:nodeIdx(),
|
||||
creation :: mod_pubsub_dev:node_creation(),
|
||||
level :: exmpp_pubsub:level(),
|
||||
owners :: mod_pubsub_dev:node_owners(),
|
||||
itemids :: [] | exmpp_pubsub:itemIds(),
|
||||
options :: pubsub_options:options_node()
|
||||
}
|
||||
).
|
||||
|
||||
-type(pubsub_nodes() :: [Pubsub_Node::mod_pubsub_dev:pubsub_node(),...]).
|
||||
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
pubsub_item/0,
|
||||
pubsub_items/0
|
||||
]).
|
||||
|
||||
-type(pubsub_item()
|
||||
:: #pubsub_item_dev{
|
||||
id :: mod_pubsub_dev:pubsub_itemId(),
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
owners :: mod_pubsub_dev:item_owners(),
|
||||
creation :: mod_pubsub_dev:item_creation(),
|
||||
modification :: mod_pubsub_dev:item_modification(),
|
||||
payload :: exmpp_pubsub:payload(),
|
||||
options :: [] | pubsub_options:options_item()
|
||||
}
|
||||
).
|
||||
|
||||
-type(pubsub_items() :: [Pubsub_Item::mod_pubsub_dev:pubsub_item(),...]).
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
pubsub_last_item/0
|
||||
]).
|
||||
|
||||
-type(pubsub_last_item()
|
||||
:: #pubsub_last_item_dev{
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
id :: exmpp_pubsub:itemId(),
|
||||
owners :: mod_pubsub_dev:item_owners(),
|
||||
creation :: mod_pubsub_dev:item_creation(),
|
||||
payload :: exmpp_pubsub:payload(),
|
||||
options :: [] | pubsub_options:options_item()
|
||||
}
|
||||
).
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
pubsub_subscription_pending/0
|
||||
]).
|
||||
|
||||
-type(pubsub_subscription_pending()
|
||||
:: #pubsub_subscription_pending{
|
||||
id :: {Entity::xmpp_jid:usr_bare(), NodeIdx::exmpp_pubsub:nodeIdx()},
|
||||
nodeidx :: exmpp_pubsub:nodeIdx(),
|
||||
subids :: [SubId::exmpp_pubsub:subId(),...]
|
||||
}
|
||||
).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_pubsub_dev).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-record(state,
|
||||
{
|
||||
host :: xmpp_jid:raw_jid_component_bare(),
|
||||
pubsub_host :: xmpp_jid:raw_jid_component_bare(),
|
||||
pubsub :: #capabilities{},
|
||||
pubsub_on_jid :: #capabilities{}
|
||||
}).
|
||||
|
||||
-type(state()
|
||||
:: #state{
|
||||
host :: xmpp_jid:raw_jid_component_bare(),
|
||||
pubsub_host :: xmpp_jid:raw_jid_component_bare(),
|
||||
pubsub :: #capabilities{},
|
||||
pubsub_on_jid :: #capabilities{}
|
||||
}
|
||||
).
|
||||
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @spec start_link(Host, Options) -> {'ok', pid()} | 'ignore' | {'error', atom()}
|
||||
%% Host = string()
|
||||
%% Options = {Option::atom(), Value::term()}
|
||||
-spec(start_link/2 ::
|
||||
(
|
||||
Host :: string(),
|
||||
Options :: [Option::{Key::atom(), Value::term()}])
|
||||
-> {'ok', pid()} | 'ignore' | {'error', _}
|
||||
).
|
||||
|
||||
start_link(Host, Options) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:start_link({local, Proc}, ?MODULE, [Host, Options], []).
|
||||
|
||||
%% @spec start(Host, Options) -> {'error',atom()} | {'ok','undefined' | pid()} | {'ok','undefined' | pid(),any()}
|
||||
%% Host = string()
|
||||
%% Options = {Option::atom(), Value::term()}
|
||||
-spec(start/2 ::
|
||||
(
|
||||
Host :: string(),
|
||||
Options :: [Option::{Key::atom(), Value::term()}])
|
||||
-> {'error',_} | {'ok','undefined' | pid()} | {'ok','undefined' | pid(),_}
|
||||
).
|
||||
|
||||
start(Host, Options) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
ChildSpec = {Proc,
|
||||
{?MODULE, start_link, [Host, Options]},
|
||||
transient, 1000, worker, [?MODULE]},
|
||||
supervisor:start_child(ejabberd_sup, ChildSpec).
|
||||
|
||||
%% @spec stop(Host) -> 'ok' | {'error','not_found' | 'running' | 'simple_one_for_one'}
|
||||
%% Host = string()
|
||||
-spec(stop/1 ::
|
||||
(
|
||||
Host :: string())
|
||||
-> 'ok' | {'error','not_found' | 'running' | 'simple_one_for_one'}
|
||||
).
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:call(Proc, stop),
|
||||
supervisor:delete_child(ejabberd_sup, Proc).
|
||||
|
||||
|
||||
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% Description: Initiates the server
|
||||
%%--------------------------------------------------------------------
|
||||
-spec(init/1 ::
|
||||
(
|
||||
Args :: [binary() | [{Option::atom(), Value::term()}]])
|
||||
-> {'ok', State::state()}
|
||||
).
|
||||
|
||||
init([Host, Options] = _Args) ->
|
||||
?DEBUG("pubsub init ~p ~p",[Host, Options]),
|
||||
Pubsub_Host = gen_mod:expand_host_name(Host, Options, <<"pubsub">>),
|
||||
IQDisc = gen_mod:get_opt('iqdisc', Options, 'one_queue'),
|
||||
|
||||
% mod_disco:register_feature(Server_HostB, ?NS_PUBSUB_s),
|
||||
%%
|
||||
%% Pubsub Hooks
|
||||
%% - Service disco hooks
|
||||
%% * disco_local_identity
|
||||
%% * disco_local_features
|
||||
%% * disco_local_items
|
||||
%% * disco_local_forms
|
||||
%% - Connectivity hooks
|
||||
%% * presence_probe_hook
|
||||
%% * sm_remove_connection_hook
|
||||
%% - Roster hooks
|
||||
%% * roster_in_subscription
|
||||
%% * roster_out_subscription
|
||||
%% - User managment hooks
|
||||
%% * remove_user
|
||||
%% * anonymous_purge_hook
|
||||
%%
|
||||
%% Pubsub On Jid hooks
|
||||
%% - Service disco hooks
|
||||
%% * disco_sm_identity
|
||||
%% * disco_sm_features
|
||||
%% * disco_sm_items
|
||||
%% * disco_sm_forms
|
||||
%% - Connectitivy
|
||||
%% * caps_update
|
||||
|
||||
|
||||
Pubsub_Features = node_flat_dev:pubsub_features(),
|
||||
% mod_disco:register_feature(Server_HostB, ?NS_PUBSUB_s),
|
||||
pubsub_db:init('mnesia', 'dev'),
|
||||
%% Register hooks according to available Pubsub Features
|
||||
register_hooks(Host, Pubsub_Features, pubsub_hooks),
|
||||
%%
|
||||
ejabberd_router:register_route(Pubsub_Host),
|
||||
{ok,
|
||||
#state{
|
||||
host = Host,
|
||||
pubsub_host = Pubsub_Host,
|
||||
pubsub = node_flat_dev:capabilities()
|
||||
}
|
||||
}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% 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
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
|
||||
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
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
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
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
|
||||
handle_info({route, From, #jid{resource = undefined} = To, Stanza},
|
||||
#state{host = Host, pubsub = Capabilities} = State) ->
|
||||
do_route(Host, To, From, Stanza, Capabilities),
|
||||
{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.
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @todo : -spec
|
||||
-spec(terminate/2 ::
|
||||
(
|
||||
Reason :: _,
|
||||
State :: state())
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
terminate(_Reason, #state{host = Host, pubsub_host = Pubsub_Host}) ->
|
||||
ejabberd_router:unregister_route(Pubsub_Host),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% Description: Convert process state when code is changed
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
|
||||
%%
|
||||
-define(Is_Stanza_IQ(Stanza),
|
||||
(
|
||||
Stanza#xmlel.name == <<"iq">>
|
||||
)).
|
||||
|
||||
-define(Is_Stanza_Message(Stanza),
|
||||
(
|
||||
Stanza#xmlel.name == <<"message">>
|
||||
)).
|
||||
|
||||
-define(Is_Stanza_Presence(Stanza),
|
||||
(
|
||||
Stanza#xmlel.name == <<"presence">>
|
||||
)).
|
||||
|
||||
-define(Is_IQ_Ignore(IQ),
|
||||
(
|
||||
IQ#iq.type == 'result'
|
||||
orelse
|
||||
IQ#iq.type == 'error'
|
||||
)).
|
||||
|
||||
-define(Is_IQ_Pubsub(IQ),
|
||||
(
|
||||
(IQ#iq.type == 'set'
|
||||
orelse
|
||||
IQ#iq.type == 'get')
|
||||
andalso
|
||||
(IQ#iq.xmlns == ?NS_PUBSUB
|
||||
orelse
|
||||
IQ#iq.xmlns == ?NS_PUBSUB_OWNER)
|
||||
andalso
|
||||
IQ#iq.sub_el#xmlel.name == <<"pubsub">>
|
||||
)).
|
||||
|
||||
-define(Is_IQ_Get_Disco_Info(IQ),
|
||||
(
|
||||
IQ#iq.type == 'get'
|
||||
andalso
|
||||
IQ#iq.xmlns == ?NS_DISCO_INFO
|
||||
andalso
|
||||
IQ#iq.sub_el#xmlel.name == <<"query">>
|
||||
% andalso
|
||||
% IQ#iq.payload#xmlel.children == []
|
||||
)).
|
||||
|
||||
-define(Is_IQ_Get_Disco_Items(IQ),
|
||||
(
|
||||
IQ#iq.type == 'get'
|
||||
andalso
|
||||
IQ#iq.xmlns == ?NS_DISCO_ITEMS
|
||||
andalso
|
||||
IQ#iq.sub_el#xmlel.name == <<"query">>
|
||||
% andalso
|
||||
% IQ#iq.payload#xmlel.children == []
|
||||
)).
|
||||
|
||||
%%
|
||||
do_route(Host,
|
||||
#jid{lserver = Pubsub_Host} = Pubsub_Component,
|
||||
#jid{luser = U, lserver = S, lresource = R} = Entity, Stanza_IQ,
|
||||
#capabilities{privacy = Privacy, plugin = Plugin} = Capabilities)
|
||||
when ?Is_Stanza_IQ(Stanza_IQ) ->
|
||||
case IQ = jlib:iq_query_or_response_info(Stanza_IQ) of
|
||||
IQ when ?Is_IQ_Ignore(IQ) ->
|
||||
ok;
|
||||
IQ when ?Is_IQ_Get_Disco_Info(IQ) ->
|
||||
case
|
||||
pubsub_disco:iq_disco_info('dev', Host, Pubsub_Host,
|
||||
Privacy, {U,S,undefined},
|
||||
case S of
|
||||
Host -> 'local';
|
||||
_ -> 'remote'
|
||||
end,
|
||||
Plugin,
|
||||
case xml:get_tag_attr_s(<<"node">>, IQ#iq.sub_el) of
|
||||
<<>> -> undefined;
|
||||
NodeId -> NodeId
|
||||
end,
|
||||
xml:remove_cdata(IQ#iq.sub_el#xmlel.children))
|
||||
of
|
||||
{result, Xmlel_Query} ->
|
||||
ejabberd_router:route(Pubsub_Component, Entity,
|
||||
jlib:iq_to_xml(IQ#iq{
|
||||
type = result,
|
||||
sub_el = [Xmlel_Query]
|
||||
}));
|
||||
{error, Error} ->
|
||||
ejabberd_router:route(Pubsub_Component, Entity,
|
||||
jlib:make_error_reply(Stanza_IQ, Error))
|
||||
end;
|
||||
IQ when ?Is_IQ_Get_Disco_Items(IQ) ->
|
||||
case
|
||||
pubsub_disco:iq_disco_items('dev', Host, Pubsub_Host,
|
||||
Privacy, {U,S,undefined},
|
||||
case S of
|
||||
Host -> 'local';
|
||||
_ -> 'remote'
|
||||
end,
|
||||
Plugin,
|
||||
case xml:get_tag_attr_s(<<"node">>, IQ#iq.sub_el) of
|
||||
<<>> -> undefined;
|
||||
NodeId -> NodeId
|
||||
end,
|
||||
xml:remove_cdata(IQ#iq.sub_el#xmlel.children))
|
||||
of
|
||||
{result, Xmlel_Query} ->
|
||||
ejabberd_router:route(Pubsub_Component, Entity,
|
||||
jlib:iq_to_xml(IQ#iq{
|
||||
type = result,
|
||||
sub_el = [Xmlel_Query]
|
||||
}));
|
||||
{error, Error} ->
|
||||
ejabberd_router:route(Pubsub_Component, Entity,
|
||||
jlib:make_error_reply(Stanza_IQ, Error))
|
||||
end;
|
||||
IQ when ?Is_IQ_Pubsub(IQ) ->
|
||||
case
|
||||
iq_pubsub(Host, Pubsub_Host, {U,S,R}, IQ#iq.type,
|
||||
_Rsm = lists:member(?NS_RSM, Plugin:features()),
|
||||
xml:remove_cdata(IQ#iq.sub_el#xmlel.children),
|
||||
IQ#iq.lang, Capabilities)
|
||||
of
|
||||
{result, []} ->
|
||||
ejabberd_router:route(Pubsub_Component, Entity,
|
||||
jlib:iq_to_xml(IQ#iq{
|
||||
type = result,
|
||||
sub_el = []
|
||||
}));
|
||||
{result, Xmlels} ->
|
||||
ejabberd_router:route(Pubsub_Component, Entity,
|
||||
jlib:iq_to_xml(IQ#iq{
|
||||
type = result,
|
||||
sub_el = Xmlels
|
||||
}));
|
||||
{error, Error} ->
|
||||
ejabberd_router:route(Pubsub_Component, Entity,
|
||||
jlib:make_error_reply(Stanza_IQ, Error))
|
||||
end
|
||||
end;
|
||||
do_route(Host,
|
||||
#jid{lserver = Pubsub_Host} = Pubsub_Component,
|
||||
#jid{luser = U, lserver = S, lresource = R} = Entity, Stanza_Message,
|
||||
Capabilities)
|
||||
when ?Is_Stanza_Message(Stanza_Message) ->
|
||||
ok;
|
||||
do_route(Host,
|
||||
#jid{lserver = Pubsub_Host} = Pubsub_Component,
|
||||
#jid{luser = U, lserver = S, lresource = R} = Entity, Stanza_Presence,
|
||||
Capabilities)
|
||||
when ?Is_Stanza_Presence(Stanza_Presence) ->
|
||||
ok.
|
||||
|
||||
|
||||
iq_pubsub(Host, Pubsub_Host, Entity, IQ_Type, Rsm, Xmlels, _Lang,
|
||||
#capabilities{api = #api{parser = Parser, core = API_Core}} = Capabilities) ->
|
||||
case Parser:parse(IQ_Type, Xmlels, Rsm, API_Core) of
|
||||
{result, Module, Function, Parameters} ->
|
||||
Module:Function(Host, Pubsub_Host, Entity, Parameters, Capabilities);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
|
||||
|
||||
register_hooks(Host, Pubsub_Features, Module) ->
|
||||
register_hooks('roster', Host, Pubsub_Features, Module),
|
||||
register_hooks('presence', Host, Pubsub_Features, Module).
|
||||
|
||||
%ejabberd_hooks:add(roster_in_subscription, Host, pubsub_hooks, roster_in_subscription, 50),
|
||||
%ejabberd_hooks:add(roster_out_subscription, Host, ?MODULE, roster_out_subscription, 50),
|
||||
%ejabberd_hooks:add(roster_get, Host, ?MODULE, roster_get, 50),
|
||||
%ejabberd_hooks:add(roster_get_jid_info, Host, ?MODULE, roster_get_jid_info, 50),
|
||||
%ejabberd_hooks:add(roster_get_subscription_lists, Host, ?MODULE, roster_get_subscription_lists, 50),
|
||||
%%ejabberd_hooks:add(roster_process_item, Host, pubsub_hooks, roster_process_item, 50).
|
||||
|
||||
|
||||
register_hooks('roster', Host, Pubsub_Features, Module) ->
|
||||
case lists:member("access-roster", Pubsub_Features) of
|
||||
true ->
|
||||
ejabberd_hooks:add(roster_process_item, Host, Module,
|
||||
roster_process_item, 50),
|
||||
ok;
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
%%
|
||||
register_hooks('presence', Host, Pubsub_Features, Module) ->
|
||||
case
|
||||
lists:member(<<"access-presence">>, Pubsub_Features)
|
||||
orelse
|
||||
lists:member(<<"last-published">>, Pubsub_Features)
|
||||
orelse
|
||||
lists:member(<<"leased-subscription">>, Pubsub_Features)
|
||||
orelse
|
||||
lists:member(<<"presence-notifications">>, Pubsub_Features)
|
||||
of
|
||||
true ->
|
||||
ejabberd_hooks:add(presence_probe_hook, Host, Module,
|
||||
presence_online, 80),
|
||||
ejabberd_hooks:add(sm_remove_connection_hook, Host, Module,
|
||||
presence_offline, 80),
|
||||
ok;
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
roster_get(Acc, {User, Server}) ->
|
||||
?INFO_MSG("ROSTER_GET Acc ~p ~nUser ~p ~nServer ~p ~n",
|
||||
[Acc, User, Server]).
|
||||
|
||||
roster_get_jid_info(Acc, User, Server, JID) ->
|
||||
?INFO_MSG("ROSTER_GET_JID_INFO Acc ~p ~nUser ~p ~nServer ~p ~nJID ~p ~n",
|
||||
[Acc, User, Server, JID]).
|
||||
|
||||
roster_get_subscription_lists(Acc, User, Server) ->
|
||||
?INFO_MSG("ROSTER_GET_SUBSCRIPTION_LISTS Acc ~p ~nUser ~p ~nServer ~p ~n",
|
||||
[Acc, User, Server]).
|
||||
|
||||
roster_in_subscription(User, Server, JID, SubscriptionType, Reason, A) ->
|
||||
?INFO_MSG("ROSTER_IN_SUBSCRITION User ~p ~n Server ~p ~n JID ~p ~n SubscriptionType ~p ~n, Reason ~p ~nA ~p ~n",
|
||||
[User, Server, JID, SubscriptionType, Reason, A]).
|
||||
|
||||
roster_out_subscription(User, Server, JID, SubscriptionType) ->
|
||||
?INFO_MSG("ROSTER_OUT_SUBSCRITION User ~p ~n Server ~p ~n JID ~p ~n SubscriptionType ~p ~n",
|
||||
[User, Server, JID, SubscriptionType]).
|
||||
|
||||
roster_process_item(RosterItem, Server) ->
|
||||
?INFO_MSG("ROSTER_PROCESS_ITEM RosterItem ~p ~nServer ~p ~n",
|
||||
[RosterItem, Server]),
|
||||
RosterItem.
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%% @copyright 2006-2013 ProcessOne
|
||||
%%% @author Karim Gemayel <karim.gemayel@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%%% @headerfile "pubsub_dev.hrl"
|
||||
|
||||
-module(node_flat_dev).
|
||||
-author('karim.gemayel@process-one.net').
|
||||
|
||||
-include("pubsub_dev.hrl").
|
||||
-include("pubsub_api.hrl").
|
||||
|
||||
-compile(export_all).
|
||||
|
||||
|
||||
|
||||
%%
|
||||
-spec(capabilities/0 :: () -> #capabilities{}).
|
||||
|
||||
capabilities() ->
|
||||
#capabilities{plugin = ?MODULE}.
|
||||
|
||||
|
||||
%%
|
||||
-spec(pubsub_features/0 :: () -> Pubsub_Features::exmpp_pubsub:pubsub_features()).
|
||||
|
||||
pubsub_features() ->
|
||||
[<<"access-authorize">>,
|
||||
<<"access-open">>,
|
||||
<<"access-presence">>,
|
||||
<<"access-roster">>,
|
||||
<<"access-whitelist">>,
|
||||
<<"auto-create">>,
|
||||
<<"auto-subscribe">>,
|
||||
%<<"collections">>,
|
||||
<<"config-node">>,
|
||||
<<"create-and-configure">>,
|
||||
<<"create-nodes">>,
|
||||
<<"delete-items">>,
|
||||
<<"delete-nodes">>,
|
||||
%<<"filtered-notifications">>,
|
||||
<<"get-pending">>,
|
||||
<<"instant-nodes">>,
|
||||
<<"item-ids">>,
|
||||
<<"last-published">>,
|
||||
<<"leased-subscription">>,
|
||||
<<"manage-subscriptions">>,
|
||||
<<"member-affiliation">>,
|
||||
<<"meta-data">>,
|
||||
<<"modify-affiliations">>,
|
||||
%<<"multi-collections">>,
|
||||
<<"multi-subscribe">>,
|
||||
<<"outcast-affiliation">>,
|
||||
<<"persistent-items">>,
|
||||
<<"presence-notifications">>,
|
||||
%"presence-subscribe">>,
|
||||
<<"publish">>,
|
||||
<<"publish-only-affiliation">>,
|
||||
<<"publish-options">>,
|
||||
<<"publisher-affiliation">>,
|
||||
<<"purge-nodes">>,
|
||||
<<"retract-items">>,
|
||||
<<"retrieve-affiliations">>,
|
||||
<<"retrieve-default">>,
|
||||
<<"retrieve-default-sub">>,
|
||||
<<"retrieve-items">>,
|
||||
<<"retrieve-subscriptions">>,
|
||||
<<"subscribe">>,
|
||||
<<"subscription-notifications">>,
|
||||
<<"subscription-options">>].
|
||||
|
||||
%%
|
||||
-spec(pubsub_features/1 ::
|
||||
(
|
||||
Entity_Type :: 'local' | 'remote')
|
||||
-> Pubsub_Features::exmpp_pubsub:pubsub_features()
|
||||
).
|
||||
|
||||
pubsub_features('local') ->
|
||||
[<<"access-authorize">>,
|
||||
<<"access-open">>,
|
||||
<<"access-presence">>,
|
||||
<<"access-roster">>,
|
||||
<<"access-whitelist">>,
|
||||
<<"auto-create">>,
|
||||
<<"auto-subscribe">>,
|
||||
%<<"collections">>,
|
||||
<<"config-node">>,
|
||||
<<"create-and-configure">>,
|
||||
<<"create-nodes">>,
|
||||
<<"delete-items">>,
|
||||
<<"delete-nodes">>,
|
||||
%<<"filtered-notifications">>,
|
||||
<<"get-pending">>,
|
||||
<<"instant-nodes">>,
|
||||
<<"item-ids">>,
|
||||
<<"last-published">>,
|
||||
<<"leased-subscription">>,
|
||||
<<"manage-subscriptions">>,
|
||||
<<"member-affiliation">>,
|
||||
<<"meta-data">>,
|
||||
<<"modify-affiliations">>,
|
||||
%<<"multi-collections">>,
|
||||
<<"multi-subscribe">>,
|
||||
<<"outcast-affiliation">>,
|
||||
<<"persistent-items">>,
|
||||
<<"presence-notifications">>,
|
||||
%<<"presence-subscribe">>,
|
||||
<<"publish">>,
|
||||
<<"publish-only-affiliation">>,
|
||||
<<"publish-options">>,
|
||||
<<"publisher-affiliation">>,
|
||||
<<"purge-nodes">>,
|
||||
<<"retract-items">>,
|
||||
<<"retrieve-affiliations">>,
|
||||
<<"retrieve-default">>,
|
||||
<<"retrieve-default-sub">>,
|
||||
<<"retrieve-items">>,
|
||||
<<"retrieve-subscriptions">>,
|
||||
<<"subscribe">>,
|
||||
<<"subscription-notifications">>,
|
||||
<<"subscription-options">>];
|
||||
%%
|
||||
pubsub_features('remote') ->
|
||||
[<<"access-authorize">>,
|
||||
<<"access-open">>,
|
||||
<<"access-presence">>,
|
||||
<<"access-roster">>,
|
||||
<<"access-whitelist">>,
|
||||
<<"auto-create">>,
|
||||
<<"auto-subscribe">>,
|
||||
%<<"collections">>,
|
||||
<<"config-node">>,
|
||||
<<"create-and-configure">>,
|
||||
<<"create-nodes">>,
|
||||
<<"delete-items">>,
|
||||
<<"delete-nodes">>,
|
||||
%<<"filtered-notifications">>,
|
||||
<<"get-pending">>,
|
||||
<<"instant-nodes">>,
|
||||
<<"item-ids">>,
|
||||
<<"last-published">>,
|
||||
%<<"leased-subscription">>,
|
||||
<<"manage-subscriptions">>,
|
||||
<<"member-affiliation">>,
|
||||
<<"meta-data">>,
|
||||
<<"modify-affiliations">>,
|
||||
%<<"multi-collections">>,
|
||||
<<"multi-subscribe">>,
|
||||
<<"outcast-affiliation">>,
|
||||
<<"persistent-items">>,
|
||||
<<"publish">>,
|
||||
<<"publish-only-affiliation">>,
|
||||
<<"publish-options">>,
|
||||
<<"publisher-affiliation">>,
|
||||
<<"purge-nodes">>,
|
||||
<<"retract-items">>,
|
||||
<<"retrieve-affiliations">>,
|
||||
<<"retrieve-default">>,
|
||||
<<"retrieve-default-sub">>,
|
||||
<<"retrieve-items">>,
|
||||
<<"retrieve-subscriptions">>,
|
||||
<<"subscribe">>,
|
||||
<<"subscription-notifications">>,
|
||||
<<"subscription-options">>].
|
||||
|
||||
%%
|
||||
%%
|
||||
-spec(node_options/1 ::
|
||||
(
|
||||
Node_Type :: 'leaf' | 'collection')
|
||||
-> Node_Options :: pubsub_options:options_node_leaf()
|
||||
| pubsub_options:options_node_collection()
|
||||
).
|
||||
|
||||
node_options('leaf') ->
|
||||
[{'access_model', open},
|
||||
{'contact', []},
|
||||
%{'collection', []},
|
||||
{'deliver_notifications', true},
|
||||
{'deliver_payloads', true},
|
||||
{'description', undefined},
|
||||
{'item_expire', undefined},
|
||||
{'itemreply', publisher},
|
||||
{'language', <<"en">>},
|
||||
{'max_items', 2},
|
||||
{'max_payload_size', undefined},
|
||||
{'node_type', leaf},
|
||||
{'notification_type', normal},
|
||||
{'notify_config', true},
|
||||
{'notify_delete', true},
|
||||
{'notify_retract', true},
|
||||
{'notify_sub', true},
|
||||
{'persist_items', true},
|
||||
{'presence_based_delivery', true},
|
||||
{'publish_model', open},
|
||||
{'purge_offline', false},
|
||||
{'roster_groups_allowed', []},
|
||||
{'send_last_published_item', on_sub_and_presence},
|
||||
{'subscribe', true},
|
||||
{'tempsub', false},
|
||||
{'title', undefined},
|
||||
{'type', undefined}];
|
||||
%%
|
||||
node_options('collection') ->
|
||||
[].
|
||||
|
||||
item_options() ->
|
||||
[{'access_model', open},
|
||||
{'deliver_notifications', true},
|
||||
{'deliver_payloads', true},
|
||||
{'item_expire', undefined},
|
||||
{'itemreply', publisher},
|
||||
% {'max_payload_size', undefined},
|
||||
{'notification_type', normal},
|
||||
{'notify_config', true},
|
||||
{'notify_retract', true},
|
||||
{'persist_items', true},
|
||||
{'presence_based_delivery', true},
|
||||
% {'publish_model', open},
|
||||
{'purge_offline', false},
|
||||
{'roster_groups_allowed', []},
|
||||
{'send_last_published_item', on_sub_and_presence},
|
||||
{'type', undefined}].
|
||||
|
||||
%%
|
||||
-spec(subscription_options/2 ::
|
||||
(
|
||||
Entity_Type :: 'local' | 'remote',
|
||||
Node_Type :: 'leaf' | 'collection')
|
||||
-> Subscription_Options :: [] | pubsub_options:options_subscription()
|
||||
).
|
||||
|
||||
subscription_options('local', 'leaf') ->
|
||||
[];
|
||||
subscription_options('remote', 'leaf') ->
|
||||
[];
|
||||
subscription_options('local', 'collection') ->
|
||||
[];
|
||||
subscription_options('remote', 'collection') ->
|
||||
[].
|
||||
|
||||
%%
|
||||
-spec(default_subscription_options/2 ::
|
||||
(
|
||||
Entity_Type :: 'local' | 'remote',
|
||||
Node_Type :: 'leaf')%| 'collection')
|
||||
-> Subscription_Options :: pubsub_options:options_subscription_leaf()
|
||||
).
|
||||
|
||||
default_subscription_options('local', 'leaf') ->
|
||||
[{'deliver', true},
|
||||
{'expire', undefined},
|
||||
%{'include_body', undefined},
|
||||
{'show-values', ['away', 'chat', 'dnd', 'online', 'xa']}];
|
||||
%%
|
||||
default_subscription_options('remote', 'leaf') ->
|
||||
[{'deliver', true}
|
||||
%{'include_body', undefined}
|
||||
].
|
||||
%%
|
||||
%%default_subscription_options('local', 'collection') ->
|
||||
%% [{'deliver', true},
|
||||
%% %{'expire', undefined},
|
||||
%% %{'include_body', undefined},
|
||||
%% {'show-values', ['away', 'chat', 'dnd', 'online', 'xa']},
|
||||
%% {'subscription_type', 'all'},
|
||||
%% {'subscription_depth', 1}];
|
||||
%%
|
||||
%%default_subscription_options('remote', 'collection') ->
|
||||
%% [{'deliver', true},
|
||||
%% %{'include_body', undefined},
|
||||
%% {'show-values', ['away', 'chat', 'dnd', 'online', 'xa']},
|
||||
%% {'subscription_type', 'all'},
|
||||
%% {'subscription_depth', 1}].
|
||||
|
||||
%%
|
||||
-spec(features/0 :: () -> Features::exmpp_pubsub:features()).
|
||||
|
||||
features() ->
|
||||
[%?NS_ADDRESS,
|
||||
?NS_DISCO_INFO,
|
||||
?NS_DISCO_ITEMS,
|
||||
%?NS_RSM,
|
||||
?NS_VCARD].
|
||||
|
||||
%%
|
||||
identity() ->
|
||||
[{<<"pubsub">>, <<"Publish-Subscribe">>, <<"service">>},
|
||||
{<<"pubsub">>, <<"ejabberd/mod_pubsub/flat">>, <<"service">>}].
|
||||
@@ -0,0 +1,101 @@
|
||||
%% API function definition
|
||||
|
||||
-record(api_core,
|
||||
{
|
||||
create_node = 'pubsub_core' :: module(),
|
||||
delete_node = 'pubsub_core' :: module(),
|
||||
purge_node = 'pubsub_core' :: module(),
|
||||
get_configure_node = 'pubsub_core' :: module(),
|
||||
get_configure_node_default = 'pubsub_core' :: module(),
|
||||
%%
|
||||
publish_item = 'pubsub_core' :: module(),
|
||||
retract_item = 'pubsub_core' :: module(),
|
||||
%%
|
||||
subscribe_node = 'pubsub_core' :: module(),
|
||||
unsubscribe_node = 'pubsub_core' :: module(),
|
||||
set_configure_subscription = 'pubsub_core' :: module(),
|
||||
get_configure_subscription = 'pubsub_core' :: module(),
|
||||
get_configure_subscription_default = 'pubsub_core' :: module(),
|
||||
get_items = 'pubsub_core' :: module(),
|
||||
%%
|
||||
get_entity_affiliations = 'pubsub_core' :: module(),
|
||||
get_entity_subscriptions = 'pubsub_core' :: module(),
|
||||
%%
|
||||
get_node_affiliations = 'pubsub_core' :: module(),
|
||||
get_node_subscriptions = 'pubsub_core' :: module()
|
||||
}).
|
||||
|
||||
-record(api_db,
|
||||
{
|
||||
create_node = 'pubsub_db' :: module(),
|
||||
delete_node = 'pubsub_db' :: module(),
|
||||
purge_node = 'pubsub_db' :: module(),
|
||||
get_configure_node = 'pubsub_db' :: module(),
|
||||
%get_configure_node_default = 'pubsub_db' :: module(),
|
||||
%%
|
||||
publish_item = 'pubsub_db' :: module(),
|
||||
retract_item = 'pubsub_db' :: module(),
|
||||
%%
|
||||
subscribe_node = 'pubsub_db' :: module(),
|
||||
unsubscribe_node = 'pubsub_db' :: module(),
|
||||
set_configure_subscription = 'pubsub_db' :: module(),
|
||||
get_configure_subscription = 'pubsub_db' :: module(),
|
||||
get_configure_subscription_default = 'pubsub_db' :: module(),
|
||||
get_items = 'pubsub_db' :: module(),
|
||||
%%
|
||||
get_entity_affiliations = 'pubsub_db' :: module(),
|
||||
get_entity_subscriptions = 'pubsub_db' :: module(),
|
||||
%%
|
||||
get_node_affiliations = 'pubsub_db' :: module(),
|
||||
get_node_subscriptions = 'pubsub_db' :: module()
|
||||
}).
|
||||
|
||||
-record(api_broadcast,
|
||||
{
|
||||
broadcast_publish = 'pubsub_broadcast' :: module(),
|
||||
broadcast_publish_last = 'pubsub_broadcast' :: module(),
|
||||
notify_create = 'pubsub_broadcast' :: module(),
|
||||
notify_delete = 'pubsub_broadcast' :: module(),
|
||||
notify_publish = 'pubsub_broadcast' :: module(),
|
||||
notify_purge = 'pubsub_broadcast' :: module(),
|
||||
notify_retract = 'pubsub_broadcast' :: module(),
|
||||
notify_subscription = 'pubsub_broadcast' :: module(),
|
||||
notify_subscriptions = 'pubsub_broadcast' :: module()
|
||||
}).
|
||||
|
||||
-record(api,
|
||||
{
|
||||
core = #api_core{} :: #api_core{},
|
||||
db = #api_db{} :: #api_db{},
|
||||
broadcast = #api_broadcast{} :: #api_broadcast{},
|
||||
parser = 'pubsub_parser' :: module(),
|
||||
options = 'pubsub_options' :: module()
|
||||
}).
|
||||
|
||||
-record(capabilities,
|
||||
{
|
||||
plugin :: exmpp_pubsub:plugin(),
|
||||
privacy = false :: boolean(),
|
||||
api = #api{} :: #api{}
|
||||
}).
|
||||
|
||||
-record(api2,
|
||||
{
|
||||
func,
|
||||
core :: 'core',
|
||||
db :: 'db',
|
||||
bkd :: 'mnesia',
|
||||
rtr :: 'router'
|
||||
}).
|
||||
|
||||
-record(mod_pubsub,
|
||||
{
|
||||
server,
|
||||
component,
|
||||
plugin,
|
||||
entity,
|
||||
parameters,
|
||||
features,
|
||||
parser,
|
||||
options
|
||||
}).
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,147 @@
|
||||
|
||||
|
||||
%-include_lib("exmpp/include/exmpp.hrl").
|
||||
%-include_lib("exmpp/include/exmpp_jid.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
-include("jlib.hrl").
|
||||
%-include("pubsub_api.hrl").
|
||||
|
||||
-define(NS_JABBER_CLIENT, <<"jabber:client">>).
|
||||
|
||||
%%
|
||||
%% Pubsub Node
|
||||
-record(pubsub_node_dev,
|
||||
{
|
||||
id ,%:: {Pubsub_Host :: exmpp_pubsub:host(),
|
||||
% NodeId :: undefined | exmpp_pubsub:nodeId()},
|
||||
idx ,%:: exmpp_pubsub:nodeIdx(),
|
||||
creation ,%:: {DateTime::erlang:timestamp(), Entity::xmpp_jid:usr_entity()},
|
||||
level = 0 ,%:: exmpp_pubsub:level(),
|
||||
owners = [] ,%:: [Owner::xmpp_jid:usr_bare(),...],
|
||||
itemids = [] ,%:: [ItemId::exmpp_pubsub:itemId()],
|
||||
options = [] %:: pubsub_options:options_node()
|
||||
}).
|
||||
|
||||
%%
|
||||
%% Pubsub State
|
||||
-record(pubsub_state_dev,
|
||||
{
|
||||
id ,%:: {Entity :: xmpp_jid:usr_bare(),
|
||||
% NodeIdx :: exmpp_pubsub:nodeIdx()},
|
||||
nodeidx ,%:: exmpp_pubsub:nodeIdx(),
|
||||
affiliation = 'none' ,%:: exmpp_pubsub:affiliation(),
|
||||
access ,%:: pubsub_options:access_model(),
|
||||
subscriptions = [] ,%:: [] | exmpp_pubsub:subscriptions(),
|
||||
%groups = [] ,%:: [Roster_Group::binary()],
|
||||
itemids = [] %:: [ItemId::exmpp_pubsub:itemId()]
|
||||
}).
|
||||
|
||||
%%
|
||||
%% Pubsub Item
|
||||
-record(pubsub_item_dev,
|
||||
{
|
||||
id ,%:: {ItemId::exmpp_pubsub:itemId(), NodeIdx::exmpp_pubsub:nodeIdx()},
|
||||
nodeidx ,%:: exmpp_pubsub:nodeIdx(),
|
||||
owners = [] ,%:: [Owner::xmpp_jid:usr_bare(),...],
|
||||
creation ,%:: {DateTime::erlang:timestamp(), Entity::xmpp_jid:usr_entity()},
|
||||
modification ,%:: {DateTime::erlang:timestamp(), Entity::xmpp_jid:usr_entity()},
|
||||
payload ,%:: exmpp_pubsub:payload(),
|
||||
options = [] %:: [] | pubsub_options:options_item()
|
||||
}).
|
||||
|
||||
%%
|
||||
%% Pubsub Last Item
|
||||
-record(pubsub_last_item_dev,
|
||||
{
|
||||
nodeidx ,%:: exmpp_pubsub:nodeIdx(),
|
||||
id ,%:: exmpp_pubsub:itemId(),
|
||||
owners = [] ,%:: [Owner::xmpp_jid:usr_bare(),...],
|
||||
creation ,%:: {DateTime::erlang:timestamp(), Entity::xmpp_jid:usr_entity()},
|
||||
payload ,%:: exmpp_pubsub:payload(),
|
||||
options = [] %:: [] | pubsub_options:options_item()
|
||||
}).
|
||||
|
||||
%%
|
||||
%% Pubsub Index
|
||||
-record(pubsub_index_dev,
|
||||
{
|
||||
index :: exmpp_pubsub:index(),
|
||||
last :: exmpp_pubsub:nodeIdx(),
|
||||
free :: [exmpp_pubsub:nodeIdx()]
|
||||
}).
|
||||
|
||||
%%
|
||||
%% Pubsub_Subscription_Pending
|
||||
-record(pubsub_subscription_pending,
|
||||
{
|
||||
id ,%:: {Entity::xmpp_jid:usr_bare(), NodeIdx::exmpp_pubsub:nodeIdx()}
|
||||
nodeidx ,%:: exmpp_pubsub:nodeIdx()
|
||||
subids %:: [SubId::exmpp_pubsub:subId(),...]
|
||||
}).
|
||||
|
||||
|
||||
|
||||
%%
|
||||
%% Internal data structures
|
||||
%%
|
||||
-record(entity,
|
||||
{
|
||||
id :: xmpp_jid:usr_bare(),
|
||||
local :: boolean(),
|
||||
affiliation :: 'member' | 'owner' | 'publisher',
|
||||
subscriptions :: []%[exmpp_pubsub:subscription(),...]
|
||||
}).
|
||||
|
||||
%%
|
||||
-record(node,
|
||||
{
|
||||
id :: exmpp_pubsub:nodeId(),
|
||||
owners :: [Node_Owner::xmpp_jid:usr_bare(),...],
|
||||
access_model :: pubsub_options:access_model(),
|
||||
% itemreply :: 'owner' | 'publisher',
|
||||
% notification_type :: 'headline' | 'normal',
|
||||
% presence_based_delivery :: boolean(),
|
||||
rosters_groups_allowed = [] :: [] | pubsub_options:rosters_groups_allowed()
|
||||
}).
|
||||
|
||||
%%
|
||||
-record(item,
|
||||
{
|
||||
access_model ,%:: undefined | pubsub_options:access_model(),
|
||||
presence_based_delivery ,%:: undefined | boolean(),
|
||||
rosters_groups_allowed = [],%:: [] | pubsub_options:rosters_groups_allowed()
|
||||
stanza %::#xmlel{}
|
||||
}).
|
||||
|
||||
%%
|
||||
-record(cache,
|
||||
{
|
||||
%% Entity is online
|
||||
presence :: undefined | mod_pubsub_dev:presence_cache(),
|
||||
%% Entity has presence subscriptions with at least one node owner
|
||||
presence_subscriptions :: undefined | boolean(),
|
||||
%% Entity has presence subscriptions with at least one node owner
|
||||
%% and is contained in at least one roster group from the node owner
|
||||
rosters_groups :: undefined | boolean() %% TODO : use a list
|
||||
}).
|
||||
|
||||
%%
|
||||
-record(subids,
|
||||
{
|
||||
presence = undefined :: undefined | [] | mod_pubsub_dev:resources_subids(),
|
||||
no_presence = undefined :: undefined | [] | mod_pubsub_dev:resources_subids()
|
||||
}).
|
||||
|
||||
%%
|
||||
-record(event,
|
||||
{
|
||||
host :: xmpp_jid:raw_jid_component_bare(),
|
||||
component :: xmpp_jid:component_bare(),
|
||||
entity :: mod_pubsub_dev:entity(),
|
||||
node :: mod_pubsub_dev:n0de(),
|
||||
cache :: mod_pubsub_dev:cache(),
|
||||
subids :: mod_pubsub_dev:subids()
|
||||
}).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,904 @@
|
||||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%% @copyright 2006-2013 ProcessOne
|
||||
%%% @author Karim Gemayel <karim.gemayel@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%%% @headerfile "pubsub_dev.hrl"
|
||||
|
||||
-module(pubsub_groups).
|
||||
-author('karim.gemayel@process-one.net').
|
||||
|
||||
-compile(export_all).
|
||||
|
||||
-include("pubsub_dev.hrl").
|
||||
|
||||
-import(pubsub_tools,
|
||||
[
|
||||
get_option/2,
|
||||
get_option/3,
|
||||
get_value/2,
|
||||
get_value/3,
|
||||
set_value/3,
|
||||
%%
|
||||
check_acces_model/3,
|
||||
check_acces_model/7,
|
||||
check_publish_model/3,
|
||||
get_entity_roster/1,
|
||||
is_contact_subscribed_to_node_owners/3,
|
||||
is_contact_in_allowed_roster_groups/2,
|
||||
has_subscriptions/1,
|
||||
get_resources/2,
|
||||
get_resources_show/2,
|
||||
rosters_groups_allowed_cache/2
|
||||
]).
|
||||
|
||||
-import(pubsub_db_mnesia,
|
||||
[
|
||||
table/2
|
||||
]).
|
||||
|
||||
|
||||
-type(group() :: binary()).
|
||||
|
||||
-type(node_group() :: group()).
|
||||
|
||||
-type(roster_group() :: group()).
|
||||
|
||||
-type(node_groups() :: [Node_Group::node_group(),...]).
|
||||
|
||||
-type(roster_groups() :: [Roster_Group::roster_group(),...]).
|
||||
|
||||
-type(nodeIdxs() :: [NodeIdx::exmpp_pubsub:nodeIdx(),...]).
|
||||
|
||||
-type(n0de()
|
||||
:: {NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
Groups :: node_groups()}
|
||||
).
|
||||
|
||||
-type(n0des() :: [Node::n0de(),...]).
|
||||
|
||||
%% -type(subscription()
|
||||
%% :: {NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
%% Groups :: roster_groups()}
|
||||
%% ).
|
||||
|
||||
-type(subscriptions() :: [Subscription::subscription(),...]).
|
||||
|
||||
-type(contact()
|
||||
:: {Entity :: xmpp_jid:usr_bare(),
|
||||
Subscriptions :: subscriptions()}
|
||||
).
|
||||
|
||||
-type(contacts() :: [Contact::contact(),...]).
|
||||
|
||||
|
||||
-record(pubsub_groups,
|
||||
{
|
||||
owner :: xmpp_jid:usr_bare(),
|
||||
nodes = [] :: n0des(),
|
||||
contacts = [] :: [] | contacts()
|
||||
}).
|
||||
|
||||
|
||||
create_table_pubsub_groups(Suffix) ->
|
||||
mnesia:create_table(table('pubsub_groups', Suffix),
|
||||
[{type, set},
|
||||
{disc_copies, [node()]},
|
||||
{record_name, pubsub_groups},
|
||||
{attributes, record_info(fields, pubsub_groups)}]).
|
||||
|
||||
|
||||
|
||||
%%
|
||||
-spec(register_groups/4 ::
|
||||
(
|
||||
Suffix :: atom(),
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
Owner :: xmpp_jid:usr_bare(),
|
||||
Groups :: [] | node_groups())
|
||||
-> undefined
|
||||
%
|
||||
| {Subscribers :: [Entity::xmpp_jid:usr_bare()],
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
|
||||
register_groups(Suffix, NodeIdx, Owner, [] = _Groups) ->
|
||||
case
|
||||
mnesia:read(Table_Pubsub_Groups = table('pubsub_groups', 'dev'),
|
||||
Owner, write)
|
||||
of
|
||||
%%
|
||||
[] ->
|
||||
undefined;
|
||||
%%
|
||||
[#pubsub_groups{nodes = [{NodeIdx, _Node_Groups}]} = Pubsub_Groups] ->
|
||||
mnesia:delete_object(Table_Pubsub_Groups, Pubsub_Groups, write),
|
||||
%
|
||||
{_Subscribers = [],
|
||||
_Unsubscribers = lists:foldl(fun
|
||||
({Entity, Subscriptions}, Unsubscribers) ->
|
||||
case lists:keymember(NodeIdx, 1, Subscriptions) of
|
||||
true -> [Entity | Unsubscribers];
|
||||
false -> Unsubscribers
|
||||
end
|
||||
%
|
||||
end, [], Pubsub_Groups#pubsub_groups.contacts)};
|
||||
%%
|
||||
[Pubsub_Groups] ->
|
||||
case lists:keyfind(NodeIdx, 1, Pubsub_Groups#pubsub_groups.nodes) of
|
||||
{_NodeIdx, Node_Groups} ->
|
||||
{Contacts, Unsubscribers} = subscriptions1(NodeIdx,
|
||||
Pubsub_Groups#pubsub_groups.contacts),
|
||||
%
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
contacts = Contacts
|
||||
},
|
||||
write),
|
||||
%
|
||||
{_Subscribers = [],
|
||||
_Unsubscribers = Unsubscribers};
|
||||
false ->
|
||||
undefined
|
||||
end
|
||||
end;
|
||||
%%
|
||||
register_groups(Suffix, NodeIdx, Owner, Groups) ->
|
||||
case
|
||||
mnesia:read(Table_Pubsub_Groups = table('pubsub_groups', 'dev'), Owner,
|
||||
write)
|
||||
of
|
||||
[] ->
|
||||
{Contacts, Subscribers} = subscriptions2(NodeIdx, Owner, Groups),
|
||||
%
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
#pubsub_groups{
|
||||
owner = Owner,
|
||||
nodes = [{NodeIdx, Groups}],
|
||||
contacts = Contacts
|
||||
},
|
||||
write),
|
||||
%
|
||||
{_Subscribers = Subscribers,
|
||||
_Unsubscribers = []};
|
||||
[Pubsub_Groups] ->
|
||||
case lists:keyfind(NodeIdx, 1, Pubsub_Groups#pubsub_groups.nodes) of
|
||||
{_NodeIdx, Node_Groups} ->
|
||||
case diff_groups(Groups, Node_Groups) of
|
||||
%%
|
||||
{[] = _New_Groups, [] = _Old_Groups} ->
|
||||
{_Subscribers = [],
|
||||
_Unsubscribers = []};
|
||||
%%
|
||||
{[] = _New_Groups, Old_Groups} ->
|
||||
{Contacts, Unsubscribers}
|
||||
= subscriptions3(NodeIdx, Old_Groups,
|
||||
Pubsub_Groups#pubsub_groups.contacts),
|
||||
%
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
nodes = lists:keyreplace(NodeIdx, 1,
|
||||
Pubsub_Groups#pubsub_groups.nodes,
|
||||
{NodeIdx, Groups}),
|
||||
contacts = Contacts
|
||||
},
|
||||
write),
|
||||
%
|
||||
{_Subscribers = [],
|
||||
_Unsubscribers = Unsubscribers};
|
||||
%%
|
||||
{_New_Groups, _Old_Groups} ->
|
||||
{Contacts, Subscribers, Unsubscribers}
|
||||
= subscriptions4(NodeIdx, Owner, Node_Groups,
|
||||
Pubsub_Groups#pubsub_groups.contacts),
|
||||
%
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
nodes = lists:keyreplace(NodeIdx, 1,
|
||||
Pubsub_Groups#pubsub_groups.nodes,
|
||||
{NodeIdx, Node_Groups}),
|
||||
contacts = Contacts
|
||||
},
|
||||
write),
|
||||
%
|
||||
{Subscribers = Subscribers,
|
||||
_Unsubscribers = Unsubscribers}
|
||||
end;
|
||||
false ->
|
||||
{Contacts, Subscribers}
|
||||
= subscriptions5(NodeIdx, Owner, Groups,
|
||||
_Contacts = Pubsub_Groups#pubsub_groups.contacts),
|
||||
%
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
nodes = [{NodeIdx, Groups}
|
||||
| Pubsub_Groups#pubsub_groups.nodes],
|
||||
contacts = Contacts
|
||||
},
|
||||
write),
|
||||
%
|
||||
{_Subscribers = Subscribers,
|
||||
_Unsubscribers = []}
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
%% TODO : fix this hook managment
|
||||
-spec(monitor_contacts/3 ::
|
||||
(
|
||||
_ :: 'subscribed',
|
||||
Entity :: xmpp_jid:usr_bare(),
|
||||
Jid_Contacts :: xmpp_jid:entity_bare())
|
||||
-> undefined
|
||||
%
|
||||
| {Server :: xmpp_jid:raw_jid_component_bare(),
|
||||
Entity :: xmpp_jid:usr_bare(),
|
||||
Contact :: xmpp_jid:usr_bare(),
|
||||
_ :: {'subscribed', NodeIdxs :: nodeIdxs()}}
|
||||
).
|
||||
|
||||
monitor_contacts('subscribed', {_, Server, _} = Entity, Jid_Contact) ->
|
||||
case roster_groups(Entity, Contact = jlid:jid_to_lower(Jid_Contact)) of
|
||||
[] ->
|
||||
undefined;
|
||||
Roster_Groups ->
|
||||
case
|
||||
mnesia:read(Table_Pubsub_Groups = table('pubsub_groups', dev),
|
||||
_Owner = Entity, write)
|
||||
of
|
||||
[] ->
|
||||
undefined;
|
||||
[Pubsub_Groups] ->
|
||||
case
|
||||
filter_subscriptions(Pubsub_Groups#pubsub_groups.nodes,
|
||||
Roster_Groups)
|
||||
of
|
||||
[] ->
|
||||
undefined;
|
||||
Subscriptions ->
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
contacts = case
|
||||
lists:keyreplace(Contact, 1,
|
||||
Pubsub_Groups#pubsub_groups.contacts,
|
||||
{Contact, Subscriptions})
|
||||
of
|
||||
_Contacts
|
||||
when _Contacts ==
|
||||
Pubsub_Groups#pubsub_groups.contacts ->
|
||||
[{Contact, Subscriptions}
|
||||
| Pubsub_Groups#pubsub_groups.contacts];
|
||||
Contacts ->
|
||||
Contacts
|
||||
end
|
||||
},
|
||||
write),
|
||||
{Server, Entity, Contact,
|
||||
{'subscribed',
|
||||
_NodeIdx = lists:map(fun
|
||||
({NodeIdx, _Groups}) ->
|
||||
NodeIdx
|
||||
end, Subscriptions)}}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
%%
|
||||
-spec(monitor_groups/2 ::
|
||||
(
|
||||
Server :: xmpp_jid:raw_jid_component_bare(),
|
||||
Roster :: #roster{groups :: [] | roster_groups()})
|
||||
-> undefined
|
||||
%
|
||||
| {Server :: xmpp_jid:raw_jid_component_bare(),
|
||||
Entity :: xmpp_jid:usr_bare(),
|
||||
Diff_NodeIdxs :: {NodeIdxs_Subscriptions :: [] | nodeIdxs(),
|
||||
NodeIdxs_Unsubscriptions :: [] | nodeIdxs()}}
|
||||
).
|
||||
|
||||
monitor_groups(Server, #roster{us = {U,S}} = _Roster) ->
|
||||
case
|
||||
mnesia:read(Table_Pubsub_Groups = table('pubsub_groups', dev),
|
||||
_Owner = {U,S,undefined}, write)
|
||||
of
|
||||
[] ->
|
||||
undefined;
|
||||
[Pubsub_Groups] ->
|
||||
case
|
||||
lists:keyfind(_Roster#roster.jid, 1,
|
||||
Pubsub_Groups#pubsub_groups.contacts)
|
||||
of %%
|
||||
{_Entity, Old_Subscriptions}
|
||||
when _Roster#roster.subscription == 'delete'
|
||||
orelse _Roster#roster.subscription == 'none' ->
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
contacts = lists:keydelete(_Roster#roster.jid, 1,
|
||||
Pubsub_Groups#pubsub_groups.contacts)
|
||||
},
|
||||
write),
|
||||
{Server,
|
||||
_Entity = _Roster#roster.jid,
|
||||
_Diff_NodeIdxs = diff_nodeidxs(_New_Subscriptions = [],
|
||||
Old_Subscriptions)};
|
||||
%%
|
||||
{_Entity, Old_Subscriptions}
|
||||
when (_Roster#roster.subscription == 'both'
|
||||
orelse
|
||||
_Roster#roster.subscription == 'from')
|
||||
andalso _Roster#roster.groups == [] ->
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
contacts = lists:keydelete(_Roster#roster.jid, 1,
|
||||
Pubsub_Groups#pubsub_groups.contacts)
|
||||
},
|
||||
write),
|
||||
{Server,
|
||||
_Roster#roster.jid,
|
||||
_Diff_NodeIdxs = diff_nodeidxs(_New_Subscriptions = [],
|
||||
Old_Subscriptions)};
|
||||
%%
|
||||
{_Entity, Old_Subscriptions}
|
||||
when _Roster#roster.subscription == 'both'
|
||||
orelse _Roster#roster.subscription == 'from' ->
|
||||
case
|
||||
filter_subscriptions(Pubsub_Groups#pubsub_groups.nodes,
|
||||
_Roster#roster.groups)
|
||||
of
|
||||
[] ->
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
contacts = lists:keydelete(_Roster#roster.jid,
|
||||
1, Pubsub_Groups#pubsub_groups.contacts)
|
||||
},
|
||||
write),
|
||||
{Server, _Roster#roster.jid,
|
||||
diff_nodeidxs(_New_Subscriptions = [],
|
||||
Old_Subscriptions)};
|
||||
New_Subscriptions ->
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
contacts = lists:keyreplace(_Roster#roster.jid,
|
||||
1, Pubsub_Groups#pubsub_groups.contacts,
|
||||
{_Roster#roster.jid, New_Subscriptions})
|
||||
},
|
||||
write),
|
||||
{Server,
|
||||
_Entity = _Roster#roster.jid,
|
||||
_Diff_NodeIdxs = diff_nodeidxs(New_Subscriptions,
|
||||
Old_Subscriptions)}
|
||||
end;
|
||||
%%
|
||||
false
|
||||
when (_Roster#roster.subscription == 'both'
|
||||
orelse
|
||||
_Roster#roster.subscription == 'from')
|
||||
andalso _Roster#roster.groups =/= [] ->
|
||||
case
|
||||
filter_subscriptions(Pubsub_Groups#pubsub_groups.nodes,
|
||||
_Roster#roster.groups)
|
||||
of
|
||||
[] ->
|
||||
undefined;
|
||||
New_Subscriptions ->
|
||||
mnesia:write(Table_Pubsub_Groups,
|
||||
Pubsub_Groups#pubsub_groups{
|
||||
contacts = [{_Roster#roster.jid, New_Subscriptions}
|
||||
| Pubsub_Groups#pubsub_groups.contacts]
|
||||
},
|
||||
write),
|
||||
{Server,
|
||||
_Entity = _Roster#roster.jid,
|
||||
_Diff_NodeIdxs = diff_nodeidxs(New_Subscriptions,
|
||||
_Old_Subscriptions = [])}
|
||||
end;
|
||||
%%
|
||||
_ ->
|
||||
undefined
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
|
||||
%%
|
||||
%%
|
||||
-spec(subscriptions1/2 ::
|
||||
(
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
Old_Contacts :: [] | contacts())
|
||||
-> Diff_Contacts :: {New_Contacts :: [] | contacts(),
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
|
||||
subscriptions1(NodeIdx, Contacts) ->
|
||||
_Diff_Contacts = subscriptions1(NodeIdx, _Old_Contacts = Contacts,
|
||||
{_New_Contacts = [], _Unsubscribers = []}).
|
||||
|
||||
|
||||
%%
|
||||
-spec(subscriptions1/3 ::
|
||||
(
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
Old_Contacts :: [] | contacts(),
|
||||
Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]})
|
||||
-> Diff_Contacts :: {New_Contacts :: [] | contacts(),
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
|
||||
subscriptions1(_NodeIdx, [] = _Old_Contacts, Diff_Contacts) ->
|
||||
Diff_Contacts;
|
||||
%%
|
||||
subscriptions1(NodeIdx, [{Entity, Subscriptions} | Old_Contacts],
|
||||
{Contacts, Unsubscribers}) ->
|
||||
subscriptions1(NodeIdx, Old_Contacts,
|
||||
_Diff_Contacts = case lists:keydelete(NodeIdx, 1, Subscriptions) of
|
||||
[] ->
|
||||
{_Contacts = Contacts,
|
||||
_Unsubscribers = [Entity | Unsubscribers]};
|
||||
Subscriptions ->
|
||||
{_Contacts = [{Entity, Subscriptions} | Contacts],
|
||||
_Unsubscribers = Unsubscribers};
|
||||
New_Subscriptions ->
|
||||
{_Contacts = [{Entity, New_Subscriptions} | Contacts],
|
||||
_Unsubscribers = [Entity | Unsubscribers]}
|
||||
end).
|
||||
|
||||
|
||||
%%
|
||||
-spec(subscriptions2/3 ::
|
||||
(
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
Owner :: xmpp_jid:usr_bare(),
|
||||
Node_Groups :: node_groups())
|
||||
-> Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Subscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
|
||||
subscriptions2(NodeIdx, Owner, Node_Groups) ->
|
||||
subscriptions2(NodeIdx, _Rosters = get_entity_roster(Owner), Node_Groups,
|
||||
{_New_Contacts = [], _Subscribers = []}).
|
||||
|
||||
%%
|
||||
-spec(subscriptions2/4 ::
|
||||
(
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
Rosters :: [Roster::#roster{groups :: [] | roster_groups()}],
|
||||
Node_Groups :: node_groups(),
|
||||
Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Subscribers :: [Entity::xmpp_jid:usr_bare()]})
|
||||
-> Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Subscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
|
||||
subscriptions2(_NodeIdx, [] = _Rosters, _Node_Groups, Diff_Contacts) ->
|
||||
Diff_Contacts;
|
||||
%%
|
||||
subscriptions2(NodeIdx, [_Roster | Rosters], Node_Groups, {Contacts, Subscribers})
|
||||
when _Roster#roster.groups =/= [] ->
|
||||
subscriptions2(NodeIdx, Rosters, Node_Groups,
|
||||
_Diff_Contacts = case
|
||||
filter_groups(_Roster#roster.groups, Node_Groups, [])
|
||||
of
|
||||
[] ->
|
||||
{_Contacts = Contacts,
|
||||
_Subscribers = Subscribers};
|
||||
Roster_Groups ->
|
||||
{_Contacts = [{_Roster#roster.jid, [{NodeIdx, Roster_Groups}]}
|
||||
| Contacts],
|
||||
_Subscribers = [_Roster#roster.jid | Subscribers]}
|
||||
end);
|
||||
%%
|
||||
subscriptions2(NodeIdx, [_Roster | Rosters], Node_Groups, Diff_Contacts) ->
|
||||
subscriptions2(NodeIdx, Rosters, Node_Groups, Diff_Contacts).
|
||||
|
||||
|
||||
%%
|
||||
-spec(subscriptions3/3 ::
|
||||
(
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
Old_Groups :: roster_groups(),
|
||||
Contacts :: contacts() | [])
|
||||
-> Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
subscriptions3(NodeIdx, Old_Groups, Contacts) ->
|
||||
subscriptions3(NodeIdx, Contacts, Old_Groups, {[],[]}).
|
||||
|
||||
%%
|
||||
-spec(subscriptions3/4 ::
|
||||
(
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
Old_Contacts :: contacts() | [],
|
||||
Old_Groups :: roster_groups(),
|
||||
Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]})
|
||||
-> Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
|
||||
subscriptions3(_NodeIdx, [] = _Contacts, _Old_Groups, Diff_Contacts) ->
|
||||
Diff_Contacts;
|
||||
%%
|
||||
subscriptions3(NodeIdx, [{Entity, Subscriptions} | Old_Contacts], Old_Groups,
|
||||
{Contacts, Unsubscribers}) ->
|
||||
subscriptions3(NodeIdx, Old_Contacts, Old_Groups,
|
||||
_Diff_Contacts = case lists:keyfind(NodeIdx, 1, Subscriptions) of
|
||||
{_NodeIdx, Roster_Groups} ->
|
||||
case delete_groups(Old_Groups, Roster_Groups) of
|
||||
[] ->
|
||||
{_Contacts = Contacts,
|
||||
_Unsubscribers = [Entity | Unsubscribers]};
|
||||
Groups ->
|
||||
{_Contacts = [{Entity,
|
||||
lists:keyreplace(NodeIdx, 1,
|
||||
Subscriptions, {NodeIdx, Groups})}
|
||||
| Contacts],
|
||||
_Unsubscribers = Unsubscribers}
|
||||
end;
|
||||
false ->
|
||||
{_Contacts = [{Entity, Subscriptions} | Contacts],
|
||||
_Unsubscribers = Unsubscribers}
|
||||
end).
|
||||
|
||||
|
||||
%%
|
||||
-spec(subscriptions4/4 ::
|
||||
(
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
_ :: xmpp_jid:usr_bare()
|
||||
| [Roster::#roster{groups :: [] | roster_groups()}],
|
||||
Node_Groups :: node_groups(),
|
||||
_ :: [] | contacts()
|
||||
| {Contacts :: [] | contacts(),
|
||||
Subscribers :: [Entity::xmpp_jid:usr_bare()],
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]})
|
||||
-> Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Subscribers :: [Entity::xmpp_jid:usr_bare()],
|
||||
Unsubscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
|
||||
subscriptions4(_NodeIdx, [] = _Rosters, _Node_Groups, Diff_Contacts) ->
|
||||
Diff_Contacts;
|
||||
%%
|
||||
subscriptions4(NodeIdx, [_Roster | Rosters], Node_Groups,
|
||||
{Contacts, Subscribers, Unsubscribers})
|
||||
when _Roster#roster.groups =/= [] ->
|
||||
subscriptions4(NodeIdx, Rosters, Node_Groups,
|
||||
_Diff_Contacts = case
|
||||
{lists:keyfind(Entity = _Roster#roster.jid, 1, Contacts),
|
||||
filter_groups(_Roster#roster.groups, Node_Groups, [])}
|
||||
of
|
||||
%%
|
||||
{false, [] = _Groups} ->
|
||||
{_Contacts = Contacts,
|
||||
_Subscribers = Subscribers,
|
||||
_Unsubscribers = Unsubscribers};
|
||||
%%
|
||||
{false, Groups} ->
|
||||
{_Contacts = [{Entity, [{NodeIdx, Groups}]} | Contacts],
|
||||
_Subscribers = [Entity | Subscribers],
|
||||
_Unsubscribers = Unsubscribers};
|
||||
%%
|
||||
{{_Entity, [{NodeIdx, _Roster_Groups}] = _Subscriptions}, [] = _Groups} ->
|
||||
{_Contacts = lists:keydelete(Entity, 1, Contacts),
|
||||
_Subscribers = Subscribers,
|
||||
_Unsubscribers = [Entity | Unsubscribers]};
|
||||
%%
|
||||
{{_Entity, Subscriptions}, [] = _Groups} ->
|
||||
case lists:keydelete(NodeIdx, 1, Subscriptions) of
|
||||
Subscriptions ->
|
||||
{_Contacts = Contacts,
|
||||
_Subscribers = Subscribers,
|
||||
_Unsubscribers = Unsubscribers};
|
||||
New_Subscriptions ->
|
||||
{_Contacts = lists:keyreplace(Entity, 1, Contacts,
|
||||
{Entity, New_Subscriptions}),
|
||||
_Subscribers = Subscribers,
|
||||
_Unsubscribers = [Entity | Unsubscribers]}
|
||||
end;
|
||||
{{_Entity, Subscriptions}, Groups} ->
|
||||
case lists:keyreplace(NodeIdx, 1, Subscriptions, {NodeIdx, Groups}) of
|
||||
Subscriptions ->
|
||||
{_Contacts = lists:keyreplace(Entity, 1, Contacts,
|
||||
{Entity,
|
||||
[{NodeIdx, Groups} | Subscriptions]}),
|
||||
_Subscribers = [Entity | Subscribers],
|
||||
_Unsubscribers = Unsubscribers};
|
||||
New_Subscriptions ->
|
||||
{_Contacts = lists:keyreplace(Entity, 1, Contacts,
|
||||
{Entity, New_Subscriptions}),
|
||||
_Subscribers = Subscribers,
|
||||
_Unsubscribers = Unsubscribers}
|
||||
end
|
||||
end);
|
||||
%%
|
||||
subscriptions4(NodeIdx, [_Roster | Rosters], Node_Groups, Diff_Contacts) ->
|
||||
subscriptions4(NodeIdx, Rosters, Node_Groups, Diff_Contacts);
|
||||
%%
|
||||
subscriptions4(NodeIdx, Owner, Node_Groups, Contacts) ->
|
||||
subscriptions4(NodeIdx, _Rosters = get_entity_roster(Owner), Node_Groups,
|
||||
_Diff_Contacts = {Contacts, _Subscribers = [], _Unsubscribers = []}).
|
||||
|
||||
|
||||
|
||||
-spec(subscriptions5/4 ::
|
||||
(
|
||||
NodeIdx :: exmpp_pubsub:nodeIdx(),
|
||||
_ :: xmpp_jid:usr_bare()
|
||||
| [Roster::#roster{groups :: [] | roster_groups()}],
|
||||
Node_Groups :: node_groups(),
|
||||
_ :: [] | contacts()
|
||||
| {Contacts :: [] | contacts(),
|
||||
Subscribers :: [Entity::xmpp_jid:usr_bare()]})
|
||||
-> Diff_Contacts :: {Contacts :: [] | contacts(),
|
||||
Subscribers :: [Entity::xmpp_jid:usr_bare()]}
|
||||
).
|
||||
|
||||
subscriptions5(NodeIdx, [_Roster | Rosters], Node_Groups, {Contacts, Subscribers})
|
||||
when _Roster#roster.groups =/= [] ->
|
||||
subscriptions5(NodeIdx, Rosters, Node_Groups,
|
||||
_Diff_Contacts = case
|
||||
filter_groups(_Roster#roster.groups, Node_Groups, [])
|
||||
of
|
||||
[] ->
|
||||
{_Contacts = Contacts,
|
||||
_Subscribers = Subscribers};
|
||||
Groups ->
|
||||
{_Contacts = case
|
||||
lists:keyfind(_Roster#roster.jid, 1, Contacts)
|
||||
of
|
||||
false ->
|
||||
[{_Roster#roster.jid, [{NodeIdx, Groups}]} | Contacts];
|
||||
{Entity, Subscriptions} ->
|
||||
lists:keyreplace(Entity, 1, Contacts,
|
||||
{Entity, [{NodeIdx, Groups} | Subscriptions]})
|
||||
end,
|
||||
_Subscribers = [_Roster#roster.jid | Subscribers]}
|
||||
end);
|
||||
%%
|
||||
subscriptions5(NodeIdx, [_Roster | Rosters], Node_Groups, Diff_Contacts) ->
|
||||
subscriptions5(NodeIdx, Rosters, Node_Groups, Diff_Contacts);
|
||||
%%
|
||||
subscriptions5(NodeIdx, Owner, Node_Groups, Contacts) ->
|
||||
subscriptions5(NodeIdx, _Rosters = get_entity_roster(Owner), Node_Groups,
|
||||
_Diff_Contacts = {Contacts, _Subscribers = []}).
|
||||
|
||||
|
||||
%%
|
||||
-spec(delete_groups/2 ::
|
||||
(
|
||||
Old_Groups :: roster_groups(),
|
||||
Groups :: [] | roster_groups())
|
||||
-> Groups :: [] | roster_groups()
|
||||
).
|
||||
|
||||
delete_groups(_Old_Groups, [] = _Groups) ->
|
||||
_Groups = [];
|
||||
%%
|
||||
delete_groups([] = _Old_Groups, Groups) ->
|
||||
Groups;
|
||||
%%
|
||||
delete_groups([Old_Group | Old_Groups], Groups) ->
|
||||
_Groups = delete_groups(Old_Groups, lists:delete(Old_Group, Groups)).
|
||||
|
||||
|
||||
%%
|
||||
-spec(diff_groups/2 ::
|
||||
(
|
||||
Groups :: node_groups() | [],
|
||||
_ :: node_groups()
|
||||
| {New_Groups :: [] | node_groups(),
|
||||
Old_Groups :: [] | node_groups()})
|
||||
-> Diff_Groups :: {New_Groups :: [] | node_groups(),
|
||||
Old_Groups :: [] | node_groups()}
|
||||
).
|
||||
|
||||
diff_groups([] = _Groups, Diff_Groups) -> %% when is_tuple(Diff_Groups)
|
||||
Diff_Groups;
|
||||
%%
|
||||
diff_groups([Group | Groups], {New_Groups, Old_Groups}) ->
|
||||
diff_groups(Groups,
|
||||
_Diff_Groups = case lists:delete(Group, Old_Groups) of
|
||||
Old_Groups -> {[Group | New_Groups], Old_Groups};
|
||||
Old_Groups2 -> {New_Groups, Old_Groups2}
|
||||
end);
|
||||
%%
|
||||
diff_groups(New_Groups, Old_Groups) ->
|
||||
diff_groups(New_Groups, {[], Old_Groups}).
|
||||
|
||||
|
||||
%%
|
||||
-spec(filter_groups/3 ::
|
||||
(
|
||||
Roster_Groups :: roster_groups() | [],
|
||||
Node_Groups :: node_groups(),
|
||||
Groups :: [] | roster_groups())
|
||||
-> Groups :: [] | roster_groups()
|
||||
).
|
||||
|
||||
filter_groups([] = _Roster_Groups, _Node_Groups, Groups) ->
|
||||
Groups;
|
||||
%%
|
||||
filter_groups([Roster_Group | Roster_Groups], Node_Groups, Groups) ->
|
||||
filter_groups(Roster_Groups, Node_Groups,
|
||||
_Groups = case lists:member(Roster_Group, Node_Groups) of
|
||||
true -> [Roster_Group | Groups];
|
||||
false -> Groups
|
||||
end).
|
||||
|
||||
|
||||
%%
|
||||
-spec(filter_subscriptions/2 ::
|
||||
(
|
||||
Nodes :: n0des(),
|
||||
Roster_Groups :: roster_groups())
|
||||
-> Subscriptions :: [] | subscriptions()
|
||||
).
|
||||
|
||||
filter_subscriptions(Nodes, Roster_Groups) ->
|
||||
_Subscriptions = filter_subscriptions(Nodes, Roster_Groups, []).
|
||||
|
||||
%%
|
||||
-spec(filter_subscriptions/3 ::
|
||||
(
|
||||
Nodes :: n0des(),
|
||||
Roster_Groups :: roster_groups(),
|
||||
Subscriptions :: [] | subscriptions())
|
||||
-> Subscriptions :: [] | subscriptions()
|
||||
).
|
||||
|
||||
filter_subscriptions([] = _Nodes, _Roster_Groups, Subscriptions) ->
|
||||
Subscriptions;
|
||||
%%
|
||||
filter_subscriptions([{NodeIdx, Node_Groups} | Nodes], Roster_Groups,
|
||||
Subscriptions) ->
|
||||
filter_subscriptions(Nodes, Roster_Groups,
|
||||
_Subscriptions = case filter_groups(Roster_Groups, Node_Groups, []) of
|
||||
[] -> Subscriptions;
|
||||
Groups -> [{NodeIdx, Groups} | Subscriptions]
|
||||
end).
|
||||
|
||||
|
||||
%%
|
||||
-spec(diff_nodeidxs/2 ::
|
||||
(
|
||||
New_Subscriptions :: [] | subscriptions(),
|
||||
Old_Subscriptions :: [] | subscriptions())
|
||||
-> Diff_NodeIdxs :: {NodeIdxs_Subscriptions :: [] | nodeIdxs(),
|
||||
NodeIdxs_Unsubscriptions :: [] | nodeIdxs()}
|
||||
).
|
||||
|
||||
diff_nodeidxs(New_Subscriptions, Old_Subscriptions) ->
|
||||
_Diff_NodeIdxs = diff_nodeidxs(New_Subscriptions, Old_Subscriptions, []).
|
||||
|
||||
%%
|
||||
-spec(diff_nodeidxs/3 ::
|
||||
(
|
||||
New_Subscriptions :: [] | subscriptions(),
|
||||
Old_Subscriptions :: [] | subscriptions(),
|
||||
NodeIdxs_Subscriptions :: [] | nodeIdxs())
|
||||
-> Diff_NodeIdxs :: {NodeIdxs_Subscriptions :: [] | nodeIdxs(),
|
||||
NodeIdxs_Unsubscriptions :: [] | nodeIdxs()}
|
||||
).
|
||||
|
||||
diff_nodeidxs([] = _New_Subscriptions, Old_Subscriptions,
|
||||
NodeIdxs_Subscriptions) ->
|
||||
_Diff_NodeIdxs = {
|
||||
_NodeIdxs_Subscriptions = NodeIdxs_Subscriptions,
|
||||
_NodeIdxs_Unsubscriptions = lists:map(fun
|
||||
({NodeIdx, _Roster_Groups}) ->
|
||||
NodeIdx
|
||||
end, Old_Subscriptions)};
|
||||
%%
|
||||
diff_nodeidxs([{NodeIdx, _Groups} | New_Subscriptions], [] = Old_Subscriptions,
|
||||
NodeIdxs_Subscriptions) ->
|
||||
diff_nodeidxs(New_Subscriptions, Old_Subscriptions,
|
||||
[NodeIdx | NodeIdxs_Subscriptions]);
|
||||
%%
|
||||
diff_nodeidxs([{NodeIdx, _Groups} | New_Subscriptions], Old_Subscriptions,
|
||||
NodeIdxs_Subscriptions) ->
|
||||
case lists:keydelete(NodeIdx, 1, Old_Subscriptions) of
|
||||
Old_Subscriptions ->
|
||||
diff_nodeidxs(New_Subscriptions, Old_Subscriptions,
|
||||
[NodeIdx | NodeIdxs_Subscriptions]);
|
||||
Subscriptions ->
|
||||
diff_nodeidxs(New_Subscriptions, _Old_Subscriptions = Subscriptions,
|
||||
NodeIdxs_Subscriptions)
|
||||
end.
|
||||
|
||||
|
||||
%% TODO : use a new mod_roster_api for this call
|
||||
-spec(roster_groups/2 ::
|
||||
(
|
||||
Entity :: xmpp_jid:usr_bare(),
|
||||
Contact :: xmpp_jid:usr_bare())
|
||||
-> Roster_Groups :: [] | roster_groups()
|
||||
).
|
||||
|
||||
roster_groups({User, Server, _} = _Entity, Contact) ->
|
||||
_Roster_Groups = case
|
||||
gen_storage:transaction(Server, rosteritem,
|
||||
fun() ->
|
||||
gen_storage:select(Server, rostergroup,
|
||||
[{'=', user_host_jid, {User, Server, Contact}}])
|
||||
end)
|
||||
of
|
||||
{atomic, _RosterGroups} ->
|
||||
lists:map(fun
|
||||
(_RosterGroup) ->
|
||||
_RosterGroup#roster.groups
|
||||
end, _RosterGroups);
|
||||
_Error
|
||||
-> {error, 'internal-server-error'}
|
||||
end.
|
||||
|
||||
|
||||
%%
|
||||
-spec(select_roster_groups/1 ::
|
||||
(
|
||||
Entity :: xmpp_jid:usr_entity())
|
||||
-> Roster_Groups :: [] | roster_groups()
|
||||
).
|
||||
|
||||
select_roster_groups({User, Server, _} = Entity) ->
|
||||
_Roster_Groups = case
|
||||
gen_storage:transaction(Server, rosteritem,
|
||||
fun() ->
|
||||
gen_storage:select(Server, rostergroup,
|
||||
[{'=', user_host_jid, {User, Server, '_'}}])
|
||||
end)
|
||||
of
|
||||
{atomic, _RosterGroups} ->
|
||||
lists:map(fun
|
||||
(_RosterGroup) ->
|
||||
_RosterGroup#roster.groups
|
||||
end, _RosterGroups);
|
||||
_Error
|
||||
-> {error, 'internal-server-error'}
|
||||
end.
|
||||
|
||||
|
||||
%% TESTS
|
||||
|
||||
%%
|
||||
monitor_roster_groups(Server, Roster) ->
|
||||
?INFO_MSG("SERVER ~p ROSTER ~p", [Server, Roster]),
|
||||
Result = pubsub_db:transaction('mnesia', ?MODULE, monitor_groups,
|
||||
[Server, Roster]),
|
||||
?INFO_MSG("MONITOR ROSTER GROUPS ~p", [Result]).
|
||||
|
||||
|
||||
monitor_contacts2('subscribed', Entity, Jid_Contact) ->
|
||||
Result = pubsub_db:transaction('mnesia', ?MODULE, monitor_contacts,
|
||||
['subscribed', Entity, Jid_Contact]),
|
||||
?INFO_MSG("MONITOR CONTACTS2 ~p", [Result]).
|
||||
|
||||
register1(NodeIdx, User, Groups) ->
|
||||
Server = <<"localhost">>,
|
||||
Owner = {list_to_binary(atom_to_list(User)), Server, undefined},
|
||||
Groups_B = lists:map(fun(Group) -> list_to_binary(atom_to_list(Group)) end, Groups),
|
||||
register_groups(dev, NodeIdx, Owner, Groups_B).
|
||||
|
||||
register(NodeIdx, User, Groups) ->
|
||||
pubsub_db:transaction('mnesia', ?MODULE, register1, [NodeIdx, User, Groups]).
|
||||
@@ -0,0 +1,570 @@
|
||||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%% @copyright 2006-2013 ProcessOne
|
||||
%%% @author Karim Gemayel <karim.gemayel@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%%% @headerfile "pubsub_dev.hrl"
|
||||
|
||||
-module(pubsub_hooks).
|
||||
-author('karim.gemayel@process-one.net').
|
||||
|
||||
-compile(export_all).
|
||||
|
||||
-include("pubsub_dev.hrl").
|
||||
|
||||
-import(pubsub_tools,
|
||||
[
|
||||
get_option/2,
|
||||
get_option/3,
|
||||
get_value/2,
|
||||
get_value/3,
|
||||
set_value/3,
|
||||
%%
|
||||
check_access_model/3,
|
||||
check_access_model/7,
|
||||
check_publish_model/3,
|
||||
is_contact_subscribed_to_node_owners/3,
|
||||
is_contact_in_allowed_roster_groups/2,
|
||||
has_subscriptions/1
|
||||
]).
|
||||
|
||||
-import(pubsub_db_mnesia,
|
||||
[
|
||||
table/2
|
||||
]).
|
||||
|
||||
%% 'node_config#send_last_published_item'
|
||||
-spec(presence_online/3 ::
|
||||
(
|
||||
From :: xmpp_jid:entity_full(),
|
||||
To :: xmpp_jid:entity_full(),
|
||||
C2SPid :: pid())
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
presence_online(#jid{luser = U, lserver = S, lresource = R} = Jid, Jid, C2SPid) ->
|
||||
?INFO_MSG("PRESENCE ONLINE From ~p ~n, To ~p ~n, C2SPid ~p ~n",
|
||||
[Jid, Jid, C2SPid]),
|
||||
Pubsub_Features = node_flat_dev:pubsub_features(),
|
||||
case lists:member(<<"last-published">>, Pubsub_Features) of
|
||||
true ->
|
||||
spawn(?MODULE, last_published_items,
|
||||
[_Host = <<"localhost">>, {U,S,R}]);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
%%
|
||||
presence_online(_, _, _) ->
|
||||
ok.
|
||||
|
||||
|
||||
%%
|
||||
-spec(last_published_items/2 ::
|
||||
(
|
||||
Host :: xmpp_jid:raw_jid_component_bare(),
|
||||
Entity :: xmpp_jid:usr_full())
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
last_published_items(Host, {U,S,_R} = Entity) ->
|
||||
Table_Pubsub_State = table('pubsub_state', 'dev'),
|
||||
Table_Pubsub_Node = table('pubsub_node', 'dev'),
|
||||
Table_Pubsub_Last_Item = table('pubsub_last_item', 'dev'),
|
||||
lists:foreach(fun
|
||||
(State) ->
|
||||
last_published_items(Host, Entity, Table_Pubsub_Node,
|
||||
Table_Pubsub_Last_Item, State)
|
||||
end,
|
||||
mnesia:dirty_select(Table_Pubsub_State,
|
||||
[{#pubsub_state_dev{
|
||||
id = {{U,S,undefined}, '$1'},
|
||||
affiliation = '$2',
|
||||
access = '$3',
|
||||
subscriptions = '$4',
|
||||
_ = '_'
|
||||
},
|
||||
[{'=/=', '$2', 'outcast'},
|
||||
{'=/=', '$3', 'pending'},
|
||||
{'=/=', '$4', []}],
|
||||
['$$']}])).
|
||||
|
||||
%%
|
||||
-spec(last_published_items/5 ::
|
||||
(
|
||||
Host :: xmpp_jid:raw_jid_component_bare(),
|
||||
Entity :: xmpp_jid:usr_full(),
|
||||
Table_Pubsub_Node :: atom(),
|
||||
Table_Pubsub_Last_Item :: atom(),
|
||||
State :: [exmpp_pubsub:nodeIdx() |
|
||||
'member' | 'owner' | 'publisher' |
|
||||
_ |
|
||||
exmpp_pubsub:subscriptions(),... ])
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
last_published_items(Host, {_, _, Online_Resource} = Entity,
|
||||
Table_Pubsub_Node, Table_Pubsub_Last_Item,
|
||||
[NodeIdx, Affiliation, _Access, Subscriptions]) ->
|
||||
case mnesia:dirty_index_read(Table_Pubsub_Node, NodeIdx, idx) of
|
||||
[#pubsub_node_dev{
|
||||
id = {Pubsub_Host, NodeId},
|
||||
owners = Node_Owners,
|
||||
options = Node_Options
|
||||
}] ->
|
||||
case get_value(Node_Options, 'send_last_published_item') of
|
||||
'on_sub_and_presence' ->
|
||||
case mnesia:dirty_read(Table_Pubsub_Last_Item, NodeIdx) of
|
||||
[#pubsub_last_item_dev{
|
||||
id = ItemId,
|
||||
creation = {DateTime, Publisher},
|
||||
payload = Payload,
|
||||
options = Item_Options
|
||||
}] ->
|
||||
spawn(pubsub_broadcast, broadcast_publish_last2,
|
||||
[Host, Pubsub_Host, NodeId, Node_Options,
|
||||
Node_Owners,
|
||||
[{ItemId, Item_Options, Payload, Publisher, DateTime}],
|
||||
Entity, Affiliation,
|
||||
lists:foldl(fun
|
||||
({Subscription_State, SubId, Resource, Subscription_Options},
|
||||
Acc)
|
||||
when Subscription_State =/= 'pending'
|
||||
andalso (Resource == undefined
|
||||
orelse
|
||||
Resource == Online_Resource) ->
|
||||
[{Subscription_State, SubId,
|
||||
Online_Resource, Subscription_Options}
|
||||
| Acc];
|
||||
(_Subscription, Acc) ->
|
||||
Acc
|
||||
end, [], Subscriptions)]);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% 'node_config#purge_offline'
|
||||
%% 'node_config#tempsub'
|
||||
%% 'subscribe_options#expire'
|
||||
|
||||
-spec(presence_offline/3 ::
|
||||
(
|
||||
_ :: _,
|
||||
Jid :: xmpp_jid:entity_full(),
|
||||
_ :: _)
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
presence_offline({_DateTime, _Pid},
|
||||
#jid{luser = U, lserver = S, lresource = Offline_Resource} = _Jid, _) ->
|
||||
Host = <<"localhost">>,
|
||||
Pubsub_Features = node_flat_dev:pubsub_features(),
|
||||
Subscribe = lists:member(<<"subscribe">>, Pubsub_Features),
|
||||
Leased_Subscription = lists:member(<<"leased-subscription">>, Pubsub_Features),
|
||||
Purge_Nodes = lists:member(<<"purge-nodes">>, Pubsub_Features),
|
||||
Persistent_Items = lists:member(<<"persistent-items">>, Pubsub_Features),
|
||||
Subscription_Notifications = lists:member(<<"subscription-notifications">>,
|
||||
Pubsub_Features),
|
||||
Table_Pubsub_State = table('pubsub_state', 'dev'),
|
||||
Table_Pubsub_Node = table('pubsub_node', 'dev'),
|
||||
Online_Resources = case ejabberd_sm:get_user_resources(U, S) of
|
||||
[] -> false;
|
||||
_ -> true
|
||||
end,
|
||||
lists:foreach(fun
|
||||
(Pubsub_State) ->
|
||||
presence_offline(Host,
|
||||
_Entity = {U,S,undefined},
|
||||
Table_Pubsub_State,
|
||||
Pubsub_State,
|
||||
Table_Pubsub_Node,
|
||||
Offline_Resource,
|
||||
Online_Resources,
|
||||
{Subscribe,
|
||||
Leased_Subscription,
|
||||
Purge_Nodes,
|
||||
Persistent_Items,
|
||||
Subscription_Notifications})
|
||||
end,
|
||||
mnesia:dirty_match_object(Table_Pubsub_State,
|
||||
#pubsub_state_dev{
|
||||
id = {{U,S,undefined}, '_'},
|
||||
_ = '_'
|
||||
})).
|
||||
|
||||
%%
|
||||
-spec(presence_offline/8 ::
|
||||
(
|
||||
Host :: xmpp_jid:raw_jid_component_bare(),
|
||||
Entity :: xmpp_jid:usr_bare(),
|
||||
Table_Pubsub_State :: atom(),
|
||||
Pubsub_State :: mod_pubsub_dev:pubsub_state(),
|
||||
Table_Pubsub_Node :: atom(),
|
||||
Offline_Resource :: xmpp_jid:resource_jid(),
|
||||
Online_Resources :: boolean(),
|
||||
Pubsub_Features :: {Subscribe :: boolean(),
|
||||
Leased_Subscription :: boolean(),
|
||||
Purge_Nodes :: boolean(),
|
||||
Persistent_Items :: boolean(),
|
||||
Subscription_Notifications :: boolean()})
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
presence_offline(Host, Entity, Table_Pubsub_State, Pubsub_State,
|
||||
Table_Pubsub_Node, Offline_Resource, Online_Resources,
|
||||
{Subscribe, Leased_Subscription, Purge_Nodes, Persistent_Items,
|
||||
Subscription_Notifications}) ->
|
||||
case
|
||||
mnesia:dirty_index_read(Table_Pubsub_Node,
|
||||
Pubsub_State#pubsub_state_dev.nodeidx, idx)
|
||||
of
|
||||
[#pubsub_node_dev{id = {Pubsub_Host, NodeId}} = Pubsub_Node] ->
|
||||
Node_Options = Pubsub_Node#pubsub_node_dev.options,
|
||||
|
||||
case
|
||||
Subscribe == true
|
||||
andalso
|
||||
get_value(Node_Options, 'tempsub') == true
|
||||
of
|
||||
true ->
|
||||
case
|
||||
filter_tempsub_subscriptions(
|
||||
get_value(Node_Options, 'notify_sub'),
|
||||
Pubsub_State#pubsub_state_dev.subscriptions,
|
||||
Offline_Resource, Online_Resources, {[], []})
|
||||
of
|
||||
%%
|
||||
{_Unexpired_Subscriptions, [] = _Expired_Subscriptions} ->
|
||||
ok;
|
||||
%%
|
||||
{[] = _Unexpired_Subscriptions, Expired_Subscriptions}
|
||||
when Pubsub_State#pubsub_state_dev.affiliation == 'member'
|
||||
andalso Pubsub_State#pubsub_state_dev.itemids == [] ->
|
||||
mnesia:dirty_delete_object(Table_Pubsub_State,
|
||||
Pubsub_State),
|
||||
Notification_Type = get_value(Node_Options,
|
||||
'notification_type', 'headline'),
|
||||
notify_subscriptions(Host, Pubsub_Host,
|
||||
Entity, NodeId, Notification_Type,
|
||||
_Recipients = case Subscription_Notifications of
|
||||
true ->
|
||||
case
|
||||
lists:member(Entity,
|
||||
Pubsub_Node#pubsub_node_dev.owners)
|
||||
of
|
||||
true ->
|
||||
Pubsub_Node#pubsub_node_dev.owners;
|
||||
false ->
|
||||
[Entity
|
||||
| Pubsub_Node#pubsub_node_dev.owners]
|
||||
end;
|
||||
false ->
|
||||
Pubsub_Node#pubsub_node_dev.owners
|
||||
end,
|
||||
Expired_Subscriptions),
|
||||
ok;
|
||||
%%
|
||||
{Unexpired_Subscriptions, Expired_Subscriptions} ->
|
||||
mnesia:dirty_write(Table_Pubsub_State,
|
||||
Pubsub_State#pubsub_state_dev{
|
||||
subscriptions = Unexpired_Subscriptions
|
||||
}),
|
||||
Notification_Type = get_value(Node_Options,
|
||||
'notification_type', 'headline'),
|
||||
notify_subscriptions(Host, Pubsub_Host,
|
||||
Entity, NodeId, Notification_Type,
|
||||
_Recipients = case Subscription_Notifications of
|
||||
true ->
|
||||
case
|
||||
lists:member(Entity,
|
||||
Pubsub_Node#pubsub_node_dev.owners)
|
||||
of
|
||||
true ->
|
||||
Pubsub_Node#pubsub_node_dev.owners;
|
||||
false ->
|
||||
[Entity
|
||||
| Pubsub_Node#pubsub_node_dev.owners]
|
||||
end;
|
||||
false ->
|
||||
Pubsub_Node#pubsub_node_dev.owners
|
||||
end,
|
||||
Expired_Subscriptions),
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
case Subscribe == true andalso Leased_Subscription == true of
|
||||
true ->
|
||||
case
|
||||
filter_expired_subscriptions(
|
||||
get_value(Node_Options, 'notify_sub'),
|
||||
Pubsub_State#pubsub_state_dev.subscriptions,
|
||||
Offline_Resource, Online_Resources, {[], []})
|
||||
of
|
||||
%%
|
||||
{_Unexpired_Subscriptions, [] = _Expired_Subscriptions} ->
|
||||
ok;
|
||||
%%
|
||||
{[] = _Unexpired_Subscriptions, Expired_Subscriptions}
|
||||
when Pubsub_State#pubsub_state_dev.affiliation == 'member' ->
|
||||
mnesia:dirty_delete_object(Table_Pubsub_State,
|
||||
Pubsub_State),
|
||||
Notification_Type = get_value(Node_Options,
|
||||
'notification_type', 'headline'),
|
||||
notify_subscriptions(Host, Pubsub_Host,
|
||||
Entity, NodeId, Notification_Type,
|
||||
_Recipients = case
|
||||
Subscription_Notifications
|
||||
of
|
||||
true ->
|
||||
case
|
||||
lists:member(Entity,
|
||||
Pubsub_Node#pubsub_node_dev.owners)
|
||||
of
|
||||
true ->
|
||||
Pubsub_Node#pubsub_node_dev.owners;
|
||||
false ->
|
||||
[Entity
|
||||
|Pubsub_Node#pubsub_node_dev.owners]
|
||||
end;
|
||||
false ->
|
||||
Pubsub_Node#pubsub_node_dev.owners
|
||||
end,
|
||||
Expired_Subscriptions),
|
||||
ok;
|
||||
%%
|
||||
{Unexpired_Subscriptions, Expired_Subscriptions} ->
|
||||
mnesia:dirty_write(Table_Pubsub_State,
|
||||
Pubsub_State#pubsub_state_dev{
|
||||
subscriptions = Unexpired_Subscriptions
|
||||
}),
|
||||
Notification_Type = get_value(Node_Options,
|
||||
'notification_type', 'headline'),
|
||||
notify_subscriptions(Host, Pubsub_Host,
|
||||
Entity, NodeId, Notification_Type,
|
||||
_Recipients = case
|
||||
Subscription_Notifications
|
||||
of
|
||||
true ->
|
||||
case
|
||||
lists:member(Entity,
|
||||
Pubsub_Node#pubsub_node_dev.owners)
|
||||
of
|
||||
true ->
|
||||
Pubsub_Node#pubsub_node_dev.owners;
|
||||
false ->
|
||||
[Entity
|
||||
| Pubsub_Node#pubsub_node_dev.owners]
|
||||
end;
|
||||
false ->
|
||||
Pubsub_Node#pubsub_node_dev.owners
|
||||
end,
|
||||
Expired_Subscriptions),
|
||||
ok
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
end;
|
||||
[] ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%%
|
||||
-spec(notify_subscriptions/7 ::
|
||||
(
|
||||
Host :: xmpp_jid:raw_jid_component_bare(),
|
||||
Pubsub_Host :: exmpp_pubsub:host(),
|
||||
Entity :: xmpp_jid:usr_bare(),
|
||||
NodeId :: exmpp_pubsub:nodeId(),
|
||||
Notification_Type :: 'message' | 'headline',
|
||||
Recipients :: [Entity::xmpp_jid:usr_bare(),...],
|
||||
Subscriptions :: [Subscription::exmpp_pubsub:subscription(),...])
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
notify_subscriptions(Host, Pubsub_Host, {U,S,_} = _Entity, NodeId,
|
||||
Notification_Type, Recipients, Subscriptions) ->
|
||||
Pubsub_Component_Jid = jlib:make_jid(<<>>, Pubsub_Host, <<>>),
|
||||
Jids = [pubsub_tools:make_jid(Recipient) || Recipient <- Recipients],
|
||||
lists:foreach(fun
|
||||
({_, SubId, Resource, _}) ->
|
||||
Subscriber = jlib:jid_to_string({U,S,Resource}),
|
||||
lists:foreach(fun
|
||||
(Recipient) ->
|
||||
spawn(pubsub_broadcast, notify_subscription,
|
||||
[Host, NodeId, Pubsub_Component_Jid, Recipient,
|
||||
Notification_Type, {Subscriber, 'none', SubId}])
|
||||
end, Jids)
|
||||
end, Subscriptions).
|
||||
|
||||
%%
|
||||
filter_purged_offline_itemids([Publisher_ItemId | Publisher_ItemIds],
|
||||
Table_Pubsub_Item, NodeIdx, {U,S,R} = _Entity,
|
||||
{Node_ItemIds, Unpurged_ItemIds, Purged_ItemIds}) ->
|
||||
filter_purged_offline_itemids(Publisher_ItemIds, Table_Pubsub_Item, NodeIdx,
|
||||
{U,S,R},
|
||||
case
|
||||
mnesia:dirty_index_match_object(Table_Pubsub_Item,
|
||||
#pubsub_item_dev{
|
||||
id = {Publisher_ItemId, NodeIdx},
|
||||
nodeidx = NodeIdx,
|
||||
_ = '_'
|
||||
},
|
||||
nodeidx)
|
||||
of
|
||||
[#pubsub_item_dev{creation = {_DateTime, {U,S,_}}} = Pubsub_Item] ->
|
||||
mnesia:dirty_delete_object(Table_Pubsub_Item, Pubsub_Item);
|
||||
_ ->
|
||||
{Node_ItemIds,
|
||||
[Publisher_ItemId | Unpurged_ItemIds],
|
||||
Purged_ItemIds}
|
||||
end).
|
||||
|
||||
|
||||
%%
|
||||
-spec(filter_tempsub_subscriptions/5 ::
|
||||
(
|
||||
Notify_Sub :: boolean() | 'none',
|
||||
Subscriptions :: [] | exmpp_pubsub:subscriptions(),
|
||||
Offline_Resource :: xmpp_jid:resource_jid(),
|
||||
Online_Resources :: boolean(),
|
||||
Filtered_TempSub_Subscriptions :: {
|
||||
Unexpired_Subscriptions :: [] | exmpp_pubsub:subscriptions(),
|
||||
Expired_Subscriptions :: [] | exmpp_pubsub:subscriptions()
|
||||
})
|
||||
-> Filtered_TempSub_Subscriptions :: {
|
||||
Unexpired_Subscriptions :: [] | exmpp_pubsub:subscriptions(),
|
||||
Expired_Subscriptions :: [] | exmpp_pubsub:subscriptions()
|
||||
}
|
||||
).
|
||||
|
||||
filter_tempsub_subscriptions(_Notify_Sub, [] = _Subscriptions,
|
||||
_Offline_Resource, _Online_Resources, Filtered_TempSub_Subscriptions) ->
|
||||
Filtered_TempSub_Subscriptions;
|
||||
%%
|
||||
filter_tempsub_subscriptions(Notify_Sub,
|
||||
[{Subscription_State, SubId, Resource, Subscription_Options} | Subscriptions],
|
||||
Offline_Resource, Online_Resources,
|
||||
{Unexpired_Subscriptions, Expired_Subscriptions})
|
||||
when ((Online_Resources == false
|
||||
andalso
|
||||
(Resource == undefined orelse Resource == Offline_Resource))
|
||||
orelse
|
||||
(Online_Resources == true
|
||||
andalso
|
||||
Resource == Offline_Resource)) ->
|
||||
filter_tempsub_subscriptions(Notify_Sub, Subscriptions, Offline_Resource,
|
||||
Online_Resources,
|
||||
{Unexpired_Subscriptions,
|
||||
_Expired_Subscriptions = case Notify_Sub of
|
||||
true ->
|
||||
[{Subscription_State, SubId, Resource, Subscription_Options}
|
||||
| Expired_Subscriptions];
|
||||
_ ->
|
||||
Expired_Subscriptions
|
||||
end});
|
||||
%%
|
||||
filter_tempsub_subscriptions(Notify_Sub, [Subscription | Subscriptions],
|
||||
Offline_Resource, Online_Resources,
|
||||
{Unexpired_Subscriptions, Expired_Subscriptions}) ->
|
||||
filter_tempsub_subscriptions(Notify_Sub, Subscriptions, Offline_Resource,
|
||||
Online_Resources,
|
||||
{[Subscription | Unexpired_Subscriptions], Expired_Subscriptions}).
|
||||
|
||||
%%
|
||||
-spec(filter_expired_subscriptions/5 ::
|
||||
(
|
||||
Notify_Sub :: boolean() | 'none',
|
||||
Subscriptions :: [] | exmpp_pubsub:subscriptions(),
|
||||
Offline_Resource :: xmpp_jid:resource_jid(),
|
||||
Online_Resources :: boolean(),
|
||||
Filtered_Expired_Subscriptions :: {
|
||||
Unexpired_Subscriptions :: [] | exmpp_pubsub:subscriptions(),
|
||||
Expired_Subscriptions :: [] | exmpp_pubsub:subscriptions()
|
||||
})
|
||||
-> Filtered_Expired_Subscriptions :: {
|
||||
Unexpired_Subscriptions :: [] | exmpp_pubsub:subscriptions(),
|
||||
Expired_Subscriptions :: [] | exmpp_pubsub:subscriptions()
|
||||
}
|
||||
).
|
||||
|
||||
filter_expired_subscriptions(_Notify_Sub, [] = _Subscriptions, _Offline_Resource,
|
||||
_Online_Resources, Filtered_Expired_Subscriptions) ->
|
||||
Filtered_Expired_Subscriptions;
|
||||
%%
|
||||
filter_expired_subscriptions(Notify_Sub,
|
||||
[{Subscription_State, SubId, Resource, Subscription_Options} | Subscriptions],
|
||||
Offline_Resource, Online_Resources,
|
||||
{Unexpired_Subscriptions, Expired_Subscriptions})
|
||||
when Subscription_Options =/= []
|
||||
andalso ((Online_Resources == false
|
||||
andalso
|
||||
(Resource == undefined orelse Resource == Offline_Resource))
|
||||
orelse
|
||||
(Online_Resources == true
|
||||
andalso
|
||||
Resource == Offline_Resource)) ->
|
||||
filter_expired_subscriptions(Notify_Sub, Subscriptions, Offline_Resource,
|
||||
Online_Resources,
|
||||
case get_value(Subscription_Options, 'expire') of
|
||||
'presence' ->
|
||||
{Unexpired_Subscriptions,
|
||||
_Expired_Subscriptions = case Notify_Sub of
|
||||
true ->
|
||||
[{Subscription_State, SubId, Resource, Subscription_Options}
|
||||
| Expired_Subscriptions];
|
||||
false ->
|
||||
Expired_Subscriptions
|
||||
end};
|
||||
_ ->
|
||||
{[{Subscription_State, SubId, Resource, Subscription_Options}
|
||||
| Unexpired_Subscriptions],
|
||||
Expired_Subscriptions}
|
||||
end);
|
||||
%%
|
||||
filter_expired_subscriptions(Notify_Sub, [Subscription | Subscriptions],
|
||||
Offline_Resource, Online_Resources,
|
||||
{Unexpired_Subscriptions, Expired_Subscriptions}) ->
|
||||
filter_expired_subscriptions(Notify_Sub, Subscriptions, Offline_Resource,
|
||||
Online_Resources,
|
||||
{[Subscription | Unexpired_Subscriptions], Expired_Subscriptions}).
|
||||
|
||||
|
||||
%%
|
||||
roster_process_item(Roster_Item, Server) ->
|
||||
spawn(pubsub_groups, monitor_roster_groups, [Server, Roster_Item]),
|
||||
Roster_Item.
|
||||
|
||||
|
||||
|
||||
roster_in_subscription(Boolean, User, Server, Jid_Contact,
|
||||
'subscribed' = _Subscription_Type) ->
|
||||
spawn(pubsub_groups, monitor_contacts2,
|
||||
['subscribed', {User, Server, undefined}, Jid_Contact]),
|
||||
Boolean;
|
||||
roster_in_subscription(Boolean, _User, _Server, _JID, _SubscriptionType) ->
|
||||
Boolean.
|
||||
@@ -0,0 +1,113 @@
|
||||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%%
|
||||
%%% @copyright 2006-2013 ProcessOne
|
||||
%%% @author Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%% important note:
|
||||
%% new/1 and free/2 MUST be called inside a transaction bloc
|
||||
|
||||
-module(pubsub_index_dev).
|
||||
-author('christophe.romain@process-one.net').
|
||||
|
||||
-include("pubsub_dev.hrl").
|
||||
|
||||
-export([
|
||||
init/1,
|
||||
new/2,
|
||||
free/3
|
||||
]).
|
||||
|
||||
-import(pubsub_db_mnesia,
|
||||
[
|
||||
table/2
|
||||
]).
|
||||
|
||||
|
||||
%%
|
||||
-spec(init/1 ::
|
||||
(
|
||||
Suffix::atom()) -> 'ok'
|
||||
).
|
||||
|
||||
init(Suffix) ->
|
||||
mnesia:create_table(table('pubsub_index_dev', Suffix),
|
||||
[{disc_copies, [node()]},
|
||||
{record_name, pubsub_index_dev},
|
||||
{attributes, record_info(fields, pubsub_index_dev)}]).
|
||||
|
||||
%%
|
||||
-spec(new/2 ::
|
||||
(
|
||||
Suffix :: atom(),
|
||||
Index :: exmpp_pubsub:index())
|
||||
-> Idx::exmpp_pubsub:nodeIdx()
|
||||
).
|
||||
|
||||
new(Suffix, Index) ->
|
||||
case
|
||||
mnesia:read(Table_Pubsub_Index = table('pubsub_index_dev', Suffix),
|
||||
Index, write)
|
||||
of
|
||||
[#pubsub_index_dev{free = []} = Pubsub_Index] ->
|
||||
mnesia:write(Table_Pubsub_Index,
|
||||
Pubsub_Index#pubsub_index_dev{
|
||||
last = Idx = Pubsub_Index#pubsub_index_dev.last + 1
|
||||
},
|
||||
write),
|
||||
Idx;
|
||||
[#pubsub_index_dev{free = [Idx|Free]} = Pubsub_Index] ->
|
||||
mnesia:write(Table_Pubsub_Index,
|
||||
Pubsub_Index#pubsub_index_dev{
|
||||
free = Free
|
||||
},
|
||||
write),
|
||||
Idx;
|
||||
_ ->
|
||||
mnesia:write(Table_Pubsub_Index,
|
||||
#pubsub_index_dev{index = Index, last = 1, free = []}, write),
|
||||
1
|
||||
end.
|
||||
|
||||
%%
|
||||
-spec(free/3 ::
|
||||
(
|
||||
Suffix :: atom(),
|
||||
Index :: exmpp_pubsub:index(),
|
||||
Idx :: exmpp_pubsub:nodeIdx())
|
||||
-> 'ok'
|
||||
).
|
||||
|
||||
free(Suffix, Index, Idx) ->
|
||||
case
|
||||
mnesia:read(Table_Pubsub_Index = table('pubsub_index_dev', Suffix),
|
||||
Index, write)
|
||||
of
|
||||
[Pubsub_Index] ->
|
||||
mnesia:write(Table_Pubsub_Index,
|
||||
Pubsub_Index#pubsub_index_dev{
|
||||
free = [Idx | Pubsub_Index#pubsub_index_dev.free]
|
||||
},
|
||||
write);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,685 @@
|
||||
%%% ====================================================================
|
||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
||||
%%% compliance with the License. You should have received a copy of the
|
||||
%%% Erlang Public License along with this software. If not, it can be
|
||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
||||
%%%
|
||||
%%% Software distributed under the License is distributed on an "AS IS"
|
||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%%% the License for the specific language governing rights and limitations
|
||||
%%% under the License.
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2013, ProcessOne.
|
||||
%%%
|
||||
%%% @copyright 2006-2013 ProcessOne
|
||||
%%% @author Karim Gemayel <karim.gemayel@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%%% @headerfile "pubsub_dev.hrl"
|
||||
|
||||
-module(pubsub_tools).
|
||||
-author('karim.gemayel@process-one.net').
|
||||
|
||||
-compile(export_all).
|
||||
|
||||
-include("pubsub_dev.hrl").
|
||||
|
||||
jid_to_string({undefined, S, undefined}) ->
|
||||
jlib:jid_to_string({<<>>, S, <<>>});
|
||||
jid_to_string({undefined, S, R}) ->
|
||||
jlib:jid_to_string({<<>>, S, R});
|
||||
jid_to_string({U, S, undefined}) ->
|
||||
jlib:jid_to_string({U, S, <<>>});
|
||||
jid_to_string(USR) ->
|
||||
jlib:jid_to_string(USR).
|
||||
|
||||
make_jid({undefined, S, undefined}) ->
|
||||
jlib:make_jid(<<>>, S, <<>>);
|
||||
make_jid({U, S, undefined}) ->
|
||||
jlib:make_jid({U, S, <<>>});
|
||||
make_jid(Entity) ->
|
||||
jlib:make_jid(Entity).
|
||||
|
||||
%% @doc Determine if data is a JID
|
||||
-spec(is_jid/1 ::
|
||||
(
|
||||
Data :: binary() | undefined)
|
||||
-> xmpp_jid:entity() | {error, 'jid-malformed'}
|
||||
).
|
||||
|
||||
is_jid(Data) ->
|
||||
try jlib:string_to_jid(Data) of
|
||||
#jid{} = Jid -> Jid
|
||||
catch
|
||||
_Error -> {error, 'jid-malformed'}
|
||||
end.
|
||||
|
||||
%%%
|
||||
%-spec(get_value/2 ::
|
||||
%(
|
||||
% Options :: {Node_Options :: pubsub_options:options_node(),
|
||||
% Item_Options :: [] | pubsub_options:options_item()}
|
||||
% | pubsub_options:options_item()
|
||||
% | pubsub_options:options_node()
|
||||
% | pubsub_options:options_subscription()
|
||||
% | [],
|
||||
% Key :: atom())
|
||||
% -> Value :: atom()
|
||||
% | binary()
|
||||
% | boolean()
|
||||
% | non_neg_integer()
|
||||
% | undefined
|
||||
% | []
|
||||
% | 'none'
|
||||
% | [atom(),...]
|
||||
% | [binary(),...]
|
||||
% | [boolean(),...]
|
||||
% | [non_neg_integer(),...]
|
||||
%).
|
||||
|
||||
get_value({Node_Options, [] = _Item_Options}, Key) ->
|
||||
get_value(Node_Options, Key, 0);
|
||||
%%
|
||||
get_value({Node_Options, Item_Options}, Key) ->
|
||||
case get_value(Item_Options, Key, 'none') of
|
||||
'none' -> get_value(Node_Options, Key, false);
|
||||
Value -> Value
|
||||
end;
|
||||
%%
|
||||
get_value([], _Key) -> 'none';
|
||||
%%
|
||||
get_value(Options, Key) ->
|
||||
get_value(Options, Key, 'none').
|
||||
|
||||
%%%
|
||||
%-spec(get_value/3 ::
|
||||
%(
|
||||
% Options :: {Node_Options :: pubsub_options:options_node(),
|
||||
% Item_Options :: [] | pubsub_options:options_item()}
|
||||
% | pubsub_options:options_item()
|
||||
% | pubsub_options:options_node()
|
||||
% | pubsub_options:options_subscription(),
|
||||
% Key :: atom(),
|
||||
% Default :: term())
|
||||
% -> Value :: atom()
|
||||
% | binary()
|
||||
% | boolean()
|
||||
% | non_neg_integer()
|
||||
% | undefined
|
||||
% | []
|
||||
% | [atom(),...]
|
||||
% | [binary(),...]
|
||||
% | [boolean(),...]
|
||||
% | [non_neg_integer(),...]
|
||||
%).
|
||||
get_value({Node_Options, [] = _Item_Options}, Key, Default) ->
|
||||
get_value(Node_Options, Key, Default);
|
||||
%%
|
||||
get_value({Node_Options, Item_Options}, Key, Default) ->
|
||||
case get_value(Item_Options, Key, 'none') of
|
||||
'none' -> get_value(Node_Options, Key, Default);
|
||||
Value -> Value
|
||||
end;
|
||||
%%
|
||||
get_value(Options, Key, Default) ->
|
||||
case lists:keyfind(Key, 1, Options) of
|
||||
{_Key, Value} -> Value;
|
||||
false -> Default
|
||||
end.
|
||||
|
||||
%%%
|
||||
%-spec(set_value/3 ::
|
||||
%(
|
||||
% Options :: pubsub_options:options_item()
|
||||
% | pubsub_options:options_node()
|
||||
% | pubsub_options:options_subscription(),
|
||||
% Key :: atom(),
|
||||
% Value :: atom()
|
||||
% | binary()
|
||||
% | boolean()
|
||||
% | non_neg_integer()
|
||||
% | undefined
|
||||
% | []
|
||||
% | [atom(),...]
|
||||
% | [binary(),...]
|
||||
% | [boolean(),...]
|
||||
% | [non_neg_integer(),...])
|
||||
% -> Options :: pubsub_options:options_item()
|
||||
% | pubsub_options:options_node()
|
||||
% | pubsub_options:options_subscription()
|
||||
%).
|
||||
|
||||
set_value(Options, Key, Value) ->
|
||||
lists:keyreplace(Key, 1, Options, {Key, Value}).
|
||||
|
||||
%%
|
||||
|
||||
%-spec(get_option/2 ::
|
||||
%(
|
||||
% Options :: {Node_Options :: pubsub_options:options_node(),
|
||||
% Item_Options :: [] | pubsub_options:options_item()}
|
||||
% | pubsub_options:options_item()
|
||||
% | pubsub_options:options_node()
|
||||
% | pubsub_options:options_subscription()
|
||||
% | [],
|
||||
% Key :: atom())
|
||||
% -> Option :: pubsub_options:option_item()
|
||||
% | pubsub_options:option_node()
|
||||
% | pubsub_options:option_subscription()
|
||||
% | 'none'
|
||||
%).
|
||||
|
||||
get_option({Node_Options, [] = _Item_Options}, Key) ->
|
||||
get_option(Node_Options, Key, 'none');
|
||||
%%
|
||||
get_option({Node_Options, Item_Options}, Key) ->
|
||||
case get_option(Item_Options, Key, 'none') of
|
||||
'none' -> get_option(Node_Options, Key, 'none');
|
||||
Option -> Option
|
||||
end;
|
||||
%%
|
||||
get_option([], _Key) -> 'none';
|
||||
%%
|
||||
get_option(Options, Key) ->
|
||||
get_option(Options, Key, 'none').
|
||||
|
||||
%%%
|
||||
%-spec(get_option/3 ::
|
||||
%(
|
||||
% Options :: {Node_Options :: pubsub_options:options_node(),
|
||||
% Item_Options :: [] | pubsub_options:options_item()}
|
||||
% | pubsub_options:options_item()
|
||||
% | pubsub_options:options_node()
|
||||
% | pubsub_options:options_subscription(),
|
||||
% Key :: atom(),
|
||||
% Default :: term())
|
||||
% -> Option :: pubsub_options:option_item()
|
||||
% | pubsub_options:option_node()
|
||||
% | pubsub_options:option_subscription()
|
||||
% | term()
|
||||
%).
|
||||
|
||||
get_option({Node_Options, [] = _Item_Options}, Key, Default) ->
|
||||
get_option(Node_Options, Key, Default);
|
||||
%%
|
||||
get_option({Node_Options, Item_Options}, Key, Default) ->
|
||||
case get_option(Item_Options, Key, 'none') of
|
||||
'none' -> get_option(Node_Options, Key, Default);
|
||||
Option -> Option
|
||||
end;
|
||||
%%
|
||||
get_option(Options, Key, Default) ->
|
||||
case lists:keyfind(Key, 1, Options) of
|
||||
{_Key, Value} -> _Option = {Key, Value};
|
||||
false -> Default
|
||||
end.
|
||||
|
||||
%%
|
||||
-spec(get_entity_roster/1 ::
|
||||
(
|
||||
Entity :: xmpp_jid:usr_entity()
|
||||
| xmpp_jid:entity())
|
||||
-> Roster::[Roster_Item::#roster{}]
|
||||
).
|
||||
|
||||
get_entity_roster({U,S, _R} = _USR_Entity) ->
|
||||
_Roster = ejabberd_hooks:run_fold(roster_get, S, [], [{U,S}]);
|
||||
get_entity_roster(#jid{luser = U, lserver = S} = _Jid_Entity) ->
|
||||
_Roster = get_entity_roster({U,S, undefined}).
|
||||
|
||||
%%
|
||||
-spec(check_access_model/3 ::
|
||||
(
|
||||
Host :: xmpp_jid:raw_jid_component_bare(),
|
||||
Entity :: xmpp_jid:usr_entity(),
|
||||
Criteria :: {Access_Model :: pubsub_options:access_model(),
|
||||
Affiliation :: exmpp_pubsub:affiliation(),
|
||||
Subscriptions :: exmpp_pubsub:subscriptions(),
|
||||
Node_Owners :: [Node_Owner::xmpp_jid:usr_bare(),...],
|
||||
Rosters_Groups_Allowed :: pubsub_options:rosters_groups_allowed()})
|
||||
-> ok
|
||||
%%%
|
||||
| {error, 'forbidden'}
|
||||
).
|
||||
|
||||
check_access_model(_Host, _Entity,
|
||||
{_Access_Model, 'outcast' = _Affiliation, _Subscriptions, _Node_Owners,
|
||||
_Rosters_Groups_Allowed}) ->
|
||||
{error, 'forbidden'};
|
||||
%%
|
||||
check_access_model(_Host, _Entity,
|
||||
{'open' = _Access_Model, _Affiliation, _Subscriptions, _Node_Owners,
|
||||
_Rosters_Groups_Allowed}) ->
|
||||
ok;
|
||||
%%
|
||||
check_access_model(Host, Entity,
|
||||
{'presence' = _Access_Model, _Affiliation, _Subscriptions, Node_Owners,
|
||||
_Rosters_Groups_Allowed}) ->
|
||||
case is_contact_subscribed_to_node_owners(Host, Entity, Node_Owners) of
|
||||
false ->
|
||||
{error, 'forbidden'};
|
||||
_Node_Owner ->
|
||||
ok
|
||||
end;
|
||||
%%
|
||||
check_access_model(_Host, Entity,
|
||||
{'roster' = _Access_Model, _Affiliation, _Subscriptions, _Node_Owners,
|
||||
Rosters_Groups_Allowed}) ->
|
||||
case is_contact_in_allowed_roster_groups(Entity, Rosters_Groups_Allowed) of
|
||||
false ->
|
||||
{error, 'forbidden'};
|
||||
{_Node_Owner, _Roster_Group_Allowed} ->
|
||||
ok
|
||||
end;
|
||||
%%
|
||||
check_access_model(_Host, _Entity,
|
||||
{'whitelist' = _Access_Model, Affiliation, _Subscriptions, _Node_Owners,
|
||||
_Roster_Groups_Allowed})
|
||||
when Affiliation == 'member'
|
||||
%%orelse Affiliation == 'owner'
|
||||
orelse Affiliation == 'publish-only'
|
||||
orelse Affiliation == 'publisher' ->
|
||||
ok;
|
||||
check_access_model(_Host, _Entity,
|
||||
{'whitelist' = _Access_Model, _Affiliation, _Subscriptions, _Node_Owners,
|
||||
_Roster_Groups_Allowed}) ->
|
||||
{error, 'forbidden'};
|
||||
%%
|
||||
check_access_model(_Host, _Entity,
|
||||
{'authorize' = _Access_Model, _Affiliation, Subscriptions, _Node_Owners,
|
||||
_Roster_Groups_Allowed}) ->
|
||||
case has_subscriptions(Subscriptions) of
|
||||
true -> ok;
|
||||
false -> {error, 'forbidden'}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
%%
|
||||
-spec(check_access_model/7 ::
|
||||
(
|
||||
Host :: xmpp_jid:raw_jid_component_bare(),
|
||||
Entity :: xmpp_jid:usr_entity(),
|
||||
Access_Model :: pubsub_options:access_model(),
|
||||
Affiliation :: exmpp_pubsub:affiliation(),
|
||||
Subscriptions :: exmpp_pubsub:subscriptions(),
|
||||
Node_Owners :: [Node_Owner::xmpp_jid:usr_bare(),...],
|
||||
Rosters_Groups_Allowed :: pubsub_options:rosters_groups_allowed())
|
||||
-> ok
|
||||
%%%
|
||||
| {error, 'forbidden'}
|
||||
| {error, 'item-not-found'}
|
||||
).
|
||||
|
||||
check_access_model(_Host, _Entity, _Access_Model, 'owner' = _Affiliation,
|
||||
_Subscriptions, _Node_Owners, _Rosters_Groups_Allowed) ->
|
||||
ok;
|
||||
%%
|
||||
check_access_model(Host, Entity, Access_Model, 'outcast' = _Affiliation,
|
||||
_Subscriptions, Node_Owners, Rosters_Groups_Allowed) ->
|
||||
case Access_Model of
|
||||
%%
|
||||
'open'->
|
||||
{error, 'forbidden'};
|
||||
%%
|
||||
'presence' ->
|
||||
case
|
||||
is_contact_subscribed_to_node_owners(Host, Entity, Node_Owners)
|
||||
of
|
||||
false ->
|
||||
{error, 'item-not-found'};
|
||||
_Node_Owner ->
|
||||
{error, 'forbidden'}
|
||||
end;
|
||||
%%
|
||||
'roster' ->
|
||||
case
|
||||
is_contact_in_allowed_roster_groups(Entity,
|
||||
Rosters_Groups_Allowed)
|
||||
of
|
||||
false ->
|
||||
{error, 'item-not-found'};
|
||||
{_Node_Owner, _Roster_Group_Allowed} ->
|
||||
{error, 'forbidden'}
|
||||
end;
|
||||
%%
|
||||
'authorize' ->
|
||||
{error, 'forbidden'};
|
||||
%%
|
||||
'whitelist' ->
|
||||
{error, 'item-not-found'}
|
||||
end;
|
||||
%%
|
||||
check_access_model(_Host, _Entity, 'open' = _Access_Model, _Affiliation,
|
||||
_Subscriptions, _Node_Owners, _Rosters_Groups_Allowed) ->
|
||||
ok;
|
||||
%%
|
||||
check_access_model(Host, Entity, 'presence' = _Access_Model, _Affiliation,
|
||||
_Subscriptions, Node_Owners, _Rosters_Groups_Allowed) ->
|
||||
case is_contact_subscribed_to_node_owners(Host, Entity, Node_Owners) of
|
||||
false ->
|
||||
{error, 'item-not-found'};
|
||||
_Node_Owner ->
|
||||
ok
|
||||
end;
|
||||
%%
|
||||
check_access_model(_Host, Entity, 'roster' = _Access_Model, _Affiliation,
|
||||
_Subscriptions, _Node_Owners, Rosters_Groups_Allowed) ->
|
||||
case is_contact_in_allowed_roster_groups(Entity, Rosters_Groups_Allowed) of
|
||||
false ->
|
||||
{error, 'item-not-found'};
|
||||
{_Node_Owner, _Roster_Group_Allowed} ->
|
||||
ok
|
||||
end;
|
||||
%%
|
||||
check_access_model(_Host, _Entity, 'whitelist' = _Access_Model, Affiliation,
|
||||
_Subscriptions, _Node_Owners, _Roster_Groups_Allowed) ->
|
||||
case Affiliation of
|
||||
Affiliation
|
||||
when Affiliation == 'member'
|
||||
orelse Affiliation == 'publish-only'
|
||||
orelse Affiliation == 'publisher' ->
|
||||
ok;
|
||||
_Affiliation ->
|
||||
{error, 'item-not-found'}
|
||||
end;
|
||||
%%
|
||||
check_access_model(_Host, _Entity, 'authorize' = _Access_Model, _Affiliation,
|
||||
Subscriptions, _Node_Owners, _Roster_Groups_Allowed) ->
|
||||
case has_subscriptions(Subscriptions) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
{error, 'item-not-found'}
|
||||
end.
|
||||
|
||||
|
||||
-spec(check_publish_model/3 ::
|
||||
(
|
||||
Publish_Model :: pubsub_options:publish_model(),
|
||||
Affiliation :: exmpp_pubsub:affiliation(),
|
||||
Subscriptions :: exmpp_pubsub:subscriptions())
|
||||
-> ok
|
||||
%%%
|
||||
| {error, 'forbidden'}
|
||||
).
|
||||
|
||||
check_publish_model('open' = _Publish_Model, _Affiliation, _Subscriptions) ->
|
||||
ok;
|
||||
%%
|
||||
check_publish_model('publishers' = _Publish_Model, Affiliation, _Subscriptions) ->
|
||||
case Affiliation of
|
||||
Affiliation
|
||||
when Affiliation == 'owner'
|
||||
orelse Affiliation == 'publish-only'
|
||||
orelse Affiliation == 'publisher' ->
|
||||
ok;
|
||||
_Affiliation ->
|
||||
{error, 'forbidden'}
|
||||
end;
|
||||
%%
|
||||
check_publish_model('subscribers' = _Publish_Model, Affiliation, Subscriptions) ->
|
||||
case Affiliation of
|
||||
'owner' ->
|
||||
ok;
|
||||
_Affiliation ->
|
||||
case has_subscriptions(Subscriptions) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
{error, 'forbidden'}
|
||||
end
|
||||
end.
|
||||
|
||||
%%
|
||||
-spec(has_subscriptions/1 ::
|
||||
(
|
||||
Subscriptions :: exmpp_pubsub:subscriptions())
|
||||
-> Has_Subscriptions::boolean()
|
||||
).
|
||||
|
||||
has_subscriptions([] = _Subscriptions) ->
|
||||
false;
|
||||
has_subscriptions(
|
||||
[{'pending' = _Subscription_State, _SubId, _Resource, _Subscription_Options}]) ->
|
||||
false;
|
||||
has_subscriptions(_Subscriptions) ->
|
||||
true.
|
||||
|
||||
%%
|
||||
%% @doc Check if a contact is subscribed to at least one local node owner
|
||||
%% amongst a list of (remote and local) node owners
|
||||
-spec(is_contact_subscribed_to_node_owners/3 ::
|
||||
(
|
||||
Host :: xmpp_jid:raw_jid_component_bare(),
|
||||
Contact :: xmpp_jid:usr_entity(),
|
||||
Node_Owners :: [] | [Node_Owner::xmpp_jid:usr_bare(),...])
|
||||
-> Is_Contact_Subscribed_To_Node_Owners :: false | xmpp_jid:usr_bare()
|
||||
).
|
||||
|
||||
is_contact_subscribed_to_node_owners(_Host, _Contact, [] = _Node_Owners) ->
|
||||
false;
|
||||
%% The node owner is a local entity, check if the contact is subscribed to it
|
||||
is_contact_subscribed_to_node_owners(Host, Contact,
|
||||
[{_U, Host, _R} = Local_Node_Owner | Node_Owners] = _Node_Owners) ->
|
||||
_Is_Contact_Subscribed_To_Node_Owners = case
|
||||
is_contact_subscribed_to_entity(Contact,
|
||||
_Local_Node_Owner_Roster = get_entity_roster(Local_Node_Owner))
|
||||
of
|
||||
true ->
|
||||
Local_Node_Owner;
|
||||
false ->
|
||||
is_contact_subscribed_to_node_owners(Host, Contact, Node_Owners)
|
||||
end;
|
||||
%% The node owner is a remote entity, don't check if the contact is subscribed to it
|
||||
is_contact_subscribed_to_node_owners(Host, Contact,
|
||||
[_Remote_Node_Owner | Node_Owners] = _Node_Owners) ->
|
||||
is_contact_subscribed_to_node_owners(Host, Contact, Node_Owners).
|
||||
|
||||
%%
|
||||
%% @doc Check if an entity is in a #roster{}
|
||||
%% with #roster.subscription == 'from' or
|
||||
%% with #roster.subscription == 'both'
|
||||
-spec(is_contact_subscribed_to_entity/2 ::
|
||||
(
|
||||
Contact :: xmpp_jid:usr_entity(),
|
||||
Entity_Roster :: [] | [Roster_Item::#roster{},...])
|
||||
-> Is_Contact_Subscribed::boolean()
|
||||
).
|
||||
|
||||
is_contact_subscribed_to_entity(_Contact, [] = _Entity_Roster) ->
|
||||
_Is_Contact_Subscribed = false;
|
||||
%% Contact is included in a #roster{} item with
|
||||
%% a subscription of type 'from' and 'both' (i.e. is subscribed to it)
|
||||
is_contact_subscribed_to_entity({U, S, _R} = _Contact,
|
||||
[#roster{jid = {U, S, _}, subscription = Subscription} = _Roster_Item
|
||||
| _Roster_Items] = _Entity_Roster)
|
||||
when Subscription == 'from' orelse Subscription == 'both' ->
|
||||
_Is_Contact_Subscribed = true;
|
||||
%% Contact is not included in a #roster{} item
|
||||
%% or has a subscription of type different than 'from' and 'both'
|
||||
%% ( i.e. is not subscribed to it),
|
||||
%% check next roster items
|
||||
is_contact_subscribed_to_entity(Contact,
|
||||
[_Roster_Item | Roster_Items] = _Entity_Roster) ->
|
||||
_Is_Contact_Subscribed = is_contact_subscribed_to_entity(Contact,
|
||||
Roster_Items).
|
||||
|
||||
%%
|
||||
-spec(is_contact_in_allowed_roster_groups/2 ::
|
||||
(
|
||||
Contact :: xmpp_jid:usr_entity(),
|
||||
Rosters_Groups_Allowed :: pubsub_options:rosters_groups_allowed())
|
||||
-> Roster_Group_Allowed :: false
|
||||
| {Entity :: xmpp_jid:usr_bare(),
|
||||
Roster_Group :: pubsub_options:roster_group()}
|
||||
).
|
||||
|
||||
is_contact_in_allowed_roster_groups(_Contact, [] = _Rosters_Groups_Allowed) ->
|
||||
false;
|
||||
%%
|
||||
is_contact_in_allowed_roster_groups(Contact,
|
||||
[{Entity, Entity_Roster_Groups_Allowed} | Rosters_Groups_Allowed]
|
||||
= _Rosters_Groups_Allowed) ->
|
||||
case
|
||||
is_contact_in_allowed_roster_group(Contact,
|
||||
_Entity_Roster = get_entity_roster(Entity),
|
||||
Entity_Roster_Groups_Allowed)
|
||||
of
|
||||
false ->
|
||||
is_contact_in_allowed_roster_groups(Contact, Rosters_Groups_Allowed);
|
||||
Roster_Group ->
|
||||
{Entity, Roster_Group}
|
||||
end.
|
||||
|
||||
%%
|
||||
-spec(is_contact_in_allowed_roster_group/3 ::
|
||||
(
|
||||
Contact :: xmpp_jid:usr_entity(),
|
||||
Entity_Roster :: [] | [Roster_Item::#roster{groups::pubsub_options:roster_groups()}],
|
||||
Roster_Groups_Allowed :: [Roster_Group_Allowed::pubsub_options:roster_group(),...])
|
||||
-> Roster_Group :: false | pubsub_options:roster_group()
|
||||
).
|
||||
|
||||
is_contact_in_allowed_roster_group(_Contact, [] = _Entity_Roster,
|
||||
_Roster_Groups_Allowed) ->
|
||||
false;
|
||||
%%
|
||||
is_contact_in_allowed_roster_group({U, S, _R} = _Contact,
|
||||
[#roster{jid = {U, S, _}, subscription = Subscription, groups = Roster_Groups}
|
||||
| _Other_Roster_Items] = _Entity_Roster, Roster_Groups_Allowed)
|
||||
when Subscription == 'from' orelse Subscription == 'both' ->
|
||||
is_contact_in_allowed_roster_group(Roster_Groups, Roster_Groups_Allowed);
|
||||
%%
|
||||
is_contact_in_allowed_roster_group(Contact,
|
||||
[_Roster_Item | Roster_Items] = _Entity_Roster, Roster_Groups_Allowed) ->
|
||||
is_contact_in_allowed_roster_group(Contact, Roster_Items,
|
||||
Roster_Groups_Allowed).
|
||||
|
||||
%%
|
||||
-spec(is_contact_in_allowed_roster_group/2 ::
|
||||
(
|
||||
Roster_Groups :: [] | [Roster_Group::pubsub_options:roster_group()],
|
||||
Roster_Groups_Allowed :: [Roster_Group_Allowed::pubsub_options:roster_group(),...])
|
||||
-> Roster_Group :: false | pubsub_options:roster_group()
|
||||
).
|
||||
|
||||
is_contact_in_allowed_roster_group([] = _Roster_Groups, _Roster_Groups_Allowed) ->
|
||||
false;
|
||||
%%
|
||||
is_contact_in_allowed_roster_group([Roster_Group | Roster_Groups]
|
||||
= _Roster_Groups, Roster_Groups_Allowed) ->
|
||||
case lists:member(Roster_Group, Roster_Groups_Allowed) of
|
||||
true ->
|
||||
Roster_Group;
|
||||
false ->
|
||||
is_contact_in_allowed_roster_group(Roster_Groups,
|
||||
Roster_Groups_Allowed)
|
||||
end.
|
||||
|
||||
%%
|
||||
-spec(get_user_resources/2 ::
|
||||
(
|
||||
User :: binary(),
|
||||
Server :: binary())
|
||||
-> Resources::[Resource::xmpp_jid:resource_jid()]
|
||||
).
|
||||
|
||||
get_user_resources(User, Server) ->
|
||||
ejabberd_sm:get_user_resources(User, Server).
|
||||
|
||||
%%
|
||||
-spec(get_resources_show/2 ::
|
||||
(
|
||||
User :: binary(),
|
||||
Server :: binary())
|
||||
-> Resources_Show :: [{Resource :: xmpp_jid:resource_jid(),
|
||||
Show :: 'away' | 'chat' | 'dnd' | 'online' | 'xa' }]
|
||||
).
|
||||
|
||||
get_resources_show(User, Server) ->
|
||||
_Resources_Show = lists:foldl(fun
|
||||
(Resource, Resources_Show) ->
|
||||
case ejabberd_sm:get_session_pid(User, Server, Resource) of
|
||||
C2SPid when is_pid(C2SPid) ->
|
||||
case ejabberd_c2s:get_presence(C2SPid) of
|
||||
{_User, _Resource, Show, _} ->
|
||||
[{Resource, list_to_atom(Show)} | Resources_Show];
|
||||
_ ->
|
||||
Resources_Show
|
||||
end;
|
||||
_ ->
|
||||
Resources_Show
|
||||
end
|
||||
end, [], _Resources = ejabberd_sm:get_user_resources(User, Server)).
|
||||
|
||||
|
||||
%%
|
||||
-spec(rosters_groups_allowed_cache/2 ::
|
||||
(
|
||||
Rosters_Groups_Allowed :: pubsub_options:rosters_groups_allowed(),
|
||||
Rosters_Groups_Allowed_Cache :: pubsub_options:rosters_groups_allowed())
|
||||
-> Is_Roster_Groups_Cached::boolean()
|
||||
).
|
||||
|
||||
rosters_groups_allowed_cache(_Rosters_Groups_Allowed,
|
||||
[] = _Rosters_Groups_Allowed_Cache) ->
|
||||
false;
|
||||
%%
|
||||
rosters_groups_allowed_cache([] = _Rosters_Groups_Allowed,
|
||||
_Rosters_Groups_Allowed_Cache) ->
|
||||
false;
|
||||
%%
|
||||
rosters_groups_allowed_cache(
|
||||
[{Entity, Roster_Groups_Allowed} | Rosters_Groups_Allowed],
|
||||
Rosters_Groups_Allowed_Cache) ->
|
||||
case lists:keyfind(Entity, 1, Rosters_Groups_Allowed_Cache) of
|
||||
{_Entity, Roster_Groups_Allowed_Cache} ->
|
||||
case
|
||||
roster_groups_allowed_cache(Roster_Groups_Allowed,
|
||||
Roster_Groups_Allowed_Cache)
|
||||
of
|
||||
true ->
|
||||
true;
|
||||
false ->
|
||||
rosters_groups_allowed_cache(Rosters_Groups_Allowed,
|
||||
Rosters_Groups_Allowed_Cache)
|
||||
end;
|
||||
false ->
|
||||
rosters_groups_allowed_cache(Rosters_Groups_Allowed,
|
||||
Rosters_Groups_Allowed_Cache)
|
||||
end.
|
||||
|
||||
%%
|
||||
-spec(roster_groups_allowed_cache/2 ::
|
||||
(
|
||||
Roster_Groups_Allowed :: [Roster_Group_Allowed::pubsub_options:roster_group()],
|
||||
Roster_Groups_Allowed_Cache :: [Roster_Group_Allowed_Cache::pubsub_options:roster_group()])
|
||||
-> Is_Roster_Groups_Cached :: boolean()
|
||||
).
|
||||
|
||||
roster_groups_allowed_cache(_Roster_Groups_Allowed,
|
||||
[] = _Roster_Groups_Allowed_Cache) ->
|
||||
false;
|
||||
%%
|
||||
roster_groups_allowed_cache([] = _Roster_Groups_Allowed,
|
||||
_Roster_Groups_Allowed_Cache) ->
|
||||
false;
|
||||
%%
|
||||
roster_groups_allowed_cache([Roster_Group_Allowed | Roster_Groups_Allowed],
|
||||
Roster_Groups_Allowed_Cache) ->
|
||||
case lists:member(Roster_Group_Allowed, Roster_Groups_Allowed_Cache) of
|
||||
true ->
|
||||
true;
|
||||
false ->
|
||||
roster_groups_allowed_cache(Roster_Groups_Allowed,
|
||||
Roster_Groups_Allowed_Cache)
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
-module(xmpp_xdata).
|
||||
|
||||
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-compile(export_all).
|
||||
|
||||
xmlcdata(CData) ->
|
||||
{xmlcdata, CData}.
|
||||
|
||||
xmlattr(Name, Value) ->
|
||||
% #xmlattr{name = Name, value = Value}.
|
||||
{Name, Value}.
|
||||
|
||||
xmlattr_label(Label) ->
|
||||
xmlattr(<<"label">>, Label).
|
||||
|
||||
xmlattr_type(Type) ->
|
||||
xmlattr(<<"type">>, Type).
|
||||
|
||||
xmlattr_var(Var) ->
|
||||
xmlattr(<<"var">>, Var).
|
||||
|
||||
|
||||
xmlel(NS, Name, Attrs, Children) ->
|
||||
#xmlel{name = Name, attrs = [{<<"xmlns">>, NS} | Attrs], children = Children}.
|
||||
|
||||
xmlel(Name, Attrs, Children) ->
|
||||
xmlel(?NS_XDATA, Name, Attrs, Children).
|
||||
|
||||
xmlel_desc(CData) ->
|
||||
xmlel(<<"desc">>, [], [xmlcdata(CData)]).
|
||||
|
||||
xmlel_value(Value) ->
|
||||
xmlel(<<"value">>, [], [xmlcdata(Value)]).
|
||||
|
||||
xmlel_option({Value, Label}) ->
|
||||
xmlel(<<"option">>, [xmlattr_label(Label)], [xmlel_value(Value)]);
|
||||
xmlel_option(Value) ->
|
||||
xmlel(<<"option">>, [], [xmlel_value(Value)]).
|
||||
|
||||
xmlel_field(Var, Type, Values) ->
|
||||
xmlel(<<"field">>,
|
||||
[xmlattr_var(Var),
|
||||
xmlattr_type(Type)],
|
||||
[xmlel_value(Value) || Value <- Values]).
|
||||
|
||||
xmlel_field(Var, Type, Label, Values, Options) ->
|
||||
xmlel(<<"field">>,
|
||||
[xmlattr_var(Var),
|
||||
xmlattr_label(Label),
|
||||
xmlattr_type(Type)],
|
||||
[xmlel_option(Option) || Option <- Options]
|
||||
++
|
||||
[xmlel_value(Value) || Value <- Values]).
|
||||
|
||||
xmlel_field_boolean(Var, Label, Value) ->
|
||||
xmlel_field(Var, <<"boolean">>, Label, [Value], []).
|
||||
|
||||
%%
|
||||
xmlel_field_fixed(Var, Label, Value) ->
|
||||
xmlel_field(Var, <<"fixed">>, Label, [Value], []).
|
||||
|
||||
%%
|
||||
xmlel_field_hidden(Var, Value) ->
|
||||
xmlel_field(Var, <<"hidden">>, [Value]).
|
||||
|
||||
%%
|
||||
xmlel_field_list_single(Var, Label, Value, Options) ->
|
||||
xmlel_field(Var, <<"list-single">>, Label, [Value], Options).
|
||||
|
||||
%%
|
||||
xmlel_field_list_multi(Var, Label, Values, Options) ->
|
||||
xmlel_field(Var, <<"list-multi">>, Label, Values, Options).
|
||||
|
||||
%%
|
||||
xmlel_field_text_single(Var, Label, undefined = _Text) ->
|
||||
xmlel_field(Var, <<"text-single">>, Label, [], []);
|
||||
%%
|
||||
xmlel_field_text_single(Var, Label, Text) ->
|
||||
xmlel_field(Var, <<"text-single">>, Label, [Text], []).
|
||||
|
||||
%%
|
||||
xmlel_field_text_multi(Var, Label, Values) ->
|
||||
xmlel_field(Var, <<"text-multi">>, Label, Values, []).
|
||||
|
||||
%%
|
||||
xmlel_field_jid_multi(Var, Label, Jids) ->
|
||||
xmlel_field(Var, <<"jid-multi">>, Label, Jids, []).
|
||||
|
||||
%%
|
||||
xmlel_field_jid_single(Var, Label, Jid) ->
|
||||
xmlel_field(Var, <<"jid-single">>, Label, [Jid], []).
|
||||
|
||||
%%
|
||||
xmlel_x(Type, Fields) ->
|
||||
xmlel(<<"x">>, [xmlattr_type(Type)], Fields).
|
||||
|
||||
%%
|
||||
xmlel_title(Title) ->
|
||||
xmlel(<<"title">>, [], [xmlcdata(Title)]).
|
||||
|
||||
%%
|
||||
xmlel_instructions(Instructions) when is_list(Instructions) ->
|
||||
lists:map(fun xmlel_instructions/1, Instructions);
|
||||
%%
|
||||
xmlel_instructions(Instruction) when is_binary(Instruction) ->
|
||||
xmlel(<<"instructions">>, [], [xmlcdata(Instruction)]).
|
||||
+321
-351
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user