diff options
-rw-r--r-- | net-im/ejabberd/Manifest | 53 | ||||
-rw-r--r-- | net-im/ejabberd/ejabberd-1.1.2-r1.ebuild | 206 | ||||
-rw-r--r-- | net-im/ejabberd/ejabberd-1.1.2-r2.ebuild | 208 | ||||
-rw-r--r-- | net-im/ejabberd/files/check_pam.diff | 472 | ||||
-rw-r--r-- | net-im/ejabberd/files/ejabberd-1.1.2-mod-proxy.patch | 423 | ||||
-rw-r--r-- | net-im/ejabberd/files/ejabberd-1.1.2-statsdx-web.patch | 318 | ||||
-rw-r--r-- | net-im/ejabberd/files/ejabberd-1.1.2-statsdx.patch | 742 | ||||
-rw-r--r-- | net-im/ejabberd/files/ejabberd-1.1.2.initd | 59 | ||||
-rw-r--r-- | net-im/ejabberd/files/http_binding.patch | 1006 | ||||
-rw-r--r-- | net-im/ejabberd/files/mod_archive.erl | 695 | ||||
-rw-r--r-- | net-im/ejabberd/files/mod_irc_utf-8.patch | 11 | ||||
-rw-r--r-- | net-im/ejabberd/files/mod_pep.patch | 2319 |
12 files changed, 0 insertions, 6512 deletions
diff --git a/net-im/ejabberd/Manifest b/net-im/ejabberd/Manifest deleted file mode 100644 index 3638dacc..00000000 --- a/net-im/ejabberd/Manifest +++ /dev/null @@ -1,53 +0,0 @@ -AUX check_pam.diff 12905 RMD160 aee968e9d72be0b0cbc37852226db14cba67a4fe SHA1 92fd41da261025f3cb4af056e46db16391d5e6e1 SHA256 d8ec3dd0ff902c465535809a94e7bd3c34bd53c7d0a50a206150e688ac6386c9 -MD5 6b0c111316a61e615be8da892b596266 files/check_pam.diff 12905 -RMD160 aee968e9d72be0b0cbc37852226db14cba67a4fe files/check_pam.diff 12905 -SHA256 d8ec3dd0ff902c465535809a94e7bd3c34bd53c7d0a50a206150e688ac6386c9 files/check_pam.diff 12905 -AUX ejabberd-1.1.2-mod-proxy.patch 14771 RMD160 4a3d817fd103aebfb8e76fc9f23396b57da5f473 SHA1 023ab0fe368bd6de78ad21a977b1d5a09818cab3 SHA256 3c130d4d76fcc6e51014c4ef7db2939d3d6ee911f59fe17bd580ca914a07d6e3 -MD5 c9f267fcc007f5a0a92834fc63d9cb3e files/ejabberd-1.1.2-mod-proxy.patch 14771 -RMD160 4a3d817fd103aebfb8e76fc9f23396b57da5f473 files/ejabberd-1.1.2-mod-proxy.patch 14771 -SHA256 3c130d4d76fcc6e51014c4ef7db2939d3d6ee911f59fe17bd580ca914a07d6e3 files/ejabberd-1.1.2-mod-proxy.patch 14771 -AUX ejabberd-1.1.2-statsdx-web.patch 9287 RMD160 e98789d5b29f6fcb55c383e551239378c8ce3eb6 SHA1 4e35a28d7692ae3d36bd0c5590b482c9f8266309 SHA256 4036facbe4bd712784e5964b1e422b1f3f60aeff8ec1058b3358c45535b4e8d9 -MD5 ae2346c4c0a0540b3236a80a2bf12451 files/ejabberd-1.1.2-statsdx-web.patch 9287 -RMD160 e98789d5b29f6fcb55c383e551239378c8ce3eb6 files/ejabberd-1.1.2-statsdx-web.patch 9287 -SHA256 4036facbe4bd712784e5964b1e422b1f3f60aeff8ec1058b3358c45535b4e8d9 files/ejabberd-1.1.2-statsdx-web.patch 9287 -AUX ejabberd-1.1.2-statsdx.patch 24201 RMD160 e04d36746649e435a68c32f2705a27fc9107009e SHA1 6d621b54addfcdb047ec7b4314573ec4aa161ef9 SHA256 27dfeaa59ab901c9c08d7dca9fc667252cec64fa08d8e43adaa7e72f961f864f -MD5 a5fc80bf820a7d5ef93354895bd2928a files/ejabberd-1.1.2-statsdx.patch 24201 -RMD160 e04d36746649e435a68c32f2705a27fc9107009e files/ejabberd-1.1.2-statsdx.patch 24201 -SHA256 27dfeaa59ab901c9c08d7dca9fc667252cec64fa08d8e43adaa7e72f961f864f files/ejabberd-1.1.2-statsdx.patch 24201 -AUX ejabberd-1.1.2.initd 1298 RMD160 ec7b25205026521ec09f8595bbe6ae36c94bc55f SHA1 012122c9e930ec56257a625a4fd3bd28e140afd3 SHA256 69481e6f156e85c69038675283f818fe3fa92d0ab442aaf78831345969aa021a -MD5 13c4ef3cee491a8529dc8866347f0636 files/ejabberd-1.1.2.initd 1298 -RMD160 ec7b25205026521ec09f8595bbe6ae36c94bc55f files/ejabberd-1.1.2.initd 1298 -SHA256 69481e6f156e85c69038675283f818fe3fa92d0ab442aaf78831345969aa021a files/ejabberd-1.1.2.initd 1298 -AUX http_binding.patch 34601 RMD160 ce1305c772e81769d9165eaa00ef75c18ede05ba SHA1 3902127e05612bfe2a57efba54c057a5db911540 SHA256 1a31e631a8bdc25a221ac81a29d5573a671c64ee0cf56cc3e4839bb91fdf6bdd -MD5 75adcbc43ce318b2c30a5ab09aab21dc files/http_binding.patch 34601 -RMD160 ce1305c772e81769d9165eaa00ef75c18ede05ba files/http_binding.patch 34601 -SHA256 1a31e631a8bdc25a221ac81a29d5573a671c64ee0cf56cc3e4839bb91fdf6bdd files/http_binding.patch 34601 -AUX mod_archive.erl 29560 RMD160 6c628ca4f1d82cd826c2a4d11c87d6b3344f3f24 SHA1 b934e9a9ed1ab7dc700b589177dc9425aa535a6f SHA256 b46976ca86a43cb13ed71009a5fadf7a2ca231e51da83150cdff0d646fbed00f -MD5 c3c630a783edc22b0d81646f2c2eb829 files/mod_archive.erl 29560 -RMD160 6c628ca4f1d82cd826c2a4d11c87d6b3344f3f24 files/mod_archive.erl 29560 -SHA256 b46976ca86a43cb13ed71009a5fadf7a2ca231e51da83150cdff0d646fbed00f files/mod_archive.erl 29560 -AUX mod_irc_utf-8.patch 376 RMD160 74a370b992810607e6c61995c2e5c31623ce10d6 SHA1 502d263d8407c5137992a25e25a998574796292a SHA256 48f3d0de218de31fced3b6e9bd160981020921dfcf41a4990a0ea84c15802a06 -MD5 6a1fe58a1e40edea5131a7cabc020f73 files/mod_irc_utf-8.patch 376 -RMD160 74a370b992810607e6c61995c2e5c31623ce10d6 files/mod_irc_utf-8.patch 376 -SHA256 48f3d0de218de31fced3b6e9bd160981020921dfcf41a4990a0ea84c15802a06 files/mod_irc_utf-8.patch 376 -AUX mod_pep.patch 74047 RMD160 04ea7221b539038a3610a2d745667abbe4b39969 SHA1 4c072204fc46e928492336dcc587c725e14992b1 SHA256 8335979552f290078015347433b0eb1aaa7f59da8431874b4720cd99f7ef91cf -MD5 f6f334f668f70146c06dcdfcb55e0f40 files/mod_pep.patch 74047 -RMD160 04ea7221b539038a3610a2d745667abbe4b39969 files/mod_pep.patch 74047 -SHA256 8335979552f290078015347433b0eb1aaa7f59da8431874b4720cd99f7ef91cf files/mod_pep.patch 74047 -DIST ejabberd-1.1.1_httpbind.tar.gz 14496 RMD160 3dd17d39daf0ff58ed6572559dbc74faadc475cc SHA1 82d9c2b63b27d241fc08495347d979fbd000b66f SHA256 a5e48ce27b9691ef428f82959e6d487e6b293b60fbc415cc88a99bed476e720c -DIST ejabberd-1.1.2.tar.gz 836240 RMD160 e763752e6c5fb46c51b71e265ab2ceda6d043a0d SHA1 9e94bdbc10fee5b781405daf43a0b4abc4dee6c1 SHA256 029129a6bcb5d15dbccc5aa756f61c52692eb6882ec7aad0193aa940b6a20bb6 -DIST mod_presence-0.0.4.tar.gz 51874 RMD160 f9a724b4c6d58fc83c09f71eacb18b6b0ba77ea9 SHA1 c9a7f05939798bdaf61f9ca8da5c0ac80258afbd SHA256 0f3983a61ac779ddbadde94961db0ce724bff2fb23483654d1be0221a13c5a11 -EBUILD ejabberd-1.1.2-r1.ebuild 6047 RMD160 67cac92974a0abad21d0742691541d4e04763ba8 SHA1 c3f5dbd14928e4f3bae8743af7c0ca0a5466fae5 SHA256 8b3a13991e2a1b2a3e9d262acfa663f6e7c054596801e96359121a0e25b15f55 -MD5 97b595d6ef284930fda4e83563e6ef3f ejabberd-1.1.2-r1.ebuild 6047 -RMD160 67cac92974a0abad21d0742691541d4e04763ba8 ejabberd-1.1.2-r1.ebuild 6047 -SHA256 8b3a13991e2a1b2a3e9d262acfa663f6e7c054596801e96359121a0e25b15f55 ejabberd-1.1.2-r1.ebuild 6047 -EBUILD ejabberd-1.1.2-r2.ebuild 6209 RMD160 7a973a37daf3a730f25d241473a31fe7f862900d SHA1 a335c84fffdf2650300e293802036b0f5794974e SHA256 9f24c0974d49c61e6e0cb74485e4f4b557a86ee803a703eb6d6565746e54f57b -MD5 879774d13f6783428209f434c395d1c8 ejabberd-1.1.2-r2.ebuild 6209 -RMD160 7a973a37daf3a730f25d241473a31fe7f862900d ejabberd-1.1.2-r2.ebuild 6209 -SHA256 9f24c0974d49c61e6e0cb74485e4f4b557a86ee803a703eb6d6565746e54f57b ejabberd-1.1.2-r2.ebuild 6209 -MD5 30a6b40979271475494b8103ac79c2b4 files/digest-ejabberd-1.1.2-r1 497 -RMD160 93a5b27c67cf098e343f1e877df1bdb5b4fe05ff files/digest-ejabberd-1.1.2-r1 497 -SHA256 e90edb5f4836aa10b0e5490f1d9ed4d690c9dbd841ad566e03797f61f41f9024 files/digest-ejabberd-1.1.2-r1 497 -MD5 28991a43f2edef2f12f58b614f87d1a3 files/digest-ejabberd-1.1.2-r2 765 -RMD160 ab10e70d0a76179cfa03d7358b790ca12caa1484 files/digest-ejabberd-1.1.2-r2 765 -SHA256 3fb5d04a5f49ed371b8f835c393ea62c566c669d9ad1557073dbc3a04d11cc82 files/digest-ejabberd-1.1.2-r2 765 diff --git a/net-im/ejabberd/ejabberd-1.1.2-r1.ebuild b/net-im/ejabberd/ejabberd-1.1.2-r1.ebuild deleted file mode 100644 index 2ba43ad6..00000000 --- a/net-im/ejabberd/ejabberd-1.1.2-r1.ebuild +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright 1999-2006 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Header: $ - -inherit eutils multilib ssl-cert versionator autotools - -JABBER_ETC="/etc/jabber" -JABBER_RUN="/var/run/jabber" -JABBER_SPOOL="/var/spool/jabber" -JABBER_LOG="/var/log/jabber" - -E_MYSQL_V="5" -E_MYSQL_N="mysql" -E_MYSQL=${E_MYSQL_N}-${E_MYSQL_V} - -E_PGSQL_V="0.0.1" -E_PGSQL_N="pgsql-cvs" -E_PGSQL=${E_PGSQL_N}-${E_PGSQL_V} - -DESCRIPTION="The Erlang Jabber Daemon" -HOMEPAGE="http://ejabberd.jabber.ru/" -SRC_URI="http://process-one.net/en/projects/${PN}/download/${PV}/${P}.tar.gz - mod_presence? ( http://www.goryachev.org/jabber/mod_presence-0.0.4.tar.gz )" -LICENSE="GPL-2" -SLOT="0" -KEYWORDS="x86 ~amd64" -IUSE="${IUSE} mod_archive mod_irc mod_muc mod_pep mod_pubsub mod_presence mod_proxy statsdx ldap odbc pam web mysql postgres http_binding" - -DEPEND="${RDEPEND} - >=net-im/jabber-base-0.01 - >=dev-libs/expat-1.95 - >=dev-lang/erlang-10.2.0 - odbc? ( dev-db/unixODBC ) - ldap? ( =net-nds/openldap-2* ) - postgres? ( =dev-erl/${E_PGSQL} ) - mysql? ( =dev-erl/${E_MYSQL} ) - pam? ( sys-libs/pam)" -RDEPEND="postgres? ( dev-db/postgresql ) - mysql? ( dev-db/mysql )" - -RESTRICT="nomirror" - -PROVIDE="virtual/jabber-server" -S=${WORKDIR}/${P}/src - -src_unpack() { - unpack ${A} - cd ${S} - - MYSQL_P=$(best_version dev-db/mysql) - MYSQL_PV=${MYSQL_P/dev-db\/mysql-/} - - # - # If we have to work with MySQL 4.1 or greater, ejabberd's native - # MySQL-driver has to be patched to query "SET NAMES 'utf8'" on connecting - # the database. - # - if use mysql && \ - (( \ - [ $(get_major_version ${MYSQL_PV}) -eq 4 ] && \ - [ $(get_major_version $(get_after_major_version ${MYSQL_PV})) -ge 1] \ - ) \ - || \ - [ $(get_major_version ${MYSQL_PV}) -ge 5 ] \ - ); then - epatch ${FILESDIR}/${P}-mysql-connect-utf8.patch - fi - - if use statsdx; then - epatch ${FILESDIR}/${P}-statsdx.patch - epatch ${FILESDIR}/${P}-statsdx-web.patch - fi - - use pam && epatch ${FILESDIR}/check_pam.diff # http://ejabberd.jabber.ru/pam - use mod_proxy && epatch ${FILESDIR}/${P}-mod-proxy.patch # http://bugs.gentoo.org/show_bug.cgi?id=137724 - use mod_pep && epatch ${FILESDIR}/mod_pep.patch # http://www.dtek.chalmers.se/~henoch/text/ejabberd-pep.html - use http_binding && epatch ${FILESDIR}/http_binding.patch # http://www.jabber.ru/bugzilla/show_bug.cgi?id=91 | http://ejabberd.jabber.ru/mod_proxy65 - use mod_archive && cp ${FILESDIR}/mod_archive.erl ${S} # http://ejabberd.jabber.ru/mod_archive - - if use mod_presence; then - cp -r ${WORKDIR}/mod_presence/pixmaps ${S} - epatch ${WORKDIR}/mod_presence/mod_presence.diff - fi -} - -src_compile() { - local myconf - - if ! use mysql && ! use postgres && ! use odbc; then - myconf="--disable-odbc" - else - myconf="--enable-odbc" - fi - - eautoconf - econf ${myconf} \ - --enable-roster-gateway-workaround \ - $(use_enable mod_irc) \ - $(use_enable ldap eldap) \ - $(use_enable mod_muc) \ - $(use_enable mod_pubsub) \ - $(use_enable ssl tls) \ - $(use_enable web) \ - $(use_enable odbc) \ - $(use_enable mod_presence) \ - || die "econf failed" - - emake || die "compiling ejabberd core failed" -} - -src_install() { - make \ - DESTDIR=${D} \ - EJABBERDDIR=${D}/usr/$(get_libdir)/erlang/lib/${P} \ - ETCDIR=${D}${JABBER_ETC} \ - LOGDIR=${D}${JABBER_LOG} \ - install \ - || die "install failed" - - insinto /usr/share/doc/${PF} - use postgres && doins odbc/pg.sql - use mysql && doins odbc/mysql.sql - cd ${S}/.. - dodoc doc/release_notes_${PV}.txt - dohtml doc/*.{html,png} - - use postgres && { - pa="-pa /usr/$(get_libdir)/erlang/lib/${E_PGSQL}/ebin" - } - - use mysql && { - pa=${pa}" -pa /usr/$(get_libdir)/erlang/lib/${E_MYSQL}/ebin" - } - - # - # Create /usr/bin/ejabberd - # - cat <<EOF > ${T}/ejabberd -#!/bin/bash - -erl -pa /usr/$(get_libdir)/erlang/lib/${P}/ebin \\ - ${pa} \\ - -sname ejabberd \\ - -s ejabberd \\ - -ejabberd config \"${JABBER_ETC}/ejabberd.cfg\" \\ - log_path \"${JABBER_LOG}/ejabberd.log\" \\ - -kernel inetrc \"${JABBER_ETC}/inetrc\" \\ - -sasl sasl_error_logger \{file,\"${JABBER_LOG}/sasl.log\"\} \\ - -mnesia dir \"${JABBER_SPOOL}\" \\ - \$@ -EOF - - # - # Create /usr/bin/ejabberdctl - # - cat <<EOF > ${T}/ejabberdctl -#!/bin/sh - -exec env HOME=${JABBER_RUN} \\ - erl -pa /usr/$(get_libdir)/erlang/lib/${P}/ebin \\ - ${pa} \\ - -noinput \\ - -sname ejabberdctl \\ - -s ejabberd_ctl \\ - -extra \$@ -EOF - - dobin ${T}/ejabberdctl - dobin ${T}/ejabberd - - newinitd ${FILESDIR}/${P}.initd ${PN} - newconfd ${FILESDIR}/${P}.confd ${PN} - - insinto ${JABBER_ETC} - if use ssl; then - docert ssl - rm -f ${D}${JABBER_ETC}/ssl.{crt,csr,key} - fowners jabber:jabber ${JABBER_ETC}/ssl.pem - fi - doins ${FILESDIR}/inetrc -} - -pkg_postinst() { - if [ ! -e ${JABBER_ETC}/ejabberd.cfg ] - then - einfo "Configuration file has been installed in ${JABBER_ETC}/ejabberd.cfg." - einfo "Edit it according to your needs. For configuration instructions," - einfo "please see /usr/share/doc/${PF}/html/guide.html" - fi - if use ssl ; then - einfo "A script to generate a ssl key has been installed in" - einfo "${JABBER_ETC}/self-cert.sh . Use it and change the config file to" - einfo "point to the full path" - fi - if ! use web ; then - einfo "The web USE flag is off, this will disable the web admin interface," - einfo "if this was not the intention then add web to your USE flags." - fi - - if use modproxy ; then - einfo "mod_proxy enabled ! http://www.jabber.ru/bugzilla/show_bug.cgi?id=25" - fi - if use statsdx ; then - einfo "Advanced stats enabled ! http://ejabberd.jabber.ru/mod_statsdx" - fi -} diff --git a/net-im/ejabberd/ejabberd-1.1.2-r2.ebuild b/net-im/ejabberd/ejabberd-1.1.2-r2.ebuild deleted file mode 100644 index 39ca1373..00000000 --- a/net-im/ejabberd/ejabberd-1.1.2-r2.ebuild +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright 1999-2006 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Header: $ - -inherit eutils multilib ssl-cert versionator autotools - -JABBER_ETC="/etc/jabber" -JABBER_RUN="/var/run/jabber" -JABBER_SPOOL="/var/spool/jabber" -JABBER_LOG="/var/log/jabber" - -E_MYSQL_V="5" -E_MYSQL_N="mysql" -E_MYSQL=${E_MYSQL_N}-${E_MYSQL_V} - -E_PGSQL_V="0.0.1" -E_PGSQL_N="pgsql-cvs" -E_PGSQL=${E_PGSQL_N}-${E_PGSQL_V} - -DESCRIPTION="The Erlang Jabber Daemon" -HOMEPAGE="http://ejabberd.jabber.ru/" -SRC_URI="http://process-one.net/en/projects/${PN}/download/${PV}/${P}.tar.gz - mod_presence? ( http://www.goryachev.org/jabber/mod_presence-0.0.4.tar.gz ) - http_binding? ( http://mabber.com/static/ejabberd-1.1.1_httpbind.tar.gz )" -LICENSE="GPL-2" -SLOT="0" -KEYWORDS="x86 ~amd64" -IUSE="${IUSE} mod_archive mod_irc mod_muc mod_pep mod_pubsub mod_presence mod_proxy statsdx ldap odbc pam web mysql postgres http_binding" - -DEPEND="${RDEPEND} - >=net-im/jabber-base-0.01 - >=dev-libs/expat-1.95 - >=dev-lang/erlang-10.2.0 - odbc? ( dev-db/unixODBC ) - ldap? ( =net-nds/openldap-2* ) - pam? ( sys-libs/pam)" -RDEPEND="postgres? ( dev-db/postgresql ) - mysql? ( dev-db/mysql )" - -RESTRICT="nomirror" - -PROVIDE="virtual/jabber-server" -S=${WORKDIR}/${P}/src - -src_unpack() { - unpack ${A} - cd ${S} - - MYSQL_P=$(best_version dev-db/mysql) - MYSQL_PV=${MYSQL_P/dev-db\/mysql-/} - - # - # If we have to work with MySQL 4.1 or greater, ejabberd's native - # MySQL-driver has to be patched to query "SET NAMES 'utf8'" on connecting - # the database. - # - if use mysql && \ - (( \ - [ $(get_major_version ${MYSQL_PV}) -eq 4 ] && \ - [ $(get_major_version $(get_after_major_version ${MYSQL_PV})) -ge 1] \ - ) \ - || \ - [ $(get_major_version ${MYSQL_PV}) -ge 5 ] \ - ); then - epatch ${FILESDIR}/${P}-mysql-connect-utf8.patch - fi - - if use statsdx; then - epatch ${FILESDIR}/${P}-statsdx.patch - epatch ${FILESDIR}/${P}-statsdx-web.patch - fi - - epatch ${FILESDIR}/mod_irc_utf-8.patch - - use pam && epatch ${FILESDIR}/check_pam.diff # http://ejabberd.jabber.ru/pam - use mod_proxy && epatch ${FILESDIR}/${P}-mod-proxy.patch # http://bugs.gentoo.org/show_bug.cgi?id=137724 - use mod_pep && epatch ${FILESDIR}/mod_pep.patch # http://www.dtek.chalmers.se/~henoch/text/ejabberd-pep.html - #use http_binding && epatch ${WORKDIR}/ejabberd-1.1.1_httpbind/ejabberd-1.1.1_httpbind.patch.gz # http://blog.mabber.de/eintrag.php?id=34 - use http_binding && ( cd .. ; epatch ${FILESDIR}/http_binding.patch ;) # http://www.jabber.ru/bugzilla/show_bug.cgi?id=91 - use mod_archive && cp ${FILESDIR}/mod_archive.erl ${S} # http://ejabberd.jabber.ru/mod_archive - - if use mod_presence; then - cp -r ${WORKDIR}/mod_presence/pixmaps ${S} - epatch ${WORKDIR}/mod_presence/mod_presence.diff - fi -} - -src_compile() { - local myconf - - if ! use mysql && ! use postgres && ! use odbc; then - myconf="--disable-odbc" - else - myconf="--enable-odbc" - fi - - eautoconf - econf ${myconf} \ - --enable-roster-gateway-workaround \ - $(use_enable mod_irc) \ - $(use_enable ldap eldap) \ - $(use_enable mod_muc) \ - $(use_enable mod_pubsub) \ - $(use_enable ssl tls) \ - $(use_enable web) \ - $(use_enable odbc) \ - $(use_enable mod_presence) \ - || die "econf failed" - - emake || die "compiling ejabberd core failed" -} - -src_install() { - make \ - DESTDIR=${D} \ - EJABBERDDIR=${D}/usr/$(get_libdir)/erlang/lib/${P} \ - ETCDIR=${D}${JABBER_ETC} \ - LOGDIR=${D}${JABBER_LOG} \ - install \ - || die "install failed" - - insinto /usr/share/doc/${PF} - use postgres && doins odbc/pg.sql - use mysql && doins odbc/mysql.sql - cd ${S}/.. - dodoc doc/release_notes_${PV}.txt - dohtml doc/*.{html,png} - - use postgres && { - pa="-pa /usr/$(get_libdir)/erlang/lib/${E_PGSQL}/ebin" - } - - use mysql && { - pa=${pa}" -pa /usr/$(get_libdir)/erlang/lib/${E_MYSQL}/ebin" - } - - # - # Create /usr/bin/ejabberd - # - cat <<EOF > ${T}/ejabberd -#!/bin/bash - -erl -pa /usr/$(get_libdir)/erlang/lib/${P}/ebin \\ - ${pa} \\ - -sname ejabberd \\ - -s ejabberd \\ - -ejabberd config \"${JABBER_ETC}/ejabberd.cfg\" \\ - log_path \"${JABBER_LOG}/ejabberd.log\" \\ - -kernel inetrc \"${JABBER_ETC}/inetrc\" \\ - -sasl sasl_error_logger \{file,\"${JABBER_LOG}/sasl.log\"\} \\ - -mnesia dir \"${JABBER_SPOOL}\" \\ - \$@ -EOF - - # - # Create /usr/bin/ejabberdctl - # - cat <<EOF > ${T}/ejabberdctl -#!/bin/sh - -exec env HOME=${JABBER_RUN} \\ - erl -pa /usr/$(get_libdir)/erlang/lib/${P}/ebin \\ - ${pa} \\ - -noinput \\ - -sname ejabberdctl \\ - -s ejabberd_ctl \\ - -extra \$@ -EOF - - dobin ${T}/ejabberdctl - dobin ${T}/ejabberd - - newinitd ${FILESDIR}/${P}.initd ${PN} - newconfd ${FILESDIR}/${P}.confd ${PN} - - insinto ${JABBER_ETC} - if use ssl; then - docert ssl - rm -f ${D}${JABBER_ETC}/ssl.{crt,csr,key} - fowners jabber:jabber ${JABBER_ETC}/ssl.pem - fi - doins ${FILESDIR}/inetrc -} - -pkg_postinst() { - if [ ! -e ${JABBER_ETC}/ejabberd.cfg ] - then - einfo "Configuration file has been installed in ${JABBER_ETC}/ejabberd.cfg." - einfo "Edit it according to your needs. For configuration instructions," - einfo "please see /usr/share/doc/${PF}/html/guide.html" - fi - if use ssl ; then - einfo "A script to generate a ssl key has been installed in" - einfo "${JABBER_ETC}/self-cert.sh . Use it and change the config file to" - einfo "point to the full path" - fi - if ! use web ; then - einfo "The web USE flag is off, this will disable the web admin interface," - einfo "if this was not the intention then add web to your USE flags." - fi - - if use modproxy ; then - einfo "mod_proxy enabled ! http://www.jabber.ru/bugzilla/show_bug.cgi?id=25" - fi - if use statsdx ; then - einfo "Advanced stats enabled ! http://ejabberd.jabber.ru/mod_statsdx" - fi -} diff --git a/net-im/ejabberd/files/check_pam.diff b/net-im/ejabberd/files/check_pam.diff deleted file mode 100644 index b03a8803..00000000 --- a/net-im/ejabberd/files/check_pam.diff +++ /dev/null @@ -1,472 +0,0 @@ -Index: Makefile.in -=================================================================== ---- Makefile.in (revisión: 626) -+++ Makefile.in (copia de trabajo) -@@ -27,7 +27,7 @@ - - prefix = @prefix@ - --SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@ -+SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@ @pam@ - ERLSHLIBS = expat_erl.so - SOURCES = $(wildcard *.erl) - BEAMS = $(SOURCES:.erl=.beam) -Index: configure.ac -=================================================================== ---- configure.ac (revisión: 626) -+++ configure.ac (copia de trabajo) -@@ -16,6 +16,8 @@ - AM_WITH_EXPAT - #locating zlib - AM_WITH_ZLIB -+#locating zlib -+AM_WITH_ZLIB - - # Checks for typedefs, structures, and compiler characteristics. - AC_C_CONST -@@ -27,6 +29,7 @@ - AC_FUNC_MALLOC - AC_HEADER_STDC - -+AC_MOD_ENABLE(pam, yes) - AC_MOD_ENABLE(mod_pubsub, yes) - AC_MOD_ENABLE(mod_irc, yes) - AC_MOD_ENABLE(mod_muc, yes) -@@ -55,6 +58,7 @@ - AC_SUBST(db_type) - - AC_CONFIG_FILES([Makefile -+ $make_pam - $make_mod_irc - $make_mod_muc - $make_mod_pubsub -Index: pam/stress_pam.erl -=================================================================== ---- pam/stress_pam.erl (revisión: 0) -+++ pam/stress_pam.erl (revisión: 0) -@@ -0,0 +1,11 @@ -+-module(stress_pam). -+-export([start/0]). -+ -+start() -> -+ Port = pam:start(), -+ stress(Port). -+ -+stress(Port) -> -+ true = pam:check_password(Port, "user", "password"), -+ timer:sleep(200), -+ stress(Port). -Index: pam/Makefile.in -=================================================================== ---- pam/Makefile.in (revisión: 0) -+++ pam/Makefile.in (revisión: 0) -@@ -0,0 +1,35 @@ -+# $Id: Makefile.in 285 2004-11-05 21:14:31Z aleksey $ -+ -+CC = @CC@ -+CFLAGS = @CFLAGS@ @PAM_CFLAGS@ @ERLANG_CFLAGS@ -+CPPFLAGS = @CPPFLAGS@ -+LDFLAGS = @LDFLAGS@ -+LIBS = @LIBS@ @PAM_LIBS@ @ERLANG_LIBS@ -+ -+SUBDIRS = -+ -+ERLSHLIBS = ../pam_drv.so -+ -+OUTDIR = .. -+EFLAGS = -I .. -pz .. -+OBJS = \ -+ $(OUTDIR)/pam.beam -+ -+all: $(OBJS) $(ERLSHLIBS) -+ -+$(OUTDIR)/%.beam: %.erl -+ @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $< -+ -+$(ERLSHLIBS): ../%.so: %.c -+ $(CC) -Wall $(CFLAGS) $(LDFLAGS) \ -+ $(subst ../,,$(subst .so,.c,$@)) $(LIBS) \ -+ -o $@ -fpic -shared -+ -+clean: -+ rm -f $(OBJS) $(ERLSHLIBS) -+ -+distclean: clean -+ rm -f Makefile -+ -+TAGS: -+ etags *.erl -Index: pam/pam_drv.c -=================================================================== ---- pam/pam_drv.c (revisión: 0) -+++ pam/pam_drv.c (revisión: 0) -@@ -0,0 +1,235 @@ -+#include <stdio.h> -+#include <string.h> -+#include <erl_driver.h> -+#include <ei.h> -+#include <security/pam_appl.h> -+ -+typedef struct _PromptMatch -+{ -+ struct _PromptMatch* next; -+ char* prompt; -+ char* response; -+} *PromptMatch; -+ -+static PromptMatch _newMatch(PromptMatch list, char* prompt, char* response) -+{ -+ PromptMatch result = (PromptMatch)malloc(sizeof(struct _PromptMatch)); -+ memset(result, '\0', sizeof(struct _PromptMatch)); -+ result->prompt = prompt; -+ result->response = response; -+ if (list != NULL) -+ { -+ list->next = result; -+ return list; -+ } -+ else -+ { -+ return result; -+ } -+} -+ -+static char* _findMatch(PromptMatch list, const char* prompt) -+{ -+ PromptMatch cur = list; -+ while (cur != NULL) -+ { -+ if (strstr(prompt, cur->prompt) != 0) -+ { -+ return cur->response; -+ } -+ cur = cur->next; -+ } -+ return NULL; -+} -+ -+static void _cleanupMatch(PromptMatch list) -+{ -+ PromptMatch cur = list; -+ PromptMatch next; -+ while (cur != NULL) -+ { -+ free(cur->prompt); -+ free(cur->response); -+ next = cur->next; -+ free(cur); -+ cur = next; -+ } -+} -+ -+static int _handle_pam_conversation(int msgcnt, const struct pam_message** msgs, struct pam_response** res, -+ void* arg) -+{ -+ int i; -+ PromptMatch prompts = (PromptMatch)arg; -+ struct pam_response *reply; -+ -+ if(msgcnt <= 0) -+ { -+ return PAM_CONV_ERR; -+ } -+ -+ // Allocate a response for each message -+ reply = (struct pam_response *) malloc(sizeof(struct pam_response) * msgcnt); -+ memset(reply, 0, sizeof(struct pam_response) * msgcnt); -+ -+ // Walk each message from PAM and lookup the response corresponding to each prompt -+ for(i = 0; i < msgcnt; i++) -+ { -+ if(msgs[i]->msg_style == PAM_PROMPT_ECHO_OFF || msgs[i]->msg_style == PAM_PROMPT_ECHO_ON) -+ { -+ // Find the response for the current prompt; if none is found, warn -+ char* response = _findMatch(prompts, msgs[i]->msg); -+ if (response != NULL) -+ { -+ // Copy password into response -+ reply[i].resp = strdup(response); -+ reply[i].resp_retcode = 0; -+ } -+ else -+ { -+ printf("Warning: No prompt found for: %s\n", msgs[i]->msg); -+ reply[i].resp = strdup("Unexpected prompt"); -+ reply[i].resp_retcode = 0; -+ } -+ } -+ } -+ -+ *res = reply; -+ -+ return PAM_SUCCESS; -+} -+ -+#define DECODE_STRING(buffer, index, result) \ -+ { \ -+ int tmp = 0; int size = 0; \ -+ ei_get_type(buffer, index, &tmp, &size); \ -+ result = malloc(size + 1); \ -+ ei_decode_string(buffer, index, result); \ -+ } -+ -+#define RETURN_INT(value) \ -+ { \ -+ ErlDrvBinary* b = driver_alloc_binary(1); \ -+ rlen = 1; \ -+ b->orig_bytes[0] = value; \ -+ *rbuf = (char*)b; \ -+ return rlen; \ -+ } -+ -+static int pam_erl_start(ErlDrvPort port, char* command) -+{ -+ set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); -+ return 0; -+} -+ -+static int pam_erl_control(ErlDrvData drv_data, unsigned int command, -+ char *buf, int len, -+ char **rbuf, int rlen) -+{ -+ int tmp = 0; -+ int index = 0; -+ int size = 0; -+ char* username = 0; -+ char* password = 0; -+ -+ int i = 0; -+ int rc = 0; -+ struct pam_conv conv; -+ pam_handle_t* pam; -+ -+ // Parse the version and tuple header -+ ei_decode_version(buf, &index, &tmp); -+ ei_decode_tuple_header(buf, &index, &tmp); -+ if (tmp != 2) -+ { -+ printf("Incorrect number of arguments!\n"); -+ RETURN_INT(-1); -+ } -+ -+ // The first item in the tuple should be the username -+ DECODE_STRING(buf, &index, username); -+ -+ // The second item in the tuple should be a list of tuples -+ // Each tuple in the list consists of the expected prompt string -+ // and the value to return at that prompt -+ ei_decode_list_header(buf, &index, &size); -+ -+ // Loop over the list and pull out each prompt/response tuple and -+ // store it into a PromptItem list that will get processed in -+ // the PAM conversation -+ PromptMatch head = NULL; -+ for (i = 0; i < size; i++) -+ { -+ int tuplecnt = 0; -+ char* prompt = 0; -+ char* response = 0; -+ -+ ei_decode_tuple_header(buf, &index, &tuplecnt); -+ if (tuplecnt != 2) -+ { -+ printf("Incorrect number of arguments in prompt/response tuple: %d\n", tuplecnt); -+ _cleanupMatch(head); -+ free(username); -+ RETURN_INT(-2); -+ } -+ -+ // Decode the prompt from the tuple -+ DECODE_STRING(buf, &index, prompt); -+ DECODE_STRING(buf, &index, response); -+ -+ // Add a new prompt item to track this prompt/response pair -+ head = _newMatch(head, prompt, response); -+ } -+ -+ // Setup PAM conversation structure; pass in the prompt match list -+ // so we can process the prompts from PAM -+ conv.conv = _handle_pam_conversation; -+ conv.appdata_ptr = head; -+ -+ // Spin up PAM -+ rc = pam_start("login", username, &conv, &pam); -+ if (rc != PAM_SUCCESS) -+ { -+ printf("Unable to spin up PAM: %s\n", pam_strerror(NULL, rc)); -+ RETURN_INT(-3); -+ } -+ -+ // Attempt to authenticate the user -+ rc = pam_authenticate(pam, 0); -+ if (rc != PAM_SUCCESS) -+ { -+ pam_end(pam, rc); -+ printf("Authentication failed for: %s. Error: %s\n", username, pam_strerror(NULL, rc)); -+ free(password); -+ free(username); -+ RETURN_INT(1); -+ } -+ -+ pam_end(pam, rc); -+ -+ _cleanupMatch(head); -+ free(username); -+ -+ RETURN_INT(0); -+} -+ -+ErlDrvEntry pam_driver_entry = { -+ NULL, /* F_PTR init, N/A */ -+ pam_erl_start, /* L_PTR start, called when port is opened */ -+ NULL, /* F_PTR stop, called when port is closed */ -+ NULL, /* F_PTR output, called when erlang has sent */ -+ NULL, /* F_PTR ready_input, called when input descriptor ready */ -+ NULL, /* F_PTR ready_output, called when -+ output descriptor ready */ -+ "pam_drv", /* char *driver_name, the argument to open_port */ -+ NULL, /* F_PTR finish, called when unloaded */ -+ NULL, /* handle */ -+ pam_erl_control, /* F_PTR control, port_command callback */ -+ NULL, /* F_PTR timeout, reserved */ -+ NULL /* F_PTR outputv, reserved */ -+}; -+ -+DRIVER_INIT(pam_drv) /* must match name in driver_entry */ -+{ -+ return &pam_driver_entry; -+} -Index: pam/pam_test.c -=================================================================== ---- pam/pam_test.c (revisión: 0) -+++ pam/pam_test.c (revisión: 0) -@@ -0,0 +1,68 @@ -+#include <stdio.h> -+#include <string.h> -+#include <security/pam_appl.h> -+ -+static int _handle_pam_conversation(int msgcnt, const struct pam_message** msgs, struct pam_response** res, -+ void* arg) -+{ -+ int i; -+ struct pam_response *reply; -+ -+ if(msgcnt <= 0) -+ { -+ return PAM_CONV_ERR; -+ } -+ -+ // Allocate a response for each message -+ reply = (struct pam_response *) malloc(sizeof(struct pam_response) * msgcnt); -+ memset(reply, 0, sizeof(struct pam_response) * msgcnt); -+ -+ // Walk each message from PAM and lookup the response corresponding to each prompt -+ for(i = 0; i < msgcnt; i++) -+ { -+ printf("PAM Message(%d): %s\n", msgs[i]->msg_style, msgs[i]->msg); -+ reply[i].resp = strdup("Unexpected prompt"); -+ reply[i].resp_retcode = 0; -+ } -+ -+ *res = reply; -+ -+ return PAM_SUCCESS; -+} -+ -+ -+int main(int args, char** argv) -+{ -+ struct pam_conv conv; -+ pam_handle_t* pam; -+ int rc; -+ -+ if (args < 3) -+ { -+ printf("Please provide a username and service as the first and second arguments!\n"); -+ return -1; -+ } -+ -+ char* username = argv[1]; -+ char* service = argv[2]; -+ -+ // Setup PAM conversation structure; pass in the prompt match list -+ // so we can process the prompts from PAM -+ conv.conv = _handle_pam_conversation; -+ conv.appdata_ptr = 0; -+ -+ // Spin up PAM -+ rc = pam_start(service, username, &conv, &pam); -+ if (rc != PAM_SUCCESS) -+ { -+ printf("Unable to spin up PAM: %s\n", pam_strerror(NULL, rc)); -+ return -3; -+ } -+ -+ // Attempt to authenticate the user -+ rc = pam_authenticate(pam, 0); -+ -+ pam_end(pam, rc); -+ -+ return 0; -+} -Index: pam/pam.erl -=================================================================== ---- pam/pam.erl (revisión: 0) -+++ pam/pam.erl (revisión: 0) -@@ -0,0 +1,19 @@ -+-module(pam). -+-author('dizzyd@dizzyd.com'). -+ -+-export([start/0, -+ check_password/3]). -+ -+start() -> -+ ok = erl_ddll:load_driver(ejabberd:get_so_path(), pam_drv), -+ open_port({spawn, pam_drv}, [binary]). -+ -+check_password(Port, Username, Password) -> -+ Bin = term_to_binary({Username, [{"Password:", Password}]}), -+ Res = port_control(Port, 0, Bin), -+ case Res of -+ <<0>> -> -+ true; -+ _ -> -+ false -+ end. -Index: aclocal.m4 -=================================================================== ---- aclocal.m4 (revisión: 626) -+++ aclocal.m4 (copia de trabajo) -@@ -292,3 +292,32 @@ - fi - ]) - dnl <openssl/> -+ -+AC_DEFUN(AM_WITH_PAM, -+[ AC_ARG_WITH(pam, [ --with-pam=PREFIX prefix where PAM is installed ]) -+unset PAM_LIBS; -+unset PAM_CFLAGS; -+ -+if test x"$with_pam" != x; then -+ PAM_CFLAGS="-I$with_pam/include" -+ PAM_LIBS="-L$with_pam/lib" -+fi -+ -+AC_CHECK_LIB(pam, pam_start, -+ [ PAM_LIBS="$PAM_LIBS -lpam" pam_found=yes ], -+ [ pam_found=no ], -+ "$PAM_LIBS") -+ -+if test $pam_found = yes; then -+ pam_save_CFLAGS="$CFLAGS" -+ CFLAGS="$PAM_CFLAGS $CFLAGS" -+ AC_CHECK_HEADERS(security/pam_appl.h, ,$pam_found=no) -+ if test $pam_found = no; then -+ AC_MSG_ERROR([Could not find security/pam_appl.h]) -+ fi -+ CFLAGS=$pam_save_CFLAGS -+ -+ AC_SUBST(PAM_CFLAGS) -+ AC_SUBST(PAM_LIBS) -+fi -+]) diff --git a/net-im/ejabberd/files/ejabberd-1.1.2-mod-proxy.patch b/net-im/ejabberd/files/ejabberd-1.1.2-mod-proxy.patch deleted file mode 100644 index 1981aee1..00000000 --- a/net-im/ejabberd/files/ejabberd-1.1.2-mod-proxy.patch +++ /dev/null @@ -1,423 +0,0 @@ -diff -Naur ejabberd-1.1.1/src/ejabberd.cfg.example ejabberd-1.1.1-r1/src/ejabberd.cfg.example ---- ejabberd-1.1.1/src/ejabberd.cfg.example 2006-04-22 15:50:30.000000000 +0200 -+++ ejabberd-1.1.1-r1/src/ejabberd.cfg.example 2006-06-23 09:38:17.000000000 +0200 -@@ -125,6 +125,7 @@ - {max_stanza_size, 131072} - ]}, - {5280, ejabberd_http, [http_poll, web_admin]}, -+ %{7777, proxy65_listener, [{shaper, c2s_shaper}]}, - {8888, ejabberd_service, [{access, all}, - {hosts, ["icq.localhost", "sms.localhost"], - [{password, "secret"}]}]} -@@ -171,6 +172,11 @@ - {mod_pubsub, []}, - {mod_time, []}, - {mod_last, []}, -+ %% Simple configuration (hostname:7777) -+ %%{mod_proxy65, []}, -+ %% Several possible hostnames -+ %%{mod_proxy65, [{access, all}, -+ %% {streamhosts, [{"example.com", 7777}, {"192.168.0.42", 7777}]}]}, - {mod_version, []} - ]}. - -diff -Naur ejabberd-1.1.1/src/jlib.hrl ejabberd-1.1.1-r1/src/jlib.hrl ---- ejabberd-1.1.1/src/jlib.hrl 2006-01-20 17:21:39.000000000 +0100 -+++ ejabberd-1.1.1-r1/src/jlib.hrl 2006-06-23 09:38:17.000000000 +0200 -@@ -34,6 +34,7 @@ - -define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner"). - -define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info"). - -define(NS_COMMANDS, "http://jabber.org/protocol/commands"). -+-define(NS_BYTESTREAMS, "http://jabber.org/protocol/bytestreams"). - - -define(NS_EJABBERD_CONFIG, "ejabberd:config"). - -diff -Naur ejabberd-1.1.1/src/mod_proxy65.erl ejabberd-1.1.1-r1/src/mod_proxy65.erl ---- ejabberd-1.1.1/src/mod_proxy65.erl 1970-01-01 01:00:00.000000000 +0100 -+++ ejabberd-1.1.1-r1/src/mod_proxy65.erl 2006-06-23 09:38:17.000000000 +0200 -@@ -0,0 +1,189 @@ -+%%%---------------------------------------------------------------------- -+%%% File : mod_proxy65.erl -+%%% Author : Magnus Henoch <henoch@dtek.chalmers.se> -+%%% Purpose : Handle Jabber communications for JEP-0065 proxy -+%%% Created : 27 Dec 2005 by Magnus Henoch <henoch@dtek.chalmers.se> -+%%% Id : $Id: ejabberd_c2s.erl 440 2005-11-22 18:00:56Z alexey $ -+%%%---------------------------------------------------------------------- -+ -+-module(mod_proxy65). -+-author('henoch@dtek.chalmers.se'). -+-vsn('$Revision$ '). -+ -+-behaviour(gen_mod). -+ -+-export([start/2, -+ init/1, -+ stop/1]). -+ -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). -+ -+-record(proxy65_connection, {cookie, firstpid = none, secondpid = none}). -+ -+-record(proxy65_options, {host, access, streamhosts}). -+ -+-define(PROCNAME, ejabberd_mod_proxy65). -+ -+start(Host, Opts) -> -+ mnesia:create_table(proxy65_connection, -+ [{ram_copies, [node()]}, -+ {attributes, record_info(fields, proxy65_connection)}]), -+ MyHost = gen_mod:get_opt(host, Opts, "proxy." ++ Host), -+ Access = gen_mod:get_opt(access, Opts, all), -+ Streamhosts = gen_mod:get_opt(streamhosts, Opts, [{Host, 7777}]), -+ -+ register(gen_mod:get_module_proc(Host, ?PROCNAME), -+ spawn(?MODULE, init, [#proxy65_options{host = MyHost, access = Access, -+ streamhosts = Streamhosts}])). -+ -+ -+stop(Host) -> -+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME), -+ Proc ! stop, -+ {wait, Proc}. -+ -+init(#proxy65_options{host = Host} = Opts) -> -+ ejabberd_router:register_route(Host), -+ loop(Opts). -+ -+loop(#proxy65_options{host = Host} = Opts) -> -+ receive -+ {route, From, To, Packet} -> -+ case catch do_route(Opts, From, To, Packet) of -+ {'EXIT', Reason} -> -+ ?ERROR_MSG("~p", [Reason]); -+ _ -> -+ ok -+ end, -+ loop(Opts); -+ stop -> -+ ejabberd_router:unregister_route(Host), -+ ok; -+ _ -> -+ loop(Opts) -+ end. -+ -+do_route(#proxy65_options{host = Host, access = Access} = Opts, -+ From, To, Packet) -> -+ case acl:match_rule(Host, Access, From) of -+ allow -> -+ do_route1(Opts, From, To, Packet); -+ _ -> -+ {xmlelement, _Name, Attrs, _Els} = Packet, -+ Lang = xml:get_attr_s("xml:lang", Attrs), -+ ErrText = "Access denied by service policy", -+ Err = jlib:make_error_reply(Packet, -+ ?ERRT_FORBIDDEN(Lang, ErrText)), -+ ejabberd_router:route(To, From, Err) -+ end. -+ -+do_route1(#proxy65_options{host = Host, streamhosts = Streamhosts}, From, To, Packet) -> -+ {xmlelement, Name, _Attrs, _Els} = Packet, -+ case Name of -+ "iq" -> -+ case jlib:iq_query_info(Packet) of -+ #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, -+ lang = Lang, sub_el = SubEl} = IQ -> -+ Node = xml:get_tag_attr_s("node", SubEl), -+ if Node == [] -> -+ Res = IQ#iq{type = result, -+ sub_el = [{xmlelement, "query", -+ [{"xmlns", XMLNS}], -+ [{xmlelement, "identity", -+ [{"category", "proxy"}, -+ {"type", "bytestreams"}, -+ {"name", translate:translate(Lang, "SOCKS5 bytestreams proxy")}], -+ []}, -+ {xmlelement, "feature", -+ [{"var", ?NS_BYTESTREAMS}], []}]}]}; -+ true -> -+ Res = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND) -+ end; -+ #iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS} = IQ -> -+ Res = IQ#iq{type = result, -+ sub_el = [{xmlelement, "query", -+ [{"xmlns", XMLNS}], []}]}; -+ #iq{type = get, xmlns = ?NS_VERSION} = IQ -> -+ OSType = case os:type() of -+ {Osfamily, Osname} -> -+ atom_to_list(Osfamily) ++ "/" ++ -+ atom_to_list(Osname); -+ Osfamily -> -+ atom_to_list(Osfamily) -+ end, -+ OSVersion = case os:version() of -+ {Major, Minor, Release} -> -+ lists:flatten( -+ io_lib:format("~w.~w.~w", -+ [Major, Minor, Release])); -+ VersionString -> -+ VersionString -+ end, -+ OS = OSType ++ " " ++ OSVersion, -+ Res = IQ#iq{type = result, -+ sub_el = [{xmlelement, "query", -+ [{"xmlns", ?NS_VERSION}], -+ [{xmlelement, "name", [], -+ [{xmlcdata, "ejabberd mod_proxy65 (unofficial)"}]}, -+ {xmlelement, "version", [], -+ [{xmlcdata, "0.1"}]}, -+ {xmlelement, "os", [], -+ [{xmlcdata, OS}]} -+ ]}]}; -+ #iq{type = get, xmlns = ?NS_BYTESTREAMS = XMLNS} = IQ -> -+ Res = IQ#iq{type = result, -+ sub_el = [{xmlelement, "query", -+ [{"xmlns", XMLNS}], -+ return_streamhosts(Host, Streamhosts)}]}; -+ #iq{type = set, xmlns = ?NS_BYTESTREAMS} = IQ -> -+ Res = activate(Packet, From, To, IQ); -+ _ -> -+ Res = jlib:make_error_reply(Packet, ?ERR_FEATURE_NOT_IMPLEMENTED) -+ end, -+ case Res of -+ #iq{} -> -+ ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); -+ _ -> -+ ejabberd_router:route(To, From, Res) -+ end; -+ _ -> -+ ejabberd_router:route(To, From, jlib:make_error_reply(Packet, ?ERR_FEATURE_NOT_IMPLEMENTED)) -+ end. -+ -+return_streamhosts(_JID, []) -> -+ []; -+return_streamhosts(JID, [{Host, Port} | Streamhosts]) -> -+ %% This is not tail-recursive, but it doesn't matter. -+ [{xmlelement, "streamhost", -+ [{"jid", JID}, -+ {"host", Host}, -+ {"port", integer_to_list(Port)}], -+ []} | return_streamhosts(JID, Streamhosts)]. -+ -+activate(Packet, From, _To, #iq{sub_el = SubEl} = IQ) -> -+ case SubEl of -+ {xmlelement, "query", Attrs, _SubEls} -> -+ Sid = xml:get_attr_s("sid", Attrs), -+ ActivateTag = xml:get_subtag(SubEl, "activate"), -+ if ActivateTag /= false -> -+ TargetJID = jlib:string_to_jid(xml:get_tag_cdata(ActivateTag)); -+ true -> -+ TargetJID = false -+ end, -+ -+ if Sid /= [], TargetJID /= false, TargetJID /= error -> -+ case proxy65_listener:activate(From, TargetJID, Sid) of -+ ok -> -+ ?INFO_MSG("Activated connection between ~s and ~s", -+ [jlib:jid_to_string(From), TargetJID]), -+ IQ#iq{type = result, sub_el = []}; -+ _ -> -+ jlib:make_error_reply(Packet, ?ERR_INTERNAL_SERVER_ERROR) -+ end; -+ true -> -+ jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST) -+ end; -+ _ -> -+ jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST) -+ end. -diff -Naur ejabberd-1.1.1/src/proxy65_listener.erl ejabberd-1.1.1-r1/src/proxy65_listener.erl ---- ejabberd-1.1.1/src/proxy65_listener.erl 1970-01-01 01:00:00.000000000 +0100 -+++ ejabberd-1.1.1-r1/src/proxy65_listener.erl 2006-06-23 09:38:17.000000000 +0200 -@@ -0,0 +1,192 @@ -+%%%---------------------------------------------------------------------- -+%%% File : proxy65_listener.erl -+%%% Author : Magnus Henoch <henoch@dtek.chalmers.se> -+%%% Purpose : Handle SOCKS5 connections for JEP-0065 proxy -+%%% Created : 27 Dec 2005 by Magnus Henoch <henoch@dtek.chalmers.se> -+%%% Id : $Id: ejabberd_c2s.erl 440 2005-11-22 18:00:56Z alexey $ -+%%%---------------------------------------------------------------------- -+ -+-module(proxy65_listener). -+-author('henoch@dtek.chalmers.se'). -+-vsn('$Revision$ '). -+ -+-export([start/2, handle_connection/2, activate/3]). -+ -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). -+ -+-record(proxy65_connection, {cookie, firstpid = none, secondpid = none}). -+ -+start({SockMod, Socket}, Opts) -> -+ {ok, proc_lib:spawn(?MODULE, handle_connection, [{SockMod, Socket}, Opts])}. -+ -+read_bytes(_SockOpts, 0, Data) -> -+ lists:flatten(Data); -+read_bytes({SockMod, Socket} = SockOpts, N, Data) -> -+ Timeout = 1000, -+ case SockMod:recv(Socket, N, Timeout) of -+ {error, closed} -> -+ %% On closed connection, return everything we have, -+ %% but not if we have nothing. -+ if Data == [] -> -+ erlang:error(closed); -+ true -> -+ lists:flatten(Data) -+ end; -+ {ok, MoreData} -> -+ ?DEBUG("read ~p", [MoreData]), -+ DataList = binary_to_list(MoreData), -+ read_bytes(SockOpts, N - length(DataList), [Data, DataList]) -+ end. -+ -+handle_connection({SockMod, Socket}, Opts) -> -+ ?DEBUG("in handle_connection", []), -+ case catch handle_auth({SockMod, Socket}, Opts) of -+ {'EXIT', Reason} -> -+ ?ERROR_MSG("~p abnormal termination:~n\t~p~n", -+ [?MODULE, Reason]), -+ SockMod:close(Socket); -+ _ -> -+ ok -+ end. -+ -+handle_auth({SockMod, Socket} = SockOpts, Opts) -> -+ ?DEBUG("in handle_auth", []), -+ %% SOCKS protocol stuff... -+ [5, NAuthMethods] = read_bytes(SockOpts, 2, []), -+ AuthMethods = read_bytes(SockOpts, NAuthMethods, []), -+ SupportsNoAuth = lists:member(0, AuthMethods), -+ -+ %% Must support no authentication, otherwise crash -+ true = SupportsNoAuth, -+ -+ SockMod:send(Socket, [5, 0]), -+ -+ %% And done. -+ handle_connect(SockOpts, Opts). -+ -+handle_connect({SockMod, Socket} = SockOpts, Opts) -> -+ ?DEBUG("in handle_connect", []), -+ %% Expect a CONNECT command and nothing else -+ [5, 1, _, 3, AddressLength] = read_bytes(SockOpts, 5, []), -+ Cookie = read_bytes(SockOpts, AddressLength, []), -+ [0, 0] = read_bytes(SockOpts, 2, []), -+ -+ %% Make sure no more than two connections claim the same cookie. -+ F = fun() -> -+ case mnesia:read({proxy65_connection, Cookie}) of -+ [] -> -+ mnesia:write(#proxy65_connection{cookie = Cookie, -+ firstpid = self()}), -+ ok; -+ [#proxy65_connection{secondpid = none} = C] -> -+ mnesia:write(C#proxy65_connection{secondpid = self()}), -+ ok -+ end -+ end, -+ -+ case mnesia:transaction(F) of -+ {atomic, ok} -> -+ SockMod:send(Socket, [5, 0, 0, 3, AddressLength, Cookie, 0, 0]), -+ wait_for_activation(SockOpts, Opts); -+ Error -> -+ %% conflict. send "general SOCKS server failure". -+ SockMod:send(Socket, [5, 1, 0, 3, AddressLength, Cookie, 0, 0]), -+ erlang:error({badconnect, Error}) -+ end. -+ -+wait_for_activation(SockOpts, Opts) -> -+ ?DEBUG("in wait_for_activation", []), -+ receive -+ {get_socket, ReplyTo} -> -+ ReplyTo ! SockOpts, -+ wait_for_activation(SockOpts, Opts); -+ {activate, TargetSocket, Initiator, Target} -> -+ ?DEBUG("activated", []), -+ -+ %% We have no way of knowing which connection belongs to -+ %% which participant, so give both the maximum traffic -+ %% allowed to either. -+ Shapers = case lists:keysearch(shaper, 1, Opts) of -+ {value, {_, S}} -> S; -+ _ -> none -+ end, -+ ?DEBUG("we have shapers: ~p", [Shapers]), -+ Shaper1 = acl:match_rule(global, Shapers, jlib:string_to_jid(Initiator)), -+ Shaper2 = acl:match_rule(global, Shapers, jlib:string_to_jid(Target)), -+ if Shaper1 == none; Shaper2 == none -> -+ MaxShaper = none; -+ true -> -+ ShaperValue1 = ejabberd_config:get_global_option({shaper, Shaper1}), -+ ShaperValue2 = ejabberd_config:get_global_option({shaper, Shaper2}), -+ -+ if ShaperValue1 > ShaperValue2 -> -+ MaxShaper = Shaper1; -+ true -> -+ MaxShaper = Shaper2 -+ end, -+ ?DEBUG("shapers have values ~p and ~p~nusing ~p", [ShaperValue1, ShaperValue2, MaxShaper]), -+ ok -+ end, -+ -+ transfer_data(SockOpts, TargetSocket, shaper:new(MaxShaper)) -+ end. -+ -+transfer_data({SockMod, Socket} = SockOpts, {TargetSockMod, TargetSocket} = TargetSockOpts, -+ Shaper) -> -+ case SockMod:recv(Socket, 0, infinity) of -+ {ok, Data} -> -+ if Data /= <<>> -> -+ NewShaper = case Shaper of -+ none -> none; -+ _ -> -+ shaper:update(Shaper, size(Data)) -+ end, -+ ok = TargetSockMod:send(TargetSocket, Data); -+ true -> -+ NewShaper = Shaper -+ end, -+ transfer_data(SockOpts, TargetSockOpts, NewShaper); -+ {error, _} -> -+ TargetSockMod:shutdown(TargetSocket, read_write) -+ end. -+ -+get_socket(PID) -> -+ PID ! {get_socket, self()}, -+ receive -+ {_SockMod, _Socket} = SockOpts -> -+ SockOpts -+ end. -+ -+%% If any argument is a jid record, convert it to normalized string form... -+activate(#jid{} = Initiator, Target, SessionID) -> -+ NormalizedInitiator = jlib:jid_to_string(jlib:make_jid(jlib:jid_tolower(Initiator))), -+ activate(NormalizedInitiator, Target, SessionID); -+activate(Initiator, #jid{} = Target, SessionID) -> -+ NormalizedTarget = jlib:jid_to_string(jlib:make_jid(jlib:jid_tolower(Target))), -+ activate(Initiator, NormalizedTarget, SessionID); -+%% ...and get on with the activation. -+activate(Initiator, Target, SessionID) -> -+ Cookie = sha:sha(SessionID ++ Initiator ++ Target), -+ F = fun() -> -+ case mnesia:read({proxy65_connection, Cookie}) of -+ [#proxy65_connection{firstpid = FirstPID, -+ secondpid = SecondPID}] -+ when is_pid(FirstPID), is_pid(SecondPID) -> -+ mnesia:delete({proxy65_connection, Cookie}), -+ {FirstPID, SecondPID}; -+ _ -> -+ error -+ end -+ end, -+ case mnesia:transaction(F) of -+ {atomic, {FirstPID, SecondPID}} -> -+ FirstSocket = get_socket(FirstPID), -+ SecondSocket = get_socket(SecondPID), -+ FirstPID ! {activate, SecondSocket, Initiator, Target}, -+ SecondPID ! {activate, FirstSocket, Initiator, Target}, -+ ok; -+ Error -> -+ ?ERROR_MSG("Proxy activation failed: ~p", [Error]), -+ error -+ end. diff --git a/net-im/ejabberd/files/ejabberd-1.1.2-statsdx-web.patch b/net-im/ejabberd/files/ejabberd-1.1.2-statsdx-web.patch deleted file mode 100644 index 2182fb04..00000000 --- a/net-im/ejabberd/files/ejabberd-1.1.2-statsdx-web.patch +++ /dev/null @@ -1,318 +0,0 @@ ---- ejabberd-1.1.1/src/web/ejabberd_web_admin.erl 2006-04-03 16:14:35.000000000 +0200 -+++ ejabberd-1.1.1-r1/src/web/ejabberd_web_admin.erl 2006-06-23 09:32:54.000000000 +0200 -@@ -905,7 +905,10 @@ - path = ["stats"], - q = Query, - lang = Lang} = Request) -> -- Res = get_stats(Host, Lang), -+ Res = case whereis(ejabberd_mod_statsdx) of -+ undefined -> get_stats(Host, Lang); -+ _ -> get_statsdx(Host, Lang) -+ end, - make_xhtml([?XCT("h1", "Statistics")] ++ Res, Host, Lang); - - process_admin(Host, -@@ -1397,6 +1400,165 @@ - ])]. - - -+get_statsdx(global, Lang) -> -+ [ -+ ?XC("h3", "Accounts"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "registeredusers") -+ ]) -+ ]), -+ ?XC("h3", "Roster"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "totalrosteritems"), -+ do_stat(global, Lang, "meanitemsinroster") -+ ]) -+ ]), -+ ?XC("h3", "Users"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "authusers"), -+ do_stat(global, Lang, "onlineusers"), -+ do_stat(global, Lang, "offlinemsg"), -+ do_stat(global, Lang, "vcards") -+ ]) -+ ]), -+ ?XC("h3", "MUC"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "totalmucrooms"), -+ do_stat(global, Lang, "permmucrooms"), -+ do_stat(global, Lang, "regmucrooms") -+ ]) -+ ]), -+ ?XC("h3", "Pub/Sub"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "regpubsubnodes") -+ ]) -+ ]), -+ ?XC("h3", "IRC"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "ircconns") -+ ]) -+ ]), -+ ?XC("h3", "Ratios"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ ]) -+ ]), -+ ?XC("h3", "Sessions: " ++ get_stat_n("client")), -+ ?XAE("table", [], -+ [?XE("tbody", -+ do_stat_table(global, Lang, "client", server) -+ ) -+ ]), -+ ?XC("h3", "Sessions: " ++ get_stat_n("os")), -+ ?XAE("table", [], -+ [?XE("tbody", -+ do_stat_table(global, Lang, "os", server) -+ ) -+ ]), -+ ?XC("h3", "Sessions: " ++ get_stat_n("client") ++ "/" ++ get_stat_n("os")), -+ ?XAE("table", [], -+ [?XE("tbody", -+ do_stat_table(global, Lang, "client_os", server) -+ ) -+ ]), -+ ?XC("h3", "Sessions: " ++ get_stat_n("languages")), -+ ?XAE("table", [], -+ [?XE("tbody", -+ do_stat_table(global, Lang, "languages", server) -+ ) -+ ]) -+ ]; -+ -+get_statsdx(Host, Lang) -> -+ [ -+ ?XC("h2", Host), -+ ?XC("h3", "Accounts"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "registeredusers", Host) -+ ]) -+ ]), -+ ?XC("h3", "Roster"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "totalrosteritems", Host), -+ do_stat(global, Lang, "meanitemsinroster", Host) -+ ]) -+ ]), -+ ?XC("h3", "Users"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "authusers", Host), -+ do_stat(global, Lang, "onlineusers", Host), -+ do_stat(global, Lang, "offlinemsg", Host), -+ do_stat(global, Lang, "vcards", Host) -+ ]) -+ ]), -+ ?XC("h3", "Connections"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "s2sconnections", Host) -+ ]) -+ ]), -+ ?XC("h3", "MUC"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "totalmucrooms", Host), -+ do_stat(global, Lang, "permmucrooms", Host), -+ do_stat(global, Lang, "regmucrooms", Host) -+ ]) -+ ]), -+ ?XC("h3", "IRC"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "ircconns", Host) -+ ]) -+ ]), -+ %?XC("h3", "Pub/Sub"), -+ %?XAE("table", [], -+ % [?XE("tbody", [ -+ % do_stat(global, Lang, "regpubsubnodes", Host) -+ % ]) -+ %]), -+ ?XC("h3", "Ratios"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ ]) -+ ]), -+ ?XC("h3", "Sessions: " ++ get_stat_n("client")), -+ ?XAE("table", [], -+ [?XE("tbody", -+ do_stat_table(global, Lang, "client", Host) -+ ) -+ ]), -+ ?XC("h3", "Sessions: " ++ get_stat_n("os")), -+ ?XAE("table", [], -+ [?XE("tbody", -+ do_stat_table(global, Lang, "os", Host) -+ ) -+ ]), -+ ?XC("h3", "Sessions: " ++ get_stat_n("client") ++ "/" ++ get_stat_n("os")), -+ ?XAE("table", [], -+ [?XE("tbody", -+ do_stat_table(global, Lang, "client_os", Host) -+ ) -+ ]), -+ ?XC("h3", "Sessions: " ++ get_stat_n("languages")), -+ ?XAE("table", [], -+ [?XE("tbody", -+ do_stat_table(global, Lang, "languages", Host) -+ ) -+ ]) -+ ]. -+ -+ -+ - list_online_users(Host, _Lang) -> - Users = [{S, U} || {U, S, R} <- ejabberd_sm:get_vh_session_list(Host)], - SUsers = lists:usort(Users), -@@ -2015,6 +2177,12 @@ - ]; - - get_node(global, Node, ["stats"], Query, Lang) -> -+ Res = case whereis(ejabberd_mod_statsdx) of -+ undefined -> get_node(global, Node, ["statslight"], Query, Lang); -+ _ -> get_node(global, Node, ["statsdx"], Query, Lang) -+ end; -+ -+get_node(global, Node, ["statslight"], Query, Lang) -> - UpTime = rpc:call(Node, erlang, statistics, [wall_clock]), - UpTimeS = io_lib:format("~.3f", [element(1, UpTime)/1000]), - CPUTime = rpc:call(Node, erlang, statistics, [runtime]), -@@ -2089,9 +2257,124 @@ - ?XCT("h3", "Script check"), ?C(atom_to_list(Check))]) - ]; - -+get_node(global, Node, ["statsdx"], Query, Lang) -> -+ TransactionsCommited = -+ rpc:call(Node, mnesia, system_info, [transaction_commits]), -+ TransactionsAborted = -+ rpc:call(Node, mnesia, system_info, [transaction_failures]), -+ TransactionsRestarted = -+ rpc:call(Node, mnesia, system_info, [transaction_restarts]), -+ TransactionsLogged = -+ rpc:call(Node, mnesia, system_info, [transaction_log_writes]), -+ -+ [?XC("h1", io_lib:format(?T("~p statistics"), [Node])), -+ ?XC("h3", "Connections"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(global, Lang, "authusers"), -+ do_stat(Node, Lang, "httppollusers"), -+ do_stat(Node, Lang, "s2sconnections"), -+ do_stat(Node, Lang, "s2sservers") -+ ]) -+ ]), -+ ?XC("h3", "Erlang"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(Node, Lang, "operatingsystem"), -+ do_stat(Node, Lang, "erlangmachine"), -+ do_stat(Node, Lang, "erlangmachinetarget"), -+ do_stat(Node, Lang, "maxprocallowed"), -+ do_stat(Node, Lang, "procqueue"), -+ do_stat(Node, Lang, "totalerlproc") -+ ]) -+ ]), -+ ?XC("h3", "Times"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(Node, Lang, "uptime"), -+ do_stat(Node, Lang, "uptimehuman"), -+ do_stat(Node, Lang, "lastrestart"), -+ do_stat(Node, Lang, "cputime") -+ ]) -+ ]), -+ ?XC("h3", "CPU"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ do_stat(Node, Lang, "cpu_avg1"), -+ do_stat(Node, Lang, "cpu_avg5"), -+ do_stat(Node, Lang, "cpu_avg15"), -+ do_stat(Node, Lang, "cpu_nprocs")%, -+ %do_stat(Node, Lang, "cpu_util_user"), -+ %do_stat(Node, Lang, "cpu_nice_user"), -+ %do_stat(Node, Lang, "cpu_kernel"), -+ %do_stat(Node, Lang, "cpu_idle"), -+ %do_stat(Node, Lang, "cpu_wait") -+ ]) -+ ]), -+ %?XC("h3", "RAM"), -+ %?XAE("table", [], -+ % [?XE("tbody", [ -+ %do_stat(Node, Lang, "memsup_system"), -+ %do_stat(Node, Lang, "memsup_free"), -+ %do_stat(Node, Lang, "reductions") -+ % ]) -+ % ]), -+ ?XC("h3", "Database"), -+ ?XAE("table", [], -+ [?XE("tbody", [ -+ ?XE("tr", [?XCT("td", "Transactions commited"), -+ ?XAC("td", [{"class", "alignright"}], -+ integer_to_list(TransactionsCommited))]), -+ ?XE("tr", [?XCT("td", "Transactions aborted"), -+ ?XAC("td", [{"class", "alignright"}], -+ integer_to_list(TransactionsAborted))]), -+ ?XE("tr", [?XCT("td", "Transactions restarted"), -+ ?XAC("td", [{"class", "alignright"}], -+ integer_to_list(TransactionsRestarted))]), -+ ?XE("tr", [?XCT("td", "Transactions logged"), -+ ?XAC("td", [{"class", "alignright"}], -+ integer_to_list(TransactionsLogged))]) -+ ]) -+ ])]; -+ - get_node(Host, Node, NPath, Query, Lang) -> - [?XCT("h1", "Not Found")]. - -+do_table_element(Lang, L, N) -> -+ ?XE("tr", [ -+ ?XCT("td", L), -+ ?XAC("td", [{"class", "alignright"}], -+ N) -+ ]). -+ -+do_stat_table(global, Lang, Stat, Host) -> -+ Os = mod_statsdx:get(global, [Stat, Host]), -+ lists:map( -+ fun({L, N}) -> -+ do_table_element(Lang, L, io_lib:format("~p", [N])) -+ end, -+ Os -+ ). -+ -+do_stat(Node, Lang, Stat) -> -+ ?XE("tr", [ -+ ?XCT("td", get_stat_n(Stat)), -+ ?XAC("td", [{"class", "alignright"}], -+ get_stat_v(Node, [Stat]))]). -+ -+do_stat(Node, Lang, Stat, Host) -> -+ %[Res] = get_stat_v(Node, [Stat, Host]), -+ %do_table_element(Lang, get_stat_n(Stat), Res). -+ do_table_element(Lang, get_stat_n(Stat), get_stat_v(Node, [Stat, Host])). -+ -+% Get a stat name -+get_stat_n(Stat) -> -+ mod_statsdx:get(foo, [Stat, title]). -+% Get a stat value -+get_stat_v(Node, Stat) -> get_stat_v2(mod_statsdx:get(Node, Stat)). -+get_stat_v2(Value) when is_list(Value) -> Value; -+get_stat_v2(Value) when is_float(Value) -> io_lib:format("~.4f", [Value]); -+get_stat_v2(Value) -> io_lib:format("~p", [Value]). - - node_parse_query(Node, Query) -> - case lists:keysearch("restart", 1, Query) of diff --git a/net-im/ejabberd/files/ejabberd-1.1.2-statsdx.patch b/net-im/ejabberd/files/ejabberd-1.1.2-statsdx.patch deleted file mode 100644 index 38383542..00000000 --- a/net-im/ejabberd/files/ejabberd-1.1.2-statsdx.patch +++ /dev/null @@ -1,742 +0,0 @@ -diff -Naur ejabberd-1.1.1/src/mod_statsdx.erl ejabberd-1.1.1-r1/src/mod_statsdx.erl ---- ejabberd-1.1.1/src/mod_statsdx.erl 1970-01-01 01:00:00.000000000 +0100 -+++ ejabberd-1.1.1-r1/src/mod_statsdx.erl 2006-05-12 23:14:17.000000000 +0200 -@@ -0,0 +1,738 @@ -+%%%---------------------------------------------------------------------- -+%%% File : mod_statsdx.erl -+%%% Author : Badlop <badlop@ono.com> -+%%% Purpose : Calculates and gathers statistics actively -+%%% Created : -+%%% Id : 0.2.3 -+%%%---------------------------------------------------------------------- -+ -+%%% INSTALL: -+%%% 1 Copy this file to ejabberd/src/mod_statsdx.erl -+%%% 2 Recompile ejabberd -+%%% 3 Add to ejabberd.cfg, 'modules' section the basic configuration: -+%%% {mod_statsdx, []}, -+ -+%%% CONFIGURE: -+%%% hooks: Set to 'false' to remove hooks and related statistics if you don't need them (default: true) -+ -+%%% EXAMPLE CONFIGURATION: -+%%% {mod_statsdx, [{hooks, false}]}, -+ -+%%% FEATURE REQUESTS: -+%%% - fix the problem with plain/ssl/tlsusers, it crashes ejabberd -+%%% - traffic: send bytes per second, received bps -+%%% - connections to a transport -+%%% - traffic: send presence per second, received mps -+%%% - Number of SASL c2s connections -+%%% - improve to work in distributed server -+ -+-module(mod_statsdx). -+-author(''). -+-vsn(''). -+ -+-behaviour(gen_mod). -+ -+-export([start/2, loop/0, stop/1, get/2, -+ remove_user/2, user_send_packet/3, user_receive_packet/4, -+ user_login/1, user_logout/4]). -+ -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). -+-include("mod_roster.hrl"). -+ -+-define(PROCNAME, ejabberd_mod_statsdx). -+-define(T(Text), translate:translate("Lang", Text)). -+ -+%% ------------------- -+%% Module control -+%% ------------------- -+ -+start(Host, Opts) -> -+ Hooks = gen_mod:get_opt(hooks, Opts, true), -+ % Default value for the counters -+ CD = case Hooks of -+ true -> 0; -+ false -> "disabled" -+ end, -+ case whereis(?PROCNAME) of -+ undefined -> -+ application:start(os_mon), -+ ets:new(stats, [named_table, public]), -+ ets:insert(stats, {{user_login, server}, CD}), -+ ets:insert(stats, {{user_logout, server}, CD}), -+ ets:insert(stats, {{remove_user, server}, CD}), -+ lists:foreach( -+ fun(E) -> ets:insert(stats, {{client, server, E}, CD}) end, -+ list_elem(clients, id) -+ ), -+ lists:foreach( -+ fun(E) -> ets:insert(stats, {{os, server, E}, CD}) end, -+ list_elem(oss, id) -+ ), -+ register(?PROCNAME, spawn(?MODULE, loop, [])); -+ _ -> -+ ok -+ end, -+ ets:insert(stats, {{user_login, Host}, CD}), -+ ets:insert(stats, {{user_logout, Host}, CD}), -+ ets:insert(stats, {{remove_user, Host}, CD}), -+ ets:insert(stats, {{send, Host, iq, in}, CD}), -+ ets:insert(stats, {{send, Host, iq, out}, CD}), -+ ets:insert(stats, {{send, Host, message, in}, CD}), -+ ets:insert(stats, {{send, Host, message, out}, CD}), -+ ets:insert(stats, {{send, Host, presence, in}, CD}), -+ ets:insert(stats, {{send, Host, presence, out}, CD}), -+ ets:insert(stats, {{recv, Host, iq, in}, CD}), -+ ets:insert(stats, {{recv, Host, iq, out}, CD}), -+ ets:insert(stats, {{recv, Host, message, in}, CD}), -+ ets:insert(stats, {{recv, Host, message, out}, CD}), -+ ets:insert(stats, {{recv, Host, presence, in}, CD}), -+ ets:insert(stats, {{recv, Host, presence, out}, CD}), -+ lists:foreach( -+ fun(E) -> ets:insert(stats, {{client, Host, E}, CD}) end, -+ list_elem(clients, id) -+ ), -+ lists:foreach( -+ fun(E) -> ets:insert(stats, {{os, Host, E}, CD}) end, -+ list_elem(oss, id) -+ ), -+ case Hooks of -+ true -> -+ ejabberd_hooks:add(user_send_packet, Host, ?MODULE, user_send_packet, 90), -+ ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, user_receive_packet, 90), -+ ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 90), -+ ejabberd_hooks:add(user_available_hook, Host, ?MODULE, user_login, 90), -+ ejabberd_hooks:add(unset_presence_hook, Host, ?MODULE, user_logout, 90); -+ false -> -+ ok -+ end. -+ -+loop() -> -+ receive -+ stop -> ok -+ end. -+ -+stop(Host) -> -+ ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet, 60), -+ ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, user_receive_packet, 60), -+ ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 60), -+ ets:delete(stats), -+ case whereis(?PROCNAME) of -+ undefined -> ok; -+ _ -> -+ ?PROCNAME ! stop -+ end. -+ -+ -+remove_user(_User, Server) -> -+ ets:update_counter(stats, {remove_user, Server}, 1), -+ ets:update_counter(stats, {remove_user, server}, 1). -+ -+user_send_packet(FromJID, ToJID, NewEl) -> -+ Host = FromJID#jid.lserver, -+ HostTo = ToJID#jid.lserver, -+ {xmlelement, Type, _, _} = NewEl, -+ Type2 = case Type of -+ "iq" -> iq; -+ "message" -> message; -+ "presence" -> presence -+ end, -+ Dest = case is_host(HostTo, Host) of -+ true -> in; -+ false -> out -+ end, -+ ets:update_counter(stats, {send, Host, Type2, Dest}, 1), -+ -+ % Registrarse para tramitar Host/mod_stats2file -+ case list_to_atom(ToJID#jid.lresource) of -+ ?MODULE -> received_response(FromJID, ToJID, NewEl); -+ _ -> ok -+ end. -+ -+user_receive_packet(_JID, From, To, FixedPacket) -> -+ HostFrom = From#jid.lserver, -+ Host = To#jid.lserver, -+ {xmlelement, Type, _, _} = FixedPacket, -+ Type2 = case Type of -+ "iq" -> iq; -+ "message" -> message; -+ "presence" -> presence -+ end, -+ Dest = case is_host(HostFrom, Host) of -+ true -> in; -+ false -> out -+ end, -+ ets:update_counter(stats, {recv, Host, Type2, Dest}, 1). -+ -+ -+%% ------------------- -+%% get(* -+%% ------------------- -+ -+%gett(Arg) -> get(node(), [Arg, title]). -+getl(Args) -> get(node(), [Args]). -+getl(Args, Host) -> get(node(), [Args, Host]). -+ -+%get(_Node, ["", title]) -> ""; -+ -+get(global, A) -> get(node(), A); -+ -+get(_, [{"reductions", _}, title]) -> "Reductions (per minute)"; -+get(_, [{"reductions", I}]) -> calc_avg(element(2, statistics(reductions)), I); %+++ -+ -+get(_, ["cpu_avg1", title]) -> "Average system load (1 min)"; -+get(N, ["cpu_avg1"]) -> rpc:call(N, cpu_sup, avg1, [])/256; -+get(_, ["cpu_avg5", title]) -> "Average system load (5 min)"; -+get(N, ["cpu_avg5"]) -> rpc:call(N, cpu_sup, avg1, [])/256; -+get(_, ["cpu_avg15", title]) -> "Average system load (15 min)"; -+get(N, ["cpu_avg15"]) -> rpc:call(N, cpu_sup, avg15, [])/256; -+get(_, ["cpu_nprocs", title]) -> "Number of UNIX processes running on this machine"; -+get(N, ["cpu_nprocs"]) -> rpc:call(N, cpu_sup, nprocs, []); -+get(_, ["cpu_util", title]) -> "CPU utilization"; -+get(N, ["cpu_util"]) -> rpc:call(N, cpu_sup, util, []); -+ -+get(_, [{"cpu_util_user", _}, title]) -> "CPU utilization - user"; -+get(_, [{"cpu_util_nice_user", _}, title]) -> "CPU utilization - nice_user"; -+get(_, [{"cpu_util_kernel", _}, title]) -> "CPU utilization - kernel"; -+get(_, [{"cpu_util_wait", _}, title]) -> "CPU utilization - wait"; -+get(_, [{"cpu_util_idle", _}, title]) -> "CPU utilization - idle"; -+get(_, [{"cpu_util_user", U}]) -> [{user, Us}, _, _] = element(2, U), Us; -+get(_, [{"cpu_util_nice_user", U}]) -> [_, {nice_user, NU}, _] = element(2, U), NU; -+get(_, [{"cpu_util_kernel", U}]) -> [_, _, {kernel, K}] = element(2, U), K; -+get(_, [{"cpu_util_wait", U}]) -> -+ case element(3, U) of -+ [{wait, W}, {idle, _}] -> W; % Solaris -+ [{idle, _}] -> 0 -+ end; -+get(_, [{"cpu_util_idle", U}]) -> -+ case element(3, U) of -+ [{wait, _}, {idle, I}] -> I; % Solaris -+ [{idle, I}] -> I -+ end; -+ -+get(_, [{"client", Id}, title]) -> atom_to_list(Id); -+get(_, [{"client", Id}, Host]) -> [{_, C}] = ets:lookup(stats, {client, Host, Id}), C; -+get(_, ["client", title]) -> "Client"; -+get(N, ["client", Host]) -> -+ lists:map( -+ fun(Id) -> -+ [Id_string] = io_lib:format("~p", [Id]), -+ {Id_string, get(N, [{"client", Id}, Host])} -+ end, -+ lists:usort(list_elem(clients, id)) -+ ); -+ -+get(_, [{"os", Id}, title]) -> atom_to_list(Id); -+get(_, [{"os", Id}, list]) -> lists:usort(list_elem(oss, Id)); -+get(_, [{"os", Id}, Host]) -> [{_, C}] = ets:lookup(stats, {os, Host, Id}), C; -+get(_, ["os", title]) -> "Operating System"; -+get(N, ["os", Host]) -> -+ lists:map( -+ fun(Id) -> -+ [Id_string] = io_lib:format("~p", [Id]), -+ {Id_string, get(N, [{"os", Id}, Host])} -+ end, -+ lists:usort(list_elem(oss, id)) -+ ); -+ -+get(_, [{"memsup_system", _}, title]) -> "Memory physical (bytes)"; -+get(_, [{"memsup_system", M}]) -> [_, _, {system_total_memory, R}] = M, R; -+get(_, [{"memsup_free", _}, title]) -> "Memory free (bytes)"; -+get(_, [{"memsup_free", M}]) -> [_, {free_memory, R}, _] = M, R; -+ -+get(_, [{"user_login", _}, title]) -> "Logins (per minute)"; -+get(_, [{"user_login", I}, Host]) -> get_stat({user_login, Host}, I); -+get(_, [{"user_logout", _}, title]) -> "Logouts (per minute)"; -+get(_, [{"user_logout", I}, Host]) -> get_stat({user_logout, Host}, I); -+get(_, [{"remove_user", _}, title]) -> "Accounts deleted (per minute)"; -+get(_, [{"remove_user", I}, Host]) -> get_stat({remove_user, Host}, I); -+get(_, [{Table, Type, Dest, _}, title]) -> filename:flatten([Table, Type, Dest]); -+get(_, [{Table, Type, Dest, I}, Host]) -> get_stat({Table, Host, Type, Dest}, I); -+ -+get(_, ["localtime", title]) -> "Local time"; -+get(N, ["localtime"]) -> -+ localtime_to_string(rpc:call(N, erlang, localtime, [])); -+ -+get(_, ["vhost", title]) -> "Virtual host"; -+get(_, ["vhost", Host]) -> Host; -+ -+get(_, ["totalerlproc", title]) -> "Total Erlang processes running"; -+get(N, ["totalerlproc"]) -> rpc:call(N, erlang, system_info, [process_count]); -+get(_, ["operatingsystem", title]) -> "Operating System"; -+get(N, ["operatingsystem"]) -> {rpc:call(N, os, type, []), rpc:call(N, os, version, [])}; -+get(_, ["erlangmachine", title]) -> "Erlang machine"; -+get(N, ["erlangmachine"]) -> rpc:call(N, erlang, system_info, [system_version]); -+get(_, ["erlangmachinetarget", title]) -> "Erlang machine target"; -+get(N, ["erlangmachinetarget"]) -> rpc:call(N, erlang, system_info, [system_architecture]); -+get(_, ["maxprocallowed", title]) -> "Maximum processes allowed"; -+get(N, ["maxprocallowed"]) -> rpc:call(N, erlang, system_info, [process_limit]); -+get(_, ["procqueue", title]) -> "Number of processes on the running queue"; -+get(N, ["procqueue"]) -> rpc:call(N, erlang, statistics, [run_queue]); -+get(_, ["uptimehuman", title]) -> "Uptime"; -+get(N, ["uptimehuman"]) -> -+ io_lib:format("~w days ~w hours ~w minutes ~p seconds", ms_to_time(get(N, ["uptime"]))); -+get(_, ["lastrestart", title]) -> "Last restart"; -+get(N, ["lastrestart"]) -> -+ Now = calendar:datetime_to_gregorian_seconds(rpc:call(N, erlang, localtime, [])), -+ UptimeMS = get(N, ["uptime"]), -+ Last_restartS = trunc(Now - (UptimeMS/1000)), -+ Last_restart = calendar:gregorian_seconds_to_datetime(Last_restartS), -+ localtime_to_string(Last_restart); -+ -+get(_, ["plainusers", title]) -> "Plain users"; -+get(_, ["plainusers"]) -> {R, _, _} = get_connectiontype(), R; -+get(_, ["tlsusers", title]) -> "TLS users"; -+get(_, ["tlsusers"]) -> {_, R, _} = get_connectiontype(), R; -+get(_, ["sslusers", title]) -> "SSL users"; -+get(_, ["sslusers"]) -> {_, _, R} = get_connectiontype(), R; -+get(_, ["registeredusers", title]) -> "Registered users"; -+get(N, ["registeredusers"]) -> rpc:call(N, mnesia, table_info, [passwd, size]); -+get(_, ["registeredusers", Host]) -> length(ejabberd_auth:get_vh_registered_users(Host)); -+get(_, ["authusers", title]) -> "Authenticated users"; -+get(N, ["authusers"]) -> rpc:call(N, mnesia, table_info, [session, size]); -+get(_, ["authusers", Host]) -> get_authusers(Host); -+get(_, ["onlineusers", title]) -> "Online users"; -+get(N, ["onlineusers"]) -> rpc:call(N, mnesia, table_info, [presence, size]); -+get(_, ["onlineusers", Host]) -> length(ejabberd_sm:get_vh_session_list(Host)); -+get(_, ["httppollusers", title]) -> "HTTP-Poll users (aprox)"; -+get(N, ["httppollusers"]) -> rpc:call(N, mnesia, table_info, [http_poll, size]); -+ -+get(_, ["s2sconnections", title]) -> "Outgoing S2S connections"; -+get(_, ["s2sconnections"]) -> length(get_S2SConns()); -+get(_, ["s2sconnections", Host]) -> get_s2sconnections(Host); -+get(_, ["s2sservers", title]) -> "Outgoing S2S servers"; -+get(_, ["s2sservers"]) -> length(lists:usort([element(2, C) || C <- get_S2SConns()])); -+ -+get(_, ["offlinemsg", title]) -> "Offline messages"; -+get(N, ["offlinemsg"]) -> rpc:call(N, mnesia, table_info, [offline_msg, size]); -+get(_, ["offlinemsg", Host]) -> get_offlinemsg(Host); -+get(_, ["totalrosteritems", title]) -> "Total roster items"; -+get(N, ["totalrosteritems"]) -> rpc:call(N, mnesia, table_info, [roster, size]); -+get(_, ["totalrosteritems", Host]) -> get_totalrosteritems(Host); -+ -+get(_, ["meanitemsinroster", title]) -> "Mean items in roster"; -+get(_, ["meanitemsinroster"]) -> get_meanitemsinroster(); -+get(_, ["meanitemsinroster", Host]) -> get_meanitemsinroster(Host); -+ -+get(_, ["totalmucrooms", title]) -> "Total MUC rooms"; -+get(_, ["totalmucrooms"]) -> ets:info(muc_online_room, size); -+get(_, ["totalmucrooms", Host]) -> get_totalmucrooms(Host); -+get(_, ["permmucrooms", title]) -> "Permanent MUC rooms"; -+get(N, ["permmucrooms"]) -> rpc:call(N, mnesia, table_info, [muc_room, size]); -+get(_, ["permmucrooms", Host]) -> get_permmucrooms(Host); -+get(_, ["regmucrooms", title]) -> "Users registered at MUC service"; -+get(N, ["regmucrooms"]) -> rpc:call(N, mnesia, table_info, [muc_registered, size]); -+get(_, ["regmucrooms", Host]) -> get_regmucrooms(Host); -+get(_, ["regpubsubnodes", title]) -> "Registered nodes at Pub/Sub"; -+get(N, ["regpubsubnodes"]) -> rpc:call(N, mnesia, table_info, [pubsub_node, size]); -+get(_, ["vcards", title]) -> "Total vCards published"; -+get(N, ["vcards"]) -> rpc:call(N, mnesia, table_info, [vcard, size]); -+get(_, ["vcards", Host]) -> get_vcards(Host); -+ -+get(_, ["ircconns", title]) -> "IRC connections"; -+get(_, ["ircconns"]) -> ets:info(irc_connection, size); -+get(_, ["ircconns", Host]) -> get_irccons(Host); -+get(_, ["uptime", title]) -> "Uptime"; -+get(N, ["uptime"]) -> element(1, rpc:call(N, erlang, statistics, [wall_clock])); -+get(_, ["cputime", title]) -> "CPU Time"; -+get(N, ["cputime"]) -> element(1, rpc:call(N, erlang, statistics, [runtime])); -+ -+get(_, ["languages", title]) -> "Languages"; -+get(_, ["languages", Server]) -> get_languages(Server); -+ -+get(_, ["client_os", title]) -> "Client/OS"; -+get(_, ["client_os", Server]) -> get_client_os(Server); -+ -+get(N, A) -> -+ io:format(" ----- node: '~p', A: '~p'~n", [N, A]), -+ "666". -+ -+%% ------------------- -+%% get_* -+%% ------------------- -+ -+get_S2SConns() -> ejabberd_s2s:dirty_get_connections(). -+ -+get_tls_drv() -> -+ R = lists:filter( -+ fun(P) -> -+ case erlang:port_info(P, name) of -+ {name, "tls_drv"} -> true; -+ _ -> false -+ end -+ end, erlang:ports()), -+ length(R). -+ -+get_connections(Port) -> -+ R = lists:filter( -+ fun(P) -> -+ case inet:port(P) of -+ {ok, Port} -> true; -+ _ -> false -+ end -+ end, erlang:ports()), -+ length(R). -+ -+get_totalrosteritems(Host) -> -+ F = fun() -> -+ F2 = fun(R, {H, A}) -> -+ {_LUser, LServer, _LJID} = R#roster.usj, -+ A2 = case LServer of -+ H -> A+1; -+ _ -> A -+ end, -+ {H, A2} -+ end, -+ mnesia:foldl(F2, {Host, 0}, roster) -+ end, -+ {atomic, {Host, Res}} = mnesia:transaction(F), -+ Res. -+ -+% Copied from ejabberd_sm.erl -+-record(session, {sid, usr, us, priority}). -+ -+get_authusers(Host) -> -+ F = fun() -> -+ F2 = fun(R, {H, A}) -> -+ {_LUser, LServer, _LResource} = R#session.usr, -+ A2 = case LServer of -+ H -> A+1; -+ _ -> A -+ end, -+ {H, A2} -+ end, -+ mnesia:foldl(F2, {Host, 0}, session) -+ end, -+ {atomic, {Host, Res}} = mnesia:transaction(F), -+ Res. -+ -+-record(offline_msg, {us, timestamp, expire, from, to, packet}). -+ -+get_offlinemsg(Host) -> -+ F = fun() -> -+ F2 = fun(R, {H, A}) -> -+ {_LUser, LServer} = R#offline_msg.us, -+ A2 = case LServer of -+ H -> A+1; -+ _ -> A -+ end, -+ {H, A2} -+ end, -+ mnesia:foldl(F2, {Host, 0}, offline_msg) -+ end, -+ {atomic, {Host, Res}} = mnesia:transaction(F), -+ Res. -+ -+-record(vcard, {us, vcard}). -+ -+get_vcards(Host) -> -+ F = fun() -> -+ F2 = fun(R, {H, A}) -> -+ {_LUser, LServer} = R#vcard.us, -+ A2 = case LServer of -+ H -> A+1; -+ _ -> A -+ end, -+ {H, A2} -+ end, -+ mnesia:foldl(F2, {Host, 0}, vcard) -+ end, -+ {atomic, {Host, Res}} = mnesia:transaction(F), -+ Res. -+ -+-record(s2s, {fromto, pid, key}). -+ -+get_s2sconnections(Host) -> -+ F = fun() -> -+ F2 = fun(R, {H, A}) -> -+ {MyServer, _Server} = R#s2s.fromto, -+ A2 = case MyServer of -+ H -> A+1; -+ _ -> A -+ end, -+ {H, A2} -+ end, -+ mnesia:foldl(F2, {Host, 0}, s2s) -+ end, -+ {atomic, {Host, Res}} = mnesia:transaction(F), -+ Res. -+ -+-record(irc_connection, {jid_server_host, pid}). -+ -+get_irccons(Host) -> -+ F2 = fun(R, {H, A}) -> -+ {From, _Server, _Host} = R#irc_connection.jid_server_host, -+ A2 = case From#jid.lserver of -+ H -> A+1; -+ _ -> A -+ end, -+ {H, A2} -+ end, -+ {Host, Res} = ets:foldl(F2, {Host, 0}, irc_connection), -+ Res. -+ -+is_host(Host, Subhost) -> -+ Pos = string:len(Host)-string:len(Subhost)+1, -+ case string:rstr(Host, Subhost) of -+ Pos -> true; -+ _ -> false -+ end. -+ -+-record(muc_online_room, {name_host, pid}). -+ -+get_totalmucrooms(Host) -> -+ F2 = fun(R, {H, A}) -> -+ {_Name, MUCHost} = R#muc_online_room.name_host, -+ A2 = case is_host(MUCHost, H) of -+ true -> A+1; -+ false -> A -+ end, -+ {H, A2} -+ end, -+ {Host, Res} = ets:foldl(F2, {Host, 0}, muc_online_room), -+ Res. -+ -+-record(muc_room, {name_host, opts}). -+ -+get_permmucrooms(Host) -> -+ F = fun() -> -+ F2 = fun(R, {H, A}) -> -+ {_Name, MUCHost} = R#muc_room.name_host, -+ A2 = case is_host(MUCHost, H) of -+ true -> A+1; -+ false -> A -+ end, -+ {H, A2} -+ end, -+ mnesia:foldl(F2, {Host, 0}, muc_room) -+ end, -+ {atomic, {Host, Res}} = mnesia:transaction(F), -+ Res. -+ -+-record(muc_registered, {us_host, nick}). -+ -+get_regmucrooms(Host) -> -+ F = fun() -> -+ F2 = fun(R, {H, A}) -> -+ {_User, MUCHost} = R#muc_registered.us_host, -+ A2 = case is_host(MUCHost, H) of -+ true -> A+1; -+ false -> A -+ end, -+ {H, A2} -+ end, -+ mnesia:foldl(F2, {Host, 0}, muc_registered) -+ end, -+ {atomic, {Host, Res}} = mnesia:transaction(F), -+ Res. -+ -+get_stat(Stat, Ims) -> -+ Res = ets:lookup(stats, Stat), -+ ets:update_counter(stats, Stat, {2,1,0,0}), -+ [{_, C}] = Res, -+ calc_avg(C, Ims). -+ %C. -+ -+calc_avg(Count, TimeMS) -> -+ TimeMIN = TimeMS/(1000*60), -+ Count/TimeMIN. -+ -+%% ------------------- -+%% utilities -+%% ------------------- -+ -+get_connectiontype() -> -+ C2 = get_connections(5222) -1, -+ C3 = get_connections(5223) -1, -+ NUplain = C2 + C3 - get_tls_drv(), -+ NUtls = C2 - NUplain, -+ NUssl = C3, -+ {NUplain, NUtls, NUssl}. -+ -+ms_to_time(T) -> -+ DMS = 24*60*60*1000, -+ HMS = 60*60*1000, -+ MMS = 60*1000, -+ SMS = 1000, -+ D = trunc(T/DMS), -+ H = trunc((T - (D*DMS)) / HMS), -+ M = trunc((T - (D*DMS) - (H*HMS)) / MMS), -+ S = trunc((T - (D*DMS) - (H*HMS) - (M*MMS)) / SMS), -+ [D, H, M, S]. -+ -+ -+% Cuando un usuario conecta, pedirle iq:version a nombre de Host/mod_stats2file -+user_login(U) -> -+ User = U#jid.luser, -+ Host = U#jid.lserver, -+ Resource = U#jid.lresource, -+ ets:update_counter(stats, {user_login, server}, 1), -+ ets:update_counter(stats, {user_login, Host}, 1), -+ request_iqversion(User, Host, Resource). -+ -+% cuando un usuario desconecta, buscar en la tabla su JID/USR y quitarlo -+user_logout(User, Host, Resource, _Status) -> -+ ets:update_counter(stats, {user_logout, server}, 1), -+ ets:update_counter(stats, {user_logout, Host}, 1), -+ -+ JID = jlib:make_jid(User, Host, Resource), -+ case ets:lookup(stats, {session, JID}) of -+ [{_, Client_id, OS_id, Lang}] -> -+ ets:delete(stats, {session, JID}), -+ ets:update_counter(stats, {client, Host, Client_id}, -1), -+ ets:update_counter(stats, {client, server, Client_id}, -1), -+ ets:update_counter(stats, {os, Host, OS_id}, -1), -+ ets:update_counter(stats, {os, server, OS_id}, -1), -+ update_counter_create(stats, {client_os, Host, Client_id, OS_id}, -1), -+ update_counter_create(stats, {client_os, server, Client_id, OS_id}, -1), -+ update_counter_create(stats, {lang, Host, Lang}, -1), -+ update_counter_create(stats, {lang, server, Lang}, -1); -+ [] -> -+ ok -+ end. -+ -+request_iqversion(User, Host, Resource) -> -+ From = jlib:make_jid("", Host, atom_to_list(?MODULE)), -+ FromStr = jlib:jid_to_string(From), -+ To = jlib:make_jid(User, Host, Resource), -+ ToStr = jlib:jid_to_string(To), -+ Packet = {xmlelement,"iq", -+ [{"from",FromStr}, {"to",ToStr}, {"type","get"}, {"xml:lang","es"}], -+ [{xmlcdata,"\n"}, {xmlcdata," "}, {xmlelement, "query", [{"xmlns","jabber:iq:version"}], []}, {xmlcdata,"\n"}]}, -+ ejabberd_local:route(From, To, Packet). -+ -+% cuando el virtualJID recibe una respuesta iqversion, -+% almacenar su JID/USR + client + OS en una tabla -+received_response(From, _To, {xmlelement, "iq", Attrs, Elc}) -> -+ User = From#jid.luser, -+ Host = From#jid.lserver, -+ Resource = From#jid.lresource, -+ -+ "result" = xml:get_attr_s("type", Attrs), -+ Lang = case xml:get_attr_s("xml:lang", Attrs) of -+ [] -> "unknown"; -+ L -> L -+ end, -+ update_counter_create(stats, {lang, Host, Lang}, 1), -+ update_counter_create(stats, {lang, server, Lang}, 1), -+ -+ [El] = xml:remove_cdata(Elc), -+ {xmlelement, _, Attrs2, _Els2} = El, -+ ?NS_VERSION = xml:get_attr_s("xmlns", Attrs2), -+ -+ Client = get_tag_cdata_subtag(El, "name"), -+ %Version = get_tag_cdata_subtag(El, "version"), -+ OS = get_tag_cdata_subtag(El, "os"), -+ {Client_id, OS_id} = identify(Client, OS), -+ -+ ets:update_counter(stats, {client, Host, Client_id}, 1), -+ ets:update_counter(stats, {client, server, Client_id}, 1), -+ ets:update_counter(stats, {os, Host, OS_id}, 1), -+ ets:update_counter(stats, {os, server, OS_id}, 1), -+ update_counter_create(stats, {client_os, Host, Client_id, OS_id}, 1), -+ update_counter_create(stats, {client_os, server, Client_id, OS_id}, 1), -+ -+ JID = jlib:make_jid(User, Host, Resource), -+ ets:insert(stats, {{session, JID}, Client_id, OS_id, Lang}). -+ -+update_counter_create(Table, Element, C) -> -+ case ets:lookup(Table, Element) of -+ [] -> ets:insert(Table, {Element, 1}); -+ _ -> ets:update_counter(Table, Element, C) -+ end. -+ -+get_tag_cdata_subtag(E, T) -> -+ E2 = xml:get_subtag(E, T), -+ case E2 of -+ false -> "unknown"; -+ _ -> xml:get_tag_cdata(E2) -+ end. -+ -+list_elem(Type, id) -> -+ {_, Ids} = lists:unzip(list_elem(Type, full)), -+ Ids; -+list_elem(clients, full) -> -+ [ -+ {"gaim", gaim}, -+ {"Gajim", gajim}, -+ {"Tkabber", tkabber}, -+ {"Psi", psi}, -+ {"Pandion", pandion}, -+ {"Kopete", kopete}, -+ {"Exodus", exodus}, -+ {"libgaim", libgaim}, -+ {"JBother", jbother}, -+ {"iChat", ichat}, -+ {"Miranda", miranda}, -+ {"Trillian", trillian}, -+ {"JAJC", jajc}, -+ {"Coccinella", coccinella}, -+ {"Gabber", gabber}, -+ {"BitlBee", bitlbee}, -+ {"jabber.el", jabberel}, -+ {"unknown", unknown} -+ ]; -+list_elem(oss, full) -> -+ [ -+ {"Linux", linux}, -+ {"Win", windows}, -+ {"Gentoo", linux}, -+ {"Mac", mac}, -+ {"BSD", bsd}, -+ {"SunOS", linux}, -+ {"Ubuntu", linux}, -+ {"unknown", unknown} -+ ]. -+ -+identify(Client, OS) -> -+ Res = {try_match(Client, list_elem(clients, full)), try_match(OS, list_elem(oss, full))}, -+ case Res of -+ {libgaim, mac} -> {adium, mac}; -+ _ -> Res -+ end. -+ -+try_match(_E, []) -> unknown; -+try_match(E, [{Str, Id} | L]) -> -+ case string:str(E, Str) of -+ 0 -> try_match(E, L); -+ _ -> Id -+ end. -+ -+get_client_os(Server) -> -+ CO1 = ets:match(stats, {{client_os, Server, '$1', '$2'}, '$3'}), -+ CO2 = lists:map( -+ fun([Cl, Os, A3]) -> -+ {lists:flatten([atom_to_list(Cl), "/", atom_to_list(Os)]), A3} -+ end, -+ CO1 -+ ), -+ lists:keysort(1, CO2). -+ -+get_languages(Server) -> -+ L1 = ets:match(stats, {{lang, Server, '$1'}, '$2'}), -+ L2 = lists:map( -+ fun([La, C]) -> -+ {La, C} -+ end, -+ L1 -+ ), -+ lists:keysort(1, L2). -+ -+get_meanitemsinroster() -> -+ get_meanitemsinroster2(getl("totalrosteritems"), getl("registeredusers")). -+get_meanitemsinroster(Host) -> -+ get_meanitemsinroster2(getl("totalrosteritems", Host), getl("registeredusers", Host)). -+get_meanitemsinroster2(Items, Users) -> -+ case Users of -+ 0 -> 0; -+ _ -> Items/Users -+ end. -+ -+localtime_to_string({{Y, Mo, D},{H, Mi, S}}) -> -+ lists:concat([H, ":", Mi, ":", S, " ", D, "/", Mo, "/", Y]). -+ -+% cuando toque mostrar estadisticas -+%get_iqversion() -> -+ % contar en la tabla cuantos tienen cliente: *psi* -+ %buscar en la tabla iqversion -+ %ok. diff --git a/net-im/ejabberd/files/ejabberd-1.1.2.initd b/net-im/ejabberd/files/ejabberd-1.1.2.initd deleted file mode 100644 index 4291e39a..00000000 --- a/net-im/ejabberd/files/ejabberd-1.1.2.initd +++ /dev/null @@ -1,59 +0,0 @@ -#!/sbin/runscript -# Copyright 1999-2005 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Header: /var/cvsroot/gentoo-x86/net-im/ejabberd/files/ejabberd-1.1.1.initd,v 1.1 2006/06/19 14:07:20 chainsaw Exp $ - -opts="${opts} reload" - -depend() { - use dns - need net - provide jabber-server -} - -checkconfig() { - if [ ! -e /etc/jabber/ejabberd.cfg ] ; then - eerror "You need an /etc/jabber/ejabberd.cfg file to run ejabberd" - return 1 - fi -} - -start() { - checkconfig || return 1 - ebegin "Starting eJabberd" - su jabber -c "HOME=/var/run/jabber /usr/bin/ejabberd -noshell -detached" - eend $? -} - -stop() { - ebegin "Stopping eJabberd" - if [ -z "$EJABBERD_NODE" ]; - then - EJABBERD_NODE="ejabberd@`hostname -s`" - fi - /usr/bin/ejabberdctl $EJABBERD_NODE stop - eend $? -} - -# Work around a bug in /sbin/runscript.sh - it won't run our custom -# restart() unless it finds these two strings in the file. -# svc_start svc_stop -restart() { - ebegin "Restarting eJabberd" - if [ -z "$EJABBERD_NODE" ]; - then - EJABBERD_NODE="ejabberd@`hostname -s`" - fi - /usr/bin/ejabberdctl $EJABBERD_NODE restart - eend $? -} - -reload() { - ebegin "Reloading eJabberd" - if [ -z "$EJABBERD_NODE" ]; - then - EJABBERD_NODE="ejabberd@`hostname -s`" - fi - /usr/bin/ejabberdctl $EJABBERD_NODE reopen-log - eend $? -} diff --git a/net-im/ejabberd/files/http_binding.patch b/net-im/ejabberd/files/http_binding.patch deleted file mode 100644 index 95b2deae..00000000 --- a/net-im/ejabberd/files/http_binding.patch +++ /dev/null @@ -1,1006 +0,0 @@ -Index: src/ejabberd.cfg.example -=================================================================== ---- src/ejabberd.cfg.example (Revision 565) -+++ src/ejabberd.cfg.example (Arbeitskopie) -@@ -124,7 +124,7 @@ - {5269, ejabberd_s2s_in, [{shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]}, -- {5280, ejabberd_http, [http_poll, web_admin]}, -+ {5280, ejabberd_http, [http_poll, http_bind, web_admin]}, - {8888, ejabberd_service, [{access, all}, - {hosts, ["icq.localhost", "sms.localhost"], - [{password, "secret"}]}]} -Index: src/web/Makefile.in -=================================================================== ---- src/web/Makefile.in (Revision 565) -+++ src/web/Makefile.in (Arbeitskopie) -@@ -15,7 +15,8 @@ - $(OUTDIR)/ejabberd_http.beam \ - $(OUTDIR)/ejabberd_web.beam \ - $(OUTDIR)/ejabberd_web_admin.beam \ -- $(OUTDIR)/ejabberd_http_poll.beam -+ $(OUTDIR)/ejabberd_http_poll.beam \ -+ $(OUTDIR)/ejabberd_http_bind.beam - - all: $(OBJS) - -Index: src/web/ejabberd_http_bind.erl -=================================================================== ---- src/web/ejabberd_http_bind.erl (Revision 0) -+++ src/web/ejabberd_http_bind.erl (Revision 0) -@@ -0,0 +1,804 @@ -+%%%---------------------------------------------------------------------- -+%%% File : ejabberd_http_bind.erl -+%%% Author : Stefan Strigler <steve@zeank.in-berlin.de> -+%%% Purpose : HTTP Binding support (JEP-0124) -+%%% Created : 21 Sep 2005 by Stefan Strigler <steve@zeank.in-berlin.de> -+%%% Id : $Id: $ -+%%%---------------------------------------------------------------------- -+ -+-module(ejabberd_http_bind). -+-author('steve@zeank.in-berlin.de'). -+-vsn('Revision: 1.4'). -+ -+-behaviour(gen_fsm). -+ -+%% External exports -+-export([start_link/2, -+ init/1, -+ handle_event/3, -+ handle_sync_event/4, -+ code_change/4, -+ handle_info/3, -+ terminate/3, -+ send/2, -+ setopts/2, -+ controlling_process/2, -+ close/1, -+ process_request/1]). -+ -+%%-define(ejabberd_debug, true). -+ -+-include("ejabberd.hrl"). -+-include("jlib.hrl"). -+-include("ejabberd_http.hrl"). -+ -+-record(http_bind, {id, pid, to, hold, wait}). -+ -+%% http binding request -+-record(hbr, {rid, -+ key, -+ in, -+ out}). -+ -+-record(state, {id, -+ rid = error, -+ key, -+ output = "", -+ input = "", -+ waiting_input = false, -+ last_receiver, -+ last_poll, -+ ctime = 0, -+ timer, -+ req_list = [] % list of requests -+ }). -+ -+ -+%-define(DBGFSM, true). -+ -+-ifdef(DBGFSM). -+-define(FSMOPTS, [{debug, [trace]}]). -+-else. -+-define(FSMOPTS, []). -+-endif. -+ -+-define(MAX_REQUESTS, 2). % number of simultaneous requests -+-define(MIN_POLLING, "2"). % don't poll faster than that or we will shoot you -+-define(MAX_WAIT, 60). % max num of secs to keep a request on hold -+-define(MAX_INACTIVITY, 30000). % msecs to wait before terminating idle sessions -+-define(CT, {"Content-Type", "text/xml; charset=utf-8"}). -+-define(BAD_REQUEST, ?CT). -+ -+ -+%%%---------------------------------------------------------------------- -+%%% API -+%%%---------------------------------------------------------------------- -+start(ID, Key) -> -+ mnesia:create_table(http_bind, -+ [{ram_copies, [node()]}, -+ {attributes, record_info(fields, http_bind)}]), -+ supervisor:start_child(ejabberd_http_bind_sup, [ID, Key]). -+ -+start_link(ID, Key) -> -+ gen_fsm:start_link(?MODULE, [ID, Key], ?FSMOPTS). -+ -+send({http_bind, FsmRef}, Packet) -> -+ gen_fsm:sync_send_all_state_event(FsmRef, {send, Packet}). -+ -+setopts({http_bind, FsmRef}, Opts) -> -+ case lists:member({active, once}, Opts) of -+ true -> -+ gen_fsm:sync_send_all_state_event(FsmRef, activate); -+ _ -> -+ ok -+ end. -+ -+controlling_process(_Socket, _Pid) -> -+ ok. -+ -+close({http_bind, FsmRef}) -> -+ catch gen_fsm:sync_send_all_state_event(FsmRef, close). -+ -+ -+process_request(#request{path = [], -+ data = Data}) -> -+ case catch parse_request(Data) of -+ {ok, ID1, RID, Key, NewKey, Attrs, Packet} -> -+ XmppDomain = xml:get_attr_s("to",Attrs), -+ if -+ (ID1 == "") and (XmppDomain == "") -> -+ {200, [?CT], "<body type='terminate' " -+ "condition='improper-addressing' " -+ "xmlns='http://jabber.org/protocol/httpbind'/>"}; -+ true -> -+ ID = if -+ (ID1 == "") -> -+ %% create new session -+ NewID = sha:sha(term_to_binary({now(), make_ref()})), -+ {ok, Pid} = start(NewID, Key), -+ Wait = case -+ string:to_integer(xml:get_attr_s("wait",Attrs)) -+ of -+ {error, _} -> -+ ?MAX_WAIT; -+ {CWait, _} -> -+ if -+ (CWait > ?MAX_WAIT) -> -+ ?MAX_WAIT; -+ true -> -+ CWait -+ end -+ end, -+ Hold = case -+ string:to_integer( -+ xml:get_attr_s("hold",Attrs)) -+ of -+ {error, _} -> -+ (?MAX_REQUESTS - 1); -+ {CHold, _} -> -+ if -+ (CHold > (?MAX_REQUESTS - 1)) -> -+ (?MAX_REQUESTS - 1); -+ true -> -+ CHold -+ end -+ end, -+ mnesia:transaction( -+ fun() -> -+ mnesia:write(#http_bind{id = NewID, -+ pid = Pid, -+ to = XmppDomain, -+ wait = Wait, -+ hold = Hold}) -+ end), -+ StreamStart = if -+ (XmppDomain /= "") -> -+ true; -+ true -> -+ false -+ end, -+ InPacket = Packet, -+ NewID; -+ true -> -+ %% old session -+ Type = xml:get_attr_s("type",Attrs), -+ StreamStart = -+ case xml:get_attr_s("xmpp:restart",Attrs) of -+ "true" -> -+ true; -+ _ -> -+ false -+ end, -+ Wait = ?MAX_WAIT, -+ Hold = (?MAX_REQUESTS - 1), -+ if -+ (Type == "terminate") -> -+ %% terminate session -+ InPacket = Packet ++ "</stream:stream>"; -+ true -> -+ InPacket = Packet -+ end, -+ ID1 -+ end, -+%% ?DEBUG("~n InPacket: ~s ~n", [InPacket]), -+ case http_put(ID, RID, Key, NewKey, Hold, InPacket, StreamStart) of -+ {error, not_exists} -> -+ ?DEBUG("no session associated with sid: ~p", [ID]), -+ {404, [?BAD_REQUEST], ""}; -+ {error, bad_key} -> -+ ?DEBUG("bad key: ~s", [Key]), -+ case mnesia:dirty_read({http_bind, ID}) of -+ [] -> -+ {404, [?BAD_REQUEST], ""}; -+ [#http_bind{pid = FsmRef}] -> -+ gen_fsm:sync_send_all_state_event(FsmRef,stop), -+ {404, [?BAD_REQUEST], ""} -+ end; -+ {error, polling_too_frequently} -> -+ ?DEBUG("polling too frequently: ~p", [ID]), -+ case mnesia:dirty_read({http_bind, ID}) of -+ [] -> %% unlikely! (?) -+ {404, [?BAD_REQUEST], ""}; -+ [#http_bind{pid = FsmRef}] -> -+ gen_fsm:sync_send_all_state_event(FsmRef,stop), -+ {403, [?BAD_REQUEST], ""} -+ end; -+ {repeat, OutPacket} -> -+ ?DEBUG("http_put said 'repeat!' ...~nOutPacket: ~p", -+ [OutPacket]), -+ send_outpacket(ID, OutPacket); -+ ok -> -+ receive_loop(ID,ID1,RID,Wait,Hold,Attrs) -+ end -+ end; -+ _ -> -+ {400, [?BAD_REQUEST], ""} -+ end; -+process_request(_Request) -> -+ {400, [], {xmlelement, "h1", [], -+ [{xmlcdata, "400 Bad Request"}]}}. -+ -+receive_loop(ID,ID1,RID,Wait,Hold,Attrs) -> -+ receive -+ after 100 -> ok -+ end, -+ prepare_response(ID,ID1,RID,Wait,Hold,Attrs). -+ -+prepare_response(ID,ID1,RID,Wait,Hold,Attrs) -> -+ case http_get(ID,RID) of -+ {error, not_exists} -> -+ ?DEBUG("no session associated with sid: ~s", ID), -+ {404, [?BAD_REQUEST], ""}; -+ {ok, keep_on_hold} -> -+ receive_loop(ID,ID1,RID,Wait,Hold,Attrs); -+ {ok, cancel} -> -+ %% actually it would be better if we could completely -+ %% cancel this request, but then we would have to hack -+ %% ejabberd_http and I'm too lazy now -+ {404, [?BAD_REQUEST], ""}; -+ {ok, OutPacket} -> -+ ?DEBUG("OutPacket: ~s", [OutPacket]), -+ if -+ ID == ID1 -> -+ send_outpacket(ID, OutPacket); -+ true -> -+ To = xml:get_attr_s("to",Attrs), -+ OutEls = case xml_stream:parse_element( -+ OutPacket++"</stream:stream>") of -+ El when element(1, El) == xmlelement -> -+ {xmlelement, _, OutAttrs, Els} = El, -+ AuthID = xml:get_attr_s("id", OutAttrs), -+ StreamError = false, -+ case Els of -+ [] -> -+ []; -+ [{xmlelement, "stream:features", StreamAttribs, StreamEls} | StreamTail] -> -+ [{xmlelement, "stream:features", [{"xmlns:stream","http://etherx.jabber.org/streams"}] ++ StreamAttribs, StreamEls}] ++ StreamTail; -+ Xml -> -+ Xml -+ end; -+ {error, _} -> -+ AuthID = "", -+ StreamError = true, -+ [] -+ end, -+ if -+ To == "" -> -+ {200, [?CT], "<body type='terminate' " -+ "condition='improper-addressing' " -+ "xmlns='http://jabber.org/protocol/httpbind'/>"}; -+ StreamError == true -> -+ {200, [?CT], "<body type='terminate' " -+ "condition='host-unknown' " -+ "xmlns='http://jabber.org/protocol/httpbind'/>"}; -+ true -> -+ {200, [?CT], -+ xml:element_to_string( -+ {xmlelement,"body", -+ [{"xmlns", -+ "http://jabber.org/protocol/httpbind"}, -+ {"sid",ID}, -+ {"wait", integer_to_list(Wait)}, -+ {"requests", integer_to_list(Hold+1)}, -+ {"inactivity", -+ integer_to_list(trunc(?MAX_INACTIVITY/1000))}, -+ {"polling", ?MIN_POLLING}, -+ {"authid", AuthID} -+ ],OutEls})} -+ end -+ end -+ end. -+ -+send_outpacket(ID, OutPacket) -> -+ case OutPacket of -+ "" -> -+ {200, [?CT], "<body xmlns='http://jabber.org/protocol/httpbind'/>"}; -+ "</stream:stream>" -> -+ case mnesia:dirty_read({http_bind, ID}) of -+ [#http_bind{pid = FsmRef}] -> -+ gen_fsm:sync_send_all_state_event(FsmRef,stop) -+ end, -+ {200, [?CT], "<body xmlns='http://jabber.org/protocol/httpbind'/>"}; -+ _ -> -+ case xml_stream:parse_element("<body>" -+ ++ OutPacket -+ ++ "</body>") -+ of -+ El when element(1, El) == xmlelement -> -+ {xmlelement, _, _, OEls} = El, -+ TypedEls = [xml:replace_tag_attr("xmlns", -+ "jabber:client",OEl) || -+ OEl <- OEls], -+ ?DEBUG(" --- outgoing data --- ~n~s~n --- END --- ~n", -+ [xml:element_to_string( -+ {xmlelement,"body", -+ [{"xmlns", -+ "http://jabber.org/protocol/httpbind"}], -+ TypedEls})] -+ ), -+ {200, [?CT], -+ xml:element_to_string( -+ {xmlelement,"body", -+ [{"xmlns", -+ "http://jabber.org/protocol/httpbind"}], -+ TypedEls})}; -+ {error, _E} -> -+ OutEls = case xml_stream:parse_element( -+ OutPacket++"</stream:stream>") of -+ SEl when element(1, SEl) == xmlelement -> -+ {xmlelement, _, _OutAttrs, SEls} = SEl, -+ StreamError = false, -+ case SEls of -+ [] -> -+ []; -+ [{xmlelement, "stream:features", StreamAttribs, StreamEls} | StreamTail] -> -+ TypedTail = [xml:replace_tag_attr("xmlns", -+ "jabber:client",OEl) || -+ OEl <- StreamTail], -+ [{xmlelement, "stream:features", [{"xmlns:stream","http://etherx.jabber.org/streams"}] ++ StreamAttribs, StreamEls}] ++ TypedTail; -+ Xml -> -+ Xml -+ end; -+ {error, _} -> -+ StreamError = true, -+ [] -+ end, -+ if -+ StreamError -> -+ StreamErrCond = case xml_stream:parse_element( -+ "<stream:stream>"++OutPacket) of -+ El when element(1, El) == xmlelement -> -+ {xmlelement, _Tag, _Attr, Els} = El, -+ [{xmlelement, SE, _, Cond} | _] = Els, -+ if -+ SE == "stream:error" -> -+ Cond; -+ true -> -+ null -+ end; -+ {error, _E} -> -+ null -+ end, -+ case mnesia:dirty_read({http_bind, ID}) of -+ [#http_bind{pid = FsmRef}] -> -+ gen_fsm:sync_send_all_state_event(FsmRef,stop); -+ _ -> -+ err %% hu? -+ end, -+ case StreamErrCond of -+ null -> -+ {200, [?CT], -+ "<body type='terminate' " -+ "condition='internal-server-error' " -+ "xmlns='http://jabber.org/protocol/httpbind'/>"}; -+ _ -> -+ {200, [?CT], -+ "<body type='terminate' " -+ "condition='remote-stream-error' " -+ "xmlns='http://jabber.org/protocol/httpbind'>" ++ -+ elements_to_string(StreamErrCond) ++ -+ "</body>"} -+ end; -+ true -> -+ {200, [?CT], -+ xml:element_to_string( -+ {xmlelement,"body", -+ [{"xmlns", -+ "http://jabber.org/protocol/httpbind"}], -+ OutEls})} -+ end -+ end -+ end. -+ -+%%%---------------------------------------------------------------------- -+%%% Callback functions from gen_fsm -+%%%---------------------------------------------------------------------- -+ -+%%---------------------------------------------------------------------- -+%% Func: init/1 -+%% Returns: {ok, StateName, StateData} | -+%% {ok, StateName, StateData, Timeout} | -+%% ignore | -+%% {stop, StopReason} -+%%---------------------------------------------------------------------- -+init([ID, Key]) -> -+ ?INFO_MSG("started: ~p", [{ID, Key}]), -+ Opts = [], % TODO -+ {ok, C2SPid} = ejabberd_c2s:start({?MODULE, {http_bind, self()}}, Opts), -+ ejabberd_c2s:become_controller(C2SPid), -+ Timer = erlang:start_timer(?MAX_INACTIVITY, self(), []), -+ {ok, loop, #state{id = ID, -+ key = Key, -+ timer = Timer}}. -+ -+%%---------------------------------------------------------------------- -+%% Func: StateName/2 -+%% Returns: {next_state, NextStateName, NextStateData} | -+%% {next_state, NextStateName, NextStateData, Timeout} | -+%% {stop, Reason, NewStateData} -+%%---------------------------------------------------------------------- -+ -+ -+%%---------------------------------------------------------------------- -+%% Func: StateName/3 -+%% Returns: {next_state, NextStateName, NextStateData} | -+%% {next_state, NextStateName, NextStateData, Timeout} | -+%% {reply, Reply, NextStateName, NextStateData} | -+%% {reply, Reply, NextStateName, NextStateData, Timeout} | -+%% {stop, Reason, NewStateData} | -+%% {stop, Reason, Reply, NewStateData} -+%%---------------------------------------------------------------------- -+%state_name(Event, From, StateData) -> -+% Reply = ok, -+% {reply, Reply, state_name, StateData}. -+ -+%%---------------------------------------------------------------------- -+%% Func: handle_event/3 -+%% Returns: {next_state, NextStateName, NextStateData} | -+%% {next_state, NextStateName, NextStateData, Timeout} | -+%% {stop, Reason, NewStateData} -+%%---------------------------------------------------------------------- -+handle_event(_Event, StateName, StateData) -> -+ {next_state, StateName, StateData}. -+ -+%%---------------------------------------------------------------------- -+%% Func: handle_sync_event/4 -+%% Returns: {next_state, NextStateName, NextStateData} | -+%% {next_state, NextStateName, NextStateData, Timeout} | -+%% {reply, Reply, NextStateName, NextStateData} | -+%% {reply, Reply, NextStateName, NextStateData, Timeout} | -+%% {stop, Reason, NewStateData} | -+%% {stop, Reason, Reply, NewStateData} -+%%---------------------------------------------------------------------- -+handle_sync_event({send, Packet}, _From, StateName, StateData) -> -+ Output = [StateData#state.output | Packet], -+ Reply = ok, -+ {reply, Reply, StateName, StateData#state{output = Output}}; -+ -+handle_sync_event(activate, From, StateName, StateData) -> -+ case StateData#state.input of -+ "" -> -+ {reply, ok, StateName, StateData#state{ -+ waiting_input = From}}; -+ Input -> -+ From ! {tcp, {http_bind, self()}, list_to_binary(Input)}, -+ {reply, ok, StateName, StateData#state{ -+ input = "", -+ waiting_input = false, -+ last_receiver = From}} -+ end; -+ -+handle_sync_event(stop, _From, _StateName, StateData) -> -+ Reply = ok, -+ {stop, normal, Reply, StateData}; -+ -+handle_sync_event({http_put, RID, Key, NewKey, Hold, Packet, StartTo}, -+ _From, StateName, StateData) -> -+ %% check if RID valid -+ RidAllow = case RID of -+ error -> -+ false; -+ _ -> -+ case StateData#state.rid of -+ error -> -+ %% first request - nothing saved so far -+ true; -+ OldRID -> -+ ?DEBUG("state.rid/cur rid: ~p/~p", -+ [OldRID, RID]), -+ if -+ (OldRID < RID) and -+ (RID =< (OldRID + Hold + 1)) -> -+ true; -+ (RID =< OldRID) and -+ (RID > OldRID - Hold - 1) -> -+ repeat; -+ true -> -+ false -+ end -+ end -+ end, -+ %% check if key valid -+ KeyAllow = case RidAllow of -+ repeat -> -+ true; -+ false -> -+ false; -+ true -> -+ case StateData#state.key of -+ "" -> -+ true; -+ OldKey -> -+ NextKey = httpd_util:to_lower( -+ hex(binary_to_list( -+ crypto:sha(Key)))), -+ ?DEBUG("Key/OldKey/NextKey: ~s/~s/~s", -+ [Key, OldKey, NextKey]), -+ if -+ OldKey == NextKey -> -+ true; -+ true -> -+ ?DEBUG("wrong key: ~s",[Key]), -+ false -+ end -+ end -+ end, -+ {_,TSec,TMSec} = now(), -+ TNow = TSec*1000*1000 + TMSec, -+ LastPoll = if -+ Packet == "" -> -+ TNow; -+ true -> -+ 0 -+ end, -+ {MinPoll, _} = string:to_integer(?MIN_POLLING), -+ if -+ (Packet == "") and -+ (TNow - StateData#state.last_poll < MinPoll*1000*1000) -> -+ Reply = {error, polling_too_frequently}, -+ {reply, Reply, StateName, StateData}; -+ KeyAllow -> -+ case RidAllow of -+ false -> -+ Reply = {error, not_exists}, -+ {reply, Reply, StateName, StateData}; -+ repeat -> -+ ?DEBUG("REPEATING ~p", [RID]), -+ [Out | _XS] = [El#hbr.out || -+ El <- StateData#state.req_list, -+ El#hbr.rid == RID], -+ case Out of -+ [[] | OutPacket] -> -+ Reply = {repeat, OutPacket}; -+ _ -> -+ Reply = {repeat, Out} -+ end, -+ {reply, Reply, StateName, -+ StateData#state{input = "cancel", last_poll = LastPoll}}; -+ true -> -+ SaveKey = if -+ NewKey == "" -> -+ Key; -+ true -> -+ NewKey -+ end, -+ ?DEBUG(" -- SaveKey: ~s~n", [SaveKey]), -+ -+ %% save request -+ ReqList = [#hbr{rid=RID, -+ key=StateData#state.key, -+ in=StateData#state.input, -+ out=StateData#state.output -+ } | -+ [El || El <- StateData#state.req_list, -+ El#hbr.rid < RID, -+ El#hbr.rid > (RID - 1 - Hold)] -+ ], -+%% ?DEBUG("reqlist: ~p", [ReqList]), -+ case StateData#state.waiting_input of -+ false -> -+ cancel_timer(StateData#state.timer), -+ Timer = erlang:start_timer( -+ ?MAX_INACTIVITY, self(), []), -+ Input = Packet ++ [StateData#state.input], -+ Reply = ok, -+ {reply, Reply, StateName, -+ StateData#state{input = Input, -+ rid = RID, -+ key = SaveKey, -+ ctime = TNow, -+ timer = Timer, -+ last_poll = LastPoll, -+ req_list = ReqList -+ }}; -+ {Receiver, _Tag} -> -+ SendPacket = -+ if -+ StartTo /= "" -> -+ ["<stream:stream to='", -+ StartTo, -+ "' xmlns='jabber:client' " -+ "version='1.0' " -+ "xmlns:stream='http://etherx.jabber.org/streams'>"] ++ Packet; -+ true -> -+ Packet -+ end, -+ ?DEBUG("really sending now: ~s", [SendPacket]), -+ Receiver ! {tcp, {http_bind, self()}, -+ list_to_binary(SendPacket)}, -+ cancel_timer(StateData#state.timer), -+ Timer = erlang:start_timer( -+ ?MAX_INACTIVITY, self(), []), -+ Reply = ok, -+ {reply, Reply, StateName, -+ StateData#state{waiting_input = false, -+ last_receiver = Receiver, -+ input = "", -+ rid = RID, -+ key = SaveKey, -+ ctime = TNow, -+ timer = Timer, -+ last_poll = LastPoll, -+ req_list = ReqList -+ }} -+ end -+ end; -+ true -> -+ Reply = {error, bad_key}, -+ {reply, Reply, StateName, StateData} -+ end; -+ -+handle_sync_event({http_get, RID, Wait, Hold}, _From, StateName, StateData) -> -+ {_,TSec,TMSec} = now(), -+ TNow = TSec*1000*1000 + TMSec, -+ cancel_timer(StateData#state.timer), -+ Timer = erlang:start_timer(?MAX_INACTIVITY, self(), []), -+ if -+ (Hold > 0) and -+ (StateData#state.output == "") and -+ ((TNow - StateData#state.ctime) < (Wait*1000*1000)) and -+ (StateData#state.rid == RID) and -+ (StateData#state.input /= "cancel") -> -+ Output = StateData#state.output, -+ ReqList = StateData#state.req_list, -+ Reply = {ok, keep_on_hold}; -+ (StateData#state.input == "cancel") -> -+ Output = StateData#state.output, -+ ReqList = StateData#state.req_list, -+ Reply = {ok, cancel}; -+ true -> -+ case StateData#state.output of -+ [[]| OutPacket] -> -+ Reply = {ok, OutPacket}; -+ _ -> -+ Reply = {ok, StateData#state.output} -+ end, -+ %% save request -+ ReqList = [#hbr{rid=RID, -+ key=StateData#state.key, -+ in=StateData#state.input, -+ out=StateData#state.output -+ } | -+ [El || El <- StateData#state.req_list, -+ El#hbr.rid /= RID ] -+ ], -+ Output = "" -+ end, -+ {reply, Reply, StateName, StateData#state{ -+ input = "", -+ output = Output, -+ timer = Timer, -+ req_list = ReqList}}; -+ -+handle_sync_event(_Event, _From, StateName, StateData) -> -+ Reply = ok, -+ {reply, Reply, StateName, StateData}. -+ -+code_change(_OldVsn, StateName, StateData, _Extra) -> -+ {ok, StateName, StateData}. -+ -+%%---------------------------------------------------------------------- -+%% Func: handle_info/3 -+%% Returns: {next_state, NextStateName, NextStateData} | -+%% {next_state, NextStateName, NextStateData, Timeout} | -+%% {stop, Reason, NewStateData} -+%%---------------------------------------------------------------------- -+handle_info({timeout, Timer, _}, _StateName, -+ #state{timer = Timer} = StateData) -> -+ ?DEBUG("ding dong", []), -+ {stop, normal, StateData}; -+ -+handle_info(_, StateName, StateData) -> -+ {next_state, StateName, StateData}. -+ -+%%---------------------------------------------------------------------- -+%% Func: terminate/3 -+%% Purpose: Shutdown the fsm -+%% Returns: any -+%%---------------------------------------------------------------------- -+terminate(_Reason, _StateName, StateData) -> -+ ?DEBUG("terminate: deleting session ~s", [StateData#state.id]), -+ mnesia:transaction( -+ fun() -> -+ mnesia:delete({http_bind, StateData#state.id}) -+ end), -+ case StateData#state.waiting_input of -+ false -> -+ case StateData#state.last_receiver of -+ undefined -> ok; -+ Receiver -> Receiver ! {tcp_closed, {http_bind, self()}} -+ end; -+ {Receiver, _Tag} -> Receiver ! {tcp_closed, {http_bind, self()}} -+ end, -+ ok. -+ -+%%%---------------------------------------------------------------------- -+%%% Internal functions -+%%%---------------------------------------------------------------------- -+ -+ -+http_put(ID, RID, Key, NewKey, Hold, Packet, Restart) -> -+ ?DEBUG("http-put",[]), -+ case mnesia:dirty_read({http_bind, ID}) of -+ [] -> -+ ?DEBUG("not found",[]), -+ {error, not_exists}; -+ [#http_bind{pid = FsmRef,to=To}] -> -+ case Restart of -+ true -> -+ ?DEBUG("restart requested for ~s", [To]), -+ gen_fsm:sync_send_all_state_event( -+ FsmRef, {http_put, RID, Key, NewKey, Hold, Packet, To}); -+ _ -> -+ gen_fsm:sync_send_all_state_event( -+ FsmRef, {http_put, RID, Key, NewKey, Hold, Packet, ""}) -+ end -+ end. -+ -+http_get(ID,RID) -> -+ case mnesia:dirty_read({http_bind, ID}) of -+ [] -> -+ {error, not_exists}; -+ [#http_bind{pid = FsmRef, wait = Wait, hold = Hold}] -> -+ gen_fsm:sync_send_all_state_event(FsmRef, -+ {http_get, RID, Wait, Hold}) -+ end. -+ -+ -+parse_request(Data) -> -+ ?DEBUG("--- incoming data --- ~n~s~n --- END --- ", -+ [Data]), -+ case xml_stream:parse_element(Data) of -+ El when element(1, El) == xmlelement -> -+ {xmlelement, Name, Attrs, Els} = El, -+ ID = xml:get_attr_s("sid",Attrs), -+ {RID,_X} = string:to_integer(xml:get_attr_s("rid",Attrs)), -+ Key = xml:get_attr_s("key",Attrs), -+ NewKey = xml:get_attr_s("newkey",Attrs), -+ Xmlns = xml:get_attr_s("xmlns",Attrs), -+ lists:map(fun(E) -> -+ EXmlns = xml:get_tag_attr_s("xmlns",E), -+ if -+ EXmlns == "jabber:client" -> -+ xml:remove_tag_attr("xmlns",E); -+ true -> -+ ok -+ end -+ end, Els), -+ Packet = [xml:element_to_string(E) || E <- Els], -+ ?DEBUG("ns fixed packet(s): ~s", [Packet]), -+ if -+ Name /= "body" -> -+ {error, bad_request}; -+ Xmlns /= "http://jabber.org/protocol/httpbind" -> -+ {error, bad_request}; -+ true -> -+ {ok, ID, RID, Key, NewKey, Attrs, Packet} -+ end; -+ {error, _Reason} -> -+ {error, bad_request} -+ end. -+ -+cancel_timer(Timer) -> -+ erlang:cancel_timer(Timer), -+ receive -+ {timeout, Timer, _} -> -+ ok -+ after 0 -> -+ ok -+ end. -+ -+hex(Bin) when binary(Bin) -> hex(binary_to_list(Bin)); -+hex([]) -> ""; -+hex([H|T]) -> -+ [A,B] = if -+ H == 0 -> "00"; -+ H < 16 -> [$0,element(H,{$1,$2,$3,$4,$5,$6,$7,$8,$9,$a,$b,$c,$d,$e,$f})]; -+ true -> erlang:integer_to_list(H,16) -+ end, -+ [A,B|hex(T)]. -+ -+elements_to_string([]) -> -+ []; -+elements_to_string([El | Els]) -> -+ xml:element_to_string(El) ++ elements_to_string(Els). -Index: src/web/ejabberd_web.erl -=================================================================== ---- src/web/ejabberd_web.erl (Revision 565) -+++ src/web/ejabberd_web.erl (Arbeitskopie) -@@ -49,7 +49,7 @@ - {"value", Value}])). - - --process_get({_, true}, -+process_get({_, _, true}, - #request{auth = Auth, - path = ["admin", "server", SHost | RPath], - q = Query, -@@ -94,7 +94,7 @@ - {404, [], make_xhtml([?XC("h1", "Not found")])} - end; - --process_get({_, true}, -+process_get({_, _, true}, - #request{auth = Auth, - path = ["admin" | RPath], - q = Query, -@@ -133,12 +133,19 @@ - [{xmlcdata, "401 Unauthorized"}]}])} - end; - --process_get({true, _}, -+process_get({true, _, _}, - #request{path = ["http-poll" | RPath], - q = _Query, - lang = _Lang} = Request) -> - ejabberd_http_poll:process_request(Request#request{path = RPath}); - -+process_get({_, true, _}, -+ #request{us = _US, -+ path = ["http-bind" | RPath], -+ q = _Query, -+ lang = _Lang} = Request) -> -+ ejabberd_http_bind:process_request(Request#request{path = RPath}); -+ - process_get(_, _Request) -> - {404, [], make_xhtml([?XC("h1", "Not found")])}. - -Index: src/web/ejabberd_http.erl -=================================================================== ---- src/web/ejabberd_http.erl (Revision 565) -+++ src/web/ejabberd_http.erl (Arbeitskopie) -@@ -30,6 +30,7 @@ - request_keepalive, - request_content_length, - request_lang = "en", -+ use_http_bind = false, - use_http_poll = false, - use_web_admin = false, - end_of_request = false, -@@ -70,15 +71,17 @@ - _ -> - ok - end, -+ UseHTTPBind = lists:member(http_bind, Opts), - UseHTTPPoll = lists:member(http_poll, Opts), - UseWebAdmin = lists:member(web_admin, Opts), -- ?DEBUG("S: ~p~n", [{UseHTTPPoll, UseWebAdmin}]), -+ ?DEBUG("S: ~p~n", [{UseHTTPPoll, UseHTTPBind, UseWebAdmin}]), - ?INFO_MSG("started: ~p", [{SockMod1, Socket1}]), - {ok, proc_lib:spawn_link(ejabberd_http, - receive_headers, - [#state{sockmod = SockMod1, - socket = Socket1, - use_http_poll = UseHTTPPoll, -+ use_http_bind = UseHTTPBind, - use_web_admin = UseWebAdmin}])}. - - -@@ -185,6 +188,7 @@ - #state{sockmod = SockMod, - socket = Socket, - use_http_poll = State#state.use_http_poll, -+ use_http_bind = State#state.use_http_bind, - use_web_admin = State#state.use_web_admin}; - _ -> - #state{end_of_request = true} -@@ -200,6 +204,7 @@ - request_auth = Auth, - request_lang = Lang, - use_http_poll = UseHTTPPoll, -+ use_http_bind = UseHTTPBind, - use_web_admin = UseWebAdmin} = State) -> - case (catch url_decode_q_split(Path)) of - {'EXIT', _} -> -@@ -217,7 +222,7 @@ - q = LQuery, - auth = Auth, - lang = Lang}, -- case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, -+ case ejabberd_web:process_get({UseHTTPPoll, UseHTTPBind, UseWebAdmin}, - Request) of - El when element(1, El) == xmlelement -> - make_xhtml_output(State, 200, [], El); -@@ -240,6 +245,7 @@ - sockmod = SockMod, - socket = Socket, - use_http_poll = UseHTTPPoll, -+ use_http_bind = UseHTTPBind, - use_web_admin = UseWebAdmin} = State) - when is_integer(Len) -> - case SockMod of -@@ -267,7 +273,7 @@ - auth = Auth, - data = Data, - lang = Lang}, -- case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, -+ case ejabberd_web:process_get({UseHTTPPoll, UseHTTPBind, UseWebAdmin}, - Request) of - El when element(1, El) == xmlelement -> - make_xhtml_output(State, 200, [], El); -Index: src/xml.erl -=================================================================== ---- src/xml.erl (Revision 565) -+++ src/xml.erl (Arbeitskopie) -@@ -18,6 +18,7 @@ - get_tag_attr/2, get_tag_attr_s/2, - get_subtag/2, - get_path_s/2, -+ remove_tag_attr/2, - replace_tag_attr/3]). - - %element_to_string(El) -> -@@ -224,6 +225,14 @@ - get_path_s(El, [cdata]) -> - get_tag_cdata(El). - -+remove_tag_attr(Attr, El) -> -+ case El of -+ {xmlelement, Name, Attrs, Els} -> -+ Attrs1 = lists:keydelete(Attr, 1, Attrs), -+ {xmlelement, Name, Attrs1, Els}; -+ _ -> -+ El -+ end. - - replace_tag_attr(Attr, Value, {xmlelement, Name, Attrs, Els}) -> - Attrs1 = lists:keydelete(Attr, 1, Attrs), -Index: src/ejabberd_sup.erl -=================================================================== ---- src/ejabberd_sup.erl (Revision 565) -+++ src/ejabberd_sup.erl (Arbeitskopie) -@@ -123,6 +123,14 @@ - infinity, - supervisor, - [ejabberd_tmp_sup]}, -+ HTTPBindSupervisor = -+ {ejabberd_http_bind_sup, -+ {ejabberd_tmp_sup, start_link, -+ [ejabberd_http_bind_sup, ejabberd_http_bind]}, -+ permanent, -+ infinity, -+ supervisor, -+ [ejabberd_tmp_sup]}, - IQSupervisor = - {ejabberd_iq_sup, - {ejabberd_tmp_sup, start_link, -@@ -145,6 +153,7 @@ - ServiceSupervisor, - HTTPSupervisor, - HTTPPollSupervisor, -+ HTTPBindSupervisor, - IQSupervisor, - Listener]}}. - diff --git a/net-im/ejabberd/files/mod_archive.erl b/net-im/ejabberd/files/mod_archive.erl deleted file mode 100644 index 88f0c8a7..00000000 --- a/net-im/ejabberd/files/mod_archive.erl +++ /dev/null @@ -1,695 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_archive.erl -%%% Author : Olivier Goffart <ogoffar@kde.org> -%%% Purpose : Message Archiving (JEP-0136) -%%% Created : 19 Aug 2006 -%%%---------------------------------------------------------------------- - -%% Version 0.0.1 2006-08-19 -%% Version 0.0.2 2006-08-21 -%% Version 0.0.3 2006-08-22 - - -%% Options: -%% save_default -> true | false if messages are stored by default or not -%% session_duration -> time in secondes before the timeout of a session - - --module(mod_archive). --author('ogoffart@kde.org'). - --vsn('0.0.3.5'). --behaviour(gen_mod). - --export([start/2, stop/1, loop/3 , - remove_user/2, send_packet/3, receive_packet/4, - process_iq/3, process_local_iq/3]). - --include("ejabberd.hrl"). --include("jlib.hrl"). - - --define(PROCNAME, ejabberd_mod_archive). --define(NS_ARCHIVE, "http://jabber.org/protocol/archive"). --define(INFINITY, calendar:datetime_to_gregorian_seconds({{2038,1,19},{0,0,0}}) ). - --define(MYDEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n", - [calendar:local_time(),?MODULE,?LINE]++Args)). - - -%NOTE i was not sure what format to adopt for archive_option. otr_list is unused --record(archive_options, {us, default=unset , save_list = [] , nosave_list =[] , otr_list = [] }). -%-record(archive_options, {usj, us , jid , type , value}). --record(archive_message, { usjs , us , jid , start , message_list=[] , subject=""}). --record(msg , {dirrection , secs, body }). - -start(Host, Opts) -> - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - Save_Default = gen_mod:get_opt(save_default, Opts, false), - Session_Duration = gen_mod:get_opt(session_duration, Opts, 1300), - mnesia:create_table(archive_options,[{disc_copies, [node()]}, {attributes, record_info(fields, archive_options)}]), - mnesia:create_table(archive_message,[{disc_copies, [node()]}, {attributes, record_info(fields, archive_message)}]), -% mnesia:add_table_index(archive_options, us), - mnesia:add_table_index(archive_message, us), - ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ARCHIVE, ?MODULE, process_iq, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_ARCHIVE, ?MODULE, process_local_iq, IQDisc), - ejabberd_hooks:add(user_send_packet, Host, ?MODULE, send_packet, 90), - ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, receive_packet, 90), - catch mod_disco:register_feature(Host, ?NS_ARCHIVE ++ "#manage"), - catch mod_disco:register_feature(Host, ?NS_ARCHIVE ++ "#save"), - catch mod_disco:register_feature(Host, ?NS_ARCHIVE ++ "#manual"), - register(gen_mod:get_module_proc(Host, ?PROCNAME), spawn(?MODULE, loop, [Host, [], { Save_Default, Session_Duration }])). - -stop(Host) -> - ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_ARCHIVE), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ARCHIVE), - ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, send_packet, 90), - ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, receive_packet, 90), - catch mod_disco:unregister_feature(Host, ?NS_ARCHIVE ++ "#manage"), - catch mod_disco:unregister_feature(Host, ?NS_ARCHIVE ++ "#save"), - catch mod_disco:unregister_feature(Host, ?NS_ARCHIVE ++ "#manual"), - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - Proc ! stop, - {wait, Proc}. - - -%Workarounf the fact that if the client send <iq type='get'> it end up like <iq type='get' from='u@h' to = 'u@h'> -process_iq(From, To, IQ) -> - #iq{sub_el = SubEl} = IQ, - #jid{lserver = LServer, luser = LUser} = To, - #jid{luser = FromUser} = From, - case {LUser , LServer , lists:member(LServer, ?MYHOSTS)} of - { FromUser , _ , true } -> - process_local_iq(From, To, IQ); - { "" , _ , true } -> - process_local_iq(From, To, IQ); - { "" , "" , _ } -> - process_local_iq(From, To, IQ); - _ -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]} - end. - -process_local_iq(From, To, #iq{sub_el = SubEl} = IQ) -> - case lists:member(From#jid.lserver, ?MYHOSTS) of - false -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - true -> - {xmlelement, Name, _Attrs, _Els} = SubEl, - case Name of - "save" -> process_local_iq_save( From, To , IQ ) ; -% "otr" -> process_local_iq_otr( From, To , IQ ) ; - "list" -> process_local_iq_list( From , To , IQ ) ; - "retrieve" -> process_local_iq_retrieve( From , To , IQ ); - "store" -> process_local_iq_store( From , To , IQ ); - "remove" -> process_local_iq_remove( From , To , IQ ); - _ -> IQ#iq{type = error, sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]} - end - end. - - -remove_user(User, Server) -> - LUser = jlib:nodeprep(User), - LServer = jlib:nameprep(Server), - US = {LUser, LServer}, - F = fun() -> - lists:foreach(fun(R) -> - mnesia:delete_object(R) - end, - mnesia:index_read(archive_message, US, #archive_message.us)) - end, - mnesia:transaction(F), - F1 = fun() -> - mnesia:delete({archive_options, US}) - end, - mnesia:transaction(F1). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 3 Automated archiving -%% - -send_packet(From, To, P) -> - Host = From#jid.lserver, - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - Proc ! {addlog, {to, From#jid.luser, Host , To, P}}. - -receive_packet(_JID, From, To, P) -> - Host = To#jid.lserver, - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - Proc ! {addlog, {from, To#jid.luser, Host , From, P}}. - - -% storage loop. Storages is a list containing 'open' storage element -% storage element are in the form: { Timestamp , { LUser , LServer , Jid , Start } } -loop(Host , Storages, { Save_Default, Session_Duration } ) -> - receive - {addlog, { Dirrection , LUser , LServer , Jid , P }} -> - NStorages = case catch should_store_jid( LUser , LServer , Jid , Save_Default) of - false -> Storages; - true -> - case parse_message(P) of - ignore -> Storages; - Body -> catch do_log( Storages , LUser, LServer, Jid , Dirrection, Body , Session_Duration ) - end - end, - loop(Host, NStorages, { Save_Default, Session_Duration }); - {get_save_default, Pid} -> - Pid ! Save_Default, - loop(Host, Storages, { Save_Default, Session_Duration }); - stop -> - ok; - _ -> - loop(Host, Storages, { Save_Default, Session_Duration }) - end. - - - -% parce a message and return the body string if sicessfull, return ignore if the message should not be stored -parse_message({xmlelement , "message" , _ , _ } = Packet) -> - case xml:get_subtag(Packet, "body") of - false -> ignore; - Body_xml -> - case xml:get_tag_attr_s("type", Packet) of - "groupchat" -> ignore ; - _ -> xml:get_tag_cdata(Body_xml) - end - end; -parse_message(_) -> ignore. - -% archive the message Body return the list of new Storages -% Storages: a list of open storages key (usjs) -% LUser , LServer : the local user's information -% Jid : the contact's jid -% Body : the message body -do_log( Storages, LUser, LServer, Jid , Dirrection, Body , Session_Duration ) -> - NStorages = smart_find_storage( LUser , LServer, Jid , Storages , get_timestamp() + Session_Duration ), - [{Tm , { _ , _ , _ , Start } = Key } | _ ] = NStorages, - Message = #msg{dirrection=Dirrection , secs=(Tm-Start), body = Body }, - mnesia:transaction( fun() -> - NE = case mnesia:read({archive_message, Key }) of - [] -> - #archive_message{ usjs= Key , - us = { LUser , LServer } , - jid = jlib:jid_tolower(jlib:jid_remove_resource(Jid)) , - start = Tm , - message_list = [ Message ] }; - [E] -> E#archive_message{ message_list=lists:append( E#archive_message.message_list , [ Message ] ) } - end, - mnesia:write( NE ) - end), - NStorages. - -%find a storage for Jid and move it on the begin on the storage list, if none are found, a new storage is created, old storages element are removed -smart_find_storage( LUser, LServer, Jid , [ C | Tail ] , TimeStampLimit ) -> - NGid=jlib:jid_remove_resource(jlib:jid_tolower(Jid)), - case C of - { _ , { LUser , LServer , NGid , _ } = St } -> - [ { get_timestamp() , St } | Tail ]; - { Tm , _ } -> - if Tm > TimeStampLimit -> - smart_find_storage( LUser, LServer, Jid , [ ] , TimeStampLimit ); - true -> - [ UJ | NT ] = smart_find_storage( LUser , LServer , Jid , Tail , TimeStampLimit ), - [ UJ | [ C | NT ] ] - end - end; - - -smart_find_storage( LUser, LServer, Jid , [ ] , _Limit ) -> - Tm = get_timestamp() , - [ { Tm , { LUser, LServer , jlib:jid_tolower(jlib:jid_remove_resource(Jid)) , Tm } } ]. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 3.1 Save Mode -%% - -process_local_iq_save(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> - Result = case Type of - set -> - {xmlelement, _Name, _Attrs, Els} = SubEl, - process_save_set(From#jid.luser , From#jid.lserver, Els); - get -> - process_save_get(From#jid.luser , From#jid.lserver) - end, - case Result of - {result , R } -> - IQ#iq{type = result, sub_el = [R] }; - ok -> - broadcast_iq(From, IQ#iq{type = set, sub_el=[SubEl]}), - IQ#iq{type = result, sub_el = [] }; - {error , E } -> - IQ#iq{type = error, sub_el = [SubEl , E]}; - _ -> - IQ#iq{type = error, sub_el = [SubEl , ?ERR_INTERNAL_SERVER_ERROR]} - end. - - - - -% return {error , xmlelement} or {result , xmlelement } -process_save_get(LUser, LServer) -> - case catch mnesia:dirty_read(archive_options, {LUser, LServer}) of - {'EXIT', _Reason} -> - {error, ?ERR_INTERNAL_SERVER_ERROR}; - [] -> - {result, {xmlelement , "save" , [{"xmlns", ?NS_ARCHIVE}] , [default_element(LServer)]}}; - [#archive_options{default = Default, save_list = Save_list , nosave_list = Nosave_list}] -> - LItems = lists:append( - lists:map( fun(J) -> - {xmlelement, "item", [{"jid", jlib:jid_to_string(J)}, {"save","true"}] , []} - end, Save_list), - lists:map( fun(J) -> - {xmlelement, "item", [{"jid", jlib:jid_to_string(J)}, {"save","false"}] , []} - end, Nosave_list) ), - DItem = case Default of - true -> - {xmlelement, "default",[{"save", "true"} ], []}; - false -> - {xmlelement, "default",[{"save", "false"} ], []}; - _ -> - default_element(LServer) - end, - {result, {xmlelement , "save" , [{"xmlns", ?NS_ARCHIVE}] , [DItem | LItems] } } - end. - -%return the <default save='unset' service='...' /> element -default_element(Host) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - Proc ! {get_save_default , self()}, - receive - Service -> - {xmlelement, "default",[{"save", "unset"} , {"service" , atom_to_list(Service)}], []} - end. - - -% return {error , xmlelement} or {result , xmlelement } or ok -process_save_set(LUser, LServer, Elms) -> - F = fun() -> - NE = case mnesia:read({archive_options, {LUser, LServer}}) of - [] -> - #archive_options{ us = { LUser , LServer } }; - [E] -> - E - end, - SNE=transaction_parse_save_elem(NE,Elms), - case SNE of - {error , _} -> SNE; - _ -> mnesia:write( SNE ) - end - end, - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, _ } -> - ok; - _ -> {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - - -% transaction_parse_save_elem( archive_options , ListOfXmlElement ) -> #archive_options -% parse the list of xml element, and modify the given archive_option -transaction_parse_save_elem(Options, [ {xmlelement , "default" , Attrs , _ } | Tail]) -> - V = case xml:get_attr_s("save", Attrs) of - "true" -> true; - "false" -> false; - _ -> unset - end, - transaction_parse_save_elem( Options#archive_options{default = V} , Tail ); - -transaction_parse_save_elem(Options, [ {xmlelement , "item" , Attrs , _} | Tail]) -> - case jlib:string_to_jid(xml:get_attr_s("jid", Attrs)) of - error -> { error , ?ERR_JID_MALFORMED }; - #jid{luser=LUser,lserver=LServer,lresource=LResource} -> - Jid = {LUser, LServer, LResource }, - case xml:get_attr_s("save", Attrs) of - "true" -> - transaction_parse_save_elem( Options#archive_options{save_list=[Jid | lists:delete(Jid,Options#archive_options.save_list) ] , - nosave_list=lists:delete(Jid,Options#archive_options.nosave_list) - } , Tail ); - "false" -> - transaction_parse_save_elem( Options#archive_options{save_list=lists:delete(Jid,Options#archive_options.save_list) , - nosave_list=[Jid | lists:delete(Jid,Options#archive_options.nosave_list) ] - } , Tail ); - _ -> - transaction_parse_save_elem( Options#archive_options{save_list=lists:delete(Jid,Options#archive_options.save_list ) , - nosave_list=lists:delete(Jid,Options#archive_options.nosave_list) - } , Tail ) - end - end; - -transaction_parse_save_elem(Options, [ ]) -> Options; -transaction_parse_save_elem(Options, [ _ | Tail ]) -> transaction_parse_save_elem(Options, Tail). - - -broadcast_iq(#jid{luser = User, lserver = Server}, IQ) -> - Fun = fun(Resource) -> - ejabberd_router:route( - jlib:make_jid("", Server, ""), - jlib:make_jid(User, Server, Resource), - jlib:iq_to_xml(IQ#iq{id="push"})) - end, - lists:foreach(Fun, ejabberd_sm:get_user_resources(User,Server)). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 3.1 Off-the-Record Mode -%% - -%TODO - - - - - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Utility function - -% Return true if LUser@LServer should log message for the contact Jid -should_store_jid( LUser , LServer , Jid , Service_Default) -> - case catch mnesia:dirty_read(archive_options, {LUser, LServer}) of - [#archive_options{default = Default, save_list = Save_list , nosave_list = Nosave_list}] -> - Jid_t = jlib:jid_tolower(Jid), - Jid_b = jlib:jid_remove_resource(Jid_t), - A = lists:member(Jid_t, Save_list), - B = lists:member(Jid_t, Nosave_list), - C = lists:member(Jid_b, Save_list), - D = lists:member(Jid_b, Nosave_list), - if A -> true; - B -> false; - C -> true; - D -> false; - Default == true -> true; - Default == false -> false; - true -> Service_Default - end; - _ -> Service_Default - end. - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 4. Manual Archiving -%% - - -process_local_iq_store(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> - #jid{luser = LUser, lserver = LServer} = From, - case Type of - get -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - set -> - case parse_store_element ( LUser, LServer, SubEl ) of - { error , E } -> IQ#iq{type = error, sub_el = [SubEl , E ]} ; - Collection -> - case mnesia:transaction(fun() -> mnesia:write( Collection ) end) of - {atomic, _ } -> - IQ#iq{type = result , sub_el=[]}; - _ -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]} - end - end - end. - - - -% return a #archive_message StoreElem is an xmlelement , or return {error, E} -parse_store_element(LUser, LServer, {xmlelement, "store" , Attrs , SubEls } ) -> - case index_from_argument(LUser, LServer, Attrs) of - {error , E } -> {error , E} ; - { LUser , LServer , Jid , Start } = Index -> - Messages = parse_store_element_sub( SubEls ) , - #archive_message{ usjs = Index , - us = { LUser, LServer} , - jid = Jid , - start = Start, - subject = xml:get_attr_s("subject" , Attrs ) , - message_list = Messages } - end. - -parse_store_element_sub( [ {xmlelement , Dir , _ , _ } = E | Tail ] ) -> - [ #msg{ dirrection=list_to_atom(Dir) , secs= list_to_integer(xml:get_tag_attr_s("secs" , E)), body=xml:get_tag_cdata(xml:get_subtag(E,"body")) } | - parse_store_element_sub( Tail ) ]; - -parse_store_element_sub( [ ] ) -> []; -parse_store_element_sub( [ _ | Tail ] ) -> parse_store_element_sub( Tail ). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 5. Archive Management -%% - - -process_local_iq_list(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> - #jid{luser = LUser, lserver = LServer} = From, - case Type of - set -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - get -> - {xmlelement, _ , Attrs, _} = SubEl, - MaxItems = case xml:get_attr("maxitems" , Attrs) of - false -> all; - {value , V } -> list_to_integer(V) - end, - Result = case parse_root_argument(Attrs) of - {error , E } -> {error , E}; - {interval, Start , Stop } -> get_list(LUser, LServer , Start, Stop, '_' , MaxItems); - {interval, Start , Stop , Jid } -> get_list(LUser, LServer , Start, Stop, Jid , MaxItems); - _ -> { error , ?ERR_BAD_REQUEST } - end, - - case Result of - {ok, Items , Partial} -> - Zero = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}), - Fun = fun(A) -> - Seconds= A#archive_message.start-Zero, - Start2 = jlib:now_to_utc_string( {Seconds div 1000000, Seconds rem 1000000, 0} ) , - Args0 = [{"with", jlib:jid_to_string(A#archive_message.jid)} , {"start" , Start2 } ] , - Args = case A#archive_message.subject of - "" -> Args0; - Subject -> [ {"subject",Subject} | Args0 ] - end, - {xmlelement, "store", Args , [] } - end, - MIA = case Partial of - false -> []; - true -> [{"partial" , "true"}] - end, - IQ#iq{type = result, sub_el = [{xmlelement, "list", [{"xmlns", ?NS_ARCHIVE} | MIA ], lists:map(Fun, lists:keysort(3, Items)) }] }; - {error, R} -> - IQ#iq{type = error, sub_el = [SubEl , R]} - end - end. - - - - -process_local_iq_retrieve(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> - #jid{luser = LUser, lserver = LServer} = From, - case Type of - set -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - get -> - {xmlelement, _ , Attrs , _ } = SubEl, - case index_from_argument(LUser, LServer, Attrs ) of - { error , E } -> IQ#iq{type = error, sub_el = [SubEl , E ]} ; - Index -> - case retrieve_collection( Index ) of - {error, Err } -> IQ#iq{type = error, sub_el = [SubEl , Err]}; - Store -> IQ#iq{type = result, sub_el = [Store] } - end - end - end. - - -retrieve_collection(Index) -> - case get_collection(Index) of - {error, E} -> - {error, E}; - A -> - Zero = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}), - Seconds = A#archive_message.start-Zero, - Start2 = jlib:now_to_utc_string( {Seconds div 1000000, Seconds rem 1000000, 0} ) , - Args0 = [{"xmlns", ?NS_ARCHIVE} , {"with", jlib:jid_to_string(A#archive_message.jid)} , {"start" , Start2 } ] , - Args = case A#archive_message.subject of - "" -> Args0; - Subject -> [ Subject | Args0 ] - end, - Format_Fun = fun(Elem) -> - {xmlelement, atom_to_list( Elem#msg.dirrection) , - [ {"secs" , integer_to_list(Elem#msg.secs) } ] , - [ {xmlelement , "body" , [] , [{xmlcdata, Elem#msg.body }] } ] } - end, - {xmlelement, "store", Args , lists:map(Format_Fun, A#archive_message.message_list ) } - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 5.3 Removing a Collection -%% - - -process_local_iq_remove(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) -> - #jid{luser = LUser, lserver = LServer} = From, - case Type of - get -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - set -> - {xmlelement, _, Attrs, _} = SubEl, - Result = case parse_root_argument(Attrs) of - {error , E } -> IQ#iq{type = error, sub_el = [SubEl, E ] } ; - {interval, Start , Stop } -> process_remove_interval(LUser, LServer , Start, Stop, '_'); - {interval, Start , Stop , Jid } -> process_remove_interval(LUser, LServer , Start, Stop, Jid); - {index , Jid, Start } -> process_remove_index({LUser, LServer , Jid, Start}) - end, - case Result of - { error , Ee } -> IQ#iq{type = error, sub_el = [SubEl , Ee ]} ; - ok -> IQ#iq{type = result , sub_el=[]} - end - end. - -process_remove_index( Index ) -> - case mnesia:transaction(fun() -> mnesia:delete({archive_message, Index}) end) of - {atomic, _} -> - ok; - {aborted, _} -> - {error, ?ERR_ITEM_NOT_FOUND} - end. - -process_remove_interval(LUser, LServer , Start, End, With) -> - Fun = fun() -> - Pat = #archive_message{ usjs= '_' , us = { LUser, LServer } , jid= With , - start='$1' , message_list='_' , subject = '_' }, - Guard = [{'>=', '$1', Start},{'<', '$1', End}], - - lists:foreach(fun(R) -> mnesia:delete_object(R) end, - mnesia:select(archive_message, [{Pat, Guard, ['$_']}]) ) - end, - - case mnesia:transaction( Fun ) of - {atomic, _} -> - ok; - {aborted, _} -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - - - -% return { ok, [ { #archive_message } ] } or { error , xmlelement } -% With is a tuple Jid, or '_' -get_list(LUser, LServer , Start, End, With , MaxItems ) -> - case mnesia:transaction(fun() -> - Pat = #archive_message{ usjs= '_' , us = { LUser, LServer } , jid= With , - start='$1' , message_list='_' , subject = '_' }, - Guard = [{'>=', '$1', Start},{'<', '$1', End}], - case MaxItems of - all -> mnesia:select(archive_message, [{Pat, Guard, ['$_']}]) ; - _ -> mnesia:select(archive_message, [{Pat, Guard, ['$_']}],MaxItems , read) - end - - end) of - {atomic, {Result, _ }} -> - {ok, Result , true}; %FIXME: how to know if it's partial ? - {atomic, Result} -> - {ok, Result , false}; - {aborted, _} -> - {error, ?ERRT_INTERNAL_SERVER_ERROR("", "plop" )} - end. - - -% Index is {LUser, LServer , With , Start} -get_collection( Index ) -> - case catch mnesia:dirty_read(archive_message, Index) of - {'EXIT', _Reason} -> - {error, ?ERR_INTERNAL_SERVER_ERROR}; - [] -> - {error, ?ERR_ITEM_NOT_FOUND}; - [C] -> C - end. - - - - -%%%%%%%%%%%%%%%%%%%%%%%%%% -% Utility - - -% return either {error , Err } or {LUser, LServer , Jid , Start} -index_from_argument(LUser, LServer, Attrs ) -> - case parse_root_argument( Attrs ) of - { error , E } -> { error , E } ; - { index , Jid , Start } -> {LUser, LServer , Jid , Start}; - _ -> { error, ?ERR_BAD_REQUEST } - end. - -%parse commons arguments of root elements -parse_root_argument( Attrs ) -> - case parse_root_argument_aux(Attrs , { undefined , undefined , undefined} ) of - {error , E } -> {error , E}; - {{ok,Jid } , {ok,Start} , undefined } -> { index , Jid , Start }; - {{ok,Jid } , undefined , undefined } -> { interval , 0 , ?INFINITY , Jid }; - {{ok,Jid } , {ok,Start} , {ok,Stop} } -> { interval , Start , Stop , Jid }; - {undefined , {ok,Start} , {ok,Stop} } -> { interval , Start , Stop }; - {undefined , undefined , undefined } -> { interval , 0 , ?INFINITY }; - _ -> {error, ?ERR_BAD_REQUEST} - end. - -parse_root_argument_aux([{"with", JidStr} | Tail ] , { _ , AS , AE} ) -> - case jlib:string_to_jid( JidStr ) of - error -> { error , ?ERR_JID_MALFORMED }; - JidS -> - Jid = jlib:jid_tolower(JidS), - parse_root_argument_aux( Tail , { {ok , Jid} , AS , AE } ) - end; -parse_root_argument_aux([{"start", Str} | Tail ] , { AW , _ , AE} ) -> - case jlib:datetime_string_to_timestamp( Str ) of - undefined -> { error , ?ERR_BAD_REQUEST }; - No -> - Val = calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(No)), - parse_root_argument_aux( Tail , { AW , {ok , Val} , AE } ) - end; -parse_root_argument_aux([{"end", Str} | Tail ] , { AW , AS , _} ) -> - case jlib:datetime_string_to_timestamp( Str ) of - undefined -> { error , ?ERR_BAD_REQUEST }; - No -> - Val = calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(No)), - parse_root_argument_aux( Tail , { AW , AS , {ok , Val} } ) - end; -parse_root_argument_aux([ _ | Tail ] , A ) -> - parse_root_argument_aux( Tail , A ); -parse_root_argument_aux([ ] , A ) -> A. - - - -get_timestamp() -> calendar:datetime_to_gregorian_seconds(calendar:local_time()). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%% -% NOTE ABOUT THE JEP-0136 -% - errors -% * when setting <save/> , how to revert to the default, for a contact or for the default -% * when setting <save/> , is it possible to have several element (<default/> , <item/>) i assume yes -% * It is STRONGLY RECOMMENDED to use a bare jid. but if a full jid is used, does the resource count when doing comparison ? -% * the name attribute of the <form/> element is missing in the shema Example 42 -% * the secs argument is required, and the utc is not there in the sheme Example 25 -% * <store/> may have partial = "true" but how to get the tail ? and how to specify the mex number -% and before example 35 s|<list/>|<store/>| -% * in the schema , the <remove> element has required attribute, => example 40 -% * How many time should an otr session persist ? -% -% - opinions -% * I don't like the format using secs='' and why also support utc ? -% * There is very few things to sort, only the body ? not the message type and all others elements ? -% * There is no syncronisation mechanisme ( a way to know all element modified (added, changed, or removed) after a given date) -% * The client should be able to disable otr support of the server (if he want the message to be logged) -% * -% * comment juste avoir les derniers message du contact - - diff --git a/net-im/ejabberd/files/mod_irc_utf-8.patch b/net-im/ejabberd/files/mod_irc_utf-8.patch deleted file mode 100644 index 1f09aeeb..00000000 --- a/net-im/ejabberd/files/mod_irc_utf-8.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- src/mod_irc/mod_irc.erl.ori 2006-12-21 08:38:09.000000000 +0100 -+++ src/mod_irc/mod_irc.erl 2006-12-21 08:38:29.000000000 +0100 -@@ -27,7 +27,7 @@ - -include("ejabberd.hrl"). - -include("jlib.hrl"). - ---define(DEFAULT_IRC_ENCODING, "koi8-r"). -+-define(DEFAULT_IRC_ENCODING, "utf-8"). - - -record(irc_connection, {jid_server_host, pid}). - -record(irc_custom, {us_host, data}). diff --git a/net-im/ejabberd/files/mod_pep.patch b/net-im/ejabberd/files/mod_pep.patch deleted file mode 100644 index a65a344c..00000000 --- a/net-im/ejabberd/files/mod_pep.patch +++ /dev/null @@ -1,2319 +0,0 @@ ---- orig/src/ejabberd_sm.erl -+++ mod/src/ejabberd_sm.erl -@@ -353,7 +353,9 @@ - true -> - ok - end -- end, PResources); -+ end, PResources), -+ ejabberd_hooks:run(incoming_presence_hook, LServer, -+ [From, To, Packet]); - true -> - ok - end; ---- orig/src/jlib.hrl -+++ mod/src/jlib.hrl -@@ -33,6 +33,7 @@ - -define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event"). - -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_COMMANDS, "http://jabber.org/protocol/commands"). - - -define(NS_EJABBERD_CONFIG, "ejabberd:config"). ---- orig/src/mod_disco.erl -+++ mod/src/mod_disco.erl -@@ -168,11 +168,12 @@ - end - end. - --get_local_identity(_Acc, _From, _To, [], _Lang) -> -- [{xmlelement, "identity", -- [{"category", "server"}, -- {"type", "im"}, -- {"name", "ejabberd"}], []}]; -+get_local_identity(Acc, _From, _To, [], _Lang) -> -+ Acc ++ -+ [{xmlelement, "identity", -+ [{"category", "server"}, -+ {"type", "im"}, -+ {"name", "ejabberd"}], []}]; - - get_local_identity(Acc, _From, _To, _Node, _Lang) -> - Acc. ---- orig/src/mod_pubsub/mod_pubsub.erl -+++ mod/src/mod_pubsub/mod_pubsub.erl -@@ -18,13 +18,12 @@ - start/2, - stop/1]). - ---export([delete_item/3, -- set_entities/4, -- delete_node/2, -- create_new_node/2, -- subscribe_node/3, -- get_node_config/4, -- set_node_config/4]). -+-export([incoming_presence/3]). -+ -+-export([disco_local_identity/5, -+ iq_pep_local/3, -+ iq_pep_sm/3, -+ pep_disco_items/5]). - - %% gen_server callbacks - -export([init/1, handle_call/3, handle_cast/2, handle_info/2, -@@ -36,10 +35,13 @@ - -record(state, {host, server_host, access}). - - -define(DICT, dict). -+%% XXX: this is currently a hard limit. Would be nice to have it -+%% configurable in certain cases. - -define(MAXITEMS, 20). - -define(MAX_PAYLOAD_SIZE, 100000). - - -record(pubsub_node, {host_node, host_parent, info}). -+-record(pep_node, {owner_node, info}). %owner is {luser, lserver, ""} - -record(nodeinfo, {items = [], - options = [], - entities = ?DICT:new() -@@ -48,10 +50,24 @@ - subscription = none}). - -record(item, {id, publisher, payload}). - -+-record(pubsub_presence, {to_from, resource}). -+%% to_from is {ToLUser, ToLServer, FromLUser, FromLServer}. -+ -+get_node_info(#pubsub_node{info = Info}) -> Info; -+get_node_info(#pep_node{info = Info}) -> Info. -+ -+set_node_info(#pubsub_node{} = N, NewInfo) -> N#pubsub_node{info = NewInfo}; -+set_node_info(#pep_node{} = N, NewInfo) -> N#pep_node{info = NewInfo}. -+ -+get_node_name(#pubsub_node{host_node = {_Host, Node}}) -> Node; -+get_node_name(#pep_node{owner_node = {_Owner, Node}}) -> Node. -+ - -define(PROCNAME, ejabberd_mod_pubsub). - -define(MYJID, #jid{user = "", server = Host, resource = "", - luser = "", lserver = Host, lresource = ""}). - -+-define(NS_PUBSUB_SUB_AUTH, "http://jabber.org/protocol/pubsub#subscribe_authorization"). -+ - %%==================================================================== - %% API - %%==================================================================== -@@ -79,29 +95,9 @@ - gen_server:call(Proc, stop), - supervisor:stop_child(ejabberd_sup, Proc). - --delete_item(From, Node, ItemID) -> -- delete_item(get_host(), From, Node, ItemID). -- --delete_node(From, Node) -> -- delete_node(get_host(), From, Node). -- --create_new_node(Node, From) -> -- create_new_node(get_host(), Node, From). -- --subscribe_node(From, JID, Node) -> -- subscribe_node(get_host(), From, JID, Node). -- --set_node_config(From, Node, Els, Lang) -> -- set_node_config(get_host(), From, Node, Els, Lang). -- --get_host() -> -- ejabberd_mod_pubsub ! {get_host, self()}, -- receive -- {pubsub_host, Host} -> -- Host -- after 5000 -> -- timeout -- end. -+incoming_presence(From, #jid{lserver = Host} = To, Packet) -> -+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME), -+ gen_server:cast(Proc, {presence, From, To, Packet}). - - %%==================================================================== - %% gen_server callbacks -@@ -118,12 +114,36 @@ - mnesia:create_table(pubsub_node, - [{disc_only_copies, [node()]}, - {attributes, record_info(fields, pubsub_node)}]), -+ mnesia:create_table(pep_node, -+ [{disc_only_copies, [node()]}, -+ {attributes, record_info(fields, pep_node)}]), -+ mnesia:create_table(pubsub_presence, -+ [{ram_copies, [node()]}, -+ {attributes, record_info(fields, pubsub_presence)}, -+ {type, bag}]), -+ - Host = gen_mod:get_opt(host, Opts, "pubsub." ++ ServerHost), - update_table(Host), - mnesia:add_table_index(pubsub_node, host_parent), - ServedHosts = gen_mod:get_opt(served_hosts, Opts, []), - Access = gen_mod:get_opt(access_createnode, Opts, all), - -+ mod_disco:register_feature(ServerHost, ?NS_PUBSUB), -+ ejabberd_hooks:add(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75), -+ ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, pep_disco_items, 50), -+ -+ IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), -+ gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB, -+ ?MODULE, iq_pep_local, IQDisc), -+ gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER, -+ ?MODULE, iq_pep_local, IQDisc), -+ gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, -+ ?MODULE, iq_pep_sm, IQDisc), -+ gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, -+ ?MODULE, iq_pep_sm, IQDisc), -+ -+ ejabberd_hooks:add(incoming_presence_hook, ServerHost, ?MODULE, incoming_presence, 50), -+ - ejabberd_router:register_route(Host), - create_new_node(Host, ["pubsub"], ?MYJID), - create_new_node(Host, ["pubsub", "nodes"], ?MYJID), -@@ -132,8 +152,6 @@ - lists:foreach(fun(H) -> - create_new_node(Host, ["home", H], ?MYJID) - end, ServedHosts), -- ets:new(gen_mod:get_module_proc(Host, pubsub_presence), -- [set, named_table]), - {ok, #state{host = Host, server_host = ServerHost, access = Access}}. - - %%-------------------------------------------------------------------- -@@ -154,6 +172,86 @@ - %% {stop, Reason, State} - %% Description: Handling cast messages - %%-------------------------------------------------------------------- -+handle_cast({presence, From, To, Packet}, State) -> -+ %% When we get available presence from a subscriber, send the last -+ %% published item. -+ Priority = case xml:get_subtag(Packet, "priority") of -+ false -> -+ 0; -+ SubEl -> -+ case catch list_to_integer(xml:get_tag_cdata(SubEl)) of -+ P when is_integer(P) -> -+ P; -+ _ -> -+ 0 -+ end -+ end, -+ PType = xml:get_tag_attr_s("type", Packet), -+ Type = case PType of -+ "" -> -+ if Priority < 0 -> -+ unavailable; -+ true -> -+ available -+ end; -+ "unavailable" -> unavailable; -+ "error" -> unavailable; -+ _ -> none -+ end, -+ LJID = jlib:jid_tolower(jlib:jid_remove_resource(To)), -+ PreviouslyAvailable = -+ lists:member(From#jid.lresource, -+ get_present_resources(element(1, LJID), element(2, LJID), -+ From#jid.luser, From#jid.lserver)), -+ Key = {element(1, LJID), element(2, LJID), From#jid.luser, From#jid.lserver}, -+ Record = #pubsub_presence{to_from = Key, resource = From#jid.lresource}, -+ Host = case LJID of -+ {"", LServer, ""} -> -+ LServer; -+ _ -> -+ LJID -+ end, -+ case Type of -+ available -> -+ mnesia:dirty_write(Record); -+ unavailable -> -+ mnesia:dirty_delete_object(Record); -+ _ -> -+ ok -+ end, -+ if PreviouslyAvailable == false, Type == available -> -+ %% A new resource is available. Loop through all nodes -+ %% and see if the contact is subscribed, and if so, and if -+ %% the node is so configured, send the last item. -+ Match = case Host of -+ {_, _, _} -> -+ #pep_node{owner_node = {LJID, '_'}, _ = '_'}; -+ _ -> -+ #pubsub_node{host_node = {LJID, '_'}, _ = '_'} -+ end, -+ case catch mnesia:dirty_match_object(Match) of -+ {'EXIT', Reason} -> -+ ?ERROR_MSG("~p", [Reason]); -+ Nodes -> -+ lists:foreach( -+ fun(N) -> -+ Node = get_node_name(N), -+ Info = get_node_info(N), -+ Subscription = get_subscription(Info, From), -+ SendWhen = get_node_option(Info, send_last_published_item), -+ if Subscription /= none, Subscription /= pending, -+ SendWhen == on_sub_and_presence -> -+ send_last_published_item(jlib:jid_tolower(From), Host, Node, Info); -+ true -> -+ ok -+ end -+ end, Nodes) -+ end, -+ {noreply, State}; -+ true -> -+ {noreply, State} -+ end; -+ - handle_cast(_Msg, State) -> - {noreply, State}. - -@@ -183,7 +281,16 @@ - %% The return value is ignored. - %%-------------------------------------------------------------------- - terminate(_Reason, State) -> -- ejabberd_router:unregister_route(State#state.host), -+ #state{host = Host, server_host = ServerHost} = State, -+ mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB), -+ ejabberd_hooks:delete(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75), -+ ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, pep_disco_items, 50), -+ gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB), -+ gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER), -+ gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB), -+ gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER), -+ ejabberd_hooks:delete(incoming_presence_hook, ServerHost, ?MODULE, incoming_presence, 50), -+ ejabberd_router:unregister_route(Host), - ok. - - %%-------------------------------------------------------------------- -@@ -197,13 +304,13 @@ - %%% Internal functions - %%-------------------------------------------------------------------- - do_route(Host, ServerHost, Access, From, To, Packet) -> -- {xmlelement, Name, Attrs, Els} = Packet, -+ {xmlelement, Name, Attrs, _Els} = Packet, - case To of - #jid{luser = "", lresource = ""} -> - case Name of - "iq" -> - case jlib:iq_query_info(Packet) of -- #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, -+ #iq{type = get, xmlns = ?NS_DISCO_INFO, - sub_el = SubEl} = IQ -> - {xmlelement, _, QAttrs, _} = SubEl, - Node = xml:get_attr_s("node", QAttrs), -@@ -214,7 +321,7 @@ - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); -- #iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS, -+ #iq{type = get, xmlns = ?NS_DISCO_ITEMS, - sub_el = SubEl} = IQ -> - {xmlelement, _, QAttrs, _} = SubEl, - Node = xml:get_attr_s("node", QAttrs), -@@ -231,7 +338,7 @@ - Packet, Error) - end, - ejabberd_router:route(To, From, Res); -- #iq{type = Type, xmlns = ?NS_PUBSUB = XMLNS, -+ #iq{type = Type, xmlns = ?NS_PUBSUB, - sub_el = SubEl} = IQ -> - Res = - case iq_pubsub(Host, ServerHost, From, Type, SubEl, Access) of -@@ -244,7 +351,7 @@ - Packet, Error) - end, - ejabberd_router:route(To, From, Res); -- #iq{type = Type, xmlns = ?NS_PUBSUB_OWNER = XMLNS, -+ #iq{type = Type, xmlns = ?NS_PUBSUB_OWNER, - lang = Lang, sub_el = SubEl} = IQ -> - Res = - case iq_pubsub_owner( -@@ -259,7 +366,7 @@ - end, - ejabberd_router:route(To, From, Res); - #iq{type = get, xmlns = ?NS_VCARD = XMLNS, -- lang = Lang, sub_el = SubEl} = IQ -> -+ lang = Lang} = IQ -> - Res = IQ#iq{type = result, - sub_el = [{xmlelement, "vCard", - [{"xmlns", XMLNS}], -@@ -276,20 +383,27 @@ - ok - end; - "presence" -> -- Type = xml:get_attr_s("type", Attrs), -- if -- (Type == "unavailable") or (Type == "error") -> -- ets:delete( -- gen_mod:get_module_proc(Host, pubsub_presence), -- {From#jid.luser, From#jid.lserver}); -- true -> -- ets:insert( -- gen_mod:get_module_proc(Host, pubsub_presence), -- {{From#jid.luser, From#jid.lserver}, []}) -- end, -+ %% XXX: subscriptions? -+ incoming_presence(From, To, Packet), - ok; -- _ -> -- ok -+ "message" -> -+ %% So why would anyone want to send messages to a -+ %% pubsub service? Subscription authorization -+ %% (section 8.6). -+ case xml:get_attr_s("type", Attrs) of -+ "error" -> -+ ok; -+ _ -> -+ case find_authorization_response(Packet) of -+ none -> -+ ok; -+ invalid -> -+ ejabberd_router:route(To, From, -+ jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST)); -+ XFields -> -+ handle_authorization_response(From, To, Host, Packet, XFields) -+ end -+ end - end; - _ -> - case xml:get_attr_s("type", Attrs) of -@@ -307,28 +421,121 @@ - - - node_to_string(Node) -> -- string:strip(lists:flatten(lists:map(fun(S) -> [S, "/"] end, Node)), -- right, $/). -+ %% Flat (PEP) or normal node? -+ case Node of -+ [[_ | _] | _] -> -+ string:strip(lists:flatten(lists:map(fun(S) -> [S, "/"] end, Node)), -+ right, $/); -+ [Head | _] when is_integer(Head) -> -+ Node -+ end. -+ -+disco_local_identity(Acc, _From, _To, [], _Lang) -> -+ Acc ++ -+ [{xmlelement, "identity", -+ [{"category", "pubsub"}, -+ {"type", "pep"}], []}]; -+disco_local_identity(Acc, _From, _To, _Node, _Lang) -> -+ Acc. -+ -+get_table(Host) -> -+ case Host of -+ {_, _, _} -> pep_node; -+ _ -> pubsub_node -+ end. -+ -+get_sender(Host) -> -+ case Host of -+ {_, _, _} -> -+ jlib:make_jid(Host); -+ _ -> -+ jlib:make_jid("", Host, "") -+ end. -+ -+iq_pep_local(From, To, -+ #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) -> -+ ServerHost = To#jid.lserver, -+ %% Accept IQs to server only from our own users. -+ if From#jid.lserver /= ServerHost -> -+ IQ#iq{type = error, sub_el = [?ERR_FORBIDDEN, SubEl]}; -+ true -> -+ LOwner = jlib:jid_tolower(jlib:jid_remove_resource(From)), -+ Res = case XMLNS of -+ ?NS_PUBSUB -> -+ %% XXX: "access all" correct? it corresponds to access_createnode. -+ iq_pubsub(LOwner, ServerHost, From, Type, SubEl, all); -+ ?NS_PUBSUB_OWNER -> -+ iq_pubsub_owner(LOwner, From, Type, Lang, SubEl) -+ end, -+ case Res of -+ {result, IQRes} -> -+ IQ#iq{type = result, sub_el = IQRes}; -+ {error, Error} -> -+ IQ#iq{type = error, sub_el = [Error, SubEl]} -+ end -+ end. - -+iq_pep_sm(From, To, -+ #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) -> -+ ServerHost = To#jid.lserver, -+ LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), -+ Res = case XMLNS of -+ ?NS_PUBSUB -> -+ iq_pubsub(LOwner, ServerHost, From, Type, SubEl, all); -+ ?NS_PUBSUB_OWNER -> -+ iq_pubsub_owner(LOwner, From, Type, Lang, SubEl) -+ end, -+ case Res of -+ {result, IQRes} -> -+ IQ#iq{type = result, sub_el = IQRes}; -+ {error, Error} -> -+ IQ#iq{type = error, sub_el = [Error, SubEl]} -+ end. - - iq_disco_info(SNode) -> - Node = string:tokens(SNode, "/"), - case Node of - [] -> -+ PubsubFeatures = -+ ["config-node", -+ "create-and-configure", -+ "create-nodes", -+ "delete-nodes", -+ %% "get-pending", -+ "instant-nodes", -+ "item-ids", -+ %% "manage-subscriptions", -+ %% "modify-affiliations", -+ "outcast-affiliation", -+ "persistent-items", -+ "presence-notifications", -+ "publish", -+ "publisher-affiliation", -+ "purge-nodes", -+ "retract-items", -+ "retrieve-affiliations", -+ %% "retrieve-default", -+ "retrieve-items", -+ "retrieve-subscriptions", -+ "subscribe" -+ %% , "subscription-notifications" -+ ], - [{xmlelement, "identity", - [{"category", "pubsub"}, -- {"type", "generic"}, -+ {"type", "service"}, - {"name", "ejabberd/mod_pubsub"}], []}, - {xmlelement, "feature", [{"var", ?NS_PUBSUB}], []}, -- {xmlelement, "feature", [{"var", ?NS_PUBSUB_EVENT}], []}, -- {xmlelement, "feature", [{"var", ?NS_PUBSUB_OWNER}], []}, -- {xmlelement, "feature", [{"var", ?NS_VCARD}], []}]; -+ {xmlelement, "feature", [{"var", ?NS_VCARD}], []}] ++ -+ lists:map(fun(Feature) -> -+ {xmlelement, "feature", -+ [{"var", ?NS_PUBSUB++"#"++Feature}], []} -+ end, PubsubFeatures); - _ -> - % TODO - [] - end. - --iq_disco_items(Host, From, SNode) -> -+iq_disco_items(Host, _From, SNode) -> - {Node,ItemID} = case SNode of - [] -> - {[],none}; -@@ -341,7 +548,7 @@ - end, - {NodeList, ItemName} - end, -- NodeFull = string:tokens(SNode,"/"), -+ %%NodeFull = string:tokens(SNode,"/"), - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info}] -> -@@ -402,6 +609,32 @@ - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - -+pep_disco_items(Acc, _From, To, "", _Lang) -> -+ LJID = jlib:jid_tolower(jlib:jid_remove_resource(To)), -+ Match = #pep_node{owner_node = {LJID, '_'}, _ = '_'}, -+ case catch mnesia:dirty_match_object(Match) of -+ {'EXIT', Reason} -> -+ ?ERROR_MSG("~p", [Reason]), -+ Acc; -+ [] -> -+ Acc; -+ Nodes -> -+ Items = case Acc of -+ {result, I} -> I; -+ _ -> [] -+ end, -+ NodeItems = lists:map( -+ fun(#pep_node{owner_node = {_, Node}}) -> -+ {xmlelement, "item", -+ [{"jid", jlib:jid_to_string(LJID)}, -+ {"node", node_to_string(Node)}], -+ []} -+ end, Nodes), -+ {result, NodeItems ++ Items} -+ end; -+pep_disco_items(Acc, _From, _To, _Node, _Lang) -> -+ Acc. -+ - iq_get_vcard(Lang) -> - [{xmlelement, "FN", [], - [{xmlcdata, "ejabberd/mod_pubsub"}]}, -@@ -416,14 +649,32 @@ - - - iq_pubsub(Host, ServerHost, From, Type, SubEl, Access) -> -+ %% Host may be a jid tuple, in which case we use PEP. - {xmlelement, _, _, SubEls} = SubEl, -- case xml:remove_cdata(SubEls) of -+ WithoutCdata = xml:remove_cdata(SubEls), -+ Configuration = lists:filter(fun({xmlelement, Name, _, _}) -> -+ Name == "configure" -+ end, WithoutCdata), -+ Action = WithoutCdata -- Configuration, -+ case Action of - [{xmlelement, Name, Attrs, Els}] -> -- SNode = xml:get_attr_s("node", Attrs), -- Node = string:tokens(SNode, "/"), -+ %% For PEP, there is no node hierarchy. -+ Node = case Host of -+ {_, _, _} -> -+ xml:get_attr_s("node", Attrs); -+ _ -> -+ SNode = xml:get_attr_s("node", Attrs), -+ string:tokens(SNode, "/") -+ end, - case {Type, Name} of - {set, "create"} -> -- create_new_node(Host, Node, From, ServerHost, Access); -+ case Configuration of -+ [{xmlelement, "configure", _, Config}] -> -+ create_new_node(Host, Node, From, ServerHost, Access, Config); -+ _ -> -+ ?INFO_MSG("Invalid configuration: ~p", [Configuration]), -+ {error, ?ERR_BAD_REQUEST} -+ end; - {set, "publish"} -> - case xml:remove_cdata(Els) of - [{xmlelement, "item", ItemAttrs, Payload}] -> -@@ -438,7 +689,7 @@ - ItemID = xml:get_attr_s("id", ItemAttrs), - delete_item(Host, From, Node, ItemID); - _ -> -- {error, ?ERR_BAD_REQUEST} -+ {error, extend_error(?ERR_BAD_REQUEST, "item-required")} - end; - {set, "subscribe"} -> - JID = xml:get_attr_s("jid", Attrs), -@@ -449,20 +700,15 @@ - {get, "items"} -> - MaxItems = xml:get_attr_s("max_items", Attrs), - get_items(Host, From, Node, MaxItems); -- {set, "delete"} -> -- delete_node(Host, From, Node); -- {set, "purge"} -> -- purge_node(Host, From, Node); -- {get, "entities"} -> -- get_entities(Host, From, Node); -- {set, "entities"} -> -- set_entities(Host, From, Node, xml:remove_cdata(Els)); - {get, "affiliations"} -> - get_affiliations(Host, From); -+ {get, "subscriptions"} -> -+ get_subscriptions(Host, From); - _ -> - {error, ?ERR_FEATURE_NOT_IMPLEMENTED} - end; - _ -> -+ ?INFO_MSG("Too many actions: ~p", [Action]), - {error, ?ERR_BAD_REQUEST} - end. - -@@ -504,16 +750,81 @@ - create_new_node(Host, Node, Owner) -> - %% This is the case use during "bootstrapping to create the initial - %% hierarchy. Should always be ... undefined,all -- create_new_node(Host, Node, Owner, undefined, all). --create_new_node(Host, Node, Owner, ServerHost, Access) -> -- case Node of -- [] -> -+ create_new_node(Host, Node, Owner, undefined, all, []). -+create_new_node(Host, Node, Owner, ServerHost, Access, Configuration) -> -+ DefaultSet = get_table(Host), % get_table happens to DTRT here -+ ConfigOptions = case xml:remove_cdata(Configuration) of -+ [] -> -+ []; -+ [{xmlelement, "x", _Attrs, _SubEls} = XEl] -> -+ case jlib:parse_xdata_submit(XEl) of -+ invalid -> -+ {error, ?ERR_BAD_REQUEST}; -+ XData -> -+ case set_xoption(XData, [{defaults, DefaultSet}]) of -+ NewOpts when is_list(NewOpts) -> -+ NewOpts; -+ Err -> -+ Err -+ end -+ end; -+ _ -> -+ ?INFO_MSG("Configuration not understood: ~p", [Configuration]), -+ {error, ?ERR_BAD_REQUEST} -+ end, -+ case {Host, Node, ConfigOptions} of -+ {_, _, {error, _} = Error} -> -+ Error; -+ %% If Host is a jid tuple, we are in PEP. -+ {{_, _, _}, [], _} -> -+ %% And in PEP, instant nodes are not supported. -+ {error, extend_error(?ERR_NOT_ACCEPTABLE, "nodeid-required")}; -+ {{_, _, _}, _, _} -> -+ LOwner = Host, -+ F = fun() -> -+ case mnesia:read({pep_node, {LOwner, Node}}) of -+ [_] -> -+ {error, ?ERR_CONFLICT}; -+ [] -> -+ Entities = -+ ?DICT:store( -+ LOwner, -+ #entity{affiliation = owner, -+ subscription = none}, -+ ?DICT:new()), -+ mnesia:write( -+ #pep_node{owner_node = {LOwner, Node}, -+ info = #nodeinfo{entities = Entities, -+ options = ConfigOptions}}), -+ ok -+ end -+ end, -+ case mnesia:transaction(F) of -+ {atomic, ok} -> -+ {result, []}; -+ {atomic, {error, _} = Error} -> -+ Error; -+ _ -> -+ {error, ?ERR_INTERNAL_SERVER_ERROR} -+ end; -+ {_, [], _} -> - {LOU, LOS, _} = jlib:jid_tolower(Owner), - HomeNode = ["home", LOS, LOU], -- create_new_node(Host, HomeNode, Owner, ServerHost, Access), -+ create_new_node(Host, HomeNode, Owner, ServerHost, Access, []), - NewNode = ["home", LOS, LOU, randoms:get_string()], -- create_new_node(Host, NewNode, Owner, ServerHost, Access); -- _ -> -+ %% When creating an instant node, we need to include the -+ %% node name in the result. -+ case create_new_node(Host, NewNode, Owner, ServerHost, Access, []) of -+ {result, []} -> -+ {result, -+ [{xmlelement, "pubsub", -+ [{"xmlns", ?NS_PUBSUB}], -+ [{xmlelement, "create", -+ [{"node", node_to_string(Node)}], []}]}]}; -+ {error, _} = Error -> -+ Error -+ end; -+ {_, _, _} -> - LOwner = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), - Parent = lists:sublist(Node, length(Node) - 1), - F = fun() -> -@@ -542,7 +853,8 @@ - #pubsub_node{host_node = {Host, Node}, - host_parent = {Host, Parent}, - info = #nodeinfo{ -- entities = Entities}}), -+ entities = Entities, -+ options = ConfigOptions}}), - ok - end - end -@@ -562,11 +874,7 @@ - ?XFIELD("jid-single", "Node Creator", - "creator", - jlib:jid_to_string(LOwner))]}]), -- {result, -- [{xmlelement, "pubsub", -- [{"xmlns", ?NS_PUBSUB}], -- [{xmlelement, "create", -- [{"node", node_to_string(Node)}], []}]}]}; -+ {result, []}; - {atomic, {error, _} = Error} -> - Error; - _ -> -@@ -579,33 +887,38 @@ - - - publish_item(Host, JID, Node, ItemID, Payload) -> -+ Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)), -+ Table = get_table(Host), -+ %% XXX: Host is not a host if this is PEP. Good thing that this -+ %% hook isn't added to anywhere yet. - ejabberd_hooks:run(pubsub_publish_item, Host, - [JID, ?MYJID, Node, ItemID, Payload]), -- Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - F = fun() -> -- case mnesia:read({pubsub_node, {Host, Node}}) of -- [#pubsub_node{info = Info} = N] -> -+ case mnesia:read({Table, {Host, Node}}) of -+ [N] -> -+ Info = get_node_info(N), - Affiliation = get_affiliation(Info, Publisher), - Subscription = get_subscription(Info, Publisher), - MaxSize = get_node_option(Info, max_payload_size), - Model = get_node_option(Info, publish_model), - Size = size(term_to_binary(Payload)), - if -- ((Model == open) or -- ((Model == publishers) and -- ((Affiliation == owner) or -- (Affiliation == publisher))) or -- ((Model == subscribers) and -- (Subscription == subscribed))) and -- (Size =< MaxSize) -> -+ not ((Model == open) or -+ ((Model == publishers) and -+ ((Affiliation == owner) or -+ (Affiliation == publisher))) or -+ ((Model == subscribers) and -+ (Subscription == subscribed))) -> -+ {error, ?ERR_FORBIDDEN}; -+ (Size > MaxSize) -> -+ {error, extend_error(?ERR_NOT_ACCEPTABLE, "payload-too-big")}; -+ true -> - NewInfo = - insert_item(Info, ItemID, - Publisher, Payload), -- mnesia:write( -- N#pubsub_node{info = NewInfo}), -- {result, []}; -- true -> -- {error, ?ERR_NOT_ALLOWED} -+ NewNode = set_node_info(N, NewInfo), -+ mnesia:write(NewNode), -+ {result, []} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} -@@ -624,20 +937,27 @@ - - delete_item(Host, JID, Node, ItemID) -> - Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)), -+ Table = get_table(Host), - F = fun() -> -- case mnesia:read({pubsub_node, {Host, Node}}) of -- [#pubsub_node{info = Info} = N] -> -- case check_item_publisher(Info, ItemID, Publisher) -+ case mnesia:read({Table, {Host, Node}}) of -+ [N] -> -+ Info = get_node_info(N), -+ ItemExists = lists:any(fun(I) -> -+ I#item.id == ItemID -+ end, Info#nodeinfo.items), -+ Allowed = check_item_publisher(Info, ItemID, Publisher) - orelse -- (get_affiliation(Info, Publisher) == owner) of -- true -> -+ (get_affiliation(Info, Publisher) == owner), -+ if not Allowed -> -+ {error, ?ERR_FORBIDDEN}; -+ not ItemExists -> -+ {error, ?ERR_ITEM_NOT_FOUND}; -+ true -> - NewInfo = - remove_item(Info, ItemID), - mnesia:write( -- N#pubsub_node{info = NewInfo}), -- {result, []}; -- _ -> -- {error, ?ERR_NOT_ALLOWED} -+ set_node_info(N, NewInfo)), -+ {result, []} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} -@@ -653,6 +973,17 @@ - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - -+%% Add pubsub-specific error element -+extend_error({xmlelement, "error", Attrs, SubEls}, Error) -> -+ {xmlelement, "error", Attrs, -+ [{xmlelement, Error, [{"xmlns", ?NS_PUBSUB_ERRORS}], []} -+ | SubEls]}. -+ -+extend_error({xmlelement, "error", Attrs, SubEls}, unsupported, Feature) -> -+ {xmlelement, "error", Attrs, -+ [{xmlelement, "unsupported", [{"xmlns", ?NS_PUBSUB_ERRORS}, -+ {"feature", Feature}], []} -+ | SubEls]}. - - subscribe_node(Host, From, JID, Node) -> - Sender = jlib:jid_tolower(jlib:jid_remove_resource(From)), -@@ -665,64 +996,234 @@ - end, - Subscriber = jlib:jid_tolower(SubscriberJID), - SubscriberWithoutResource = jlib:jid_remove_resource(Subscriber), -+ AuthorizedToSubscribe = Sender == SubscriberWithoutResource, -+ Table = get_table(Host), -+ case catch mnesia:dirty_read({Table, {Host, Node}}) of -+ [NodeData] -> -+ NodeInfo = get_node_info(NodeData), -+ AllowSubscriptions = get_node_option(NodeInfo, subscribe), -+ AccessModel = get_node_option(NodeInfo, access_model), -+ AllowedGroups = get_node_option(NodeInfo, access_roster_groups), -+ Affiliation = get_affiliation(NodeInfo, Subscriber), -+ OldSubscription = get_subscription(NodeInfo, Subscriber), -+ CurrentApprover = get_node_option(NodeInfo, current_approver); -+ [] -> -+ {AllowSubscriptions, -+ AccessModel, -+ AllowedGroups, -+ Affiliation, -+ OldSubscription, -+ CurrentApprover} = {notfound, notfound, notfound, notfound, notfound, notfound}; -+ _ -> -+ {AllowSubscriptions, -+ AccessModel, -+ AllowedGroups, -+ Affiliation, -+ OldSubscription, -+ CurrentApprover} = {error, error, error, error, error, error} -+ end, -+ Subscription = if AllowSubscriptions == notfound -> -+ {error, ?ERR_ITEM_NOT_FOUND}; -+ AllowSubscriptions == error -> -+ {error, ?ERR_INTERNAL_SERVER_ERROR}; -+ not AuthorizedToSubscribe -> -+ {error, extend_error(?ERR_BAD_REQUEST, "invalid-jid")}; -+ not AllowSubscriptions -> -+ {error, extend_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")}; -+ OldSubscription == pending -> -+ {error, extend_error(?ERR_NOT_AUTHORIZED, "pending-subscription")}; -+ Affiliation == outcast -> -+ {error, ?ERR_FORBIDDEN}; -+ AccessModel == open; Affiliation == owner; Affiliation == publisher -> -+ subscribed; -+ AccessModel == authorize -> -+ pending; -+ AccessModel == presence -> -+ %% XXX: this applies only to PEP -+ {OUser, OServer, _} = Host, -+ {Subscription1, _Groups} = -+ ejabberd_hooks:run_fold( -+ roster_get_jid_info, OServer, -+ {none, []}, [OUser, OServer, SubscriberWithoutResource]), -+ if (Subscription1 == both) or -+ (Subscription1 == from) -> -+ subscribed; -+ true -> -+ {error, extend_error(?ERR_NOT_AUTHORIZED, "presence-subscription-required")} -+ end; -+ AccessModel == roster -> -+ %% XXX: this applies only to PEP -+ {OUser, OServer, _} = Host, -+ {_Subscription, Groups} = -+ ejabberd_hooks:run_fold( -+ roster_get_jid_info, OServer, -+ {none, []}, [OUser, OServer, SubscriberWithoutResource]), -+ case lists:any([lists:member(Group, AllowedGroups) || Group <- Groups]) of -+ true -> -+ subscribed; -+ false -> -+ {error, extend_error(?ERR_NOT_AUTHORIZED, "not-in-roster-group")} -+ end; -+ AccessModel == whitelist -> -+ %% Subscribers are added by owner (see set_entities) -+ {error, extend_error(?ERR_NOT_ALLOWED, "closed-node")} -+ end, - F = fun() -> -- case mnesia:read({pubsub_node, {Host, Node}}) of -- [#pubsub_node{info = Info} = N] -> -- Affiliation = get_affiliation(Info, Subscriber), -- AllowSubscriptions = get_node_option(Info, subscribe), -- if -- AllowSubscriptions and -- (Affiliation /= outcast) -> -- NewInfo = add_subscriber(Info, Subscriber), -- mnesia:write(N#pubsub_node{info = NewInfo}), -- {result, [], Info}; -- true -> -- {error, ?ERR_NOT_ALLOWED} -- end; -- [] -> -- {error, ?ERR_ITEM_NOT_FOUND} -+ case mnesia:read({Table, {Host, Node}}) of -+ [N] -> -+ Info = get_node_info(N), -+ NewInfo = add_subscriber(Info, Subscriber, Subscription), -+ mnesia:write(set_node_info(N, NewInfo)), -+ {result, [{xmlelement, "subscription", -+ [{"node", Node}, -+ {"jid", jlib:jid_to_string(Subscriber)}, -+ {"subscription", -+ subscription_to_string(Subscription)}], -+ []}], -+ Info} - end - end, -- if -- Sender == SubscriberWithoutResource -> -+ case Subscription of -+ {error, _} = Error -> -+ Error; -+ _ -> - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, Res, Info}} -> -- case get_node_option(Info, send_item_subscribe) of -- true -> -- ItemsEls = -- lists:map( -- fun(#item{id = ItemID, -- payload = Payload}) -> -- ItemAttrs = case ItemID of -- "" -> []; -- _ -> [{"id", ItemID}] -- end, -- {xmlelement, "item", -- ItemAttrs, Payload} -- end, Info#nodeinfo.items), -- Stanza = -- {xmlelement, "message", -- [], -- [{xmlelement, "x", -- [{"xmlns", ?NS_PUBSUB_EVENT}], -- [{xmlelement, "items", -- [{"node", node_to_string(Node)}], -- ItemsEls}]}]}, -- ejabberd_router:route( -- ?MYJID, jlib:make_jid(Subscriber), Stanza); -- false -> -- ok -- end, -+ if Subscription == subscribed -> -+ SendLastPublishedItem = get_node_option(Info, send_last_published_item), -+ if SendLastPublishedItem /= never -> -+ send_last_published_item(Subscriber, Host, Node, Info); -+ true -> -+ ok -+ end; -+ Subscription == pending -> -+ %% send authorization request to node owner (section 8.6) -+ -+ %% XXX: fix translation -+ Lang = "en", -+ send_authorization_request(Lang, CurrentApprover, Subscriber, Node, Host) -+ end, - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} -- end; -- true -> -- {error, ?ERR_NOT_ALLOWED} -+ end -+ end. -+ -+send_authorization_request(Lang, Approver, Subscriber, Node, Host) -> -+ Stanza = -+ {xmlelement, "message", -+ [], -+ [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, -+ {"type", "form"}], -+ [{xmlelement, "title", [], -+ [{xmlcdata, translate:translate(Lang, "PubSub subscriber request")}]}, -+ {xmlelement, "instructions", [], -+ [{xmlcdata, translate:translate(Lang, "Choose whether to approve this entity's subscription.")}]}, -+ {xmlelement, "field", [{"var", "FORM_TYPE"}, {"type", "hidden"}], -+ [{xmlelement, "value", [], [{xmlcdata, ?NS_PUBSUB_SUB_AUTH}]}]}, -+ {xmlelement, "field", [{"var", "pubsub#node"}, {"type", "text-single"}, -+ {"label", translate:translate(Lang, "Node ID")}], -+ [{xmlelement, "value", [], [{xmlcdata, node_to_string(Node)}]}]}, -+ {xmlelement, "field", [{"var", "pubsub#subscriber_jid"}, -+ {"type", "jid-single"}, -+ {"label", translate:translate(Lang, "Subscriber Address")}], -+ [{xmlelement, "value", [], [{xmlcdata, jlib:jid_to_string(Subscriber)}]}]}, -+ {xmlelement, "field", [{"var", "pubsub#allow"}, {"type", "boolean"}, -+ {"label", translate:translate(Lang, "Allow this JID to subscribe to this pubsub node?")}], -+ [{xmlelement, "value", [], [{xmlcdata, "false"}]}]}]}]}, -+ ejabberd_router:route(get_sender(Host), Approver, Stanza). -+ -+find_authorization_response(Packet) -> -+ {xmlelement, _Name, _Attrs, Els} = Packet, -+ XData1 = lists:map(fun({xmlelement, "x", XAttrs, _} = XEl) -> -+ case xml:get_attr_s("xmlns", XAttrs) of -+ ?NS_XDATA -> -+ case xml:get_attr_s("type", XAttrs) of -+ "cancel" -> -+ none; -+ _ -> -+ jlib:parse_xdata_submit(XEl) -+ end; -+ _ -> -+ none -+ end; -+ (_) -> -+ none -+ end, xml:remove_cdata(Els)), -+ XData = lists:filter(fun(E) -> E /= none end, XData1), -+ case XData of -+ [invalid] -> invalid; -+ [] -> none; -+ [XFields] when is_list(XFields) -> -+ case lists:keysearch("FORM_TYPE", 1, XFields) of -+ {value, {_, ?NS_PUBSUB_SUB_AUTH}} -> -+ XFields; -+ _ -> -+ invalid -+ end - end. - -+handle_authorization_response(From, To, Host, Packet, XFields) -> -+ case {lists:keysearch("pubsub#node", 1, XFields), -+ lists:keysearch("pubsub#subscriber_jid", 1, XFields), -+ lists:keysearch("pubsub#allow", 1, XFields)} of -+ {{value, {_, SNode}}, {value, {_, SSubscriber}}, -+ {value, {_, SAllow}}} -> -+ Node = case Host of -+ {_, _, _} -> -+ SNode; -+ _ -> -+ string:tokens(SNode, "/") -+ end, -+ Subscriber = jlib:string_to_jid(SSubscriber), -+ Allow = case SAllow of -+ "1" -> true; -+ "true" -> true; -+ _ -> false -+ end, -+ Table = get_table(Host), -+ F = fun() -> -+ case mnesia:read({Table, {Host, Node}}) of -+ [N] -> -+ Info = get_node_info(N), -+ Subscription = get_subscription(Info, Subscriber), -+ Approver = get_node_option(N, current_approver), -+ IsApprover = jlib:jid_tolower(jlib:jid_remove_resource(From)) == -+ jlib:jid_tolower(jlib:jid_remove_resource(Approver)), -+ if not IsApprover -> -+ {error, ?ERR_FORBIDDEN}; -+ Subscription /= pending -> -+ {error, ?ERR_UNEXPECTED_REQUEST}; -+ true -> -+ NewSubscription = case Allow of -+ true -> subscribed; -+ false -> none -+ end, -+ NewInfo = add_subscriber(Info, Subscriber, NewSubscription), -+ mnesia:write(set_node_info(N, NewInfo)), -+ NewSubscription -+ end; -+ [] -> -+ {error, ?ERR_ITEM_NOT_FOUND} -+ end -+ end, -+ case mnesia:transaction(F) of -+ {atomic, {error, Error}} -> -+ ejabberd_router:route(To, From, -+ jlib:make_error_reply(Packet, Error)); -+ {atomic, NewSubscription} -> -+ %% XXX: notify about subscription state change, section 12.11 -+ ok; -+ _ -> -+ ejabberd_router:route(To, From, -+ jlib:make_error_reply(Packet, ?ERR_INTERNAL_SERVER_ERROR)) -+ end; -+ _ -> -+ ejabberd_router:route(To, From, -+ jlib:make_error_reply(Packet, ?ERR_NOT_ACCEPTABLE)) -+ end. - - unsubscribe_node(Host, From, JID, Node) -> - Sender = jlib:jid_tolower(jlib:jid_remove_resource(From)), -@@ -734,19 +1235,21 @@ - J - end, - Subscriber = jlib:jid_tolower(SubscriberJID), -+ Table = get_table(Host), - F = fun() -> -- case mnesia:read({pubsub_node, {Host, Node}}) of -- [#pubsub_node{info = Info} = N] -> -+ case mnesia:read({Table, {Host, Node}}) of -+ [N] -> -+ Info = get_node_info(N), - Subscription = get_subscription(Info, Subscriber), - if - Subscription /= none -> - NewInfo = - remove_subscriber(Info, Subscriber), - mnesia:write( -- N#pubsub_node{info = NewInfo}), -+ set_node_info(N, NewInfo)), - {result, []}; - true -> -- {error, ?ERR_NOT_ALLOWED} -+ {error, extend_error(?ERR_UNEXPECTED_REQUEST, "not-subscribed")} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} -@@ -763,7 +1266,7 @@ - {error, ?ERR_INTERNAL_SERVER_ERROR} - end; - true -> -- {error, ?ERR_NOT_ALLOWED} -+ {error, ?ERR_FORBIDDEN} - end. - - -@@ -780,12 +1283,14 @@ - Val - end - end, -+ Table = get_table(Host), - case MaxItems of - {error, _} = Error -> - Error; - _ -> -- case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of -- [#pubsub_node{info = Info}] -> -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), - Items = lists:sublist(Info#nodeinfo.items, MaxItems), - ItemsEls = - lists:map( -@@ -810,30 +1315,39 @@ - - delete_node(Host, JID, Node) -> - Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)), -+ Table = get_table(Host), - F = fun() -> -- case mnesia:read({pubsub_node, {Host, Node}}) of -- [#pubsub_node{info = Info}] -> -+ case mnesia:read({Table, {Host, Node}}) of -+ [N1] -> -+ Info = get_node_info(N1), - case get_affiliation(Info, Owner) of - owner -> -- % TODO: don't iterate over entire table -- Removed = -- mnesia:foldl( -- fun(#pubsub_node{host_node = {_, N}, -- info = NInfo}, Acc) -> -- case lists:prefix(Node, N) of -- true -> -- [{N, NInfo} | Acc]; -- _ -> -- Acc -- end -- end, [], pubsub_node), -- lists:foreach( -- fun({N, _}) -> -- mnesia:delete({pubsub_node, {Host, N}}) -- end, Removed), -- {removed, Removed}; -+ %% PEP nodes are not hierarchical, so removal is easier. -+ case Table of -+ pep_node -> -+ mnesia:delete({Table, {Host, Node}}), -+ {removed, [{N1, Info}]}; -+ pubsub_node -> -+ %% TODO: don't iterate over entire table -+ Removed = -+ mnesia:foldl( -+ fun(#pubsub_node{host_node = {_, N}, -+ info = NInfo}, Acc) -> -+ case lists:prefix(Node, N) of -+ true -> -+ [{N, NInfo} | Acc]; -+ _ -> -+ Acc -+ end -+ end, [], pubsub_node), -+ lists:foreach( -+ fun({N, _}) -> -+ mnesia:delete({pubsub_node, {Host, N}}) -+ end, Removed), -+ {removed, Removed} -+ end; - _ -> -- {error, ?ERR_NOT_ALLOWED} -+ {error, ?ERR_FORBIDDEN} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} -@@ -844,7 +1358,6 @@ - Error; - {atomic, {removed, Removed}} -> - broadcast_removed_node(Host, Removed), -- Lang = "", - broadcast_retract_item( - Host, ["pubsub", "nodes"], node_to_string(Node)), - {result, []}; -@@ -855,17 +1368,18 @@ - - purge_node(Host, JID, Node) -> - Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)), -+ Table = get_table(Host), - F = fun() -> -- case mnesia:read({pubsub_node, {Host, Node}}) of -- [#pubsub_node{info = Info} = N] -> -+ case mnesia:read({Table, {Host, Node}}) of -+ [N] -> -+ Info = get_node_info(N), - case get_affiliation(Info, Owner) of - owner -> - NewInfo = Info#nodeinfo{items = []}, -- mnesia:write( -- N#pubsub_node{info = NewInfo}), -- {result, Info#nodeinfo.items, []}; -+ mnesia:write(set_node_info(N, NewInfo)), -+ {result, []}; - _ -> -- {error, ?ERR_NOT_ALLOWED} -+ {error, ?ERR_FORBIDDEN} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} -@@ -874,52 +1388,170 @@ - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; -- {atomic, {result, Items, Res}} -> -- lists:foreach( -- fun(#item{id = ItemID}) -> -- broadcast_retract_item(Host, Node, ItemID) -- end, Items), -+ {atomic, {result, Res}} -> -+ broadcast_purge_node(Host, Node), - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - - --get_entities(Host, OJID, Node) -> -+owner_get_subscriptions(Host, OJID, Node) -> - Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)), -- case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of -- [#pubsub_node{info = Info}] -> -+ Table = get_table(Host), -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), - case get_affiliation(Info, Owner) of - owner -> - Entities = Info#nodeinfo.entities, - EntitiesEls = - ?DICT:fold( - fun(JID, -- #entity{affiliation = Affiliation, -- subscription = Subscription}, -+ #entity{subscription = Subscription}, - Acc) -> -- [{xmlelement, "entity", -- [{"jid", jlib:jid_to_string(JID)}, -- {"affiliation", -- affiliation_to_string(Affiliation)}, -- {"subscription", -- subscription_to_string(Subscription)}], -- []} | Acc] -+ case Subscription of -+ none -> -+ Acc; -+ _ -> -+ [{xmlelement, "subscription", -+ [{"jid", jlib:jid_to_string(JID)}, -+ {"subscription", -+ subscription_to_string(Subscription)}], -+ []} | Acc] -+ end - end, [], Entities), - {result, [{xmlelement, "pubsub", -- [{"xmlns", ?NS_PUBSUB_EVENT}], -- [{xmlelement, "entities", -+ [{"xmlns", ?NS_PUBSUB_OWNER}], -+ [{xmlelement, "subscriptions", - [{"node", node_to_string(Node)}], - EntitiesEls}]}]}; - _ -> -- {error, ?ERR_NOT_ALLOWED} -+ {error, ?ERR_FORBIDDEN} - end; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} - end. - - --set_entities(Host, OJID, Node, EntitiesEls) -> -+owner_set_subscriptions(Host, OJID, Node, EntitiesEls) -> -+ %% XXX: not updated for PEP and new pubsub revision -+ Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)), -+ Entities = -+ lists:foldl( -+ fun(El, Acc) -> -+ case Acc of -+ error -> -+ error; -+ _ -> -+ case El of -+ {xmlelement, "entity", Attrs, _} -> -+ JID = jlib:string_to_jid( -+ xml:get_attr_s("jid", Attrs)), -+ Affiliation = -+ case xml:get_attr_s("affiliation", -+ Attrs) of -+ "owner" -> owner; -+ "publisher" -> publisher; -+ "outcast" -> outcast; -+ "none" -> none; -+ _ -> false -+ end, -+ Subscription = -+ case xml:get_attr_s("subscription", -+ Attrs) of -+ "subscribed" -> subscribed; -+ "pending" -> pending; -+ "unconfigured" -> unconfigured; -+ "none" -> none; -+ _ -> false -+ end, -+ if -+ (JID == error) or -+ (Affiliation == false) or -+ (Subscription == false) -> -+ error; -+ true -> -+ [{jlib:jid_tolower(JID), -+ #entity{ -+ affiliation = Affiliation, -+ subscription = Subscription}} | -+ Acc] -+ end -+ end -+ end -+ end, [], EntitiesEls), -+ case Entities of -+ error -> -+ {error, ?ERR_BAD_REQUEST}; -+ _ -> -+ F = fun() -> -+ case mnesia:read({pubsub_node, {Host, Node}}) of -+ [#pubsub_node{info = Info} = N] -> -+ case get_affiliation(Info, Owner) of -+ owner -> -+ NewInfo = -+ set_info_entities(Info, Entities), -+ mnesia:write( -+ N#pubsub_node{info = NewInfo}), -+ {result, []}; -+ _ -> -+ {error, ?ERR_NOT_ALLOWED} -+ end; -+ [] -> -+ {error, ?ERR_ITEM_NOT_FOUND} -+ end -+ end, -+ case mnesia:transaction(F) of -+ {atomic, {error, _} = Error} -> -+ Error; -+ {atomic, {result, _}} -> -+ {result, []}; -+ _ -> -+ {error, ?ERR_INTERNAL_SERVER_ERROR} -+ end -+ end. -+ -+owner_get_affiliations(Host, OJID, Node) -> -+ Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)), -+ Table = get_table(Host), -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), -+ case get_affiliation(Info, Owner) of -+ owner -> -+ Entities = Info#nodeinfo.entities, -+ EntitiesEls = -+ ?DICT:fold( -+ fun(JID, -+ #entity{affiliation = Affiliation}, -+ Acc) -> -+ case Affiliation of -+ none -> -+ Acc; -+ _ -> -+ [{xmlelement, "affiliation", -+ [{"jid", jlib:jid_to_string(JID)}, -+ {"affiliation", -+ affiliation_to_string(Affiliation)}], -+ []} | Acc] -+ end -+ end, [], Entities), -+ {result, [{xmlelement, "pubsub", -+ [{"xmlns", ?NS_PUBSUB_OWNER}], -+ [{xmlelement, "affiliations", -+ [{"node", node_to_string(Node)}], -+ EntitiesEls}]}]}; -+ _ -> -+ {error, ?ERR_FORBIDDEN} -+ end; -+ _ -> -+ {error, ?ERR_ITEM_NOT_FOUND} -+ end. -+ -+ -+owner_set_affiliations(Host, OJID, Node, EntitiesEls) -> -+ %% XXX: not updated for PEP and new pubsub revision - Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)), - Entities = - lists:foldl( -@@ -999,42 +1631,114 @@ - - get_affiliations(Host, JID) -> - LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), -+ Table = get_table(Host), -+ Template = case Table of -+ pubsub_node -> -+ #pubsub_node{_ = '_'}; -+ pep_node -> -+ #pep_node{_ = '_'} -+ end, - case catch mnesia:dirty_select( -- pubsub_node, -- [{#pubsub_node{_ = '_'}, -+ Table, -+ [{Template, - [], - ['$_']}]) of - {'EXIT', _} -> -- {error, ?ERR_INTERNAL_SERVER_ERROR}; -+ {error, ?ERR_INTERNAL_SERVER_ERROR}; - Nodes -> -- Entities = -- lists:flatmap( -- fun(#pubsub_node{host_node = {H, Node}, info = Info}) -- when H == Host -> -- Affiliation = get_affiliation(Info, LJID), -- Subscription = get_subscription(Info, LJID), -- if -- (Affiliation /= none) or -- (Subscription /= none) -> -- [{xmlelement, "entity", -- [{"node", node_to_string(Node)}, -- {"jid", jlib:jid_to_string(JID)}, -- {"affiliation", -- affiliation_to_string(Affiliation)}, -- {"subscription", -- subscription_to_string(Subscription)}], -- []}]; -- true -> -- [] -- end; -- (_) -> -- [] -- end, Nodes), -- {result, [{xmlelement, "pubsub", -- [{"xmlns", ?NS_PUBSUB_EVENT}], -- [{xmlelement, "affiliations", [], -- Entities}]}]} -- end. -+ Entities = -+ lists:flatmap( -+ fun(N) -> -+ Info = get_node_info(N), -+ {H, Node} = -+ case Table of -+ pubsub_node -> -+ N#pubsub_node.host_node; -+ pep_node -> -+ N#pep_node.owner_node -+ end, -+ if Host == H -> -+ Affiliation = get_affiliation(Info, LJID), -+ if Affiliation /= none -> -+ [{xmlelement, "affiliation", -+ [{"node", node_to_string(Node)}, -+ {"affiliation", -+ affiliation_to_string(Affiliation)}], -+ []}]; -+ true -> -+ [] -+ end; -+ true -> -+ [] -+ end -+ end, -+ Nodes), -+ case Entities of -+ [] -> -+ {error, ?ERR_ITEM_NOT_FOUND}; -+ _ -> -+ {result, [{xmlelement, "pubsub", -+ [{"xmlns", ?NS_PUBSUB}], -+ [{xmlelement, "affiliations", [], -+ Entities}]}]} -+ end -+ end. -+ -+get_subscriptions(Host, JID) -> -+ LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), -+ Table = get_table(Host), -+ Template = case Table of -+ pubsub_node -> -+ #pubsub_node{_ = '_'}; -+ pep_node -> -+ #pep_node{_ = '_'} -+ end, -+ case catch mnesia:dirty_select( -+ Table, -+ [{Template, -+ [], -+ ['$_']}]) of -+ {'EXIT', _} -> -+ {error, ?ERR_INTERNAL_SERVER_ERROR}; -+ Nodes -> -+ Entities = -+ lists:flatmap( -+ fun(N) -> -+ Info = get_node_info(N), -+ {H, Node} = -+ case Table of -+ pubsub_node -> -+ N#pubsub_node.host_node; -+ pep_node -> -+ N#pep_node.owner_node -+ end, -+ if Host == H -> -+ Subscription = get_subscription(Info, LJID), -+ if Subscription /= none -> -+ [{xmlelement, "subscription", -+ [{"node", node_to_string(Node)}, -+ {"jid", jlib:jid_to_string(LJID)}, %XXX: full JID? -+ {"subscription", -+ subscription_to_string(Subscription)}], -+ []}]; -+ true -> -+ [] -+ end; -+ true -> -+ [] -+ end -+ end, -+ Nodes), -+ case Entities of -+ [] -> -+ {error, ?ERR_ITEM_NOT_FOUND}; -+ _ -> -+ {result, [{xmlelement, "pubsub", -+ [{"xmlns", ?NS_PUBSUB}], -+ [{xmlelement, "subscriptions", [], -+ Entities}]}]} -+ end -+ end. - - - -@@ -1123,18 +1827,18 @@ - false - end. - --add_subscriber(Info, Subscriber) -> -+add_subscriber(Info, Subscriber, Subscription) -> - Entities = Info#nodeinfo.entities, - case ?DICT:find(Subscriber, Entities) of - {ok, Entity} -> - Info#nodeinfo{ - entities = ?DICT:store(Subscriber, -- Entity#entity{subscription = subscribed}, -+ Entity#entity{subscription = Subscription}, - Entities)}; - _ -> - Info#nodeinfo{ - entities = ?DICT:store(Subscriber, -- #entity{subscription = subscribed}, -+ #entity{subscription = Subscription}, - Entities)} - end. - -@@ -1167,32 +1871,48 @@ - end, Info#nodeinfo.entities, Entities), - Info#nodeinfo{entities = NewEntities}. - -+send_last_published_item(Subscriber, Host, Node, Info) -> -+ case Info#nodeinfo.items of -+ [] -> -+ %% No published items - can't send anything. -+ ok; -+ [#item{id = ItemID, payload = Payload} | _] -> -+ %% At least one item - send the last one. -+ ItemAttrs = case ItemID of -+ "" -> []; -+ _ -> [{"id", ItemID}] -+ end, -+ ItemsEl = {xmlelement, "item", -+ ItemAttrs, Payload}, -+ Stanza = -+ {xmlelement, "message", -+ [], -+ [{xmlelement, "event", -+ [{"xmlns", ?NS_PUBSUB_EVENT}], -+ [{xmlelement, "items", -+ [{"node", node_to_string(Node)}], -+ [ItemsEl]}]}]}, -+ ejabberd_router:route( -+ get_sender(Host), jlib:make_jid(Subscriber), Stanza) -+ end. -+ - - - broadcast_publish_item(Host, Node, ItemID, Payload) -> -- case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of -- [#pubsub_node{info = Info}] -> -+ ?DEBUG("broadcasting for ~p / ~p", [Host, Node]), -+ Table = get_table(Host), -+ Sender = get_sender(Host), -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), - ?DICT:fold( - fun(JID, #entity{subscription = Subscription}, _) -> -- Present = case get_node_option( -- Info, presence_based_delivery) of -- true -> -- case ets:lookup( -- gen_mod:get_module_proc(Host, pubsub_presence), -- {element(1, JID), -- element(2, JID)}) of -- [_] -> -- true; -- [] -> -- false -- end; -- false -> -- true -- end, -+ Resources = get_recipient_resources(Host, JID, Info), -+ ?DEBUG("subscriber ~p: delivering to resources ~p", [JID, Resources]), - if -- (Subscription /= none) and -- (Subscription /= pending) and -- Present -> -+ Subscription /= none, -+ Subscription /= pending, -+ Resources /= [] -> - ItemAttrs = case ItemID of - "" -> []; - _ -> [{"id", ItemID}] -@@ -1213,8 +1933,12 @@ - [{xmlelement, "item", - ItemAttrs, - Content}]}]}]}, -- ejabberd_router:route( -- ?MYJID, jlib:make_jid(JID), Stanza); -+ TheJID = jlib:make_jid(JID), -+ lists:foreach(fun(Resource) -> -+ FullJID = jlib:jid_replace_resource(TheJID, Resource), -+ ejabberd_router:route( -+ Sender, FullJID, Stanza) -+ end, Resources); - true -> - ok - end -@@ -1225,10 +1949,14 @@ - - - broadcast_retract_item(Host, Node, ItemID) -> -- case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of -- [#pubsub_node{info = Info}] -> -+ Table = get_table(Host), -+ Sender = get_sender(Host), -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), - case get_node_option(Info, notify_retract) of - true -> -+ %% XXX: presence-based notifications? - ?DICT:fold( - fun(JID, #entity{subscription = Subscription}, _) -> - if -@@ -1240,14 +1968,48 @@ - end, - Stanza = - {xmlelement, "message", [], -- [{xmlelement, "x", -+ [{xmlelement, "event", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", - [{"node", node_to_string(Node)}], - [{xmlelement, "retract", - ItemAttrs, []}]}]}]}, - ejabberd_router:route( -- ?MYJID, jlib:make_jid(JID), Stanza); -+ Sender, jlib:make_jid(JID), Stanza); -+ true -> -+ ok -+ end -+ end, ok, Info#nodeinfo.entities); -+ false -> -+ ok -+ end; -+ _ -> -+ false -+ end. -+ -+broadcast_purge_node(Host, Node) -> -+ Table = get_table(Host), -+ Sender = get_sender(Host), -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), -+ case get_node_option(Info, notify_retract) of -+ true -> -+ %% XXX: presence-based notifications? -+ ?DICT:fold( -+ fun(JID, #entity{subscription = Subscription}, _) -> -+ if -+ (Subscription /= none) and -+ (Subscription /= pending) -> -+ Stanza = -+ {xmlelement, "message", [], -+ [{xmlelement, "event", -+ [{"xmlns", ?NS_PUBSUB_EVENT}], -+ [{xmlelement, "purge", -+ [{"node", node_to_string(Node)}], -+ []}]}]}, -+ ejabberd_router:route( -+ Sender, jlib:make_jid(JID), Stanza); - true -> - ok - end -@@ -1265,6 +2027,7 @@ - fun({Node, Info}) -> - case get_node_option(Info, notify_delete) of - true -> -+ %% XXX: presence-based notifications? - Entities = Info#nodeinfo.entities, - ?DICT:fold( - fun(JID, #entity{subscription = Subscription}, _) -> -@@ -1273,13 +2036,13 @@ - (Subscription /= pending) -> - Stanza = - {xmlelement, "message", [], -- [{xmlelement, "x", -+ [{xmlelement, "event", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "delete", - [{"node", node_to_string(Node)}], - []}]}]}, - ejabberd_router:route( -- ?MYJID, jlib:make_jid(JID), Stanza); -+ get_sender(Host), jlib:make_jid(JID), Stanza); - true -> - ok - end -@@ -1291,31 +2054,19 @@ - - - broadcast_config_notification(Host, Node, Lang) -> -- case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of -- [#pubsub_node{info = Info}] -> -+ Table = get_table(Host), -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), - case get_node_option(Info, notify_config) of - true -> - ?DICT:fold( - fun(JID, #entity{subscription = Subscription}, _) -> -- Present = case get_node_option( -- Info, presence_based_delivery) of -- true -> -- case ets:lookup( -- gen_mod:get_module_proc(Host, pubsub_presence), -- {element(1, JID), -- element(2, JID)}) of -- [_] -> -- true; -- [] -> -- false -- end; -- false -> -- true -- end, -+ Resources = get_recipient_resources(Host, JID, Info), - if -- (Subscription /= none) and -- (Subscription /= pending) and -- Present -> -+ Subscription /= none, -+ Subscription /= pending, -+ Resources /= [] -> - Fields = get_node_config_xfields( - Node, Info, Lang), - Content = case get_node_option( -@@ -1323,22 +2074,24 @@ - true -> - [{xmlelement, "x", - [{"xmlns", ?NS_XDATA}, -- {"type", "form"}], -+ {"type", "result"}], - Fields}]; - false -> - [] - end, - Stanza = - {xmlelement, "message", [], -- [{xmlelement, "x", -+ [{xmlelement, "event", - [{"xmlns", ?NS_PUBSUB_EVENT}], -- [{xmlelement, "items", -+ [{xmlelement, "configuration", - [{"node", node_to_string(Node)}], -- [{xmlelement, "item", -- [{"id", "configuration"}], -- Content}]}]}]}, -- ejabberd_router:route( -- ?MYJID, jlib:make_jid(JID), Stanza); -+ Content}]}]}, -+ TheJID = jlib:make_jid(JID), -+ lists:foreach(fun(Resource) -> -+ FullJID = jlib:jid_replace_resource(TheJID, Resource), -+ ejabberd_router:route( -+ get_sender(Host), FullJID, Stanza) -+ end, Resources); - true -> - ok - end -@@ -1350,19 +2103,74 @@ - false - end. - -+get_recipient_resources(Host, JID, Info) -> -+ %% Return a list of resources that are supposed to receive event -+ %% notifications. An empty string in that list means a bare JID. -+ case get_node_option(Info, presence_based_delivery) of -+ false -> -+ [""]; -+ true -> -+ To = case Host of -+ {_, _, _} -> -+ Host; -+ _ -> -+ {"", Host, ""} -+ end, -+ Resources = get_present_resources(To, JID), -+ %% Here, there is a difference between JEP-0060 and -+ %% JEP-0163. JEP-0060, section 12.1, says that the -+ %% service should not attempt to guess the correct -+ %% resource. JEP-0163, section 7.1.1.2, says that the -+ %% service must send a notification to each resource. -+ case Info#nodeinfo.options of -+ [{default, pep_node} | _] -> -+ Resources; -+ _ -> -+ %% That means, if noone is online, nothing is -+ %% sent. If someone is online, send one -+ %% notification. -+ case Resources of -+ [] -> -+ []; -+ _ -> -+ [""] -+ end -+ end -+ end. -+ - - - iq_pubsub_owner(Host, From, Type, Lang, SubEl) -> - {xmlelement, _, _, SubEls} = SubEl, - case xml:remove_cdata(SubEls) of - [{xmlelement, Name, Attrs, Els}] -> -- SNode = xml:get_attr_s("node", Attrs), -- Node = string:tokens(SNode, "/"), -+ %% For PEP, there is no node hierarchy. -+ Node = case Host of -+ {_, _, _} -> -+ xml:get_attr_s("node", Attrs); -+ _ -> -+ SNode = xml:get_attr_s("node", Attrs), -+ string:tokens(SNode, "/") -+ end, - case {Type, Name} of - {get, "configure"} -> - get_node_config(Host, From, Node, Lang); - {set, "configure"} -> - set_node_config(Host, From, Node, Els, Lang); -+ {get, "default"} -> -+ {error, extend_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "retrieve-default")}; -+ {set, "delete"} -> -+ delete_node(Host, From, Node); -+ {set, "purge"} -> -+ purge_node(Host, From, Node); -+ {get, "subscriptions"} -> -+ owner_get_subscriptions(Host, From, Node); -+ {set, "subscriptions"} -> -+ owner_set_subscriptions(Host, From, Node, xml:remove_cdata(Els)); -+ {get, "affiliations"} -> -+ owner_get_affiliations(Host, From, Node); -+ {set, "affiliations"} -> -+ owner_set_affiliations(Host, From, Node, xml:remove_cdata(Els)); - _ -> - {error, ?ERR_FEATURE_NOT_IMPLEMENTED} - end; -@@ -1371,8 +2179,10 @@ - end. - - get_node_config(Host, From, Node, Lang) -> -- case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of -- [#pubsub_node{info = Info}] -> -+ Table = get_table(Host), -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), - case get_affiliation(Info, From) of - owner -> - Fields = get_node_config_xfields(Node, Info, Lang), -@@ -1384,7 +2194,7 @@ - {"type", "form"}], - Fields}]}]}]}; - _ -> -- {error, ?ERR_NOT_AUTHORIZED} -+ {error, ?ERR_FORBIDDEN} - end; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} -@@ -1416,7 +2226,7 @@ - [atom_to_list(O) || O <- Opts])). - - ---define(DEFAULT_OPTIONS, -+-define(DEFAULT_PUBSUB_OPTIONS, - [{deliver_payloads, true}, - {notify_config, false}, - {notify_delete, false}, -@@ -1424,12 +2234,28 @@ - {persist_items, true}, - {max_items, ?MAXITEMS div 2}, - {subscribe, true}, -- {subscription_model, open}, -+ {access_model, open}, -+ {access_roster_groups, []}, - {publish_model, publishers}, - {max_payload_size, ?MAX_PAYLOAD_SIZE}, -- {send_item_subscribe, false}, -+ {send_last_published_item, never}, - {presence_based_delivery, false}]). - -+-define(DEFAULT_PEP_OPTIONS, -+ [{deliver_payloads, true}, -+ {notify_config, false}, -+ {notify_delete, false}, -+ {notify_retract, false}, -+ {persist_items, false}, -+ {max_items, ?MAXITEMS div 2}, -+ {subscribe, true}, -+ {access_model, presence}, -+ {access_roster_groups, []}, -+ {publish_model, publishers}, -+ {max_payload_size, ?MAX_PAYLOAD_SIZE}, -+ {send_last_published_item, on_sub_and_presence}, -+ {presence_based_delivery, true}]). -+ - get_node_option(Info, current_approver) -> - Default = hd(get_owners_jids(Info)), - Options = Info#nodeinfo.options, -@@ -1438,15 +2264,32 @@ - current_approver, 1, - Options ++ [{current_approver, Default}]))); - get_node_option(#nodeinfo{options = Options}, Var) -> -+ %% At this level, it's hard to know which set of defaults to -+ %% apply. Therefore, all newly created nodes have an extra -+ %% "defaults" field. We assume that all nodes created before this -+ %% change are pubsub nodes. -+ {Defaults, Opts} = case Options of -+ [{defaults, pubsub_node} | Tail] -> -+ {?DEFAULT_PUBSUB_OPTIONS, Tail}; -+ [{defaults, pep_node} | Tail] -> -+ {?DEFAULT_PEP_OPTIONS, Tail}; -+ _ -> -+ {?DEFAULT_PUBSUB_OPTIONS, Options} -+ end, - element( -- 2, element(2, lists:keysearch(Var, 1, Options ++ ?DEFAULT_OPTIONS))). -+ 2, element(2, lists:keysearch(Var, 1, Opts ++ Defaults))). - - get_max_items(Info) -> - case get_node_option(Info, persist_items) of - true -> - get_node_option(Info, max_items); - false -> -- 0 -+ case get_node_option(Info, send_last_published_item) of -+ never -> -+ 0; -+ _ -> -+ 1 -+ end - end. - - get_owners_jids(Info) -> -@@ -1454,8 +2297,7 @@ - Owners = - ?DICT:fold( - fun(JID, -- #entity{affiliation = Affiliation, -- subscription = Subscription}, -+ #entity{affiliation = Affiliation}, - Acc) -> - case Affiliation of - owner -> -@@ -1467,7 +2309,11 @@ - lists:sort(Owners). - - --get_node_config_xfields(Node, Info, Lang) -> -+get_node_config_xfields(_Node, Info, Lang) -> -+ Type = case Info#nodeinfo.options of -+ [{defaults, D} | _] -> D; -+ _ -> pubsub_node -+ end, - [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NODE_CONFIG), - ?BOOL_CONFIG_FIELD("Deliver payloads with event notifications", deliver_payloads), - ?BOOL_CONFIG_FIELD("Notify subscribers when the node configuration changes", notify_config), -@@ -1476,12 +2322,24 @@ - ?BOOL_CONFIG_FIELD("Persist items to storage", persist_items), - ?INTEGER_CONFIG_FIELD("Max # of items to persist", max_items), - ?BOOL_CONFIG_FIELD("Whether to allow subscriptions", subscribe), -- ?ALIST_CONFIG_FIELD("Specify the subscriber model", subscription_model, -- [open]), -+ ?ALIST_CONFIG_FIELD("Specify the access model", access_model, -+ [open, whitelist] ++ -+ case Type of -+ pep_node -> [presence, roster]; -+ pubsub_node -> [authorize] -+ end), -+ %% XXX: change to list-multi, include current roster groups as options -+ {xmlelement, "field", [{"type", "text-multi"}, -+ {"label", translate:translate(Lang, "Roster groups that may subscribe (if access model is roster)")}, -+ {"var", "pubsub#access_roster_groups"}], -+ [{xmlelement, "value", [], [{xmlcdata, Value}]} || -+ Value <- get_node_option(Info, access_roster_groups)]}, - ?ALIST_CONFIG_FIELD("Specify the publisher model", publish_model, - [publishers, subscribers, open]), - ?INTEGER_CONFIG_FIELD("Max payload size in bytes", max_payload_size), -- ?BOOL_CONFIG_FIELD("Send items to new subscribers", send_item_subscribe), -+ %% XXX: fix labels for options -+ ?ALIST_CONFIG_FIELD("When to send the last published item", send_last_published_item, -+ [never, on_sub, on_sub_and_presence]), - ?BOOL_CONFIG_FIELD("Only deliver notifications to available users", presence_based_delivery), - ?JLIST_CONFIG_FIELD("Specify the current subscription approver", current_approver, - get_owners_jids(Info)) -@@ -1489,8 +2347,10 @@ - - - set_node_config(Host, From, Node, Els, Lang) -> -- case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of -- [#pubsub_node{info = Info} = N] -> -+ Table = get_table(Host), -+ case catch mnesia:dirty_read(Table, {Host, Node}) of -+ [N] -> -+ Info = get_node_info(N), - case get_affiliation(Info, From) of - owner -> - case xml:remove_cdata(Els) of -@@ -1510,14 +2370,14 @@ - {error, ?ERR_BAD_REQUEST} - end; - _ -> -- {error, ?ERR_NOT_AUTHORIZED} -+ {error, ?ERR_FORBIDDEN} - end; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} - end. - - --set_node_config1(Host, From, Node, XEl, CurOpts, Lang) -> -+set_node_config1(Host, _From, Node, XEl, CurOpts, Lang) -> - XData = jlib:parse_xdata_submit(XEl), - case XData of - invalid -> -@@ -1537,78 +2397,97 @@ - - - -define(SET_BOOL_XOPT(Opt, Val), -- case Val of -- "0" -> set_xoption(Opts, add_opt(Opt, false, NewOpts)); -- "1" -> set_xoption(Opts, add_opt(Opt, true, NewOpts)); -- _ -> {error, ?ERR_BAD_REQUEST} -+ BoolVal = case Val of -+ "0" -> false; -+ "1" -> true; -+ "false" -> false; -+ "true" -> true; -+ _ -> error -+ end, -+ case BoolVal of -+ error -> {error, ?ERR_NOT_ACCEPTABLE}; -+ _ -> set_xoption(Opts, add_opt(Opt, BoolVal, NewOpts), Type) - end). - - -define(SET_STRING_XOPT(Opt, Val), -- set_xoption(Opts, add_opt(Opt, Val, NewOpts))). -+ set_xoption(Opts, add_opt(Opt, Val, NewOpts), Type)). - - -define(SET_INTEGER_XOPT(Opt, Val, Min, Max), - case catch list_to_integer(Val) of - IVal when is_integer(IVal), - IVal >= Min, - IVal =< Max -> -- set_xoption(Opts, add_opt(Opt, IVal, NewOpts)); -+ set_xoption(Opts, add_opt(Opt, IVal, NewOpts), Type); - _ -> -- {error, ?ERR_BAD_REQUEST} -+ {error, ?ERR_NOT_ACCEPTABLE} - end). - - -define(SET_ALIST_XOPT(Opt, Val, Vals), - case lists:member(Val, [atom_to_list(V) || V <- Vals]) of - true -> -- set_xoption(Opts, add_opt(Opt, list_to_atom(Val), NewOpts)); -+ set_xoption(Opts, add_opt(Opt, list_to_atom(Val), NewOpts), Type); - false -> -- {error, ?ERR_BAD_REQUEST} -+ {error, ?ERR_NOT_ACCEPTABLE} - end). - - --set_xoption([], NewOpts) -> -+set_xoption(Opts, [{defaults, Type} = Defaults | NewOpts]) -> -+ %% Make sure that "defaults" remains at head of option list. -+ [Defaults | set_xoption(Opts, NewOpts, Type)]; -+set_xoption(Opts, NewOpts) -> -+ set_xoption(Opts, NewOpts, pubsub_node). -+ -+set_xoption([], NewOpts, _Type) -> - NewOpts; --set_xoption([{"FORM_TYPE", _} | Opts], NewOpts) -> -- set_xoption(Opts, NewOpts); --set_xoption([{"pubsub#deliver_payloads", [Val]} | Opts], NewOpts) -> -+set_xoption([{"FORM_TYPE", _} | Opts], NewOpts, Type) -> -+ set_xoption(Opts, NewOpts, Type); -+set_xoption([{"pubsub#deliver_payloads", [Val]} | Opts], NewOpts, Type) -> - ?SET_BOOL_XOPT(deliver_payloads, Val); --set_xoption([{"pubsub#notify_config", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#notify_config", [Val]} | Opts], NewOpts, Type) -> - ?SET_BOOL_XOPT(notify_config, Val); --set_xoption([{"pubsub#notify_delete", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#notify_delete", [Val]} | Opts], NewOpts, Type) -> - ?SET_BOOL_XOPT(notify_delete, Val); --set_xoption([{"pubsub#notify_retract", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#notify_retract", [Val]} | Opts], NewOpts, Type) -> - ?SET_BOOL_XOPT(notify_retract, Val); --set_xoption([{"pubsub#persist_items", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#persist_items", [Val]} | Opts], NewOpts, Type) -> - ?SET_BOOL_XOPT(persist_items, Val); --set_xoption([{"pubsub#max_items", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#max_items", [Val]} | Opts], NewOpts, Type) -> - ?SET_INTEGER_XOPT(max_items, Val, 0, ?MAXITEMS); --set_xoption([{"pubsub#subscribe", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#subscribe", [Val]} | Opts], NewOpts, Type) -> - ?SET_BOOL_XOPT(subscribe, Val); --set_xoption([{"pubsub#subscription_model", [Val]} | Opts], NewOpts) -> -- ?SET_ALIST_XOPT(subscription_model, Val, [open]); --set_xoption([{"pubsub#publish_model", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#access_model", [Val]} | Opts], NewOpts, Type) -> -+ AllowedModels = case Type of -+ pubsub_node -> [open, authorize, whitelist]; -+ pep_node -> [open, presence, roster, whitelist] -+ end, -+ ?SET_ALIST_XOPT(access_model, Val, AllowedModels); -+set_xoption([{"pubsub#access_roster_groups", Values} | Opts], NewOpts, Type) -> -+ set_xoption(Opts, add_opt(access_roster_groups, Values, NewOpts), Type); -+set_xoption([{"pubsub#publish_model", [Val]} | Opts], NewOpts, Type) -> - ?SET_ALIST_XOPT(publish_model, Val, [publishers, subscribers, open]); --set_xoption([{"pubsub#max_payload_size", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#max_payload_size", [Val]} | Opts], NewOpts, Type) -> - ?SET_INTEGER_XOPT(max_payload_size, Val, 0, ?MAX_PAYLOAD_SIZE); --set_xoption([{"pubsub#send_item_subscribe", [Val]} | Opts], NewOpts) -> -- ?SET_BOOL_XOPT(send_item_subscribe, Val); --set_xoption([{"pubsub#presence_based_delivery", [Val]} | Opts], NewOpts) -> -+set_xoption([{"pubsub#send_last_published_item", [Val]} | Opts], NewOpts, Type) -> -+ ?SET_ALIST_XOPT(send_last_published_item, Val, [never, on_sub, on_sub_and_presence]); -+set_xoption([{"pubsub#presence_based_delivery", [Val]} | Opts], NewOpts, Type) -> - ?SET_BOOL_XOPT(presence_based_delivery, Val); --set_xoption([{"pubsub#current_approver", _} | Opts], NewOpts) -> -+set_xoption([{"pubsub#current_approver", _} | Opts], NewOpts, Type) -> - % TODO -- set_xoption(Opts, NewOpts); -+ set_xoption(Opts, NewOpts, Type); - %set_xoption([{"title", [Val]} | Opts], NewOpts) -> - % ?SET_STRING_XOPT(title, Val); --set_xoption([_ | _Opts], _NewOpts) -> -- {error, ?ERR_BAD_REQUEST}. -+set_xoption([_ | _Opts], _NewOpts, _Type) -> -+ {error, ?ERR_NOT_ACCEPTABLE}. - - - change_node_opts(Host, NewOpts, Node, Lang) -> -+ Table = get_table(Host), - F = fun() -> -- case mnesia:read({pubsub_node, {Host, Node}}) of -- [#pubsub_node{info = Info} = N] -> -- NewInfo = Info#nodeinfo{options = NewOpts}, -- mnesia:write( -- N#pubsub_node{info = NewInfo}), -+ case mnesia:read({Table, {Host, Node}}) of -+ [N] -> -+ Info = get_node_info(N), -+ NewInfo = Info#nodeinfo{options = maybe_add_defaults(NewOpts, Table)}, -+ mnesia:write(set_node_info(N, NewInfo)), - {result, []}; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} -@@ -1624,29 +2503,33 @@ - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - -+maybe_add_defaults(NewOpts, Table) -> -+ %% When we change node configuration, we add an explicit default -+ %% marker, pubsub_node or pep_node. -+ case NewOpts of -+ [{defaults, _} | _] -> -+ NewOpts; -+ _ -> -+ [{defaults, Table} | NewOpts] -+ end. - - -+get_present_resources({ToUser, ToServer, _}, {FromUser, FromServer, _}) -> -+ get_present_resources(ToUser, ToServer, FromUser, FromServer). -+ -+get_present_resources(ToUser, ToServer, FromUser, FromServer) -> -+ %% Return a list of resources of FromUser@FromServer that have -+ %% sent presence to ToUser@ToServer. -+ Key = {ToUser, ToServer, FromUser, FromServer}, -+ lists:map(fun(#pubsub_presence{resource = Res}) -> Res end, -+ case catch mnesia:dirty_read(pubsub_presence, Key) of -+ Result when is_list(Result) -> -+ Result; -+ _ -> -+ [] -+ end). - - --find_my_host(LServer) -> -- Parts = string:tokens(LServer, "."), -- find_my_host(Parts, ?MYHOSTS). -- --find_my_host([], _Hosts) -> -- ?MYNAME; --find_my_host([_ | Tail] = Parts, Hosts) -> -- Domain = parts_to_string(Parts), -- case lists:member(Domain, Hosts) of -- true -> -- Domain; -- false -> -- find_my_host(Tail, Hosts) -- end. -- --parts_to_string(Parts) -> -- string:strip(lists:flatten(lists:map(fun(S) -> [S, $.] end, Parts)), -- right, $.). -- - - - update_table(Host) -> |