Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cec11aca59 |
@@ -1,6 +1,3 @@
|
||||
As a special exception, the authors give permission to link this program
|
||||
with the OpenSSL library and distribute the resulting binary.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
|
||||
@@ -1,753 +1,5 @@
|
||||
2005-05-23 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd.hrl: Updated version to 0.9.1
|
||||
|
||||
* src/msgs/fr.msg: Updated (thanks to Mickael Remond)
|
||||
|
||||
* src/odbc/Makefile.in: Added
|
||||
|
||||
* src/configure.ac: Updated for odbc support
|
||||
* src/Makefile.in: Likewise
|
||||
|
||||
* src/aclocal.m4: Bugfix
|
||||
|
||||
* src/mod_last_odbc.erl: Added store_last_info/4 function (thanks
|
||||
to Sergei Golovan)
|
||||
* src/mod_last.erl: Likewise
|
||||
|
||||
* src/jd2ejd.erl: Support for exporting iq:last information,
|
||||
better error handling (thanks to Sergei Golovan)
|
||||
|
||||
* src/ejabberd_ctl.erl: Added "import-file" and "import-dir"
|
||||
commands (thanks to Sergei Golovan)
|
||||
|
||||
* doc/guide.tex: Updated (thanks to Sergei Golovan)
|
||||
* doc/dev.tex: Likewise
|
||||
* doc/disco.png: Likewise
|
||||
* doc/discorus.png: Likewise
|
||||
* doc/webadmin.png: Likewise
|
||||
* doc/webadminru.png: Likewise
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
* src/msgs/uk.msg: Likewise
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Updated CSS, added modules
|
||||
management, fixed bug with configuring of listened ports on
|
||||
different nodes (thanks to Sergei Golovan)
|
||||
|
||||
* src/gen_mod.erl: Added function loaded_modules_with_opts/0, new
|
||||
API for module stopping (thanks to Sergei Golovan)
|
||||
* src/mod_muc/mod_muc.erl: Moved a process name to a macros,
|
||||
updated module stopping (thanks to Sergei Golovan)
|
||||
* src/mod_irc/mod_irc.erl: Likewise
|
||||
* src/mod_pubsub/mod_pubsub.erl: Likewise
|
||||
* src/mod_announce.erl: Updated module stopping (thanks to Sergei
|
||||
Golovan)
|
||||
* src/mod_echo.erl: Likewise
|
||||
* src/mod_offline.erl: Likewise
|
||||
|
||||
* src/web/ejabberd_http.erl: "Connection:" header value now
|
||||
matched case-insensitive, and returned to client, replaced
|
||||
duplicate is_space($\r) with is_space($\t) (thanks to Maxim
|
||||
Ryazanov)
|
||||
|
||||
2005-05-21 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Fixed XML element name for
|
||||
pubsub#event namespace (thanks to Magnus Henoch)
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
* src/msgs/uk.msg: Likewise
|
||||
|
||||
* src/msgs/es.msg: Updated (thanks to Badlop)
|
||||
|
||||
2005-05-19 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Now possible to subscribe to a
|
||||
pubsub node with a JID that includes a resource (thanks to Martijn
|
||||
van Beers)
|
||||
|
||||
* src/stringprep/uni_parse2.tcl: Bugfix
|
||||
* src/stringprep/stringprep_drv.c: Likewise
|
||||
* src/stringprep/uni_norm.c: Regenerated
|
||||
|
||||
2005-05-18 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_irc/mod_irc_connection.erl: Added support for WHOIS
|
||||
requests and reply to USERINFO (thanks to Oleg V. Motienko)
|
||||
|
||||
2005-05-17 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/stringprep/stringprep_drv.c: Bugfix
|
||||
|
||||
2005-05-16 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/win32/ejabberd.cfg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/odbc/Makefile.win32: Added (thanks to Sergei Golovan)
|
||||
|
||||
* src/Makefile.win32: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2005-05-15 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/jd2ejd.erl: Fixed private xml setting
|
||||
|
||||
* src/mod_last.erl: Bugfix
|
||||
* src/mod_last_odbc.erl: Likewise
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Fixed service stopping
|
||||
|
||||
2005-05-09 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Fixed bug with storing
|
||||
affiliations of invited users in members-only room (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
* src/ejabberd_c2s.erl: Fixed starttls_required behaviour for
|
||||
legacy connections (thanks to Brian Campbell)
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Images now specified via CSS,
|
||||
design slightly updated, added last activity statistics (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
2005-05-07 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/stringprep/stringprep_drv.c: Added check for bidi
|
||||
|
||||
* src/stringprep/uni_parse.tcl: Now handle all Unicode code points
|
||||
up to U+10FFFF
|
||||
* src/stringprep/uni_parse2.tcl: Likewise
|
||||
* src/stringprep/uni_data.c: Regenerated
|
||||
* src/stringprep/uni_norm.c: Likewise
|
||||
|
||||
2005-05-06 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/stringprep/uni_norm.c: Regenerated with Unicode 3.2 tables
|
||||
as required by RFC3454
|
||||
|
||||
* src/stringprep/uni_parse2.tcl: Bugfixes
|
||||
|
||||
* src/stringprep/stringprep_drv.c: Bugfixes, added hangul
|
||||
composition
|
||||
|
||||
2005-05-05 Mickael Remond <mremond@erlang-fr.org>
|
||||
|
||||
* src/msgs/fr.msg: Added missing version 0.9 fields and removed unused
|
||||
ones.
|
||||
|
||||
2005-05-04 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Store ban reasons for outcast
|
||||
items, updated affiliation matching rules to latest JEP-0045
|
||||
|
||||
2005-05-02 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Updated changing roles and
|
||||
affiliations tables to latest JEP-0045
|
||||
|
||||
2005-05-01 Mickael Remond <mremond@erlang-fr.org>
|
||||
|
||||
* src/msgs/fr.msg: Updated
|
||||
|
||||
2005-05-01 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/msgs/sw.msg: Renamed to sv.msg
|
||||
* src/msgs/sv.msg: Likewise
|
||||
|
||||
* src/msgs/pl.msg: Updated (thanks to Andrew Smyk)
|
||||
|
||||
* contrib/extract_translations/README: Better phrasing (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
* contrib/extract_translations/extract_translations.erl: Fix to
|
||||
avoid duplication of lines (thanks to Sergei Golovan)
|
||||
|
||||
* src/msgs/sw.msg: New Swedish translation (thanks to Magnus
|
||||
Henoch)
|
||||
|
||||
* src/msgs/pt.msg: New Portuguese translation (thanks to iceburn)
|
||||
|
||||
* src/msgs/es.msg: Updated (thanks to Badlop)
|
||||
|
||||
* src/msgs/nl.msg: Updated (thanks to Sander Devrieze)
|
||||
|
||||
2005-04-27 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_auth_ldap.erl: Added listing of users support
|
||||
|
||||
* src/ejabberd.cfg.example: Updated LDAP options
|
||||
|
||||
* src/ejabberd_ctl.erl: Better spelling, now prints full file
|
||||
paths, fixed checking of mnesia:install_fallback result, now
|
||||
"dump" command dumps only persistent tables
|
||||
|
||||
* contrib/extract_translations/: A tool for extracting of
|
||||
translation strings from ejabberd code (thanks to Sergei Golovan)
|
||||
|
||||
2005-04-26 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_vcard_ldap.erl: Bugfix (thanks to Mickael Remond)
|
||||
|
||||
2005-04-24 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Added translation to submit
|
||||
button and "shared roster groups" header (thanks to iceburn and
|
||||
Sergei Golovan)
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
* src/msgs/uk.msg: Likewise
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Minor update to simplify
|
||||
translation (thanks to Sergei Golovan)
|
||||
|
||||
* src/tls/tls_drv.c: Reverted previous patch
|
||||
|
||||
2005-04-22 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* doc/guide.tex: Added mod_shared_roster documentation
|
||||
|
||||
* src/ejabberd.hrl: Updated version
|
||||
|
||||
2005-04-21 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/jd2ejd.erl: Bugfix
|
||||
|
||||
2005-04-20 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_auth.erl: Added check for domain of registered user
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Fixed user registration via web
|
||||
interface, fixed path to user's offline messages (thanks to
|
||||
Mickael Remond)
|
||||
|
||||
* src/mod_disco.erl: Fixed domain listing when one virtual host is
|
||||
a subdomain of another
|
||||
|
||||
2005-04-18 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* (all): ejabberd-0.9 released
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Added link to shared roster page
|
||||
|
||||
* src/odbc/ejabberd_odbc.erl: ODBC connection string can be
|
||||
specified via odbc_server option now
|
||||
* src/ejabberd.cfg.example: Added ODBC usage example
|
||||
|
||||
* doc/guide.tex: Updated
|
||||
|
||||
* src/msgs/pl.msg: New Polish translation (thanks to Andrew Smyk)
|
||||
|
||||
2005-04-17 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* (all): Merged virtual hosting support
|
||||
|
||||
2005-04-09 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Send new id for each new stream inside one
|
||||
session (thanks to Maxim Ryazanov)
|
||||
|
||||
* src/tls/tls_drv.c: Now reads all certificates from certificate
|
||||
file instead of reading only first one (thanks to Karl-Johan
|
||||
Karlsson)
|
||||
|
||||
2005-04-06 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* examples/transport-configs/init-scripts/jabber-gg-transport:
|
||||
Fixed typo (thanks to Sander Devrieze)
|
||||
|
||||
2005-04-05 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* examples/transport-configs/configs/msn-transport.xml: Fixed typo
|
||||
(thanks to Sander Devrieze)
|
||||
|
||||
2005-04-02 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* examples/transport-configs/: Updated (thanks to Sander Devrieze)
|
||||
|
||||
2005-03-31 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_ctl.erl: Bugfix
|
||||
|
||||
2005-03-17 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Bugfix
|
||||
|
||||
2005-03-15 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_sm.erl: Bugfix
|
||||
|
||||
2005-03-12 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_router.erl: Bugfix
|
||||
|
||||
2005-01-14 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_irc/mod_irc_connection.erl: Added filtering of quit
|
||||
status
|
||||
|
||||
2005-01-04 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Copyright update
|
||||
* src/mod_vcard_ldap.erl: Likewise
|
||||
* src/mod_vcard.erl: Likewise
|
||||
* src/mod_pubsub/mod_pubsub.erl: Likewise
|
||||
* src/mod_muc/mod_muc.erl: Likewise
|
||||
* src/mod_irc/mod_irc.erl: Likewise
|
||||
|
||||
2004-12-30 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/odbc/ejabberd_odbc.erl: Load-balance ODBC requests between
|
||||
several connections
|
||||
|
||||
* src/odbc/ejabberd_odbc_sup.erl: Supervisor for ODBC connections
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Added missed type='form' attribute
|
||||
in room configuration response (thanks to Badlop)
|
||||
|
||||
2004-12-19 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_roster_odbc.erl: Roster support via ODBC (not completed)
|
||||
|
||||
* src/ejabberd_auth_internal.erl: Added remove_user hook
|
||||
* src/ejabberd_auth_odbc.erl: Likewise
|
||||
* src/mod_roster.erl: Use remove_user hook
|
||||
* src/mod_offline.erl: Likewise
|
||||
* src/mod_offline_odbc.erl: Likewise
|
||||
* src/mod_last.erl: Likewise
|
||||
* src/mod_last_odbc.erl: Likewise
|
||||
* src/mod_vcard.erl: Likewise
|
||||
* src/mod_private.erl: Likewise
|
||||
|
||||
* src/mod_roster.erl: Added hooks for functions exported by
|
||||
mod_roster
|
||||
* src/ejabberd_c2s.erl: Likewise
|
||||
* src/ejabberd_sm.erl: Likewise
|
||||
* src/mod_privacy.erl: Likewise
|
||||
* src/mod_last.erl: Likewise
|
||||
* src/mod_last_odbc.erl: Likewise
|
||||
|
||||
2004-12-14 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_sm.erl: Updated missed message passing from
|
||||
previous patch
|
||||
|
||||
2004-12-13 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/odbc/pg.sql: DB creation script for postgres
|
||||
|
||||
* src/odbc/ejabberd_odbc.erl: Experimental support for ODBC
|
||||
* src/mod_last_odbc.erl: Likewise
|
||||
* src/mod_offline_odbc.erl: Likewise
|
||||
* src/ejabberd_auth_odbc.erl: Likewise
|
||||
* src/ejabberd_auth.erl: Likewise
|
||||
|
||||
2004-12-12 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_stats.erl: Minor optimizations
|
||||
|
||||
* src/ejabberd_sm.erl: Added unset_presence_hook
|
||||
* src/mod_last.erl: Use unset_presence_hook instead of direct call
|
||||
|
||||
* src/ejabberd_auth.erl: Splitted into ejabberd_auth_internal.erl,
|
||||
ejabberd_auth_ldap.erl, and ejabberd_auth_external.erl,
|
||||
* src/ejabberd_auth_internal.erl: Likewise
|
||||
* src/ejabberd_auth_ldap.erl: Likewise
|
||||
* src/ejabberd_auth_external.erl: Likewise
|
||||
|
||||
2004-12-05 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Changed type of password field
|
||||
to "password"
|
||||
|
||||
* src/jlib.hrl: More stream error defines (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/ejabberd_c2s.erl: Support for starttls_required option
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Fixed mistake in case condition
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/xml_stream.erl: Added function parse_element/1
|
||||
|
||||
* src/expat_erl.c: Added PARSE_FINAL_COMMAND
|
||||
|
||||
2004-12-03 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_listener.erl: Enable keepalive option
|
||||
|
||||
* src/xml_stream.erl: Added API for managing xml streams without
|
||||
creating process
|
||||
* src/ejabberd_receiver.erl: Use this API, now 2 processes are
|
||||
created per C2S connection
|
||||
|
||||
2004-12-01 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/expat_erl.c: Now uses port control instead of port output
|
||||
* src/xml_stream.erl: Likewise
|
||||
|
||||
2004-11-30 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/stringprep/stringprep.erl: Now register port instead of
|
||||
storing it in ets table
|
||||
|
||||
2004-11-28 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* doc/guide.tex: Updated URLs to R10C release
|
||||
|
||||
2004-11-20 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_vcard.erl: Added missed index
|
||||
|
||||
2004-11-08 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* doc/guide.tex: Updated (thanks to Sander Devrieze)
|
||||
|
||||
2004-11-05 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/aclocal.m4: Fixed headers detecting in AM_WITH_OPENSSL
|
||||
(thanks to Leif Johansson)
|
||||
|
||||
* src/ejabberd_auth.erl: Added support for ldap_rootdn and
|
||||
ldap_password options (thanks to Stefan de Konink)
|
||||
* src/mod_vcard_ldap.erl: Likewise
|
||||
|
||||
* src/ejabberd_router.erl: Now possible to route packet via
|
||||
function call instead of message sending
|
||||
* src/ejabberd_sm.erl: Added function route/3, use it in route
|
||||
table
|
||||
* src/ejabberd_local.erl: Likewise
|
||||
* src/ejabberd_s2s.erl: Likewise
|
||||
|
||||
2004-10-23 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* (all): Fixed spelling of word "authentication"
|
||||
|
||||
* src/*/Makefile.in: Replaced erlc with @ERLC@
|
||||
|
||||
2004-10-15 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_s2s.erl: Added remove_connection/1
|
||||
* src/ejabberd_s2s_out.erl: Use ejabberd_s2s:remove_connection/1
|
||||
|
||||
* src/ejabberd_s2s_in.erl: Minor cleanup
|
||||
|
||||
* examples/transport-configs/: Transport config examples (thanks
|
||||
to Sander Devrieze)
|
||||
|
||||
* src/msgs/de.msg: German translation (thanks to Marina Hahn)
|
||||
|
||||
2004-10-12 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/win32/: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/msgs/es.msg: Updated (thanks to Badlop)
|
||||
|
||||
* src/mod_irc/iconv_erl.c: Bugfix (thanks to Jacek Konieczny)
|
||||
|
||||
2004-10-10 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* (all): ejabberd-0.7.5 released
|
||||
|
||||
* src/tls/Makefile.win32: Added (thanks to Sergei Golovan)
|
||||
|
||||
* src/win32/: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2004-10-09 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Fixed user listing
|
||||
|
||||
* src/msgs/uk.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/msgs/nl.msg: Updated (thanks to Sander Devrieze)
|
||||
|
||||
* src/msgs/ua.msg: Renamed to uk.msg
|
||||
|
||||
* COPYING: Added permission to link with OpenSSL
|
||||
|
||||
2004-10-08 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/msgs/ua.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Fixed room destroying
|
||||
|
||||
* src/ejabberd.cfg.example: Updated
|
||||
|
||||
* src/ejabberd_sm.erl: Fixed message routing when all resources
|
||||
have negative priority
|
||||
|
||||
* src/msgs/*.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Table titles now bold (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
2004-10-06 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* doc/guide.tex: Updated
|
||||
|
||||
* src/ejabberd_s2s_out.erl: Fixed socket closing condition
|
||||
|
||||
2004-10-05 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_http_poll.erl: Properly handle bad requests
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Ported features from J-EAI
|
||||
|
||||
2004-09-30 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_http.erl: Fixed processing of POST body for
|
||||
HTTP Polling
|
||||
|
||||
* src/web/ejabberd_http.erl: Support for "Connection" HTTP header
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/translate.erl: Much better handling of xml:lang (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
2004-09-29 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_listener.erl: Check result of controlling_process
|
||||
|
||||
* src/web/ejabberd_http.erl: Bugfix
|
||||
|
||||
2004-09-27 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/Makefile.in: Updated (thanks to Badlop)
|
||||
|
||||
2004-09-26 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/aclocal.m4: Better expat and openssl detection (thanks to
|
||||
Anton Vanin)
|
||||
|
||||
* src/**/Makefile.in: Updated (thanks to Anton Vanin)
|
||||
* src/configure.ac: Likewise
|
||||
|
||||
2004-09-25 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/jlib.hrl: Added namespace for iq-register stream feature
|
||||
* src/ejabberd_c2s.erl: Send iq-register feature
|
||||
|
||||
* src/ejabberd_config.erl: Config file can be configured via
|
||||
environment variable (thanks to Mickael Remond)
|
||||
|
||||
* src/web/ejabberd_http.erl: Added SSL support (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/msgs/*.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/jlib.hrl: Updated error codes (thanks to Sergei Golovan)
|
||||
* src/ejabberd_c2s.erl: Likewise
|
||||
|
||||
2004-09-17 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Send password in room invitation
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_disco.erl: Added registration of sm features and nodes
|
||||
(thanks to Sergei Golovan)
|
||||
* src/mod_vcard.erl: Register vcard-temp feature (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/jlib.erl: Added functions now_to_utc_string/1,
|
||||
now_to_local_string/1, and datetime_string_to_timestamp/1 (thanks
|
||||
to Sergei Golovan)
|
||||
* src/mod_muc/mod_muc_room.erl: Use time parsing functions from
|
||||
jlib (thanks to Sergei Golovan)
|
||||
|
||||
2004-09-16 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* ejabberd/src/mod_pubsub/mod_pubsub.erl: Bugfix (thanks to
|
||||
Mickael Remond)
|
||||
|
||||
2004-09-15 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Bugfix
|
||||
|
||||
2004-09-10 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* tools/ejabberdctl: Added call to "exec" (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/msgs/ru.msg: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_vcard.erl: Support for searching of prefix substring and
|
||||
limiting of result items (thanks to Sergei Golovan)
|
||||
|
||||
* src/mod_offline.erl: Support for message expiration (JEP-0023)
|
||||
(thanks to Sergei Golovan)
|
||||
* src/jlib.hrl: Added NS_EXPIRE macros (thanks to Sergei Golovan)
|
||||
|
||||
* src/ejabberd_logger_h.erl: Added reopen_log/0 (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/ejabberd_ctl.erl: Added return codes, updated "reopen-log"
|
||||
command, added "delete-expired-messages" and "status" commands
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* doc/guide.tex: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2004-09-04 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_roster.erl: Removed useless transactions
|
||||
|
||||
2004-08-28 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* doc/guide.tex: Fix (thanks to Sander Devrieze)
|
||||
|
||||
2004-08-27 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/xml_stream.erl: Few optimizations
|
||||
|
||||
2004-08-24 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_service_log.erl: Support for logging of user packets via
|
||||
external service (e.g. bandersnatch)
|
||||
* doc/guide.tex: Updated
|
||||
|
||||
2004-08-23 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_offline.erl: Added entire table locking on large message
|
||||
queue
|
||||
|
||||
* src/ejabberd_sm.erl: Added offline_subscription_hook
|
||||
* src/mod_offline.erl: Use offline_subscription_hook
|
||||
|
||||
* src/configure.erl: Updated (thanks to Sergei Golovan)
|
||||
* src/Makefile.win32: Likewise
|
||||
* src/tls/Makefile.win32: Likewise
|
||||
* src/win32/: Likewise
|
||||
|
||||
* src/mod_announce.erl: Added announce to all users (thanks to
|
||||
Sergei Golovan)
|
||||
* doc/guide.tex: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2004-08-14 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/msgs/nl.msg: Updated (thanks to Sander Devrieze)
|
||||
|
||||
* src/web/ejabberd_http_poll.erl: Fixed sending of Set-Cookie
|
||||
header
|
||||
|
||||
2004-08-12 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Bugfix in resend_offline_messages/1
|
||||
|
||||
* src/mod_announce.erl: New module to manage announce messages
|
||||
(thanks to Sergei Golovan)
|
||||
|
||||
* src/ejabberd_local.erl: Moved processing of announce messages to
|
||||
mod_announce (thanks to Sergei Golovan)
|
||||
|
||||
* src/ejabberd_c2s.erl: Added several hooks
|
||||
|
||||
* src/ejabberd_hooks.erl: Fixed run_fold (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
* src/ejabberd.cfg.example: Updated (thanks to Sergei Golovan)
|
||||
|
||||
* doc/guide.tex: Updated (thanks to Sergei Golovan)
|
||||
|
||||
2004-08-08 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Use resend_offline_messages_hook to fetch
|
||||
offline messages
|
||||
* src/mod_offline.erl: Likewise
|
||||
|
||||
* src/mod_offline.erl: Added table locking in
|
||||
remove_old_messages/1
|
||||
|
||||
* src/ejabberd_sm.erl: Use offline_message_hook to store offline
|
||||
messages
|
||||
* src/mod_offline.erl: Likewise
|
||||
|
||||
* src/ejabberd_hooks.erl: Hooks support
|
||||
* src/ejabberd_sup.erl: Added ejabberd_hooks
|
||||
|
||||
* doc/guide.tex: Updated
|
||||
|
||||
* src/ejabberd.cfg.example: Updated
|
||||
|
||||
* src/ejabberd_c2s.erl: Changed TLS options (thanks to Sergei
|
||||
Golovan)
|
||||
|
||||
2004-08-05 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/aclocal.m4: Updated to check for openssl library (thanks to
|
||||
AV)
|
||||
* src/configure.ac: Likewise
|
||||
* src/configure: Likewise
|
||||
* src/Makefile.in: Likewise
|
||||
* src/tls/Makefile.in: Likewise
|
||||
|
||||
2004-08-03 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Added user's roster page
|
||||
|
||||
* src/mod_irc/mod_irc_connection.erl: Bugfix
|
||||
|
||||
2004-08-01 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/tls/tls.erl: Added recv_data/2 function
|
||||
|
||||
* src/jlib.erl: Added NS_TLS macro
|
||||
|
||||
* src/ejabberd_receiver.erl: Support for STARTTLS
|
||||
* src/ejabberd_c2s.erl: Likewise
|
||||
|
||||
2004-07-30 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* examples/extauth/check_pass_null.pl: A reference "null"
|
||||
implementation of external authentification script (thanks to Leif
|
||||
Johansson)
|
||||
|
||||
* src/extauth.erl: Support for external authentification
|
||||
(thanks to Leif Johansson)
|
||||
* src/ejabberd_auth.erl: Likewise
|
||||
|
||||
* src/mod_vcard_ldap.erl: A drop-in replacement for mod_vcard.erl
|
||||
which uses ldap for JUD and vCard (thanks to Leif Johansson)
|
||||
|
||||
2004-07-28 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/tls/tls_drv.c: Added freeing of SSL stuff
|
||||
|
||||
* src/xml_stream.erl: Added start/2 function
|
||||
* src/ejabberd_receiver.erl: Now using xml_stream:start/2
|
||||
|
||||
2004-07-27 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/ejabberd_c2s.erl: Support for TLS library (not completed)
|
||||
|
||||
* src/tls/tls_drv.c: Updated to return binaries instead of lists
|
||||
* src/tls/tls.erl: Likewise
|
||||
|
||||
2004-07-26 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/tls/tls.erl: Updated
|
||||
|
||||
2004-07-25 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/tls/: Library for TLS support (not completed)
|
||||
|
||||
* src/ejabberd_auth.erl: Now uses two LDAP connections
|
||||
|
||||
* src/ejabberd_c2s.erl: Return resource on get_presence request
|
||||
(thanks to Mickael Remond)
|
||||
|
||||
* src/mod_configure2.erl: Bugfix (thanks to Sergei Golovan)
|
||||
|
||||
* src/msgs/ua.msg: New Ukrainian translation (thanks to usercard)
|
||||
|
||||
* src/msgs/nl.msg: Updated (thanks to Sander Devrieze)
|
||||
|
||||
2004-07-23 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/eldap/eldap.erl: Bugfix
|
||||
|
||||
2004-07-13 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* (all): ejabberd-0.7 released
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Better i18n support (thanks to
|
||||
Sergei Golovan)
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
extract_translations - auxiliary tool that extracts lines to be translated
|
||||
from ejabberd source tree.
|
||||
|
||||
Building:
|
||||
erlc extract_translations.erl
|
||||
|
||||
Invoking 1:
|
||||
erl -noinput -s extract_translations -extra dirname message_file
|
||||
|
||||
where dirname is the directory "src" in ejabberd's source tree root,
|
||||
message_file is a file with translated messages (src/msgs/*.msg).
|
||||
|
||||
Result is a list of messages from source files which aren't contained in
|
||||
message file.
|
||||
|
||||
Invoking 2:
|
||||
erl -noinput -s extract_translations -extra -unused dirname message_file
|
||||
|
||||
Result is a list of messages from message file which aren't in source
|
||||
files anymore.
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : extract_translations.erl
|
||||
%%% Author : Sergei Golovan <sgolovan@nes.ru>
|
||||
%%% Purpose : Auxiliary tool for interface/messages translators
|
||||
%%% Created : 23 Apr 2005 by Sergei Golovan <sgolovan@nes.ru>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(extract_translations).
|
||||
-author('sgolovan@nes.ru').
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
-define(STATUS_SUCCESS, 0).
|
||||
-define(STATUS_ERROR, 1).
|
||||
-define(STATUS_USAGE, 2).
|
||||
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
|
||||
start() ->
|
||||
ets:new(translations, [named_table, public]),
|
||||
ets:new(files, [named_table, public]),
|
||||
ets:new(vars, [named_table, public]),
|
||||
case init:get_plain_arguments() of
|
||||
["-unused", Dir, File] ->
|
||||
Status = process(Dir, File, unused),
|
||||
halt(Status);
|
||||
[Dir, File] ->
|
||||
Status = process(Dir, File, used),
|
||||
halt(Status);
|
||||
_ ->
|
||||
print_usage(),
|
||||
halt(?STATUS_USAGE)
|
||||
end.
|
||||
|
||||
|
||||
process(Dir, File, Used) ->
|
||||
case load_file(File) of
|
||||
{error, Reason} ->
|
||||
io:format("~s: ~s~n", [File, file:format_error(Reason)]),
|
||||
?STATUS_ERROR;
|
||||
_ ->
|
||||
FileList = find_src_files(Dir),
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_file(Dir, F, Used)
|
||||
end, FileList),
|
||||
case Used of
|
||||
unused ->
|
||||
ets:foldl(fun({Key, _}, _) ->
|
||||
io:format("~p~n", [Key])
|
||||
end, ok, translations);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
?STATUS_SUCCESS
|
||||
end.
|
||||
|
||||
parse_file(Dir, File, Used) ->
|
||||
ets:delete_all_objects(vars),
|
||||
case epp:parse_file(File, [Dir, filename:dirname(File)], []) of
|
||||
{ok, Forms} ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, Forms);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
parse_form(Dir, File, Form, Used) ->
|
||||
case Form of
|
||||
{call,
|
||||
_,
|
||||
{remote, _, {atom, _, translate}, {atom, _, translate}},
|
||||
[_, {string, _, Str}]
|
||||
} ->
|
||||
process_string(Dir, File, Str, Used);
|
||||
{call,
|
||||
_,
|
||||
{remote, _, {atom, _, translate}, {atom, _, translate}},
|
||||
[_, {var, _, Name}]
|
||||
} ->
|
||||
case ets:lookup(vars, Name) of
|
||||
[{_Name, Value}] ->
|
||||
process_string(Dir, File, Value, Used);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
{match,
|
||||
_,
|
||||
{var, _, Name},
|
||||
{string, _, Value}
|
||||
} ->
|
||||
ets:insert(vars, {Name, Value});
|
||||
L when is_list(L) ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, L);
|
||||
T when is_tuple(T) ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, tuple_to_list(T));
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
process_string(Dir, File, Str, Used) ->
|
||||
case {ets:lookup(translations, Str), Used} of
|
||||
{[{_Key, _Trans}], unused} ->
|
||||
ets:delete(translations, Str);
|
||||
{[{_Key, _Trans}], used} ->
|
||||
ok;
|
||||
{_, used} ->
|
||||
case ets:lookup(files, File) of
|
||||
[{_}] ->
|
||||
ok;
|
||||
_ ->
|
||||
io:format("~n% ~s~n", [File]),
|
||||
ets:insert(files, {File})
|
||||
end,
|
||||
io:format("{~p, \"\"}.~n", [Str]),
|
||||
ets:insert(translations, {Str, ""});
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
load_file(File) ->
|
||||
case file:consult(File) of
|
||||
{ok, Terms} ->
|
||||
lists:foreach(
|
||||
fun({Orig, Trans}) ->
|
||||
case Trans of
|
||||
"" ->
|
||||
ok;
|
||||
_ ->
|
||||
ets:insert(translations, {Orig, Trans})
|
||||
end
|
||||
end, Terms);
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
find_src_files(Dir) ->
|
||||
case file:list_dir(Dir) of
|
||||
{ok, FileList} ->
|
||||
recurse_filelist(
|
||||
lists:map(
|
||||
fun(F) ->
|
||||
filename:join(Dir, F)
|
||||
end, FileList));
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
recurse_filelist(FileList) ->
|
||||
recurse_filelist(FileList, []).
|
||||
|
||||
recurse_filelist([], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
|
||||
recurse_filelist([H | T], Acc) ->
|
||||
case file:read_file_info(H) of
|
||||
{ok, #file_info{type = directory}} ->
|
||||
recurse_filelist(T, lists:reverse(find_src_files(H)) ++ Acc);
|
||||
{ok, #file_info{type = regular}} ->
|
||||
case string:substr(H, string:len(H) - 3) of
|
||||
".erl" ->
|
||||
recurse_filelist(T, [H | Acc]);
|
||||
".hrl" ->
|
||||
recurse_filelist(T, [H | Acc]);
|
||||
_ ->
|
||||
recurse_filelist(T, Acc)
|
||||
end;
|
||||
_ ->
|
||||
recurse_filelist(T, Acc)
|
||||
end.
|
||||
|
||||
|
||||
print_usage() ->
|
||||
io:format(
|
||||
"Usage: extract_translations [-unused] dir file~n"
|
||||
"~n"
|
||||
"Example:~n"
|
||||
" extract_translations . ./msgs/ru.msg~n"
|
||||
).
|
||||
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
@@ -61,38 +61,34 @@
|
||||
|
||||
<A NAME="sec:intro"></A>
|
||||
<TT>ejabberd</TT> is a Free and Open Source fault-tolerant distributed Jabber
|
||||
server. It is written mostly in Erlang.<BR>
|
||||
server. It is writen mostly in Erlang.<BR>
|
||||
<BR>
|
||||
The main features of <TT>ejabberd</TT> are:
|
||||
The main features of <TT>ejabberd</TT> is:
|
||||
<UL><LI>
|
||||
Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
|
||||
NetBSD) and Win32
|
||||
<LI>Distributed: You can run <TT>ejabberd</TT> on a cluster of machines to let all of
|
||||
them serve one Jabber domain.
|
||||
Works on most of popular platforms: *nix (tested on Linux and FreeBSD)
|
||||
and Win32
|
||||
<LI>Distributed: You can run <TT>ejabberd</TT> on a cluster of machines and all of
|
||||
them will serve one Jabber domain.
|
||||
<LI>Fault-tolerance: You can setup an <TT>ejabberd</TT> cluster so that all the
|
||||
information required for a properly working service will be stored
|
||||
permanently on more than one node. This means that if one of the nodes
|
||||
crashes, then the others will continue working without disruption.
|
||||
You can also add or replace nodes ``on the fly''.
|
||||
<LI>Support for virtual hosting
|
||||
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User Chat</A> service
|
||||
You can also add or replace more nodes ``on the fly''.
|
||||
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User
|
||||
Chat</A> service
|
||||
<LI>Built-in IRC transport
|
||||
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A> service
|
||||
<LI>Built-in
|
||||
<A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A>
|
||||
service
|
||||
<LI>Built-in Jabber Users Directory service based on users vCards
|
||||
<LI>Built-in web-based administration interface
|
||||
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> service
|
||||
<LI>SSL support
|
||||
<LI>Support for LDAP authentication
|
||||
<LI>Ability to interface with external components (JIT, MSN-t, Yahoo-t, etc.)
|
||||
<LI>Migration from jabberd14 is possible
|
||||
<LI>Mostly XMPP-compliant
|
||||
<LI>Support for <A HREF="http://www.jabber.org/jeps/jep-0030.html">Service Discovery</A>.
|
||||
<LI>Support for <A HREF="http://www.jabber.org/jeps/jep-0039.html">Statistics Gathering</A>.
|
||||
<LI>Support for <TT>xml:lang</TT>
|
||||
<LI>Support for
|
||||
<A HREF="http://www.jabber.org/jeps/jep-0030.html">JEP-0030</A>
|
||||
(Service Discovery).
|
||||
<LI>Support for
|
||||
<A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A>
|
||||
(Statistics Gathering).
|
||||
<LI>Support for <TT>xml:lang</TT> attribute in many XML elements
|
||||
</UL>
|
||||
<TT>ejabberd</TT> is a Free and Open Source fault-tolerant distributed Jabber
|
||||
server. It is written mostly in Erlang.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection How it works-->
|
||||
|
||||
<H3><A NAME="htoc2">1.1</A> How it works</H3><!--SEC END -->
|
||||
@@ -170,13 +166,13 @@ XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
||||
CDATA = {xmlcdata, string()}
|
||||
</PRE>E. g. this stanza:
|
||||
<PRE>
|
||||
<message to='test@conference.example.org' type='groupchat'>
|
||||
<message to='test@conference.e.localhost' type='groupchat'>
|
||||
<body>test</body>
|
||||
</message>
|
||||
</PRE>represented as following structure:
|
||||
<PRE>
|
||||
{xmlelement, "message",
|
||||
[{"to", "test@conference.example.org"},
|
||||
[{"to", "test@conference.e.localhost"},
|
||||
{"type", "groupchat"}],
|
||||
[{xmlelement, "body",
|
||||
[],
|
||||
|
||||
@@ -31,9 +31,7 @@
|
||||
\newcommand{\modprivate}{\texttt{mod\_private}}
|
||||
\newcommand{\modtime}{\texttt{mod\_time}}
|
||||
\newcommand{\modversion}{\texttt{mod\_version}}
|
||||
|
||||
\newcommand{\tjepref}[2]{\footahref{http://www.jabber.org/jeps/jep-#1.html}{#2}}
|
||||
\newcommand{\jepref}[1]{\tjepref{#1}{JEP-#1}}
|
||||
c
|
||||
%\setcounter{tocdepth}{3}
|
||||
|
||||
|
||||
@@ -60,37 +58,36 @@
|
||||
\label{sec:intro}
|
||||
|
||||
\ejabberd{} is a Free and Open Source fault-tolerant distributed \Jabber{}
|
||||
server. It is written mostly in Erlang.
|
||||
server. It is writen mostly in Erlang.
|
||||
|
||||
The main features of \ejabberd{} are:
|
||||
The main features of \ejabberd{} is:
|
||||
\begin{itemize}
|
||||
\item Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
|
||||
NetBSD) and Win32
|
||||
\item Distributed: You can run \ejabberd{} on a cluster of machines to let all of
|
||||
them serve one Jabber domain.
|
||||
\item Works on most of popular platforms: *nix (tested on Linux and FreeBSD)
|
||||
and Win32
|
||||
\item Distributed: You can run \ejabberd{} on a cluster of machines and all of
|
||||
them will serve one Jabber domain.
|
||||
\item Fault-tolerance: You can setup an \ejabberd{} cluster so that all the
|
||||
information required for a properly working service will be stored
|
||||
permanently on more than one node. This means that if one of the nodes
|
||||
crashes, then the others will continue working without disruption.
|
||||
You can also add or replace nodes ``on the fly''.
|
||||
\item Support for virtual hosting
|
||||
\item Built-in \tjepref{0045}{Multi-User Chat} service
|
||||
You can also add or replace more nodes ``on the fly''.
|
||||
\item Built-in \footahref{http://www.jabber.org/jeps/jep-0045.html}{Multi-User
|
||||
Chat} service
|
||||
\item Built-in IRC transport
|
||||
\item Built-in \tjepref{0060}{Publish-Subscribe} service
|
||||
\item Built-in
|
||||
\footahref{http://www.jabber.org/jeps/jep-0060.html}{Publish-Subscribe}
|
||||
service
|
||||
\item Built-in Jabber Users Directory service based on users vCards
|
||||
\item Built-in web-based administration interface
|
||||
\item Built-in \tjepref{0025}{HTTP Polling} service
|
||||
\item SSL support
|
||||
\item Support for LDAP authentication
|
||||
\item Ability to interface with external components (JIT, MSN-t, Yahoo-t, etc.)
|
||||
\item Migration from jabberd14 is possible
|
||||
\item Mostly XMPP-compliant
|
||||
\item Support for \tjepref{0030}{Service Discovery}.
|
||||
\item Support for \tjepref{0039}{Statistics Gathering}.
|
||||
\item Support for \ns{xml:lang}
|
||||
\item Support for
|
||||
\footahref{http://www.jabber.org/jeps/jep-0030.html}{JEP-0030}
|
||||
(Service Discovery).
|
||||
\item Support for
|
||||
\footahref{http://www.jabber.org/jeps/jep-0039.html}{JEP-0039}
|
||||
(Statistics Gathering).
|
||||
\item Support for \ns{xml:lang} attribute in many XML elements
|
||||
\end{itemize}
|
||||
\ejabberd{} is a Free and Open Source fault-tolerant distributed \Jabber{}
|
||||
server. It is written mostly in Erlang.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -98,6 +95,7 @@ server. It is written mostly in Erlang.
|
||||
\label{sec:howitworks}
|
||||
|
||||
|
||||
|
||||
A \Jabber{} domain is served by one or more \ejabberd{} nodes. These nodes can
|
||||
be run on different machines that are connected via a network. They all must
|
||||
have the ability to connect to port 4369 of all another nodes, and must have
|
||||
@@ -171,14 +169,14 @@ XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
|
||||
\end{verbatim}
|
||||
E.\,g. this stanza:
|
||||
\begin{verbatim}
|
||||
<message to='test@conference.example.org' type='groupchat'>
|
||||
<message to='test@conference.e.localhost' type='groupchat'>
|
||||
<body>test</body>
|
||||
</message>
|
||||
\end{verbatim}
|
||||
represented as following structure:
|
||||
\begin{verbatim}
|
||||
{xmlelement, "message",
|
||||
[{"to", "test@conference.example.org"},
|
||||
[{"to", "test@conference.e.localhost"},
|
||||
{"type", "groupchat"}],
|
||||
[{xmlelement, "body",
|
||||
[],
|
||||
|
||||
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 7.8 KiB |
@@ -21,7 +21,7 @@
|
||||
<A HREF="mailto:alexey@sevcom.net"><TT>mailto:alexey@sevcom.net</TT></A><BR>
|
||||
<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A></H3>
|
||||
|
||||
<H3 ALIGN=center>May 23, 2005</H3><DIV ALIGN=center>
|
||||
<H3 ALIGN=center>June 24, 2004</H3><DIV ALIGN=center>
|
||||
|
||||
<IMG SRC="logo.png">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<UL><LI>
|
||||
<A HREF="#htoc1">1 Introduction</A>
|
||||
<LI><A HREF="#htoc2">2 Installation from Source</A>
|
||||
<LI><A HREF="#htoc2">2 Installation</A>
|
||||
<UL><LI>
|
||||
<A HREF="#htoc3">2.1 Installation Requirements</A>
|
||||
<UL><LI>
|
||||
@@ -55,7 +55,7 @@
|
||||
<UL><LI>
|
||||
<A HREF="#htoc12">3.1 Initial Configuration</A>
|
||||
<UL><LI>
|
||||
<A HREF="#htoc13">3.1.1 Host Names</A>
|
||||
<A HREF="#htoc13">3.1.1 Host Name</A>
|
||||
<LI><A HREF="#htoc14">3.1.2 Default Language</A>
|
||||
<LI><A HREF="#htoc15">3.1.3 Access Rules</A>
|
||||
<LI><A HREF="#htoc16">3.1.4 Shapers Configuration</A>
|
||||
@@ -68,7 +68,7 @@
|
||||
<LI><A HREF="#htoc21">3.2.2 <TT>ejabberdctl</TT> tool</A>
|
||||
</UL>
|
||||
</UL>
|
||||
<LI><A HREF="#htoc22">4 Clustering</A>
|
||||
<LI><A HREF="#htoc22">4 Distribution</A>
|
||||
<UL><LI>
|
||||
<A HREF="#htoc23">4.1 How it works</A>
|
||||
<UL><LI>
|
||||
@@ -77,37 +77,32 @@
|
||||
<LI><A HREF="#htoc26">4.1.3 Session Manager</A>
|
||||
<LI><A HREF="#htoc27">4.1.4 S2S Manager</A>
|
||||
</UL>
|
||||
<LI><A HREF="#htoc28">4.2 How to setup ejabberd cluster</A>
|
||||
</UL>
|
||||
<LI><A HREF="#htoc29">A Built-in Modules</A>
|
||||
<LI><A HREF="#htoc28">A Built-in Modules</A>
|
||||
<UL><LI>
|
||||
<A HREF="#htoc30">A.1 Common Options</A>
|
||||
<A HREF="#htoc29">A.1 Common Options</A>
|
||||
<UL><LI>
|
||||
<A HREF="#htoc31">A.1.1 <TT>iqdisc</TT></A>
|
||||
<LI><A HREF="#htoc32">A.1.2 <TT>host</TT></A>
|
||||
<LI><A HREF="#htoc33">A.1.3 <TT>hosts</TT></A>
|
||||
<A HREF="#htoc30">A.1.1 Option <TT>iqdisc</TT></A>
|
||||
<LI><A HREF="#htoc31">A.1.2 Option <TT>host</TT></A>
|
||||
</UL>
|
||||
<LI><A HREF="#htoc34">A.2 <TT>mod_announce</TT></A>
|
||||
<LI><A HREF="#htoc35">A.3 <TT>mod_configure</TT></A>
|
||||
<LI><A HREF="#htoc36">A.4 <TT>mod_disco</TT></A>
|
||||
<LI><A HREF="#htoc37">A.5 <TT>mod_echo</TT></A>
|
||||
<LI><A HREF="#htoc38">A.6 <TT>mod_irc</TT></A>
|
||||
<LI><A HREF="#htoc39">A.7 <TT>mod_last</TT></A>
|
||||
<LI><A HREF="#htoc40">A.8 <TT>mod_muc</TT></A>
|
||||
<LI><A HREF="#htoc41">A.9 <TT>mod_offline</TT></A>
|
||||
<LI><A HREF="#htoc42">A.10 <TT>mod_privacy</TT></A>
|
||||
<LI><A HREF="#htoc43">A.11 <TT>mod_private</TT></A>
|
||||
<LI><A HREF="#htoc44">A.12 <TT>mod_pubsub</TT></A>
|
||||
<LI><A HREF="#htoc45">A.13 <TT>mod_register</TT></A>
|
||||
<LI><A HREF="#htoc46">A.14 <TT>mod_roster</TT></A>
|
||||
<LI><A HREF="#htoc47">A.15 <TT>mod_service_log</TT></A>
|
||||
<LI><A HREF="#htoc48">A.16 <TT>mod_shared_roster</TT></A>
|
||||
<LI><A HREF="#htoc49">A.17 <TT>mod_stats</TT></A>
|
||||
<LI><A HREF="#htoc50">A.18 <TT>mod_time</TT></A>
|
||||
<LI><A HREF="#htoc51">A.19 <TT>mod_vcard</TT></A>
|
||||
<LI><A HREF="#htoc52">A.20 <TT>mod_version</TT></A>
|
||||
<LI><A HREF="#htoc32">A.2 <TT>mod_configure</TT></A>
|
||||
<LI><A HREF="#htoc33">A.3 <TT>mod_disco</TT></A>
|
||||
<LI><A HREF="#htoc34">A.4 <TT>mod_echo</TT></A>
|
||||
<LI><A HREF="#htoc35">A.5 <TT>mod_irc</TT></A>
|
||||
<LI><A HREF="#htoc36">A.6 <TT>mod_last</TT></A>
|
||||
<LI><A HREF="#htoc37">A.7 <TT>mod_muc</TT></A>
|
||||
<LI><A HREF="#htoc38">A.8 <TT>mod_offline</TT></A>
|
||||
<LI><A HREF="#htoc39">A.9 <TT>mod_privacy</TT></A>
|
||||
<LI><A HREF="#htoc40">A.10 <TT>mod_private</TT></A>
|
||||
<LI><A HREF="#htoc41">A.11 <TT>mod_pubsub</TT></A>
|
||||
<LI><A HREF="#htoc42">A.12 <TT>mod_register</TT></A>
|
||||
<LI><A HREF="#htoc43">A.13 <TT>mod_roster</TT></A>
|
||||
<LI><A HREF="#htoc44">A.14 <TT>mod_stats</TT></A>
|
||||
<LI><A HREF="#htoc45">A.15 <TT>mod_time</TT></A>
|
||||
<LI><A HREF="#htoc46">A.16 <TT>mod_vcard</TT></A>
|
||||
<LI><A HREF="#htoc47">A.17 <TT>mod_version</TT></A>
|
||||
</UL>
|
||||
<LI><A HREF="#htoc53">B I18n/L10n</A>
|
||||
<LI><A HREF="#htoc48">B I18n/L10n</A>
|
||||
</UL>
|
||||
|
||||
<!--TOC section Introduction-->
|
||||
@@ -129,7 +124,6 @@ Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
|
||||
permanently on more than one node. This means that if one of the nodes
|
||||
crashes, then the others will continue working without disruption.
|
||||
You can also add or replace nodes ``on the fly''.
|
||||
<LI>Support for virtual hosting
|
||||
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0045.html">Multi-User Chat</A> service
|
||||
<LI>Built-in IRC transport
|
||||
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A> service
|
||||
@@ -137,23 +131,22 @@ Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
|
||||
<LI>Built-in web-based administration interface
|
||||
<LI>Built-in <A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> service
|
||||
<LI>SSL support
|
||||
<LI>Support for LDAP authentication
|
||||
<LI>Support for LDAP authentification
|
||||
<LI>Ability to interface with external components (JIT, MSN-t, Yahoo-t, etc.)
|
||||
<LI>Migration from jabberd14 is possible
|
||||
<LI>Mostly XMPP-compliant
|
||||
<LI>Support for <A HREF="http://www.jabber.org/jeps/jep-0030.html">Service Discovery</A>.
|
||||
<LI>Support for <A HREF="http://www.jabber.org/jeps/jep-0039.html">Statistics Gathering</A>.
|
||||
<LI>Support for <A HREF="http://www.jabber.org/jeps/jep-0030.html">JEP-0030</A> (Service Discovery).
|
||||
<LI>Support for <A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A> (Statistics Gathering).
|
||||
<LI>Support for <TT>xml:lang</TT>
|
||||
</UL>
|
||||
The misfeatures of <TT>ejabberd</TT> are:
|
||||
<UL><LI>
|
||||
No support for authentication and STARTTLS in S2S connections
|
||||
<LI>Access rules can be defined only for global conext, not for specific
|
||||
virtual host
|
||||
No support for virtual domains
|
||||
<LI>No support for STARTTLS
|
||||
</UL>
|
||||
<!--TOC section Installation from Source-->
|
||||
<!--TOC section Installation-->
|
||||
|
||||
<H2><A NAME="htoc2">2</A> Installation from Source</H2><!--SEC END -->
|
||||
<H2><A NAME="htoc2">2</A> Installation</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:installation"></A>
|
||||
<!--TOC subsection Installation Requirements-->
|
||||
@@ -171,8 +164,7 @@ To compile <TT>ejabberd</TT>, you will need the following packages:
|
||||
GNU Make;
|
||||
<LI>GCC;
|
||||
<LI>libexpat 1.95 or later;
|
||||
<LI>Erlang/OTP R8B or later;
|
||||
<LI>OpenSSL 0.9.6 or later (optional).
|
||||
<LI>Erlang/OTP R8B or later.
|
||||
</UL>
|
||||
<!--TOC subsubsection Windows-->
|
||||
|
||||
@@ -183,7 +175,7 @@ To compile <TT>ejabberd</TT> in MS Windows environment, you will need the follow
|
||||
packages:
|
||||
<UL><LI>
|
||||
MS Visual C++ 6.0 Compiler
|
||||
<LI><A HREF="http://erlang.org/download/otp_win32_R10B-1a.exe">Erlang/OTP R10B-1a</A>
|
||||
<LI><A HREF="http://www.erlang.org/download/otp_win32_R9C-0.exe">Erlang/OTP R9C-0</A>
|
||||
<LI><A HREF="http://prdownloads.sourceforge.net/expat/expat_win32bin_1_95_7.exe?download">Expat 1.95.7</A>
|
||||
<LI><A HREF="http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.9.1.tar.gz">Iconv 1.9.1</A>
|
||||
(optional)
|
||||
@@ -196,11 +188,14 @@ MS Visual C++ 6.0 Compiler
|
||||
|
||||
<A NAME="sec:obtaining"></A>
|
||||
Stable <TT>ejabberd</TT> release can be obtained at
|
||||
<A HREF="http://www.process-one.net/en/projects/ejabberd/download.html"><TT>http://www.process-one.net/en/projects/ejabberd/download.html</TT></A>.<BR>
|
||||
<A HREF="http://www.jabberstudio.org/projects/ejabberd/releases/"><TT>http://www.jabberstudio.org/projects/ejabberd/releases/</TT></A>.<BR>
|
||||
<BR>
|
||||
The latest alpha version can be retrieved from Subversion repository.
|
||||
The latest alpha version can be retrieved from CVS.
|
||||
<PRE>
|
||||
svn co svn://svn.process-one.net/opt/data/svn/ejabberd/trunk ejabberd
|
||||
export CVSROOT=:pserver:anonymous@jabberstudio.org:/home/cvs
|
||||
cvs login
|
||||
<press Enter when asked for a password>
|
||||
cvs -z3 co ejabberd
|
||||
</PRE>
|
||||
<!--TOC subsection Compilation-->
|
||||
|
||||
@@ -325,25 +320,15 @@ in config:
|
||||
</PRE>With this lines old global or local options or ACLs will be removed before
|
||||
adding new ones.<BR>
|
||||
<BR>
|
||||
<!--TOC subsubsection Host Names-->
|
||||
<!--TOC subsubsection Host Name-->
|
||||
|
||||
<H4><A NAME="htoc13">3.1.1</A> Host Names</H4><!--SEC END -->
|
||||
<H4><A NAME="htoc13">3.1.1</A> Host Name</H4><!--SEC END -->
|
||||
|
||||
<A NAME="sec:confighostname"></A>
|
||||
Option <TT>hosts</TT> defines a list of Jabber domains that <TT>ejabberd</TT>
|
||||
serves. E. g. to serve <TT>example.org</TT> and <TT>example.com</TT> domains add
|
||||
the following line in the config:
|
||||
Option <TT>hostname</TT> defines name of Jabber domain that <TT>ejabberd</TT>
|
||||
serves. E. g. to use <TT>jabber.org</TT> domain add the following line in the config:
|
||||
<PRE>
|
||||
{hosts, ["example.org", "example.com"]}.
|
||||
</PRE>
|
||||
Option <TT>host</TT> defines one Jabber domain that <TT>ejabberd</TT> serves.
|
||||
E. g. to serve only <TT>example.org</TT> domain add the following line in the
|
||||
config:
|
||||
<PRE>
|
||||
{host, "example.org"}.
|
||||
</PRE>It have the same effect as
|
||||
<PRE>
|
||||
{hosts, ["example.org"]}.
|
||||
{host, "jabber.org"}.
|
||||
</PRE>
|
||||
<!--TOC subsubsection Default Language-->
|
||||
|
||||
@@ -372,8 +357,8 @@ declarations of ACL in config file have following syntax:
|
||||
<B><TT>all</TT></B><DD> Matches all JIDs. Example:
|
||||
<PRE>
|
||||
{acl, all, all}.
|
||||
</PRE><DT><B><TT>{user, <username>}</TT></B><DD> Matches user with name
|
||||
<TT><username></TT> at the first virtual host. Example:
|
||||
</PRE><DT><B><TT>{user, <username>}</TT></B><DD> Matches local user with name
|
||||
<TT><username></TT>. Example:
|
||||
<PRE>
|
||||
{acl, admin, {user, "aleksey"}}.
|
||||
</PRE><DT><B><TT>{user, <username>, <server>}</TT></B><DD> Matches user with JID
|
||||
@@ -385,13 +370,13 @@ declarations of ACL in config file have following syntax:
|
||||
<PRE>
|
||||
{acl, jabberorg, {server, "jabber.org"}}.
|
||||
</PRE><DT><B><TT>{user_regexp, <regexp>}</TT></B><DD> Matches local user with name that
|
||||
matches <TT><regexp></TT> at the first virtual host. Example:
|
||||
matches <TT><regexp></TT>. Example:
|
||||
<PRE>
|
||||
{acl, tests, {user, "^test[0-9]*$"}}.
|
||||
</PRE><DT><B><TT>{user_regexp, <regexp>, <server>}</TT></B><DD> Matches user with name
|
||||
that matches <TT><regexp></TT> and from server <TT><server></TT>. Example:
|
||||
<PRE>
|
||||
{acl, tests, {user, "^test", "example.org"}}.
|
||||
{acl, tests, {user, "^test", "localhost"}}.
|
||||
</PRE><DT><B><TT>{server_regexp, <regexp>}</TT></B><DD> Matches any JID from server that
|
||||
matches <TT><regexp></TT>. Example:
|
||||
<PRE>
|
||||
@@ -416,13 +401,12 @@ declarations of ACL in config file have following syntax:
|
||||
character not enclosed is matched.
|
||||
</DL>
|
||||
</DL>
|
||||
The following ACLs are pre-defined:
|
||||
The following ACLs pre-defined:
|
||||
<DL COMPACT=compact><DT>
|
||||
<B><TT>all</TT></B><DD> Matches all JIDs.
|
||||
<DT><B><TT>none</TT></B><DD> Matches none JIDs.
|
||||
</DL>
|
||||
An entry allowing or denying access to different services would look similar to
|
||||
this:
|
||||
An entry allowing or denying different services would look similar to this:
|
||||
<PRE>
|
||||
{access, <accessname>, [{allow, <aclname>},
|
||||
{deny, <aclname>},
|
||||
@@ -441,8 +425,8 @@ Example:
|
||||
</PRE>
|
||||
Following access rules pre-defined:
|
||||
<DL COMPACT=compact><DT>
|
||||
<B><TT>all</TT></B><DD> Always returns ``<TT>allow</TT>''
|
||||
<DT><B><TT>none</TT></B><DD> Always returns ``<TT>deny</TT>''
|
||||
<B><TT>all</TT></B><DD> Always return ``<TT>allow</TT>''
|
||||
<DT><B><TT>none</TT></B><DD> Always return ``<TT>deny</TT>''
|
||||
</DL>
|
||||
<!--TOC subsubsection Shapers Configuration-->
|
||||
|
||||
@@ -489,15 +473,8 @@ The following options are defined:
|
||||
<DT><B><TT>{ip, IPAddress}</TT></B><DD> This option specifies which network interface to
|
||||
listen on. For example <CODE>{ip, {192, 168, 1, 1}}</CODE>.
|
||||
<DT><B><TT>inet6</TT></B><DD> Set up the socket for IPv6.
|
||||
<DT><B><TT>starttls</TT></B><DD> This option specifies that STARTTLS extension is available
|
||||
on connections to this port. You should also set ``<CODE>certfile</CODE>''
|
||||
option.
|
||||
<DT><B><TT>tls</TT></B><DD> This option specifies that traffic on this port will be
|
||||
encrypted using SSL immediately after connecting. You should also set
|
||||
``<CODE>certfile</CODE>'' option.
|
||||
<DT><B><TT>ssl</TT></B><DD> This option specifies that traffic on this port will be
|
||||
encrypted using SSL. You should also set ``<CODE>certfile</CODE>'' option. It
|
||||
is recommended to use <TT>tls</TT> option instead.
|
||||
encrypted using SSL. You should also set ``<CODE>certfile</CODE>'' option.
|
||||
<DT><B><TT>{certfile, Path}</TT></B><DD> Path to a file containing the SSL certificate.
|
||||
</DL>
|
||||
<DT><B><TT>ejabberd_s2s_in</TT></B><DD> This module serves incoming S2S connections.
|
||||
@@ -518,7 +495,7 @@ The following additional options are defined for <TT>ejabberd_service</TT>
|
||||
<BR>
|
||||
The following options are defined:
|
||||
<DL COMPACT=compact><DT>
|
||||
<B><TT>http_poll</TT></B><DD> This option enables <A HREF="http://www.jabber.org/jeps/jep-0025.html">JEP-0025</A> (HTTP Polling)
|
||||
<B><TT>http_poll</TT></B><DD> This option enables <A HREF="http://www.jabber.org/jeps/jep-0025.html">HTTP Polling</A> .
|
||||
support. It is available then at <CODE>http://server:port/http-poll/</CODE>.<BR>
|
||||
<BR>
|
||||
<DT><B><TT>web_admin</TT></B><DD> This option enables web-based interface for <TT>ejabberd</TT>
|
||||
@@ -628,8 +605,7 @@ Example:
|
||||
{mod_stats, []},
|
||||
{mod_vcard, []},
|
||||
{mod_offline, []},
|
||||
{mod_announce, [{access, announce}]},
|
||||
{mod_echo, [{host, "echo.example.org"}]},
|
||||
{mod_echo, [{host, "echo.localhost"}]},
|
||||
{mod_private, []},
|
||||
{mod_irc, []},
|
||||
{mod_muc, []},
|
||||
@@ -653,8 +629,7 @@ To perform online reconfiguration of <TT>ejabberd</TT> you need to enable
|
||||
<TT>ejabberd_http</TT> listener with option <TT>web_admin</TT> (see
|
||||
section <A HREF="#sec:configlistened">3.1.5</A>). After that you can open URL
|
||||
<CODE>http://server:port/admin/</CODE> with you favorite web-browser and enter
|
||||
username and password of an <TT>ejabberd</TT> user with administrator rights. E. g.
|
||||
with such config:
|
||||
username and password of <TT>ejabberd</TT> administrator. E. g. with such config:
|
||||
<PRE>
|
||||
...
|
||||
{host, "example.org"}.
|
||||
@@ -666,7 +641,7 @@ with such config:
|
||||
]
|
||||
}.
|
||||
</PRE>you should enter URL <CODE>http://example.org:5280/admin/</CODE>. After
|
||||
authentication you should see something like in figure <A HREF="#fig:webadmmain">1</A>.
|
||||
authentification you should see something like in figure <A HREF="#fig:webadmmain">1</A>.
|
||||
<BLOCKQUOTE><DIV ALIGN=center><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
|
||||
|
||||
<IMG SRC="webadmmain.png">
|
||||
@@ -708,11 +683,11 @@ Available commands:
|
||||
Example:
|
||||
ejabberdctl ejabberd@host restart
|
||||
</PRE>
|
||||
<!--TOC section Clustering-->
|
||||
<!--TOC section Distribution-->
|
||||
|
||||
<H2><A NAME="htoc22">4</A> Clustering</H2><!--SEC END -->
|
||||
<H2><A NAME="htoc22">4</A> Distribution</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:clustering"></A>
|
||||
<A NAME="sec:distribution"></A>
|
||||
<!--TOC subsection How it works-->
|
||||
|
||||
<H3><A NAME="htoc23">4.1</A> How it works</H3><!--SEC END -->
|
||||
@@ -737,126 +712,58 @@ router;
|
||||
|
||||
<H4><A NAME="htoc24">4.1.1</A> Router</H4><!--SEC END -->
|
||||
|
||||
This module is the main router of Jabber packets on each node. It
|
||||
routes them based on their destinations domains. It uses a global
|
||||
routing table. A domain of packet destination is searched in the
|
||||
routing table, and if it is found, then the packet is routed to
|
||||
appropriate process. If no, then it is sent to the S2S manager.<BR>
|
||||
This module is the main router of Jabber packets on each node. It routes
|
||||
them based on their destinations domains. It has two tables: local and global
|
||||
routes. First, domain of packet destination searched in local table, and if it
|
||||
found, then the packet is routed to appropriate process. If no, then it
|
||||
searches in global table, and is routed to the appropriate <TT>ejabberd</TT> node or
|
||||
process. If it does not exists in either tables, then it sent to the S2S
|
||||
manager.<BR>
|
||||
<BR>
|
||||
<!--TOC subsubsection Local Router-->
|
||||
|
||||
<H4><A NAME="htoc25">4.1.2</A> Local Router</H4><!--SEC END -->
|
||||
|
||||
This module routes packets which have a destination domain equal to
|
||||
this server name. If destination JID has a non-empty user part, then
|
||||
it is routed to the session manager, else it is processed depending on
|
||||
its content.<BR>
|
||||
This module routes packets which have a destination domain equal to this server
|
||||
name. If destination JID has a non-empty user part, then it routed to the
|
||||
session manager, else it is processed depending on it's content.<BR>
|
||||
<BR>
|
||||
<!--TOC subsubsection Session Manager-->
|
||||
|
||||
<H4><A NAME="htoc26">4.1.3</A> Session Manager</H4><!--SEC END -->
|
||||
|
||||
This module routes packets to local users. It searches to what user
|
||||
resource a packet must be sent via a presence table. Then packet is
|
||||
either routed to appropriate C2S process, or stored in offline
|
||||
storage, or bounced back.<BR>
|
||||
This module routes packets to local users. It searches for what user resource
|
||||
packet must be sended via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.<BR>
|
||||
<BR>
|
||||
<!--TOC subsubsection S2S Manager-->
|
||||
|
||||
<H4><A NAME="htoc27">4.1.4</A> S2S Manager</H4><!--SEC END -->
|
||||
|
||||
This module routes packets to other Jabber servers. First, it
|
||||
checks if an opened S2S connection from the domain of the packet
|
||||
source to the domain of packet destination is existing. If it is
|
||||
existing, then the S2S manager routes the packet to the process
|
||||
serving this connection, else a new connection is opened.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection How to setup ejabberd cluster-->
|
||||
|
||||
<H3><A NAME="htoc28">4.2</A> How to setup ejabberd cluster</H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:cluster"></A>
|
||||
Suppose you already setuped ejabberd on one of machines (<TT>first</TT>), and
|
||||
you need to setup another one to make <TT>ejabberd</TT> cluster. Then do
|
||||
following steps:
|
||||
<OL type=1><LI>
|
||||
Copy <CODE>~ejabberd/.erlang.cookie</CODE> file from <TT>first</TT> to
|
||||
<TT>second</TT>.<BR>
|
||||
<BR>
|
||||
(alt) You can also add ``<CODE>-cookie content_of_.erlang.cookie</CODE>''
|
||||
option to all ``<TT>erl</TT>'' commands below.<BR>
|
||||
<BR>
|
||||
<LI>On <TT>second</TT> run under `<TT>ejabberd</TT>' user in a directory
|
||||
where ejabberd will work later the following command:
|
||||
<PRE>
|
||||
erl -sname ejabberd \
|
||||
-mnesia extra_db_nodes "['ejabberd@first']" \
|
||||
-s mnesia
|
||||
</PRE>This will start mnesia serving same DB as <TT>ejabberd@first</TT>.
|
||||
You can check this running ``<CODE>mnesia:info().</CODE>'' command. You
|
||||
should see a lot of remote tables and a line like the following:
|
||||
<PRE>
|
||||
running db nodes = [ejabberd@first, ejabberd@second]
|
||||
</PRE>
|
||||
|
||||
<LI>Now run the following in the same ``<TT>erl</TT>'' session:
|
||||
<PRE>
|
||||
mnesia:change_table_copy_type(schema, node(), disc_copies).
|
||||
</PRE>
|
||||
This will create local disc storage for DB.<BR>
|
||||
<BR>
|
||||
(alt) Change storage type of `<TT>scheme</TT>' table to ``RAM and disc
|
||||
copy'' on second node via web interface.<BR>
|
||||
<BR>
|
||||
<LI>Now you can add replicas of various tables to this node with
|
||||
``<CODE>mnesia:add_table_copy</CODE>'' or
|
||||
``<CODE>mnesia:change_table_copy_type</CODE>'' as above (just replace
|
||||
``<CODE>schema</CODE>'' with another table name and ``<CODE>disc_copies</CODE>''
|
||||
can be replaced with ``<CODE>ram_copies</CODE>'' or
|
||||
``<CODE>disc_only_copies</CODE>'').<BR>
|
||||
<BR>
|
||||
What tables to replicate is very depend on your needs, you can get
|
||||
some hints from ``<CODE>mnesia:info().</CODE>'' command, by looking at
|
||||
size of tables and default storage type for each table on 'first'.<BR>
|
||||
<BR>
|
||||
Replicating of table makes lookup in this table faster on this node,
|
||||
but writing will be slower. And of course if machine with one of
|
||||
replicas is down, other replicas will be used.<BR>
|
||||
<BR>
|
||||
Also section 5.3 (Table Fragmentation) of
|
||||
<A HREF="http://www.erlang.se/doc/doc-5.4/lib/mnesia-4.2/doc/html/index.html">Mnesia Reference Manual</A> can be useful.<BR>
|
||||
<BR>
|
||||
(alt) Same as in previous item, but for other tables.<BR>
|
||||
<BR>
|
||||
<LI>Run ``<CODE>init:stop().</CODE>'' or just ``<CODE>q().</CODE>'' to exit from
|
||||
erlang shell. This probably can take some time if mnesia is not yet
|
||||
transfer and process all data it needed from <TT>first</TT>.<BR>
|
||||
<BR>
|
||||
<LI>Now run ejabberd on <TT>second</TT> with almost the same config as
|
||||
on <TT>first</TT> (you probably don't need to duplicate ``<CODE>acl</CODE>''
|
||||
and ``<CODE>access</CODE>'' options --- they will be taken from
|
||||
<TT>first</TT>, and <CODE>mod_muc</CODE> and <CODE>mod_irc</CODE> should be
|
||||
enabled only on one machine in cluster).
|
||||
</OL>
|
||||
You can repeat these steps for other machines supposed to serve this
|
||||
domain.<BR>
|
||||
This module routes packets to other Jabber servers. First, it checks if an
|
||||
open S2S connection from the domain of the packet source to the domain of
|
||||
packet destination already exists. If it is open on another node, then it
|
||||
routes the packet to S2S manager on that node, if it is open on this node, then
|
||||
it is routed to the process that serves this connection, and if a connection
|
||||
does not exist, then it is opened and registered.<BR>
|
||||
<BR>
|
||||
<!--TOC section Built-in Modules-->
|
||||
|
||||
<H2><A NAME="htoc29">A</A> Built-in Modules</H2><!--SEC END -->
|
||||
<H2><A NAME="htoc28">A</A> Built-in Modules</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modules"></A>
|
||||
<!--TOC subsection Common Options-->
|
||||
|
||||
<H3><A NAME="htoc30">A.1</A> Common Options</H3><!--SEC END -->
|
||||
<H3><A NAME="htoc29">A.1</A> Common Options</H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modcommonopts"></A>
|
||||
The following options are used by many modules, so they are described in
|
||||
separate section.<BR>
|
||||
<BR>
|
||||
<!--TOC subsubsection <TT>iqdisc</TT>-->
|
||||
<!--TOC subsubsection Option <TT>iqdisc</TT>-->
|
||||
|
||||
<H4><A NAME="htoc31">A.1.1</A> <TT>iqdisc</TT></H4><!--SEC END -->
|
||||
<H4><A NAME="htoc30">A.1.1</A> Option <TT>iqdisc</TT></H4><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modiqdiscoption"></A>
|
||||
Many modules define handlers for processing IQ queries of different namespaces
|
||||
@@ -887,9 +794,9 @@ Example:
|
||||
...
|
||||
]}.
|
||||
</PRE>
|
||||
<!--TOC subsubsection <TT>host</TT>-->
|
||||
<!--TOC subsubsection Option <TT>host</TT>-->
|
||||
|
||||
<H4><A NAME="htoc32">A.1.2</A> <TT>host</TT></H4><!--SEC END -->
|
||||
<H4><A NAME="htoc31">A.1.2</A> Option <TT>host</TT></H4><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modhostoption"></A>
|
||||
This option explicitly defines hostname for the module which acts as a service.<BR>
|
||||
@@ -903,73 +810,9 @@ Example:
|
||||
...
|
||||
]}.
|
||||
</PRE>
|
||||
<!--TOC subsubsection <TT>hosts</TT>-->
|
||||
|
||||
<H4><A NAME="htoc33">A.1.3</A> <TT>hosts</TT></H4><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modhostsoption"></A>
|
||||
This option explicitly defines a list of hostnames for the module which acts as
|
||||
a service.<BR>
|
||||
<BR>
|
||||
Example:
|
||||
<PRE>
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_echo, [{hosts, ["echo.example.org", "echo.example.com"]}]},
|
||||
...
|
||||
]}.
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_announce</TT>-->
|
||||
|
||||
<H3><A NAME="htoc34">A.2</A> <TT>mod_announce</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modannounce"></A>
|
||||
This module adds support for broadcast announce messages and MOTD.
|
||||
When the module is loaded, it handles messages sent to the following JID's
|
||||
(suppose that main server has address <TT>example.org</TT>):
|
||||
<DL COMPACT=compact><DT>
|
||||
<B><TT>example.org/announce/all</TT></B><DD> Message is sent to all registered users at
|
||||
<TT>example.org</TT>. If the user is online and connected to several resources,
|
||||
only resource with the highest priority will receive the message. If the
|
||||
registered user is not connected, the message will be stored offline (if
|
||||
oflline storage is available).
|
||||
<DT><B><TT>example.org/announce/online</TT></B><DD> Message is sent to all connected users at
|
||||
<TT>example.org</TT>. If the user is online and connected to several resources,
|
||||
all resources will receive the message.
|
||||
<DT><B><TT>example.org/announce/all-hosts/online</TT></B><DD> Message is sent to all connected
|
||||
users at every virtual host. If the user is online and connected to several
|
||||
resources, all resources will receive the message.
|
||||
<DT><B><TT>example.org/announce/motd</TT></B><DD> Message is set as MOTD (Message of the Day)
|
||||
and is sent to users at <TT>example.org</TT> as they login. In addition the
|
||||
message is sent to all connected users (similar to <TT>announce/online</TT>
|
||||
resource).
|
||||
<DT><B><TT>example.org/announce/motd/update</TT></B><DD> Message is set as MOTD (Message of the
|
||||
Day) and is sent to users at <TT>example.org</TT> as they login. The message
|
||||
is <EM>not sent</EM> to all connected users.
|
||||
<DT><B><TT>example.org/announce/motd/delete</TT></B><DD> Any message sent to this JID
|
||||
removes existing MOTD.
|
||||
</DL>
|
||||
Options:
|
||||
<DL COMPACT=compact><DT>
|
||||
<B><TT>access</TT></B><DD> Specifies who is allowed to send announce messages
|
||||
and set MOTD (default value is <TT>none</TT>).
|
||||
</DL>
|
||||
Example:
|
||||
<PRE>
|
||||
% Only admins can send announcement messages:
|
||||
{access, announce, [{allow, admin}]}.
|
||||
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_announce, [{access, announce}]},
|
||||
...
|
||||
]}.
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_configure</TT>-->
|
||||
|
||||
<H3><A NAME="htoc35">A.3</A> <TT>mod_configure</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc32">A.2</A> <TT>mod_configure</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modconfigure"></A>
|
||||
Options:
|
||||
@@ -979,7 +822,7 @@ discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
</DL>
|
||||
<!--TOC subsection <TT>mod_disco</TT>-->
|
||||
|
||||
<H3><A NAME="htoc36">A.4</A> <TT>mod_disco</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc33">A.3</A> <TT>mod_disco</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:moddisco"></A>
|
||||
This module adds support for <A HREF="http://www.jabber.org/jeps/jep-0030.html">JEP-0030</A> (Service Discovery).<BR>
|
||||
@@ -1004,38 +847,30 @@ Example:
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_echo</TT>-->
|
||||
|
||||
<H3><A NAME="htoc37">A.5</A> <TT>mod_echo</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc34">A.4</A> <TT>mod_echo</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modecho"></A>
|
||||
This module acts as a service and simply returns to sender any Jabber
|
||||
packet. Module may be useful for debugging.<BR>
|
||||
This module acts as a service and simply returns to sender any Jabber packet. Module may be
|
||||
useful for debugging.<BR>
|
||||
<BR>
|
||||
Options:
|
||||
<DL COMPACT=compact><DT>
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of the service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>).
|
||||
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
|
||||
(see <A HREF="#sec:modhostsoption">A.1.3</A>). If neither <TT>host</TT> nor <TT>hosts</TT>
|
||||
are not present, then prefix <TT>echo.</TT> is added to all <TT>ejabberd</TT> hostnames.
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>). If not present
|
||||
then prefix <TT>echo.</TT> is added to main <TT>ejabberd</TT> hostname.
|
||||
</DL>
|
||||
<!--TOC subsection <TT>mod_irc</TT>-->
|
||||
|
||||
<H3><A NAME="htoc38">A.6</A> <TT>mod_irc</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc35">A.5</A> <TT>mod_irc</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modirc"></A>
|
||||
This module implements IRC transport.<BR>
|
||||
<BR>
|
||||
Options:
|
||||
<DL COMPACT=compact><DT>
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of the service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>).
|
||||
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
|
||||
(see <A HREF="#sec:modhostsoption">A.1.3</A>). If neither <TT>host</TT> nor <TT>hosts</TT>
|
||||
are not present, then prefix <TT>irc.</TT> is added to all <TT>ejabberd</TT> hostnames.
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>). If not present
|
||||
then prefix <TT>irc.</TT> is added to main <TT>ejabberd</TT> hostname.
|
||||
<DT><B><TT>access</TT></B><DD> Specifies who is allowed to use IRC transport (default value is <TT>all</TT>).
|
||||
</DL>
|
||||
Example:
|
||||
@@ -1049,7 +884,7 @@ Example:
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_last</TT>-->
|
||||
|
||||
<H3><A NAME="htoc39">A.7</A> <TT>mod_last</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc36">A.6</A> <TT>mod_last</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modlast"></A>
|
||||
This module adds support for <A HREF="http://www.jabber.org/jeps/jep-0012.html">JEP-0012</A> (Last Activity)<BR>
|
||||
@@ -1061,20 +896,16 @@ discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
</DL>
|
||||
<!--TOC subsection <TT>mod_muc</TT>-->
|
||||
|
||||
<H3><A NAME="htoc40">A.8</A> <TT>mod_muc</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc37">A.7</A> <TT>mod_muc</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modmuc"></A>
|
||||
This module implements <A HREF="http://www.jabber.org/jeps/jep-0045.html">JEP-0045</A> (Multi-User Chat) service.<BR>
|
||||
<BR>
|
||||
Options:
|
||||
<DL COMPACT=compact><DT>
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of the service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>).
|
||||
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
|
||||
(see <A HREF="#sec:modhostsoption">A.1.3</A>). If neither <TT>host</TT> nor <TT>hosts</TT>
|
||||
are not present, then prefix <TT>conference.</TT> is added to all <TT>ejabberd</TT> hostnames.
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>). If not present
|
||||
then prefix <TT>conference.</TT> is added to main <TT>ejabberd</TT> hostname.
|
||||
<DT><B><TT>access</TT></B><DD> Specifies who is allowed to use MUC service (default value is <TT>all</TT>).
|
||||
<DT><B><TT>access_create</TT></B><DD> Specifies who is allowed to create new rooms at
|
||||
MUC service (default value is <TT>all</TT>).
|
||||
@@ -1100,14 +931,14 @@ Example:
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_offline</TT>-->
|
||||
|
||||
<H3><A NAME="htoc41">A.9</A> <TT>mod_offline</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc38">A.8</A> <TT>mod_offline</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modoffline"></A>
|
||||
This module implements offline message storage.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection <TT>mod_privacy</TT>-->
|
||||
|
||||
<H3><A NAME="htoc42">A.10</A> <TT>mod_privacy</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc39">A.9</A> <TT>mod_privacy</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modprivacy"></A>
|
||||
This module implements Privacy Rules as defined in XMPP IM
|
||||
@@ -1120,7 +951,7 @@ discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
</DL>
|
||||
<!--TOC subsection <TT>mod_private</TT>-->
|
||||
|
||||
<H3><A NAME="htoc43">A.11</A> <TT>mod_private</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc40">A.10</A> <TT>mod_private</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modprivate"></A>
|
||||
This module adds support of <A HREF="http://www.jabber.org/jeps/jep-0049.html">JEP-0049</A> (Private XML Storage).<BR>
|
||||
@@ -1132,20 +963,16 @@ discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
</DL>
|
||||
<!--TOC subsection <TT>mod_pubsub</TT>-->
|
||||
|
||||
<H3><A NAME="htoc44">A.12</A> <TT>mod_pubsub</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc41">A.11</A> <TT>mod_pubsub</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modpubsub"></A>
|
||||
This module implements <A HREF="http://www.jabber.org/jeps/jep-0060.html">JEP-0060</A> (Publish-Subscribe Service).<BR>
|
||||
<BR>
|
||||
Options:
|
||||
<DL COMPACT=compact><DT>
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of the service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>).
|
||||
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
|
||||
(see <A HREF="#sec:modhostsoption">A.1.3</A>). If neither <TT>host</TT> nor <TT>hosts</TT>
|
||||
are not present, then prefix <TT>pubsub.</TT> is added to all <TT>ejabberd</TT> hostnames.
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>). If not present
|
||||
then prefix <TT>pubsub.</TT> is added to main <TT>ejabberd</TT> hostname.
|
||||
<DT><B><TT>served_hosts</TT></B><DD> Specifies which hosts are served by the service.
|
||||
If absent then only main <TT>ejabberd</TT> host is served.
|
||||
</DL>
|
||||
@@ -1161,7 +988,7 @@ Example:
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_register</TT>-->
|
||||
|
||||
<H3><A NAME="htoc45">A.13</A> <TT>mod_register</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc42">A.12</A> <TT>mod_register</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modregister"></A>
|
||||
This module adds support for <A HREF="http://www.jabber.org/jeps/jep-0077.html">JEP-0077</A> (In-Band Registration).<BR>
|
||||
@@ -1194,7 +1021,7 @@ Example:
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_roster</TT>-->
|
||||
|
||||
<H3><A NAME="htoc46">A.14</A> <TT>mod_roster</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc43">A.13</A> <TT>mod_roster</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modroster"></A>
|
||||
This module implements roster management.<BR>
|
||||
@@ -1204,143 +1031,9 @@ Options:
|
||||
<B><TT>iqdisc</TT></B><DD> <TT>jabber:iq:roster</TT> IQ queries processing
|
||||
discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
</DL>
|
||||
<!--TOC subsection <TT>mod_service_log</TT>-->
|
||||
|
||||
<H3><A NAME="htoc47">A.15</A> <TT>mod_service_log</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modservicelog"></A>
|
||||
This module adds support for logging of user packets via any jabber service.
|
||||
These packets encapsulated in <route/> element and sended to specified
|
||||
services.<BR>
|
||||
<BR>
|
||||
Options:
|
||||
<DL COMPACT=compact><DT>
|
||||
<B><TT>loggers</TT></B><DD> Specifies a list of services which will receive users
|
||||
packets.
|
||||
</DL>
|
||||
Example:
|
||||
<PRE>
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_service_log, [{loggers, ["bandersnatch.example.com"]}]},
|
||||
...
|
||||
]}.
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_shared_roster</TT>-->
|
||||
|
||||
<H3><A NAME="htoc48">A.16</A> <TT>mod_shared_roster</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modsharedroster"></A>
|
||||
This module implements shared roster groups support.<BR>
|
||||
<BR>
|
||||
You can edit shared roster groups via web-interface. Each group has an unique
|
||||
ID and the following parameters:
|
||||
<DL COMPACT=compact><DT>
|
||||
<B>Name</B><DD> The name of the group, which will be displayed in roster.
|
||||
<DT><B>Description</B><DD> Textual description of this group, doesn't affect anything.
|
||||
<DT><B>Members</B><DD> List of full JIDs of group members, entered one per line in
|
||||
web-interface.
|
||||
<DT><B>Displayed groups</B><DD> List of IDs of groups which will be in rosters of this
|
||||
group members.
|
||||
</DL>
|
||||
For example, to have a group of users which can see each other in roster,
|
||||
create a group like on table <A HREF="#tab:srge1">1</A>.
|
||||
<BLOCKQUOTE><DIV ALIGN=center><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
|
||||
<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=1>
|
||||
<TR><TD ALIGN=left NOWRAP> </TD>
|
||||
<TD ALIGN=left NOWRAP>Group `<TT>users</TT>'</TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP>Name</TD>
|
||||
<TD ALIGN=left NOWRAP>Users</TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP>Members</TD>
|
||||
<TD ALIGN=left NOWRAP><TABLE CELLSPACING=2 CELLPADDING=0>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user1@example.org</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user2@example.org</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user3@example.org</TT></TD>
|
||||
</TR></TABLE></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP>Displayed groups</TD>
|
||||
<TD ALIGN=left NOWRAP><TT>users</TT></TD>
|
||||
</TR></TABLE>
|
||||
<BR>
|
||||
<DIV ALIGN=center>Table 1: Shared group example N1</DIV><BR>
|
||||
|
||||
<A NAME="tab:srge1"></A>
|
||||
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE>
|
||||
To have 3 groups `<TT>managers</TT>', `<TT>workgroup1</TT>', and
|
||||
`<TT>workgroup2</TT>', where group `<TT>managers</TT>' can see members of all
|
||||
groups, and other two groups can see `<TT>managers</TT>' group and themselves,
|
||||
create groups like on table <A HREF="#tab:srge2">2</A>.
|
||||
<BLOCKQUOTE><DIV ALIGN=center><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
|
||||
<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=1>
|
||||
<TR><TD ALIGN=left NOWRAP> </TD>
|
||||
<TD ALIGN=left NOWRAP>Group `<TT>managers</TT>'</TD>
|
||||
<TD ALIGN=left NOWRAP>Group `<TT>workgroup1</TT>'</TD>
|
||||
<TD ALIGN=left NOWRAP>Group `<TT>workgroup2</TT>'</TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP>Name</TD>
|
||||
<TD ALIGN=left NOWRAP>Managers</TD>
|
||||
<TD ALIGN=left NOWRAP>Workgroup1</TD>
|
||||
<TD ALIGN=left NOWRAP>Workgroup2</TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP>Members</TD>
|
||||
<TD ALIGN=left NOWRAP><TABLE CELLSPACING=2 CELLPADDING=0>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>manager1@example.org</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>manager2@example.org</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>manager3@example.org</TT></TD>
|
||||
</TR></TABLE>
|
||||
</TD>
|
||||
<TD ALIGN=left NOWRAP><TABLE CELLSPACING=2 CELLPADDING=0>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user1@example.org</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user2@example.org</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user3@example.org</TT></TD>
|
||||
</TR></TABLE>
|
||||
</TD>
|
||||
<TD ALIGN=left NOWRAP><TABLE CELLSPACING=2 CELLPADDING=0>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user4@example.org</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user5@example.org</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>user6@example.org</TT></TD>
|
||||
</TR></TABLE></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP>Displayed groups</TD>
|
||||
<TD ALIGN=left NOWRAP><TABLE CELLSPACING=2 CELLPADDING=0>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>managers</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>workgroup1</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>workgroup2</TT></TD>
|
||||
</TR></TABLE>
|
||||
</TD>
|
||||
<TD ALIGN=left NOWRAP><TABLE CELLSPACING=2 CELLPADDING=0>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>managers</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>workgroup1</TT></TD>
|
||||
</TR></TABLE>
|
||||
</TD>
|
||||
<TD ALIGN=left NOWRAP><TABLE CELLSPACING=2 CELLPADDING=0>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>managers</TT></TD>
|
||||
</TR>
|
||||
<TR><TD ALIGN=left NOWRAP><TT>workgroup2</TT></TD>
|
||||
</TR></TABLE></TD>
|
||||
</TR></TABLE>
|
||||
<BR>
|
||||
<DIV ALIGN=center>Table 2: Shared group example N2</DIV><BR>
|
||||
|
||||
<A NAME="tab:srge2"></A>
|
||||
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE>
|
||||
<!--TOC subsection <TT>mod_stats</TT>-->
|
||||
|
||||
<H3><A NAME="htoc49">A.17</A> <TT>mod_stats</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc44">A.14</A> <TT>mod_stats</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modstats"></A>
|
||||
This module adds support for <A HREF="http://www.jabber.org/jeps/jep-0039.html">JEP-0039</A> (Statistics Gathering).<BR>
|
||||
@@ -1350,9 +1043,11 @@ Options:
|
||||
<B><TT>iqdisc</TT></B><DD> <TT>http://jabber.org/protocol/stats</TT> IQ queries processing
|
||||
discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
</DL>
|
||||
TBD about access.<BR>
|
||||
<BR>
|
||||
<!--TOC subsection <TT>mod_time</TT>-->
|
||||
|
||||
<H3><A NAME="htoc50">A.18</A> <TT>mod_time</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc45">A.15</A> <TT>mod_time</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modtime"></A>
|
||||
This module answers UTC time on <TT>jabber:iq:time</TT> queries.<BR>
|
||||
@@ -1364,7 +1059,7 @@ discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
</DL>
|
||||
<!--TOC subsection <TT>mod_vcard</TT>-->
|
||||
|
||||
<H3><A NAME="htoc51">A.19</A> <TT>mod_vcard</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc46">A.16</A> <TT>mod_vcard</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modvcard"></A>
|
||||
This module implements simple Jabber User Directory (based on user vCards)
|
||||
@@ -1372,41 +1067,27 @@ and answers server vCard on <TT>vcard-temp</TT> queries.<BR>
|
||||
<BR>
|
||||
Options:
|
||||
<DL COMPACT=compact><DT>
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of the service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>).
|
||||
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
|
||||
(see <A HREF="#sec:modhostsoption">A.1.3</A>). If neither <TT>host</TT> nor <TT>hosts</TT>
|
||||
are not present, then prefix <TT>vjud.</TT> is added to all <TT>ejabberd</TT> hostnames.
|
||||
|
||||
<B><TT>host</TT></B><DD> Defines hostname of service
|
||||
(see <A HREF="#sec:modhostoption">A.1.2</A>). If not present
|
||||
then prefix <TT>vjud.</TT> is added to main <TT>ejabberd</TT> hostname.
|
||||
<DT><B><TT>iqdisc</TT></B><DD> <TT>vcard-temp</TT> IQ queries processing
|
||||
discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
<DT><B><TT>search</TT></B><DD> Specifies whether search is enabled (value is <TT>true</TT>, default) or
|
||||
<DT><B><TT>search</TT></B><DD> Specifies wheather search is enabled (value is <TT>true</TT>, default) or
|
||||
disabled (value is <TT>false</TT>) by the service. If <TT>search</TT> is set to <TT>false</TT>,
|
||||
option <TT>host</TT> is ignored and service does not appear in Jabber Discovery items.
|
||||
<DT><B><TT>matches</TT></B><DD> Limits the number of reported search results. If value is set to
|
||||
<TT>infinity</TT> then all search results are reported. Default value is <TT>30</TT>.
|
||||
<DT><B><TT>allow_return_all</TT></B><DD> Specifies whether search with empty input fields can
|
||||
return all known users. Default is <TT>false</TT>.
|
||||
<DT><B><TT>search_all_hosts</TT></B><DD> If set in <TT>true</TT> then search returns matched
|
||||
items at all virtual hosts. Otherwise only current host items are returned.
|
||||
Default is <TT>true</TT>.
|
||||
</DL>
|
||||
Example:
|
||||
<PRE>
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_vcard, [{search, true},
|
||||
{matches, 20},
|
||||
{allow_return_all, true},
|
||||
{search_all_hosts, false}]}
|
||||
{mod_vcard, [{search, false}]}
|
||||
...
|
||||
]}.
|
||||
</PRE>
|
||||
<!--TOC subsection <TT>mod_version</TT>-->
|
||||
|
||||
<H3><A NAME="htoc52">A.20</A> <TT>mod_version</TT></H3><!--SEC END -->
|
||||
<H3><A NAME="htoc47">A.17</A> <TT>mod_version</TT></H3><!--SEC END -->
|
||||
|
||||
<A NAME="sec:modversion"></A>
|
||||
This module answers <TT>ejabberd</TT> version on <TT>jabber:iq:version</TT> queries.<BR>
|
||||
@@ -1418,14 +1099,14 @@ discipline (see <A HREF="#sec:modiqdiscoption">A.1.1</A>).
|
||||
</DL>
|
||||
<!--TOC section I18n/L10n-->
|
||||
|
||||
<H2><A NAME="htoc53">B</A> I18n/L10n</H2><!--SEC END -->
|
||||
<H2><A NAME="htoc48">B</A> I18n/L10n</H2><!--SEC END -->
|
||||
|
||||
<A NAME="sec:i18nl10n"></A>
|
||||
All built-in modules support <TT>xml:lang</TT> attribute inside IQ queries.
|
||||
E. g. on figure <A HREF="#fig:discorus">2</A> showed the reply on the following query:
|
||||
<PRE>
|
||||
<iq id='5'
|
||||
to='example.org'
|
||||
to='e.localhost'
|
||||
type='get'
|
||||
xml:lang='ru'>
|
||||
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
\newcommand{\Jabber}{Jabber}
|
||||
|
||||
\newcommand{\module}[1]{\texttt{#1}}
|
||||
\newcommand{\modannounce}{\module{mod\_announce}}
|
||||
\newcommand{\modconfigure}{\module{mod\_configure}}
|
||||
\newcommand{\moddisco}{\module{mod\_disco}}
|
||||
\newcommand{\modirc}{\module{mod\_irc}}
|
||||
@@ -47,8 +46,6 @@
|
||||
\newcommand{\modpubsub}{\module{mod\_pubsub}}
|
||||
\newcommand{\modregister}{\module{mod\_register}}
|
||||
\newcommand{\modroster}{\module{mod\_roster}}
|
||||
\newcommand{\modservicelog}{\module{mod\_service\_log}}
|
||||
\newcommand{\modsharedroster}{\module{mod\_shared\_roster}}
|
||||
\newcommand{\modstats}{\module{mod\_stats}}
|
||||
\newcommand{\modtime}{\module{mod\_time}}
|
||||
\newcommand{\modvcard}{\module{mod\_vcard}}
|
||||
@@ -70,19 +67,15 @@
|
||||
|
||||
\newcommand{\iqdiscitem}[1]{\titem{iqdisc} #1 IQ queries processing
|
||||
discipline (see~\ref{sec:modiqdiscoption}).}
|
||||
\newcommand{\hostitem}[1]{
|
||||
\titem{host} Defines hostname of the service
|
||||
(see~\ref{sec:modhostoption}).
|
||||
\titem{hosts} Defines hostnames of the service
|
||||
(see~\ref{sec:modhostsoption}). If neither \texttt{host} nor \texttt{hosts}
|
||||
are not present, then prefix \jid{#1.} is added to all \ejabberd{} hostnames.
|
||||
}
|
||||
\newcommand{\hostitem}[1]{\titem{host} Defines hostname of service
|
||||
(see~\ref{sec:modhostoption}). If not present
|
||||
then prefix \jid{#1.} is added to main \ejabberd{} hostname.}
|
||||
|
||||
\title{Ejabberd Installation and Operation Guide}
|
||||
\author{Alexey Shchepin \\
|
||||
\ahrefurl{mailto:alexey@sevcom.net} \\
|
||||
\ahrefurl{xmpp:aleksey@jabber.ru}}
|
||||
\date{May 23, 2005}
|
||||
\date{June 24, 2004}
|
||||
|
||||
\begin{document}
|
||||
\begin{titlepage}
|
||||
@@ -114,7 +107,6 @@ The main features of \ejabberd{} are:
|
||||
permanently on more than one node. This means that if one of the nodes
|
||||
crashes, then the others will continue working without disruption.
|
||||
You can also add or replace nodes ``on the fly''.
|
||||
\item Support for virtual hosting
|
||||
\item Built-in \tjepref{0045}{Multi-User Chat} service
|
||||
\item Built-in IRC transport
|
||||
\item Built-in \tjepref{0060}{Publish-Subscribe} service
|
||||
@@ -122,24 +114,23 @@ The main features of \ejabberd{} are:
|
||||
\item Built-in web-based administration interface
|
||||
\item Built-in \tjepref{0025}{HTTP Polling} service
|
||||
\item SSL support
|
||||
\item Support for LDAP authentication
|
||||
\item Support for LDAP authentification
|
||||
\item Ability to interface with external components (JIT, MSN-t, Yahoo-t, etc.)
|
||||
\item Migration from jabberd14 is possible
|
||||
\item Mostly XMPP-compliant
|
||||
\item Support for \tjepref{0030}{Service Discovery}.
|
||||
\item Support for \tjepref{0039}{Statistics Gathering}.
|
||||
\item Support for \jepref{0030} (Service Discovery).
|
||||
\item Support for \jepref{0039} (Statistics Gathering).
|
||||
\item Support for \ns{xml:lang}
|
||||
\end{itemize}
|
||||
|
||||
The misfeatures of \ejabberd{} are:
|
||||
\begin{itemize}
|
||||
\item No support for authentication and STARTTLS in S2S connections
|
||||
\item Access rules can be defined only for global conext, not for specific
|
||||
virtual host
|
||||
\item No support for virtual domains
|
||||
\item No support for STARTTLS
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\section{Installation from Source}
|
||||
\section{Installation}
|
||||
\label{sec:installation}
|
||||
|
||||
\subsection{Installation Requirements}
|
||||
@@ -153,8 +144,7 @@ To compile \ejabberd{}, you will need the following packages:
|
||||
\item GNU Make;
|
||||
\item GCC;
|
||||
\item libexpat 1.95 or later;
|
||||
\item Erlang/OTP R8B or later;
|
||||
\item OpenSSL 0.9.6 or later (optional).
|
||||
\item Erlang/OTP R8B or later.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Windows}
|
||||
@@ -164,7 +154,7 @@ To compile \ejabberd{} in MS Windows environment, you will need the following
|
||||
packages:
|
||||
\begin{itemize}
|
||||
\item MS Visual C++ 6.0 Compiler
|
||||
\item \footahref{http://erlang.org/download/otp\_win32\_R10B-1a.exe}{Erlang/OTP R10B-1a}
|
||||
\item \footahref{http://www.erlang.org/download/otp\_win32\_R9C-0.exe}{Erlang/OTP R9C-0}
|
||||
\item \footahref{http://prdownloads.sourceforge.net/expat/expat\_win32bin\_1\_95\_7.exe?download}{Expat 1.95.7}
|
||||
\item
|
||||
\footahref{http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.9.1.tar.gz}{Iconv 1.9.1}
|
||||
@@ -178,11 +168,14 @@ packages:
|
||||
\label{sec:obtaining}
|
||||
|
||||
Stable \ejabberd{} release can be obtained at
|
||||
\ahrefurl{http://www.process-one.net/en/projects/ejabberd/download.html}.
|
||||
\ahrefurl{http://www.jabberstudio.org/projects/ejabberd/releases/}.
|
||||
|
||||
The latest alpha version can be retrieved from Subversion repository\@.
|
||||
The latest alpha version can be retrieved from CVS\@.
|
||||
\begin{verbatim}
|
||||
svn co svn://svn.process-one.net/opt/data/svn/ejabberd/trunk ejabberd
|
||||
export CVSROOT=:pserver:anonymous@jabberstudio.org:/home/cvs
|
||||
cvs login
|
||||
<press Enter when asked for a password>
|
||||
cvs -z3 co ejabberd
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
@@ -292,6 +285,8 @@ But in this case \ejabberd{} can start to work slower.
|
||||
\subsection{Initial Configuration}
|
||||
\label{sec:initconfig}
|
||||
|
||||
%\verbatiminput{../src/ejabberd.cfg}
|
||||
|
||||
The configuration file is initially loaded the first time \ejabberd{} is
|
||||
executed, when it is parsed and stored in a database. Subsequently the
|
||||
configuration is loaded from the database and any commands in the configuration
|
||||
@@ -313,25 +308,13 @@ With this lines old global or local options or ACLs will be removed before
|
||||
adding new ones.
|
||||
|
||||
|
||||
\subsubsection{Host Names}
|
||||
\subsubsection{Host Name}
|
||||
\label{sec:confighostname}
|
||||
|
||||
Option \option{hosts} defines a list of \Jabber{} domains that \ejabberd{}
|
||||
serves. E.\,g.\ to serve \jid{example.org} and \jid{example.com} domains add
|
||||
the following line in the config:
|
||||
Option \option{hostname} defines name of \Jabber{} domain that \ejabberd{}
|
||||
serves. E.\,g.\ to use \jid{jabber.org} domain add the following line in the config:
|
||||
\begin{verbatim}
|
||||
{hosts, ["example.org", "example.com"]}.
|
||||
\end{verbatim}
|
||||
|
||||
Option \option{host} defines one \Jabber{} domain that \ejabberd{} serves.
|
||||
E.\,g.\ to serve only \jid{example.org} domain add the following line in the
|
||||
config:
|
||||
\begin{verbatim}
|
||||
{host, "example.org"}.
|
||||
\end{verbatim}
|
||||
It have the same effect as
|
||||
\begin{verbatim}
|
||||
{hosts, ["example.org"]}.
|
||||
{host, "jabber.org"}.
|
||||
\end{verbatim}
|
||||
|
||||
%This option is mandatory.
|
||||
@@ -363,8 +346,8 @@ declarations of ACL in config file have following syntax:
|
||||
\begin{verbatim}
|
||||
{acl, all, all}.
|
||||
\end{verbatim}
|
||||
\titem{\{user, <username>\}} Matches user with name
|
||||
\term{<username>} at the first virtual host. Example:
|
||||
\titem{\{user, <username>\}} Matches local user with name
|
||||
\term{<username>}. Example:
|
||||
\begin{verbatim}
|
||||
{acl, admin, {user, "aleksey"}}.
|
||||
\end{verbatim}
|
||||
@@ -379,7 +362,7 @@ declarations of ACL in config file have following syntax:
|
||||
{acl, jabberorg, {server, "jabber.org"}}.
|
||||
\end{verbatim}
|
||||
\titem{\{user\_regexp, <regexp>\}} Matches local user with name that
|
||||
matches \term{<regexp>} at the first virtual host. Example:
|
||||
matches \term{<regexp>}. Example:
|
||||
\begin{verbatim}
|
||||
{acl, tests, {user, "^test[0-9]*$"}}.
|
||||
\end{verbatim}
|
||||
@@ -387,7 +370,7 @@ declarations of ACL in config file have following syntax:
|
||||
\titem{\{user\_regexp, <regexp>, <server>\}} Matches user with name
|
||||
that matches \term{<regexp>} and from server \term{<server>}. Example:
|
||||
\begin{verbatim}
|
||||
{acl, tests, {user, "^test", "example.org"}}.
|
||||
{acl, tests, {user, "^test", "localhost"}}.
|
||||
\end{verbatim}
|
||||
\titem{\{server\_regexp, <regexp>\}} Matches any JID from server that
|
||||
matches \term{<regexp>}. Example:
|
||||
@@ -416,14 +399,13 @@ declarations of ACL in config file have following syntax:
|
||||
\end{description}
|
||||
\end{description}
|
||||
|
||||
The following ACLs are pre-defined:
|
||||
The following ACLs pre-defined:
|
||||
\begin{description}
|
||||
\titem{all} Matches all JIDs.
|
||||
\titem{none} Matches none JIDs.
|
||||
\end{description}
|
||||
|
||||
An entry allowing or denying access to different services would look similar to
|
||||
this:
|
||||
An entry allowing or denying different services would look similar to this:
|
||||
\begin{verbatim}
|
||||
{access, <accessname>, [{allow, <aclname>},
|
||||
{deny, <aclname>},
|
||||
@@ -444,8 +426,8 @@ Example:
|
||||
|
||||
Following access rules pre-defined:
|
||||
\begin{description}
|
||||
\titem{all} Always returns ``\term{allow}''
|
||||
\titem{none} Always returns ``\term{deny}''
|
||||
\titem{all} Always return ``\term{allow}''
|
||||
\titem{none} Always return ``\term{deny}''
|
||||
\end{description}
|
||||
|
||||
|
||||
@@ -495,15 +477,8 @@ Currently these modules are implemented:
|
||||
\titem{\{ip, IPAddress\}} This option specifies which network interface to
|
||||
listen on. For example \verb|{ip, {192, 168, 1, 1}}|.
|
||||
\titem{inet6} Set up the socket for IPv6.
|
||||
\titem{starttls} This option specifies that STARTTLS extension is available
|
||||
on connections to this port. You should also set ``\verb|certfile|''
|
||||
option.
|
||||
\titem{tls} This option specifies that traffic on this port will be
|
||||
encrypted using SSL immediately after connecting. You should also set
|
||||
``\verb|certfile|'' option.
|
||||
\titem{ssl} This option specifies that traffic on this port will be
|
||||
encrypted using SSL. You should also set ``\verb|certfile|'' option. It
|
||||
is recommended to use \term{tls} option instead.
|
||||
encrypted using SSL. You should also set ``\verb|certfile|'' option.
|
||||
\titem{\{certfile, Path\}} Path to a file containing the SSL certificate.
|
||||
\end{description}
|
||||
\titem{ejabberd\_s2s\_in} This module serves incoming S2S connections.
|
||||
@@ -524,7 +499,7 @@ Currently these modules are implemented:
|
||||
|
||||
The following options are defined:
|
||||
\begin{description}
|
||||
\titem{http\_poll} This option enables \jepref{0025} (HTTP Polling)
|
||||
\titem{http\_poll} This option enables \tjepref{0025}{HTTP Polling} .
|
||||
support. It is available then at \verb|http://server:port/http-poll/|.
|
||||
|
||||
\titem{web\_admin} This option enables web-based interface for \ejabberd{}
|
||||
@@ -636,8 +611,7 @@ Example:
|
||||
{mod_stats, []},
|
||||
{mod_vcard, []},
|
||||
{mod_offline, []},
|
||||
{mod_announce, [{access, announce}]},
|
||||
{mod_echo, [{host, "echo.example.org"}]},
|
||||
{mod_echo, [{host, "echo.localhost"}]},
|
||||
{mod_private, []},
|
||||
{mod_irc, []},
|
||||
{mod_muc, []},
|
||||
@@ -659,8 +633,7 @@ To perform online reconfiguration of \ejabberd{} you need to enable
|
||||
\term{ejabberd\_http} listener with option \term{web\_admin} (see
|
||||
section~\ref{sec:configlistened}). After that you can open URL
|
||||
\verb|http://server:port/admin/| with you favorite web-browser and enter
|
||||
username and password of an \ejabberd{} user with administrator rights. E.\,g.
|
||||
with such config:
|
||||
username and password of \ejabberd{} administrator. E.\,g. with such config:
|
||||
\begin{verbatim}
|
||||
...
|
||||
{host, "example.org"}.
|
||||
@@ -673,7 +646,7 @@ with such config:
|
||||
}.
|
||||
\end{verbatim}
|
||||
you should enter URL \verb|http://example.org:5280/admin/|. After
|
||||
authentication you should see something like in figure~\ref{fig:webadmmain}.
|
||||
authentification you should see something like in figure~\ref{fig:webadmmain}.
|
||||
\begin{figure}[htbp]
|
||||
\centering
|
||||
\insimg{webadmmain.png}
|
||||
@@ -713,8 +686,8 @@ Example:
|
||||
|
||||
|
||||
|
||||
\section{Clustering}
|
||||
\label{sec:clustering}
|
||||
\section{Distribution}
|
||||
\label{sec:distribution}
|
||||
|
||||
|
||||
\subsection{How it works}
|
||||
@@ -739,118 +712,38 @@ Each \ejabberd{} node have following modules:
|
||||
|
||||
\subsubsection{Router}
|
||||
|
||||
This module is the main router of \Jabber{} packets on each node. It
|
||||
routes them based on their destinations domains. It uses a global
|
||||
routing table. A domain of packet destination is searched in the
|
||||
routing table, and if it is found, then the packet is routed to
|
||||
appropriate process. If no, then it is sent to the S2S manager.
|
||||
This module is the main router of \Jabber{} packets on each node. It routes
|
||||
them based on their destinations domains. It has two tables: local and global
|
||||
routes. First, domain of packet destination searched in local table, and if it
|
||||
found, then the packet is routed to appropriate process. If no, then it
|
||||
searches in global table, and is routed to the appropriate \ejabberd{} node or
|
||||
process. If it does not exists in either tables, then it sent to the S2S
|
||||
manager.
|
||||
|
||||
|
||||
\subsubsection{Local Router}
|
||||
|
||||
This module routes packets which have a destination domain equal to
|
||||
this server name. If destination JID has a non-empty user part, then
|
||||
it is routed to the session manager, else it is processed depending on
|
||||
its content.
|
||||
This module routes packets which have a destination domain equal to this server
|
||||
name. If destination JID has a non-empty user part, then it routed to the
|
||||
session manager, else it is processed depending on it's content.
|
||||
|
||||
|
||||
\subsubsection{Session Manager}
|
||||
|
||||
This module routes packets to local users. It searches to what user
|
||||
resource a packet must be sent via a presence table. Then packet is
|
||||
either routed to appropriate C2S process, or stored in offline
|
||||
storage, or bounced back.
|
||||
This module routes packets to local users. It searches for what user resource
|
||||
packet must be sended via presence table. If this resource is connected to
|
||||
this node, it is routed to C2S process, if it connected via another node, then
|
||||
the packet is sent to session manager on that node.
|
||||
|
||||
|
||||
\subsubsection{S2S Manager}
|
||||
|
||||
This module routes packets to other \Jabber{} servers. First, it
|
||||
checks if an opened S2S connection from the domain of the packet
|
||||
source to the domain of packet destination is existing. If it is
|
||||
existing, then the S2S manager routes the packet to the process
|
||||
serving this connection, else a new connection is opened.
|
||||
|
||||
|
||||
\subsection{How to setup ejabberd cluster}
|
||||
\label{sec:cluster}
|
||||
|
||||
Suppose you already setuped ejabberd on one of machines (\term{first}), and
|
||||
you need to setup another one to make \ejabberd{} cluster. Then do
|
||||
following steps:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Copy \verb|~ejabberd/.erlang.cookie| file from \term{first} to
|
||||
\term{second}.
|
||||
|
||||
(alt) You can also add ``\verb|-cookie content_of_.erlang.cookie|''
|
||||
option to all ``\shell{erl}'' commands below.
|
||||
|
||||
\item On \term{second} run under `\term{ejabberd}' user in a directory
|
||||
where ejabberd will work later the following command:
|
||||
|
||||
\begin{verbatim}
|
||||
erl -sname ejabberd \
|
||||
-mnesia extra_db_nodes "['ejabberd@first']" \
|
||||
-s mnesia
|
||||
\end{verbatim}
|
||||
|
||||
This will start mnesia serving same DB as \node{ejabberd@first}.
|
||||
You can check this running ``\verb|mnesia:info().|'' command. You
|
||||
should see a lot of remote tables and a line like the following:
|
||||
|
||||
\begin{verbatim}
|
||||
running db nodes = [ejabberd@first, ejabberd@second]
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
\item Now run the following in the same ``\shell{erl}'' session:
|
||||
|
||||
\begin{verbatim}
|
||||
mnesia:change_table_copy_type(schema, node(), disc_copies).
|
||||
\end{verbatim}
|
||||
|
||||
This will create local disc storage for DB.
|
||||
|
||||
(alt) Change storage type of `\term{scheme}' table to ``RAM and disc
|
||||
copy'' on second node via web interface.
|
||||
|
||||
|
||||
\item Now you can add replicas of various tables to this node with
|
||||
``\verb|mnesia:add_table_copy|'' or
|
||||
``\verb|mnesia:change_table_copy_type|'' as above (just replace
|
||||
``\verb|schema|'' with another table name and ``\verb|disc_copies|''
|
||||
can be replaced with ``\verb|ram_copies|'' or
|
||||
``\verb|disc_only_copies|'').
|
||||
|
||||
What tables to replicate is very depend on your needs, you can get
|
||||
some hints from ``\verb|mnesia:info().|'' command, by looking at
|
||||
size of tables and default storage type for each table on 'first'.
|
||||
|
||||
Replicating of table makes lookup in this table faster on this node,
|
||||
but writing will be slower. And of course if machine with one of
|
||||
replicas is down, other replicas will be used.
|
||||
|
||||
Also section 5.3 (Table Fragmentation) of
|
||||
\footahref{http://www.erlang.se/doc/doc-5.4/lib/mnesia-4.2/doc/html/index.html}
|
||||
{Mnesia Reference Manual} can be useful.
|
||||
|
||||
(alt) Same as in previous item, but for other tables.
|
||||
|
||||
|
||||
\item Run ``\verb|init:stop().|'' or just ``\verb|q().|'' to exit from
|
||||
erlang shell. This probably can take some time if mnesia is not yet
|
||||
transfer and process all data it needed from \term{first}.
|
||||
|
||||
|
||||
\item Now run ejabberd on \term{second} with almost the same config as
|
||||
on \term{first} (you probably don't need to duplicate ``\verb|acl|''
|
||||
and ``\verb|access|'' options --- they will be taken from
|
||||
\term{first}, and \verb|mod_muc| and \verb|mod_irc| should be
|
||||
enabled only on one machine in cluster).
|
||||
\end{enumerate}
|
||||
|
||||
You can repeat these steps for other machines supposed to serve this
|
||||
domain.
|
||||
This module routes packets to other \Jabber{} servers. First, it checks if an
|
||||
open S2S connection from the domain of the packet source to the domain of
|
||||
packet destination already exists. If it is open on another node, then it
|
||||
routes the packet to S2S manager on that node, if it is open on this node, then
|
||||
it is routed to the process that serves this connection, and if a connection
|
||||
does not exist, then it is opened and registered.
|
||||
|
||||
|
||||
\appendix{}
|
||||
@@ -864,7 +757,7 @@ domain.
|
||||
The following options are used by many modules, so they are described in
|
||||
separate section.
|
||||
|
||||
\subsubsection{\option{iqdisc}}
|
||||
\subsubsection{Option \option{iqdisc}}
|
||||
\label{sec:modiqdiscoption}
|
||||
|
||||
Many modules define handlers for processing IQ queries of different namespaces
|
||||
@@ -897,7 +790,7 @@ Example:
|
||||
]}.
|
||||
\end{verbatim}
|
||||
|
||||
\subsubsection{\option{host}}
|
||||
\subsubsection{Option \option{host}}
|
||||
\label{sec:modhostoption}
|
||||
|
||||
This option explicitly defines hostname for the module which acts as a service.
|
||||
@@ -912,71 +805,6 @@ Example:
|
||||
]}.
|
||||
\end{verbatim}
|
||||
|
||||
\subsubsection{\option{hosts}}
|
||||
\label{sec:modhostsoption}
|
||||
|
||||
This option explicitly defines a list of hostnames for the module which acts as
|
||||
a service.
|
||||
|
||||
Example:
|
||||
\begin{verbatim}
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_echo, [{hosts, ["echo.example.org", "echo.example.com"]}]},
|
||||
...
|
||||
]}.
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
\subsection{\modannounce{}}
|
||||
\label{sec:modannounce}
|
||||
|
||||
This module adds support for broadcast announce messages and MOTD.
|
||||
When the module is loaded, it handles messages sent to the following JID's
|
||||
(suppose that main server has address \jid{example.org}):
|
||||
\begin{description}
|
||||
\titem{example.org/announce/all} Message is sent to all registered users at
|
||||
\jid{example.org}. If the user is online and connected to several resources,
|
||||
only resource with the highest priority will receive the message. If the
|
||||
registered user is not connected, the message will be stored offline (if
|
||||
oflline storage is available).
|
||||
\titem{example.org/announce/online} Message is sent to all connected users at
|
||||
\jid{example.org}. If the user is online and connected to several resources,
|
||||
all resources will receive the message.
|
||||
\titem{example.org/announce/all-hosts/online} Message is sent to all connected
|
||||
users at every virtual host. If the user is online and connected to several
|
||||
resources, all resources will receive the message.
|
||||
\titem{example.org/announce/motd} Message is set as MOTD (Message of the Day)
|
||||
and is sent to users at \jid{example.org} as they login. In addition the
|
||||
message is sent to all connected users (similar to \term{announce/online}
|
||||
resource).
|
||||
\titem{example.org/announce/motd/update} Message is set as MOTD (Message of the
|
||||
Day) and is sent to users at \jid{example.org} as they login. The message
|
||||
is \emph{not sent} to all connected users.
|
||||
\titem{example.org/announce/motd/delete} Any message sent to this JID
|
||||
removes existing MOTD.
|
||||
\end{description}
|
||||
|
||||
Options:
|
||||
\begin{description}
|
||||
\titem{access} Specifies who is allowed to send announce messages
|
||||
and set MOTD (default value is \term{none}).
|
||||
\end{description}
|
||||
|
||||
Example:
|
||||
\begin{verbatim}
|
||||
% Only admins can send announcement messages:
|
||||
{access, announce, [{allow, admin}]}.
|
||||
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_announce, [{access, announce}]},
|
||||
...
|
||||
]}.
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
\subsection{\modconfigure{}}
|
||||
\label{sec:modconfigure}
|
||||
@@ -1015,8 +843,8 @@ Example:
|
||||
\subsection{\modecho{}}
|
||||
\label{sec:modecho}
|
||||
|
||||
This module acts as a service and simply returns to sender any \Jabber{}
|
||||
packet. Module may be useful for debugging.
|
||||
This module acts as a service and simply returns to sender any \Jabber{} packet. Module may be
|
||||
useful for debugging.
|
||||
|
||||
Options:
|
||||
\begin{description}
|
||||
@@ -1188,119 +1016,6 @@ Options:
|
||||
\end{description}
|
||||
|
||||
|
||||
\subsection{\modservicelog{}}
|
||||
\label{sec:modservicelog}
|
||||
|
||||
This module adds support for logging of user packets via any jabber service.
|
||||
These packets encapsulated in <route/> element and sended to specified
|
||||
services.
|
||||
|
||||
Options:
|
||||
\begin{description}
|
||||
\titem{loggers} Specifies a list of services which will receive users
|
||||
packets.
|
||||
\end{description}
|
||||
|
||||
Example:
|
||||
\begin{verbatim}
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_service_log, [{loggers, ["bandersnatch.example.com"]}]},
|
||||
...
|
||||
]}.
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
\subsection{\modsharedroster{}}
|
||||
\label{sec:modsharedroster}
|
||||
|
||||
This module implements shared roster groups support.
|
||||
|
||||
You can edit shared roster groups via web-interface. Each group has an unique
|
||||
ID and the following parameters:
|
||||
\begin{description}
|
||||
\item[Name] The name of the group, which will be displayed in roster.
|
||||
\item[Description] Textual description of this group, doesn't affect anything.
|
||||
\item[Members] List of full JIDs of group members, entered one per line in
|
||||
web-interface.
|
||||
\item[Displayed groups] List of IDs of groups which will be in rosters of this
|
||||
group members.
|
||||
\end{description}
|
||||
|
||||
For example, to have a group of users which can see each other in roster,
|
||||
create a group like on table~\ref{tab:srge1}.
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\begin{tabular}{|l|l|}
|
||||
& Group `\texttt{users}'\\
|
||||
Name& Users\\
|
||||
Members&
|
||||
{\begin{tabular}{l}
|
||||
\jid{user1@example.org}\\
|
||||
\jid{user2@example.org}\\
|
||||
\jid{user3@example.org}
|
||||
\end{tabular}
|
||||
}\\
|
||||
Displayed groups& \texttt{users}
|
||||
\end{tabular}
|
||||
\caption{Shared group example N1}
|
||||
\label{tab:srge1}
|
||||
\end{table}
|
||||
|
||||
To have 3 groups `\texttt{managers}', `\texttt{workgroup1}', and
|
||||
`\texttt{workgroup2}', where group `\texttt{managers}' can see members of all
|
||||
groups, and other two groups can see `\texttt{managers}' group and themselves,
|
||||
create groups like on table~\ref{tab:srge2}.
|
||||
\begin{table}[htbp]
|
||||
\centering
|
||||
\begin{tabular}{|l|l|l|l|}
|
||||
&
|
||||
Group `\texttt{managers}'&
|
||||
Group `\texttt{workgroup1}'&
|
||||
Group `\texttt{workgroup2}'\\
|
||||
Name& Managers& Workgroup1& Workgroup2\\
|
||||
Members&
|
||||
{\begin{tabular}{l}
|
||||
\jid{manager1@example.org}\\
|
||||
\jid{manager2@example.org}\\
|
||||
\jid{manager3@example.org}
|
||||
\end{tabular}
|
||||
}&
|
||||
{\begin{tabular}{l}
|
||||
\jid{user1@example.org}\\
|
||||
\jid{user2@example.org}\\
|
||||
\jid{user3@example.org}
|
||||
\end{tabular}
|
||||
}&
|
||||
{\begin{tabular}{l}
|
||||
\jid{user4@example.org}\\
|
||||
\jid{user5@example.org}\\
|
||||
\jid{user6@example.org}
|
||||
\end{tabular}
|
||||
}\\
|
||||
Displayed groups&
|
||||
{\begin{tabular}{l}
|
||||
\texttt{managers}\\
|
||||
\texttt{workgroup1}\\
|
||||
\texttt{workgroup2}
|
||||
\end{tabular}
|
||||
}&
|
||||
{\begin{tabular}{l}
|
||||
\texttt{managers}\\
|
||||
\texttt{workgroup1}
|
||||
\end{tabular}
|
||||
}&
|
||||
{\begin{tabular}{l}
|
||||
\texttt{managers}\\
|
||||
\texttt{workgroup2}
|
||||
\end{tabular}
|
||||
}
|
||||
\end{tabular}
|
||||
\caption{Shared group example N2}
|
||||
\label{tab:srge2}
|
||||
\end{table}
|
||||
|
||||
\subsection{\modstats{}}
|
||||
\label{sec:modstats}
|
||||
|
||||
@@ -1311,6 +1026,8 @@ Options:
|
||||
\iqdiscitem{\ns{http://jabber.org/protocol/stats}}
|
||||
\end{description}
|
||||
|
||||
TBD about access.
|
||||
|
||||
|
||||
\subsection{\modtime{}}
|
||||
\label{sec:modtime}
|
||||
@@ -1333,16 +1050,9 @@ Options:
|
||||
\begin{description}
|
||||
\hostitem{vjud}
|
||||
\iqdiscitem{\ns{vcard-temp}}
|
||||
\titem{search} Specifies whether search is enabled (value is \term{true}, default) or
|
||||
\titem{search} Specifies wheather search is enabled (value is \term{true}, default) or
|
||||
disabled (value is \term{false}) by the service. If \term{search} is set to \term{false},
|
||||
option \term{host} is ignored and service does not appear in Jabber Discovery items.
|
||||
\titem{matches} Limits the number of reported search results. If value is set to
|
||||
\term{infinity} then all search results are reported. Default value is \term{30}.
|
||||
\titem{allow\_return\_all} Specifies whether search with empty input fields can
|
||||
return all known users. Default is \term{false}.
|
||||
\titem{search\_all\_hosts} If set in \term{true} then search returns matched
|
||||
items at all virtual hosts. Otherwise only current host items are returned.
|
||||
Default is \term{true}.
|
||||
\end{description}
|
||||
|
||||
Example:
|
||||
@@ -1350,10 +1060,7 @@ Example:
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_vcard, [{search, true},
|
||||
{matches, 20},
|
||||
{allow_return_all, true},
|
||||
{search_all_hosts, false}]}
|
||||
{mod_vcard, [{search, false}]}
|
||||
...
|
||||
]}.
|
||||
\end{verbatim}
|
||||
@@ -1377,7 +1084,7 @@ All built-in modules support \texttt{xml:lang} attribute inside IQ queries.
|
||||
E.\,g.\ on figure~\ref{fig:discorus} showed the reply on the following query:
|
||||
\begin{verbatim}
|
||||
<iq id='5'
|
||||
to='example.org'
|
||||
to='e.localhost'
|
||||
type='get'
|
||||
xml:lang='ru'>
|
||||
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
Release notes
|
||||
ejabberd 0.9
|
||||
|
||||
This document describes the major new features of and changes to
|
||||
ejabberd 0.9, compared to latest public release ejabber 0.7.5.
|
||||
|
||||
For more detailed information, please refer to ejabberd User
|
||||
Guide.
|
||||
|
||||
|
||||
Virtual Hosting
|
||||
|
||||
ejabberd now can host several domain on the same instance.
|
||||
This option is enabled by using:
|
||||
|
||||
{hosts, ["erlang-projects.org", "erlang-fr.org"]}.
|
||||
|
||||
instead of the previous host directive.
|
||||
|
||||
Note that you are now using a list of hosts. The main one should
|
||||
be the first listed. See migration section further in this release
|
||||
note for details.
|
||||
|
||||
|
||||
Shared Roster
|
||||
|
||||
Shared roster is a new feature that allow the ejabberd
|
||||
administrator to add jabber user that will be present in the
|
||||
roster of every users on the server.
|
||||
Shared roster are enabled by adding:
|
||||
|
||||
{mod_shared_roster, []}
|
||||
|
||||
at the end of your module list in your ejabberd.cfg file.
|
||||
|
||||
|
||||
PostgreSQL (ODBC) support
|
||||
|
||||
This feature is experimental and not yet properly documented. This
|
||||
feature is released for testing purpose.
|
||||
|
||||
You need to have Erlang/OTP R10 to compile with ODBC on various
|
||||
flavour of *nix. You should use Erlang/OTP R10B-4, as this task
|
||||
has became easier with this release. It comes already build in
|
||||
Erlang/OTP Microsoft Windows binary.
|
||||
|
||||
PostgreSQL support is enabled by using the following module in
|
||||
ejabberd.cfg instead of their standard counterpart:
|
||||
|
||||
mod_last_odbc.erl
|
||||
mod_offline_odbc.erl
|
||||
mod_roster_odbc.erl
|
||||
|
||||
The database schema is located in the src/odbc/pq.sql file.
|
||||
|
||||
Look at the src/ejabberd.cfg.example file for more information on
|
||||
how to configure ejabberd with odbc support. You can get support
|
||||
on how to configure ejabberd with a relational database.
|
||||
|
||||
|
||||
Migration from ejabberd 0.7.5
|
||||
|
||||
Migration is pretty straightforward as Mnesia database schema
|
||||
conversions is handled automatically. Remember however that you
|
||||
must backup your ejabberd database before migration.
|
||||
|
||||
Here are the following steps to proceed:
|
||||
|
||||
1. Stop your instance of ejabberd.
|
||||
|
||||
2. In ejabberd.cfg, define the host lists. Change the host
|
||||
directive to the hosts one:
|
||||
Before:
|
||||
{host, "erlang-projects.org"}.
|
||||
After:
|
||||
{hosts, ["erlang-projects.org", "erlang-fr.org"]}.
|
||||
Note that when you restart the server the existing users will be
|
||||
affected to the first virtual host, so the order is important. You
|
||||
should keep the previous hostname as the first virtual host.
|
||||
|
||||
3. Restart ejabberd.
|
||||
|
||||
|
||||
Bugfixes
|
||||
|
||||
This release contains several bugfixes and architectural changes.
|
||||
Please refer to the Changelog file supplied with this release for
|
||||
details of all improvements in the ejabberd code.
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 24 KiB |
@@ -1,50 +0,0 @@
|
||||
#!/usr/local/bin/perl
|
||||
|
||||
use Unix::Syslog qw(:macros :subs);
|
||||
|
||||
my $domain = $ARGV[0] || "example.com";
|
||||
|
||||
while(1)
|
||||
{
|
||||
# my $rin = '',$rout;
|
||||
# vec($rin,fileno(STDIN),1) = 1;
|
||||
# $ein = $rin;
|
||||
# my $nfound = select($rout=$rin,undef,undef,undef);
|
||||
|
||||
my $buf = "";
|
||||
syslog LOG_INFO,"waiting for packet";
|
||||
my $nread = sysread STDIN,$buf,2;
|
||||
do { syslog LOG_INFO,"port closed"; exit; } unless $nread == 2;
|
||||
my $len = unpack "n",$buf;
|
||||
my $nread = sysread STDIN,$buf,$len;
|
||||
|
||||
my ($op,$user,$password) = split /:/,$buf;
|
||||
#$user =~ s/\./\//og;
|
||||
my $jid = "$user\@$domain";
|
||||
my $result;
|
||||
|
||||
syslog(LOG_INFO,"request (%s)", $op);
|
||||
|
||||
SWITCH:
|
||||
{
|
||||
$op eq 'auth' and do
|
||||
{
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
|
||||
$op eq 'setpass' and do
|
||||
{
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
|
||||
$op eq 'isuser' and do
|
||||
{
|
||||
# password is null. Return 1 if the user $user\@$domain exitst.
|
||||
$result = 1;
|
||||
},last SWITCH;
|
||||
};
|
||||
my $out = pack "nn",2,$result ? 1 : 0;
|
||||
syswrite STDOUT,$out;
|
||||
}
|
||||
|
||||
closelog;
|
||||
@@ -1,77 +0,0 @@
|
||||
<!-- aim-transport.xml -->
|
||||
|
||||
<jabber>
|
||||
|
||||
<!--
|
||||
You need to add elogger and rlogger entries when using ejabberd.
|
||||
In this case the transport will do the logging.
|
||||
-->
|
||||
|
||||
<log id='elogger'>
|
||||
<host/>
|
||||
<logtype/>
|
||||
<format>%d: [%t] (%h): %s</format>
|
||||
<file>/var/log/jabber/aim-transport-error.log</file>
|
||||
</log>
|
||||
|
||||
<log id='rlogger'>
|
||||
<host/>
|
||||
<logtype>record</logtype>
|
||||
<format>%d %h %s</format>
|
||||
<file>/var/log/jabber/aim-transport-record.log</file>
|
||||
</log>
|
||||
|
||||
<!--
|
||||
ejabberd do not provide XDB services.
|
||||
xdb_file.so is loaded in to handle all XDB requests.
|
||||
-->
|
||||
|
||||
<xdb id="xdb">
|
||||
<host/>
|
||||
<load>
|
||||
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file> <!-- This file is part of jabberd-1.4.x. -->
|
||||
</load>
|
||||
<xdb_file xmlns="jabber:config:xdb_file">
|
||||
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
|
||||
</xdb_file>
|
||||
</xdb>
|
||||
|
||||
<!--
|
||||
Make sure that all host names here are resolveable via DNS if you
|
||||
want the transport to be available to the public.
|
||||
-->
|
||||
|
||||
<service id='aim.SERVER.COM'>
|
||||
<!-- aim-transport configuration. -->
|
||||
<aimtrans xmlns='jabber:config:aimtrans'>
|
||||
<vCard>
|
||||
<FN>AIM/ICQ Transport</FN>
|
||||
<DESC>This is the AIM/ICQ Transport.</DESC>
|
||||
<MAIL>EMAIL@ADDRESS.COM</MAIL>
|
||||
<URL>http://aim-transport.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
<charset>cp1252</charset>
|
||||
</aimtrans>
|
||||
<!-- aim-transport module. -->
|
||||
<load>
|
||||
<aim_transport>/usr/local/lib/jabber/aim-transport.so</aim_transport>
|
||||
</load>
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The settings below have to match the settings you made
|
||||
in your ejabberd.cfg configuration file.
|
||||
-->
|
||||
|
||||
<service id="icq-linker">
|
||||
<uplink/>
|
||||
<connect>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5233</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
</service>
|
||||
|
||||
<pidfile>/var/run/jabber/aim-transport.pid</pidfile>
|
||||
|
||||
</jabber>
|
||||
@@ -1,136 +0,0 @@
|
||||
<!-- ile.xml -->
|
||||
|
||||
<config>
|
||||
<jabber>
|
||||
<server>127.0.0.1</server>
|
||||
<port>5238</port>
|
||||
<secret>SECRET</secret>
|
||||
<service>ile.SERVER.COM</service>
|
||||
<connectsleep>7</connectsleep> <!-- seconds to wait if we get disconnected -->
|
||||
<language>en</language>
|
||||
<vCard>
|
||||
<FN>I Love Email</FN>
|
||||
<DESC>With this service you can receive email notifications.
|
||||
|
||||
Security warning: Be careful when using this. Your password will travel in clear from your client to your jabber server if you don't use SSL and it will probably travel in clear from the jabber server to your email server. Use with care. This shouldn't be an issue in your Intranet, but it is if you use an ILE installed in a foreign jabber server.</DESC>
|
||||
<MAIL>EMAIL@ADDRESS.COM</MAIL>
|
||||
<URL>http://ile.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
</jabber>
|
||||
|
||||
<debug>
|
||||
<file>/var/log/jabber/ile.log</file>
|
||||
<level>1</level> <!-- man Net::Jabber::Debug -->
|
||||
</debug>
|
||||
|
||||
<mail>
|
||||
<checkinterval>10</checkinterval> <!-- in minutes -->
|
||||
<timeout>20</timeout> <!-- timeout for IMAP/POP connection, in seconds -->
|
||||
</mail>
|
||||
|
||||
<files>
|
||||
<users>/var/spool/jabber/ile.SERVER.COM/users.db</users>
|
||||
<passwords>/var/spool/jabber/ile.SERVER.COM/passwords.db</passwords>
|
||||
<hosts>/var/spool/jabber/ile.SERVER.COM/hosts.db</hosts>
|
||||
<types>/var/spool/jabber/ile.SERVER.COM/types.db</types>
|
||||
<notifyxa>/var/spool/jabber/ile.SERVER.COM/notifyxa.db</notifyxa>
|
||||
<notifydnd>/var/spool/jabber/ile.SERVER.COM/notifydnd.db</notifydnd>
|
||||
<urls>/var/spool/jabber/ile.SERVER.COM/urls.db</urls>
|
||||
</files>
|
||||
|
||||
<form>
|
||||
<en>
|
||||
<instructions>Please fill in the fields,according to your email account settings and notification preferences</instructions>
|
||||
<title>ILE: Email notification service</title>
|
||||
<email_options>Email account settings</email_options>
|
||||
<user>Username</user>
|
||||
<pass>Password</pass>
|
||||
<host>Hostname</host>
|
||||
<type>Type</type>
|
||||
<newmail>You have received NUM email messages since last time I checked, which was CHECKINTERVAL minutes ago.</newmail>
|
||||
<errorcheck>There was an error while trying to check mail for ACCOUNT.</errorcheck>
|
||||
<notify_options>Notification Options</notify_options>
|
||||
<notifyxa>Notify even when Xtended Away (XA)</notifyxa>
|
||||
<notifydnd>Notify even when Do Not Disturb (DND)</notifydnd>
|
||||
<webmail_url>Webmail URL</webmail_url>
|
||||
<webmail_login>Login to ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: an email notifier component: http://ile.jabberstudio.org</iledesc>
|
||||
</en>
|
||||
|
||||
<es>
|
||||
<instructions>Por favor, rellene los campos del formulario.</instructions>
|
||||
<title>ILE: Servicio de notificación de correo</title>
|
||||
<email_options>Configuración de la cuenta de correo</email_options>
|
||||
<user>Usuario</user>
|
||||
<pass>Clave</pass>
|
||||
<host>Host</host>
|
||||
<type>Tipo</type>
|
||||
<newmail>Ha recibido NUM email(s) desde la última comprobación que fue hace CHECKINTERVAL minutos</newmail>
|
||||
<errorcheck>Ha habido un error en la comprobación del correo para la cuenta ACCOUNT.</errorcheck>
|
||||
<notify_options>Opciones de notificación</notify_options>
|
||||
<notifyxa>Notificar incluso si muy ausente (XA)</notifyxa>
|
||||
<notifydnd>Notificar incluso si no molestar (DND)</notifydnd>
|
||||
<webmail_url>Webmail URL</webmail_url>
|
||||
<webmail_login>Leer correo de ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: un notificador de nuevo email - http://ile.jabberstudio.org</iledesc>
|
||||
</es>
|
||||
|
||||
<ca>
|
||||
<instructions>Ompli els camps del formulari.</instructions>
|
||||
<title>ILE: Servei de notificació de nou email</title>
|
||||
<email_options>Dades del compte de mail</email_options>
|
||||
<user>Usuari</user>
|
||||
<pass>Clau</pass>
|
||||
<host>Host</host>
|
||||
<type>Tipus</type>
|
||||
<newmail>Ha rebut NUM email(s) des de la última comprobació que va ser fa CHECKINTERVAL minuts.</newmail>
|
||||
<errorcheck>S'ha produit un error en la comprobació del correu per al compte ACCOUNT.</errorcheck>
|
||||
<notify_options>Opcions de notificació</notify_options>
|
||||
<notifyxa>Notificar si molt absent (XA)</notifyxa>
|
||||
<notifydnd>Notificar si no molestar (DND)</notifydnd>
|
||||
<webmail_url>Webmail URL</webmail_url>
|
||||
<webmail_login>Llegir correu de ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: un notificador de nou email - http://ile.jabberstudio.org</iledesc>
|
||||
</ca>
|
||||
|
||||
<ro>
|
||||
<!-- Contributed by Adrian Rappa -->
|
||||
<instructions>Va rog completati urmatoarele campuri</instructions>
|
||||
<title>I Love Email: new email notification service</title>
|
||||
<email_options>Email account settings</email_options>
|
||||
<user>Nume utilizator</user>
|
||||
<pass>Parola</pass>
|
||||
<host>Nume gazda</host>
|
||||
<type>Tip</type>
|
||||
<newmail>Ati primit NUM mesaj(e) de la ultima verificare, care a fost acum CHECKINTERVAL minute.</newmail>
|
||||
<errorcheck>A fost eroare in timp ce incercam sa verific posta pentru ACCOUNT.</errorcheck>
|
||||
<notify_options>Notification Options</notify_options>
|
||||
<notifyxa>Notify even when Xtended Away (XA)</notifyxa>
|
||||
<notifydnd>Notify even when Do Not Disturb (DND)</notifydnd>
|
||||
<webmail_url>Webmail URL</webmail_url>
|
||||
<webmail_login>Login to ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: an email notifier component: http://ile.jabberstudio.org</iledesc>
|
||||
</ro>
|
||||
|
||||
<nl>
|
||||
<!-- Contributed by Sander Devrieze -->
|
||||
<instructions>Vul volgende velden in.</instructions>
|
||||
<title>ILE: Dienst voor e-mailnotificaties</title>
|
||||
<email_options>Instellingen van e-mailaccount</email_options>
|
||||
<user>Gebruikersnaam</user>
|
||||
<pass>Wachtwoord</pass>
|
||||
<host>Inkomende mailserver</host>
|
||||
<type>Type verbinding</type>
|
||||
<newmail>U hebt NUM berichten ontvangen sinds CHECKINTERVAL minuten geleden.</newmail>
|
||||
<errorcheck>Fout tijdens controle op nieuwe e-mails bij ACCOUNT. ILE zal deze account niet meer opnieuw controleren tot u uw registratiegegevens wijzigt of opnieuw aanmeldt.</errorcheck>
|
||||
<notify_options>Notificatie-instellingen</notify_options>
|
||||
<notifyxa>Notificeer ook in de status Niet Beschikbaar (XA)</notifyxa>
|
||||
<notifydnd>Notificeer ook in de status Niet Storen (DND)</notifydnd>
|
||||
<webmail_url>URL van webmail</webmail_url>
|
||||
<webmail_login>Aanmelden op ACCOUNT</webmail_login>
|
||||
<iledesc>ILE: een dienst om e-mailnotificaties te ontvangen: http://ile.jabberstudio.org</iledesc>
|
||||
</nl>
|
||||
|
||||
</form>
|
||||
|
||||
</config>
|
||||
@@ -1,149 +0,0 @@
|
||||
<jggtrans>
|
||||
|
||||
<service jid="gg.SERVER.COM"/>
|
||||
|
||||
<!-- This connects the jabber-gg-transport process to ejabberd. -->
|
||||
<connect id="gglinker">
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5237</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
|
||||
<register>
|
||||
<!-- This tag contains the message displayed to users at registration time.
|
||||
You can use <p/> and/or <br/> to break lines. Multiple spaces and newlines
|
||||
are converted to just one, so formatting of config file doesn't really matter. -->
|
||||
<instructions>
|
||||
Fill in your GG number (after "username")
|
||||
and password to register on the transport.
|
||||
<p/>To change your information in the GaduGadu directory you need to fill in the other fields.
|
||||
<p/>To remove registration you need to leave the form blank.
|
||||
</instructions>
|
||||
</register>
|
||||
|
||||
<search>
|
||||
<!-- This tag contains the message displayed to users at search time. -->
|
||||
<instructions>
|
||||
To search people:<br/>
|
||||
First fill in surname or family name, nickname, city, birthyear or range of birthyears (eg. 1950-1960)
|
||||
and gender (you may fill in more fields at once).<br/>
|
||||
or<br/>
|
||||
Fill in phone number<br/>
|
||||
or<br/>
|
||||
Fill in the GG number of the person you are searching.
|
||||
</instructions>
|
||||
</search>
|
||||
|
||||
<gateway>
|
||||
<!-- This is message, that may be displayed to user when adding gg contact. -->
|
||||
<desc>Please fill in the GaduGadu number of the person you want to add.</desc>
|
||||
<!-- And this is the prompt for GG number. -->
|
||||
<prompt>GG Nummer</prompt>
|
||||
</gateway>
|
||||
|
||||
<vCard>
|
||||
<FN>Gadu-Gadu Transport</FN>
|
||||
<DESC>This is the Gadu-Gadu Transport.</DESC>
|
||||
<EMAIL>EMAIL@ADDRESS.COM</EMAIL>
|
||||
<URL>http://www.jabberstudio.org/projects/jabber-gg-transport/</URL>
|
||||
</vCard>
|
||||
|
||||
<!-- Default user locale (language).
|
||||
Empty means system locale setting,
|
||||
no (or commented-out) <default_locale> tag means no translations. -->
|
||||
<!-- <default_locale>pl_PL</default_locale> -->
|
||||
|
||||
<!-- Logger configuration.
|
||||
You may configure one logger of type "syslog" and/or one of type "file".
|
||||
You may also not configure logging at all. -->
|
||||
<log type="syslog" facility="local0"/>
|
||||
<log type="file">/var/log/jabber/jabber-gg-transport.log</log>
|
||||
|
||||
<!-- Uncomment this, if you want proxy to be used for Gadu-Gadu connection. -->
|
||||
<!--
|
||||
<proxy>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>8080</port>
|
||||
</proxy>
|
||||
-->
|
||||
|
||||
<!-- You can change these values according to your needs. -->
|
||||
<conn_timeout>60</conn_timeout>
|
||||
<ping_interval>10</ping_interval>
|
||||
|
||||
<!-- Gadu-Gadu server doesn't seem to answer pings anymore :-(
|
||||
So let's give it 10 year :-) -->
|
||||
<pong_timeout>315360000</pong_timeout>
|
||||
|
||||
<!-- This time after disconnection from Gadu-Gadu server the transport
|
||||
will try to connect again. -->
|
||||
<reconnect>300</reconnect>
|
||||
|
||||
<!-- How long to wait before restart, after jabber server connection is broken
|
||||
negative value means, that jggtrans should terminate. -->
|
||||
<restart_timeout>60</restart_timeout>
|
||||
|
||||
<!-- Delay between the unavailable presence is received from user and loggin out
|
||||
from Gadu-Gadu - for nice <presence type="invisible"/> support. -->
|
||||
<disconnect_delay>5</disconnect_delay>
|
||||
|
||||
<!-- list of Gadu-Gadu servers to use.
|
||||
<hub/> means "use GG hub to find server"
|
||||
<server/> tag should contain server address and may contain "port"
|
||||
attribute with port number. When TLS is available (supported by libgadu)
|
||||
it will be used unless "tls" attribute is set to "no". Please notice,
|
||||
that not all servers will accept TLS connections.
|
||||
Servers (including hub) are tried in order as they appear in <servers/>
|
||||
element.
|
||||
A reasonable default server list is hardcoded in jggtrans.
|
||||
-->
|
||||
<!--
|
||||
<servers>
|
||||
<hub/>
|
||||
<server port="443">217.17.41.90</server>
|
||||
<server tls="no">217.17.41.85</server>
|
||||
<server tls="no">217.17.41.88</server>
|
||||
</servers>
|
||||
-->
|
||||
|
||||
<!-- Spool directory. This is the place, where user info will be stored. -->
|
||||
<!-- Be careful about permissions - users' Gadu-Gadu passwords are stored there. -->
|
||||
<spool>/var/spool/jabber/gg.SERVER.COM/</spool>
|
||||
|
||||
<!-- Where to store pid file. This tag is optional. -->
|
||||
<pidfile>/var/run/jabber/jabber-gg-transport.pid</pidfile>
|
||||
|
||||
<!-- jid allowed to do some administrative task (eg. discovering online users).
|
||||
May be used multiple times. -->
|
||||
<admin>GG_TRANSPORT_ADMIN@SERVER.COM</admin>
|
||||
|
||||
<!-- ACL gives detailed access control to the transport. -->
|
||||
<acl>
|
||||
<!-- Example entries: -->
|
||||
|
||||
<allow who="admin@SERVER.COM" what="iq/query?xmlns=http://jabber.org/protocol/stats"/>
|
||||
<!-- will allow statistics gathering to admin@SERVER.COM -->
|
||||
|
||||
<deny who="*" what="iq/query?xmlns=http://jabber.org/protocol/stats"/>
|
||||
<!-- will deny statistics gathering for anybody else -->
|
||||
|
||||
<!-- <allow who="*@SERVER.COM"/> -->
|
||||
<!-- will allow anything else to users from "SERVER.COM" -->
|
||||
|
||||
<!-- <deny what="iq/query?xmlns=jabber:x:register"/> -->
|
||||
<!-- will deny registration for all other users -->
|
||||
|
||||
<!-- <allow what="presence"/> -->
|
||||
<!-- allow presence from anybody -->
|
||||
|
||||
<!-- <allow what="iq"/> -->
|
||||
<!-- allow iq from anybody -->
|
||||
|
||||
<!-- <allow what="message"/> -->
|
||||
<!-- allow message from anybody -->
|
||||
|
||||
<!-- <deny/> -->
|
||||
<!-- will deny anything else -->
|
||||
</acl>
|
||||
|
||||
</jggtrans>
|
||||
@@ -1,128 +0,0 @@
|
||||
<!-- jit.xml -->
|
||||
|
||||
<jabber>
|
||||
|
||||
<!--
|
||||
You need to add elogger and rlogger entries here when using ejabberd.
|
||||
In this case the transport will do the logging.
|
||||
-->
|
||||
|
||||
<log id='elogger'>
|
||||
<host/>
|
||||
<logtype/>
|
||||
<file>/var/log/jabber/jit-error</file> <!-- WPJabber logs with date. -->
|
||||
</log>
|
||||
|
||||
<log id='rlogger'>
|
||||
<host/>
|
||||
<logtype>record</logtype>
|
||||
<file>/var/log/jabber/jit-record</file> <!-- WPJabber logs with date. -->
|
||||
</log>
|
||||
|
||||
<!--
|
||||
ejabberd do not provide XDB services.
|
||||
xdb_file-jit.so (the renamed xdb_file.so from WPJabber) is
|
||||
loaded in to handle all XDB requests.
|
||||
Read also the documentation in xdb_file/README from the JIT package.
|
||||
-->
|
||||
|
||||
<xdb id="xdb">
|
||||
<host/>
|
||||
<load>
|
||||
<xdb_file>/usr/local/lib/jabber/xdb_file.so</xdb_file> <!-- The xdb_file.so from WPJabber/JIT. -->
|
||||
</load>
|
||||
<xdb_file xmlns="jabber:config:xdb_file">
|
||||
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
|
||||
</xdb_file>
|
||||
</xdb>
|
||||
|
||||
<!--
|
||||
Make sure that all host names here are resolveable via DNS if you
|
||||
want the transport to be available to the public.
|
||||
-->
|
||||
|
||||
<service id="icq.SERVER.COM">
|
||||
<!--
|
||||
Replace SERVER.COM with the same as above to enable sms.
|
||||
-->
|
||||
<host>sms.icq.SERVER.COM</host>
|
||||
<!-- JIT configuration. -->
|
||||
<icqtrans xmlns="jabber:config:icqtrans">
|
||||
<sms>
|
||||
<host>sms.icq.SERVER.COM</host>
|
||||
<!-- Status of virtual "sms-contacts". -->
|
||||
<show>away</show>
|
||||
<status/>
|
||||
</sms>
|
||||
<instructions>Fill in your UIN and password.</instructions>
|
||||
<search>Search ICQ users.</search>
|
||||
<vCard>
|
||||
<FN>ICQ Transport (JIT)</FN>
|
||||
<DESC>This is the Jabber ICQ Transport.</DESC>
|
||||
<MAIL>EMAIL@ADDRESS.COM</MAIL>
|
||||
<URL>http://jit.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
<!-- Hashtable for users. -->
|
||||
<prime>3907</prime>
|
||||
<!-- Send messages from ICQ as chat to Jabber clients. -->
|
||||
<chat/>
|
||||
<!-- Enable this for ICQ web presence. -->
|
||||
<web/>
|
||||
<!--
|
||||
If you don't want jabber:x:data forms
|
||||
in reg and search uncomment this tag
|
||||
(Not recomended).
|
||||
-->
|
||||
<no_xdata/>
|
||||
<!--
|
||||
This tag is necessary when using ejabberd.
|
||||
In this way JIT will have its own contact list.
|
||||
-->
|
||||
<own_roster/>
|
||||
<!--
|
||||
When present, this tag will tell JIT not to try to
|
||||
get the user's roster (which will take a bit of time
|
||||
to fail in scenarios described above).
|
||||
-->
|
||||
<no_jabber_roster/>
|
||||
<!-- File with stats. -->
|
||||
<user_count_file>/var/spool/jabber/jit-count</user_count_file>
|
||||
<!--
|
||||
Interval beetween checking sessions: ping, messages, acks.
|
||||
-->
|
||||
<session_check>5</session_check>
|
||||
<!-- Reconnect retries. -->
|
||||
<reconnects>5</reconnects>
|
||||
<!--
|
||||
Time in sec when session can be inactive, 0=disabled.
|
||||
-->
|
||||
<session_timeout>18000</session_timeout>
|
||||
<charset>windows-1252</charset>
|
||||
<server>
|
||||
<host port="5190">login.icq.com</host>
|
||||
</server>
|
||||
</icqtrans>
|
||||
<!-- JIT module. -->
|
||||
<load>
|
||||
<icqtrans>/usr/local/lib/jabber/jit.so</icqtrans>
|
||||
</load>
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The settings below have to match the settings you made
|
||||
in your ejabberd.cfg configuration file.
|
||||
-->
|
||||
|
||||
<service id="icq-linker">
|
||||
<host>SERVER.COM</host>
|
||||
<uplink/>
|
||||
<connect>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5234</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
</service>
|
||||
|
||||
<pidfile>/var/run/jabber/jit.pid</pidfile>
|
||||
|
||||
</jabber>
|
||||
@@ -1,118 +0,0 @@
|
||||
<!-- msn-transport.xml -->
|
||||
|
||||
<jabber>
|
||||
|
||||
<!--
|
||||
You need to add elogger and rlogger entries here when using ejabberd.
|
||||
In this case the transport will do the logging.
|
||||
-->
|
||||
|
||||
<log id='elogger'>
|
||||
<host/>
|
||||
<logtype/>
|
||||
<format>%d: [%t] (%h): %s</format>
|
||||
<file>/var/log/jabber/msn-transport-error.log</file>
|
||||
</log>
|
||||
|
||||
<log id='rlogger'>
|
||||
<host/>
|
||||
<logtype>record</logtype>
|
||||
<format>%d %h %s</format>
|
||||
<file>/var/log/jabber/msn-transport-record.log</file>
|
||||
</log>
|
||||
|
||||
<!--
|
||||
ejabberd do not provide XDB services.
|
||||
xdb_file.so is loaded in to handle all XDB requests.
|
||||
-->
|
||||
|
||||
<xdb id="xdb">
|
||||
<host/>
|
||||
<load>
|
||||
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file>
|
||||
</load>
|
||||
<xdb_file xmlns="jabber:config:xdb_file">
|
||||
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
|
||||
</xdb_file>
|
||||
</xdb>
|
||||
|
||||
<!--
|
||||
Make sure that all host names here are resolveable via DNS if you
|
||||
want the transport to be available to the public.
|
||||
-->
|
||||
|
||||
<service id="msn.SERVER.COM">
|
||||
<!-- msn-transport configuration. -->
|
||||
<msntrans xmlns="jabber:config:msntrans">
|
||||
<instructions>Fill in your MSN account and password (eg: user1@hotmail.com). A nickname is optional.</instructions>
|
||||
<vCard>
|
||||
<FN>MSN Transport</FN>
|
||||
<DESC>This is the MSN Transport.</DESC>
|
||||
<EMAIL>EMAIL@ADDRESS.COM</EMAIL>
|
||||
<URL>http://msn-transport.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
<!--
|
||||
Conference support allows you to create groupchat rooms on the
|
||||
msn-transport and invite MSN users to join.
|
||||
-->
|
||||
<conference id="conference.msn.SERVER.COM">
|
||||
<!--
|
||||
This will make MSN transport invite you to a special groupchat
|
||||
room when more then one user joins a normal one-on-one session.
|
||||
Joining this room will make MSN transport "switch" the session
|
||||
into groupchat mode. If you ignore it, MSN transport will
|
||||
continue to send the messages as one-on-one chats.
|
||||
-->
|
||||
<invite>More than one user entered this chat session. Enter this room to switch to groupchat modus.</invite>
|
||||
<notice>
|
||||
<join> is available</join>
|
||||
<leave> has leaved the room</leave>
|
||||
</notice>
|
||||
</conference>
|
||||
<!-- Enable Hotmail inbox notification. -->
|
||||
<headlines/>
|
||||
<!--
|
||||
Enable fancy friendly names
|
||||
If the user enters a nickname upon registration, and the user has
|
||||
a status message, their MSN friendly name will be "nickname - status message".
|
||||
|
||||
If the user does not enter a nickname on registration, but they do have
|
||||
a status message, their friendly name will just be their status message.
|
||||
|
||||
If the user did enter a nickname on registration, but they have a blank status message,
|
||||
then their friendly name will just be the registered nickname.
|
||||
|
||||
If the user did not enter a nickname on registration, and they have a blank status message,
|
||||
their nickname will just be the username portion of their JID.
|
||||
|
||||
If the above chosen friendly name is too long, then it will be truncated and "..." placed
|
||||
at the end. MSN only supports friendly names of 128 characters, so this is unavoidable.
|
||||
|
||||
If this is disabled, then the registered nick is always sent as the MSN friendly name,
|
||||
or if that is blank, the username portion of their JID is sent instead.
|
||||
-->
|
||||
<fancy_friendly/>
|
||||
</msntrans>
|
||||
<!-- msn-transport module. -->
|
||||
<load>
|
||||
<msntrans>/usr/local/lib/jabber/msn-transport.so</msntrans>
|
||||
</load>
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The settings below have to match the settings you made
|
||||
in your ejabberd.cfg configuration file.
|
||||
-->
|
||||
|
||||
<service id="msn-linker">
|
||||
<uplink/>
|
||||
<connect>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5235</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
</service>
|
||||
|
||||
<pidfile>/var/run/jabber/msn-transport.pid</pidfile>
|
||||
|
||||
</jabber>
|
||||
@@ -1,86 +0,0 @@
|
||||
<!-- yahoo-transport-2.xml -->
|
||||
|
||||
<jabber>
|
||||
|
||||
<!--
|
||||
You need to add the elogger entry here when using ejabberd.
|
||||
In this case the transport will do the logging.
|
||||
-->
|
||||
|
||||
<log id='elogger'>
|
||||
<host/>
|
||||
<logtype/>
|
||||
<format>%d: [%t] (%h): %s</format>
|
||||
<file>/var/log/jabber/yahoo-transport-2-error.log</file>
|
||||
<stderr/>
|
||||
</log>
|
||||
|
||||
<!--
|
||||
ejabberd do not provide XDB services.
|
||||
xdb_file.so is loaded in to handle all XDB requests.
|
||||
-->
|
||||
|
||||
<xdb id="xdb">
|
||||
<host/>
|
||||
<load>
|
||||
<xdb_file>/usr/local/lib/jabber/libjabberdxdbfile.so</xdb_file>
|
||||
</load>
|
||||
<xdb_file xmlns="jabber:config:xdb_file">
|
||||
<spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
|
||||
</xdb_file>
|
||||
</xdb>
|
||||
|
||||
<!--
|
||||
Make sure that all host names here are resolveable via DNS if you
|
||||
want the transport to be available to the public.
|
||||
-->
|
||||
|
||||
<service id="yahoo.SERVER.COM">
|
||||
<!-- yahoo-transport-2 configuration. -->
|
||||
<config xmlns="jabber:config:yahoo">
|
||||
<vCard>
|
||||
<NAME>Yahoo! Transport</NAME>
|
||||
<FN>vCard not implemented in current version</FN>
|
||||
<DESC>This is the Yahoo! transport.</DESC>
|
||||
<MAIL>EMAIL@ADDRESS.COM</MAIL>
|
||||
<URL>http://yahoo-transport-2.jabberstudio.org/</URL>
|
||||
</vCard>
|
||||
<instructions>Fill in your YAHOO! Messenger username and password to register on this transport.</instructions>
|
||||
<server>scs.msg.yahoo.com</server>
|
||||
<port>5050</port>
|
||||
<!--
|
||||
The character map. This provides character set translation from UTF-8
|
||||
to the indicated character map. See the man page for 'iconv' for available
|
||||
character maps on your platform. CP1252 is the standard Windows character
|
||||
set.
|
||||
-->
|
||||
<charmap>CP1252</charmap>
|
||||
<!--
|
||||
When this element exists, the transport will send new mail notifications as
|
||||
well as a count of unread messages when the user initially logs in.
|
||||
-->
|
||||
<newmail/>
|
||||
</config>
|
||||
<!-- yahoo-transport-2 module. -->
|
||||
<load>
|
||||
<yahoo_transport>/usr/local/lib/jabber/yahoo-transport-2.so</yahoo_transport>
|
||||
</load>
|
||||
</service>
|
||||
|
||||
<!--
|
||||
The settings below have to match the settings you made
|
||||
in your ejabberd.cfg configuration file.
|
||||
-->
|
||||
|
||||
<service id="yahoo-linker">
|
||||
<uplink/>
|
||||
<connect>
|
||||
<ip>127.0.0.1</ip>
|
||||
<port>5236</port>
|
||||
<secret>SECRET</secret>
|
||||
</connect>
|
||||
</service>
|
||||
|
||||
<pidfile>/var/run/jabber/yahoo-transport-2.pid</pidfile>
|
||||
|
||||
</jabber>
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# aim-transport -- script to start aim-transport.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/jabberd-aim-transport
|
||||
CONF=/etc/jabber/aim-transport.xml
|
||||
NAME=jabberd-aim-transport
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# ile -- script to start ILE.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/ile.pl
|
||||
NAME=ile.pl
|
||||
CONF=/etc/jabber/ile.xml
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
echo "Not implemented yet. Starting in normal mode"
|
||||
$0 start
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|status|restart}"
|
||||
exit 1
|
||||
esac
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# jabber-gg-transport -- script to start jabber-gg-transport.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/jggtrans
|
||||
CONF=/etc/jabber/jabber-gg-transport.xml
|
||||
NAME=jggtrans
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
rm /var/run/jabber/jabber-gg-transport.pid
|
||||
;;
|
||||
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# jit -- script to start JIT.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/wpjabber-jit
|
||||
CONF=/etc/jabber/jit.xml
|
||||
NAME=wpjabber-jit
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/bin/sh
|
||||
#########################################################
|
||||
#
|
||||
# msn-transport -- script to start MSN Transport.
|
||||
#
|
||||
#########################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/jabberd-msn-transport
|
||||
CONF=/etc/jabber/msn-transport.xml
|
||||
NAME=jabberd-msn-transport
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#########################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
strace)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in strace mode."
|
||||
strace -o /opt/ejabberd/var/log/jabber/strace.log $DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/sh
|
||||
##############################################################
|
||||
#
|
||||
# yahoo-transport-2 -- script to start Yahoo-transport-2.
|
||||
#
|
||||
#############################################################
|
||||
|
||||
DAEMON=/usr/local/sbin/jabberd-yahoo-transport-2
|
||||
CONF=/etc/jabber/yahoo-transport-2.xml
|
||||
NAME=jabberd-yahoo-transport-2
|
||||
HOME=/etc/jabber/
|
||||
USER=ejabberd
|
||||
|
||||
#############################################################
|
||||
|
||||
if [ "`/usr/bin/whoami`" != "$USER" ]; then
|
||||
|
||||
echo "You need to be" $USER "user to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
debug)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME in debugging mode."
|
||||
$DAEMON -D -H $HOME -c $CONF &
|
||||
;;
|
||||
start)
|
||||
test -f $DAEMON -a -f $CONF || exit 0
|
||||
echo "Starting $NAME."
|
||||
$DAEMON -H $HOME -c $CONF &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping $NAME."
|
||||
killall $NAME &
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
sleep 3
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {debug|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
@@ -6,15 +6,14 @@ CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
EXPAT_CFLAGS = @EXPAT_CFLAGS@
|
||||
ERLANG_CFLAGS= @ERLANG_CFLAGS@
|
||||
|
||||
EXPAT_LIBS = @EXPAT_LIBS@
|
||||
ERLANG_LIBS = @ERLANG_LIBS@
|
||||
|
||||
prefix = @prefix@
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@
|
||||
INCLUDES = @ERLANG_CFLAGS@ @EXPAT_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@ @EXPAT_LIBS@
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep
|
||||
|
||||
ERLSHLIBS = expat_erl.so
|
||||
|
||||
DESTDIR =
|
||||
@@ -30,7 +29,7 @@ ETCDIR = $(DESTDIR)/etc/ejabberd
|
||||
all: $(ERLSHLIBS) compile-beam all-recursive
|
||||
|
||||
compile-beam:
|
||||
@ERL@ -s make all report -noinput -s erlang halt
|
||||
@erl -s make all report -noinput -s erlang halt
|
||||
|
||||
|
||||
all-recursive install-recursive uninstall-recursive \
|
||||
@@ -44,11 +43,12 @@ mostlyclean-recursive maintainer-clean-recursive:
|
||||
|
||||
|
||||
$(ERLSHLIBS): %.so: %.c
|
||||
gcc -Wall $(CFLAGS) $(LDFLAGS) $(LIBS) \
|
||||
gcc -Wall $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(LIBDIRS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) \
|
||||
$(EXPAT_LIBS) $(EXPAT_CFLAGS) \
|
||||
$(ERLANG_LIBS) $(ERLANG_CFLAGS) \
|
||||
-o $@ -fpic -shared
|
||||
-lexpat \
|
||||
-lerl_interface \
|
||||
-lei \
|
||||
-o $@ -fpic -shared \
|
||||
|
||||
install: all
|
||||
install -d $(BEAMDIR)
|
||||
|
||||
@@ -45,6 +45,8 @@ release : build release_clean
|
||||
copy $(SYSTEMROOT)\system32\libeay32.dll $(EREL)
|
||||
copy $(SYSTEMROOT)\system32\ssleay32.dll $(EREL)
|
||||
copy win32\ejabberd.ico $(WIN32_DIR)
|
||||
mkdir $(WIN32_DIR)\5.3
|
||||
copy win32\5.3\*.beam $(WIN32_DIR)\5.3
|
||||
mkdir $(SRC_DIR)
|
||||
copy *.app $(SRC_DIR)
|
||||
copy *.erl $(SRC_DIR)
|
||||
@@ -64,12 +66,11 @@ release : build release_clean
|
||||
copy stringprep\*.erl $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.c $(SRC_DIR)\stringprep
|
||||
copy stringprep\*.tcl $(SRC_DIR)\stringprep
|
||||
mkdir $(SRC_DIR)\tls
|
||||
copy tls\*.erl $(SRC_DIR)\tls
|
||||
mkdir $(SRC_DIR)\web
|
||||
copy web\*.erl $(SRC_DIR)\web
|
||||
mkdir $(SRC_DIR)\odbc
|
||||
copy odbc\*.erl $(SRC_DIR)\odbc
|
||||
mkdir $(SRC_DIR)\win32
|
||||
mkdir $(SRC_DIR)\win32\5.3
|
||||
copy win32\5.3\*.erl $(SRC_DIR)\win32\5.3
|
||||
mkdir $(DOC_DIR)
|
||||
copy ..\doc\*.html $(DOC_DIR)
|
||||
copy ..\doc\*.png $(DOC_DIR)
|
||||
@@ -91,13 +92,11 @@ all-recursive :
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\stringprep
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\tls
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\web
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..\odbc
|
||||
cd ..\win32\5.3
|
||||
nmake -nologo -f Makefile.win32
|
||||
cd ..
|
||||
cd ..\..
|
||||
|
||||
compile-beam :
|
||||
erl -s make all report -noinput -s erlang halt
|
||||
@@ -122,13 +121,11 @@ clean-recursive :
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\stringprep
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\tls
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\web
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..\odbc
|
||||
cd ..\win32\5.3
|
||||
nmake -nologo -f Makefile.win32 clean
|
||||
cd ..
|
||||
cd ..\..
|
||||
|
||||
distclean : release_clean clean
|
||||
-@erase $(NSIS_HEADER)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
AC_DEFUN(AM_WITH_EXPAT,
|
||||
[ AC_ARG_WITH(expat,
|
||||
[ --with-expat=PREFIX prefix where EXPAT is installed])
|
||||
[ --with-expat=PREFIX path to expat library],
|
||||
, with_expat=no)
|
||||
|
||||
EXPAT_CFLAGS=
|
||||
EXPAT_LIBS=
|
||||
if test x"$with_expat" != x; then
|
||||
if test $with_expat != no; then
|
||||
if test $with_expat != yes; then
|
||||
EXPAT_CFLAGS="-I$with_expat/include"
|
||||
EXPAT_LIBS="-L$with_expat/lib"
|
||||
fi
|
||||
|
||||
AC_CHECK_LIB(expat, XML_ParserCreate,
|
||||
[ EXPAT_LIBS="$EXPAT_LIBS -lexpat"
|
||||
expat_found=yes ],
|
||||
@@ -24,6 +25,7 @@ AC_DEFUN(AM_WITH_EXPAT,
|
||||
AC_MSG_ERROR([Could not find expat.h])
|
||||
fi
|
||||
CFLAGS="$expat_save_CFLAGS"
|
||||
fi
|
||||
|
||||
AC_SUBST(EXPAT_CFLAGS)
|
||||
AC_SUBST(EXPAT_LIBS)
|
||||
@@ -73,7 +75,7 @@ _EOF
|
||||
ERLANG_DIR=`cat conftest.out | tail -n 1`
|
||||
|
||||
ERLANG_CFLAGS="-I$ERLANG_EI_DIR/include -I$ERLANG_DIR/usr/include"
|
||||
ERLANG_LIBS="-L$ERLANG_EI_DIR/lib -lerl_interface -lei"
|
||||
ERLANG_LIBS="-L$ERLANG_EI_DIR/lib"
|
||||
|
||||
AC_SUBST(ERLANG_CFLAGS)
|
||||
AC_SUBST(ERLANG_LIBS)
|
||||
@@ -91,7 +93,7 @@ AC_ARG_ENABLE($1,
|
||||
[ --enable-$1 enable $1 (default: $2)],
|
||||
[mr_enable_$1="$enableval"],
|
||||
[mr_enable_$1=$2])
|
||||
if test "$mr_enable_$1" = "yes"; then
|
||||
if test "$mr_enable_$1" = $2; then
|
||||
$1=$1
|
||||
make_$1=$1/Makefile
|
||||
fi
|
||||
@@ -108,8 +110,9 @@ AC_DEFUN([AM_ICONV],
|
||||
[
|
||||
dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
|
||||
dnl those with the standalone portable GNU libiconv installed).
|
||||
|
||||
AC_ARG_WITH([libiconv-prefix],
|
||||
[ --with-libiconv-prefix=PREFIX prefix where libiconv is installed], [
|
||||
[ --with-libiconv-prefix=DIR Search for libiconv in DIR/include and DIR/lib], [
|
||||
for dir in `echo "$withval" | tr : ' '`; do
|
||||
if test -d $dir/include; then CPPFLAGS="$CPPFLAGS -I$dir/include"; fi
|
||||
if test -d $dir/include; then CFLAGS="$CFLAGS -I$dir/include"; fi
|
||||
@@ -190,40 +193,3 @@ size_t iconv();
|
||||
fi
|
||||
AC_SUBST(LIBICONV)
|
||||
])
|
||||
|
||||
dnl <openssl>
|
||||
AC_DEFUN(AM_WITH_OPENSSL,
|
||||
[ AC_ARG_WITH(openssl,
|
||||
[ --with-openssl=PREFIX prefix where OPENSSL is installed ])
|
||||
unset SSL_LIBS;
|
||||
unset SSL_CFLAGS;
|
||||
have_openssl=no
|
||||
if test x"$tls" != x; then
|
||||
for ssl_prefix in $withval /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr; do
|
||||
printf "looking for openssl in $ssl_prefix...\n"
|
||||
SSL_CFLAGS="-I$ssl_prefix/include/openssl"
|
||||
SSL_LIBS="-L$ssl_prefix/lib -lcrypto"
|
||||
AC_CHECK_LIB(ssl, SSL_new, [ have_openssl=yes ], [ have_openssl=no ], [ $SSL_LIBS $SSL_CFLAGS ])
|
||||
if test x"$have_openssl" = xyes; then
|
||||
save_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="-I$ssl_prefix/lib $CPPFLAGS"
|
||||
AC_CHECK_HEADERS(openssl/ssl.h, have_openssl_h=yes)
|
||||
CPPFLAGS=$save_CPPFLAGS
|
||||
if test x"$have_openssl_h" = xyes; then
|
||||
have_openssl=yes
|
||||
printf "openssl found in $ssl_prefix\n";
|
||||
SSL_LIBS="-L$ssl_prefix/lib -lssl -lcrypto"
|
||||
CPPFLAGS="-I$ssl_prefix/lib $CPPFLAGS"
|
||||
SSL_CFLAGS="-DHAVE_SSL"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if test x${have_openssl} != xyes; then
|
||||
AC_MSG_ERROR([openssl library cannot be found. Install openssl or disable `tls' module (--disable-tls).])
|
||||
fi
|
||||
AC_SUBST(SSL_LIBS)
|
||||
AC_SUBST(SSL_CFLAGS)
|
||||
fi
|
||||
])
|
||||
dnl <openssl/>
|
||||
|
||||
@@ -830,16 +830,13 @@ Optional Features:
|
||||
--enable-mod_muc enable mod_muc (default: yes)
|
||||
--enable-eldap enable eldap (default: yes)
|
||||
--enable-web enable web (default: yes)
|
||||
--enable-tls enable tls (default: yes)
|
||||
--enable-odbc enable odbc (default: no)
|
||||
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
--with-erlang=PREFIX path to erlc and erl
|
||||
--with-libiconv-prefix=PREFIX prefix where libiconv is installed
|
||||
--with-expat=PREFIX prefix where EXPAT is installed
|
||||
--with-openssl=PREFIX prefix where OPENSSL is installed
|
||||
--with-libiconv-prefix=DIR Search for libiconv in DIR/include and DIR/lib
|
||||
--with-expat=PREFIX path to expat library
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command
|
||||
@@ -2227,7 +2224,7 @@ echo "$as_me: error: erlang program was not properly executed, (conftest.out was
|
||||
ERLANG_DIR=`cat conftest.out | tail -n 1`
|
||||
|
||||
ERLANG_CFLAGS="-I$ERLANG_EI_DIR/include -I$ERLANG_DIR/usr/include"
|
||||
ERLANG_LIBS="-L$ERLANG_EI_DIR/lib -lerl_interface -lei"
|
||||
ERLANG_LIBS="-L$ERLANG_EI_DIR/lib"
|
||||
|
||||
|
||||
|
||||
@@ -2238,6 +2235,7 @@ echo "$as_me: error: erlang program was not properly executed, (conftest.out was
|
||||
|
||||
|
||||
|
||||
|
||||
# Check whether --with-libiconv-prefix or --without-libiconv-prefix was given.
|
||||
if test "${with_libiconv_prefix+set}" = set; then
|
||||
withval="$with_libiconv_prefix"
|
||||
@@ -2884,15 +2882,17 @@ done
|
||||
if test "${with_expat+set}" = set; then
|
||||
withval="$with_expat"
|
||||
|
||||
else
|
||||
with_expat=no
|
||||
fi;
|
||||
|
||||
EXPAT_CFLAGS=
|
||||
EXPAT_LIBS=
|
||||
if test x"$with_expat" != x; then
|
||||
if test $with_expat != no; then
|
||||
if test $with_expat != yes; then
|
||||
EXPAT_CFLAGS="-I$with_expat/include"
|
||||
EXPAT_LIBS="-L$with_expat/lib"
|
||||
fi
|
||||
|
||||
echo "$as_me:$LINENO: checking for XML_ParserCreate in -lexpat" >&5
|
||||
echo $ECHO_N "checking for XML_ParserCreate in -lexpat... $ECHO_C" >&6
|
||||
if test "${ac_cv_lib_expat_XML_ParserCreate+set}" = set; then
|
||||
@@ -3084,6 +3084,10 @@ echo "$as_me: error: Could not find expat.h" >&2;}
|
||||
{ (exit 1); exit 1; }; }
|
||||
fi
|
||||
CFLAGS="$expat_save_CFLAGS"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3606,6 +3610,7 @@ fi
|
||||
|
||||
|
||||
|
||||
|
||||
mod_pubsub=
|
||||
make_mod_pubsub=
|
||||
echo "$as_me:$LINENO: checking whether build mod_pubsub" >&5
|
||||
@@ -3617,7 +3622,7 @@ if test "${enable_mod_pubsub+set}" = set; then
|
||||
else
|
||||
mr_enable_mod_pubsub=yes
|
||||
fi;
|
||||
if test "$mr_enable_mod_pubsub" = "yes"; then
|
||||
if test "$mr_enable_mod_pubsub" = yes; then
|
||||
mod_pubsub=mod_pubsub
|
||||
make_mod_pubsub=mod_pubsub/Makefile
|
||||
fi
|
||||
@@ -3639,7 +3644,7 @@ if test "${enable_mod_irc+set}" = set; then
|
||||
else
|
||||
mr_enable_mod_irc=yes
|
||||
fi;
|
||||
if test "$mr_enable_mod_irc" = "yes"; then
|
||||
if test "$mr_enable_mod_irc" = yes; then
|
||||
mod_irc=mod_irc
|
||||
make_mod_irc=mod_irc/Makefile
|
||||
fi
|
||||
@@ -3661,7 +3666,7 @@ if test "${enable_mod_muc+set}" = set; then
|
||||
else
|
||||
mr_enable_mod_muc=yes
|
||||
fi;
|
||||
if test "$mr_enable_mod_muc" = "yes"; then
|
||||
if test "$mr_enable_mod_muc" = yes; then
|
||||
mod_muc=mod_muc
|
||||
make_mod_muc=mod_muc/Makefile
|
||||
fi
|
||||
@@ -3683,7 +3688,7 @@ if test "${enable_eldap+set}" = set; then
|
||||
else
|
||||
mr_enable_eldap=yes
|
||||
fi;
|
||||
if test "$mr_enable_eldap" = "yes"; then
|
||||
if test "$mr_enable_eldap" = yes; then
|
||||
eldap=eldap
|
||||
make_eldap=eldap/Makefile
|
||||
fi
|
||||
@@ -3705,7 +3710,7 @@ if test "${enable_web+set}" = set; then
|
||||
else
|
||||
mr_enable_web=yes
|
||||
fi;
|
||||
if test "$mr_enable_web" = "yes"; then
|
||||
if test "$mr_enable_web" = yes; then
|
||||
web=web
|
||||
make_web=web/Makefile
|
||||
fi
|
||||
@@ -3716,264 +3721,8 @@ echo "${ECHO_T}$mr_enable_web" >&6
|
||||
|
||||
|
||||
|
||||
tls=
|
||||
make_tls=
|
||||
echo "$as_me:$LINENO: checking whether build tls" >&5
|
||||
echo $ECHO_N "checking whether build tls... $ECHO_C" >&6
|
||||
# Check whether --enable-tls or --disable-tls was given.
|
||||
if test "${enable_tls+set}" = set; then
|
||||
enableval="$enable_tls"
|
||||
mr_enable_tls="$enableval"
|
||||
else
|
||||
mr_enable_tls=yes
|
||||
fi;
|
||||
if test "$mr_enable_tls" = "yes"; then
|
||||
tls=tls
|
||||
make_tls=tls/Makefile
|
||||
fi
|
||||
echo "$as_me:$LINENO: result: $mr_enable_tls" >&5
|
||||
echo "${ECHO_T}$mr_enable_tls" >&6
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
odbc=
|
||||
make_odbc=
|
||||
echo "$as_me:$LINENO: checking whether build odbc" >&5
|
||||
echo $ECHO_N "checking whether build odbc... $ECHO_C" >&6
|
||||
# Check whether --enable-odbc or --disable-odbc was given.
|
||||
if test "${enable_odbc+set}" = set; then
|
||||
enableval="$enable_odbc"
|
||||
mr_enable_odbc="$enableval"
|
||||
else
|
||||
mr_enable_odbc=no
|
||||
fi;
|
||||
if test "$mr_enable_odbc" = "yes"; then
|
||||
odbc=odbc
|
||||
make_odbc=odbc/Makefile
|
||||
fi
|
||||
echo "$as_me:$LINENO: result: $mr_enable_odbc" >&5
|
||||
echo "${ECHO_T}$mr_enable_odbc" >&6
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap $make_web stringprep/Makefile $make_tls $make_odbc"
|
||||
|
||||
#openssl
|
||||
|
||||
# Check whether --with-openssl or --without-openssl was given.
|
||||
if test "${with_openssl+set}" = set; then
|
||||
withval="$with_openssl"
|
||||
|
||||
fi;
|
||||
unset SSL_LIBS;
|
||||
unset SSL_CFLAGS;
|
||||
have_openssl=no
|
||||
if test x"$tls" != x; then
|
||||
for ssl_prefix in $withval /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr; do
|
||||
printf "looking for openssl in $ssl_prefix...\n"
|
||||
SSL_CFLAGS="-I$ssl_prefix/include/openssl"
|
||||
SSL_LIBS="-L$ssl_prefix/lib -lcrypto"
|
||||
echo "$as_me:$LINENO: checking for SSL_new in -lssl" >&5
|
||||
echo $ECHO_N "checking for SSL_new in -lssl... $ECHO_C" >&6
|
||||
if test "${ac_cv_lib_ssl_SSL_new+set}" = set; then
|
||||
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lssl $SSL_LIBS $SSL_CFLAGS $LIBS"
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
#line $LINENO "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
/* Override any gcc2 internal prototype to avoid an error. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
/* We use char because int might match the return type of a gcc2
|
||||
builtin and then its argument prototype would still apply. */
|
||||
char SSL_new ();
|
||||
#ifdef F77_DUMMY_MAIN
|
||||
# ifdef __cplusplus
|
||||
extern "C"
|
||||
# endif
|
||||
int F77_DUMMY_MAIN() { return 1; }
|
||||
#endif
|
||||
int
|
||||
main ()
|
||||
{
|
||||
SSL_new ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
rm -f conftest.$ac_objext conftest$ac_exeext
|
||||
if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
|
||||
(eval $ac_link) 2>&5
|
||||
ac_status=$?
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } &&
|
||||
{ ac_try='test -s conftest$ac_exeext'
|
||||
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
|
||||
(eval $ac_try) 2>&5
|
||||
ac_status=$?
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); }; }; then
|
||||
ac_cv_lib_ssl_SSL_new=yes
|
||||
else
|
||||
echo "$as_me: failed program was:" >&5
|
||||
cat conftest.$ac_ext >&5
|
||||
ac_cv_lib_ssl_SSL_new=no
|
||||
fi
|
||||
rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
echo "$as_me:$LINENO: result: $ac_cv_lib_ssl_SSL_new" >&5
|
||||
echo "${ECHO_T}$ac_cv_lib_ssl_SSL_new" >&6
|
||||
if test $ac_cv_lib_ssl_SSL_new = yes; then
|
||||
have_openssl=yes
|
||||
else
|
||||
have_openssl=no
|
||||
fi
|
||||
|
||||
if test x"$have_openssl" = xyes; then
|
||||
save_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="-I$ssl_prefix/lib $CPPFLAGS"
|
||||
|
||||
for ac_header in openssl/ssl.h
|
||||
do
|
||||
as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
|
||||
if eval "test \"\${$as_ac_Header+set}\" = set"; then
|
||||
echo "$as_me:$LINENO: checking for $ac_header" >&5
|
||||
echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
|
||||
if eval "test \"\${$as_ac_Header+set}\" = set"; then
|
||||
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||
fi
|
||||
echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
|
||||
echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
|
||||
else
|
||||
# Is the header compilable?
|
||||
echo "$as_me:$LINENO: checking $ac_header usability" >&5
|
||||
echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
#line $LINENO "configure"
|
||||
#include "confdefs.h"
|
||||
$ac_includes_default
|
||||
#include <$ac_header>
|
||||
_ACEOF
|
||||
rm -f conftest.$ac_objext
|
||||
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
|
||||
(eval $ac_compile) 2>&5
|
||||
ac_status=$?
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } &&
|
||||
{ ac_try='test -s conftest.$ac_objext'
|
||||
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
|
||||
(eval $ac_try) 2>&5
|
||||
ac_status=$?
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); }; }; then
|
||||
ac_header_compiler=yes
|
||||
else
|
||||
echo "$as_me: failed program was:" >&5
|
||||
cat conftest.$ac_ext >&5
|
||||
ac_header_compiler=no
|
||||
fi
|
||||
rm -f conftest.$ac_objext conftest.$ac_ext
|
||||
echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
|
||||
echo "${ECHO_T}$ac_header_compiler" >&6
|
||||
|
||||
# Is the header present?
|
||||
echo "$as_me:$LINENO: checking $ac_header presence" >&5
|
||||
echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
#line $LINENO "configure"
|
||||
#include "confdefs.h"
|
||||
#include <$ac_header>
|
||||
_ACEOF
|
||||
if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
|
||||
(eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
|
||||
ac_status=$?
|
||||
egrep -v '^ *\+' conftest.er1 >conftest.err
|
||||
rm -f conftest.er1
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } >/dev/null; then
|
||||
if test -s conftest.err; then
|
||||
ac_cpp_err=$ac_c_preproc_warn_flag
|
||||
else
|
||||
ac_cpp_err=
|
||||
fi
|
||||
else
|
||||
ac_cpp_err=yes
|
||||
fi
|
||||
if test -z "$ac_cpp_err"; then
|
||||
ac_header_preproc=yes
|
||||
else
|
||||
echo "$as_me: failed program was:" >&5
|
||||
cat conftest.$ac_ext >&5
|
||||
ac_header_preproc=no
|
||||
fi
|
||||
rm -f conftest.err conftest.$ac_ext
|
||||
echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
|
||||
echo "${ECHO_T}$ac_header_preproc" >&6
|
||||
|
||||
# So? What about this header?
|
||||
case $ac_header_compiler:$ac_header_preproc in
|
||||
yes:no )
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
|
||||
echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
|
||||
echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;};;
|
||||
no:yes )
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
|
||||
echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
|
||||
echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
|
||||
echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;};;
|
||||
esac
|
||||
echo "$as_me:$LINENO: checking for $ac_header" >&5
|
||||
echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
|
||||
if eval "test \"\${$as_ac_Header+set}\" = set"; then
|
||||
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||
else
|
||||
eval "$as_ac_Header=$ac_header_preproc"
|
||||
fi
|
||||
echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
|
||||
echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
|
||||
|
||||
fi
|
||||
if test `eval echo '${'$as_ac_Header'}'` = yes; then
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
|
||||
_ACEOF
|
||||
have_openssl_h=yes
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
CPPFLAGS=$save_CPPFLAGS
|
||||
if test x"$have_openssl_h" = xyes; then
|
||||
have_openssl=yes
|
||||
printf "openssl found in $ssl_prefix\n";
|
||||
SSL_LIBS="-L$ssl_prefix/lib -lssl -lcrypto"
|
||||
CPPFLAGS="-I$ssl_prefix/lib $CPPFLAGS"
|
||||
SSL_CFLAGS="-DHAVE_SSL"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if test x${have_openssl} != xyes; then
|
||||
{ { echo "$as_me:$LINENO: error: openssl library cannot be found. Install openssl or disable \`tls' module (--disable-tls)." >&5
|
||||
echo "$as_me: error: openssl library cannot be found. Install openssl or disable \`tls' module (--disable-tls)." >&2;}
|
||||
{ (exit 1); exit 1; }; }
|
||||
fi
|
||||
|
||||
|
||||
fi
|
||||
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap $make_web stringprep/Makefile"
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
@@ -4478,8 +4227,6 @@ do
|
||||
"$make_eldap" ) CONFIG_FILES="$CONFIG_FILES $make_eldap" ;;
|
||||
"$make_web" ) CONFIG_FILES="$CONFIG_FILES $make_web" ;;
|
||||
"stringprep/Makefile" ) CONFIG_FILES="$CONFIG_FILES stringprep/Makefile" ;;
|
||||
"$make_tls" ) CONFIG_FILES="$CONFIG_FILES $make_tls" ;;
|
||||
"$make_odbc" ) CONFIG_FILES="$CONFIG_FILES $make_odbc" ;;
|
||||
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
|
||||
echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
|
||||
{ (exit 1); exit 1; }; };;
|
||||
@@ -4588,12 +4335,6 @@ s,@eldap@,$eldap,;t t
|
||||
s,@make_eldap@,$make_eldap,;t t
|
||||
s,@web@,$web,;t t
|
||||
s,@make_web@,$make_web,;t t
|
||||
s,@tls@,$tls,;t t
|
||||
s,@make_tls@,$make_tls,;t t
|
||||
s,@odbc@,$odbc,;t t
|
||||
s,@make_odbc@,$make_odbc,;t t
|
||||
s,@SSL_LIBS@,$SSL_LIBS,;t t
|
||||
s,@SSL_CFLAGS@,$SSL_CFLAGS,;t t
|
||||
CEOF
|
||||
|
||||
_ACEOF
|
||||
|
||||
@@ -15,6 +15,9 @@ AM_ICONV
|
||||
#locating libexpat
|
||||
AM_WITH_EXPAT
|
||||
|
||||
|
||||
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
|
||||
@@ -22,23 +25,19 @@ AC_C_CONST
|
||||
AC_FUNC_MALLOC
|
||||
AC_HEADER_STDC
|
||||
|
||||
|
||||
AC_MOD_ENABLE(mod_pubsub, yes)
|
||||
AC_MOD_ENABLE(mod_irc, yes)
|
||||
AC_MOD_ENABLE(mod_muc, yes)
|
||||
AC_MOD_ENABLE(eldap, yes)
|
||||
AC_MOD_ENABLE(web, yes)
|
||||
AC_MOD_ENABLE(tls, yes)
|
||||
AC_MOD_ENABLE(odbc, no)
|
||||
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
$make_mod_irc
|
||||
$make_mod_muc
|
||||
$make_mod_pubsub
|
||||
$make_eldap
|
||||
$make_web
|
||||
stringprep/Makefile
|
||||
$make_tls
|
||||
$make_odbc])
|
||||
#openssl
|
||||
AM_WITH_OPENSSL
|
||||
stringprep/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -40,7 +40,6 @@ start() ->
|
||||
RootDirS = "ERLANG_DIR = " ++ code:root_dir() ++ "\n",
|
||||
Version = "EJABBERD_VERSION = " ++ ?VERSION ++ "\n",
|
||||
ExpatDir = "EXPAT_DIR = c:\\progra~1\\expat-1.95.7\n",
|
||||
OpenSSLDir = "OPENSSL_DIR = c:\\progra~1\\OpenSSL\n",
|
||||
|
||||
SSLDir = "SSLDIR = " ++ code:lib_dir("ssl") ++ "\n",
|
||||
StdLibDir = "STDLIBDIR = " ++ code:lib_dir("stdlib") ++ "\n",
|
||||
@@ -51,7 +50,6 @@ start() ->
|
||||
Version ++
|
||||
SSLDir ++
|
||||
StdLibDir ++
|
||||
OpenSSLDir ++
|
||||
ExpatDir ++
|
||||
ExpatLib ++
|
||||
ExpatFlag ++
|
||||
|
||||
@@ -13,19 +13,17 @@
|
||||
-export([start/0,
|
||||
register_mechanism/2,
|
||||
listmech/0,
|
||||
server_new/6,
|
||||
server_new/4,
|
||||
server_start/3,
|
||||
server_step/2]).
|
||||
|
||||
-record(sasl_mechanism, {mechanism, module}).
|
||||
-record(sasl_state, {service, myname, realm,
|
||||
get_password, check_password,
|
||||
mech_mod, mech_state}).
|
||||
-record(sasl_state, {service, myname, realm, mech_mod, mech_state}).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
behaviour_info(callbacks) ->
|
||||
[{mech_new, 2},
|
||||
[{mech_new, 0},
|
||||
{mech_step, 2}];
|
||||
behaviour_info(Other) ->
|
||||
undefined.
|
||||
@@ -82,19 +80,15 @@ listmech() ->
|
||||
[{#sasl_mechanism{mechanism = '$1', _ = '_'}, [], ['$1']}]).
|
||||
|
||||
|
||||
server_new(Service, ServerFQDN, UserRealm, SecFlags,
|
||||
GetPassword, CheckPassword) ->
|
||||
server_new(Service, ServerFQDN, UserRealm, SecFlags) ->
|
||||
#sasl_state{service = Service,
|
||||
myname = ServerFQDN,
|
||||
realm = UserRealm,
|
||||
get_password = GetPassword,
|
||||
check_password = CheckPassword}.
|
||||
realm = UserRealm}.
|
||||
|
||||
server_start(State, Mech, ClientIn) ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Module}] ->
|
||||
{ok, MechState} = Module:mech_new(State#sasl_state.get_password,
|
||||
State#sasl_state.check_password),
|
||||
{ok, MechState} = Module:mech_new(),
|
||||
server_step(State#sasl_state{mech_mod = Module,
|
||||
mech_state = MechState},
|
||||
ClientIn);
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
mech_new/2,
|
||||
mech_new/0,
|
||||
mech_step/2]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
%-behaviour(gen_mod).
|
||||
|
||||
-record(state, {step, nonce, username, authzid, get_password}).
|
||||
-record(state, {step, nonce, username, authzid}).
|
||||
|
||||
start(_Opts) ->
|
||||
start(Opts) ->
|
||||
case ejabberd_auth:plain_password_required() of
|
||||
true ->
|
||||
ok;
|
||||
@@ -31,15 +32,15 @@ start(_Opts) ->
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(GetPassword, _CheckPassword) ->
|
||||
mech_new() ->
|
||||
{ok, #state{step = 1,
|
||||
nonce = randoms:get_string(),
|
||||
get_password = GetPassword}}.
|
||||
nonce = randoms:get_string()}}.
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
{continue,
|
||||
"nonce=\"" ++ Nonce ++
|
||||
"\",qop=\"auth\",charset=utf-8,algorithm=md5-sess",
|
||||
%"\",qop=\"auth,auth-int\",charset=utf-8,algorithm=md5-sess",
|
||||
State#state{step = 3}};
|
||||
mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
@@ -48,9 +49,9 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
KeyVals ->
|
||||
UserName = xml:get_attr_s("username", KeyVals),
|
||||
AuthzId = xml:get_attr_s("authzid", KeyVals),
|
||||
case (State#state.get_password)(UserName) of
|
||||
case ejabberd_auth:get_password(UserName) of
|
||||
false ->
|
||||
{error, "not-authorized"};
|
||||
{error, "no-user"};
|
||||
Passwd ->
|
||||
Response = response(KeyVals, UserName, Passwd,
|
||||
Nonce, AuthzId, "AUTHENTICATE"),
|
||||
@@ -65,13 +66,13 @@ mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
_ ->
|
||||
{error, "not-authorized"}
|
||||
{error, "bad-auth"}
|
||||
end
|
||||
end
|
||||
end;
|
||||
mech_step(#state{step = 5,
|
||||
username = UserName,
|
||||
authzid = AuthzId}, "") ->
|
||||
authzid = AuthzId} = State, "") ->
|
||||
{ok, [{username, UserName}, {authzid, AuthzId}]};
|
||||
mech_step(A, B) ->
|
||||
io:format("SASL DIGEST: A ~p B ~p", [A,B]),
|
||||
@@ -87,7 +88,7 @@ parse1([C | Cs], S, Ts) ->
|
||||
parse1(Cs, [C | S], Ts);
|
||||
parse1([], [], T) ->
|
||||
lists:reverse(T);
|
||||
parse1([], _S, _T) ->
|
||||
parse1([], S, T) ->
|
||||
bad.
|
||||
|
||||
parse2([$" | Cs], Key, Val, Ts) ->
|
||||
@@ -147,13 +148,13 @@ response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) ->
|
||||
crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
|
||||
":" ++ Nonce ++ ":" ++ CNonce ++ ":" ++ AuthzId
|
||||
end,
|
||||
A2 = case QOP of
|
||||
"auth" ->
|
||||
A2Prefix ++ ":" ++ DigestURI;
|
||||
_ ->
|
||||
A2Prefix ++ ":" ++ DigestURI ++
|
||||
":00000000000000000000000000000000"
|
||||
end,
|
||||
case QOP of
|
||||
"auth" ->
|
||||
A2 = A2Prefix ++ ":" ++ DigestURI;
|
||||
_ ->
|
||||
A2 = A2Prefix ++ ":" ++ DigestURI ++
|
||||
":00000000000000000000000000000000"
|
||||
end,
|
||||
T = hex(binary_to_list(crypto:md5(A1))) ++ ":" ++ Nonce ++ ":" ++
|
||||
NC ++ ":" ++ CNonce ++ ":" ++ QOP ++ ":" ++
|
||||
hex(binary_to_list(crypto:md5(A2))),
|
||||
|
||||
@@ -10,26 +10,25 @@
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/1, stop/0, mech_new/2, mech_step/2, parse/1]).
|
||||
-export([start/1, stop/0, mech_new/0, mech_step/2, parse/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
%-behaviour(gen_mod).
|
||||
|
||||
-record(state, {check_password}).
|
||||
|
||||
start(_Opts) ->
|
||||
start(Opts) ->
|
||||
cyrsasl:register_mechanism("PLAIN", ?MODULE),
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
|
||||
mech_new(_GetPassword, CheckPassword) ->
|
||||
{ok, #state{check_password = CheckPassword}}.
|
||||
mech_new() ->
|
||||
{ok, []}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
[AuthzId, User, Password] ->
|
||||
case (State#state.check_password)(User, Password) of
|
||||
case ejabberd_auth:check_password(User, Password) of
|
||||
true ->
|
||||
{ok, [{username, User}, {authzid, AuthzId}]};
|
||||
_ ->
|
||||
|
||||
@@ -75,25 +75,15 @@
|
||||
% this line:
|
||||
{auth_method, internal}.
|
||||
|
||||
% For LDAP authentification use these lines instead of above one:
|
||||
% For LDAP uthentification use these lines instead of above one:
|
||||
%{auth_method, ldap}.
|
||||
%{ldap_servers, ["localhost"]}. % List of LDAP servers
|
||||
%{ldap_uidattr, "uid"}. % LDAP attribute that holds user ID
|
||||
%{ldap_base, "dc=example,dc=com"}. % Search base of LDAP directory
|
||||
%{ldap_rootdn, "dc=example,dc=com"}. % LDAP manager
|
||||
%{ldap_password, "******"}. % Password to LDAP manager
|
||||
|
||||
% For authentification via external script use the following:
|
||||
%{auth_method, external}.
|
||||
%{extauth_program, "/path/to/authentification/script"}.
|
||||
|
||||
% For authentification via ODBC use the following:
|
||||
%{auth_method, odbc}.
|
||||
%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}.
|
||||
%{ldap_base, "dc=example,dc=com"}. % Base of LDAP directory
|
||||
|
||||
|
||||
% Host name:
|
||||
{hosts, ["localhost"]}.
|
||||
{host, "localhost"}.
|
||||
|
||||
|
||||
% Default language for server messages
|
||||
@@ -101,13 +91,8 @@
|
||||
|
||||
% Listened ports:
|
||||
{listen,
|
||||
[{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper},
|
||||
starttls, {certfile, "./ssl.pem"}]},
|
||||
{5223, ejabberd_c2s, [{access, c2s},
|
||||
tls, {certfile, "./ssl.pem"}]},
|
||||
% Use these two lines instead if TLS support is not compiled
|
||||
%{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]},
|
||||
%{5223, ejabberd_c2s, [{access, c2s}, ssl, {certfile, "./ssl.pem"}]},
|
||||
[{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]},
|
||||
{5223, ejabberd_c2s, [{access, c2s}, ssl, {certfile, "./ssl.pem"}]},
|
||||
{5269, ejabberd_s2s_in, [{shaper, s2s_shaper}]},
|
||||
{5280, ejabberd_http, [http_poll, web_admin]},
|
||||
{8888, ejabberd_service, [{access, all},
|
||||
@@ -131,7 +116,6 @@
|
||||
{mod_stats, []},
|
||||
{mod_vcard, []},
|
||||
{mod_offline, []},
|
||||
{mod_announce, [{access, announce}]},
|
||||
{mod_echo, [{host, "echo.localhost"}]},
|
||||
{mod_private, []},
|
||||
{mod_irc, []},
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(VERSION, "0.9.1").
|
||||
-define(VERSION, "0.6-alpha").
|
||||
|
||||
%-define(ejabberd_debug, true).
|
||||
%-define(DBGFSM, true).
|
||||
@@ -27,9 +27,10 @@
|
||||
[self(),?MODULE,?LINE]++Args)).
|
||||
|
||||
|
||||
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)).
|
||||
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
|
||||
%-define(MYNAME,"e.localhost").
|
||||
-define(MYNAME, ejabberd_config:get_global_option(host)).
|
||||
-define(S2STIMEOUT, 600000).
|
||||
%-define(S2STIMEOUT, 6000).
|
||||
-define(MYLANG, ejabberd_config:get_global_option(language)).
|
||||
|
||||
-define(MSGS_DIR, "msgs").
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification
|
||||
%%% Purpose :
|
||||
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -10,84 +10,350 @@
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
-export([start/0, start_link/0,
|
||||
set_password/2,
|
||||
check_password/2,
|
||||
check_password/4,
|
||||
try_register/2,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
get_password/1,
|
||||
get_password_s/1,
|
||||
is_user_exists/1,
|
||||
remove_user/1,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
plain_password_required/0,
|
||||
check_password_ldap/2, % TODO: remove
|
||||
is_user_exists_ldap/1 % TODO: remove
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
code_change/3,
|
||||
handle_info/2,
|
||||
terminate/2]).
|
||||
|
||||
-include("eldap/eldap.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-record(passwd, {user, password}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start() ->
|
||||
(auth_module()):start().
|
||||
gen_server:start({local, ejabberd_auth}, ejabberd_auth, [], []).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ejabberd_auth}, ejabberd_auth, [], []).
|
||||
|
||||
plain_password_required() ->
|
||||
(auth_module()):plain_password_required().
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_server
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
(auth_module()):check_password(User, Server, Password).
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([]) ->
|
||||
mnesia:create_table(passwd,[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
case auth_method() of
|
||||
internal ->
|
||||
ok;
|
||||
ldap ->
|
||||
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
||||
eldap:start_link("ejabberd", LDAPServers, 389, "", "")
|
||||
end,
|
||||
{ok, #state{}}.
|
||||
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
(auth_module()):check_password(User, Server, Password, StreamID, Digest).
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_call/3
|
||||
%% Returns: {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} | (terminate/2 is called)
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
(auth_module()):set_password(User, Server, Password).
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_cast/2
|
||||
%% Returns: {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
case lists:member(jlib:nameprep(Server), ?MYHOSTS) of
|
||||
true ->
|
||||
(auth_module()):try_register(User, Server, Password);
|
||||
false ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
(auth_module()):dirty_get_registered_users().
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
(auth_module()):get_vh_registered_users(Server).
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/2
|
||||
%% Returns: {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
get_password(User, Server) ->
|
||||
(auth_module()):get_password(User, Server).
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
(auth_module()):get_password_s(User, Server).
|
||||
|
||||
is_user_exists(User, Server) ->
|
||||
(auth_module()):is_user_exists(User, Server).
|
||||
|
||||
remove_user(User, Server) ->
|
||||
(auth_module()):remove_user(User, Server).
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
(auth_module()):remove_user(User, Server, Password).
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/2
|
||||
%% Purpose: Shutdown the server
|
||||
%% Returns: any (ignored by gen_server)
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
auth_module() ->
|
||||
auth_method() ->
|
||||
case ejabberd_config:get_local_option(auth_method) of
|
||||
external ->
|
||||
ejabberd_auth_external;
|
||||
ldap ->
|
||||
ejabberd_auth_ldap;
|
||||
odbc ->
|
||||
ejabberd_auth_odbc;
|
||||
ldap;
|
||||
_ ->
|
||||
ejabberd_auth_internal
|
||||
internal
|
||||
end.
|
||||
|
||||
plain_password_required() ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
false;
|
||||
ldap ->
|
||||
true
|
||||
end.
|
||||
|
||||
|
||||
check_password(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
check_password_internal(User, Password);
|
||||
ldap ->
|
||||
check_password_ldap(User, Password)
|
||||
end.
|
||||
|
||||
check_password_internal(User, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[#passwd{password = Password}] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
check_password(User, Password, StreamID, Digest) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
check_password_internal(User, Password, StreamID, Digest);
|
||||
ldap ->
|
||||
check_password_ldap(User, Password, StreamID, Digest)
|
||||
end.
|
||||
|
||||
check_password_internal(User, Password, StreamID, Digest) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[#passwd{password = Passwd}] ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
Digest == sha:sha(StreamID ++ Passwd);
|
||||
true ->
|
||||
false
|
||||
end,
|
||||
if DigRes ->
|
||||
true;
|
||||
true ->
|
||||
(Passwd == Password) and (Password /= "")
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
set_password(User, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error -> {error, invalid_jid};
|
||||
LUser ->
|
||||
F = fun() ->
|
||||
mnesia:write(#passwd{user = LUser,
|
||||
password = Password})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
try_register_internal(User, Password);
|
||||
ldap ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
try_register_internal(User, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error -> {error, invalid_jid};
|
||||
LUser ->
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, LUser}) of
|
||||
[] ->
|
||||
mnesia:write(#passwd{user = LUser,
|
||||
password = Password}),
|
||||
ok;
|
||||
[_E] ->
|
||||
exists
|
||||
end
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
mnesia:dirty_all_keys(passwd).
|
||||
|
||||
get_password(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read(passwd, LUser) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_password_s(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read(passwd, LUser) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
is_user_exists(User) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
is_user_exists_internal(User);
|
||||
ldap ->
|
||||
is_user_exists_ldap(User)
|
||||
end.
|
||||
|
||||
is_user_exists_internal(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[] ->
|
||||
false;
|
||||
[_] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
remove_user(User) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
remove_user_internal(User);
|
||||
ldap ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
remove_user_internal(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
mnesia:delete({passwd, LUser})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
catch mod_roster:remove_user(User),
|
||||
catch mod_offline:remove_user(User),
|
||||
catch mod_last:remove_user(User),
|
||||
catch mod_vcard:remove_user(User),
|
||||
catch mod_private:remove_user(User).
|
||||
|
||||
remove_user(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
remove_user_internal(User, Password);
|
||||
ldap ->
|
||||
not_allowed
|
||||
end.
|
||||
|
||||
remove_user_internal(User, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, LUser}) of
|
||||
[#passwd{password = Password}] ->
|
||||
mnesia:delete({passwd, LUser}),
|
||||
ok;
|
||||
[_] ->
|
||||
not_allowed;
|
||||
_ ->
|
||||
not_exists
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
catch mod_roster:remove_user(User),
|
||||
catch mod_offline:remove_user(User),
|
||||
catch mod_last:remove_user(User),
|
||||
catch mod_vcard:remove_user(User),
|
||||
catch mod_private:remove_user(User),
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
bad_request
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
check_password_ldap(User, Password, StreamID, Digest) ->
|
||||
check_password_ldap(User, Password).
|
||||
|
||||
check_password_ldap(User, Password) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
DN ->
|
||||
case eldap:bind("ejabberd", DN, Password) of
|
||||
ok ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
is_user_exists_ldap(User) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
_DN ->
|
||||
true
|
||||
end.
|
||||
|
||||
find_user_dn(User) ->
|
||||
Attr = ejabberd_config:get_local_option(ldap_uidattr),
|
||||
Filter = eldap:equalityMatch(Attr, User),
|
||||
Base = ejabberd_config:get_local_option(ldap_base),
|
||||
case eldap:search("ejabberd", [{base, Base},
|
||||
{filter, Filter},
|
||||
{attributes, []}]) of
|
||||
#eldap_search_result{entries = [E | _]} ->
|
||||
E#eldap_entry.object_name;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_external.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification via LDAP external script
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_external).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start() ->
|
||||
extauth:start(ejabberd_config:get_local_option(extauth_program)),
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
||||
check_password(User, _Server, Password) ->
|
||||
extauth:check_password(User, Password).
|
||||
|
||||
check_password(User, Server, Password, _StreamID, _Digest) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(User, _Server, Password) ->
|
||||
extauth:set_password(User, Password).
|
||||
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
|
||||
get_vh_registered_users(_Server) ->
|
||||
[].
|
||||
|
||||
get_password(_User, _Server) ->
|
||||
false.
|
||||
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
is_user_exists(User, _Server) ->
|
||||
extauth:is_user_exists(User).
|
||||
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_internal.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification via mnesia
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_internal).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(passwd, {us, password}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start() ->
|
||||
mnesia:create_table(passwd, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
update_table(),
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
false.
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[#passwd{password = Passwd}] ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
Digest == sha:sha(StreamID ++ Passwd);
|
||||
true ->
|
||||
false
|
||||
end,
|
||||
if DigRes ->
|
||||
true;
|
||||
true ->
|
||||
(Passwd == Password) and (Password /= "")
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if
|
||||
(LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
F = fun() ->
|
||||
mnesia:write(#passwd{us = US,
|
||||
password = Password})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if
|
||||
(LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, US}) of
|
||||
[] ->
|
||||
mnesia:write(#passwd{us = US,
|
||||
password = Password}),
|
||||
ok;
|
||||
[_E] ->
|
||||
exists
|
||||
end
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
mnesia:dirty_all_keys(passwd).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
mnesia:dirty_select(
|
||||
passwd,
|
||||
[{#passwd{us = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}],
|
||||
['$1']}]).
|
||||
|
||||
get_password(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read(passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read(passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
is_user_exists(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[] ->
|
||||
false;
|
||||
[_] ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({passwd, US})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(remove_user, [User, Server]).
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
mnesia:delete({passwd, US}),
|
||||
ok;
|
||||
[_] ->
|
||||
not_allowed;
|
||||
_ ->
|
||||
not_exists
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
ejabberd_hooks:run(remove_user, [User, Server]),
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
bad_request
|
||||
end.
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, passwd),
|
||||
case mnesia:table_info(passwd, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, password] ->
|
||||
?INFO_MSG("Converting passwd table from "
|
||||
"{user, password} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
ejabberd_auth_internal_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, passwd},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
mnesia:transform_table(passwd, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(ejabberd_auth_internal_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#passwd{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
ejabberd_auth_internal_tmp_table,
|
||||
R#passwd{us = {U, Host}})
|
||||
end, ok, passwd)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(passwd),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(passwd),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, ejabberd_auth_internal_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(ejabberd_auth_internal_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating passwd table", []),
|
||||
mnesia:transform_table(passwd, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_ldap.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification via LDAP
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_ldap).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("eldap/eldap.hrl").
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start() ->
|
||||
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
||||
RootDN = ejabberd_config:get_local_option(ldap_rootdn),
|
||||
Password = ejabberd_config:get_local_option(ldap_password),
|
||||
eldap:start_link("ejabberd", LDAPServers, 389, RootDN, Password),
|
||||
eldap:start_link("ejabberd_bind", LDAPServers, 389, RootDN, Password),
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
||||
check_password(User, _Server, Password) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
DN ->
|
||||
case eldap:bind("ejabberd_bind", DN, Password) of
|
||||
ok ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
check_password(User, Server, Password, _StreamID, _Digest) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
get_vh_registered_users(?MYNAME).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
Attr = ejabberd_config:get_local_option(ldap_uidattr),
|
||||
Filter = eldap:present(Attr),
|
||||
Base = ejabberd_config:get_local_option(ldap_base),
|
||||
case eldap:search("ejabberd", [{base, Base},
|
||||
{filter, Filter},
|
||||
{attributes, [Attr]}]) of
|
||||
#eldap_search_result{entries = Es} ->
|
||||
lists:flatmap(
|
||||
fun(E) ->
|
||||
case lists:keysearch(Attr, 1, E#eldap_entry.attributes) of
|
||||
{value, {_, [U]}} ->
|
||||
case jlib:nodeprep(U) of
|
||||
error ->
|
||||
[];
|
||||
LU ->
|
||||
[{LU, LServer}]
|
||||
end;
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end, Es);
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
get_password(_User, _Server) ->
|
||||
false.
|
||||
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
|
||||
is_user_exists(User, _Server) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
_DN ->
|
||||
true
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
find_user_dn(User) ->
|
||||
Attr = ejabberd_config:get_local_option(ldap_uidattr),
|
||||
Filter = eldap:equalityMatch(Attr, User),
|
||||
Base = ejabberd_config:get_local_option(ldap_base),
|
||||
case eldap:search("ejabberd", [{base, Base},
|
||||
{filter, Filter},
|
||||
{attributes, []}]) of
|
||||
#eldap_search_result{entries = [E | _]} ->
|
||||
E#eldap_entry.object_name;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_auth_odbc.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Authentification via ODBC
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_auth_odbc).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
%% External exports
|
||||
-export([start/0,
|
||||
set_password/3,
|
||||
check_password/3,
|
||||
check_password/5,
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-record(passwd, {user, password}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start() ->
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
false.
|
||||
|
||||
check_password(User, _Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select password from users "
|
||||
"where username='", Username, "'"]) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
check_password(User, _Server, Password, StreamID, Digest) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select password from users "
|
||||
"where username='", Username, "'"]) of
|
||||
{selected, ["password"], [{Passwd}]} ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
Digest == sha:sha(StreamID ++ Passwd);
|
||||
true ->
|
||||
false
|
||||
end,
|
||||
if DigRes ->
|
||||
true;
|
||||
true ->
|
||||
(Passwd == Password) and (Password /= "")
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
set_password(User, _Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
{error, invalid_jid};
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Pass = ejabberd_odbc:escape(Password),
|
||||
catch ejabberd_odbc:sql_query(
|
||||
["begin;"
|
||||
"delete from users where username='", Username ,"';"
|
||||
"insert into users(username, password) "
|
||||
"values ('", Username, "', '", Pass, "'); commit"])
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, _Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
{error, invalid_jid};
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Pass = ejabberd_odbc:escape(Password),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["insert into users(username, password) "
|
||||
"values ('", Username, "', '", Pass, "')"]) of
|
||||
{updated, _} ->
|
||||
{atomic, ok};
|
||||
_ ->
|
||||
{atomic, exists}
|
||||
end
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
case catch ejabberd_odbc:sql_query("select username from users") of
|
||||
{selected, ["username"], Res} ->
|
||||
[U || {U} <- Res];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
dirty_get_registered_users().
|
||||
|
||||
get_password(User, _Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select password from users "
|
||||
"where username='", Username, "'"]) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
Password;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
get_password_s(User, _Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
"";
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select password from users "
|
||||
"where username='", Username, "'"]) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
Password;
|
||||
_ ->
|
||||
""
|
||||
end
|
||||
end.
|
||||
|
||||
is_user_exists(User, _Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
false;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select password from users "
|
||||
"where username='", Username, "'"]) of
|
||||
{selected, ["password"], [{_Password}]} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
remove_user(User, _Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
error;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
catch ejabberd_odbc:sql_query(
|
||||
["delete from users where username='", Username ,"'"]),
|
||||
ejabberd_hooks:run(remove_user, [User])
|
||||
end.
|
||||
|
||||
remove_user(User, _Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
error;
|
||||
LUser ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Pass = ejabberd_odbc:escape(Password),
|
||||
case catch
|
||||
ejabberd_odbc:sql_query(
|
||||
["begin;"
|
||||
"select password from users where username='", Username, "';"
|
||||
"delete from users "
|
||||
"where username='", Username, "' and password='", Pass, "';"
|
||||
"commit"]) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
ejabberd_hooks:run(remove_user, [User]),
|
||||
ok;
|
||||
{selected, ["password"], []} ->
|
||||
not_exists;
|
||||
_ ->
|
||||
not_allowed
|
||||
end
|
||||
end.
|
||||
@@ -23,7 +23,7 @@
|
||||
-export([init/1,
|
||||
wait_for_stream/2,
|
||||
wait_for_auth/2,
|
||||
wait_for_feature_request/2,
|
||||
wait_for_sasl_auth/2,
|
||||
wait_for_bind/2,
|
||||
wait_for_session/2,
|
||||
wait_for_sasl_response/2,
|
||||
@@ -45,10 +45,6 @@
|
||||
sasl_state,
|
||||
access,
|
||||
shaper,
|
||||
tls = false,
|
||||
tls_required = false,
|
||||
tls_enabled = false,
|
||||
tls_options = [],
|
||||
authentificated = false,
|
||||
jid,
|
||||
user = "", server = ?MYNAME, resource = "",
|
||||
@@ -81,12 +77,10 @@
|
||||
|
||||
-define(INVALID_NS_ERR,
|
||||
xml:element_to_string(?SERR_INVALID_NAMESPACE)).
|
||||
%-define(INVALID_XML_ERR,
|
||||
% "<stream:error code='400'>Invalid XML</stream:error>").
|
||||
-define(INVALID_XML_ERR,
|
||||
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
|
||||
-define(HOST_UNKNOWN_ERR,
|
||||
xml:element_to_string(?SERR_HOST_UNKNOWN)).
|
||||
-define(POLICY_VIOLATION_ERR(Lang, Text),
|
||||
xml:element_to_string(?SERRT_POLICY_VIOLATION(Lang, Text))).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
@@ -113,6 +107,7 @@ get_presence(FsmRef) ->
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([{SockMod, Socket}, Opts]) ->
|
||||
ReceiverPid = ejabberd_receiver:start(Socket, SockMod, none),
|
||||
Access = case lists:keysearch(access, 1, Opts) of
|
||||
{value, {_, A}} -> A;
|
||||
_ -> all
|
||||
@@ -121,34 +116,12 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
{value, {_, S}} -> S;
|
||||
_ -> none
|
||||
end,
|
||||
StartTLS = lists:member(starttls, Opts),
|
||||
StartTLSRequired = lists:member(starttls_required, Opts),
|
||||
TLSEnabled = lists:member(tls, Opts),
|
||||
TLS = StartTLS orelse StartTLSRequired orelse TLSEnabled,
|
||||
TLSOpts = lists:filter(fun({certfile, _}) -> true;
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
{SockMod1, Socket1, ReceiverPid} =
|
||||
if
|
||||
TLSEnabled ->
|
||||
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
|
||||
RecPid = ejabberd_receiver:start(TLSSocket, tls, none),
|
||||
{tls, TLSSocket, RecPid};
|
||||
true ->
|
||||
RecPid = ejabberd_receiver:start(Socket, SockMod, none),
|
||||
{SockMod, Socket, RecPid}
|
||||
end,
|
||||
{ok, wait_for_stream, #state{socket = Socket1,
|
||||
sockmod = SockMod1,
|
||||
receiver = ReceiverPid,
|
||||
tls = TLS,
|
||||
tls_required = StartTLSRequired,
|
||||
tls_enabled = TLSEnabled,
|
||||
tls_options = TLSOpts,
|
||||
streamid = new_id(),
|
||||
access = Access,
|
||||
shaper = Shaper}}.
|
||||
|
||||
{ok, wait_for_stream, #state{socket = Socket,
|
||||
sockmod = SockMod,
|
||||
receiver = ReceiverPid,
|
||||
streamid = new_id(),
|
||||
access = Access,
|
||||
shaper = Shaper}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
@@ -166,128 +139,63 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
end,
|
||||
case xml:get_attr_s("xmlns:stream", Attrs) of
|
||||
?NS_STREAM ->
|
||||
Server = jlib:nameprep(xml:get_attr_s("to", Attrs)),
|
||||
case lists:member(Server, ?MYHOSTS) of
|
||||
true ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case xml:get_attr_s("version", Attrs) of
|
||||
"1.0" ->
|
||||
Header = io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.streamid,
|
||||
Server,
|
||||
" version='1.0'",
|
||||
DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
case StateData#state.authentificated of
|
||||
false ->
|
||||
SASLState =
|
||||
cyrsasl:server_new(
|
||||
"jabber", Server, "", [],
|
||||
fun(U) ->
|
||||
ejabberd_auth:get_password(
|
||||
U, Server)
|
||||
end,
|
||||
fun(U, P) ->
|
||||
ejabberd_auth:check_password(
|
||||
U, Server, P)
|
||||
end),
|
||||
Mechs = lists:map(
|
||||
fun(S) ->
|
||||
{xmlelement, "mechanism", [],
|
||||
[{xmlcdata, S}]}
|
||||
end, cyrsasl:listmech()),
|
||||
TLS = StateData#state.tls,
|
||||
TLSEnabled = StateData#state.tls_enabled,
|
||||
TLSRequired = StateData#state.tls_required,
|
||||
SockMod = StateData#state.sockmod,
|
||||
TLSFeature =
|
||||
case (TLS == true) andalso
|
||||
(TLSEnabled == false) andalso
|
||||
(SockMod == gen_tcp) of
|
||||
true ->
|
||||
case TLSRequired of
|
||||
true ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}],
|
||||
[{xmlelement, "required",
|
||||
[], []}]}];
|
||||
_ ->
|
||||
[{xmlelement, "starttls",
|
||||
[{"xmlns", ?NS_TLS}], []}]
|
||||
end;
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
TLSFeature ++
|
||||
[{xmlelement, "mechanisms",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
Mechs},
|
||||
{xmlelement, "register",
|
||||
[{"xmlns", ?NS_FEATURE_IQREGISTER}],
|
||||
[]}]}),
|
||||
{next_state, wait_for_feature_request,
|
||||
StateData#state{server = Server,
|
||||
sasl_state = SASLState,
|
||||
lang = Lang}};
|
||||
_ ->
|
||||
case StateData#state.resource of
|
||||
"" ->
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
[{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}], []},
|
||||
{xmlelement, "session",
|
||||
[{"xmlns", ?NS_SESSION}], []}]}),
|
||||
{next_state, wait_for_bind,
|
||||
StateData#state{server = Server,
|
||||
lang = Lang}};
|
||||
_ ->
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [], []}),
|
||||
{next_state, wait_for_session,
|
||||
StateData#state{server = Server,
|
||||
lang = Lang}}
|
||||
end
|
||||
end;
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case xml:get_attr_s("version", Attrs) of
|
||||
"1.0" ->
|
||||
Header = io_lib:format(?STREAM_HEADER,
|
||||
[StateData#state.streamid,
|
||||
?MYNAME,
|
||||
" version='1.0'",
|
||||
DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
case StateData#state.authentificated of
|
||||
false ->
|
||||
SASLState =
|
||||
cyrsasl:server_new("jabber", ?MYNAME, "", []),
|
||||
Mechs = lists:map(
|
||||
fun(S) ->
|
||||
{xmlelement, "mechanism", [],
|
||||
[{xmlcdata, S}]}
|
||||
end, cyrsasl:listmech()),
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
[{xmlelement, "mechanisms",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
Mechs}]}),
|
||||
{next_state, wait_for_sasl_auth,
|
||||
StateData#state{sasl_state = SASLState,
|
||||
lang = Lang}};
|
||||
_ ->
|
||||
Header = io_lib:format(
|
||||
?STREAM_HEADER,
|
||||
[StateData#state.streamid, Server, "",
|
||||
DefaultLang]),
|
||||
if
|
||||
(not StateData#state.tls_enabled) and
|
||||
StateData#state.tls_required ->
|
||||
send_text(StateData,
|
||||
Header ++
|
||||
?POLICY_VIOLATION_ERR(
|
||||
Lang,
|
||||
"Use of STARTTLS required") ++
|
||||
?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
true ->
|
||||
send_text(StateData, Header),
|
||||
{next_state, wait_for_auth,
|
||||
StateData#state{server = Server,
|
||||
lang = Lang}}
|
||||
case StateData#state.resource of
|
||||
"" ->
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
[{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}], []},
|
||||
{xmlelement, "session",
|
||||
[{"xmlns", ?NS_SESSION}], []}]}),
|
||||
{next_state, wait_for_bind,
|
||||
StateData#state{lang = Lang}};
|
||||
_ ->
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [], []}),
|
||||
{next_state, wait_for_session,
|
||||
StateData#state{lang = Lang}}
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Header = io_lib:format(
|
||||
?STREAM_HEADER,
|
||||
[StateData#state.streamid, ?MYNAME, "",
|
||||
DefaultLang]),
|
||||
send_text(StateData,
|
||||
Header ++ ?HOST_UNKNOWN_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
[StateData#state.streamid, ?MYNAME, "", DefaultLang]),
|
||||
send_text(StateData, Header),
|
||||
{next_state, wait_for_auth, StateData#state{lang = Lang}}
|
||||
end;
|
||||
_ ->
|
||||
Header = io_lib:format(
|
||||
?STREAM_HEADER,
|
||||
[StateData#state.streamid, ?MYNAME, "", DefaultLang]),
|
||||
[StateData#state.streamid, ?MYNAME, "", ""]),
|
||||
send_text(StateData,
|
||||
Header ++ ?INVALID_NS_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData}
|
||||
@@ -332,41 +240,34 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
send_element(StateData, Res),
|
||||
{next_state, wait_for_auth, StateData};
|
||||
{auth, _ID, set, {U, P, D, ""}} ->
|
||||
Err = jlib:make_error_reply(
|
||||
El,
|
||||
?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)),
|
||||
Err = jlib:make_error_reply(El, ?ERR_AUTH_NO_RESOURCE_PROVIDED),
|
||||
send_element(StateData, Err),
|
||||
{next_state, wait_for_auth, StateData};
|
||||
{auth, _ID, set, {U, P, D, R}} ->
|
||||
io:format("AUTH: ~p~n", [{U, P, D, R}]),
|
||||
JID = jlib:make_jid(U, StateData#state.server, R),
|
||||
case (JID /= error) andalso
|
||||
(acl:match_rule(StateData#state.access, JID) == allow) of
|
||||
true ->
|
||||
case ejabberd_auth:check_password(
|
||||
U, StateData#state.server, P,
|
||||
StateData#state.streamid, D) of
|
||||
U, P, StateData#state.streamid, D) of
|
||||
true ->
|
||||
?INFO_MSG(
|
||||
"(~w) Accepted legacy authentication for ~s",
|
||||
"(~w) Accepted legacy authentification for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
ejabberd_sm:open_session(
|
||||
U, StateData#state.server, R),
|
||||
ejabberd_sm:open_session(U, R),
|
||||
Res1 = jlib:make_result_iq_reply(El),
|
||||
Res = setelement(4, Res1, []),
|
||||
send_element(StateData, Res),
|
||||
change_shaper(StateData, JID),
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
{[], []},
|
||||
[U, StateData#state.server]),
|
||||
{Fs, Ts} = mod_roster:get_subscription_lists(U),
|
||||
LJID = jlib:jid_tolower(
|
||||
jlib:jid_remove_resource(JID)),
|
||||
Fs1 = [LJID | Fs],
|
||||
Ts1 = [LJID | Ts],
|
||||
PrivList =
|
||||
case catch mod_privacy:get_user_list(
|
||||
U, StateData#state.server) of
|
||||
case catch mod_privacy:get_user_list(U) of
|
||||
{'EXIT', _} -> none;
|
||||
PL -> PL
|
||||
end,
|
||||
@@ -379,11 +280,11 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
privacy_list = PrivList}};
|
||||
_ ->
|
||||
?INFO_MSG(
|
||||
"(~w) Failed legacy authentication for ~s",
|
||||
"(~w) Failed legacy authentification for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
Err = jlib:make_error_reply(
|
||||
El, ?ERR_NOT_AUTHORIZED),
|
||||
El, ?ERR_FORBIDDEN),
|
||||
send_element(StateData, Err),
|
||||
{next_state, wait_for_auth, StateData}
|
||||
end;
|
||||
@@ -391,7 +292,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
if
|
||||
JID == error ->
|
||||
?INFO_MSG(
|
||||
"(~w) Forbidden legacy authentication for "
|
||||
"(~w) Forbidden legacy authentification for "
|
||||
"username '~s' with resource '~s'",
|
||||
[StateData#state.socket, U, R]),
|
||||
Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED),
|
||||
@@ -399,7 +300,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
{next_state, wait_for_auth, StateData};
|
||||
true ->
|
||||
?INFO_MSG(
|
||||
"(~w) Forbidden legacy authentication for ~s",
|
||||
"(~w) Forbidden legacy authentification for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
|
||||
@@ -411,10 +312,8 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""},
|
||||
jlib:make_jid("", StateData#state.server, ""),
|
||||
IQ),
|
||||
Res1 = jlib:replace_from_to({"", StateData#state.server, ""},
|
||||
{"", "", ""}, {"", ?MYNAME, ""}, IQ),
|
||||
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
|
||||
{"", "", ""},
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
Res = jlib:remove_attr("to", Res1),
|
||||
@@ -437,14 +336,10 @@ wait_for_auth(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
wait_for_sasl_auth({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, Name, Attrs, Els} = El,
|
||||
TLS = StateData#state.tls,
|
||||
TLSEnabled = StateData#state.tls_enabled,
|
||||
TLSRequired = StateData#state.tls_required,
|
||||
SockMod = StateData#state.sockmod,
|
||||
case {xml:get_attr_s("xmlns", Attrs), Name} of
|
||||
{?NS_SASL, "auth"} when not ((SockMod == gen_tcp) and TLSRequired) ->
|
||||
{?NS_SASL, "auth"} ->
|
||||
Mech = xml:get_attr_s("mechanism", Attrs),
|
||||
ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
|
||||
case cyrsasl:server_start(StateData#state.sasl_state,
|
||||
@@ -456,11 +351,10 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
U = xml:get_attr_s(username, Props),
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s",
|
||||
?INFO_MSG("(~w) Accepted authentification for ~s",
|
||||
[StateData#state.socket, U]),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{streamid = new_id(),
|
||||
authentificated = true,
|
||||
StateData#state{authentificated = true,
|
||||
user = U
|
||||
}};
|
||||
{continue, ServerOut, NewSASLState} ->
|
||||
@@ -476,60 +370,33 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, "failure",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
[{xmlelement, Error, [], []}]}),
|
||||
{next_state, wait_for_feature_request, StateData}
|
||||
{next_state, wait_for_sasl_auth, StateData}
|
||||
end;
|
||||
{?NS_TLS, "starttls"} when TLS == true,
|
||||
TLSEnabled == false,
|
||||
SockMod == gen_tcp ->
|
||||
Socket = StateData#state.socket,
|
||||
TLSOpts = StateData#state.tls_options,
|
||||
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
|
||||
ejabberd_receiver:starttls(StateData#state.receiver, TLSSocket),
|
||||
send_element(StateData,
|
||||
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []}),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{sockmod = tls,
|
||||
socket = TLSSocket,
|
||||
streamid = new_id(),
|
||||
tls_enabled = true
|
||||
}};
|
||||
_ ->
|
||||
if
|
||||
(SockMod == gen_tcp) and TLSRequired ->
|
||||
Lang = StateData#state.lang,
|
||||
send_text(StateData, ?POLICY_VIOLATION_ERR(
|
||||
Lang,
|
||||
"Use of STARTTLS required") ++
|
||||
?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
true ->
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""},
|
||||
jlib:make_jid("", StateData#state.server, ""),
|
||||
IQ),
|
||||
Res1 = jlib:replace_from_to({"", StateData#state.server, ""},
|
||||
{"", "", ""},
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
Res = jlib:remove_attr("to", Res1),
|
||||
send_element(StateData, Res),
|
||||
{next_state, wait_for_feature_request, StateData};
|
||||
_ ->
|
||||
{next_state, wait_for_feature_request, StateData}
|
||||
end
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""}, {"", ?MYNAME, ""}, IQ),
|
||||
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
|
||||
{"", "", ""},
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
Res = jlib:remove_attr("to", Res1),
|
||||
send_element(StateData, Res),
|
||||
{next_state, wait_for_sasl_auth, StateData};
|
||||
_ ->
|
||||
{next_state, wait_for_sasl_auth, StateData}
|
||||
end
|
||||
end;
|
||||
|
||||
wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
|
||||
wait_for_sasl_auth({xmlstreamend, _Name}, StateData) ->
|
||||
send_text(StateData, ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_feature_request({xmlstreamerror, _}, StateData) ->
|
||||
wait_for_sasl_auth({xmlstreamerror, _}, StateData) ->
|
||||
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||
{stop, normal, StateData};
|
||||
|
||||
wait_for_feature_request(closed, StateData) ->
|
||||
wait_for_sasl_auth(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
@@ -546,11 +413,10 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
U = xml:get_attr_s(username, Props),
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s",
|
||||
?INFO_MSG("(~w) Accepted authentification for ~s",
|
||||
[StateData#state.socket, U]),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{streamid = new_id(),
|
||||
authentificated = true,
|
||||
StateData#state{authentificated = true,
|
||||
user = U
|
||||
}};
|
||||
{continue, ServerOut, NewSASLState} ->
|
||||
@@ -566,23 +432,21 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, "failure",
|
||||
[{"xmlns", ?NS_SASL}],
|
||||
[{xmlelement, Error, [], []}]}),
|
||||
{next_state, wait_for_feature_request, StateData}
|
||||
{next_state, wait_for_sasl_auth, StateData}
|
||||
end;
|
||||
_ ->
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
ResIQ = mod_register:process_iq(
|
||||
{"", "", ""},
|
||||
jlib:make_jid("", StateData#state.server, ""),
|
||||
IQ),
|
||||
Res1 = jlib:replace_from_to({"", StateData#state.server, ""},
|
||||
{"", "", ""}, {"", ?MYNAME, ""}, IQ),
|
||||
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
|
||||
{"", "", ""},
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
Res = jlib:remove_attr("to", Res1),
|
||||
send_element(StateData, Res),
|
||||
{next_state, wait_for_feature_request, StateData};
|
||||
{next_state, wait_for_sasl_auth, StateData};
|
||||
_ ->
|
||||
{next_state, wait_for_feature_request, StateData}
|
||||
{next_state, wait_for_sasl_auth, StateData}
|
||||
end
|
||||
end;
|
||||
|
||||
@@ -650,27 +514,23 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
|
||||
#iq{type = set, xmlns = ?NS_SESSION} ->
|
||||
U = StateData#state.user,
|
||||
R = StateData#state.resource,
|
||||
io:format("SASLAUTH: ~p~n", [{U, R}]),
|
||||
JID = StateData#state.jid,
|
||||
case acl:match_rule(StateData#state.access, JID) of
|
||||
allow ->
|
||||
?INFO_MSG("(~w) Opened session for ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
ejabberd_sm:open_session(
|
||||
U, StateData#state.server, R),
|
||||
ejabberd_sm:open_session(U, R),
|
||||
Res = jlib:make_result_iq_reply(El),
|
||||
send_element(StateData, Res),
|
||||
change_shaper(StateData, JID),
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
{[], []},
|
||||
[U, StateData#state.server]),
|
||||
{Fs, Ts} = mod_roster:get_subscription_lists(U),
|
||||
LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
Fs1 = [LJID | Fs],
|
||||
Ts1 = [LJID | Ts],
|
||||
PrivList =
|
||||
case catch mod_privacy:get_user_list(
|
||||
U, StateData#state.server) of
|
||||
case catch mod_privacy:get_user_list(U) of
|
||||
{'EXIT', _} -> none;
|
||||
PL -> PL
|
||||
end,
|
||||
@@ -762,17 +622,12 @@ session_established({xmlstreamelement, El}, StateData) ->
|
||||
process_privacy_iq(
|
||||
FromJID, ToJID, IQ, StateData);
|
||||
_ ->
|
||||
ejabberd_hooks:run(
|
||||
user_send_packet,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
ejabberd_router:route(
|
||||
FromJID, ToJID, NewEl),
|
||||
StateData
|
||||
end
|
||||
end;
|
||||
"message" ->
|
||||
ejabberd_hooks:run(user_send_packet,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
ejabberd_router:route(FromJID, ToJID, NewEl),
|
||||
StateData;
|
||||
_ ->
|
||||
@@ -831,9 +686,8 @@ handle_sync_event({get_presence}, _From, StateName, StateData) ->
|
||||
|
||||
Show = get_showtag(PresLast),
|
||||
Status = get_statustag(PresLast),
|
||||
Resource = StateData#state.resource,
|
||||
|
||||
Reply = {User, Resource, Show, Status},
|
||||
Reply = {User, Show, Status},
|
||||
{reply, Reply, StateName, StateData};
|
||||
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
@@ -885,7 +739,6 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in) of
|
||||
@@ -935,7 +788,6 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
#iq{} ->
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in) of
|
||||
@@ -957,7 +809,6 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
"message" ->
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
in) of
|
||||
@@ -979,11 +830,8 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
NewAttrs),
|
||||
FixedPacket = {xmlelement, Name, Attrs2, Els},
|
||||
Text = xml:element_to_string(FixedPacket),
|
||||
Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
|
||||
send_text(StateData, Text),
|
||||
ejabberd_hooks:run(user_receive_packet,
|
||||
[StateData#state.jid, From, To, FixedPacket]),
|
||||
{next_state, StateName, NewState};
|
||||
true ->
|
||||
{next_state, StateName, NewState}
|
||||
@@ -1008,7 +856,6 @@ terminate(_Reason, StateName, StateData) ->
|
||||
[{xmlelement, "status", [],
|
||||
[{xmlcdata, "Replaced by new connection"}]}]},
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource,
|
||||
"Replaced by new connection"),
|
||||
presence_broadcast(
|
||||
@@ -1020,7 +867,6 @@ terminate(_Reason, StateName, StateData) ->
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(StateData#state.jid)]),
|
||||
ejabberd_sm:close_session(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource),
|
||||
|
||||
Tmp = ?SETS:new(),
|
||||
@@ -1035,7 +881,6 @@ terminate(_Reason, StateName, StateData) ->
|
||||
Packet = {xmlelement, "presence",
|
||||
[{"type", "unavailable"}], []},
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource,
|
||||
""),
|
||||
presence_broadcast(
|
||||
@@ -1126,7 +971,6 @@ process_presence_probe(From, To, StateData) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{To, From, Packet},
|
||||
out) of
|
||||
@@ -1159,7 +1003,6 @@ presence_update(From, Packet, StateData) ->
|
||||
xml:get_tag_cdata(StatusTag)
|
||||
end,
|
||||
ejabberd_sm:unset_presence(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource,
|
||||
Status),
|
||||
presence_broadcast(StateData, From, StateData#state.pres_a, Packet),
|
||||
@@ -1207,8 +1050,6 @@ presence_update(From, Packet, StateData) ->
|
||||
NewState =
|
||||
if
|
||||
FromUnavail ->
|
||||
ejabberd_hooks:run(user_available_hook,
|
||||
[StateData#state.jid]),
|
||||
resend_offline_messages(StateData),
|
||||
presence_broadcast_first(
|
||||
From, StateData#state{pres_last = Packet,
|
||||
@@ -1231,7 +1072,6 @@ presence_track(From, To, Packet, StateData) ->
|
||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||
LTo = jlib:jid_tolower(To),
|
||||
User = StateData#state.user,
|
||||
Server = StateData#state.server,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"unavailable" ->
|
||||
ejabberd_router:route(From, To, Packet),
|
||||
@@ -1247,23 +1087,19 @@ presence_track(From, To, Packet, StateData) ->
|
||||
pres_a = A};
|
||||
"subscribe" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
[User, Server, To, subscribe]),
|
||||
mod_roster:out_subscription(User, To, subscribe),
|
||||
StateData;
|
||||
"subscribed" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
[User, Server, To, subscribed]),
|
||||
mod_roster:out_subscription(User, To, subscribed),
|
||||
StateData;
|
||||
"unsubscribe" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
[User, Server, To, unsubscribe]),
|
||||
mod_roster:out_subscription(User, To, unsubscribe),
|
||||
StateData;
|
||||
"unsubscribed" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
[User, Server, To, unsubscribed]),
|
||||
mod_roster:out_subscription(User, To, unsubscribed),
|
||||
StateData;
|
||||
"error" ->
|
||||
ejabberd_router:route(From, To, Packet),
|
||||
@@ -1275,7 +1111,6 @@ presence_track(From, To, Packet, StateData) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, To, Packet},
|
||||
out) of
|
||||
@@ -1299,7 +1134,6 @@ presence_broadcast(StateData, From, JIDSet, Packet) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out) of
|
||||
@@ -1322,7 +1156,6 @@ presence_broadcast_to_trusted(StateData, From, T, A, Packet) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out) of
|
||||
@@ -1362,7 +1195,6 @@ presence_broadcast_first(From, StateData, Packet) ->
|
||||
%-ifdef(PRIVACY_SUPPORT).
|
||||
case catch mod_privacy:check_packet(
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.privacy_list,
|
||||
{From, FJID, Packet},
|
||||
out) of
|
||||
@@ -1458,10 +1290,9 @@ update_priority(El, StateData) ->
|
||||
end
|
||||
end,
|
||||
ejabberd_sm:set_presence(StateData#state.user,
|
||||
StateData#state.server,
|
||||
StateData#state.resource,
|
||||
Pri).
|
||||
|
||||
|
||||
|
||||
|
||||
process_privacy_iq(From, To,
|
||||
@@ -1502,39 +1333,18 @@ process_privacy_iq(From, To,
|
||||
NewStateData.
|
||||
|
||||
|
||||
resend_offline_messages(#state{user = User,
|
||||
server = Server,
|
||||
privacy_list = PrivList} = StateData) ->
|
||||
case ejabberd_hooks:run_fold(resend_offline_messages_hook, [],
|
||||
[User, Server]) of
|
||||
resend_offline_messages(StateData) ->
|
||||
case catch mod_offline:pop_offline_messages(StateData#state.user) of
|
||||
{'EXIT', _Reason} ->
|
||||
ok;
|
||||
Rs when list(Rs) ->
|
||||
lists:foreach(
|
||||
fun({route,
|
||||
From, To, {xmlelement, Name, Attrs, Els} = Packet}) ->
|
||||
Pass = case catch mod_privacy:check_packet(
|
||||
User,
|
||||
Server,
|
||||
PrivList,
|
||||
{From, To, Packet},
|
||||
in) of
|
||||
{'EXIT', _Reason} ->
|
||||
true;
|
||||
allow ->
|
||||
true;
|
||||
deny ->
|
||||
false
|
||||
end,
|
||||
if
|
||||
Pass ->
|
||||
Attrs2 = jlib:replace_from_to_attrs(
|
||||
jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
send_element(StateData,
|
||||
{xmlelement, Name, Attrs2, Els});
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
fun({route, From, To, {xmlelement, Name, Attrs, Els}}) ->
|
||||
Attrs2 = jlib:replace_from_to_attrs(
|
||||
jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
send_element(StateData, {xmlelement, Name, Attrs2, Els})
|
||||
end, Rs)
|
||||
end.
|
||||
|
||||
|
||||
@@ -34,17 +34,9 @@ start() ->
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, local_config)}]),
|
||||
mnesia:add_table_copy(local_config, node(), ram_copies),
|
||||
|
||||
%% mremond: Config file can be configured from the command line
|
||||
Config = case application:get_env(config) of
|
||||
{ok, Path} -> Path;
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_CONFIG_PATH") of
|
||||
false ->
|
||||
?CONFIG_PATH;
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
undefined -> ?CONFIG_PATH
|
||||
end,
|
||||
load_file(Config).
|
||||
|
||||
@@ -78,15 +70,13 @@ process_term(Term, State) ->
|
||||
State#state{opts = [#config{key = {shaper, Name},
|
||||
value = Data} |
|
||||
State#state.opts]};
|
||||
{host, Host} ->
|
||||
add_option(hosts, [Host], State);
|
||||
{Opt, Val} ->
|
||||
add_option(Opt, Val, State)
|
||||
end.
|
||||
|
||||
add_option(Opt, Val, State) ->
|
||||
Table = case Opt of
|
||||
hosts ->
|
||||
host ->
|
||||
config;
|
||||
language ->
|
||||
config;
|
||||
|
||||
@@ -9,196 +9,126 @@
|
||||
-module(ejabberd_ctl).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([start/0, dump_to_textfile/1]).
|
||||
|
||||
-define(STATUS_SUCCESS, 0).
|
||||
-define(STATUS_ERROR, 1).
|
||||
-define(STATUS_USAGE, 2).
|
||||
-define(STATUS_BADRPC, 3).
|
||||
-export([start/0]).
|
||||
|
||||
start() ->
|
||||
case init:get_plain_arguments() of
|
||||
[SNode | Args] ->
|
||||
Node = list_to_atom(SNode),
|
||||
Status = process(Node, Args),
|
||||
halt(Status);
|
||||
process(Node, Args);
|
||||
_ ->
|
||||
print_usage(),
|
||||
halt(?STATUS_USAGE)
|
||||
end.
|
||||
print_usage()
|
||||
end,
|
||||
halt().
|
||||
|
||||
|
||||
process(Node, ["status"]) ->
|
||||
case rpc:call(Node, init, get_status, []) of
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't get node ~p status: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_BADRPC;
|
||||
{InternalStatus, ProvidedStatus} ->
|
||||
io:format("Node ~p is ~p. Status: ~p~n",
|
||||
[Node, InternalStatus, ProvidedStatus]),
|
||||
?STATUS_SUCCESS
|
||||
end;
|
||||
|
||||
process(Node, ["stop"]) ->
|
||||
case rpc:call(Node, init, stop, []) of
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't stop node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_BADRPC;
|
||||
[Node, Reason]);
|
||||
_ ->
|
||||
?STATUS_SUCCESS
|
||||
ok
|
||||
end;
|
||||
|
||||
process(Node, ["restart"]) ->
|
||||
case rpc:call(Node, init, restart, []) of
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't restart node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_BADRPC;
|
||||
[Node, Reason]);
|
||||
_ ->
|
||||
?STATUS_SUCCESS
|
||||
ok
|
||||
end;
|
||||
|
||||
process(Node, ["reopen-log"]) ->
|
||||
case rpc:call(Node, ejabberd_logger_h, reopen_log, []) of
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't reopen node ~p log: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_BADRPC;
|
||||
_ ->
|
||||
?STATUS_SUCCESS
|
||||
end;
|
||||
{error_logger, Node} ! {emulator, noproc, reopen};
|
||||
|
||||
process(Node, ["register", User, Server, Password]) ->
|
||||
case rpc:call(Node, ejabberd_auth, try_register, [User, Server, Password]) of
|
||||
process(Node, ["register", User, Password]) ->
|
||||
case rpc:call(Node, ejabberd_auth, try_register, [User, Password]) of
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
ok;
|
||||
{atomic, exists} ->
|
||||
io:format("User ~p already registered at node ~p~n",
|
||||
[User ++ "@" ++ Server, Node]),
|
||||
?STATUS_ERROR;
|
||||
io:format("User ~p already registered on node ~p~n",
|
||||
[User, Node]);
|
||||
{error, Reason} ->
|
||||
io:format("Can't register user ~p at node ~p: ~p~n",
|
||||
[User ++ "@" ++ Server, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
io:format("Can't register user ~p on node ~p: ~p~n",
|
||||
[User, Node, Reason]);
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't register user ~p at node ~p: ~p~n",
|
||||
[User ++ "@" ++ Server, Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
io:format("Can't register user ~p on node ~p: ~p~n",
|
||||
[User, Node, Reason])
|
||||
end;
|
||||
|
||||
process(Node, ["unregister", User, Server]) ->
|
||||
case rpc:call(Node, ejabberd_auth, remove_user, [User, Server]) of
|
||||
process(Node, ["unregister", User]) ->
|
||||
case rpc:call(Node, ejabberd_auth, remove_user, [User]) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
io:format("Can't unregister user ~p at node ~p: ~p~n",
|
||||
[User ++ "@" ++ Server, Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
io:format("Can't unregister user ~p on node ~p: ~p~n",
|
||||
[User, Node, Reason]);
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't unregister user ~p at node ~p: ~p~n",
|
||||
[User ++ "@" ++ Server, Node, Reason]),
|
||||
?STATUS_BADRPC;
|
||||
_ ->
|
||||
?STATUS_SUCCESS
|
||||
io:format("Can't unregister user ~p on node ~p: ~p~n",
|
||||
[User, Node, Reason])
|
||||
end;
|
||||
|
||||
process(Node, ["backup", Path]) ->
|
||||
case rpc:call(Node, mnesia, backup, [Path]) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
io:format("Can't store backup in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
io:format("Can't store backup in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]);
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't store backup in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
io:format("Can't store backup in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason])
|
||||
end;
|
||||
|
||||
process(Node, ["dump", Path]) ->
|
||||
case rpc:call(Node, ejabberd_ctl, dump_to_textfile, [Path]) of
|
||||
case rpc:call(Node, mnesia, dump_to_textfile, [Path]) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
io:format("Can't store dump in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
io:format("Can't store dump in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]);
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't store dump in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
io:format("Can't store dump in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason])
|
||||
end;
|
||||
|
||||
process(Node, ["load", Path]) ->
|
||||
case rpc:call(Node, mnesia, load_textfile, [Path]) of
|
||||
{atomic, ok} ->
|
||||
?STATUS_SUCCESS;
|
||||
ok ->
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
io:format("Can't load dump in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
io:format("Can't load dump in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]);
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't load dump in ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
io:format("Can't load dump in ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason])
|
||||
end;
|
||||
|
||||
process(Node, ["restore", Path]) ->
|
||||
case rpc:call(Node,
|
||||
mnesia, restore, [Path, [{default_op, keep_tables}]]) of
|
||||
{atomic, _} ->
|
||||
?STATUS_SUCCESS;
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
io:format("Can't restore backup from ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
io:format("Can't restore backup from ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]);
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't restore backup from ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
io:format("Can't restore backup from ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason])
|
||||
end;
|
||||
|
||||
process(Node, ["install-fallback", Path]) ->
|
||||
case rpc:call(Node, mnesia, install_fallback, [Path]) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
io:format("Can't install fallback from ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
io:format("Can't install fallback from ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason]);
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't install fallback from ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["import-file", Path]) ->
|
||||
case rpc:call(Node, jd2ejd, import_file, [Path]) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't import jabberd 1.4 spool file ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't import jabberd 1.4 spool file ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["import-dir", Path]) ->
|
||||
case rpc:call(Node, jd2ejd, import_dir, [Path]) of
|
||||
ok ->
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't import jabberd 1.4 spool dir ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't import jabberd 1.4 spool dir ~p at node ~p: ~p~n",
|
||||
[filename:absname(Path), Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
io:format("Can't install fallback from ~p on node ~p: ~p~n",
|
||||
[Path, Node, Reason])
|
||||
end;
|
||||
|
||||
process(Node, ["registered-users"]) ->
|
||||
@@ -206,32 +136,16 @@ process(Node, ["registered-users"]) ->
|
||||
Users when is_list(Users) ->
|
||||
NewLine = io_lib:format("~n", []),
|
||||
SUsers = lists:sort(Users),
|
||||
FUsers = lists:map(fun({U, S}) -> [U, $@, S, NewLine] end, SUsers),
|
||||
FUsers = lists:map(fun(U) -> [U, NewLine] end, SUsers),
|
||||
io:format("~s", [FUsers]),
|
||||
?STATUS_SUCCESS;
|
||||
{error, Reason} ->
|
||||
io:format("Can't get list of registered users at node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_ERROR;
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't get list of registered users at node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_BADRPC
|
||||
end;
|
||||
|
||||
process(Node, ["delete-expired-messages"]) ->
|
||||
case rpc:call(Node, mod_offline, remove_expired_messages, []) of
|
||||
{badrpc, Reason} ->
|
||||
io:format("Can't delete expired messages at node ~p: ~p~n",
|
||||
[Node, Reason]),
|
||||
?STATUS_BADRPC;
|
||||
_ ->
|
||||
?STATUS_SUCCESS
|
||||
ok;
|
||||
{ErrorTag, Reason} when (ErrorTag == error) or (ErrorTag == badrpc) ->
|
||||
io:format("Can't get list of registered users on node ~p: ~p~n",
|
||||
[Node, Reason])
|
||||
end;
|
||||
|
||||
process(_Node, _Args) ->
|
||||
print_usage(),
|
||||
?STATUS_USAGE.
|
||||
print_usage().
|
||||
|
||||
|
||||
|
||||
@@ -240,57 +154,18 @@ print_usage() ->
|
||||
"Usage: ejabberdctl node command~n"
|
||||
"~n"
|
||||
"Available commands:~n"
|
||||
" status\t\t\tget ejabberd status~n"
|
||||
" stop\t\t\t\tstop ejabberd~n"
|
||||
" restart\t\t\trestart ejabberd~n"
|
||||
" reopen-log\t\t\treopen log file~n"
|
||||
" register user server password\tregister a user~n"
|
||||
" unregister user server\tunregister a user~n"
|
||||
" backup file\t\t\tstore a database backup to file~n"
|
||||
" register user password\tregister a user~n"
|
||||
" unregister user\t\tunregister a user~n"
|
||||
" backup file\t\t\tstore a database backup in file~n"
|
||||
" restore file\t\t\trestore a database backup from file~n"
|
||||
" install-fallback file\t\tinstall a database fallback from file~n"
|
||||
" dump file\t\t\tdump a database to a text file~n"
|
||||
" dump file\t\t\tdump a database in a text file~n"
|
||||
" load file\t\t\trestore a database from a text file~n"
|
||||
" import-file file\t\timport user data from jabberd 1.4 spool file~n"
|
||||
" import-dir dir\t\timport user data from jabberd 1.4 spool directory~n"
|
||||
" registered-users\t\tlist all registered users~n"
|
||||
" delete-expired-messages\tdelete expired offline messages from database~n"
|
||||
"~n"
|
||||
"Example:~n"
|
||||
" ejabberdctl ejabberd@host restart~n"
|
||||
).
|
||||
|
||||
dump_to_textfile(File) ->
|
||||
dump_to_textfile(mnesia:system_info(is_running), file:open(File, write)).
|
||||
dump_to_textfile(yes, {ok, F}) ->
|
||||
Tabs1 = lists:delete(schema, mnesia:system_info(local_tables)),
|
||||
Tabs = lists:filter(
|
||||
fun(T) ->
|
||||
case mnesia:table_info(T, storage_type) of
|
||||
disc_copies -> true;
|
||||
disc_only_copies -> true;
|
||||
_ -> false
|
||||
end
|
||||
end, Tabs1),
|
||||
Defs = lists:map(
|
||||
fun(T) -> {T, [{record_name, mnesia:table_info(T, record_name)},
|
||||
{attributes, mnesia:table_info(T, attributes)}]}
|
||||
end,
|
||||
Tabs),
|
||||
io:format(F, "~p.~n", [{tables, Defs}]),
|
||||
lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
|
||||
file:close(F);
|
||||
dump_to_textfile(_, {ok, F}) ->
|
||||
file:close(F),
|
||||
{error, mnesia_not_running};
|
||||
dump_to_textfile(_, {error, Reason}) ->
|
||||
{error, Reason}.
|
||||
|
||||
|
||||
dump_tab(F, T) ->
|
||||
W = mnesia:table_info(T, wild_pattern),
|
||||
{atomic,All} = mnesia:transaction(
|
||||
fun() -> mnesia:match_object(T, W, read) end),
|
||||
lists:foreach(
|
||||
fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All).
|
||||
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_hooks.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Manage hooks
|
||||
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_hooks).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% External exports
|
||||
-export([start_link/0,
|
||||
add/4,
|
||||
delete/4,
|
||||
run/2,
|
||||
run_fold/3]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
code_change/3,
|
||||
handle_info/2,
|
||||
terminate/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []).
|
||||
|
||||
add(Hook, Module, Function, Seq) ->
|
||||
gen_server:call(ejabberd_hooks, {add, Hook, Module, Function, Seq}).
|
||||
|
||||
delete(Hook, Module, Function, Seq) ->
|
||||
gen_server:call(ejabberd_hooks, {delete, Hook, Module, Function, Seq}).
|
||||
|
||||
run(Hook, Args) ->
|
||||
case ets:lookup(hooks, Hook) of
|
||||
[{_, Ls}] ->
|
||||
run1(Ls, Hook, Args);
|
||||
[] ->
|
||||
ok
|
||||
end.
|
||||
|
||||
run_fold(Hook, Val, Args) ->
|
||||
case ets:lookup(hooks, Hook) of
|
||||
[{_, Ls}] ->
|
||||
run_fold1(Ls, Hook, Val, Args);
|
||||
[] ->
|
||||
Val
|
||||
end.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_server
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([]) ->
|
||||
ets:new(hooks, [named_table]),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_call/3
|
||||
%% Returns: {reply, Reply, State} |
|
||||
%% {reply, Reply, State, Timeout} |
|
||||
%% {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, Reply, State} | (terminate/2 is called)
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_call({add, Hook, Module, Function, Seq}, From, State) ->
|
||||
Reply = case ets:lookup(hooks, Hook) of
|
||||
[{_, Ls}] ->
|
||||
El = {Seq, Module, Function},
|
||||
case lists:member(El, Ls) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
NewLs = lists:merge(Ls, [El]),
|
||||
ets:insert(hooks, {Hook, NewLs}),
|
||||
ok
|
||||
end;
|
||||
[] ->
|
||||
NewLs = [{Seq, Module, Function}],
|
||||
ets:insert(hooks, {Hook, NewLs}),
|
||||
ok
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
handle_call({delete, Hook, Module, Function, Seq}, From, State) ->
|
||||
Reply = case ets:lookup(hooks, Hook) of
|
||||
[{_, Ls}] ->
|
||||
NewLs = lists:delete({Seq, Module, Function}, Ls),
|
||||
ets:insert(hooks, {Hook, NewLs}),
|
||||
ok;
|
||||
[] ->
|
||||
ok
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
handle_call(Request, From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_cast/2
|
||||
%% Returns: {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_cast(Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/2
|
||||
%% Returns: {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_info(Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/2
|
||||
%% Purpose: Shutdown the server
|
||||
%% Returns: any (ignored by gen_server)
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, State) ->
|
||||
ok.
|
||||
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
run1([], Hook, Args) ->
|
||||
ok;
|
||||
run1([{_Seq, Module, Function} | Ls], Hook, Args) ->
|
||||
case catch apply(Module, Function, Args) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nrunning hook: ~p",
|
||||
[Reason, {Hook, Args}]),
|
||||
run1(Ls, Hook, Args);
|
||||
stop ->
|
||||
ok;
|
||||
_ ->
|
||||
run1(Ls, Hook, Args)
|
||||
end.
|
||||
|
||||
|
||||
run_fold1([], Hook, Val, Args) ->
|
||||
Val;
|
||||
run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) ->
|
||||
case catch apply(Module, Function, [Val | Args]) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nrunning hook: ~p",
|
||||
[Reason, {Hook, Args}]),
|
||||
run_fold1(Ls, Hook, Val, Args);
|
||||
stop ->
|
||||
stopped;
|
||||
{stop, NewVal} ->
|
||||
NewVal;
|
||||
NewVal ->
|
||||
run_fold1(Ls, Hook, NewVal, Args)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
@@ -70,8 +70,7 @@ init(Port, Module, Opts) ->
|
||||
{packet, 0},
|
||||
{active, false},
|
||||
{reuseaddr, true},
|
||||
{nodelay, true},
|
||||
{keepalive, true} |
|
||||
{nodelay, true} |
|
||||
SockOpts]),
|
||||
case Res of
|
||||
{ok, ListenSocket} ->
|
||||
@@ -93,12 +92,7 @@ accept(ListenSocket, Module, Opts) ->
|
||||
ok
|
||||
end,
|
||||
{ok, Pid} = Module:start({gen_tcp, Socket}, Opts),
|
||||
case gen_tcp:controlling_process(Socket, Pid) of
|
||||
ok ->
|
||||
ok;
|
||||
{error, _Reason} ->
|
||||
gen_tcp:close(Socket)
|
||||
end,
|
||||
gen_tcp:controlling_process(Socket, Pid),
|
||||
accept(ListenSocket, Module, Opts);
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("(~w) Failed TCP accept: ~w",
|
||||
@@ -145,7 +139,7 @@ accept_ssl(ListenSocket, Module, Opts) ->
|
||||
ok
|
||||
end,
|
||||
{ok, Pid} = Module:start({ssl, Socket}, Opts),
|
||||
catch ssl:controlling_process(Socket, Pid),
|
||||
ssl:controlling_process(Socket, Pid),
|
||||
accept_ssl(ListenSocket, Module, Opts);
|
||||
{error, timeout} ->
|
||||
accept_ssl(ListenSocket, Module, Opts);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_local.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Route local packets
|
||||
%%% Purpose :
|
||||
%%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -12,18 +12,16 @@
|
||||
|
||||
-export([start_link/0, init/0]).
|
||||
|
||||
-export([route/3,
|
||||
register_iq_handler/3,
|
||||
-export([register_iq_handler/3,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/1,
|
||||
refresh_iq_handlers/0,
|
||||
bounce_resource_packet/3
|
||||
refresh_iq_handlers/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(IQTABLE, local_iqtable).
|
||||
-record(state, {mydomain, iqtable}).
|
||||
|
||||
start_link() ->
|
||||
register(ejabberd_local,
|
||||
@@ -31,44 +29,41 @@ start_link() ->
|
||||
{ok, Pid}.
|
||||
|
||||
init() ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_router:register_route(Host, {apply, ?MODULE, route})
|
||||
end, ?MYHOSTS),
|
||||
catch ets:new(?IQTABLE, [named_table, public]),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook,
|
||||
?MODULE, bounce_resource_packet, 100),
|
||||
loop().
|
||||
MyDomain = ?MYNAME,
|
||||
ejabberd_router:register_route(MyDomain),
|
||||
catch ets:new(local_iqtable, [named_table, public]),
|
||||
loop(#state{mydomain = MyDomain,
|
||||
iqtable = local_iqtable}).
|
||||
|
||||
loop() ->
|
||||
loop(State) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
case catch do_route(State, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop();
|
||||
loop(State);
|
||||
{register_iq_handler, XMLNS, Module, Function} ->
|
||||
ets:insert(?IQTABLE, {XMLNS, Module, Function}),
|
||||
ets:insert(State#state.iqtable, {XMLNS, Module, Function}),
|
||||
catch mod_disco:register_feature(XMLNS),
|
||||
loop();
|
||||
loop(State);
|
||||
{register_iq_handler, XMLNS, Module, Function, Opts} ->
|
||||
ets:insert(?IQTABLE, {XMLNS, Module, Function, Opts}),
|
||||
ets:insert(State#state.iqtable, {XMLNS, Module, Function, Opts}),
|
||||
catch mod_disco:register_feature(XMLNS),
|
||||
loop();
|
||||
loop(State);
|
||||
{unregister_iq_handler, XMLNS} ->
|
||||
case ets:lookup(?IQTABLE, XMLNS) of
|
||||
case ets:lookup(State#state.iqtable, XMLNS) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
ets:delete(?IQTABLE, XMLNS),
|
||||
ets:delete(State#state.iqtable, XMLNS),
|
||||
catch mod_disco:unregister_feature(XMLNS),
|
||||
loop();
|
||||
loop(State);
|
||||
refresh_iq_handlers ->
|
||||
lists:foreach(
|
||||
fun(T) ->
|
||||
@@ -80,24 +75,22 @@ loop() ->
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, ets:tab2list(?IQTABLE)),
|
||||
loop();
|
||||
end, ets:tab2list(State#state.iqtable)),
|
||||
loop(State);
|
||||
_ ->
|
||||
loop()
|
||||
loop(State)
|
||||
end.
|
||||
|
||||
|
||||
do_route(From, To, Packet) ->
|
||||
do_route(State, From, To, Packet) ->
|
||||
?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
|
||||
[From, To, Packet, 8]),
|
||||
if
|
||||
To#jid.luser /= "" ->
|
||||
ejabberd_sm:route(From, To, Packet);
|
||||
To#jid.lresource == "" ->
|
||||
case To of
|
||||
#jid{luser = "", lresource = ""} ->
|
||||
{xmlelement, Name, Attrs, _Els} = Packet,
|
||||
case Name of
|
||||
"iq" ->
|
||||
process_iq(From, To, Packet);
|
||||
process_iq(State, From, To, Packet);
|
||||
"message" ->
|
||||
ok;
|
||||
"presence" ->
|
||||
@@ -105,22 +98,30 @@ do_route(From, To, Packet) ->
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
true ->
|
||||
#jid{luser = "", lresource = Res} ->
|
||||
{xmlelement, Name, Attrs, _Els} = Packet,
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"error" -> ok;
|
||||
"result" -> ok;
|
||||
_ ->
|
||||
ejabberd_hooks:run(local_send_to_resource_hook,
|
||||
[From, To, Packet])
|
||||
end
|
||||
end.
|
||||
case {Res, Name} of
|
||||
{"announce/online", "message"} ->
|
||||
announce_online(From, To, Packet);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
ejabberd_sm ! {route, From, To, Packet}
|
||||
end.
|
||||
|
||||
process_iq(From, To, Packet) ->
|
||||
process_iq(State, From, To, Packet) ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
#iq{xmlns = XMLNS} ->
|
||||
case ets:lookup(?IQTABLE, XMLNS) of
|
||||
case ets:lookup(State#state.iqtable, XMLNS) of
|
||||
[{_, Module, Function}] ->
|
||||
ResIQ = Module:Function(From, To, IQ),
|
||||
if
|
||||
@@ -146,15 +147,6 @@ process_iq(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
register_iq_handler(XMLNS, Module, Fun) ->
|
||||
ejabberd_local ! {register_iq_handler, XMLNS, Module, Fun}.
|
||||
|
||||
@@ -167,7 +159,17 @@ unregister_iq_handler(XMLNS) ->
|
||||
refresh_iq_handlers() ->
|
||||
ejabberd_local ! refresh_iq_handlers.
|
||||
|
||||
bounce_resource_packet(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
stop.
|
||||
announce_online(From, To, Packet) ->
|
||||
case acl:match_rule(announce, From) of
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
Local = jlib:make_jid("", ?MYNAME, ""),
|
||||
lists:foreach(
|
||||
fun({U, R}) ->
|
||||
Dest = jlib:make_jid(U, ?MYNAME, R),
|
||||
ejabberd_router:route(Local, Dest, Packet)
|
||||
end, ejabberd_sm:dirty_get_sessions_list())
|
||||
end.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
%% gen_event callbacks
|
||||
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
|
||||
code_change/3, reopen_log/0]).
|
||||
code_change/3]).
|
||||
|
||||
-record(state, {fd, file}).
|
||||
|
||||
@@ -89,9 +89,6 @@ terminate(_Reason, _State) ->
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
reopen_log() ->
|
||||
error_logger ! {emulator, noproc, reopen}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
-export([start/3,
|
||||
receiver/4,
|
||||
change_shaper/2,
|
||||
reset_stream/1,
|
||||
starttls/2]).
|
||||
reset_stream/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
@@ -24,7 +23,7 @@ start(Socket, SockMod, Shaper) ->
|
||||
|
||||
|
||||
receiver(Socket, SockMod, Shaper, C2SPid) ->
|
||||
XMLStreamState = xml_stream:new(C2SPid),
|
||||
XMLStreamPid = xml_stream:start(C2SPid),
|
||||
ShaperState = shaper:new(Shaper),
|
||||
Timeout = case SockMod of
|
||||
ssl ->
|
||||
@@ -32,33 +31,10 @@ receiver(Socket, SockMod, Shaper, C2SPid) ->
|
||||
_ ->
|
||||
infinity
|
||||
end,
|
||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout).
|
||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout).
|
||||
|
||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout) ->
|
||||
Res = (catch SockMod:recv(Socket, 0, Timeout)),
|
||||
case Res of
|
||||
{ok, Data} ->
|
||||
receive
|
||||
{starttls, TLSSocket} ->
|
||||
xml_stream:close(XMLStreamState),
|
||||
XMLStreamState1 = xml_stream:new(C2SPid),
|
||||
TLSRes = tls:recv_data(TLSSocket, Data),
|
||||
receiver1(TLSSocket, tls,
|
||||
ShaperState, C2SPid, XMLStreamState1, Timeout,
|
||||
TLSRes)
|
||||
after 0 ->
|
||||
receiver1(Socket, SockMod,
|
||||
ShaperState, C2SPid, XMLStreamState, Timeout,
|
||||
Res)
|
||||
end;
|
||||
_ ->
|
||||
receiver1(Socket, SockMod,
|
||||
ShaperState, C2SPid, XMLStreamState, Timeout, Res)
|
||||
end.
|
||||
|
||||
|
||||
receiver1(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout, Res) ->
|
||||
case Res of
|
||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout) ->
|
||||
case catch SockMod:recv(Socket, 0, Timeout) of
|
||||
{ok, Text} ->
|
||||
ShaperSt1 = receive
|
||||
{change_shaper, Shaper} ->
|
||||
@@ -67,27 +43,27 @@ receiver1(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout, Res) ->
|
||||
ShaperState
|
||||
end,
|
||||
NewShaperState = shaper:update(ShaperSt1, size(Text)),
|
||||
XMLStreamState1 = receive
|
||||
reset_stream ->
|
||||
xml_stream:close(XMLStreamState),
|
||||
xml_stream:new(C2SPid)
|
||||
after 0 ->
|
||||
XMLStreamState
|
||||
end,
|
||||
XMLStreamState2 = xml_stream:parse(XMLStreamState1, Text),
|
||||
receiver(Socket, SockMod, NewShaperState, C2SPid, XMLStreamState2,
|
||||
XMLStreamPid1 = receive
|
||||
reset_stream ->
|
||||
exit(XMLStreamPid, closed),
|
||||
xml_stream:start(C2SPid)
|
||||
after 0 ->
|
||||
XMLStreamPid
|
||||
end,
|
||||
xml_stream:send_text(XMLStreamPid1, Text),
|
||||
receiver(Socket, SockMod, NewShaperState, C2SPid, XMLStreamPid1,
|
||||
Timeout);
|
||||
{error, timeout} ->
|
||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamState,
|
||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid,
|
||||
Timeout);
|
||||
{error, Reason} ->
|
||||
xml_stream:close(XMLStreamState),
|
||||
exit(XMLStreamPid, closed),
|
||||
gen_fsm:send_event(C2SPid, closed),
|
||||
ok;
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("(~w) abnormal ~w:recv termination:~n\t~p~n",
|
||||
[Socket, SockMod, Reason]),
|
||||
xml_stream:close(XMLStreamState),
|
||||
exit(XMLStreamPid, closed),
|
||||
gen_fsm:send_event(C2SPid, closed),
|
||||
ok
|
||||
end.
|
||||
@@ -99,7 +75,4 @@ change_shaper(Pid, Shaper) ->
|
||||
reset_stream(Pid) ->
|
||||
Pid ! reset_stream.
|
||||
|
||||
starttls(Pid, TLSSocket) ->
|
||||
Pid ! {starttls, TLSSocket}.
|
||||
|
||||
|
||||
|
||||
@@ -12,10 +12,7 @@
|
||||
|
||||
-export([route/3,
|
||||
register_route/1,
|
||||
register_route/2,
|
||||
register_routes/1,
|
||||
unregister_route/1,
|
||||
unregister_routes/1,
|
||||
dirty_get_all_routes/0,
|
||||
dirty_get_all_domains/0
|
||||
]).
|
||||
@@ -25,10 +22,15 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(route, {domain, pid, local_hint}).
|
||||
-record(route, {domain, pid}).
|
||||
|
||||
|
||||
start_link() ->
|
||||
Pid = proc_lib:spawn_link(ejabberd_router, init, []),
|
||||
register(ejabberd_router, Pid),
|
||||
{ok, Pid}.
|
||||
|
||||
init() ->
|
||||
update_tables(),
|
||||
mnesia:create_table(route,
|
||||
[{ram_copies, [node()]},
|
||||
@@ -36,17 +38,7 @@ start_link() ->
|
||||
{attributes,
|
||||
record_info(fields, route)}]),
|
||||
mnesia:add_table_copy(route, node(), ram_copies),
|
||||
Pid = proc_lib:spawn_link(ejabberd_router, init, []),
|
||||
register(ejabberd_router, Pid),
|
||||
{ok, Pid}.
|
||||
|
||||
init() ->
|
||||
mnesia:subscribe({table, route, simple}),
|
||||
lists:foreach(
|
||||
fun(Pid) ->
|
||||
erlang:monitor(process, Pid)
|
||||
end,
|
||||
mnesia:dirty_select(route, [{{route, '_', '$1', '_'}, [], ['$1']}])),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
@@ -60,6 +52,20 @@ loop() ->
|
||||
ok
|
||||
end,
|
||||
loop();
|
||||
{register_route, Domain, Pid} ->
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = Domain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
loop();
|
||||
{unregister_route, Domain, Pid} ->
|
||||
F = fun() ->
|
||||
mnesia:delete_object(#route{domain = Domain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
loop();
|
||||
{mnesia_table_event, {write, #route{pid = Pid}, _ActivityId}} ->
|
||||
erlang:monitor(process, Pid),
|
||||
loop();
|
||||
@@ -85,105 +91,35 @@ do_route(From, To, Packet) ->
|
||||
LDstDomain = To#jid.lserver,
|
||||
case mnesia:dirty_read(route, LDstDomain) of
|
||||
[] ->
|
||||
ejabberd_s2s:route(From, To, Packet);
|
||||
ejabberd_s2s ! {route, From, To, Packet};
|
||||
[R] ->
|
||||
Pid = R#route.pid,
|
||||
if
|
||||
node(Pid) == node() ->
|
||||
case R#route.local_hint of
|
||||
{apply, Module, Function} ->
|
||||
Module:Function(From, To, Packet);
|
||||
_ ->
|
||||
Pid ! {route, From, To, Packet}
|
||||
end;
|
||||
true ->
|
||||
Pid ! {route, From, To, Packet}
|
||||
end;
|
||||
?DEBUG("routed to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet};
|
||||
Rs ->
|
||||
case [R || R <- Rs, node(R#route.pid) == node()] of
|
||||
[] ->
|
||||
R = lists:nth(erlang:phash(now(), length(Rs)), Rs),
|
||||
Pid = R#route.pid,
|
||||
Pid ! {route, From, To, Packet};
|
||||
LRs ->
|
||||
LRs,
|
||||
R = lists:nth(erlang:phash(now(), length(LRs)), LRs),
|
||||
Pid = R#route.pid,
|
||||
case R#route.local_hint of
|
||||
{apply, Module, Function} ->
|
||||
Module:Function(From, To, Packet);
|
||||
_ ->
|
||||
Pid ! {route, From, To, Packet}
|
||||
end
|
||||
end
|
||||
Rs1 = case [R || R <- Rs, node(R#route.pid) == node()] of
|
||||
[] -> Rs;
|
||||
LRs -> LRs
|
||||
end,
|
||||
R = lists:nth(erlang:phash(now(), length(Rs1)), Rs1),
|
||||
Pid = R#route.pid,
|
||||
?DEBUG("routed to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet}
|
||||
end.
|
||||
|
||||
|
||||
%route(From, To, Packet) ->
|
||||
% ejabberd_router ! {route, From, To, Packet}.
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
ejabberd_router ! {route, From, To, Packet}.
|
||||
|
||||
register_route(Domain) ->
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = LDomain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
register_route(Domain, LocalHint) ->
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:write(#route{domain = LDomain,
|
||||
pid = Pid,
|
||||
local_hint = LocalHint})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
register_routes(Domains) ->
|
||||
lists:foreach(fun(Domain) ->
|
||||
register_route(Domain)
|
||||
end, Domains).
|
||||
ejabberd_router ! {register_route, Domain, self()}.
|
||||
|
||||
unregister_route(Domain) ->
|
||||
case jlib:nameprep(Domain) of
|
||||
error ->
|
||||
[] = {invalid_domain, Domain};
|
||||
LDomain ->
|
||||
Pid = self(),
|
||||
F = fun() ->
|
||||
mnesia:delete_object(#route{domain = LDomain,
|
||||
pid = Pid})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
unregister_routes(Domains) ->
|
||||
lists:foreach(fun(Domain) ->
|
||||
unregister_route(Domain)
|
||||
end, Domains).
|
||||
ejabberd_router ! {unregister_route, Domain, self()}.
|
||||
|
||||
|
||||
dirty_get_all_routes() ->
|
||||
lists:usort(mnesia:dirty_all_keys(route)) -- ?MYHOSTS.
|
||||
lists:delete(?MYNAME, lists:usort(mnesia:dirty_all_keys(route))).
|
||||
|
||||
dirty_get_all_domains() ->
|
||||
lists:usort(mnesia:dirty_all_keys(route)).
|
||||
@@ -195,8 +131,6 @@ update_tables() ->
|
||||
[domain, node, pid] ->
|
||||
mnesia:delete_table(route);
|
||||
[domain, pid] ->
|
||||
mnesia:delete_table(route);
|
||||
[domain, pid, local_hint] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
|
||||
@@ -11,11 +11,9 @@
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start_link/0, init/0,
|
||||
route/3,
|
||||
have_connection/1,
|
||||
get_key/1,
|
||||
try_register/1,
|
||||
remove_connection/1,
|
||||
dirty_get_connections/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -31,14 +29,17 @@ start_link() ->
|
||||
|
||||
init() ->
|
||||
update_tables(),
|
||||
mnesia:create_table(s2s, [{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, s2s)}]),
|
||||
mnesia:create_table(s2s,[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, s2s)}]),
|
||||
mnesia:add_table_copy(s2s, node(), ram_copies),
|
||||
mnesia:subscribe(system),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
receive
|
||||
{closed_conection, FromTo} ->
|
||||
remove_connection(FromTo),
|
||||
loop();
|
||||
{mnesia_system_event, {mnesia_down, Node}} ->
|
||||
clean_table_from_bad_node(Node),
|
||||
loop();
|
||||
@@ -56,15 +57,6 @@ loop() ->
|
||||
end.
|
||||
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
remove_connection(FromTo) ->
|
||||
F = fun() ->
|
||||
|
||||
@@ -311,6 +311,14 @@ handle_info(_, StateName, StateData) ->
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, _StateName, StateData) ->
|
||||
% case StateData#state.user of
|
||||
% "" ->
|
||||
% ok;
|
||||
% _ ->
|
||||
% %ejabberd_sm:close_session(StateData#state.user,
|
||||
% % StateData#state.resource)
|
||||
% end,
|
||||
%ejabberd_s2s ! {closed_conection, StateData#state.server},
|
||||
?INFO_MSG("terminated: ~p", [Reason]),
|
||||
gen_tcp:close(StateData#state.socket),
|
||||
ok.
|
||||
|
||||
@@ -150,8 +150,9 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
Server = StateData#state.server,
|
||||
New = case StateData#state.new of
|
||||
false ->
|
||||
case ejabberd_s2s:try_register(
|
||||
{StateData#state.myname, Server}) of
|
||||
case
|
||||
ejabberd_s2s:try_register(
|
||||
{StateData#state.myname, Server}) of
|
||||
{key, Key} ->
|
||||
Key;
|
||||
false ->
|
||||
@@ -233,12 +234,7 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
|
||||
StateData#state.server,
|
||||
StateData#state.myname})
|
||||
end,
|
||||
if
|
||||
StateData#state.verify == false ->
|
||||
{stop, normal, StateData};
|
||||
true ->
|
||||
{next_state, wait_for_validation, StateData}
|
||||
end
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
_ ->
|
||||
{next_state, wait_for_validation, StateData}
|
||||
@@ -397,8 +393,8 @@ terminate(Reason, StateName, StateData) ->
|
||||
false ->
|
||||
ok;
|
||||
Key ->
|
||||
ejabberd_s2s:remove_connection({StateData#state.myname,
|
||||
StateData#state.server})
|
||||
ejabberd_s2s ! {closed_conection, {StateData#state.myname,
|
||||
StateData#state.server}}
|
||||
end,
|
||||
case StateData#state.socket of
|
||||
undefined ->
|
||||
|
||||
@@ -10,17 +10,12 @@
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start_link/0, init/0,
|
||||
route/3,
|
||||
open_session/3, close_session/3,
|
||||
bounce_offline_message/3,
|
||||
disconnect_removed_user/2,
|
||||
get_user_resources/2,
|
||||
set_presence/4,
|
||||
unset_presence/4,
|
||||
-export([start_link/0, init/0, open_session/2, close_session/2,
|
||||
get_user_resources/1,
|
||||
set_presence/3,
|
||||
unset_presence/3,
|
||||
dirty_get_sessions_list/0,
|
||||
dirty_get_my_sessions_list/0,
|
||||
get_vh_session_list/1,
|
||||
register_iq_handler/3,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/1
|
||||
@@ -29,8 +24,8 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(session, {usr, us, pid}).
|
||||
-record(presence, {usr, us, priority}).
|
||||
-record(session, {ur, user, pid}).
|
||||
-record(presence, {ur, user, priority}).
|
||||
|
||||
start_link() ->
|
||||
Pid = proc_lib:spawn_link(ejabberd_sm, init, []),
|
||||
@@ -41,18 +36,14 @@ init() ->
|
||||
update_tables(),
|
||||
mnesia:create_table(session, [{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, session)}]),
|
||||
mnesia:add_table_index(session, us),
|
||||
mnesia:add_table_index(session, user),
|
||||
mnesia:add_table_copy(session, node(), ram_copies),
|
||||
mnesia:create_table(presence,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, presence)}]),
|
||||
mnesia:add_table_index(presence, us),
|
||||
mnesia:add_table_index(presence, user),
|
||||
mnesia:subscribe(system),
|
||||
ets:new(sm_iqtable, [named_table]),
|
||||
ejabberd_hooks:add(offline_message_hook,
|
||||
ejabberd_sm, bounce_offline_message, 100),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
ejabberd_sm, disconnect_removed_user, 100),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
@@ -66,6 +57,12 @@ loop() ->
|
||||
ok
|
||||
end,
|
||||
loop();
|
||||
{open_session, User, Resource, From} ->
|
||||
register_connection(User, Resource, From),
|
||||
loop();
|
||||
{close_session, User, Resource} ->
|
||||
remove_connection(User, Resource),
|
||||
loop();
|
||||
{mnesia_system_event, {mnesia_down, Node}} ->
|
||||
clean_table_from_bad_node(Node),
|
||||
loop();
|
||||
@@ -89,31 +86,20 @@ loop() ->
|
||||
end.
|
||||
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
open_session(User, Resource) ->
|
||||
ejabberd_sm ! {open_session, User, Resource, self()}.
|
||||
|
||||
open_session(User, Server, Resource) ->
|
||||
register_connection(User, Server, Resource, self()).
|
||||
|
||||
close_session(User, Server, Resource) ->
|
||||
remove_connection(User, Server, Resource).
|
||||
close_session(User, Resource) ->
|
||||
ejabberd_sm ! {close_session, User, Resource}.
|
||||
|
||||
|
||||
register_connection(User, Server, Resource, Pid) ->
|
||||
register_connection(User, Resource, Pid) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
US = {LUser, LServer},
|
||||
USR = {LUser, LServer, LResource},
|
||||
UR = {LUser, LResource},
|
||||
F = fun() ->
|
||||
Ss = mnesia:wread({session, USR}),
|
||||
mnesia:write(#session{usr = USR, us = US, pid = Pid}),
|
||||
Ss = mnesia:wread({session, UR}),
|
||||
mnesia:write(#session{ur = UR, user = LUser, pid = Pid}),
|
||||
Ss
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
@@ -127,13 +113,12 @@ register_connection(User, Server, Resource, Pid) ->
|
||||
end.
|
||||
|
||||
|
||||
remove_connection(User, Server, Resource) ->
|
||||
remove_connection(User, Resource) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
LServer = jlib:nameprep(Server),
|
||||
USR = {LUser, LServer, LResource},
|
||||
UR = {LUser, LResource},
|
||||
F = fun() ->
|
||||
mnesia:delete({session, USR})
|
||||
mnesia:delete({session, UR})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
@@ -146,8 +131,7 @@ clean_table_from_bad_node(Node) ->
|
||||
[{'==', {node, '$1'}, Node}],
|
||||
['$_']}]),
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete_object(E),
|
||||
mnesia:delete({presence, E#session.usr})
|
||||
mnesia:delete_object(E)
|
||||
end, Es)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
@@ -158,7 +142,7 @@ clean_table_from_bad_node(Node) ->
|
||||
do_route(From, To, Packet) ->
|
||||
?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
|
||||
[From, To, Packet, 8]),
|
||||
#jid{user = User, server = Server,
|
||||
#jid{user = User,
|
||||
luser = LUser, lserver = LServer, lresource = LResource} = To,
|
||||
{xmlelement, Name, Attrs, _Els} = Packet,
|
||||
case LResource of
|
||||
@@ -168,46 +152,42 @@ do_route(From, To, Packet) ->
|
||||
{Pass, Subsc} =
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"subscribe" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
false,
|
||||
[User, Server, From, subscribe]),
|
||||
{mod_roster:in_subscription(User,
|
||||
From,
|
||||
subscribe),
|
||||
true};
|
||||
"subscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
false,
|
||||
[User, Server, From, subscribed]),
|
||||
{mod_roster:in_subscription(User,
|
||||
From,
|
||||
subscribed),
|
||||
true};
|
||||
"unsubscribe" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
false,
|
||||
[User, Server, From, unsubscribe]),
|
||||
{mod_roster:in_subscription(User,
|
||||
From,
|
||||
unsubscribe),
|
||||
true};
|
||||
"unsubscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
false,
|
||||
[User, Server, From, unsubscribed]),
|
||||
{mod_roster:in_subscription(User,
|
||||
From,
|
||||
unsubscribed),
|
||||
true};
|
||||
_ ->
|
||||
{true, false}
|
||||
end,
|
||||
if Pass ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
PResources = get_user_present_resources(
|
||||
LUser, LServer),
|
||||
PResources = get_user_present_resources(User),
|
||||
if
|
||||
PResources /= [] ->
|
||||
lists:foreach(
|
||||
fun({_, R}) ->
|
||||
if LFrom /=
|
||||
{LUser, LServer, R} ->
|
||||
do_route(
|
||||
From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet);
|
||||
ejabberd_sm !
|
||||
{route,
|
||||
From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet};
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
@@ -215,12 +195,10 @@ do_route(From, To, Packet) ->
|
||||
true ->
|
||||
if
|
||||
Subsc ->
|
||||
case ejabberd_auth:is_user_exists(
|
||||
LUser, LServer) of
|
||||
case ejabberd_auth:is_user_exists(LUser) of
|
||||
true ->
|
||||
ejabberd_hooks:run(
|
||||
offline_subscription_hook,
|
||||
[From, To, Packet]);
|
||||
catch mod_offline:store_packet(
|
||||
From, To, Packet);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
@@ -240,16 +218,17 @@ do_route(From, To, Packet) ->
|
||||
"broadcast" ->
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
do_route(From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet)
|
||||
end, get_user_resources(User, Server));
|
||||
ejabberd_sm ! {route,
|
||||
From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet}
|
||||
end, get_user_resources(User));
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
USR = {LUser, LServer, LResource},
|
||||
case mnesia:dirty_read({session, USR}) of
|
||||
LUR = {LUser, LResource},
|
||||
case mnesia:dirty_read({session, LUR}) of
|
||||
[] ->
|
||||
case Name of
|
||||
"message" ->
|
||||
@@ -276,94 +255,84 @@ do_route(From, To, Packet) ->
|
||||
|
||||
route_message(From, To, Packet) ->
|
||||
LUser = To#jid.luser,
|
||||
LServer = To#jid.lserver,
|
||||
case catch lists:max(get_user_present_resources(LUser, LServer)) of
|
||||
{Priority, R} when is_integer(Priority),
|
||||
Priority >= 0 ->
|
||||
case catch lists:max(get_user_present_resources(LUser)) of
|
||||
{'EXIT', _} ->
|
||||
case xml:get_tag_attr_s("type", Packet) of
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
case ejabberd_auth:is_user_exists(LUser) of
|
||||
true ->
|
||||
case catch mod_offline:store_packet(
|
||||
From, To, Packet) of
|
||||
{'EXIT', _} ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end;
|
||||
{_, R} ->
|
||||
LResource = jlib:resourceprep(R),
|
||||
USR = {LUser, LServer, LResource},
|
||||
case mnesia:dirty_read({session, USR}) of
|
||||
LUR = {LUser, LResource},
|
||||
case mnesia:dirty_read({session, LUR}) of
|
||||
[] ->
|
||||
ok; % Race condition
|
||||
[Sess] ->
|
||||
Pid = Sess#session.pid,
|
||||
?DEBUG("sending to process ~p~n", [Pid]),
|
||||
Pid ! {route, From, To, Packet}
|
||||
end;
|
||||
_ ->
|
||||
case xml:get_tag_attr_s("type", Packet) of
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
case ejabberd_auth:is_user_exists(LUser, LServer) of
|
||||
true ->
|
||||
ejabberd_hooks:run(offline_message_hook,
|
||||
[From, To, Packet]);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
bounce_offline_message(From, To, Packet) ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
stop.
|
||||
|
||||
disconnect_removed_user(User, Server) ->
|
||||
ejabberd_sm:route(jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, Server, ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{exit, "User removed"}]}).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
get_user_resources(User, Server) ->
|
||||
get_user_resources(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_index_read(session, US, #session.us) of
|
||||
case catch mnesia:dirty_index_read(session, LUser, #session.user) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
Rs ->
|
||||
lists:map(fun(R) ->
|
||||
element(3, R#session.usr)
|
||||
element(2, R#session.ur)
|
||||
end, Rs)
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_presence(User, Server, Resource, Priority) ->
|
||||
set_presence(User, Resource, Priority) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
USR = {User, Server, Resource},
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:write(#presence{usr = USR, us = US,
|
||||
UR = {User, Resource},
|
||||
mnesia:write(#presence{ur = UR, user = LUser,
|
||||
priority = Priority})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
unset_presence(User, Server, Resource, Status) ->
|
||||
USR = {User, Server, Resource},
|
||||
unset_presence(User, Resource, Status) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
mnesia:delete({presence, USR})
|
||||
UR = {User, Resource},
|
||||
mnesia:delete({presence, UR})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(unset_presence_hook, [User, Server, Resource, Status]).
|
||||
catch mod_last:on_presence_update(LUser, Status).
|
||||
|
||||
get_user_present_resources(LUser, LServer) ->
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_index_read(presence, US, #presence.us) of
|
||||
get_user_present_resources(LUser) ->
|
||||
case catch mnesia:dirty_index_read(presence, LUser, #presence.user) of
|
||||
{'EXIT', _Reason} ->
|
||||
[];
|
||||
Rs ->
|
||||
lists:map(fun(R) ->
|
||||
{R#presence.priority, element(3, R#presence.usr)}
|
||||
{R#presence.priority, element(2, R#presence.ur)}
|
||||
end, Rs)
|
||||
end.
|
||||
|
||||
@@ -377,14 +346,6 @@ dirty_get_my_sessions_list() ->
|
||||
[{'==', {node, '$1'}, node()}],
|
||||
['$_']}]).
|
||||
|
||||
get_vh_session_list(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{usr = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}],
|
||||
['$1']}]).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
@@ -397,8 +358,10 @@ process_iq(From, To, Packet) ->
|
||||
ResIQ = Module:Function(From, To, IQ),
|
||||
if
|
||||
ResIQ /= ignore ->
|
||||
ejabberd_router:route(To, From,
|
||||
jlib:iq_to_xml(ResIQ));
|
||||
ejabberd_router ! {route,
|
||||
To,
|
||||
From,
|
||||
jlib:iq_to_xml(ResIQ)};
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
@@ -408,7 +371,7 @@ process_iq(From, To, Packet) ->
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router ! {route, To, From, Err}
|
||||
end;
|
||||
reply ->
|
||||
ok;
|
||||
@@ -434,16 +397,6 @@ update_tables() ->
|
||||
[ur, user, node] ->
|
||||
mnesia:delete_table(session);
|
||||
[ur, user, pid] ->
|
||||
mnesia:delete_table(session);
|
||||
[usr, us, pid] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
end,
|
||||
case catch mnesia:table_info(presence, attributes) of
|
||||
[ur, user, priority] ->
|
||||
mnesia:delete_table(presence);
|
||||
[usr, us, priority] ->
|
||||
ok;
|
||||
{'EXIT', _} ->
|
||||
ok
|
||||
|
||||
@@ -19,13 +19,36 @@ start_link() ->
|
||||
|
||||
|
||||
init([]) ->
|
||||
Hooks =
|
||||
{ejabberd_hooks,
|
||||
{ejabberd_hooks, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_hooks]},
|
||||
Router = {ejabberd_router,
|
||||
{ejabberd_router, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_router]},
|
||||
SM = {ejabberd_sm,
|
||||
{ejabberd_sm, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_sm]},
|
||||
S2S = {ejabberd_s2s,
|
||||
{ejabberd_s2s, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_s2s]},
|
||||
Local = {ejabberd_local,
|
||||
{ejabberd_local, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_local]},
|
||||
Listener = {ejabberd_listener,
|
||||
{ejabberd_listener, start_link, []},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_listener]},
|
||||
StringPrep =
|
||||
{stringprep,
|
||||
{stringprep, start_link, []},
|
||||
@@ -33,41 +56,6 @@ init([]) ->
|
||||
brutal_kill,
|
||||
worker,
|
||||
[stringprep]},
|
||||
Router =
|
||||
{ejabberd_router,
|
||||
{ejabberd_router, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_router]},
|
||||
SM =
|
||||
{ejabberd_sm,
|
||||
{ejabberd_sm, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_sm]},
|
||||
S2S =
|
||||
{ejabberd_s2s,
|
||||
{ejabberd_s2s, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_s2s]},
|
||||
Local =
|
||||
{ejabberd_local,
|
||||
{ejabberd_local, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_local]},
|
||||
Listener =
|
||||
{ejabberd_listener,
|
||||
{ejabberd_listener, start_link, []},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_listener]},
|
||||
C2SSupervisor =
|
||||
{ejabberd_c2s_sup,
|
||||
{ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]},
|
||||
@@ -124,12 +112,8 @@ init([]) ->
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[Hooks,
|
||||
[Router, SM, S2S, Local,
|
||||
StringPrep,
|
||||
Router,
|
||||
SM,
|
||||
S2S,
|
||||
Local,
|
||||
C2SSupervisor,
|
||||
S2SInSupervisor,
|
||||
S2SOutSupervisor,
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -17,10 +22,10 @@ OBJS = \
|
||||
all: $(OBJS)
|
||||
|
||||
ELDAPv3.erl: ELDAPv3.asn
|
||||
@ERLC@ -bber_bin -W $(EFLAGS) $<
|
||||
erlc -bber_bin -W $(EFLAGS) $<
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
clean:
|
||||
|
||||
@@ -765,7 +765,7 @@ retry_connect() ->
|
||||
cmd_timeout(Timer, Id, S) ->
|
||||
Dict = S#eldap.dict,
|
||||
case dict:find(Id, Dict) of
|
||||
{ok, [{Timer, From, Name}|Res]} ->
|
||||
{ok, [{Id, Timer, From, Name}|Res]} ->
|
||||
case Name of
|
||||
searchRequest ->
|
||||
{Res1, Ref1} = polish(Res),
|
||||
|
||||
@@ -106,10 +106,6 @@ int ei_x_encode_string_fixed(ei_x_buff* x, const char* s)
|
||||
#define XML_CDATA 2
|
||||
#define XML_ERROR 3
|
||||
|
||||
#define PARSE_COMMAND 0
|
||||
#define PARSE_FINAL_COMMAND 1
|
||||
|
||||
ei_x_buff event_buf;
|
||||
|
||||
typedef struct {
|
||||
ErlDrvPort port;
|
||||
@@ -121,39 +117,44 @@ void *erlXML_StartElementHandler(expat_data *d,
|
||||
const XML_Char **atts)
|
||||
{
|
||||
int i;
|
||||
|
||||
ei_x_encode_list_header(&event_buf, 1);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_long(&event_buf, XML_START);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_string_fixed(&event_buf, name);
|
||||
|
||||
ei_x_buff buf;
|
||||
|
||||
ei_x_new_with_version(&buf);
|
||||
ei_x_encode_tuple_header(&buf, 2);
|
||||
ei_x_encode_long(&buf, XML_START);
|
||||
ei_x_encode_tuple_header(&buf, 2);
|
||||
ei_x_encode_string_fixed(&buf, name);
|
||||
|
||||
for (i = 0; atts[i]; i += 2) {}
|
||||
|
||||
if (i > 0)
|
||||
ei_x_encode_list_header(&buf, i/2);
|
||||
|
||||
for (i = 0; atts[i]; i += 2)
|
||||
{
|
||||
ei_x_encode_list_header(&event_buf, i/2);
|
||||
|
||||
for (i = 0; atts[i]; i += 2)
|
||||
{
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_string_fixed(&event_buf, atts[i]);
|
||||
ei_x_encode_string_fixed(&event_buf, atts[i+1]);
|
||||
}
|
||||
ei_x_encode_tuple_header(&buf, 2);
|
||||
ei_x_encode_string_fixed(&buf, atts[i]);
|
||||
ei_x_encode_string_fixed(&buf, atts[i+1]);
|
||||
}
|
||||
|
||||
ei_x_encode_empty_list(&event_buf);
|
||||
|
||||
|
||||
ei_x_encode_empty_list(&buf);
|
||||
|
||||
driver_output(d->port, buf.buff, buf.index);
|
||||
ei_x_free(&buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *erlXML_EndElementHandler(expat_data *d,
|
||||
const XML_Char *name)
|
||||
{
|
||||
ei_x_encode_list_header(&event_buf, 1);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_long(&event_buf, XML_END);
|
||||
ei_x_encode_string_fixed(&event_buf, name);
|
||||
ei_x_buff buf;
|
||||
|
||||
ei_x_new_with_version(&buf);
|
||||
ei_x_encode_tuple_header(&buf, 2);
|
||||
ei_x_encode_long(&buf, XML_END);
|
||||
ei_x_encode_string_fixed(&buf, name);
|
||||
|
||||
driver_output(d->port, buf.buff, buf.index);
|
||||
ei_x_free(&buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -161,10 +162,15 @@ void *erlXML_CharacterDataHandler(expat_data *d,
|
||||
const XML_Char *s,
|
||||
int len)
|
||||
{
|
||||
ei_x_encode_list_header(&event_buf, 1);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_long(&event_buf, XML_CDATA);
|
||||
ei_x_encode_string_len_fixed(&event_buf, s, len);
|
||||
ei_x_buff buf;
|
||||
|
||||
ei_x_new_with_version(&buf);
|
||||
ei_x_encode_tuple_header(&buf, 2);
|
||||
ei_x_encode_long(&buf, XML_CDATA);
|
||||
ei_x_encode_string_len_fixed(&buf, s, len);
|
||||
|
||||
driver_output(d->port, buf.buff, buf.index);
|
||||
ei_x_free(&buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -176,8 +182,6 @@ static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
|
||||
d->parser = XML_ParserCreate("UTF-8");
|
||||
XML_SetUserData(d->parser, d);
|
||||
|
||||
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
||||
|
||||
XML_SetStartElementHandler(
|
||||
d->parser, (XML_StartElementHandler)erlXML_StartElementHandler);
|
||||
XML_SetEndElementHandler(
|
||||
@@ -195,66 +199,48 @@ static void expat_erl_stop(ErlDrvData handle)
|
||||
driver_free((char*)handle);
|
||||
}
|
||||
|
||||
static int expat_erl_control(ErlDrvData drv_data,
|
||||
unsigned int command,
|
||||
char *buf, int len,
|
||||
char **rbuf, int rlen)
|
||||
static void expat_erl_output(ErlDrvData handle, char *buff, int bufflen)
|
||||
{
|
||||
expat_data* d = (expat_data*)drv_data;
|
||||
expat_data* d = (expat_data*)handle;
|
||||
int res, errcode;
|
||||
char *errstring;
|
||||
ErlDrvBinary *b;
|
||||
size_t size;
|
||||
ei_x_buff buf;
|
||||
|
||||
switch (command)
|
||||
res = XML_Parse(d->parser, buff, bufflen, 0);
|
||||
|
||||
if(!res)
|
||||
{
|
||||
case PARSE_COMMAND:
|
||||
case PARSE_FINAL_COMMAND:
|
||||
ei_x_new_with_version(&event_buf);
|
||||
res = XML_Parse(d->parser, buf, len, command == PARSE_FINAL_COMMAND);
|
||||
errcode = XML_GetErrorCode(d->parser);
|
||||
errstring = (char *)XML_ErrorString(errcode);
|
||||
|
||||
if(!res)
|
||||
{
|
||||
errcode = XML_GetErrorCode(d->parser);
|
||||
errstring = (char *)XML_ErrorString(errcode);
|
||||
ei_x_new_with_version(&buf);
|
||||
ei_x_encode_tuple_header(&buf, 2);
|
||||
ei_x_encode_long(&buf, XML_ERROR);
|
||||
ei_x_encode_tuple_header(&buf, 2);
|
||||
ei_x_encode_long(&buf, errcode);
|
||||
ei_x_encode_string_fixed(&buf, errstring);
|
||||
|
||||
ei_x_encode_list_header(&event_buf, 1);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_long(&event_buf, XML_ERROR);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_long(&event_buf, errcode);
|
||||
ei_x_encode_string_fixed(&event_buf, errstring);
|
||||
}
|
||||
|
||||
ei_x_encode_empty_list(&event_buf);
|
||||
|
||||
size = event_buf.index;
|
||||
|
||||
b = driver_alloc_binary(size);
|
||||
memcpy(b->orig_bytes, event_buf.buff, size);
|
||||
|
||||
ei_x_free(&event_buf);
|
||||
|
||||
*rbuf = (char *)b;
|
||||
return size;
|
||||
default:
|
||||
return 0;
|
||||
driver_output(d->port, buf.buff, buf.index);
|
||||
ei_x_free(&buf);
|
||||
}
|
||||
|
||||
//driver_output(d->port, &res, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ErlDrvEntry expat_driver_entry = {
|
||||
NULL, /* F_PTR init, N/A */
|
||||
expat_erl_start, /* L_PTR start, called when port is opened */
|
||||
expat_erl_stop, /* 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 */
|
||||
"expat_erl", /* char *driver_name, the argument to open_port */
|
||||
NULL, /* F_PTR finish, called when unloaded */
|
||||
NULL, /* handle */
|
||||
expat_erl_control, /* F_PTR control, port_command callback */
|
||||
NULL, /* F_PTR timeout, reserved */
|
||||
NULL /* F_PTR outputv, reserved */
|
||||
NULL, /* F_PTR init, N/A */
|
||||
expat_erl_start, /* L_PTR start, called when port is opened */
|
||||
expat_erl_stop, /* F_PTR stop, called when port is closed */
|
||||
expat_erl_output, /* 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 */
|
||||
"expat_erl", /* char *driver_name, the argument to open_port */
|
||||
NULL, /* F_PTR finish, called when unloaded */
|
||||
NULL, /* F_PTR control, port_command callback */
|
||||
NULL, /* F_PTR timeout, reserved */
|
||||
NULL /* F_PTR outputv, reserved */
|
||||
};
|
||||
|
||||
DRIVER_INIT(expat_erl) /* must match name in driver_entry */
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : extauth.erl
|
||||
%%% Author : Leif Johansson <leifj@it.su.se>
|
||||
%%% Purpose : External authentication using a simple port-driver
|
||||
%%% Created : 30 Jul 2004 by Leif Johansson <leifj@it.su.se>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(extauth).
|
||||
-author('leifj@it.su.se').
|
||||
|
||||
-export([start/1, stop/0, init/1,
|
||||
check_password/2, set_password/2, is_user_exists/1 ]).
|
||||
|
||||
|
||||
start(ExtPrg) ->
|
||||
spawn(?MODULE, init, [ExtPrg]).
|
||||
|
||||
init(ExtPrg) ->
|
||||
register(eauth,self()),
|
||||
process_flag(trap_exit,true),
|
||||
Port = open_port({spawn, ExtPrg}, [{packet,2}]),
|
||||
loop(Port).
|
||||
|
||||
stop() ->
|
||||
eauth ! stop.
|
||||
|
||||
check_password(User,Password) ->
|
||||
call_port(["auth",User,Password]).
|
||||
|
||||
is_user_exists(User) ->
|
||||
call_port(["isuser",User]).
|
||||
|
||||
set_password(User,Password) ->
|
||||
call_port(["setpass",User,Password]).
|
||||
|
||||
call_port(Msg) ->
|
||||
eauth ! {call, self(), Msg},
|
||||
receive
|
||||
{eauth,Result}->
|
||||
Result
|
||||
end.
|
||||
|
||||
loop(Port) ->
|
||||
receive
|
||||
{call, Caller, Msg} ->
|
||||
Port ! {self(), {command, encode(Msg)}},
|
||||
receive
|
||||
{Port, {data, Data}} ->
|
||||
Caller ! {eauth, decode(Data)}
|
||||
end,
|
||||
loop(Port);
|
||||
stop ->
|
||||
Port ! {self(), close},
|
||||
receive
|
||||
{Port, closed} ->
|
||||
exit(normal)
|
||||
end;
|
||||
{'EXIT', Port, Reason} ->
|
||||
io:format("~p ~n", [Reason]),
|
||||
exit(port_terminated)
|
||||
end.
|
||||
|
||||
join(List, Sep) ->
|
||||
lists:foldl(fun(A, "") -> A;
|
||||
(A, Acc) -> Acc ++ Sep ++ A
|
||||
end, "", List).
|
||||
|
||||
encode(L) ->
|
||||
join(L,":").
|
||||
|
||||
decode([0,0]) ->
|
||||
false;
|
||||
decode([0,1]) ->
|
||||
true.
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
get_opt/2,
|
||||
get_opt/3,
|
||||
get_module_opt/3,
|
||||
loaded_modules/0,
|
||||
loaded_modules_with_opts/0,
|
||||
get_hosts/2]).
|
||||
loaded_modules/0]).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
@@ -29,7 +27,7 @@
|
||||
behaviour_info(callbacks) ->
|
||||
[{start, 1},
|
||||
{stop, 0}];
|
||||
behaviour_info(_Other) ->
|
||||
behaviour_info(Other) ->
|
||||
undefined.
|
||||
|
||||
start() ->
|
||||
@@ -53,45 +51,17 @@ stop_module(Module) ->
|
||||
case catch Module:stop() of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
{wait, ProcList} when is_list(ProcList) ->
|
||||
lists:foreach(fun wait_for_process/1, ProcList),
|
||||
ets:delete(ejabberd_modules, Module),
|
||||
ok;
|
||||
{wait, Process} ->
|
||||
wait_for_process(Process),
|
||||
ets:delete(ejabberd_modules, Module),
|
||||
ok;
|
||||
_ ->
|
||||
ets:delete(ejabberd_modules, Module),
|
||||
ok
|
||||
end.
|
||||
|
||||
wait_for_process(Process) ->
|
||||
MonitorReference = erlang:monitor(process, Process),
|
||||
wait_for_stop(Process, MonitorReference).
|
||||
|
||||
wait_for_stop(Process, MonitorReference) ->
|
||||
receive
|
||||
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
|
||||
ok
|
||||
after 5000 ->
|
||||
catch exit(whereis(Process), kill),
|
||||
wait_for_stop1(MonitorReference)
|
||||
end.
|
||||
|
||||
wait_for_stop1(MonitorReference) ->
|
||||
receive
|
||||
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
|
||||
ok
|
||||
after 5000 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
get_opt(Opt, Opts) ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
false ->
|
||||
% TODO: replace with more appropriate function
|
||||
[] = {undefined_option, Opt};
|
||||
% TODO: replace with more appropriate function
|
||||
[] = {undefined_option, Opt};
|
||||
{value, {_, Val}} ->
|
||||
Val
|
||||
end.
|
||||
@@ -115,25 +85,5 @@ get_module_opt(Module, Opt, Default) ->
|
||||
|
||||
loaded_modules() ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module = '$1'},
|
||||
[],
|
||||
['$1']}]).
|
||||
[{#ejabberd_module{_ = '_', module = '$1'}, [],['$1']}]).
|
||||
|
||||
loaded_modules_with_opts() ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module = '$1', opts = '$2'},
|
||||
[],
|
||||
[{{'$1', '$2'}}]}]).
|
||||
|
||||
get_hosts(Opts, Prefix) ->
|
||||
case catch gen_mod:get_opt(hosts, Opts) of
|
||||
{'EXIT', _Error1} ->
|
||||
case catch gen_mod:get_opt(host, Opts) of
|
||||
{'EXIT', _Error2} ->
|
||||
[Prefix ++ Host || Host <- ?MYHOSTS];
|
||||
Host ->
|
||||
[Host]
|
||||
end;
|
||||
Hosts ->
|
||||
Hosts
|
||||
end.
|
||||
|
||||
@@ -10,52 +10,248 @@
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([import_file/1,
|
||||
-export([start/1,
|
||||
start/2,
|
||||
import_file/1,
|
||||
import_dir/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
wait_for_xdb/2,
|
||||
xdb_data/2,
|
||||
handle_event/3,
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(state, {socket, pid, xml_stream_pid,
|
||||
user = "", server = ?MYNAME, resource = ""
|
||||
}).
|
||||
|
||||
%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
-define(FSMOPTS, [{debug, [trace]}]).
|
||||
-else.
|
||||
-define(FSMOPTS, []).
|
||||
-endif.
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(File) ->
|
||||
User = filename:rootname(filename:basename(File)),
|
||||
start(File, User).
|
||||
|
||||
start(File, User) ->
|
||||
gen_fsm:start(?MODULE, [File, User, self()], ?FSMOPTS).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%%----------------------------------------------------------------------
|
||||
init([File, User, Pid]) ->
|
||||
% Profiling
|
||||
%eprof:start(),
|
||||
%eprof:profile([self()]),
|
||||
XMLStreamPid = xml_stream:start(self()),
|
||||
{ok, Text} = file:read_file(File),
|
||||
xml_stream:send_text(XMLStreamPid, Text),
|
||||
{ok, wait_for_xdb, #state{user = User, pid = Pid,
|
||||
xml_stream_pid = XMLStreamPid}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
wait_for_xdb({xmlstreamstart, Name, _Attrs}, StateData) ->
|
||||
case Name of
|
||||
"xdb" ->
|
||||
{next_state, xdb_data, StateData};
|
||||
_ ->
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
|
||||
wait_for_xdb(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
xdb_data({xmlstreamelement, El}, StateData) ->
|
||||
{xmlelement, _Name, Attrs, _Els} = El,
|
||||
Server = StateData#state.server,
|
||||
From = jlib:make_jid(StateData#state.user, Server, ""),
|
||||
NewState =
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_AUTH ->
|
||||
Password = xml:get_tag_cdata(El),
|
||||
ejabberd_auth:set_password(StateData#state.user, Password),
|
||||
StateData;
|
||||
?NS_ROSTER ->
|
||||
%catch mod_roster:process_iq(
|
||||
% From,
|
||||
% {"", ?MYNAME, ""},
|
||||
% #iq{type = set, xmlns = ?NS_ROSTER, sub_el = El}),
|
||||
catch mod_roster:set_items(StateData#state.user, El),
|
||||
StateData;
|
||||
?NS_VCARD ->
|
||||
catch mod_vcard:process_sm_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El}),
|
||||
StateData;
|
||||
"jabber:x:offline" ->
|
||||
process_offline(From, El),
|
||||
StateData;
|
||||
%?NS_REGISTER ->
|
||||
% catch mod_register:process_iq(
|
||||
% {"", "", ""}, {"", ?MYNAME, ""},
|
||||
% #iq{type =set, xmlns = ?NS_REGISTER, xub_el = El}),
|
||||
% User = xml:get_path_s(El, [{elem, "username"}, cdata]),
|
||||
% io:format("user ~s~n", [User]),
|
||||
% StateData;
|
||||
XMLNS ->
|
||||
case xml:get_attr_s("j_private_flag", Attrs) of
|
||||
"1" ->
|
||||
catch mod_private:process_local_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
#iq{type = set, xmlns = ?NS_PRIVATE,
|
||||
sub_el = {xmlelement, "query", [],
|
||||
[jlib:remove_attr(
|
||||
"j_private_flag",
|
||||
jlib:remove_attr("xdbns", El))]}}),
|
||||
StateData;
|
||||
_ ->
|
||||
io:format("jd2ejd: Unknown namespace \"~s\"~n",
|
||||
[XMLNS]),
|
||||
StateData
|
||||
end
|
||||
end,
|
||||
{next_state, xdb_data, NewState};
|
||||
|
||||
xdb_data({xmlstreamend, _Name}, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
xdb_data(closed, StateData) ->
|
||||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% 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(_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(_, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, _StateName, StateData) ->
|
||||
exit(StateData#state.xml_stream_pid, closed),
|
||||
StateData#state.pid ! {jd2ejd, Reason},
|
||||
% Profiling
|
||||
%eprof:log("/tmp/eprof"),
|
||||
%eprof:analyse(),
|
||||
%eprof:stop(),
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
process_offline(To, {xmlelement, _, _, Els}) ->
|
||||
lists:foreach(fun({xmlelement, _, Attrs, _} = El) ->
|
||||
FromS = xml:get_attr_s("from", Attrs),
|
||||
From = case FromS of
|
||||
"" ->
|
||||
{"", ?MYNAME, ""};
|
||||
_ ->
|
||||
jlib:string_to_jid(FromS)
|
||||
end,
|
||||
case From of
|
||||
error ->
|
||||
ok;
|
||||
_ ->
|
||||
catch mod_offline:store_packet(From, To, El)
|
||||
end
|
||||
end, Els).
|
||||
|
||||
|
||||
import_file(File) ->
|
||||
User = filename:rootname(filename:basename(File)),
|
||||
Server = filename:basename(filename:dirname(File)),
|
||||
case (jlib:nodeprep(User) /= error) andalso
|
||||
(jlib:nameprep(Server) /= error) of
|
||||
true ->
|
||||
case file:read_file(File) of
|
||||
{ok, Text} ->
|
||||
case xml_stream:parse_element(Text) of
|
||||
El when element(1, El) == xmlelement ->
|
||||
case catch process_xdb(User, Server, El) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG(
|
||||
"Error while processing file \"~s\": ~p~n",
|
||||
[File, Reason]),
|
||||
{error, Reason};
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Can't parse file \"~s\": ~p~n",
|
||||
[File, Reason]),
|
||||
{error, Reason}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Can't read file \"~s\": ~p~n", [File, Reason]),
|
||||
{error, Reason}
|
||||
end;
|
||||
false ->
|
||||
?ERROR_MSG("Illegal user/server name in file \"~s\"~n", [File]),
|
||||
{error, "illegal user/server"}
|
||||
clear_queue(),
|
||||
start(File),
|
||||
receive
|
||||
{jd2ejd, Result} -> Result
|
||||
%after 4000 -> timeout
|
||||
end.
|
||||
|
||||
clear_queue() ->
|
||||
receive
|
||||
{jd2ejd, _Result} -> clear_queue
|
||||
after 0 -> ok
|
||||
end.
|
||||
|
||||
|
||||
import_dir(Dir) ->
|
||||
{ok, Files} = file:list_dir(Dir),
|
||||
@@ -69,93 +265,11 @@ import_dir(Dir) ->
|
||||
false
|
||||
end
|
||||
end, Files),
|
||||
lists:foldl(
|
||||
fun(FN, A) ->
|
||||
Res = import_file(filename:join([Dir, FN])),
|
||||
case {A, Res} of
|
||||
{ok, ok} -> ok;
|
||||
{ok, _} -> {error, "see ejabberd log for details"};
|
||||
_ -> A
|
||||
end
|
||||
end, ok, MsgFiles).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
process_xdb(User, Server, {xmlelement, Name, _Attrs, Els}) ->
|
||||
case Name of
|
||||
"xdb" ->
|
||||
lists:foreach(
|
||||
fun(El) ->
|
||||
xdb_data(User, Server, El)
|
||||
end, Els);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
lists:foreach(
|
||||
fun(FN) ->
|
||||
import_file(filename:join([Dir, FN]))
|
||||
end, MsgFiles),
|
||||
ok.
|
||||
|
||||
|
||||
xdb_data(User, Server, {xmlcdata, _CData}) ->
|
||||
ok;
|
||||
xdb_data(User, Server, {xmlelement, _Name, Attrs, _Els} = El) ->
|
||||
From = jlib:make_jid(User, Server, ""),
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_AUTH ->
|
||||
Password = xml:get_tag_cdata(El),
|
||||
ejabberd_auth:set_password(User, Server, Password),
|
||||
ok;
|
||||
?NS_ROSTER ->
|
||||
catch mod_roster:set_items(User, Server, El),
|
||||
ok;
|
||||
?NS_LAST ->
|
||||
TimeStamp = xml:get_attr_s("last", Attrs),
|
||||
Status = xml:get_tag_cdata(El),
|
||||
catch mod_last:store_last_info(User,
|
||||
Server,
|
||||
list_to_integer(TimeStamp),
|
||||
Status),
|
||||
ok;
|
||||
?NS_VCARD ->
|
||||
catch mod_vcard:process_sm_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El}),
|
||||
ok;
|
||||
"jabber:x:offline" ->
|
||||
process_offline(From, El),
|
||||
ok;
|
||||
XMLNS ->
|
||||
case xml:get_attr_s("j_private_flag", Attrs) of
|
||||
"1" ->
|
||||
catch mod_private:process_sm_iq(
|
||||
From,
|
||||
jlib:make_jid("", ?MYNAME, ""),
|
||||
#iq{type = set, xmlns = ?NS_PRIVATE,
|
||||
sub_el = {xmlelement, "query", [],
|
||||
[jlib:remove_attr(
|
||||
"j_private_flag",
|
||||
jlib:remove_attr("xdbns", El))]}});
|
||||
_ ->
|
||||
?DEBUG("jd2ejd: Unknown namespace \"~s\"~n", [XMLNS])
|
||||
end,
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
process_offline(To, {xmlelement, _, _, Els}) ->
|
||||
lists:foreach(fun({xmlelement, _, Attrs, _} = El) ->
|
||||
FromS = xml:get_attr_s("from", Attrs),
|
||||
From = case FromS of
|
||||
"" ->
|
||||
jlib:make_jid("", ?MYNAME, "");
|
||||
_ ->
|
||||
jlib:string_to_jid(FromS)
|
||||
end,
|
||||
case From of
|
||||
error ->
|
||||
ok;
|
||||
_ ->
|
||||
catch mod_offline:store_packet(From, To, El)
|
||||
end
|
||||
end, Els).
|
||||
|
||||
|
||||
@@ -37,9 +37,6 @@
|
||||
parse_xdata_submit/1,
|
||||
timestamp_to_iso/1,
|
||||
timestamp_to_xml/1,
|
||||
now_to_utc_string/1,
|
||||
now_to_local_string/1,
|
||||
datetime_string_to_timestamp/1,
|
||||
decode_base64/1,
|
||||
encode_base64/1]).
|
||||
|
||||
@@ -441,123 +438,6 @@ timestamp_to_xml({{Year, Month, Day}, {Hour, Minute, Second}}) ->
|
||||
[Year, Month, Day, Hour, Minute, Second]))}],
|
||||
[]}.
|
||||
|
||||
now_to_utc_string({MegaSecs, Secs, MicroSecs}) ->
|
||||
{{Year, Month, Day}, {Hour, Minute, Second}} =
|
||||
calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}),
|
||||
lists:flatten(
|
||||
io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6..0wZ",
|
||||
[Year, Month, Day, Hour, Minute, Second, MicroSecs])).
|
||||
|
||||
now_to_local_string({MegaSecs, Secs, MicroSecs}) ->
|
||||
LocalTime = calendar:now_to_local_time({MegaSecs, Secs, MicroSecs}),
|
||||
UTCTime = calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}),
|
||||
Seconds = calendar:datetime_to_gregorian_seconds(LocalTime) -
|
||||
calendar:datetime_to_gregorian_seconds(UTCTime),
|
||||
{{H, M, _}, Sign} = if
|
||||
Seconds < 0 ->
|
||||
{calendar:seconds_to_time(-Seconds), "-"};
|
||||
true ->
|
||||
{calendar:seconds_to_time(Seconds), "+"}
|
||||
end,
|
||||
{{Year, Month, Day}, {Hour, Minute, Second}} = LocalTime,
|
||||
lists:flatten(
|
||||
io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6..0w~s~2..0w:~2..0w",
|
||||
[Year, Month, Day, Hour, Minute, Second, MicroSecs, Sign, H, M])).
|
||||
|
||||
|
||||
% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {MegaSecs, Secs, MicroSecs}
|
||||
datetime_string_to_timestamp(TimeStr) ->
|
||||
case catch parse_datetime(TimeStr) of
|
||||
{'EXIT', _Err} ->
|
||||
undefined;
|
||||
TimeStamp ->
|
||||
TimeStamp
|
||||
end.
|
||||
|
||||
parse_datetime(TimeStr) ->
|
||||
[Date, Time] = string:tokens(TimeStr, "T"),
|
||||
D = parse_date(Date),
|
||||
{T, MS, TZH, TZM} = parse_time(Time),
|
||||
S = calendar:datetime_to_gregorian_seconds({D, T}),
|
||||
S1 = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
|
||||
Seconds = (S - S1) - TZH * 60 * 60 - TZM * 60,
|
||||
{Seconds div 1000000, Seconds rem 1000000, MS}.
|
||||
|
||||
% yyyy-mm-dd
|
||||
parse_date(Date) ->
|
||||
[Y, M, D] = string:tokens(Date, "-"),
|
||||
Date1 = {list_to_integer(Y), list_to_integer(M), list_to_integer(D)},
|
||||
case calendar:valid_date(Date1) of
|
||||
true ->
|
||||
Date1;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
% hh:mm:ss[.sss]TZD
|
||||
parse_time(Time) ->
|
||||
case string:str(Time, "Z") of
|
||||
0 ->
|
||||
parse_time_with_timezone(Time);
|
||||
_ ->
|
||||
[T | _] = string:tokens(Time, "Z"),
|
||||
{TT, MS} = parse_time1(T),
|
||||
{TT, MS, 0, 0}
|
||||
end.
|
||||
|
||||
parse_time_with_timezone(Time) ->
|
||||
case string:str(Time, "+") of
|
||||
0 ->
|
||||
case string:str(Time, "-") of
|
||||
0 ->
|
||||
false;
|
||||
_ ->
|
||||
parse_time_with_timezone(Time, "-")
|
||||
end;
|
||||
_ ->
|
||||
parse_time_with_timezone(Time, "+")
|
||||
end.
|
||||
|
||||
parse_time_with_timezone(Time, Delim) ->
|
||||
[T, TZ] = string:tokens(Time, Delim),
|
||||
{TZH, TZM} = parse_timezone(TZ),
|
||||
{TT, MS} = parse_time1(T),
|
||||
case Delim of
|
||||
"-" ->
|
||||
{TT, MS, -TZH, -TZM};
|
||||
"+" ->
|
||||
{TT, MS, TZH, TZM}
|
||||
end.
|
||||
|
||||
parse_timezone(TZ) ->
|
||||
[H, M] = string:tokens(TZ, ":"),
|
||||
{[H1, M1], true} = check_list([{H, 12}, {M, 60}]),
|
||||
{H1, M1}.
|
||||
|
||||
parse_time1(Time) ->
|
||||
[HMS | T] = string:tokens(Time, "."),
|
||||
MS = case T of
|
||||
[] ->
|
||||
0;
|
||||
[Val] ->
|
||||
list_to_integer(string:left(Val, 6, $0))
|
||||
end,
|
||||
[H, M, S] = string:tokens(HMS, ":"),
|
||||
{[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]),
|
||||
{{H1, M1, S1}, MS}.
|
||||
|
||||
check_list(List) ->
|
||||
lists:mapfoldl(
|
||||
fun({L, N}, B)->
|
||||
V = list_to_integer(L),
|
||||
if
|
||||
(V >= 0) and (V =< N) ->
|
||||
{V, B};
|
||||
true ->
|
||||
{false, false}
|
||||
end
|
||||
end, true, List).
|
||||
|
||||
|
||||
%
|
||||
% Base64 stuff (based on httpd_util.erl)
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
-define(NS_XDATA, "jabber:x:data").
|
||||
-define(NS_IQDATA, "jabber:iq:data").
|
||||
-define(NS_DELAY, "jabber:x:delay").
|
||||
-define(NS_EXPIRE, "jabber:x:expire").
|
||||
-define(NS_EVENT, "jabber:x:event").
|
||||
-define(NS_XCONFERENCE, "jabber:x:conference").
|
||||
-define(NS_STATS, "http://jabber.org/protocol/stats").
|
||||
@@ -41,13 +40,11 @@
|
||||
-define(NS_STANZAS, "urn:ietf:params:xml:ns:xmpp-stanzas").
|
||||
-define(NS_STREAMS, "urn:ietf:params:xml:ns:xmpp-streams").
|
||||
|
||||
-define(NS_TLS, "urn:ietf:params:xml:ns:xmpp-tls").
|
||||
-define(NS_SASL, "urn:ietf:params:xml:ns:xmpp-sasl").
|
||||
-define(NS_SESSION, "urn:ietf:params:xml:ns:xmpp-session").
|
||||
-define(NS_BIND, "urn:ietf:params:xml:ns:xmpp-bind").
|
||||
|
||||
-define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth").
|
||||
-define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register").
|
||||
|
||||
% TODO: remove "code" attribute (currently it used for backward-compatibility)
|
||||
-define(STANZA_ERROR(Code, Type, Condition),
|
||||
@@ -150,13 +147,21 @@
|
||||
-define(ERRT_UNEXPECTED_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT("400", "wait", "unexpected-request", Lang, Text)).
|
||||
|
||||
% Auth stanza errors
|
||||
-define(ERR_AUTH_NO_RESOURCE_PROVIDED(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang, "No resource provided")).
|
||||
-define(ERR_AUTH_BAD_RESOURCE_FORMAT(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang, "Illegal resource format")).
|
||||
-define(ERR_AUTH_RESOURCE_CONFLICT(Lang),
|
||||
?ERRT_CONFLICT(Lang, "Resource conflict")).
|
||||
% TODO: update to new-style
|
||||
% Application-specific stanza errors
|
||||
-define(AUTH_STANZA_ERROR(Condition),
|
||||
{xmlelement, "error",
|
||||
[{"code", "406"}, {"class", "app"}],
|
||||
[{xmlelement, "auth-condition",
|
||||
[{"xmlns", ?NS_AUTH_ERROR}],
|
||||
[{xmlelement, Condition, [], []}]}]}).
|
||||
|
||||
-define(ERR_AUTH_NO_RESOURCE_PROVIDED,
|
||||
?AUTH_STANZA_ERROR("no-resource-provided")).
|
||||
-define(ERR_AUTH_BAD_RESOURCE_FORMAT,
|
||||
?AUTH_STANZA_ERROR("bad-resource-format")).
|
||||
-define(ERR_AUTH_RESOURCE_CONFLICT,
|
||||
?AUTH_STANZA_ERROR("resource-conflict")).
|
||||
|
||||
|
||||
-define(STREAM_ERROR(Condition),
|
||||
@@ -214,63 +219,6 @@
|
||||
%-define(SERR_,
|
||||
% ?STREAM_ERROR("")).
|
||||
|
||||
-define(STREAM_ERRORT(Condition, Lang, Text),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []},
|
||||
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(SERRT_BAD_FORMAT(Lang, Text),
|
||||
?STREAM_ERRORT("bad-format", Lang, Text)).
|
||||
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
|
||||
?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)).
|
||||
-define(SERRT_CONFLICT(Lang, Text),
|
||||
?STREAM_ERRORT("conflict", Lang, Text)).
|
||||
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
|
||||
?STREAM_ERRORT("connection-timeout", Lang, Text)).
|
||||
-define(SERRT_HOST_GONE(Lang, Text),
|
||||
?STREAM_ERRORT("host-gone", Lang, Text)).
|
||||
-define(SERRT_HOST_UNKNOWN(Lang, Text),
|
||||
?STREAM_ERRORT("host-unknown", Lang, Text)).
|
||||
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
|
||||
?STREAM_ERRORT("improper-addressing", Lang, Text)).
|
||||
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STREAM_ERRORT("internal-server-error", Lang, Text)).
|
||||
-define(SERRT_INVALID_FROM(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-from", Lang, Text)).
|
||||
-define(SERRT_INVALID_ID(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-id", Lang, Text)).
|
||||
-define(SERRT_INVALID_NAMESPACE(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-namespace", Lang, Text)).
|
||||
-define(SERRT_INVALID_XML(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-xml", Lang, Text)).
|
||||
-define(SERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STREAM_ERRORT("not-authorized", Lang, Text)).
|
||||
-define(SERRT_POLICY_VIOLATION(Lang, Text),
|
||||
?STREAM_ERRORT("policy-violation", Lang, Text)).
|
||||
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
|
||||
?STREAM_ERRORT("remote-connection-failed", Lang, Text)).
|
||||
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
|
||||
?STREAM_ERRORT("resource-constraint", Lang, Text)).
|
||||
-define(SERRT_RESTRICTED_XML(Lang, Text),
|
||||
?STREAM_ERRORT("restricted-xml", Lang, Text)).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERRT_SEE_OTHER_HOST(Lang, Text),
|
||||
?STREAM_ERRORT("see-other-host", Lang, Text)).
|
||||
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
|
||||
?STREAM_ERRORT("system-shutdown", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-encoding", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-stanza-type", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-version", Lang, Text)).
|
||||
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
|
||||
?STREAM_ERRORT("xml-not-well-formed", Lang, Text)).
|
||||
%-define(SERRT_(Lang, Text),
|
||||
% ?STREAM_ERRORT("", Lang, Text)).
|
||||
|
||||
|
||||
-record(jid, {user, server, resource,
|
||||
luser, lserver, lresource}).
|
||||
|
||||
@@ -1,320 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_announce.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Manage announce messages
|
||||
%%% Created : 11 Aug 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_announce).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
init/0,
|
||||
stop/0,
|
||||
announce/3,
|
||||
send_motd/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(motd, {server, packet}).
|
||||
-record(motd_users, {us, dummy = []}).
|
||||
|
||||
-define(PROCNAME, ejabberd_announce).
|
||||
|
||||
start(_) ->
|
||||
mnesia:create_table(motd, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, motd)}]),
|
||||
mnesia:create_table(motd_users, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, motd_users)}]),
|
||||
update_tables(),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook,
|
||||
?MODULE, announce, 50),
|
||||
ejabberd_hooks:add(user_available_hook,
|
||||
?MODULE, send_motd, 50),
|
||||
register(?PROCNAME, proc_lib:spawn(?MODULE, init, [])).
|
||||
|
||||
init() ->
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
receive
|
||||
{announce_all, From, To, Packet} ->
|
||||
announce_all(From, To, Packet),
|
||||
loop();
|
||||
{announce_online, From, To, Packet} ->
|
||||
announce_online(From, To, Packet),
|
||||
loop();
|
||||
{announce_all_hosts_online, From, To, Packet} ->
|
||||
announce_all_hosts_online(From, To, Packet),
|
||||
loop();
|
||||
{announce_motd, From, To, Packet} ->
|
||||
announce_motd(From, To, Packet),
|
||||
loop();
|
||||
{announce_motd_update, From, To, Packet} ->
|
||||
announce_motd_update(From, To, Packet),
|
||||
loop();
|
||||
{announce_motd_delete, From, To, Packet} ->
|
||||
announce_motd_delete(From, To, Packet),
|
||||
loop();
|
||||
_ ->
|
||||
loop()
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(local_send_to_resource_hook,
|
||||
?MODULE, announce, 50),
|
||||
ejabberd_hooks:delete(sm_register_connection_hook,
|
||||
?MODULE, send_motd, 50),
|
||||
exit(whereis(?PROCNAME), stop),
|
||||
{wait, ?PROCNAME}.
|
||||
|
||||
announce(From, To, Packet) ->
|
||||
case To of
|
||||
#jid{luser = "", lresource = Res} ->
|
||||
{xmlelement, Name, _Attrs, _Els} = Packet,
|
||||
case {Res, Name} of
|
||||
{"announce/all", "message"} ->
|
||||
?PROCNAME ! {announce_all, From, To, Packet},
|
||||
stop;
|
||||
{"announce/online", "message"} ->
|
||||
?PROCNAME ! {announce_online, From, To, Packet},
|
||||
stop;
|
||||
{"announce/all-hosts/online", "message"} ->
|
||||
?PROCNAME ! {announce_all_hosts_online, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd", "message"} ->
|
||||
?PROCNAME ! {announce_motd, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd/update", "message"} ->
|
||||
?PROCNAME ! {announce_motd_update, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd/delete", "message"} ->
|
||||
?PROCNAME ! {announce_motd_delete, From, To, Packet},
|
||||
stop;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
announce_all(From, To, Packet) ->
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, none),
|
||||
case acl:match_rule(Access, From) of
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
Local = jlib:make_jid("", To#jid.server, ""),
|
||||
lists:foreach(
|
||||
fun({User, Server}) ->
|
||||
Dest = jlib:make_jid(User, Server, ""),
|
||||
ejabberd_router:route(Local, Dest, Packet)
|
||||
end, ejabberd_auth:get_vh_registered_users(To#jid.lserver))
|
||||
end.
|
||||
|
||||
announce_online(From, To, Packet) ->
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, none),
|
||||
case acl:match_rule(Access, From) of
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_online1(ejabberd_sm:get_vh_session_list(To#jid.lserver),
|
||||
To#jid.server,
|
||||
Packet)
|
||||
end.
|
||||
|
||||
announce_all_hosts_online(From, To, Packet) ->
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, none),
|
||||
case acl:match_rule(Access, From) of
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_online1(ejabberd_sm:dirty_get_sessions_list(),
|
||||
To#jid.server,
|
||||
Packet)
|
||||
end.
|
||||
|
||||
announce_online1(Sessions, Server, Packet) ->
|
||||
Local = jlib:make_jid("", Server, ""),
|
||||
lists:foreach(
|
||||
fun({U, S, R}) ->
|
||||
Dest = jlib:make_jid(U, S, R),
|
||||
ejabberd_router:route(Local, Dest, Packet)
|
||||
end, Sessions).
|
||||
|
||||
announce_motd(From, To, Packet) ->
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, none),
|
||||
case acl:match_rule(Access, From) of
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_motd_update(To#jid.lserver, Packet),
|
||||
Sessions = ejabberd_sm:get_vh_session_list(To#jid.lserver),
|
||||
announce_online1(Sessions, To#jid.server, Packet),
|
||||
F = fun() ->
|
||||
lists:foreach(
|
||||
fun({U, S, _R}) ->
|
||||
mnesia:write(#motd_users{us = {U, S}})
|
||||
end, Sessions)
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
announce_motd_update(From, To, Packet) ->
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, none),
|
||||
case acl:match_rule(Access, From) of
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_motd_update(To#jid.lserver, Packet)
|
||||
end.
|
||||
|
||||
announce_motd_update(LServer, Packet) ->
|
||||
announce_motd_delete(LServer),
|
||||
F = fun() ->
|
||||
mnesia:write(#motd{server = LServer, packet = Packet})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
announce_motd_delete(From, To, Packet) ->
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, none),
|
||||
case acl:match_rule(Access, From) of
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
allow ->
|
||||
announce_motd_delete(To#jid.lserver)
|
||||
end.
|
||||
|
||||
announce_motd_delete(LServer) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({motd, LServer}),
|
||||
mnesia:write_lock_table(motd_users),
|
||||
Users = mnesia:select(
|
||||
motd_users,
|
||||
[{#motd_users{us = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}],
|
||||
['$1']}]),
|
||||
lists:foreach(fun(US) ->
|
||||
mnesia:delete({motd_users, US})
|
||||
end, Users)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
|
||||
case catch mnesia:dirty_read({motd, LServer}) of
|
||||
[#motd{packet = Packet}] ->
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({motd_users, US}) of
|
||||
[#motd_users{}] ->
|
||||
ok;
|
||||
_ ->
|
||||
Local = jlib:make_jid("", LServer, ""),
|
||||
ejabberd_router:route(Local, JID, Packet),
|
||||
F = fun() ->
|
||||
mnesia:write(#motd_users{us = US})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
update_tables() ->
|
||||
update_motd_table(),
|
||||
update_motd_users_table().
|
||||
|
||||
update_motd_table() ->
|
||||
Fields = record_info(fields, motd),
|
||||
case mnesia:table_info(motd, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[id, packet] ->
|
||||
?INFO_MSG("Converting motd table from "
|
||||
"{id, packet} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_announce_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, motd},
|
||||
{attributes, record_info(fields, motd)}]),
|
||||
mnesia:transform_table(motd, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_announce_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#motd{server = _} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_announce_tmp_table,
|
||||
R#motd{server = Host})
|
||||
end, ok, motd)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(motd),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(motd),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_announce_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_announce_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating motd table", []),
|
||||
mnesia:transform_table(motd, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
update_motd_users_table() ->
|
||||
Fields = record_info(fields, motd_users),
|
||||
case mnesia:table_info(motd_users, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[luser, dummy] ->
|
||||
?INFO_MSG("Converting motd_users table from "
|
||||
"{luser, dummy} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_announce_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, motd_users},
|
||||
{attributes, record_info(fields, motd_users)}]),
|
||||
mnesia:transform_table(motd_users, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_announce_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#motd_users{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_announce_tmp_table,
|
||||
R#motd_users{us = {U, Host}})
|
||||
end, ok, motd_users)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(motd_users),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(motd_users),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_announce_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_announce_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating motd_users table", []),
|
||||
mnesia:transform_table(motd_users, ignore, Fields)
|
||||
end.
|
||||
@@ -690,6 +690,11 @@ set_form(["config", "remusers"], Lang, XData) ->
|
||||
fun({Var, Vals}) ->
|
||||
case Vals of
|
||||
["1"] ->
|
||||
ejabberd_sm ! {route,
|
||||
jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(Var, "", ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{exit, "User removed"}]}},
|
||||
catch ejabberd_auth:remove_user(Var);
|
||||
_ ->
|
||||
ok
|
||||
@@ -723,7 +728,7 @@ process_sm_iq(From, To,
|
||||
deny ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
allow ->
|
||||
#jid{user = User, server = Server} = To,
|
||||
#jid{user = User} = To,
|
||||
case Type of
|
||||
set ->
|
||||
XDataEl = find_xdata_el(SubEl),
|
||||
@@ -748,11 +753,10 @@ process_sm_iq(From, To,
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_sm_form(
|
||||
User, Server, Node,
|
||||
Lang, XData) of
|
||||
User, Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el =
|
||||
sub_el =
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
Res
|
||||
@@ -770,7 +774,7 @@ process_sm_iq(From, To,
|
||||
get ->
|
||||
Node =
|
||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
case get_sm_form(User, Server, Node, Lang) of
|
||||
case get_sm_form(User, Node, Lang) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el =
|
||||
@@ -784,7 +788,7 @@ process_sm_iq(From, To,
|
||||
end.
|
||||
|
||||
|
||||
get_sm_form(User, Server, [], Lang) ->
|
||||
get_sm_form(User, [], Lang) ->
|
||||
{result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata,
|
||||
@@ -807,7 +811,7 @@ get_sm_form(User, Server, [], Lang) ->
|
||||
[{xmlelement, "value", [], [{xmlcdata, "remove"}]}]}
|
||||
]},
|
||||
?XFIELD("text-private", "Password", "password",
|
||||
ejabberd_auth:get_password_s(User, Server))
|
||||
ejabberd_auth:get_password_s(User))
|
||||
%{xmlelement, "field", [{"type", "text-single"},
|
||||
% {"label",
|
||||
% translate:translate(Lang, "Host name")},
|
||||
@@ -815,27 +819,32 @@ get_sm_form(User, Server, [], Lang) ->
|
||||
% [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
|
||||
]}]};
|
||||
|
||||
get_sm_form(_User, _Server, _Node, Lang) ->
|
||||
get_sm_form(_, _, Lang) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
|
||||
set_sm_form(User, Server, [], Lang, XData) ->
|
||||
set_sm_form(User, [], Lang, XData) ->
|
||||
case lists:keysearch("action", 1, XData) of
|
||||
{value, {_, ["edit"]}} ->
|
||||
case lists:keysearch("password", 1, XData) of
|
||||
{value, {_, [Password]}} ->
|
||||
ejabberd_auth:set_password(User, Server, Password),
|
||||
ejabberd_auth:set_password(User, Password),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
{value, {_, ["remove"]}} ->
|
||||
catch ejabberd_auth:remove_user(User, Server),
|
||||
ejabberd_sm ! {route,
|
||||
jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, "", ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{exit, "User removed"}]}},
|
||||
catch ejabberd_auth:remove_user(User),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
set_sm_form(_User, _Server, _Node, Lang, XData) ->
|
||||
set_sm_form(_, _, Lang, XData) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||
|
||||
@@ -135,7 +135,7 @@ process_get({xmlelement, "access", Attrs, _SubEls}) ->
|
||||
{result, {xmlelement, "access", Attrs, [{xmlcdata, Str}]}};
|
||||
process_get({xmlelement, "last", Attrs, _SubEls}) ->
|
||||
case catch mnesia:dirty_select(
|
||||
last_activity, [{{last_activity, '_', '$1', '_'}, [], ['$1']}]) of
|
||||
last_activity, [{{last_activity, '_', '$1'}, [], ['$1']}]) of
|
||||
{'EXIT', _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
Vals ->
|
||||
|
||||
@@ -21,11 +21,7 @@
|
||||
register_feature/1,
|
||||
unregister_feature/1,
|
||||
register_extra_domain/1,
|
||||
unregister_extra_domain/1,
|
||||
register_sm_feature/1,
|
||||
unregister_sm_feature/1,
|
||||
register_sm_node/4,
|
||||
unregister_sm_node/1]).
|
||||
unregister_extra_domain/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
@@ -57,8 +53,6 @@ start(Opts) ->
|
||||
catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
|
||||
ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []),
|
||||
lists:foreach(fun register_extra_domain/1, ExtraDomains),
|
||||
catch ets:new(disco_sm_features, [named_table, ordered_set, public]),
|
||||
catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]),
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
@@ -102,11 +96,10 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
get_services_only(To#jid.lserver)
|
||||
get_services_only()
|
||||
}]};
|
||||
_ ->
|
||||
case get_local_items(To#jid.lserver, Node,
|
||||
jlib:jid_to_string(To), Lang) of
|
||||
case get_local_items(Node, jlib:jid_to_string(To), Lang) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@@ -223,15 +216,15 @@ domain_to_xml(Domain) ->
|
||||
{"node", Node}], []}).
|
||||
|
||||
|
||||
get_services_only(Host) ->
|
||||
get_services_only() ->
|
||||
lists:map(fun domain_to_xml/1,
|
||||
get_vh_services(Host)) ++
|
||||
ejabberd_router:dirty_get_all_routes()) ++
|
||||
lists:map(fun domain_to_xml/1, ets:tab2list(disco_extra_domains)).
|
||||
|
||||
get_local_items(Host, [], Server, Lang) ->
|
||||
get_local_items([], Server, Lang) ->
|
||||
Domains =
|
||||
lists:map(fun domain_to_xml/1,
|
||||
get_vh_services(Host)) ++
|
||||
ejabberd_router:dirty_get_all_routes()) ++
|
||||
lists:map(fun domain_to_xml/1, ets:tab2list(disco_extra_domains)),
|
||||
{result,
|
||||
Domains ++
|
||||
@@ -243,61 +236,61 @@ get_local_items(Host, [], Server, Lang) ->
|
||||
?NODE("Stopped Nodes", "stopped nodes")
|
||||
]};
|
||||
|
||||
get_local_items(Host, ["config"], Server, Lang) ->
|
||||
get_local_items(["config"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Host Name", "config/hostname"),
|
||||
?NODE("Access Control Lists", "config/acls"),
|
||||
?NODE("Access Rules", "config/access")
|
||||
% Too expensive on big hosts
|
||||
%?NODE("Remove Users", "config/remusers")
|
||||
?NODE("Access Rules", "config/access"),
|
||||
?NODE("Remove Users", "config/remusers")
|
||||
]};
|
||||
|
||||
get_local_items(Host, ["config", _], Server, Lang) ->
|
||||
get_local_items(["config", _], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(Host, ["online users"], Server, Lang) ->
|
||||
{result, get_online_vh_users(Host)};
|
||||
get_local_items(["online users"], Server, Lang) ->
|
||||
{result, get_online_users()};
|
||||
|
||||
get_local_items(Host, ["all users"], Server, Lang) ->
|
||||
{result, get_all_vh_users(Host)};
|
||||
get_local_items(["all users"], Server, Lang) ->
|
||||
{result, get_all_users()};
|
||||
|
||||
get_local_items(Host, ["all users", [$@ | Diap]], Server, Lang) ->
|
||||
get_local_items(["all users", [$@ | Diap]], Server, Lang) ->
|
||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||
{'EXIT', Reason} ->
|
||||
?ERR_INTERNAL_SERVER_ERROR;
|
||||
Users ->
|
||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||
SUsers = lists:sort(Users),
|
||||
case catch begin
|
||||
{ok, [S1, S2]} = regexp:split(Diap, "-"),
|
||||
N1 = list_to_integer(S1),
|
||||
N2 = list_to_integer(S2),
|
||||
Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
|
||||
lists:map(fun({S, U}) ->
|
||||
lists:map(fun(U) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", U ++ "@" ++ S},
|
||||
{"name", U ++ "@" ++ S}], []}
|
||||
[{"jid", U ++ "@" ++ ?MYNAME},
|
||||
{"name", U}], []}
|
||||
end, Sub)
|
||||
end of
|
||||
{'EXIT', Reason} ->
|
||||
?ERR_NOT_ACCEPTABLE;
|
||||
% TODO: must be "not acceptable"
|
||||
?ERR_BAD_REQUEST;
|
||||
Res ->
|
||||
{result, Res}
|
||||
end
|
||||
end;
|
||||
|
||||
get_local_items(Host, ["outgoing s2s"], Server, Lang) ->
|
||||
{result, get_outgoing_s2s(Host, Lang)};
|
||||
get_local_items(["outgoing s2s"], Server, Lang) ->
|
||||
{result, get_outgoing_s2s(Lang)};
|
||||
|
||||
get_local_items(Host, ["outgoing s2s", To], Server, Lang) ->
|
||||
{result, get_outgoing_s2s(Host, Lang, To)};
|
||||
get_local_items(["outgoing s2s", To], Server, Lang) ->
|
||||
{result, get_outgoing_s2s(Lang, To)};
|
||||
|
||||
get_local_items(Host, ["running nodes"], Server, Lang) ->
|
||||
get_local_items(["running nodes"], Server, Lang) ->
|
||||
{result, get_running_nodes(Lang)};
|
||||
|
||||
get_local_items(Host, ["stopped nodes"], Server, Lang) ->
|
||||
get_local_items(["stopped nodes"], Server, Lang) ->
|
||||
{result, get_stopped_nodes(Lang)};
|
||||
|
||||
get_local_items(Host, ["running nodes", ENode], Server, Lang) ->
|
||||
get_local_items(["running nodes", ENode], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("DB", "running nodes/" ++ ENode ++ "/DB"),
|
||||
?NODE("Modules", "running nodes/" ++ ENode ++ "/modules"),
|
||||
@@ -306,19 +299,19 @@ get_local_items(Host, ["running nodes", ENode], Server, Lang) ->
|
||||
"running nodes/" ++ ENode ++ "/import")
|
||||
]};
|
||||
|
||||
get_local_items(Host, ["running nodes", ENode, "DB"], Server, Lang) ->
|
||||
get_local_items(["running nodes", ENode, "DB"], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(Host, ["running nodes", ENode, "modules"], Server, Lang) ->
|
||||
get_local_items(["running nodes", ENode, "modules"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Start Modules", "running nodes/" ++ ENode ++ "/modules/start"),
|
||||
?NODE("Stop Modules", "running nodes/" ++ ENode ++ "/modules/stop")
|
||||
]};
|
||||
|
||||
get_local_items(Host, ["running nodes", ENode, "modules", _], Server, Lang) ->
|
||||
get_local_items(["running nodes", ENode, "modules", _], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(Host, ["running nodes", ENode, "backup"], Server, Lang) ->
|
||||
get_local_items(["running nodes", ENode, "backup"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Backup", "running nodes/" ++ ENode ++ "/backup/backup"),
|
||||
?NODE("Restore", "running nodes/" ++ ENode ++ "/backup/restore"),
|
||||
@@ -326,62 +319,49 @@ get_local_items(Host, ["running nodes", ENode, "backup"], Server, Lang) ->
|
||||
"running nodes/" ++ ENode ++ "/backup/textfile")
|
||||
]};
|
||||
|
||||
get_local_items(Host, ["running nodes", ENode, "backup", _], Server, Lang) ->
|
||||
get_local_items(["running nodes", ENode, "backup", _], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(Host, ["running nodes", ENode, "import"], Server, Lang) ->
|
||||
get_local_items(["running nodes", ENode, "import"], Server, Lang) ->
|
||||
{result,
|
||||
[?NODE("Import File", "running nodes/" ++ ENode ++ "/import/file"),
|
||||
?NODE("Import Directory", "running nodes/" ++ ENode ++ "/import/dir")
|
||||
]};
|
||||
|
||||
get_local_items(Host, ["running nodes", ENode, "import", _], Server, Lang) ->
|
||||
get_local_items(["running nodes", ENode, "import", _], Server, Lang) ->
|
||||
{result, []};
|
||||
|
||||
get_local_items(_Host, _, _, _) ->
|
||||
get_local_items(_, _, _) ->
|
||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}.
|
||||
|
||||
|
||||
|
||||
get_vh_services(Host) ->
|
||||
Hosts = lists:sort(fun(H1, H2) -> length(H1) >= length(H2) end, ?MYHOSTS),
|
||||
lists:filter(fun(H) ->
|
||||
case lists:dropwhile(
|
||||
fun(VH) ->
|
||||
not lists:suffix("." ++ VH, H)
|
||||
end, Hosts) of
|
||||
[] ->
|
||||
false;
|
||||
[VH | _] ->
|
||||
VH == Host
|
||||
end
|
||||
end, ejabberd_router:dirty_get_all_routes()).
|
||||
|
||||
get_online_vh_users(Host) ->
|
||||
case catch ejabberd_sm:get_vh_session_list(Host) of
|
||||
|
||||
get_online_users() ->
|
||||
case catch ejabberd_sm:dirty_get_sessions_list() of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
USRs ->
|
||||
SURs = lists:sort([{S, U, R} || {U, S, R} <- USRs]),
|
||||
lists:map(fun({S, U, R}) ->
|
||||
URs ->
|
||||
lists:map(fun({U, R}) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", U ++ "@" ++ S ++ "/" ++ R},
|
||||
{"name", U ++ "@" ++ S}], []}
|
||||
end, SURs)
|
||||
[{"jid", U ++ "@" ++ ?MYNAME ++ "/" ++ R},
|
||||
{"name", U}], []}
|
||||
end, lists:sort(URs))
|
||||
end.
|
||||
|
||||
get_all_vh_users(Host) ->
|
||||
case catch ejabberd_auth:get_vh_registered_users(Host) of
|
||||
get_all_users() ->
|
||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
Users ->
|
||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||
SUsers = lists:sort(Users),
|
||||
case length(SUsers) of
|
||||
N when N =< 100 ->
|
||||
lists:map(fun({S, U}) ->
|
||||
lists:map(fun(U) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", U ++ "@" ++ S},
|
||||
{"name", U ++ "@" ++ S}], []}
|
||||
[{"jid", U ++ "@" ++ ?MYNAME},
|
||||
{"name", U}], []}
|
||||
end, SUsers);
|
||||
N ->
|
||||
NParts = trunc(math:sqrt(N * 0.618)) + 1,
|
||||
@@ -391,35 +371,30 @@ get_all_vh_users(Host) ->
|
||||
Node =
|
||||
"@" ++ integer_to_list(K) ++
|
||||
"-" ++ integer_to_list(L),
|
||||
{FS, FU} = lists:nth(K, SUsers),
|
||||
{LS, LU} =
|
||||
if L < N -> lists:nth(L, SUsers);
|
||||
true -> lists:last(SUsers)
|
||||
end,
|
||||
Last = if L < N -> lists:nth(L, SUsers);
|
||||
true -> lists:last(SUsers)
|
||||
end,
|
||||
Name =
|
||||
FU ++ "@" ++ FS ++
|
||||
" -- " ++
|
||||
LU ++ "@" ++ LS,
|
||||
lists:nth(K, SUsers) ++ " -- " ++
|
||||
Last,
|
||||
{xmlelement, "item",
|
||||
[{"jid", Host},
|
||||
[{"jid", ?MYNAME},
|
||||
{"node", "all users/" ++ Node},
|
||||
{"name", Name}], []}
|
||||
end, lists:seq(1, N, M))
|
||||
end
|
||||
end.
|
||||
|
||||
get_outgoing_s2s(Host, Lang) ->
|
||||
get_outgoing_s2s(Lang) ->
|
||||
case catch ejabberd_s2s:dirty_get_connections() of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
Connections ->
|
||||
DotHost = "." ++ Host,
|
||||
TConns = [TH || {FH, TH} <- Connections,
|
||||
Host == FH orelse lists:suffix(DotHost, FH)],
|
||||
TConns = [element(2, C) || C <- Connections],
|
||||
lists:map(
|
||||
fun(T) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", Host},
|
||||
[{"jid", ?MYNAME},
|
||||
{"node", "outgoing s2s/" ++ T},
|
||||
{"name",
|
||||
lists:flatten(
|
||||
@@ -429,7 +404,7 @@ get_outgoing_s2s(Host, Lang) ->
|
||||
end, lists:usort(TConns))
|
||||
end.
|
||||
|
||||
get_outgoing_s2s(Host, Lang, To) ->
|
||||
get_outgoing_s2s(Lang, To) ->
|
||||
case catch ejabberd_s2s:dirty_get_connections() of
|
||||
{'EXIT', Reason} ->
|
||||
[];
|
||||
@@ -437,7 +412,7 @@ get_outgoing_s2s(Host, Lang, To) ->
|
||||
lists:map(
|
||||
fun({F, T}) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", Host},
|
||||
[{"jid", ?MYNAME},
|
||||
{"node", "outgoing s2s/" ++ To ++ "/" ++ F},
|
||||
{"name",
|
||||
lists:flatten(
|
||||
@@ -488,122 +463,52 @@ get_stopped_nodes(Lang) ->
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
register_sm_feature(Feature) ->
|
||||
catch ets:new(disco_sm_features, [named_table, ordered_set, public]),
|
||||
ets:insert(disco_sm_features, {Feature}).
|
||||
|
||||
unregister_sm_feature(Feature) ->
|
||||
catch ets:new(disco_sm_features, [named_table, ordered_set, public]),
|
||||
ets:delete(disco_sm_features, Feature).
|
||||
|
||||
register_sm_node(Node, Name, Module, Function) ->
|
||||
catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]),
|
||||
ets:insert(disco_sm_nodes, {Node, Name, Module, Function}).
|
||||
|
||||
unregister_sm_node(Node) ->
|
||||
catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]),
|
||||
ets:delete(disco_sm_nodes, Node).
|
||||
|
||||
process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
#jid{user = User, luser = LTo} = To,
|
||||
#jid{luser = LFrom, lserver = LServer} = From,
|
||||
Self = (LTo == LFrom) andalso (LServer == ?MYNAME),
|
||||
Node = xml:get_tag_attr_s("node", SubEl),
|
||||
case {acl:match_rule(configure, From), Type, Self, Node} of
|
||||
{_, set, _, _} ->
|
||||
process_sm_iq_items(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
#jid{user = User} = To,
|
||||
case {acl:match_rule(configure, From), Type} of
|
||||
{deny, _} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{_, get, true, []} ->
|
||||
Nodes = lists:map(fun({Nod, Name, _, _}) ->
|
||||
node_to_xml(User,
|
||||
Nod,
|
||||
translate:translate(Lang, Name))
|
||||
end, ets:tab2list(disco_sm_nodes)),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
get_user_resources(User) ++ Nodes}]};
|
||||
{allow, get, _, []} ->
|
||||
Nodes = lists:map(fun({Nod, Name, _, _}) ->
|
||||
node_to_xml(User,
|
||||
Nod,
|
||||
translate:translate(Lang, Name))
|
||||
end, ets:tab2list(disco_sm_nodes)),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
get_user_resources(User) ++ Nodes}]};
|
||||
{A, get, S, _} when (A == allow) or (S == true) ->
|
||||
case ets:lookup(disco_sm_nodes, Node) of
|
||||
[] ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]};
|
||||
[{Node, _Name, Module, Function}] ->
|
||||
case Module:Function(From, To, IQ) of
|
||||
{error, Err} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, Err]};
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS},
|
||||
{"node", Node}],
|
||||
Res}]}
|
||||
end
|
||||
end;
|
||||
{_, get, _, _} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]};
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
{allow, set} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{allow, get} ->
|
||||
case xml:get_tag_attr_s("node", SubEl) of
|
||||
"" ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
||||
get_user_resources(User)
|
||||
}]};
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
process_sm_iq_info(From, To, #iq{type = Type, xmlns = XMLNS,
|
||||
sub_el = SubEl} = IQ) ->
|
||||
#jid{luser = LTo} = To,
|
||||
#jid{luser = LFrom, lserver = LServer} = From,
|
||||
Self = (LTo == LFrom) andalso (LServer == ?MYNAME),
|
||||
Node = xml:get_tag_attr_s("node", SubEl),
|
||||
case {acl:match_rule(configure, From), Type, Self, Node} of
|
||||
{_, set, _, _} ->
|
||||
case {acl:match_rule(configure, From), Type} of
|
||||
{deny, _} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{allow, get, _, []} ->
|
||||
Features = lists:map(fun feature_to_xml/1,
|
||||
ets:tab2list(disco_sm_features)),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
[feature_to_xml({?NS_EJABBERD_CONFIG})] ++
|
||||
Features}]};
|
||||
{_, get, _, []} ->
|
||||
Features = lists:map(fun feature_to_xml/1,
|
||||
ets:tab2list(disco_sm_features)),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
Features}]};
|
||||
{A, get, S, _} when (A == allow) or (S == true) ->
|
||||
case ets:lookup(disco_sm_nodes, Node) of
|
||||
[] ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]};
|
||||
{allow, set} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
{allow, get} ->
|
||||
case xml:get_tag_attr_s("node", SubEl) of
|
||||
"" ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}],
|
||||
[feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
|
||||
_ ->
|
||||
IQ#iq{type = result, sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", XMLNS},
|
||||
{"node", Node}], []}]}
|
||||
end;
|
||||
{_, get, _, _} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]};
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
|
||||
get_user_resources(User) ->
|
||||
Rs = ejabberd_sm:get_user_resources(User, 'TODO'),
|
||||
Rs = ejabberd_sm:get_user_resources(User),
|
||||
lists:map(fun(R) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", User ++ "@" ++ ?MYNAME ++ "/" ++ R},
|
||||
{"name", User}], []}
|
||||
end, lists:sort(Rs)).
|
||||
|
||||
node_to_xml(User, Node, Name) ->
|
||||
{xmlelement, "item", [{"jid", User ++ "@" ++ ?MYNAME},
|
||||
{"node", Node},
|
||||
{"name", Name}], []}.
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_echo).
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
%Host = gen_mod:get_opt(host, Opts),
|
||||
Host = gen_mod:get_opt(host, Opts, "echo." ++ ?MYNAME),
|
||||
register(?PROCNAME, spawn(?MODULE, init, [Host])).
|
||||
register(ejabberd_mod_echo, spawn(?MODULE, init, [Host])).
|
||||
|
||||
init(Host) ->
|
||||
ejabberd_router:register_route(Host),
|
||||
@@ -41,6 +41,5 @@ loop(Host) ->
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
?PROCNAME ! stop,
|
||||
{wait, ?PROCNAME}.
|
||||
|
||||
ejabberd_mod_echo ! stop,
|
||||
ok.
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@ @LIBICONV@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
ERLSHLIBS = ../iconv_erl.so
|
||||
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -20,15 +26,19 @@ OBJS = \
|
||||
all: $(OBJS) $(ERLSHLIBS)
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
|
||||
#all: $(ERLSHLIBS)
|
||||
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
|
||||
|
||||
$(ERLSHLIBS): ../%.so: %.c
|
||||
$(CC) -Wall $(INCLUDES) $(CFLAGS) $(LDFLAGS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
|
||||
-o $@ -fpic -shared
|
||||
$(CC) -Wall $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(LIBDIRS) \
|
||||
$(subst ../,,$(subst .so,.c,$@)) \
|
||||
-lerl_interface @LIBICONV@ \
|
||||
-lei \
|
||||
-o $@ -fpic -shared \
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(ERLSHLIBS)
|
||||
|
||||
@@ -37,7 +37,7 @@ static int iconv_erl_control(ErlDrvData drv_data,
|
||||
int size;
|
||||
int index = 0;
|
||||
int avail;
|
||||
size_t inleft, outleft;
|
||||
int inleft, outleft;
|
||||
ErlDrvBinary *b;
|
||||
char *from, *to, *string, *stmp, *rstring, *rtmp;
|
||||
iconv_t cd;
|
||||
|
||||
@@ -12,53 +12,48 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, init/2, stop/0,
|
||||
closed_connection/3,
|
||||
get_user_and_encoding/3]).
|
||||
-export([start/1, init/2, stop/0, closed_conection/2,
|
||||
get_user_and_encoding/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(DEFAULT_IRC_ENCODING, "koi8-r").
|
||||
|
||||
-record(irc_connection, {jid_server_host, pid}).
|
||||
-record(irc_custom, {us_host, data}).
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_irc).
|
||||
-record(irc_connection, {userserver, pid}).
|
||||
-record(irc_custom, {userserver, data}).
|
||||
|
||||
start(Opts) ->
|
||||
iconv:start(),
|
||||
mnesia:create_table(irc_custom,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, irc_custom)}]),
|
||||
Hosts = gen_mod:get_hosts(Opts, "irc."),
|
||||
Host = hd(Hosts),
|
||||
update_table(Host),
|
||||
Host = gen_mod:get_opt(host, Opts, "irc." ++ ?MYNAME),
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
register(?PROCNAME, spawn(?MODULE, init, [Hosts, Access])).
|
||||
register(ejabberd_mod_irc, spawn(?MODULE, init, [Host, Access])).
|
||||
|
||||
init(Hosts, Access) ->
|
||||
init(Host, Access) ->
|
||||
catch ets:new(irc_connection, [named_table,
|
||||
public,
|
||||
{keypos, #irc_connection.jid_server_host}]),
|
||||
ejabberd_router:register_routes(Hosts),
|
||||
loop(Hosts, Access).
|
||||
{keypos, #irc_connection.userserver}]),
|
||||
ejabberd_router:register_route(Host),
|
||||
loop(Host, Access).
|
||||
|
||||
loop(Hosts, Access) ->
|
||||
loop(Host, Access) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(To#jid.lserver, Access, From, To, Packet) of
|
||||
case catch do_route(Host, Access, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Hosts, Access);
|
||||
loop(Host, Access);
|
||||
stop ->
|
||||
ejabberd_router:unregister_routes(Hosts),
|
||||
ejabberd_router:unregister_global_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Hosts, Access)
|
||||
loop(Host, Access)
|
||||
end.
|
||||
|
||||
|
||||
@@ -101,7 +96,7 @@ do_route1(Host, From, To, Packet) ->
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||
process_register(Host, From, To, IQ);
|
||||
process_register(From, To, IQ);
|
||||
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||
lang = Lang} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
@@ -126,17 +121,17 @@ do_route1(Host, From, To, Packet) ->
|
||||
_ ->
|
||||
case string:tokens(ChanServ, "%") of
|
||||
[[_ | _] = Channel, [_ | _] = Server] ->
|
||||
case ets:lookup(irc_connection, {From, Server, Host}) of
|
||||
case ets:lookup(irc_connection, {From, Server}) of
|
||||
[] ->
|
||||
io:format("open new connection~n"),
|
||||
{Username, Encoding} = get_user_and_encoding(
|
||||
Host, From, Server),
|
||||
From, Server),
|
||||
{ok, Pid} = mod_irc_connection:start(
|
||||
From, Host, Server,
|
||||
Username, Encoding),
|
||||
ets:insert(
|
||||
irc_connection,
|
||||
#irc_connection{jid_server_host = {From, Server, Host},
|
||||
#irc_connection{userserver = {From, Server},
|
||||
pid = Pid}),
|
||||
mod_irc_connection:route_chan(
|
||||
Pid, Channel, Resource, Packet),
|
||||
@@ -152,7 +147,7 @@ do_route1(Host, From, To, Packet) ->
|
||||
_ ->
|
||||
case string:tokens(ChanServ, "!") of
|
||||
[[_ | _] = Nick, [_ | _] = Server] ->
|
||||
case ets:lookup(irc_connection, {From, Server, Host}) of
|
||||
case ets:lookup(irc_connection, {From, Server}) of
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
@@ -174,13 +169,14 @@ do_route1(Host, From, To, Packet) ->
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
stop() ->
|
||||
?PROCNAME ! stop,
|
||||
{wait, ?PROCNAME}.
|
||||
ejabberd_mod_irc ! stop,
|
||||
ok.
|
||||
|
||||
|
||||
closed_connection(Host, From, Server) ->
|
||||
ets:delete(irc_connection, {From, Server, Host}).
|
||||
closed_conection(From, Server) ->
|
||||
ets:delete(irc_connection, {From, Server}).
|
||||
|
||||
|
||||
iq_disco() ->
|
||||
@@ -203,10 +199,10 @@ iq_get_vcard(Lang) ->
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module\n"
|
||||
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
|
||||
process_register(Host, From, To, #iq{} = IQ) ->
|
||||
case catch process_irc_register(Host, From, To, IQ) of
|
||||
process_register(From, To, #iq{} = IQ) ->
|
||||
case catch process_irc_register(From, To, IQ) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
ResIQ ->
|
||||
@@ -236,7 +232,7 @@ find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
|
||||
find_xdata_el1([_ | Els]) ->
|
||||
find_xdata_el1(Els).
|
||||
|
||||
process_irc_register(Host, From, To,
|
||||
process_irc_register(From, To,
|
||||
#iq{type = Type, xmlns = XMLNS,
|
||||
lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
@@ -261,8 +257,7 @@ process_irc_register(Host, From, To,
|
||||
Node = string:tokens(
|
||||
xml:get_tag_attr_s("node", SubEl),
|
||||
"/"),
|
||||
case set_form(
|
||||
Host, From, Node, Lang, XData) of
|
||||
case set_form(From, Node, Lang, XData) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@@ -282,7 +277,7 @@ process_irc_register(Host, From, To,
|
||||
get ->
|
||||
Node =
|
||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
case get_form(Host, From, Node, Lang) of
|
||||
case get_form(From, Node, Lang) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@@ -297,12 +292,11 @@ process_irc_register(Host, From, To,
|
||||
|
||||
|
||||
|
||||
get_form(Host, From, [], Lang) ->
|
||||
get_form(From, [], Lang) ->
|
||||
#jid{user = User, server = Server,
|
||||
luser = LUser, lserver = LServer} = From,
|
||||
US = {LUser, LServer},
|
||||
Customs =
|
||||
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
||||
case catch mnesia:dirty_read({irc_custom, {LUser, LServer}}) of
|
||||
{'EXIT', Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
[] ->
|
||||
@@ -312,7 +306,7 @@ get_form(Host, From, [], Lang) ->
|
||||
xml:get_attr_s(encodings, Data)}
|
||||
end,
|
||||
case Customs of
|
||||
{error, _Error} ->
|
||||
{error, _, _} ->
|
||||
Customs;
|
||||
{Username, Encodings} ->
|
||||
{result,
|
||||
@@ -376,15 +370,15 @@ get_form(Host, From, [], Lang) ->
|
||||
]}]}
|
||||
end;
|
||||
|
||||
get_form(_Host, _, _, Lang) ->
|
||||
|
||||
get_form(_, _, Lang) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
|
||||
|
||||
|
||||
set_form(Host, From, [], Lang, XData) ->
|
||||
set_form(From, [], Lang, XData) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||
US = {LUser, LServer},
|
||||
case {lists:keysearch("username", 1, XData),
|
||||
lists:keysearch("encodings", 1, XData)} of
|
||||
{{value, {_, [Username]}}, {value, {_, Strings}}} ->
|
||||
@@ -398,8 +392,8 @@ set_form(Host, From, [], Lang, XData) ->
|
||||
case mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:write(
|
||||
#irc_custom{us_host =
|
||||
{US, Host},
|
||||
#irc_custom{userserver =
|
||||
{LUser, LServer},
|
||||
data =
|
||||
[{username,
|
||||
Username},
|
||||
@@ -422,15 +416,14 @@ set_form(Host, From, [], Lang, XData) ->
|
||||
end;
|
||||
|
||||
|
||||
set_form(_Host, _, _, Lang, XData) ->
|
||||
set_form(_, _, Lang, XData) ->
|
||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||
|
||||
|
||||
get_user_and_encoding(Host, From, IRCServer) ->
|
||||
get_user_and_encoding(From, IRCServer) ->
|
||||
#jid{user = User, server = Server,
|
||||
luser = LUser, lserver = LServer} = From,
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
||||
case catch mnesia:dirty_read({irc_custom, {LUser, LServer}}) of
|
||||
{'EXIT', Reason} ->
|
||||
{User, ?DEFAULT_IRC_ENCODING};
|
||||
[] ->
|
||||
@@ -443,44 +436,3 @@ get_user_and_encoding(Host, From, IRCServer) ->
|
||||
end}
|
||||
end.
|
||||
|
||||
|
||||
update_table(Host) ->
|
||||
Fields = record_info(fields, irc_custom),
|
||||
case mnesia:table_info(irc_custom, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[userserver, data] ->
|
||||
?INFO_MSG("Converting irc_custom table from "
|
||||
"{userserver, data} format", []),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_irc_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, irc_custom},
|
||||
{attributes, record_info(fields, irc_custom)}]),
|
||||
mnesia:transform_table(irc_custom, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_irc_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#irc_custom{us_host = US} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_irc_tmp_table,
|
||||
R#irc_custom{us_host = {US, Host}})
|
||||
end, ok, irc_custom)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(irc_custom),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(irc_custom),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_irc_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_irc_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating irc_custom table", []),
|
||||
mnesia:transform_table(irc_custom, ignore, Fields)
|
||||
end.
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
-define(SETS, gb_sets).
|
||||
|
||||
-record(state, {socket, encoding, receiver, queue,
|
||||
user, host, server, nick,
|
||||
user, myname, server, nick,
|
||||
channels = dict:new(),
|
||||
inbuf = "", outbuf = ""}).
|
||||
|
||||
@@ -67,7 +67,7 @@ init([From, Host, Server, Username, Encoding]) ->
|
||||
encoding = Encoding,
|
||||
user = From,
|
||||
nick = Username,
|
||||
host = Host,
|
||||
myname = Host,
|
||||
server = Server}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@@ -90,7 +90,7 @@ open_socket(init, StateData) ->
|
||||
"USER ~s ~s ~s :~s\r\n",
|
||||
[StateData#state.nick,
|
||||
StateData#state.nick,
|
||||
StateData#state.host,
|
||||
StateData#state.myname,
|
||||
StateData#state.nick])),
|
||||
send_text(NewStateData,
|
||||
io_lib:format("CODEPAGE ~s\r\n", [StateData#state.encoding])),
|
||||
@@ -231,7 +231,7 @@ handle_info({route_chan, Channel, Resource,
|
||||
jlib:make_jid(
|
||||
lists:concat(
|
||||
[Channel, "%", StateData#state.server]),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
StateData#state.user, El),
|
||||
Body = xml:get_path_s(El, [{elem, "body"}, cdata]),
|
||||
case Body of
|
||||
@@ -303,8 +303,8 @@ handle_info({route_chan, Channel, Resource,
|
||||
{xmlelement, "iq", Attrs, Els} = El},
|
||||
StateName, StateData) ->
|
||||
From = StateData#state.user,
|
||||
To = jlib:make_jid(lists:concat([Channel, "%", StateData#state.server]),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
To = {lists:concat([Channel, "%", StateData#state.server]),
|
||||
StateData#state.myname, StateData#state.nick},
|
||||
case jlib:iq_query_info(El) of
|
||||
#iq{xmlns = ?NS_MUC_ADMIN} = IQ ->
|
||||
iq_admin(StateData, Channel, From, To, IQ);
|
||||
@@ -322,13 +322,6 @@ handle_info({route_chan, Channel, Resource,
|
||||
Err = jlib:make_error_reply(
|
||||
El, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
#iq{xmlns = ?NS_VCARD} ->
|
||||
Res = io_lib:format("WHOIS ~s \r\n",
|
||||
[Resource]),
|
||||
?SEND(Res),
|
||||
Err = jlib:make_error_reply(
|
||||
El, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
#iq{} ->
|
||||
Err = jlib:make_error_reply(
|
||||
El, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||
@@ -397,18 +390,6 @@ handle_info({ircstring, [$: | String]}, StateName, StateData) ->
|
||||
[_, "332", Nick, [$# | Chan] | _] ->
|
||||
process_channel_topic(StateData, Chan, String),
|
||||
StateData;
|
||||
[_, "318", _, Nick | _] ->
|
||||
process_endofwhois(StateData, String, Nick),
|
||||
StateData;
|
||||
[_, "311", _, Nick, Ident, Irchost | _ ] ->
|
||||
process_whois311(StateData, String, Nick, Ident, Irchost),
|
||||
StateData;
|
||||
[_, "312", _, Nick, Ircserver | _ ] ->
|
||||
process_whois312(StateData, String, Nick, Ircserver),
|
||||
StateData;
|
||||
[_, "319", _, Nick | _ ] ->
|
||||
process_whois319(StateData, String, Nick),
|
||||
StateData;
|
||||
[From, "PRIVMSG", [$# | Chan] | _] ->
|
||||
process_chanprivmsg(StateData, Chan, From, String),
|
||||
StateData;
|
||||
@@ -418,9 +399,6 @@ handle_info({ircstring, [$: | String]}, StateName, StateData) ->
|
||||
[From, "PRIVMSG", Nick, ":\001VERSION\001" | _] ->
|
||||
process_version(StateData, Nick, From),
|
||||
StateData;
|
||||
[From, "PRIVMSG", Nick, ":\001USERINFO\001" | _] ->
|
||||
process_userinfo(StateData, Nick, From),
|
||||
StateData;
|
||||
[From, "PRIVMSG", Nick | _] ->
|
||||
process_privmsg(StateData, Nick, From, String),
|
||||
StateData;
|
||||
@@ -496,16 +474,15 @@ handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StateData) ->
|
||||
mod_irc:closed_connection(StateData#state.host,
|
||||
StateData#state.user,
|
||||
StateData#state.server),
|
||||
mod_irc:closed_conection(StateData#state.user,
|
||||
StateData#state.server),
|
||||
bounce_messages("Server Connect Failed"),
|
||||
lists:foreach(
|
||||
fun(Chan) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "error"}],
|
||||
[{xmlelement, "error", [{"code", "502"}],
|
||||
@@ -615,7 +592,7 @@ process_channel_list_user(StateData, Chan, User) ->
|
||||
end,
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, User2),
|
||||
StateData#state.myname, User2),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@@ -637,60 +614,25 @@ process_channel_list_user(StateData, Chan, User) ->
|
||||
process_channel_topic(StateData, Chan, String) ->
|
||||
FromUser = "someone",
|
||||
{ok, Msg, _} = regexp:sub(String, ".*332[^:]*:", ""),
|
||||
Msg1 = filter_message(Msg),
|
||||
Msg1 = lists:filter(
|
||||
fun(C) ->
|
||||
if (C < 32) and
|
||||
(C /= 9) and
|
||||
(C /= 10) and
|
||||
(C /= 13) ->
|
||||
false;
|
||||
true -> true
|
||||
end
|
||||
end, Msg),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "subject", [], [{xmlcdata, Msg1}]}]}).
|
||||
|
||||
|
||||
process_endofwhois(StateData, String, Nick) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Nick, "!", StateData#state.server]),
|
||||
StateData#state.host, ""),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, "End of WHOIS"}]}]}).
|
||||
|
||||
process_whois311(StateData, String, Nick, Ident, Irchost) ->
|
||||
{ok, Fullname, _} = regexp:sub(String, ".*311[^:]*:", ""),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Nick, "!", StateData#state.server]),
|
||||
StateData#state.host, ""),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [],
|
||||
[{xmlcdata, lists:concat(
|
||||
["WHOIS: ", Nick, " is ",
|
||||
Ident, "@" , Irchost, " : " , Fullname])}]}]}).
|
||||
|
||||
process_whois312(StateData, String, Nick, Ircserver) ->
|
||||
{ok, Ircserverdesc, _} = regexp:sub(String, ".*312[^:]*:", ""),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Nick, "!", StateData#state.server]),
|
||||
StateData#state.host, ""),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [],
|
||||
[{xmlcdata, lists:concat(["WHOIS: ", Nick, " use ",
|
||||
Ircserver, " : ", Ircserverdesc])}]}]}).
|
||||
|
||||
process_whois319(StateData, String, Nick) ->
|
||||
{ok, Chanlist, _} = regexp:sub(String, ".*319[^:]*:", ""),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Nick, "!", StateData#state.server]),
|
||||
StateData#state.host, ""),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [],
|
||||
[{xmlcdata, lists:concat(["WHOIS: ", Nick, " is on ",
|
||||
Chanlist])}]}]}).
|
||||
|
||||
|
||||
|
||||
process_chanprivmsg(StateData, Chan, From, String) ->
|
||||
[FromUser | _] = string:tokens(From, "!"),
|
||||
{ok, Msg, _} = regexp:sub(String, ".*PRIVMSG[^:]*:", ""),
|
||||
@@ -700,10 +642,19 @@ process_chanprivmsg(StateData, Chan, From, String) ->
|
||||
_ ->
|
||||
Msg
|
||||
end,
|
||||
Msg2 = filter_message(Msg1),
|
||||
Msg2 = lists:filter(
|
||||
fun(C) ->
|
||||
if (C < 32) and
|
||||
(C /= 9) and
|
||||
(C /= 10) and
|
||||
(C /= 13) ->
|
||||
false;
|
||||
true -> true
|
||||
end
|
||||
end, Msg1),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
|
||||
@@ -719,10 +670,19 @@ process_channotice(StateData, Chan, From, String) ->
|
||||
_ ->
|
||||
Msg
|
||||
end,
|
||||
Msg2 = filter_message(Msg1),
|
||||
Msg2 = lists:filter(
|
||||
fun(C) ->
|
||||
if (C < 32) and
|
||||
(C /= 9) and
|
||||
(C /= 10) and
|
||||
(C /= 13) ->
|
||||
false;
|
||||
true -> true
|
||||
end
|
||||
end, Msg1),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, "NOTICE: " ++ Msg2}]}]}).
|
||||
@@ -739,10 +699,19 @@ process_privmsg(StateData, Nick, From, String) ->
|
||||
_ ->
|
||||
Msg
|
||||
end,
|
||||
Msg2 = filter_message(Msg1),
|
||||
Msg2 = lists:filter(
|
||||
fun(C) ->
|
||||
if (C < 32) and
|
||||
(C /= 9) and
|
||||
(C /= 10) and
|
||||
(C /= 13) ->
|
||||
false;
|
||||
true -> true
|
||||
end
|
||||
end, Msg1),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([FromUser, "!", StateData#state.server]),
|
||||
StateData#state.host, ""),
|
||||
StateData#state.myname, ""),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
|
||||
@@ -757,10 +726,19 @@ process_notice(StateData, Nick, From, String) ->
|
||||
_ ->
|
||||
Msg
|
||||
end,
|
||||
Msg2 = filter_message(Msg1),
|
||||
Msg2 = lists:filter(
|
||||
fun(C) ->
|
||||
if (C < 32) and
|
||||
(C /= 9) and
|
||||
(C /= 10) and
|
||||
(C /= 13) ->
|
||||
false;
|
||||
true -> true
|
||||
end
|
||||
end, Msg1),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([FromUser, "!", StateData#state.server]),
|
||||
StateData#state.host, ""),
|
||||
StateData#state.myname, ""),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, "NOTICE: " ++ Msg2}]}]}).
|
||||
@@ -780,24 +758,22 @@ process_version(StateData, Nick, From) ->
|
||||
[FromUser])).
|
||||
|
||||
|
||||
process_userinfo(StateData, Nick, From) ->
|
||||
[FromUser | _] = string:tokens(From, "!"),
|
||||
send_text(
|
||||
StateData,
|
||||
io_lib:format("NOTICE ~s :\001USERINFO "
|
||||
"This user uses xmpp:~s"
|
||||
"\001\r\n",
|
||||
[FromUser,
|
||||
jlib:jid_to_string(StateData#state.user)])).
|
||||
|
||||
|
||||
process_topic(StateData, Chan, From, String) ->
|
||||
[FromUser | _] = string:tokens(From, "!"),
|
||||
{ok, Msg, _} = regexp:sub(String, ".*TOPIC[^:]*:", ""),
|
||||
Msg1 = filter_message(Msg),
|
||||
Msg1 = lists:filter(
|
||||
fun(C) ->
|
||||
if (C < 32) and
|
||||
(C /= 9) and
|
||||
(C /= 10) and
|
||||
(C /= 13) ->
|
||||
false;
|
||||
true -> true
|
||||
end
|
||||
end, Msg),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "subject", [], [{xmlcdata, Msg1}]},
|
||||
@@ -808,19 +784,18 @@ process_topic(StateData, Chan, From, String) ->
|
||||
process_part(StateData, Chan, From, String) ->
|
||||
[FromUser | FromIdent] = string:tokens(From, "!"),
|
||||
{ok, Msg, _} = regexp:sub(String, ".*PART[^:]*", ""),
|
||||
Msg1 = filter_message(Msg),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [],
|
||||
[{xmlcdata, "/me has part: " ++
|
||||
Msg1 ++ "(" ++ FromIdent ++ ")" }]}]}),
|
||||
Msg ++ "(" ++ FromIdent ++ ")" }]}]}),
|
||||
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "unavailable"}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@@ -829,7 +804,7 @@ process_part(StateData, Chan, From, String) ->
|
||||
{"role", "none"}],
|
||||
[]}]},
|
||||
{xmlelement, "status", [],
|
||||
[{xmlcdata, Msg1 ++ "(" ++ FromIdent ++ ")"}]}]
|
||||
[{xmlcdata, Msg ++ "(" ++ FromIdent ++ ")"}]}]
|
||||
}),
|
||||
case catch dict:update(Chan,
|
||||
fun(Ps) ->
|
||||
@@ -846,7 +821,6 @@ process_quit(StateData, From, String) ->
|
||||
[FromUser | FromIdent] = string:tokens(From, "!"),
|
||||
|
||||
{ok, Msg, _} = regexp:sub(String, ".*QUIT[^:]*:", ""),
|
||||
Msg1 = filter_message(Msg),
|
||||
NewChans =
|
||||
dict:map(
|
||||
fun(Chan, Ps) ->
|
||||
@@ -855,17 +829,17 @@ process_quit(StateData, From, String) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [],
|
||||
[{xmlcdata, "/me has quit: " ++
|
||||
Msg1 ++ "(" ++ FromIdent ++ ")" }]}]}),
|
||||
Msg ++ "(" ++ FromIdent ++ ")" }]}]}),
|
||||
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "unavailable"}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@@ -874,7 +848,7 @@ process_quit(StateData, From, String) ->
|
||||
{"role", "none"}],
|
||||
[]}]},
|
||||
{xmlelement, "status", [],
|
||||
[{xmlcdata, Msg1 ++ "(" ++ FromIdent ++ ")"}]}
|
||||
[{xmlcdata, Msg ++ "(" ++ FromIdent ++ ")"}]}
|
||||
]}),
|
||||
remove_element(FromUser, Ps);
|
||||
_ ->
|
||||
@@ -889,7 +863,7 @@ process_join(StateData, Channel, From, String) ->
|
||||
Chan = lists:subtract(Channel, ":#"),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@@ -900,15 +874,14 @@ process_join(StateData, Channel, From, String) ->
|
||||
{xmlelement, "status", [],
|
||||
[{xmlcdata, FromIdent}]}]}),
|
||||
{ok, Msg, _} = regexp:sub(String, ".*JOIN[^:]*:", ""),
|
||||
Msg1 = filter_message(Msg),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "message", [{"type", "groupchat"}],
|
||||
[{xmlelement, "body", [],
|
||||
[{xmlcdata, "/me has joined " ++
|
||||
Msg1 ++ "(" ++ FromIdent ++ ")" }]}]}),
|
||||
Msg ++ "(" ++ FromIdent ++ ")" }]}]}),
|
||||
|
||||
case catch dict:update(Chan,
|
||||
fun(Ps) ->
|
||||
@@ -926,7 +899,7 @@ process_mode_o(StateData, Chan, From, Nick, Affiliation, Role) ->
|
||||
%Msg = lists:last(string:tokens(String, ":")),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, Nick),
|
||||
StateData#state.myname, Nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@@ -939,7 +912,7 @@ process_kick(StateData, Chan, From, Nick) ->
|
||||
%Msg = lists:last(string:tokens(String, ":")),
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, Nick),
|
||||
StateData#state.myname, Nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "unavailable"}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@@ -961,7 +934,7 @@ process_nick(StateData, From, NewNick) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, FromUser),
|
||||
StateData#state.myname, FromUser),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "unavailable"}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@@ -975,7 +948,7 @@ process_nick(StateData, From, NewNick) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, Nick),
|
||||
StateData#state.myname, Nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
@@ -999,7 +972,7 @@ process_error(StateData, String) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
StateData#state.myname, StateData#state.nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "error"}],
|
||||
[{xmlelement, "error", [{"code", "502"}],
|
||||
@@ -1085,14 +1058,7 @@ process_admin(StateData, Channel, Nick, Affiliation, Role, Reason) ->
|
||||
|
||||
|
||||
|
||||
filter_message(Msg) ->
|
||||
lists:filter(
|
||||
fun(C) ->
|
||||
if (C < 32) and
|
||||
(C /= 9) and
|
||||
(C /= 10) and
|
||||
(C /= 13) ->
|
||||
false;
|
||||
true -> true
|
||||
end
|
||||
end, Msg).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
stop/0,
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
on_presence_update/4,
|
||||
store_last_info/4,
|
||||
remove_user/2]).
|
||||
on_presence_update/2,
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(last_activity, {us, timestamp, status}).
|
||||
-record(last_activity, {user, timestamp, status}).
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
@@ -35,17 +34,9 @@ start(Opts) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_LAST,
|
||||
?MODULE, process_local_iq, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_LAST,
|
||||
?MODULE, process_sm_iq, IQDisc),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:add(unset_presence_hook,
|
||||
?MODULE, on_presence_update, 50).
|
||||
?MODULE, process_sm_iq, IQDisc).
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:delete(unset_presence_hook,
|
||||
?MODULE, on_presence_update, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_LAST),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_LAST).
|
||||
|
||||
@@ -69,25 +60,23 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
User = To#jid.luser,
|
||||
Server = To#jid.lserver,
|
||||
{Subscription, _Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []}, [User, Server, From]),
|
||||
mod_roster:get_jid_info(User, From),
|
||||
if
|
||||
(Subscription == both) or (Subscription == from) ->
|
||||
case catch mod_privacy:get_user_list(User, Server) of
|
||||
case catch mod_privacy:get_user_list(User) of
|
||||
{'EXIT', _Reason} ->
|
||||
get_last(IQ, SubEl, User, Server);
|
||||
get_last(IQ, SubEl, User);
|
||||
List ->
|
||||
case catch mod_privacy:check_packet(
|
||||
User, Server, List,
|
||||
User, List,
|
||||
{From, To,
|
||||
{xmlelement, "presence", [], []}},
|
||||
out) of
|
||||
{'EXIT', _Reason} ->
|
||||
get_last(IQ, SubEl, User, Server);
|
||||
get_last(IQ, SubEl, User);
|
||||
allow ->
|
||||
get_last(IQ, SubEl, User, Server);
|
||||
get_last(IQ, SubEl, User);
|
||||
deny ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
@@ -99,8 +88,8 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
end
|
||||
end.
|
||||
|
||||
get_last(IQ, SubEl, LUser, LServer) ->
|
||||
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
||||
get_last(IQ, SubEl, LUser) ->
|
||||
case catch mnesia:dirty_read(last_activity, LUser) of
|
||||
{'EXIT', _Reason} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
|
||||
[] ->
|
||||
@@ -118,29 +107,21 @@ get_last(IQ, SubEl, LUser, LServer) ->
|
||||
|
||||
|
||||
|
||||
on_presence_update(User, Server, _Resource, Status) ->
|
||||
on_presence_update(LUser, Status) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp = MegaSecs * 1000000 + Secs,
|
||||
store_last_info(User, Server, TimeStamp, Status).
|
||||
|
||||
store_last_info(User, Server, TimeStamp, Status) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:write(#last_activity{us = US,
|
||||
mnesia:write(#last_activity{user = LUser,
|
||||
timestamp = TimeStamp,
|
||||
status = Status})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
remove_user(User, Server) ->
|
||||
|
||||
remove_user(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({last_activity, US})
|
||||
mnesia:delete({last_activity, LUser})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
@@ -150,44 +131,13 @@ update_table() ->
|
||||
case mnesia:table_info(last_activity, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, timestamp, status] ->
|
||||
?INFO_MSG("Converting last_activity table from {user, timestamp, status} format", []),
|
||||
Host = ?MYNAME,
|
||||
mnesia:transform_table(last_activity, ignore, Fields),
|
||||
F = fun() ->
|
||||
mnesia:write_lock_table(last_activity),
|
||||
mnesia:foldl(
|
||||
fun({_, U, T, S} = R, _) ->
|
||||
mnesia:delete_object(R),
|
||||
mnesia:write(
|
||||
#last_activity{us = {U, Host},
|
||||
timestamp = T,
|
||||
status = S})
|
||||
end, ok, last_activity)
|
||||
end,
|
||||
mnesia:transaction(F);
|
||||
[user, timestamp] ->
|
||||
?INFO_MSG("Converting last_activity table from {user, timestamp} format", []),
|
||||
Host = ?MYNAME,
|
||||
mnesia:transform_table(
|
||||
last_activity,
|
||||
fun({_, U, T}) ->
|
||||
#last_activity{us = U,
|
||||
timestamp = T,
|
||||
status = ""}
|
||||
end, Fields),
|
||||
F = fun() ->
|
||||
mnesia:write_lock_table(last_activity),
|
||||
mnesia:foldl(
|
||||
fun({_, U, T, S} = R, _) ->
|
||||
mnesia:delete_object(R),
|
||||
mnesia:write(
|
||||
#last_activity{us = {U, Host},
|
||||
timestamp = T,
|
||||
status = S})
|
||||
end, ok, last_activity)
|
||||
end,
|
||||
mnesia:transaction(F);
|
||||
#last_activity{user = U, timestamp = T, status = ""}
|
||||
end, Fields);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating last_activity table", []),
|
||||
mnesia:transform_table(last_activity, ignore, Fields)
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_last_odbc.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : jabber:iq:last support (JEP-0012)
|
||||
%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_last_odbc).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
on_presence_update/4,
|
||||
store_last_info/4,
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_LAST,
|
||||
?MODULE, process_local_iq, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_LAST,
|
||||
?MODULE, process_sm_iq, IQDisc),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:add(unset_presence_hook,
|
||||
?MODULE, on_presence_update, 50).
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:delete(unset_presence_hook,
|
||||
?MODULE, on_presence_update, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_LAST),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_LAST).
|
||||
|
||||
process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_LAST},
|
||||
{"seconds", integer_to_list(Sec)}],
|
||||
[]}]}
|
||||
end.
|
||||
|
||||
|
||||
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
User = To#jid.luser,
|
||||
Server = To#jid.lserver,
|
||||
{Subscription, _Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []}, [User, From]),
|
||||
if
|
||||
(Subscription == both) or (Subscription == from) ->
|
||||
case catch mod_privacy:get_user_list(User, Server) of
|
||||
{'EXIT', _Reason} ->
|
||||
get_last(IQ, SubEl, User);
|
||||
List ->
|
||||
case catch mod_privacy:check_packet(
|
||||
User, ?MYNAME, List, % TODO
|
||||
{From, To,
|
||||
{xmlelement, "presence", [], []}},
|
||||
out) of
|
||||
{'EXIT', _Reason} ->
|
||||
get_last(IQ, SubEl, User);
|
||||
allow ->
|
||||
get_last(IQ, SubEl, User);
|
||||
deny ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end
|
||||
end;
|
||||
true ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end
|
||||
end.
|
||||
|
||||
get_last(IQ, SubEl, LUser) ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select seconds, state from last "
|
||||
"where username='", Username, "'"]) of
|
||||
{'EXIT', _Reason} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
|
||||
{selected, ["seconds","state"], []} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
|
||||
{selected, ["seconds","state"], [{STimeStamp, Status}]} ->
|
||||
case catch list_to_integer(STimeStamp) of
|
||||
TimeStamp when is_integer(TimeStamp) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp2 = MegaSecs * 1000000 + Secs,
|
||||
Sec = TimeStamp2 - TimeStamp,
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_LAST},
|
||||
{"seconds", integer_to_list(Sec)}],
|
||||
[{xmlcdata, Status}]}]};
|
||||
_ ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
|
||||
on_presence_update(User, Server, _Resource, Status) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
TimeStamp = MegaSecs * 1000000 + Secs,
|
||||
store_last_info(User, Server, TimeStamp, Status).
|
||||
|
||||
store_last_info(User, Server, TimeStamp, Status) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
|
||||
State = ejabberd_odbc:escape(Status),
|
||||
ejabberd_odbc:sql_query(
|
||||
["begin;"
|
||||
"delete from last where username='", Username, "';"
|
||||
"insert into last(username, seconds, state) "
|
||||
"values ('", Username, "', '", Seconds, "', '", State, "');",
|
||||
"commit"]).
|
||||
|
||||
|
||||
remove_user(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
ejabberd_odbc:sql_query(
|
||||
["delete from last where username='", Username, "'"]).
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -17,7 +22,8 @@ OBJS = \
|
||||
all: $(OBJS)
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
@@ -15,22 +15,21 @@
|
||||
-export([start/1,
|
||||
init/2,
|
||||
stop/0,
|
||||
room_destroyed/2,
|
||||
store_room/3,
|
||||
restore_room/2,
|
||||
forget_room/2,
|
||||
room_destroyed/1,
|
||||
store_room/2,
|
||||
restore_room/1,
|
||||
forget_room/1,
|
||||
process_iq_disco_items/4,
|
||||
can_use_nick/3]).
|
||||
can_use_nick/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
-record(muc_room, {name_host, opts}).
|
||||
-record(muc_online_room, {name_host, pid}).
|
||||
-record(muc_registered, {us_host, nick}).
|
||||
-record(muc_room, {name, opts}).
|
||||
-record(muc_online_room, {name, pid}).
|
||||
-record(muc_registered, {user, nick}).
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_muc).
|
||||
|
||||
start(Opts) ->
|
||||
mnesia:create_table(muc_room,
|
||||
@@ -39,45 +38,43 @@ start(Opts) ->
|
||||
mnesia:create_table(muc_registered,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, muc_registered)}]),
|
||||
Hosts = gen_mod:get_hosts(Opts, "conference."),
|
||||
Host = hd(Hosts),
|
||||
update_tables(Host),
|
||||
mnesia:add_table_index(muc_registered, nick),
|
||||
Host = gen_mod:get_opt(host, Opts, "conference." ++ ?MYNAME),
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
AccessCreate = gen_mod:get_opt(access_create, Opts, all),
|
||||
AccessAdmin = gen_mod:get_opt(access_admin, Opts, none),
|
||||
register(?PROCNAME,
|
||||
spawn(?MODULE, init,
|
||||
[Hosts, {Access, AccessCreate, AccessAdmin}])).
|
||||
register(ejabberd_mod_muc,
|
||||
spawn(?MODULE, init, [Host, {Access, AccessCreate, AccessAdmin}])).
|
||||
|
||||
|
||||
|
||||
init(Hosts, Access) ->
|
||||
init(Host, Access) ->
|
||||
catch ets:new(muc_online_room, [named_table,
|
||||
public,
|
||||
{keypos, #muc_online_room.name_host}]),
|
||||
ejabberd_router:register_routes(Hosts),
|
||||
load_permanent_rooms(Access),
|
||||
loop(Hosts, Access).
|
||||
{keypos, #muc_online_room.name}]),
|
||||
ejabberd_router:register_route(Host),
|
||||
load_permanent_rooms(Host, Access),
|
||||
loop(Host, Access).
|
||||
|
||||
loop(Hosts, Access) ->
|
||||
loop(Host, Access) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(To#jid.lserver, Access, From, To, Packet) of
|
||||
case catch do_route(Host, Access, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Hosts, Access);
|
||||
{room_destroyed, RoomHost} ->
|
||||
ets:delete(muc_online_room, RoomHost),
|
||||
loop(Hosts, Access);
|
||||
loop(Host, Access);
|
||||
{room_destroyed, Room} ->
|
||||
ets:delete(muc_online_room, Room),
|
||||
loop(Host, Access);
|
||||
stop ->
|
||||
ejabberd_router:unregister_routes(Hosts),
|
||||
% TODO
|
||||
ejabberd_router:unregister_global_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Hosts, Access)
|
||||
loop(Host, Access)
|
||||
end.
|
||||
|
||||
|
||||
@@ -130,7 +127,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", XMLNS}],
|
||||
iq_get_register_info(
|
||||
Host, From, Lang)}]},
|
||||
From, Host, Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(Res));
|
||||
@@ -138,7 +135,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
xmlns = ?NS_REGISTER = XMLNS,
|
||||
lang = Lang,
|
||||
sub_el = SubEl} = IQ ->
|
||||
case process_iq_register_set(Host, From, SubEl, Lang) of
|
||||
case process_iq_register_set(From, SubEl, Lang) of
|
||||
{result, IQRes} ->
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el =
|
||||
@@ -183,7 +180,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
Msg = xml:get_path_s(
|
||||
Packet,
|
||||
[{elem, "body"}, cdata]),
|
||||
broadcast_service_message(Host, Msg);
|
||||
broadcast_service_message(Msg);
|
||||
_ ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Only service administrators "
|
||||
@@ -211,7 +208,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
case ets:lookup(muc_online_room, {Room, Host}) of
|
||||
case ets:lookup(muc_online_room, Room) of
|
||||
[] ->
|
||||
Type = xml:get_attr_s("type", Attrs),
|
||||
case {Name, Type} of
|
||||
@@ -223,8 +220,7 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
Host, Access, Room, From, Nick),
|
||||
ets:insert(
|
||||
muc_online_room,
|
||||
#muc_online_room{name_host = {Room, Host},
|
||||
pid = Pid}),
|
||||
#muc_online_room{name = Room, pid = Pid}),
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
ok;
|
||||
_ ->
|
||||
@@ -252,24 +248,24 @@ do_route1(Host, Access, From, To, Packet) ->
|
||||
|
||||
|
||||
|
||||
room_destroyed(Host, Room) ->
|
||||
?PROCNAME ! {room_destroyed, {Room, Host}},
|
||||
room_destroyed(Room) ->
|
||||
ejabberd_mod_muc ! {room_destroyed, Room},
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
?PROCNAME ! stop,
|
||||
{wait, ?PROCNAME}.
|
||||
ejabberd_mod_muc ! stop,
|
||||
ok.
|
||||
|
||||
|
||||
store_room(Host, Name, Opts) ->
|
||||
store_room(Name, Opts) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#muc_room{name_host = {Name, Host},
|
||||
mnesia:write(#muc_room{name = Name,
|
||||
opts = Opts})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
restore_room(Host, Name) ->
|
||||
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
|
||||
restore_room(Name) ->
|
||||
case catch mnesia:dirty_read(muc_room, Name) of
|
||||
[#muc_room{opts = Opts}] ->
|
||||
Opts;
|
||||
_ ->
|
||||
@@ -277,21 +273,21 @@ restore_room(Host, Name) ->
|
||||
end.
|
||||
|
||||
|
||||
forget_room(Host, Name) ->
|
||||
forget_room(Name) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({muc_room, {Name, Host}})
|
||||
mnesia:delete({muc_room, Name})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
load_permanent_rooms(Access) ->
|
||||
load_permanent_rooms(Host, Access) ->
|
||||
case catch mnesia:dirty_select(muc_room, [{'_', [], ['$_']}]) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
ok;
|
||||
Rs ->
|
||||
lists:foreach(fun(R) ->
|
||||
{Room, Host} = R#muc_room.name_host,
|
||||
Room = R#muc_room.name,
|
||||
{ok, Pid} = mod_muc_room:start(
|
||||
Host,
|
||||
Access,
|
||||
@@ -299,8 +295,7 @@ load_permanent_rooms(Access) ->
|
||||
R#muc_room.opts),
|
||||
ets:insert(
|
||||
muc_online_room,
|
||||
#muc_online_room{name_host = {Room, Host},
|
||||
pid = Pid})
|
||||
#muc_online_room{name = Room, pid = Pid})
|
||||
end, Rs)
|
||||
end.
|
||||
|
||||
@@ -325,7 +320,7 @@ process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
|
||||
jlib:iq_to_xml(Res)).
|
||||
|
||||
iq_disco_items(Host, From, Lang) ->
|
||||
lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
|
||||
lists:zf(fun(#muc_online_room{name = Name, pid = Pid}) ->
|
||||
case catch gen_fsm:sync_send_all_state_event(
|
||||
Pid, {get_disco_item, From, Lang}, 100) of
|
||||
{item, Desc} ->
|
||||
@@ -336,7 +331,7 @@ iq_disco_items(Host, From, Lang) ->
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, get_vh_rooms(Host)).
|
||||
end, ets:tab2list(muc_online_room)).
|
||||
|
||||
|
||||
-define(XFIELD(Type, Label, Var, Val),
|
||||
@@ -345,18 +340,17 @@ iq_disco_items(Host, From, Lang) ->
|
||||
{"var", Var}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
|
||||
|
||||
iq_get_register_info(Host, From, Lang) ->
|
||||
iq_get_register_info(From, Host, Lang) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||
LUS = {LUser, LServer},
|
||||
{Nick, Registered} =
|
||||
case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of
|
||||
{'EXIT', _Reason} ->
|
||||
{"", []};
|
||||
[] ->
|
||||
{"", []};
|
||||
[#muc_registered{nick = N}] ->
|
||||
{N, [{xmlelement, "registered", [], []}]}
|
||||
end,
|
||||
{Nick, Registered} = case catch mnesia:dirty_read(muc_registered, LUS) of
|
||||
{'EXIT', _Reason} ->
|
||||
{"", []};
|
||||
[] ->
|
||||
{"", []};
|
||||
[#muc_registered{nick = N}] ->
|
||||
{N, [{xmlelement, "registered", [], []}]}
|
||||
end,
|
||||
Registered ++
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata,
|
||||
@@ -374,7 +368,7 @@ iq_get_register_info(Host, From, Lang) ->
|
||||
Lang, "Enter nickname you want to register")}]},
|
||||
?XFIELD("text-single", "Nickname", "nick", Nick)]}].
|
||||
|
||||
iq_set_register_info(Host, From, XData, Lang) ->
|
||||
iq_set_register_info(From, XData, Lang) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||
LUS = {LUser, LServer},
|
||||
case lists:keysearch("nick", 1, XData) of
|
||||
@@ -385,26 +379,22 @@ iq_set_register_info(Host, From, XData, Lang) ->
|
||||
F = fun() ->
|
||||
case Nick of
|
||||
"" ->
|
||||
mnesia:delete({muc_registered, {LUS, Host}}),
|
||||
mnesia:delete({muc_registered, LUS}),
|
||||
ok;
|
||||
_ ->
|
||||
Allow =
|
||||
case mnesia:select(
|
||||
muc_registered,
|
||||
[{#muc_registered{us_host = '$1',
|
||||
nick = Nick,
|
||||
_ = '_'},
|
||||
[{'==', {element, 2, '$1'}, Host}],
|
||||
['$_']}]) of
|
||||
[] ->
|
||||
true;
|
||||
[#muc_registered{us_host = {U, _Host}}] ->
|
||||
U == LUS
|
||||
end,
|
||||
Allow = case mnesia:index_read(
|
||||
muc_registered,
|
||||
Nick,
|
||||
#muc_registered.nick) of
|
||||
[] ->
|
||||
true;
|
||||
[#muc_registered{user = U}] ->
|
||||
U == LUS
|
||||
end,
|
||||
if
|
||||
Allow ->
|
||||
mnesia:write(
|
||||
#muc_registered{us_host = {LUS, Host},
|
||||
#muc_registered{user = LUS,
|
||||
nick = Nick}),
|
||||
ok;
|
||||
true ->
|
||||
@@ -423,7 +413,7 @@ iq_set_register_info(Host, From, XData, Lang) ->
|
||||
end
|
||||
end.
|
||||
|
||||
process_iq_register_set(Host, From, SubEl, Lang) ->
|
||||
process_iq_register_set(From, SubEl, Lang) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
|
||||
@@ -437,7 +427,7 @@ process_iq_register_set(Host, From, SubEl, Lang) ->
|
||||
invalid ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
iq_set_register_info(Host, From, XData, Lang)
|
||||
iq_set_register_info(From, XData, Lang)
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
@@ -454,128 +444,33 @@ iq_get_vcard(Lang) ->
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, translate:translate(Lang, "ejabberd MUC module\n"
|
||||
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
|
||||
|
||||
broadcast_service_message(Host, Msg) ->
|
||||
broadcast_service_message(Msg) ->
|
||||
lists:foreach(
|
||||
fun(#muc_online_room{pid = Pid}) ->
|
||||
gen_fsm:send_all_state_event(
|
||||
Pid, {service_message, Msg})
|
||||
end, get_vh_rooms(Host)).
|
||||
|
||||
get_vh_rooms(Host) ->
|
||||
ets:select(muc_online_room,
|
||||
[{#muc_online_room{name_host = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, Host}],
|
||||
['$_']}]).
|
||||
end, ets:tab2list(muc_online_room)).
|
||||
|
||||
|
||||
can_use_nick(_Host, _JID, "") ->
|
||||
|
||||
can_use_nick(_JID, "") ->
|
||||
false;
|
||||
can_use_nick(Host, JID, Nick) ->
|
||||
can_use_nick(JID, Nick) ->
|
||||
{LUser, LServer, _} = jlib:jid_tolower(JID),
|
||||
LUS = {LUser, LServer},
|
||||
case catch mnesia:dirty_select(
|
||||
muc_registered,
|
||||
[{#muc_registered{us_host = '$1',
|
||||
nick = Nick,
|
||||
_ = '_'},
|
||||
[{'==', {element, 2, '$1'}, Host}],
|
||||
['$_']}]) of
|
||||
case catch mnesia:dirty_index_read(muc_registered,
|
||||
Nick,
|
||||
#muc_registered.nick) of
|
||||
{'EXIT', _Reason} ->
|
||||
true;
|
||||
[] ->
|
||||
true;
|
||||
[#muc_registered{us_host = {U, _Host}}] ->
|
||||
[#muc_registered{user = U}] ->
|
||||
U == LUS
|
||||
end.
|
||||
|
||||
|
||||
update_tables(Host) ->
|
||||
update_muc_room_table(Host),
|
||||
update_muc_registered_table(Host).
|
||||
|
||||
update_muc_room_table(Host) ->
|
||||
Fields = record_info(fields, muc_room),
|
||||
case mnesia:table_info(muc_room, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[name, opts] ->
|
||||
?INFO_MSG("Converting muc_room table from "
|
||||
"{name, opts} format", []),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_muc_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, muc_room},
|
||||
{attributes, record_info(fields, muc_room)}]),
|
||||
mnesia:transform_table(muc_room, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_muc_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#muc_room{name_host = Name} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_muc_tmp_table,
|
||||
R#muc_room{name_host = {Name, Host}})
|
||||
end, ok, muc_room)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(muc_room),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(muc_room),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_muc_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_muc_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating muc_room table", []),
|
||||
mnesia:transform_table(muc_room, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
update_muc_registered_table(Host) ->
|
||||
Fields = record_info(fields, muc_registered),
|
||||
case mnesia:table_info(muc_registered, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, nick] ->
|
||||
?INFO_MSG("Converting muc_registered table from "
|
||||
"{user, nick} format", []),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_muc_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, muc_registered},
|
||||
{attributes, record_info(fields, muc_registered)}]),
|
||||
mnesia:del_table_index(muc_registered, nick),
|
||||
mnesia:transform_table(muc_registered, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_muc_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#muc_registered{us_host = US} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_muc_tmp_table,
|
||||
R#muc_registered{us_host = {US, Host}})
|
||||
end, ok, muc_registered)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(muc_registered),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(muc_registered),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_muc_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_muc_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating muc_registered table", []),
|
||||
mnesia:transform_table(muc_registered, ignore, Fields)
|
||||
end.
|
||||
|
||||
@@ -149,7 +149,6 @@ normal_state({route, From, "",
|
||||
case (NSD#state.config)#config.persistent of
|
||||
true ->
|
||||
mod_muc:store_room(
|
||||
NSD#state.host,
|
||||
NSD#state.room,
|
||||
make_opts(NSD));
|
||||
_ ->
|
||||
@@ -178,23 +177,20 @@ normal_state({route, From, "",
|
||||
NewStateData1),
|
||||
{next_state, normal_state, NewStateData2};
|
||||
_ ->
|
||||
Err =
|
||||
ErrText =
|
||||
case (StateData#state.config)#config.allow_change_subj of
|
||||
true ->
|
||||
?ERRT_FORBIDDEN(
|
||||
Lang,
|
||||
"Only moderators and participants "
|
||||
"are allowed to change subject in this room");
|
||||
"Only moderators and participants "
|
||||
"are allowed to change subject in this room";
|
||||
_ ->
|
||||
?ERRT_FORBIDDEN(
|
||||
Lang,
|
||||
"Only moderators "
|
||||
"are allowed to change subject in this room")
|
||||
"Only moderators "
|
||||
"are allowed to change subject in this room"
|
||||
end,
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(
|
||||
StateData#state.jid,
|
||||
From,
|
||||
jlib:make_error_reply(Packet, Err)),
|
||||
From, Err),
|
||||
{next_state, normal_state, StateData}
|
||||
end;
|
||||
true ->
|
||||
@@ -249,15 +245,6 @@ normal_state({route, From, "",
|
||||
IJID,
|
||||
member,
|
||||
StateData),
|
||||
case (NSD#state.config)#config.persistent of
|
||||
true ->
|
||||
mod_muc:store_room(
|
||||
NSD#state.host,
|
||||
NSD#state.room,
|
||||
make_opts(NSD));
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{next_state, normal_state, NSD};
|
||||
_ ->
|
||||
{next_state, normal_state,
|
||||
@@ -377,8 +364,7 @@ normal_state({route, From, Nick,
|
||||
case is_nick_change(From, Nick, StateData) of
|
||||
true ->
|
||||
case {is_nick_exists(Nick, StateData),
|
||||
mod_muc:can_use_nick(
|
||||
StateData#state.host, From, Nick)} of
|
||||
mod_muc:can_use_nick(From, Nick)} of
|
||||
{true, _} ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
ErrText = "Nickname is already in use by another occupant",
|
||||
@@ -556,6 +542,7 @@ normal_state({route, From, ToNick,
|
||||
{next_state, normal_state, StateData};
|
||||
|
||||
normal_state(Event, StateData) ->
|
||||
io:format("MUC: unknown event ~p~n", [Event]),
|
||||
{next_state, normal_state, StateData}.
|
||||
|
||||
|
||||
@@ -652,7 +639,7 @@ handle_info(_Info, StateName, StateData) ->
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(_Reason, _StateName, StateData) ->
|
||||
mod_muc:room_destroyed(StateData#state.host, StateData#state.room),
|
||||
mod_muc:room_destroyed(StateData#state.room),
|
||||
ok.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -716,57 +703,19 @@ set_affiliation(JID, Affiliation, StateData) ->
|
||||
end,
|
||||
StateData#state{affiliations = Affiliations}.
|
||||
|
||||
set_affiliation_and_reason(JID, Affiliation, Reason, StateData) ->
|
||||
LJID = jlib:jid_remove_resource(jlib:jid_tolower(JID)),
|
||||
Affiliations = case Affiliation of
|
||||
none ->
|
||||
?DICT:erase(LJID,
|
||||
StateData#state.affiliations);
|
||||
_ ->
|
||||
?DICT:store(LJID,
|
||||
{Affiliation, Reason},
|
||||
StateData#state.affiliations)
|
||||
end,
|
||||
StateData#state{affiliations = Affiliations}.
|
||||
|
||||
get_affiliation(JID, StateData) ->
|
||||
{_AccessRoute, _AccessCreate, AccessAdmin} = StateData#state.access,
|
||||
Res =
|
||||
case acl:match_rule(AccessAdmin, JID) of
|
||||
allow ->
|
||||
owner;
|
||||
_ ->
|
||||
LJID = jlib:jid_tolower(JID),
|
||||
case ?DICT:find(LJID, StateData#state.affiliations) of
|
||||
{ok, Affiliation} ->
|
||||
Affiliation;
|
||||
_ ->
|
||||
LJID1 = jlib:jid_remove_resource(LJID),
|
||||
case ?DICT:find(LJID1, StateData#state.affiliations) of
|
||||
{ok, Affiliation} ->
|
||||
Affiliation;
|
||||
_ ->
|
||||
LJID2 = setelement(1, LJID, ""),
|
||||
case ?DICT:find(LJID2, StateData#state.affiliations) of
|
||||
{ok, Affiliation} ->
|
||||
Affiliation;
|
||||
_ ->
|
||||
LJID3 = jlib:jid_remove_resource(LJID2),
|
||||
case ?DICT:find(LJID3, StateData#state.affiliations) of
|
||||
{ok, Affiliation} ->
|
||||
Affiliation;
|
||||
_ ->
|
||||
none
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
case Res of
|
||||
{A, _Reason} ->
|
||||
A;
|
||||
case acl:match_rule(AccessAdmin, JID) of
|
||||
allow ->
|
||||
owner;
|
||||
_ ->
|
||||
Res
|
||||
LJID = jlib:jid_remove_resource(jlib:jid_tolower(JID)),
|
||||
case ?DICT:find(LJID, StateData#state.affiliations) of
|
||||
{ok, Affiliation} ->
|
||||
Affiliation;
|
||||
_ ->
|
||||
none
|
||||
end
|
||||
end.
|
||||
|
||||
set_role(JID, Role, StateData) ->
|
||||
@@ -857,8 +806,8 @@ filter_presence({xmlelement, "presence", Attrs, Els}) ->
|
||||
case El of
|
||||
{xmlcdata, _} ->
|
||||
false;
|
||||
{xmlelement, Name1, Attrs1, _Els1} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs1),
|
||||
{xmlelement, Name1, _Attrs1, _Els1} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||
case {Name1, XMLNS} of
|
||||
{"show", ""} ->
|
||||
true;
|
||||
@@ -923,7 +872,7 @@ is_nick_change(JID, Nick, StateData) ->
|
||||
add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
case {is_nick_exists(Nick, StateData),
|
||||
mod_muc:can_use_nick(StateData#state.host, From, Nick)} of
|
||||
mod_muc:can_use_nick(From, Nick)} of
|
||||
{true, _} ->
|
||||
ErrText = "Nickname is already in use by another occupant",
|
||||
Err = jlib:make_error_reply(Packet, ?ERRT_CONFLICT(Lang, ErrText)),
|
||||
@@ -1074,6 +1023,7 @@ count_stanza_shift(Nick, Els, StateData) ->
|
||||
_ ->
|
||||
count_maxchars_shift(Nick, MaxChars, HL)
|
||||
end,
|
||||
|
||||
lists:max([Shift0, Shift1, Shift2, Shift3]).
|
||||
|
||||
count_seconds_shift(Seconds, HistoryList) ->
|
||||
@@ -1129,18 +1079,23 @@ extract_history([{xmlelement, _Name, Attrs, _SubEls} = El | Els], Type) ->
|
||||
[{elem, "history"}, {attr, Type}]),
|
||||
case Type of
|
||||
"since" ->
|
||||
case jlib:datetime_string_to_timestamp(AttrVal) of
|
||||
undefined ->
|
||||
case catch parse_datetime(AttrVal) of
|
||||
{'EXIT', _Err} ->
|
||||
false;
|
||||
TS ->
|
||||
calendar:now_to_universal_time(TS)
|
||||
Res ->
|
||||
Res
|
||||
end;
|
||||
_ ->
|
||||
case catch list_to_integer(AttrVal) of
|
||||
IntVal when is_integer(IntVal) and (IntVal >= 0) ->
|
||||
IntVal;
|
||||
_ ->
|
||||
false
|
||||
{'EXIT', _} ->
|
||||
false;
|
||||
IntVal ->
|
||||
if
|
||||
IntVal >= 0 ->
|
||||
IntVal;
|
||||
true ->
|
||||
false
|
||||
end
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
@@ -1149,6 +1104,80 @@ extract_history([{xmlelement, _Name, Attrs, _SubEls} = El | Els], Type) ->
|
||||
extract_history([_ | Els], Type) ->
|
||||
extract_history(Els, Type).
|
||||
|
||||
% JEP-0082
|
||||
% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {{yyyy, mm, dd}, {hh, mm, ss}} (UTC)
|
||||
parse_datetime(TimeStr) ->
|
||||
[Date, Time] = string:tokens(TimeStr, "T"),
|
||||
D = parse_date(Date),
|
||||
{T, TZH, TZM} = parse_time(Time),
|
||||
S = calendar:datetime_to_gregorian_seconds({D, T}),
|
||||
calendar:gregorian_seconds_to_datetime(S - TZH * 60 * 60 - TZM * 60).
|
||||
|
||||
% yyyy-mm-dd
|
||||
parse_date(Date) ->
|
||||
YearMonthDay = string:tokens(Date, "-"),
|
||||
[Y, M, D] = lists:map(
|
||||
fun(L)->
|
||||
list_to_integer(L)
|
||||
end, YearMonthDay),
|
||||
{Y, M, D}.
|
||||
|
||||
% hh:mm:ss[.sss]TZD
|
||||
parse_time(Time) ->
|
||||
case string:str(Time, "Z") of
|
||||
0 ->
|
||||
parse_time_with_timezone(Time);
|
||||
_ ->
|
||||
[T | _] = string:tokens(Time, "Z"),
|
||||
{parse_time1(T), 0, 0}
|
||||
end.
|
||||
|
||||
parse_time_with_timezone(Time) ->
|
||||
case string:str(Time, "+") of
|
||||
0 ->
|
||||
case string:str(Time, "-") of
|
||||
0 ->
|
||||
false;
|
||||
_ ->
|
||||
parse_time_with_timezone(Time, "-")
|
||||
end;
|
||||
_ ->
|
||||
parse_time_with_timezone(Time, "+")
|
||||
end.
|
||||
|
||||
parse_time_with_timezone(Time, Delim) ->
|
||||
[T, TZ] = string:tokens(Time, Delim),
|
||||
{TZH, TZM} = parse_timezone(TZ),
|
||||
TT = parse_time1(T),
|
||||
case Delim of
|
||||
"-" ->
|
||||
{TT, -TZH, -TZM};
|
||||
"+" ->
|
||||
{TT, TZH, TZM}
|
||||
end.
|
||||
|
||||
parse_timezone(TZ) ->
|
||||
[H, M] = string:tokens(TZ, ":"),
|
||||
{[H1, M1], true} = check_list([{H, 12}, {M, 60}]),
|
||||
{H1, M1}.
|
||||
|
||||
parse_time1(Time) ->
|
||||
[HMS | _] = string:tokens(Time, "."),
|
||||
[H, M, S] = string:tokens(HMS, ":"),
|
||||
{[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]),
|
||||
{H1, M1, S1}.
|
||||
|
||||
check_list(List) ->
|
||||
lists:mapfoldl(
|
||||
fun({L, N}, B)->
|
||||
V = list_to_integer(L),
|
||||
if
|
||||
(V >= 0) and (V =< N) ->
|
||||
{V, B};
|
||||
true ->
|
||||
{false, false}
|
||||
end
|
||||
end, true, List).
|
||||
|
||||
send_update_presence(JID, StateData) ->
|
||||
LJID = jlib:jid_tolower(JID),
|
||||
@@ -1483,12 +1512,7 @@ items_with_role(SRole, StateData) ->
|
||||
|
||||
items_with_affiliation(SAffiliation, StateData) ->
|
||||
lists:map(
|
||||
fun({JID, {Affiliation, Reason}}) ->
|
||||
{xmlelement, "item",
|
||||
[{"affiliation", affiliation_to_list(Affiliation)},
|
||||
{"jid", jlib:jid_to_string(JID)}],
|
||||
[{xmlelement, "reason", [], [{xmlcdata, Reason}]}]};
|
||||
({JID, Affiliation}) ->
|
||||
fun({JID, Affiliation}) ->
|
||||
{xmlelement, "item",
|
||||
[{"affiliation", affiliation_to_list(Affiliation)},
|
||||
{"jid", jlib:jid_to_string(JID)}],
|
||||
@@ -1516,12 +1540,7 @@ search_role(Role, StateData) ->
|
||||
search_affiliation(Affiliation, StateData) ->
|
||||
lists:filter(
|
||||
fun({_, A}) ->
|
||||
case A of
|
||||
{A1, _Reason} ->
|
||||
Affiliation == A1;
|
||||
_ ->
|
||||
Affiliation == A
|
||||
end
|
||||
Affiliation == A
|
||||
end, ?DICT:to_list(StateData#state.affiliations)).
|
||||
|
||||
|
||||
@@ -1542,11 +1561,11 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
|
||||
{JID, affiliation, outcast, Reason} ->
|
||||
catch send_kickban_presence(
|
||||
JID, Reason, "301", SD),
|
||||
set_affiliation_and_reason(
|
||||
JID, outcast, Reason,
|
||||
set_affiliation(
|
||||
JID, outcast,
|
||||
set_role(JID, none, SD));
|
||||
{JID, affiliation, A, Reason} when
|
||||
(A == admin) or (A == owner) ->
|
||||
(A == admin) or (A == owner)->
|
||||
SD1 = set_affiliation(JID, A, SD),
|
||||
SD2 = set_role(JID, moderator, SD1),
|
||||
send_update_presence(JID, SD2),
|
||||
@@ -1568,17 +1587,17 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
|
||||
end
|
||||
) of
|
||||
{'EXIT', ErrReason} ->
|
||||
?ERROR_MSG("MUC ITEMS SET ERR: ~p~n",
|
||||
[ErrReason]),
|
||||
io:format("MUC ITEMS SET ERR: ~p~n",
|
||||
[ErrReason]),
|
||||
SD;
|
||||
NSD ->
|
||||
NSD
|
||||
end
|
||||
end, StateData, Res),
|
||||
io:format("MUC SET: ~p~n", [Res]),
|
||||
case (NSD#state.config)#config.persistent of
|
||||
true ->
|
||||
mod_muc:store_room(NSD#state.host, NSD#state.room,
|
||||
make_opts(NSD));
|
||||
mod_muc:store_room(NSD#state.room, make_opts(NSD));
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
@@ -1731,19 +1750,6 @@ can_change_ra(FAffiliation, FRole,
|
||||
affiliation, none)
|
||||
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
||||
true;
|
||||
can_change_ra(FAffiliation, FRole,
|
||||
outcast, TRole,
|
||||
affiliation, member)
|
||||
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
||||
true;
|
||||
can_change_ra(owner, FRole,
|
||||
outcast, TRole,
|
||||
affiliation, admin) ->
|
||||
true;
|
||||
can_change_ra(owner, FRole,
|
||||
outcast, TRole,
|
||||
affiliation, owner) ->
|
||||
true;
|
||||
can_change_ra(FAffiliation, FRole,
|
||||
none, TRole,
|
||||
affiliation, outcast)
|
||||
@@ -1782,11 +1788,15 @@ can_change_ra(owner, FRole,
|
||||
true;
|
||||
can_change_ra(owner, FRole,
|
||||
admin, TRole,
|
||||
affiliation, _Affiliation) ->
|
||||
affiliation, member) ->
|
||||
true;
|
||||
can_change_ra(owner, FRole,
|
||||
admin, TRole,
|
||||
affiliation, owner) ->
|
||||
true;
|
||||
can_change_ra(owner, FRole,
|
||||
owner, TRole,
|
||||
affiliation, _Affiliation) ->
|
||||
affiliation, admin) ->
|
||||
true;
|
||||
can_change_ra(FAffiliation, FRole,
|
||||
TAffiliation, TRole,
|
||||
@@ -1818,22 +1828,6 @@ can_change_ra(FAffiliation, FRole,
|
||||
role, moderator)
|
||||
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
||||
true;
|
||||
can_change_ra(FAffiliation, FRole,
|
||||
owner, moderator,
|
||||
role, visitor) ->
|
||||
false;
|
||||
can_change_ra(owner, FRole,
|
||||
TAffiliation, moderator,
|
||||
role, visitor) ->
|
||||
true;
|
||||
can_change_ra(FAffiliation, FRole,
|
||||
admin, moderator,
|
||||
role, visitor) ->
|
||||
false;
|
||||
can_change_ra(admin, FRole,
|
||||
TAffiliation, moderator,
|
||||
role, visitor) ->
|
||||
true;
|
||||
can_change_ra(FAffiliation, FRole,
|
||||
owner, moderator,
|
||||
role, participant) ->
|
||||
@@ -2056,10 +2050,7 @@ get_config(Lang, StateData) ->
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang, "You need an x:data capable client to configure room")}]},
|
||||
{xmlelement, "x", [{"xmlns", ?NS_XDATA},
|
||||
{"type", "form"}],
|
||||
Res}],
|
||||
StateData}.
|
||||
{xmlelement, "x", [{"xmlns", ?NS_XDATA}], Res}], StateData}.
|
||||
|
||||
|
||||
|
||||
@@ -2129,9 +2120,9 @@ change_config(Config, StateData) ->
|
||||
case {(StateData#state.config)#config.persistent,
|
||||
Config#config.persistent} of
|
||||
{_, true} ->
|
||||
mod_muc:store_room(NSD#state.host, NSD#state.room, make_opts(NSD));
|
||||
mod_muc:store_room(NSD#state.room, make_opts(NSD));
|
||||
{true, false} ->
|
||||
mod_muc:forget_room(NSD#state.host, NSD#state.room);
|
||||
mod_muc:forget_room(NSD#state.room);
|
||||
{false, false} ->
|
||||
ok
|
||||
end,
|
||||
@@ -2214,12 +2205,6 @@ destroy_room(DEls, StateData) ->
|
||||
Info#user.jid,
|
||||
Packet)
|
||||
end, ?DICT:to_list(StateData#state.users)),
|
||||
case (StateData#state.config)#config.persistent of
|
||||
true ->
|
||||
mod_muc:forget_room(StateData#state.host, StateData#state.room);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
{result, [], stop}.
|
||||
|
||||
|
||||
@@ -2331,14 +2316,7 @@ check_invitation(From, Els, StateData) ->
|
||||
jlib:jid_to_string(From)}],
|
||||
[{xmlelement, "reason", [],
|
||||
[{xmlcdata, Reason}]}]}],
|
||||
PasswdEl =
|
||||
case (StateData#state.config)#config.password_protected of
|
||||
true ->
|
||||
[{xmlelement, "password", [],
|
||||
[{xmlcdata, (StateData#state.config)#config.password}]}];
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
PasswdEl = [],
|
||||
Msg =
|
||||
{xmlelement, "message",
|
||||
[{"type", "normal"}],
|
||||
|
||||
@@ -15,34 +15,22 @@
|
||||
init/0,
|
||||
stop/0,
|
||||
store_packet/3,
|
||||
resend_offline_messages/2,
|
||||
pop_offline_messages/3,
|
||||
remove_expired_messages/0,
|
||||
resend_offline_messages/1,
|
||||
pop_offline_messages/1,
|
||||
remove_old_messages/1,
|
||||
remove_user/2]).
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(offline_msg, {us, timestamp, expire, from, to, packet}).
|
||||
-record(offline_msg, {user, timestamp, from, to, packet}).
|
||||
|
||||
-define(PROCNAME, ejabberd_offline).
|
||||
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
||||
|
||||
start(_) ->
|
||||
mnesia:create_table(offline_msg,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, offline_msg)}]),
|
||||
update_table(),
|
||||
ejabberd_hooks:add(offline_message_hook,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:add(offline_subscription_hook,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:add(resend_offline_messages_hook,
|
||||
?MODULE, pop_offline_messages, 50),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
register(?PROCNAME, spawn(?MODULE, init, [])).
|
||||
|
||||
init() ->
|
||||
@@ -52,14 +40,7 @@ loop() ->
|
||||
receive
|
||||
#offline_msg{} = Msg ->
|
||||
Msgs = receive_all([Msg]),
|
||||
Len = length(Msgs),
|
||||
F = fun() ->
|
||||
if
|
||||
Len >= ?OFFLINE_TABLE_LOCK_THRESHOLD ->
|
||||
mnesia:write_lock_table(offline_msg);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(fun(M) ->
|
||||
mnesia:write(M)
|
||||
end, Msgs)
|
||||
@@ -80,38 +61,23 @@ receive_all(Msgs) ->
|
||||
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(offline_message_hook,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:delete(offline_subscription_hook,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:delete(resend_offline_messages_hook,
|
||||
?MODULE, pop_offline_messages, 50),
|
||||
ejabberd_hooks:delete(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
exit(whereis(?PROCNAME), stop),
|
||||
{wait, ?PROCNAME}.
|
||||
ok.
|
||||
|
||||
store_packet(From, To, Packet) ->
|
||||
true = is_process_alive(whereis(?PROCNAME)),
|
||||
Type = xml:get_tag_attr_s("type", Packet),
|
||||
if
|
||||
(Type /= "error") and (Type /= "groupchat") ->
|
||||
case check_event(From, To, Packet) of
|
||||
true ->
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
TimeStamp = now(),
|
||||
{xmlelement, _Name, _Attrs, Els} = Packet,
|
||||
Expire = find_x_expire(TimeStamp, Els),
|
||||
?PROCNAME ! #offline_msg{us = {LUser, LServer},
|
||||
timestamp = TimeStamp,
|
||||
expire = Expire,
|
||||
from = From,
|
||||
to = To,
|
||||
packet = Packet},
|
||||
stop;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
true = Type /= "error" andalso Type /= "groupchat",
|
||||
case check_event(From, To, Packet) of
|
||||
true ->
|
||||
#jid{luser = LUser} = To,
|
||||
TimeStamp = now(),
|
||||
?PROCNAME ! #offline_msg{user = LUser,
|
||||
timestamp = TimeStamp,
|
||||
from = From,
|
||||
to = To,
|
||||
packet = Packet};
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
@@ -160,42 +126,12 @@ find_x_event([El | Els]) ->
|
||||
find_x_event(Els)
|
||||
end.
|
||||
|
||||
find_x_expire(_, []) ->
|
||||
never;
|
||||
find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
|
||||
find_x_expire(TimeStamp, Els);
|
||||
find_x_expire(TimeStamp, [El | Els]) ->
|
||||
case xml:get_tag_attr_s("xmlns", El) of
|
||||
?NS_EXPIRE ->
|
||||
case xml:get_tag_attr_s("seconds", El) of
|
||||
Val ->
|
||||
case catch list_to_integer(Val) of
|
||||
{'EXIT', _} ->
|
||||
never;
|
||||
Int when Int > 0 ->
|
||||
{MegaSecs, Secs, MicroSecs} = TimeStamp,
|
||||
S = MegaSecs * 1000000 + Secs + Int,
|
||||
MegaSecs1 = S div 1000000,
|
||||
Secs1 = S rem 1000000,
|
||||
{MegaSecs1, Secs1, MicroSecs};
|
||||
_ ->
|
||||
never
|
||||
end;
|
||||
_ ->
|
||||
never
|
||||
end;
|
||||
_ ->
|
||||
find_x_expire(TimeStamp, Els)
|
||||
end.
|
||||
|
||||
|
||||
resend_offline_messages(User, Server) ->
|
||||
resend_offline_messages(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
Rs = mnesia:wread({offline_msg, US}),
|
||||
mnesia:delete({offline_msg, US}),
|
||||
Rs = mnesia:wread({offline_msg, LUser}),
|
||||
mnesia:delete({offline_msg, LUser}),
|
||||
Rs
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
@@ -218,65 +154,32 @@ resend_offline_messages(User, Server) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
pop_offline_messages(Ls, User, Server) ->
|
||||
pop_offline_messages(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
Rs = mnesia:wread({offline_msg, US}),
|
||||
mnesia:delete({offline_msg, US}),
|
||||
Rs = mnesia:wread({offline_msg, LUser}),
|
||||
mnesia:delete({offline_msg, LUser}),
|
||||
Rs
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Rs} ->
|
||||
TS = now(),
|
||||
Ls ++ lists:map(
|
||||
fun(R) ->
|
||||
{xmlelement, Name, Attrs, Els} = R#offline_msg.packet,
|
||||
{route,
|
||||
R#offline_msg.from,
|
||||
R#offline_msg.to,
|
||||
{xmlelement, Name, Attrs,
|
||||
Els ++
|
||||
[jlib:timestamp_to_xml(
|
||||
calendar:now_to_universal_time(
|
||||
R#offline_msg.timestamp))]}}
|
||||
end,
|
||||
lists:filter(
|
||||
fun(R) ->
|
||||
case R#offline_msg.expire of
|
||||
never ->
|
||||
true;
|
||||
TimeStamp ->
|
||||
TS < TimeStamp
|
||||
end
|
||||
end,
|
||||
lists:keysort(#offline_msg.timestamp, Rs)));
|
||||
lists:map(
|
||||
fun(R) ->
|
||||
{xmlelement, Name, Attrs, Els} = R#offline_msg.packet,
|
||||
{route,
|
||||
R#offline_msg.from,
|
||||
R#offline_msg.to,
|
||||
{xmlelement, Name, Attrs,
|
||||
Els ++
|
||||
[jlib:timestamp_to_xml(
|
||||
calendar:now_to_universal_time(
|
||||
R#offline_msg.timestamp))]}}
|
||||
end,
|
||||
lists:keysort(#offline_msg.timestamp, Rs));
|
||||
_ ->
|
||||
Ls
|
||||
[]
|
||||
end.
|
||||
|
||||
remove_expired_messages() ->
|
||||
TimeStamp = now(),
|
||||
F = fun() ->
|
||||
mnesia:write_lock_table(offline_msg),
|
||||
mnesia:foldl(
|
||||
fun(Rec, _Acc) ->
|
||||
case Rec#offline_msg.expire of
|
||||
never ->
|
||||
ok;
|
||||
TS ->
|
||||
if
|
||||
TS < TimeStamp ->
|
||||
mnesia:delete_object(Rec);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end
|
||||
end, ok, offline_msg)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
remove_old_messages(Days) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
S = MegaSecs * 1000000 + Secs - 60 * 60 * 24 * Days,
|
||||
@@ -284,7 +187,6 @@ remove_old_messages(Days) ->
|
||||
Secs1 = S rem 1000000,
|
||||
TimeStamp = {MegaSecs1, Secs1, 0},
|
||||
F = fun() ->
|
||||
mnesia:write_lock_table(offline_msg),
|
||||
mnesia:foldl(
|
||||
fun(#offline_msg{timestamp = TS} = Rec, _Acc)
|
||||
when TS < TimeStamp ->
|
||||
@@ -294,97 +196,9 @@ remove_old_messages(Days) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
remove_user(User, Server) ->
|
||||
remove_user(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({offline_msg, US})
|
||||
mnesia:delete({offline_msg, LUser})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, offline_msg),
|
||||
case mnesia:table_info(offline_msg, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, timestamp, expire, from, to, packet] ->
|
||||
?INFO_MSG("Converting offline_msg table from "
|
||||
"{user, timestamp, expire, from, to, packet} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_offline_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, offline_msg},
|
||||
{attributes, record_info(fields, offline_msg)}]),
|
||||
mnesia:transform_table(offline_msg, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_offline_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#offline_msg{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_offline_tmp_table,
|
||||
R#offline_msg{us = {U, Host}})
|
||||
end, ok, offline_msg)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(offline_msg),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(offline_msg),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_offline_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_offline_tmp_table);
|
||||
[user, timestamp, from, to, packet] ->
|
||||
?INFO_MSG("Converting offline_msg table from "
|
||||
"{user, timestamp, from, to, packet} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_offline_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, offline_msg},
|
||||
{attributes, record_info(fields, offline_msg)}]),
|
||||
mnesia:transform_table(
|
||||
offline_msg,
|
||||
fun({_, U, TS, F, T, P}) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = P,
|
||||
Expire = find_x_expire(TS, Els),
|
||||
#offline_msg{us = U,
|
||||
timestamp = TS,
|
||||
expire = Expire,
|
||||
from = F,
|
||||
to = T,
|
||||
packet = P}
|
||||
end, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_offline_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#offline_msg{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_offline_tmp_table,
|
||||
R#offline_msg{us = {U, Host}})
|
||||
end, ok, offline_msg)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(offline_msg),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(offline_msg),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_offline_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_offline_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating offline_msg table", []),
|
||||
mnesia:transform_table(offline_msg, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_offline_odbc.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_offline_odbc).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
init/0,
|
||||
stop/0,
|
||||
store_packet/3,
|
||||
pop_offline_messages/2,
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(offline_msg, {user, timestamp, expire, from, to, packet}).
|
||||
|
||||
-define(PROCNAME, ejabberd_offline).
|
||||
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
||||
|
||||
start(_) ->
|
||||
% TODO: remove
|
||||
ejabberd_odbc:start(),
|
||||
ejabberd_hooks:add(offline_message_hook,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:add(offline_subscription_hook,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:add(resend_offline_messages_hook,
|
||||
?MODULE, pop_offline_messages, 50),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
register(?PROCNAME, spawn(?MODULE, init, [])).
|
||||
|
||||
init() ->
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
receive
|
||||
#offline_msg{} = Msg ->
|
||||
Msgs = receive_all([Msg]),
|
||||
% TODO
|
||||
Query = lists:map(
|
||||
fun(M) ->
|
||||
Username =
|
||||
ejabberd_odbc:escape(
|
||||
(M#offline_msg.to)#jid.luser),
|
||||
From = M#offline_msg.from,
|
||||
To = M#offline_msg.to,
|
||||
{xmlelement, Name, Attrs, Els} =
|
||||
M#offline_msg.packet,
|
||||
Attrs2 = jlib:replace_from_to_attrs(
|
||||
jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
Packet = {xmlelement, Name, Attrs2,
|
||||
Els ++
|
||||
[jlib:timestamp_to_xml(
|
||||
calendar:now_to_universal_time(
|
||||
M#offline_msg.timestamp))]},
|
||||
XML =
|
||||
ejabberd_odbc:escape(
|
||||
lists:flatten(
|
||||
xml:element_to_string(Packet))),
|
||||
["insert into spool(username, xml) "
|
||||
"values ('", Username, "', '",
|
||||
XML,
|
||||
"');"]
|
||||
end, Msgs),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["begin; ", Query, " commit"]) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~n", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop();
|
||||
_ ->
|
||||
loop()
|
||||
end.
|
||||
|
||||
receive_all(Msgs) ->
|
||||
receive
|
||||
#offline_msg{} = Msg ->
|
||||
receive_all([Msg | Msgs])
|
||||
after 0 ->
|
||||
Msgs
|
||||
end.
|
||||
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(offline_message_hook,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:delete(offline_subscription_hook,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:delete(resend_offline_messages_hook,
|
||||
?MODULE, pop_offline_messages, 50),
|
||||
ejabberd_hooks:delete(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
exit(whereis(?PROCNAME), stop),
|
||||
ok.
|
||||
|
||||
store_packet(From, To, Packet) ->
|
||||
Type = xml:get_tag_attr_s("type", Packet),
|
||||
if
|
||||
(Type /= "error") and (Type /= "groupchat") ->
|
||||
case check_event(From, To, Packet) of
|
||||
true ->
|
||||
#jid{luser = LUser} = To,
|
||||
TimeStamp = now(),
|
||||
{xmlelement, _Name, _Attrs, Els} = Packet,
|
||||
Expire = find_x_expire(TimeStamp, Els),
|
||||
?PROCNAME ! #offline_msg{user = LUser,
|
||||
timestamp = TimeStamp,
|
||||
expire = Expire,
|
||||
from = From,
|
||||
to = To,
|
||||
packet = Packet},
|
||||
stop;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
|
||||
check_event(From, To, Packet) ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
case find_x_event(Els) of
|
||||
false ->
|
||||
true;
|
||||
El ->
|
||||
case xml:get_subtag(El, "id") of
|
||||
false ->
|
||||
case xml:get_subtag(El, "offline") of
|
||||
false ->
|
||||
true;
|
||||
_ ->
|
||||
ID = case xml:get_tag_attr_s("id", Packet) of
|
||||
"" ->
|
||||
{xmlelement, "id", [], []};
|
||||
S ->
|
||||
{xmlelement, "id", [],
|
||||
[{xmlcdata, S}]}
|
||||
end,
|
||||
ejabberd_router:route(
|
||||
To, From, {xmlelement, Name, Attrs,
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_EVENT}],
|
||||
[ID,
|
||||
{xmlelement, "offline", [], []}]}]
|
||||
}),
|
||||
true
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
find_x_event([]) ->
|
||||
false;
|
||||
find_x_event([{xmlcdata, _} | Els]) ->
|
||||
find_x_event(Els);
|
||||
find_x_event([El | Els]) ->
|
||||
case xml:get_tag_attr_s("xmlns", El) of
|
||||
?NS_EVENT ->
|
||||
El;
|
||||
_ ->
|
||||
find_x_event(Els)
|
||||
end.
|
||||
|
||||
find_x_expire(_, []) ->
|
||||
never;
|
||||
find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
|
||||
find_x_expire(TimeStamp, Els);
|
||||
find_x_expire(TimeStamp, [El | Els]) ->
|
||||
case xml:get_tag_attr_s("xmlns", El) of
|
||||
?NS_EXPIRE ->
|
||||
case xml:get_tag_attr_s("seconds", El) of
|
||||
Val ->
|
||||
case catch list_to_integer(Val) of
|
||||
{'EXIT', _} ->
|
||||
never;
|
||||
Int when Int > 0 ->
|
||||
{MegaSecs, Secs, MicroSecs} = TimeStamp,
|
||||
S = MegaSecs * 1000000 + Secs + Int,
|
||||
MegaSecs1 = S div 1000000,
|
||||
Secs1 = S rem 1000000,
|
||||
{MegaSecs1, Secs1, MicroSecs};
|
||||
_ ->
|
||||
never
|
||||
end;
|
||||
_ ->
|
||||
never
|
||||
end;
|
||||
_ ->
|
||||
find_x_expire(TimeStamp, Els)
|
||||
end.
|
||||
|
||||
|
||||
pop_offline_messages(Ls, User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
EUser = ejabberd_odbc:escape(LUser),
|
||||
case ejabberd_odbc:sql_query(
|
||||
["begin;"
|
||||
"select * from spool where username='", EUser, "';"
|
||||
"delete from spool where username='", EUser, "';"
|
||||
"commit"]) of
|
||||
{selected, ["username","xml"], Rs} ->
|
||||
Ls ++ lists:flatmap(
|
||||
fun({_, XML}) ->
|
||||
case xml_stream:parse_element(XML) of
|
||||
{error, _Reason} ->
|
||||
[];
|
||||
El ->
|
||||
To = jlib:string_to_jid(
|
||||
xml:get_tag_attr_s("to", El)),
|
||||
From = jlib:string_to_jid(
|
||||
xml:get_tag_attr_s("from", El)),
|
||||
if
|
||||
(To /= error) and
|
||||
(From /= error) ->
|
||||
[{route, From, To, El}];
|
||||
true ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
end, Rs);
|
||||
_ ->
|
||||
Ls
|
||||
end.
|
||||
|
||||
|
||||
remove_user(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
ejabberd_odbc:sql_query(
|
||||
["delete from spool where username='", Username, "'"]).
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
process_iq/3,
|
||||
process_iq_set/3,
|
||||
process_iq_get/4,
|
||||
get_user_list/2,
|
||||
check_packet/5,
|
||||
get_user_list/1,
|
||||
check_packet/4,
|
||||
updated_list/2]).
|
||||
|
||||
%-include_lib("mnemosyne/include/mnemosyne.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(privacy, {us,
|
||||
-record(privacy, {user,
|
||||
default = none,
|
||||
lists = []}).
|
||||
|
||||
@@ -46,7 +46,6 @@ start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
mnesia:create_table(privacy, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, privacy)}]),
|
||||
update_table(),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_PRIVACY,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
@@ -79,16 +78,16 @@ process_iq(From, _To, IQ) ->
|
||||
|
||||
process_iq_get(From, _To, #iq{sub_el = SubEl},
|
||||
#userlist{name = Active}) ->
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
#jid{luser = LUser} = From,
|
||||
{xmlelement, _, _, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[] ->
|
||||
process_lists_get(LUser, LServer, Active);
|
||||
process_lists_get(LUser, Active);
|
||||
[{xmlelement, Name, Attrs, _SubEls}] ->
|
||||
case Name of
|
||||
"list" ->
|
||||
ListName = xml:get_attr("name", Attrs),
|
||||
process_list_get(LUser, LServer, ListName);
|
||||
process_list_get(LUser, ListName);
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
@@ -97,8 +96,8 @@ process_iq_get(From, _To, #iq{sub_el = SubEl},
|
||||
end.
|
||||
|
||||
|
||||
process_lists_get(LUser, LServer, Active) ->
|
||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||
process_lists_get(LUser, Active) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
{'EXIT', _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
[] ->
|
||||
@@ -136,8 +135,8 @@ process_lists_get(LUser, LServer, Active) ->
|
||||
end
|
||||
end.
|
||||
|
||||
process_list_get(LUser, LServer, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||
process_list_get(LUser, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
{'EXIT', _Reason} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
[] ->
|
||||
@@ -156,7 +155,7 @@ process_list_get(LUser, LServer, {value, Name}) ->
|
||||
end
|
||||
end;
|
||||
|
||||
process_list_get(_LUser, _LServer, false) ->
|
||||
process_list_get(_LUser, false) ->
|
||||
{error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
item_to_xml(Item) ->
|
||||
@@ -243,19 +242,18 @@ list_to_action(S) ->
|
||||
|
||||
|
||||
process_iq_set(From, _To, #iq{sub_el = SubEl}) ->
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
#jid{luser = LUser} = From,
|
||||
{xmlelement, _, _, Els} = SubEl,
|
||||
case xml:remove_cdata(Els) of
|
||||
[{xmlelement, Name, Attrs, SubEls}] ->
|
||||
ListName = xml:get_attr("name", Attrs),
|
||||
case Name of
|
||||
"list" ->
|
||||
process_list_set(LUser, LServer, ListName,
|
||||
xml:remove_cdata(SubEls));
|
||||
process_list_set(LUser, ListName, xml:remove_cdata(SubEls));
|
||||
"active" ->
|
||||
process_active_set(LUser, LServer, ListName);
|
||||
process_active_set(LUser, ListName);
|
||||
"default" ->
|
||||
process_default_set(LUser, LServer, ListName);
|
||||
process_default_set(LUser, ListName);
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
@@ -264,9 +262,9 @@ process_iq_set(From, _To, #iq{sub_el = SubEl}) ->
|
||||
end.
|
||||
|
||||
|
||||
process_default_set(LUser, LServer, {value, Name}) ->
|
||||
process_default_set(LUser, {value, Name}) ->
|
||||
F = fun() ->
|
||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||
case mnesia:read({privacy, LUser}) of
|
||||
[] ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
[#privacy{lists = Lists} = P] ->
|
||||
@@ -289,9 +287,9 @@ process_default_set(LUser, LServer, {value, Name}) ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end;
|
||||
|
||||
process_default_set(LUser, LServer, false) ->
|
||||
process_default_set(LUser, false) ->
|
||||
F = fun() ->
|
||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||
case mnesia:read({privacy, LUser}) of
|
||||
[] ->
|
||||
{result, []};
|
||||
[R] ->
|
||||
@@ -309,8 +307,8 @@ process_default_set(LUser, LServer, false) ->
|
||||
end.
|
||||
|
||||
|
||||
process_active_set(LUser, LServer, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||
process_active_set(LUser, {value, Name}) ->
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
[] ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
[#privacy{lists = Lists}] ->
|
||||
@@ -322,7 +320,7 @@ process_active_set(LUser, LServer, {value, Name}) ->
|
||||
end
|
||||
end;
|
||||
|
||||
process_active_set(_LUser, _LServer, false) ->
|
||||
process_active_set(_LUser, false) ->
|
||||
{result, [], #userlist{}}.
|
||||
|
||||
|
||||
@@ -330,14 +328,14 @@ process_active_set(_LUser, _LServer, false) ->
|
||||
|
||||
|
||||
|
||||
process_list_set(LUser, LServer, {value, Name}, Els) ->
|
||||
process_list_set(LUser, {value, Name}, Els) ->
|
||||
case parse_items(Els) of
|
||||
false ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
remove ->
|
||||
F =
|
||||
fun() ->
|
||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||
case mnesia:read({privacy, LUser}) of
|
||||
[] ->
|
||||
{result, []};
|
||||
[#privacy{default = Default, lists = Lists} = P] ->
|
||||
@@ -370,10 +368,10 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
|
||||
List ->
|
||||
F =
|
||||
fun() ->
|
||||
case mnesia:wread({privacy, {LUser, LServer}}) of
|
||||
case mnesia:wread({privacy, LUser}) of
|
||||
[] ->
|
||||
NewLists = [{Name, List}],
|
||||
mnesia:write(#privacy{us = {LUser, LServer},
|
||||
mnesia:write(#privacy{user = LUser,
|
||||
lists = NewLists}),
|
||||
{result, []};
|
||||
[#privacy{lists = Lists} = P] ->
|
||||
@@ -398,7 +396,7 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
|
||||
end
|
||||
end;
|
||||
|
||||
process_list_set(_LUser, _LServer, false, _Els) ->
|
||||
process_list_set(_LUser, false, _Els) ->
|
||||
{error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
|
||||
@@ -513,10 +511,9 @@ parse_matches1(_Item, [{xmlelement, _, _, _} | _Els]) ->
|
||||
|
||||
|
||||
|
||||
get_user_list(User, Server) ->
|
||||
get_user_list(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||
case catch mnesia:dirty_read(privacy, LUser) of
|
||||
[] ->
|
||||
#userlist{};
|
||||
[#privacy{default = Default, lists = Lists}] ->
|
||||
@@ -537,7 +534,7 @@ get_user_list(User, Server) ->
|
||||
end.
|
||||
|
||||
|
||||
check_packet(User, Server,
|
||||
check_packet(User,
|
||||
#userlist{list = List},
|
||||
{From, To, {xmlelement, PName, _, _}},
|
||||
Dir) ->
|
||||
@@ -554,33 +551,25 @@ check_packet(User, Server,
|
||||
{message, in} ->
|
||||
LJID = jlib:jid_tolower(From),
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []},
|
||||
[User, Server, LJID]),
|
||||
mod_roster:get_jid_info(User, LJID),
|
||||
check_packet_aux(List, message,
|
||||
LJID, Subscription, Groups);
|
||||
{iq, in} ->
|
||||
LJID = jlib:jid_tolower(From),
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []},
|
||||
[User, Server, LJID]),
|
||||
mod_roster:get_jid_info(User, LJID),
|
||||
check_packet_aux(List, iq,
|
||||
LJID, Subscription, Groups);
|
||||
{presence, in} ->
|
||||
LJID = jlib:jid_tolower(From),
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []},
|
||||
[User, Server, LJID]),
|
||||
mod_roster:get_jid_info(User, LJID),
|
||||
check_packet_aux(List, presence_in,
|
||||
LJID, Subscription, Groups);
|
||||
{presence, out} ->
|
||||
LJID = jlib:jid_tolower(To),
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, {none, []},
|
||||
[User, Server, LJID]),
|
||||
mod_roster:get_jid_info(User, LJID),
|
||||
check_packet_aux(List, presence_out,
|
||||
LJID, Subscription, Groups);
|
||||
_ ->
|
||||
@@ -669,46 +658,4 @@ updated_list(#userlist{name = OldName} = Old,
|
||||
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, privacy),
|
||||
case mnesia:table_info(privacy, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, default, lists] ->
|
||||
?INFO_MSG("Converting privacy table from "
|
||||
"{user, default, lists} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_privacy_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, privacy},
|
||||
{attributes, record_info(fields, privacy)}]),
|
||||
mnesia:transform_table(privacy, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_privacy_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#privacy{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_privacy_tmp_table,
|
||||
R#privacy{us = {U, Host}})
|
||||
end, ok, privacy)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(privacy),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(privacy),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_privacy_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_privacy_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating privacy table", []),
|
||||
mnesia:transform_table(privacy, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
@@ -15,48 +15,43 @@
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
process_sm_iq/3,
|
||||
remove_user/2]).
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(private_storage, {usns, xml}).
|
||||
-record(private_storage, {userns, xml}).
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
mnesia:create_table(private_storage,
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes, record_info(fields, private_storage)}]),
|
||||
update_table(),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_PRIVATE,
|
||||
?MODULE, process_sm_iq, IQDisc).
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_PRIVATE).
|
||||
|
||||
|
||||
process_sm_iq(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
{xmlelement, Name, Attrs, Els} = SubEl,
|
||||
case Type of
|
||||
set ->
|
||||
F = fun() ->
|
||||
lists:foreach(
|
||||
fun(El) ->
|
||||
set_data(LUser, LServer, El)
|
||||
set_data(LUser, El)
|
||||
end, Els)
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, Name, Attrs, []}]};
|
||||
get ->
|
||||
case catch get_data(LUser, LServer, Els) of
|
||||
case catch get_data(LUser, Els) of
|
||||
{'EXIT', _Reason} ->
|
||||
IQ#iq{type = error,
|
||||
sub_el = [SubEl,
|
||||
@@ -66,11 +61,11 @@ process_sm_iq(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
sub_el = [{xmlelement, Name, Attrs, Res}]}
|
||||
end
|
||||
end;
|
||||
false ->
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end.
|
||||
|
||||
set_data(LUser, LServer, El) ->
|
||||
set_data(LUser, El) ->
|
||||
case El of
|
||||
{xmlelement, _Name, Attrs, _Els} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||
@@ -78,45 +73,40 @@ set_data(LUser, LServer, El) ->
|
||||
"" ->
|
||||
ignore;
|
||||
_ ->
|
||||
mnesia:write(
|
||||
#private_storage{usns = {LUser, LServer, XMLNS},
|
||||
xml = El})
|
||||
mnesia:write(#private_storage{userns = {LUser, XMLNS},
|
||||
xml = El})
|
||||
end;
|
||||
_ ->
|
||||
ignore
|
||||
end.
|
||||
|
||||
get_data(LUser, LServer, Els) ->
|
||||
get_data(LUser, LServer, Els, []).
|
||||
get_data(LUser, Els) ->
|
||||
get_data(LUser, Els, []).
|
||||
|
||||
get_data(_LUser, _LServer, [], Res) ->
|
||||
get_data(_LUser, [], Res) ->
|
||||
lists:reverse(Res);
|
||||
get_data(LUser, LServer, [El | Els], Res) ->
|
||||
get_data(LUser, [El | Els], Res) ->
|
||||
case El of
|
||||
{xmlelement, _Name, Attrs, _} ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||
case mnesia:dirty_read(private_storage, {LUser, LServer, XMLNS}) of
|
||||
case mnesia:dirty_read(private_storage, {LUser, XMLNS}) of
|
||||
[R] ->
|
||||
get_data(LUser, LServer, Els,
|
||||
[R#private_storage.xml | Res]);
|
||||
get_data(LUser, Els, [R#private_storage.xml | Res]);
|
||||
[] ->
|
||||
get_data(LUser, LServer, Els,
|
||||
[El | Res])
|
||||
get_data(LUser, Els, [El | Res])
|
||||
end;
|
||||
_ ->
|
||||
get_data(LUser, LServer, Els, Res)
|
||||
get_data(LUser, Els, Res)
|
||||
end.
|
||||
|
||||
|
||||
% TODO: use mnesia:select
|
||||
remove_user(User, Server) ->
|
||||
remove_user(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
F = fun() ->
|
||||
lists:foreach(
|
||||
fun({U, S, _} = Key) ->
|
||||
fun({U, _} = Key) ->
|
||||
if
|
||||
(U == LUser) and (S == LServer) ->
|
||||
U == LUser ->
|
||||
mnesia:delete({private_storage, Key});
|
||||
true ->
|
||||
ok
|
||||
@@ -125,47 +115,3 @@ remove_user(User, Server) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, private_storage),
|
||||
case mnesia:table_info(private_storage, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[userns, xml] ->
|
||||
?INFO_MSG("Converting private_storage table from "
|
||||
"{user, default, lists} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_private_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, private_storage},
|
||||
{attributes, record_info(fields, private_storage)}]),
|
||||
mnesia:transform_table(private_storage, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_private_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#private_storage{usns = {U, NS}} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_private_tmp_table,
|
||||
R#private_storage{usns = {U, Host, NS}})
|
||||
end, ok, private_storage)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(private_storage),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(private_storage),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_private_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_private_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating private_storage table", []),
|
||||
mnesia:transform_table(private_storage, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ @ERLANG_CFLAGS@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@ @ERLANG_LIBS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
@@ -16,7 +21,8 @@ OBJS = \
|
||||
all: $(OBJS)
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
-define(DICT, dict).
|
||||
-define(MAXITEMS, 10).
|
||||
|
||||
-record(pubsub_node, {host_node, host_parent, info}).
|
||||
-record(pubsub_node, {node, parent, info}).
|
||||
-record(nodeinfo, {items = [],
|
||||
options = [],
|
||||
entities = ?DICT:new()
|
||||
@@ -35,60 +35,53 @@
|
||||
subscription = none}).
|
||||
-record(item, {id, publisher, payload}).
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_pubsub).
|
||||
|
||||
start(Opts) ->
|
||||
mnesia:create_table(pubsub_node,
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes, record_info(fields, pubsub_node)}]),
|
||||
Hosts = gen_mod:get_hosts(Opts, "pubsub."),
|
||||
Host = hd(Hosts),
|
||||
update_table(Host),
|
||||
mnesia:add_table_index(pubsub_node, host_parent),
|
||||
ServedHosts = gen_mod:get_opt(served_hosts, Opts, []),
|
||||
register(?PROCNAME,
|
||||
proc_lib:spawn_link(?MODULE, init, [Hosts, ServedHosts, self()])).
|
||||
mnesia:add_table_index(pubsub_node, parent),
|
||||
Host = gen_mod:get_opt(host, Opts, "pubsub." ++ ?MYNAME),
|
||||
ServedHosts = gen_mod:get_opt(served_hosts, Opts, [?MYNAME]),
|
||||
register(ejabberd_mod_pubsub,
|
||||
proc_lib:spawn_link(?MODULE, init, [Host, ServedHosts, self()])).
|
||||
|
||||
|
||||
-define(MYJID, #jid{user = "", server = Host, resource = "",
|
||||
luser = "", lserver = Host, lresource = ""}).
|
||||
|
||||
init(Hosts, ServedHosts, Parent) ->
|
||||
ejabberd_router:register_routes(Hosts),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
create_new_node(Host, ["pubsub"], ?MYJID),
|
||||
create_new_node(Host, ["pubsub", "nodes"], ?MYJID),
|
||||
create_new_node(Host, ["home"], ?MYJID),
|
||||
create_new_node(Host, ["home", find_my_host(Host)], ?MYJID),
|
||||
lists:foreach(fun(H) ->
|
||||
create_new_node(Host, ["home", H], ?MYJID)
|
||||
end, ServedHosts)
|
||||
end, Hosts),
|
||||
loop(Hosts, Parent).
|
||||
init(Host, ServedHosts, Parent) ->
|
||||
ejabberd_router:register_route(Host),
|
||||
create_new_node(Host, ["pubsub"], ?MYJID),
|
||||
create_new_node(Host, ["pubsub", "nodes"], ?MYJID),
|
||||
create_new_node(Host, ["home"], ?MYJID),
|
||||
lists:foreach(fun(H) ->
|
||||
create_new_node(Host, ["home", H], ?MYJID)
|
||||
end, ServedHosts),
|
||||
loop(Host, Parent).
|
||||
|
||||
loop(Hosts, Parent) ->
|
||||
loop(Host, Parent) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(To#jid.lserver, From, To, Packet) of
|
||||
case catch do_route(Host, From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Hosts, Parent);
|
||||
loop(Host, Parent);
|
||||
{room_destroyed, Room} ->
|
||||
ets:delete(muc_online_room, Room),
|
||||
loop(Hosts, Parent);
|
||||
loop(Host, Parent);
|
||||
stop ->
|
||||
ejabberd_router:unregister_routes(Hosts),
|
||||
ejabberd_router:unregister_global_route(Host),
|
||||
ok;
|
||||
reload ->
|
||||
?MODULE:loop(Hosts, Parent);
|
||||
?MODULE:loop(Host, Parent);
|
||||
{system, From, Request} ->
|
||||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], Hosts);
|
||||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], Host);
|
||||
_ ->
|
||||
loop(Hosts, Parent)
|
||||
loop(Host, Parent)
|
||||
end.
|
||||
|
||||
|
||||
@@ -203,8 +196,8 @@ do_route(Host, From, To, Packet) ->
|
||||
|
||||
|
||||
stop() ->
|
||||
?PROCNAME ! stop,
|
||||
{wait, ?PROCNAME}.
|
||||
ejabberd_mod_pubsub ! stop,
|
||||
ok.
|
||||
|
||||
|
||||
|
||||
@@ -235,13 +228,13 @@ iq_disco_info(SNode) ->
|
||||
iq_disco_items(Host, From, SNode) ->
|
||||
Node = string:tokens(SNode, "/"),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
SubNodes = mnesia:index_read(pubsub_node,
|
||||
{Host, Node},
|
||||
#pubsub_node.host_parent),
|
||||
Node,
|
||||
#pubsub_node.parent),
|
||||
SubItems =
|
||||
lists:map(fun(#pubsub_node{host_node = {_, N}}) ->
|
||||
lists:map(fun(#pubsub_node{node = N}) ->
|
||||
SN = node_to_string(N),
|
||||
{xmlelement, "item",
|
||||
[{"jid", Host},
|
||||
@@ -262,10 +255,10 @@ iq_disco_items(Host, From, SNode) ->
|
||||
[] ->
|
||||
SubNodes = mnesia:index_read(
|
||||
pubsub_node,
|
||||
{Host, Node},
|
||||
#pubsub_node.host_parent),
|
||||
Node,
|
||||
#pubsub_node.parent),
|
||||
lists:map(
|
||||
fun(#pubsub_node{host_node = {_, N}}) ->
|
||||
fun(#pubsub_node{node = N}) ->
|
||||
SN = node_to_string(N),
|
||||
{xmlelement, "item",
|
||||
[{"jid", Host},
|
||||
@@ -397,7 +390,7 @@ iq_get_vcard(Lang) ->
|
||||
[{xmlcdata, translate:translate(
|
||||
Lang,
|
||||
"ejabberd pub/sub module\n"
|
||||
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
|
||||
|
||||
iq_pubsub(Host, From, Type, SubEl) ->
|
||||
@@ -439,9 +432,9 @@ iq_pubsub(Host, From, Type, SubEl) ->
|
||||
{set, "purge"} ->
|
||||
purge_node(Host, From, Node);
|
||||
{get, "entities"} ->
|
||||
get_entities(Host, From, Node);
|
||||
get_entities(From, Node);
|
||||
{set, "entities"} ->
|
||||
set_entities(Host, From, Node, xml:remove_cdata(Els));
|
||||
set_entities(From, Node, xml:remove_cdata(Els));
|
||||
%{get, "configure"} ->
|
||||
% get_node_config(From, Node);
|
||||
_ ->
|
||||
@@ -459,8 +452,6 @@ iq_pubsub(Host, From, Type, SubEl) ->
|
||||
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
|
||||
|
||||
|
||||
%% Create new pubsub nodes
|
||||
%% This function is used during init to create the first bootstrap nodes
|
||||
create_new_node(Host, Node, Owner) ->
|
||||
case Node of
|
||||
[] ->
|
||||
@@ -474,7 +465,7 @@ create_new_node(Host, Node, Owner) ->
|
||||
Parent = lists:sublist(Node, length(Node) - 1),
|
||||
F = fun() ->
|
||||
ParentExists = (Parent == []) orelse
|
||||
case mnesia:read({pubsub_node, {Host, Parent}}) of
|
||||
case mnesia:read({pubsub_node, Parent}) of
|
||||
[_] ->
|
||||
true;
|
||||
[] ->
|
||||
@@ -484,7 +475,7 @@ create_new_node(Host, Node, Owner) ->
|
||||
false ->
|
||||
{error, ?ERR_CONFLICT};
|
||||
_ ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[_] ->
|
||||
{error, ?ERR_CONFLICT};
|
||||
[] ->
|
||||
@@ -495,8 +486,8 @@ create_new_node(Host, Node, Owner) ->
|
||||
subscription = none},
|
||||
?DICT:new()),
|
||||
mnesia:write(
|
||||
#pubsub_node{host_node = {Host, Node},
|
||||
host_parent = {Host, Parent},
|
||||
#pubsub_node{node = Node,
|
||||
parent = Parent,
|
||||
info = #nodeinfo{
|
||||
entities = Entities}}),
|
||||
ok
|
||||
@@ -537,7 +528,7 @@ create_new_node(Host, Node, Owner) ->
|
||||
publish_item(Host, JID, Node, ItemID, Payload) ->
|
||||
Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
Affiliation = get_affiliation(Info, Publisher),
|
||||
if
|
||||
@@ -570,7 +561,7 @@ publish_item(Host, JID, Node, ItemID, Payload) ->
|
||||
delete_item(Host, JID, Node, ItemID) ->
|
||||
Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
case check_item_publisher(Info, ItemID, Publisher)
|
||||
orelse
|
||||
@@ -609,9 +600,8 @@ subscribe_node(Host, From, JID, Node) ->
|
||||
J
|
||||
end,
|
||||
Subscriber = jlib:jid_tolower(SubscriberJID),
|
||||
SubscriberWithoutResource = jlib:jid_remove_resource(Subscriber),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
Affiliation = get_affiliation(Info, Subscriber),
|
||||
if
|
||||
@@ -629,7 +619,7 @@ subscribe_node(Host, From, JID, Node) ->
|
||||
end
|
||||
end,
|
||||
if
|
||||
Sender == SubscriberWithoutResource ->
|
||||
Sender == Subscriber ->
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
@@ -654,7 +644,7 @@ unsubscribe_node(Host, From, JID, Node) ->
|
||||
end,
|
||||
Subscriber = jlib:jid_tolower(SubscriberJID),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
Subscription = get_subscription(Info, Subscriber),
|
||||
if
|
||||
@@ -703,7 +693,7 @@ get_items(Host, JID, Node, SMaxItems) ->
|
||||
{error, _} = Error ->
|
||||
Error;
|
||||
_ ->
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
case catch mnesia:dirty_read(pubsub_node, Node) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
Items = lists:sublist(Info#nodeinfo.items, MaxItems),
|
||||
ItemsEls =
|
||||
@@ -717,7 +707,7 @@ get_items(Host, JID, Node, SMaxItems) ->
|
||||
{xmlelement, "item", ItemAttrs, Payload}
|
||||
end, Items),
|
||||
{result, [{xmlelement, "pubsub",
|
||||
[{"xmlns", ?NS_PUBSUB}],
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "items",
|
||||
[{"node", node_to_string(Node)}],
|
||||
ItemsEls}]}]};
|
||||
@@ -730,14 +720,14 @@ get_items(Host, JID, Node, SMaxItems) ->
|
||||
delete_node(Host, JID, Node) ->
|
||||
Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
case get_affiliation(Info, Owner) of
|
||||
owner ->
|
||||
% TODO: don't iterate over entire table
|
||||
% TODO: don't iterate over all table
|
||||
Removed =
|
||||
mnesia:foldl(
|
||||
fun(#pubsub_node{host_node = {_, N},
|
||||
fun(#pubsub_node{node = N,
|
||||
info = #nodeinfo{
|
||||
entities = Entities
|
||||
}}, Acc) ->
|
||||
@@ -750,7 +740,7 @@ delete_node(Host, JID, Node) ->
|
||||
end, [], pubsub_node),
|
||||
lists:foreach(
|
||||
fun({N, _}) ->
|
||||
mnesia:delete({pubsub_node, {Host, N}})
|
||||
mnesia:delete({pubsub_node, N})
|
||||
end, Removed),
|
||||
{removed, Removed};
|
||||
_ ->
|
||||
@@ -777,7 +767,7 @@ delete_node(Host, JID, Node) ->
|
||||
purge_node(Host, JID, Node) ->
|
||||
Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
case get_affiliation(Info, Owner) of
|
||||
owner ->
|
||||
@@ -806,9 +796,9 @@ purge_node(Host, JID, Node) ->
|
||||
end.
|
||||
|
||||
|
||||
get_entities(Host, OJID, Node) ->
|
||||
get_entities(OJID, Node) ->
|
||||
Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)),
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
case catch mnesia:dirty_read(pubsub_node, Node) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
case get_affiliation(Info, Owner) of
|
||||
owner ->
|
||||
@@ -828,7 +818,7 @@ get_entities(Host, OJID, Node) ->
|
||||
[]} | Acc]
|
||||
end, [], Entities),
|
||||
{result, [{xmlelement, "pubsub",
|
||||
[{"xmlns", ?NS_PUBSUB}],
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "entities",
|
||||
[{"node", node_to_string(Node)}],
|
||||
EntitiesEls}]}]};
|
||||
@@ -840,7 +830,7 @@ get_entities(Host, OJID, Node) ->
|
||||
end.
|
||||
|
||||
|
||||
set_entities(Host, OJID, Node, EntitiesEls) ->
|
||||
set_entities(OJID, Node, EntitiesEls) ->
|
||||
Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)),
|
||||
Entities =
|
||||
lists:foldl(
|
||||
@@ -877,7 +867,7 @@ set_entities(Host, OJID, Node, EntitiesEls) ->
|
||||
(Subscription == false) ->
|
||||
error;
|
||||
true ->
|
||||
[{jlib:jid_tolower(JID),
|
||||
[{JID,
|
||||
#entity{
|
||||
affiliation = Affiliation,
|
||||
subscription = Subscription}} |
|
||||
@@ -891,7 +881,7 @@ set_entities(Host, OJID, Node, EntitiesEls) ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
case mnesia:read({pubsub_node, Node}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
case get_affiliation(Info, Owner) of
|
||||
owner ->
|
||||
@@ -994,7 +984,7 @@ subscription_to_string(Subscription) ->
|
||||
|
||||
check_create_permission(Host, Node, Owner) ->
|
||||
if
|
||||
Owner#jid.lserver == Host ->
|
||||
#jid{lserver = Host} == Owner ->
|
||||
true;
|
||||
true ->
|
||||
#jid{luser = User, lserver = Server} = Owner,
|
||||
@@ -1079,7 +1069,7 @@ set_info_entities(Info, Entities) ->
|
||||
|
||||
|
||||
broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
case catch mnesia:dirty_read(pubsub_node, Node) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
?DICT:fold(
|
||||
fun(JID, #entity{subscription = Subscription}, _) ->
|
||||
@@ -1092,7 +1082,7 @@ broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
||||
end,
|
||||
Stanza =
|
||||
{xmlelement, "message", [],
|
||||
[{xmlelement, "event",
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "items",
|
||||
[{"node", node_to_string(Node)}],
|
||||
@@ -1111,7 +1101,7 @@ broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
||||
|
||||
|
||||
broadcast_retract_item(Host, Node, ItemID) ->
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
case catch mnesia:dirty_read(pubsub_node, Node) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
?DICT:fold(
|
||||
fun(JID, #entity{subscription = Subscription}, _) ->
|
||||
@@ -1124,7 +1114,7 @@ broadcast_retract_item(Host, Node, ItemID) ->
|
||||
end,
|
||||
Stanza =
|
||||
{xmlelement, "message", [],
|
||||
[{xmlelement, "event",
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "items",
|
||||
[{"node", node_to_string(Node)}],
|
||||
@@ -1151,7 +1141,7 @@ broadcast_removed_node(Host, Removed) ->
|
||||
(Subscription /= pending) ->
|
||||
Stanza =
|
||||
{xmlelement, "message", [],
|
||||
[{xmlelement, "event",
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "delete",
|
||||
[{"node", node_to_string(Node)}],
|
||||
@@ -1174,73 +1164,3 @@ system_terminate(Reason, Parent, _, State) ->
|
||||
|
||||
system_code_change(State, _Mod, Ver, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
|
||||
|
||||
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) ->
|
||||
Fields = record_info(fields, pubsub_node),
|
||||
case mnesia:table_info(pubsub_node, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[node, parent, info] ->
|
||||
?INFO_MSG("Converting pubsub_node table from "
|
||||
"{node, parent, info} format", []),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_pubsub_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, pubsub_node},
|
||||
{attributes, record_info(fields, pubsub_node)}]),
|
||||
mnesia:del_table_index(pubsub_node, parent),
|
||||
mnesia:transform_table(pubsub_node, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_pubsub_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#pubsub_node{host_node = N,
|
||||
host_parent = P} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_pubsub_tmp_table,
|
||||
R#pubsub_node{host_node = {Host, N},
|
||||
host_parent = {Host, P}})
|
||||
end, ok, pubsub_node)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(pubsub_node),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(pubsub_node),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_pubsub_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_pubsub_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating pubsub_node table", []),
|
||||
mnesia:transform_table(pubsub_node, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -29,26 +29,25 @@ stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_REGISTER),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_REGISTER).
|
||||
|
||||
process_iq(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
process_iq(From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
UTag = xml:get_subtag(SubEl, "username"),
|
||||
PTag = xml:get_subtag(SubEl, "password"),
|
||||
RTag = xml:get_subtag(SubEl, "remove"),
|
||||
Server = To#jid.lserver,
|
||||
Server = ?MYNAME,
|
||||
if
|
||||
(UTag /= false) and (RTag /= false) ->
|
||||
User = xml:get_tag_cdata(UTag),
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:remove_user(User, Server),
|
||||
ejabberd_auth:remove_user(User),
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
if
|
||||
PTag /= false ->
|
||||
Password = xml:get_tag_cdata(PTag),
|
||||
case ejabberd_auth:remove_user(User,
|
||||
Server,
|
||||
Password) of
|
||||
ok ->
|
||||
IQ#iq{type = result,
|
||||
@@ -75,7 +74,7 @@ process_iq(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
(UTag == false) and (RTag /= false) ->
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:remove_user(User, Server),
|
||||
ejabberd_auth:remove_user(User),
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
IQ#iq{type = error,
|
||||
@@ -86,10 +85,10 @@ process_iq(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
Password = xml:get_tag_cdata(PTag),
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
ejabberd_auth:set_password(User, Server, Password),
|
||||
ejabberd_auth:set_password(User, Password),
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
_ ->
|
||||
case try_register(User, Server, Password) of
|
||||
case try_register(User, Password) of
|
||||
ok ->
|
||||
IQ#iq{type = result, sub_el = [SubEl]};
|
||||
{error, Error} ->
|
||||
@@ -117,18 +116,18 @@ process_iq(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
try_register(User, Password) ->
|
||||
case jlib:is_nodename(User) of
|
||||
false ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
JID = jlib:make_jid(User, Server, ""),
|
||||
JID = jlib:make_jid(User, ?MYNAME, ""),
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, all),
|
||||
case acl:match_rule(Access, JID) of
|
||||
deny ->
|
||||
{error, ?ERR_CONFLICT};
|
||||
allow ->
|
||||
case ejabberd_auth:try_register(User, Server, Password) of
|
||||
case ejabberd_auth:try_register(User, Password) of
|
||||
{atomic, ok} ->
|
||||
send_welcome_message(JID),
|
||||
send_registration_notifications(JID),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_roster.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Roster management
|
||||
%%% Purpose :
|
||||
%%% Created : 11 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -15,53 +15,35 @@
|
||||
-export([start/1, stop/0,
|
||||
process_iq/3,
|
||||
process_local_iq/3,
|
||||
get_user_roster/2,
|
||||
get_subscription_lists/3,
|
||||
in_subscription/5,
|
||||
out_subscription/4,
|
||||
set_items/3,
|
||||
remove_user/2,
|
||||
get_jid_info/4]).
|
||||
get_subscription_lists/1,
|
||||
in_subscription/3,
|
||||
out_subscription/3,
|
||||
set_items/2,
|
||||
remove_user/1,
|
||||
get_jid_info/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
|
||||
-record(roster, {uj,
|
||||
user,
|
||||
jid,
|
||||
name = "",
|
||||
subscription = none,
|
||||
ask = none,
|
||||
groups = [],
|
||||
xattrs = [],
|
||||
xs = []}).
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
mnesia:create_table(roster,[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, roster)}]),
|
||||
update_table(),
|
||||
mnesia:add_table_index(roster, us),
|
||||
ejabberd_hooks:add(roster_get,
|
||||
?MODULE, get_user_roster, 50),
|
||||
ejabberd_hooks:add(roster_in_subscription,
|
||||
?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:add(roster_out_subscription,
|
||||
?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:add(roster_get_subscription_lists,
|
||||
?MODULE, get_subscription_lists, 50),
|
||||
ejabberd_hooks:add(roster_get_jid_info,
|
||||
?MODULE, get_jid_info, 50),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
mnesia:add_table_index(roster, user),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_ROSTER,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(roster_get,
|
||||
?MODULE, get_user_roster, 50),
|
||||
ejabberd_hooks:delete(roster_in_subscription,
|
||||
?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:delete(roster_out_subscription,
|
||||
?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:delete(roster_get_subscription_lists,
|
||||
?MODULE, get_subscription_lists, 50),
|
||||
ejabberd_hooks:delete(roster_get_jid_info,
|
||||
?MODULE, get_jid_info, 50),
|
||||
ejabberd_hooks:delete(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_ROSTER).
|
||||
|
||||
|
||||
@@ -72,8 +54,8 @@ stop() ->
|
||||
process_iq(From, To, IQ) ->
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#jid{lserver = LServer} = From,
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
ResIQ = process_local_iq(From, To, IQ),
|
||||
ejabberd_router:route(From, From,
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
@@ -87,8 +69,8 @@ process_iq(From, To, IQ) ->
|
||||
process_iq(From, To, IQ) ->
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#jid{lserver = LServer} = From,
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
process_local_iq(From, To, IQ);
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
@@ -107,11 +89,12 @@ process_local_iq(From, To, #iq{type = Type} = IQ) ->
|
||||
|
||||
|
||||
process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
|
||||
LUser = From#jid.luser,
|
||||
LServer = From#jid.lserver,
|
||||
US = {LUser, LServer},
|
||||
case catch ejabberd_hooks:run_fold(roster_get, [], [US]) of
|
||||
Items when is_list(Items) ->
|
||||
#jid{luser = LUser} = From,
|
||||
F = fun() ->
|
||||
mnesia:index_read(roster, LUser, #roster.user)
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Items} ->
|
||||
XItems = lists:map(fun item_to_xml/1, Items),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@@ -121,17 +104,6 @@ process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||
end.
|
||||
|
||||
get_user_roster(Acc, US) ->
|
||||
case catch mnesia:dirty_index_read(roster, US, #roster.us) of
|
||||
Items when is_list(Items) ->
|
||||
Items ++ Acc;
|
||||
_ ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
item_to_xml(Item) ->
|
||||
Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
|
||||
Attrs2 = case Item#roster.name of
|
||||
@@ -175,7 +147,7 @@ process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
|
||||
|
||||
process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
|
||||
#jid{user = User, luser = LUser, lserver = LServer} = From,
|
||||
#jid{user = User, luser = LUser} = From,
|
||||
case JID1 of
|
||||
error ->
|
||||
ok;
|
||||
@@ -183,14 +155,15 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
||||
LJID = jlib:jid_tolower(JID1),
|
||||
F = fun() ->
|
||||
Res = mnesia:read({roster, {LUser, LServer, LJID}}),
|
||||
Res = mnesia:read({roster, {LUser, LJID}}),
|
||||
Item = case Res of
|
||||
[] ->
|
||||
#roster{usj = {LUser, LServer, LJID},
|
||||
us = {LUser, LServer},
|
||||
#roster{uj = {LUser, LJID},
|
||||
user = LUser,
|
||||
jid = JID};
|
||||
[I] ->
|
||||
I#roster{jid = JID,
|
||||
I#roster{user = LUser,
|
||||
jid = JID,
|
||||
name = "",
|
||||
groups = [],
|
||||
xattrs = [],
|
||||
@@ -200,7 +173,7 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
Item2 = process_item_els(Item1, Els),
|
||||
case Item2#roster.subscription of
|
||||
remove ->
|
||||
mnesia:delete({roster, {LUser, LServer, LJID}});
|
||||
mnesia:delete({roster, {LUser, LJID}});
|
||||
_ ->
|
||||
mnesia:write(Item2)
|
||||
end,
|
||||
@@ -208,7 +181,7 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, {OldItem, Item}} ->
|
||||
push_item(User, LServer, To, Item),
|
||||
push_item(User, To, Item),
|
||||
case Item#roster.subscription of
|
||||
remove ->
|
||||
IsTo = case OldItem#roster.subscription of
|
||||
@@ -302,57 +275,59 @@ process_item_els(Item, []) ->
|
||||
Item.
|
||||
|
||||
|
||||
push_item(User, Server, From, Item) ->
|
||||
ejabberd_sm:route(jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, Server, ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{item,
|
||||
Item#roster.jid,
|
||||
Item#roster.subscription}]}),
|
||||
push_item(User, From, Item) ->
|
||||
ejabberd_sm ! {route,
|
||||
jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, "", ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{item,
|
||||
Item#roster.jid,
|
||||
Item#roster.subscription}]}},
|
||||
lists:foreach(fun(Resource) ->
|
||||
push_item(User, Server, Resource, From, Item)
|
||||
end, ejabberd_sm:get_user_resources(User, Server)).
|
||||
push_item(User, Resource, From, Item)
|
||||
end, ejabberd_sm:get_user_resources(User)).
|
||||
|
||||
% TODO: don't push to those who not load roster
|
||||
-ifdef(PSI_ROSTER_WORKAROUND).
|
||||
|
||||
push_item(User, Server, Resource, _From, Item) ->
|
||||
push_item(User, Resource, _From, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(User, Server, Resource),
|
||||
jlib:make_jid(User, Server, Resource),
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:iq_to_xml(ResIQ)).
|
||||
|
||||
-else.
|
||||
|
||||
push_item(User, Server, Resource, From, Item) ->
|
||||
push_item(User, Resource, From, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
ejabberd_router:route(
|
||||
From,
|
||||
jlib:make_jid(User, Server, Resource),
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:iq_to_xml(ResIQ)).
|
||||
|
||||
-endif.
|
||||
|
||||
get_subscription_lists(_, User, Server) ->
|
||||
get_subscription_lists(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case mnesia:dirty_index_read(roster, US, #roster.us) of
|
||||
Items when is_list(Items) ->
|
||||
F = fun() ->
|
||||
mnesia:index_read(roster, LUser, #roster.user)
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Items} ->
|
||||
fill_subscription_lists(Items, [], []);
|
||||
_ ->
|
||||
{[], []}
|
||||
end.
|
||||
|
||||
fill_subscription_lists([I | Is], F, T) ->
|
||||
J = element(3, I#roster.usj),
|
||||
J = element(2, I#roster.uj),
|
||||
case I#roster.subscription of
|
||||
both ->
|
||||
fill_subscription_lists(Is, [J | F], [J | T]);
|
||||
@@ -372,25 +347,23 @@ ask_to_pending(Ask) -> Ask.
|
||||
|
||||
|
||||
|
||||
in_subscription(_, User, Server, JID, Type) ->
|
||||
process_subscription(in, User, Server, JID, Type).
|
||||
in_subscription(User, JID, Type) ->
|
||||
process_subscription(in, User, JID, Type).
|
||||
|
||||
out_subscription(User, Server, JID, Type) ->
|
||||
process_subscription(out, User, Server, JID, Type).
|
||||
out_subscription(User, JID, Type) ->
|
||||
process_subscription(out, User, JID, Type).
|
||||
|
||||
process_subscription(Direction, User, Server, JID1, Type) ->
|
||||
process_subscription(Direction, User, JID1, Type) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
LJID = jlib:jid_tolower(JID1),
|
||||
F = fun() ->
|
||||
Item = case mnesia:read({roster, {LUser, LServer, LJID}}) of
|
||||
Item = case mnesia:read({roster, {LUser, LJID}}) of
|
||||
[] ->
|
||||
JID = {JID1#jid.user,
|
||||
JID1#jid.server,
|
||||
JID1#jid.resource},
|
||||
#roster{usj = {LUser, LServer, LJID},
|
||||
us = US,
|
||||
#roster{uj = {LUser, LJID},
|
||||
user = LUser,
|
||||
jid = JID};
|
||||
[I] ->
|
||||
I
|
||||
@@ -434,13 +407,12 @@ process_subscription(Direction, User, Server, JID1, Type) ->
|
||||
unsubscribed -> "unsubscribed"
|
||||
end,
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(User, Server, ""), JID1,
|
||||
jlib:make_jid(User, ?MYNAME, ""), JID1,
|
||||
{xmlelement, "presence", [{"type", T}], []})
|
||||
end,
|
||||
case Push of
|
||||
{push, Item} ->
|
||||
push_item(User, Server,
|
||||
jlib:make_jid("", Server, ""), Item),
|
||||
push_item(User, jlib:make_jid("", ?MYNAME, ""), Item),
|
||||
true;
|
||||
none ->
|
||||
false
|
||||
@@ -540,32 +512,27 @@ in_auto_reply(both, none, unsubscribe) -> unsubscribed;
|
||||
in_auto_reply(_, _, _) -> none.
|
||||
|
||||
|
||||
remove_user(User, Server) ->
|
||||
remove_user(User) ->
|
||||
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(roster, US, #roster.us))
|
||||
mnesia:index_read(roster, LUser, #roster.user))
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_items(User, Server, SubEl) ->
|
||||
set_items(User, SubEl) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
F = fun() ->
|
||||
lists:foreach(fun(El) ->
|
||||
process_item_set_t(LUser, LServer, El)
|
||||
end, Els)
|
||||
lists:foreach(fun(El) -> process_item_set_t(LUser, El) end, Els)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
|
||||
process_item_set_t(LUser, {xmlelement, _Name, Attrs, Els}) ->
|
||||
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
|
||||
case JID1 of
|
||||
error ->
|
||||
@@ -573,19 +540,19 @@ process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
|
||||
_ ->
|
||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
||||
LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
|
||||
Item = #roster{usj = {LUser, LServer, LJID},
|
||||
us = {LUser, LServer},
|
||||
Item = #roster{uj = {LUser, LJID},
|
||||
user = LUser,
|
||||
jid = JID},
|
||||
Item1 = process_item_attrs_ws(Item, Attrs),
|
||||
Item2 = process_item_els(Item1, Els),
|
||||
case Item2#roster.subscription of
|
||||
remove ->
|
||||
mnesia:delete({roster, {LUser, LServer, LJID}});
|
||||
mnesia:delete({roster, {LUser, LJID}});
|
||||
_ ->
|
||||
mnesia:write(Item2)
|
||||
end
|
||||
end;
|
||||
process_item_set_t(_LUser, _LServer, _) ->
|
||||
process_item_set_t(_LUser, _) ->
|
||||
ok.
|
||||
|
||||
process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
|
||||
@@ -633,11 +600,10 @@ process_item_attrs_ws(Item, []) ->
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
get_jid_info(_, User, Server, JID) ->
|
||||
get_jid_info(User, JID) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LJID = jlib:jid_tolower(JID),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch mnesia:dirty_read(roster, {LUser, LServer, LJID}) of
|
||||
case catch mnesia:dirty_read(roster, {LUser, LJID}) of
|
||||
[#roster{subscription = Subscription, groups = Groups}] ->
|
||||
{Subscription, Groups};
|
||||
_ ->
|
||||
@@ -646,8 +612,7 @@ get_jid_info(_, User, Server, JID) ->
|
||||
LRJID == LJID ->
|
||||
{none, []};
|
||||
true ->
|
||||
case catch mnesia:dirty_read(
|
||||
roster, {LUser, LServer, LRJID}) of
|
||||
case catch mnesia:dirty_read(roster, {LUser, LRJID}) of
|
||||
[#roster{subscription = Subscription,
|
||||
groups = Groups}] ->
|
||||
{Subscription, Groups};
|
||||
@@ -657,51 +622,4 @@ get_jid_info(_, User, Server, JID) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, roster),
|
||||
case mnesia:table_info(roster, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[uj, user, jid, name, subscription, ask, groups, xattrs, xs] ->
|
||||
?INFO_MSG("Converting roster table from "
|
||||
"{uj, user, jid, name, subscription, ask, groups, xattrs, xs} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_roster_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, roster},
|
||||
{attributes, record_info(fields, roster)}]),
|
||||
mnesia:del_table_index(roster, user),
|
||||
mnesia:transform_table(roster, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_roster_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#roster{usj = {U, JID}, us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_roster_tmp_table,
|
||||
R#roster{usj = {U, Host, JID},
|
||||
us = {U, Host}})
|
||||
end, ok, roster)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(roster),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(roster),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_roster_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_roster_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating roster table", []),
|
||||
mnesia:transform_table(roster, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_roster.hrl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Roster management
|
||||
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id: mod_roster.hrl 11 2005-03-06 22:36:15Z alexey $
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(roster, {usj,
|
||||
us,
|
||||
jid,
|
||||
name = "",
|
||||
subscription = none,
|
||||
ask = none,
|
||||
groups = [],
|
||||
xattrs = [],
|
||||
xs = []}).
|
||||
|
||||
@@ -1,745 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_roster_odbc.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Roster management
|
||||
%%% Created : 15 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_roster_odbc).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, stop/0,
|
||||
process_iq/3,
|
||||
process_local_iq/3,
|
||||
get_subscription_lists/2,
|
||||
in_subscription/4,
|
||||
out_subscription/3,
|
||||
set_items/2,
|
||||
remove_user/1,
|
||||
get_jid_info/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(roster, {user,
|
||||
jid,
|
||||
name = "",
|
||||
subscription = none,
|
||||
ask = none}).
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
ejabberd_hooks:add(roster_out_subscription,
|
||||
?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:add(roster_in_subscription,
|
||||
?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:add(roster_out_subscription,
|
||||
?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:add(roster_get_subscription_lists,
|
||||
?MODULE, get_subscription_lists, 50),
|
||||
ejabberd_hooks:add(roster_get_jid_info,
|
||||
?MODULE, get_jid_info, 50),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_ROSTER,
|
||||
?MODULE, process_iq, IQDisc).
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(roster_in_subscription,
|
||||
?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:delete(roster_out_subscription,
|
||||
?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:delete(roster_get_subscription_lists,
|
||||
?MODULE, get_subscription_lists, 50),
|
||||
ejabberd_hooks:delete(roster_get_jid_info,
|
||||
?MODULE, get_jid_info, 50),
|
||||
ejabberd_hooks:delete(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_ROSTER).
|
||||
|
||||
|
||||
-define(PSI_ROSTER_WORKAROUND, true).
|
||||
|
||||
-ifdef(PSI_ROSTER_WORKAROUND).
|
||||
|
||||
process_iq(From, To, IQ) ->
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#jid{lserver = LServer} = From,
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
ResIQ = process_local_iq(From, To, IQ),
|
||||
ejabberd_router:route(From, From,
|
||||
jlib:iq_to_xml(ResIQ)),
|
||||
ignore;
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
end.
|
||||
|
||||
-else.
|
||||
|
||||
process_iq(From, To, IQ) ->
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#jid{lserver = LServer} = From,
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
process_local_iq(From, To, IQ);
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
||||
end.
|
||||
|
||||
-endif.
|
||||
|
||||
process_local_iq(From, To, #iq{type = Type} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
process_iq_set(From, To, IQ);
|
||||
get ->
|
||||
process_iq_get(From, To, IQ)
|
||||
end.
|
||||
|
||||
|
||||
|
||||
process_iq_get(From, _To, #iq{sub_el = SubEl} = IQ) ->
|
||||
LUser = From#jid.luser,
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select username, jid, nick, subscription, ask, "
|
||||
"server, subscribe, type from rosterusers "
|
||||
"where username='", Username, "'"]) of
|
||||
{selected, ["username", "jid", "nick", "subscription", "ask",
|
||||
"server", "subscribe", "type"],
|
||||
Items} when is_list(Items) ->
|
||||
XItems = lists:flatmap(
|
||||
fun(I) ->
|
||||
case raw_to_record(I) of
|
||||
error ->
|
||||
[];
|
||||
R ->
|
||||
[item_to_xml(R)]
|
||||
end
|
||||
end, Items),
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
XItems}]};
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||
end.
|
||||
|
||||
item_to_xml(Item) ->
|
||||
Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
|
||||
Attrs2 = case Item#roster.name of
|
||||
"" ->
|
||||
Attrs1;
|
||||
Name ->
|
||||
[{"name", Name} | Attrs1]
|
||||
end,
|
||||
Attrs3 = case Item#roster.subscription of
|
||||
none ->
|
||||
[{"subscription", "none"} | Attrs2];
|
||||
from ->
|
||||
[{"subscription", "from"} | Attrs2];
|
||||
to ->
|
||||
[{"subscription", "to"} | Attrs2];
|
||||
both ->
|
||||
[{"subscription", "both"} | Attrs2];
|
||||
remove ->
|
||||
[{"subscription", "remove"} | Attrs2]
|
||||
end,
|
||||
Attrs = case ask_to_pending(Item#roster.ask) of
|
||||
out ->
|
||||
[{"ask", "subscribe"} | Attrs3];
|
||||
both ->
|
||||
[{"ask", "subscribe"} | Attrs3];
|
||||
_ ->
|
||||
Attrs3
|
||||
end,
|
||||
% TODO
|
||||
%SubEls = lists:map(fun(G) ->
|
||||
% {xmlelement, "group", [], [{xmlcdata, G}]}
|
||||
% end, Item#roster.groups),
|
||||
SubEls = [],
|
||||
{xmlelement, "item", Attrs, SubEls}.
|
||||
|
||||
|
||||
process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||
lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els),
|
||||
IQ#iq{type = result, sub_el = []}.
|
||||
|
||||
process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
||||
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
|
||||
#jid{user = User, luser = LUser} = From,
|
||||
case JID1 of
|
||||
error ->
|
||||
ok;
|
||||
_ ->
|
||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
||||
LJID = jlib:jid_tolower(JID1),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select username, jid, nick, subscription, ask, "
|
||||
"server, subscribe, type from rosterusers "
|
||||
"where username='", Username, "' "
|
||||
"and jid='", SJID, "'"]) of
|
||||
{selected, ["username", "jid", "nick", "subscription", "ask",
|
||||
"server", "subscribe", "type"],
|
||||
Res} ->
|
||||
Item = case Res of
|
||||
[] ->
|
||||
#roster{user = LUser,
|
||||
jid = LJID};
|
||||
[I] ->
|
||||
(raw_to_record(I))#roster{user = LUser,
|
||||
jid = JID,
|
||||
name = ""}
|
||||
end,
|
||||
Item1 = process_item_attrs(Item, Attrs),
|
||||
case Item1#roster.subscription of
|
||||
remove ->
|
||||
catch ejabberd_odbc:sql_query(
|
||||
["begin;"
|
||||
"delete from rosterusers "
|
||||
" where username='", Username, "' "
|
||||
" and jid='", SJID, "';"
|
||||
"delete from rostergroups "
|
||||
" where username='", Username, "' "
|
||||
" and jid='", SJID, "';"
|
||||
"commit"]);
|
||||
_ ->
|
||||
ItemVals = record_to_string(Item1),
|
||||
catch ejabberd_odbc:sql_query(
|
||||
["begin;"
|
||||
"delete from rosterusers "
|
||||
" where username='", Username, "' "
|
||||
" and jid='", SJID, "';"
|
||||
"insert into rosterusers("
|
||||
" username, jid, nick, "
|
||||
" subscription, ask, "
|
||||
" server, subscribe, type) "
|
||||
" values ", ItemVals, ";"
|
||||
"delete from rostergroups "
|
||||
" where username='", Username, "' "
|
||||
" and jid='", SJID, "';"
|
||||
% TODO
|
||||
"commit"])
|
||||
|
||||
%mnesia:write(Item1)
|
||||
end,
|
||||
push_item(User, To, Item1),
|
||||
case Item1#roster.subscription of
|
||||
remove ->
|
||||
IsTo = case Item#roster.subscription of
|
||||
both -> true;
|
||||
to -> true;
|
||||
_ -> false
|
||||
end,
|
||||
IsFrom = case Item#roster.subscription of
|
||||
both -> true;
|
||||
from -> true;
|
||||
_ -> false
|
||||
end,
|
||||
if IsTo ->
|
||||
ejabberd_router:route(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(Item#roster.jid),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribe"}],
|
||||
[]});
|
||||
true -> ok
|
||||
end,
|
||||
if IsFrom ->
|
||||
ejabberd_router:route(
|
||||
jlib:jid_remove_resource(From),
|
||||
jlib:make_jid(Item#roster.jid),
|
||||
{xmlelement, "presence",
|
||||
[{"type", "unsubscribed"}],
|
||||
[]});
|
||||
true -> ok
|
||||
end,
|
||||
ok;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
E ->
|
||||
?DEBUG("ROSTER: roster item set error: ~p~n", [E]),
|
||||
ok
|
||||
end
|
||||
end;
|
||||
process_item_set(_From, _To, _) ->
|
||||
ok.
|
||||
|
||||
process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
|
||||
case Attr of
|
||||
"jid" ->
|
||||
case jlib:string_to_jid(Val) of
|
||||
error ->
|
||||
process_item_attrs(Item, Attrs);
|
||||
JID1 ->
|
||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
||||
process_item_attrs(Item#roster{jid = JID}, Attrs)
|
||||
end;
|
||||
"name" ->
|
||||
process_item_attrs(Item#roster{name = Val}, Attrs);
|
||||
"subscription" ->
|
||||
case Val of
|
||||
"remove" ->
|
||||
process_item_attrs(Item#roster{subscription = remove},
|
||||
Attrs);
|
||||
_ ->
|
||||
process_item_attrs(Item, Attrs)
|
||||
end;
|
||||
"ask" ->
|
||||
process_item_attrs(Item, Attrs);
|
||||
_ ->
|
||||
process_item_attrs(Item, Attrs)
|
||||
end;
|
||||
process_item_attrs(Item, []) ->
|
||||
Item.
|
||||
|
||||
|
||||
% TODO
|
||||
%process_item_els(Item, [{xmlelement, Name, Attrs, SEls} | Els]) ->
|
||||
% case Name of
|
||||
% "group" ->
|
||||
% Groups = [xml:get_cdata(SEls) | Item#roster.groups],
|
||||
% process_item_els(Item#roster{groups = Groups}, Els);
|
||||
% _ ->
|
||||
% case xml:get_attr_s("xmlns", Attrs) of
|
||||
% "" ->
|
||||
% process_item_els(Item, Els);
|
||||
% _ ->
|
||||
% XEls = [{xmlelement, Name, Attrs, SEls} | Item#roster.xs],
|
||||
% process_item_els(Item#roster{xs = XEls}, Els)
|
||||
% end
|
||||
% end;
|
||||
process_item_els(Item, [{xmlcdata, _} | Els]) ->
|
||||
process_item_els(Item, Els);
|
||||
process_item_els(Item, []) ->
|
||||
Item.
|
||||
|
||||
|
||||
push_item(User, From, Item) ->
|
||||
ejabberd_sm:route(jlib:make_jid("", "", ""),
|
||||
jlib:make_jid(User, "", ""),
|
||||
{xmlelement, "broadcast", [],
|
||||
[{item,
|
||||
Item#roster.jid,
|
||||
Item#roster.subscription}]}),
|
||||
lists:foreach(fun(Resource) ->
|
||||
push_item(User, Resource, From, Item)
|
||||
end, ejabberd_sm:get_user_resources(User, 'TODO')).
|
||||
|
||||
% TODO: don't push to those who not load roster
|
||||
-ifdef(PSI_ROSTER_WORKAROUND).
|
||||
|
||||
push_item(User, Resource, _From, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:iq_to_xml(ResIQ)).
|
||||
|
||||
-else.
|
||||
|
||||
push_item(User, Resource, From, Item) ->
|
||||
ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_ROSTER}],
|
||||
[item_to_xml(Item)]}]},
|
||||
ejabberd_router:route(
|
||||
From,
|
||||
jlib:make_jid(User, ?MYNAME, Resource),
|
||||
jlib:iq_to_xml(ResIQ)).
|
||||
|
||||
-endif.
|
||||
|
||||
get_subscription_lists(_, User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
case catch ejabberd_odbc:sql_query(
|
||||
["select username, jid, nick, subscription, ask, "
|
||||
"server, subscribe, type from rosterusers "
|
||||
"where username='", Username, "'"]) of
|
||||
{selected, ["username", "jid", "nick", "subscription", "ask",
|
||||
"server", "subscribe", "type"],
|
||||
Items} when is_list(Items) ->
|
||||
fill_subscription_lists(Items, [], []);
|
||||
_ ->
|
||||
{[], []}
|
||||
end.
|
||||
|
||||
fill_subscription_lists([I | Is], F, T) ->
|
||||
J = I#roster.jid,
|
||||
case I#roster.subscription of
|
||||
both ->
|
||||
fill_subscription_lists(Is, [J | F], [J | T]);
|
||||
from ->
|
||||
fill_subscription_lists(Is, [J | F], T);
|
||||
to ->
|
||||
fill_subscription_lists(Is, F, [J | T]);
|
||||
_ ->
|
||||
fill_subscription_lists(Is, F, T)
|
||||
end;
|
||||
fill_subscription_lists([], F, T) ->
|
||||
{F, T}.
|
||||
|
||||
ask_to_pending(subscribe) -> out;
|
||||
ask_to_pending(unsubscribe) -> none;
|
||||
ask_to_pending(Ask) -> Ask.
|
||||
|
||||
|
||||
|
||||
in_subscription(_, User, JID, Type) ->
|
||||
process_subscription(in, User, JID, Type).
|
||||
|
||||
out_subscription(User, JID, Type) ->
|
||||
process_subscription(out, User, JID, Type).
|
||||
|
||||
process_subscription(Direction, User, JID1, Type) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LJID = jlib:jid_tolower(JID1),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
|
||||
Item = case catch ejabberd_odbc:sql_query(
|
||||
["select username, jid, nick, subscription, ask, "
|
||||
"server, subscribe, type from rosterusers "
|
||||
"where username='", Username, "' "
|
||||
"and jid='", SJID, "'"]) of
|
||||
{selected, ["username", "jid", "nick", "subscription", "ask",
|
||||
"server", "subscribe", "type"],
|
||||
[I]} ->
|
||||
raw_to_record(I);
|
||||
_ ->
|
||||
#roster{user = LUser,
|
||||
jid = LJID}
|
||||
end,
|
||||
NewState = case Direction of
|
||||
out ->
|
||||
out_state_change(Item#roster.subscription,
|
||||
Item#roster.ask,
|
||||
Type);
|
||||
in ->
|
||||
in_state_change(Item#roster.subscription,
|
||||
Item#roster.ask,
|
||||
Type)
|
||||
end,
|
||||
AutoReply = case Direction of
|
||||
out ->
|
||||
none;
|
||||
in ->
|
||||
in_auto_reply(Item#roster.subscription,
|
||||
Item#roster.ask,
|
||||
Type)
|
||||
end,
|
||||
Push = case NewState of
|
||||
none ->
|
||||
none;
|
||||
{Subscription, Pending} ->
|
||||
NewItem = Item#roster{subscription = Subscription,
|
||||
ask = Pending},
|
||||
ItemVals = record_to_string(NewItem),
|
||||
catch ejabberd_odbc:sql_query(
|
||||
["begin;"
|
||||
"delete from rosterusers "
|
||||
" where username='", Username, "' "
|
||||
" and jid='", SJID, "';"
|
||||
"insert into rosterusers("
|
||||
" username, jid, nick, "
|
||||
" subscription, ask, "
|
||||
" server, subscribe, type) "
|
||||
" values ", ItemVals, ";"
|
||||
"commit"]),
|
||||
{push, NewItem}
|
||||
end,
|
||||
case AutoReply of
|
||||
none ->
|
||||
ok;
|
||||
_ ->
|
||||
T = case AutoReply of
|
||||
subscribed -> "subscribed";
|
||||
unsubscribed -> "unsubscribed"
|
||||
end,
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(User, ?MYNAME, ""), JID1,
|
||||
{xmlelement, "presence", [{"type", T}], []})
|
||||
end,
|
||||
case Push of
|
||||
{push, PushItem} ->
|
||||
push_item(User, jlib:make_jid("", ?MYNAME, ""), PushItem),
|
||||
true;
|
||||
none ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
%% in_state_change(Subscription, Pending, Type) -> NewState
|
||||
%% NewState = none | {NewSubscription, NewPending}
|
||||
|
||||
in_state_change(none, none, subscribe) -> {none, in};
|
||||
in_state_change(none, none, subscribed) -> {to, none}; % Workaround for gateways
|
||||
%in_state_change(none, none, subscribed) -> none;
|
||||
in_state_change(none, none, unsubscribe) -> none;
|
||||
in_state_change(none, none, unsubscribed) -> none;
|
||||
in_state_change(none, out, subscribe) -> {none, both};
|
||||
in_state_change(none, out, subscribed) -> {to, none};
|
||||
in_state_change(none, out, unsubscribe) -> none;
|
||||
in_state_change(none, out, unsubscribed) -> {none, none};
|
||||
in_state_change(none, in, subscribe) -> none;
|
||||
in_state_change(none, in, subscribed) -> {to, in}; % Workaround for gateways
|
||||
%in_state_change(none, in, subscribed) -> none;
|
||||
in_state_change(none, in, unsubscribe) -> {none, none};
|
||||
in_state_change(none, in, unsubscribed) -> none;
|
||||
in_state_change(none, both, subscribe) -> none;
|
||||
in_state_change(none, both, subscribed) -> {to, in};
|
||||
in_state_change(none, both, unsubscribe) -> {none, out};
|
||||
in_state_change(none, both, unsubscribed) -> {none, in};
|
||||
in_state_change(to, none, subscribe) -> {to, in};
|
||||
in_state_change(to, none, subscribed) -> none;
|
||||
in_state_change(to, none, unsubscribe) -> none;
|
||||
in_state_change(to, none, unsubscribed) -> {none, none};
|
||||
in_state_change(to, in, subscribe) -> none;
|
||||
in_state_change(to, in, subscribed) -> none;
|
||||
in_state_change(to, in, unsubscribe) -> {to, none};
|
||||
in_state_change(to, in, unsubscribed) -> {none, in};
|
||||
in_state_change(from, none, subscribe) -> none;
|
||||
in_state_change(from, none, subscribed) -> none;
|
||||
in_state_change(from, none, unsubscribe) -> {none, none};
|
||||
in_state_change(from, none, unsubscribed) -> none;
|
||||
in_state_change(from, out, subscribe) -> none;
|
||||
in_state_change(from, out, subscribed) -> {both, none};
|
||||
in_state_change(from, out, unsubscribe) -> {none, out};
|
||||
in_state_change(from, out, unsubscribed) -> {from, none};
|
||||
in_state_change(both, none, subscribe) -> none;
|
||||
in_state_change(both, none, subscribed) -> none;
|
||||
in_state_change(both, none, unsubscribe) -> {to, none};
|
||||
in_state_change(both, none, unsubscribed) -> {from, none}.
|
||||
|
||||
out_state_change(none, none, subscribe) -> {none, out};
|
||||
out_state_change(none, none, subscribed) -> none;
|
||||
out_state_change(none, none, unsubscribe) -> none;
|
||||
out_state_change(none, none, unsubscribed) -> none;
|
||||
out_state_change(none, out, subscribe) -> none;
|
||||
out_state_change(none, out, subscribed) -> none;
|
||||
out_state_change(none, out, unsubscribe) -> {none, none};
|
||||
out_state_change(none, out, unsubscribed) -> none;
|
||||
out_state_change(none, in, subscribe) -> {none, both};
|
||||
out_state_change(none, in, subscribed) -> {from, none};
|
||||
out_state_change(none, in, unsubscribe) -> none;
|
||||
out_state_change(none, in, unsubscribed) -> {none, none};
|
||||
out_state_change(none, both, subscribe) -> none;
|
||||
out_state_change(none, both, subscribed) -> {from, out};
|
||||
out_state_change(none, both, unsubscribe) -> {none, in};
|
||||
out_state_change(none, both, unsubscribed) -> {none, out};
|
||||
out_state_change(to, none, subscribe) -> none;
|
||||
out_state_change(to, none, subscribed) -> none;
|
||||
out_state_change(to, none, unsubscribe) -> {none, none};
|
||||
out_state_change(to, none, unsubscribed) -> none;
|
||||
out_state_change(to, in, subscribe) -> none;
|
||||
out_state_change(to, in, subscribed) -> {both, none};
|
||||
out_state_change(to, in, unsubscribe) -> {none, in};
|
||||
out_state_change(to, in, unsubscribed) -> {to, none};
|
||||
out_state_change(from, none, subscribe) -> {from, out};
|
||||
out_state_change(from, none, subscribed) -> none;
|
||||
out_state_change(from, none, unsubscribe) -> none;
|
||||
out_state_change(from, none, unsubscribed) -> {none, none};
|
||||
out_state_change(from, out, subscribe) -> none;
|
||||
out_state_change(from, out, subscribed) -> none;
|
||||
out_state_change(from, out, unsubscribe) -> {from, none};
|
||||
out_state_change(from, out, unsubscribed) -> {none, out};
|
||||
out_state_change(both, none, subscribe) -> none;
|
||||
out_state_change(both, none, subscribed) -> none;
|
||||
out_state_change(both, none, unsubscribe) -> {from, none};
|
||||
out_state_change(both, none, unsubscribed) -> {to, none}.
|
||||
|
||||
in_auto_reply(from, none, subscribe) -> subscribed;
|
||||
in_auto_reply(from, out, subscribe) -> subscribed;
|
||||
in_auto_reply(both, none, subscribe) -> subscribed;
|
||||
in_auto_reply(none, in, unsubscribe) -> unsubscribed;
|
||||
in_auto_reply(none, both, unsubscribe) -> unsubscribed;
|
||||
in_auto_reply(to, in, unsubscribe) -> unsubscribed;
|
||||
in_auto_reply(from, none, unsubscribe) -> unsubscribed;
|
||||
in_auto_reply(from, out, unsubscribe) -> unsubscribed;
|
||||
in_auto_reply(both, none, unsubscribe) -> unsubscribed;
|
||||
in_auto_reply(_, _, _) -> none.
|
||||
|
||||
|
||||
remove_user(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
catch ejabberd_odbc:sql_query(
|
||||
["begin;"
|
||||
"delete from rosterusers "
|
||||
" where username='", Username, "';"
|
||||
"delete from rostergroups "
|
||||
" where username='", Username, "';"
|
||||
"commit"]),
|
||||
ok.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_items(User, SubEl) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
lists:foreach(fun(El) -> process_item_set_t(LUser, El) end, Els)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
process_item_set_t(LUser, {xmlelement, _Name, Attrs, Els}) ->
|
||||
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
|
||||
case JID1 of
|
||||
error ->
|
||||
ok;
|
||||
_ ->
|
||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
||||
LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
|
||||
Item = #roster{user = LUser,
|
||||
jid = LJID},
|
||||
Item1 = process_item_attrs_ws(Item, Attrs),
|
||||
Item2 = process_item_els(Item1, Els),
|
||||
case Item2#roster.subscription of
|
||||
remove ->
|
||||
mnesia:delete({roster, {LUser, LJID}});
|
||||
_ ->
|
||||
mnesia:write(Item2)
|
||||
end
|
||||
end;
|
||||
process_item_set_t(_LUser, _) ->
|
||||
ok.
|
||||
|
||||
process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
|
||||
case Attr of
|
||||
"jid" ->
|
||||
case jlib:string_to_jid(Val) of
|
||||
error ->
|
||||
process_item_attrs_ws(Item, Attrs);
|
||||
JID1 ->
|
||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
||||
process_item_attrs_ws(Item#roster{jid = JID}, Attrs)
|
||||
end;
|
||||
"name" ->
|
||||
process_item_attrs_ws(Item#roster{name = Val}, Attrs);
|
||||
"subscription" ->
|
||||
case Val of
|
||||
"remove" ->
|
||||
process_item_attrs_ws(Item#roster{subscription = remove},
|
||||
Attrs);
|
||||
"none" ->
|
||||
process_item_attrs_ws(Item#roster{subscription = none},
|
||||
Attrs);
|
||||
"both" ->
|
||||
process_item_attrs_ws(Item#roster{subscription = both},
|
||||
Attrs);
|
||||
"from" ->
|
||||
process_item_attrs_ws(Item#roster{subscription = from},
|
||||
Attrs);
|
||||
"to" ->
|
||||
process_item_attrs_ws(Item#roster{subscription = to},
|
||||
Attrs);
|
||||
_ ->
|
||||
process_item_attrs_ws(Item, Attrs)
|
||||
end;
|
||||
"ask" ->
|
||||
process_item_attrs_ws(Item, Attrs);
|
||||
_ ->
|
||||
process_item_attrs_ws(Item, Attrs)
|
||||
end;
|
||||
process_item_attrs_ws(Item, []) ->
|
||||
Item.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
get_jid_info(_, User, JID) ->
|
||||
% TODO
|
||||
% LUser = jlib:nodeprep(User),
|
||||
% LJID = jlib:jid_tolower(JID),
|
||||
% case catch mnesia:dirty_read(roster, {LUser, LJID}) of
|
||||
% [#roster{subscription = Subscription, groups = Groups}] ->
|
||||
% {Subscription, Groups};
|
||||
% _ ->
|
||||
% LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
% if
|
||||
% LRJID == LJID ->
|
||||
% {none, []};
|
||||
% true ->
|
||||
% case catch mnesia:dirty_read(roster, {LUser, LRJID}) of
|
||||
% [#roster{subscription = Subscription,
|
||||
% groups = Groups}] ->
|
||||
% {Subscription, Groups};
|
||||
% _ ->
|
||||
% {none, []}
|
||||
% end
|
||||
% end
|
||||
% end.
|
||||
{none, []}.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
raw_to_record({User, SJID, Nick, SSubscription, SAsk,
|
||||
_SServer, _SSubscribe, _SType}) ->
|
||||
case jlib:string_to_jid(SJID) of
|
||||
error ->
|
||||
error;
|
||||
JID ->
|
||||
LJID = jlib:jid_tolower(JID),
|
||||
Subscription = case SSubscription of
|
||||
"B" -> both;
|
||||
"T" -> to;
|
||||
"F" -> from;
|
||||
_ -> none
|
||||
end,
|
||||
Ask = case SAsk of
|
||||
"S" -> subscribe;
|
||||
"U" -> unsubscribe;
|
||||
"B" -> both;
|
||||
"O" -> out;
|
||||
"I" -> in;
|
||||
_ -> none
|
||||
end,
|
||||
#roster{user = User,
|
||||
jid = LJID,
|
||||
name = Nick,
|
||||
subscription = Subscription,
|
||||
ask = Ask}
|
||||
end.
|
||||
|
||||
record_to_string(#roster{user = User,
|
||||
jid = JID,
|
||||
name = Name,
|
||||
subscription = Subscription,
|
||||
ask = Ask}) ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(JID)),
|
||||
Nick = ejabberd_odbc:escape(Name),
|
||||
SSubscription = case Subscription of
|
||||
both -> "B";
|
||||
to -> "T";
|
||||
from -> "F";
|
||||
none -> "N"
|
||||
end,
|
||||
SAsk = case Ask of
|
||||
subscribe -> "S";
|
||||
unsubscribe -> "U";
|
||||
both -> "B";
|
||||
out -> "O";
|
||||
in -> "I";
|
||||
none -> "N"
|
||||
end,
|
||||
["("
|
||||
"'", Username, "',"
|
||||
"'", SJID, "',"
|
||||
"'", Nick, "',"
|
||||
"'", SSubscription, "',"
|
||||
"'", SAsk, "',"
|
||||
"'N', '', 'item')"].
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_service_log.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Manage announce messages
|
||||
%%% Created : 24 Aug 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_service_log).
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
stop/0,
|
||||
log_user_send/3,
|
||||
log_user_receive/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
start(_) ->
|
||||
ejabberd_hooks:add(user_send_packet,
|
||||
?MODULE, log_user_send, 50),
|
||||
ejabberd_hooks:add(user_receive_packet,
|
||||
?MODULE, log_user_receive, 50),
|
||||
ok.
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(user_send_packet,
|
||||
?MODULE, log_user_send, 50),
|
||||
ejabberd_hooks:delete(user_receive_packet,
|
||||
?MODULE, log_user_receive, 50),
|
||||
ok.
|
||||
|
||||
log_user_send(From, To, Packet) ->
|
||||
log_packet(From, To, Packet).
|
||||
|
||||
log_user_receive(_JID, From, To, Packet) ->
|
||||
log_packet(From, To, Packet).
|
||||
|
||||
|
||||
log_packet(From, To, {xmlelement, Name, Attrs, Els}) ->
|
||||
Loggers = gen_mod:get_module_opt(?MODULE, loggers, []),
|
||||
ServerJID = #jid{user = "", server = ?MYNAME, resource = "",
|
||||
luser = "", lserver = ?MYNAME, lresource = ""},
|
||||
NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
FixedPacket = {xmlelement, Name, NewAttrs, Els},
|
||||
lists:foreach(
|
||||
fun(Logger) ->
|
||||
ejabberd_router:route(
|
||||
ServerJID,
|
||||
#jid{user = "", server = Logger, resource = "",
|
||||
luser = "", lserver = Logger, lresource = ""},
|
||||
{xmlelement, "route", [], [FixedPacket]})
|
||||
end, Loggers).
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_shared_roster.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : Shared roster management
|
||||
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id: mod_shared_roster.erl 24 2005-04-14 01:15:31Z alexey $
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_shared_roster).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision: 24 $ ').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, stop/0,
|
||||
get_user_roster/2,
|
||||
get_subscription_lists/3,
|
||||
get_jid_info/4,
|
||||
in_subscription/5,
|
||||
out_subscription/4,
|
||||
list_groups/1,
|
||||
create_group/2,
|
||||
create_group/3,
|
||||
delete_group/2,
|
||||
get_group_opts/2,
|
||||
set_group_opts/3,
|
||||
get_group_users/2,
|
||||
add_user_to_group/3,
|
||||
remove_user_from_group/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
|
||||
-record(sr_group, {group_host, opts}).
|
||||
-record(sr_user, {us, group_host}).
|
||||
|
||||
start(_Opts) ->
|
||||
mnesia:create_table(sr_group,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, sr_group)}]),
|
||||
mnesia:create_table(sr_user,
|
||||
[{disc_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, sr_user)}]),
|
||||
mnesia:add_table_index(sr_user, group_host),
|
||||
ejabberd_hooks:add(roster_get,
|
||||
?MODULE, get_user_roster, 70),
|
||||
ejabberd_hooks:add(roster_in_subscription,
|
||||
?MODULE, in_subscription, 30),
|
||||
ejabberd_hooks:add(roster_out_subscription,
|
||||
?MODULE, out_subscription, 30),
|
||||
ejabberd_hooks:add(roster_get_subscription_lists,
|
||||
?MODULE, get_subscription_lists, 70),
|
||||
ejabberd_hooks:add(roster_get_jid_info,
|
||||
?MODULE, get_jid_info, 70).
|
||||
%ejabberd_hooks:add(remove_user,
|
||||
% ?MODULE, remove_user, 50),
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(roster_get,
|
||||
?MODULE, get_user_roster, 70),
|
||||
ejabberd_hooks:delete(roster_in_subscription,
|
||||
?MODULE, in_subscription, 30),
|
||||
ejabberd_hooks:delete(roster_out_subscription,
|
||||
?MODULE, out_subscription, 30),
|
||||
ejabberd_hooks:delete(roster_get_subscription_lists,
|
||||
?MODULE, get_subscription_lists, 70),
|
||||
ejabberd_hooks:delete(roster_get_jid_info,
|
||||
?MODULE, get_jid_info, 70).
|
||||
%ejabberd_hooks:delete(remove_user,
|
||||
% ?MODULE, remove_user, 50),
|
||||
|
||||
|
||||
get_user_roster(Items, US) ->
|
||||
{U, S} = US,
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
SRUsers =
|
||||
lists:foldl(
|
||||
fun(Group, Acc1) ->
|
||||
lists:foldl(
|
||||
fun(User, Acc2) ->
|
||||
dict:append(User, get_group_name(S, Group), Acc2)
|
||||
end, Acc1, get_group_users(S, Group))
|
||||
end, dict:new(), DisplayedGroups),
|
||||
{NewItems1, SRUsersRest} =
|
||||
lists:mapfoldl(
|
||||
fun(Item, SRUsers1) ->
|
||||
{_, _, {U1, S1, _}} = Item#roster.usj,
|
||||
US1 = {U1, S1},
|
||||
case dict:find(US1, SRUsers1) of
|
||||
{ok, _GroupNames} ->
|
||||
{Item#roster{subscription = both, ask = none},
|
||||
dict:erase(US1, SRUsers1)};
|
||||
error ->
|
||||
{Item, SRUsers1}
|
||||
end
|
||||
end, SRUsers, Items),
|
||||
SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
|
||||
us = US,
|
||||
jid = {U1, S1, ""},
|
||||
name = "",
|
||||
subscription = both,
|
||||
ask = none,
|
||||
groups = GroupNames} ||
|
||||
{{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
|
||||
SRItems ++ NewItems1.
|
||||
|
||||
get_subscription_lists({F, T}, User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
SRUsers =
|
||||
lists:usort(
|
||||
lists:flatmap(
|
||||
fun(Group) ->
|
||||
get_group_users(LServer, Group)
|
||||
end, DisplayedGroups)),
|
||||
SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
|
||||
{lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
|
||||
|
||||
get_jid_info({Subscription, Groups}, User, Server, JID) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
{U1, S1, _} = jlib:jid_tolower(JID),
|
||||
US1 = {U1, S1},
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
SRUsers =
|
||||
lists:foldl(
|
||||
fun(Group, Acc1) ->
|
||||
lists:foldl(
|
||||
fun(User1, Acc2) ->
|
||||
dict:append(
|
||||
User1, get_group_name(LServer, Group), Acc2)
|
||||
end, Acc1, get_group_users(LServer, Group))
|
||||
end, dict:new(), DisplayedGroups),
|
||||
case dict:find(US1, SRUsers) of
|
||||
{ok, GroupNames} ->
|
||||
NewGroups = if
|
||||
Groups == [] -> GroupNames;
|
||||
true -> Groups
|
||||
end,
|
||||
{both, NewGroups};
|
||||
error ->
|
||||
{Subscription, Groups}
|
||||
end.
|
||||
|
||||
in_subscription(Acc, User, Server, JID, Type) ->
|
||||
process_subscription(in, User, Server, JID, Type, Acc).
|
||||
|
||||
out_subscription(User, Server, JID, Type) ->
|
||||
process_subscription(out, User, Server, JID, Type, false).
|
||||
|
||||
process_subscription(Direction, User, Server, JID, _Type, Acc) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
{U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
US1 = {U1, S1},
|
||||
DisplayedGroups = get_user_displayed_groups(US),
|
||||
SRUsers =
|
||||
lists:usort(
|
||||
lists:flatmap(
|
||||
fun(Group) ->
|
||||
get_group_users(LServer, Group)
|
||||
end, DisplayedGroups)),
|
||||
case lists:member(US1, SRUsers) of
|
||||
true ->
|
||||
case Direction of
|
||||
in ->
|
||||
{stop, false};
|
||||
out ->
|
||||
stop
|
||||
end;
|
||||
false ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
list_groups(Host) ->
|
||||
mnesia:dirty_select(
|
||||
sr_group,
|
||||
[{#sr_group{group_host = {'$1', '$2'},
|
||||
_ = '_'},
|
||||
[{'==', '$2', Host}],
|
||||
['$1']}]).
|
||||
|
||||
create_group(Host, Group) ->
|
||||
create_group(Host, Group, []).
|
||||
|
||||
create_group(Host, Group, Opts) ->
|
||||
R = #sr_group{group_host = {Group, Host}, opts = Opts},
|
||||
F = fun() ->
|
||||
mnesia:write(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
delete_group(Host, Group) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({sr_group, {Group, Host}})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
get_group_opts(Host, Group) ->
|
||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
||||
[#sr_group{opts = Opts}] ->
|
||||
Opts;
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
set_group_opts(Host, Group, Opts) ->
|
||||
R = #sr_group{group_host = {Group, Host}, opts = Opts},
|
||||
F = fun() ->
|
||||
mnesia:write(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
|
||||
get_user_groups(US) ->
|
||||
Host = element(2, US),
|
||||
case catch mnesia:dirty_read(sr_user, US) of
|
||||
Rs when is_list(Rs) ->
|
||||
[Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
is_group_enabled(Host, Group) ->
|
||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
||||
[#sr_group{opts = Opts}] ->
|
||||
not lists:member(disabled, Opts);
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_group_opt(Host, Group, Opt, Default) ->
|
||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
||||
[#sr_group{opts = Opts}] ->
|
||||
case lists:keysearch(Opt, 1, Opts) of
|
||||
{value, {_, Val}} ->
|
||||
Val;
|
||||
false ->
|
||||
Default
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_group_users(Host, Group) ->
|
||||
case catch mnesia:dirty_index_read(
|
||||
sr_user, {Group, Host}, #sr_user.group_host) of
|
||||
Rs when is_list(Rs) ->
|
||||
[R#sr_user.us || R <- Rs];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
get_group_name(Host, Group) ->
|
||||
get_group_opt(Host, Group, name, Group).
|
||||
|
||||
get_user_displayed_groups(US) ->
|
||||
Host = element(2, US),
|
||||
DisplayedGroups1 =
|
||||
lists:usort(
|
||||
lists:flatmap(
|
||||
fun(Group) ->
|
||||
case is_group_enabled(Host, Group) of
|
||||
true ->
|
||||
get_group_opt(Host, Group, displayed_groups, []);
|
||||
false ->
|
||||
[]
|
||||
end
|
||||
end, get_user_groups(US))),
|
||||
[Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
|
||||
|
||||
add_user_to_group(Host, US, Group) ->
|
||||
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||
F = fun() ->
|
||||
mnesia:write(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
remove_user_from_group(Host, US, Group) ->
|
||||
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||
F = fun() ->
|
||||
mnesia:delete_object(R)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
@@ -38,7 +38,7 @@ process_local_iq(From, To, #iq{id = ID, type = Type,
|
||||
Node = string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||
Names = get_names(Els, []),
|
||||
|
||||
case get_local_stats(To#jid.server, Node, Names) of
|
||||
case get_local_stats(Node, Names) of
|
||||
{result, Res} ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "query",
|
||||
@@ -66,20 +66,16 @@ get_names([_ | Els], Res) ->
|
||||
|
||||
-define(STAT(Name), {xmlelement, "stat", [{"name", Name}], []}).
|
||||
|
||||
get_local_stats(_Server, [], []) ->
|
||||
get_local_stats([], []) ->
|
||||
{result,
|
||||
[?STAT("users/online"),
|
||||
?STAT("users/total"),
|
||||
?STAT("users/all-hosts/online"),
|
||||
?STAT("users/all-hosts/total")
|
||||
?STAT("users/total")
|
||||
]};
|
||||
|
||||
get_local_stats(Server, [], Names) ->
|
||||
{result, lists:map(fun(Name) ->
|
||||
get_local_stat(Server, [], Name)
|
||||
end, Names)};
|
||||
get_local_stats([], Names) ->
|
||||
{result, lists:map(fun(Name) -> get_local_stat([], Name) end, Names)};
|
||||
|
||||
get_local_stats(_Server, ["running nodes", _], []) ->
|
||||
get_local_stats(["running nodes", _], []) ->
|
||||
{result,
|
||||
[?STAT("time/uptime"),
|
||||
?STAT("time/cputime"),
|
||||
@@ -90,7 +86,7 @@ get_local_stats(_Server, ["running nodes", _], []) ->
|
||||
?STAT("transactions/logged")
|
||||
]};
|
||||
|
||||
get_local_stats(_Server, ["running nodes", ENode], Names) ->
|
||||
get_local_stats(["running nodes", ENode], Names) ->
|
||||
case search_running_node(ENode) of
|
||||
false ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND};
|
||||
@@ -99,7 +95,7 @@ get_local_stats(_Server, ["running nodes", ENode], Names) ->
|
||||
lists:map(fun(Name) -> get_node_stat(Node, Name) end, Names)}
|
||||
end;
|
||||
|
||||
get_local_stats(_Server, _, _) ->
|
||||
get_local_stats(_, _) ->
|
||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}.
|
||||
|
||||
|
||||
@@ -119,40 +115,27 @@ get_local_stats(_Server, _, _) ->
|
||||
[{xmlcdata, Desc}]}]}).
|
||||
|
||||
|
||||
get_local_stat(Server, [], Name) when Name == "users/online" ->
|
||||
case catch ejabberd_sm:get_vh_session_list(Server) of
|
||||
%get_local_stat([], Name) when Name == "time/uptime" ->
|
||||
% ?STATVAL(io_lib:format("~.3f", [element(1, statistics(wall_clock))/1000]),
|
||||
% "seconds");
|
||||
%get_local_stat([], Name) when Name == "time/cputime" ->
|
||||
% ?STATVAL(io_lib:format("~.3f", [element(1, statistics(runtime))/1000]),
|
||||
% "seconds");
|
||||
get_local_stat([], Name) when Name == "users/online" ->
|
||||
case catch ejabberd_sm:dirty_get_sessions_list() of
|
||||
{'EXIT', Reason} ->
|
||||
?STATERR("500", "Internal Server Error");
|
||||
Users ->
|
||||
?STATVAL(integer_to_list(length(Users)), "users")
|
||||
end;
|
||||
|
||||
get_local_stat(Server, [], Name) when Name == "users/total" ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch ejabberd_auth:get_vh_registered_users(Server) of
|
||||
get_local_stat([], Name) when Name == "users/total" ->
|
||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||
{'EXIT', Reason} ->
|
||||
?STATERR("500", "Internal Server Error");
|
||||
Users ->
|
||||
?STATVAL(integer_to_list(length(Users)), "users")
|
||||
end;
|
||||
|
||||
get_local_stat(_Server, [], Name) when Name == "users/all-hosts/online" ->
|
||||
case catch mnesia:table_info(session, size) of
|
||||
{'EXIT', Reason} ->
|
||||
?STATERR("500", "Internal Server Error");
|
||||
Users ->
|
||||
?STATVAL(integer_to_list(Users), "users")
|
||||
end;
|
||||
|
||||
get_local_stat(_Server, [], Name) when Name == "users/all-hosts/total" ->
|
||||
case catch mnesia:table_info(passwd, size) of
|
||||
{'EXIT', Reason} ->
|
||||
?STATERR("500", "Internal Server Error");
|
||||
Users ->
|
||||
?STATVAL(integer_to_list(Users), "users")
|
||||
end;
|
||||
|
||||
get_local_stat(_Server, _, Name) ->
|
||||
get_local_stat(_, Name) ->
|
||||
?STATERR("404", "Not Found").
|
||||
|
||||
|
||||
|
||||
@@ -16,16 +16,15 @@
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
reindex_vcards/0,
|
||||
remove_user/2]).
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
-define(JUD_MATCHES, 30).
|
||||
%-define(JUD_ALLOW_RETURN_ALL, true)
|
||||
|
||||
-record(vcard_search, {us,
|
||||
user, luser,
|
||||
-record(vcard_search, {user, luser,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
@@ -38,7 +37,7 @@
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit
|
||||
}).
|
||||
-record(vcard, {us, vcard}).
|
||||
-record(vcard, {user, vcard}).
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
@@ -47,8 +46,6 @@ start(Opts) ->
|
||||
mnesia:create_table(vcard_search,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, vcard_search)}]),
|
||||
update_tables(),
|
||||
mnesia:add_table_index(vcard_search, luser),
|
||||
mnesia:add_table_index(vcard_search, lfn),
|
||||
mnesia:add_table_index(vcard_search, lfamily),
|
||||
mnesia:add_table_index(vcard_search, lgiven),
|
||||
@@ -61,29 +58,26 @@ start(Opts) ->
|
||||
mnesia:add_table_index(vcard_search, lorgname),
|
||||
mnesia:add_table_index(vcard_search, lorgunit),
|
||||
|
||||
ejabberd_hooks:add(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_VCARD,
|
||||
?MODULE, process_local_iq, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_VCARD,
|
||||
?MODULE, process_sm_iq, IQDisc),
|
||||
catch mod_disco:register_sm_feature(?NS_VCARD),
|
||||
Hosts = gen_mod:get_hosts(Opts, "vjud."),
|
||||
Host = gen_mod:get_opt(host, Opts, "vjud." ++ ?MYNAME),
|
||||
Search = gen_mod:get_opt(search, Opts, true),
|
||||
register(ejabberd_mod_vcard, spawn(?MODULE, init, [Hosts, Search])).
|
||||
register(ejabberd_mod_vcard, spawn(?MODULE, init, [Host, Search])).
|
||||
|
||||
|
||||
init(Hosts, Search) ->
|
||||
init(Host, Search) ->
|
||||
case Search of
|
||||
false ->
|
||||
loop(Hosts);
|
||||
loop(Host);
|
||||
_ ->
|
||||
ejabberd_router:register_routes(Hosts),
|
||||
loop(Hosts)
|
||||
ejabberd_router:register_route(Host),
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
loop(Hosts) ->
|
||||
loop(Host) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
@@ -92,20 +86,17 @@ loop(Hosts) ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Hosts);
|
||||
loop(Host);
|
||||
stop ->
|
||||
catch ejabberd_router:unregister_routes(Hosts),
|
||||
catch ejabberd_router:unregister_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Hosts)
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(remove_user,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_VCARD),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_VCARD),
|
||||
catch mod_disco:unregister_sm_feature(?NS_VCARD),
|
||||
ejabberd_mod_vcard ! stop,
|
||||
ok.
|
||||
|
||||
@@ -127,7 +118,7 @@ process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Erlang Jabber Server\n"
|
||||
"Copyright (c) 2002-2005 Alexey Shchepin")}]},
|
||||
"Copyright (c) 2002-2004 Alexey Shchepin")}]},
|
||||
{xmlelement, "BDAY", [],
|
||||
[{xmlcdata, "2002-11-16"}]}
|
||||
]}]}
|
||||
@@ -138,18 +129,17 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
#jid{user = User, lserver = LServer, luser = LUser} = From,
|
||||
case lists:member(LServer, ?MYHOSTS) of
|
||||
true ->
|
||||
set_vcard(User, LServer, SubEl),
|
||||
case ?MYNAME of
|
||||
LServer ->
|
||||
set_vcard(User, SubEl),
|
||||
IQ#iq{type = result, sub_el = []};
|
||||
false ->
|
||||
_ ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
||||
end;
|
||||
get ->
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
US = {LUser, LServer},
|
||||
#jid{luser = LUser} = To,
|
||||
F = fun() ->
|
||||
mnesia:read({vcard, US})
|
||||
mnesia:read({vcard, LUser})
|
||||
end,
|
||||
Els = case mnesia:transaction(F) of
|
||||
{atomic, Rs} ->
|
||||
@@ -162,7 +152,7 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = result, sub_el = Els}
|
||||
end.
|
||||
|
||||
set_vcard(User, LServer, VCARD) ->
|
||||
set_vcard(User, VCARD) ->
|
||||
FN = xml:get_path_s(VCARD, [{elem, "FN"}, cdata]),
|
||||
Family = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "FAMILY"}, cdata]),
|
||||
Given = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "GIVEN"}, cdata]),
|
||||
@@ -195,8 +185,6 @@ set_vcard(User, LServer, VCARD) ->
|
||||
LOrgName = stringprep:tolower(OrgName),
|
||||
LOrgUnit = stringprep:tolower(OrgUnit),
|
||||
|
||||
US = {LUser, LServer},
|
||||
|
||||
if
|
||||
(LUser == error) or
|
||||
(LFN == error) or
|
||||
@@ -213,11 +201,9 @@ set_vcard(User, LServer, VCARD) ->
|
||||
{error, badarg};
|
||||
true ->
|
||||
F = fun() ->
|
||||
mnesia:write(#vcard{us = US, vcard = VCARD}),
|
||||
mnesia:write(#vcard{user = LUser, vcard = VCARD}),
|
||||
mnesia:write(
|
||||
#vcard_search{us = US,
|
||||
user = {User, LServer},
|
||||
luser = LUser,
|
||||
#vcard_search{user = User, luser = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
@@ -248,10 +234,8 @@ set_vcard(User, LServer, VCARD) ->
|
||||
[{xmlcdata, translate:translate(Lang, "Search users in ") ++
|
||||
jlib:jid_to_string(JID)}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata, translate:translate(Lang, "Fill in the form to search "
|
||||
"for any matching Jabber User "
|
||||
"(Add * to the end of field to "
|
||||
"match substring)")}]},
|
||||
[{xmlcdata, translate:translate(Lang, "Fill in fields to search "
|
||||
"for any matching Jabber User")}]},
|
||||
?TLFIELD("text-single", "User", "user"),
|
||||
?TLFIELD("text-single", "Full Name", "fn"),
|
||||
?TLFIELD("text-single", "Name", "given"),
|
||||
@@ -396,7 +380,7 @@ iq_get_vcard(Lang) ->
|
||||
[{xmlcdata, translate:translate(
|
||||
Lang,
|
||||
"ejabberd vCard module\n"
|
||||
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
|
||||
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||
|
||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||
find_xdata_el1(SubEls).
|
||||
@@ -434,7 +418,7 @@ search_result(Lang, JID, Data) ->
|
||||
?LFIELD("email", "email"),
|
||||
?LFIELD("Organization Name", "orgname"),
|
||||
?LFIELD("Organization Unit", "orgunit")
|
||||
]}] ++ lists:map(fun record_to_item/1, search(JID#jid.lserver, Data)).
|
||||
]}] ++ lists:map(fun record_to_item/1, search(Data)).
|
||||
|
||||
-define(FIELD(Var, Val),
|
||||
{xmlelement, "field", [{"var", Var}],
|
||||
@@ -442,10 +426,9 @@ search_result(Lang, JID, Data) ->
|
||||
[{xmlcdata, Val}]}]}).
|
||||
|
||||
record_to_item(R) ->
|
||||
{User, Server} = R#vcard_search.user,
|
||||
{xmlelement, "item", [],
|
||||
[
|
||||
?FIELD("jid", User ++ "@" ++ Server),
|
||||
{xmlelement, "item", [],
|
||||
[
|
||||
?FIELD("jid", R#vcard_search.user ++ "@" ++ ?MYNAME),
|
||||
?FIELD("fn", R#vcard_search.fn),
|
||||
?FIELD("family", R#vcard_search.family),
|
||||
?FIELD("given", R#vcard_search.given),
|
||||
@@ -460,12 +443,24 @@ record_to_item(R) ->
|
||||
]
|
||||
}.
|
||||
|
||||
-ifdef(JUD_ALLOW_RETURN_ALL).
|
||||
|
||||
search(LServer, Data) ->
|
||||
MatchSpec = make_matchspec(LServer, Data),
|
||||
AllowReturnAll = gen_mod:get_module_opt(?MODULE, allow_return_all, false),
|
||||
search(Data) ->
|
||||
MatchSpec = make_matchspec(Data),
|
||||
case catch mnesia:dirty_select(vcard_search, [{MatchSpec, [], ['$_']}]) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
[];
|
||||
Rs ->
|
||||
Rs
|
||||
end.
|
||||
|
||||
-else.
|
||||
|
||||
search(Data) ->
|
||||
MatchSpec = make_matchspec(Data),
|
||||
if
|
||||
(MatchSpec == #vcard_search{_ = '_'}) and (not AllowReturnAll) ->
|
||||
MatchSpec == #vcard_search{_ = '_'} ->
|
||||
[];
|
||||
true ->
|
||||
case catch mnesia:dirty_select(vcard_search,
|
||||
@@ -474,92 +469,59 @@ search(LServer, Data) ->
|
||||
?ERROR_MSG("~p", [Reason]),
|
||||
[];
|
||||
Rs ->
|
||||
case gen_mod:get_module_opt(?MODULE, matches, ?JUD_MATCHES) of
|
||||
infinity ->
|
||||
Rs;
|
||||
Val when is_integer(Val) and (Val > 0) ->
|
||||
lists:sublist(Rs, Val);
|
||||
Val ->
|
||||
?ERROR_MSG("Illegal option value ~p. "
|
||||
"Default value ~p substituted.",
|
||||
[{matches, Val}, ?JUD_MATCHES]),
|
||||
lists:sublist(Rs, ?JUD_MATCHES)
|
||||
end
|
||||
Rs
|
||||
end
|
||||
end.
|
||||
|
||||
-endif.
|
||||
|
||||
make_matchspec(LServer, Data) ->
|
||||
|
||||
% TODO: remove
|
||||
% F = fun() ->
|
||||
% mnesia:select(vcard_search, [{MatchSpec, [], ['$_']}])
|
||||
% end,
|
||||
% case mnesia:transaction(F) of
|
||||
% {atomic, Rs} ->
|
||||
% Rs;
|
||||
% _ ->
|
||||
% []
|
||||
% end.
|
||||
|
||||
|
||||
make_matchspec(Data) ->
|
||||
GlobMatch = #vcard_search{_ = '_'},
|
||||
Match = filter_fields(Data, GlobMatch, LServer),
|
||||
Match = filter_fields(Data, GlobMatch),
|
||||
Match.
|
||||
|
||||
filter_fields([], Match, _LServer) ->
|
||||
filter_fields([], Match) ->
|
||||
Match;
|
||||
filter_fields([{SVar, [Val]} | Ds], Match, LServer)
|
||||
filter_fields([{SVar, [Val]} | Ds], Match)
|
||||
when is_list(Val) and (Val /= "") ->
|
||||
LVal = stringprep:tolower(Val),
|
||||
NewMatch = case SVar of
|
||||
"user" ->
|
||||
case gen_mod:get_module_opt(
|
||||
?MODULE, search_all_hosts, true) of
|
||||
true ->
|
||||
Match#vcard_search{luser = make_val(LVal)};
|
||||
false ->
|
||||
Host = find_my_host(LServer),
|
||||
Match#vcard_search{us = {make_val(LVal), Host}}
|
||||
end;
|
||||
"fn" -> Match#vcard_search{lfn = make_val(LVal)};
|
||||
"family" -> Match#vcard_search{lfamily = make_val(LVal)};
|
||||
"given" -> Match#vcard_search{lgiven = make_val(LVal)};
|
||||
"middle" -> Match#vcard_search{lmiddle = make_val(LVal)};
|
||||
"nickname" -> Match#vcard_search{lnickname = make_val(LVal)};
|
||||
"bday" -> Match#vcard_search{lbday = make_val(LVal)};
|
||||
"ctry" -> Match#vcard_search{lctry = make_val(LVal)};
|
||||
"locality" -> Match#vcard_search{llocality = make_val(LVal)};
|
||||
"email" -> Match#vcard_search{lemail = make_val(LVal)};
|
||||
"orgname" -> Match#vcard_search{lorgname = make_val(LVal)};
|
||||
"orgunit" -> Match#vcard_search{lorgunit = make_val(LVal)};
|
||||
"user" -> Match#vcard_search{luser = LVal};
|
||||
"fn" -> Match#vcard_search{lfn = LVal};
|
||||
"family" -> Match#vcard_search{lfamily = LVal};
|
||||
"given" -> Match#vcard_search{lgiven = LVal};
|
||||
"middle" -> Match#vcard_search{lmiddle = LVal};
|
||||
"nickname" -> Match#vcard_search{lnickname = LVal};
|
||||
"bday" -> Match#vcard_search{lbday = LVal};
|
||||
"ctry" -> Match#vcard_search{lctry = LVal};
|
||||
"locality" -> Match#vcard_search{llocality = LVal};
|
||||
"email" -> Match#vcard_search{lemail = LVal};
|
||||
"orgname" -> Match#vcard_search{lorgname = LVal};
|
||||
"orgunit" -> Match#vcard_search{lorgunit = LVal};
|
||||
_ -> Match
|
||||
end,
|
||||
filter_fields(Ds, NewMatch, LServer);
|
||||
filter_fields([_ | Ds], Match, LServer) ->
|
||||
filter_fields(Ds, Match, LServer).
|
||||
|
||||
make_val(Val) ->
|
||||
case lists:suffix("*", Val) of
|
||||
true ->
|
||||
lists:sublist(Val, length(Val) - 1) ++ '_';
|
||||
_ ->
|
||||
Val
|
||||
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, $.).
|
||||
|
||||
filter_fields(Ds, NewMatch);
|
||||
filter_fields([_ | Ds], Match) ->
|
||||
filter_fields(Ds, Match).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_vcard_t(R, _) ->
|
||||
US = R#vcard.us,
|
||||
User = US,
|
||||
User = R#vcard.user,
|
||||
VCARD = R#vcard.vcard,
|
||||
|
||||
FN = xml:get_path_s(VCARD, [{elem, "FN"}, cdata]),
|
||||
@@ -574,7 +536,7 @@ set_vcard_t(R, _) ->
|
||||
OrgName = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGNAME"}, cdata]),
|
||||
OrgUnit = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGUNIT"}, cdata]),
|
||||
|
||||
{LUser, _LServer} = US,
|
||||
LUser = jlib:nodeprep(User),
|
||||
LFN = stringprep:tolower(FN),
|
||||
LFamily = stringprep:tolower(Family),
|
||||
LGiven = stringprep:tolower(Given),
|
||||
@@ -603,8 +565,7 @@ set_vcard_t(R, _) ->
|
||||
{error, badarg};
|
||||
true ->
|
||||
mnesia:write(
|
||||
#vcard_search{us = US,
|
||||
user = User, luser = LUser,
|
||||
#vcard_search{user = User, luser = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
@@ -627,159 +588,16 @@ reindex_vcards() ->
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
remove_user(User, Server) ->
|
||||
remove_user(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
mnesia:delete({vcard, US}),
|
||||
mnesia:delete({vcard_search, US})
|
||||
mnesia:delete({vcard, LUser}),
|
||||
lists:foreach(fun(R) ->
|
||||
mnesia:delete_object(R)
|
||||
end,
|
||||
mnesia:index_read(vcard_search,
|
||||
LUser,
|
||||
#vcard_search.luser))
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
|
||||
update_tables() ->
|
||||
update_vcard_table(),
|
||||
update_vcard_search_table().
|
||||
|
||||
update_vcard_table() ->
|
||||
Fields = record_info(fields, vcard),
|
||||
case mnesia:table_info(vcard, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, vcard] ->
|
||||
?INFO_MSG("Converting vcard table from "
|
||||
"{user, vcard} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_vcard_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, vcard},
|
||||
{attributes, record_info(fields, vcard)}]),
|
||||
mnesia:transform_table(vcard, ignore, Fields),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_vcard_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun(#vcard{us = U} = R, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_vcard_tmp_table,
|
||||
R#vcard{us = {U, Host}})
|
||||
end, ok, vcard)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
mnesia:clear_table(vcard),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(vcard),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_vcard_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_vcard_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating vcard table", []),
|
||||
mnesia:transform_table(vcard, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
update_vcard_search_table() ->
|
||||
Fields = record_info(fields, vcard_search),
|
||||
case mnesia:table_info(vcard_search, attributes) of
|
||||
Fields ->
|
||||
ok;
|
||||
[user, luser,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
middle, lmiddle,
|
||||
nickname, lnickname,
|
||||
bday, lbday,
|
||||
ctry, lctry,
|
||||
locality, llocality,
|
||||
email, lemail,
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit] ->
|
||||
?INFO_MSG("Converting vcard_search table from "
|
||||
"{user, luser, fn, lfn, family, lfamily, given, lgiven, middle, lmiddle, nickname, lnickname, bday, lbday, ctry, lctry, locality, llocality, email, lemail, orgname, lorgname, orgunit, lorgunit} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_vcard_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, vcard_search},
|
||||
{attributes, record_info(fields, vcard_search)}]),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_vcard_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun({vcard_search,
|
||||
User, LUser,
|
||||
FN, LFN,
|
||||
Family, LFamily,
|
||||
Given, LGiven,
|
||||
Middle, LMiddle,
|
||||
Nickname, LNickname,
|
||||
BDay, LBDay,
|
||||
CTRY, LCTRY,
|
||||
Locality, LLocality,
|
||||
EMail, LEMail,
|
||||
OrgName, LOrgName,
|
||||
OrgUnit, LOrgUnit
|
||||
}, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_vcard_tmp_table,
|
||||
#vcard_search{
|
||||
us = {LUser, Host},
|
||||
user = {User, Host},
|
||||
luser = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
middle = Middle, lmiddle = LMiddle,
|
||||
nickname = Nickname, lnickname = LNickname,
|
||||
bday = BDay, lbday = LBDay,
|
||||
ctry = CTRY, lctry = LCTRY,
|
||||
locality = Locality, llocality = LLocality,
|
||||
email = EMail, lemail = LEMail,
|
||||
orgname = OrgName, lorgname = LOrgName,
|
||||
orgunit = OrgUnit, lorgunit = LOrgUnit
|
||||
})
|
||||
end, ok, vcard_search)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
lists:foreach(fun(I) ->
|
||||
mnesia:del_table_index(
|
||||
vcard_search,
|
||||
element(I, {vcard_search,
|
||||
user, luser,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
middle, lmiddle,
|
||||
nickname, lnickname,
|
||||
bday, lbday,
|
||||
ctry, lctry,
|
||||
locality, llocality,
|
||||
email, lemail,
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit}))
|
||||
end, mnesia:table_info(vcard_search, index)),
|
||||
mnesia:clear_table(vcard_search),
|
||||
mnesia:transform_table(vcard_search, ignore, Fields),
|
||||
F2 = fun() ->
|
||||
mnesia:write_lock_table(vcard_search),
|
||||
mnesia:foldl(
|
||||
fun(R, _) ->
|
||||
mnesia:dirty_write(R)
|
||||
end, ok, mod_vcard_tmp_table)
|
||||
end,
|
||||
mnesia:transaction(F2),
|
||||
mnesia:delete_table(mod_vcard_tmp_table);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating vcard_search table", []),
|
||||
mnesia:transform_table(vcard_search, ignore, Fields)
|
||||
end.
|
||||
|
||||
|
||||
@@ -1,576 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_vcard_ldap.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_vcard_ldap).
|
||||
-author('alexey@sevcom.net').
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1, init/2, stop/0,
|
||||
process_local_iq/3,
|
||||
process_sm_iq/3,
|
||||
remove_user/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("eldap/eldap.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
|
||||
start(Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_VCARD,
|
||||
?MODULE, process_local_iq, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_VCARD,
|
||||
?MODULE, process_sm_iq, IQDisc),
|
||||
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
||||
RootDN = ejabberd_config:get_local_option(ldap_rootdn),
|
||||
Password = ejabberd_config:get_local_option(ldap_password),
|
||||
eldap:start_link("mod_vcard_ldap", LDAPServers, 389, RootDN, Password),
|
||||
Host = gen_mod:get_opt(host, Opts, "vjud." ++ ?MYNAME),
|
||||
Search = gen_mod:get_opt(search, Opts, true),
|
||||
register(ejabberd_mod_vcard_ldap, spawn(?MODULE, init, [Host, Search])).
|
||||
|
||||
init(Host, Search) ->
|
||||
case Search of
|
||||
false ->
|
||||
loop(Host);
|
||||
_ ->
|
||||
ejabberd_router:register_route(Host),
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
loop(Host) ->
|
||||
receive
|
||||
{route, From, To, Packet} ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
loop(Host);
|
||||
stop ->
|
||||
catch ejabberd_router:unregister_route(Host),
|
||||
ok;
|
||||
_ ->
|
||||
loop(Host)
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_VCARD),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_VCARD),
|
||||
ejabberd_mod_vcard_ldap ! stop,
|
||||
ok.
|
||||
|
||||
process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "vCard",
|
||||
[{"xmlns", ?NS_VCARD}],
|
||||
[{xmlelement, "FN", [],
|
||||
[{xmlcdata, "ejabberd"}]},
|
||||
{xmlelement, "URL", [],
|
||||
[{xmlcdata,
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata,
|
||||
translate:translate(
|
||||
Lang,
|
||||
"Erlang Jabber Server\n"
|
||||
"Copyright (c) 2002-2005 Alexey Shchepin")}]},
|
||||
{xmlelement, "BDAY", [],
|
||||
[{xmlcdata, "2002-11-16"}]}
|
||||
]}]}
|
||||
end.
|
||||
|
||||
find_ldap_user(User) ->
|
||||
Attr = ejabberd_config:get_local_option(ldap_uidattr),
|
||||
Filter = eldap:equalityMatch(Attr, User),
|
||||
Base = ejabberd_config:get_local_option(ldap_base),
|
||||
case eldap:search("mod_vcard_ldap", [{base, Base},
|
||||
{filter, Filter},
|
||||
{attributes, []}]) of
|
||||
#eldap_search_result{entries = [E | _]} ->
|
||||
E;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
is_attribute_read_allowed(Name,From,To) ->
|
||||
true.
|
||||
|
||||
ldap_attribute_to_vcard(Prefix,{Name,Values},From,To) ->
|
||||
case is_attribute_read_allowed(Name,From,To) of
|
||||
true ->
|
||||
ldap_lca_to_vcard(Prefix,stringprep:tolower(Name),Values);
|
||||
_ ->
|
||||
none
|
||||
end.
|
||||
|
||||
ldap_lca_to_vcard(vCard,"displayname",[Value|_]) ->
|
||||
{xmlelement,"FN",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCard,"uid",[Value|_]) ->
|
||||
{xmlelement,"NICKNAME",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCard,"title",[Value|_]) ->
|
||||
{xmlelement,"TITLE",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCard,"labeleduri",[Value|_]) ->
|
||||
{xmlelement,"URL",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCard,"description",[Value|_]) ->
|
||||
{xmlelement,"DESC",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCard,"telephonenumber",[Value|_]) ->
|
||||
{xmlelement,"TEL",[],[{xmlelement,"VOICE",[],[]},
|
||||
{xmlelement,"WORK",[],[]},
|
||||
{xmlelement,"NUMBER",[],[{xmlcdata,Value}]}]};
|
||||
|
||||
ldap_lca_to_vcard(vCard,"mail",[Value|_]) ->
|
||||
{xmlelement,"EMAIL",[],[{xmlelement,"INTERNET",[],[]},
|
||||
{xmlelement,"PREF",[],[]},
|
||||
{xmlelement,"USERID",[],[{xmlcdata,Value}]}]};
|
||||
|
||||
ldap_lca_to_vcard(vCardN,"sn",[Value|_]) ->
|
||||
{xmlelement,"FAMILY",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCardN,"givenname",[Value|_]) ->
|
||||
{xmlelement,"GIVEN",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCardN,"initials",[Value|_]) ->
|
||||
{xmlelement,"MIDDLE",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCardO,"o",[Value|_]) ->
|
||||
{xmlelement,"ORGNAME",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(vCardO,"ou",[Value|_]) ->
|
||||
{xmlelement,"ORGUNIT",[],[{xmlcdata,Value}]};
|
||||
|
||||
ldap_lca_to_vcard(_,_,_) -> none.
|
||||
|
||||
ldap_attributes_to_vcard(Attributes,From,To) ->
|
||||
Elts = lists:map(fun(Attr) ->
|
||||
ldap_attribute_to_vcard(vCard,Attr,From,To)
|
||||
end,Attributes),
|
||||
FElts = [ X || X <- Elts, X /= none ],
|
||||
NElts = lists:map(fun(Attr) ->
|
||||
ldap_attribute_to_vcard(vCardN,Attr,From,To)
|
||||
end,Attributes),
|
||||
FNElts = [ X || X <- NElts, X /= none ],
|
||||
OElts = lists:map(fun(Attr) ->
|
||||
ldap_attribute_to_vcard(vCardO,Attr,From,To)
|
||||
end,Attributes),
|
||||
FOElts = [ X || X <- OElts, X /= none ],
|
||||
[{xmlelement, "vCard", [{"xmlns", ?NS_VCARD}],
|
||||
lists:append(FElts,
|
||||
[{xmlelement,"N",[],FNElts},
|
||||
{xmlelement,"ORG",[],FOElts}])
|
||||
}].
|
||||
|
||||
is_self_request(From,To) ->
|
||||
#jid{luser = RUser, lserver = RServer } = From,
|
||||
#jid{luser = LUser} = To,
|
||||
case RServer == ?MYNAME of
|
||||
true ->
|
||||
LUser == RUser;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
#jid{luser = LUser} = To,
|
||||
case find_ldap_user(LUser) of
|
||||
#eldap_entry{attributes = Attributes} ->
|
||||
Vcard = ldap_attributes_to_vcard(Attributes,From,To),
|
||||
IQ#iq{type = result, sub_el = Vcard};
|
||||
_ -> IQ#iq{type = result, sub_el = []}
|
||||
end
|
||||
end.
|
||||
|
||||
-define(TLFIELD(Type, Label, Var),
|
||||
{xmlelement, "field", [{"type", Type},
|
||||
{"label", translate:translate(Lang, Label)},
|
||||
{"var", Var}], []}).
|
||||
|
||||
|
||||
-define(FORM(JID),
|
||||
[{xmlelement, "instructions", [],
|
||||
[{xmlcdata, translate:translate(Lang, "You need an x:data capable client to search")}]},
|
||||
{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata, translate:translate(Lang, "Search users in ") ++
|
||||
jlib:jid_to_string(JID)}]},
|
||||
{xmlelement, "instructions", [],
|
||||
[{xmlcdata, translate:translate(Lang, "Fill in fields to search "
|
||||
"for any matching Jabber User")}]},
|
||||
?TLFIELD("text-single", "User", "user"),
|
||||
?TLFIELD("text-single", "Full Name", "fn"),
|
||||
?TLFIELD("text-single", "Given Name", "given"),
|
||||
?TLFIELD("text-single", "Middle Name", "middle"),
|
||||
?TLFIELD("text-single", "Family Name", "family"),
|
||||
?TLFIELD("text-single", "Nickname", "nickname"),
|
||||
?TLFIELD("text-single", "Birthday", "bday"),
|
||||
?TLFIELD("text-single", "Country", "ctry"),
|
||||
?TLFIELD("text-single", "City", "locality"),
|
||||
?TLFIELD("text-single", "email", "email"),
|
||||
?TLFIELD("text-single", "Organization Name", "orgname"),
|
||||
?TLFIELD("text-single", "Organization Unit", "orgunit")
|
||||
]}]).
|
||||
|
||||
|
||||
|
||||
|
||||
do_route(From, To, Packet) ->
|
||||
#jid{user = User, resource = Resource} = To,
|
||||
if
|
||||
(User /= "") or (Resource /= "") ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router ! {route, To, From, Err};
|
||||
true ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
#iq{type = Type, xmlns = ?NS_SEARCH, lang = Lang, sub_el = SubEl} ->
|
||||
case Type of
|
||||
set ->
|
||||
XDataEl = find_xdata_el(SubEl),
|
||||
case XDataEl of
|
||||
false ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_BAD_REQUEST),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
_ ->
|
||||
XData = jlib:parse_xdata_submit(XDataEl),
|
||||
case XData of
|
||||
invalid ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet,
|
||||
?ERR_BAD_REQUEST),
|
||||
ejabberd_router:route(To, From,
|
||||
Err);
|
||||
_ ->
|
||||
ResIQ =
|
||||
IQ#iq{
|
||||
type = result,
|
||||
sub_el =
|
||||
[{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_SEARCH}],
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_XDATA},
|
||||
{"type", "result"}],
|
||||
search_result(Lang, To, XData)
|
||||
}]}]},
|
||||
ejabberd_router:route(
|
||||
To, From, jlib:iq_to_xml(ResIQ))
|
||||
end
|
||||
end;
|
||||
get ->
|
||||
ResIQ = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_SEARCH}],
|
||||
?FORM(To)
|
||||
}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(ResIQ))
|
||||
end;
|
||||
#iq{type = Type, xmlns = ?NS_DISCO_INFO, sub_el = SubEl} ->
|
||||
case Type of
|
||||
set ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
get ->
|
||||
ResIQ =
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_DISCO_INFO}],
|
||||
[{xmlelement, "identity",
|
||||
[{"category", "directory"},
|
||||
{"type", "user"},
|
||||
{"name",
|
||||
"vCard User Search"}],
|
||||
[]},
|
||||
{xmlelement, "feature",
|
||||
[{"var", ?NS_SEARCH}], []},
|
||||
{xmlelement, "feature",
|
||||
[{"var", ?NS_VCARD}], []}
|
||||
]
|
||||
}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(ResIQ))
|
||||
end;
|
||||
#iq{type = Type, xmlns = ?NS_DISCO_ITEMS, sub_el = SubEl} ->
|
||||
case Type of
|
||||
set ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
get ->
|
||||
ResIQ =
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
[{"xmlns", ?NS_DISCO_INFO}],
|
||||
[]}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(ResIQ))
|
||||
end;
|
||||
#iq{type = get, xmlns = ?NS_VCARD, lang = Lang} ->
|
||||
ResIQ =
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"vCard",
|
||||
[{"xmlns", ?NS_VCARD}],
|
||||
iq_get_vcard(Lang)}]},
|
||||
ejabberd_router:route(To,
|
||||
From,
|
||||
jlib:iq_to_xml(ResIQ));
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
?ERR_SERVICE_UNAVAILABLE),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end.
|
||||
|
||||
iq_get_vcard(Lang) ->
|
||||
[{xmlelement, "FN", [],
|
||||
[{xmlcdata, "ejabberd/mod_vcard"}]},
|
||||
{xmlelement, "URL", [],
|
||||
[{xmlcdata,
|
||||
"http://ejabberd.jabberstudio.org/"}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, translate:translate(
|
||||
Lang,
|
||||
"ejabberd vCard module\n"
|
||||
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
|
||||
|
||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||
find_xdata_el1(SubEls).
|
||||
|
||||
find_xdata_el1([]) ->
|
||||
false;
|
||||
find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_XDATA ->
|
||||
{xmlelement, Name, Attrs, SubEls};
|
||||
_ ->
|
||||
find_xdata_el1(Els)
|
||||
end;
|
||||
find_xdata_el1([_ | Els]) ->
|
||||
find_xdata_el1(Els).
|
||||
|
||||
-define(LFIELD(Label, Var),
|
||||
{xmlelement, "field", [{"label", translate:translate(Lang, Label)},
|
||||
{"var", Var}], []}).
|
||||
|
||||
search_result(Lang, JID, Data) ->
|
||||
[{xmlelement, "title", [],
|
||||
[{xmlcdata, translate:translate(Lang, "Results of search in ") ++
|
||||
jlib:jid_to_string(JID)}]},
|
||||
{xmlelement, "reported", [],
|
||||
[?LFIELD("JID", "jid"),
|
||||
?LFIELD("Full Name", "fn"),
|
||||
?LFIELD("Given Name", "given"),
|
||||
?LFIELD("Middle Name", "middle"),
|
||||
?LFIELD("Family Name", "family"),
|
||||
?LFIELD("Nickname", "nickname"),
|
||||
?LFIELD("Birthday", "bday"),
|
||||
?LFIELD("Country", "ctry"),
|
||||
?LFIELD("City", "locality"),
|
||||
?LFIELD("email", "email"),
|
||||
?LFIELD("Organization Name", "orgname"),
|
||||
?LFIELD("Organization Unit", "orgunit")
|
||||
]}] ++ lists:map(fun(E) ->
|
||||
record_to_item(E#eldap_entry.attributes)
|
||||
end, search(JID#jid.lserver, Data)).
|
||||
|
||||
-define(FIELD(Var, Val),
|
||||
{xmlelement, "field", [{"var", Var}],
|
||||
[{xmlelement, "value", [],
|
||||
[{xmlcdata, Val}]}]}).
|
||||
|
||||
case_exact_compare(none,_) ->
|
||||
false;
|
||||
case_exact_compare(_,none) ->
|
||||
false;
|
||||
case_exact_compare(X,Y) ->
|
||||
X > Y.
|
||||
|
||||
ldap_sort_entries(L) ->
|
||||
lists:sort(fun(E1,E2) ->
|
||||
case_exact_compare(ldap_get_value(E1,"cn"),ldap_get_value(E2,"cn"))
|
||||
end,L).
|
||||
|
||||
ldap_get_value(E,Attribute) ->
|
||||
#eldap_entry{attributes = Attributes} = E,
|
||||
case lists:filter(fun({A,_}) ->
|
||||
string:equal(A,Attribute)
|
||||
end,Attributes) of
|
||||
[{Attr,[Value|_]}] ->
|
||||
Value;
|
||||
_ ->
|
||||
none
|
||||
end.
|
||||
|
||||
|
||||
ldap_attribute_to_item("uid",Value) ->
|
||||
[
|
||||
?FIELD("jid",Value ++ "@" ++ ?MYNAME),
|
||||
?FIELD("uid",Value),
|
||||
?FIELD("nickname",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item("displayname",Value) ->
|
||||
[
|
||||
?FIELD("fn",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item("sn",Value) ->
|
||||
[
|
||||
?FIELD("family",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item("displayname",Value) ->
|
||||
[
|
||||
?FIELD("fn",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item("givenname",Value) ->
|
||||
[
|
||||
?FIELD("given",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item("initials",Value) ->
|
||||
[
|
||||
?FIELD("middle",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item("mail",Value) ->
|
||||
[
|
||||
?FIELD("email",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item("o",Value) ->
|
||||
[
|
||||
?FIELD("orgname",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item("ou",Value) ->
|
||||
[
|
||||
?FIELD("orgunit",Value)
|
||||
];
|
||||
|
||||
ldap_attribute_to_item(_,_) ->
|
||||
[none].
|
||||
|
||||
record_to_item(Attributes) ->
|
||||
List = lists:append(lists:map(fun({Attr,[Value|_]}) ->
|
||||
ldap_attribute_to_item(stringprep:tolower(Attr),Value)
|
||||
end,Attributes)),
|
||||
FList = [X || X <- List, X /= none],
|
||||
{xmlelement, "item", [],FList}.
|
||||
|
||||
search(LServer, Data) ->
|
||||
Filter = make_filter(Data),
|
||||
Base = ejabberd_config:get_local_option(ldap_base),
|
||||
UIDAttr = ejabberd_config:get_local_option(ldap_uidattr),
|
||||
case eldap:search("mod_vcard_ldap",[{base, Base},
|
||||
{filter, Filter},
|
||||
{attributes, []}]) of
|
||||
#eldap_search_result{entries = E} ->
|
||||
[X || X <- E,
|
||||
ejabberd_auth:is_user_exists(
|
||||
ldap_get_value(X, UIDAttr), LServer)];
|
||||
_ ->
|
||||
?ERROR_MSG("~p", ["Bad search"])
|
||||
end.
|
||||
|
||||
|
||||
make_filter(Data) ->
|
||||
Filter = [X || X <- lists:map(fun(R) ->
|
||||
make_assertion(R)
|
||||
end,Data),
|
||||
X /= none ],
|
||||
case Filter of
|
||||
[F] ->
|
||||
F;
|
||||
_ ->
|
||||
eldap:'and'(Filter)
|
||||
end.
|
||||
|
||||
|
||||
make_assertion("givenName",Value) ->
|
||||
eldap:substrings("givenName",[{any,Value}]);
|
||||
|
||||
make_assertion("cn",Value) ->
|
||||
eldap:substrings("cn",[{any,Value}]);
|
||||
|
||||
make_assertion("sn",Value) ->
|
||||
eldap:substrings("sn",[{any,Value}]);
|
||||
|
||||
make_assertion(Attr, Value) ->
|
||||
eldap:equalityMatch(Attr,Value).
|
||||
|
||||
make_assertion({SVar, [Val]}) ->
|
||||
LAttr = ldap_attribute(SVar),
|
||||
case LAttr of
|
||||
none ->
|
||||
none;
|
||||
_ ->
|
||||
if
|
||||
is_list(Val) and (Val /= "") ->
|
||||
make_assertion(LAttr,Val);
|
||||
true ->
|
||||
none
|
||||
end
|
||||
end.
|
||||
|
||||
ldap_attribute("user") ->
|
||||
"uid";
|
||||
|
||||
ldap_attribute("fn") ->
|
||||
"cn";
|
||||
|
||||
ldap_attribute("family") ->
|
||||
"sn";
|
||||
|
||||
ldap_attribute("given") ->
|
||||
"givenName";
|
||||
|
||||
ldap_attribute("middle") ->
|
||||
"initials";
|
||||
|
||||
ldap_attribute("email") ->
|
||||
"mail";
|
||||
|
||||
ldap_attribute("orgname") ->
|
||||
"o";
|
||||
|
||||
ldap_attribute("orgunit") ->
|
||||
"ou";
|
||||
|
||||
ldap_attribute(_) ->
|
||||
none.
|
||||
|
||||
remove_user(User) ->
|
||||
true.
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
% $Id$
|
||||
|
||||
% jlib.hrl
|
||||
{"No resource provided", "Keine Ressource zur Verfügung"}.
|
||||
{"Illegal resource format", "Illegales Ressource Format"}.
|
||||
{"Resource conflict", "Ressourcen Konflikt"}.
|
||||
|
||||
% mod_configure.erl
|
||||
{"DB Tables Configuration", "DB Tabellen Konfiguration"}.
|
||||
{"Choose storage type of tables", "Wähle Speichertyp der Tabellen"}.
|
||||
{"RAM copy", "Kopie im RAM"}.
|
||||
{"RAM and disc copy", "Kopie im RAM und auf der Festplatte"}.
|
||||
{"Disc only copy", "Kopie nur auf Festplatte"}.
|
||||
{"Remote copy", "Fernkopie"}.
|
||||
{"Stop Modules at ", "Stop Module bei"}.
|
||||
{"Choose modules to stop", "Wähle zu stoppende Module"}.
|
||||
{"Start Modules at ", "Start Module bei"}.
|
||||
{"Enter list of {Module, [Options]}", "Geben Sie die Liste der Module ein {Module, [Optionen]}"}.
|
||||
{"List of modules to start", "Liste der zu startenden Module"}.
|
||||
{"Backup to File at ", "Datensicherung bei "}.
|
||||
{"Enter path to backup file", "Geben Sie den Pfad zur Datensicherung ein"}.
|
||||
{"Path to File", "Pfad zur Datei"}.
|
||||
{"Restore Backup from File at ", "Datenwiederherstellung von Datei "}.
|
||||
{"Dump Backup to Text File at ", "Ausgabe der Sicherung in diese Textdatei "}.
|
||||
{"Enter path to text file", "Geben Sie den Pfad zur Textdatei ein"}.
|
||||
{"Import User from File at ", "Benutzer aus dieser Datei importieren "}.
|
||||
{"Enter path to jabberd1.4 spool file", "Geben Sie den Pfad zur jabberd1.4 spool Datei ein"}.
|
||||
{"Import Users from Dir at ", "Benutzer vom Verzeichnis importieren "}.
|
||||
{"Enter path to jabberd1.4 spool dir", "Geben Sie den Pfad zum jabberd1.4 spool Verzeichnis ein"}.
|
||||
{"Path to Dir", "Pfad zum Verzeichnis"}.
|
||||
{"Hostname Configuration", "Konfiguration des Hostnamens"}.
|
||||
{"Choose host name", "Wählen Sie den Hostnamen"}.
|
||||
{"Host name", "Hostname"}.
|
||||
{"Access Control List Configuration", "Access Control List(ACL) Konfiguration"}.
|
||||
{"Access control lists", "Access Control Listen"}.
|
||||
{"Access Configuration", "Zugangskonfiguration"}.
|
||||
{"Access rules", "Zugangsregeln"}.
|
||||
{"Remove Users", "Benutzer löschen"}.
|
||||
{"Choose users to remove", "Wählen Sie die zu löschenden Benutzer"}.
|
||||
{"Administration of ", "Administration der"}.
|
||||
{"Action on user", "Aktion auf Benutzer"}.
|
||||
{"Edit Properties", "Einstellungen ändern"}.
|
||||
{"Remove User", "Benutzer löschen"}.
|
||||
|
||||
% mod_disco.erl
|
||||
{"Configuration", "Konfiguration"}.
|
||||
{"Online Users", "Online Benutzer"}.
|
||||
{"All Users", "Alle Benutzer"}.
|
||||
{"Outgoing S2S connections", "Rausgehende S2S Verbindungen"}.
|
||||
{"To ~s", "An ~s"}.
|
||||
{"From ~s", "Vom ~s"}.
|
||||
{"Running Nodes", "Aktive Knoten"}.
|
||||
{"Stopped Nodes", "Inaktive Knoten"}.
|
||||
{"Host Name", "Hostname"}.
|
||||
{"Access Control Lists", "Zugangskontrolllisten"}.
|
||||
{"Access Rules", "Zugangsregeln"}.
|
||||
{"Remove Users", "Benutzer löschen"}.
|
||||
{"DB", "DB"}.
|
||||
{"Modules", "Module"}.
|
||||
{"Start Modules", "Module starten"}.
|
||||
{"Stop Modules", "Module stoppen"}.
|
||||
{"Backup Management", "Datensicherungsmanagement"}.
|
||||
{"Import users from jabberd1.4 spool files", "Benutzer von jabberd1.4 spool Dateien importieren"}.
|
||||
{"Backup", "Sicherung"}.
|
||||
{"Restore", "Wiederherstellung"}.
|
||||
{"Dump to Text File", "Ausgabe in Textdatei"}.
|
||||
{"Import File", "Datei importieren"}.
|
||||
{"Import Directory", "Verzeichnis importieren"}.
|
||||
|
||||
% mod_register.erl
|
||||
{"Choose a username and password to register with this server",
|
||||
"Wählen Sie zum Registrieren einen Benutzernamen und ein Passwort"}.
|
||||
|
||||
% mod_vcard.erl
|
||||
{"Erlang Jabber Server\nCopyright (c) 2002-2004 Alexey Shchepin", ""}.
|
||||
{"ejabberd vCard module\nCopyright (c) 2003-2004 Alexey Shchepin", ""}.
|
||||
{"You need an x:data capable client to search",
|
||||
"Sie brauchen einen x:data kompatiblen Client um zu suchen"}.
|
||||
{"Search users in ", "Benutzer suchen in "}.
|
||||
{"Fill in fields to search for any matching Jabber User",
|
||||
"Felder ausfüllen, um nach passenden Jabber Benutzern zu suchen"}.
|
||||
{"Results of search in ", "Ergebnisse der Suche in "}.
|
||||
|
||||
{"User", "User"}.
|
||||
{"Full Name", "Ganzer Name"}.
|
||||
{"Name", "Vorname"}.
|
||||
{"Middle Name", "Mittelname"}.
|
||||
{"Family Name", "Nachname"}.
|
||||
{"Nickname", "Spitzname"}.
|
||||
{"Birthday", "Geburtsdatum"}.
|
||||
{"Country", "Land"}.
|
||||
{"City", "Stadt"}.
|
||||
{"email", "E-Mail"}.
|
||||
{"Organization Name", "Firmenname"}.
|
||||
{"Organization Unit", "Organisationseinheit"}.
|
||||
|
||||
% mod_pubsub/mod_pubsub.erl
|
||||
{"ejabberd pub/sub module\nCopyright (c) 2003-2004 Alexey Shchepin", ""}.
|
||||
|
||||
% mod_muc/mod_muc.erl
|
||||
{"You need an x:data capable client to register nickname",
|
||||
"Sie brauchen einen für x:data geeigneten Client, um sich mit dem Spitznamen zu registrieren"}.
|
||||
{"Nickname Registration at ", "Registrieren des Spitznamens "}.
|
||||
{"Enter nickname you want to register", "Geben Sie den zu registrierenden Spitznamen ein"}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin", ""}.
|
||||
{"Only service administrators are allowed to send service messages",
|
||||
"Nur Service Administratoren sind berechtigt, Servicenachrichten zu lesen"}.
|
||||
{"Room creation is not allowed by service policy", "Eröffnung eines neuen Raumes ist in der Service Policy nicht erlaubt"}.
|
||||
{"Conference room does not exist", "Konferenzraum existiert nicht"}.
|
||||
{"Access denied by service policy", "Zutritt verweigert von der Service Policy"}.
|
||||
{"You must fill in field \"nick\" in the form", "Sie müssen etwas ins Feld \"nick\" eintragen"}.
|
||||
{"Specified nickname is already registered", "Der angegebene Name ist bereits vergeben"}.
|
||||
|
||||
% mod_muc/mod_muc_room.erl
|
||||
{" has set the subject to: ", "hat das Thema geändert zu"}.
|
||||
{"You need an x:data capable client to configure room",
|
||||
"Sie brauchen einen für x:data geeigneten Client um den Raum zu konfigurieren"}.
|
||||
{"Configuration for ", "Konfiguration für"}.
|
||||
{"Room title", "Raumname"}.
|
||||
{"Allow users to change subject?", "Benutzer dürfen das Thema ändern?"}.
|
||||
{"Allow users to query other users?",
|
||||
"Benutzer dürfen andere Benutzer anfragen(query)?"}.
|
||||
{"Allow users to send private messages?",
|
||||
"Benutzer dürfen private Nachrichten verschicken?"}.
|
||||
{"Make room public searchable?", "Den Raum öffentlich dursuchbar machen?"}.
|
||||
{"Make participants list public?", "Die Teilnehmerliste öffentlich machen?"}.
|
||||
{"Make room persistent?", "Den Raum persistent machen?"}.
|
||||
{"Make room moderated?", "Den Raum moderiert machen?"}.
|
||||
{"Default users as members?",
|
||||
"Standardbenutzer als Mitglieder ansehen?"}.
|
||||
{"Make room members only?",
|
||||
"Den Raum nur für Mitglieder zugänglich machen?"}.
|
||||
{"Allow users to send invites?",
|
||||
"Benutzer dürfen Einladungen senden?"}.
|
||||
{"Make room password protected?", "Den Raum passwort geschützt machen?"}.
|
||||
{"Password", "Passwort"}.
|
||||
{"Make room anonymous?", "Den Raum anonym machen?"}.
|
||||
{"Enable logging?", "Nachrichten loggen?"}.
|
||||
{"Only moderators and participants are allowed to change subject in this room",
|
||||
"Nur Moderatoren und Teilnehmer dürfen das Thema in diesem Raum ändern"}.
|
||||
{"Only moderators are allowed to change subject in this room",
|
||||
"Nur Moderatoren dürfen das Thema in diesem Raum ändern"}.
|
||||
{"Visitors are not allowed to send messages to all occupants",
|
||||
"Besucher dürfen nicht an alle im Raum Nachrichten verschicken"}.
|
||||
{"Only occupants are allowed to send messages to the conference",
|
||||
"Nur Leute im Raum dürfen Nachrichten an die Konferenz schicken"}.
|
||||
{"It is not allowed to send normal messages to the conference",
|
||||
"Es ist nicht erlaubt normale Nachrichten an die Konferenz zu schicken"}.
|
||||
{"It is not allowed to send private messages to the conference",
|
||||
"Es ist nicht erlaubt private Nachrichten an die Konferenz zu schicken"}.
|
||||
{"Improper message type", "Unzulässiger Nachrichtentyp"}.
|
||||
{"Nickname is already in use by another occupant", "Spitzname wird schon von jemand anderem genutzt"}.
|
||||
{"Nickname is registered by another person", "Spitzname wurde schon von jemand anderem registriert"}.
|
||||
{"It is not allowed to send private messages of type \"groupchat\"",
|
||||
"Es ist nicht erlaubt private Nachrichten des Typs \"Gruppenchat\" zu senden"}.
|
||||
{"Recipient is not in the conference room", "Der Empfänger ist nicht im Konferenzraum"}.
|
||||
{"Only occupants are allowed to send queries to the conference",
|
||||
"Nur Teilnehmer sind berechtig Anfragen an die Konferenz zu senden"}.
|
||||
{"Queries to the conference members are not allowed in this room",
|
||||
"Anfragen an die Konferenzmitglieder sind in diesem Raum nicht erlaubt"}.
|
||||
{"You have been banned from this room", "Sie wurden aus diesem Raum verbannt"}.
|
||||
{"Membership required to enter this room", "Um diesen Raum zu betreten müssen sie ein Mitglied sein"}.
|
||||
{"Password required to enter this room", "Sie brauchen ein Passwort um diesen Raum zu betreten"}.
|
||||
{"Incorrect password", "Falsches Passwort"}.
|
||||
{"Administrator privileges required", "Administratorenrechte benötigt"}.
|
||||
{"Moderator privileges required", "Moderatorrechte benötigt"}.
|
||||
{"JID ~s is invalid", "JID ~s ist ungültig"}.
|
||||
{"Nickname ~s does not exist in the room", "Spitzname ~s funktioniert im Raum nicht"}.
|
||||
{"Invalid affiliation: ~s", "Ungültige Option: ~s"}.
|
||||
{"Invalid role: ~s", "Ungültige Funktion: ~s"}.
|
||||
{"Owner privileges required", "Besitzerrechte benötigt"}.
|
||||
{"private, ", "privat"}.
|
||||
|
||||
% mod_irc/mod_irc.erl
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin", ""}.
|
||||
{"You need an x:data capable client to configure mod_irc settings",
|
||||
"Sie brauchen einen x:data kompatiblen Client um die mod_irc Einstellungen zu konfigurieren"}.
|
||||
{"Registration in mod_irc for ", "Registrierung in mod_irc für"}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers",
|
||||
"Geben Sie Benutzernamen und Verschlüsselung für die Verbindung zum IRC Server an"}.
|
||||
{"IRC Username", "IRC Benutzername"}.
|
||||
{"If you want to specify different encodings for IRC servers, fill this list with values in format
|
||||
'{\"irc server\", \"encoding\"}'. By default this service use \"~s\" encoding.",
|
||||
"Wenn Sie verschiedene Verschlüsselungen für IRC Server angeben, schreiben Sie diese im Format
|
||||
'{\"irc server\", \"encoding\"}'. Standardmäßig benutzt der Dienst die \"~s\" Verschlüsselung"}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].",
|
||||
"Beispiel: [{\"irc.lucky.net\",\"koi8-r\"}, {\vendetta.fef.net\", \"iso8859-1\"}]."}.
|
||||
{"Encodings", "Verschlüsselungen"}.
|
||||
|
||||
% web/ejabberd_web_admin.erl
|
||||
{"ejabberd administration", "Ejabberd Administration"}.
|
||||
{"Users", "Benutzer"}.
|
||||
{"Nodes", "Knoten(nodes)"}.
|
||||
{"Statistics", "Statistik"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin", ""}.
|
||||
{"(raw)", ""}.
|
||||
{"submitted", "bestätigt"}.
|
||||
{"bad format", "ungültiges Format"}.
|
||||
{"raw", ""}.
|
||||
{"ejabberd access control lists configuration", "Ejabberd access control lists Konfiguration"}.
|
||||
{"Delete Selected", "Lösche Markiertes"}.
|
||||
{"Submit", "Bestätigen"}.
|
||||
{"ejabberd access rules configuration", "Ejabberd Zutrittsregeln Konfiguration"}.
|
||||
{"~s access rule configuration", "~s Zurtrittsregel Konfiguration"}.
|
||||
{"ejabberd users", "Ejabberd Benutzer"}.
|
||||
{"ejabberd stats", "Ejabberd Statistik"}.
|
||||
{"Node not found", "Knoten nicht gefunden"}.
|
||||
{"Add New", "Neuen hinzufügen"}.
|
||||
{"Registered users", "Registrierte Benutzer"}.
|
||||
{"Authentificated users", "Authentifizierte Benutzer"}.
|
||||
{"Online users", "Online Benutzer"}.
|
||||
{"Outgoing S2S servers", "Rausgehende S2S Server"}.
|
||||
{"Change Password", "Passwort ändern"}.
|
||||
{"Connected Resources:", "Verbundene Resourcen"}.
|
||||
{"Password:", "Passwort:"}.
|
||||
{"None", "Keine"}.
|
||||
{"Node ", "Knoten"}.
|
||||
{"DB Management", "DB Management"}.
|
||||
{"Listened Ports Management", "Ports Management"}.
|
||||
{"Restart", "Neustart"}.
|
||||
{"Stop", "Stop"}.
|
||||
{"RPC call error", "RPC-Aufruf Fehler"}.
|
||||
{"DB Tables at ", "DB Tabellen"}.
|
||||
{"Name", "Name"}.
|
||||
{"Storage Type", "Speichertyp"}.
|
||||
{"Size", "Größe"}.
|
||||
{"Memory", "Speicher"}.
|
||||
{"Backup Management at ", "Backup Management"}.
|
||||
{"Store a backup in a file", "Die Sicherung in eine Datei legen "}.
|
||||
{"OK", "OK"}.
|
||||
{"Restore a backup from a file", "Die Sicherung aus einer Datei wiederherstellen"}.
|
||||
{"Install a database fallback from a file", "Eine Sicherungsdatenbank aus einer Datei installieren"}.
|
||||
{"Dump a database in a text file", "Ausgabe der Datenbank in eine Textdatei"}.
|
||||
{"Restore a database from a text file", "Datenbank aus einer Textdatei wiederherstellen"}.
|
||||
{"Listened Ports at ", "Listened Ports at"}.
|
||||
{"~p statistics", "~p Statistiken"}.
|
||||
{"Uptime", "Betriebszeit"}.
|
||||
{"CPU Time", "CPU Zeit"}.
|
||||
{"Transactions commited", "abgeschlossene Vorgänge"}.
|
||||
{"Transactions aborted", "abgebrochene Vorgänge"}.
|
||||
{"Transactions restarted", "neugestartete Vorgänge"}.
|
||||
{"Transactions logged", "geloggte Vorgänge"}.
|
||||
{"Port", "Port"}.
|
||||
{"Module", "Modul"}.
|
||||
{"Options", "Optionen"}.
|
||||
{"Update", "Aktualisieren"}.
|
||||
{"Delete", "Löschen"}.
|
||||
{"Add User", "Benutzer hinzufügen"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin, 2004 Process One", ""}.
|
||||
{"Offline messages", "Offline Nachrichten"}.
|
||||
{"Last Activity", "Letzte Aktion"}.
|
||||
{"Never", "Nie"}.
|
||||
{"~s offline messages queue", "~s offline Nachrichtenschlange"}.
|
||||
{"Time", "Uhrzeit"}.
|
||||
{"From", "Von"}.
|
||||
{"To", "Zu"}.
|
||||
{"Packet", "Paket"}.
|
||||
{"Offline messages:", "Offline Nachrichten"}.
|
||||
{"Roster", "Liste"}.
|
||||
{"Nickname", "Spitzname"}.
|
||||
{"Subscription", "Subskription"}.
|
||||
{"Pending", "schwebend"}.
|
||||
{"Groups", "Gruppe"}.
|
||||
{"Remove", "Entfernen"}.
|
||||
{"Add JID", "JID hinzufügen"}.
|
||||
{"User ", "Benutzer"}.
|
||||
{"Roster of ", "Liste von "}.
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
% End:
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
% Ejabberd - Spanish - badlop AT jabberes.org
|
||||
|
||||
% jlib.hrl
|
||||
{"No resource provided", "No se ha proporcionado recurso"}.
|
||||
|
||||
% mod_configure.erl
|
||||
{"DB Tables Configuration at ", "Configuración de tablas de la BD en "}.
|
||||
{"Choose storage type of tables", "Selecciona tipo de almacenamiento de las tablas"}.
|
||||
@@ -46,7 +43,7 @@
|
||||
{"Configuration", "Configuración"}.
|
||||
{"Online Users", "Usuarios conectados"}.
|
||||
{"All Users", "Todos los usuarios"}.
|
||||
{"Outgoing S2S connections", "Conexiones S2S de salida"}.
|
||||
{"Outgoing S2S connections", "Conexiones S2S salientes"}.
|
||||
{"To ~s", "A ~s"}.
|
||||
{"From ~s", "De ~s"}.
|
||||
{"Running Nodes", "Nodos funcionando"}.
|
||||
@@ -71,11 +68,11 @@
|
||||
{"Choose a username and password to register with this server", "Escoge un nombre de usuario y contraseña para registrarte en este servidor"}.
|
||||
|
||||
% mod_vcard.erl
|
||||
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin", "Servidor Jabber en Erlang\nCopyright (c) 2002-2005 Alexey Shchepin"}.
|
||||
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)", "Rellena el formulario para buscar usuarios Jabber. Añade * al final de un campo para buscar subcadenas."}.
|
||||
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo vCard para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
|
||||
{"Erlang Jabber Server\nCopyright (c) 2002-2004 Alexey Shchepin", "Servidor Jabber en Erlang\nCopyright (c) 2002-2004 Alexey Shchepin"}.
|
||||
{"ejabberd vCard module\nCopyright (c) 2003-2004 Alexey Shchepin", "Módulo vCard de Ejabberd\nCopyright (c) 2003-2004 Alexey Shchepin"}.
|
||||
{"You need an x:data capable client to search", "Necesitas un cliente con soporte de x:data para poder buscar"}.
|
||||
{"Search users in ", "Buscar usuarios en "}.
|
||||
{"Fill in fields to search for any matching Jabber User", "Rellena los campos para buscar usuarios Jabber coincidentes"}.
|
||||
{"Results of search in ", "Resultados de la búsqueda en "}.
|
||||
|
||||
{"User", "Usuario"}.
|
||||
@@ -92,21 +89,19 @@
|
||||
{"Organization Unit", "Unidad de la organización"}.
|
||||
|
||||
% mod_pubsub/mod_pubsub.erl
|
||||
{"ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo Pub/Sub para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
|
||||
{"Node Creator", "Creador del nodo"}.
|
||||
{[], " "}.
|
||||
{"ejabberd pub/sub module\nCopyright (c) 2003-2004 Alexey Shchepin", "Módulo pub/sub de Ejabberd\nCopyright (c) 2003-2004 Alexey Shchepin"}.
|
||||
|
||||
% mod_muc/mod_muc.erl
|
||||
{"You need an x:data capable client to register nickname", "Necesitas un cliente con soporte de x:data para poder registrar el apodo"}.
|
||||
{"Nickname Registration at ", "Registro del apodo en "}.
|
||||
{"Enter nickname you want to register", "Introduce el apodo que quieras registrar"}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin", "Módulo MUC de Ejabberd\nCopyright (c) 2003-2004 Alexey Shchepin"}.
|
||||
{"Only service administrators are allowed to send service messages", "Solo los administradores del servicio tienen permiso para enviar mensajes de servicio"}.
|
||||
{"Room creation is not allowed by service policy", "La política del servicio prohibe la creación de salas"}.
|
||||
{"Conference room does not exist", "La sala de conferencias no existe"}.
|
||||
{"Access denied by service policy", "Acceso denegado por la política del servicio"}.
|
||||
{"You must fill in field \"nick\" in the form", "Debes rellenar el campo \"apodo\" en el formulario"}.
|
||||
{"Specified nickname is already registered", "El apodo especificado ya está registrado, tendrás que buscar otro"}.
|
||||
{"Room creation is denied by service policy", "Se te ha denegado crear la sala por política del servicio"}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo de MUC para ejabbed\nCopyright (c) 2002-2005 Alexey Shchepin"}.
|
||||
|
||||
% mod_muc/mod_muc_room.erl
|
||||
{" has set the subject to: ", " ha puesto el asunto: "}.
|
||||
@@ -154,42 +149,42 @@
|
||||
{"private, ", "privado"}.
|
||||
|
||||
% mod_irc/mod_irc.erl
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin", "Módulo de IRC Ejabberd\nCopyright (c) 2003-2004 Alexey Shchepin"}.
|
||||
{"You need an x:data capable client to configure mod_irc settings", "Necesitas un cliente con soporte de x:data para configurar las opciones de mod_irc"}.
|
||||
{"Registration in mod_irc for ", "Registro en mod_irc para"}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers", "Introduce el nombre de usuario y codificaciones de carácteres que quieras usar al conectar en los servidores de IRC"}.
|
||||
{"IRC Username", "Nombre de usuario en IRC"}.
|
||||
{"If you want to specify different encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\"}'. By default this service use \"~s\" encoding.", "Si quieres especificar codificaciones de carácteres distintos para cada servidor IRC rellena esta lista con valores en el formato '{\"servidor irc\", \"codificación\"}'. Este servicio usa por defecto la codificación \"~s\"."}.
|
||||
{"Encodings", "Codificaciones"}.
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Módulo de IRC para ejabberd\nCopyright (c) 2002-2005 Alexey Shchepin"}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", "Ejemplo: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}]."}.
|
||||
|
||||
% web/ejabberd_web_admin.erl
|
||||
{"ejabberd administration", "Administración de ejabberd"}.
|
||||
{"ejabberd administration", "Administración de Ejabberd"}.
|
||||
{"Users", "Usuarios"}.
|
||||
{"Nodes", "Nodos"}.
|
||||
{"Statistics", "Estadísticas"}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin", "Ejabberd (c) 2002-2004 Alexey Shchepin"}.
|
||||
{"(raw)", "(crudo)"}.
|
||||
{"submitted", "enviado"}.
|
||||
{"bad format", "mal formato"}.
|
||||
{"raw", "crudo"}.
|
||||
{"ejabberd access control lists configuration", "Configuración de las Listas de Control de Acceso de ejabberd"}.
|
||||
{"ejabberd access control lists configuration", "Configuración de las Listas de Control de Acceso de Ejabberd"}.
|
||||
{"Delete Selected", "Eliminar los seleccionados"}.
|
||||
{"Submit", "Enviar"}.
|
||||
{"ejabberd access rules configuration", "Configuración de las Reglas de Acceso de ejabberd"}.
|
||||
{"ejabberd access rules configuration", "Configuración de las Reglas de Acceso de Ejabberd"}.
|
||||
{"~s access rule configuration", "Configuración de las Regla de Acceso ~s"}.
|
||||
{"ejabberd users", "Usuarios de ejabberd"}.
|
||||
{"ejabberd stats", "Estadísticas de ejabberd"}.
|
||||
{"ejabberd users", "Usuarios de Ejabberd"}.
|
||||
{"ejabberd stats", "Estadísticas de Ejabberd"}.
|
||||
{"Node not found", "Nodo no encontrado"}.
|
||||
{"Add New", "Añadir nuevo"}.
|
||||
{"Registered users", "Usuarios registrados"}.
|
||||
{"Authentificated users", "Usuarios autentificados"}.
|
||||
{"Online users", "Usuarios conectados"}.
|
||||
{"Outgoing S2S servers", "Servidores conectados"}.
|
||||
{"Outgoing S2S servers", "Servidores S2S de salida"}.
|
||||
{"Change Password", "Cambiar contraseña"}.
|
||||
{"Connected Resources:", "Recursos conectados:"}.
|
||||
{"Password:", "Contraseña:"}.
|
||||
{"None", "Ninguno"}.
|
||||
{"Node ", "Nodo "}.
|
||||
{"Node ", "Nodo"}.
|
||||
{"DB Management", "Gestión de la BD"}.
|
||||
{"Listened Ports Management", "Gestión de los puertos de escucha"}.
|
||||
{"Restart", "Reiniciar"}.
|
||||
@@ -202,11 +197,11 @@
|
||||
{"Memory", "Memoria"}.
|
||||
{"Backup Management at ", "Gestión de copia de seguridad en "}.
|
||||
{"Store a backup in a file", "Almacenar una copia de seguridad en fichero"}.
|
||||
{"OK", "Aceptar"}.
|
||||
{"OK", "De acuerdo"}.
|
||||
{"Restore a backup from a file", "Recuperar una copia de seguridad de un fichero"}.
|
||||
{"Install a database fallback from a file", "Instalar una recuperación de BD desde un fichero"}.
|
||||
{"Dump a database in a text file", "Exportar una Base de Datos a un fichero de texto"}.
|
||||
{"Restore a database from a text file", "Restaurar una Base de Datos desde un fichero de texto"}.
|
||||
{"Restore a database from a text file", "Restaurar una Bse de Datos desde un fichero de texto"}.
|
||||
{"Listened Ports at ", "Puertos de escucha en "}.
|
||||
{"~p statistics", "Estadísticas de ~p"}.
|
||||
{"Uptime", "Tiempo de funcionamiento"}.
|
||||
@@ -220,52 +215,7 @@
|
||||
{"Options", "Opciones"}.
|
||||
{"Update", "Actualizar"}.
|
||||
{"Delete", "Eliminar"}.
|
||||
{"Add User", "Añadir usuario"}.
|
||||
{"Offline messages", "Mensajes diferidos"}.
|
||||
{"Last Activity", "Última actividad"}.
|
||||
{"Never", "Nunca"}.
|
||||
{"~s offline messages queue", "~s cola de mensajes diferidos"}.
|
||||
{"Time", "Fecha"}.
|
||||
{"From", "De"}.
|
||||
{"To", "Para"}.
|
||||
{"Packet", "Paquete"}.
|
||||
{"Offline messages:", "Mensajes diferidos:"}.
|
||||
{"Roster", "Lista de contactos"}.
|
||||
{"Nickname", "Apodo"}.
|
||||
{"Subscription", "Subscripción"}.
|
||||
{"Pending", "Pendiente"}.
|
||||
{"Groups", "Grupos"}.
|
||||
{"Remove", "Borrar"}.
|
||||
{"Add JID", "Añadir JID"}.
|
||||
{"User ", "Usuario "}.
|
||||
{"Roster of ", "Lista de contactos de "}.
|
||||
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One", "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One"}.
|
||||
{"Shared Roster", "Grupos Compartidos"}.
|
||||
{"Online", "Conectado"}.
|
||||
{"JID", "JID"}.
|
||||
{"Validate", "Validar"}.
|
||||
{"Not found", "No encontrado"}.
|
||||
{"Shared roster groups", "Grupos Compartidos"}.
|
||||
{"Name:", "Nombre:"}.
|
||||
{"Description:", "Descripción:"}.
|
||||
{"Members:", "Miembros:"}.
|
||||
{"Displayed Groups:", "Mostrar grupos:"}.
|
||||
{"Group ", "Grupo "}.
|
||||
{"Users last activity", "Última actividad de los usuarios"}.
|
||||
{"Period: ", "Periodo: "}.
|
||||
{"Last month", "Último mes"}.
|
||||
{"Last year", "Último año"}.
|
||||
{"All activity", "Toda la actividad"}.
|
||||
{"Show Ordinary Table", "Mostrar Tabla Ordinaria"}.
|
||||
{"Show Integral Table", "Mostrar Tabla Integral"}.
|
||||
|
||||
% ejabberd_c2s.erl
|
||||
{"Use of STARTTLS required", "Es obligatorio usar STARTTLS"}.
|
||||
|
||||
% mod_vcard_ldap.erl
|
||||
{"Given Name", "Nombre dado"}.
|
||||
{"JID", "JID"}.
|
||||
{"Fill in fields to search for any matching Jabber User", "Rellena campos para buscar usuarios Jabber que concuerden"}.
|
||||
{"", ""}.
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
|
||||
@@ -1,47 +1,41 @@
|
||||
% $Id$
|
||||
|
||||
% jlib.hrl
|
||||
{"No resource provided", "Aucune ressource fournie"}.
|
||||
|
||||
% mod_configure.erl
|
||||
{"DB Tables Configuration at ", "Configuration des tables de BdD sur "}.
|
||||
{"DB Tables Configuration", "Configuration des tables de BD"}.
|
||||
{"Choose storage type of tables", "Choisissez un type de stockage pour les tables"}.
|
||||
{"RAM copy", "Copie en mémoire vive (RAM)"}.
|
||||
{"RAM and disc copy", "Copie en mémoire vive (RAM) et sur disque"}.
|
||||
{"Disc only copy", "Copie sur disque uniquement"}.
|
||||
{"Remote copy", "Copie distante"}.
|
||||
{"Stop Modules at ", "Arrêter les modules sur "}.
|
||||
{"Choose modules to stop", "Sélectionnez les modules à�arrêter"}.
|
||||
{"Start Modules at ", "Démarrer les modules sur "}.
|
||||
{"Enter list of {Module, [Options]}", "Entrez une liste de {Module, [Options]}"}.
|
||||
{"List of modules to start", "Liste des modules à�démarrer"}.
|
||||
{"Backup to File at ", "Sauvegarde sur fichier sur "}.
|
||||
{"Enter path to backup file", "Entrez le chemin vers le fichier de sauvegarde"}.
|
||||
{"Path to File", "Chemin vers le fichier"}.
|
||||
{"Restore Backup from File at ", "Restaurer la sauvegarde depuis le fichier sur "}.
|
||||
{"Dump Backup to Text File at ", "Enregistrer la sauvegarde dans un fichier texte sur "}.
|
||||
{"Enter path to text file", "Entrez le chemin vers le fichier texte"}.
|
||||
{"Import User from File at ", "Importer un utilisateur depuis le fichier sur "}.
|
||||
{"Enter path to jabberd1.4 spool file", "Entrez le chemin vers le fichier spool jabberd1.4"}.
|
||||
{"Import Users from Dir at ", "Importer des utilisateurs depuis le répertoire sur "}.
|
||||
{"Enter path to jabberd1.4 spool dir", "Entrez le chemin vers le répertoire de spool jabberd1.4"}.
|
||||
{"Path to Dir", "Chemin vers le répertoire"}.
|
||||
{"Hostname Configuration", "Configuration du nom d'hôte"}.
|
||||
{"Choose host name", "Choisissez le nom d'hôte"}.
|
||||
{"Host name", "Nom d'hôte"}.
|
||||
{"Access Control List Configuration", "Configuration des droits (ACL)"}.
|
||||
{"Access control lists", "Droits (ACL)"}.
|
||||
{"Access Configuration", "Configuration d'accès"}.
|
||||
{"Access rules", "Règles d'accès"}.
|
||||
{"Remove Users", "Supprimer des utilisateurs"}.
|
||||
{"Choose users to remove", "Sélectionner les utilisateurs à supprimer"}.
|
||||
{"Administration of ", "Administration de "}.
|
||||
{"Action on user", "Action sur l'utilisateur"}.
|
||||
{"Edit Properties", "Modifier les propriétés"}.
|
||||
{"Remove User", "Supprimer l'utilisateur"}.
|
||||
|
||||
% src/ejabberd_c2s.erl
|
||||
{"Use of STARTTLS required", "L'utilisation de STARTTLS est impérative"}.
|
||||
{"RAM copy", ""}.
|
||||
{"RAM and disc copy", ""}.
|
||||
{"Disc only copy", ""}.
|
||||
{"Remote copy", ""}.
|
||||
{"Stop Modules at ", ""}.
|
||||
{"Choose modules to stop", ""}.
|
||||
{"Start Modules at ", ""}.
|
||||
{"Enter list of {Module, [Options]}", ""}.
|
||||
{"List of modules to start", ""}.
|
||||
{"Backup to File at ", ""}.
|
||||
{"Enter path to backup file", ""}.
|
||||
{"Path to File", ""}.
|
||||
{"Restore Backup from File at ", ""}.
|
||||
{"Dump Backup to Text File at ", ""}.
|
||||
{"Enter path to text file", ""}.
|
||||
{"Import User from File at ", ""}.
|
||||
{"Enter path to jabberd1.4 spool file", ""}.
|
||||
{"Import Users from Dir at ", ""}.
|
||||
{"Enter path to jabberd1.4 spool dir", ""}.
|
||||
{"Path to Dir", ""}.
|
||||
{"Hostname Configuration", ""}.
|
||||
{"Choose host name", ""}.
|
||||
{"Host name", ""}.
|
||||
{"Access Control List Configuration", ""}.
|
||||
{"Access control lists", ""}.
|
||||
{"Access Configuration", ""}.
|
||||
{"Access rules", ""}.
|
||||
{"Remove Users", ""}.
|
||||
{"Choose users to remove", ""}.
|
||||
{"Administration of ", ""}.
|
||||
{"Action on user", ""}.
|
||||
{"Edit Properties", ""}.
|
||||
{"Remove User", ""}.
|
||||
|
||||
% mod_disco.erl
|
||||
{"Configuration", "Configuration"}.
|
||||
@@ -53,38 +47,32 @@
|
||||
{"Running Nodes", "Noeuds actifs"}.
|
||||
{"Stopped Nodes", "Noeuds arrêtés"}.
|
||||
{"Host Name", "Nom du serveur"}.
|
||||
{"Access Control Lists", "Droits (ACL)"}.
|
||||
{"Access Control Lists", ""}.
|
||||
{"Access Rules", "Règles d'accès"}.
|
||||
{"Remove Users", "Supprimer des utilisateurs"}.
|
||||
{"DB", "BdD"}.
|
||||
{"DB", "BD"}.
|
||||
{"Modules", "Modules"}.
|
||||
{"Start Modules", "Modules de démarrage"}.
|
||||
{"Stop Modules", "Modules d'arrêt"}.
|
||||
{"Backup Management", "Gestion des sauvegardes"}.
|
||||
{"Import users from jabberd1.4 spool files", "Importer des utilisateurs depuis un fichier spool jabberd1.4"}.
|
||||
{"Backup", "Sauvegarde"}.
|
||||
{"Restore", "Restauration"}.
|
||||
{"Dump to Text File", "Sauvegarder dans un fichier texte"}.
|
||||
{"Import File", "Importer un fichier"}.
|
||||
{"Import Directory", "Importer une répertoire"}.
|
||||
{"Backup Management", ""}.
|
||||
{"Import users from jabberd1.4 spool files", ""}.
|
||||
{"Backup", ""}.
|
||||
{"Restore", ""}.
|
||||
{"Dump to Text File", ""}.
|
||||
{"Import File", ""}.
|
||||
{"Import Directory", ""}.
|
||||
|
||||
% mod_register.erl
|
||||
{"Choose a username and password to register with this server", "Choisissez un nom d'utilisateur et un mot de passe pour s'enregistrer sur ce serveur"}.
|
||||
|
||||
% src/mod_vcard_ldap.erl
|
||||
{"Erlang Jabber Server\nCopyright (c) 2002-2005 Alexey Shchepin", "Serveur Jabber Erlang\nCopyright (c) 2002-2005 Alexey Shchepin"}.
|
||||
{"Given Name", "Prénom"}.
|
||||
{"ejabberd vCard module\nCopyright (c) 2003-2005 Alexey Shchepin", "Module vCard ejabberd\nCopyright (c) 2003-2005 Alexey Shchepin"}.
|
||||
{"JID", "JID"}.
|
||||
{"Choose a username and password to register with this server", ""}.
|
||||
|
||||
% mod_vcard.erl
|
||||
{"Erlang Jabber Server\nCopyright (c) 2002-2004 Alexey Shchepin", ""}.
|
||||
{"ejabberd vCard module\nCopyright (c) 2003-2004 Alexey Shchepin", ""}.
|
||||
{"You need an x:data capable client to search",
|
||||
"Vous avez besoin d'un client supportant x:data pour faire une recherche"}.
|
||||
{"Search users in ", "Rechercher des utilisateurs "}.
|
||||
|
||||
{"Fill in fields to search for any matching Jabber User",
|
||||
"Remplissez les champs pour rechercher un utilisateur Jabber"}.
|
||||
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)", "Remplissez le formulaire pour recherche un utilisateur Jabber (Ajouter * à la fin du champ pour chercher n'importe quelle fin de chaîne"}.
|
||||
"Remplissez les champs pour rechercher n'importe quel utilisateur Jabber correspondant"}.
|
||||
{"Results of search in ", "Résultat de la recherche des utilisateurs "}.
|
||||
|
||||
{"User", "Utilisateur"}.
|
||||
@@ -101,26 +89,24 @@
|
||||
{"Organization Unit", "Unité de l'organisation"}.
|
||||
|
||||
% mod_pubsub/mod_pubsub.erl
|
||||
{"ejabberd pub/sub module\nCopyright (c) 2003-2005 Alexey Shchepin", "Module pub/sub ejabberd \nCopyright (c) 2003-2005 Alexey Shchepin"}.
|
||||
{[], ""}.
|
||||
{"Node Creator", "Créateur du noeud"}.
|
||||
{"ejabberd pub/sub module\nCopyright (c) 2003-2004 Alexey Shchepin", ""}.
|
||||
|
||||
% mod_muc/mod_muc.erl
|
||||
{"You need an x:data capable client to register nickname", "Vous avez besoin d'un client supportant x:data pour enregistrer un pseudo"}.
|
||||
{"Nickname Registration at ", "Enregistrement d'un pseudo sur "}.
|
||||
{"Enter nickname you want to register", "Entrez le pseudo que vous souhaitez enregistrer"}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Module MUC ejabberd\nCopyright (c) 2003-2005 Alexey Shchepin"}.
|
||||
{"Only service administrators are allowed to send service messages", "Seuls les administrateurs du service sont autoriser à envoyer des messages de service"}.
|
||||
{"Room creation is denied by service policy", "La création de salons est interdite par le service"}.
|
||||
{"Conference room does not exist", "La salle de conférence n'existe pas"}.
|
||||
{"Access denied by service policy", "L'accès au service est refusé"}.
|
||||
{"You must fill in field \"nick\" in the form", "Vous devez préciser le champ \"pseudo\" dans le formulaire"}.
|
||||
{"Specified nickname is already registered", "Le pseudo demandé est déjà enregistré"}.
|
||||
{"You need an x:data capable client to register nickname", ""}.
|
||||
{"Nickname Registration at ", ""}.
|
||||
{"Enter nickname you want to register", ""}.
|
||||
{"ejabberd MUC module\nCopyright (c) 2003-2004 Alexey Shchepin", ""}.
|
||||
{"Only service administrators are allowed to send service messages", ""}.
|
||||
{"Room creation is not allowed by service policy", ""}.
|
||||
{"Conference room does not exist", ""}.
|
||||
{"Access denied by service policy", ""}.
|
||||
{"You must fill in field \"nick\" in the form", ""}.
|
||||
{"Specified nickname is already registered", ""}.
|
||||
|
||||
% mod_muc/mod_muc_room.erl
|
||||
{" has set the subject to: ", " a changé le sujet pour: "}.
|
||||
{"You need an x:data capable client to configure room", "Vous avez besoin d'un client supportant x:data pour configurer le salon"}.
|
||||
{"Configuration for ", "Configuration pour "}.
|
||||
{" has set the subject to: ", ""}.
|
||||
{"You need an x:data capable client to configure room", ""}.
|
||||
{"Configuration for ", ""}.
|
||||
{"Room title", "Titre du salon"}.
|
||||
{"Allow users to change subject?", "Autoriser les utilisateurs à changer le sujet ?"}.
|
||||
{"Allow users to query other users?",
|
||||
@@ -132,148 +118,110 @@
|
||||
{"Make room persistent?", "Créer un salon persistant ?"}.
|
||||
{"Make room moderated?", "Créer un salon modéré ?"}.
|
||||
{"Default users as members?",
|
||||
"Les utilisateurs sont membres par défaut ?"}.
|
||||
"Les utilisateurs par défaut sont membres ?"}.
|
||||
{"Make room members only?",
|
||||
"Créer un salon réservé aux membres uniquement ?"}.
|
||||
"Créer un salon réservés aux membres uniquement ?"}.
|
||||
{"Allow users to send invites?",
|
||||
"Autoriser des utilisateurs à envoyer des invitations ?"}.
|
||||
{"Make room password protected?", "Créer un salon protégé par mot de passe ?"}.
|
||||
{"Password", "Mot de passe"}.
|
||||
{"Password", ""}.
|
||||
{"Make room anonymous?", "Créer un salon anonyme ?"}.
|
||||
{"Enable logging?", "Activer la journalisation ?"}.
|
||||
{"Only moderators and participants are allowed to change subject in this room", "Seuls les modérateurs et les participants peuvent changer le sujet dans ce salon"}.
|
||||
{"Only moderators are allowed to change subject in this room", "Seuls les modérateurs peuvent changer le sujet dans ce salon"}.
|
||||
{"Visitors are not allowed to send messages to all occupants", "Les visiteurs ne sont pas autorisés à envoyer des messages à tout les occupants"}.
|
||||
{"Only occupants are allowed to send messages to the conference", "Seuls les occupants peuvent envoyer des messages à la conférence"}.
|
||||
{"It is not allowed to send normal messages to the conference", "Il n'est pas permis d'envoyer des messages \"normaux\" à la conférence"}.
|
||||
{"It is not allowed to send private messages to the conference", "Il n'est pas permis d'envoyer des messages \"normaux\" à la conférence"}.
|
||||
{"Improper message type", "Mauvais type de message"}.
|
||||
{"Nickname is already in use by another occupant", "Le pseudo est déjà utilisé par un autre occupant"}.
|
||||
{"Nickname is registered by another person", "Le pseudo est enregistré par une autre personne"}.
|
||||
{"It is not allowed to send private messages of type \"groupchat\"", "Il n'est pas permis d'envoyer des messages privés de type \"groupchat\""}.
|
||||
{"Recipient is not in the conference room", "Le destinataire n'est pas dans la conférence"}.
|
||||
{"Only occupants are allowed to send queries to the conference", "Seuls les occupants sont autorisés à envoyer des requêtes à la conférence"}.
|
||||
{"Queries to the conference members are not allowed in this room", "Les requêtes sur les membres de la conférence ne sont pas autorisé dans ce salon"}.
|
||||
{"You have been banned from this room", "Vous avez été exclus de ce salon"}.
|
||||
{"Membership required to enter this room", "Vous devez être membre pour accèder à ce salon"}.
|
||||
{"Password required to enter this room", "Un mot de passe est nécessaire pour accèder à ce salon"}.
|
||||
{"Incorrect password", "Mot de passe incorrect"}.
|
||||
{"Administrator privileges required", "Les droits d'administrateur sont nécessaires"}.
|
||||
{"Moderator privileges required", "Les droits de modérateur sont nécessaires"}.
|
||||
{"JID ~s is invalid", "Le JID ~s n'est pas valide"}.
|
||||
{"Nickname ~s does not exist in the room", "Le pseudo ~s n'existe pas dans ce salon"}.
|
||||
{"Invalid affiliation: ~s", "Affiliation invalide: ~s"}.
|
||||
{"Invalid role: ~s", "Role invalide: ~s"}.
|
||||
{"Owner privileges required", "Les droits de propriétaire sont nécessaires"}.
|
||||
{"private, ", "privé"}.
|
||||
{"Only moderators and participants are allowed to change subject in this room", ""}.
|
||||
{"Only moderators are allowed to change subject in this room", ""}.
|
||||
{"Visitors are not allowed to send messages to all occupants", ""}.
|
||||
{"Only occupants are allowed to send messages to the conference", ""}.
|
||||
{"It is not allowed to send normal messages to the conference", ""}.
|
||||
{"It is not allowed to send private messages to the conference", ""}.
|
||||
{"Improper message type", ""}.
|
||||
{"Nickname is already in use by another occupant", ""}.
|
||||
{"Nickname is registered by another person", ""}.
|
||||
{"It is not allowed to send private messages of type \"groupchat\"", ""}.
|
||||
{"Recipient is not in the conference room", ""}.
|
||||
{"Only occupants are allowed to send queries to the conference", ""}.
|
||||
{"Queries to the conference members are not allowed in this room", ""}.
|
||||
{"You have been banned from this room", ""}.
|
||||
{"Membership required to enter this room", ""}.
|
||||
{"Password required to enter this room", ""}.
|
||||
{"Incorrect password", ""}.
|
||||
{"Administrator privileges required", ""}.
|
||||
{"Moderator privileges required", ""}.
|
||||
{"JID ~s is invalid", ""}.
|
||||
{"Nickname ~s does not exist in the room", ""}.
|
||||
{"Invalid affiliation: ~s", ""}.
|
||||
{"Invalid role: ~s", ""}.
|
||||
{"Owner privileges required", ""}.
|
||||
{"private, ", ""}.
|
||||
|
||||
% mod_irc/mod_irc.erl
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2005 Alexey Shchepin", "Module IRC ejabberd\nCopyright (c) 2003-2005 Alexey Shchepin"}.
|
||||
{"You need an x:data capable client to configure mod_irc settings", "Vous avez besoin d'un client supportant x:data pour configurer le module IRC"}.
|
||||
{"Registration in mod_irc for ", "Enregistrement du mod_irc pour "}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers", "Entrez le nom d'utilisateur et les encodages que vous souhaitez utiliser pour vous connecter aux serveurs IRC"}.
|
||||
{"IRC Username", "Nom d'utilisateur IRC"}.
|
||||
{"If you want to specify different encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\"}'. By default this service use \"~s\" encoding.", "Si vous voulez préciser différents encodages pour les serveurs IRC, remplissez cette liste avec des valeurs dans le format '{\"serveur irc\", \"encodage\"}'. Par défaut ce service utilise l'encodage \"~s\"."}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", "Exemple: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}]."}.
|
||||
{"Encodings", "Encodages"}.
|
||||
{"ejabberd IRC module\nCopyright (c) 2003-2004 Alexey Shchepin", ""}.
|
||||
{"You need an x:data capable client to configure mod_irc settings", ""}.
|
||||
{"Registration in mod_irc for ", ""}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers", ""}.
|
||||
{"IRC Username", ""}.
|
||||
{"If you want to specify different encodings for IRC servers, fill this list with values in format '{\"irc server\", \"encoding\"}'. By default this service use \"~s\" encoding.", ""}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\"}, {\"vendetta.fef.net\", \"iso8859-1\"}].", ""}.
|
||||
{"Encodings", ""}.
|
||||
|
||||
% web/ejabberd_web_admin.erl
|
||||
{"ejabberd administration", "Administration d'ejabberd"}.
|
||||
{"Users", "Utilisateurs"}.
|
||||
{"Nodes", "Noeuds"}.
|
||||
{"Shared Roster", "Liste de contacts partagée"}.
|
||||
{"Statistics", "Statistiques"}.
|
||||
{"ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One", "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One"}.
|
||||
{"(raw)", "(brut)"}.
|
||||
{"submitted", "Soumis"}.
|
||||
{"bad format", "Mauvais format"}.
|
||||
{"raw", "brut"}.
|
||||
{"ejabberd access control lists configuration", "Configuration des droits (ACL) ejabberd"}.
|
||||
{"Delete Selected", "Suppression des éléments sélectionnés"}.
|
||||
{"Submit", "Soumettre"}.
|
||||
{"ejabberd access rules configuration", "Configuration des règles d'accès ejabberd"}.
|
||||
{"~s access rule configuration", "Configuration des règles d'accès ~s"}.
|
||||
{"ejabberd users", "Utilisateurs ejabberd"}.
|
||||
{"ejabberd stats", "Statistiques ejabberd"}.
|
||||
{"Node not found", "Noeud non trouvé"}.
|
||||
{"Add New", "Ajouter"}.
|
||||
{"Registered users", "Utilisateurs enregistrés"}.
|
||||
{"Authentificated users", "Utilisateurs authentifiés"}.
|
||||
{"Online users", "Utilisateurs connectés"}.
|
||||
{"Outgoing S2S servers", "Connexions S2S sortantes"}.
|
||||
{"Change Password", "Modifier le mot de passe"}.
|
||||
{"Connected Resources:", "Ressources connectées:"}.
|
||||
{"Password:", "Mot de passe:"}.
|
||||
{"None", "Aucun"}.
|
||||
{"Node ", "Noeud"}.
|
||||
{"DB Management", "Gestion BdD"}.
|
||||
{"Listened Ports Management", "Gestion des ports ouverts"}.
|
||||
{"Restart", "Redémarrer"}.
|
||||
{"Stop", "Arrêter"}.
|
||||
{"RPC call error", "Erreur d'appel RPC"}.
|
||||
{"DB Tables at ", "Table de BdD sur "}.
|
||||
{"Name", "Nom"}.
|
||||
{"Storage Type", "Type de stockage"}.
|
||||
{"Size", "Taille"}.
|
||||
{"Memory", "Mémoire"}.
|
||||
{"Backup Management at ", "Gestion des sauvegardes sur "}.
|
||||
{"Store a backup in a file", "Sauvegarder les données dans un fichier"}.
|
||||
{"OK", "OK"}.
|
||||
{"Restore a backup from a file", "Recharger une sauvegarde depuis un fichier"}.
|
||||
{"Install a database fallback from a file", "Installer un point de contrôle de base de données depuis un fichier"}.
|
||||
{"Dump a database in a text file", "Sauvegarder la base de données dans un fichier texte"}.
|
||||
{"Restore a database from a text file", "Restaurer la base de données depuis un fichier texte"}.
|
||||
{"Listened Ports at ", "Ports ouverts sur "}.
|
||||
{"~p statistics", "Statistiques ~p"}.
|
||||
{"Uptime", "Temps depuis le démarrage"}.
|
||||
{"CPU Time", "Temps CPU"}.
|
||||
{"Transactions commited", "Transactions commitées"}.
|
||||
{"Transactions aborted", "Transactions annulées"}.
|
||||
{"Transactions restarted", "Transactions redémarrées"}.
|
||||
{"Transactions logged", "Transactions journalisées"}.
|
||||
{"Port", "Port"}.
|
||||
{"Module", "Module"}.
|
||||
{"Options", "Options"}.
|
||||
{"Update", "Mettre à jour"}.
|
||||
{"Delete", "Supprimer"}.
|
||||
{"Add User", "Ajouter un utilisateur"}.
|
||||
{"Offline messages", "Messages en attente"}.
|
||||
{"Last Activity", "Dernière activité"}.
|
||||
{"Never", "Jamais"}.
|
||||
{"~s offline messages queue", "~s messages en file d'attente"}.
|
||||
{"Time", "Heure"}.
|
||||
{"From", "De"}.
|
||||
{"To", "A"}.
|
||||
{"Packet", "Paquet"}.
|
||||
{"Offline messages:", "Messages en attente:"}.
|
||||
{"Roster", "Liste de contacts"}.
|
||||
{"Nickname", "Pseudo"}.
|
||||
{"Subscription", "Abonnement"}.
|
||||
{"Pending", "En suspend"}.
|
||||
{"Groups", "Groupes"}.
|
||||
{"Remove", "Enlever"}.
|
||||
{"Add JID", "Ajouter un JID"}.
|
||||
{"User ", "Utilisateur"}.
|
||||
{"Roster of ", "Liste de contact de "}.
|
||||
{"Online", "En ligne"}.
|
||||
{"Validate", "Valider"}.
|
||||
{"Not found", "Non trouvé"}.
|
||||
{"Shared roster groups", "Groupes pour la liste de contacts partagée"}.
|
||||
{"Name:", "Nom:"}.
|
||||
{"Description:", "Description:"}.
|
||||
{"Members:", "Membres:"}.
|
||||
{"Displayed Groups:", "Groupes affichés:"}.
|
||||
{"Group ", "Groupe "}.
|
||||
{"Users last activity", "Dernière activité des utilisateurs"}.
|
||||
{"Period: ", "Période:"}.
|
||||
{"Last month", "Dernier mois"}.
|
||||
{"Last year", "Dernière année"}.
|
||||
{"All activity", "Toute activité"}.
|
||||
{"Show Ordinary Table", "Montrer la table ordinaire"}.
|
||||
{"Show Integral Table", "Montrer la table intégralement"}.
|
||||
{"Modules Management", "Gestion des modules"}.
|
||||
{"Modules at ", "Modules sur "}.
|
||||
{"Start", "Démarrer"}.
|
||||
{"ejabberd administration", ""}.
|
||||
{"Users", ""}.
|
||||
{"Nodes", ""}.
|
||||
{"Statistics", ""}.
|
||||
{"ejabberd (c) 2002-2004 Alexey Shchepin", ""}.
|
||||
{"(raw)", ""}.
|
||||
{"submitted", ""}.
|
||||
{"bad format", ""}.
|
||||
{"raw", ""}.
|
||||
{"ejabberd access control lists configuration", ""}.
|
||||
{"Delete Selected", ""}.
|
||||
{"Submit", ""}.
|
||||
{"ejabberd access rules configuration", ""}.
|
||||
{"~s access rule configuration", ""}.
|
||||
{"ejabberd users", ""}.
|
||||
{"ejabberd stats", ""}.
|
||||
{"Node not found", ""}.
|
||||
{"Add New", ""}.
|
||||
{"Registered users", ""}.
|
||||
{"Authentificated users", ""}.
|
||||
{"Online users", ""}.
|
||||
{"Outgoing S2S servers", ""}.
|
||||
{"Change Password", ""}.
|
||||
{"Connected Resources:", ""}.
|
||||
{"Password:", ""}.
|
||||
{"None", ""}.
|
||||
{"Node ", ""}.
|
||||
{"DB Management", ""}.
|
||||
{"Listened Ports Management", ""}.
|
||||
{"Restart", ""}.
|
||||
{"Stop", ""}.
|
||||
{"RPC call error", ""}.
|
||||
{"DB Tables at ", ""}.
|
||||
{"Name", ""}.
|
||||
{"Storage Type", ""}.
|
||||
{"Size", ""}.
|
||||
{"Memory", ""}.
|
||||
{"Backup Management at ", ""}.
|
||||
{"Store a backup in a file", ""}.
|
||||
{"OK", ""}.
|
||||
{"Restore a backup from a file", ""}.
|
||||
{"Install a database fallback from a file", ""}.
|
||||
{"Dump a database in a text file", ""}.
|
||||
{"Restore a database from a text file", ""}.
|
||||
{"Listened Ports at ", ""}.
|
||||
{"~p statistics", ""}.
|
||||
{"Uptime", ""}.
|
||||
{"CPU Time", ""}.
|
||||
{"Transactions commited", ""}.
|
||||
{"Transactions aborted", ""}.
|
||||
{"Transactions restarted", ""}.
|
||||
{"Transactions logged", ""}.
|
||||
{"Port", ""}.
|
||||
{"Module", ""}.
|
||||
{"Options", ""}.
|
||||
{"Update", ""}.
|
||||
{"Delete", ""}.
|
||||
{"", ""}.
|
||||
|
||||
% Local Variables:
|
||||
% mode: erlang
|
||||
|
||||