Compare commits

..

1 Commits

Author SHA1 Message Date
Alexey Shchepin cec11aca59 Added release tags
SVN Revision: 306
2005-04-17 00:21:31 +00:00
139 changed files with 5046 additions and 18224 deletions
-3
View File
@@ -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
-748
View File
@@ -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)
-21
View File
@@ -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"
).
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

+21 -25
View File
@@ -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>&nbsp;&nbsp;How it works</H3><!--SEC END -->
@@ -170,13 +166,13 @@ XMLElement = {xmlelement, Name, Attrs, [ElementOrCDATA]}
CDATA = {xmlcdata, string()}
</PRE>E.&nbsp;g. this stanza:
<PRE>
&lt;message to='test@conference.example.org' type='groupchat'&gt;
&lt;message to='test@conference.e.localhost' type='groupchat'&gt;
&lt;body&gt;test&lt;/body&gt;
&lt;/message&gt;
</PRE>represented as following structure:
<PRE>
{xmlelement, "message",
[{"to", "test@conference.example.org"},
[{"to", "test@conference.e.localhost"},
{"type", "groupchat"}],
[{xmlelement, "body",
[],
+25 -27
View File
@@ -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",
[],
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

+126 -445
View File
@@ -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&nbsp;&nbsp;Introduction</A>
<LI><A HREF="#htoc2">2&nbsp;&nbsp;Installation from Source</A>
<LI><A HREF="#htoc2">2&nbsp;&nbsp;Installation</A>
<UL><LI>
<A HREF="#htoc3">2.1&nbsp;&nbsp;Installation Requirements</A>
<UL><LI>
@@ -55,7 +55,7 @@
<UL><LI>
<A HREF="#htoc12">3.1&nbsp;&nbsp;Initial Configuration</A>
<UL><LI>
<A HREF="#htoc13">3.1.1&nbsp;&nbsp;Host Names</A>
<A HREF="#htoc13">3.1.1&nbsp;&nbsp;Host Name</A>
<LI><A HREF="#htoc14">3.1.2&nbsp;&nbsp;Default Language</A>
<LI><A HREF="#htoc15">3.1.3&nbsp;&nbsp;Access Rules</A>
<LI><A HREF="#htoc16">3.1.4&nbsp;&nbsp;Shapers Configuration</A>
@@ -68,7 +68,7 @@
<LI><A HREF="#htoc21">3.2.2&nbsp;&nbsp;<TT>ejabberdctl</TT> tool</A>
</UL>
</UL>
<LI><A HREF="#htoc22">4&nbsp;&nbsp;Clustering</A>
<LI><A HREF="#htoc22">4&nbsp;&nbsp;Distribution</A>
<UL><LI>
<A HREF="#htoc23">4.1&nbsp;&nbsp;How it works</A>
<UL><LI>
@@ -77,37 +77,32 @@
<LI><A HREF="#htoc26">4.1.3&nbsp;&nbsp;Session Manager</A>
<LI><A HREF="#htoc27">4.1.4&nbsp;&nbsp;S2S Manager</A>
</UL>
<LI><A HREF="#htoc28">4.2&nbsp;&nbsp;How to setup ejabberd cluster</A>
</UL>
<LI><A HREF="#htoc29">A&nbsp;&nbsp;Built-in Modules</A>
<LI><A HREF="#htoc28">A&nbsp;&nbsp;Built-in Modules</A>
<UL><LI>
<A HREF="#htoc30">A.1&nbsp;&nbsp;Common Options</A>
<A HREF="#htoc29">A.1&nbsp;&nbsp;Common Options</A>
<UL><LI>
<A HREF="#htoc31">A.1.1&nbsp;&nbsp;<TT>iqdisc</TT></A>
<LI><A HREF="#htoc32">A.1.2&nbsp;&nbsp;<TT>host</TT></A>
<LI><A HREF="#htoc33">A.1.3&nbsp;&nbsp;<TT>hosts</TT></A>
<A HREF="#htoc30">A.1.1&nbsp;&nbsp;Option <TT>iqdisc</TT></A>
<LI><A HREF="#htoc31">A.1.2&nbsp;&nbsp;Option <TT>host</TT></A>
</UL>
<LI><A HREF="#htoc34">A.2&nbsp;&nbsp;<TT>mod_announce</TT></A>
<LI><A HREF="#htoc35">A.3&nbsp;&nbsp;<TT>mod_configure</TT></A>
<LI><A HREF="#htoc36">A.4&nbsp;&nbsp;<TT>mod_disco</TT></A>
<LI><A HREF="#htoc37">A.5&nbsp;&nbsp;<TT>mod_echo</TT></A>
<LI><A HREF="#htoc38">A.6&nbsp;&nbsp;<TT>mod_irc</TT></A>
<LI><A HREF="#htoc39">A.7&nbsp;&nbsp;<TT>mod_last</TT></A>
<LI><A HREF="#htoc40">A.8&nbsp;&nbsp;<TT>mod_muc</TT></A>
<LI><A HREF="#htoc41">A.9&nbsp;&nbsp;<TT>mod_offline</TT></A>
<LI><A HREF="#htoc42">A.10&nbsp;&nbsp;<TT>mod_privacy</TT></A>
<LI><A HREF="#htoc43">A.11&nbsp;&nbsp;<TT>mod_private</TT></A>
<LI><A HREF="#htoc44">A.12&nbsp;&nbsp;<TT>mod_pubsub</TT></A>
<LI><A HREF="#htoc45">A.13&nbsp;&nbsp;<TT>mod_register</TT></A>
<LI><A HREF="#htoc46">A.14&nbsp;&nbsp;<TT>mod_roster</TT></A>
<LI><A HREF="#htoc47">A.15&nbsp;&nbsp;<TT>mod_service_log</TT></A>
<LI><A HREF="#htoc48">A.16&nbsp;&nbsp;<TT>mod_shared_roster</TT></A>
<LI><A HREF="#htoc49">A.17&nbsp;&nbsp;<TT>mod_stats</TT></A>
<LI><A HREF="#htoc50">A.18&nbsp;&nbsp;<TT>mod_time</TT></A>
<LI><A HREF="#htoc51">A.19&nbsp;&nbsp;<TT>mod_vcard</TT></A>
<LI><A HREF="#htoc52">A.20&nbsp;&nbsp;<TT>mod_version</TT></A>
<LI><A HREF="#htoc32">A.2&nbsp;&nbsp;<TT>mod_configure</TT></A>
<LI><A HREF="#htoc33">A.3&nbsp;&nbsp;<TT>mod_disco</TT></A>
<LI><A HREF="#htoc34">A.4&nbsp;&nbsp;<TT>mod_echo</TT></A>
<LI><A HREF="#htoc35">A.5&nbsp;&nbsp;<TT>mod_irc</TT></A>
<LI><A HREF="#htoc36">A.6&nbsp;&nbsp;<TT>mod_last</TT></A>
<LI><A HREF="#htoc37">A.7&nbsp;&nbsp;<TT>mod_muc</TT></A>
<LI><A HREF="#htoc38">A.8&nbsp;&nbsp;<TT>mod_offline</TT></A>
<LI><A HREF="#htoc39">A.9&nbsp;&nbsp;<TT>mod_privacy</TT></A>
<LI><A HREF="#htoc40">A.10&nbsp;&nbsp;<TT>mod_private</TT></A>
<LI><A HREF="#htoc41">A.11&nbsp;&nbsp;<TT>mod_pubsub</TT></A>
<LI><A HREF="#htoc42">A.12&nbsp;&nbsp;<TT>mod_register</TT></A>
<LI><A HREF="#htoc43">A.13&nbsp;&nbsp;<TT>mod_roster</TT></A>
<LI><A HREF="#htoc44">A.14&nbsp;&nbsp;<TT>mod_stats</TT></A>
<LI><A HREF="#htoc45">A.15&nbsp;&nbsp;<TT>mod_time</TT></A>
<LI><A HREF="#htoc46">A.16&nbsp;&nbsp;<TT>mod_vcard</TT></A>
<LI><A HREF="#htoc47">A.17&nbsp;&nbsp;<TT>mod_version</TT></A>
</UL>
<LI><A HREF="#htoc53">B&nbsp;&nbsp;I18n/L10n</A>
<LI><A HREF="#htoc48">B&nbsp;&nbsp;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>&nbsp;&nbsp;Installation from Source</H2><!--SEC END -->
<H2><A NAME="htoc2">2</A>&nbsp;&nbsp;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
&lt;press Enter when asked for a password&gt;
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>&nbsp;&nbsp;Host Names</H4><!--SEC END -->
<H4><A NAME="htoc13">3.1.1</A>&nbsp;&nbsp;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.&nbsp;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.&nbsp;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.&nbsp;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, &lt;username&gt;}</TT></B><DD> Matches user with name
<TT>&lt;username&gt;</TT> at the first virtual host. Example:
</PRE><DT><B><TT>{user, &lt;username&gt;}</TT></B><DD> Matches local user with name
<TT>&lt;username&gt;</TT>. Example:
<PRE>
{acl, admin, {user, "aleksey"}}.
</PRE><DT><B><TT>{user, &lt;username&gt;, &lt;server&gt;}</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, &lt;regexp&gt;}</TT></B><DD> Matches local user with name that
matches <TT>&lt;regexp&gt;</TT> at the first virtual host. Example:
matches <TT>&lt;regexp&gt;</TT>. Example:
<PRE>
{acl, tests, {user, "^test[0-9]*$"}}.
</PRE><DT><B><TT>{user_regexp, &lt;regexp&gt;, &lt;server&gt;}</TT></B><DD> Matches user with name
that matches <TT>&lt;regexp&gt;</TT> and from server <TT>&lt;server&gt;</TT>. Example:
<PRE>
{acl, tests, {user, "^test", "example.org"}}.
{acl, tests, {user, "^test", "localhost"}}.
</PRE><DT><B><TT>{server_regexp, &lt;regexp&gt;}</TT></B><DD> Matches any JID from server that
matches <TT>&lt;regexp&gt;</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, &lt;accessname&gt;, [{allow, &lt;aclname&gt;},
{deny, &lt;aclname&gt;},
@@ -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&nbsp;<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.&nbsp;g.
with such config:
username and password of <TT>ejabberd</TT> administrator. E.&nbsp;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&nbsp;<A HREF="#fig:webadmmain">1</A>.
authentification you should see something like in figure&nbsp;<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>&nbsp;&nbsp;Clustering</H2><!--SEC END -->
<H2><A NAME="htoc22">4</A>&nbsp;&nbsp;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>&nbsp;&nbsp;How it works</H3><!--SEC END -->
@@ -737,126 +712,58 @@ router;
<H4><A NAME="htoc24">4.1.1</A>&nbsp;&nbsp;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>&nbsp;&nbsp;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>&nbsp;&nbsp;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>&nbsp;&nbsp;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>&nbsp;&nbsp;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>&nbsp;&nbsp;Built-in Modules</H2><!--SEC END -->
<H2><A NAME="htoc28">A</A>&nbsp;&nbsp;Built-in Modules</H2><!--SEC END -->
<A NAME="sec:modules"></A>
<!--TOC subsection Common Options-->
<H3><A NAME="htoc30">A.1</A>&nbsp;&nbsp;Common Options</H3><!--SEC END -->
<H3><A NAME="htoc29">A.1</A>&nbsp;&nbsp;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>&nbsp;&nbsp;<TT>iqdisc</TT></H4><!--SEC END -->
<H4><A NAME="htoc30">A.1.1</A>&nbsp;&nbsp;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>&nbsp;&nbsp;<TT>host</TT></H4><!--SEC END -->
<H4><A NAME="htoc31">A.1.2</A>&nbsp;&nbsp;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>&nbsp;&nbsp;<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>&nbsp;&nbsp;<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>&nbsp;&nbsp;<TT>mod_configure</TT></H3><!--SEC END -->
<H3><A NAME="htoc32">A.2</A>&nbsp;&nbsp;<TT>mod_configure</TT></H3><!--SEC END -->
<A NAME="sec:modconfigure"></A>
Options:
@@ -979,7 +822,7 @@ discipline (see&nbsp;<A HREF="#sec:modiqdiscoption">A.1.1</A>).
</DL>
<!--TOC subsection <TT>mod_disco</TT>-->
<H3><A NAME="htoc36">A.4</A>&nbsp;&nbsp;<TT>mod_disco</TT></H3><!--SEC END -->
<H3><A NAME="htoc33">A.3</A>&nbsp;&nbsp;<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>&nbsp;&nbsp;<TT>mod_echo</TT></H3><!--SEC END -->
<H3><A NAME="htoc34">A.4</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modhostoption">A.1.2</A>).
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
(see&nbsp;<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&nbsp;<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>&nbsp;&nbsp;<TT>mod_irc</TT></H3><!--SEC END -->
<H3><A NAME="htoc35">A.5</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modhostoption">A.1.2</A>).
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
(see&nbsp;<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&nbsp;<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>&nbsp;&nbsp;<TT>mod_last</TT></H3><!--SEC END -->
<H3><A NAME="htoc36">A.6</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modiqdiscoption">A.1.1</A>).
</DL>
<!--TOC subsection <TT>mod_muc</TT>-->
<H3><A NAME="htoc40">A.8</A>&nbsp;&nbsp;<TT>mod_muc</TT></H3><!--SEC END -->
<H3><A NAME="htoc37">A.7</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modhostoption">A.1.2</A>).
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
(see&nbsp;<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&nbsp;<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>&nbsp;&nbsp;<TT>mod_offline</TT></H3><!--SEC END -->
<H3><A NAME="htoc38">A.8</A>&nbsp;&nbsp;<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>&nbsp;&nbsp;<TT>mod_privacy</TT></H3><!--SEC END -->
<H3><A NAME="htoc39">A.9</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modiqdiscoption">A.1.1</A>).
</DL>
<!--TOC subsection <TT>mod_private</TT>-->
<H3><A NAME="htoc43">A.11</A>&nbsp;&nbsp;<TT>mod_private</TT></H3><!--SEC END -->
<H3><A NAME="htoc40">A.10</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modiqdiscoption">A.1.1</A>).
</DL>
<!--TOC subsection <TT>mod_pubsub</TT>-->
<H3><A NAME="htoc44">A.12</A>&nbsp;&nbsp;<TT>mod_pubsub</TT></H3><!--SEC END -->
<H3><A NAME="htoc41">A.11</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modhostoption">A.1.2</A>).
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
(see&nbsp;<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&nbsp;<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>&nbsp;&nbsp;<TT>mod_register</TT></H3><!--SEC END -->
<H3><A NAME="htoc42">A.12</A>&nbsp;&nbsp;<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>&nbsp;&nbsp;<TT>mod_roster</TT></H3><!--SEC END -->
<H3><A NAME="htoc43">A.13</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modiqdiscoption">A.1.1</A>).
</DL>
<!--TOC subsection <TT>mod_service_log</TT>-->
<H3><A NAME="htoc47">A.15</A>&nbsp;&nbsp;<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 &lt;route/&gt; 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>&nbsp;&nbsp;<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&nbsp;<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>&nbsp;</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&nbsp;<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>&nbsp;</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>&nbsp;&nbsp;<TT>mod_stats</TT></H3><!--SEC END -->
<H3><A NAME="htoc44">A.14</A>&nbsp;&nbsp;<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&nbsp;<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>&nbsp;&nbsp;<TT>mod_time</TT></H3><!--SEC END -->
<H3><A NAME="htoc45">A.15</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modiqdiscoption">A.1.1</A>).
</DL>
<!--TOC subsection <TT>mod_vcard</TT>-->
<H3><A NAME="htoc51">A.19</A>&nbsp;&nbsp;<TT>mod_vcard</TT></H3><!--SEC END -->
<H3><A NAME="htoc46">A.16</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modhostoption">A.1.2</A>).
<DT><B><TT>hosts</TT></B><DD> Defines hostnames of the service
(see&nbsp;<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&nbsp;<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&nbsp;<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>&nbsp;&nbsp;<TT>mod_version</TT></H3><!--SEC END -->
<H3><A NAME="htoc47">A.17</A>&nbsp;&nbsp;<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&nbsp;<A HREF="#sec:modiqdiscoption">A.1.1</A>).
</DL>
<!--TOC section I18n/L10n-->
<H2><A NAME="htoc53">B</A>&nbsp;&nbsp;I18n/L10n</H2><!--SEC END -->
<H2><A NAME="htoc48">B</A>&nbsp;&nbsp;I18n/L10n</H2><!--SEC END -->
<A NAME="sec:i18nl10n"></A>
All built-in modules support <TT>xml:lang</TT> attribute inside IQ queries.
E.&nbsp;g. on figure&nbsp;<A HREF="#fig:discorus">2</A> showed the reply on the following query:
<PRE>
&lt;iq id='5'
to='example.org'
to='e.localhost'
type='get'
xml:lang='ru'&gt;
&lt;query xmlns='http://jabber.org/protocol/disco#items'/&gt;
+68 -361
View File
@@ -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'/>
-88
View File
@@ -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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 24 KiB

-50
View File
@@ -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>
-136
View File
@@ -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>
-128
View File
@@ -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
+12 -12
View File
@@ -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)
+9 -12
View File
@@ -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)
+9 -43
View File
@@ -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/>
+19 -278
View File
@@ -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
+6 -7
View File
@@ -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
-2
View File
@@ -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 ++
+6 -12
View File
@@ -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);
+19 -18
View File
@@ -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))),
+6 -7
View File
@@ -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}]};
_ ->
+5 -21
View File
@@ -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, []},
+4 -3
View File
@@ -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").
+320 -54
View File
@@ -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.
-71
View File
@@ -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.
-239
View File
@@ -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.
-135
View File
@@ -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.
-206
View File
@@ -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.
+118 -308
View File
@@ -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.
+2 -12
View File
@@ -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;
+66 -191
View File
@@ -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).
-183
View File
@@ -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.
+3 -9
View File
@@ -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);
+55 -53
View File
@@ -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.
+1 -4
View File
@@ -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
%%%----------------------------------------------------------------------
+17 -44
View File
@@ -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}.
+35 -101
View File
@@ -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
+5 -13
View File
@@ -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() ->
+8
View File
@@ -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.
+6 -10
View File
@@ -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 ->
+100 -147
View File
@@ -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
+31 -47
View File
@@ -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,
+9 -4
View File
@@ -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:
+1 -1
View File
@@ -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),
+68 -82
View File
@@ -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 */
-76
View File
@@ -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.
+5 -55
View File
@@ -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.
+232 -118
View File
@@ -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).
-120
View File
@@ -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)
+15 -67
View File
@@ -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}).
-320
View File
@@ -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.
+21 -12
View File
@@ -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}) ->
+1 -1
View File
@@ -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 ->
+92 -187
View File
@@ -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}], []}.
+4 -5
View File
@@ -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.
+16 -6
View File
@@ -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)
+1 -1
View File
@@ -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;
+43 -91
View File
@@ -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.
+96 -130
View File
@@ -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).
+19 -69
View File
@@ -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)
-147
View File
@@ -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, "'"]).
+10 -4
View File
@@ -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)
+78 -183
View File
@@ -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.
+131 -153
View File
@@ -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"}],
+38 -224
View File
@@ -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.
-246
View File
@@ -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, "'"]).
+34 -87
View File
@@ -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.
+21 -75
View File
@@ -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.
+9 -3
View File
@@ -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)
+63 -143
View File
@@ -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.
+9 -10
View File
@@ -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),
+79 -161
View File
@@ -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.
-18
View File
@@ -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 = []}).
-745
View File
@@ -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')"].
-59
View File
@@ -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).
-291
View File
@@ -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).
+19 -36
View File
@@ -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").
+92 -274
View File
@@ -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.
-576
View File
@@ -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.
-271
View File
@@ -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:
+19 -69
View File
@@ -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
+155 -207
View File
@@ -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

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